[clang] [CIR] Implement emitNewArrayInit for constant and strings (PR #192666)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 17 07:15:07 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Erich Keane (erichkeane)
<details>
<summary>Changes</summary>
This patch further fleshes out the emit New ArrayInit for constant and string variables. Implementation wise, this is pretty much the same as classic-codegen, however it required a few differences. First, our use of cir.copy instead of a memcpy call means we had to 'lift' an dyn_allocated pointer type to the array type. Second, we had to make some changes to make sure that 'empty' extra init was skipped in a place we didn't do before.
In order to test this, I found 2 tests from classic-codegen that I pulled in nearly verbatum. 'Check' lines from paren-list-agg-init.cpp are converted to LLVM lines with slight relaxation, mostly to make up for cases where CIR lowering ntroduces extra branches or GEPS on conversion changes.
new-array-init.cpp's 'Check' lines were particularly bad/not detailed,
so I wrote new ones.
ONE test was commented out, as it requires the rest of emitNewArrayInit to be implemented.
---
Patch is 103.06 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/192666.diff
4 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp (+111-16)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+5)
- (added) clang/test/CIR/CodeGen/paren-list-agg-init.cpp (+884)
- (added) clang/test/CIR/CodeGenCXX/new-array-init.cpp (+651)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
index 30a833564ec2f..22c5926926dfb 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprCXX.cpp
@@ -488,9 +488,13 @@ static mlir::Value emitCXXNewAllocSize(CIRGenFunction &cgf, const CXXNewExpr *e,
// The equivalent code in CodeGen/CGExprCXX.cpp handles these cases as
// overflow, but that should never happen. The size argument is implicitly
// cast to a size_t, so it can never be negative and numElementsWidth will
- // always equal sizeWidth.
+ // always equal sizeWidth. However, sometimes in operator-new, it seems that
+ // `numElements` might remain an 'int', so we have to support smaller than
+ // that. We immediately do the zextOrTrunc below (which should really only
+ // do zext, since our assert handles the trunc), but it will make sure the
+ // width is correct.
assert(!count.isNegative() && "Expected non-negative array size");
- assert(numElementsWidth == sizeWidth &&
+ assert(numElementsWidth <= sizeWidth &&
"Expected a size_t array size constant");
// Okay, compute a count at the right width.
@@ -1006,8 +1010,55 @@ void CIRGenFunction::emitNewArrayInitializer(
// Initializing from a (braced) string literal is a special case; the init
// list element does not initialize a (single) array element.
if ((ile && ile->isStringLiteralInit()) || sl || ocee) {
- cgm.errorNYI(ile->getSourceRange(),
- "emitNewArrayInitializer: string literal init");
+ if (!ile)
+ init = ignoreParen;
+
+ // Initialize the initial portion of length equal to that of the string
+ // literal. The allocation must be for at least this much; we emitted a
+ // check for that earlier. Since we intend to use a cir.copy here, we must
+ // introduce a cast to the string-literal-size here, so that cir.copy does
+ // the right thing.
+
+ const Expr *initExpr = ile ? ile->getInit(0) : init;
+ mlir::Type initExprTy = convertType(initExpr->getType());
+ Address coercedPtrTy{
+ builder.createBitcast(curPtr.getPointer(),
+ builder.getPointerTo(initExprTy)),
+ curPtr.getAlignment()};
+
+ AggValueSlot slot = AggValueSlot::forAddr(
+ coercedPtrTy, elementType.getQualifiers(), AggValueSlot::IsDestructed,
+ AggValueSlot::IsNotAliased, AggValueSlot::DoesNotOverlap,
+ AggValueSlot::IsNotZeroed);
+ emitAggExpr(initExpr, slot);
+
+ // Move past these elements.
+ initListElements =
+ cast<ConstantArrayType>(init->getType()->getAsArrayTypeUnsafe())
+ ->getZExtSize();
+
+ bool alreadyInitedAll = false;
+ auto constElts =
+ mlir::dyn_cast<cir::ConstantOp>(numElements.getDefiningOp());
+ if (constElts) {
+ if (auto constIntAttr =
+ mlir::dyn_cast<cir::IntAttr>(constElts.getValue()))
+ alreadyInitedAll = (constIntAttr.getUInt() == initListElements);
+ }
+
+ // Init the rest with memset, unless we've already done everything.
+ if (!alreadyInitedAll) {
+ mlir::Location initLoc = cgm.getLoc(init->getSourceRange());
+ mlir::Value initListElementsOp = builder.getUnsignedInt(
+ initLoc, initListElements,
+ getContext().getTypeSize(getContext().getSizeType()));
+ curPtr = curPtr.withPointer(builder.createPtrStride(
+ initLoc, curPtr.getPointer(), initListElementsOp));
+
+ bool ok = tryMemsetInitialization();
+ (void)ok;
+ assert(ok && "couldn't memset character type?");
+ }
return;
}
@@ -1020,10 +1071,9 @@ void CIRGenFunction::emitNewArrayInitializer(
QualType allocType = e->getAllocatedType();
if (const ConstantArrayType *cat = dyn_cast_or_null<ConstantArrayType>(
allocType->getAsArrayTypeUnsafe())) {
- (void)cat;
- cgm.errorNYI(ile->getSourceRange(),
- "emitNewArrayInitializer: constant array init");
- return;
+ elementTy = convertTypeForMem(allocType);
+ curPtr = curPtr.withElementType(builder, elementTy);
+ initListElements *= getContext().getConstantArrayElementCount(cat);
}
// Enter a partial-destruction Cleanup if necessary.
@@ -1154,14 +1204,54 @@ void CIRGenFunction::emitNewArrayInitializer(
}
// If this is value-initialization, we can usually use memset.
+ ImplicitValueInitExpr ivie(elementType);
if (isa<ImplicitValueInitExpr>(init)) {
if (tryMemsetInitialization())
return;
- cgm.errorNYI(init->getSourceRange(),
- "emitNewArrayInitializer: implicit value init");
- return;
+ // Switch to an ImplicitValueInitExpr for the element type. This handles
+ // only one case: multidimensional array new of pointers to members. In
+ // all other cases, we already have an initializer for the array element.
+ init = &ivie;
+ }
+
+ // At this point we should have found an initializer for the individual
+ // elements of the array.
+ assert(getContext().hasSameUnqualifiedType(elementType, init->getType()) &&
+ "got wrong type of element to initialize");
+
+ // If we have a struct whose every field is value-initialized, we can
+ // usually use memset.
+ if (auto *ile = dyn_cast<InitListExpr>(init)) {
+
+ // If we have an empty initializer list, we can usually use memset.
+ if (ile->getNumInits() == 0 && tryMemsetInitialization())
+ return;
+
+ if (const RecordType *rtype =
+ ile->getType()->getAsCanonical<RecordType>()) {
+ if (rtype->getDecl()->isStruct()) {
+ const RecordDecl *rd = rtype->getDecl()->getDefinitionOrSelf();
+ unsigned numElements = 0;
+ if (auto *cxxrd = dyn_cast<CXXRecordDecl>(rd))
+ numElements = cxxrd->getNumBases();
+ for (auto *field : rd->fields())
+ if (!field->isUnnamedBitField())
+ ++numElements;
+ // FIXME: Recurse into nested InitListExprs.
+ if (ile->getNumInits() == numElements)
+ for (unsigned i = 0, e = ile->getNumInits(); i != e; ++i)
+ if (!isa<ImplicitValueInitExpr>(ile->getInit(i)))
+ --numElements;
+ if (ile->getNumInits() == numElements && tryMemsetInitialization())
+ return;
+ }
+ }
}
+ // The rest of this has to go through the rest of the initializer, generating
+ // a loop with cleanups/destruction/etc. See the test
+ // 'check_array_value_init'(currently disabled) in
+ // CodeGenCXX/new-array-init.cpp when we get more of this implemented.
cgm.errorNYI(init->getSourceRange(),
"emitNewArrayInitializer: unsupported initializer");
return;
@@ -1376,14 +1466,19 @@ mlir::Value CIRGenFunction::emitCXXNewExpr(const CXXNewExpr *e) {
// If there is a brace-initializer, cannot allocate fewer elements than inits.
unsigned minElements = 0;
if (e->isArray() && e->hasInitializer()) {
- const InitListExpr *ile = dyn_cast<InitListExpr>(e->getInitializer());
- if (ile && ile->isStringLiteralInit())
+ const Expr *init = e->getInitializer();
+ const InitListExpr *ile = dyn_cast<InitListExpr>(init);
+ const CXXParenListInitExpr *cplie = dyn_cast<CXXParenListInitExpr>(init);
+ const Expr *ignoreParen = init->IgnoreParenImpCasts();
+ if ((ile && ile->isStringLiteralInit()) ||
+ isa<StringLiteral>(ignoreParen) || isa<ObjCEncodeExpr>(ignoreParen)) {
minElements =
- cast<ConstantArrayType>(ile->getType()->getAsArrayTypeUnsafe())
+ cast<ConstantArrayType>(init->getType()->getAsArrayTypeUnsafe())
->getSize()
.getZExtValue();
- else if (ile)
- minElements = ile->getNumInits();
+ } else if (ile || cplie) {
+ minElements = ile ? ile->getNumInits() : cplie->getInitExprs().size();
+ }
}
mlir::Value numElements = nullptr;
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 5003c16296cc1..e9369d70669e5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1194,6 +1194,11 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
return emitInitListLValue(cast<InitListExpr>(e));
case Expr::PseudoObjectExprClass:
return emitPseudoObjectLValue(cast<PseudoObjectExpr>(e));
+ case Expr::CXXDefaultInitExprClass: {
+ auto *die = cast<CXXDefaultInitExpr>(e);
+ CXXDefaultInitExprScope scope(*this, die);
+ return emitLValue(die->getExpr());
+ }
}
}
diff --git a/clang/test/CIR/CodeGen/paren-list-agg-init.cpp b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
new file mode 100644
index 0000000000000..0f282fdf6d322
--- /dev/null
+++ b/clang/test/CIR/CodeGen/paren-list-agg-init.cpp
@@ -0,0 +1,884 @@
+// 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=LLVM
+
+template <typename T>
+struct IsChar {
+ constexpr operator bool() const { return false; }
+};
+
+template<>
+struct IsChar<char> {
+ constexpr operator bool() const { return true; }
+};
+
+template <typename T>
+concept SameAsChar = (bool)IsInt<T>();
+
+// LLVM-DAG: [[STRUCT_A:%.*]] = type { i8, double }
+// CIR-DAG: ![[STRUCT_A:.*]] = !cir.record<struct "A" {!s8i, !cir.double}>
+struct A {
+ char i;
+ double j;
+
+ template <SameAsChar T>
+ operator T() const { return i; };
+};
+
+// LLVM-DAG: [[STRUCT_B:%.*]] = type { [[STRUCT_A]], i32 }
+// CIR-DAG: ![[STRUCT_B:.*]] = !cir.record<struct "B" {![[STRUCT_A]], !s32i}>
+struct B {
+ A a;
+ int b;
+};
+
+// LLVM-DAG: [[STRUCT_C:%.*]] = type <{ [[STRUCT_B]], [[STRUCT_A]], i32, [4 x i8] }>
+// CIR-DAG: ![[STRUCT_C:.*]] = !cir.record<struct "C" packed padded {![[STRUCT_B]], ![[STRUCT_A]], !s32i, !cir.array<!u8i x 4>}>
+struct C : public B, public A {
+ int c;
+};
+
+// LLVM-DAG: [[STRUCT_D:%.*]] = type { [[STRUCT_A]], [[STRUCT_A]], i8, [[STRUCT_A]] }
+// CIR-DAG: ![[STRUCT_D:.*]] = !cir.record<struct "D" {![[STRUCT_A]], ![[STRUCT_A]], !u8i, ![[STRUCT_A]]}>
+struct D {
+ A a;
+ A b = A{2, 2.0};
+ unsigned : 2;
+ A c;
+};
+
+// LLVM-DAG: [[STRUCT_E:%.*]] = type { i32, ptr }
+// CIR-DAG: ![[STRUCT_E:.*]] = !cir.record<struct "E" {!s32i, !cir.ptr<!s8i>}>
+struct E {
+ int a;
+ const char* fn = __builtin_FUNCTION();
+ ~E() {};
+};
+
+// CIR-DAG: ![[STRUCT_F:.*]] = !cir.record<struct "F" padded {!u8i}>
+struct F {
+ F (int i = 1);
+ F (const F &f) = delete;
+ F (F &&f) = default;
+};
+
+// LLVM-DAG: [[STRUCT_G:%.*]] = type <{ i32, [4 x i8] }>
+// CIR-DAG: ![[STRUCT_G:.*]] = !cir.record<struct "G" packed padded {!s32i, !cir.array<!u8i x 4>}>
+struct G {
+ int a;
+ F f;
+};
+
+// LLVM-DAG: [[UNION_U:%.*]] = type { [[STRUCT_A]] }
+// LLVM-DAG: [[STR:@.*]] = private {{.*}}constant [6 x i8] {{.*}}foo18{{.*}}, align 1
+// CIR-DAG: ![[UNION_U:.*]] = !cir.record<union "U" {!u8i, ![[STRUCT_A]], !s8i}>
+union U {
+ unsigned : 1;
+ A a;
+ char b;
+};
+
+
+namespace gh61145 {
+ // LLVM-DAG: [[STRUCT_VEC:%.*Vec.*]] = type { i8 }
+ // CIR-DAG: ![[STRUCT_VEC:.*]] = !cir.record<struct "gh61145::Vec" padded {!u8i}>
+ struct Vec {
+ Vec();
+ Vec(Vec&&);
+ ~Vec();
+ };
+
+ // LLVM-DAG: [[STRUCT_S1:%.*]] = type { i8 }
+ // CIR-DAG: ![[STRUCT_S1:.*]] = !cir.record<struct "gh61145::S1" padded {!u8i}>
+ struct S1 {
+ Vec v;
+ };
+
+ // LLVM-DAG: [[STRUCT_S2:%.*]] = type { i8, i8 }
+ // CIR-DAG: ![[STRUCT_S2:.*]] = !cir.record<struct "gh61145::S2" padded {!u8i, !s8i}>
+ struct S2 {
+ Vec v;
+ char c;
+ };
+}
+
+namespace gh62266 {
+ // LLVM-DAG: [[STRUCT_H:%.*H.*]] = type { i32, i32 }
+ // CIR-DAG: ![[STRUCT_H:.*]] = !cir.record<struct "gh62266::H<2>" {!s32i, !s32i}>
+ template <int J>
+ struct H {
+ int i;
+ int j = J;
+ };
+}
+
+namespace gh61567 {
+ // LLVM-DAG: [[STRUCT_I:%.*I.*]] = type { i32, ptr }
+ // CIR-DAG: ![[STRUCT_I:.*]] = !cir.record<struct "gh61567::I" {!s32i, !cir.ptr<!s32i>}>
+ struct I {
+ int a;
+ int&& r = 2;
+ };
+}
+
+// LLVM-DAG: [[A1:@.*a1.*]] = internal constant [[STRUCT_A]] { i8 3, double 2.000000e+00 }, align 8
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2a1 = #cir.const_record<{#cir.int<3> : !s8i, #cir.fp<2.000000e+00> : !cir.double}> : ![[STRUCT_A]] {alignment = 8 : i64}
+constexpr A a1(3.1, 2.0);
+// LLVM-DAG: [[A2:@.*a2.*]] = internal constant [[STRUCT_A]] { i8 99, double 0.000000e+00 }, align 8
+// CIR-DAG: cir.global "private" constant internal dso_local @_ZL2a2 = #cir.const_record<{#cir.int<99> : !s8i, #cir.fp<0.000000e+00> : !cir.double}> : ![[STRUCT_A]] {alignment = 8 : i64}
+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}>
+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]]}>
+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}
+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}
+constexpr int arr5[2](2);
+
+// LLVM: define dso_local {{.*}} @{{.*foo1.*}}
+// LLVM: [[RETVAL:%.*]] = alloca [[STRUCT_A]]
+// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}[[RETVAL]], ptr {{.*}}[[A1]], i64 16, i1 false)
+// LLVM-NEXT: [[TMP_0:%.*]] = load {{.*}}, ptr [[RETVAL]], align 8
+// LLVM-NEXT: ret {{.*}}[[TMP_0]]
+// CIR-LABEL: cir.func {{.*}}@_Z4foo1v()
+// CIR: %[[A_ALLOCA:.*]] = cir.alloca ![[STRUCT_A]], !cir.ptr<![[STRUCT_A]]>, ["__retval"] {alignment = 8 : i64}
+// CIR: %[[GET_A1:.*]] = cir.get_global @_ZL2a1 : !cir.ptr<![[STRUCT_A]]>
+// CIR: cir.copy %[[GET_A1]] to %[[A_ALLOCA]] : !cir.ptr<![[STRUCT_A]]>
+A foo1() {
+ return a1;
+}
+
+// LLVM: define dso_local {{.*}}@{{.*foo2.*}}
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}[[B1]], i64 24, i1 false)
+// CIR: cir.func {{.*}}@_Z4foo2v()
+// CIR: %[[B_ALLOCA:.*]] = cir.alloca ![[STRUCT_B]], !cir.ptr<![[STRUCT_B]]>, ["__retval"] {alignment = 8 : i64}
+// CIR: %[[GET_GLOB:.*]] = cir.get_global @_ZL2b1 : !cir.ptr<![[STRUCT_B]]>
+// CIR: cir.copy %[[GET_GLOB]] to %[[B_ALLOCA]] : !cir.ptr<![[STRUCT_B]]>
+B foo2() {
+ return b1;
+}
+
+// LLVM: define dso_local {{.*}}@{{.*foo3.*}}
+// LLVM: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}, ptr {{.*}}[[C1]], i64 48, i1 false)
+// CIR: cir.func {{.*}}@_Z4foo3v()
+// CIR: %[[C_ALLOCA:.*]] = cir.alloca ![[STRUCT_C]], !cir.ptr<![[STRUCT_C]]>, ["__retval"] {alignment = 8 : i64}
+// 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]]>
+C foo3() {
+ return c1;
+}
+
+// LLVM: define dso_local void @{{.*foo4.*}}
+// LLVM-DAG: [[C2:%.*]] = alloca [[STRUCT_C]]
+// LLVM-DAG: [[REF_TMP:%.*]] = alloca [[STRUCT_B]]
+// LLVM-DAG: [[REF_TMP_1:%.*]] = alloca [[STRUCT_A]]
+// LLVM: [[A:%.*]] = getelementptr {{.*}}[[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 0
+// LLVM-NEXT: [[I:%.*]] = getelementptr {{.*}}[[STRUCT_A]], ptr [[A]], i32 0, i32 0
+// LLVM-NEXT: store i8 1, ptr [[I]], align 8
+// LLVM-NEXT: [[J:%.*]] = getelementptr {{.*}}[[STRUCT_A]], ptr [[A]], i32 0, i32 1
+// LLVM-NEXT: store double 1.000000e+00, ptr [[J]], align 8
+// LLVM-NEXT: [[B:%.*]] = getelementptr {{.*}}[[STRUCT_B]], ptr [[REF_TMP]], i32 0, i32 1
+// LLVM-NEXT: store i32 1, ptr [[B]], align 8
+// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}[[C2]], ptr {{.*}}[[REF_TMP]], i64 24, i1 false)
+// LLVM-NEXT: [[TMP_0:%.*]] = getelementptr {{.*}}i8, ptr [[C2]], i{{.*}} 24
+// LLVM-NEXT: [[I2:%.*]] = getelementptr {{.*}}[[STRUCT_A]], ptr [[REF_TMP_1]], i32 0, i32 0
+// LLVM-NEXT: store i8 97, ptr [[I2]], align 8
+// LLVM-NEXT: [[J3:%.*]] = getelementptr {{.*}}[[STRUCT_A]], ptr [[REF_TMP_1]], i32 0, i32 1
+// LLVM-NEXT: store double 0.000000e+00, ptr [[J3]], align 8
+// LLVM-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr {{.*}}[[TMP_0]], ptr {{.*}}[[REF_TMP_1]], i64 16, i1 false)
+// LLVM-NEXT: [[C:%.*]] = getelementptr {{.*}}[[STRUCT_C]], ptr [[C2]], i32 0, i32 2
+// LLVM-NEXT: store i32 2, ptr [[C]]
+// LLVM: ret void
+// CIR-LABEL: cir.func {{.*}}@_Z4foo4v()
+// CIR: %[[C2_ALLOCA:.*]] = cir.alloca ![[STRUCT_C]], !cir.ptr<![[STRUCT_C]]>, ["c2", init]
+// CIR: %[[B_TMP:.*]] = cir.alloca ![[STRUCT_B]], !cir.ptr<![[STRUCT_B]]>, ["ref.tmp0"]
+// CIR: %[[A_TMP:.*]] = cir.alloca ![[STRUCT_A]], !cir.ptr<![[STRUCT_A]]>, ["ref.tmp1"]
+// CIR: %[[C_BASE:.*]] = cir.base_class_addr %[[C2_ALLOCA]] : !cir.ptr<![[STRUCT_C]]> nonnull [0] -> !cir.ptr<![[STRUCT_B]]>
+// CIR: %[[GET_A:.*]] = cir.get_member %[[B_TMP]][0] {name = "a"} : !cir.ptr<![[STRUCT_B]]> -> !cir.ptr<![[STRUCT_A]]>
+// CIR: %[[GET_I:.*]] = cir.get_member %[[GET_A]][0] {name = "i"} : !cir.ptr<![[STRUCT_A]]> -> !cir.ptr<!s8i>
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s8i
+// CIR: cir.store{{.*}} %[[ONE:.*]], %[[GET_I]] : !s8i, !cir.ptr<!s8i>
+// CIR: %[[GET_J:.*]] = cir.get_member %[[GET_A]][1] {name = "j"} : !cir.ptr<![[STRUCT_A]]> -> !cir.ptr<!cir.double>
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: %[[ONE_F:.*]] = cir.cast int_to_float %[[ONE]] : !s32i -> !cir.double
+// CIR: cir.store{{.*}} %[[ONE_F]], %[[GET_J]] : !cir.double, !cir.ptr<!cir.double>
+// CIR: %[[GET_B:.*]] = cir.get_member %[[B_TMP]][1] {name = "b"} : !ci...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/192666
More information about the cfe-commits
mailing list