https://github.com/erichkeane created 
https://github.com/llvm/llvm-project/pull/192053

This ended up being a fairly common pattern: a copy operation on a structure 
with an array inside of it.  Classic-Codegen has a few different ways of 
initializing/copying an array, of which this is one. However, this patch uses 
the array-init functionality we already have.

This ends up being a bit verbose, but will make sure we don't have to worry 
about separately handling throwing types/etc for this AST node.

Additionally, this has to handle the ArrayInitIndexExpr, but that is as simple 
as making sure we properly cache the index value when doing our initialization.

>From 53f96dd9c177935fed1eac73b2c7dbc21bff161b Mon Sep 17 00:00:00 2001
From: erichkeane <[email protected]>
Date: Fri, 10 Apr 2026 14:02:11 -0700
Subject: [PATCH] [CIR] Implement 'ArrayInitLoopExpr lowering' in ExprAgg.

This ended up being a fairly common pattern: a copy operation on a
structure with an array inside of it.  Classic-Codegen has a few
different ways of initializing/copying an array, of which this is one.
However, this patch uses the array-init functionality we already have.

This ends up being a bit verbose, but will make sure we don't have to
worry about separately handling throwing types/etc for this AST node.

Additionally, this has to handle the ArrayInitIndexExpr, but that is as
simple as making sure we properly cache the index value when doing our
initialization.
---
 clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp |  34 +-
 clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp    |   6 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.h        |  31 ++
 clang/lib/CodeGen/CGExprAgg.cpp               |   3 +-
 .../CIR/CodeGen/array-init-loop-exprs.cpp     | 340 ++++++++++++++++++
 5 files changed, 406 insertions(+), 8 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/array-init-loop-exprs.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index ec8ebf75ccbee..622012292e175 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -497,11 +497,26 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> 
{
                                     e->getArrayFiller());
   }
 
-  void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *e,
-                              llvm::Value *outerBegin = nullptr) {
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "AggExprEmitter: VisitArrayInitLoopExpr");
+  void VisitArrayInitLoopExpr(const ArrayInitLoopExpr *e) {
+    CIRGenFunction::OpaqueValueMapping binding(cgf, e->getCommonExpr());
+    uint64_t numElements = e->getArraySize().getZExtValue();
+
+    if (!numElements)
+      return;
+
+    const mlir::Location loc = cgf.getLoc(e->getSourceRange());
+
+    if (!e->getType()->isConstantArrayType())
+      cgf.cgm.errorNYI(e->getSourceRange(),
+                       "VisitArrayInitLoopExpr: Non-constant array");
+
+    Address dest = ensureSlot(loc, e->getType()).getAddress();
+    cir::ArrayType arrayTy = cast<cir::ArrayType>(dest.getElementType());
+
+    emitArrayInit(dest, arrayTy, e->getType(),
+                  const_cast<ArrayInitLoopExpr *>(e), {}, e->getSubExpr());
   }
