[clang] [CIR] Implement emitNewArrayInit for constant and strings (PR #192666)

via cfe-commits cfe-commits at lists.llvm.org
Fri Apr 17 07:15:06 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clangir

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