521 lines · cpp
1// RUN: %clang_cc1 --std=c++20 -fexceptions -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefixes=EH %s2// RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefixes=NOEH,CHECK %s3 4struct Printy {5 Printy(const char *name) : name(name) {}6 ~Printy() {}7 const char *name;8};9 10int foo() { return 2; }11 12struct Printies {13 Printy a;14 Printy b;15 Printy c;16};17 18void ParenInit() {19 // CHECK-LABEL: define dso_local void @_Z9ParenInitv()20 // CHECK: [[CLEANUP_DEST:%.+]] = alloca i32, align 421 Printies ps(Printy("a"), 22 // CHECK: call void @_ZN6PrintyC1EPKc23 ({24 if (foo()) return;25 // CHECK: if.then:26 // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 427 // CHECK-NEXT: br label %cleanup28 Printy("b");29 // CHECK: if.end:30 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc31 }),32 ({33 if (foo()) return;34 // CHECK: if.then{{.*}}:35 // CHECK-NEXT: store i32 1, ptr [[CLEANUP_DEST]], align 436 // CHECK-NEXT: call void @_ZN6PrintyD1Ev37 // CHECK-NEXT: br label %cleanup38 Printy("c");39 // CHECK: if.end{{.*}}:40 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc41 // CHECK-NEXT: call void @_ZN8PrintiesD1Ev42 // CHECK-NEXT: br label %return43 }));44 // CHECK: cleanup:45 // CHECK-NEXT: call void @_ZN6PrintyD1Ev46 // CHECK-NEXT: br label %return47}48 49void break_in_stmt_expr() {50 // Verify that the "break" in "if.then".calls dtor before jumping to "for.end".51 52 // CHECK-LABEL: define dso_local void @_Z18break_in_stmt_exprv()53 Printies p{Printy("a"), 54 // CHECK: call void @_ZN6PrintyC1EPKc55 ({56 for (;;) {57 Printies ps{58 Printy("b"), 59 // CHECK: for.cond:60 // CHECK: call void @_ZN6PrintyC1EPKc61 ({62 if (foo()) {63 break;64 // CHECK: if.then:65 // CHECK-NEXT: call void @_ZN6PrintyD1Ev66 // CHECK-NEXT: br label %for.end67 }68 Printy("c");69 // CHECK: if.end:70 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc71 }),72 Printy("d")};73 // CHECK: call void @_ZN6PrintyC1EPKc74 // CHECK-NEXT: call void @_ZN8PrintiesD1Ev75 // CHECK-NEXT: br label %for.cond76 }77 Printy("e");78 // CHECK: for.end:79 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc80 }),81 Printy("f")};82 // CHECK: call void @_ZN6PrintyC1EPKc83 // CHECK-NEXT: call void @_ZN8PrintiesD1Ev84}85 86void goto_in_stmt_expr() {87 // Verify that:88 // - correct branch fixups for deactivated normal cleanups are generated correctly.89 90 // CHECK-LABEL: define dso_local void @_Z17goto_in_stmt_exprv()91 // CHECK: [[CLEANUP_DEST_SLOT:%cleanup.dest.slot.*]] = alloca i32, align 492 {93 Printies p1{Printy("a"), // CHECK: call void @_ZN6PrintyC1EPKc94 ({95 {96 Printies p2{Printy("b"),97 // CHECK: call void @_ZN6PrintyC1EPKc98 ({99 if (foo() == 1) {100 goto in;101 // CHECK: if.then:102 // CHECK-NEXT: store i32 2, ptr [[CLEANUP_DEST_SLOT]], align 4103 // CHECK-NEXT: br label %[[CLEANUP1:.+]]104 }105 if (foo() == 2) {106 goto out;107 // CHECK: if.then{{.*}}:108 // CHECK-NEXT: store i32 3, ptr [[CLEANUP_DEST_SLOT]], align 4109 // CHECK-NEXT: br label %[[CLEANUP1]]110 }111 Printy("c");112 // CHECK: if.end{{.*}}:113 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc114 }),115 Printy("d")};116 // CHECK: call void @_ZN6PrintyC1EPKc117 // CHECK-NEXT: call void @_ZN8PrintiesD1Ev118 // CHECK-NEXT: br label %in119 120 }121 in:122 Printy("e");123 // CHECK: in: ; preds = %if.end{{.*}}, %[[CLEANUP1]]124 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc125 }),126 Printy("f")};127 // CHECK: call void @_ZN6PrintyC1EPKc128 // CHECK-NEXT: call void @_ZN8PrintiesD1Ev129 // CHECK-NEXT: br label %out130 }131out:132 return;133 // CHECK: out:134 // CHECK-NEXT: ret void135 136 // CHECK: [[CLEANUP1]]: ; preds = %if.then{{.*}}, %if.then137 // CHECK-NEXT: call void @_ZN6PrintyD1Ev138 // CHECK-NEXT: %cleanup.dest = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4139 // CHECK-NEXT: switch i32 %cleanup.dest, label %[[CLEANUP2:.+]] [140 // CHECK-NEXT: i32 2, label %in141 // CHECK-NEXT: ]142 143 // CHECK: [[CLEANUP2]]: ; preds = %[[CLEANUP1]]144 // CHECK-NEXT: call void @_ZN6PrintyD1Ev145 // CHECK-NEXT: %cleanup.dest{{.*}} = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4146 // CHECK-NEXT: switch i32 %cleanup.dest{{.*}}, label %unreachable [147 // CHECK-NEXT: i32 3, label %out148 // CHECK-NEXT: ]149}150 151void ArrayInit() {152 // Printy arr[4] = {ctorA, ctorB, stmt-exprC, stmt-exprD};153 // Verify that:154 // - We do the necessary stores for array cleanups (endOfInit and last constructed element).155 // - We update the array init element correctly for ctorA, ctorB and stmt-exprC.156 // - stmt-exprC and stmt-exprD share the array body dtor code (see %cleanup).157 158 // CHECK-LABEL: define dso_local void @_Z9ArrayInitv()159 // CHECK: %arrayinit.endOfInit = alloca ptr, align 8160 // CHECK: %cleanup.dest.slot = alloca i32, align 4161 // CHECK: store ptr %arr, ptr %arrayinit.endOfInit, align 8162 Printy arr[4] = {163 Printy("a"),164 // CHECK: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) %arr, ptr noundef @.str)165 // CHECK: [[ARRAYINIT_ELEMENT1:%.+]] = getelementptr inbounds %struct.Printy, ptr %arr, i64 1166 // CHECK: store ptr [[ARRAYINIT_ELEMENT1]], ptr %arrayinit.endOfInit, align 8167 Printy("b"),168 // CHECK: call void @_ZN6PrintyC1EPKc(ptr noundef nonnull align 8 dereferenceable(8) [[ARRAYINIT_ELEMENT1]], ptr noundef @.str.1)169 // CHECK: [[ARRAYINIT_ELEMENT2:%.+]] = getelementptr inbounds %struct.Printy, ptr %arr, i64 2170 // CHECK: store ptr [[ARRAYINIT_ELEMENT2]], ptr %arrayinit.endOfInit, align 8171 ({172 // CHECK: br i1 {{.*}}, label %if.then, label %if.end173 if (foo()) {174 return;175 // CHECK: if.then:176 // CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot, align 4177 // CHECK-NEXT: br label %cleanup178 }179 // CHECK: if.end:180 Printy("c");181 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc182 // CHECK-NEXT: %arrayinit.element2 = getelementptr inbounds %struct.Printy, ptr %arr, i64 3183 // CHECK-NEXT: store ptr %arrayinit.element2, ptr %arrayinit.endOfInit, align 8184 }),185 ({186 // CHECK: br i1 {{%.+}} label %[[IF_THEN2:.+]], label %[[IF_END2:.+]]187 if (foo()) {188 return;189 // CHECK: [[IF_THEN2]]:190 // CHECK-NEXT: store i32 1, ptr %cleanup.dest.slot, align 4191 // CHECK-NEXT: br label %cleanup192 }193 // CHECK: [[IF_END2]]:194 Printy("d");195 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc196 // CHECK-NEXT: %array.begin = getelementptr inbounds [4 x %struct.Printy], ptr %arr, i32 0, i32 0197 // CHECK-NEXT: %0 = getelementptr inbounds %struct.Printy, ptr %array.begin, i64 4198 // CHECK-NEXT: br label %[[ARRAY_DESTROY_BODY1:.+]]199 }),200 };201 202 // CHECK: [[ARRAY_DESTROY_BODY1]]:203 // CHECK-NEXT: %arraydestroy.elementPast{{.*}} = phi ptr [ %0, %[[IF_END2]] ], [ %arraydestroy.element{{.*}}, %[[ARRAY_DESTROY_BODY1]] ]204 // CHECK-NEXT: %arraydestroy.element{{.*}} = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast{{.*}}, i64 -1205 // CHECK-NEXT: call void @_ZN6PrintyD1Ev206 // CHECK-NEXT: %arraydestroy.done{{.*}} = icmp eq ptr %arraydestroy.element{{.*}}, %array.begin207 // CHECK-NEXT: br i1 %arraydestroy.done{{.*}}, label %[[ARRAY_DESTROY_DONE1:.+]], label %[[ARRAY_DESTROY_BODY1]]208 209 // CHECK: [[ARRAY_DESTROY_DONE1]]:210 // CHECK-NEXT: ret void211 212 // CHECK: cleanup:213 // CHECK-NEXT: %1 = load ptr, ptr %arrayinit.endOfInit, align 8214 // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr %arr, %1215 // CHECK-NEXT: br i1 %arraydestroy.isempty, label %[[ARRAY_DESTROY_DONE2:.+]], label %[[ARRAY_DESTROY_BODY2:.+]]216 217 // CHECK: [[ARRAY_DESTROY_BODY2]]:218 // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %1, %cleanup ], [ %arraydestroy.element, %[[ARRAY_DESTROY_BODY2]] ]219 // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1220 // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)221 // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arr222 // CHECK-NEXT: br i1 %arraydestroy.done, label %[[ARRAY_DESTROY_DONE2]], label %[[ARRAY_DESTROY_BODY2]]223 224 // CHECK: [[ARRAY_DESTROY_DONE2]]:225 // CHECK-NEXT: br label %[[ARRAY_DESTROY_DONE1]]226}227 228void ArraySubobjects() {229 struct S {230 Printy arr1[2];231 Printy arr2[2];232 Printy p;233 };234 // CHECK-LABEL: define dso_local void @_Z15ArraySubobjectsv()235 // CHECK: %arrayinit.endOfInit = alloca ptr, align 8236 S s{{Printy("a"), Printy("b")},237 // CHECK: call void @_ZN6PrintyC1EPKc238 // CHECK: call void @_ZN6PrintyC1EPKc239 {Printy("a"),240 // CHECK: store ptr %arr2, ptr %arrayinit.endOfInit, align 8241 // CHECK: call void @_ZN6PrintyC1EPKc242 // CHECK: [[ARRAYINIT_ELEMENT:%.+]] = getelementptr inbounds %struct.Printy243 // CHECK: store ptr [[ARRAYINIT_ELEMENT]], ptr %arrayinit.endOfInit, align 8244 ({245 if (foo()) {246 return;247 // CHECK: if.then:248 // CHECK-NEXT: [[V0:%.+]] = load ptr, ptr %arrayinit.endOfInit, align 8249 // CHECK-NEXT: %arraydestroy.isempty = icmp eq ptr %arr2, [[V0]]250 // CHECK-NEXT: br i1 %arraydestroy.isempty, label %[[ARRAY_DESTROY_DONE:.+]], label %[[ARRAY_DESTROY_BODY:.+]]251 }252 Printy("b");253 })254 },255 Printy("c")256 // CHECK: if.end:257 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc258 // CHECK: call void @_ZN6PrintyC1EPKc259 // CHECK-NEXT: call void @_ZZ15ArraySubobjectsvEN1SD1Ev260 // CHECK-NEXT: br label %return261 };262 // CHECK: return:263 // CHECK-NEXT: ret void264 265 // CHECK: [[ARRAY_DESTROY_BODY]]:266 // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %0, %if.then ], [ %arraydestroy.element, %[[ARRAY_DESTROY_BODY]] ]267 // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1268 // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)269 // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arr2270 // CHECK-NEXT: br i1 %arraydestroy.done, label %[[ARRAY_DESTROY_DONE]], label %[[ARRAY_DESTROY_BODY]]271 272 // CHECK: [[ARRAY_DESTROY_DONE]]273 // CHECK-NEXT: [[ARRAY_BEGIN:%.+]] = getelementptr inbounds [2 x %struct.Printy], ptr %arr1, i32 0, i32 0274 // CHECK-NEXT: [[V1:%.+]] = getelementptr inbounds %struct.Printy, ptr [[ARRAY_BEGIN]], i64 2275 // CHECK-NEXT: br label %[[ARRAY_DESTROY_BODY2:.+]]276 277 // CHECK: [[ARRAY_DESTROY_BODY2]]:278 // CHECK-NEXT: %arraydestroy.elementPast4 = phi ptr [ %1, %[[ARRAY_DESTROY_DONE]] ], [ %arraydestroy.element5, %[[ARRAY_DESTROY_BODY2]] ]279 // CHECK-NEXT: %arraydestroy.element5 = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast4, i64 -1280 // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element5)281 // CHECK-NEXT: %arraydestroy.done6 = icmp eq ptr %arraydestroy.element5, [[ARRAY_BEGIN]]282 // CHECK-NEXT: br i1 %arraydestroy.done6, label %[[ARRAY_DESTROY_DONE2:.+]], label %[[ARRAY_DESTROY_BODY2]]283 284 285 // CHECK: [[ARRAY_DESTROY_DONE2]]:286 // CHECK-NEXT: br label %return287}288 289void LambdaInit() {290 // CHECK-LABEL: define dso_local void @_Z10LambdaInitv()291 auto S = [a = Printy("a"), b = ({292 if (foo()) {293 return;294 // CHECK: if.then:295 // CHECK-NEXT: call void @_ZN6PrintyD1Ev296 // CHECK-NEXT: br label %return297 }298 Printy("b");299 })]() { return a; };300}301 302struct PrintyRefBind {303 const Printy &a;304 const Printy &b;305};306 307struct Temp {308 Temp();309 ~Temp();310};311Temp CreateTemp();312Printy CreatePrinty();313Printy CreatePrinty(const Temp&);314 315void LifetimeExtended() {316 // CHECK-LABEL: define dso_local void @_Z16LifetimeExtendedv317 PrintyRefBind ps = {Printy("a"), ({318 if (foo()) {319 return;320 // CHECK: if.then:321 // CHECK-NEXT: call void @_ZN6PrintyD1Ev322 // CHECK-NEXT: br label %return323 }324 Printy("b");325 })};326}327 328void ConditionalLifetimeExtended() {329 // CHECK-LABEL: @_Z27ConditionalLifetimeExtendedv()330 331 // Verify that we create two cleanup flags.332 // 1. First for the cleanup which is deactivated after full expression.333 // 2. Second for the life-ext cleanup which is activated if the branch is taken.334 335 // Note: We use `CreateTemp()` to ensure that life-ext destroy cleanup is not at336 // the top of EHStack on deactivation. This ensures using active flags.337 338 Printy* p1 = nullptr;339 // CHECK: store i1 false, ptr [[BRANCH1_DEFERRED:%cleanup.cond]], align 1340 // CHECK-NEXT: store i1 false, ptr [[BRANCH1_LIFEEXT:%cleanup.cond.*]], align 1341 PrintyRefBind ps = {342 p1 != nullptr ? static_cast<const Printy&>(CreatePrinty())343 // CHECK: cond.true:344 // CHECK-NEXT: call void @_Z12CreatePrintyv345 // CHECK-NEXT: store i1 true, ptr [[BRANCH1_DEFERRED]], align 1346 // CHECK-NEXT: store i1 true, ptr [[BRANCH1_LIFEEXT]], align 1347 // CHECK-NEXT: br label %{{.*}}348 : foo() ? static_cast<const Printy&>(CreatePrinty(CreateTemp()))349 : *p1,350 ({351 if (foo()) return;352 Printy("c");353 // CHECK: if.end:354 // CHECK-NEXT: call void @_ZN6PrintyC1EPKc355 // CHECK-NEXT: store ptr356 })};357 // CHECK-NEXT: store i1 false, ptr [[BRANCH1_DEFERRED]], align 1358 // CHECK-NEXT: store i32 0, ptr %cleanup.dest.slot, align 4359 // CHECK-NEXT: br label %cleanup360 361}362 363void NewArrayInit() {364 // CHECK-LABEL: define dso_local void @_Z12NewArrayInitv()365 // CHECK: %array.init.end = alloca ptr, align 8366 // CHECK: store ptr %0, ptr %array.init.end, align 8367 Printy *array = new Printy[3]{368 "a",369 // CHECK: call void @_ZN6PrintyC1EPKc370 // CHECK: store ptr %array.exp.next, ptr %array.init.end, align 8371 "b", 372 // CHECK: call void @_ZN6PrintyC1EPKc373 // CHECK: store ptr %array.exp.next1, ptr %array.init.end, align 8374 ({375 if (foo()) {376 return;377 // CHECK: if.then:378 // CHECK: br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body379 }380 "b";381 // CHECK: if.end:382 // CHECK: call void @_ZN6PrintyC1EPKc383 })};384 // CHECK: arraydestroy.body:385 // CHECK-NEXT: %arraydestroy.elementPast = phi ptr [ %{{.*}}, %if.then ], [ %arraydestroy.element, %arraydestroy.body ]386 // CHECK-NEXT: %arraydestroy.element = getelementptr inbounds %struct.Printy, ptr %arraydestroy.elementPast, i64 -1387 // CHECK-NEXT: call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)388 // CHECK-NEXT: %arraydestroy.done = icmp eq ptr %arraydestroy.element, %0389 // CHECK-NEXT: br i1 %arraydestroy.done, label %arraydestroy.done{{.*}}, label %arraydestroy.body390 391 // CHECK: arraydestroy.done{{.*}}: ; preds = %arraydestroy.body, %if.then392 // CHECK-NEXT: br label %return393}394 395void DestroyInConditionalCleanup() {396 // EH-LABEL: DestroyInConditionalCleanupv()397 // NOEH-LABEL: DestroyInConditionalCleanupv()398 struct A {399 A() {}400 ~A() {}401 };402 403 struct Value {404 Value(A) {}405 ~Value() {}406 };407 408 struct V2 {409 Value K;410 Value V;411 };412 // Verify we use conditional cleanups.413 (void)(foo() ? V2{A(), A()} : V2{A(), A()});414 // NOEH: cond.true:415 // NOEH: call void @_ZZ27DestroyInConditionalCleanupvEN1AC1Ev416 // NOEH: store ptr %{{.*}}, ptr %cond-cleanup.save417 418 // EH: cond.true:419 // EH: invoke void @_ZZ27DestroyInConditionalCleanupvEN1AC1Ev420 // EH: store ptr %{{.*}}, ptr %cond-cleanup.save421}422 423void ArrayInitWithContinue() {424 // CHECK-LABEL: @_Z21ArrayInitWithContinuev425 // Verify that we start to emit the array destructor.426 // CHECK: %arrayinit.endOfInit = alloca ptr, align 8427 for (int i = 0; i < 1; ++i) {428 Printy arr[2] = {"a", ({429 if (foo()) {430 continue;431 }432 "b";433 })};434 }435}436 437struct [[clang::trivial_abi]] HasTrivialABI {438 HasTrivialABI();439 ~HasTrivialABI();440};441void AcceptTrivialABI(HasTrivialABI, int);442void TrivialABI() {443 // CHECK-LABEL: define dso_local void @_Z10TrivialABIv()444 AcceptTrivialABI(HasTrivialABI(), ({445 if (foo()) return;446 // CHECK: if.then:447 // CHECK-NEXT: call void @_ZN13HasTrivialABID1Ev448 // CHECK-NEXT: br label %return449 0;450 }));451}452 453namespace CleanupFlag {454struct A {455 A() {}456 ~A() {}457};458 459struct B {460 B(const A&) {}461 B() {}462 ~B() {}463};464 465struct S {466 A a;467 B b;468};469 470int AcceptS(S s);471 472void Accept2(int x, int y);473 474void InactiveNormalCleanup() {475 // CHECK-LABEL: define {{.*}}InactiveNormalCleanupEv()476 477 // The first A{} below is an inactive normal cleanup which478 // is not popped from EHStack on deactivation. This needs an479 // "active" cleanup flag.480 481 // CHECK: [[ACTIVE:%cleanup.isactive.*]] = alloca i1, align 1482 // CHECK: call void [[A_CTOR:@.*AC1Ev]]483 // CHECK: store i1 true, ptr [[ACTIVE]], align 1484 // CHECK: call void [[A_CTOR]]485 // CHECK: call void [[B_CTOR:@.*BC1ERKNS_1AE]]486 // CHECK: store i1 false, ptr [[ACTIVE]], align 1487 // CHECK: call noundef i32 [[ACCEPTS:@.*AcceptSENS_1SE]]488 Accept2(AcceptS({.a = A{}, .b = A{}}), ({489 if (foo()) return;490 // CHECK: if.then:491 // CHECK: br label %cleanup492 0;493 // CHECK: if.end:494 // CHECK: call void [[ACCEPT2:@.*Accept2Eii]]495 // CHECK: br label %cleanup496 }));497 // CHECK: cleanup:498 // CHECK: call void [[S_DTOR:@.*SD1Ev]]499 // CHECK: call void [[A_DTOR:@.*AD1Ev]]500 // CHECK: %cleanup.is_active = load i1, ptr [[ACTIVE]]501 // CHECK: br i1 %cleanup.is_active, label %cleanup.action, label %cleanup.done502 503 // CHECK: cleanup.action:504 // CHECK: call void [[A_DTOR]]505 506 // The "active" cleanup flag is not required for unused cleanups.507 Accept2(AcceptS({.a = A{}, .b = A{}}), 0);508 // CHECK: cleanup.cont:509 // CHECK: call void [[A_CTOR]]510 // CHECK-NOT: store i1 true511 // CHECK: call void [[A_CTOR]]512 // CHECK: call void [[B_CTOR]]513 // CHECK-NOT: store i1 false514 // CHECK: call noundef i32 [[ACCEPTS]]515 // CHECK: call void [[ACCEPT2]]516 // CHECK: call void [[S_DTOR]]517 // CHECK: call void [[A_DTOR]]518 // CHECK: br label %return519}520} // namespace CleanupFlag521