summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ffi.c47
-rw-r--r--txr.1156
2 files changed, 137 insertions, 66 deletions
diff --git a/ffi.c b/ffi.c
index 52627547..3871cd7d 100644
--- a/ffi.c
+++ b/ffi.c
@@ -2695,7 +2695,6 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
ucnum most_align = 0;
int need_out_handler = 0;
int bit_offs = 0;
- int prev_size = 0;
const int bits_int = 8 * sizeof(int);
ft->type = FFI_TYPE_STRUCT;
@@ -2727,53 +2726,55 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
memb[i].mtft = mtft;
if (bitfield_syntax_p(mtft->syntax)) {
- int bits_type = 8 * mtft->size;
- int bits = mtft->nelem;
- int room = bits_type - bit_offs; /* assuming same size, checked below */
+ ucnum size = mtft->size;
+ ucnum bits_type = 8 * size;
+ ucnum bits = mtft->nelem;
+ ucnum offs_mask = size - 1;
+ ucnum align_mask = ~offs_mask;
+ ucnum unit_offs = offs & align_mask;
+ ucnum bits_alloc = 8 * (offs - unit_offs) + bit_offs;
+ ucnum room = bits_type - bits_alloc;
if (bits == 0) {
- if (bit_offs > 0) {
- offs += prev_size;
- bit_offs = 0;
- prev_size = 0;
- }
+ if (offs != unit_offs)
+ offs = unit_offs + size;
+ bit_offs = 0;
nmemb--, i--;
continue;
}
- if ((prev_size && prev_size != mtft->size) || bits > room) {
- offs += prev_size;
+ if (bits > room) {
+ offs = unit_offs + size;
bit_offs = 0;
}
- if (bit_offs == 0) {
- ucnum almask = mtft->align - 1;
- offs = (offs + almask) & ~almask;
+ if (bits_alloc == 0) {
if (most_align < mtft->align)
most_align = mtft->align;
}
- memb[i].offs = offs;
+ memb[i].offs = unit_offs;
#if HAVE_LITTLE_ENDIAN
- mtft->shift = bit_offs;
+ mtft->shift = bits_alloc;
#else
- mtft->shift = bits_int - bit_offs - bits;
+ mtft->shift = bits_int - bits_alloc - bits;
#endif
if (bits == bits_int)
mtft->mask = UINT_MAX;
else
mtft->mask = ((1U << bits) - 1) << mtft->shift;
bit_offs += bits;
- prev_size = mtft->size;
+ offs += bit_offs / 8;
+ bit_offs %= 8;
} else {
ucnum align = mtft->align;
ucnum almask = align - 1;
if (bit_offs > 0) {
- offs += prev_size;
+ bug_unless (bit_offs < 8);
+ offs++;
bit_offs = 0;
- prev_size = 0;
}
offs = (offs + almask) & ~almask;
@@ -2787,8 +2788,10 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
need_out_handler = need_out_handler || mtft->out != 0;
}
- if (bit_offs > 0)
- offs += prev_size;
+ if (bit_offs > 0) {
+ bug_unless (bit_offs < 8);
+ offs++;
+ }
tft->nelem = i;
diff --git a/txr.1 b/txr.1
index ce42dfca..912c15d8 100644
--- a/txr.1
+++ b/txr.1
@@ -54571,34 +54571,21 @@ bitfields, it is allocated in the same manner as a named member of type
.code uint
would be at the same position.
-If a bitfield with non-zero width is not preceded by any non-zero-width
-bitfield, then a new unit is allocated, and the bitfield is placed into the
-first available position in that unit. On a big-endian machine, the first
-available position starts at the most significant bit of the underlying
-storage word. On a little-endian machine, the first available bit position
-is the least significant bit of the storage word.
-
-If a non-zero-width bitfield is preceded by a non-zero-width bitfield, then
-the new bitfield is packed into the same storage unit as that bitfield if
-there is enough remaining room in that unit. Otherwise, it is placed into a
-new unit. Bitfields are not split across unit boundaries.
-
A zero-length bitfield is permitted. It may be given a name, but the field
will not perform any conversions to and from the corresponding slot in the
-Lisp structure. Note that the FFI struct definition itself causes the
-corresponding Lisp structure type to come into existence, then the Lisp
-structure type will have slots for all the zero width named bitfields,
+Lisp structure. Note that in situations when the FFI struct definition
+causes the corresponding Lisp structure type to come into existence, the
+Lisp structure type will have slots for all the zero width named bitfields,
even though those slots don't participate in any conversions in conjunction
with the FFI type.
-A zero-length bitfield functions in a similar manner in the FFI type
-system as in the C language. If it is placed between two bitfields, then it
-forces them to be in separate storage units. That is to say, the bitfield
-which follows is placed into a new storage unit, even if the previous
-bitfield leaves enough room in it storage unit.
+The presence of a zero-length bitfield ensures that a subsequent
+structure member, whether bitfield or not, is placed in a new storage
+unit of the size of the bitfield's base type.
-A zero-length bitfield that does not appear between non-zero-length
-bitfields has no effect.
+Details about the algorithm by which bitfields are allocated within a structure
+are given in the paragraph below entitled
+.BR "Bitfield Allocation Rules" .
A
.code ubit
@@ -54692,23 +54679,11 @@ and
.code sbit
apply to
.code bit
-also. The existence of
-.code bit
-creates the possibility that bitfields of different sizes may be
-placed adjacently within a structure. The rule is that whenever a non-zero-width
-bitfield follows another non-zero-width bitfield of a different storage
-unit size, a new storage unit begins for the new bitfield, even if the
-previous storage unit has room for the new bitfield. Bitfields occupying
-units of different sizes are never placed into the same unit.
-
-For this consideration, only size matters, not type or signedness. If two
-consecutive non-zero-width bitfields have storage unit types of the same
-size, they can be packed into the same storage unit.
-
-The alignment of the storage units follows that of type from which
-they are derived, unless overridden with the
-.code align
-operator.
+also.
+
+Details about the algorithm by which bitfields are allocated within a structure
+are given in the paragraph below entitled
+.BR "Bitfield Allocation Rules" .
.meIP ({buf | buf-d} << size )
The parametrized
@@ -54876,11 +54851,10 @@ to manipulate the alignment of individual members.
When
.code align
is applied to the type of a bitfield member of a structure, it has no effect on
-placement, except when applied to the leading bitfield which begins a new
-storage unit. The alignment of such a leading bitfield determines the
-alignment of that storage unit, and is taken into account for determining the
-most strictly aligned member of the structure. The alignment of all other
-bitfields is ignored.
+placement. The alignment of a non-zero bitfield which begins a new
+storage unit is taken into consideration for the purpose of determining
+the most strictly alignment member of the structure. The alignment of all
+other bitfields is ignored.
.PP
The following additional typedef names are defined denoting some common
@@ -54985,6 +54959,100 @@ members of structs and elements of arrays.
representation to the foreign representations exhibiting the specified
endianness.
+.NP* Bitfield Allocation Rules
+The \*(TL FFI type system follows rules for bitfield allocation which were
+experimentally derived from the behavior of the GNU C compiler on several
+mainstream architectures.
+
+The allocation algorithm can be imagined to walk through the structure
+from the first member to the last, maintaining a byte offset
+.I O
+which indicates how many whole bytes have been allocated to members so far,
+and a bit offset
+.I B
+which indicates, additionally, how many bits have been allocated in the
+byte which follows these
+.I O
+bytes, between 0 and 7.
+
+When a non-bitfield member is placed, then there are two cases: either
+.I B
+is zero (only
+.I O
+bytes have been allocated, with no fractional byte) or else
+.I B
+is nonzero. In this latter case,
+.I B
+is reset to zero and
+.I O
+is incremented by one. In either case,
+.I O
+is adjusted up to the required alignment boundary for the new member.
+The member is placed, and
+.I O
+is incremented again by the size of that member.
+
+When a bitfield member is placed, the algorithm considers the structure
+to be allocated in units of the base type of that bitfield member.
+For instance if the bitfield is derived from type
+.code uint16
+then the structure's layout is considered to have been allocated in
+.code uint16
+units. The algorithm examines the value of
+.I O
+and
+.I B
+to determine the first available unit in which at least
+one bit of unallocated space remains.
+Then, if the unit at that offset has enough space to hold the new
+bitfield, according to the bitfield's width, then the bitfield is
+placed into that unit. Otherwise, the bitfield is placed into the
+next available unit.
+
+Whenever a bitfield is placed at the start of new unit of its size,
+it is called the
+.IR "leading bitfield" .
+The alignment of the leading bitfield is taken into account for
+the purposes of determining the most strictly aligned member of
+the structure. This alignment property of the leading bitfield
+can be altered with the
+.code align
+type operator, applied either to the bitfield type, or to its base type.
+
+After a bitfield is placed, the values of
+.I O
+and
+.I B
+are adjusted so that
+.I O
+reflects the whole number of bytes which have been allocated to the
+structure so far, and
+.I B
+indicates the 0 to 7 additional bits of any bitfield material protruding
+past those whole bytes.
+
+A zero-width bitfield is also considered with regard to the storage
+unit size indicated by its type. As in the case of the nonzero-width
+bitfield, the offset of the first available unit is found which
+has at least one bit of unallocated space. Then, if that unit is
+entirely empty, the zero-width bitfield has no effect. If that unit is
+partially filled, then
+.I O
+is adjusted to point to the next unit after that, and
+.I B
+is reset to zero. Note that according to this semantics, a zero-width bitfield
+can have an effect even if placed between non-bitfield members.
+
+If, after the placement of all structure members,
+.I B
+has a nonzero value, then the offset
+.I O
+is incremented by one to cover that byte.
+
+Lastly, the size of the structure is then padded up to a size
+which is a multiple of the alignment of the most strictly aligned
+member.
+
.NP* FFI Call Descriptors
The FFI mechanism makes use of a type-like representation called the "call