Skip to content

Commit 71b64bc

Browse files
[CIR] Add support for discrete bit-field (#156085)
This PR adds support for the discrete bit-field layout. It is the same as this PR: llvm/clangir#1860
1 parent d7a3ab2 commit 71b64bc

File tree

4 files changed

+136
-2
lines changed

4 files changed

+136
-2
lines changed

clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,18 @@ class CIRDataLayout {
6868
return llvm::alignTo(getTypeStoreSize(ty), getABITypeAlign(ty).value());
6969
}
7070

71+
/// Returns the offset in bits between successive objects of the
72+
/// specified type, including alignment padding; always a multiple of 8.
73+
///
74+
/// If Ty is a scalable vector type, the scalable property will be set and
75+
/// the runtime size will be a positive integer multiple of the base size.
76+
///
77+
/// This is the amount that alloca reserves for this type. For example,
78+
/// returns 96 or 128 for x86_fp80, depending on alignment.
79+
llvm::TypeSize getTypeAllocSizeInBits(mlir::Type ty) const {
80+
return 8 * getTypeAllocSize(ty);
81+
}
82+
7183
llvm::TypeSize getTypeSizeInBits(mlir::Type ty) const;
7284
};
7385

clang/include/clang/CIR/MissingFeatures.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,6 @@ struct MissingFeatures {
146146
static bool cxxabiUseARMMethodPtrABI() { return false; }
147147
static bool cxxabiUseARMGuardVarABI() { return false; }
148148
static bool cxxabiAppleARM64CXXABI() { return false; }
149-
static bool isDiscreteBitFieldABI() { return false; }
150149

151150
// Address class
152151
static bool addressOffset() { return false; }

clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ struct CIRRecordLowering final {
108108
// not the primary vbase of some base class.
109109
bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query);
110110

111+
/// The Microsoft bitfield layout rule allocates discrete storage
112+
/// units of the field's formal type and only combines adjacent
113+
/// fields of the same formal type. We want to emit a layout with
114+
/// these discrete storage units instead of combining them into a
115+
/// continuous run.
116+
bool isDiscreteBitFieldABI() {
117+
return astContext.getTargetInfo().getCXXABI().isMicrosoft() ||
118+
recordDecl->isMsStruct(astContext);
119+
}
120+
111121
CharUnits bitsToCharUnits(uint64_t bitOffset) {
112122
return astContext.toCharUnitsFromBits(bitOffset);
113123
}
@@ -323,7 +333,45 @@ void CIRRecordLowering::fillOutputFields() {
323333
RecordDecl::field_iterator
324334
CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
325335
RecordDecl::field_iterator fieldEnd) {
326-
assert(!cir::MissingFeatures::isDiscreteBitFieldABI());
336+
if (isDiscreteBitFieldABI()) {
337+
// run stores the first element of the current run of bitfields. fieldEnd is
338+
// used as a special value to note that we don't have a current run. A
339+
// bitfield run is a contiguous collection of bitfields that can be stored
340+
// in the same storage block. Zero-sized bitfields and bitfields that would
341+
// cross an alignment boundary break a run and start a new one.
342+
RecordDecl::field_iterator run = fieldEnd;
343+
// tail is the offset of the first bit off the end of the current run. It's
344+
// used to determine if the ASTRecordLayout is treating these two bitfields
345+
// as contiguous. StartBitOffset is offset of the beginning of the Run.
346+
uint64_t startBitOffset, tail = 0;
347+
for (; field != fieldEnd && field->isBitField(); ++field) {
348+
// Zero-width bitfields end runs.
349+
if (field->isZeroLengthBitField()) {
350+
run = fieldEnd;
351+
continue;
352+
}
353+
uint64_t bitOffset = getFieldBitOffset(*field);
354+
mlir::Type type = cirGenTypes.convertTypeForMem(field->getType());
355+
// If we don't have a run yet, or don't live within the previous run's
356+
// allocated storage then we allocate some storage and start a new run.
357+
if (run == fieldEnd || bitOffset >= tail) {
358+
run = field;
359+
startBitOffset = bitOffset;
360+
tail = startBitOffset + dataLayout.getTypeAllocSizeInBits(type);
361+
// Add the storage member to the record. This must be added to the
362+
// record before the bitfield members so that it gets laid out before
363+
// the bitfields it contains get laid out.
364+
members.push_back(
365+
makeStorageInfo(bitsToCharUnits(startBitOffset), type));
366+
}
367+
// Bitfields get the offset of their storage but come afterward and remain
368+
// there after a stable sort.
369+
members.push_back(MemberInfo(bitsToCharUnits(startBitOffset),
370+
MemberInfo::InfoKind::Field, nullptr,
371+
*field));
372+
}
373+
return field;
374+
}
327375

328376
CharUnits regSize =
329377
bitsToCharUnits(astContext.getTargetInfo().getRegisterWidth());
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mms-bitfields -fclangir -emit-cir %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 -mms-bitfields -fclangir -emit-llvm %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 -mms-bitfields -emit-llvm %s -o %t.ll
6+
// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
7+
8+
struct s1 {
9+
int f32 : 2;
10+
long long f64 : 30;
11+
} s1;
12+
13+
// CIR-DAG: !rec_s1 = !cir.record<struct "s1" {!s32i, !s64i}>
14+
// LLVM-DAG: %struct.s1 = type { i32, i64 }
15+
// OGCG-DAG: %struct.s1 = type { i32, i64 }
16+
17+
struct s2 {
18+
int a : 24;
19+
char b;
20+
int c : 30;
21+
} Clip;
22+
23+
// CIR-DAG: !rec_s2 = !cir.record<struct "s2" {!s32i, !s8i, !s32i}>
24+
// LLVM-DAG: %struct.s2 = type { i32, i8, i32 }
25+
// OGCG-DAG: %struct.s2 = type { i32, i8, i32 }
26+
27+
struct s3 {
28+
int a : 18;
29+
int : 0;
30+
int c : 14;
31+
} zero_bit;
32+
33+
// CIR-DAG: !rec_s3 = !cir.record<struct "s3" {!s32i, !s32i}>
34+
// LLVM-DAG: %struct.s3 = type { i32, i32 }
35+
// OGCG-DAG: %struct.s3 = type { i32, i32 }
36+
37+
#pragma pack (push,1)
38+
39+
struct Inner {
40+
unsigned int A : 1;
41+
unsigned int B : 1;
42+
unsigned int C : 1;
43+
unsigned int D : 30;
44+
} Inner;
45+
46+
#pragma pack (pop)
47+
48+
// CIR-DAG: !rec_Inner = !cir.record<struct "Inner" {!u32i, !u32i}>
49+
// LLVM-DAG: %struct.Inner = type { i32, i32 }
50+
// OGCG-DAG: %struct.Inner = type { i32, i32 }
51+
52+
#pragma pack(push, 1)
53+
54+
union HEADER {
55+
struct A {
56+
int : 3; // Bits 2:0
57+
int a : 9; // Bits 11:3
58+
int : 12; // Bits 23:12
59+
int b : 17; // Bits 40:24
60+
int : 7; // Bits 47:41
61+
int c : 4; // Bits 51:48
62+
int : 4; // Bits 55:52
63+
int d : 3; // Bits 58:56
64+
int : 5; // Bits 63:59
65+
} Bits;
66+
} HEADER;
67+
68+
#pragma pack(pop)
69+
70+
// CIR-DAG: !rec_A = !cir.record<struct "A" {!s32i, !s32i, !s32i}>
71+
// CIR-DAG: !rec_HEADER = !cir.record<union "HEADER" {!rec_A}>
72+
// LLVM-DAG: %struct.A = type { i32, i32, i32 }
73+
// LLVM-DAG: %union.HEADER = type { %struct.A }
74+
// OGCG-DAG: %struct.A = type { i32, i32, i32 }
75+
// OGCG-DAG: %union.HEADER = type { %struct.A }

0 commit comments

Comments
 (0)