Files
llvm-project/clang/test/CIR/CodeGen/partial-array-cleanup.cpp
Andy Kaylor cd0f775fbc [CIR] Handle irregular partial init destroy (#192158)
This implements CIR handling for the case where an array of a destructed
type is being initialized using an initializer list and therefore the
address of the last successfully constructed element must be loaded from
a temporary variable before the partial array destroy loop can begin.

In classic codegen this shares its implementation with the general array
ctor/dtor handling, but in CIR we have dedicated operations to abstract
array ctors/dtors, so the implementation of the irregular partial
destruction happens in a different place in codegen.

Assisted-by: Cursor / claude-4.6-opus-high
2026-04-15 09:12:16 -07:00

823 lines
40 KiB
C++

// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fclangir -fexceptions -fcxx-exceptions -emit-cir -mmlir --mlir-print-ir-before=cir-lowering-prepare %s -o %t.cir 2> %t-before-lp.cir
// RUN: FileCheck --input-file=%t-before-lp.cir %s -check-prefix=CIR-BEFORE-LPP
// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
// RUN: %clang_cc1 -triple x86_64-unknown-linux-gnu -Wno-unused-value -fexceptions -fcxx-exceptions -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 -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll
// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
struct S {
S();
~S();
};
void test_partial_array_cleanup() {
S s[4];
}
// CIR-BEFORE-LPP: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
// CIR-BEFORE-LPP: %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, ["s", init]
// CIR-BEFORE-LPP: cir.array.ctor %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 4>> {
// 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: cir.func {{.*}} @_Z26test_partial_array_cleanupv()
// CIR: %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, ["s", init]
// CIR: %[[CONST4:.*]] = cir.const #cir.int<4> : !u64i
// CIR: %[[BEGIN:.*]] = cir.cast array_to_ptrdecay %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
// CIR: %[[END:.*]] = cir.ptr_stride %[[BEGIN]], %[[CONST4]] : (!cir.ptr<!rec_S>, !u64i) -> !cir.ptr<!rec_S>
// 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: }
// LLVM: define dso_local void @_Z26test_partial_array_cleanupv()
// LLVM: %[[ARRAY:.*]] = alloca [4 x %struct.S]
// LLVM: %[[BEGIN:.*]] = getelementptr %struct.S, ptr %[[ARRAY]], i32 0
// LLVM: %[[END:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 4
// LLVM: %[[ITER:.*]] = alloca ptr
// LLVM: store ptr %[[BEGIN]], ptr %[[ITER]]
//
// --- ctor loop condition ---
// LLVM: [[CLEANUP_SCOPE:.*]]:
// LLVM: br label %[[CTOR_LOOP_COND:.*]]
//
// LLVM: [[CTOR_LOOP_COND:]]:
// LLVM: %[[COND_CUR:.*]] = load ptr, ptr %[[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 %[[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 %[[ITER]]
// LLVM: br label %[[CTOR_LOOP_COND]]
//
// --- landing pad + cleanup guard ---
// LLVM: [[LPAD]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: %[[PAD_CUR:.*]] = load ptr, ptr %[[ITER]]
// LLVM: %[[GUARD:.*]] = icmp ne ptr %[[PAD_CUR]], %[[BEGIN]]
// LLVM: br i1 %[[GUARD]], label %[[DTOR_ENTRY:.*]], label %[[EH_RESUME:.*]]
//
// --- partial dtor do-while entry ---
// LLVM: [[DTOR_ENTRY]]:
// LLVM: br label %[[DTOR_BODY:.*]]
//
// --- partial dtor loop condition (back-edge) ---
// LLVM: [[DTOR_LOOP_COND:.*]]:
// LLVM: %[[DTOR_CUR:.*]] = load ptr, ptr %[[ITER]]
// LLVM: %[[DTOR_CONT:.*]] = icmp ne ptr %[[DTOR_CUR]], %[[BEGIN]]
// LLVM: br i1 %[[DTOR_CONT]], label %[[DTOR_BODY]], label %[[DTOR_DONE:.*]]
//
// --- partial dtor loop body ---
// LLVM: [[DTOR_BODY]]:
// LLVM: %[[DCUR:.*]] = load ptr, ptr %[[ITER]]
// LLVM: %[[PREV:.*]] = getelementptr %struct.S, ptr %[[DCUR]], i64 -1
// LLVM: store ptr %[[PREV]], ptr %[[ITER]]
// LLVM: call void @_ZN1SD1Ev(ptr{{.*}} %[[PREV]])
// LLVM: br label %[[DTOR_LOOP_COND]]
//
// LLVM: [[DTOR_DONE]]:
// LLVM: br label %[[EH_RESUME]]
//
// LLVM: [[EH_RESUME]]:
// LLVM: resume { ptr, i32 }
// OGCG: define dso_local void @_Z26test_partial_array_cleanupv()
// OGCG: [[ENTRY:.*]]:
// OGCG: %[[ARRAY:.*]] = alloca [4 x %struct.S]
// OGCG: %[[BEGIN:.*]] = getelementptr inbounds [4 x %struct.S], ptr %[[ARRAY]], i32 0, i32 0
// OGCG: %[[END:.*]] = getelementptr inbounds %struct.S, ptr %[[BEGIN]], i64 4
//
// --- ctor loop ---
// OGCG: [[CTOR_BODY:.*]]:
// OGCG: %[[CUR:.*]] = phi ptr [ %[[BEGIN]], %[[ENTRY]] ], [ %[[NEXT:.*]], %[[CONT:.*]] ]
// OGCG: invoke void @_ZN1SC1Ev(ptr{{.*}})
// OGCG: to label %[[CONT]] unwind label %[[LPAD:.*]]
//
// OGCG: [[CONT]]:
// OGCG: %[[NEXT]] = getelementptr inbounds %struct.S, ptr %[[CUR]], i64 1
// OGCG: %[[DONE:.*]] = icmp eq ptr %[[NEXT]], %[[END]]
// OGCG: br i1 %[[DONE]], label %[[CTOR_EXIT:.*]], label %[[CTOR_BODY:.*]]
//
// --- landing pad + cleanup guard ---
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: %[[ISEMPTY:.*]] = icmp eq ptr %[[BEGIN]], %[[CUR]]
// OGCG: br i1 %[[ISEMPTY]], label %[[EH_RESUME:.*]], label %[[DTOR_LOOP:.*]]
//
// --- partial dtor loop ---
// OGCG: [[DTOR_LOOP]]:
// OGCG: %[[PAST:.*]] = phi ptr [ %[[CUR]], %[[LPAD]] ], [ %[[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]], %[[BEGIN]]
// OGCG: br i1 %[[DDONE]], label %[[EH_RESUME]], label %[[DTOR_LOOP]]
//
// 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 }
void test_init_list_partial_array_cleanup() {
S arr[4] = { S(), S() };
}
// CIR-LABEL: cir.func {{.*}} @_Z36test_init_list_partial_array_cleanupv()
// CIR: %[[ARRAY:.*]] = cir.alloca !cir.array<!rec_S x 4>, !cir.ptr<!cir.array<!rec_S x 4>>, ["arr", init]
// CIR: %[[END_OF_INIT:.*]] = cir.alloca !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>, ["arrayinit.endOfInit"]
// CIR: %[[BEGIN:.*]] = cir.cast array_to_ptrdecay %[[ARRAY]] : !cir.ptr<!cir.array<!rec_S x 4>> -> !cir.ptr<!rec_S>
// CIR: cir.store {{.*}} %[[BEGIN]], %[[END_OF_INIT]] : !cir.ptr<!rec_S>, !cir.ptr<!cir.ptr<!rec_S>>
// CIR: cir.cleanup.scope {
// --- first explicit init ---
// CIR: cir.call @_ZN1SC1Ev(%[[BEGIN]])
// --- advance + update endOfInit for second element ---
// CIR: %[[SECOND:.*]] = cir.ptr_stride %[[BEGIN]], %{{.*}}
// CIR: cir.store {{.*}} %[[SECOND]], %[[END_OF_INIT]]
// --- second explicit init ---
// CIR: cir.call @_ZN1SC1Ev(%[[SECOND]])
// --- advance + update endOfInit for filler start ---
// CIR: %[[FILLER_START:.*]] = cir.ptr_stride %[[SECOND]], %{{.*}}
// CIR: cir.store {{.*}} %[[FILLER_START]], %[[END_OF_INIT]]
// --- do-while filler loop ---
// CIR: cir.do {
// CIR: %[[CUR:.*]] = cir.load {{.*}} %{{.*}} : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
// CIR: cir.call @_ZN1SC1Ev(%[[CUR]])
// CIR: %[[NEXT:.*]] = cir.ptr_stride %[[CUR]], %{{.*}}
// CIR: cir.store {{.*}} %[[NEXT]], %[[END_OF_INIT]]
// CIR: cir.yield
// CIR: } while {
// CIR: cir.condition(%{{.*}})
// CIR: }
// CIR: cir.yield
// --- EH cleanup: partial destruction ---
// CIR: } cleanup eh {
// CIR: %[[END_VAL:.*]] = cir.load {{.*}} %[[END_OF_INIT]] : !cir.ptr<!cir.ptr<!rec_S>>, !cir.ptr<!rec_S>
// CIR: %[[NE:.*]] = cir.cmp ne %[[END_VAL]], %[[BEGIN]] : !cir.ptr<!rec_S>
// CIR: cir.if %[[NE]] {
// CIR: cir.do {
// CIR: %[[EL:.*]] = cir.load {{.*}} %{{.*}} : !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.call @_ZN1SD1Ev(%[[PREV]])
// CIR: cir.yield
// CIR: } while {
// CIR: %[[EL2:.*]] = cir.load {{.*}} %{{.*}} : !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: }
// LLVM-LABEL: define dso_local void @_Z36test_init_list_partial_array_cleanupv()
// LLVM: %[[ARRAY:.*]] = alloca [4 x %struct.S]
// LLVM: %[[BEGIN:.*]] = getelementptr %struct.S, ptr %[[ARRAY]], i32 0
// LLVM: store ptr %[[BEGIN]], ptr %[[END_OF_INIT:.*]]
//
// --- first ctor ---
// LLVM: invoke void @_ZN1SC1Ev(ptr {{.*}} %[[BEGIN]])
// LLVM: to label %[[CONT1:.*]] unwind label %[[LPAD:.*]]
//
// --- second ctor ---
// LLVM: [[CONT1]]:
// LLVM: %[[SECOND:.*]] = getelementptr %struct.S, ptr %[[BEGIN]], i64 1
// LLVM: store ptr %[[SECOND]], ptr %[[END_OF_INIT]]
// LLVM: invoke void @_ZN1SC1Ev(ptr {{.*}} %[[SECOND]])
// LLVM: to label %{{.*}} unwind label %[[LPAD]]
//
// --- filler loop with invoke ---
// LLVM: invoke void @_ZN1SC1Ev(ptr {{.*}})
// LLVM: to label %[[FILLER_CONT:.*]] unwind label %[[LPAD]]
//
// LLVM: [[FILLER_CONT]]:
// LLVM: %[[FNEXT:.*]] = getelementptr %struct.S, ptr %{{.*}}, i64 1
// LLVM: store ptr %[[FNEXT]], ptr %[[END_OF_INIT]]
//
// --- landing pad + cleanup guard ---
// LLVM: [[LPAD]]:
// LLVM: landingpad { ptr, i32 }
// LLVM: cleanup
// LLVM: %[[PAD_CUR:.*]] = load ptr, ptr %[[END_OF_INIT]]
// LLVM: %[[GUARD:.*]] = icmp ne ptr %[[PAD_CUR]], %[[BEGIN]]
// LLVM: br i1 %[[GUARD]], label %[[DTOR_ENTRY:.*]], label %[[EH_RESUME:.*]]
//
// --- partial dtor do-while loop ---
// LLVM: [[DTOR_ENTRY]]:
// LLVM: store ptr %[[PAD_CUR]], ptr %[[DTOR_ITER:.*]]
// LLVM: br label %[[DTOR_BODY:.*]]
//
// LLVM: [[DTOR_LOOP_COND:.*]]:
// LLVM: %[[DTOR_CUR:.*]] = load ptr, ptr %[[DTOR_ITER]]
// LLVM: %[[DTOR_CONT:.*]] = icmp ne ptr %[[DTOR_CUR]], %[[BEGIN]]
// LLVM: br i1 %[[DTOR_CONT]], label %[[DTOR_BODY]], label %[[DTOR_DONE:.*]]
//
// LLVM: [[DTOR_BODY]]:
// LLVM: %[[DCUR:.*]] = load ptr, ptr %[[DTOR_ITER]]
// LLVM: %[[PREV:.*]] = getelementptr %struct.S, ptr %[[DCUR]], i64 -1
// LLVM: store ptr %[[PREV]], ptr %[[DTOR_ITER]]
// LLVM: call void @_ZN1SD1Ev(ptr {{.*}} %[[PREV]])
// LLVM: br label %[[DTOR_LOOP_COND]]
//
// LLVM: [[DTOR_DONE]]:
// LLVM: br label %[[EH_RESUME]]
//
// LLVM: [[EH_RESUME]]:
// LLVM: resume { ptr, i32 }
// OGCG-LABEL: define dso_local void @_Z36test_init_list_partial_array_cleanupv()
// OGCG: %[[ARR:.*]] = alloca [4 x %struct.S]
// OGCG: %[[END_OF_INIT:.*]] = alloca ptr
// OGCG: store ptr %[[ARR]], ptr %[[END_OF_INIT]]
//
// --- first explicit ctor ---
// OGCG: invoke void @_ZN1SC1Ev(ptr {{.*}} %[[ARR]])
// OGCG: to label %[[CONT1:.*]] unwind label %[[LPAD:.*]]
//
// --- second explicit ctor ---
// OGCG: [[CONT1]]:
// OGCG: %[[EL1:.*]] = getelementptr inbounds %struct.S, ptr %[[ARR]], i64 1
// OGCG: store ptr %[[EL1]], ptr %[[END_OF_INIT]]
// OGCG: invoke void @_ZN1SC1Ev(ptr {{.*}} %[[EL1]])
// OGCG: to label %[[CONT2:.*]] unwind label %[[LPAD]]
//
// --- filler loop setup ---
// OGCG: [[CONT2]]:
// OGCG: %[[FILLER_START:.*]] = getelementptr inbounds %struct.S, ptr %[[ARR]], i64 2
// OGCG: store ptr %[[FILLER_START]], ptr %[[END_OF_INIT]]
// OGCG: %[[FILLER_END:.*]] = getelementptr inbounds %struct.S, ptr %[[ARR]], i64 4
// OGCG: br label %[[FILLER_BODY:.*]]
//
// --- filler loop body ---
// OGCG: [[FILLER_BODY]]:
// OGCG: %[[FILLER_CUR:.*]] = phi ptr [ %[[FILLER_START]], %[[CONT2]] ], [ %[[FILLER_NEXT:.*]], %[[FILLER_CONT:.*]] ]
// OGCG: invoke void @_ZN1SC1Ev(ptr {{.*}} %[[FILLER_CUR]])
// OGCG: to label %[[FILLER_CONT]] unwind label %[[LPAD]]
//
// OGCG: [[FILLER_CONT]]:
// OGCG: %[[FILLER_NEXT]] = getelementptr inbounds %struct.S, ptr %[[FILLER_CUR]], i64 1
// OGCG: store ptr %[[FILLER_NEXT]], ptr %[[END_OF_INIT]]
// OGCG: %[[FILLER_DONE:.*]] = icmp eq ptr %[[FILLER_NEXT]], %[[FILLER_END]]
// OGCG: br i1 %[[FILLER_DONE]], label %{{.*}}, label %[[FILLER_BODY]]
//
// --- landing pad + partial destruction ---
// OGCG: [[LPAD]]:
// OGCG: landingpad { ptr, i32 }
// OGCG: cleanup
// OGCG: %[[PAD_END:.*]] = load ptr, ptr %[[END_OF_INIT]]
// OGCG: %[[ISEMPTY:.*]] = icmp eq ptr %[[ARR]], %[[PAD_END]]
// OGCG: br i1 %[[ISEMPTY]], label %[[DTOR_DONE:.*]], label %[[DTOR_LOOP:.*]]
//
// OGCG: [[DTOR_LOOP]]:
// OGCG: %[[DTOR_PAST:.*]] = phi ptr [ %[[PAD_END]], %[[LPAD]] ], [ %[[DTOR_PREV:.*]], %[[DTOR_LOOP]] ]
// OGCG: %[[DTOR_PREV]] = getelementptr inbounds %struct.S, ptr %[[DTOR_PAST]], i64 -1
// OGCG: call void @_ZN1SD1Ev(ptr {{.*}} %[[DTOR_PREV]])
// OGCG: %[[DTOR_CONT:.*]] = icmp eq ptr %[[DTOR_PREV]], %[[ARR]]
// OGCG: br i1 %[[DTOR_CONT]], label %[[DTOR_DONE]], label %[[DTOR_LOOP]]
//
// OGCG: [[DTOR_DONE]]:
// OGCG: br label %[[EH_RESUME:.*]]
//
// OGCG: [[EH_RESUME]]:
// OGCG: resume { ptr, i32 }