Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions clang/include/clang/CIR/Dialect/IR/CIRDataLayout.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
};

Expand Down
1 change: 0 additions & 1 deletion clang/include/clang/CIR/MissingFeatures.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ struct MissingFeatures {
static bool cxxabiUseARMMethodPtrABI() { return false; }
static bool cxxabiUseARMGuardVarABI() { return false; }
static bool cxxabiAppleARM64CXXABI() { return false; }
static bool isDiscreteBitFieldABI() { return false; }

// Address class
static bool addressOffset() { return false; }
Expand Down
50 changes: 49 additions & 1 deletion clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a test case or two that encounter this condition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

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());
Expand Down
75 changes: 75 additions & 0 deletions clang/test/CIR/CodeGen/mms-bitfields.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// 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 }

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 {
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 }