| // RUN: %clang_cc1 --std=c++20 -fexceptions -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefixes=EH %s | |
| // RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefixes=NOEH,CHECK %s | |
| struct Printy { | |
| Printy(const char *name) : name(name) {} | |
| ~Printy() {} | |
| const char *name; | |
| }; | |
| int foo() { return 2; } | |
| struct Printies { | |
| Printy a; | |
| Printy b; | |
| Printy c; | |
| }; | |
| void ParenInit() { | |
| // CHECK-LABEL: define dso_local void @_Z9ParenInitv() | |
| // CHECK: [[CLEANUP_DEST:%.+]] = alloca i32, align 4 | |
| Printies ps(Printy("a"), | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| ({ | |
| if (foo()) return; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| Printy("b"); | |
| // CHECK: if.end: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| }), | |
| ({ | |
| if (foo()) return; | |
| // CHECK: if.then{{.*}}: | |
| // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 4 | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: br label %cleanup | |
| Printy("c"); | |
| // CHECK: if.end{{.*}}: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: call void @_ZN8PrintiesD1Ev | |
| // CHECK-NEXT: br label %return | |
| })); | |
| // CHECK: cleanup: | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: br label %return | |
| } | |
| void break_in_stmt_expr() { | |
| // Verify that the "break" in "if.then".calls dtor before jumping to "for.end". | |
| // CHECK-LABEL: define dso_local void @_Z18break_in_stmt_exprv() | |
| Printies p{Printy("a"), | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| ({ | |
| for (;;) { | |
| Printies ps{ | |
| Printy("b"), | |
| // CHECK: for.cond: | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| ({ | |
| if (foo()) { | |
| break; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: br label %for.end | |
| } | |
| Printy("c"); | |
| // CHECK: if.end: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| }), | |
| Printy("d")}; | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: call void @_ZN8PrintiesD1Ev | |
| // CHECK-NEXT: br label %for.cond | |
| } | |
| Printy("e"); | |
| // CHECK: for.end: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| }), | |
| Printy("f")}; | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: call void @_ZN8PrintiesD1Ev | |
| } | |
| void goto_in_stmt_expr() { | |
| // Verify that: | |
| // - correct branch fixups for deactivated normal cleanups are generated correctly. | |
| // CHECK-LABEL: define dso_local void @_Z17goto_in_stmt_exprv() | |
| // CHECK: [[CLEANUP_DEST_SLOT:%cleanup.dest.slot.*]] = alloca i32, align 4 | |
| { | |
| Printies p1{Printy("a"), // CHECK: call void @_ZN6PrintyC1EPKc | |
| ({ | |
| { | |
| Printies p2{Printy("b"), | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| ({ | |
| if (foo() == 1) { | |
| goto in; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: store i32 2, ptr [[CLEANUP_DEST_SLOT]], align 4 | |
| // CHECK-NEXT: br label %[[CLEANUP1:.+]] | |
| } | |
| if (foo() == 2) { | |
| goto out; | |
| // CHECK: if.then{{.*}}: | |
| // CHECK-NEXT: store i32 3, ptr [[CLEANUP_DEST_SLOT]], align 4 | |
| // CHECK-NEXT: br label %[[CLEANUP1]] | |
| } | |
| Printy("c"); | |
| // CHECK: if.end{{.*}}: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| }), | |
| Printy("d")}; | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: call void @_ZN8PrintiesD1Ev | |
| // CHECK-NEXT: br label %in | |
| } | |
| in: | |
| Printy("e"); | |
| // CHECK: in: ; preds = %if.end{{.*}}, %[[CLEANUP1]] | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| }), | |
| Printy("f")}; | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: call void @_ZN8PrintiesD1Ev | |
| // CHECK-NEXT: br label %out | |
| } | |
| out: | |
| return; | |
| // CHECK: out: | |
| // CHECK-NEXT: ret void | |
| // CHECK: [[CLEANUP1]]: ; preds = %if.then{{.*}}, %if.then | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: %cleanup.dest = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4 | |
| // CHECK-NEXT: switch i32 %cleanup.dest, label %[[CLEANUP2:.+]] [ | |
| // CHECK-NEXT: i32 2, label %in | |
| // CHECK-NEXT: ] | |
| // CHECK: [[CLEANUP2]]: ; preds = %[[CLEANUP1]] | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: %cleanup.dest{{.*}} = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4 | |
| // CHECK-NEXT: switch i32 %cleanup.dest{{.*}}, label %unreachable [ | |
| // CHECK-NEXT: i32 3, label %out | |
| // CHECK-NEXT: ] | |
| } | |
| void ArrayInit() { | |
| // Printy arr[4] = {ctorA, ctorB, stmt-exprC, stmt-exprD}; | |
| // Verify that: | |
| // - We do the necessary stores for array cleanups (endOfInit and last constructed element). | |
| // - We update the array init element correctly for ctorA, ctorB and stmt-exprC. | |
| // - stmt-exprC and stmt-exprD share the array body dtor code (see %cleanup). | |
| // CHECK-LABEL: define dso_local void @_Z9ArrayInitv() | |
| // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 | |
| // CHECK: %cleanup.dest.slot = alloca i32, align 4 | |
| // CHECK: store ptr %arr, ptr %arrayinit.endOfInit, align 8 | |
| Printy arr[4] = { | |
| Printy("a"), | |
| // CHECK: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) %arr, ptr noundef @.str) | |
| // CHECK: [[ARRAYINIT_ELEMENT1:%.+]] = getelementptr inbounds %struct.Printy, ptr %arr, i64 1 | |
| // CHECK: store ptr [[ARRAYINIT_ELEMENT1]], ptr %arrayinit.endOfInit, align 8 | |
| Printy("b"), | |
| // CHECK: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) [[ARRAYINIT_ELEMENT1]], ptr noundef @.str.1) | |
| // CHECK: [[ARRAYINIT_ELEMENT2:%.+]] = getelementptr inbounds %struct.Printy, ptr %arr, i64 2 | |
| // CHECK: store ptr [[ARRAYINIT_ELEMENT2]], ptr %arrayinit.endOfInit, align 8 | |
| ({ | |
| // CHECK: br i1 {{.*}}, label %if.then, label %if.end | |
| if (foo()) { | |
| return; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| } | |
| // CHECK: if.end: | |
| Printy("c"); | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: %arrayinit.element2 = getelementptr inbounds %struct.Printy, ptr %arr, i64 3 | |
| // CHECK-NEXT: store ptr %arrayinit.element2, ptr %arrayinit.endOfInit, align 8 | |
| }), | |
| ({ | |
| // CHECK: br i1 {{%.+}} label %[[IF_THEN2:.+]], label %[[IF_END2:.+]] | |
| if (foo()) { | |
| return; | |
| // CHECK: [[IF_THEN2]]: | |
| // CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| } | |
| // CHECK: [[IF_END2]]: | |
| Printy("d"); | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: %array.begin = getelementptr inbounds [4 x %struct.Printy], ptr %arr, i32 0, i32 0 | |
| // CHECK-NEXT: %0 = getelementptr inbounds %struct.Printy, ptr %array.begin, i64 4 | |
| // CHECK-NEXT: br label %[[ARRAY_DESTROY_BODY1:.+]] | |
| }), | |
| }; | |
| // CHECK: [[ARRAY_DESTROY_BODY1]]: | |
| // CHECK-NEXT: %arraydestroy.elementPast{{.*}} = phi ptr [ %0, %[[IF_END2]] ], [ %arraydestroy.element{{.*}}, %[[ARRAY_DESTROY_BODY1]] ] | |
| // CHECK-NEXT: %arraydestroy.element{{.*}} = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast{{.*}}, i64 -1 | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: %arraydestroy.done{{.*}} = icmp eq ptr %arraydestroy.element{{.*}}, %array.begin | |
| // CHECK-NEXT: br i1 %arraydestroy.done{{.*}}, label %[[ARRAY_DESTROY_DONE1:.+]], label %[[ARRAY_DESTROY_BODY1]] | |
| // CHECK: [[ARRAY_DESTROY_DONE1]]: | |
| // CHECK-NEXT: ret void | |
| // CHECK: cleanup: | |
| // CHECK-NEXT: %1 = load ptr, ptr %arrayinit.endOfInit, align 8 | |
| // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr %arr, %1 | |
| // CHECK-NEXT: br i1 %arraydestroy.isempty, label %[[ARRAY_DESTROY_DONE2:.+]], label %[[ARRAY_DESTROY_BODY2:.+]] | |
| // CHECK: [[ARRAY_DESTROY_BODY2]]: | |
| // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %1, %cleanup ], [ %arraydestroy.element, %[[ARRAY_DESTROY_BODY2]] ] | |
| // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1 | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %arraydestroy.element) | |
| // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arr | |
| // CHECK-NEXT: br i1 %arraydestroy.done, label %[[ARRAY_DESTROY_DONE2]], label %[[ARRAY_DESTROY_BODY2]] | |
| // CHECK: [[ARRAY_DESTROY_DONE2]]: | |
| // CHECK-NEXT: br label %[[ARRAY_DESTROY_DONE1]] | |
| } | |
| void ArraySubobjects() { | |
| struct S { | |
| Printy arr1[2]; | |
| Printy arr2[2]; | |
| Printy p; | |
| }; | |
| // CHECK-LABEL: define dso_local void @_Z15ArraySubobjectsv() | |
| // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 | |
| S s{{Printy("a"), Printy("b")}, | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| {Printy("a"), | |
| // CHECK: store ptr %arr2, ptr %arrayinit.endOfInit, align 8 | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK: [[ARRAYINIT_ELEMENT:%.+]] = getelementptr inbounds %struct.Printy | |
| // CHECK: store ptr [[ARRAYINIT_ELEMENT]], ptr %arrayinit.endOfInit, align 8 | |
| ({ | |
| if (foo()) { | |
| return; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: [[V0:%.+]] = load ptr, ptr %arrayinit.endOfInit, align 8 | |
| // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr %arr2, [[V0]] | |
| // CHECK-NEXT: br i1 %arraydestroy.isempty, label %[[ARRAY_DESTROY_DONE:.+]], label %[[ARRAY_DESTROY_BODY:.+]] | |
| } | |
| Printy("b"); | |
| }) | |
| }, | |
| Printy("c") | |
| // CHECK: if.end: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: call void @_ZZ15ArraySubobjectsvEN1SD1Ev | |
| // CHECK-NEXT: br label %return | |
| }; | |
| // CHECK: return: | |
| // CHECK-NEXT: ret void | |
| // CHECK: [[ARRAY_DESTROY_BODY]]: | |
| // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %0, %if.then ], [ %arraydestroy.element, %[[ARRAY_DESTROY_BODY]] ] | |
| // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1 | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %arraydestroy.element) | |
| // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arr2 | |
| // CHECK-NEXT: br i1 %arraydestroy.done, label %[[ARRAY_DESTROY_DONE]], label %[[ARRAY_DESTROY_BODY]] | |
| // CHECK: [[ARRAY_DESTROY_DONE]] | |
| // CHECK-NEXT: [[ARRAY_BEGIN:%.+]] = getelementptr inbounds [2 x %struct.Printy], ptr %arr1, i32 0, i32 0 | |
| // CHECK-NEXT: [[V1:%.+]] = getelementptr inbounds %struct.Printy, ptr [[ARRAY_BEGIN]], i64 2 | |
| // CHECK-NEXT: br label %[[ARRAY_DESTROY_BODY2:.+]] | |
| // CHECK: [[ARRAY_DESTROY_BODY2]]: | |
| // CHECK-NEXT: %arraydestroy.elementPast4 = phi ptr [ %1, %[[ARRAY_DESTROY_DONE]] ], [ %arraydestroy.element5, %[[ARRAY_DESTROY_BODY2]] ] | |
| // CHECK-NEXT: %arraydestroy.element5 = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast4, i64 -1 | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %arraydestroy.element5) | |
| // CHECK-NEXT: %arraydestroy.done6 = icmp eq ptr %arraydestroy.element5, [[ARRAY_BEGIN]] | |
| // CHECK-NEXT: br i1 %arraydestroy.done6, label %[[ARRAY_DESTROY_DONE2:.+]], label %[[ARRAY_DESTROY_BODY2]] | |
| // CHECK: [[ARRAY_DESTROY_DONE2]]: | |
| // CHECK-NEXT: br label %return | |
| } | |
| void LambdaInit() { | |
| // CHECK-LABEL: define dso_local void @_Z10LambdaInitv() | |
| auto S = [a = Printy("a"), b = ({ | |
| if (foo()) { | |
| return; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: br label %return | |
| } | |
| Printy("b"); | |
| })]() { return a; }; | |
| } | |
| struct PrintyRefBind { | |
| const Printy &a; | |
| const Printy &b; | |
| }; | |
| struct Temp { | |
| Temp(); | |
| ~Temp(); | |
| }; | |
| Temp CreateTemp(); | |
| Printy CreatePrinty(); | |
| Printy CreatePrinty(const Temp&); | |
| void LifetimeExtended() { | |
| // CHECK-LABEL: define dso_local void @_Z16LifetimeExtendedv | |
| PrintyRefBind ps = {Printy("a"), ({ | |
| if (foo()) { | |
| return; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev | |
| // CHECK-NEXT: br label %return | |
| } | |
| Printy("b"); | |
| })}; | |
| } | |
| void ConditionalLifetimeExtended() { | |
| // CHECK-LABEL: @_Z27ConditionalLifetimeExtendedv() | |
| // Verify that we create two cleanup flags. | |
| // 1. First for the cleanup which is deactivated after full expression. | |
| // 2. Second for the life-ext cleanup which is activated if the branch is taken. | |
| // Note: We use `CreateTemp()` to ensure that life-ext destroy cleanup is not at | |
| // the top of EHStack on deactivation. This ensures using active flags. | |
| Printy* p1 = nullptr; | |
| // CHECK: store i1 false, ptr [[BRANCH1_DEFERRED:%cleanup.cond]], align 1 | |
| // CHECK-NEXT: store i1 false, ptr [[BRANCH1_LIFEEXT:%cleanup.cond.*]], align 1 | |
| PrintyRefBind ps = { | |
| p1 != nullptr ? static_cast<const Printy&>(CreatePrinty()) | |
| // CHECK: cond.true: | |
| // CHECK-NEXT: call void @_Z12CreatePrintyv | |
| // CHECK-NEXT: store i1 true, ptr [[BRANCH1_DEFERRED]], align 1 | |
| // CHECK-NEXT: store i1 true, ptr [[BRANCH1_LIFEEXT]], align 1 | |
| // CHECK-NEXT: br label %{{.*}} | |
| : foo() ? static_cast<const Printy&>(CreatePrinty(CreateTemp())) | |
| : *p1, | |
| ({ | |
| if (foo()) return; | |
| Printy("c"); | |
| // CHECK: if.end: | |
| // CHECK-NEXT: call void @_ZN6PrintyC1EPKc | |
| // CHECK-NEXT: store ptr | |
| })}; | |
| // CHECK-NEXT: store i1 false, ptr [[BRANCH1_DEFERRED]], align 1 | |
| // CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot, align 4 | |
| // CHECK-NEXT: br label %cleanup | |
| } | |
| void NewArrayInit() { | |
| // CHECK-LABEL: define dso_local void @_Z12NewArrayInitv() | |
| // CHECK: %array.init.end = alloca ptr, align 8 | |
| // CHECK: store ptr %0, ptr %array.init.end, align 8 | |
| Printy *array = new Printy[3]{ | |
| "a", | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK: store ptr %array.exp.next, ptr %array.init.end, align 8 | |
| "b", | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| // CHECK: store ptr %array.exp.next1, ptr %array.init.end, align 8 | |
| ({ | |
| if (foo()) { | |
| return; | |
| // CHECK: if.then: | |
| // CHECK: br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body | |
| } | |
| "b"; | |
| // CHECK: if.end: | |
| // CHECK: call void @_ZN6PrintyC1EPKc | |
| })}; | |
| // CHECK: arraydestroy.body: | |
| // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %{{.*}}, %if.then ], [ %arraydestroy.element, %arraydestroy.body ] | |
| // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1 | |
| // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dead_on_return(8) dereferenceable(8) %arraydestroy.element) | |
| // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %0 | |
| // CHECK-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done{{.*}}, label %arraydestroy.body | |
| // CHECK: arraydestroy.done{{.*}}: ; preds = %arraydestroy.body, %if.then | |
| // CHECK-NEXT: br label %return | |
| } | |
| void DestroyInConditionalCleanup() { | |
| // EH-LABEL: DestroyInConditionalCleanupv() | |
| // NOEH-LABEL: DestroyInConditionalCleanupv() | |
| struct A { | |
| A() {} | |
| ~A() {} | |
| }; | |
| struct Value { | |
| Value(A) {} | |
| ~Value() {} | |
| }; | |
| struct V2 { | |
| Value K; | |
| Value V; | |
| }; | |
| // Verify we use conditional cleanups. | |
| (void)(foo() ? V2{A(), A()} : V2{A(), A()}); | |
| // NOEH: cond.true: | |
| // NOEH: call void @_ZZ27DestroyInConditionalCleanupvEN1AC1Ev | |
| // NOEH: store ptr %{{.*}}, ptr %cond-cleanup.save | |
| // EH: cond.true: | |
| // EH: invoke void @_ZZ27DestroyInConditionalCleanupvEN1AC1Ev | |
| // EH: store ptr %{{.*}}, ptr %cond-cleanup.save | |
| } | |
| void ArrayInitWithContinue() { | |
| // CHECK-LABEL: @_Z21ArrayInitWithContinuev | |
| // Verify that we start to emit the array destructor. | |
| // CHECK: %arrayinit.endOfInit = alloca ptr, align 8 | |
| for (int i = 0; i < 1; ++i) { | |
| Printy arr[2] = {"a", ({ | |
| if (foo()) { | |
| continue; | |
| } | |
| "b"; | |
| })}; | |
| } | |
| } | |
| struct [[clang::trivial_abi]] HasTrivialABI { | |
| HasTrivialABI(); | |
| ~HasTrivialABI(); | |
| }; | |
| void AcceptTrivialABI(HasTrivialABI, int); | |
| void TrivialABI() { | |
| // CHECK-LABEL: define dso_local void @_Z10TrivialABIv() | |
| AcceptTrivialABI(HasTrivialABI(), ({ | |
| if (foo()) return; | |
| // CHECK: if.then: | |
| // CHECK-NEXT: call void @_ZN13HasTrivialABID1Ev | |
| // CHECK-NEXT: br label %return | |
| 0; | |
| })); | |
| } | |
| namespace CleanupFlag { | |
| struct A { | |
| A() {} | |
| ~A() {} | |
| }; | |
| struct B { | |
| B(const A&) {} | |
| B() {} | |
| ~B() {} | |
| }; | |
| struct S { | |
| A a; | |
| B b; | |
| }; | |
| int AcceptS(S s); | |
| void Accept2(int x, int y); | |
| void InactiveNormalCleanup() { | |
| // CHECK-LABEL: define {{.*}}InactiveNormalCleanupEv() | |
| // The first A{} below is an inactive normal cleanup which | |
| // is not popped from EHStack on deactivation. This needs an | |
| // "active" cleanup flag. | |
| // CHECK: [[ACTIVE:%cleanup.isactive.*]] = alloca i1, align 1 | |
| // CHECK: call void [[A_CTOR:@.*AC1Ev]] | |
| // CHECK: store i1 true, ptr [[ACTIVE]], align 1 | |
| // CHECK: call void [[A_CTOR]] | |
| // CHECK: call void [[B_CTOR:@.*BC1ERKNS_1AE]] | |
| // CHECK: store i1 false, ptr [[ACTIVE]], align 1 | |
| // CHECK: call noundef i32 [[ACCEPTS:@.*AcceptSENS_1SE]] | |
| Accept2(AcceptS({.a = A{}, .b = A{}}), ({ | |
| if (foo()) return; | |
| // CHECK: if.then: | |
| // CHECK: br label %cleanup | |
| 0; | |
| // CHECK: if.end: | |
| // CHECK: call void [[ACCEPT2:@.*Accept2Eii]] | |
| // CHECK: br label %cleanup | |
| })); | |
| // CHECK: cleanup: | |
| // CHECK: call void [[S_DTOR:@.*SD1Ev]] | |
| // CHECK: call void [[A_DTOR:@.*AD1Ev]] | |
| // CHECK: %cleanup.is_active = load i1, ptr [[ACTIVE]] | |
| // CHECK: br i1 %cleanup.is_active, label %cleanup.action, label %cleanup.done | |
| // CHECK: cleanup.action: | |
| // CHECK: call void [[A_DTOR]] | |
| // The "active" cleanup flag is not required for unused cleanups. | |
| Accept2(AcceptS({.a = A{}, .b = A{}}), 0); | |
| // CHECK: cleanup.cont: | |
| // CHECK: call void [[A_CTOR]] | |
| // CHECK-NOT: store i1 true | |
| // CHECK: call void [[A_CTOR]] | |
| // CHECK: call void [[B_CTOR]] | |
| // CHECK-NOT: store i1 false | |
| // CHECK: call noundef i32 [[ACCEPTS]] | |
| // CHECK: call void [[ACCEPT2]] | |
| // CHECK: call void [[S_DTOR]] | |
| // CHECK: call void [[A_DTOR]] | |
| // CHECK: br label %return | |
| } | |
| } // namespace CleanupFlag |