FFI: Simplify initializer rules. Clarify docs.

This commit is contained in:
Mike Pall 2011-01-23 14:23:21 +01:00
parent f529d22869
commit 72b3fff72f
6 changed files with 58 additions and 33 deletions

View File

@ -73,8 +73,8 @@ The FFI library is tightly integrated into LuaJIT (it's not available
as a separate module). The code generated by the JIT-compiler for as a separate module). The code generated by the JIT-compiler for
accesses to C data structures from Lua code is on par with the accesses to C data structures from Lua code is on par with the
code a C compiler would generate. Calls to C functions can code a C compiler would generate. Calls to C functions can
be inlined in the JIT-compiled code, unlike calls to functions bound be inlined in JIT-compiled code, unlike calls to functions bound via
via the classic Lua/C API. the classic Lua/C API.
</p> </p>
<p> <p>
This page gives a short introduction to the usage of the FFI library. This page gives a short introduction to the usage of the FFI library.
@ -253,14 +253,17 @@ would consume 40&nbsp;Megabytes in plain Lua (on x64).
Next, performance: the pure Lua version runs in 9.57 seconds (52.9 Next, performance: the pure Lua version runs in 9.57 seconds (52.9
seconds with the Lua interpreter) and the FFI version runs in 0.48 seconds with the Lua interpreter) and the FFI version runs in 0.48
seconds on my machine (YMMV). That's a factor of 20x faster (110x seconds on my machine (YMMV). That's a factor of 20x faster (110x
faster than with plain Lua). faster than the Lua interpreter).
</p> </p>
<p style="font-size: 8pt;"> <p style="font-size: 8pt;">
The avid reader may notice that converting the pure Lua version over The avid reader may notice that converting the pure Lua version over
to use array indexes for the colors (<tt>[1]</tt> instead of to use array indexes for the colors (<tt>[1]</tt> instead of
<tt>.red</tt>, <tt>[2]</tt> instead of <tt>.green</tt> etc.) ought to <tt>.red</tt>, <tt>[2]</tt> instead of <tt>.green</tt> etc.) ought to
be more compact and faster. This is certainly true (by a factor of be more compact and faster. This is certainly true (by a factor of
~1.7x), but the resulting code would be less idiomatic and rather ~1.7x). Switching to a struct-of-arrays would help, too.
</p>
<p style="font-size: 8pt;">
However the resulting code would be less idiomatic and rather
error-prone. And it still doesn't get even close to the performance of error-prone. And it still doesn't get even close to the performance of
the FFI version of the code. Also, high-level data structures cannot the FFI version of the code. Also, high-level data structures cannot
be easily passed to other C&nbsp;functions, especially I/O functions, be easily passed to other C&nbsp;functions, especially I/O functions,

View File

@ -195,23 +195,10 @@ require the <tt>nelem</tt> argument. The second syntax uses a ctype as
a constructor and is otherwise fully equivalent. a constructor and is otherwise fully equivalent.
</p> </p>
<p> <p>
The <tt>init</tt> arguments provide optional initializers. The created The cdata object is initialized according to the
cdata object is filled with zero bytes if no initializers are given. <a href="ext_ffi_semantics.html#init">rules for initializers</a>,
Scalar types accept a single initializer. Aggregates can either be using the optional <tt>init</tt> arguments. Excess initializers cause
initialized with a flat list of initializers or a single aggregate an error.
initializer (see the <a href="ext_ffi_semantics.html#convert">C&nbsp;type
conversion rules</a>). Excess initializers cause an error.
</p>
<p>
If a single initializer is given for an array, it's repeated for all
remaining elements. This doesn't happen if two or more initializers
are given &mdash; all uninitialized elements are filled with zero
bytes. The fields of a <tt>struct</tt> are initialized in the order of
their declaration. Uninitialized fields are filled with zero bytes.
Only the first field of <tt>union</tt> can be initialized with a flat
initializer. Elements or fields which are aggregates themselves are
initialized with a <em>single</em> <tt>init</tt> argument, but this
may be an aggregate initializer of course.
</p> </p>
<p> <p>
Performance notice: if you want to create many objects of one kind, Performance notice: if you want to create many objects of one kind,
@ -357,8 +344,8 @@ order of arguments!
<h3 id="ffi_abi"><tt>status = ffi.abi(param)</tt></h3> <h3 id="ffi_abi"><tt>status = ffi.abi(param)</tt></h3>
<p> <p>
Returns <tt>true</tt> if <tt>param</tt> (a Lua string) applies for the Returns <tt>true</tt> if <tt>param</tt> (a Lua string) applies for the
target ABI (Application Binary Interface). Otherwise returns target ABI (Application Binary Interface). Returns <tt>false</tt>
<tt>false</tt>. The following parameters are currently defined: otherwise. The following parameters are currently defined:
</p> </p>
<table class="abitable"> <table class="abitable">
<tr class="abihead"> <tr class="abihead">

View File

