Skip to content

Commit a5db2c2

Browse files
[CIR] Add support for -ffine-grained-bitfield-accesses (#150687)
This PR adds support for `-ffine-grained-bitfield-accesses`. I reused the tests from classic CodeGen, available here: [https://github.com/llvm/llvm-project/blob/c2c881fcc85e0c2d7a050b0199d4dadf8f556b9e/clang/test/CodeGenCXX/finegrain-bitfield-access.cpp](https://github.com/llvm/llvm-project/blob/c2c881fcc85e0c2d7a050b0199d4dadf8f556b9e/clang/test/CodeGenCXX/finegrain-bitfield-access.cpp) We produce almost exactly the same codegen, except when returning a variable: we emit an extra variable to hold the return value, whereas classic CodeGen does not. Also, the GEP instructions use slightly different syntax compared to classic CodeGen.
1 parent 860b1e6 commit a5db2c2

File tree

3 files changed

+272
-4
lines changed

3 files changed

+272
-4
lines changed

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,6 @@ struct MissingFeatures {
224224
static bool moduleNameHash() { return false; }
225225
static bool msabi() { return false; }
226226
static bool needsGlobalCtorDtor() { return false; }
227-
static bool nonFineGrainedBitfields() { return false; }
228227
static bool objCBlocks() { return false; }
229228
static bool objCGC() { return false; }
230229
static bool objCLifetime() { return false; }

clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -438,9 +438,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
438438
} else if (cirGenTypes.getCGModule()
439439
.getCodeGenOpts()
440440
.FineGrainedBitfieldAccesses) {
441-
assert(!cir::MissingFeatures::nonFineGrainedBitfields());
442-
cirGenTypes.getCGModule().errorNYI(field->getSourceRange(),
443-
"NYI FineGrainedBitfield");
441+
installBest = true;
444442
} else {
445443
// Otherwise, we're not installing. Update the bit size
446444
// of the current span to go all the way to limitOffset, which is
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -ffine-grained-bitfield-accesses %s -o %t.cir
2+
// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
3+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm -ffine-grained-bitfield-accesses %s -o %t-cir.ll
4+
// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
5+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm -ffine-grained-bitfield-accesses %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
7+
8+
struct S1 {
9+
unsigned f1:2;
10+
unsigned f2:6;
11+
unsigned f3:8;
12+
unsigned f4:4;
13+
unsigned f5:8;
14+
};
15+
16+
// CIR-DAG: !rec_S1 = !cir.record<struct "S1" {!u8i, !u8i, !u16i}>
17+
// LLVM-DAG: %struct.S1 = type { i8, i8, i16 }
18+
// OGCG-DAG: %struct.S1 = type { i8, i8, i16 }
19+
20+
struct S2 {
21+
unsigned long f1:16;
22+
unsigned long f2:16;
23+
unsigned long f3:6;
24+
};
25+
26+
// CIR-DAG: !rec_S2 = !cir.record<struct "S2" padded {!u16i, !u16i, !u8i, !cir.array<!u8i x 3>}>
27+
// LLVM-DAG: %struct.S2 = type { i16, i16, i8, [3 x i8] }
28+
// OGCG-DAG: %struct.S2 = type { i16, i16, i8, [3 x i8] }
29+
30+
struct S3 {
31+
unsigned long f1:14;
32+
unsigned long f2:18;
33+
unsigned long f3:32;
34+
};
35+
36+
// CIR-DAG: !rec_S3 = !cir.record<struct "S3" {!u32i, !u32i}>
37+
// LLVM-DAG: %struct.S3 = type { i32, i32 }
38+
// OGCG-DAG: %struct.S3 = type { i32, i32 }
39+
40+
S1 a1;
41+
S2 a2;
42+
S3 a3;
43+
44+
unsigned read8_1() {
45+
return a1.f3;
46+
}
47+
48+
// CIR-LABEL: @_Z7read8_1v
49+
// CIR: [[MEMBER:%.*]] = cir.get_member %1[1] {name = "f3"} : !cir.ptr<!rec_S1> -> !cir.ptr<!u8i>
50+
// CIR: [[BITFI:%.*]] = cir.get_bitfield align(1) (#bfi_f3, [[MEMBER]] : !cir.ptr<!u8i>) -> !u32i
51+
// CIR: cir.store [[BITFI]], {{.*}} : !u32i, !cir.ptr<!u32i>
52+
// CIR: [[RET:%.*]] = cir.load {{.*}} : !cir.ptr<!u32i>, !u32i
53+
// CIR: cir.return [[RET]] : !u32i
54+
55+
// LLVM-LABEL: @_Z7read8_1v
56+
// LLVM: [[MEMBER:%.*]] = load i8, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 1), align 1
57+
// LLVM: [[BFCAST:%.*]] = zext i8 [[MEMBER]] to i32
58+
// LLVM: store i32 [[BFCAST]], ptr {{.*}}, align 4
59+
// LLVM: [[RET:%.*]] = load i32, ptr {{.*}}, align 4
60+
// LLVM: ret i32 [[RET]]
61+
62+
// OGCG-LABEL: @_Z7read8_1v
63+
// OGCG: [[BFLOAD:%.*]] = load i8, ptr getelementptr inbounds nuw (%struct.S1, ptr {{.*}}, i32 0, i32 1), align 1
64+
// OGCG-NEXT: [[BFCAST:%.*]] = zext i8 [[BFLOAD]] to i32
65+
// OGCG-NEXT: ret i32 [[BFCAST]]
66+
67+
void write8_1() {
68+
a1.f3 = 3;
69+
}
70+
71+
// CIR-LABEL: @_Z8write8_1v
72+
// CIR: [[CONST3:%.*]] = cir.const #cir.int<3> : !s32i
73+
// CIR: [[INT3:%.*]] = cir.cast(integral, [[CONST3]] : !s32i), !u32i
74+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[1] {name = "f3"} : !cir.ptr<!rec_S1> -> !cir.ptr<!u8i>
75+
// CIR: cir.set_bitfield align(1) (#bfi_f3, [[MEMBER]] : !cir.ptr<!u8i>, [[INT3]] : !u32i) -> !u32i
76+
77+
// LLVM-LABEL: @_Z8write8_1v
78+
// LLVM: store i8 3, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 1), align 1
79+
// LLVM: ret void
80+
81+
// OGCG-LABEL: @_Z8write8_1v
82+
// OGCG: store i8 3, ptr getelementptr inbounds nuw (%struct.S1, ptr {{.*}}, i32 0, i32 1), align 1
83+
// OGCG-NEXT: ret void
84+
85+
unsigned read8_2() {
86+
87+
return a1.f5;
88+
}
89+
90+
// CIR-LABEL: @_Z7read8_2v
91+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[2] {name = "f5"} : !cir.ptr<!rec_S1> -> !cir.ptr<!u16i>
92+
// CIR: [[BITFI:%.*]] = cir.get_bitfield align(2) (#bfi_f5, [[MEMBER]] : !cir.ptr<!u16i>) -> !u32i
93+
// CIR: cir.store [[BITFI]], {{.*}} : !u32i, !cir.ptr<!u32i>
94+
// CIR: [[RET:%.*]] = cir.load {{.*}} : !cir.ptr<!u32i>, !u32i
95+
// CIR: cir.return [[RET]] : !u32i
96+
97+
// LLVM-LABEL: @_Z7read8_2v
98+
// LLVM: [[BFLOAD:%.*]] = load i16, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 2), align 2
99+
// LLVM: [[BFLSHR:%.*]] = lshr i16 [[BFLOAD]], 4
100+
// LLVM: [[BFCLEAR:%.*]] = and i16 [[BFLSHR]], 255
101+
// LLVM: [[BFCAST:%.*]] = zext i16 [[BFCLEAR]] to i32
102+
// LLVM: store i32 [[BFCAST]], ptr {{.*}}, align 4
103+
// LLVM: [[RET:%.*]] = load i32, ptr {{.*}}, align 4
104+
// LLVM: ret i32 [[RET]]
105+
106+
// OGCG-LABEL: @_Z7read8_2v
107+
// OGCG: [[BFLOAD:%.*]] = load i16, ptr getelementptr inbounds nuw (%struct.S1, ptr {{.*}}, i32 0, i32 2), align 2
108+
// OGCG-NEXT: [[BFLSHR:%.*]] = lshr i16 [[BFLOAD]], 4
109+
// OGCG-NEXT: [[BFCLEAR:%.*]] = and i16 [[BFLSHR]], 255
110+
// OGCG-NEXT: [[BFCAST:%.*]] = zext i16 [[BFCLEAR]] to i32
111+
// OGCG-NEXT: ret i32 [[BFCAST]]
112+
113+
void write8_2() {
114+
a1.f5 = 3;
115+
}
116+
117+
// CIR-LABEL: @_Z8write8_2v
118+
// CIR: [[CONST3:%.*]] = cir.const #cir.int<3> : !s32i
119+
// CIR: [[INT3:%.*]] = cir.cast(integral, [[CONST3]] : !s32i), !u32i
120+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[2] {name = "f5"} : !cir.ptr<!rec_S1> -> !cir.ptr<!u16i>
121+
// CIR: cir.set_bitfield align(2) (#bfi_f5, %3 : !cir.ptr<!u16i>, {{.*}} : !u32i) -> !u32i
122+
123+
// LLVM-LABEL: @_Z8write8_2v
124+
// LLVM: [[BFLOAD:%.*]] = load i16, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 2), align 2
125+
// LLVM: [[BFCLEAR:%.*]] = and i16 [[BFLOAD]], -4081
126+
// LLVM: [[BFSET:%.*]] = or i16 [[BFCLEAR]], 48
127+
// LLVM: store i16 [[BFSET]], ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 2), align 2
128+
// LLVM: ret void
129+
130+
// OGCG-LABEL: @_Z8write8_2v
131+
// OGCG: [[BFLOAD:%.*]] = load i16, ptr getelementptr inbounds nuw (%struct.S1, ptr {{.*}}, i32 0, i32 2), align 2
132+
// OGCG-NEXT: [[BFCLEAR:%.*]] = and i16 [[BFLOAD]], -4081
133+
// OGCG-NEXT: [[BFSET:%.*]] = or i16 [[BFCLEAR]], 48
134+
// OGCG-NEXT: store i16 [[BFSET]], ptr getelementptr inbounds nuw (%struct.S1, ptr {{.*}}, i32 0, i32 2), align 2
135+
// OGCG-NEXT: ret void
136+
137+
unsigned read16_1() {
138+
return a2.f1;
139+
}
140+
141+
// CIR-LABEL: @_Z8read16_1v
142+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[0] {name = "f1"} : !cir.ptr<!rec_S2> -> !cir.ptr<!u16i>
143+
// CIR: [[BITFI:%.*]] = cir.get_bitfield align(8) (#bfi_f1, [[MEMBER]] : !cir.ptr<!u16i>) -> !u64i
144+
// CIR: [[BFCAST:%.*]] = cir.cast(integral, [[BITFI]] : !u64i), !u32i
145+
// CIR: cir.store [[BFCAST]], {{.*}} : !u32i, !cir.ptr<!u32i>
146+
// CIR: [[RET:%.*]] = cir.load {{.*}} : !cir.ptr<!u32i>, !u32i
147+
// CIR: cir.return [[RET]] : !u32i
148+
149+
// LLVM-LABEL: @_Z8read16_1v
150+
// LLVM: [[BFLOAD:%.*]] = load i16, ptr {{.*}}, align 8
151+
// LLVM: [[BFCAST:%.*]] = zext i16 [[BFLOAD]] to i64
152+
// LLVM: [[BF:%.*]] = trunc i64 [[BFCAST]] to i32
153+
// LLVM: store i32 [[BF]], ptr {{.*}}, align 4
154+
// LLVM: [[RET:%.*]] = load i32, ptr {{.*}}, align 4
155+
// LLVM: ret i32 [[RET]]
156+
157+
// OGCG-LABEL: @_Z8read16_1v
158+
// OGCG: [[BFLOAD:%.*]] = load i16, ptr {{.*}}, align 8
159+
// OGCG-NEXT: [[BFCAST:%.*]] = zext i16 [[BFLOAD]] to i64
160+
// OGCG-NEXT: [[RET:%.*]] = trunc i64 [[BFCAST]] to i32
161+
// OGCG-NEXT: ret i32 [[RET]]
162+
163+
unsigned read16_2() {
164+
return a2.f2;
165+
}
166+
167+
// CIR-LABEL: @_Z8read16_2v
168+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[1] {name = "f2"} : !cir.ptr<!rec_S2> -> !cir.ptr<!u16i>
169+
// CIR: [[BITFI:%.*]] = cir.get_bitfield align(2) (#bfi_f2, [[MEMBER]] : !cir.ptr<!u16i>) -> !u64i
170+
// CIR: [[BFCAST:%.*]] = cir.cast(integral, [[BITFI]] : !u64i), !u32i
171+
// CIR: cir.store [[BFCAST]], {{.*}} : !u32i, !cir.ptr<!u32i>
172+
// CIR: [[RET:%.*]] = cir.load {{.*}} : !cir.ptr<!u32i>, !u32i
173+
// CIR: cir.return [[RET]] : !u32i
174+
175+
// LLVM-LABEL: @_Z8read16_2v
176+
// LLVM: [[BFLOAD:%.*]] = load i16, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 2), align 2
177+
// LLVM: [[BFCAST:%.*]] = zext i16 [[BFLOAD]] to i64
178+
// LLVM: [[BF:%.*]] = trunc i64 [[BFCAST]] to i32
179+
// LLVM: store i32 [[BF]], ptr {{.*}}, align 4
180+
// LLVM: [[RET:%.*]] = load i32, ptr {{.*}}, align 4
181+
// LLVM: ret i32 [[RET]]
182+
183+
// OGCG-LABEL: @_Z8read16_2v
184+
// OGCG: [[BFLOAD:%.*]] = load i16, ptr getelementptr inbounds nuw (%struct.S2, ptr {{.*}}, i32 0, i32 1), align 2
185+
// OGCG-NEXT: [[BFCAST:%.*]] = zext i16 [[BFLOAD]] to i64
186+
// OGCG-NEXT: [[RET:%.*]] = trunc i64 [[BFCAST]] to i32
187+
// OGCG-NEXT: ret i32 [[RET]]
188+
189+
void write16_1() {
190+
a2.f1 = 5;
191+
}
192+
193+
// CIR-LABEL: @_Z9write16_1v
194+
// CIR: [[CONST5:%.*]] = cir.const #cir.int<5> : !s32i
195+
// CIR: [[INT5:%.*]] = cir.cast(integral, [[CONST5]] : !s32i), !u64i
196+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[0] {name = "f1"} : !cir.ptr<!rec_S2> -> !cir.ptr<!u16i>
197+
// CIR: cir.set_bitfield align(8) (#bfi_f1, [[MEMBER]] : !cir.ptr<!u16i>, [[INT5]] : !u64i) -> !u64i
198+
// CIR: cir.return
199+
200+
// LLVM-LABEL: @_Z9write16_1v
201+
// LLVM: store i16 5, ptr {{.*}}, align 8
202+
// LLVM: ret void
203+
204+
// OGCG-LABEL: @_Z9write16_1v
205+
// OGCG: store i16 5, ptr {{.*}}, align 8
206+
// OGCG-NEXT: ret void
207+
208+
void write16_2() {
209+
210+
a2.f2 = 5;
211+
}
212+
213+
// CIR-LABEL: @_Z9write16_2v
214+
// CIR: [[CONST5:%.*]] = cir.const #cir.int<5> : !s32i
215+
// CIR: [[INT5:%.*]] = cir.cast(integral, [[CONST5]] : !s32i), !u64i
216+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[1] {name = "f2"} : !cir.ptr<!rec_S2> -> !cir.ptr<!u16i>
217+
// CIR: cir.set_bitfield align(2) (#bfi_f2, [[MEMBER]] : !cir.ptr<!u16i>, {{.*}} : !u64i) -> !u64i
218+
// CIR: cir.return
219+
220+
// LLVM-LABEL: @_Z9write16_2v
221+
// LLVM: store i16 5, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 2), align 2
222+
// LLVM: ret void
223+
224+
// OGCG-LABEL: @_Z9write16_2v
225+
// OGCG: store i16 5, ptr getelementptr inbounds nuw (%struct.S2, ptr {{.*}}, i32 0, i32 1), align 2
226+
// OGCG-NEXT: ret void
227+
228+
unsigned read32_1() {
229+
230+
return a3.f3;
231+
}
232+
// CIR-LABEL: @_Z8read32_1v
233+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[1] {name = "f3"} : !cir.ptr<!rec_S3> -> !cir.ptr<!u32i>
234+
// CIR: [[BITFI:%.*]] = cir.get_bitfield align(4) (#bfi_f3_1, [[MEMBER]] : !cir.ptr<!u32i>) -> !u64i
235+
// CIR: [[BFCAST:%.*]] = cir.cast(integral, [[BITFI]] : !u64i), !u32i
236+
// CIR: cir.store [[BFCAST]], {{.*}} : !u32i, !cir.ptr<!u32i>
237+
// CIR: [[RET:%.*]] = cir.load {{.*}} : !cir.ptr<!u32i>, !u32i
238+
// CIR: cir.return [[RET]] : !u32i
239+
240+
// LLVM-LABEL: @_Z8read32_1v
241+
// LLVM: [[BFLOAD:%.*]] = load i32, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 4), align 4
242+
// LLVM: [[BFCAST:%.*]] = zext i32 [[BFLOAD]] to i64
243+
// LLVM: [[BF:%.*]] = trunc i64 [[BFCAST]] to i32
244+
// LLVM: store i32 [[BF]], ptr {{.*}}, align 4
245+
// LLVM: [[RET:%.*]] = load i32, ptr {{.*}}, align 4
246+
// LLVM: ret i32 [[RET]]
247+
248+
// OGCG-LABEL: @_Z8read32_1v
249+
// OGCG: [[BFLOAD:%.*]] = load i32, ptr getelementptr inbounds nuw (%struct.S3, ptr {{.*}}, i32 0, i32 1), align 4
250+
// OGCG-NEXT: [[BFCAST:%.*]] = zext i32 %bf.load to i64
251+
// OGCG-NEXT: [[RET:%.*]] = trunc i64 %bf.cast to i32
252+
// OGCG-NEXT: ret i32 [[RET]]
253+
254+
void write32_1() {
255+
a3.f3 = 5;
256+
}
257+
258+
// CIR-LABEL: @_Z9write32_1v
259+
// CIR: [[CONST5:%.*]] = cir.const #cir.int<5> : !s32i
260+
// CIR: [[INT5:%.*]] = cir.cast(integral, [[CONST5]] : !s32i), !u64i
261+
// CIR: [[MEMBER:%.*]] = cir.get_member {{.*}}[1] {name = "f3"} : !cir.ptr<!rec_S3> -> !cir.ptr<!u32i>
262+
// CIR: cir.set_bitfield align(4) (#bfi_f3_1, [[MEMBER]] : !cir.ptr<!u32i>, [[INT5]] : !u64i) -> !u64i
263+
// CIR: cir.return
264+
265+
// LLVM-LABEL: @_Z9write32_1v
266+
// LLVM: store i32 5, ptr getelementptr inbounds nuw (i8, ptr {{.*}}, i64 4), align 4
267+
// LLVM: ret void
268+
269+
// OGCG-LABEL: @_Z9write32_1v
270+
// OGCG: store i32 5, ptr getelementptr inbounds nuw (%struct.S3, ptr {{.*}}, i32 0, i32 1), align 4
271+
// OGCG-NEXT: ret void

0 commit comments

Comments
 (0)