[clang] [CIR] Rewrite `ConstRecordBuilder` to be based on layout (PR #206137)
Erich Keane via cfe-commits
cfe-commits at lists.llvm.org
Fri Jun 26 10:57:45 PDT 2026
https://github.com/erichkeane created https://github.com/llvm/llvm-project/pull/206137
Note: this is a pretty sizable change, and for that I apologize. Fortunately most of it is test changes, and the actual generation code is fairly managable. Unfortunately this couldn't really get any smaller, as the individual actual differences (unions, arrays, etc) all cascade.
The existing implementation of the ConstRecordBuilder mirrors the ConstStructBuilder from classic codegen. In both cases, this is a type that attempts to do a layout of the record/struct type in a byte-compatible way with the actual struct layout for the purposes of a constant.
This has a few problems:
First: it is another layout that we have to keep in sync with the main one.
Second: it adds an additional layer of complexity to the IR in a way that is mildly confusing.
Third: Most importantly, this attempts to be byte compatible, but NOT field compatible. This results in inconsistent field/padding/etc in the structs, so any time we access one via a member-wise GEP (rather than casting to i8, which we've been attempting to do!), we end up with mismatched GEPs, incorrect values, and in some cases, LLVM-IR Dialect verification errors (since doing a member-wise GEP will cause mismatched
types).
For these reasons, I believe this to be the 'correct' way forward.
This DOES result in a handful of visible changes:
-Bitfields are represented as their storage type, which can make them
seem odd.
-Arrays are no longer stored as a struct of values + zeroes. Arrays in CIR have 'trailing zeros' as a concept, so this is unnecessary for CIR anyway. These are unfortunately currently lowered to full-arrays in LLVM, so can result in massive 'zero' lists. However #205918 intends to fix this.
-"Inline" constants are now promoted to private "global" constants more often. This is because of an optimization we have in LoweringPrepare which does this if it sees a constant immediately put in an alloca. This didn't happen previously, because we would have to Bitcast it, losing that ability.
-Fewer bitcasts: We lose a bunch of bitcasts because the types match now!
Note: This is a pretty sizable change, so I suspect we'll see a handful of regressions here on things that I missed as they don't have test coverage that reproduces them. I will likely need to be responding to these, so if you notice something like this, please let me know!
ONE such regression that I'm aware of is Flexible Array Members. I have a patch in my local tree that fixes this, but it touches quite a bit of other stuff and makes this an even bigger patch, so I've left an NYI here. I'll do that as a followup after this is merged.
AI Disclosure: A few weeks ago I used Claude to generate #201430 which attempts to accomplish the same thing. When doing self-review on that, I found that it wasn't fit for task, and didn't accomplish what I wanted. Therefore, I implemented this from scratch myself, however, influenced by that patch at times. I also kept the tests from that implementation, as I found them useful to cover the cases I was concerned about.
>From 700dfac99eb22bf9d8c6b818c3417bc2bf7440d6 Mon Sep 17 00:00:00 2001
From: erichkeane <ekeane at nvidia.com>
Date: Wed, 24 Jun 2026 09:26:03 -0700
Subject: [PATCH] [CIR] Rewrite `ConstRecordBuilder` to be based on layout
Note: this is a pretty sizable change, and for that I apologize.
Fortunately most of it is test changes, and the actual generation code
is fairly managable. Unfortunately this couldn't really get any smaller,
as the individual actual differences (unions, arrays, etc) all cascade.
The existing implementation of the ConstRecordBuilder mirrors the
ConstStructBuilder from classic codegen. In both cases, this is a type
that attempts to do a layout of the record/struct type in a
byte-compatible way with the actual struct layout for the purposes of a
constant.
This has a few problems:
First: it is another layout that we have to keep in sync with the main one.
Second: it adds an additional layer of complexity to the IR in a way
that is mildly confusing.
Third: Most importantly, this attempts to be byte compatible, but NOT
field compatible. This results in inconsistent field/padding/etc in the
structs, so any time we access one via a member-wise GEP (rather than
casting to i8, which we've been attempting to do!), we end up with
mismatched GEPs, incorrect values, and in some cases, LLVM-IR Dialect
verification errors (since doing a member-wise GEP will cause mismatched
types).
For these reasons, I believe this to be the 'correct' way forward.
This DOES result in a handful of visible changes:
-Bitfields are represented as their storage type, which can make them
seem odd.
-Arrays are no longer stored as a struct of values + zeroes. Arrays in
CIR have 'trailing zeros' as a concept, so this is unnecessary for CIR
anyway. These are unfortunately currently lowered to full-arrays in
LLVM, so can result in massive 'zero' lists. However #205918 intends to
fix this.
-"Inline" constants are now promoted to private "global" constants more
often. This is because of an optimization we have in LoweringPrepare
which does this if it sees a constant immediately put in an alloca. This
didn't happen previously, because we would have to Bitcast it, losing
that ability.
-Fewer bitcasts: We lose a bunch of bitcasts because the types match
now!
Note: This is a pretty sizable change, so I suspect we'll see a handful
of regressions here on things that I missed as they don't have test
coverage that reproduces them. I will likely need to be responding to
these, so if you notice something like this, please let me know!
ONE such regression that I'm aware of is Flexible Array Members. I have
a patch in my local tree that fixes this, but it touches quite a bit of
other stuff and makes this an even bigger patch, so I've left an NYI here.
I'll do that as a followup after this is merged.
AI Disclosure: A few weeks ago I used Claude to generate #201430 which
attempts to accomplish the same thing. When doing self-review on that, I
found that it wasn't fit for task, and didn't accomplish what I wanted.
Therefore, I implemented this from scratch myself, however, influenced
by that patch at times. I also kept the tests from that implementation,
as I found them useful to cover the cases I was concerned about.
---
clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp | 1146 +++++------------
clang/lib/CIR/CodeGen/CIRGenRecordLayout.h | 9 +
clang/lib/CIR/Dialect/IR/CIRAttrs.cpp | 13 +
clang/lib/CIR/Lowering/LoweringHelpers.cpp | 11 +
clang/test/CIR/CodeGen/agg-expr-lvalue.c | 2 +-
clang/test/CIR/CodeGen/agg-init-constexpr.cpp | 13 +-
.../test/CIR/CodeGen/anonymous-nested-init.c | 36 +
clang/test/CIR/CodeGen/array.cpp | 43 +-
clang/test/CIR/CodeGen/bitfield-init-values.c | 28 +
clang/test/CIR/CodeGen/bitfields.cpp | 10 +
.../CIR/CodeGen/const-array-floating-point.c | 12 +-
clang/test/CIR/CodeGen/constant-inits.cpp | 22 +-
.../test/CIR/CodeGen/constexpr-ptr-offset.cpp | 17 +-
.../CIR/CodeGen/cross-reference-globals.c | 61 +
.../CodeGen/global-dtor-union-narrowed.cpp | 23 +-
clang/test/CIR/CodeGen/globals.cpp | 2 +-
clang/test/CIR/CodeGen/lambda.cpp | 4 +-
.../test/CIR/CodeGen/paren-list-agg-init.cpp | 29 +-
.../CIR/CodeGen/record-zero-init-padding.c | 32 +-
clang/test/CIR/CodeGen/replace-global.cpp | 11 +-
.../test/CIR/CodeGen/self-ref-temporaries.cpp | 15 +-
clang/test/CIR/CodeGen/struct-init.cpp | 12 +-
clang/test/CIR/CodeGen/union-agg-init.c | 11 +-
clang/test/CIR/CodeGenCXX/base-layout.cpp | 36 +
clang/test/CIR/CodeGenCXX/global-refs.cpp | 17 +-
clang/test/CIR/CodeGenCXX/sizeof-pack.cpp | 2 +-
clang/test/CIR/CodeGenCXX/zero_init_bases.cpp | 9 +-
.../compute-reduction-clause-default-ops.c | 45 +-
clang/test/CIR/IR/invalid-const-record.cir | 29 +
clang/test/CIR/Lowering/array.cpp | 2 +-
30 files changed, 685 insertions(+), 1017 deletions(-)
create mode 100644 clang/test/CIR/CodeGen/anonymous-nested-init.c
create mode 100644 clang/test/CIR/CodeGen/bitfield-init-values.c
create mode 100644 clang/test/CIR/CodeGen/cross-reference-globals.c
create mode 100644 clang/test/CIR/CodeGenCXX/base-layout.cpp
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
index eddb00c6fb2c1..3747eecb5d1fb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprConstant.cpp
@@ -43,830 +43,334 @@ using namespace clang::CIRGen;
//===----------------------------------------------------------------------===//
namespace {
-class ConstExprEmitter;
-
-static mlir::TypedAttr computePadding(CIRGenModule &cgm, CharUnits size) {
- mlir::Type eltTy = cgm.uCharTy;
- clang::CharUnits::QuantityType arSize = size.getQuantity();
- CIRGenBuilderTy &bld = cgm.getBuilder();
- if (size > CharUnits::One()) {
- SmallVector<mlir::Attribute> elts(arSize, cir::ZeroAttr::get(eltTy));
- return bld.getConstArray(mlir::ArrayAttr::get(bld.getContext(), elts),
- cir::ArrayType::get(eltTy, arSize));
- }
-
- return cir::ZeroAttr::get(eltTy);
-}
-
-static mlir::Attribute
-emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
- mlir::Type commonElementType, unsigned arrayBound,
- SmallVectorImpl<mlir::TypedAttr> &elements,
- mlir::TypedAttr filler);
-
-struct ConstantAggregateBuilderUtils {
- CIRGenModule &cgm;
- cir::CIRDataLayout dataLayout;
-
- ConstantAggregateBuilderUtils(CIRGenModule &cgm)
- : cgm(cgm), dataLayout{cgm.getModule()} {}
+namespace ConstRecordBuilder {
+// A class to manage the list of 'initializers' for building the record
+// initialization. This abstracts out the APValue and the InitListExpr.
+class RecordBuilderInitList {
+ unsigned initIdx = 0;
+ bool isUnion = false;
+ std::variant<APValue, const InitListExpr *> value;
- CharUnits getAlignment(const mlir::TypedAttr c) const {
- return CharUnits::fromQuantity(
- dataLayout.getAlignment(c.getType(), /*useABIAlign=*/true));
+ bool holdsExpr() const {
+ return std::holds_alternative<const InitListExpr *>(value);
}
- CharUnits getSize(mlir::Type ty) const {
- return CharUnits::fromQuantity(dataLayout.getTypeAllocSize(ty));
+ const Expr *getExpr() {
+ assert(holdsExpr());
+ return std::get<const InitListExpr *>(value)->getInit(initIdx);
}
- CharUnits getSize(const mlir::TypedAttr c) const {
- return getSize(c.getType());
+ mlir::Location getExprLoc(CIRGenModule &cgm) {
+ assert(holdsExpr());
+ return cgm.getLoc(std::get<const InitListExpr *>(value)->getBeginLoc());
}
- mlir::TypedAttr getPadding(CharUnits size) const {
- return computePadding(cgm, size);
+ const APValue getAPVal() {
+ assert(!holdsExpr());
+ if (isUnion)
+ return std::get<APValue>(value).getUnionValue();
+ return std::get<APValue>(value).getStructField(initIdx);
}
-};
-
-/// Incremental builder for an mlir::TypedAttr holding a record or array
-/// constant.
-class ConstantAggregateBuilder : private ConstantAggregateBuilderUtils {
- struct Element {
- Element(mlir::TypedAttr element, CharUnits offset)
- : element(element), offset(offset) {}
-
- mlir::TypedAttr element;
- /// Describes the offset of `element` within the constant.
- CharUnits offset;
- };
- /// The elements of the constant. The elements are kept in increasing offset
- /// order, and we ensure that there is no overlap:
- /// elements.offset[i+1] >= elements.offset[i] + getSize(elements.element[i])
- ///
- /// This may contain explicit padding elements (in order to create a
- /// natural layout), but need not. Gaps between elements are implicitly
- /// considered to be filled with undef.
- llvm::SmallVector<Element, 32> elements;
-
- /// The size of the constant (the maximum end offset of any added element).
- /// May be larger than the end of elems.back() if we split the last element
- /// and removed some trailing undefs.
- CharUnits size = CharUnits::Zero();
-
- /// This is true only if laying out elems in order as the elements of a
- /// non-packed LLVM struct will give the correct layout.
- bool naturalLayout = true;
-
- bool split(size_t index, CharUnits hint);
- std::optional<size_t> splitAt(CharUnits pos);
-
- static mlir::Attribute buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
- CharUnits startOffset, CharUnits size,
- bool naturalLayout, mlir::Type desiredTy,
- bool allowOversized);
public:
- ConstantAggregateBuilder(CIRGenModule &cgm)
- : ConstantAggregateBuilderUtils(cgm) {}
-
- /// Update or overwrite the value starting at \p offset with \c c.
- ///
- /// \param allowOverwrite If \c true, this constant might overwrite (part of)
- /// a constant that has already been added. This flag is only used to
- /// detect bugs.
- bool add(mlir::TypedAttr typedAttr, CharUnits offset, bool allowOverwrite);
-
- /// Update or overwrite the bits starting at \p offsetInBits with \p bits.
- bool addBits(llvm::APInt bits, uint64_t offsetInBits, bool allowOverwrite);
-
- /// Attempt to condense the value starting at \p offset to a constant of type
- /// \p desiredTy.
- void condense(CharUnits offset, mlir::Type desiredTy);
-
- /// Produce a constant representing the entire accumulated value, ideally of
- /// the specified type. If \p allowOversized, the constant might be larger
- /// than implied by \p desiredTy (eg, if there is a flexible array member).
- /// Otherwise, the constant will be of exactly the same size as \p desiredTy
- /// even if we can't represent it as that type.
- mlir::Attribute build(mlir::Type desiredTy, bool allowOversized) const {
- return buildFrom(cgm, elements, CharUnits::Zero(), size, naturalLayout,
- desiredTy, allowOversized);
+ RecordBuilderInitList(const RecordDecl *rd, APValue val)
+ : isUnion(rd->isUnion()), value(val) {}
+ RecordBuilderInitList(const RecordDecl *rd, const InitListExpr *ile)
+ : isUnion(rd->isUnion()), value(ile) {
+ assert(ile);
}
-};
-template <typename Container, typename Range = std::initializer_list<
- typename Container::value_type>>
-static void replace(Container &c, size_t beginOff, size_t endOff, Range vals) {
- assert(beginOff <= endOff && "invalid replacement range");
- llvm::replace(c, c.begin() + beginOff, c.begin() + endOff, vals);
-}
+ bool empty() const {
+ if (auto *const *ile = std::get_if<const InitListExpr *>(&value))
+ return initIdx >= (*ile)->getNumInits();
-bool ConstantAggregateBuilder::add(mlir::TypedAttr typedAttr, CharUnits offset,
- bool allowOverwrite) {
- // Common case: appending to a layout.
- if (offset >= size) {
- CharUnits align = getAlignment(typedAttr);
- CharUnits alignedSize = size.alignTo(align);
- if (alignedSize > offset || offset.alignTo(align) != offset) {
- naturalLayout = false;
- } else if (alignedSize < offset) {
- elements.emplace_back(getPadding(offset - size), size);
- }
- elements.emplace_back(typedAttr, offset);
- size = offset + getSize(typedAttr);
- return true;
- }
-
- // Uncommon case: constant overlaps what we've already created.
- std::optional<size_t> firstElemToReplace = splitAt(offset);
- if (!firstElemToReplace)
- return false;
+ // This branch is likely always true, but we guard against it being 'none'
+ // anyway.
+ if (isUnion)
+ return !std::get<APValue>(value).isUnion();
- CharUnits cSize = getSize(typedAttr);
- std::optional<size_t> lastElemToReplace = splitAt(offset + cSize);
- if (!lastElemToReplace)
- return false;
-
- assert((firstElemToReplace == lastElemToReplace || allowOverwrite) &&
- "unexpectedly overwriting field");
-
- Element newElt(typedAttr, offset);
- replace(elements, *firstElemToReplace, *lastElemToReplace, {newElt});
- size = std::max(size, offset + cSize);
- naturalLayout = false;
- return true;
-}
-
-bool ConstantAggregateBuilder::addBits(llvm::APInt bits, uint64_t offsetInBits,
- bool allowOverwrite) {
- const ASTContext &astContext = cgm.getASTContext();
- const uint64_t charWidth = astContext.getCharWidth();
- mlir::Type charTy = cgm.getBuilder().getUIntNTy(charWidth);
-
- // Offset of where we want the first bit to go within the bits of the
- // current char.
- unsigned offsetWithinChar = offsetInBits % charWidth;
-
- // We split bit-fields up into individual bytes. Walk over the bytes and
- // update them.
- for (CharUnits offsetInChars =
- astContext.toCharUnitsFromBits(offsetInBits - offsetWithinChar);
- /**/; ++offsetInChars) {
- // Number of bits we want to fill in this char.
- unsigned wantedBits =
- std::min((uint64_t)bits.getBitWidth(), charWidth - offsetWithinChar);
-
- // Get a char containing the bits we want in the right places. The other
- // bits have unspecified values.
- llvm::APInt bitsThisChar = bits;
- if (bitsThisChar.getBitWidth() < charWidth)
- bitsThisChar = bitsThisChar.zext(charWidth);
- if (cgm.getDataLayout().isBigEndian()) {
- // Figure out how much to shift by. We may need to left-shift if we have
- // less than one byte of Bits left.
- int shift = bits.getBitWidth() - charWidth + offsetWithinChar;
- if (shift > 0)
- bitsThisChar.lshrInPlace(shift);
- else if (shift < 0)
- bitsThisChar = bitsThisChar.shl(-shift);
- } else {
- bitsThisChar = bitsThisChar.shl(offsetWithinChar);
- }
- if (bitsThisChar.getBitWidth() > charWidth)
- bitsThisChar = bitsThisChar.trunc(charWidth);
-
- if (wantedBits == charWidth) {
- // Got a full byte: just add it directly.
- add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
- allowOverwrite);
- } else {
- // Partial byte: update the existing integer if there is one. If we
- // can't split out a 1-CharUnit range to update, then we can't add
- // these bits and fail the entire constant emission.
- std::optional<size_t> firstElemToUpdate = splitAt(offsetInChars);
- if (!firstElemToUpdate)
- return false;
- std::optional<size_t> lastElemToUpdate =
- splitAt(offsetInChars + CharUnits::One());
- if (!lastElemToUpdate)
- return false;
- assert(*lastElemToUpdate - *firstElemToUpdate < 2 &&
- "should have at most one element covering one byte");
-
- // Figure out which bits we want and discard the rest.
- llvm::APInt updateMask(charWidth, 0);
- if (cgm.getDataLayout().isBigEndian())
- updateMask.setBits(charWidth - offsetWithinChar - wantedBits,
- charWidth - offsetWithinChar);
- else
- updateMask.setBits(offsetWithinChar, offsetWithinChar + wantedBits);
- bitsThisChar &= updateMask;
- bool isNull = false;
- if (*firstElemToUpdate < elements.size()) {
- auto firstEltToUpdate =
- mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
- isNull = firstEltToUpdate && firstEltToUpdate.isNullValue();
- }
-
- if (*firstElemToUpdate == *lastElemToUpdate || isNull) {
- // All existing bits are either zero or undef.
- add(cir::IntAttr::get(charTy, bitsThisChar), offsetInChars,
- /*allowOverwrite*/ true);
- } else {
- cir::IntAttr ci =
- mlir::dyn_cast<cir::IntAttr>(elements[*firstElemToUpdate].element);
- // In order to perform a partial update, we need the existing bitwise
- // value, which we can only extract for a constant int.
- if (!ci)
- return false;
- // Because this is a 1-CharUnit range, the constant occupying it must
- // be exactly one CharUnit wide.
- assert(ci.getBitWidth() == charWidth && "splitAt failed");
- assert((!(ci.getValue() & updateMask) || allowOverwrite) &&
- "unexpectedly overwriting bitfield");
- bitsThisChar |= (ci.getValue() & ~updateMask);
- elements[*firstElemToUpdate].element =
- cir::IntAttr::get(charTy, bitsThisChar);
- }
- }
-
- // Stop if we've added all the bits.
- if (wantedBits == bits.getBitWidth())
- break;
-
- // Remove the consumed bits from Bits.
- if (!cgm.getDataLayout().isBigEndian())
- bits.lshrInPlace(wantedBits);
- bits = bits.trunc(bits.getBitWidth() - wantedBits);
-
- // The remaining bits go at the start of the following bytes.
- offsetWithinChar = 0;
+ return initIdx >= std::get<APValue>(value).getStructNumFields();
}
- return true;
-}
-
-/// Returns a position within elements such that all elements
-/// before the returned index end before pos and all elements at or after
-/// the returned index begin at or after pos. Splits elements as necessary
-/// to ensure this. Returns std::nullopt if we find something we can't split.
-std::optional<size_t> ConstantAggregateBuilder::splitAt(CharUnits pos) {
- if (pos >= size)
- return elements.size();
-
- while (true) {
- // Find the first element that starts after pos.
- Element *iter =
- llvm::upper_bound(elements, pos, [](CharUnits pos, const Element &elt) {
- return pos < elt.offset;
- });
-
- if (iter == elements.begin())
- return 0;
-
- size_t index = iter - elements.begin() - 1;
- const Element &elt = elements[index];
-
- // If we already have an element starting at pos, we're done.
- if (elt.offset == pos)
- return index;
-
- // Check for overlap with the element that starts before pos.
- CharUnits eltEnd = elt.offset + getSize(elt.element);
- if (eltEnd <= pos)
- return index + 1;
-
- // Try to decompose it into smaller constants.
- if (!split(index, pos))
- return std::nullopt;
+ const FieldDecl *getActiveUnionField() const {
+ if (holdsExpr())
+ return std::get<const InitListExpr *>(value)
+ ->getInitializedFieldInUnion();
+ return std::get<APValue>(value).getUnionField();
}
-}
-/// Split the constant at index, if possible. Return true if we did.
-/// Hint indicates the location at which we'd like to split, but may be
-/// ignored.
-bool ConstantAggregateBuilder::split(size_t index, CharUnits hint) {
- cgm.errorNYI("split constant at index");
- return false;
-}
+ // Return whether this is a field that should be skipped for one reason or
+ // another.
+ bool shouldSkip(const FieldDecl *fd) {
+ if (fd->isUnnamedBitField())
+ return true;
-void ConstantAggregateBuilder::condense(CharUnits offset,
- mlir::Type desiredTy) {
- CharUnits desiredSize = getSize(desiredTy);
+ if (holdsExpr() && isa_and_nonnull<NoInitExpr>(getExpr()))
+ return true;
- std::optional<size_t> firstElemToReplace = splitAt(offset);
- if (!firstElemToReplace)
- return;
- size_t first = *firstElemToReplace;
-
- std::optional<size_t> lastElemToReplace = splitAt(offset + desiredSize);
- if (!lastElemToReplace)
- return;
- size_t last = *lastElemToReplace;
-
- size_t length = last - first;
- if (length == 0)
- return;
-
- if (length == 1 && elements[first].offset == offset &&
- getSize(elements[first].element) == desiredSize) {
- cgm.errorNYI("re-wrapping single element records");
- return;
+ return false;
}
- // Build a new constant from the elements in the range.
- SmallVector<Element> subElems(elements.begin() + first,
- elements.begin() + last);
- mlir::Attribute replacement =
- buildFrom(cgm, subElems, offset, desiredSize,
- /*naturalLayout=*/false, desiredTy, false);
-
- // Replace the range with the condensed constant.
- Element newElt(mlir::cast<mlir::TypedAttr>(replacement), offset);
- replace(elements, first, last, {newElt});
-}
-
-mlir::Attribute
-ConstantAggregateBuilder::buildFrom(CIRGenModule &cgm, ArrayRef<Element> elems,
- CharUnits startOffset, CharUnits size,
- bool naturalLayout, mlir::Type desiredTy,
- bool allowOversized) {
- ConstantAggregateBuilderUtils utils(cgm);
-
- if (elems.empty())
- return cir::UndefAttr::get(desiredTy);
-
- // If we want an array type, see if all the elements are the same type and
- // appropriately spaced.
- if (mlir::isa<cir::ArrayType>(desiredTy)) {
- cgm.errorNYI("array aggregate constants");
- return {};
+ // Advance the iterator on a 'skipped' field. Note in the case of an
+ // init-list this doesn't advance if its an unnamed bitfield, as those aren't
+ // represented in the AST.
+ void advanceSkip(const FieldDecl *fd) {
+ assert(!isUnion);
+ if (holdsExpr())
+ if (fd->isUnnamedBitField())
+ return;
+ ++initIdx;
}
+ // Advance the iterator on a 'normal' field, which always just increments the
+ // index.
+ void advance() {
+ assert(!isUnion);
+ ++initIdx;
+ }
+
+ APValue getBase(unsigned idx) {
+ // We could potentially handle this with init-list, but we just skip it
+ // because classic codegen does. If we decide to, we'll probably have to do
+ // something where we get through the init-list elements to make this work
+ // right (sub-init-list?).
+ assert(!holdsExpr());
- // The size of the constant we plan to generate. This is usually just the size
- // of the initialized type, but in AllowOversized mode (i.e. flexible array
- // init), it can be larger.
- CharUnits desiredSize = utils.getSize(desiredTy);
- if (size > desiredSize) {
- assert(allowOversized && "elems are oversized");
- desiredSize = size;
+ return std::get<APValue>(value).getStructBase(idx);
}
- // The natural alignment of an unpacked CIR record with the given elements.
- CharUnits align = CharUnits::One();
- for (auto [e, offset] : elems)
- align = std::max(align, utils.getAlignment(e));
-
- // The natural size of an unpacked LLVM struct with the given elements.
- CharUnits alignedSize = size.alignTo(align);
-
- bool packed = false;
- bool padded = false;
-
- llvm::SmallVector<mlir::Attribute, 32> unpackedElems;
- if (desiredSize < alignedSize || desiredSize.alignTo(align) != desiredSize) {
- naturalLayout = false;
- packed = true;
- } else {
- // The natural layout would be too small. Add padding to fix it. (This
- // is ignored if we choose a packed layout.)
- unpackedElems.reserve(elems.size() + 1);
- llvm::transform(elems, std::back_inserter(unpackedElems),
- std::mem_fn(&Element::element));
- if (desiredSize > alignedSize)
- unpackedElems.push_back(utils.getPadding(desiredSize - size));
+ bool hasSideEffects(const ASTContext &ctx) {
+ if (holdsExpr() && getExpr()->HasSideEffects(ctx))
+ return true;
+ // APValue never has side effects.
+ return false;
}
- // If we don't have a natural layout, insert padding as necessary.
- // As we go, double-check to see if we can actually just emit Elems
- // as a non-packed record and do so opportunistically if possible.
- llvm::SmallVector<mlir::Attribute, 32> packedElems;
- packedElems.reserve(elems.size());
- if (!naturalLayout) {
- CharUnits sizeSoFar = CharUnits::Zero();
- for (auto [element, offset] : elems) {
- CharUnits align = utils.getAlignment(element);
- CharUnits naturalOffset = sizeSoFar.alignTo(align);
- CharUnits desiredOffset = offset - startOffset;
- assert(desiredOffset >= sizeSoFar && "elements out of order");
-
- if (desiredOffset != naturalOffset)
- packed = true;
- if (desiredOffset != sizeSoFar)
- packedElems.push_back(utils.getPadding(desiredOffset - sizeSoFar));
- packedElems.push_back(element);
- sizeSoFar = desiredOffset + utils.getSize(element);
- }
- // If we're using the packed layout, pad it out to the desired size if
- // necessary.
- if (packed) {
- assert(sizeSoFar <= desiredSize &&
- "requested size is too small for contents");
-
- if (sizeSoFar < desiredSize)
- packedElems.push_back(utils.getPadding(desiredSize - sizeSoFar));
+ mlir::Attribute emit(ConstantEmitter &emitter, QualType fieldTy) {
+ if (holdsExpr()) {
+ const Expr *e = getExpr();
+ return e ? emitter.tryEmitPrivateForMemory(e, fieldTy)
+ : emitter.emitNullForMemory(getExprLoc(emitter.cgm), fieldTy);
}
+ return emitter.tryEmitPrivateForMemory(getAPVal(), fieldTy);
}
-
- CIRGenBuilderTy &builder = cgm.getBuilder();
- auto arrAttr = mlir::ArrayAttr::get(builder.getContext(),
- packed ? packedElems : unpackedElems);
-
- cir::RecordType recordType = builder.getCompleteRecordType(arrAttr, packed);
- if (auto desired = mlir::dyn_cast<cir::RecordType>(desiredTy))
- if (desired.isLayoutIdentical(recordType))
- recordType = desired;
-
- return builder.getConstRecordOrZeroAttr(arrAttr, packed, padded, recordType);
-}
-
-//===----------------------------------------------------------------------===//
-// ConstRecordBuilder
-//===----------------------------------------------------------------------===//
-
-class ConstRecordBuilder {
- CIRGenModule &cgm;
- ConstantEmitter &emitter;
- ConstantAggregateBuilder &builder;
- CharUnits startOffset;
-
-public:
- static mlir::Attribute buildRecord(ConstantEmitter &emitter,
- InitListExpr *ile, QualType valTy);
- static mlir::Attribute buildRecord(ConstantEmitter &emitter,
- const APValue &value, QualType valTy);
- static bool updateRecord(ConstantEmitter &emitter,
- ConstantAggregateBuilder &constant, CharUnits offset,
- InitListExpr *updater);
-
-private:
- ConstRecordBuilder(ConstantEmitter &emitter,
- ConstantAggregateBuilder &builder, CharUnits startOffset)
- : cgm(emitter.cgm), emitter(emitter), builder(builder),
- startOffset(startOffset) {}
-
- bool appendField(const FieldDecl *field, uint64_t fieldOffset,
- mlir::TypedAttr initCst, bool allowOverwrite = false);
-
- bool appendBytes(CharUnits fieldOffsetInChars, mlir::TypedAttr initCst,
- bool allowOverwrite = false);
-
- bool appendBitField(const FieldDecl *field, uint64_t fieldOffset,
- cir::IntAttr ci, bool allowOverwrite = false);
-
- /// Applies zero-initialization to padding bytes before and within a field.
- /// \param layout The record layout containing field offset information.
- /// \param fieldNo The field index in the record.
- /// \param field The field declaration.
- /// \param allowOverwrite Whether to allow overwriting existing values.
- /// \param sizeSoFar The current size processed, updated by this function.
- /// \param zeroFieldSize Set to true if the field has zero size.
- /// \returns true on success, false if padding could not be applied.
- bool applyZeroInitPadding(const ASTRecordLayout &layout, unsigned fieldNo,
- const FieldDecl &field, bool allowOverwrite,
- CharUnits &sizeSoFar, bool &zeroFieldSize);
-
- /// Applies zero-initialization to trailing padding bytes in a record.
- /// \param layout The record layout containing size information.
- /// \param allowOverwrite Whether to allow overwriting existing values.
- /// \param sizeSoFar The current size processed.
- /// \returns true on success, false if padding could not be applied.
- bool applyZeroInitPadding(const ASTRecordLayout &layout, bool allowOverwrite,
- CharUnits &sizeSoFar);
-
- bool build(InitListExpr *ile, bool allowOverwrite);
- bool build(const APValue &val, const RecordDecl *rd, bool isPrimaryBase,
- const CXXRecordDecl *vTableClass, CharUnits baseOffset);
-
- mlir::Attribute finalize(QualType ty);
};
-bool ConstRecordBuilder::appendField(const FieldDecl *field,
- uint64_t fieldOffset,
- mlir::TypedAttr initCst,
- bool allowOverwrite) {
- const ASTContext &astContext = cgm.getASTContext();
-
- CharUnits fieldOffsetInChars = astContext.toCharUnitsFromBits(fieldOffset);
+mlir::Attribute updateBitfieldInit(CIRGenModule &cgm, cir::IntAttr existingVal,
+ cir::IntAttr newVal, bool isSigned,
+ const CIRGenBitFieldInfo &bfInfo) {
+ llvm::APInt result(bfInfo.storageSize, 0);
+ if (existingVal)
+ result = existingVal.getValue();
+
+ llvm::APInt curValue = newVal.getValue();
+ // Make sure we truncate (or properly extend) the existing value for the
+ // number of bits in the bitfield. The AST/Sema doesn't do a good job of
+ // making sure this is done.
+ if (isSigned)
+ curValue = curValue.sextOrTrunc(bfInfo.size);
+ else
+ curValue = curValue.zextOrTrunc(bfInfo.size);
- return appendBytes(fieldOffsetInChars, initCst, allowOverwrite);
-}
+ // Extend to the full storage size so we can shift/mask.
+ curValue = curValue.zext(bfInfo.storageSize);
-bool ConstRecordBuilder::appendBytes(CharUnits fieldOffsetInChars,
- mlir::TypedAttr initCst,
- bool allowOverwrite) {
- return builder.add(initCst, startOffset + fieldOffsetInChars, allowOverwrite);
-}
+ unsigned offset = bfInfo.offset;
+ if (cgm.getDataLayout().isBigEndian())
+ offset = bfInfo.storageSize - bfInfo.size - offset;
-bool ConstRecordBuilder::appendBitField(const FieldDecl *field,
- uint64_t fieldOffset, cir::IntAttr ci,
- bool allowOverwrite) {
- const CIRGenRecordLayout &rl =
- cgm.getTypes().getCIRGenRecordLayout(field->getParent());
- const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(field);
- llvm::APInt fieldValue = ci.getValue();
-
- // Promote the size of FieldValue if necessary
- // FIXME: This should never occur, but currently it can because initializer
- // constants are cast to bool, and because clang is not enforcing bitfield
- // width limits.
- if (info.size > fieldValue.getBitWidth())
- fieldValue = fieldValue.zext(info.size);
-
- // Truncate the size of FieldValue to the bit field size.
- if (info.size < fieldValue.getBitWidth())
- fieldValue = fieldValue.trunc(info.size);
-
- return builder.addBits(fieldValue,
- cgm.getASTContext().toBits(startOffset) + fieldOffset,
- allowOverwrite);
-}
+ curValue = curValue.shl(offset);
+ llvm::APInt mask(bfInfo.storageSize, 0);
+ mask.setBits(offset, offset + bfInfo.size);
-bool ConstRecordBuilder::applyZeroInitPadding(
- const ASTRecordLayout &layout, unsigned fieldNo, const FieldDecl &field,
- bool allowOverwrite, CharUnits &sizeSoFar, bool &zeroFieldSize) {
- uint64_t startBitOffset = layout.getFieldOffset(fieldNo);
- CharUnits startOffset =
- cgm.getASTContext().toCharUnitsFromBits(startBitOffset);
- if (sizeSoFar < startOffset) {
- if (!appendBytes(sizeSoFar, computePadding(cgm, startOffset - sizeSoFar),
- allowOverwrite))
- return false;
- }
+ result &= ~mask;
+ result |= curValue;
- if (!field.isBitField()) {
- CharUnits fieldSize =
- cgm.getASTContext().getTypeSizeInChars(field.getType());
- sizeSoFar = startOffset + fieldSize;
- zeroFieldSize = fieldSize.isZero();
- } else {
- const CIRGenRecordLayout &rl =
- cgm.getTypes().getCIRGenRecordLayout(field.getParent());
- const CIRGenBitFieldInfo &info = rl.getBitFieldInfo(&field);
- uint64_t endBitOffset = startBitOffset + info.size;
- sizeSoFar = cgm.getASTContext().toCharUnitsFromBits(endBitOffset);
- if (endBitOffset % cgm.getASTContext().getCharWidth() != 0)
- sizeSoFar++;
- zeroFieldSize = info.size == 0;
- }
- return true;
+ return cir::IntAttr::get(bfInfo.storageType, result);
}
-bool ConstRecordBuilder::applyZeroInitPadding(const ASTRecordLayout &layout,
- bool allowOverwrite,
- CharUnits &sizeSoFar) {
- CharUnits totalSize = layout.getSize();
- if (sizeSoFar < totalSize) {
- if (!appendBytes(sizeSoFar, computePadding(cgm, totalSize - sizeSoFar),
- allowOverwrite))
- return false;
- }
- sizeSoFar = totalSize;
- return true;
+mlir::Attribute
+setBitfieldInit(CIRGenModule &cgm, const CIRGenRecordLayout &cirLayout,
+ CIRGenBuilderTy &builder, const FieldDecl *field,
+ mlir::Attribute existingVal, mlir::Attribute newVal) {
+ const CIRGenBitFieldInfo &info = cirLayout.getBitFieldInfo(field);
+ auto intAttr = mlir::dyn_cast<cir::IntAttr>(newVal);
+ // This could alternatively be a 'bool' attr here, so do a quick fixup to
+ // get the value correctly initialized.
+ if (!intAttr) {
+ auto boolAttr = mlir::cast<cir::BoolAttr>(newVal);
+ intAttr = cir::IntAttr::get(
+ builder.getUIntNTy(1), llvm::APInt(/*numBits=*/1, boolAttr.getValue()));
+ }
+
+ return updateBitfieldInit(
+ cgm, dyn_cast_if_present<cir::IntAttr>(existingVal), intAttr,
+ field->getType()->isSignedIntegerOrEnumerationType(), info);
}
-bool ConstRecordBuilder::build(InitListExpr *ile, bool allowOverwrite) {
- RecordDecl *rd = ile->getType()->castAsRecordDecl();
- const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd);
+mlir::Attribute buildRecordHelper(ConstantEmitter &emitter,
+ const RecordDecl *rd,
+ const RecordDecl *vtableBaseTy,
+ RecordBuilderInitList inits, bool handleBases,
+ CharUnits offsetInDerived,
+ bool asBaseSubObj) {
+ CIRGenModule &cgm = emitter.cgm;
+ CIRGenBuilderTy &builder = cgm.getBuilder();
+ const CIRGenRecordLayout &cirLayout =
+ cgm.getTypes().getCIRGenRecordLayout(rd);
+ cir::RecordType recordTy = asBaseSubObj ? cirLayout.getBaseSubobjectCIRType()
+ : cirLayout.getCIRType();
+ // Unions in CIR are represented by all of their types, so we should be able
+ // to just initialize it with whatever the active field is.
+ if (rd->isUnion()) {
+ if (inits.empty())
+ return builder.getZeroInitAttr(recordTy);
+
+ const FieldDecl *activeField = inits.getActiveUnionField();
+ if (!activeField || activeField->isZeroSize(cgm.getASTContext()))
+ return builder.getZeroInitAttr(recordTy);
+
+ mlir::Attribute eltAttr = inits.emit(emitter, activeField->getType());
+ if (!eltAttr)
+ return {};
- // Bail out if we have base classes. We could support these, but they only
- // arise in C++1z where we will have already constant folded most interesting
- // cases. FIXME: There are still a few more cases we can handle this way.
- if (auto *cxxrd = dyn_cast<CXXRecordDecl>(rd))
- if (cxxrd->getNumBases())
- return false;
-
- const bool zeroInitPadding = cgm.shouldZeroInitPadding();
- bool zeroFieldSize = false;
- CharUnits sizeSoFar = CharUnits::Zero();
-
- unsigned elementNo = 0;
- for (auto [index, field] : llvm::enumerate(rd->fields())) {
-
- // If this is a union, skip all the fields that aren't being initialized.
- if (rd->isUnion() &&
- !declaresSameEntity(ile->getInitializedFieldInUnion(), field))
- continue;
+ if (activeField->isBitField())
+ eltAttr = setBitfieldInit(cgm, cirLayout, builder, activeField,
+ /*existingVal=*/{}, eltAttr);
- // Don't emit anonymous bitfields.
- if (field->isUnnamedBitField())
- continue;
+ return cir::ConstRecordAttr::get(recordTy, builder.getArrayAttr({eltAttr}));
+ }
- // Get the initializer. A record can include fields without initializers,
- // we just use explicit null values for them.
- Expr *init = nullptr;
- if (elementNo < ile->getNumInits())
- init = ile->getInit(elementNo++);
- if (isa_and_nonnull<NoInitExpr>(init))
- continue;
+ llvm::SmallVector<mlir::Attribute> elements(recordTy.getNumElements());
- // Zero-sized fields are not emitted, but their initializers may still
- // prevent emission of this record as a constant.
- if (field->isZeroSize(cgm.getASTContext())) {
- if (init->HasSideEffects(cgm.getASTContext()))
- return false;
- continue;
+ if (auto *cxxrd = dyn_cast<CXXRecordDecl>(rd)) {
+ const ASTRecordLayout &astLayout =
+ emitter.cgm.getASTContext().getASTRecordLayout(cxxrd);
+ if (astLayout.hasOwnVFPtr()) {
+ mlir::Value addrPtr = emitter.cgm.getCXXABI().getVTableAddressPoint(
+ BaseSubobject(cxxrd, offsetInDerived),
+ cast<CXXRecordDecl>(vtableBaseTy));
+ assert(!cir::MissingFeatures::addressPointerAuthInfo());
+ auto apOp = addrPtr.getDefiningOp<cir::VTableAddrPointOp>();
+ mlir::ArrayAttr indices = builder.getArrayAttr(
+ {builder.getI32IntegerAttr(apOp.getAddressPoint().getIndex()),
+ builder.getI32IntegerAttr(apOp.getAddressPoint().getOffset())});
+ elements[0] =
+ cir::GlobalViewAttr::get(cir::VPtrType::get(builder.getContext()),
+ apOp.getNameAttr(), indices);
}
- if (zeroInitPadding &&
- !applyZeroInitPadding(layout, index, *field, allowOverwrite, sizeSoFar,
- zeroFieldSize))
- return false;
-
- // When emitting a DesignatedInitUpdateExpr, a nested InitListExpr
- // represents additional overwriting of our current constant value, and not
- // a new constant to emit independently.
- if (allowOverwrite &&
- (field->getType()->isArrayType() || field->getType()->isRecordType())) {
- cgm.errorNYI(field->getSourceRange(), "designated init lists");
- return false;
- }
+ for (auto [idx, base] : llvm::enumerate(cxxrd->bases())) {
+ // Our init-list implementation here just skips bases because classic
+ // compiler does (see the comment in buildRecord). We perhaps COULD do
+ // this, but for now we'll skip them.
+ if (!handleBases)
+ return {};
- mlir::Attribute eltInitAttr =
- init ? emitter.tryEmitPrivateForMemory(init, field->getType())
- : emitter.emitNullForMemory(cgm.getLoc(ile->getSourceRange()),
- field->getType());
- if (!eltInitAttr)
- return false;
-
- mlir::TypedAttr eltInit = mlir::cast<mlir::TypedAttr>(eltInitAttr);
- if (!field->isBitField()) {
- // Handle non-bitfield members.
- if (!appendField(field, layout.getFieldOffset(index), eltInit,
- allowOverwrite))
- return false;
- // After emitting a non-empty field with [[no_unique_address]], we may
- // need to overwrite its tail padding.
- if (field->hasAttr<NoUniqueAddressAttr>())
- allowOverwrite = true;
- } else {
- // Otherwise we have a bitfield.
- if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
- if (!appendBitField(field, layout.getFieldOffset(index), constInt,
- allowOverwrite))
- return false;
- } else {
- // We are trying to initialize a bitfield with a non-trivial constant,
- // this must require run-time code.
- return false;
- }
- }
- }
+ if (base.isVirtual())
+ continue;
- return !zeroInitPadding ||
- applyZeroInitPadding(layout, allowOverwrite, sizeSoFar);
-}
+ const auto *baseDecl = base.getType()->castAsCXXRecordDecl();
-namespace {
-struct BaseInfo {
- BaseInfo(const CXXRecordDecl *decl, CharUnits offset, unsigned index)
- : decl(decl), offset(offset), index(index) {}
+ if (!cirLayout.hasNonVirtualBaseCIRField(baseDecl))
+ continue;
- const CXXRecordDecl *decl;
- CharUnits offset;
- unsigned index;
+ APValue baseValue = inits.getBase(idx);
- bool operator<(const BaseInfo &o) const { return offset < o.offset; }
-};
-} // namespace
+ const ASTRecordLayout &derivedLayout =
+ cgm.getASTContext().getASTRecordLayout(cxxrd);
+ CharUnits baseOff =
+ offsetInDerived + derivedLayout.getBaseClassOffset(baseDecl);
-bool ConstRecordBuilder::build(const APValue &val, const RecordDecl *rd,
- bool isPrimaryBase,
- const CXXRecordDecl *vTableClass,
- CharUnits offset) {
- const ASTRecordLayout &layout = cgm.getASTContext().getASTRecordLayout(rd);
- if (const CXXRecordDecl *cd = dyn_cast<CXXRecordDecl>(rd)) {
- // Add a vtable pointer, if we need one and it hasn't already been added.
- if (layout.hasOwnVFPtr()) {
- CIRGenBuilderTy &builder = cgm.getBuilder();
- cir::GlobalOp vtable =
- cgm.getCXXABI().getAddrOfVTable(vTableClass, CharUnits());
- clang::VTableLayout::AddressPointLocation addressPoint =
- cgm.getItaniumVTableContext()
- .getVTableLayout(vTableClass)
- .getAddressPoint(BaseSubobject(cd, offset));
- assert(!cir::MissingFeatures::addressPointerAuthInfo());
- mlir::ArrayAttr indices = builder.getArrayAttr({
- builder.getI32IntegerAttr(addressPoint.VTableIndex),
- builder.getI32IntegerAttr(addressPoint.AddressPointIndex),
- });
- auto vptrTy = cir::VPtrType::get(cgm.getBuilder().getContext());
- auto symbol = mlir::FlatSymbolRefAttr::get(vtable.getSymNameAttr());
- cir::GlobalViewAttr vtableInit =
- cir::GlobalViewAttr::get(vptrTy, symbol, indices);
- if (!appendBytes(offset, vtableInit))
- return false;
+ unsigned baseFieldIdx = cirLayout.getNonVirtualBaseCIRFieldNo(baseDecl);
+ elements[baseFieldIdx] = buildRecordHelper(
+ emitter, baseDecl, vtableBaseTy, RecordBuilderInitList(rd, baseValue),
+ handleBases, baseOff, /*asBaseSubObj=*/true);
}
- // Accumulate and sort bases, in order to visit them in address order, which
- // may not be the same as declaration order.
- SmallVector<BaseInfo> bases;
- bases.reserve(cd->getNumBases());
- for (auto [index, base] : llvm::enumerate(cd->bases())) {
- assert(!base.isVirtual() && "should not have virtual bases here");
- const CXXRecordDecl *bd = base.getType()->getAsCXXRecordDecl();
- CharUnits baseOffset = layout.getBaseClassOffset(bd);
- bases.push_back(BaseInfo(bd, baseOffset, index));
- }
-#ifdef EXPENSIVE_CHECKS
- assert(llvm::is_sorted(bases) && "bases not sorted by offset");
-#endif
-
- for (BaseInfo &base : bases) {
- bool isPrimaryBase = layout.getPrimaryBase() == base.decl;
- build(val.getStructBase(base.index), base.decl, isPrimaryBase,
- vTableClass, offset + base.offset);
+ if (cxxrd->getNumVBases()) {
+ cgm.errorNYI(cxxrd->getSourceRange(),
+ "buildRecordHelper: virtual base classes");
+ return {};
}
}
- uint64_t offsetBits = cgm.getASTContext().toBits(offset);
+ for (const FieldDecl *field : rd->fields()) {
+ // If we don't have any initializers left, we'll just zero-init below. This
+ // isn't perfectly accurate to classic compiler, since we are potentially
+ // zero-initing padding (instead of leaving it undef), but that is a
+ // complexity we can deal with later if we find it necessary.
+ if (inits.empty())
+ break;
- bool allowOverwrite = false;
- for (auto [index, field] : llvm::enumerate(rd->fields())) {
- // If this is a union, skip all the fields that aren't being initialized.
- if (rd->isUnion() && !declaresSameEntity(val.getUnionField(), field))
+ if (inits.shouldSkip(field)) {
+ inits.advanceSkip(field);
continue;
+ }
- // Don't emit anonymous bitfields or zero-sized fields.
- if (field->isUnnamedBitField() || field->isZeroSize(cgm.getASTContext()))
+ // If we didn't lay it out, there is nothing to initialize. This is
+ // either zero size or nothing at all. IF our init has side effects, we
+ // cannot const init this.
+ if (!cirLayout.hasCIRField(field)) {
+ if (inits.hasSideEffects(cgm.getASTContext()))
+ return {};
+ inits.advance();
continue;
-
- // Emit the value of the initializer.
- const APValue &fieldValue =
- rd->isUnion() ? val.getUnionValue() : val.getStructField(index);
- mlir::TypedAttr eltInit = mlir::cast<mlir::TypedAttr>(
- emitter.tryEmitPrivateForMemory(fieldValue, field->getType()));
- if (!eltInit)
- return false;
-
- if (!field->isBitField()) {
- // Handle non-bitfield members.
- if (!appendField(field, layout.getFieldOffset(index) + offsetBits,
- eltInit, allowOverwrite))
- return false;
- // After emitting a non-empty field with [[no_unique_address]], we may
- // need to overwrite its tail padding.
- if (field->hasAttr<NoUniqueAddressAttr>())
- allowOverwrite = true;
- } else {
- // Otherwise we have a bitfield.
- if (auto constInt = dyn_cast<cir::IntAttr>(eltInit)) {
- if (!appendBitField(field, layout.getFieldOffset(index) + offsetBits,
- constInt, allowOverwrite))
- return false;
- } else {
- // We are trying to initialize a bitfield with a non-trivial constant,
- // this must require run-time code.
- return false;
- }
}
- }
- return true;
-}
+ unsigned fieldIdx = cirLayout.getCIRFieldNo(field);
-mlir::Attribute ConstRecordBuilder::finalize(QualType type) {
- type = type.getNonReferenceType();
- RecordDecl *rd =
- type->castAs<clang::RecordType>()->getDecl()->getDefinitionOrSelf();
- mlir::Type valTy = cgm.convertType(type);
- return builder.build(valTy, rd->hasFlexibleArrayMember());
-}
+ mlir::Attribute eltAttr = inits.emit(emitter, field->getType());
+ inits.advance();
-mlir::Attribute ConstRecordBuilder::buildRecord(ConstantEmitter &emitter,
- InitListExpr *ile,
- QualType valTy) {
- ConstantAggregateBuilder constant(emitter.cgm);
- ConstRecordBuilder builder(emitter, constant, CharUnits::Zero());
+ if (!eltAttr)
+ return {};
- if (!builder.build(ile, /*allowOverwrite*/ false))
- return nullptr;
+ if (field->isBitField())
+ elements[fieldIdx] = setBitfieldInit(cgm, cirLayout, builder, field,
+ elements[fieldIdx], eltAttr);
+ else
+ elements[fieldIdx] = eltAttr;
+ }
- return builder.finalize(valTy);
+ // Anything we haven't initialized, we try to zero init. We could/should
+ // probably leave the padding as undef if !CGM.ZeroInitPadding, but that ends
+ // up being quite an additional bit of complexity (but could be implemented in
+ // the field searching above).
+ for (unsigned i = 0; i < elements.size(); ++i)
+ if (!elements[i]) {
+ elements[i] = builder.getZeroInitAttr(recordTy.getElementType(i));
+ if (!elements[i])
+ return {};
+ }
+
+ return builder.getConstRecordOrZeroAttr(builder.getArrayAttr(elements),
+ /*packed=*/recordTy.getPacked(),
+ /*padded=*/recordTy.getPadded(),
+ recordTy);
}
-mlir::Attribute ConstRecordBuilder::buildRecord(ConstantEmitter &emitter,
- const APValue &val,
- QualType valTy) {
- ConstantAggregateBuilder constant(emitter.cgm);
- ConstRecordBuilder builder(emitter, constant, CharUnits::Zero());
+mlir::Attribute buildRecord(ConstantEmitter &emitter, InitListExpr *ile,
+ QualType valTy) {
+ // Bail out if we have base classes. We could support these, but they only
+ // arise in C++1z where we will have already constant folded most
+ // interesting cases. FIXME: There are still a few more cases we can handle
+ // this way.
+ const bool handleBases = false;
+ const RecordDecl *rd = ile->getType()->castAsRecordDecl();
+ return buildRecordHelper(emitter, rd, rd, RecordBuilderInitList(rd, ile),
+ handleBases, CharUnits::Zero(),
+ /*asBaseSubObj=*/false);
+}
+mlir::Attribute buildRecord(ConstantEmitter &emitter, const APValue &val,
+ QualType valTy) {
const RecordDecl *rd =
valTy->castAs<clang::RecordType>()->getDecl()->getDefinitionOrSelf();
- const CXXRecordDecl *cd = dyn_cast<CXXRecordDecl>(rd);
- if (!builder.build(val, rd, false, cd, CharUnits::Zero()))
- return nullptr;
-
- return builder.finalize(valTy);
-}
-
-bool ConstRecordBuilder::updateRecord(ConstantEmitter &emitter,
- ConstantAggregateBuilder &constant,
- CharUnits offset, InitListExpr *updater) {
- return ConstRecordBuilder(emitter, constant, offset)
- .build(updater, /*allowOverwrite*/ true);
+ return buildRecordHelper(emitter, rd, rd, RecordBuilderInitList(rd, val),
+ /*handleBases=*/true, CharUnits::Zero(),
+ /*asBaseSubObj=*/false);
}
+} // namespace ConstRecordBuilder
//===----------------------------------------------------------------------===//
// ConstExprEmitter
@@ -1140,88 +644,6 @@ static QualType getNonMemoryType(CIRGenModule &cgm, QualType type) {
}
return type;
}
-
-static mlir::Attribute
-emitArrayConstant(CIRGenModule &cgm, mlir::Type desiredType,
- mlir::Type commonElementType, unsigned arrayBound,
- SmallVectorImpl<mlir::TypedAttr> &elements,
- mlir::TypedAttr filler) {
- CIRGenBuilderTy &builder = cgm.getBuilder();
-
- unsigned nonzeroLength = arrayBound;
- if (elements.size() < nonzeroLength && builder.isNullValue(filler))
- nonzeroLength = elements.size();
-
- if (nonzeroLength == elements.size()) {
- while (nonzeroLength > 0 &&
- builder.isNullValue(elements[nonzeroLength - 1]))
- --nonzeroLength;
- }
-
- if (nonzeroLength == 0)
- return cir::ZeroAttr::get(desiredType);
-
- const unsigned trailingZeroes = arrayBound - nonzeroLength;
-
- // Add a zeroinitializer array filler if we have lots of trailing zeroes.
- if (trailingZeroes >= 8) {
- assert(elements.size() >= nonzeroLength &&
- "missing initializer for non-zero element");
-
- if (commonElementType && nonzeroLength >= 8) {
- // If all the elements had the same type up to the trailing zeroes and
- // there are eight or more nonzero elements, emit a struct of two arrays
- // (the nonzero data and the zeroinitializer).
- SmallVector<mlir::Attribute> eles;
- eles.reserve(nonzeroLength);
- for (unsigned i = 0; i < nonzeroLength; ++i)
- eles.push_back(elements[i]);
- auto initial = cir::ConstArrayAttr::get(
- cir::ArrayType::get(commonElementType, nonzeroLength),
- mlir::ArrayAttr::get(builder.getContext(), eles));
- elements.resize(2);
- elements[0] = initial;
- } else {
- // Otherwise, emit a struct with individual elements for each nonzero
- // initializer, followed by a zeroinitializer array filler.
- elements.resize(nonzeroLength + 1);
- }
-
- mlir::Type fillerType =
- commonElementType
- ? commonElementType
- : mlir::cast<cir::ArrayType>(desiredType).getElementType();
- fillerType = cir::ArrayType::get(fillerType, trailingZeroes);
- elements.back() = cir::ZeroAttr::get(fillerType);
- commonElementType = nullptr;
- } else if (elements.size() != arrayBound) {
- elements.resize(arrayBound, filler);
-
- if (filler.getType() != commonElementType)
- commonElementType = {};
- }
-
- if (commonElementType) {
- SmallVector<mlir::Attribute> eles;
- eles.reserve(elements.size());
-
- for (const auto &element : elements)
- eles.push_back(element);
-
- return cir::ConstArrayAttr::get(
- cir::ArrayType::get(commonElementType, arrayBound),
- mlir::ArrayAttr::get(builder.getContext(), eles));
- }
-
- SmallVector<mlir::Attribute> eles;
- eles.reserve(elements.size());
- for (auto const &element : elements)
- eles.push_back(element);
-
- auto arrAttr = mlir::ArrayAttr::get(builder.getContext(), eles);
- return builder.getAnonConstRecord(arrAttr, /*packed=*/true);
-}
-
} // namespace
//===----------------------------------------------------------------------===//
@@ -1899,20 +1321,31 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
const unsigned numElements = value.getArraySize();
const unsigned numInitElts = value.getArrayInitializedElts();
- mlir::Attribute filler;
+ mlir::TypedAttr filler;
if (value.hasArrayFiller()) {
- filler = tryEmitPrivate(value.getArrayFiller(), arrayElementTy);
- if (!filler)
+ mlir::Attribute fillerTemp =
+ tryEmitPrivate(value.getArrayFiller(), arrayElementTy);
+ if (!fillerTemp)
+ return {};
+ filler = dyn_cast<mlir::TypedAttr>(fillerTemp);
+ if (!filler) {
+ cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array filler should "
+ "always be typed");
return {};
+ }
}
- SmallVector<mlir::TypedAttr, 16> elements;
- if (filler && builder.isNullValue(filler))
- elements.reserve(numInitElts + 1);
+ CIRGenBuilderTy &builder = cgm.getBuilder();
+ cir::ArrayType desiredType =
+ cast<cir::ArrayType>(cgm.convertType(destType));
+
+ llvm::SmallVector<mlir::Attribute> elts;
+ if (!filler || builder.isNullValue(filler))
+ elts.reserve(numInitElts);
else
- elements.reserve(numInitElts);
+ elts.reserve(numElements);
- mlir::Type commonElementType;
+ // Fill in the known values.
for (unsigned i = 0; i < numInitElts; ++i) {
const APValue &arrayElement = value.getArrayInitializedElt(i);
const mlir::Attribute element =
@@ -1920,23 +1353,28 @@ mlir::Attribute ConstantEmitter::tryEmitPrivate(const APValue &value,
if (!element)
return {};
- const mlir::TypedAttr elementTyped = mlir::cast<mlir::TypedAttr>(element);
- if (i == 0)
- commonElementType = elementTyped.getType();
- else if (elementTyped.getType() != commonElementType) {
- commonElementType = {};
- }
-
- elements.push_back(elementTyped);
+ elts.push_back(element);
}
- mlir::TypedAttr typedFiller = llvm::cast_or_null<mlir::TypedAttr>(filler);
- if (filler && !typedFiller)
- cgm.errorNYI("array filler should always be typed");
+ // If we have an actual value we have to insert for the filler, do so now.
+ if (filler && !builder.isNullValue(filler))
+ elts.insert(elts.end(), numElements - elts.size(), filler);
- mlir::Type desiredType = cgm.convertType(destType);
- return emitArrayConstant(cgm, desiredType, commonElementType, numElements,
- elements, typedFiller);
+ // Remove all null values at the end, so they become 'trailing zeroes'.
+ while (!elts.empty() && builder.isNullValue(elts.back()))
+ elts.pop_back();
+
+ if (elts.empty())
+ return cir::ZeroAttr::get(desiredType);
+
+ if (desiredType.getSize() == 0 && elts.size() > 0) {
+ cgm.errorNYI("ConstExprEmitter::tryEmitPrivate array type as flexible "
+ "array member");
+ return {};
+ }
+
+ return cir::ConstArrayAttr::get(
+ desiredType, mlir::ArrayAttr::get(builder.getContext(), elts));
}
case APValue::Vector: {
const QualType elementType =
diff --git a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
index c9364971e080c..9733d18f52ce8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
+++ b/clang/lib/CIR/CodeGen/CIRGenRecordLayout.h
@@ -184,11 +184,20 @@ class CIRGenRecordLayout {
return fieldIdxMap.lookup(fd);
}
+ bool hasCIRField(const clang::FieldDecl *fd) const {
+ fd = fd->getCanonicalDecl();
+ return fieldIdxMap.contains(fd);
+ }
+
unsigned getNonVirtualBaseCIRFieldNo(const CXXRecordDecl *rd) const {
assert(nonVirtualBases.count(rd) && "Invalid non-virtual base!");
return nonVirtualBases.lookup(rd);
}
+ bool hasNonVirtualBaseCIRField(const CXXRecordDecl *rd) const {
+ return nonVirtualBases.contains(rd);
+ }
+
/// Check whether this struct can be C++ zero-initialized
/// with a zeroinitializer.
bool isZeroInitializable() const { return zeroInitializable; }
diff --git a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
index eba5aec0ca142..b4d2bb01b2043 100644
--- a/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRAttrs.cpp
@@ -214,6 +214,19 @@ ConstRecordAttr::verify(function_ref<InFlightDiagnostic()> emitError,
if (!sTy)
return emitError() << "expected !cir.struct or !cir.union type";
+ // union record initializer is just a single element that has to match one of
+ // the fields in the union (the new ative member).
+ if (sTy.isUnion()) {
+ if (members.size() != 1)
+ return emitError() << "union constant must have exactly one element, got "
+ << members.size();
+ auto m = mlir::cast<mlir::TypedAttr>(members[0]);
+ if (!llvm::is_contained(sTy.getMembers(), m.getType()))
+ return emitError() << "union element type " << m.getType()
+ << " is not a member of " << sTy;
+ return success();
+ }
+
if (sTy.getMembers().size() != members.size())
return emitError() << "number of elements must match";
diff --git a/clang/lib/CIR/Lowering/LoweringHelpers.cpp b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
index f298095e56824..dfd8886cb8f1c 100644
--- a/clang/lib/CIR/Lowering/LoweringHelpers.cpp
+++ b/clang/lib/CIR/Lowering/LoweringHelpers.cpp
@@ -332,6 +332,17 @@ lowerConstRecordAttr(cir::ConstRecordAttr constRecord,
return std::nullopt;
loweredMembers.push_back(*lowered);
}
+
+ // For a union, the CIR representation is a single field. However, if the
+ // union has padding, we would have added that as an LLVM field, so make sure
+ // we have a corresponding undef for that spot.
+ if (auto unionTy = mlir::dyn_cast<cir::UnionType>(constRecord.getType());
+ unionTy && unionTy.getPadded()) {
+ assert(loweredMembers.size() == 1);
+ loweredMembers.push_back(
+ mlir::LLVM::UndefAttr::get(constRecord.getContext()));
+ }
+
return mlir::ArrayAttr::get(constRecord.getContext(), loweredMembers);
}
diff --git a/clang/test/CIR/CodeGen/agg-expr-lvalue.c b/clang/test/CIR/CodeGen/agg-expr-lvalue.c
index 51109d3082ab0..343821be6d2dd 100644
--- a/clang/test/CIR/CodeGen/agg-expr-lvalue.c
+++ b/clang/test/CIR/CodeGen/agg-expr-lvalue.c
@@ -21,7 +21,7 @@ void test_member_in_array(void) {
}
// CIR-DAG: cir.global "private" constant cir_private @[[LINE_CONST:.*]] = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i}> : !rec_Point, #cir.const_record<{#cir.int<3> : !s32i, #cir.int<4> : !s32i}> : !rec_Point}> : !rec_Line
-// CIR-DAG: cir.global "private" constant cir_private @[[MATRIX_CONST:.*]] = #cir.const_array<[#cir.const_array<[#cir.int<104> : !s8i, #cir.int<101> : !s8i, #cir.int<108> : !s8i, #cir.int<108> : !s8i, #cir.int<111> : !s8i, #cir.int<0> : !s8i]> : !cir.array<!s8i x 6>, #cir.const_array<[#cir.int<119> : !s8i, #cir.int<111> : !s8i, #cir.int<114> : !s8i, #cir.int<108> : !s8i, #cir.int<100> : !s8i, #cir.int<0> : !s8i]> : !cir.array<!s8i x 6>]>
+// CIR-DAG: cir.global "private" constant cir_private @[[MATRIX_CONST:.*]] = #cir.const_array<[#cir.const_array<[#cir.int<104> : !s8i, #cir.int<101> : !s8i, #cir.int<108> : !s8i, #cir.int<108> : !s8i, #cir.int<111> : !s8i], trailing_zeros> : !cir.array<!s8i x 6>, #cir.const_array<[#cir.int<119> : !s8i, #cir.int<111> : !s8i, #cir.int<114> : !s8i, #cir.int<108> : !s8i, #cir.int<100> : !s8i], trailing_zeros> : !cir.array<!s8i x 6>]>
// LLVM-DAG: @[[LINE_CONST:.*]] = private constant %struct.Line { %struct.Point { i32 1, i32 2 }, %struct.Point { i32 3, i32 4 } }
// LLVM-DAG: @[[MATRIX_CONST:.*]] = private constant [2 x [6 x i8]] {{.*}}
diff --git a/clang/test/CIR/CodeGen/agg-init-constexpr.cpp b/clang/test/CIR/CodeGen/agg-init-constexpr.cpp
index 7bf98318caad3..b16e20fc28d10 100644
--- a/clang/test/CIR/CodeGen/agg-init-constexpr.cpp
+++ b/clang/test/CIR/CodeGen/agg-init-constexpr.cpp
@@ -19,15 +19,18 @@ extern "C" void construct() {
WithCtor c(2,5);
}
+// CIR: cir.global "private" constant cir_private @__const.construct.c = #cir.const_record<{#cir.int<4> : !s32i, #cir.zero : !cir.array<!u8i x 4>, #cir.int<10> : !s64i, #cir.const_record<{#cir.int<5> : !s32i}> : !rec_HasVal, #cir.zero : !cir.array<!u8i x 4>}> : !rec_WithCtor
// CIR-LABEL: construct()
-// CIR-NEXT: %[[WC_ALLOCA:.*]] = cir.alloca {{.*}} : !cir.ptr<!rec_WithCtor>
-// CIR-NEXT: %[[CONST_VAL:.*]] = cir.const #cir.const_record<{#cir.int<4> : !s32i, #cir.int<10> : !s64i, #cir.const_record<{#cir.int<5> : !s32i}> : !rec_HasVal}>
-// CIR-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[WC_ALLOCA]]
-// CIR-NEXT: cir.store{{.*}}%[[CONST_VAL]], %[[BITCAST]]
+// CIR-NEXT: %[[WC_ALLOCA:.*]] = cir.alloca "c" {{.*}} : !cir.ptr<!rec_WithCtor>
+// CIR-NEXT: %[[GET_GLOBAL:.*]] = cir.get_global @__const.construct.c : !cir.ptr<!rec_WithCtor>
+// CIR-NEXT: cir.copy %[[GET_GLOBAL]] to %[[WC_ALLOCA]] : !cir.ptr<!rec_WithCtor>
+// CIR-NEXT: cir.return
+// LLVM: @__const.construct.c = private constant %struct.WithCtor <{ i32 4, [4 x i8] zeroinitializer, i64 10, %struct.HasVal { i32 5 }, [4 x i8] zeroinitializer }>
// LLVM-LABEL: construct()
// LLVM-NEXT: %[[WC_ALLOCA:.*]] = alloca %struct.WithCtor
-// LLVM-NEXT: store { i32, i64, %struct.HasVal } { i32 4, i64 10, %struct.HasVal { i32 5 } }, ptr %[[WC_ALLOCA]]
+// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr %1, ptr @__const.construct.c, i64 24, i1 false)
+// LLVM-NEXT: ret void
// OGCG-LABEL: construct()
// OGCG: %[[WC_ALLOCA:.*]] = alloca %struct.WithCtor
diff --git a/clang/test/CIR/CodeGen/anonymous-nested-init.c b/clang/test/CIR/CodeGen/anonymous-nested-init.c
new file mode 100644
index 0000000000000..6006f8b3ad48c
--- /dev/null
+++ b/clang/test/CIR/CodeGen/anonymous-nested-init.c
@@ -0,0 +1,36 @@
+// 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,LLVMCIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,OGCG
+
+// Anonymous struct as a field.
+struct StructWithAnon {
+ struct { int x; int y; } inner;
+ int z;
+};
+struct StructWithAnon g_anon_struct = { {1, 2}, 3 };
+// CIR: cir.global external @g_anon_struct = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i}> : !rec_anon2E0, #cir.int<3> : !s32i}> : !rec_StructWithAnon
+// LLVM: @g_anon_struct = global %struct.StructWithAnon { %struct.anon{{.*}} { i32 1, i32 2 }, i32 3 }
+
+struct StructWithAnonUnion {
+ union { int i; float f; } u;
+ int n;
+};
+struct StructWithAnonUnion g_anon_union = { {.f = 1.5f}, 42 };
+
+// CIR: cir.global external @g_anon_union = #cir.const_record<{#cir.const_record<{#cir.fp<1.500000e+00> : !cir.float}> : !rec_anon2E1, #cir.int<42> : !s32i}> : !rec_StructWithAnonUnion
+// Union lowering chooses to lower this as an int instead of a float since the storage type is 'int', but active type is 'float'.
+// LLVMCIR: @g_anon_union = global %struct.StructWithAnonUnion { %union{{.*}} { i32 1069547520 }, i32 42 }
+// OGCG: @g_anon_union = global { { float }, i32 } { { float } { float 1.500000e+00 }, i32 42 }
+
+struct StructWithAnonBitfields {
+ int leading;
+ struct { unsigned a : 4; unsigned b : 4; } bits;
+ int trailing;
+};
+struct StructWithAnonBitfields g_anon_bits = { 7, {0xA, 0x5}, 9 };
+
+// CIR: cir.global external @g_anon_bits = #cir.const_record<{#cir.int<7> : !s32i, #cir.const_record<{#cir.int<90> : !u8i, #cir.zero : !cir.array<!u8i x 3>}> : !rec_anon2E2, #cir.int<9> : !s32i}> : !rec_StructWithAnonBitfields
+// LLVM: @g_anon_bits = global %struct.StructWithAnonBitfields { i32 7, %struct.anon{{.*}} { i8 90, [3 x i8] zeroinitializer }, i32 9 }
diff --git a/clang/test/CIR/CodeGen/array.cpp b/clang/test/CIR/CodeGen/array.cpp
index 5d288ad91a576..835166da8c7c6 100644
--- a/clang/test/CIR/CodeGen/array.cpp
+++ b/clang/test/CIR/CodeGen/array.cpp
@@ -5,10 +5,10 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
-// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC2_ARR:.*]] = #cir.const_array<[#cir.int<5> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 2>
+// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC2_ARR:.*]] = #cir.const_array<[#cir.int<5> : !s32i], trailing_zeros> : !cir.array<!s32i x 2>
// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC3_ARR:.*]] = #cir.const_array<[#cir.int<5> : !s32i, #cir.int<6> : !s32i]> : !cir.array<!s32i x 2>
// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC4_ARR:.*]] = #cir.const_array<[#cir.const_array<[#cir.int<5> : !s32i]> : !cir.array<!s32i x 1>, #cir.const_array<[#cir.int<6> : !s32i]> : !cir.array<!s32i x 1>]> : !cir.array<!cir.array<!s32i x 1> x 2>
-// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC5_ARR:.*]] = #cir.const_array<[#cir.const_array<[#cir.int<5> : !s32i]> : !cir.array<!s32i x 1>, #cir.zero : !cir.array<!s32i x 1>]> : !cir.array<!cir.array<!s32i x 1> x 2>
+// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC5_ARR:.*]] = #cir.const_array<[#cir.const_array<[#cir.int<5> : !s32i]> : !cir.array<!s32i x 1>], trailing_zeros> : !cir.array<!cir.array<!s32i x 1> x 2>
// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[FUNC7_ARR:.*]] = #cir.zero : !cir.array<!cir.ptr<!s32i> x 1>
// CIR-DAG: cir.global "private"{{.*}}constant cir_private @[[COMPLEX_ARR:.*]] = #cir.const_array<[#cir.const_complex<#cir.fp<1.100000e+00> : !cir.float, #cir.fp<2.200000e+00> : !cir.float> : !cir.complex<!cir.float>, #cir.const_complex<#cir.fp<3.300000e+00> : !cir.float, #cir.fp<4.400000e+00> : !cir.float> : !cir.complex<!cir.float>]> : !cir.array<!cir.complex<!cir.float> x 2>
@@ -52,33 +52,31 @@ int dd[3][2] = {{1, 2}, {3, 4}, {5, 6}};
// OGCG: [i32 3, i32 4], [2 x i32] [i32 5, i32 6]]
int e[10] = {1, 2};
-// CIR: cir.global external @e = #cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.zero : !cir.array<!s32i x 8>}> : !rec_anon_struct
+// CIR: cir.global external @e = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i], trailing_zeros> : !cir.array<!s32i x 10>
-// LLVM: @e = global <{ i32, i32, [8 x i32] }> <{ i32 1, i32 2, [8 x i32] zeroinitializer }>
+// FIXME: we should figure out how to lower this with a 'trailing-zeros' type thing, like classic codegen.
+// LLVM: @e = global [10 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0]
// OGCG: @e = global <{ i32, i32, [8 x i32] }> <{ i32 1, i32 2, [8 x i32] zeroinitializer }>
int f[5] = {1, 2};
-// CIR: cir.global external @f = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 5>
+// CIR: cir.global external @f = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i], trailing_zeros> : !cir.array<!s32i x 5>
// LLVM: @f = global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0]
// OGCG: @f = global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0]
int g[16] = {1, 2, 3, 4, 5, 6, 7, 8};
-// CIR: cir.global external @g = #cir.const_record<{
+// CIR: cir.global external @g =
// CIR-SAME: #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i,
// CIR-SAME: #cir.int<3> : !s32i, #cir.int<4> : !s32i,
// CIR-SAME: #cir.int<5> : !s32i, #cir.int<6> : !s32i,
-// CIR-SAME: #cir.int<7> : !s32i, #cir.int<8> : !s32i]>
-// CIR-SAME: : !cir.array<!s32i x 8>,
-// CIR-SAME: #cir.zero : !cir.array<!s32i x 8>}> : !rec_anon_struct1
+// CIR-SAME: #cir.int<7> : !s32i, #cir.int<8> : !s32i], trailing_zeros>
+// CIR-SAME: : !cir.array<!s32i x 16>
-// LLVM: @g = global <{ [8 x i32], [8 x i32] }>
-// LLVM-SAME: <{ [8 x i32]
-// LLVM-SAME: [i32 1, i32 2, i32 3, i32 4,
-// LLVM-SAME: i32 5, i32 6, i32 7, i32 8],
-// LLVM-SAME: [8 x i32] zeroinitializer }>
+// LLVM: @g = global [16 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5,
+// LLVM-SAME: i32 6, i32 7, i32 8, i32 0, i32 0,
+// LLVM-SAME: i32 0, i32 0, i32 0, i32 0, i32 0, i32 0]
// OGCG: @g = global <{ [8 x i32], [8 x i32] }>
// OGCG-SAME: <{ [8 x i32]
@@ -90,19 +88,16 @@ int g[16] = {1, 2, 3, 4, 5, 6, 7, 8};
// a zero initializer array.
int h[16] = {1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0, 0};
-// CIR: cir.global external @h = #cir.const_record<{
+// CIR: cir.global external @h =
// CIR-SAME: #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i,
// CIR-SAME: #cir.int<3> : !s32i, #cir.int<4> : !s32i,
// CIR-SAME: #cir.int<5> : !s32i, #cir.int<6> : !s32i,
-// CIR-SAME: #cir.int<7> : !s32i, #cir.int<8> : !s32i]>
-// CIR-SAME: : !cir.array<!s32i x 8>,
-// CIR-SAME: #cir.zero : !cir.array<!s32i x 8>}> : !rec_anon_struct1
-
-// LLVM: @h = global <{ [8 x i32], [8 x i32] }>
-// LLVM-SAME: <{ [8 x i32]
-// LLVM-SAME: [i32 1, i32 2, i32 3, i32 4,
-// LLVM-SAME: i32 5, i32 6, i32 7, i32 8],
-// LLVM-SAME: [8 x i32] zeroinitializer }>
+// CIR-SAME: #cir.int<7> : !s32i, #cir.int<8> : !s32i], trailing_zeros>
+// CIR-SAME: : !cir.array<!s32i x 16>
+
+// LLVM: @h = global [16 x i32] [i32 1, i32 2, i32 3, i32 4, i32 5, i32 6,
+// LLVM-SAME: i32 7, i32 8, i32 0, i32 0, i32 0, i32 0,
+// LLVM-SAME: i32 0, i32 0, i32 0, i32 0]
// OGCG: @h = global <{ [8 x i32], [8 x i32] }>
// OGCG-SAME: <{ [8 x i32]
diff --git a/clang/test/CIR/CodeGen/bitfield-init-values.c b/clang/test/CIR/CodeGen/bitfield-init-values.c
new file mode 100644
index 0000000000000..ef659347ffdd6
--- /dev/null
+++ b/clang/test/CIR/CodeGen/bitfield-init-values.c
@@ -0,0 +1,28 @@
+// 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,LLVMCIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,OGCG
+
+struct B1 { unsigned int a : 3; unsigned int b : 5; };
+struct B1 b1 = { 0b101, 0b01111 };
+// CIR: cir.global external @b1 = #cir.const_record<{#cir.int<125> : !u8i, #cir.zero : !cir.array<!u8i x 3>}> : !rec_B1
+// LLVM: @b1 = global %struct.B1 { i8 125, [3 x i8] zeroinitializer }
+
+struct B2 { unsigned int a : 8; unsigned int b : 8; unsigned int c : 16; };
+struct B2 b2 = { 0xAA, 0xBB, 0xCCDD };
+// CIR: cir.global external @b2 = #cir.const_record<{#cir.int<3437083562> : !u32i}> : !rec_B2
+// LLVMCIR: @b2 = global %struct.B2 { i32 -857883734 }
+// OGCG: @b2 = global { i8, i8, i8, i8 } { i8 -86, i8 -69, i8 -35, i8 -52 }
+
+struct BP { unsigned int a : 3; unsigned int b : 5; int c; };
+struct BP bp = { 1, 2, 99 };
+// CIR: cir.global external @bp = #cir.const_record<{#cir.int<17> : !u8i, #cir.int<99> : !s32i}> : !rec_BP
+// LLVMCIR: @bp = global %struct.BP { i8 17, i32 99 }
+// OGCG: @bp = global { i8, [3 x i8], i32 } { i8 17, [3 x i8] zeroinitializer, i32 99 }
+
+struct BS { int a : 4; int b : 4; };
+struct BS bs = { -1, 3 };
+// CIR: cir.global external @bs = #cir.const_record<{#cir.int<63> : !u8i, #cir.zero : !cir.array<!u8i x 3>}> : !rec_BS
+// LLVM: @bs = global %struct.BS { i8 63, [3 x i8] zeroinitializer }
diff --git a/clang/test/CIR/CodeGen/bitfields.cpp b/clang/test/CIR/CodeGen/bitfields.cpp
index 689201c0742b0..2117d2848a531 100644
--- a/clang/test/CIR/CodeGen/bitfields.cpp
+++ b/clang/test/CIR/CodeGen/bitfields.cpp
@@ -27,6 +27,16 @@ typedef struct {
// LLVM-DAG: %struct.T = type { i8, i32 }
// OGCG-DAG: %struct.T = type { i8, i32 }
+union U { int x : 3; };
+const U u = {5};
+// CIR-DAG: cir.global "private" {{.*}}@_ZL1u = #cir.const_record<{#cir.int<5> : !u8i}> : !rec_U
+// LLVM-DAG: @_ZL1u = internal constant %union.U { i8 5, [3 x i8] undef }
+// OGCG-DAG: @_ZL1u = internal constant %union.U { i8 5, [3 x i8] undef }
+auto use() {
+ return u;
+}
+
+
void def() {
S s;
T t;
diff --git a/clang/test/CIR/CodeGen/const-array-floating-point.c b/clang/test/CIR/CodeGen/const-array-floating-point.c
index e09f7c2e83dfc..540ae041dac0f 100644
--- a/clang/test/CIR/CodeGen/const-array-floating-point.c
+++ b/clang/test/CIR/CodeGen/const-array-floating-point.c
@@ -13,13 +13,13 @@ __bf16 bf_arr[3] = {1.0, -2.0, 0.0};
float f_arr[3] = {1.0, -2.0, 0.0};
double d_arr[3] = {1.0, -2.0, 0.0};
-// CIR-DAG: cir.global external @ld_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.long_double<!cir.f80>, #cir.fp<-2.000000e+00> : !cir.long_double<!cir.f80>, #cir.fp<0.000000e+00> : !cir.long_double<!cir.f80>]> : !cir.array<!cir.long_double<!cir.f80> x 3>
+// CIR-DAG: cir.global external @ld_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.long_double<!cir.f80>, #cir.fp<-2.000000e+00> : !cir.long_double<!cir.f80>], trailing_zeros> : !cir.array<!cir.long_double<!cir.f80> x 3>
// CIR-DAG: cir.global external @ld_zero = #cir.zero : !cir.array<!cir.long_double<!cir.f80> x 4>
-// CIR-DAG: cir.global external @q_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.f128, #cir.fp<-2.000000e+00> : !cir.f128, #cir.fp<0.000000e+00> : !cir.f128]> : !cir.array<!cir.f128 x 3>
-// CIR-DAG: cir.global external @h_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.f16, #cir.fp<-2.000000e+00> : !cir.f16, #cir.fp<0.000000e+00> : !cir.f16]> : !cir.array<!cir.f16 x 3>
-// CIR-DAG: cir.global external @bf_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.bf16, #cir.fp<-2.000000e+00> : !cir.bf16, #cir.fp<0.000000e+00> : !cir.bf16]> : !cir.array<!cir.bf16 x 3>
-// CIR-DAG: cir.global external @f_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.float, #cir.fp<-2.000000e+00> : !cir.float, #cir.fp<0.000000e+00> : !cir.float]> : !cir.array<!cir.float x 3>
-// CIR-DAG: cir.global external @d_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.double, #cir.fp<-2.000000e+00> : !cir.double, #cir.fp<0.000000e+00> : !cir.double]> : !cir.array<!cir.double x 3>
+// CIR-DAG: cir.global external @q_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.f128, #cir.fp<-2.000000e+00> : !cir.f128], trailing_zeros> : !cir.array<!cir.f128 x 3>
+// CIR-DAG: cir.global external @h_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.f16, #cir.fp<-2.000000e+00> : !cir.f16], trailing_zeros> : !cir.array<!cir.f16 x 3>
+// CIR-DAG: cir.global external @bf_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.bf16, #cir.fp<-2.000000e+00> : !cir.bf16], trailing_zeros> : !cir.array<!cir.bf16 x 3>
+// CIR-DAG: cir.global external @f_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.float, #cir.fp<-2.000000e+00> : !cir.float], trailing_zeros> : !cir.array<!cir.float x 3>
+// CIR-DAG: cir.global external @d_arr = #cir.const_array<[#cir.fp<1.000000e+00> : !cir.double, #cir.fp<-2.000000e+00> : !cir.double], trailing_zeros> : !cir.array<!cir.double x 3>
// LLVM-DAG: @ld_arr = global [3 x x86_fp80] [x86_fp80 1.000000e+00, x86_fp80 -2.000000e+00, x86_fp80 0.000000e+00]
// LLVM-DAG: @ld_zero = global [4 x x86_fp80] zeroinitializer
diff --git a/clang/test/CIR/CodeGen/constant-inits.cpp b/clang/test/CIR/CodeGen/constant-inits.cpp
index 4f9e55843ff20..dfd724b2d1665 100644
--- a/clang/test/CIR/CodeGen/constant-inits.cpp
+++ b/clang/test/CIR/CodeGen/constant-inits.cpp
@@ -101,15 +101,11 @@ void function() {
constexpr static mixed_partial_bitfields p_bf3 = {1, 0, 1, 15};
}
-// Anonymous struct type definitions for bitfields
-// CIR-DAG: !rec_anon_struct = !cir.struct<{!u8i, !u8i, !u8i, !u8i}>
-// CIR-DAG: !rec_anon_struct1 = !cir.struct<{!u8i, !u8i, !cir.array<!u8i x 2>}>
-
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE1e = #cir.zero : !rec_empty
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE1s = #cir.const_record<{#cir.int<0> : !s32i, #cir.int<-1> : !s32i}> : !rec_simple
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE2p1 = #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, #cir.const_array<[#cir.int<99> : !s8i, #cir.int<88> : !s8i, #cir.int<77> : !s8i]> : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE2p2 = #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE3paa = #cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, #cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : !rec_packed_and_aligned
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE3paa = #cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, #cir.fp<3.000000e+00> : !cir.float, #cir.int<0> : !u8i}> : !rec_packed_and_aligned
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE5array = #cir.const_array<[#cir.const_record<{#cir.int<123> : !s32i, #cir.int<456> : !s32i, #cir.const_array<[#cir.int<11> : !s8i, #cir.int<22> : !s8i, #cir.int<33> : !s8i]> : !cir.array<!s8i x 3>, #cir.int<789> : !s32i}> : !rec_Point, #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i, #cir.zero : !cir.array<!s8i x 3>, #cir.int<40> : !s32i}> : !rec_Point]> : !cir.array<!rec_Point x 2>
@@ -117,13 +113,13 @@ void function() {
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE12packed_array = #cir.const_array<[#cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed, #cir.const_record<{#cir.int<123> : !s8i, #cir.int<456> : !s32i}> : !rec_packed]> : !cir.array<!rec_packed x 2>
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE9paa_array = #cir.const_array<[#cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, #cir.fp<3.000000e+00> : !cir.float, #cir.zero : !u8i}> : !rec_packed_and_aligned, #cir.zero : !rec_packed_and_aligned]> : !cir.array<!rec_packed_and_aligned x 2>
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE9paa_array = #cir.const_array<[#cir.const_record<{#cir.int<1> : !s16i, #cir.int<2> : !s8i, #cir.fp<3.000000e+00> : !cir.float, #cir.int<0> : !u8i}> : !rec_packed_and_aligned], trailing_zeros> : !cir.array<!rec_packed_and_aligned x 2>
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE6ba_bf1 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<170> : !u8i, #cir.int<52> : !u8i, #cir.int<18> : !u8i}> : !rec_anon_struct
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE6ba_bf2 = #cir.const_record<{#cir.int<255> : !u8i, #cir.int<127> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>}> : !rec_anon_struct1
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE6ba_bf1 = #cir.const_record<{#cir.int<305441535> : !u32i}> : !rec_byte_aligned_bitfields
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE6ba_bf2 = #cir.const_record<{#cir.int<32767> : !u16i, #cir.zero : !cir.array<!u8i x 2>}> : !rec_signed_byte_aligned_bitfields
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE6ba_bf3 = #cir.const_record<{#cir.int<42> : !u8i}> : !rec_single_byte_bitfield
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE5p_bf1 = #cir.const_record<{#cir.int<17> : !u8i, #cir.int<3> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 2>}> : !rec_anon_struct1
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE5p_bf2 = #cir.const_record<{#cir.int<127> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>}> : !rec_signed_partial_bitfields
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE5p_bf1 = #cir.const_record<{#cir.int<785> : !u16i, #cir.zero : !cir.array<!u8i x 2>}> : !rec_partial_bitfields
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE5p_bf2 = #cir.const_record<{#cir.int<127> : !u8i, #cir.zero : !cir.array<!u8i x 3>}> : !rec_signed_partial_bitfields
// CIR-DAG: cir.global "private" constant internal dso_local @_ZZ8functionvE5p_bf3 = #cir.const_record<{#cir.int<125> : !u8i}> : !rec_mixed_partial_bitfields
// CIR-LABEL: cir.func {{.*}} @_Z8functionv()
@@ -139,10 +135,10 @@ void function() {
// LLVM-DAG: @_ZZ8functionvE3paa = internal constant %struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>
// LLVM-DAG: @_ZZ8functionvE5array = internal constant [2 x %struct.Point] [%struct.Point { i32 123, i32 456, [3 x i8] c"\0B\16!", i32 789 }, %struct.Point { i32 10, i32 20, [3 x i8] zeroinitializer, i32 40 }]
// LLVM-DAG: @_ZZ8functionvE9paa_array = internal constant [2 x %struct.packed_and_aligned] [%struct.packed_and_aligned <{ i16 1, i8 2, float 3.000000e+00, i8 0 }>, %struct.packed_and_aligned zeroinitializer]
-// LLVM-DAG: @_ZZ8functionvE6ba_bf1 = internal constant { i8, i8, i8, i8 } { i8 -1, i8 -86, i8 52, i8 18 }
-// LLVM-DAG: @_ZZ8functionvE6ba_bf2 = internal constant { i8, i8, [2 x i8] } { i8 -1, i8 127, [2 x i8] zeroinitializer }
+// LLVM-DAG: @_ZZ8functionvE6ba_bf1 = internal constant %struct.byte_aligned_bitfields { i32 305441535 }
+// LLVM-dAG: @_ZZ8functionvE6ba_bf2 = internal constant %struct.signed_byte_aligned_bitfields { i16 32767, [2 x i8] zeroinitializer }
// LLVM-DAG: @_ZZ8functionvE6ba_bf3 = internal constant %struct.single_byte_bitfield { i8 42 }
-// LLVM-DAG: @_ZZ8functionvE5p_bf1 = internal constant { i8, i8, [2 x i8] } { i8 17, i8 3, [2 x i8] zeroinitializer }
+// LLVM-DAG: @_ZZ8functionvE5p_bf1 = internal constant %struct.partial_bitfields { i16 785, [2 x i8] zeroinitializer }
// LLVM-DAG: @_ZZ8functionvE5p_bf2 = internal constant %struct.signed_partial_bitfields { i8 127, [3 x i8] zeroinitializer }
// LLVM-DAG: @_ZZ8functionvE5p_bf3 = internal constant %struct.mixed_partial_bitfields { i8 125 }
diff --git a/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp b/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp
index cbe5fc8467293..5c4f0f13ef758 100644
--- a/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp
+++ b/clang/test/CIR/CodeGen/constexpr-ptr-offset.cpp
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o %t.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
-// RUN: FileCheck --check-prefix=LLVM --input-file=%t.ll %s
+// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
struct View {
const char *ptr;
@@ -18,7 +18,16 @@ void test() {
(void)v;
}
+// CIR: cir.global "private" constant cir_private @__const._Z4testv.v = #cir.const_record<{#cir.global_view<@"[[STR_NAME:.*]]", [2 : i32]> : !cir.ptr<!s8i>, #cir.int<3> : !s32i, #cir.zero : !cir.array<!u8i x 4>}> : !rec_View
+// CIR: cir.global "private" constant cir_private dso_local @"[[STR_NAME]]" = #cir.const_array<"hello" : !cir.array<!s8i x 5>, trailing_zeros> : !cir.array<!s8i x 6>
+// LLVMCIR: @__const._Z4testv.v = private constant %struct.View <{ ptr getelementptr inbounds nuw (i8, ptr @[[STR_NAME:.*]], i64 2), i32 3, [4 x i8] zeroinitializer }>
+// LLVMCIR: @[[STR_NAME]] = private {{.*}}constant [6 x i8] c"hello\00"
+
+// OGCG: @[[STR_NAME:.*]] = private {{.*}}constant [6 x i8] c"hello\00"
+// OGCG: @__const._Z4testv.v = private unnamed_addr constant { ptr, i32 } { ptr getelementptr (i8, ptr @.str, i64 2), i32 3 }
+
// CIR-LABEL: @_Z4testv
-// CIR: #cir.global_view<@{{.*}}str{{.*}}, [2 : i32]> : !cir.ptr<!s8i>
+// CIR: cir.get_global @__const._Z4testv.v : !cir.ptr<!rec_View>
-// LLVM: getelementptr{{.*}}(i8, ptr @{{.*}}str{{.*}}, i64 2)
+// LLVM-LABEL: @_Z4testv
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}@__const._Z4testv.v, i64 16, i1 false)
diff --git a/clang/test/CIR/CodeGen/cross-reference-globals.c b/clang/test/CIR/CodeGen/cross-reference-globals.c
new file mode 100644
index 0000000000000..fbf613123f85a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/cross-reference-globals.c
@@ -0,0 +1,61 @@
+// 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,LLVMCIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=LLVM,OGCG
+
+extern int b1;
+int *a1 = &b1;
+int b1 = 100;
+
+// CIR-DAG: cir.global external @a1 = #cir.global_view<@b1> : !cir.ptr<!s32i>
+// CIR-DAG: cir.global external @b1 = #cir.int<100> : !s32i
+// LLVM-DAG: @b1 = global i32 100
+// LLVM-DAG: @a1 = global ptr @b1
+
+struct E2 {};
+struct B2 { struct E2 e; int x; int y; };
+extern struct B2 b2;
+int *a2 = &b2.y;
+struct B2 b2 = { {}, 10, 20 };
+// CIR-DAG: cir.global external @b2 = #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s32i}> : !rec_B2
+// CIR-DAG: cir.global external @a2 = #cir.global_view<@b2, [1 : i32]> : !cir.ptr<!s32i>
+// LLVM-DAG: @b2 = global %struct.B2 { i32 10, i32 20 }
+// LLVM-DAG: @a2 = global ptr getelementptr {{.*}}(i8, ptr @b2, i64 4)
+
+struct E3 {};
+struct In3 { int u, v; };
+struct B3 {
+ struct E3 e;
+ int x;
+ struct In3 inner;
+ int *self; // forces 4 bytes alignment padding before self
+};
+extern struct B3 b3;
+int *a3 = &b3.inner.v;
+struct B3 b3 = { .x = 7, .self = (int *)&b3 };
+// CIR-DAG: cir.global external @b3 = #cir.const_record<{#cir.int<7> : !s32i, #cir.zero : !rec_In3, #cir.global_view<@b3> : !cir.ptr<!s32i>}> : !rec_B3
+// CIR-DAG: cir.global external @a3 = #cir.global_view<@b3, [1 : i32, 1 : i32]> : !cir.ptr<!s32i>
+// LLVMCIR-DAG: @b3 = global %struct.B3 { i32 7, %struct.In3 zeroinitializer, ptr @b3 }
+// OGCG-DAG: @b3 = global { i32, %struct.In3, [4 x i8], ptr } { i32 7, %struct.In3 zeroinitializer, [4 x i8] zeroinitializer, ptr @b3 }
+// LLVM-DAG: @a3 = global ptr getelementptr {{.*}}(i8, ptr @b3, i64 8)
+
+struct E4 {};
+struct In4 { int p, q; };
+struct B4 {
+ struct E4 e;
+ int x;
+ struct In4 inner;
+ int *self;
+};
+extern struct B4 b4_fwd;
+struct A4 { int *target; };
+struct A4 a4 = { .target = &b4_fwd.inner.q };
+struct B4 b4_fwd = { .x = 11, .self = (int *)&b4_fwd };
+// CIR-DAG: cir.global external @b4_fwd = #cir.const_record<{#cir.int<11> : !s32i, #cir.zero : !rec_In4, #cir.global_view<@b4_fwd> : !cir.ptr<!s32i>}> : !rec_B4
+// CIR-DAG: cir.global external @a4 = #cir.const_record<{#cir.global_view<@b4_fwd, [1 : i32, 1 : i32]> : !cir.ptr<!s32i>}> : !rec_A4
+// LLVMCIR-DAG: @b4_fwd = global %struct.B4 { i32 11, %struct.In4 zeroinitializer, ptr @b4_fwd }
+// OGCG-DAG: @b4_fwd = global { i32, %struct.In4, [4 x i8], ptr } { i32 11, %struct.In4 zeroinitializer, [4 x i8] zeroinitializer, ptr @b4_fwd }
+// LLVM-DAG: @a4 = global %struct.A4 { ptr getelementptr {{.*}}(i8, ptr @b4_fwd, i64 8) }
+
diff --git a/clang/test/CIR/CodeGen/global-dtor-union-narrowed.cpp b/clang/test/CIR/CodeGen/global-dtor-union-narrowed.cpp
index f251adbc0eabb..8575d2f8d721c 100644
--- a/clang/test/CIR/CodeGen/global-dtor-union-narrowed.cpp
+++ b/clang/test/CIR/CodeGen/global-dtor-union-narrowed.cpp
@@ -1,9 +1,9 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
// RUN: FileCheck --check-prefix=CIR --input-file=%t-before.cir %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -fclangir -emit-llvm %s -o %t-cir.ll
-// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s
+// RUN: FileCheck --check-prefix=LLVM,LLVMCIR --input-file=%t-cir.ll %s
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -mconstructor-aliases -emit-llvm %s -o %t.ll
-// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s
+// RUN: FileCheck --check-prefix=LLVM,OGCG --input-file=%t.ll %s
struct S {
struct Rep {
@@ -24,18 +24,13 @@ S g;
// before the dtor call.
// CIR: !rec_S = !cir.struct<"S"
-// CIR: cir.global external @g = #cir.zero : ![[NARROW_TY:rec_anon_struct[0-9]*]] dtor {
-// CIR: %[[ADDR:.+]] = cir.get_global @g : !cir.ptr<![[NARROW_TY]]>
-// CIR: %[[CAST:.+]] = cir.cast bitcast %[[ADDR]] : !cir.ptr<![[NARROW_TY]]> -> !cir.ptr<!rec_S>
-// CIR: cir.call @_ZN1SD2Ev(%[[CAST]]) : (!cir.ptr<!rec_S>) -> ()
+// CIR: cir.global external @g = #cir.zero : !rec_S dtor {
+// CIR: %[[ADDR:.+]] = cir.get_global @g : !cir.ptr<!rec_S>
+// CIR: cir.call @_ZN1SD2Ev(%[[ADDR]]) : (!cir.ptr<!rec_S>) -> ()
// CIR: }
-// LLVM: @g = global { { { [16 x i8] } } } zeroinitializer
-// LLVM: define internal void @__cxx_global_array_dtor(ptr noundef %[[A0:.*]])
-// LLVM: call void @_ZN1SD2Ev(ptr %[[A0]])
-// LLVM: define internal void @__cxx_global_var_init()
-// LLVM: call i32 @__cxa_atexit(ptr @__cxx_global_array_dtor, ptr @g, ptr @__dso_handle)
-
+// LLVMCIR: @g = global %struct.S zeroinitializer
// OGCG: @g = global { { { [16 x i8] } } } zeroinitializer
-// OGCG: define internal void @__cxx_global_var_init()
-// OGCG: call i32 @__cxa_atexit(ptr @_ZN1SD2Ev, ptr @g, ptr @__dso_handle)
+
+// LLVM: define internal void @__cxx_global_var_init()
+// LLVM: call i32 @__cxa_atexit(ptr @_ZN1SD2Ev, ptr @g, ptr @__dso_handle)
diff --git a/clang/test/CIR/CodeGen/globals.cpp b/clang/test/CIR/CodeGen/globals.cpp
index aa67be879e9d1..be6816a7e046c 100644
--- a/clang/test/CIR/CodeGen/globals.cpp
+++ b/clang/test/CIR/CodeGen/globals.cpp
@@ -44,6 +44,6 @@ bool bool_global = true;
bool boolArr_global[4] = {true, false, true, false};
-// CIR: cir.global external @boolArr_global = #cir.const_array<[#true, #false, #true, #false]> : !cir.array<!cir.bool x 4>
+// CIR: cir.global external @boolArr_global = #cir.const_array<[#true, #false, #true], trailing_zeros> : !cir.array<!cir.bool x 4>
// LLVM: @boolArr_global = global [4 x i8] c"\01\00\01\00", align 1
// OGCG: @boolArr_global = global [4 x i8] c"\01\00\01\00", align 1
diff --git a/clang/test/CIR/CodeGen/lambda.cpp b/clang/test/CIR/CodeGen/lambda.cpp
index a682209ad54e6..c2c24ca20eee9 100644
--- a/clang/test/CIR/CodeGen/lambda.cpp
+++ b/clang/test/CIR/CodeGen/lambda.cpp
@@ -13,7 +13,7 @@ void use_global_lambda() {
global_lambda();
}
-// CIR: cir.global "private" internal dso_local @global_lambda = #cir.undef : ![[REC_LAM_GLOBAL_LAMBDA:.*]] {alignment = 1 : i64}
+// CIR: cir.global "private" internal dso_local @global_lambda = #cir.zero : ![[REC_LAM_GLOBAL_LAMBDA:.*]] {alignment = 1 : i64}
// CIR: cir.func {{.*}} lambda internal private dso_local @_ZNK3$_0clEv(%[[THIS_ARG:.*]]: !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]> {{.*}})
// CIR: %[[THIS:.*]] = cir.alloca "this" {{.*}} init : !cir.ptr<!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>>
// CIR: cir.store %[[THIS_ARG]], %[[THIS]]
@@ -23,7 +23,7 @@ void use_global_lambda() {
// CIR: %[[LAMBDA:.*]] = cir.get_global @global_lambda : !cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]>
// CIR: cir.call @_ZNK3$_0clEv(%[[LAMBDA]]) : (!cir.ptr<![[REC_LAM_GLOBAL_LAMBDA]]> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> ()
-// LLVM: @global_lambda = internal global %[[REC_LAM_GLOBAL_LAMBDA:.*]] undef, align 1
+// LLVM: @global_lambda = internal global %[[REC_LAM_GLOBAL_LAMBDA:.*]] zeroinitializer, align 1
// LLVM: define internal void @"_ZNK3$_0clEv"(ptr {{.*}} %[[THIS_ARG:.*]])
// LLVM: %[[THIS_ADDR:.*]] = alloca ptr
// LLVM: store ptr %[[THIS_ARG]], ptr %[[THIS_ADDR]]
diff --git a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
index 2b64dd60af9df..3f03854c5cac5 100644
--- a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
+++ b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
@@ -133,23 +133,25 @@ constexpr auto a2 = static_cast<A>('c');
// LLVM-DAG: [[B1:@.*b1.*]] = internal constant [[STRUCT_B]] { [[STRUCT_A]] { i8 99, double 0.000000e+00 }, i32 0 }, align 8
// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2b1 = #cir.const_record<{#cir.const_record<{#cir.int<99> : !s8i, #cir.fp<0.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.int<0> : !s32i}> : ![[STRUCT_B]] {alignment = 8 : i64}
constexpr B b1(A('c'));
-// LLVM-DAG: [[C1:@.*c1.*]] = internal constant { [[STRUCT_A]], i32, [4 x i8], i8, double, i32 } { [[STRUCT_A]] { i8 99, double 0.000000e+00 }, i32 0, [4 x i8] {{.*}}, i8 3, double 2.000000e+00, i32 0 }, align
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2c1 = #cir.const_record<{#cir.const_record<{#cir.int<99> : !s8i, #cir.fp<0.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.int<0> : !s32i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 4>, #cir.int<3> : !s8i, #cir.fp<2.000000e+00> : !cir.double, #cir.int<0> : !s32i}>
+// LLVMCIR-DAG: [[C1:@.*c1.*]] = internal constant [[STRUCT_C]] <{ [[STRUCT_B]] { [[STRUCT_A]] { i8 99, double 0.000000e+00 }, i32 0 }, [[STRUCT_A]] { i8 3, double 2.000000e+00 }, i32 0, [4 x i8] zeroinitializer }>, align 8
+// OGCG-DAG: [[C1:@.*c1.*]] = internal constant { [[STRUCT_A]], i32, [4 x i8], i8, double, i32 } { [[STRUCT_A]] { i8 99, double 0.000000e+00 }, i32 0, [4 x i8] {{.*}}, i8 3, double 2.000000e+00, i32 0 }, align
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2c1 = #cir.const_record<{#cir.const_record<{#cir.const_record<{#cir.int<99> : !s8i, #cir.fp<0.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.int<0> : !s32i}> : !rec_B, #cir.const_record<{#cir.int<3> : !s8i, #cir.fp<2.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.int<0> : !s32i, #cir.zero : !cir.array<!u8i x 4>}> : !rec_C
constexpr C c1(b1, a1);
// LLVM-DAG: [[U1:@.*]] = internal constant {{.*}} { [[STRUCT_A]] { i8 1, double 1.000000e+00 } }, align 8
// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2u1 = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s8i, #cir.fp<1.000000e+00> : !cir.double}> : ![[STRUCT_A]]}> : !{{.*}}{alignment = 8 : i64}
constexpr U u1(A(1, 1));
-// LLVM-DAG: [[D1:@.*d1.*]] = internal constant { [[STRUCT_A]], [[STRUCT_A]], [8 x i8], [[STRUCT_A]] } { [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [8 x i8] {{.*}}, [[STRUCT_A]] zeroinitializer }, align 8
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2d1 = #cir.const_record<{#cir.const_record<{#cir.int<2> : !s8i, #cir.fp<2.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.const_record<{#cir.int<2> : !s8i, #cir.fp<2.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 8>, #cir.zero : ![[STRUCT_A]]}>
+// LLVMCIR-DAG: [[D1:@.*d1.*]] = internal constant [[STRUCT_D]] { [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [[STRUCT_A]] { i8 2, double 2.000000e+00 }, i8 0, [[STRUCT_A]] zeroinitializer }, align 8
+// OGCG-DAG: [[D1:@.*d1.*]] = internal constant { [[STRUCT_A]], [[STRUCT_A]], [8 x i8], [[STRUCT_A]] } { [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [[STRUCT_A]] { i8 2, double 2.000000e+00 }, [8 x i8] {{.*}}, [[STRUCT_A]] zeroinitializer }, align 8
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2d1 = #cir.const_record<{#cir.const_record<{#cir.int<2> : !s8i, #cir.fp<2.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.const_record<{#cir.int<2> : !s8i, #cir.fp<2.000000e+00> : !cir.double}> : ![[STRUCT_A]], #cir.int<0> : !u8i, #cir.zero : ![[STRUCT_A]]}> : !rec_D {alignment = 8 : i64} loc(#loc284)
constexpr D d1(A(2, 2));
// LLVM-DAG: [[ARR1:@.*arr1.*]] = internal constant [3 x i32] [i32 1, i32 2, i32 0], align 4
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZL4arr1 = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 3> {alignment = 4 : i64}
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL4arr1 = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i], trailing_zeros> : !cir.array<!s32i x 3> {alignment = 4 : i64}
constexpr int arr1[3](1, 2);
// LLVM-DAG: [[ARR4:@.*arr4.*]] = internal constant [1 x i32] [i32 1], align 4
// CIR-DAG: cir.global "private" constant internal dso_local @_ZL4arr4 = #cir.const_array<[#cir.int<1> : !s32i]> : !cir.array<!s32i x 1> {alignment = 4 : i64}
constexpr int arr4[](1);
// LLVM-DAG: [[ARR5:@.*arr5.*]] = internal constant [2 x i32] [i32 2, i32 0], align 4
-// CIR-DAG: cir.global "private" constant internal dso_local @_ZL4arr5 = #cir.const_array<[#cir.int<2> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 2> {alignment = 4 : i64}
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL4arr5 = #cir.const_array<[#cir.int<2> : !s32i], trailing_zeros> : !cir.array<!s32i x 2> {alignment = 4 : i64}
constexpr int arr5[2](2);
// LLVM: define dso_local {{.*}} @{{.*foo1.*}}
@@ -179,9 +181,8 @@ B foo2() {
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}[[C1]], i64 48, i1 false)
// CIR: cir.func {{.*}}@_Z4foo3v()
// CIR: %[[C_ALLOCA:.*]] = cir.alloca "__retval" align(8) : !cir.ptr<![[STRUCT_C]]>
-// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2c1
-// CIR: %[[GLOB_CAST:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!{{.*}}> -> !cir.ptr<![[STRUCT_C]]>
-// CIR: cir.copy %[[GLOB_CAST]] to %[[C_ALLOCA]] : !cir.ptr<![[STRUCT_C]]>
+// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2c1 : !cir.ptr<![[STRUCT_C]]>
+// CIR: cir.copy %[[GET_GLOB]] to %[[C_ALLOCA]] : !cir.ptr<![[STRUCT_C]]>
C foo3() {
return c1;
}
@@ -244,9 +245,8 @@ void foo4() {
// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}[[RETVAL]], ptr {{.*}}[[U1]], i64 16, i1 false)
// CIR-LABEL: cir.func no_inline dso_local @_Z4foo5v()
// CIR: %[[RET:.*]] = cir.alloca "__retval" align(8) : !cir.ptr<![[UNION_U]]>
-// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2u1 : !cir.ptr<!{{.*}}>
-// CIR: %[[GLOB_TO_U:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!{{.*}}> -> !cir.ptr<![[UNION_U]]>
-// CIR: cir.copy %[[GLOB_TO_U]] to %[[RET]] : !cir.ptr<![[UNION_U]]>
+// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2u1 : !cir.ptr<![[UNION_U]]>
+// CIR: cir.copy %[[GET_GLOB]] to %[[RET]] : !cir.ptr<![[UNION_U]]>
U foo5() {
return u1;
}
@@ -317,9 +317,8 @@ void foo7() {
// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}[[D1]], i64 56, i1 false)
// CIR-LABEL: cir.func no_inline dso_local @_Z4foo8v()
// CIR: %[[RET_ALLOCA:.*]] = cir.alloca "__retval" align(8) : !cir.ptr<![[STRUCT_D]]>
-// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2d1 :
-// CIR: %[[GLOB_CAST:.*]] = cir.cast bitcast %[[GET_GLOB]] : !cir.ptr<!{{.*}}> -> !cir.ptr<![[STRUCT_D]]>
-// CIR: cir.copy %[[GLOB_CAST]] to %[[RET_ALLOCA]] : !cir.ptr<![[STRUCT_D]]>
+// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2d1 : !cir.ptr<![[STRUCT_D]]>
+// CIR: cir.copy %[[GET_GLOB]] to %[[RET_ALLOCA]] : !cir.ptr<![[STRUCT_D]]>
D foo8() {
return d1;
}
diff --git a/clang/test/CIR/CodeGen/record-zero-init-padding.c b/clang/test/CIR/CodeGen/record-zero-init-padding.c
index 1a34e4911d3d0..9bb4df0d99e3b 100644
--- a/clang/test/CIR/CodeGen/record-zero-init-padding.c
+++ b/clang/test/CIR/CodeGen/record-zero-init-padding.c
@@ -35,30 +35,30 @@ void test_zero_init_padding(void) {
}
// Type definitions for anonymous structs with padding
-// CIR-DAG: !rec_anon_struct = !cir.struct<{!s8i, !u8i, !s16i, !cir.array<!u8i x 4>, !s64i}>
-// CIR-DAG: !rec_anon_struct1 = !cir.struct<{!s32i, !s8i, !cir.array<!u8i x 3>}>
-// CIR-DAG: !rec_anon_struct2 = !cir.struct<{!u8i, !cir.array<!u8i x 3>, !s32i}>
-// CIR-DAG: !rec_anon_struct3 = !cir.struct<{!s8i, !cir.array<!u8i x 3>, !s32i}>
+// CIR-DAG: !rec_bitfield_with_padding = !cir.struct<"bitfield_with_padding" {!u8i, !s32i}>
+// CIR-DAG: !rec_multiple_padding = !cir.struct<"multiple_padding" {!s8i, !s16i, !s64i}>
+// CIR-DAG: !rec_padding_after_field = !cir.struct<"padding_after_field" {!s8i, !s32i}>
+// CIR-DAG: !rec_tail_padding = !cir.struct<"tail_padding" {!s32i, !s8i}>
-// paf: char + 3 bytes padding + int -> uses !rec_anon_struct3
-// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.paf = #cir.const_record<{#cir.int<1> : !s8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>, #cir.int<42> : !s32i}> : !rec_anon_struct3
+// paf: char + 3 bytes padding + int
+// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.paf = #cir.const_record<{#cir.int<1> : !s8i, #cir.int<42> : !s32i}> : !rec_padding_after_field
-// bfp: unsigned bitfield byte + 3 bytes padding + int -> uses !rec_anon_struct2
-// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.bfp = #cir.const_record<{#cir.int<17> : !u8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>, #cir.int<99> : !s32i}> : !rec_anon_struct2
+// bfp: unsigned bitfield byte + 3 bytes padding + int
+// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.bfp = #cir.const_record<{#cir.int<17> : !u8i, #cir.int<99> : !s32i}> : !rec_bitfield_with_padding
-// tp: int + char + 3 bytes tail padding -> uses !rec_anon_struct1
-// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.tp = #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s8i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 3>}> : !rec_anon_struct1
+// tp: int + char + 3 bytes tail padding
+// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.tp = #cir.const_record<{#cir.int<10> : !s32i, #cir.int<20> : !s8i}> : !rec_tail_padding
-// mp: char + 1 byte padding + short + 4 bytes padding + long long -> uses !rec_anon_struct
-// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.mp = #cir.const_record<{#cir.int<5> : !s8i, #cir.zero : !u8i, #cir.int<10> : !s16i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 4>, #cir.int<100> : !s64i}> : !rec_anon_struct
+// mp: char + 1 byte padding + short + 4 bytes padding + long long
+// CIR-DAG: cir.global "private" constant internal dso_local @test_zero_init_padding.mp = #cir.const_record<{#cir.int<5> : !s8i, #cir.int<10> : !s16i, #cir.int<100> : !s64i}> : !rec_multiple_padding
// CIR-LABEL: cir.func {{.*}}@test_zero_init_padding
// CIR: cir.return
-// LLVM-DAG: @test_zero_init_padding.paf = internal constant { i8, [3 x i8], i32 } { i8 1, [3 x i8] zeroinitializer, i32 42 }
-// LLVM-DAG: @test_zero_init_padding.bfp = internal constant { i8, [3 x i8], i32 } { i8 17, [3 x i8] zeroinitializer, i32 99 }
-// LLVM-DAG: @test_zero_init_padding.tp = internal constant { i32, i8, [3 x i8] } { i32 10, i8 20, [3 x i8] zeroinitializer }
-// LLVM-DAG: @test_zero_init_padding.mp = internal constant { i8, i8, i16, [4 x i8], i64 } { i8 5, i8 0, i16 10, [4 x i8] zeroinitializer, i64 100 }
+// LLVM-DAG: @test_zero_init_padding.paf = internal constant %struct.padding_after_field { i8 1, i32 42 }
+// LLVM-DAG: @test_zero_init_padding.bfp = internal constant %struct.bitfield_with_padding { i8 17, i32 99 }
+// LLVM-DAG: @test_zero_init_padding.tp = internal constant %struct.tail_padding { i32 10, i8 20 }
+// LLVM-DAG: @test_zero_init_padding.mp = internal constant %struct.multiple_padding { i8 5, i16 10, i64 100 }
// LLVM-LABEL: define{{.*}} void @test_zero_init_padding
// LLVM: ret void
diff --git a/clang/test/CIR/CodeGen/replace-global.cpp b/clang/test/CIR/CodeGen/replace-global.cpp
index 2bd3780b03c08..e3cfe22667a7e 100644
--- a/clang/test/CIR/CodeGen/replace-global.cpp
+++ b/clang/test/CIR/CodeGen/replace-global.cpp
@@ -40,26 +40,25 @@ S gSMulti = {{0x50, 0x4B, 0x03, 0x04}};
char *get_ptr_to_element() { return ptrToElement; }
-// CIR: cir.global {{.*}} @_ZL2gS = #cir.const_record<{#cir.const_record<{#cir.int<80> : !s8i, #cir.int<75> : !s8i, #cir.int<3> : !s8i, #cir.int<4> : !s8i, #cir.zero : !cir.array<!s8i x 24>}> : !rec_anon_struct}> : !rec_anon_struct1
+// CIR: cir.global {{.*}} @_ZL2gS = #cir.const_record<{#cir.const_array<[#cir.int<80> : !s8i, #cir.int<75> : !s8i, #cir.int<3> : !s8i, #cir.int<4> : !s8i], trailing_zeros> : !cir.array<!s8i x 28>}> : !rec_S
// CIR: cir.global {{.*}} @ptrToS = #cir.global_view<@_ZL2gS> : !cir.ptr<!rec_S>
// CIR: cir.func {{.*}} @_ZN1RC2Ev
-// CIR: %[[GS_PTR:.*]] = cir.get_global @_ZL2gS : !cir.ptr<!rec_anon_struct1>
-// CIR: %[[GS_AS_S:.*]] = cir.cast bitcast %[[GS_PTR]] : !cir.ptr<!rec_anon_struct1> -> !cir.ptr<!rec_S>
-// CIR: %[[GS_AS_VOID:.*]] = cir.cast bitcast %[[GS_AS_S]] : !cir.ptr<!rec_S> -> !cir.ptr<!void>
+// CIR: %[[GS_PTR:.*]] = cir.get_global @_ZL2gS : !cir.ptr<!rec_S>
+// CIR: %[[GS_AS_VOID:.*]] = cir.cast bitcast %[[GS_PTR]] : !cir.ptr<!rec_S> -> !cir.ptr<!void>
// CIR: cir.call @_Z3usePv(%[[GS_AS_VOID]]) : (!cir.ptr<!void> {{.*}}) -> ()
// Multi-index case: ptrToElement = &gSMulti.arr[5] produces a global_view with
// multiple indices, exercising createNewGlobalView.
// CIR: cir.global {{.*}} @gSMulti = #cir.const_record<
-// CIR: cir.global {{.*}} @ptrToElement = #cir.global_view<@gSMulti, [0, 4, 1]> : !cir.ptr<
+// CIR: cir.global {{.*}} @ptrToElement = #cir.global_view<@gSMulti, [0 : i32, 5 : i32]> : !cir.ptr<!s8i>
// CIR: cir.func {{.*}} @_Z15use_as_constantv()
// CIR: %[[PTR_TO_S:.*]] = cir.alloca "ptrToS" {{.*}} init const : !cir.ptr<!cir.ptr<!rec_S>>
// CIR: %[[GLOBAL_PTR:.*]] = cir.const #cir.global_view<@_ZL2gS> : !cir.ptr<!rec_S>
// CIR: cir.store{{.*}} %[[GLOBAL_PTR]], %[[PTR_TO_S]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
-// LLVM: @_ZL2gS = internal global { <{ i8, i8, i8, i8, [24 x i8] }> } { <{ i8, i8, i8, i8, [24 x i8] }> <{ i8 80, i8 75, i8 3, i8 4, [24 x i8] zeroinitializer }> }, align 1
+// LLVM: @_ZL2gS = internal global %struct.S { [28 x i8] c"PK\03\04\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" }, align 1
// LLVM: @ptrToS = global ptr @_ZL2gS, align 8
// LLVM: @gSMulti = global {{.*}} align 1
// LLVM: @ptrToElement = global ptr getelementptr
diff --git a/clang/test/CIR/CodeGen/self-ref-temporaries.cpp b/clang/test/CIR/CodeGen/self-ref-temporaries.cpp
index e1e82620d2725..f31bbc03c2e75 100644
--- a/clang/test/CIR/CodeGen/self-ref-temporaries.cpp
+++ b/clang/test/CIR/CodeGen/self-ref-temporaries.cpp
@@ -1,6 +1,6 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir %s -o - | FileCheck %s --check-prefix=CIR
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM,LLVMCIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LLVM,OGCG
constexpr const int &normal = 42;
// CIR: cir.global "private" constant internal @_ZGR6normal_ = #cir.int<42> : !s32i
@@ -13,9 +13,11 @@ struct SelfRef {
int ints[3] = {1, 2, 3};
};
constexpr const SelfRef &sr = SelfRef();
-// CIR: cir.global "private" constant internal @_ZGR2sr_ = #cir.const_record<{#cir.global_view<@_ZGR2sr_, [1 : i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>}>
+// CIR: cir.global "private" constant internal @_ZGR2sr_ = #cir.const_record<{#cir.global_view<@_ZGR2sr_, [1 : i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>, #cir.zero : !cir.array<!u8i x 4>}> : !rec_SelfRef
// CIR: cir.global constant external @sr = #cir.global_view<@_ZGR2sr_> : !cir.ptr<!rec_SelfRef>
-// LLVM: @_ZGR2sr_ = {{.*}}constant { ptr, [3 x i32] } { ptr getelementptr {{.*}}(i8, ptr @_ZGR2sr_, i64 8), [3 x i32] [i32 1, i32 2, i32 3] }, align 8
+// LLVMCIR: @_ZGR2sr_ = {{.*}}constant %struct.SelfRef <{ ptr getelementptr inbounds nuw (i8, ptr @_ZGR2sr_, i64 8), [3 x i32] [i32 1, i32 2, i32 3], [4 x i8] zeroinitializer }>, align 8
+// OGCG: @_ZGR2sr_ = {{.*}}constant { ptr, [3 x i32] } { ptr getelementptr {{.*}}(i8, ptr @_ZGR2sr_, i64 8), [3 x i32] [i32 1, i32 2, i32 3] }, align 8
+
// LLVM: @sr = constant ptr @_ZGR2sr_, align 8
struct MultiSelfRef {
@@ -25,8 +27,9 @@ struct MultiSelfRef {
};
constexpr const MultiSelfRef &msr = MultiSelfRef();
-// CIR: cir.global "private" constant internal @_ZGR3msr_ = #cir.const_record<{#cir.global_view<@_ZGR3msr_, [2 : i32]> : !cir.ptr<!s32i>, #cir.global_view<@_ZGR3msr_, [2 : i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>}>
+// CIR: cir.global "private" constant internal @_ZGR3msr_ = #cir.const_record<{#cir.global_view<@_ZGR3msr_, [2 : i32]> : !cir.ptr<!s32i>, #cir.global_view<@_ZGR3msr_, [2 : i32]> : !cir.ptr<!s32i>, #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i]> : !cir.array<!s32i x 3>, #cir.zero : !cir.array<!u8i x 4>}> : !rec_MultiSelfRef
// CIR: cir.global constant external @msr = #cir.global_view<@_ZGR3msr_> : !cir.ptr<!rec_MultiSelfRef>
-// LLVM: @_ZGR3msr_ = {{.*}}constant { ptr, ptr, [3 x i32] } { ptr getelementptr {{.*}}(i8, ptr @_ZGR3msr_, i64 16), ptr getelementptr {{.*}}(i8, ptr @_ZGR3msr_, i64 16), [3 x i32] [i32 1, i32 2, i32 3] }, align 8
+// LLVMCIR: @_ZGR3msr_ = internal constant %struct.MultiSelfRef <{ ptr getelementptr inbounds nuw (i8, ptr @_ZGR3msr_, i64 16), ptr getelementptr inbounds nuw (i8, ptr @_ZGR3msr_, i64 16), [3 x i32] [i32 1, i32 2, i32 3], [4 x i8] zeroinitializer }>, align 8
+// OGCG: @_ZGR3msr_ = {{.*}}constant { ptr, ptr, [3 x i32] } { ptr getelementptr {{.*}}(i8, ptr @_ZGR3msr_, i64 16), ptr getelementptr {{.*}}(i8, ptr @_ZGR3msr_, i64 16), [3 x i32] [i32 1, i32 2, i32 3] }, align 8
// LLVM: @msr = constant ptr @_ZGR3msr_, align 8
diff --git a/clang/test/CIR/CodeGen/struct-init.cpp b/clang/test/CIR/CodeGen/struct-init.cpp
index fe83719ec054e..1d01584906308 100644
--- a/clang/test/CIR/CodeGen/struct-init.cpp
+++ b/clang/test/CIR/CodeGen/struct-init.cpp
@@ -19,8 +19,8 @@ BitfieldStruct overlapping_init = { 3, 2, 1 };
// This is unintuitive. The bitfields are initialized using a struct of constants
// that maps to the bitfields but splits the value into bytes.
-// CIR: cir.global external @overlapping_init = #cir.const_record<{#cir.int<35> : !u8i, #cir.int<0> : !u8i, #cir.int<4> : !u8i, #cir.int<0> : !u8i}> : !rec_anon_struct
-// LLVM: @overlapping_init = global { i8, i8, i8, i8 } { i8 35, i8 0, i8 4, i8 0 }
+// CIR: cir.global external @overlapping_init = #cir.const_record<{#cir.int<262179> : !u32i}> : !rec_BitfieldStruct
+// LLVM: @overlapping_init = global %struct.BitfieldStruct { i32 262179 }
// OGCG: @overlapping_init = global { i8, i8, i8, i8 } { i8 35, i8 0, i8 4, i8 0 }
struct S {
@@ -50,14 +50,14 @@ struct StructWithFieldInitFromConst {
StructWithFieldInitFromConst swfifc = {};
-// CIR: cir.global external @swfifc = #cir.zero : !rec_anon_struct
-// LLVM: @swfifc = global { i8, i8, i32 } zeroinitializer, align 4
+// CIR: cir.global external @swfifc = #cir.zero : !rec_StructWithFieldInitFromConst
+// LLVM: @swfifc = global %struct.StructWithFieldInitFromConst zeroinitializer, align 4
// OGCG: @swfifc = global { i8, i8, i32 } zeroinitializer, align 4
StructWithFieldInitFromConst swfifc2 = { 2 };
-// CIR: cir.global external @swfifc2 = #cir.const_record<{#cir.int<2> : !u8i, #cir.int<0> : !u8i, #cir.int<2> : !s32i}> : !rec_anon_struct
-// LLVM: @swfifc2 = global { i8, i8, i32 } { i8 2, i8 0, i32 2 }, align 4
+// CIR: cir.global external @swfifc2 = #cir.const_record<{#cir.int<2> : !u16i, #cir.int<2> : !s32i}> : !rec_StructWithFieldInitFromConst
+// LLVM: @swfifc2 = global %struct.StructWithFieldInitFromConst { i16 2, i32 2 }, align 4
// OGCG: @swfifc2 = global { i8, i8, i32 } { i8 2, i8 0, i32 2 }, align 4
diff --git a/clang/test/CIR/CodeGen/union-agg-init.c b/clang/test/CIR/CodeGen/union-agg-init.c
index ccf61d55b1226..5e32480d71d6b 100644
--- a/clang/test/CIR/CodeGen/union-agg-init.c
+++ b/clang/test/CIR/CodeGen/union-agg-init.c
@@ -10,8 +10,11 @@ typedef union vec3 {
double component[3];
} vec3;
+// LLVMCIR: @__const.ret_outer.__retval = {{.*}}%struct.outer { %union.needs_padding zeroinitializer, i32 1 }
// OGCG: @__const.ret_outer.o = {{.*}}{ { i32, [4 x i8] }, i32, [4 x i8] } { { i32, [4 x i8] } zeroinitializer, i32 1, [4 x i8] zeroinitializer }
+// CIR: cir.global "private" constant cir_private @__const.ret_outer.__retval = #cir.const_record<{#cir.zero : !rec_needs_padding, #cir.int<1> : !s32i}> : !rec_outer
+
// In C mode, this does do zero padding.
vec3 ret_vec3() {
// CIR-LABEL: ret_vec3
@@ -54,12 +57,10 @@ struct outer ret_outer() {
// CIR-LABEL: ret_outer
// CIR: %[[RET_ALLOCA:.*]] = cir.alloca "__retval" {{.*}} init : !cir.ptr<!rec_outer>
- // CIR: %[[BITCAST:.*]] = cir.cast bitcast %0 : !cir.ptr<!rec_outer> -> !cir.ptr<!{{.*}}>
- // CIR: %[[RECORD:.*]] = cir.const #cir.const_record<{#cir.zero : !{{.*}}, #cir.int<1> : !s32i, #cir.const_array<[#cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i, #cir.zero : !u8i]> : !cir.array<!u8i x 4>}>
- // CIR: cir.store {{.*}}%[[RECORD]], %[[BITCAST]]
+ // CIR: %[[GET_GLOB:.*]] = cir.get_global @__const.ret_outer.__retval : !cir.ptr<!rec_outer>
+ // CIR: cir.copy %[[GET_GLOB]] to %[[RET_ALLOCA]] : !cir.ptr<!rec_outer>
// LLVM-LABEL: ret_outer
// LLVM: %[[RET_ALLOCA:.*]] = alloca %struct.outer
- // LLVMCIR: store { { i32, [4 x i8] }, i32, [4 x i8] } { { i32, [4 x i8] } zeroinitializer, i32 1, [4 x i8] zeroinitializer }, ptr %[[RET_ALLOCA]]
- // OGCG: call void @llvm.memcpy{{.*}}(ptr{{.*}}%[[RET_ALLOCA]], ptr {{.*}}@__const.ret_outer.o, i64 16, i1 false)
+ // LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}%[[RET_ALLOCA]], ptr {{.*}}@__const.ret_outer.{{.*}}, i64 16, i1 false)
}
diff --git a/clang/test/CIR/CodeGenCXX/base-layout.cpp b/clang/test/CIR/CodeGenCXX/base-layout.cpp
new file mode 100644
index 0000000000000..62d2a1e8b285f
--- /dev/null
+++ b/clang/test/CIR/CodeGenCXX/base-layout.cpp
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -std=c++20 -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++20 -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++20 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+struct V1 { virtual void v1(); };
+struct V2 { virtual void v2(); };
+struct V3 { virtual void v3(); };
+
+struct B : V1, V2 {};
+
+struct D : V3, B {};
+
+constinit D d{};
+// CIR: cir.global external @d = #cir.const_record<{#cir.const_record<{#cir.global_view<@_ZTV1D, [0 : i32, 2 : i32]> : !cir.vptr}> : !rec_V3, #cir.const_record<{#cir.const_record<{#cir.global_view<@_ZTV1D, [1 : i32, 2 : i32]> : !cir.vptr}> : !rec_V1, #cir.const_record<{#cir.global_view<@_ZTV1D, [2 : i32, 2 : i32]> : !cir.vptr}> : !rec_V2}> : !rec_B}> : !rec_D
+// LLVM: @d = global %struct.D { %struct.V3 { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 16) }, %struct.B { %struct.V1 { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 40) }, %struct.V2 { ptr getelementptr inbounds nuw (i8, ptr @_ZTV1D, i64 64) } } }
+// OGCG: @d = global { ptr, ptr, ptr } { ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr], [3 x ptr] }, ptr @_ZTV1D, i32 0, i32 0, i32 2), ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr], [3 x ptr] }, ptr @_ZTV1D, i32 0, i32 1, i32 2), ptr getelementptr inbounds inrange(-16, 8) ({ [3 x ptr], [3 x ptr], [3 x ptr] }, ptr @_ZTV1D, i32 0, i32 2, i32 2) }
+
+
+struct Base {
+ int i;
+ char c;
+ constexpr Base(int i, char c) : i(i), c(c) {}
+};
+struct Derived : Base {
+ char d;
+ constexpr Derived(int i, char c, char d) : Base(i, c), d(d) {}
+};
+
+Derived gd {1, 2, 3};
+// CIR: cir.global external @gd = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s8i}> : !rec_Base2Ebase, #cir.int<3> : !s8i, #cir.zero : !cir.array<!u8i x 2>}> : !rec_Derived
+// LLVM: @gd = global %struct.Derived { %struct.Base.base <{ i32 1, i8 2 }>, i8 3, [2 x i8] zeroinitializer }
+// OGCG: @gd = global { i32, i8, i8 } { i32 1, i8 2, i8 3 }
+
diff --git a/clang/test/CIR/CodeGenCXX/global-refs.cpp b/clang/test/CIR/CodeGenCXX/global-refs.cpp
index 93a342ec19caf..307900d06cd0d 100644
--- a/clang/test/CIR/CodeGenCXX/global-refs.cpp
+++ b/clang/test/CIR/CodeGenCXX/global-refs.cpp
@@ -1,8 +1,8 @@
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before.cir
// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR,CIR-BEFORE
// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR,CIR-AFTER
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM
-// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -fclangir -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM,LLVMCIR
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=LLVM,OGCG
struct DefCtor{};
struct WithCtor{
@@ -32,17 +32,22 @@ const int &constGlobalIntRef = 5;
// LLVM: @constGlobalIntRef = constant ptr @_ZGR17constGlobalIntRef_, align 8
DefCtor defCtor{};
-// CIR: cir.global external @defCtor = #cir.undef : !rec_DefCtor {alignment = 1 : i64}
-// LLVM: @defCtor = global %struct.DefCtor undef, align 1
+// FIXME(cir): Classic-codegen leaves this as undef, but we don't differentiate
+// between padding and not defined fields in lowering. IF this ends up being
+// necessary, we should do it here.
+// CIR: cir.global external @defCtor = #cir.zero : !rec_DefCtor {alignment = 1 : i64}
+// LLVMCIR: @defCtor = global %struct.DefCtor zeroinitializer, align 1
+// OGCG: @defCtor = global %struct.DefCtor undef, align 1
DefCtor &defCtorRef = defCtor;
// CIR: cir.global constant external @defCtorRef = #cir.global_view<@defCtor> : !cir.ptr<!rec_DefCtor> {alignment = 8 : i64}
// LLVM: @defCtorRef = constant ptr @defCtor, align 8
const DefCtor &constDefCtorRef{};
-// CIR: cir.global "private" constant internal @_ZGR15constDefCtorRef_ = #cir.undef : !rec_DefCtor {alignment = 1 : i64}
+// CIR: cir.global "private" constant internal @_ZGR15constDefCtorRef_ = #cir.zero : !rec_DefCtor {alignment = 1 : i64}
// CIR: cir.global constant external @constDefCtorRef = #cir.global_view<@_ZGR15constDefCtorRef_> : !cir.ptr<!rec_DefCtor> {alignment = 8 : i64}
-// LLVM: @_ZGR15constDefCtorRef_ = {{.*}}constant %struct.DefCtor undef, align 1
+// LLVMCIR: @_ZGR15constDefCtorRef_ = {{.*}}constant %struct.DefCtor zeroinitializer, align 1
+// OGCG: @_ZGR15constDefCtorRef_ = {{.*}}constant %struct.DefCtor undef, align 1
// LLVM: @constDefCtorRef = constant ptr @_ZGR15constDefCtorRef_, align 8
WithCtor withCtor{};
diff --git a/clang/test/CIR/CodeGenCXX/sizeof-pack.cpp b/clang/test/CIR/CodeGenCXX/sizeof-pack.cpp
index c951725198dce..b2bbdef57b7e5 100644
--- a/clang/test/CIR/CodeGenCXX/sizeof-pack.cpp
+++ b/clang/test/CIR/CodeGenCXX/sizeof-pack.cpp
@@ -25,7 +25,7 @@ void test() {
foo<>();
foo<S1, S2, S3>();
}
-// CIR-DAG: cir.global "private" constant cir_private @__const._Z3fooIJ2S12S22S3EEDav.values = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.int<0> : !s32i]> : !cir.array<!s32i x 4>
+// CIR-DAG: cir.global "private" constant cir_private @__const._Z3fooIJ2S12S22S3EEDav.values = #cir.const_array<[#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i], trailing_zeros> : !cir.array<!s32i x 4>
// LLVM-DAG: @__const._Z3fooIJ2S12S22S3EEDav.values = private constant [4 x i32] [i32 1, i32 2, i32 3, i32 0]
// OGCG-DAG: @__const._Z3fooIJ2S12S22S3EEDav.values = private {{.*}}constant [4 x i32] [i32 1, i32 2, i32 3, i32 0]
// CIR-DAG: cir.global "private" constant cir_private @__const._Z3fooIJEEDav.values = #cir.zero : !cir.array<!s32i x 1>
diff --git a/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp b/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
index 48a76adf6079b..c41fa0d7be0a1 100644
--- a/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
+++ b/clang/test/CIR/CodeGenCXX/zero_init_bases.cpp
@@ -1,8 +1,8 @@
// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fclangir -emit-cir -fcxx-exceptions -fexceptions -mmlir --mlir-print-ir-before=cir-cxxabi-lowering -o %t.cir 2> %t-before.cir
// RUN: FileCheck %s --input-file=%t-before.cir --check-prefixes=CIR,CIR-BEFORE
// RUN: FileCheck %s --input-file=%t.cir --check-prefixes=CIR,CIR-AFTER
-// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fclangir -emit-llvm -fcxx-exceptions -fexceptions -o - | FileCheck %s --check-prefixes=LLVM
-// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -fcxx-exceptions -fexceptions -o - | FileCheck %s --check-prefixes=LLVM
+// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -fclangir -emit-llvm -fcxx-exceptions -fexceptions -o - | FileCheck %s --check-prefixes=LLVM,LLVMCIR
+// RUN: %clang_cc1 %s -triple x86_64-apple-darwin10 -emit-llvm -fcxx-exceptions -fexceptions -o - | FileCheck %s --check-prefixes=LLVM,OGCG
struct Base1 {
int i, j, k;
@@ -37,8 +37,9 @@ Inherits I;
// LLVM: @I = global %struct.Inherits zeroinitializer, align 4
Inherits I2 {{1,2,3},{1.1, 2.2, 3.3}, 4, 5, 6};
-// CIR: cir.global external @I2 = #cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i, #cir.fp<1.100000e+00> : !cir.float, #cir.fp<2.200000e+00> : !cir.float, #cir.fp<3.300000e+00> : !cir.float, #cir.int<4> : !s32i, #cir.int<5> : !s32i, #cir.int<6> : !s32i}> : !rec_anon_struct {alignment = 4 : i64}
-// LLVM: @I2 = global { i32, i32, i32, float, float, float, i32, i32, i32 } { i32 1, i32 2, i32 3, float {{.*}}, float {{.*}}, float {{.*}}, i32 4, i32 5, i32 6 }, align 4
+// CIR: cir.global external @I2 = #cir.const_record<{#cir.const_record<{#cir.int<1> : !s32i, #cir.int<2> : !s32i, #cir.int<3> : !s32i}> : !rec_Base1, #cir.const_record<{#cir.fp<1.1{{.*}}> : !cir.float, #cir.fp<2.2{{.*}}> : !cir.float, #cir.fp<3.3{{.*}}> : !cir.float}> : !rec_Base2, #cir.int<4> : !s32i, #cir.int<5> : !s32i, #cir.int<6> : !s32i}> : !rec_Inherits
+// LLVMCIR: @I2 = global %struct.Inherits { %struct.Base1 { i32 1, i32 2, i32 3 }, %struct.Base2 { float 1.1{{.*}}, float 2.2{{.*}}, float 3.3{{.*}} }, i32 4, i32 5, i32 6 }
+// OGCG: @I2 = global { i32, i32, i32, float, float, float, i32, i32, i32 } { i32 1, i32 2, i32 3, float {{.*}}, float {{.*}}, float {{.*}}, i32 4, i32 5, i32 6 }, align 4
VirtualInherits VI;
// CIR-BEFORE: cir.global external @VI = ctor : !rec_VirtualInherits {
diff --git a/clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.c b/clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.c
index cee253c1a0e2e..c60bc7638d0b2 100644
--- a/clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.c
+++ b/clang/test/CIR/CodeGenOpenACC/compute-reduction-clause-default-ops.c
@@ -24,9 +24,8 @@ void acc_compute() {
// CHECK: acc.reduction.recipe @reduction_add__ZTS16DefaultOperators : !cir.ptr<!rec_DefaultOperators> reduction_operator <add> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperators>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperators>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!rec_anon_struct>
-// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_anon_struct
-// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[BITCAST]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DefaultOperators
+// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[ALLOCA]] : !rec_DefaultOperators, !cir.ptr<!rec_DefaultOperators>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -71,9 +70,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_mul__ZTS16DefaultOperators : !cir.ptr<!rec_DefaultOperators> reduction_operator <mul> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperators>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperators>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!rec_anon_struct>
-// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<1> : !s32i, #cir.int<1> : !u32i, #cir.fp<1{{.*}}> : !cir.float, {{.*}}, #cir.fp<1{{.*}}> : !cir.double, #true, {{.*}}}> : !rec_anon_struct
-// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[BITCAST]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct>
+// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<1> : !s32i, #cir.int<1> : !u32i, #cir.fp<1{{.*}}> : !cir.float, #cir.fp<1{{.*}}> : !cir.double, #true}> : !rec_DefaultOperators
+// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[ALLOCA]] : !rec_DefaultOperators, !cir.ptr<!rec_DefaultOperators>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -118,9 +116,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_max__ZTS16DefaultOperators : !cir.ptr<!rec_DefaultOperators> reduction_operator <max> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperators>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperators>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!rec_anon_struct>
-// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<-2147483648> : !s32i, #cir.int<0> : !u32i, #cir.fp<-3.4{{.*}}E+38> : !cir.float, {{.*}}, #cir.fp<-1.7{{.*}}E+308> : !cir.double, #false, {{.*}}}> : !rec_anon_struct
-// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[BITCAST]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct>
+// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<-2147483648> : !s32i, #cir.int<0> : !u32i, #cir.fp<-3.4{{.*}}E+38> : !cir.float, #cir.fp<-1.7{{.*}}E+308> : !cir.double, #false}> : !rec_DefaultOperators
+// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[ALLOCA]] : !rec_DefaultOperators, !cir.ptr<!rec_DefaultOperators>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -207,9 +204,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_min__ZTS16DefaultOperators : !cir.ptr<!rec_DefaultOperators> reduction_operator <min> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperators>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperators>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!rec_anon_struct>
-// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<2147483647> : !s32i, #cir.int<4294967295> : !u32i, #cir.fp<3.4{{.*}}E+38> : !cir.float, {{.*}}, #cir.fp<1.7{{.*}}E+308> : !cir.double, #true, {{.*}}}> : !rec_anon_struct
-// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[BITCAST]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct>
+// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<2147483647> : !s32i, #cir.int<4294967295> : !u32i, #cir.fp<3.4{{.*}}E+38> : !cir.float, #cir.fp<1.7{{.*}}E+308> : !cir.double, #true}> : !rec_DefaultOperators
+// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[ALLOCA]] : !rec_DefaultOperators, !cir.ptr<!rec_DefaultOperators>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -296,9 +292,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_iand__ZTS24DefaultOperatorsNoFloats : !cir.ptr<!rec_DefaultOperatorsNoFloats> reduction_operator <iand> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperatorsNoFloats>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperatorsNoFloats>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperatorsNoFloats> -> !cir.ptr<!rec_anon_struct1>
-// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<-1> : !s32i, #cir.int<4294967295> : !u32i, #true, {{.*}}}> : !rec_anon_struct1
-// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[BITCAST]] : !rec_anon_struct1, !cir.ptr<!rec_anon_struct1>
+// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<-1> : !s32i, #cir.int<4294967295> : !u32i, #true}> : !rec_DefaultOperatorsNoFloats
+// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[ALLOCA]] : !rec_DefaultOperatorsNoFloats, !cir.ptr<!rec_DefaultOperatorsNoFloats>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -331,9 +326,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_ior__ZTS24DefaultOperatorsNoFloats : !cir.ptr<!rec_DefaultOperatorsNoFloats> reduction_operator <ior> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperatorsNoFloats>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperatorsNoFloats>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperatorsNoFloats> -> !cir.ptr<!rec_anon_struct1>
-// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_anon_struct1
-// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[BITCAST]] : !rec_anon_struct1, !cir.ptr<!rec_anon_struct1>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DefaultOperatorsNoFloats
+// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[ALLOCA]] : !rec_DefaultOperatorsNoFloats, !cir.ptr<!rec_DefaultOperatorsNoFloats>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -366,9 +360,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_xor__ZTS24DefaultOperatorsNoFloats : !cir.ptr<!rec_DefaultOperatorsNoFloats> reduction_operator <xor> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperatorsNoFloats>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperatorsNoFloats>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperatorsNoFloats> -> !cir.ptr<!rec_anon_struct1>
-// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_anon_struct1
-// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[BITCAST]] : !rec_anon_struct1, !cir.ptr<!rec_anon_struct1>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DefaultOperatorsNoFloats
+// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[ALLOCA]] : !rec_DefaultOperatorsNoFloats, !cir.ptr<!rec_DefaultOperatorsNoFloats>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -401,9 +394,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_land__ZTS16DefaultOperators : !cir.ptr<!rec_DefaultOperators> reduction_operator <land> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperators>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperators>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!rec_anon_struct>
-// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<1> : !s32i, #cir.int<1> : !u32i, #cir.fp<1{{.*}}> : !cir.float, {{.*}}, #cir.fp<1{{.*}}> : !cir.double, #true, {{.*}}}> : !rec_anon_struct
-// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[BITCAST]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct>
+// CHECK-NEXT: %[[CONST:.*]] = cir.const #cir.const_record<{#cir.int<1> : !s32i, #cir.int<1> : !u32i, #cir.fp<1{{.*}}> : !cir.float, #cir.fp<1{{.*}}> : !cir.double, #true}> : !rec_DefaultOperators
+// CHECK-NEXT: cir.store{{.*}} %[[CONST]], %[[ALLOCA]] : !rec_DefaultOperators, !cir.ptr<!rec_DefaultOperators>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
@@ -489,9 +481,8 @@ void acc_compute() {
// CHECK-NEXT: acc.reduction.recipe @reduction_lor__ZTS16DefaultOperators : !cir.ptr<!rec_DefaultOperators> reduction_operator <lor> init {
// CHECK-NEXT: ^bb0(%[[ARG:.*]]: !cir.ptr<!rec_DefaultOperators>{{.*}})
// CHECK-NEXT: %[[ALLOCA:.*]] = cir.alloca "openacc.reduction.init" {{.*}} : !cir.ptr<!rec_DefaultOperators>
-// CHECK-NEXT: %[[BITCAST:.*]] = cir.cast bitcast %[[ALLOCA]] : !cir.ptr<!rec_DefaultOperators> -> !cir.ptr<!rec_anon_struct>
-// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_anon_struct
-// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[BITCAST]] : !rec_anon_struct, !cir.ptr<!rec_anon_struct>
+// CHECK-NEXT: %[[ZERO:.*]] = cir.const #cir.zero : !rec_DefaultOperators
+// CHECK-NEXT: cir.store{{.*}} %[[ZERO]], %[[ALLOCA]] : !rec_DefaultOperators, !cir.ptr<!rec_DefaultOperators>
// CHECK-NEXT: acc.yield
//
// CHECK-NEXT: } combiner {
diff --git a/clang/test/CIR/IR/invalid-const-record.cir b/clang/test/CIR/IR/invalid-const-record.cir
index cff120ea49264..c716ec8b7acbc 100644
--- a/clang/test/CIR/IR/invalid-const-record.cir
+++ b/clang/test/CIR/IR/invalid-const-record.cir
@@ -21,3 +21,32 @@ cir.global external @e = #cir.const_record<{#cir.int<1> : !s32i, #cir.zero : !ci
// expected-error @below {{element at index 1 has type '!cir.float' but the expected type for this element is '!cir.int<s, 32>'}}
cir.global external @e = #cir.const_record<{#cir.int<1> : !s32i, #cir.fp<2.000000e+00> : !cir.float, #cir.zero : !cir.array<!s32i x 8>}> : !rec_anon_struct
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!rec_U = !cir.union<"U" {!s32i, !u8i}, padding = {!cir.array<!u8i x 3>}>
+
+// A union constant must have exactly one element (the active member).
+// expected-error @below {{union constant must have exactly one element, got 2}}
+cir.global external @u = #cir.const_record<{#cir.int<5> : !s32i, #cir.int<7> : !u8i}> : !rec_U
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!rec_U = !cir.union<"U" {!s32i, !u8i}, padding = {!cir.array<!u8i x 3>}>
+
+// expected-error @below {{union constant must have exactly one element, got 0}}
+cir.global external @u = #cir.const_record<{}> : !rec_U
+
+// -----
+
+!s32i = !cir.int<s, 32>
+!u8i = !cir.int<u, 8>
+!rec_U = !cir.union<"U" {!s32i, !u8i}, padding = {!cir.array<!u8i x 3>}>
+
+// The single element's type must be one of the union's member types.
+// expected-error @below {{union element type '!cir.float' is not a member of}}
+cir.global external @u = #cir.const_record<{#cir.fp<2.000000e+00> : !cir.float}> : !rec_U
diff --git a/clang/test/CIR/Lowering/array.cpp b/clang/test/CIR/Lowering/array.cpp
index 92d03041d2324..f8657c59a9666 100644
--- a/clang/test/CIR/Lowering/array.cpp
+++ b/clang/test/CIR/Lowering/array.cpp
@@ -25,7 +25,7 @@ int dd[3][2] = {{1, 2}, {3, 4}, {5, 6}};
// CHECK: [i32 3, i32 4], [2 x i32] [i32 5, i32 6]]
int e[10] = {1, 2};
-// CHECK: @e = global <{ i32, i32, [8 x i32] }> <{ i32 1, i32 2, [8 x i32] zeroinitializer }>
+// CHECK: @e = global [10 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0]
int f[5] = {1, 2};
// CHECK: @f = global [5 x i32] [i32 1, i32 2, i32 0, i32 0, i32 0]
More information about the cfe-commits
mailing list