Author: Andres-Salamanca Date: 2025-06-20T09:03:02-05:00 New Revision: bd36f7331a9f575272aebb9e0163194541110912
URL: https://github.com/llvm/llvm-project/commit/bd36f7331a9f575272aebb9e0163194541110912 DIFF: https://github.com/llvm/llvm-project/commit/bd36f7331a9f575272aebb9e0163194541110912.diff LOG: [CIR] Add initial support for bitfields in structs (#142041) This change adds support for bitfields CIR records can now contain bit fields. I’ve updated the `CIRGenBitFieldInfo` comment, which originally came from the incubator and was identical to the one in OGCodeGen, to better reflect the current implementation. Support for bitfields in unions big-endian architectures and `get` and `set` operations remains unimplemented and will be addressed in a future patch. Added: clang/test/CIR/CodeGen/bitfields.c clang/test/CIR/CodeGen/bitfields.cpp Modified: clang/include/clang/CIR/MissingFeatures.h clang/lib/CIR/CodeGen/CIRGenRecordLayout.h clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp clang/lib/CIR/CodeGen/TargetInfo.cpp clang/lib/CIR/CodeGen/TargetInfo.h Removed: ################################################################################ diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h index 45452c5929a3b..e0b2959f374f8 100644 --- a/clang/include/clang/CIR/MissingFeatures.h +++ b/clang/include/clang/CIR/MissingFeatures.h @@ -149,6 +149,8 @@ struct MissingFeatures { static bool cxxabiUseARMGuardVarABI() { return false; } static bool cxxabiAppleARM64CXXABI() { return false; } static bool cxxabiStructorImplicitParam() { return false; } + static bool isDiscreteBitFieldABI() { return false; } + static bool isBigEndian() { return false; } // Address class static bool addressOffset() { return false; } @@ -239,6 +241,7 @@ struct MissingFeatures { static bool builtinCall() { return false; } static bool builtinCallF128() { return false; } static bool builtinCallMathErrno() { return false; } + static bool nonFineGrainedBitfields() { return false; } // Missing types static bool dataMemberType() { return false; } diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h index ac8832b8c9b24..3b51ab784d374 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h @@ -14,6 +14,106 @@ namespace clang::CIRGen { +/// Record with information about how a bitfield should be accessed. This is +/// very similar to what LLVM codegen does, once CIR evolves it's possible we +/// can use a more higher level representation. +/// +/// Often we lay out a sequence of bitfields as a contiguous sequence of bits. +/// When the AST record layout does this, we represent it in CIR as a +/// `!cir.record` type, which directly reflects the structure's layout, +/// including bitfield packing and padding, using CIR types such as +/// `!cir.bool`, `!s8i`, `!u16i`. +/// +/// To access a particular bitfield in CIR, we use the operations +/// `cir.get_bitfield` (`GetBitfieldOp`) or `cir.set_bitfield` +/// (`SetBitfieldOp`). These operations rely on the `bitfield_info` +/// attribute, which provides detailed metadata required for access, +/// such as the size and offset of the bitfield, the type and size of +/// the underlying storage, and whether the value is signed. +/// The CIRGenRecordLayout also has a bitFields map which encodes which +/// byte-sequence this bitfield falls within. Let's assume the following C +/// struct: +/// +/// struct S { +/// char a, b, c; +/// unsigned bits : 3; +/// unsigned more_bits : 4; +/// unsigned still_more_bits : 7; +/// }; +/// +/// This will end up as the following cir.record. The bitfield members are +/// represented by one !u16i value, and the array provides padding to align the +/// struct to a 4-byte alignment. +/// +/// !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u16i, +/// !cir.array<!u8i x 3>}> +/// +/// When generating code to access more_bits, we'll generate something +/// essentially like this: +/// +/// #bfi_more_bits = #cir.bitfield_info<name = "more_bits", storage_type = +/// !u16i, size = 4, offset = 3, is_signed = false> +/// +/// cir.func @store_field() { +/// %0 = cir.alloca !rec_S, !cir.ptr<!rec_S>, ["s"] {alignment = 4 : i64} +/// %1 = cir.const #cir.int<2> : !s32i +/// %2 = cir.cast(integral, %1 : !s32i), !u32i +/// %3 = cir.get_member %0[3] {name = "more_bits"} : !cir.ptr<!rec_S> -> +/// !cir.ptr<!u16i> +/// %4 = cir.set_bitfield(#bfi_more_bits, %3 : +/// !cir.ptr<!u16i>, %2 : !u32i) -> !u32i +/// cir.return +/// } +/// +struct CIRGenBitFieldInfo { + /// The offset within a contiguous run of bitfields that are represented as + /// a single "field" within the cir.record type. This offset is in bits. + unsigned offset : 16; + + /// The total size of the bit-field, in bits. + unsigned size : 15; + + /// Whether the bit-field is signed. + unsigned isSigned : 1; + + /// The storage size in bits which should be used when accessing this + /// bitfield. + unsigned storageSize; + + /// The offset of the bitfield storage from the start of the record. + clang::CharUnits storageOffset; + + /// The offset within a contiguous run of bitfields that are represented as a + /// single "field" within the cir.record type, taking into account the AAPCS + /// rules for volatile bitfields. This offset is in bits. + unsigned volatileOffset : 16; + + /// The storage size in bits which should be used when accessing this + /// bitfield. + unsigned volatileStorageSize; + + /// The offset of the bitfield storage from the start of the record. + clang::CharUnits volatileStorageOffset; + + /// The name of a bitfield + llvm::StringRef name; + + // The actual storage type for the bitfield + mlir::Type storageType; + + CIRGenBitFieldInfo() + : offset(), size(), isSigned(), storageSize(), volatileOffset(), + volatileStorageSize() {} + + CIRGenBitFieldInfo(unsigned offset, unsigned size, bool isSigned, + unsigned storageSize, clang::CharUnits storageOffset) + : offset(offset), size(size), isSigned(isSigned), + storageSize(storageSize), storageOffset(storageOffset) {} + + void print(llvm::raw_ostream &os) const; + LLVM_DUMP_METHOD void dump() const; +}; + /// This class handles record and union layout info while lowering AST types /// to CIR types. /// @@ -41,6 +141,10 @@ class CIRGenRecordLayout { // for both virtual and non-virtual bases. llvm::DenseMap<const clang::CXXRecordDecl *, unsigned> nonVirtualBases; + /// Map from (bit-field) record field to the corresponding CIR record type + /// field no. This info is populated by record builder. + llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo> bitFields; + /// False if any direct or indirect subobject of this class, when considered /// as a complete object, requires a non-zero bitpattern when /// zero-initialized. @@ -83,6 +187,16 @@ class CIRGenRecordLayout { /// Check whether this struct can be C++ zero-initialized /// with a zeroinitializer when considered as a base subobject. bool isZeroInitializableAsBase() const { return zeroInitializableAsBase; } + + /// Return the BitFieldInfo that corresponds to the field FD. + const CIRGenBitFieldInfo &getBitFieldInfo(const clang::FieldDecl *fd) const { + fd = fd->getCanonicalDecl(); + assert(fd->isBitField() && "Invalid call for non-bit-field decl!"); + llvm::DenseMap<const clang::FieldDecl *, CIRGenBitFieldInfo>::const_iterator + it = bitFields.find(fd); + assert(it != bitFields.end() && "Unable to find bitfield info"); + return it->second; + } }; } // namespace clang::CIRGen diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp index 0aeef7fd89aef..8dbf1b36a93b2 100644 --- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp @@ -20,6 +20,7 @@ #include "clang/AST/RecordLayout.h" #include "clang/CIR/Dialect/IR/CIRAttrs.h" #include "clang/CIR/Dialect/IR/CIRDataLayout.h" +#include "clang/CIR/MissingFeatures.h" #include "llvm/Support/Casting.h" #include <memory> @@ -66,6 +67,10 @@ struct CIRRecordLowering final { return MemberInfo(offset, MemberInfo::InfoKind::Field, data); } + // Layout routines. + void setBitFieldInfo(const FieldDecl *fd, CharUnits startOffset, + mlir::Type storageType); + void lower(); void lowerUnion(); @@ -77,6 +82,9 @@ struct CIRRecordLowering final { void accumulateBases(const CXXRecordDecl *cxxRecordDecl); void accumulateVPtrs(); void accumulateFields(); + RecordDecl::field_iterator + accumulateBitFields(RecordDecl::field_iterator field, + RecordDecl::field_iterator fieldEnd); CharUnits bitsToCharUnits(uint64_t bitOffset) { return astContext.toCharUnitsFromBits(bitOffset); @@ -87,6 +95,9 @@ struct CIRRecordLowering final { CharUnits getSize(mlir::Type Ty) { return CharUnits::fromQuantity(dataLayout.layout.getTypeSize(Ty)); } + CharUnits getSizeInBits(mlir::Type ty) { + return CharUnits::fromQuantity(dataLayout.layout.getTypeSizeInBits(ty)); + } CharUnits getAlignment(mlir::Type Ty) { return CharUnits::fromQuantity(dataLayout.layout.getTypeABIAlignment(Ty)); } @@ -124,6 +135,17 @@ struct CIRRecordLowering final { mlir::Type getStorageType(const CXXRecordDecl *RD) { return cirGenTypes.getCIRGenRecordLayout(RD).getBaseSubobjectCIRType(); } + // This is diff erent from LLVM traditional codegen because CIRGen uses arrays + // of bytes instead of arbitrary-sized integers. This is important for packed + // structures support. + mlir::Type getBitfieldStorageType(unsigned numBits) { + unsigned alignedBits = llvm::alignTo(numBits, astContext.getCharWidth()); + if (cir::isValidFundamentalIntWidth(alignedBits)) + return builder.getUIntNTy(alignedBits); + + mlir::Type type = getCharType(); + return cir::ArrayType::get(type, alignedBits / astContext.getCharWidth()); + } mlir::Type getStorageType(const FieldDecl *fieldDecl) { mlir::Type type = cirGenTypes.convertTypeForMem(fieldDecl->getType()); @@ -157,6 +179,7 @@ struct CIRRecordLowering final { std::vector<MemberInfo> members; // Output fields, consumed by CIRGenTypes::computeRecordLayout llvm::SmallVector<mlir::Type, 16> fieldTypes; + llvm::DenseMap<const FieldDecl *, CIRGenBitFieldInfo> bitFields; llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap; llvm::DenseMap<const CXXRecordDecl *, unsigned> nonVirtualBases; cir::CIRDataLayout dataLayout; @@ -186,6 +209,32 @@ CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes, zeroInitializable(true), zeroInitializableAsBase(true), packed(packed), padded(false) {} +void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd, + CharUnits startOffset, + mlir::Type storageType) { + CIRGenBitFieldInfo &info = bitFields[fd->getCanonicalDecl()]; + info.isSigned = fd->getType()->isSignedIntegerOrEnumerationType(); + info.offset = + (unsigned)(getFieldBitOffset(fd) - astContext.toBits(startOffset)); + info.size = fd->getBitWidthValue(); + info.storageSize = getSizeInBits(storageType).getQuantity(); + info.storageOffset = startOffset; + info.storageType = storageType; + info.name = fd->getName(); + + if (info.size > info.storageSize) + info.size = info.storageSize; + // Reverse the bit offsets for big endian machines. Since bitfields are laid + // out as packed bits within an integer-sized unit, we can imagine the bits + // counting from the most-significant-bit instead of the + // least-significant-bit. + assert(!cir::MissingFeatures::isBigEndian()); + + info.volatileStorageSize = 0; + info.volatileOffset = 0; + info.volatileStorageOffset = CharUnits::Zero(); +} + void CIRRecordLowering::lower() { if (recordDecl->isUnion()) { lowerUnion(); @@ -233,6 +282,8 @@ void CIRRecordLowering::fillOutputFields() { fieldTypes.size() - 1; // A field without storage must be a bitfield. assert(!cir::MissingFeatures::bitfields()); + if (!member.data) + setBitFieldInfo(member.fieldDecl, member.offset, fieldTypes.back()); } else if (member.kind == MemberInfo::InfoKind::Base) { nonVirtualBases[member.cxxRecordDecl] = fieldTypes.size() - 1; } @@ -240,16 +291,217 @@ void CIRRecordLowering::fillOutputFields() { } } +RecordDecl::field_iterator +CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field, + RecordDecl::field_iterator fieldEnd) { + assert(!cir::MissingFeatures::isDiscreteBitFieldABI()); + + CharUnits regSize = + bitsToCharUnits(astContext.getTargetInfo().getRegisterWidth()); + unsigned charBits = astContext.getCharWidth(); + + // Data about the start of the span we're accumulating to create an access + // unit from. 'Begin' is the first bitfield of the span. If 'begin' is + // 'fieldEnd', we've not got a current span. The span starts at the + // 'beginOffset' character boundary. 'bitSizeSinceBegin' is the size (in bits) + // of the span -- this might include padding when we've advanced to a + // subsequent bitfield run. + RecordDecl::field_iterator begin = fieldEnd; + CharUnits beginOffset; + uint64_t bitSizeSinceBegin; + + // The (non-inclusive) end of the largest acceptable access unit we've found + // since 'begin'. If this is 'begin', we're gathering the initial set of + // bitfields of a new span. 'bestEndOffset' is the end of that acceptable + // access unit -- it might extend beyond the last character of the bitfield + // run, using available padding characters. + RecordDecl::field_iterator bestEnd = begin; + CharUnits bestEndOffset; + bool bestClipped; // Whether the representation must be in a byte array. + + for (;;) { + // atAlignedBoundary is true if 'field' is the (potential) start of a new + // span (or the end of the bitfields). When true, limitOffset is the + // character offset of that span and barrier indicates whether the new + // span cannot be merged into the current one. + bool atAlignedBoundary = false; + bool barrier = false; // a barrier can be a zero Bit Width or non bit member + if (field != fieldEnd && field->isBitField()) { + uint64_t bitOffset = getFieldBitOffset(*field); + if (begin == fieldEnd) { + // Beginning a new span. + begin = field; + bestEnd = begin; + + assert((bitOffset % charBits) == 0 && "Not at start of char"); + beginOffset = bitsToCharUnits(bitOffset); + bitSizeSinceBegin = 0; + } else if ((bitOffset % charBits) != 0) { + // Bitfield occupies the same character as previous bitfield, it must be + // part of the same span. This can include zero-length bitfields, should + // the target not align them to character boundaries. Such non-alignment + // is at variance with the standards, which require zero-length + // bitfields be a barrier between access units. But of course we can't + // achieve that in the middle of a character. + assert(bitOffset == + astContext.toBits(beginOffset) + bitSizeSinceBegin && + "Concatenating non-contiguous bitfields"); + } else { + // Bitfield potentially begins a new span. This includes zero-length + // bitfields on non-aligning targets that lie at character boundaries + // (those are barriers to merging). + if (field->isZeroLengthBitField()) + barrier = true; + atAlignedBoundary = true; + } + } else { + // We've reached the end of the bitfield run. Either we're done, or this + // is a barrier for the current span. + if (begin == fieldEnd) + break; + + barrier = true; + atAlignedBoundary = true; + } + + // 'installBest' indicates whether we should create an access unit for the + // current best span: fields ['begin', 'bestEnd') occupying characters + // ['beginOffset', 'bestEndOffset'). + bool installBest = false; + if (atAlignedBoundary) { + // 'field' is the start of a new span or the end of the bitfields. The + // just-seen span now extends to 'bitSizeSinceBegin'. + + // Determine if we can accumulate that just-seen span into the current + // accumulation. + CharUnits accessSize = bitsToCharUnits(bitSizeSinceBegin + charBits - 1); + if (bestEnd == begin) { + // This is the initial run at the start of a new span. By definition, + // this is the best seen so far. + bestEnd = field; + bestEndOffset = beginOffset + accessSize; + // Assume clipped until proven not below. + bestClipped = true; + if (!bitSizeSinceBegin) + // A zero-sized initial span -- this will install nothing and reset + // for another. + installBest = true; + } else if (accessSize > regSize) { + // Accumulating the just-seen span would create a multi-register access + // unit, which would increase register pressure. + installBest = true; + } + + if (!installBest) { + // Determine if accumulating the just-seen span will create an expensive + // access unit or not. + mlir::Type type = getUIntNType(astContext.toBits(accessSize)); + if (!astContext.getTargetInfo().hasCheapUnalignedBitFieldAccess()) + cirGenTypes.getCGModule().errorNYI( + field->getSourceRange(), "NYI CheapUnalignedBitFieldAccess"); + + if (!installBest) { + // Find the next used storage offset to determine what the limit of + // the current span is. That's either the offset of the next field + // with storage (which might be field itself) or the end of the + // non-reusable tail padding. + CharUnits limitOffset; + for (auto probe = field; probe != fieldEnd; ++probe) + if (!isEmptyFieldForLayout(astContext, *probe)) { + // A member with storage sets the limit. + assert((getFieldBitOffset(*probe) % charBits) == 0 && + "Next storage is not byte-aligned"); + limitOffset = bitsToCharUnits(getFieldBitOffset(*probe)); + goto FoundLimit; + } + assert(!cir::MissingFeatures::cxxSupport()); + limitOffset = astRecordLayout.getDataSize(); + FoundLimit: + CharUnits typeSize = getSize(type); + if (beginOffset + typeSize <= limitOffset) { + // There is space before limitOffset to create a naturally-sized + // access unit. + bestEndOffset = beginOffset + typeSize; + bestEnd = field; + bestClipped = false; + } + if (barrier) { + // The next field is a barrier that we cannot merge across. + installBest = true; + } else if (cirGenTypes.getCGModule() + .getCodeGenOpts() + .FineGrainedBitfieldAccesses) { + assert(!cir::MissingFeatures::nonFineGrainedBitfields()); + cirGenTypes.getCGModule().errorNYI(field->getSourceRange(), + "NYI FineGrainedBitfield"); + } else { + // Otherwise, we're not installing. Update the bit size + // of the current span to go all the way to limitOffset, which is + // the (aligned) offset of next bitfield to consider. + bitSizeSinceBegin = astContext.toBits(limitOffset - beginOffset); + } + } + } + } + + if (installBest) { + assert((field == fieldEnd || !field->isBitField() || + (getFieldBitOffset(*field) % charBits) == 0) && + "Installing but not at an aligned bitfield or limit"); + CharUnits accessSize = bestEndOffset - beginOffset; + if (!accessSize.isZero()) { + // Add the storage member for the access unit to the record. The + // bitfields get the offset of their storage but come afterward and + // remain there after a stable sort. + mlir::Type type; + if (bestClipped) { + assert(getSize(getUIntNType(astContext.toBits(accessSize))) > + accessSize && + "Clipped access need not be clipped"); + type = getByteArrayType(accessSize); + } else { + type = getUIntNType(astContext.toBits(accessSize)); + assert(getSize(type) == accessSize && + "Unclipped access must be clipped"); + } + members.push_back(makeStorageInfo(beginOffset, type)); + for (; begin != bestEnd; ++begin) + if (!begin->isZeroLengthBitField()) + members.push_back(MemberInfo( + beginOffset, MemberInfo::InfoKind::Field, nullptr, *begin)); + } + // Reset to start a new span. + field = bestEnd; + begin = fieldEnd; + } else { + assert(field != fieldEnd && field->isBitField() && + "Accumulating past end of bitfields"); + assert(!barrier && "Accumulating across barrier"); + // Accumulate this bitfield into the current (potential) span. + bitSizeSinceBegin += field->getBitWidthValue(); + ++field; + } + } + + return field; +} + void CIRRecordLowering::accumulateFields() { - for (const FieldDecl *field : recordDecl->fields()) { + for (RecordDecl::field_iterator field = recordDecl->field_begin(), + fieldEnd = recordDecl->field_end(); + field != fieldEnd;) { if (field->isBitField()) { - cirGenTypes.getCGModule().errorNYI(recordDecl->getSourceRange(), - "accumulate bitfields"); - ++field; + RecordDecl::field_iterator start = field; + // Iterate to gather the list of bitfields. + for (++field; field != fieldEnd && field->isBitField(); ++field) + ; + field = accumulateBitFields(start, field); + assert((field == fieldEnd || !field->isBitField()) && + "Failed to accumulate all the bitfields"); } else if (!field->isZeroSize(astContext)) { - members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(field)), + members.push_back(MemberInfo(bitsToCharUnits(getFieldBitOffset(*field)), MemberInfo::InfoKind::Field, - getStorageType(field), field)); + getStorageType(*field), *field)); ++field; } else { // TODO(cir): do we want to do anything special about zero size members? @@ -383,6 +635,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) { // Add all the field numbers. rl->fieldIdxMap.swap(lowering.fieldIdxMap); + rl->bitFields.swap(lowering.bitFields); + // Dump the layout, if requested. if (getASTContext().getLangOpts().DumpRecordLayouts) { cgm.errorNYI(rd->getSourceRange(), "computeRecordLayout: dump layout"); diff --git a/clang/lib/CIR/CodeGen/TargetInfo.cpp b/clang/lib/CIR/CodeGen/TargetInfo.cpp index 551341ff20c00..d2d32bbd9403c 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.cpp +++ b/clang/lib/CIR/CodeGen/TargetInfo.cpp @@ -3,6 +3,43 @@ using namespace clang; using namespace clang::CIRGen; + +bool clang::CIRGen::isEmptyRecordForLayout(const ASTContext &context, + QualType t) { + const RecordType *rt = t->getAs<RecordType>(); + if (!rt) + return false; + + const RecordDecl *rd = rt->getDecl(); + + // If this is a C++ record, check the bases first. + if (const CXXRecordDecl *cxxrd = dyn_cast<CXXRecordDecl>(rd)) { + if (cxxrd->isDynamicClass()) + return false; + + for (const auto &I : cxxrd->bases()) + if (!isEmptyRecordForLayout(context, I.getType())) + return false; + } + + for (const auto *I : rd->fields()) + if (!isEmptyFieldForLayout(context, I)) + return false; + + return true; +} + +bool clang::CIRGen::isEmptyFieldForLayout(const ASTContext &context, + const FieldDecl *fd) { + if (fd->isZeroLengthBitField()) + return true; + + if (fd->isUnnamedBitField()) + return false; + + return isEmptyRecordForLayout(context, fd->getType()); +} + namespace { class X8664ABIInfo : public ABIInfo { diff --git a/clang/lib/CIR/CodeGen/TargetInfo.h b/clang/lib/CIR/CodeGen/TargetInfo.h index d31d1ee82d90a..a5c548aa2c7c4 100644 --- a/clang/lib/CIR/CodeGen/TargetInfo.h +++ b/clang/lib/CIR/CodeGen/TargetInfo.h @@ -22,6 +22,16 @@ namespace clang::CIRGen { +/// isEmptyFieldForLayout - Return true if the field is "empty", that is, +/// either a zero-width bit-field or an isEmptyRecordForLayout. +bool isEmptyFieldForLayout(const ASTContext &context, const FieldDecl *fd); + +/// isEmptyRecordForLayout - Return true if a structure contains only empty +/// base classes (per isEmptyRecordForLayout) and fields (per +/// isEmptyFieldForLayout). Note, C++ record fields are considered empty +/// if the [[no_unique_address]] attribute would have made them empty. +bool isEmptyRecordForLayout(const ASTContext &context, QualType t); + class TargetCIRGenInfo { std::unique_ptr<ABIInfo> info; diff --git a/clang/test/CIR/CodeGen/bitfields.c b/clang/test/CIR/CodeGen/bitfields.c new file mode 100644 index 0000000000000..ff5c6bc1787b4 --- /dev/null +++ b/clang/test/CIR/CodeGen/bitfields.c @@ -0,0 +1,78 @@ +// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -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 -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 -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +typedef struct { + char a, b, c; + unsigned bits : 3; + unsigned more_bits : 4; + unsigned still_more_bits : 7; +} A; + +// CIR-DAG: !rec_A = !cir.record<struct "A" packed padded {!s8i, !s8i, !s8i, !u16i, !cir.array<!u8i x 3>}> +// LLVM-DAG: %struct.A = type <{ i8, i8, i8, i16, [3 x i8] }> +// OGCG-DAG: %struct.A = type <{ i8, i8, i8, i16, [3 x i8] }> + +typedef struct { + int a : 4; + int b : 5; + int c; +} D; + +// CIR-DAG: !rec_D = !cir.record<struct "D" {!u16i, !s32i}> +// LLVM-DAG: %struct.D = type { i16, i32 } +// OGCG-DAG: %struct.D = type { i16, i32 } + +typedef struct { + int a : 4; + int b : 27; + int c : 17; + int d : 2; + int e : 15; + unsigned f; // type other than int above, not a bitfield +} S; +// CIR-DAG: !rec_S = !cir.record<struct "S" {!u64i, !u16i, !u32i}> +// LLVM-DAG: %struct.S = type { i64, i16, i32 } +// OGCG-DAG: %struct.S = type { i64, i16, i32 } + +typedef struct { + int a : 3; // one bitfield with size < 8 + unsigned b; +} T; + +// CIR-DAG: !rec_T = !cir.record<struct "T" {!u8i, !u32i}> +// LLVM-DAG: %struct.T = type { i8, i32 } +// OGCG-DAG: %struct.T = type { i8, i32 } + +typedef struct { + char a; + char b; + char c; + + // startOffset 24 bits, new storage from here + int d: 2; + int e: 2; + int f: 4; + int g: 25; + int h: 3; + int i: 4; + int j: 3; + int k: 8; + + int l: 14; +} U; + +// CIR-DAG: !rec_U = !cir.record<struct "U" packed {!s8i, !s8i, !s8i, !u8i, !u64i}> +// LLVM-DAG: %struct.U = type <{ i8, i8, i8, i8, i64 }> +// OGCG-DAG: %struct.U = type <{ i8, i8, i8, i8, i64 }> + +void def() { + A a; + D d; + S s; + T t; + U u; +} diff --git a/clang/test/CIR/CodeGen/bitfields.cpp b/clang/test/CIR/CodeGen/bitfields.cpp new file mode 100644 index 0000000000000..762d249884741 --- /dev/null +++ b/clang/test/CIR/CodeGen/bitfields.cpp @@ -0,0 +1,32 @@ +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s --check-prefix=CIR +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --input-file=%t-cir.ll %s --check-prefix=LLVM +// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s --check-prefix=OGCG + +typedef struct { + int a : 4; + int b : 27; + int c : 17; + int d : 2; + int e : 15; + unsigned f; // type other than int above, not a bitfield +} S; +// CIR-DAG: !rec_S = !cir.record<struct "S" {!u64i, !u16i, !u32i}> +// LLVM-DAG: %struct.S = type { i64, i16, i32 } +// OGCG-DAG: %struct.S = type { i64, i16, i32 } + +typedef struct { + int a : 3; // one bitfield with size < 8 + unsigned b; +} T; + +// CIR-DAG: !rec_T = !cir.record<struct "T" {!u8i, !u32i}> +// LLVM-DAG: %struct.T = type { i8, i32 } +// OGCG-DAG: %struct.T = type { i8, i32 } + +void def() { + S s; + T t; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits