Author: Andy Kaylor
Date: 2026-04-09T15:07:50-07:00
New Revision: 5f27de8c4d895f2128f7ca5ce788d997fd3ea99c

URL: 
https://github.com/llvm/llvm-project/commit/5f27de8c4d895f2128f7ca5ce788d997fd3ea99c
DIFF: 
https://github.com/llvm/llvm-project/commit/5f27de8c4d895f2128f7ca5ce788d997fd3ea99c.diff

LOG: [CIR] Implement variable size array cleanup (#191247)

This implements partial array destruction for variable sized arrays. The
cir.array.dtor operation already had support for variable length, so
this change only needs to add the variable handling in
`emitArrayDestroy` and `emitArrayLength`.

Assisted-by: Cursor / claude-4.6-opus-high

Added: 
    

Modified: 
    clang/lib/CIR/CodeGen/CIRGenDecl.cpp
    clang/lib/CIR/CodeGen/CIRGenFunction.cpp
    clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
    clang/test/CIR/CodeGen/partial-array-cleanup.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp 
b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
index c3c0ec1c1af0d..b96b822609c10 100644
--- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp
@@ -1039,7 +1039,6 @@ void CIRGenFunction::pushLifetimeExtendedCleanupToEHStack(
 }
 
 /// Destroys all the elements of the given array, beginning from last to first.
-/// The array cannot be zero-length.
 ///
 /// \param begin - a type* denoting the first element of the array
 /// \param numElements - the number of elements in the array
@@ -1057,33 +1056,33 @@ void CIRGenFunction::emitArrayDestroy(mlir::Value begin,
   mlir::Type cirElementType = convertTypeForMem(elementType);
   cir::PointerType ptrToElmType = builder.getPointerTo(cirElementType);
 
-  uint64_t size = 0;
+  auto regionBuilder = [&](mlir::OpBuilder &b, mlir::Location loc) {
+    mlir::BlockArgument arg =
+        b.getInsertionBlock()->addArgument(ptrToElmType, loc);
+    Address curAddr = Address(arg, cirElementType, elementAlign);
+    assert(!cir::MissingFeatures::dtorCleanups());
 
-  // Optimize for a constant array size.
+    // Perform the actual destruction there.
+    destroyer(*this, curAddr, elementType);
+
+    cir::YieldOp::create(b, loc);
+  };
+
+  // For a constant array size, use the static form of ArrayDtor.
   if (auto constantCount = numElements.getDefiningOp<cir::ConstantOp>()) {
+    uint64_t size = 0;
     if (auto constIntAttr = constantCount.getValueAttr<cir::IntAttr>())
       size = constIntAttr.getUInt();
-  } else {
-    cgm.errorNYI(begin.getDefiningOp()->getLoc(),
-                 "emitArrayDestroy: dynamic-length array expression");
+    auto arrayTy = cir::ArrayType::get(cirElementType, size);
+    mlir::Value arrayOp = builder.createPtrBitcast(begin, arrayTy);
+    cir::ArrayDtor::create(builder, *currSrcLoc, arrayOp, regionBuilder);
+    return;
   }
 
-  auto arrayTy = cir::ArrayType::get(cirElementType, size);
-  mlir::Value arrayOp = builder.createPtrBitcast(begin, arrayTy);
-
-  // Emit the dtor call that will execute for every array element.
-  cir::ArrayDtor::create(
-      builder, *currSrcLoc, arrayOp,
-      [&](mlir::OpBuilder &b, mlir::Location loc) {
-        auto arg = b.getInsertionBlock()->addArgument(ptrToElmType, loc);
-        Address curAddr = Address(arg, cirElementType, elementAlign);
-        assert(!cir::MissingFeatures::dtorCleanups());
-
-        // Perform the actual destruction there.
-        destroyer(*this, curAddr, elementType);
-
-        cir::YieldOp::create(builder, loc);
-      });
+  // For a dynamic array size (VLA), use the dynamic form of ArrayDtor.
+  mlir::Value elemBegin = builder.createPtrBitcast(begin, cirElementType);
+  cir::ArrayDtor::create(builder, *currSrcLoc, elemBegin, numElements,
+                         regionBuilder);
 }
 
 /// Immediately perform the destruction of the given object.
@@ -1104,24 +1103,20 @@ void CIRGenFunction::emitDestroy(Address addr, QualType 
type,
   CharUnits elementAlign = addr.getAlignment().alignmentOfArrayElement(
       getContext().getTypeSizeInChars(type));
 
+  // If the array length is constant, we can check for zero at compile time.
   auto constantCount = length.getDefiningOp<cir::ConstantOp>();
-  if (!constantCount) {
-    assert(!cir::MissingFeatures::vlas());
-    cgm.errorNYI("emitDestroy: variable length array");
-    return;
+  if (constantCount) {
+    auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
+    if (constIntAttr && constIntAttr.getUInt() == 0)
+      return;
   }
 
-  auto constIntAttr = mlir::dyn_cast<cir::IntAttr>(constantCount.getValue());
-  // If it's constant zero, we can just skip the entire thing.
-  if (constIntAttr && constIntAttr.getUInt() == 0)
-    return;
-
   mlir::Value begin = addr.getPointer();
   assert(!cir::MissingFeatures::useEHCleanupForArray());
   emitArrayDestroy(begin, length, type, elementAlign, destroyer);
 
   // If the array destroy didn't use the length op, we can erase it.
-  if (constantCount.use_empty())
+  if (constantCount && constantCount.use_empty())
     constantCount.erase();
 }
 

diff  --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp 
b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 572addda77cec..f7f9331060956 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1231,12 +1231,32 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType 
*origArrayType,
 
   // If it's a VLA, we have to load the stored size.  Note that
   // this is the size of the VLA in bytes, not its size in elements.
+  mlir::Value numVLAElements = nullptr;
   if (isa<VariableArrayType>(arrayType)) {
-    assert(!cir::MissingFeatures::vlas());
-    cgm.errorNYI(*currSrcLoc, "VLAs");
-    return builder.getConstInt(*currSrcLoc, sizeTy, 0);
+    numVLAElements = getVLASize(cast<VariableArrayType>(arrayType)).numElts;
+
+    // Walk into all VLAs.  This doesn't require changes to addr,
+    // which has type T* where T is the first non-VLA element type.
+    do {
+      QualType elementType = arrayType->getElementType();
+      arrayType = getContext().getAsArrayType(elementType);
+
+      // If we only have VLA components, 'addr' requires no adjustment.
+      if (!arrayType) {
+        baseType = elementType;
+        return numVLAElements;
+      }
+    } while (isa<VariableArrayType>(arrayType));
+
+    // We get out here only if we find a constant array type
+    // inside the VLA.
   }
 
+  // Classic codegen emits an all-zero inbounds GEP to convert addr from
+  // [M x [N x T]]* to T*. CIR doesn't need this because callers handle
+  // the array-to-element pointer conversion themselves (via array_to_ptrdecay
+  // casts, ptr_bitcast, or manual array type peeling).
+
   uint64_t countFromCLAs = 1;
   QualType eltType;
 
@@ -1263,7 +1283,17 @@ CIRGenFunction::emitArrayLength(const clang::ArrayType 
*origArrayType,
   }
 
   baseType = eltType;
-  return builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
+
+  mlir::Value numElements =
+      builder.getConstInt(*currSrcLoc, sizeTy, countFromCLAs);
+
+  // If we had any VLA dimensions, factor them in.
+  if (numVLAElements)
+    numElements =
+        builder.createMul(numVLAElements.getLoc(), numVLAElements, numElements,
+                          cir::OverflowBehavior::NoUnsignedWrap);
+
+  return numElements;
 }
 
 void CIRGenFunction::instantiateIndirectGotoBlock() {

diff  --git a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp 
b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
index 482e73f9f3050..8a9bfbf81d453 100644
--- a/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
+++ b/clang/lib/CIR/Dialect/Transforms/LoweringPrepare.cpp
@@ -1547,7 +1547,6 @@ void LoweringPreparePass::lowerArrayDtor(cir::ArrayDtor 
op) {
     return;
   }
 
-  assert(!cir::MissingFeatures::vlas());
   auto arrayLen =
       
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
   lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),
@@ -1568,7 +1567,6 @@ void LoweringPreparePass::lowerArrayCtor(cir::ArrayCtor 
op) {
     return;
   }
 
-  assert(!cir::MissingFeatures::vlas());
   auto arrayLen =
       
mlir::cast<cir::ArrayType>(op.getAddr().getType().getPointee()).getSize();
   lowerArrayDtorCtorIntoLoop(builder, astCtx, op, eltTy, op.getAddr(),

diff  --git a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp 
b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
index 7c260a6f13cb0..520170246eb8e 100644
--- a/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
+++ b/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
@@ -159,3 +159,502 @@ void test_partial_array_cleanup() {
 //
 // OGCG:     [[EH_RESUME]]:
 // OGCG:       resume { ptr, i32 }
+
+void test_variable_size_partial_array_cleanup(int n) {
+    S s[n];
+}
+
+// CIR-BEFORE-LPP:     cir.func {{.*}} 
@_Z40test_variable_size_partial_array_cleanupi
+// CIR-BEFORE-LPP:       %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, 
["n", init]
+// CIR-BEFORE-LPP:       %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : 
!cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP:       %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> 
!u64i
+// CIR-BEFORE-LPP:       cir.stacksave
+// CIR-BEFORE-LPP:       cir.cleanup.scope {
+// CIR-BEFORE-LPP:         %[[VLA:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, 
%[[N]] : !u64i, ["s", init]
+// CIR-BEFORE-LPP:         cir.array.ctor %[[VLA]], %[[N]] : !cir.ptr<!rec_S>, 
!u64i {
+// CIR-BEFORE-LPP:         ^bb0(%[[CTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:           cir.call @_ZN1SC1Ev(%[[CTOR_ARG]]) : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:         } partial_dtor {
+// CIR-BEFORE-LPP:         ^bb0(%[[DTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:           cir.call @_ZN1SD1Ev(%[[DTOR_ARG]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:         }
+// CIR-BEFORE-LPP:         cir.cleanup.scope {
+// CIR-BEFORE-LPP:           cir.yield
+// CIR-BEFORE-LPP:         } cleanup all {
+// CIR-BEFORE-LPP:           cir.array.dtor %[[VLA]], %[[N]] : 
!cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP:           ^bb0(%[[DTOR_ARG2:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:             cir.call @_ZN1SD1Ev(%[[DTOR_ARG2]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:           }
+// CIR-BEFORE-LPP:       } cleanup normal {
+// CIR-BEFORE-LPP:         cir.stackrestore
+// CIR-BEFORE-LPP:       }
+
+// CIR:     cir.func {{.*}} @_Z40test_variable_size_partial_array_cleanupi
+// CIR:       %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR:       %[[SAVED_STACK:.*]] = cir.alloca !cir.ptr<!u8i>, 
!cir.ptr<!cir.ptr<!u8i>>, ["saved_stack"]
+// CIR:       %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:       %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR:       %[[STACK:.*]] = cir.stacksave : !cir.ptr<!u8i>
+// CIR:       cir.store {{.*}} %[[STACK]], %[[SAVED_STACK]] : !cir.ptr<!u8i>, 
!cir.ptr<!cir.ptr<!u8i>>
+// CIR:       cir.cleanup.scope {
+// CIR:         %[[BEGIN:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, %[[N]] : 
!u64i, ["s", init]
+// CIR:         %[[END:.*]] = cir.ptr_stride %[[BEGIN]], %[[N]] : 
(!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
+// CIR:         %[[ZERO:.*]] = cir.const #cir.int<0> : !u64i
+// CIR:         %[[IS_NONZERO:.*]] = cir.cmp ne %[[N]], %[[ZERO]] : !u64i
+// CIR:         cir.if %[[IS_NONZERO]] {
+// CIR:           %[[ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
+// CIR:           cir.store %[[BEGIN]], %[[ITER]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
+// CIR:           cir.cleanup.scope {
+// CIR:             cir.do {
+// CIR:               %[[CUR:.*]] = cir.load %[[ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:               cir.call @_ZN1SC1Ev(%[[CUR]]) : (!cir.ptr<!rec_S>{{.*}}) 
-> ()
+// CIR:               %[[CONST1:.*]] = cir.const #cir.int<1> : !u64i
+// CIR:               %[[NEXT:.*]] = cir.ptr_stride %[[CUR]], %[[CONST1]] : 
(!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
+// CIR:               cir.store %[[NEXT]], %[[ITER]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
+// CIR:               cir.yield
+// CIR:             } while {
+// CIR:               %[[CUR2:.*]] = cir.load %[[ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:               %[[CMP:.*]] = cir.cmp ne %[[CUR2]], %[[END]] : 
!cir.ptr<!rec_S>
+// CIR:               cir.condition(%[[CMP]])
+// CIR:             }
+// CIR:             cir.yield
+// CIR:           } cleanup eh {
+// CIR:             %[[CUR3:.*]] = cir.load %[[ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:             %[[NE:.*]] = cir.cmp ne %[[CUR3]], %[[BEGIN]] : 
!cir.ptr<!rec_S>
+// CIR:             cir.if %[[NE]] {
+// CIR:               cir.do {
+// CIR:                 %[[EL:.*]] = cir.load %[[ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:                 %[[NEG1:.*]] = cir.const #cir.int<-1> : !s64i
+// CIR:                 %[[PREV:.*]] = cir.ptr_stride %[[EL]], %[[NEG1]] : 
(!cir.ptr<!rec_S>, !s64i) -> !cir.ptr<!rec_S>
+// CIR:                 cir.store %[[PREV]], %[[ITER]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
+// CIR:                 cir.call @_ZN1SD1Ev(%[[PREV]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR:                 cir.yield
+// CIR:               } while {
+// CIR:                 %[[EL2:.*]] = cir.load %[[ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:                 %[[NE2:.*]] = cir.cmp ne %[[EL2]], %[[BEGIN]] : 
!cir.ptr<!rec_S>
+// CIR:                 cir.condition(%[[NE2]])
+// CIR:               }
+// CIR:             }
+// CIR:             cir.yield
+// CIR:           }
+// CIR:         }
+//
+//              --- normal dtor ---
+// CIR:         } cleanup all {
+// CIR:           %[[LAST:.*]] = cir.ptr_stride %[[BEGIN]], %[[N]] : 
(!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
+// CIR:           %[[DTOR_NE:.*]] = cir.cmp ne %[[LAST]], %[[BEGIN]] : 
!cir.ptr<!rec_S>
+// CIR:           cir.if %[[DTOR_NE]] {
+// CIR:             %[[DTOR_ITER:.*]] = cir.alloca !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>, ["__array_idx"]
+// CIR:             cir.store %[[LAST]], %[[DTOR_ITER]] : !cir.ptr<!rec_S>, 
!cir.ptr<!cir.ptr<!rec_S>>
+// CIR:             cir.do {
+// CIR:               %[[DTOR_CUR:.*]] = cir.load %[[DTOR_ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:               %[[DTOR_NEG1:.*]] = cir.const #cir.int<-1> : !s64i
+// CIR:               %[[DTOR_PREV:.*]] = cir.ptr_stride %[[DTOR_CUR]], 
%[[DTOR_NEG1]] : (!cir.ptr<!rec_S>, !s64i) -> !cir.ptr<!rec_S>
+// CIR:               cir.store %[[DTOR_PREV]], %[[DTOR_ITER]] : 
!cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
+// CIR:               cir.call @_ZN1SD1Ev(%[[DTOR_PREV]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR:               cir.yield
+// CIR:             } while {
+// CIR:               %[[DTOR_CUR2:.*]] = cir.load %[[DTOR_ITER]] : 
!cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
+// CIR:               %[[DTOR_NE2:.*]] = cir.cmp ne %[[DTOR_CUR2]], %[[BEGIN]] 
: !cir.ptr<!rec_S>
+// CIR:               cir.condition(%[[DTOR_NE2]])
+// CIR:             }
+// CIR:           }
+// CIR:           cir.yield
+// CIR:         }
+// CIR:         cir.yield
+// CIR:       } cleanup normal {
+// CIR:         cir.stackrestore {{.*}} : !cir.ptr<!u8i>
+// CIR:         cir.yield
+// CIR:       }
+
+// LLVM:     define dso_local void 
@_Z40test_variable_size_partial_array_cleanupi
+// LLVM:       %[[N_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM:       %[[N:.*]] = sext i32 %[[N_I32]] to i64
+// LLVM:       call ptr @llvm.stacksave.p0()
+//
+//            --- VLA alloca + zero check ---
+// LLVM:       %[[BEGIN:.*]] = alloca %struct.S, i64 %[[N]]
+// LLVM:       %[[END:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 
%[[N]]
+// LLVM:       %[[IS_NONZERO:.*]] = icmp ne i64 %[[N]], 0
+// LLVM:       br i1 %[[IS_NONZERO]], label %[[CTOR_SETUP:.*]], label 
%[[AFTER_CTOR:.*]]
+//
+//            --- ctor init ---
+// LLVM:     [[CTOR_SETUP]]:
+// LLVM:       store ptr %[[BEGIN]], ptr %[[CTOR_ITER:.*]]
+// LLVM:       br label %[[CTOR_ENTRY:.*]]
+//
+// LLVM:     [[CTOR_ENTRY]]:
+// LLVM:       br label %[[CTOR_BODY:.*]]
+//
+//            --- ctor loop condition (back-edge) ---
+// LLVM:     [[CTOR_LOOP_COND:.*]]:
+// LLVM:       %[[COND_CUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM:       %[[CTOR_DONE:.*]] = icmp ne ptr %[[COND_CUR]], %[[END]]
+// LLVM:       br i1 %[[CTOR_DONE]], label %[[CTOR_BODY]], label 
%[[CTOR_EXIT:.*]]
+//
+//            --- ctor loop body ---
+// LLVM:     [[CTOR_BODY]]:
+// LLVM:       %[[CUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM:       invoke void @_ZN1SC1Ev(ptr{{.*}} %[[CUR]])
+// LLVM:         to label %[[CTOR_CONT:.*]] unwind label %[[LPAD:.*]]
+//
+// LLVM:     [[CTOR_CONT]]:
+// LLVM:       %[[NEXT:.*]] = getelementptr %struct.S, ptr %[[CUR]], i64 1
+// LLVM:       store ptr %[[NEXT]], ptr %[[CTOR_ITER]]
+// LLVM:       br label %[[CTOR_LOOP_COND]]
+//
+// LLVM:     [[CTOR_EXIT]]:
+// LLVM:       br label %[[CTOR_JOIN:.*]]
+//
+//            --- landing pad + cleanup guard ---
+// LLVM:     [[LPAD]]:
+// LLVM:       landingpad { ptr, i32 }
+// LLVM:         cleanup
+// LLVM:       %[[PAD_CUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM:       %[[GUARD:.*]] = icmp ne ptr %[[PAD_CUR]], %[[BEGIN]]
+// LLVM:       br i1 %[[GUARD]], label %[[PDTOR_ENTRY:.*]], label 
%[[EH_RESUME:.*]]
+//
+//            --- partial dtor do-while entry ---
+// LLVM:     [[PDTOR_ENTRY]]:
+// LLVM:       br label %[[PDTOR_BODY:.*]]
+//
+//            --- partial dtor loop condition (back-edge) ---
+// LLVM:     [[PDTOR_LOOP_COND:.*]]:
+// LLVM:       %[[PDTOR_CUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM:       %[[PDTOR_CONT:.*]] = icmp ne ptr %[[PDTOR_CUR]], %[[BEGIN]]
+// LLVM:       br i1 %[[PDTOR_CONT]], label %[[PDTOR_BODY]], label 
%[[PDTOR_DONE:.*]]
+//
+//            --- partial dtor loop body ---
+// LLVM:     [[PDTOR_BODY]]:
+// LLVM:       %[[PDCUR:.*]] = load ptr, ptr %[[CTOR_ITER]]
+// LLVM:       %[[PPREV:.*]] = getelementptr %struct.S, ptr %[[PDCUR]], i64 -1
+// LLVM:       store ptr %[[PPREV]], ptr %[[CTOR_ITER]]
+// LLVM:       call void @_ZN1SD1Ev(ptr{{.*}} %[[PPREV]])
+// LLVM:       br label %[[PDTOR_LOOP_COND]]
+//
+// LLVM:     [[PDTOR_DONE]]:
+// LLVM:       br label %[[EH_RESUME]]
+//
+// LLVM:     [[EH_RESUME]]:
+// LLVM:       resume { ptr, i32 }
+//
+//            --- ctor done join ---
+// LLVM:     [[CTOR_JOIN]]:
+// LLVM:       br label %[[AFTER_CTOR]]
+//
+//            --- normal dtor setup ---
+// LLVM:     [[AFTER_CTOR]]:
+// LLVM:       %[[LAST:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 
%[[N]]
+// LLVM:       %[[DTOR_NE:.*]] = icmp ne ptr %[[LAST]], %[[BEGIN]]
+// LLVM:       br i1 %[[DTOR_NE]], label %[[NDTOR_ENTRY:.*]], label 
%[[NDTOR_DONE:.*]]
+//
+// LLVM:     [[NDTOR_ENTRY]]:
+// LLVM:       store ptr %[[LAST]], ptr %[[DTOR_ITER:.*]]
+// LLVM:       br label %[[NDTOR_BODY:.*]]
+//
+//            --- normal dtor loop condition (back-edge) ---
+// LLVM:     [[NDTOR_LOOP_COND:.*]]:
+// LLVM:       %[[NDCUR_CHECK:.*]] = load ptr, ptr %[[DTOR_ITER]]
+// LLVM:       %[[NDTOR_CONT:.*]] = icmp ne ptr %[[NDCUR_CHECK]], %[[BEGIN]]
+// LLVM:       br i1 %[[NDTOR_CONT]], label %[[NDTOR_BODY]], label 
%[[NDTOR_EXIT:.*]]
+//
+//            --- normal dtor loop body ---
+// LLVM:     [[NDTOR_BODY]]:
+// LLVM:       %[[NDCUR:.*]] = load ptr, ptr %[[DTOR_ITER]]
+// LLVM:       %[[NDPREV:.*]] = getelementptr %struct.S, ptr %[[NDCUR]], i64 -1
+// LLVM:       store ptr %[[NDPREV]], ptr %[[DTOR_ITER]]
+// LLVM:       call void @_ZN1SD1Ev(ptr{{.*}} %[[NDPREV]])
+// LLVM:       br label %[[NDTOR_LOOP_COND]]
+//
+// LLVM:     [[NDTOR_EXIT]]:
+// LLVM:       br label %[[NDTOR_DONE]]
+//
+// LLVM:     [[NDTOR_DONE]]:
+// LLVM:       call void @llvm.stackrestore.p0
+// LLVM:       ret void
+
+// OGCG:     define dso_local void 
@_Z40test_variable_size_partial_array_cleanupi
+// OGCG:     [[ENTRY:.*]]:
+// OGCG:       %[[N:.*]] = zext i32 %{{.*}} to i64
+// OGCG:       %[[VLA:.*]] = alloca %struct.S, i64 %[[N]]
+// OGCG:       %[[ISEMPTY:.*]] = icmp eq i64 %[[N]], 0
+// OGCG:       br i1 %[[ISEMPTY]], label %[[CTOR_CONT:.*]], label 
%[[CTOR_LOOP_ENTRY:.*]]
+//
+//            --- ctor loop ---
+// OGCG:     [[CTOR_LOOP_ENTRY]]:
+// OGCG:       %[[CTOR_END:.*]] = getelementptr inbounds %struct.S, ptr 
%[[VLA]], i64 %[[N]]
+// OGCG:       br label %[[CTOR_LOOP:.*]]
+//
+// OGCG:     [[CTOR_LOOP]]:
+// OGCG:       %[[CUR:.*]] = phi ptr [ %[[VLA]], %[[CTOR_LOOP_ENTRY]] ], [ 
%[[NEXT:.*]], %[[INVOKE_CONT:.*]] ]
+// OGCG:       invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG:         to label %[[INVOKE_CONT]] unwind label %[[LPAD:.*]]
+//
+// OGCG:     [[INVOKE_CONT]]:
+// OGCG:       %[[NEXT]] = getelementptr inbounds %struct.S, ptr %[[CUR]], i64 
1
+// OGCG:       %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[CTOR_END]]
+// OGCG:       br i1 %[[DONE]], label %[[CTOR_CONT]], label %[[CTOR_LOOP]]
+//
+//            --- normal dtor ---
+// OGCG:     [[CTOR_CONT]]:
+// OGCG:       %[[DTOR_END:.*]] = getelementptr inbounds %struct.S, ptr 
%[[VLA]], i64 %[[N]]
+// OGCG:       %[[DTOR_ISEMPTY:.*]] = icmp eq ptr %[[VLA]], %[[DTOR_END]]
+// OGCG:       br i1 %[[DTOR_ISEMPTY]], label %[[DTOR_DONE:.*]], label 
%[[DTOR_LOOP:.*]]
+//
+// OGCG:     [[DTOR_LOOP]]:
+// OGCG:       %[[PAST:.*]] = phi ptr [ %[[DTOR_END]], %[[CTOR_CONT]] ], [ 
%[[PREV:.*]], %[[DTOR_LOOP]] ]
+// OGCG:       %[[PREV]] = getelementptr inbounds %struct.S, ptr %[[PAST]], 
i64 -1
+// OGCG:       call void @_ZN1SD1Ev(ptr{{.*}} %[[PREV]])
+// OGCG:       %[[DDONE:.*]] = icmp eq ptr %[[PREV]], %[[VLA]]
+// OGCG:       br i1 %[[DDONE]], label %[[DTOR_DONE]], label %[[DTOR_LOOP]]
+//
+// OGCG:     [[DTOR_DONE]]:
+// OGCG:       call void @llvm.stackrestore.p0(ptr %{{.*}})
+// OGCG:       ret void
+//
+//            --- landing pad + cleanup guard ---
+// OGCG:     [[LPAD]]:
+// OGCG:       landingpad { ptr, i32 }
+// OGCG:         cleanup
+// OGCG:       %[[PISEMPTY:.*]] = icmp eq ptr %[[VLA]], %[[CUR]]
+// OGCG:       br i1 %[[PISEMPTY]], label %[[PDTOR_DONE:.*]], label 
%[[PDTOR_LOOP:.*]]
+//
+//            --- partial dtor loop ---
+// OGCG:     [[PDTOR_LOOP]]:
+// OGCG:       %[[PPAST:.*]] = phi ptr [ %[[CUR]], %[[LPAD]] ], [ 
%[[PPREV:.*]], %[[PDTOR_LOOP]] ]
+// OGCG:       %[[PPREV]] = getelementptr inbounds %struct.S, ptr %[[PPAST]], 
i64 -1
+// OGCG:       call void @_ZN1SD1Ev(ptr{{.*}} %[[PPREV]])
+// OGCG:       %[[PDDONE:.*]] = icmp eq ptr %[[PPREV]], %[[VLA]]
+// OGCG:       br i1 %[[PDDONE]], label %[[PDTOR_DONE]], label %[[PDTOR_LOOP]]
+//
+// OGCG:     [[PDTOR_DONE]]:
+// OGCG:       br label %[[EH_RESUME:.*]]
+//
+// OGCG:     [[EH_RESUME]]:
+// OGCG:       resume { ptr, i32 }
+
+void test_multi_dim_vla(int n, int m) {
+    S s[n][m];
+}
+
+// CIR-BEFORE-LPP:     cir.func {{.*}} @_Z18test_multi_dim_vlaii
+// CIR-BEFORE-LPP:       %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, 
["n", init]
+// CIR-BEFORE-LPP:       %[[M_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, 
["m", init]
+// CIR-BEFORE-LPP:       %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : 
!cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP:       %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> 
!u64i
+// CIR-BEFORE-LPP:       %[[M_VAL:.*]] = cir.load {{.*}} %[[M_ADDR]] : 
!cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP:       %[[M:.*]] = cir.cast integral %[[M_VAL]] : !s32i -> 
!u64i
+// CIR-BEFORE-LPP:       cir.stacksave
+// CIR-BEFORE-LPP:       cir.cleanup.scope {
+// CIR-BEFORE-LPP:         %[[NM:.*]] = cir.mul nuw %[[N]], %[[M]] : !u64i
+// CIR-BEFORE-LPP:         %[[VLA:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, 
%[[NM]] : !u64i, ["s", init]
+// CIR-BEFORE-LPP:         cir.array.ctor %[[VLA]], {{.*}} : !cir.ptr<!rec_S>, 
!u64i {
+// CIR-BEFORE-LPP:         ^bb0(%[[CTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:           cir.call @_ZN1SC1Ev(%[[CTOR_ARG]]) : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:         } partial_dtor {
+// CIR-BEFORE-LPP:         ^bb0(%[[DTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:           cir.call @_ZN1SD1Ev(%[[DTOR_ARG]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:         }
+// CIR-BEFORE-LPP:         cir.cleanup.scope {
+// CIR-BEFORE-LPP:           cir.yield
+// CIR-BEFORE-LPP:         } cleanup all {
+// CIR-BEFORE-LPP:           cir.array.dtor %[[VLA]], {{.*}} : 
!cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP:           ^bb0(%[[DTOR_ARG2:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:             cir.call @_ZN1SD1Ev(%[[DTOR_ARG2]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:           }
+// CIR-BEFORE-LPP:       } cleanup normal {
+// CIR-BEFORE-LPP:         cir.stackrestore
+// CIR-BEFORE-LPP:       }
+
+// CIR:     cir.func {{.*}} @_Z18test_multi_dim_vlaii
+// CIR:       %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR:       %[[M_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["m", init]
+// CIR:       %[[SAVED_STACK:.*]] = cir.alloca !cir.ptr<!u8i>, 
!cir.ptr<!cir.ptr<!u8i>>, ["saved_stack"]
+// CIR:       %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:       %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR:       %[[M_VAL:.*]] = cir.load {{.*}} %[[M_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:       %[[M:.*]] = cir.cast integral %[[M_VAL]] : !s32i -> !u64i
+// CIR:       cir.cleanup.scope {
+// CIR:         %[[NM:.*]] = cir.mul nuw %[[N]], %[[M]] : !u64i
+// CIR:         %[[BEGIN:.*]] = cir.alloca !rec_S, !cir.ptr<!rec_S>, %[[NM]] : 
!u64i, ["s", init]
+// CIR:         cir.call @_ZN1SC1Ev
+// CIR:         } cleanup eh {
+// CIR:           cir.call @_ZN1SD1Ev
+// CIR:         } cleanup all {
+// CIR:           %[[NM3:.*]] = cir.mul nuw %[[N]], %[[M]] : !u64i
+// CIR:           cir.call @_ZN1SD1Ev
+// CIR:       } cleanup normal {
+// CIR:         cir.stackrestore {{.*}} : !cir.ptr<!u8i>
+// CIR:       }
+
+// LLVM:     define dso_local void @_Z18test_multi_dim_vlaii
+// LLVM:       %[[N_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM:       %[[N:.*]] = sext i32 %[[N_I32]] to i64
+// LLVM:       %[[M_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM:       %[[M:.*]] = sext i32 %[[M_I32]] to i64
+// LLVM:       call ptr @llvm.stacksave.p0()
+//
+//            --- VLA alloca with n*m ---
+// LLVM:       %[[NM:.*]] = mul nuw i64 %[[N]], %[[M]]
+// LLVM:       %[[BEGIN:.*]] = alloca %struct.S, i64 %[[NM]]
+//
+//            --- ctor loop ---
+// LLVM:       invoke void @_ZN1SC1Ev(ptr{{.*}})
+// LLVM:         to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+//            --- landing pad + partial dtor ---
+// LLVM:     [[LPAD]]:
+// LLVM:       landingpad { ptr, i32 }
+// LLVM:         cleanup
+// LLVM:       call void @_ZN1SD1Ev
+// LLVM:       resume { ptr, i32 }
+//
+//            --- normal dtor ---
+// LLVM:       %[[NM2:.*]] = mul nuw i64 %[[N]], %[[M]]
+// LLVM:       %[[LAST:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 
%[[NM2]]
+// LLVM:       %[[DTOR_NE:.*]] = icmp ne ptr %[[LAST]], %[[BEGIN]]
+// LLVM:       call void @_ZN1SD1Ev
+// LLVM:       call void @llvm.stackrestore.p0
+// LLVM:       ret void
+
+// OGCG:     define dso_local void @_Z18test_multi_dim_vlaii
+// OGCG:     [[ENTRY:.*]]:
+// OGCG:       %[[N:.*]] = zext i32 %{{.*}} to i64
+// OGCG:       %[[M:.*]] = zext i32 %{{.*}} to i64
+// OGCG:       %[[NM:.*]] = mul nuw i64 %[[N]], %[[M]]
+// OGCG:       %[[VLA:.*]] = alloca %struct.S, i64 %[[NM]]
+// OGCG:       %[[ISEMPTY:.*]] = icmp eq i64 %{{.*}}, 0
+// OGCG:       br i1 %[[ISEMPTY]], label %[[CTOR_CONT:.*]], label 
%[[CTOR_LOOP_ENTRY:.*]]
+//
+//            --- ctor loop ---
+// OGCG:     [[CTOR_LOOP_ENTRY]]:
+// OGCG:       invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG:         to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+//            --- normal dtor ---
+// OGCG:     [[CTOR_CONT]]:
+// OGCG:       %[[NM2:.*]] = mul nuw i64 %[[N]], %[[M]]
+// OGCG:       call void @_ZN1SD1Ev
+//
+// OGCG:       call void @llvm.stackrestore.p0(ptr %{{.*}})
+// OGCG:       ret void
+//
+//            --- landing pad + partial dtor ---
+// OGCG:     [[LPAD]]:
+// OGCG:       landingpad { ptr, i32 }
+// OGCG:         cleanup
+// OGCG:       call void @_ZN1SD1Ev
+//
+// OGCG:       resume { ptr, i32 }
+
+void test_vla_of_constant_array(int n) {
+    S s[n][4];
+}
+
+// CIR-BEFORE-LPP:     cir.func {{.*}} @_Z26test_vla_of_constant_arrayi
+// CIR-BEFORE-LPP:       %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, 
["n", init]
+// CIR-BEFORE-LPP:       %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : 
!cir.ptr<!s32i>, !s32i
+// CIR-BEFORE-LPP:       %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> 
!u64i
+// CIR-BEFORE-LPP:       cir.stacksave
+// CIR-BEFORE-LPP:       cir.cleanup.scope {
+// CIR-BEFORE-LPP:         %[[VLA:.*]] = cir.alloca !cir.array<!rec_S x 4>, 
!cir.ptr<!cir.array<!rec_S x 4>>, %[[N]] : !u64i, ["s", init]
+// CIR-BEFORE-LPP:         %[[FOUR:.*]] = cir.const #cir.int<4> : !u64i
+// CIR-BEFORE-LPP:         %[[TOTAL:.*]] = cir.mul nuw %[[N]], %[[FOUR]] : 
!u64i
+// CIR-BEFORE-LPP:         %[[ELEM_PTR:.*]] = cir.cast bitcast %[[VLA]] : 
!cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR-BEFORE-LPP:         cir.array.ctor %[[ELEM_PTR]], %[[TOTAL]] : 
!cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP:         ^bb0(%[[CTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:           cir.call @_ZN1SC1Ev(%[[CTOR_ARG]]) : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:         } partial_dtor {
+// CIR-BEFORE-LPP:         ^bb0(%[[DTOR_ARG:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:           cir.call @_ZN1SD1Ev(%[[DTOR_ARG]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:         }
+// CIR-BEFORE-LPP:         cir.cleanup.scope {
+// CIR-BEFORE-LPP:           cir.yield
+// CIR-BEFORE-LPP:         } cleanup all {
+// CIR-BEFORE-LPP:           %{{.*}} = cir.const #cir.int<4> : !u64i
+// CIR-BEFORE-LPP:           %{{.*}} = cir.mul nuw %[[N]], %{{.*}} : !u64i
+// CIR-BEFORE-LPP:           %{{.*}} = cir.cast bitcast %[[VLA]] : 
!cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR-BEFORE-LPP:           cir.array.dtor {{.*}} : !cir.ptr<!rec_S>, !u64i {
+// CIR-BEFORE-LPP:           ^bb0(%[[DTOR_ARG2:.*]]: !cir.ptr<!rec_S>):
+// CIR-BEFORE-LPP:             cir.call @_ZN1SD1Ev(%[[DTOR_ARG2]]){{.*}} : 
(!cir.ptr<!rec_S>{{.*}}) -> ()
+// CIR-BEFORE-LPP:           }
+// CIR-BEFORE-LPP:       } cleanup normal {
+// CIR-BEFORE-LPP:         cir.stackrestore
+// CIR-BEFORE-LPP:       }
+
+// CIR:     cir.func {{.*}} @_Z26test_vla_of_constant_arrayi
+// CIR:       %[[N_ADDR:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["n", init]
+// CIR:       %[[N_VAL:.*]] = cir.load {{.*}} %[[N_ADDR]] : !cir.ptr<!s32i>, 
!s32i
+// CIR:       %[[N:.*]] = cir.cast integral %[[N_VAL]] : !s32i -> !u64i
+// CIR:       cir.cleanup.scope {
+// CIR:         %[[VLA:.*]] = cir.alloca !cir.array<!rec_S x 4>, 
!cir.ptr<!cir.array<!rec_S x 4>>, %[[N]] : !u64i, ["s", init]
+// CIR:         %[[FOUR:.*]] = cir.const #cir.int<4> : !u64i
+// CIR:         %[[TOTAL:.*]] = cir.mul nuw %[[N]], %[[FOUR]] : !u64i
+// CIR:         %[[ELEM_PTR:.*]] = cir.cast bitcast %[[VLA]] : 
!cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR:         cir.call @_ZN1SC1Ev
+// CIR:         } cleanup eh {
+// CIR:           cir.call @_ZN1SD1Ev
+// CIR:         } cleanup all {
+// CIR:           %{{.*}} = cir.const #cir.int<4> : !u64i
+// CIR:           %{{.*}} = cir.mul nuw %[[N]], %{{.*}} : !u64i
+// CIR:           %{{.*}} = cir.cast bitcast %[[VLA]] : 
!cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
+// CIR:           cir.call @_ZN1SD1Ev
+// CIR:       } cleanup normal {
+// CIR:         cir.stackrestore {{.*}} : !cir.ptr<!u8i>
+// CIR:       }
+
+// LLVM:     define dso_local void @_Z26test_vla_of_constant_arrayi
+// LLVM:       %[[N_I32:.*]] = load i32, ptr %{{.*}}
+// LLVM:       %[[N:.*]] = sext i32 %[[N_I32]] to i64
+// LLVM:       call ptr @llvm.stacksave.p0()
+//
+//            --- VLA alloca with [4 x %struct.S] ---
+// LLVM:       %[[BEGIN:.*]] = alloca [4 x %struct.S], i64 %[[N]]
+// LLVM:       %[[TOTAL:.*]] = mul nuw i64 %[[N]], 4
+//
+//            --- ctor loop ---
+// LLVM:       invoke void @_ZN1SC1Ev(ptr{{.*}})
+// LLVM:         to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+//            --- landing pad + partial dtor ---
+// LLVM:     [[LPAD]]:
+// LLVM:       landingpad { ptr, i32 }
+// LLVM:         cleanup
+// LLVM:       call void @_ZN1SD1Ev
+// LLVM:       resume { ptr, i32 }
+//
+//            --- normal dtor ---
+// LLVM:       %[[TOTAL2:.*]] = mul nuw i64 %[[N]], 4
+// LLVM:       call void @_ZN1SD1Ev
+// LLVM:       call void @llvm.stackrestore.p0
+// LLVM:       ret void
+
+// OGCG:     define dso_local void @_Z26test_vla_of_constant_arrayi
+// OGCG:     [[ENTRY:.*]]:
+// OGCG:       %[[N:.*]] = zext i32 %{{.*}} to i64
+// OGCG:       %[[VLA:.*]] = alloca [4 x %struct.S], i64 %[[N]]
+// OGCG:       %[[ARRAY_BEGIN:.*]] = getelementptr inbounds [4 x %struct.S], 
ptr %[[VLA]], i32 0, i32 0
+// OGCG:       %[[TOTAL:.*]] = mul nuw i64 %[[N]], 4
+// OGCG:       %[[ISEMPTY:.*]] = icmp eq i64 %[[TOTAL]], 0
+// OGCG:       br i1 %[[ISEMPTY]], label %[[CTOR_CONT:.*]], label 
%[[CTOR_LOOP_ENTRY:.*]]
+//
+//            --- ctor loop ---
+// OGCG:     [[CTOR_LOOP_ENTRY]]:
+// OGCG:       invoke void @_ZN1SC1Ev(ptr{{.*}})
+// OGCG:         to label %{{.*}} unwind label %[[LPAD:.*]]
+//
+//            --- normal dtor ---
+// OGCG:     [[CTOR_CONT]]:
+// OGCG:       %[[TOTAL2:.*]] = mul nuw i64 %[[N]], 4
+// OGCG:       call void @_ZN1SD1Ev
+//
+// OGCG:       call void @llvm.stackrestore.p0(ptr %{{.*}})
+// OGCG:       ret void
+//
+//            --- landing pad + partial dtor ---
+// OGCG:     [[LPAD]]:
+// OGCG:       landingpad { ptr, i32 }
+// OGCG:         cleanup
+// OGCG:       call void @_ZN1SD1Ev
+//
+// OGCG:       resume { ptr, i32 }


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

Reply via email to