This is an archive of the discontinued LLVM Phabricator instance.

Respect alignment of nested bitfields
ClosedPublic

Authored by uweigand on Jul 8 2015, 6:20 AM.

Details

Summary

tools/clang/test/CodeGen/packed-nest-unpacked.c contains this test:

struct XBitfield {

unsigned b1 : 10;
unsigned b2 : 12;
unsigned b3 : 10;

};
struct YBitfield {

char x;
struct XBitfield y;

} __attribute((packed));
struct YBitfield gbitfield;

unsigned test7() {

// CHECK: @test7
// CHECK: load i32, i32* getelementptr inbounds (%struct.YBitfield, %struct.YBitfield* @gbitfield, i32 0, i32 1, i32 0), align 4
return gbitfield.y.b2;

}

The "align 4" is actually wrong. Accessing all of "gbitfield.y" as a single
i32 is of course possible, but that still doesn't make it 4-byte aligned as
it remains packed at offset 1 in the surrounding gbitfield object.

This alignment was changed by commit r169489 by chandlerc, which also
introduced changes to bitfield access code in CGExpr.cpp. Code before
that change used to take into account *both* the alignment of the field to
be accessed within the current struct, *and* the alignment of that outer
struct itself; this logic was removed by the above commit.

However, this is incorrect; we do need to consider both these alignment values:

LV.getAlignment() is the alignment of the surrounding struct
                  (see e.g. EmitLValueForField code computing this value)
Info.StorageAlignment is the alignment of the storage backing
                      the bitfield relative to the struct

Neglecting to consider both values can cause incorrect code to be generated
(I've seen an unaligned access crash on SystemZ due to this bug).

This patch adds back logic to also consider alignment of the outer struct
in EmitLoadOfBitfieldLValue and EmitStoreThroughBitfieldLValue. It also
extends the test case to cover the case of storing into the nested bitfield.

(Patch originally posted to cfe-commits; now moved to Phabricator
to simplify review.)

Diff Detail

Repository
rL LLVM

Event Timeline

uweigand updated this revision to Diff 29261.Jul 8 2015, 6:20 AM
uweigand retitled this revision from to Respect alignment of nested bitfields.
uweigand updated this object.
uweigand added reviewers: rjmccall, rsmith, chandlerc.
uweigand added a subscriber: cfe-commits.
rjmccall edited edge metadata.Jul 8 2015, 5:11 PM

Good catch!

The better fix would be to ignore the bitfield's abstract storage alignment and instead use the base l-value's alignment, adjusted with alignmentAtOffset to the offset of the bitfield's storage. This ensures we get the right alignment when the base l-value is *over*-aligned: in a packed struct, bitfield storage at offset 0 will have alignment 1, but if the packed struct itself is known to have alignment 4 (e.g., if it is embedded in an unpacked struct), then we still know that the storage has alignment 4.alignmentAtOffset(0) == 4.

Also, please use CreateAlignedStore and CreateAlignedLoad.

uweigand updated this revision to Diff 29347.Jul 9 2015, 10:03 AM
uweigand edited edge metadata.

Addressed review comments:

  • Use CreateAlignedLoad / CreateAlignedStore

Good catch!

The better fix would be to ignore the bitfield's abstract storage alignment and instead use the base l-value's alignment, adjusted with alignmentAtOffset to the offset of the bitfield's storage. This ensures we get the right alignment when the base l-value is *over*-aligned: in a packed struct, bitfield storage at offset 0 will have alignment 1, but if the packed struct itself is known to have alignment 4 (e.g., if it is embedded in an unpacked struct), then we still know that the storage has alignment 4.alignmentAtOffset(0) == 4.

Good point. Unfortunately, at the point the access is generated, there doesn't appear to be an easy way to retrieve the offset of the bitfield's storage relative to the surrounding struct. This value is present as "StartOffset" in CGRecordLowering::setBitFieldInfo, but is not actually stored in the CGBitFieldInfo struct. Maybe instead of the StorageAlignment field, the CGBitFieldInfo struct should instead have a StorageOffset field?

Also, please use CreateAlignedStore and CreateAlignedLoad.

OK, done.

Good catch!

The better fix would be to ignore the bitfield's abstract storage alignment and instead use the base l-value's alignment, adjusted with alignmentAtOffset to the offset of the bitfield's storage. This ensures we get the right alignment when the base l-value is *over*-aligned: in a packed struct, bitfield storage at offset 0 will have alignment 1, but if the packed struct itself is known to have alignment 4 (e.g., if it is embedded in an unpacked struct), then we still know that the storage has alignment 4.alignmentAtOffset(0) == 4.

Good point. Unfortunately, at the point the access is generated, there doesn't appear to be an easy way to retrieve the offset of the bitfield's storage relative to the surrounding struct. This value is present as "StartOffset" in CGRecordLowering::setBitFieldInfo, but is not actually stored in the CGBitFieldInfo struct. Maybe instead of the StorageAlignment field, the CGBitFieldInfo struct should instead have a StorageOffset field?

Storing the offset makes sense. If this means that we don't otherwise need StorageAlignment — probably true — then we can drop that at the same time.

uweigand updated this revision to Diff 29434.Jul 10 2015, 3:29 AM

Replaced CGBitFieldInfo::StorageAlignment with a StorageOffset field.

LGTM, thanks!

This revision was automatically updated to reflect the committed changes.