diff options
author | Kaz Kylheku <kaz@kylheku.com> | 2022-05-24 07:25:24 -0700 |
---|---|---|
committer | Kaz Kylheku <kaz@kylheku.com> | 2022-05-24 07:25:24 -0700 |
commit | 7b39d691a0fa1cffb2553237318c224d91bb9407 (patch) | |
tree | af831a63ba71960c3b61cc38c9b0394931313f53 | |
parent | fd37633953e63ac317b3dfd663f5cb0659d0aceb (diff) | |
download | txr-7b39d691a0fa1cffb2553237318c224d91bb9407.tar.gz txr-7b39d691a0fa1cffb2553237318c224d91bb9407.tar.bz2 txr-7b39d691a0fa1cffb2553237318c224d91bb9407.zip |
ffi: reproduce odd GNU C behavior for aligned bitfields.
We've already taken care of imitating the situation that GNU
C allows __attribute__((aligned(n))) to weaken the alignment
of a bitfield, contrary to it being documented that align only
strengthens alignment. Even a value of n == 1 is meaningful
in that it can cause the bitfield to start allocating from
a new byte.
This patch corrects a newly discovered nuance: when a bitfield
is attributed with a weaker alignment than its underlying
type (e.g. uint32_t field marked with 2 byte alignment),
the original type's alignment is still in effect for calculating
the alignment of the structure, and the padding.
* ffi.c (struct txr_ffi_type): New member oalign, for keeping
track of the type's original alignment, prior to adjustment.
(make_ffi_type_struct): For a named bitfield, take the oalign
value into account when determining the most strict member
alignment.
(ffi_type_compile): When marking a type as aligned, the
we remember the original alignment in atft->oalign.
* tests/017/bitfields.tl: New test case, struct s16.
* txr.1: Documented.
-rw-r--r-- | ffi.c | 18 | ||||
-rw-r--r-- | tests/017/bitfields.tl | 7 | ||||
-rw-r--r-- | txr.1 | 9 |
3 files changed, 27 insertions, 7 deletions
@@ -202,7 +202,7 @@ struct txr_ffi_type { val lt; val syntax; val eltype; - cnum size, align; + cnum size, align, oalign; unsigned shift; union { unsigned mask; @@ -3864,8 +3864,12 @@ static val make_ffi_type_struct(val syntax, val lisp_type, offs += bit_offs / 8; bit_offs %= 8; - if (slot && most_align < align) - most_align = align; + if (slot) { + if (align > most_align) + most_align = align; + if ((ucnum) mtft->oalign > most_align) + most_align = mtft->oalign; + } } else { memb[i].offs = offs; offs += size; @@ -4647,11 +4651,15 @@ val ffi_type_compile(val syntax) } else { val altype_copy = ffi_type_copy(altype); struct txr_ffi_type *atft = ffi_type_struct(altype_copy); - if (al > atft->align || sym == pack_s || + cnum oalign = atft->align; + if (al > atft->align || sym == pack_s || atft->bitfield || (opt_compat && opt_compat <= 275)) atft->align = al; - if (al != 1 || sym != pack_s) + if (al != 1 || sym != pack_s) { + if (!atft->oalign) + atft->oalign = oalign; atft->aligned = 1; + } return altype_copy; } } diff --git a/tests/017/bitfields.tl b/tests/017/bitfields.tl index 5e8277f1..155a8b95 100644 --- a/tests/017/bitfields.tl +++ b/tests/017/bitfields.tl @@ -598,3 +598,10 @@ (b (bit 7 le-uint32))))) (conv-test #S(s15 x 0 a #x1ff b #x7f) #b'00ffff') + +(typedef s16 (struct s16 + (x uint8) + (a (align 2 (bit 9 le-uint32))) + (b (align 2 (bit 7 le-uint32))))) + +(conv-test #S(s16 x 0 a #x1ff b #x7f) #b'0000ff017f000000') @@ -81259,7 +81259,8 @@ That is to say, values of .meta width which are less than or equal to .metn type 's -existing alignment have no effect. +existing alignment have no effect on alignment, except when the +type is used as a bitfield. The .code pack @@ -81315,7 +81316,11 @@ both specified with a of 1. If the requested alignment for the type of a bitfield is 1, and the previous member is a bitfield which has left a byte partially filled, then the new bitfield starts on a fresh byte, even if it would otherwise -be packed with the previous bitfield. +be packed with the previous bitfield. If a named bitfield has weakened +alignment, other than one byte alignment produced by +.codn pack , +the bitfield's original type's alignment is used for the purposes of +determining its contribution to the alignment of the structure. When .meta type |