Skip to content

Commit 0d4a054

Browse files
bruteforceboysmeenai
authored andcommitted
[CIR][CodeGen] Fix packed structures (llvm#839)
Consider the following code snippet `test.c`: ``` 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 04de121 commit 0d4a054

File tree

4 files changed

+146
-17
lines changed

4 files changed

+146
-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
@@ -23,7 +23,8 @@ StructLayout::StructLayout(mlir::cir::StructType ST, const CIRDataLayout &DL)
2323

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

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

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

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

Lines changed: 103 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
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
5+
// XFAIL: *
36

47
#pragma pack(1)
58

@@ -20,18 +23,110 @@ typedef struct {
2023
} __attribute__((aligned(2))) C;
2124

2225

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

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}
37+
// LLVM: %struct.A = type <{ i32, i8 }>
38+
// LLVM: %struct.B = type <{ i32, i8, [6 x %struct.A] }>
39+
// LLVM: %struct.C = type <{ i32, i8, i8 }>
40+
// LLVM: %struct.E = type <{ %struct.D, i32 }>
41+
// LLVM: %struct.D = type <{ i8, i8, i32 }>
42+
// LLVM: %struct.G = type { %struct.F, i8 }
43+
// LLVM: %struct.F = type <{ i64, i8 }>
44+
// LLVM: %struct.J = type <{ i8, i8, i8, i8, %struct.I, i32 }>
45+
// LLVM: %struct.I = type <{ i8, %struct.H }>
46+
// LLVM: %struct.H = type { i32, %union.anon.{{.*}} }
47+
48+
// CIR: cir.func {{.*@foo()}}
49+
// CIR: {{.*}} = cir.alloca !ty_A, !cir.ptr<!ty_A>, ["a"] {alignment = 1 : i64}
50+
// CIR: {{.*}} = cir.alloca !ty_B, !cir.ptr<!ty_B>, ["b"] {alignment = 1 : i64}
51+
// CIR: {{.*}} = cir.alloca !ty_C, !cir.ptr<!ty_C>, ["c"] {alignment = 2 : i64}
52+
53+
// LLVM: {{.*}} = alloca %struct.A, i64 1, align 1
54+
// LLVM: {{.*}} = alloca %struct.B, i64 1, align 1
55+
// LLVM: {{.*}} = alloca %struct.C, i64 1, align 2
3156
void foo() {
3257
A a;
3358
B b;
3459
C c;
3560
}
3661

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

117+
typedef struct {
118+
char a0;
119+
char a1;
120+
char a2;
121+
char a3;
122+
I c;
123+
int a;
124+
} J;
125+
126+
// CIR: cir.func {{.*@f3()}}
127+
// CIR: {{.*}} = cir.alloca !ty_J, !cir.ptr<!ty_J>, ["a"] {alignment = 1 : i64}
128+
129+
// LLVM: {{.*}} = alloca %struct.J, i64 1, align 1
130+
void f3() {
131+
J a = {0};
132+
}

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)