+
   void VisitImplicitValueInitExpr(ImplicitValueInitExpr *e) {
     cgf.cgm.errorNYI(e->getSourceRange(),
                      "AggExprEmitter: VisitImplicitValueInitExpr");
@@ -687,6 +702,8 @@ void AggExprEmitter::emitArrayInit(Address destPtr, 
cir::ArrayType arrayTy,
 
   const uint64_t numInitElements = args.size();
 
+  bool setArrayInitLoopExprScope = isa<ArrayInitLoopExpr>(e);
+
   const QualType elementType =
       cgf.getContext().getAsArrayType(arrayQTy)->getElementType();
 
@@ -784,6 +801,15 @@ void AggExprEmitter::emitArrayInit(Address destPtr, 
cir::ArrayType arrayTy,
           LValue elementLV = cgf.makeAddrLValue(
               Address(currentElement, cirElementType, elementAlign),
               elementType);
+
+          mlir::Value idx;
+          if (setArrayInitLoopExprScope)
+            idx = cir::PtrDiffOp::create(b, loc, cgf.ptrDiffTy, currentElement,
+                                         begin);
+
+          CIRGenFunction::ArrayInitLoopExprScope loopExprScope(
+              cgf, setArrayInitLoopExprScope, idx);
+
           if (arrayFiller)
             emitInitializationToLValue(arrayFiller, elementLV);
           else
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp 
b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
index b1498f376725d..d39047cfff280 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprScalar.cpp
@@ -415,9 +415,9 @@ class ScalarExprEmitter : public 
StmtVisitor<ScalarExprEmitter, mlir::Value> {
   mlir::Value VisitInitListExpr(InitListExpr *e);
 
   mlir::Value VisitArrayInitIndexExpr(ArrayInitIndexExpr *e) {
-    cgf.cgm.errorNYI(e->getSourceRange(),
-                     "ScalarExprEmitter: array init index");
-    return {};
+    assert(cgf.getArrayInitIndex() &&
+           "ArrayInitIndexExpr not inside an ArrayInitLoopExpr?");
+    return cgf.getArrayInitIndex();
   }
 
   mlir::Value VisitImplicitValueInitExpr(const ImplicitValueInitExpr *e) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h 
b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 4f0f33a933e01..08677d53eba59 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -182,6 +182,10 @@ class CIRGenFunction : public CIRGenTypeCache {
   /// CXXInheritedCtorInitExprs within this context.
   CallArgList cxxInheritedCtorInitExprArgs;
 
+  /// The current array initialization index when evaluating an
+  /// ArrayInitIndexExpr within an ArrayInitLoopExpr.
+  mlir::Value arrayInitIndex = nullptr;
+
   // Holds the Decl for the current outermost non-closure context
   const clang::Decl *curFuncDecl = nullptr;
   /// This is the inner-most code context, which includes blocks.
@@ -885,6 +889,33 @@ class CIRGenFunction : public CIRGenTypeCache {
         : SourceLocExprScopeGuard(e, cfg.curSourceLocExprScope) {}
   };
 
+  /// The scope of an ArrayInitLoopExpr. Within this scope, the value of the
+  /// current loop index is overridden. In order to encourage re-use of 
existing
+  /// array initialization, this uses a flag to determine if it is a 'no-op' or
+  /// not.
+  class ArrayInitLoopExprScope {
+  public:
+    ArrayInitLoopExprScope(CIRGenFunction &cgf, bool setIdx, mlir::Value index)
+        : cgf(cgf),
+          oldArrayInitIndex(setIdx
+                                ? 
std::optional<mlir::Value>(cgf.arrayInitIndex)
+                                : std::nullopt) {
+      if (setIdx)
+        cgf.arrayInitIndex = index;
+    }
+    ~ArrayInitLoopExprScope() {
+      if (oldArrayInitIndex.has_value())
+        cgf.arrayInitIndex = *oldArrayInitIndex;
+    }
+
+  private:
+    CIRGenFunction &cgf;
+    std::optional<mlir::Value> oldArrayInitIndex;
+  };
+
+  /// Get the index of the current ArrayInitLoopExpr, if any.
+  mlir::Value getArrayInitIndex() { return arrayInitIndex; }
+
   LValue makeNaturalAlignPointeeAddrLValue(mlir::Value v, clang::QualType t);
   LValue makeNaturalAlignAddrLValue(mlir::Value val, QualType ty);
 
diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp
index 3a4291719da74..b47b46791060d 100644
--- a/clang/lib/CodeGen/CGExprAgg.cpp
+++ b/clang/lib/CodeGen/CGExprAgg.cpp
@@ -2018,8 +2018,9 @@ void AggExprEmitter::VisitArrayInitLoopExpr(const 
ArrayInitLoopExpr *E,
           AggValueSlot::DoesNotOverlap);
       AggExprEmitter(CGF, elementSlot, false)
           .VisitArrayInitLoopExpr(InnerLoop, outerBegin);
-    } else
+    } else {
       EmitInitializationToLValue(E->getSubExpr(), elementLV);
+    }
   }
 
   // Move on to the next element.
diff --git a/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp 
b/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp
new file mode 100644
index 0000000000000..13af4e01fa41a
--- /dev/null
+++ b/clang/test/CIR/CodeGen/array-init-loop-exprs.cpp
@@ -0,0 +1,340 @@
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-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 -Wno-unused-value 
-fclangir -emit-llvm %s -o %t-cir.ll
+// RUN: FileCheck --input-file=%t-cir.ll %s -check-prefix=LLVM
+// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value 
-emit-llvm %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+// Non-trivially copyable element type.
+struct NonTrivial {
+    NonTrivial(const NonTrivial &);
+    int val;
+};
+
+// A struct with a non-trivially copyable array member. The implicit copy
+// constructor will use ArrayInitLoopExpr to copy each element.
+struct HasNonTrivialArray {
+    NonTrivial arr[3];
+};
+
+// CIR-LABEL: cir.func no_inline comdat linkonce_odr 
@_ZN18HasNonTrivialArrayC2ERKS_({{.*}}) 
special_member<#cir.cxx_ctor<!rec_HasNonTrivialArray, copy>> 
+// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_HasNonTrivialArray>, 
!cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>>, ["this", init]
+// CIR: %[[RHS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_HasNonTrivialArray>, 
!cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>>, ["", init, const]
+// CIR: %[[ITR_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_NonTrivial>, 
!cir.ptr<!cir.ptr<!rec_NonTrivial>>, ["arrayinit.temp"]
+// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>>, !cir.ptr<!rec_HasNonTrivialArray>
+// CIR: %[[THIS_ARR:.*]] = cir.get_member %[[THIS_LOAD]][0] {name = "arr"} : 
!cir.ptr<!rec_HasNonTrivialArray> -> !cir.ptr<!cir.array<!rec_NonTrivial x 3>>
+// CIR: %[[RHS_LOAD:.*]] = cir.load %[[RHS_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_HasNonTrivialArray>>, !cir.ptr<!rec_HasNonTrivialArray>
+// CIR: %[[RHS_ARR:.*]] = cir.get_member %[[RHS_LOAD]][0] {name = "arr"} : 
!cir.ptr<!rec_HasNonTrivialArray> -> !cir.ptr<!cir.array<!rec_NonTrivial x 3>>
+// CIR: %[[THIS_ARR_DECAY:.*]] = cir.cast array_to_ptrdecay %[[THIS_ARR]] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 3>> -> !cir.ptr<!rec_NonTrivial>
+// CIR: cir.store {{.*}}%[[THIS_ARR_DECAY]], %[[ITR_ALLOCA]] : 
!cir.ptr<!rec_NonTrivial>, !cir.ptr<!cir.ptr<!rec_NonTrivial>>
+// CIR: %[[SIZE_CONST:.*]] = cir.const #cir.int<3>
+// CIR: %[[END_ITR:.*]] = cir.ptr_stride %[[THIS_ARR_DECAY]], %[[SIZE_CONST]] 
: (!cir.ptr<!rec_NonTrivial>, !s64i) -> !cir.ptr<!rec_NonTrivial>
+// CIR: cir.do {
+// CIR:   %[[ITR_LOAD:.*]] = cir.load {{.*}}%[[ITR_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_NonTrivial>>, !cir.ptr<!rec_NonTrivial>
+// CIR:   %[[IDX:.*]] = cir.ptr_diff %[[ITR_LOAD]], %[[THIS_ARR_DECAY]] : 
!cir.ptr<!rec_NonTrivial> -> !s64i
+// CIR:   %[[RHS_ELT:.*]] = cir.get_element %[[RHS_ARR]][%[[IDX]] : !s64i] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 3>> -> !cir.ptr<!rec_NonTrivial>
+// CIR:   cir.call @_ZN10NonTrivialC1ERKS_(%[[ITR_LOAD]], %[[RHS_ELT]]) : 
(!cir.ptr<!rec_NonTrivial> {{.*}}, !cir.ptr<!rec_NonTrivial> {{.*}}) -> ()
+// CIR:   %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CIR:   %[[NEXT_ITR:.*]] = cir.ptr_stride %[[ITR_LOAD]], %[[ONE]] : 
(!cir.ptr<!rec_NonTrivial>, !s64i) -> !cir.ptr<!rec_NonTrivial>
+// CIR:   cir.store align(8) %[[NEXT_ITR]], %[[ITR_ALLOCA]] : 
!cir.ptr<!rec_NonTrivial>, !cir.ptr<!cir.ptr<!rec_NonTrivial>>
+// CIR:   cir.yield
+// CIR: } while {
+// CIR:   %[[ITR_LOAD:.*]] = cir.load {{.*}}%[[ITR_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_NonTrivial>>, !cir.ptr<!rec_NonTrivial>
+// CIR:   %[[COND:.*]] = cir.cmp ne %[[ITR_LOAD]], %[[END_ITR]] : 
!cir.ptr<!rec_NonTrivial>
+// CIR:   cir.condition(%[[COND]])
+// CIR: }
+// CIR: cir.return
+
+// CIR-LABEL: cir.func {{.*}}@_ZN18HasNonTrivialArrayC1ERKS_(
+// CIR: cir.call @_ZN18HasNonTrivialArrayC2ERKS_(
+// CIR-LABEL: cir.func{{.*}}make_copy(
+// CIR: cir.call @_ZN18HasNonTrivialArrayC1ERKS_(
+
+
+// LLVM-LABEL: define {{.*}}@_ZN18HasNonTrivialArrayC2ERKS_(
+// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[RHS_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[ITR_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
+// LLVM: %[[THIS_ARR:.*]] = getelementptr %struct.HasNonTrivialArray, ptr 
%[[THIS_LOAD]], i32 0, i32 0
+// LLVM: %[[RHS_LOAD:.*]] = load ptr, ptr %[[RHS_ALLOCA]]
+// LLVM: %[[RHS_ARR:.*]] = getelementptr %struct.HasNonTrivialArray, ptr 
%[[RHS_LOAD]], i32 0, i32 0
+// LLVM: %[[THIS_ARR_DECAY:.*]] = getelementptr %struct.NonTrivial, ptr 
%[[THIS_ARR]], i32 0
+// LLVM: store ptr %[[THIS_ARR_DECAY]], ptr %[[ITR_ALLOCA]]
+// LLVM: %[[END_ITR:.*]] = getelementptr %struct.NonTrivial, ptr 
%[[THIS_ARR_DECAY]], i64 3
+// LLVM: br label %[[BODY:.*]]
+
+// LLVM: [[COND_BLOCK:.*]]:
+// LLVM: %[[LOAD_ITR:.*]] = load ptr, ptr %[[ITR_ALLOCA]]
+// LLVM: %[[COND:.*]] = icmp ne ptr %[[LOAD_ITR]], %[[END_ITR]]
+// LLVM: br i1 %[[COND]], label %[[BODY]], label %[[ENDBLOCK:.*]]
+
+// LLVM: [[BODY]]:
+// LLVM: %[[LOAD_ITR:.*]] = load ptr, ptr %[[ITR_ALLOCA]]
+// LLVM: %[[ITR_PTR:.*]] = ptrtoint ptr %[[LOAD_ITR]] to i64
+// LLVM: %[[START_PTR:.*]] = ptrtoint ptr %[[THIS_ARR_DECAY]] to i64
+// LLVM: %[[PTR_DIFF:.*]] = sub i64 %[[ITR_PTR]], %[[START_PTR]]
+// LLVM: %[[IDX:.*]] = sdiv exact i64 %[[PTR_DIFF]], 4
+// LLVM: %[[RHS_ELT:.*]] = getelementptr [3 x %struct.NonTrivial], ptr 
%[[RHS_ARR]], i32 0, i64 %[[IDX]]
+// LLVM: call void @_ZN10NonTrivialC1ERKS_(ptr {{.*}}%[[LOAD_ITR]], ptr 
{{.*}}%[[RHS_ELT]])
+// LLVM: %[[NEXT_ITR:.*]] = getelementptr %struct.NonTrivial, ptr 
%[[LOAD_ITR]], i64 1
+// LLVM: store ptr %[[NEXT_ITR]], ptr %[[ITR_ALLOCA]]
+// LLVM: br label %[[COND_BLOCK]]
+
+// LLVM: [[ENDBLOCK]]: 
+// LLVM: ret void
+
+// LLVM-LABEL: define {{.*}}@_ZN18HasNonTrivialArrayC1ERKS_(
+// LLVM: call void @_ZN18HasNonTrivialArrayC2ERKS_(
+// LLVM-LABEL: define dso_local %struct.HasNonTrivialArray @make_copy(
+// LLVM: call void @_ZN18HasNonTrivialArrayC1ERKS_(
+
+// OGCG-LABEL: define {{.*}}@make_copy(
+// OGCG: call void @_ZN18HasNonTrivialArrayC1ERKS_(
+//
+// OGCG-LABEL: define {{.*}}@_ZN18HasNonTrivialArrayC1ERKS_(
+// OGCG: call void @_ZN18HasNonTrivialArrayC2ERKS_(
+//
+// Note: CIR lowering puts these in a different order, Classic codegen seems to
+// emit these early and the bases later, so these two declarations are out of
+// order.
+// OGCG-LABEL: define {{.*}}@make_multi_copy(
+// OGCG: call void @_ZN16HasMultiDimArrayC1ERKS_(
+
+// OGCG-LABEL: define {{.*}}@_ZN16HasMultiDimArrayC1ERKS_(
+// OGCG: call void @_ZN16HasMultiDimArrayC2ERKS_(
+
+// OGCG-LABEL: define {{.*}}@_ZN18HasNonTrivialArrayC2ERKS_(
+// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr
+// OGCG: %[[RHS_ALLOCA:.*]] = alloca ptr
+// OGCG: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
+// OGCG: %[[THIS_ARR:.*]] = getelementptr inbounds nuw 
%struct.HasNonTrivialArray, ptr %[[THIS_LOAD]], i32 0, i32 0
+// OGCG: %[[RHS_LOAD:.*]] = load ptr, ptr %[[RHS_ALLOCA]]
+// OGCG: %[[RHS_ARR:.*]] = getelementptr inbounds nuw 
%struct.HasNonTrivialArray, ptr %[[RHS_LOAD]], i32 0, i32 0
+// OGCG: %[[ITR_BEGIN:.*]] = getelementptr inbounds [3 x %struct.NonTrivial], 
ptr %[[THIS_ARR]], i64 0, i64 0
+// OGCG: br label %[[ARR_BODY:.*]]
+
+// OGCG: [[ARR_BODY]]:
+// OGCG: %[[IDX:.*]] = phi i64 [ 0, %entry ], [ %[[ITR_NEXT:.*]], 
%[[ARR_BODY]] ]
+// OGCG: %[[ITR:.*]] = getelementptr inbounds %struct.NonTrivial, ptr 
%[[ITR_BEGIN]], i64 %[[IDX]]
+// OGCG: %[[RHS_ITR:.*]] = getelementptr inbounds nuw [3 x 
%struct.NonTrivial], ptr %[[RHS_ARR]], i64 0, i64 %[[IDX]]
+// OGCG: call void @_ZN10NonTrivialC1ERKS_(ptr {{.*}}%[[ITR]], ptr 
{{.*}}%[[RHS_ITR]])
+// OGCG: %[[ITR_NEXT]] = add nuw i64 %[[IDX]], 1
+// OGCG: %[[COND:.*]] = icmp eq i64 %[[ITR_NEXT]], 3
+// OGCG: br i1 %[[COND]], label %[[END_BLOCK:.*]], label %[[ARR_BODY]]
+
+// OGCG: [[END_BLOCK]]:
+// OGCG:   ret void
+
+extern "C" HasNonTrivialArray make_copy(const HasNonTrivialArray &src) {
+    return src;
+}
+
+// Multi-dimensional: the outer loop iterates over rows, the inner loop
+// (a nested ArrayInitLoopExpr) iterates over columns.
+struct HasMultiDimArray {
+    NonTrivial arr[2][3][4];
+};
+
+// CIR-LABEL: cir.func {{.*}}@_ZN16HasMultiDimArrayC2ERKS_({{.*}}) 
special_member<#cir.cxx_ctor<!rec_HasMultiDimArray, copy>> 
+// CIR: %[[THIS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_HasMultiDimArray>, 
!cir.ptr<!cir.ptr<!rec_HasMultiDimArray>>, ["this", init]
+// CIR: %[[RHS_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_HasMultiDimArray>, 
!cir.ptr<!cir.ptr<!rec_HasMultiDimArray>>, ["", init, const]
+// CIR: %[[ITR1_ALLOCA:.*]] = cir.alloca 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>, 
!cir.ptr<!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>>, 
["arrayinit.temp"]
+// CIR: %[[ITR2_ALLOCA:.*]] = cir.alloca !cir.ptr<!cir.array<!rec_NonTrivial x 
4>>, !cir.ptr<!cir.ptr<!cir.array<!rec_NonTrivial x 4>>>, ["arrayinit.temp"]
+// CIR: %[[ITR3_ALLOCA:.*]] = cir.alloca !cir.ptr<!rec_NonTrivial>, 
!cir.ptr<!cir.ptr<!rec_NonTrivial>>, ["arrayinit.temp"] {alignment = 8 : i64}
+// CIR: %[[THIS_LOAD:.*]] = cir.load %[[THIS_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_HasMultiDimArray>>, !cir.ptr<!rec_HasMultiDimArray>
+// CIR: %[[THIS_ARR:.*]] = cir.get_member %[[THIS_LOAD]][0] {name = "arr"} : 
!cir.ptr<!rec_HasMultiDimArray> -> 
!cir.ptr<!cir.array<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3> x 2>>
+// CIR: %[[RHS_LOAD:.*]] = cir.load %[[RHS_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_HasMultiDimArray>>, !cir.ptr<!rec_HasMultiDimArray>
+// CIR: %[[RHS_ARR:.*]] = cir.get_member %[[RHS_LOAD]][0] {name = "arr"} : 
!cir.ptr<!rec_HasMultiDimArray> -> 
!cir.ptr<!cir.array<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3> x 2>>
+// CIR: %[[THIS_ARR_DECAY:.*]] = cir.cast array_to_ptrdecay %[[THIS_ARR]] : 
!cir.ptr<!cir.array<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3> x 2>> -> 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR: cir.store {{.*}}%[[THIS_ARR_DECAY]], %[[ITR1_ALLOCA]] : 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>, 
!cir.ptr<!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>>
+// CIR: %[[SIZE1_CONST:.*]] = cir.const #cir.int<2> : !s64i
+// CIR: %[[END_ITR1:.*]] = cir.ptr_stride %[[THIS_ARR_DECAY]], 
%[[SIZE1_CONST]] : (!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>, 
!s64i) -> !cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR: cir.do {
+// CIR:   %[[ITR1_LOAD:.*]] = cir.load {{.*}}%[[ITR1_ALLOCA]] : 
!cir.ptr<!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>>, 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR:   %[[IDX1:.*]] = cir.ptr_diff %[[ITR1_LOAD]], %[[THIS_ARR_DECAY]] : 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>> -> !s64i
+// CIR:   %[[RHS_ELT1:.*]] = cir.get_element %[[RHS_ARR]][%[[IDX1]] : !s64i] : 
!cir.ptr<!cir.array<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3> x 2>> -> 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR:   %[[ARR1_DECAY:.*]] = cir.cast array_to_ptrdecay %[[ITR1_LOAD]] : 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>> -> 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:   cir.store {{.*}}%[[ARR1_DECAY]], %[[ITR2_ALLOCA]] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>, 
!cir.ptr<!cir.ptr<!cir.array<!rec_NonTrivial x 4>>>
+// CIR:   %[[SIZE2_CONST:.*]] = cir.const #cir.int<3> : !s64i
+// CIR:   %[[END_ITR2:.*]] = cir.ptr_stride %[[ARR1_DECAY]], %[[SIZE2_CONST]] 
: (!cir.ptr<!cir.array<!rec_NonTrivial x 4>>, !s64i) -> 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:   cir.do {
+// CIR:     %[[ITR2_LOAD:.*]] = cir.load {{.*}}%[[ITR2_ALLOCA]] : 
!cir.ptr<!cir.ptr<!cir.array<!rec_NonTrivial x 4>>>, 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:     %[[IDX2:.*]] = cir.ptr_diff %[[ITR2_LOAD]], %[[ARR1_DECAY]] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>> -> !s64i
+// CIR:     %[[RHS_ELT2:.*]] = cir.get_element %[[RHS_ELT1]][%[[IDX2]] : 
!s64i] : !cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>> -> 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:     %[[ARR2_DECAY:.*]] = cir.cast array_to_ptrdecay %[[ITR2_LOAD]] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>> -> !cir.ptr<!rec_NonTrivial>
+// CIR:     cir.store {{.*}}%[[ARR2_DECAY]], %[[ITR3_ALLOCA]] : 
!cir.ptr<!rec_NonTrivial>, !cir.ptr<!cir.ptr<!rec_NonTrivial>>
+// CIR:     %[[SIZE3_CONST:.*]] = cir.const #cir.int<4> : !s64i
+// CIR:     %[[END_ITR3:.*]] = cir.ptr_stride %[[ARR2_DECAY]], 
%[[SIZE3_CONST]] : (!cir.ptr<!rec_NonTrivial>, !s64i) -> 
!cir.ptr<!rec_NonTrivial>
+// CIR:     cir.do {
+// CIR:       %[[ITR3_LOAD:.*]] = cir.load {{.*}}%[[ITR3_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_NonTrivial>>, !cir.ptr<!rec_NonTrivial>
+// CIR:       %[[IDX3:.*]] = cir.ptr_diff %[[ITR3_LOAD]], %[[ARR2_DECAY]] : 
!cir.ptr<!rec_NonTrivial> -> !s64i
+// CIR:       %[[RHS_ELT3:.*]] = cir.get_element %[[RHS_ELT2]][%[[IDX3]] : 
!s64i] : !cir.ptr<!cir.array<!rec_NonTrivial x 4>> -> !cir.ptr<!rec_NonTrivial>
+// CIR:       cir.call @_ZN10NonTrivialC1ERKS_(%[[ITR3_LOAD]], %[[RHS_ELT3]]) 
: (!cir.ptr<!rec_NonTrivial> {{.*}}, !cir.ptr<!rec_NonTrivial> {{.*}}) -> ()
+// CIR:       %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CIR:       %[[NEXT_ITR3:.*]] = cir.ptr_stride %[[ITR3_LOAD]], %[[ONE]] : 
(!cir.ptr<!rec_NonTrivial>, !s64i) -> !cir.ptr<!rec_NonTrivial>
+// CIR:       cir.store {{.*}}%[[NEXT_ITR3]], %[[ITR3_ALLOCA]] : 
!cir.ptr<!rec_NonTrivial>, !cir.ptr<!cir.ptr<!rec_NonTrivial>>
+// CIR:       cir.yield
+// CIR:     } while {
+// CIR:     %[[ITR3_LOAD:.*]] = cir.load {{.*}}%[[ITR3_ALLOCA]] : 
!cir.ptr<!cir.ptr<!rec_NonTrivial>>, !cir.ptr<!rec_NonTrivial>
+// CIR:     %[[COND3:.*]] = cir.cmp ne %[[ITR3_LOAD]], %[[END_ITR3]] : 
!cir.ptr<!rec_NonTrivial>
+// CIR:     cir.condition(%[[COND3]])
+// CIR:     }
+// CIR:     %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CIR:     %[[NEXT_ITR2:.*]] = cir.ptr_stride %[[ITR2_LOAD]], %[[ONE]] : 
(!cir.ptr<!cir.array<!rec_NonTrivial x 4>>, !s64i) -> 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:     cir.store {{.*}}%[[NEXT_ITR2]], %[[ITR2_ALLOCA]] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>, 
!cir.ptr<!cir.ptr<!cir.array<!rec_NonTrivial x 4>>>
+// CIR:     cir.yield
+// CIR:   } while {
+// CIR:   %[[ITR2_LOAD:.*]] = cir.load align(8) %[[ITR2_ALLOCA]] : 
!cir.ptr<!cir.ptr<!cir.array<!rec_NonTrivial x 4>>>, 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:   %[[COND2:.*]] = cir.cmp ne %[[ITR2_LOAD]], %[[END_ITR2]] : 
!cir.ptr<!cir.array<!rec_NonTrivial x 4>>
+// CIR:   cir.condition(%[[COND2]])
+// CIR:   }
+// CIR:   %[[ONE:.*]] = cir.const #cir.int<1> : !s64i
+// CIR:   %[[NEXT_ITR1:.*]] = cir.ptr_stride %[[ITR1_LOAD]], %[[ONE]] : 
(!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>, !s64i) -> 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR:   cir.store {{.*}}%[[NEXT_ITR1]], %[[ITR1_ALLOCA]] : 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>, 
!cir.ptr<!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>>
+// CIR:   cir.yield
+// CIR: } while {
+// CIR: %[[ITR1_LOAD:.*]] = cir.load {{.*}}%[[ITR1_ALLOCA]] : 
!cir.ptr<!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>>, 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR: %[[COND1:.*]] = cir.cmp ne %[[ITR1_LOAD]], %[[END_ITR1]] : 
!cir.ptr<!cir.array<!cir.array<!rec_NonTrivial x 4> x 3>>
+// CIR: cir.condition(%[[COND1]])
+// CIR: }
+// CIR: cir.return
+
+// CIR-LABEL: cir.func {{.*}}@_ZN16HasMultiDimArrayC1ERKS_(
+// CIR:    cir.call @_ZN16HasMultiDimArrayC2ERKS_(
+
+// CIR-LABEL: cir.func{{.*}}make_multi_copy(
+// CIR: cir.call @_ZN16HasMultiDimArrayC1ERKS_(
+
+// LLVM-LABEL: define linkonce_odr void @_ZN16HasMultiDimArrayC2ERKS_(
+// LLVM: %[[THIS_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[RHS_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[ITR1_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[ITR2_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[ITR3_ALLOCA:.*]] = alloca ptr
+// LLVM: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
+// LLVM: %[[THIS_ARR:.*]] = getelementptr %struct.HasMultiDimArray, ptr 
%[[THIS_LOAD]], i32 0, i32 0
+// LLVM: %[[RHS_LOAD:.*]] = load ptr, ptr %[[RHS_ALLOCA]]
+// LLVM: %[[RHS_ARR:.*]] = getelementptr %struct.HasMultiDimArray, ptr 
%[[RHS_LOAD]], i32 0, i32 0
+// LLVM: %[[THIS_ARR_DECAY:.*]] = getelementptr [3 x [4 x 
%struct.NonTrivial]], ptr %[[THIS_ARR]], i32 0
+// LLVM: store ptr %[[THIS_ARR_DECAY]], ptr %[[ITR1_ALLOCA]]
+// LLVM: %[[END_ITR1:.*]] = getelementptr [3 x [4 x %struct.NonTrivial]], ptr 
%[[THIS_ARR_DECAY]], i64 2
+// LLVM: br label %[[WHILE1_BODY:.*]]
+
+// LLVM: [[WHILE1_COND:.*]]:
+// LLVM: %[[ITR1_LOAD:.*]] = load ptr, ptr %[[ITR1_ALLOCA]]
+// LLVM: %[[COND1:.*]] = icmp ne ptr %[[ITR1_LOAD]], %[[END_ITR1]]
+// LLVM: br i1 %[[COND1]], label %[[WHILE1_BODY]], label %[[END_BLOCK:.*]]
+
+// LLVM: [[WHILE1_BODY]]:
+// LLVM: %[[ITR1_LOAD:.*]] = load ptr, ptr %[[ITR1_ALLOCA]]
+// LLVM: %[[ITR1_PTR:.*]] = ptrtoint ptr %[[ITR1_LOAD]] to i64
+// LLVM: %[[ITR1_START_PTR:.*]] = ptrtoint ptr %[[THIS_ARR_DECAY]] to i64
+// LLVM: %[[PTR_DIFF:.*]] = sub i64 %[[ITR1_PTR]], %[[ITR1_START_PTR]]
+// LLVM: %[[IDX1:.*]] = sdiv exact i64 %[[PTR_DIFF]], 48
+// LLVM: %[[RHS_ELT1:.*]] = getelementptr [2 x [3 x [4 x 
%struct.NonTrivial]]], ptr %[[RHS_ARR]], i32 0, i64 %[[IDX1]]
+// LLVM: %[[ARR1_DECAY:.*]] = getelementptr [4 x %struct.NonTrivial], ptr 
%[[ITR1_LOAD]], i32 0
+// LLVM: store ptr %[[ARR1_DECAY]], ptr %[[ITR2_ALLOCA]]
+// LLVM: %[[END_ITR2:.*]] = getelementptr [4 x %struct.NonTrivial], ptr 
%[[ARR1_DECAY]], i64 3
+// LLVM: br label %[[WHILE2_BODY:.*]]
+
+// LLVM: [[WHILE2_COND:.*]]:
+// LLVM: %[[ITR2_LOAD:.*]] = load ptr, ptr %[[ITR2_ALLOCA]]
+// LLVM: %[[COND2:.*]] = icmp ne ptr %[[ITR2_LOAD]], %[[END_ITR2]]
+// LLVM: br i1 %[[COND2]], label %[[WHILE2_BODY]], label 
%[[WHILE1_BODY_REST:.*]]
+
+// LLVM: [[WHILE2_BODY]]:
+// LLVM: %[[ITR2_LOAD:.*]] = load ptr, ptr %[[ITR2_ALLOCA]]
+// LLVM: %[[ITR2_PTR:.*]] = ptrtoint ptr %[[ITR2_LOAD]] to i64
+// LLVM: %[[ITR2_START_PTR:.*]] = ptrtoint ptr %[[ARR1_DECAY]] to i64
+// LLVM: %[[PTR_DIFF:.*]] = sub i64 %[[ITR2_PTR]], %[[ITR2_START_PTR]]
+// LLVM: %[[IDX2:.*]] = sdiv exact i64 %[[PTR_DIFF]], 16
+// LLVM: %[[RHS_ELT2:.*]] = getelementptr [3 x [4 x %struct.NonTrivial]], ptr 
%[[RHS_ELT1]], i32 0, i64 %[[IDX2]]
+// LLVM: %[[ARR2_DECAY:.*]] = getelementptr %struct.NonTrivial, ptr 
%[[ITR2_LOAD]], i32 0
+// LLVM: store ptr %[[ARR2_DECAY]], ptr %[[ITR3_ALLOCA]]
+// LLVM: %[[END_ITR3:.*]] = getelementptr %struct.NonTrivial, ptr 
%[[ARR2_DECAY]], i64 4
+// LLVM: br label %[[WHILE3_BODY:.*]]
+
+// LLVM: [[WHILE3_COND:.*]]:
+// LLVM: %[[ITR3_LOAD:.*]] = load ptr, ptr %[[ITR3_ALLOCA]]
+// LLVM: %[[COND3:.*]] = icmp ne ptr %[[ITR3_LOAD]], %[[END_ITR3]]
+// LLVM: br i1 %[[COND3]], label %[[WHILE3_BODY:.*]], label 
%[[WHILE2_BODY_REST:.*]]
+
+// LLVM: [[WHILE3_BODY]]:
+// LLVM: %[[ITR3_LOAD:.*]] = load ptr, ptr %[[ITR3_ALLOCA]]
+// LLVM: %[[ITR3_PTR:.*]] = ptrtoint ptr %[[ITR3_LOAD]] to i64
+// LLVM: %[[ITR3_START_PTR:.*]] = ptrtoint ptr %[[ARR2_DECAY]] to i64
+// LLVM: %[[PTR_DIFF:.*]] = sub i64 %[[ITR3_PTR]], %[[ITR3_START_PTR]]
+// LLVM: %[[IDX3:.*]] = sdiv exact i64 %[[PTR_DIFF]], 4
+// LLVM: %[[RHS_ELT3:.*]] = getelementptr [4 x %struct.NonTrivial], ptr 
%[[RHS_ELT2]], i32 0, i64 %[[IDX3]]
+// LLVM: call void @_ZN10NonTrivialC1ERKS_(ptr{{.*}} %[[ITR3_LOAD]], ptr{{.*}} 
%[[RHS_ELT3]])
+// LLVM: %[[NEXT_ITR3:.*]] = getelementptr %struct.NonTrivial, ptr 
%[[ITR3_LOAD]], i64 1
+// LLVM: store ptr %[[NEXT_ITR3]], ptr %[[ITR3_ALLOCA]]
+// LLVM: br label %[[WHILE3_COND]]
+
+// LLVM: [[WHILE2_BODY_REST]]:
+// LLVM: %[[NEXT_ITR2:.*]] = getelementptr [4 x %struct.NonTrivial], ptr 
%[[ITR2_LOAD]], i64 1
+// LLVM: store ptr %[[NEXT_ITR2]], ptr %[[ITR2_ALLOCA]]
+// LLVM: br label %[[WHILE2_COND]]
+
+// LLVM: [[WHILE1_BODY_REST]]:
+// LLVM: %[[NEXT_ITR1:.*]] = getelementptr [3 x [4 x %struct.NonTrivial]], ptr 
%[[ITR1_LOAD]], i64 1
+// LLVM: store ptr %[[NEXT_ITR1]], ptr %[[ITR1_ALLOCA]]
+// LLVM: br label %[[WHILE1_COND]]
+
+// LLVM: [[END_BLOCK]]:
+// LLVM:   ret void
+
+// LLVM-LABEL: define {{.*}}@_ZN16HasMultiDimArrayC1ERKS_(
+// LLVM: call void @_ZN16HasMultiDimArrayC2ERKS_(
+// LLVM-LABEL: define {{.*}}@make_multi_copy(
+// LLVM: call void @_ZN16HasMultiDimArrayC1ERKS_(
+
+// OGCG-LABEL: define linkonce_odr void @_ZN16HasMultiDimArrayC2ERKS_(
+// OGCG: %[[THIS_ALLOCA:.*]] = alloca ptr
+// OGCG: %[[RHS_ALLOCA:.*]] = alloca ptr
+// OGCG: %[[THIS_LOAD:.*]] = load ptr, ptr %[[THIS_ALLOCA]]
+// OGCG: %[[THIS_ARR:.*]] = getelementptr inbounds nuw 
%struct.HasMultiDimArray, ptr %[[THIS_LOAD]], i32 0, i32 0
+// OGCG: %[[RHS_LOAD:.*]] = load ptr, ptr %[[RHS_ALLOCA]]
+// OGCG: %[[RHS_ARR:.*]] = getelementptr inbounds nuw 
%struct.HasMultiDimArray, ptr %[[RHS_LOAD]], i32 0, i32 0
+// OGCG: %[[ITR1_BEGIN:.*]] = getelementptr inbounds [2 x [3 x [4 x 
%struct.NonTrivial]]], ptr %[[THIS_ARR]], i64 0, i64 0
+// OGCG: br label %[[ARR_BODY1:.*]]
+
+// OGCG: [[ARR_BODY1]]:
+// OGCG: %[[IDX1:.*]] = phi i64 [ 0, %entry ], [ %[[ITR1_NEXT:.*]], 
%[[ARR_BODY1_CTD:.*]] ]
+// OGCG: %[[ITR1:.*]] = getelementptr inbounds [3 x [4 x %struct.NonTrivial]], 
ptr %[[ITR1_BEGIN]], i64 %[[IDX1]]
+// OGCG: %[[RHS_ITR1:.*]] = getelementptr inbounds nuw [2 x [3 x [4 x 
%struct.NonTrivial]]], ptr %[[RHS_ARR]], i64 0, i64 %[[IDX1]]
+// OGCG: %[[ITR2_BEGIN:.*]] = getelementptr inbounds [3 x [4 x 
%struct.NonTrivial]], ptr %[[ITR1]], i64 0, i64 0
+// OGCG: br label %[[ARR_BODY2:.*]]
+
+// OGCG: [[ARR_BODY2]]:
+// OGCG: %[[IDX2:.*]] = phi i64 [ 0, %[[ARR_BODY1]] ], [ %[[ITR2_NEXT:.*]], 
%[[ARR_BODY2_CTD:.*]] ]
+// OGCG: %[[ITR2:.*]] = getelementptr inbounds [4 x %struct.NonTrivial], ptr 
%[[ITR2_BEGIN]], i64 %[[IDX2]]
+// OGCG: %[[RHS_ITR2:.*]] = getelementptr inbounds nuw [3 x [4 x 
%struct.NonTrivial]], ptr %[[RHS_ITR1]], i64 0, i64 %[[IDX2]]
+// OGCG: %[[ITR3_BEGIN:.*]] = getelementptr inbounds [4 x %struct.NonTrivial], 
ptr %[[ITR2]], i64 0, i64 0
+// OGCG: br label %[[ARR_BODY3:.*]]
+
+// OGCG: [[ARR_BODY3]]:
+// OGCG: %[[IDX3:.*]] = phi i64 [ 0, %[[ARR_BODY2]] ], [ %[[ITR3_NEXT:.*]], 
%[[ARR_BODY3]] ]
+// OGCG: %[[ITR3:.*]] = getelementptr inbounds %struct.NonTrivial, ptr 
%[[ITR3_BEGIN]], i64 %[[IDX3]]
+// OGCG: %[[RHS_ITR3:.*]] = getelementptr inbounds nuw [4 x 
%struct.NonTrivial], ptr %[[RHS_ITR2]], i64 0, i64 %[[IDX3]]
+// OGCG: call void @_ZN10NonTrivialC1ERKS_(ptr {{.*}}%[[ITR3]], ptr 
{{.*}}%[[RHS_ITR3]])
+// OGCG: %[[ITR3_NEXT]] = add nuw i64 %[[IDX3]], 1
+// OGCG: %[[COND3:.*]] = icmp eq i64 %[[ITR3_NEXT]], 4
+// OGCG: br i1 %[[COND3]], label %[[ARR_BODY2_CTD]], label %[[ARR_BODY3]]
+
+// OGCG: [[ARR_BODY2_CTD]]:
+// OGCG: %[[ITR2_NEXT]] = add nuw i64 %[[IDX2]], 1
+// OGCG: %[[COND2:.*]] = icmp eq i64 %[[ITR2_NEXT]], 3
+// OGCG: br i1 %[[COND2]], label %[[ARR_BODY1_CTD]], label %[[ARR_BODY2]]
+
+// OGCG: [[ARR_BODY1_CTD]]:
+// OGCG: %[[ITR1_NEXT]] = add nuw i64 %[[IDX1]], 1
+// OGCG: %[[COND1:.*]] = icmp eq i64 %[[ITR1_NEXT]], 2
+// OGCG: br i1 %[[COND1]], label %[[END_BLOCK:.*]], label %[[ARR_BODY1]]
+
+// OGCG: [[END_BLOCK]]:
+// OGCG: ret void
+extern "C" HasMultiDimArray make_multi_copy(const HasMultiDimArray &src) {
+    return src;
+}

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to