[llvm-branch-commits] [clang] [CIR] Mark record members as data, pad, or empty in CIRGen (PR #215175)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Sun Aug 9 21:08:23 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Adam Smith (adams381)
<details>
<summary>Changes</summary>
The per-member marks landed with no producer, so every record still reads as though all of its members held data. A record type still can't say whether it carries anything for argument passing. The x86_64 classifier has to know that before it can drop an empty class from a signature.
CIRGen fills the marks in now. Every member goes through one `addField` that takes its kind, so a new push site can't quietly inherit a default.
A field's mark comes from `isEmptyFieldForABI`, ported from `isEmptyField` in `ABIInfoImpl.cpp`. Taking the ABI predicate rather than the layout one is what gets C right. Given `struct E {}`, a struct holding one `E` is empty for the ABI in C but not in C++.
An assert on every record checks the marks against `isEmptyRecordForABI`, so the existing `-fclangir` tests exercise them.
One case is now NYI. A `[[no_unique_address]]` field that `isEmptyFieldForLayout` drops from the layout can still hold ABI data. With the field gone there is no member left to mark, so CIRGen says so rather than emit a record that understates what it holds.
This is the second of three PRs. Nothing reads the marks yet. The `padded` bool stays, and `computeStructDataSize` still uses it. The next PR will properly use the marks and remove `padded`.
Depends on [#<!-- -->215174](https://github.com/llvm/llvm-project/pull/215174).
Assisted-by: Cursor / claude-opus-5
---
Patch is 62.37 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/215175.diff
27 Files Affected:
- (modified) clang/include/clang/CIR/Dialect/IR/CIRAttrs.td (+1-1)
- (modified) clang/include/clang/CIR/Dialect/IR/CIROps.td (+3-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.cpp (+2-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenBuilder.h (+5-13)
- (modified) clang/lib/CIR/CodeGen/CIRGenRecordLayout.h (+1-1)
- (modified) clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp (+169-38)
- (modified) clang/lib/CIR/CodeGen/TargetInfo.cpp (+56)
- (modified) clang/lib/CIR/CodeGen/TargetInfo.h (+23)
- (modified) clang/lib/CIR/Dialect/Transforms/CXXABILowering.cpp (+6-3)
- (modified) clang/test/CIR/CodeGen/bitfields.c (+1-1)
- (modified) clang/test/CIR/CodeGen/cleanup.cpp (+1-1)
- (modified) clang/test/CIR/CodeGen/dumb-record.cpp (+2-2)
- (modified) clang/test/CIR/CodeGen/empty-union.cpp (+1-1)
- (modified) clang/test/CIR/CodeGen/finegrain-bitfield-access.cpp (+1-1)
- (modified) clang/test/CIR/CodeGen/member-functions.cpp (+1-1)
- (modified) clang/test/CIR/CodeGen/no-unique-address.cpp (+2-2)
- (modified) clang/test/CIR/CodeGen/paren-list-agg-init.cpp (+6-6)
- (modified) clang/test/CIR/CodeGen/pointer-to-empty-data-member.cpp (+5-5)
- (added) clang/test/CIR/CodeGen/record-member-kinds-nyi.cpp (+14)
- (added) clang/test/CIR/CodeGen/record-member-kinds.c (+97)
- (added) clang/test/CIR/CodeGen/record-member-kinds.cpp (+141)
- (modified) clang/test/CIR/CodeGen/record-type-metadata.cpp (+2-2)
- (modified) clang/test/CIR/CodeGen/struct.c (+1-1)
- (modified) clang/test/CIR/CodeGen/template-specialization.cpp (+1-1)
- (modified) clang/test/CIR/CodeGen/vtt.cpp (+2-2)
- (modified) clang/test/CIR/CodeGenCXX/zero_init_bases.cpp (+1-1)
- (modified) clang/test/CIR/CodeGenCoroutines/coro-task.cpp (+9-9)
``````````diff
diff --git a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
index 71585cd83fb66..9a726a3619ffc 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIRAttrs.td
@@ -1564,7 +1564,7 @@ def CIR_BitfieldInfoAttr : CIR_Attr<"BitfieldInfo", "bitfield_info"> {
The CIR representation of the struct `S` might look like:
```
!rec_S = !cir.record<struct "S" packed padded {!u64i, !u16i,
- !cir.array<!u8i x 2>}>
+ pad !cir.array<!u8i x 2>}>
```
And the bitfield info attribute for member `a` would be:
```
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index d0f3c9ee6715f..513d765fff23d 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3752,7 +3752,7 @@ def CIR_SetBitfieldOp : CIR_Op<"set_bitfield"> {
```
// 'e' is in the storage with the index 1
!record_type = !cir.record<struct "S" packed padded {!u64i, !u16i,
- !cir.array<!u8i x 2>} #cir.record.decl.ast>
+ pad !cir.array<!u8i x 2>} #cir.record.decl.ast>
#bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
offset = 0, is_signed = true>
@@ -3842,7 +3842,8 @@ def CIR_GetBitfieldOp : CIR_Op<"get_bitfield"> {
```
// 'e' is in the storage with the index 1
- !cir.record<struct "S" packed padded {!u64i, !u16i, !cir.array<!u8i x 2>}>
+ !cir.record<struct "S" packed padded {!u64i, !u16i,
+ pad !cir.array<!u8i x 2>}>
#bfi_e = #cir.bitfield_info<name = "e", storage_type = !u16i, size = 15,
offset = 0, is_signed = true>
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
index a562c4b7b763f..d339c35fbffa0 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.cpp
@@ -208,7 +208,8 @@ cir::RecordType clang::CIRGen::CIRGenBuilderTy::getCompleteRecordType(
if (name.empty())
return getAnonRecordTy(members, packed, padded);
- return getCompleteNamedRecordType(members, packed, padded, name);
+ return getCompleteNamedRecordType(members, packed, padded, name,
+ /*memberKinds=*/{});
}
mlir::Attribute clang::CIRGen::CIRGenBuilderTy::getConstRecordOrZeroAttr(
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuilder.h b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
index c906e65a132c2..f414f67c76fbe 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuilder.h
+++ b/clang/lib/CIR/CodeGen/CIRGenBuilder.h
@@ -154,26 +154,18 @@ class CIRGenBuilderTy : public cir::CIRBaseBuilderTy {
///
/// If a record already exists and is complete, but the client tries to fetch
/// it with a different set of attributes, this method will crash.
- cir::RecordType getCompleteNamedRecordType(llvm::ArrayRef<mlir::Type> members,
- bool packed, bool padded,
- llvm::StringRef name) {
+ cir::RecordType getCompleteNamedRecordType(
+ llvm::ArrayRef<mlir::Type> members, bool packed, bool padded,
+ llvm::StringRef name, llvm::ArrayRef<cir::RecordMemberKind> memberKinds) {
const auto nameAttr = getStringAttr(name);
assert(!cir::MissingFeatures::astRecordDeclAttr());
// Create or get the struct type (named anonymous struct helper — always
// struct, never class or union at this call site).
auto type = cir::StructType::get(getContext(), members, nameAttr, packed,
- padded, /*is_class=*/false);
+ padded, /*is_class=*/false, memberKinds);
- // If we found an existing type, verify that either it is incomplete or
- // it matches the requested attributes.
- assert(!type.isIncomplete() ||
- (type.getMembers() == members && type.getPacked() == packed &&
- type.getPadded() == padded));
-
- // Complete an incomplete record or ensure the existing complete record
- // matches the requested attributes.
- type.complete(members, packed, padded);
+ type.complete(members, packed, padded, memberKinds);
return type;
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index 9733d18f52ce8..c4968f3024d99 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -46,7 +46,7 @@ namespace clang::CIRGen {
/// struct to a 4-byte alignment.
///
/// !rec_S = !cir.record<struct "S" padded {!s8i, !s8i, !s8i, !u16i,
-/// !cir.array<!u8i x 3>}>
+/// pad !cir.array<!u8i x 3>}>
///
/// When generating code to access more_bits, we'll generate something
/// essentially like this:
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
index e4476b88ec6f7..8258f1bf1aa33 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayoutBuilder.cpp
@@ -13,6 +13,7 @@
#include "CIRGenBuilder.h"
#include "CIRGenModule.h"
#include "CIRGenTypes.h"
+#include "TargetInfo.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
@@ -43,16 +44,23 @@ struct CIRRecordLowering final {
CharUnits offset;
enum class InfoKind { VFPtr, Field, Base, VBase } kind;
mlir::Type data;
+ /// What this member holds, recorded on the CIR record type so that later
+ /// passes can tell declared storage from compiler-inserted padding. Every
+ /// constructor takes it, so a new member cannot be added without deciding.
+ cir::RecordMemberKind memberKind;
union {
const FieldDecl *fieldDecl;
const CXXRecordDecl *cxxRecordDecl;
};
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
+ cir::RecordMemberKind memberKind,
const FieldDecl *fieldDecl = nullptr)
- : offset{offset}, kind{kind}, data{data}, fieldDecl{fieldDecl} {}
+ : offset{offset}, kind{kind}, data{data}, memberKind{memberKind},
+ fieldDecl{fieldDecl} {}
MemberInfo(CharUnits offset, InfoKind kind, mlir::Type data,
- const CXXRecordDecl *rd)
- : offset{offset}, kind{kind}, data{data}, cxxRecordDecl{rd} {}
+ cir::RecordMemberKind memberKind, const CXXRecordDecl *rd)
+ : offset{offset}, kind{kind}, data{data}, memberKind{memberKind},
+ cxxRecordDecl{rd} {}
// MemberInfos are sorted so we define a < operator.
bool operator<(const MemberInfo &other) const {
return offset < other.offset;
@@ -63,8 +71,9 @@ struct CIRRecordLowering final {
bool packed);
/// Constructs a MemberInfo instance from an offset and mlir::Type.
- MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data) {
- return MemberInfo(offset, MemberInfo::InfoKind::Field, data);
+ MemberInfo makeStorageInfo(CharUnits offset, mlir::Type data,
+ cir::RecordMemberKind memberKind) {
+ return MemberInfo(offset, MemberInfo::InfoKind::Field, data, memberKind);
}
// Layout routines.
@@ -83,7 +92,7 @@ struct CIRRecordLowering final {
void accumulateBases();
void accumulateVPtrs();
void accumulateVBases();
- void accumulateFields();
+ void accumulateFields(bool nonVirtualBaseType);
RecordDecl::field_iterator
accumulateBitFields(RecordDecl::field_iterator field,
RecordDecl::field_iterator fieldEnd);
@@ -141,6 +150,22 @@ struct CIRRecordLowering final {
return cirGenTypes.isZeroInitializable(rd);
}
+ /// The mark for a field.
+ cir::RecordMemberKind getFieldMemberKind(const FieldDecl *fd) {
+ return isEmptyFieldForABI(astContext, fd) ? cir::RecordMemberKind::Empty
+ : cir::RecordMemberKind::Data;
+ }
+
+ /// The mark for a base subobject. A base contributes no ABI data when it is
+ /// empty for the ABI, which is not the same as CXXRecordDecl::isEmpty(): a
+ /// base holding only unnamed bit-fields is laid out but carries no data.
+ cir::RecordMemberKind getBaseMemberKind(const CXXRecordDecl *baseDecl) {
+ return isEmptyRecordForABI(astContext,
+ astContext.getCanonicalTagType(baseDecl))
+ ? cir::RecordMemberKind::Empty
+ : cir::RecordMemberKind::Data;
+ }
+
/// Wraps cir::IntType with some implicit arguments.
mlir::Type getUIntNType(uint64_t numBits) {
unsigned alignedBits = llvm::PowerOf2Ceil(numBits);
@@ -204,10 +229,26 @@ struct CIRRecordLowering final {
assert(!unionPadding && "at most one union tail-padding type");
unionPadding = padTy;
} else {
- fieldTypes.push_back(padTy);
+ addField(padTy, cir::RecordMemberKind::Pad);
}
}
+ /// The single entry point for appending an output field.
+ void addField(mlir::Type ty, cir::RecordMemberKind memberKind) {
+ fieldTypes.push_back(ty);
+ fieldKinds.push_back(memberKind);
+ }
+
+ void clearFields() {
+ fieldTypes.clear();
+ fieldKinds.clear();
+ }
+
+ llvm::ArrayRef<mlir::Type> getFieldTypes() const { return fieldTypes; }
+ llvm::ArrayRef<cir::RecordMemberKind> getFieldKinds() const {
+ return fieldKinds;
+ }
+
CIRGenTypes &cirGenTypes;
CIRGenBuilderTy &builder;
const ASTContext &astContext;
@@ -216,8 +257,6 @@ struct CIRRecordLowering final {
const ASTRecordLayout &astRecordLayout;
// Helpful intermediate data-structures
std::vector<MemberInfo> members;
- // Output fields, consumed by CIRGenTypes::computeRecordLayout
- llvm::SmallVector<mlir::Type, 16> fieldTypes;
mlir::Type unionPadding;
llvm::DenseMap<const FieldDecl *, CIRGenBitFieldInfo> bitFields;
llvm::DenseMap<const FieldDecl *, unsigned> fieldIdxMap;
@@ -233,8 +272,19 @@ struct CIRRecordLowering final {
unsigned packed : 1;
LLVM_PREFERRED_TYPE(bool)
unsigned padded : 1;
+ /// Whether a field was dropped whose data no member can represent. Reported
+ /// as NYI. Until that gap is closed the completed type reads as ABI-empty
+ /// when it is not, so the differential asserts below cannot run on it.
+ LLVM_PREFERRED_TYPE(bool)
+ unsigned droppedFieldHoldingData : 1;
private:
+ // Output fields, consumed by CIRGenTypes::computeRecordLayout. Private so
+ // that every append goes through addField and fieldKinds stays parallel to
+ // fieldTypes.
+ llvm::SmallVector<mlir::Type, 16> fieldTypes;
+ llvm::SmallVector<cir::RecordMemberKind> fieldKinds;
+
CIRRecordLowering(const CIRRecordLowering &) = delete;
void operator=(const CIRRecordLowering &) = delete;
}; // CIRRecordLowering
@@ -249,7 +299,7 @@ CIRRecordLowering::CIRRecordLowering(CIRGenTypes &cirGenTypes,
cirGenTypes.getASTContext().getASTRecordLayout(recordDecl)},
dataLayout{cirGenTypes.getCGModule().getModule()},
zeroInitializable{true}, zeroInitializableAsBase{true}, packed{packed},
- padded{false} {}
+ padded{false}, droppedFieldHoldingData{false} {}
void CIRRecordLowering::setBitFieldInfo(const FieldDecl *fd,
CharUnits startOffset,
@@ -288,7 +338,7 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
CharUnits size = nonVirtualBaseType ? astRecordLayout.getNonVirtualSize()
: astRecordLayout.getSize();
- accumulateFields();
+ accumulateFields(nonVirtualBaseType);
if (cxxRecordDecl) {
accumulateVPtrs();
@@ -306,7 +356,10 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
// TODO: Verify bitfield clipping
assert(!cir::MissingFeatures::checkBitfieldClipping());
- members.push_back(makeStorageInfo(size, getUIntNType(8)));
+ // The sentinel is popped before fillOutputFields, so its kind never reaches
+ // the type.
+ members.push_back(
+ makeStorageInfo(size, getUIntNType(8), cir::RecordMemberKind::Data));
determinePacked(nonVirtualBaseType);
insertPadding();
members.pop_back();
@@ -318,8 +371,11 @@ void CIRRecordLowering::lower(bool nonVirtualBaseType) {
void CIRRecordLowering::fillOutputFields() {
for (const MemberInfo &member : members) {
+ // A bit-field occupant and a primary virtual base without own storage both
+ // carry null data, so the kind must be appended inside this guard or every
+ // later mark shifts by one while the two lengths still agree.
if (member.data)
- fieldTypes.push_back(member.data);
+ addField(member.data, member.memberKind);
if (member.kind == MemberInfo::InfoKind::Field) {
if (member.fieldDecl)
fieldIdxMap[member.fieldDecl->getCanonicalDecl()] =
@@ -352,6 +408,9 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
// 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;
+ // Where the current run's storage member sits in members, so that a named
+ // occupant joining the run can promote it to data.
+ size_t runStorageIdx = 0;
for (; field != fieldEnd && field->isBitField(); ++field) {
// Zero-width bitfields end runs.
if (field->isZeroLengthBitField()) {
@@ -368,15 +427,22 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
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));
+ // the bitfields it contains get laid out. The run is only known one
+ // field at a time here, so the unit starts out holding no data and is
+ // promoted below when a named occupant lands in it.
+ runStorageIdx = members.size();
+ members.push_back(makeStorageInfo(bitsToCharUnits(startBitOffset), type,
+ cir::RecordMemberKind::Empty));
}
+ assert(members[runStorageIdx].offset == bitsToCharUnits(startBitOffset) &&
+ "runStorageIdx must name the current run's storage");
+ if (!field->isUnnamedBitField())
+ members[runStorageIdx].memberKind = cir::RecordMemberKind::Data;
// 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));
+ cir::RecordMemberKind::Data, *field));
}
return field;
}
@@ -548,11 +614,20 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
assert(getSize(type) == accessSize &&
"Unclipped access must be clipped");
}
- members.push_back(makeStorageInfo(beginOffset, type));
+ // An unnamed bit-field of any width occupies no ABI class, so a unit
+ // made up of nothing but those is declared storage holding no data.
+ const bool hasNamedOccupant = llvm::any_of(
+ llvm::make_range(begin, bestEnd),
+ [](const FieldDecl *fd) { return !fd->isUnnamedBitField(); });
+ members.push_back(makeStorageInfo(beginOffset, type,
+ hasNamedOccupant
+ ? cir::RecordMemberKind::Data
+ : cir::RecordMemberKind::Empty));
for (; begin != bestEnd; ++begin)
if (!begin->isZeroLengthBitField())
- members.push_back(MemberInfo(
- beginOffset, MemberInfo::InfoKind::Field, nullptr, *begin));
+ members.push_back(MemberInfo(beginOffset,
+ MemberInfo::InfoKind::Field, nullptr,
+ cir::RecordMemberKind::Data, *begin));
}
// Reset to start a new span.
field = bestEnd;
@@ -570,7 +645,7 @@ CIRRecordLowering::accumulateBitFields(RecordDecl::field_iterator field,
return field;
}
-void CIRRecordLowering::accumulateFields() {
+void CIRRecordLowering::accumulateFields(bool nonVirtualBaseType) {
for (RecordDecl::field_iterator field = recordDecl->field_begin(),
fieldEnd = recordDecl->field_end();
field != fieldEnd;) {
@@ -591,17 +666,34 @@ void CIRRecordLowering::accumulateFields() {
// problem with taking the address of one of these, so it is in practice
// not a horrifyingly problematic issue.
assert(!cir::MissingFeatures::noUniqueAddressLayout());
+ // Dropping the field leaves no member to mark, so its bytes read as
+ // padding. That is only sound when the field carries no ABI data
+ // either, which isEmptyFieldForLayout does not guarantee. Report the
+ // gap rather than claim an emptiness the record does not have. The base
+ // subobject lowering sees the same field, so only the complete object
+ // reports it.
+ if (!isEmptyFieldForABI(astContext, *field)) {
+ if (!nonVirtualBaseType)
+ cirGenTypes.getCGModule().errorNYI(
+ field->getSourceRange(),
+ "[[no_unique_address]] field that is empty for layout but holds "
+ "data for the ABI");
+ droppedFieldHoldingData = true;
+ }
++field;
} else {
// Use base subobject layout for potentially-overlapping fields,
// as it is done in RecordLayoutBuilder.
+ //
+ // The mark comes from isEmptyFieldForABI, not the isEmptyFieldForLayout
+ // above. Neither predicate subsumes the other.
members.push_back(MemberInfo(
bitsToCharUnits(getFieldBitOffset(*field)),
MemberInfo::InfoKind::Field,
field->isPotentiallyOverlapping()
? getStorageType(field->getType()->getAsCXXRecordDecl())
: getStorageType(*field),
- *field));
+ getFieldMemberKind(*field), *field));
++field;
}
}
@@ -679,7 +771,8 @@ void CIRRecordLowering::insertPadding() {
// Add the padding to the Members list and sort it.
for (const std::pair<CharUnits, CharUnits> &paddingPair : padding)
members.push_back(makeStorageInfo(paddingPair.first,
- getByteArrayType(paddingPair.second)));
+ getByteArrayType(paddingPair.second),
+ cir::RecordMemberKind::Pad));
llvm::stable_sort(members);
}
@@ -696,6 +789,18 @@ convertRecordArgPassingKind(RecordArgPassingKind kind) {
llvm_unreachable("unknown RecordArgPassingKind");
}
+/// Whether the member kinds on \p recordTy answer the record's ABI emptiness
+/// the same way the AST predicate does. A lowering that dropped a field
+/// holding data has no member left to carry that data, so it is exempt.
+[[maybe_unused]] static bool
+marksMatchABIEmptiness(const ASTContext &astContext, const RecordDecl *rd,
+ cir::RecordType recordTy, bool droppedFieldHoldingData) {
+ if (droppedFieldHoldingData)
+ return true;
+ return cir::allMembersNonData(recordTy) ==
+ isEmptyRecordForABI(astContext, astContext.getCanonicalTagType(rd));
+}
+
std::unique_ptr<CIRGenRecordLayout>
CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
CIRRecordLowering lowering(*this, rd, /*packed=*/false);
@@ -726,8 +831,8 @@ CIRGenTypes::computeRecordLayout(const RecordDecl *rd, cir::RecordType *ty) {
baseLowering.lower(/*nonVirtualBaseType=*/true);
std::string baseIdentifier = getRecordTypeName(rd, ".base");
baseTy = builder.getCompleteNamedRecordType(
- baseLowering.fieldTypes, baseLowering.packed, baseLowering.padde...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/215175
More information about the llvm-branch-commits
mailing list