@ -70,6 +70,47 @@ TODO
TODO TODO
</p> </p>
<h2 id="init">Initializers</h2>
<p>
Creating a cdata object with <a href="ffi_ext_api.html#ffi_new">ffi.new()</a>
or the equivalent constructor syntax always initializes its contents,
too. Different rules apply, depending on the number of optional
initializers and the C&nbsp;types involved:
</p>
<ul>
<li>If no initializers are given, the object is filled with zero bytes.</li>
<li>Scalar types (numbers and pointers) accept a single initializer.
The standard <a href="#convert">C&nbsp;type conversion rules</a>
apply.</li>
<li>Valarrays (complex numbers and vectors) are treated like scalars
when a single initializer is given. Otherwise they are treated like
regular arrays.</li>
<li>Aggregate types (arrays and structs) accept either a single
compound initializer (Lua table or string) or a flat list of
initializers.</li>
<li>The elements of an array are initialized, starting at index zero.
If a single initializer is given for an array, it's repeated for all
remaining elements. This doesn't happen if two or more initializers
are given: all remaining uninitialized elements are filled with zero
bytes.</li>
<li>The fields of a <tt>struct</tt> are initialized in the order of
their declaration. Uninitialized fields are filled with zero
bytes.</li>
<li>Only the first field of a <tt>union</tt> can be initialized with a
flat initializer.</li>
<li>Elements or fields which are aggregates themselves are initialized
with a <em>single</em> initializer, but this may be a compound
initializer or a compatible aggregate, of course.</li>
</ul>
<h2 id="clib">C Library Namespaces</h2> <h2 id="clib">C Library Namespaces</h2>
<p> <p>
A C&nbsp;library namespace is a special kind of object which allows A C&nbsp;library namespace is a special kind of object which allows

View File

@ -679,17 +679,12 @@ static void cconv_struct_init(CTState *cts, CType *d, CTSize sz, uint8_t *dp,
** This is true if an aggregate is to be initialized with a value. ** This is true if an aggregate is to be initialized with a value.
** Valarrays are treated as values here so ct_tv handles (V|C, I|F). ** Valarrays are treated as values here so ct_tv handles (V|C, I|F).
*/ */
int lj_cconv_multi_init(CTState *cts, CType *d, TValue *o) int lj_cconv_multi_init(CType *d, TValue *o)
{ {
if (!(ctype_isrefarray(d->info) || ctype_isstruct(d->info))) if (!(ctype_isrefarray(d->info) || ctype_isstruct(d->info)))
return 0; /* Destination is not an aggregate. */ return 0; /* Destination is not an aggregate. */
if (tvistab(o) || (tvisstr(o) && !ctype_isstruct(d->info))) if (tvistab(o) || (tvisstr(o) && !ctype_isstruct(d->info)))
return 0; /* Initializer is not a value. */ return 0; /* Initializer is not a value. */
if (tviscdata(o)) {
CTInfo info = lj_ctype_rawref(cts, cdataV(o)->typeid)->info;
if (ctype_isrefarray(info) || ctype_isstruct(info))
return 0; /* Initializer is not a value. */
}
return 1; /* Otherwise the initializer is a value. */ return 1; /* Otherwise the initializer is a value. */
} }
@ -699,7 +694,7 @@ void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz,
{ {
if (len == 0) if (len == 0)
memset(dp, 0, sz); memset(dp, 0, sz);
else if (len == 1 && !lj_cconv_multi_init(cts, d, o)) else if (len == 1 && !lj_cconv_multi_init(d, o))
lj_cconv_ct_tv(cts, d, dp, o, 0); lj_cconv_ct_tv(cts, d, dp, o, 0);
else if (ctype_isarray(d->info)) /* Also handles valarray init with len>1. */ else if (ctype_isarray(d->info)) /* Also handles valarray init with len>1. */
cconv_array_init(cts, d, sz, dp, o, len); cconv_array_init(cts, d, sz, dp, o, len);

View File

@ -58,7 +58,7 @@ LJ_FUNC int lj_cconv_tv_bf(CTState *cts, CType *s, TValue *o, uint8_t *sp);
LJ_FUNC void lj_cconv_ct_tv(CTState *cts, CType *d, LJ_FUNC void lj_cconv_ct_tv(CTState *cts, CType *d,
uint8_t *dp, TValue *o, CTInfo flags); uint8_t *dp, TValue *o, CTInfo flags);
LJ_FUNC void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o); LJ_FUNC void lj_cconv_bf_tv(CTState *cts, CType *d, uint8_t *dp, TValue *o);
LJ_FUNC int lj_cconv_multi_init(CTState *cts, CType *d, TValue *o); LJ_FUNC int lj_cconv_multi_init(CType *d, TValue *o);
LJ_FUNC void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz, LJ_FUNC void lj_cconv_ct_init(CTState *cts, CType *d, CTSize sz,
uint8_t *dp, TValue *o, MSize len); uint8_t *dp, TValue *o, MSize len);

View File

@ -566,8 +566,7 @@ static void crec_alloc(jit_State *J, RecordFFData *rd, CTypeID id)
CType *d = ctype_raw(cts, id); CType *d = ctype_raw(cts, id);
TRef trcd = emitir(IRTG(IR_CNEW, IRT_CDATA), trid, TREF_NIL); TRef trcd = emitir(IRTG(IR_CNEW, IRT_CDATA), trid, TREF_NIL);
J->base[0] = trcd; J->base[0] = trcd;
if (J->base[1] && !J->base[2] && if (J->base[1] && !J->base[2] && !lj_cconv_multi_init(d, &rd->argv[1])) {
!lj_cconv_multi_init(cts, d, &rd->argv[1])) {
goto single_init; goto single_init;
} else if (ctype_isarray(d->info)) { } else if (ctype_isarray(d->info)) {
CType *dc = ctype_rawchild(cts, d); /* Array element type. */ CType *dc = ctype_rawchild(cts, d); /* Array element type. */