[clang] [CIR] Add support for discrete bit-field (PR #156085)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 2 19:03:22 PDT 2025
https://github.com/Andres-Salamanca updated https://github.com/llvm/llvm-project/pull/156085
>From 3267ff0b8f7308f6d2654e50fe83f2fdb7b42a05 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Fri, 29 Aug 2025 13:44:22 -0500
Subject: [PATCH 1/4] [CIR] Add support for discrete bit-field ABI
---
.../clang/CIR/Dialect/IR/CIRDataLayout.h | 12 ++++
.../CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp | 50 +++++++++++++-
clang/test/CIR/CodeGen/mms-bitfields.c | 65 +++++++++++++++++++
3 files changed, 126 insertions(+), 1 deletion(-)
create mode 100644 clang/test/CIR/CodeGen/mms-bitfields.c
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
index ecc681ee310e3..4da435e0ea8f7 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
+++ b/clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
@@ -68,6 +68,18 @@ class CIRDataLayout {
return llvm::alignTo(getTypeStoreSize(ty), getABITypeAlign(ty).value());
}
+ /// Returns the offset in bits between successive objects of the
+ /// specified type, including alignment padding; always a multiple of 8.
+ ///
+ /// If Ty is a scalable vector type, the scalable property will be set and
+ /// the runtime size will be a positive integer multiple of the base size.
+ ///
+ /// This is the amount that alloca reserves for this type. For example,
+ /// returns 96 or 128 for x86_fp80, depending on alignment.
+ llvm::TypeSize getTypeAllocSizeInBits(mlir::Type ty) const {
+ return 8 * getTypeAllocSize(ty);
+ }
+
llvm::TypeSize getTypeSizeInBits(mlir::Type ty) const;
};
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index 6c7cf75aa2c99..9ebcf39190f45 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -108,6 +108,16 @@ struct CIRRecordLowering final {
// not the primary vbase of some base class.
bool hasOwnStorage(const CXXRecordDecl *decl, const CXXRecordDecl *query);
+ /// The Microsoft bitfield layout rule allocates discrete storage
+ /// units of the field's formal type and only combines adjacent
+ /// fields of the same formal type. We want to emit a layout with
+ /// these discrete storage units instead of combining them into a
+ /// continuous run.
+ bool isDiscreteBitFieldABI() {
+ return astContext.getTargetInfo().getCXXABI().isMicrosoft() ||
+ recordDecl->isMsStruct(astContext);
+ }
+
CharUnits bitsToCharUnits(uint64_t bitOffset) {
return astContext.toCharUnitsFromBits(bitOffset);
}
@@ -323,7 +333,45 @@ void CIRRecordLowering::fillOutputFields() {
RecordDecl::field_iterator
CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
RecordDecl::field_iterator fieldEnd) {
- assert(!cir::MissingFeatures::isDiscreteBitFieldABI());
+ if (isDiscreteBitFieldABI()) {
+ // run stores the first element of the current run of bitfields. fieldEnd is
+ // used as a special value to note that we don't have a current run. A
+ // bitfield run is a contiguous collection of bitfields that can be stored
+ // in the same storage block. Zero-sized bitfields and bitfields that would
+ // cross an alignment boundary break a run and start a new one.
+ RecordDecl::field_iterator run = fieldEnd;
+ // tail is the offset of the first bit off the end of the current run. It's
+ // used to determine if the ASTRecordLayout is treating these two bitfields
+ // as contiguous. StartBitOffset is offset of the beginning of the Run.
+ uint64_t startBitOffset, tail = 0;
+ for (; field != fieldEnd && field->isBitField(); ++field) {
+ // Zero-width bitfields end runs.
+ if (field->isZeroLengthBitField()) {
+ run = fieldEnd;
+ continue;
+ }
+ uint64_t bitOffset = getFieldBitOffset(*field);
+ mlir::Type type = cirGenTypes.convertTypeForMem(field->getType());
+ // If we don't have a run yet, or don't live within the previous run's
+ // allocated storage then we allocate some storage and start a new run.
+ if (run == fieldEnd || bitOffset >= tail) {
+ run = field;
+ startBitOffset = bitOffset;
+ tail = startBitOffset + dataLayout.getTypeAllocSizeInBits(type);
+ // Add the storage member to the record. This must be added to the
+ // record before the bitfield members so that it gets laid out before
+ // the bitfields it contains get laid out.
+ members.push_back(
+ makeStorageInfo(bitsToCharUnits(startBitOffset), type));
+ }
+ // Bitfields get the offset of their storage but come afterward and remain
+ // there after a stable sort.
+ members.push_back(MemberInfo(bitsToCharUnits(startBitOffset),
+ MemberInfo::InfoKind::Field, nullptr,
+ *field));
+ }
+ return field;
+ }
CharUnits regSize =
bitsToCharUnits(astContext.getTargetInfo().getRegisterWidth());
diff --git a/clang/test/CIR/CodeGen/mms-bitfields.c b/clang/test/CIR/CodeGen/mms-bitfields.c
new file mode 100644
index 0000000000000..071746822e745
--- /dev/null
+++ b/clang/test/CIR/CodeGen/mms-bitfields.c
@@ -0,0 +1,65 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mms-bitfields -fclangir -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mms-bitfields -fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mms-bitfields -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG
+
+struct s1 {
+ int f32 : 2;
+ long long f64 : 30;
+} s1;
+
+// CIR-DAG: !rec_s1 = !cir.record<struct "s1" {!s32i, !s64i}>
+// LLVM-DAG: %struct.s1 = type { i32, i64 }
+// OGCG-DAG: %struct.s1 = type { i32, i64 }
+
+struct s2 {
+ int a : 24;
+ char b;
+ int c : 30;
+} Clip;
+
+// CIR-DAG: !rec_s2 = !cir.record<struct "s2" {!s32i, !s8i, !s32i}>
+// LLVM-DAG: %struct.s2 = type { i32, i8, i32 }
+// OGCG-DAG: %struct.s2 = type { i32, i8, i32 }
+
+#pragma pack (push,1)
+
+struct Inner {
+ unsigned int A : 1;
+ unsigned int B : 1;
+ unsigned int C : 1;
+ unsigned int D : 30;
+} Inner;
+
+#pragma pack (pop)
+
+// CIR-DAG: !rec_Inner = !cir.record<struct "Inner" {!u32i, !u32i}>
+// LLVM-DAG: %struct.Inner = type { i32, i32 }
+// OGCG-DAG: %struct.Inner = type { i32, i32 }
+
+#pragma pack(push, 1)
+
+union HEADER {
+ struct A {
+ int : 3; // Bits 2:0
+ int a : 9; // Bits 11:3
+ int : 12; // Bits 23:12
+ int b : 17; // Bits 40:24
+ int : 7; // Bits 47:41
+ int c : 4; // Bits 51:48
+ int : 4; // Bits 55:52
+ int d : 3; // Bits 58:56
+ int : 5; // Bits 63:59
+ } Bits;
+} HEADER;
+
+#pragma pack(pop)
+
+// CIR-DAG: !rec_A = !cir.record<struct "A" {!s32i, !s32i, !s32i}>
+// CIR-DAG: !rec_HEADER = !cir.record<union "HEADER" {!rec_A}>
+// LLVM-DAG: %struct.A = type { i32, i32, i32 }
+// LLVM-DAG: %union.HEADER = type { %struct.A }
+// OGCG-DAG: %struct.A = type { i32, i32, i32 }
+// OGCG-DAG: %union.HEADER = type { %struct.A }
>From ba0123517c7f8a1a121f37669f8f63610f39421a Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Fri, 29 Aug 2025 13:53:23 -0500
Subject: [PATCH 2/4] Remove unused isDiscreteBitFieldABI
---
clang/include/clang/CIR/MissingFeatures.h | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 70e0abe30e416..fbed18ca14345 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -149,7 +149,6 @@ struct MissingFeatures {
static bool cxxabiUseARMGuardVarABI() { return false; }
static bool cxxabiAppleARM64CXXABI() { return false; }
static bool cxxabiStructorImplicitParam() { return false; }
- static bool isDiscreteBitFieldABI() { return false; }
// Address class
static bool addressOffset() { return false; }
>From e77ee7bea108e734db918ddf471711df5b743cb5 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Sat, 30 Aug 2025 11:21:12 -0500
Subject: [PATCH 3/4] Add requested test
---
clang/test/CIR/CodeGen/mms-bitfields.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/clang/test/CIR/CodeGen/mms-bitfields.c b/clang/test/CIR/CodeGen/mms-bitfields.c
index 071746822e745..624fa6a2a2af8 100644
--- a/clang/test/CIR/CodeGen/mms-bitfields.c
+++ b/clang/test/CIR/CodeGen/mms-bitfields.c
@@ -24,6 +24,16 @@ struct s2 {
// LLVM-DAG: %struct.s2 = type { i32, i8, i32 }
// OGCG-DAG: %struct.s2 = type { i32, i8, i32 }
+struct s3 {
+ int a : 18;
+ int : 0;
+ int c : 14;
+} zero_bit;
+
+// CIR-DAG: !rec_s3 = !cir.record<struct "s3" {!s32i, !s32i}>
+// LLVM-DAG: %struct.s3 = type { i32, i32 }
+// OGCG-DAG: %struct.s3 = type { i32, i32 }
+
#pragma pack (push,1)
struct Inner {
>From abf2354af91c06930a43039cda33c8278d5d35bc Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Tue, 2 Sep 2025 21:03:03 -0500
Subject: [PATCH 4/4] Address review feedback
---
clang/test/CIR/CodeGen/mms-bitfields.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/clang/test/CIR/CodeGen/mms-bitfields.c b/clang/test/CIR/CodeGen/mms-bitfields.c
index 624fa6a2a2af8..698034aadb100 100644
--- a/clang/test/CIR/CodeGen/mms-bitfields.c
+++ b/clang/test/CIR/CodeGen/mms-bitfields.c
@@ -15,9 +15,9 @@ struct s1 {
// OGCG-DAG: %struct.s1 = type { i32, i64 }
struct s2 {
- int a : 24;
+ int a : 24;
char b;
- int c : 30;
+ int c : 30;
} Clip;
// CIR-DAG: !rec_s2 = !cir.record<struct "s2" {!s32i, !s8i, !s32i}>
@@ -25,9 +25,9 @@ struct s2 {
// OGCG-DAG: %struct.s2 = type { i32, i8, i32 }
struct s3 {
- int a : 18;
- int : 0;
- int c : 14;
+ int a : 18;
+ int : 0;
+ int c : 14;
} zero_bit;
// CIR-DAG: !rec_s3 = !cir.record<struct "s3" {!s32i, !s32i}>
@@ -37,10 +37,10 @@ struct s3 {
#pragma pack (push,1)
struct Inner {
- unsigned int A : 1;
- unsigned int B : 1;
- unsigned int C : 1;
- unsigned int D : 30;
+ unsigned int A : 1;
+ unsigned int B : 1;
+ unsigned int C : 1;
+ unsigned int D : 30;
} Inner;
#pragma pack (pop)
More information about the cfe-commits
mailing list