https://github.com/andykaylor updated https://github.com/llvm/llvm-project/pull/192172
>From 20f4f3781a98f96c21f87bc4f6b41e97b6f8c177 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Tue, 14 Apr 2026 17:46:17 -0700 Subject: [PATCH 1/2] [CIR] Implement cleanup handling for destructor ILE initializers This adds EH cleanup handling for C++ initializer list expressions containing destructed types. The necessary support for deferred deactivation cleanups was already in place, so this just needed to push the deferred destroy cleanup when the init list element is constructed. --- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 14 ++- clang/test/CIR/CodeGen/paren-init-list-eh.cpp | 115 ++++++++++++++++++ clang/test/CIR/CodeGen/paren-init-list.cpp | 54 ++++++++ 3 files changed, 178 insertions(+), 5 deletions(-) create mode 100644 clang/test/CIR/CodeGen/paren-init-list-eh.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index ec8ebf75ccbee..ae243f2ec3905 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -1088,7 +1088,7 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr( // We'll need to enter cleanup scopes in case any of the element // initializers throws an exception. - assert(!cir::MissingFeatures::requiresCleanups()); + CIRGenFunction::CleanupDeactivationScope deactivateCleanups(cgf); unsigned curInitIndex = 0; @@ -1189,10 +1189,14 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr( // Push a destructor if necessary. // FIXME: if we have an array of structures, all explicitly // initialized, we can end up pushing a linear number of cleanups. - if (field->getType().isDestructedType()) { - cgf.cgm.errorNYI(e->getSourceRange(), - "visitCXXParenListOrInitListExpr destructor"); - return; + if (QualType::DestructionKind dtorKind = + field->getType().isDestructedType()) { + assert(lv.isSimple()); + if (dtorKind) { + cgf.pushDestroyAndDeferDeactivation(NormalAndEHCleanup, lv.getAddress(), + field->getType(), + cgf.getDestroyer(dtorKind), false); + } } // From classic codegen, maybe not useful for CIR: diff --git a/clang/test/CIR/CodeGen/paren-init-list-eh.cpp b/clang/test/CIR/CodeGen/paren-init-list-eh.cpp new file mode 100644 index 0000000000000..89e400b70700d --- /dev/null +++ b/clang/test/CIR/CodeGen/paren-init-list-eh.cpp @@ -0,0 +1,115 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-cir %s -o %t.cir +// RUN: FileCheck --check-prefix=CIR --input-file=%t.cir %s +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -fclangir -emit-llvm %s -o %t-cir.ll +// RUN: FileCheck --check-prefix=LLVM --input-file=%t-cir.ll %s +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm %s -o %t.ll +// RUN: FileCheck --check-prefix=OGCG --input-file=%t.ll %s + +struct Struk { + int val; + Struk(int); + ~Struk(); +}; + +struct Outer { + Struk s1; + Struk s2; + int x; +}; + +void test_init_list_with_dtor() { + Outer o = {Struk{1}, Struk{2}, 3}; +} + +// CIR: cir.func {{.*}} @_Z24test_init_list_with_dtorv +// CIR: %[[O:.*]] = cir.alloca !rec_Outer, !cir.ptr<!rec_Outer>, ["o", init] +// CIR: cir.scope { +// CIR: %[[S1:.*]] = cir.get_member %[[O]][0] {name = "s1"} : !cir.ptr<!rec_Outer> -> !cir.ptr<!rec_Struk> +// CIR: %[[ONE:.*]] = cir.const #cir.int<1> +// CIR: cir.call @_ZN5StrukC1Ei(%[[S1]], %[[ONE]]) +// CIR: cir.cleanup.scope { +// CIR: %[[S2:.*]] = cir.get_member %[[O]][1] {name = "s2"} : !cir.ptr<!rec_Outer> -> !cir.ptr<!rec_Struk> +// CIR: %[[TWO:.*]] = cir.const #cir.int<2> +// CIR: cir.call @_ZN5StrukC1Ei(%[[S2]], %[[TWO]]) +// CIR: cir.cleanup.scope { +// CIR: %[[X:.*]] = cir.get_member %[[O]][2] {name = "x"} +// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i +// CIR: cir.store align(4) %[[THREE]], %[[X]] +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: cir.call @_ZN5StrukD1Ev(%[[S2]]) +// CIR: cir.yield +// CIR: } +// CIR: cir.yield +// CIR: } cleanup eh { +// CIR: cir.call @_ZN5StrukD1Ev(%[[S1]]) +// CIR: cir.yield +// CIR: } +// CIR: } +// CIR: cir.cleanup.scope { +// CIR: cir.yield +// CIR: } cleanup all { +// CIR: cir.call @_ZN5OuterD1Ev(%[[O]]) +// CIR: cir.yield +// CIR: } +// CIR: cir.return +// CIR: } + +// LLVM: define {{.*}} void @_Z24test_init_list_with_dtorv +// LLVM: %[[O:.*]] = alloca %struct.Outer +// LLVM: %[[S1_ADDR:.*]] = getelementptr %struct.Outer, ptr %[[O]], i32 0, i32 0 +// LLVM: call void @_ZN5StrukC1Ei(ptr {{.*}} %[[S1_ADDR]], i32 {{.*}} 1) +// LLVM: %[[S2_ADDR:.*]] = getelementptr %struct.Outer, ptr %[[O]], i32 0, i32 1 +// LLVM: invoke void @_ZN5StrukC1Ei(ptr {{.*}} %[[S2_ADDR]], i32 {{.*}} 2) +// LLVM: to label %[[CONT:.*]] unwind label %[[LPAD:.*]] +// LLVM: [[CONT]]: +// LLVM: %[[X_ADDR:.*]] = getelementptr %struct.Outer, ptr %[[O]], i32 0, i32 2 +// LLVM: store i32 3, ptr %[[X_ADDR]] +// LLVM: br label %[[EXIT_CLEANUP_SCOPE:.*]] +// LLVM: [[EXIT_CLEANUP_SCOPE]]: +// LLVM: br label %[[DONE:.*]] +// LLVM: [[LPAD]]: +// LLVM: %[[EXN:.*]] = landingpad { ptr, i32 } +// LLVM: cleanup +// LLVM: %[[EXN_ADDR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 0 +// LLVM: %[[EXN_SELECTOR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 1 +// LLVM: br label %[[EH_CLEANUP:.*]] +// LLVM: [[EH_CLEANUP]]: +// LLVM: %[[EXN_ADDR_PHI:.*]] = phi ptr [ %[[EXN_ADDR]], %[[LPAD]] ] +// LLVM: %[[EXN_SELECTOR_PHI:.*]] = phi i32 [ %[[EXN_SELECTOR]], %[[LPAD]] ] +// LLVM: call void @_ZN5StrukD1Ev(ptr {{.*}} %[[S1_ADDR]]) +// LLVM: %[[EXN_INS:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_ADDR_PHI]], 0 +// LLVM: %[[EXN_INS2:.*]] = insertvalue { ptr, i32 } %[[EXN_INS]], i32 %[[EXN_SELECTOR_PHI]], 1 +// LLVM: resume { ptr, i32 } %[[EXN_INS2]] +// LLVM: [[DONE]]: +// LLVM: ret void + +// OGCG: define {{.*}} void @_Z24test_init_list_with_dtorv +// OGCG: %[[O:.*]] = alloca %struct.Outer +// OGCG: %[[EXN_SLOT:.*]] = alloca ptr +// OGCG: %[[EHSELECTOR_SLOT:.*]] = alloca i32 +// OGCG: %[[S1_ADDR:.*]] = getelementptr inbounds nuw %struct.Outer, ptr %[[O]], i32 0, i32 0 +// OGCG: call void @_ZN5StrukC1Ei(ptr {{.*}} %[[S1_ADDR]], i32 {{.*}} 1) +// OGCG: %[[S2_ADDR:.*]] = getelementptr inbounds nuw %struct.Outer, ptr %[[O]], i32 0, i32 1 +// OGCG: invoke void @_ZN5StrukC1Ei(ptr {{.*}} %[[S2_ADDR]], i32 {{.*}} 2) +// OGCG: to label %[[CONT:.*]] unwind label %[[LPAD:.*]] +// OGCG: [[CONT]]: +// OGCG: %[[X_ADDR:.*]] = getelementptr inbounds nuw %struct.Outer, ptr %[[O]], i32 0, i32 2 +// OGCG: store i32 3, ptr %[[X_ADDR]] +// OGCG: call void @_ZN5OuterD1Ev(ptr{{.*}} %[[O]]) +// OGCG: ret void +// OGCG: [[LPAD]]: +// OGCG: %[[EXN:.*]] = landingpad { ptr, i32 } +// OGCG: cleanup +// OGCG: %[[EXN_ADDR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 0 +// OGCG: store ptr %[[EXN_ADDR]], ptr %[[EXN_SLOT]] +// OGCG: %[[EXN_SELECTOR:.*]] = extractvalue { ptr, i32 } %[[EXN]], 1 +// OGCG: store i32 %[[EXN_SELECTOR]], ptr %[[EHSELECTOR_SLOT]] +// OGCG: call void @_ZN5StrukD1Ev(ptr {{.*}} %[[S1_ADDR]]) +// OGCG: br label %[[EH_RESUME:.*]] +// OGCG: [[EH_RESUME]]: +// OGCG: %[[EXN_SLOT_LOAD:.*]] = load ptr, ptr %[[EXN_SLOT]] +// OGCG: %[[EHSELECTOR_SLOT_LOAD:.*]] = load i32, ptr %[[EHSELECTOR_SLOT]] +// OGCG: %[[EXN_INSERT:.*]] = insertvalue { ptr, i32 } poison, ptr %[[EXN_SLOT_LOAD]], 0 +// OGCG: %[[EXN_INSERT_2:.*]] = insertvalue { ptr, i32 } %[[EXN_INSERT]], i32 %[[EHSELECTOR_SLOT_LOAD]], 1 +// OGCG: resume { ptr, i32 } %[[EXN_INSERT_2]] diff --git a/clang/test/CIR/CodeGen/paren-init-list.cpp b/clang/test/CIR/CodeGen/paren-init-list.cpp index aba8b05374b1b..0b658acf18dda 100644 --- a/clang/test/CIR/CodeGen/paren-init-list.cpp +++ b/clang/test/CIR/CodeGen/paren-init-list.cpp @@ -24,3 +24,57 @@ void cxx_paren_list_init_expr() { CompleteS a(1, 'a'); } // OGCG: %[[A_ADDR:.*]] = alloca %struct.CompleteS, align 4 // OGCG: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %[[A_ADDR]], ptr align 4 @__const._Z24cxx_paren_list_init_exprv.a, i64 8, i1 false) + +struct HasDtor { + int val; + ~HasDtor(); +}; + +struct Outer { + HasDtor h; + int x; +}; + +void test_init_list_with_dtor() { + Outer o = {HasDtor{1}, 2}; +} + +// CIR: cir.func {{.*}} @_Z24test_init_list_with_dtorv +// CIR: %[[O:.*]] = cir.alloca !rec_Outer, !cir.ptr<!rec_Outer>, ["o", init] +// CIR: cir.scope { +// CIR: %[[H:.*]] = cir.get_member %[[O]][0] {name = "h"} : !cir.ptr<!rec_Outer> -> !cir.ptr<!rec_HasDtor> +// CIR: %[[VAL:.*]] = cir.get_member %[[H]][0] {name = "val"} : !cir.ptr<!rec_HasDtor> -> !cir.ptr<!s32i> +// CIR: %[[CONST:.*]] = cir.const #cir.int<1> +// CIR: cir.store{{.*}} %[[CONST]], %[[VAL]] +// CIR: %[[X:.*]] = cir.get_member %[[O]][1] {name = "x"} : !cir.ptr<!rec_Outer> -> !cir.ptr<!s32i> +// CIR: %[[CONST:.*]] = cir.const #cir.int<2> +// CIR: cir.store{{.*}} %[[CONST]], %[[X]] +// CIR: } +// CIR: cir.cleanup.scope { +// CIR: cir.yield +// CIR: } cleanup normal { +// CIR: cir.call @_ZN5OuterD1Ev(%[[O]]) nothrow : (!cir.ptr<!rec_Outer> {llvm.align = 4 : i64, llvm.dereferenceable = 8 : i64, llvm.nonnull, llvm.noundef}) -> () +// CIR: cir.yield +// CIR: } +// CIR: cir.return +// CIR: } + +// LLVM: define {{.*}} void @_Z24test_init_list_with_dtorv +// LLVM: %[[O:.*]] = alloca %struct.Outer +// LLVM: %[[O_ADDR:.*]] = getelementptr %struct.Outer, ptr %[[O]], i32 0, i32 0 +// LLVM: %[[H_ADDR:.*]] = getelementptr %struct.HasDtor, ptr %[[O_ADDR]], i32 0, i32 0 +// LLVM: store i32 1, ptr %[[H_ADDR]] +// LLVM: %[[X_ADDR:.*]] = getelementptr %struct.Outer, ptr %[[O]], i32 0, i32 1 +// LLVM: store i32 2, ptr %[[X_ADDR]] +// LLVM: call void @_ZN5OuterD1Ev(ptr{{.*}} %[[O]]) +// LLVM: ret void + +// OGCG: define {{.*}} void @_Z24test_init_list_with_dtorv +// OGCG: %[[O:.*]] = alloca %struct.Outer +// OGCG: %[[O_ADDR:.*]] = getelementptr inbounds nuw %struct.Outer, ptr %[[O]], i32 0, i32 0 +// OGCG: %[[H_ADDR:.*]] = getelementptr inbounds nuw %struct.HasDtor, ptr %[[O_ADDR]], i32 0, i32 0 +// OGCG: store i32 1, ptr %[[H_ADDR]] +// OGCG: %[[X_ADDR:.*]] = getelementptr inbounds nuw %struct.Outer, ptr %[[O]], i32 0, i32 1 +// OGCG: store i32 2, ptr %[[X_ADDR]] +// OGCG: call void @_ZN5OuterD1Ev(ptr{{.*}} %[[O]]) +// OGCG: ret void >From 0aea6c1451234a96776e7f43c7bb545bc174bfb3 Mon Sep 17 00:00:00 2001 From: Andy Kaylor <[email protected]> Date: Wed, 15 Apr 2026 09:20:54 -0700 Subject: [PATCH 2/2] Address review feedback --- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp index ae243f2ec3905..0fc4cb9e598d5 100644 --- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp @@ -1192,11 +1192,9 @@ void AggExprEmitter::visitCXXParenListOrInitListExpr( if (QualType::DestructionKind dtorKind = field->getType().isDestructedType()) { assert(lv.isSimple()); - if (dtorKind) { - cgf.pushDestroyAndDeferDeactivation(NormalAndEHCleanup, lv.getAddress(), - field->getType(), - cgf.getDestroyer(dtorKind), false); - } + cgf.pushDestroyAndDeferDeactivation(NormalAndEHCleanup, lv.getAddress(), + field->getType(), + cgf.getDestroyer(dtorKind), false); } // From classic codegen, maybe not useful for CIR: _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
