Skip to content

Commit 4005edd

Browse files
[CIR] Add ComputeVolatileBitfields Implementation (#151252)
This PR adds the implementation of the `ComputeVolatileBitfields` function for the AAPCS ABI, following the rules described in [AAPCS64 §8.1.8.5 Volatile Bit-fields](https://github.com/ARM-software/abi-aa/blob/f52e1ad3f81254497a83578dc102f6aac89e52d0/aapcs64/aapcs64.rst#8185volatile-bit-fields----preserving-number-and-width-of-container-accesses). When accessing a volatile bit-field either reading or writing the compiler must perform a load or store using the access size that matches the width of the declared type (i.e., the type of the container), rather than the packed bit-field size. For example, if a field is declared as `int`, it must read or write 32 bits, even if the bit-field is only 3 bits wide. The `ComputeVolatileBitfields` function calculates the correct values and offsets necessary for proper lowering of volatile bitfields. Support for emitting calls to `get_bitfield` and `set_bitfield` with the correct access size for volatile bitfields will be implemented in a future PR.
1 parent 08c5944 commit 4005edd

File tree

2 files changed

+174
-1
lines changed

2 files changed

+174
-1
lines changed

clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp

Lines changed: 101 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ struct CIRRecordLowering final {
9191
return astContext.getTargetInfo().getABI().starts_with("aapcs");
9292
}
9393

94+
/// Helper function to check if the target machine is BigEndian.
95+
bool isBigEndian() const { return astContext.getTargetInfo().isBigEndian(); }
96+
9497
CharUnits bitsToCharUnits(uint64_t bitOffset) {
9598
return astContext.toCharUnitsFromBits(bitOffset);
9699
}
@@ -771,7 +774,104 @@ void CIRRecordLowering::computeVolatileBitfields() {
771774
!cirGenTypes.getCGModule().getCodeGenOpts().AAPCSBitfieldWidth)
772775
return;
773776

774-
assert(!cir::MissingFeatures::armComputeVolatileBitfields());
777+
for (auto &[field, info] : bitFields) {
778+
mlir::Type resLTy = cirGenTypes.convertTypeForMem(field->getType());
779+
780+
if (astContext.toBits(astRecordLayout.getAlignment()) <
781+
getSizeInBits(resLTy).getQuantity())
782+
continue;
783+
784+
// CIRRecordLowering::setBitFieldInfo() pre-adjusts the bit-field offsets
785+
// for big-endian targets, but it assumes a container of width
786+
// info.storageSize. Since AAPCS uses a different container size (width
787+
// of the type), we first undo that calculation here and redo it once
788+
// the bit-field offset within the new container is calculated.
789+
const unsigned oldOffset =
790+
isBigEndian() ? info.storageSize - (info.offset + info.size)
791+
: info.offset;
792+
// Offset to the bit-field from the beginning of the struct.
793+
const unsigned absoluteOffset =
794+
astContext.toBits(info.storageOffset) + oldOffset;
795+
796+
// Container size is the width of the bit-field type.
797+
const unsigned storageSize = getSizeInBits(resLTy).getQuantity();
798+
// Nothing to do if the access uses the desired
799+
// container width and is naturally aligned.
800+
if (info.storageSize == storageSize && (oldOffset % storageSize == 0))
801+
continue;
802+
803+
// Offset within the container.
804+
unsigned offset = absoluteOffset & (storageSize - 1);
805+
// Bail out if an aligned load of the container cannot cover the entire
806+
// bit-field. This can happen for example, if the bit-field is part of a
807+
// packed struct. AAPCS does not define access rules for such cases, we let
808+
// clang to follow its own rules.
809+
if (offset + info.size > storageSize)
810+
continue;
811+
812+
// Re-adjust offsets for big-endian targets.
813+
if (isBigEndian())
814+
offset = storageSize - (offset + info.size);
815+
816+
const CharUnits storageOffset =
817+
astContext.toCharUnitsFromBits(absoluteOffset & ~(storageSize - 1));
818+
const CharUnits end = storageOffset +
819+
astContext.toCharUnitsFromBits(storageSize) -
820+
CharUnits::One();
821+
822+
const ASTRecordLayout &layout =
823+
astContext.getASTRecordLayout(field->getParent());
824+
// If we access outside memory outside the record, than bail out.
825+
const CharUnits recordSize = layout.getSize();
826+
if (end >= recordSize)
827+
continue;
828+
829+
// Bail out if performing this load would access non-bit-fields members.
830+
bool conflict = false;
831+
for (const auto *f : recordDecl->fields()) {
832+
// Allow sized bit-fields overlaps.
833+
if (f->isBitField() && !f->isZeroLengthBitField())
834+
continue;
835+
836+
const CharUnits fOffset = astContext.toCharUnitsFromBits(
837+
layout.getFieldOffset(f->getFieldIndex()));
838+
839+
// As C11 defines, a zero sized bit-field defines a barrier, so
840+
// fields after and before it should be race condition free.
841+
// The AAPCS acknowledges it and imposes no restritions when the
842+
// natural container overlaps a zero-length bit-field.
843+
if (f->isZeroLengthBitField()) {
844+
if (end > fOffset && storageOffset < fOffset) {
845+
conflict = true;
846+
break;
847+
}
848+
}
849+
850+
const CharUnits fEnd =
851+
fOffset +
852+
astContext.toCharUnitsFromBits(astContext.toBits(
853+
getSizeInBits(cirGenTypes.convertTypeForMem(f->getType())))) -
854+
CharUnits::One();
855+
// If no overlap, continue.
856+
if (end < fOffset || fEnd < storageOffset)
857+
continue;
858+
859+
// The desired load overlaps a non-bit-field member, bail out.
860+
conflict = true;
861+
break;
862+
}
863+
864+
if (conflict)
865+
continue;
866+
// Write the new bit-field access parameters.
867+
// As the storage offset now is defined as the number of elements from the
868+
// start of the structure, we should divide the Offset by the element size.
869+
info.volatileStorageOffset =
870+
storageOffset /
871+
astContext.toCharUnitsFromBits(storageSize).getQuantity();
872+
info.volatileStorageSize = storageSize;
873+
info.volatileOffset = offset;
874+
}
775875
}
776876

777877
void CIRRecordLowering::accumulateBases(const CXXRecordDecl *cxxRecordDecl) {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -fclangir -emit-cir -fdump-record-layouts %s -o %t.cir 1> %t.cirlayout
2+
// RUN: FileCheck --input-file=%t.cirlayout %s --check-prefix=CIR-LAYOUT
3+
4+
// RUN: %clang_cc1 -triple aarch64-unknown-linux-gnu -emit-llvm -fdump-record-layouts %s -o %t.ll 1> %t.ogcglayout
5+
// RUN: FileCheck --input-file=%t.ogcglayout %s --check-prefix=OGCG-LAYOUT
6+
7+
typedef struct {
8+
unsigned int a : 9;
9+
volatile unsigned int b : 1;
10+
unsigned int c : 1;
11+
} st1;
12+
13+
// CIR-LAYOUT: BitFields:[
14+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:a offset:0 size:9 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:0 volatileStorageSize:32 volatileStorageOffset:0>
15+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:b offset:9 size:1 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:9 volatileStorageSize:32 volatileStorageOffset:0>
16+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:c offset:10 size:1 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:10 volatileStorageSize:32 volatileStorageOffset:0>
17+
18+
// OGCG-LAYOUT: BitFields:[
19+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:0 Size:9 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:0 VolatileStorageSize:32 VolatileStorageOffset:0>
20+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:9 Size:1 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:9 VolatileStorageSize:32 VolatileStorageOffset:0>
21+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:10 Size:1 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:10 VolatileStorageSize:32 VolatileStorageOffset:0>
22+
23+
// different base types
24+
typedef struct{
25+
volatile short a : 3;
26+
volatile int b: 13;
27+
volatile long c : 5;
28+
} st2;
29+
30+
// CIR-LAYOUT: BitFields:[
31+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:a offset:0 size:3 isSigned:1 storageSize:32 storageOffset:0 volatileOffset:0 volatileStorageSize:16 volatileStorageOffset:0>
32+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:b offset:3 size:13 isSigned:1 storageSize:32 storageOffset:0 volatileOffset:3 volatileStorageSize:32 volatileStorageOffset:0>
33+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:c offset:16 size:5 isSigned:1 storageSize:32 storageOffset:0 volatileOffset:16 volatileStorageSize:64 volatileStorageOffset:0>
34+
35+
// OGCG-LAYOUT: BitFields:[
36+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:0 Size:3 IsSigned:1 StorageSize:32 StorageOffset:0 VolatileOffset:0 VolatileStorageSize:16 VolatileStorageOffset:0>
37+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:3 Size:13 IsSigned:1 StorageSize:32 StorageOffset:0 VolatileOffset:3 VolatileStorageSize:32 VolatileStorageOffset:0>
38+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:16 Size:5 IsSigned:1 StorageSize:32 StorageOffset:0 VolatileOffset:16 VolatileStorageSize:64 VolatileStorageOffset:0>
39+
40+
typedef struct{
41+
volatile unsigned int a : 3;
42+
unsigned int : 0; // zero-length bit-field force next field to aligned int boundary
43+
volatile unsigned int b : 5;
44+
} st3;
45+
46+
// CIR-LAYOUT: BitFields:[
47+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:a offset:0 size:3 isSigned:0 storageSize:8 storageOffset:0 volatileOffset:0 volatileStorageSize:32 volatileStorageOffset:0>
48+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:b offset:0 size:5 isSigned:0 storageSize:8 storageOffset:4 volatileOffset:0 volatileStorageSize:0 volatileStorageOffset:0>
49+
50+
// OGCG-LAYOUT: BitFields:[
51+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:0 Size:3 IsSigned:0 StorageSize:8 StorageOffset:0 VolatileOffset:0 VolatileStorageSize:32 VolatileStorageOffset:0>
52+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:0 Size:5 IsSigned:0 StorageSize:8 StorageOffset:4 VolatileOffset:0 VolatileStorageSize:0 VolatileStorageOffset:0>
53+
54+
typedef struct{
55+
volatile unsigned int a : 3;
56+
unsigned int z: 2;
57+
volatile unsigned int b : 5;
58+
} st4;
59+
60+
// CIR-LAYOUT: BitFields:[
61+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:a offset:0 size:3 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:0 volatileStorageSize:32 volatileStorageOffset:0>
62+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:z offset:3 size:2 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:3 volatileStorageSize:32 volatileStorageOffset:0>
63+
// CIR-LAYOUT-NEXT: <CIRBitFieldInfo name:b offset:5 size:5 isSigned:0 storageSize:16 storageOffset:0 volatileOffset:5 volatileStorageSize:32 volatileStorageOffset:0>
64+
65+
// OGCG-LAYOUT: BitFields:[
66+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:0 Size:3 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:0 VolatileStorageSize:32 VolatileStorageOffset:0>
67+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:3 Size:2 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:3 VolatileStorageSize:32 VolatileStorageOffset:0>
68+
// OGCG-LAYOUT-NEXT: <CGBitFieldInfo Offset:5 Size:5 IsSigned:0 StorageSize:16 StorageOffset:0 VolatileOffset:5 VolatileStorageSize:32 VolatileStorageOffset:0>
69+
70+
st1 s1;
71+
st2 s2;
72+
st3 s3;
73+
st4 s4;

0 commit comments

Comments
 (0)