Skip to content

Commit cdca0aa

Browse files
[CIR][CodeGen] Fix packed structures (#839)
Consider the following code snippet `test.c`: ``` #pragma pack(2) typedef struct { char b; int c; } D; typedef struct { D e; int f; } E; void f1() { E a = {}; } ``` When emitting the CIR using `bin/clang test.c -Xclang -fclangir -Xclang -emit-cir -S -o -` the current implementation gets: ``` NYI UNREACHABLE executed at ~/clangir/clang/lib/CIR/CodeGen/CIRGenExprConst.cpp:338! ``` This is only one of the many tests where this happens. Comparing the implementations of `CIR/CodeGen/CIRRecordLayoutBuilder.cpp` and clang's codegen `lib/CodeGen/CGRecordLayoutBuilder.cpp`, there is some padding missing for packed structures, and some alignments that need to be corrected. This PR also updates 2 existing tests. In the first test, `structural-binding.cpp`, I updated some `cir.get_member` indexes. In the second test, `packed-structs.c`, I updated the `cir` layout for the structure, and added more tests. I have compared the changes I made in the tests to the original clang codegen and everything seems fine.
1 parent cef3459 commit cdca0aa

File tree

4 files changed

+145
-17
lines changed

4 files changed

+145
-17
lines changed

clang/lib/CIR/CodeGen/CIRRecordLayoutBuilder.cpp

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ struct CIRRecordLowering final {
6969

7070
/// Determines if we need a packed llvm struct.
7171
void determinePacked(bool NVBaseType);
72+
/// Inserts padding everywhere it's needed.
73+
void insertPadding();
7274

7375
void computeVolatileBitfields();
7476
void accumulateBases();
@@ -294,6 +296,7 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
294296

295297
members.push_back(StorageInfo(Size, getUIntNType(8)));
296298
determinePacked(nonVirtualBaseType);
299+
insertPadding();
297300
members.pop_back();
298301

299302
fillOutputFields();
@@ -657,6 +660,33 @@ void CIRRecordLowering::determinePacked(bool NVBaseType) {
657660
members.back().data = getUIntNType(astContext.toBits(Alignment));
658661
}
659662

663+
void CIRRecordLowering::insertPadding() {
664+
std::vector<std::pair<CharUnits, CharUnits>> Padding;
665+
CharUnits Size = CharUnits::Zero();
666+
for (std::vector<MemberInfo>::const_iterator Member = members.begin(),
667+
MemberEnd = members.end();
668+
Member != MemberEnd; ++Member) {
669+
if (!Member->data)
670+
continue;
671+
CharUnits Offset = Member->offset;
672+
assert(Offset >= Size);
673+
// Insert padding if we need to.
674+
if (Offset !=
675+
Size.alignTo(isPacked ? CharUnits::One() : getAlignment(Member->data)))
676+
Padding.push_back(std::make_pair(Size, Offset - Size));
677+
Size = Offset + getSize(Member->data);
678+
}
679+
if (Padding.empty())
680+
return;
681+
// Add the padding to the Members list and sort it.
682+
for (std::vector<std::pair<CharUnits, CharUnits>>::const_iterator
683+
Pad = Padding.begin(),
684+
PadEnd = Padding.end();
685+
Pad != PadEnd; ++Pad)
686+
members.push_back(StorageInfo(Pad->first, getByteArrayType(Pad->second)));
687+
llvm::stable_sort(members);
688+
}
689+
660690
std::unique_ptr<CIRGenRecordLayout>
661691
CIRGenTypes::computeRecordLayout(const RecordDecl *D,
662692
mlir::cir::StructType *Ty) {
@@ -674,9 +704,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *D,
674704
CIRRecordLowering baseBuilder(*this, D, /*Packed=*/builder.isPacked);
675705
baseBuilder.lower(/*NonVirtualBaseType=*/true);
676706
auto baseIdentifier = getRecordTypeName(D, ".base");
677-
BaseTy =
678-
Builder.getCompleteStructTy(baseBuilder.fieldTypes, baseIdentifier,
679-
/*packed=*/false, D);
707+
BaseTy = Builder.getCompleteStructTy(
708+
baseBuilder.fieldTypes, baseIdentifier, baseBuilder.isPacked, D);
680709
// TODO(cir): add something like addRecordTypeName
681710

682711
// BaseTy and Ty must agree on their packedness for getCIRFieldNo to work

clang/lib/CIR/Dialect/IR/CIRDataLayout.cpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ StructLayout::StructLayout(mlir::cir::StructType ST, const CIRDataLayout &DL)
2222

2323
assert(!::cir::MissingFeatures::recordDeclIsPacked() &&
2424
"Cannot identify packed structs");
25-
const llvm::Align TyAlign = DL.getABITypeAlign(Ty);
25+
const llvm::Align TyAlign =
26+
ST.getPacked() ? llvm::Align(1) : DL.getABITypeAlign(Ty);
2627

2728
// Add padding if necessary to align the data element properly.
2829
// Currently the only structure with scalable size will be the homogeneous
@@ -170,6 +171,10 @@ llvm::Align CIRDataLayout::getAlignment(mlir::Type Ty, bool abiOrPref) const {
170171
if (::cir::MissingFeatures::recordDeclIsPacked() && abiOrPref)
171172
llvm_unreachable("NYI");
172173

174+
auto stTy = llvm::dyn_cast<mlir::cir::StructType>(Ty);
175+
if (stTy && stTy.getPacked() && abiOrPref)
176+
return llvm::Align(1);
177+
173178
// Get the layout annotation... which is lazily created on demand.
174179
const StructLayout *Layout =
175180
getStructLayout(llvm::cast<mlir::cir::StructType>(Ty));

clang/test/CIR/CodeGen/packed-structs.c

Lines changed: 102 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
2-
// RUN: FileCheck --input-file=%t.cir %s
2+
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t.ll
4+
// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
35

46
#pragma pack(1)
57

@@ -20,18 +22,110 @@ typedef struct {
2022
} __attribute__((aligned(2))) C;
2123

2224

23-
// CHECK: !ty_A = !cir.struct<struct "A" packed {!cir.int<s, 32>, !cir.int<s, 8>}>
24-
// CHECK: !ty_C = !cir.struct<struct "C" packed {!cir.int<s, 32>, !cir.int<s, 8>}>
25-
// CHECK: !ty_B = !cir.struct<struct "B" packed {!cir.int<s, 32>, !cir.int<s, 8>, !cir.array<!cir.struct<struct "A" packed {!cir.int<s, 32>, !cir.int<s, 8>}> x 6>}>
25+
// CIR: !ty_A = !cir.struct<struct "A" packed {!cir.int<s, 32>, !cir.int<s, 8>}>
26+
// CIR: !ty_C = !cir.struct<struct "C" packed {!cir.int<s, 32>, !cir.int<s, 8>, !cir.int<u, 8>}>
27+
// CIR: !ty_D = !cir.struct<struct "D" packed {!cir.int<s, 8>, !cir.int<u, 8>, !cir.int<s, 32>}
28+
// CIR: !ty_F = !cir.struct<struct "F" packed {!cir.int<s, 64>, !cir.int<s, 8>}
29+
// CIR: !ty_E = !cir.struct<struct "E" packed {!cir.struct<struct "D" packed {!cir.int<s, 8>, !cir.int<u, 8>, !cir.int<s, 32>}
30+
// CIR: !ty_G = !cir.struct<struct "G" {!cir.struct<struct "F" packed {!cir.int<s, 64>, !cir.int<s, 8>}
31+
// CIR: !ty_H = !cir.struct<struct "H" {!cir.int<s, 32>, !cir.struct<union "anon.{{.*}}" {!cir.int<s, 8>, !cir.int<s, 32>}
32+
// CIR: !ty_B = !cir.struct<struct "B" packed {!cir.int<s, 32>, !cir.int<s, 8>, !cir.array<!cir.struct<struct "A" packed {!cir.int<s, 32>, !cir.int<s, 8>}> x 6>}>
33+
// CIR: !ty_I = !cir.struct<struct "I" packed {!cir.int<s, 8>, !cir.struct<struct "H" {!cir.int<s, 32>, !cir.struct<union "anon.{{.*}}" {!cir.int<s, 8>, !cir.int<s, 32>}
34+
// CIR: !ty_J = !cir.struct<struct "J" packed {!cir.int<s, 8>, !cir.int<s, 8>, !cir.int<s, 8>, !cir.int<s, 8>, !cir.struct<struct "I" packed {!cir.int<s, 8>, !cir.struct<struct "H" {!cir.int<s, 32>, !cir.struct<union "anon.{{.*}}" {!cir.int<s, 8>, !cir.int<s, 32>}
2635

27-
// CHECK: cir.func {{.*@foo()}}
28-
// CHECK: %0 = cir.alloca !ty_A, !cir.ptr<!ty_A>, ["a"] {alignment = 1 : i64}
29-
// CHECK: %1 = cir.alloca !ty_B, !cir.ptr<!ty_B>, ["b"] {alignment = 1 : i64}
30-
// CHECK: %2 = cir.alloca !ty_C, !cir.ptr<!ty_C>, ["c"] {alignment = 2 : i64}
36+
// LLVM: %struct.A = type <{ i32, i8 }>
37+
// LLVM: %struct.B = type <{ i32, i8, [6 x %struct.A] }>
38+
// LLVM: %struct.C = type <{ i32, i8, i8 }>
39+
// LLVM: %struct.E = type <{ %struct.D, i32 }>
40+
// LLVM: %struct.D = type <{ i8, i8, i32 }>
41+
// LLVM: %struct.G = type { %struct.F, i8 }
42+
// LLVM: %struct.F = type <{ i64, i8 }>
43+
// LLVM: %struct.J = type <{ i8, i8, i8, i8, %struct.I, i32 }>
44+
// LLVM: %struct.I = type <{ i8, %struct.H }>
45+
// LLVM: %struct.H = type { i32, %union.anon.{{.*}} }
46+
47+
// CIR: cir.func {{.*@foo()}}
48+
// CIR: {{.*}} = cir.alloca !ty_A, !cir.ptr<!ty_A>, ["a"] {alignment = 1 : i64}
49+
// CIR: {{.*}} = cir.alloca !ty_B, !cir.ptr<!ty_B>, ["b"] {alignment = 1 : i64}
50+
// CIR: {{.*}} = cir.alloca !ty_C, !cir.ptr<!ty_C>, ["c"] {alignment = 2 : i64}
51+
52+
// LLVM: {{.*}} = alloca %struct.A, i64 1, align 1
53+
// LLVM: {{.*}} = alloca %struct.B, i64 1, align 1
54+
// LLVM: {{.*}} = alloca %struct.C, i64 1, align 2
3155
void foo() {
3256
A a;
3357
B b;
3458
C c;
3559
}
3660

61+
#pragma pack(2)
62+
63+
typedef struct {
64+
char b;
65+
int c;
66+
} D;
67+
68+
typedef struct {
69+
D e;
70+
int f;
71+
} E;
72+
73+
// CIR: cir.func {{.*@f1()}}
74+
// CIR: {{.*}} = cir.alloca !ty_E, !cir.ptr<!ty_E>, ["a"] {alignment = 2 : i64}
75+
76+
// LLVM: {{.*}} = alloca %struct.E, i64 1, align 2
77+
void f1() {
78+
E a = {};
79+
}
80+
81+
#pragma pack(1)
82+
83+
typedef struct {
84+
long b;
85+
char c;
86+
} F;
87+
88+
typedef struct {
89+
F e;
90+
char f;
91+
} G;
92+
93+
// CIR: cir.func {{.*@f2()}}
94+
// CIR: {{.*}} = cir.alloca !ty_G, !cir.ptr<!ty_G>, ["a"] {alignment = 1 : i64}
95+
96+
// LLVM: {{.*}} = alloca %struct.G, i64 1, align 1
97+
void f2() {
98+
G a = {};
99+
}
100+
101+
#pragma pack(1)
102+
103+
typedef struct {
104+
int d0;
105+
union {
106+
char null;
107+
int val;
108+
} value;
109+
} H;
110+
111+
typedef struct {
112+
char t;
113+
H d;
114+
} I;
115+
116+
typedef struct {
117+
char a0;
118+
char a1;
119+
char a2;
120+
char a3;
121+
I c;
122+
int a;
123+
} J;
124+
125+
// CIR: cir.func {{.*@f3()}}
126+
// CIR: {{.*}} = cir.alloca !ty_J, !cir.ptr<!ty_J>, ["a"] {alignment = 1 : i64}
37127

128+
// LLVM: {{.*}} = alloca %struct.J, i64 1, align 1
129+
void f3() {
130+
J a = {0};
131+
}

clang/test/CIR/CodeGen/structural-binding.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,19 +52,19 @@ void f(A &a) {
5252
// CIR: %[[a:.*]] = cir.load %1 : !cir.ptr<!cir.ptr<!ty_A>>, !cir.ptr<!ty_A>
5353
// CIR: {{.*}} = cir.get_member %[[a]][0] {name = "a"} : !cir.ptr<!ty_A> -> !cir.ptr<!ty_B>
5454
// CIR: %[[a:.*]] = cir.load %1 : !cir.ptr<!cir.ptr<!ty_A>>, !cir.ptr<!ty_A>
55-
// CIR: {{.*}} = cir.get_member %[[a]][1] {name = "b"} : !cir.ptr<!ty_A> -> !cir.ptr<!s32i>
55+
// CIR: {{.*}} = cir.get_member %[[a]][2] {name = "b"} : !cir.ptr<!ty_A> -> !cir.ptr<!s32i>
5656
// CIR: %[[a:.*]] = cir.load %1 : !cir.ptr<!cir.ptr<!ty_A>>, !cir.ptr<!ty_A>
57-
// CIR: {{.*}} = cir.get_member %[[a]][2] {name = "c"} : !cir.ptr<!ty_A> -> !cir.ptr<!s8i>
57+
// CIR: {{.*}} = cir.get_member %[[a]][3] {name = "c"} : !cir.ptr<!ty_A> -> !cir.ptr<!s8i>
5858
// LLVM: {{.*}} = getelementptr %struct.A, ptr {{.*}}, i32 0, i32 0
59-
// LLVM: {{.*}} = getelementptr %struct.A, ptr {{.*}}, i32 0, i32 1
6059
// LLVM: {{.*}} = getelementptr %struct.A, ptr {{.*}}, i32 0, i32 2
60+
// LLVM: {{.*}} = getelementptr %struct.A, ptr {{.*}}, i32 0, i32 3
6161

6262
auto [x2, y2, z2] = a;
6363
(x2, y2, z2);
6464
// CIR: cir.call @_ZN1AC1ERKS_(%2, {{.*}}) : (!cir.ptr<!ty_A>, !cir.ptr<!ty_A>) -> ()
6565
// CIR: {{.*}} = cir.get_member %2[0] {name = "a"} : !cir.ptr<!ty_A> -> !cir.ptr<!ty_B>
66-
// CIR: {{.*}} = cir.get_member %2[1] {name = "b"} : !cir.ptr<!ty_A> -> !cir.ptr<!s32i>
67-
// CIR: {{.*}} = cir.get_member %2[2] {name = "c"} : !cir.ptr<!ty_A> -> !cir.ptr<!s8i>
66+
// CIR: {{.*}} = cir.get_member %2[2] {name = "b"} : !cir.ptr<!ty_A> -> !cir.ptr<!s32i>
67+
// CIR: {{.*}} = cir.get_member %2[3] {name = "c"} : !cir.ptr<!ty_A> -> !cir.ptr<!s8i>
6868

6969
// for the rest, just expect the codegen does't crash
7070
auto &&[x3, y3, z3] = a;

0 commit comments

Comments
 (0)