summaryrefslogtreecommitdiffstats
path: root/ffi.c
diff options
context:
space:
mode:
authorKaz Kylheku <kaz@kylheku.com>2020-08-24 08:25:19 -0700
committerKaz Kylheku <kaz@kylheku.com>2020-08-24 08:25:19 -0700
commit1cf4652639d22e0e3ed7385e20cdcad6c5a6df2f (patch)
tree5d0ed41db4c0666e43845ac87297229962a544c4 /ffi.c
parent2b088c2de1e23cc7c01533a49746fbdca55034d7 (diff)
downloadtxr-1cf4652639d22e0e3ed7385e20cdcad6c5a6df2f.tar.gz
txr-1cf4652639d22e0e3ed7385e20cdcad6c5a6df2f.tar.bz2
txr-1cf4652639d22e0e3ed7385e20cdcad6c5a6df2f.zip
ffi/doc: bugfixes to bitfield alignment.
Three issues issues. The documentation is wrong about the concept of a "leading bitfield" whose alignment matters. The alignment of all named bitfields matters. Secondly, the alignment of unnamed bitfields doesn't matter. Thirdly, there is a flaw in the treatment of bitfields in unions, from both alignment and sizing. * ffi.c (make_ffi_type_struct): A bitfield member contributes to alignment regardless of its offset within an allocation unit: no bits_alloc == 0 check should be applied. Secondly, a bitfield member that is unnamed has an alignment of 1, otherwise the alignment of its declared type. (make_ffi_type_union): The size and alignment of a bitfield member is not simply that of its type. The size of a bitfield is the number of bytes required to store all of the bits. The alignment is 1 if it is unnamed, otherwise that of its type. The sizing of bitfields now means that alignment can add padding to a union. For instance, a uint32_t x : 17 bitfield has size 3, but alignment 4, requiring one byte of alignment padding in the union so that its size is 4. This means that we must now do the padding alignment calculation for unions to adjust the size, just like we do for structures.
Diffstat (limited to 'ffi.c')
-rw-r--r--ffi.c29
1 files changed, 17 insertions, 12 deletions
diff --git a/ffi.c b/ffi.c
index d8522d89..821c7685 100644
--- a/ffi.c
+++ b/ffi.c
@@ -3293,6 +3293,7 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
ucnum unit_offs = offs & align_mask;
ucnum bits_alloc = 8 * (offs - unit_offs) + bit_offs;
ucnum room = bits_type - bits_alloc;
+ ucnum align = if3(slot, mtft->align, 1);
if (bits == 0) {
if (offs != unit_offs || bit_offs > 0)
@@ -3307,11 +3308,6 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
bit_offs = bits_alloc = 0;
}
- if (bits_alloc == 0) {
- if (most_align < (ucnum) mtft->align)
- most_align = mtft->align;
- }
-
memb[i].offs = offs;
#if HAVE_LITTLE_ENDIAN
@@ -3326,6 +3322,9 @@ static val make_ffi_type_struct(val syntax, val lisp_type,
bit_offs += bits;
offs += bit_offs / 8;
bit_offs %= 8;
+
+ if (most_align < align)
+ most_align = align;
} else {
ucnum align = mtft->align;
ucnum almask = align - 1;
@@ -3451,14 +3450,10 @@ static val make_ffi_type_union(val syntax, val use_existing, val self)
setcheck(obj, slot);
setcheck(obj, type);
- if (most_align < (ucnum) mtft->align)
- most_align = mtft->align;
-
- if (biggest_size < (ucnum) mtft->size)
- biggest_size = mtft->size;
-
if (mtft->bitfield) {
ucnum bits = mtft->nelem;
+ ucnum size = (bits + 7) / 8;
+ ucnum align = if3(slot, mtft->align, 1);
if (bits == 0) {
nmemb--, i--;
@@ -3474,6 +3469,16 @@ static val make_ffi_type_union(val syntax, val use_existing, val self)
mtft->mask = UINT_MAX;
else
mtft->mask = ((1U << bits) - 1) << mtft->shift;
+
+ if (most_align < align)
+ most_align = align;
+ if (biggest_size < size)
+ biggest_size = size;
+ } else {
+ if (most_align < (ucnum) mtft->align)
+ most_align = mtft->align;
+ if (biggest_size < (ucnum) mtft->size)
+ biggest_size = mtft->size;
}
}
@@ -3486,7 +3491,7 @@ static val make_ffi_type_union(val syntax, val use_existing, val self)
tft->nelem = i;
- tft->size = biggest_size;
+ tft->size = (biggest_size + most_align - 1) & ~(most_align - 1);
tft->align = most_align;
#if HAVE_LIBFFI