xref: /llvm-project/clang/test/CodeGenCXX/control-flow-in-stmt-expr.cpp (revision 12d24e0c56a154c88247e55c7c352030e4d9073d)
1 // RUN: %clang_cc1 --std=c++20 -fexceptions -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefixes=EH %s
2 // RUN: %clang_cc1 --std=c++20 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck -check-prefixes=NOEH,CHECK %s
3 
4 struct Printy {
PrintyPrinty5   Printy(const char *name) : name(name) {}
~PrintyPrinty6   ~Printy() {}
7   const char *name;
8 };
9 
foo()10 int foo() { return 2; }
11 
12 struct Printies {
13   Printy a;
14   Printy b;
15   Printy c;
16 };
17 
ParenInit()18 void ParenInit() {
19   // CHECK-LABEL: define dso_local void @_Z9ParenInitv()
20   // CHECK: [[CLEANUP_DEST:%.+]] = alloca i32, align 4
21   Printies ps(Printy("a"),
22               // CHECK: call void @_ZN6PrintyC1EPKc
23               ({
24                 if (foo()) return;
25                 // CHECK:     if.then:
26                 // CHECK-NEXT:   store i32 1, ptr [[CLEANUP_DEST]], align 4
27                 // CHECK-NEXT:   br label %cleanup
28                 Printy("b");
29                 // CHECK:     if.end:
30                 // CHECK-NEXT:  call void @_ZN6PrintyC1EPKc
31               }),
32               ({
33                 if (foo()) return;
34                 // CHECK:     if.then{{.*}}:
35                 // CHECK-NEXT:  store i32 1, ptr [[CLEANUP_DEST]], align 4
36                 // CHECK-NEXT:  call void @_ZN6PrintyD1Ev
37                 // CHECK-NEXT:  br label %cleanup
38                 Printy("c");
39                 // CHECK:     if.end{{.*}}:
40                 // CHECK-NEXT:  call void @_ZN6PrintyC1EPKc
41                 // CHECK-NEXT:  call void @_ZN8PrintiesD1Ev
42                 // CHECK-NEXT:  br label %return
43               }));
44   // CHECK:     cleanup:
45   // CHECK-NEXT:  call void @_ZN6PrintyD1Ev
46   // CHECK-NEXT:  br label %return
47 }
48 
break_in_stmt_expr()49 void 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 @_ZN6PrintyC1EPKc
55             ({
56                 for (;;) {
57                     Printies ps{
58                       Printy("b"),
59                       // CHECK: for.cond:
60                       // CHECK:   call void @_ZN6PrintyC1EPKc
61                       ({
62                         if (foo()) {
63                           break;
64                           // CHECK:       if.then:
65                           // CHECK-NEXT:    call void @_ZN6PrintyD1Ev
66                           // CHECK-NEXT:    br label %for.end
67                         }
68                         Printy("c");
69                         // CHECK:       if.end:
70                         // CHECK-NEXT:    call void @_ZN6PrintyC1EPKc
71                       }),
72                       Printy("d")};
73                       // CHECK:           call void @_ZN6PrintyC1EPKc
74                       // CHECK-NEXT:      call void @_ZN8PrintiesD1Ev
75                       // CHECK-NEXT:      br label %for.cond
76                 }
77                 Printy("e");
78   // CHECK:       for.end:
79   // CHECK-NEXT:    call void @_ZN6PrintyC1EPKc
80               }),
81               Printy("f")};
82   // CHECK:         call void @_ZN6PrintyC1EPKc
83   // CHECK-NEXT:    call void @_ZN8PrintiesD1Ev
84 }
85 
goto_in_stmt_expr()86 void 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 4
92   {
93     Printies p1{Printy("a"), // CHECK: call void @_ZN6PrintyC1EPKc
94                 ({
95                   {
96                     Printies p2{Printy("b"),
97                                 // CHECK: call void @_ZN6PrintyC1EPKc
98                                 ({
99                                   if (foo() == 1) {
100                                     goto in;
101                                     // CHECK:       if.then:
102                                     // CHECK-NEXT:    store i32 2, ptr [[CLEANUP_DEST_SLOT]], align 4
103                                     // 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 4
109                                     // CHECK-NEXT:    br label %[[CLEANUP1]]
110                                   }
111                                   Printy("c");
112                                   // CHECK:       if.end{{.*}}:
113                                   // CHECK-NEXT:    call void @_ZN6PrintyC1EPKc
114                                 }),
115                                 Printy("d")};
116                                 // CHECK:           call void @_ZN6PrintyC1EPKc
117                                 // CHECK-NEXT:      call void @_ZN8PrintiesD1Ev
118                                 // CHECK-NEXT:      br label %in
119 
120                   }
121                 in:
122                   Printy("e");
123                 // CHECK:       in:                                               ; preds = %if.end{{.*}}, %[[CLEANUP1]]
124                 // CHECK-NEXT:    call void @_ZN6PrintyC1EPKc
125                 }),
126                 Printy("f")};
127                 // CHECK:         call void @_ZN6PrintyC1EPKc
128                 // CHECK-NEXT:    call void @_ZN8PrintiesD1Ev
129                 // CHECK-NEXT:    br label %out
130   }
131 out:
132   return;
133   // CHECK:       out:
134   // CHECK-NEXT:    ret void
135 
136   // CHECK:       [[CLEANUP1]]:                                          ; preds = %if.then{{.*}}, %if.then
137   // CHECK-NEXT:    call void @_ZN6PrintyD1Ev
138   // CHECK-NEXT:    %cleanup.dest = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4
139   // CHECK-NEXT:    switch i32 %cleanup.dest, label %[[CLEANUP2:.+]] [
140   // CHECK-NEXT:      i32 2, label %in
141   // CHECK-NEXT:    ]
142 
143   // CHECK:       [[CLEANUP2]]:                                         ; preds = %[[CLEANUP1]]
144   // CHECK-NEXT:    call void @_ZN6PrintyD1Ev
145   // CHECK-NEXT:    %cleanup.dest{{.*}} = load i32, ptr [[CLEANUP_DEST_SLOT]], align 4
146   // CHECK-NEXT:    switch i32 %cleanup.dest{{.*}}, label %unreachable [
147   // CHECK-NEXT:      i32 3, label %out
148   // CHECK-NEXT:    ]
149 }
150 
ArrayInit()151 void 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 8
160   // CHECK: %cleanup.dest.slot = alloca i32, align 4
161   // CHECK: store ptr %arr, ptr %arrayinit.endOfInit, align 8
162   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 1
166     // CHECK: store ptr [[ARRAYINIT_ELEMENT1]], ptr %arrayinit.endOfInit, align 8
167     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 2
170     // CHECK: store ptr [[ARRAYINIT_ELEMENT2]], ptr %arrayinit.endOfInit, align 8
171     ({
172     // CHECK: br i1 {{.*}}, label %if.then, label %if.end
173       if (foo()) {
174         return;
175       // CHECK:       if.then:
176       // CHECK-NEXT:    store i32 1, ptr %cleanup.dest.slot, align 4
177       // CHECK-NEXT:    br label %cleanup
178       }
179       // CHECK:       if.end:
180       Printy("c");
181       // CHECK-NEXT:    call void @_ZN6PrintyC1EPKc
182       // CHECK-NEXT:    %arrayinit.element2 = getelementptr inbounds %struct.Printy, ptr %arr, i64 3
183       // CHECK-NEXT:    store ptr %arrayinit.element2, ptr %arrayinit.endOfInit, align 8
184     }),
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 4
191       // CHECK-NEXT:    br label %cleanup
192       }
193       // CHECK:       [[IF_END2]]:
194       Printy("d");
195       // CHECK-NEXT:    call void @_ZN6PrintyC1EPKc
196       // CHECK-NEXT:    %array.begin = getelementptr inbounds [4 x %struct.Printy], ptr %arr, i32 0, i32 0
197       // CHECK-NEXT:    %0 = getelementptr inbounds %struct.Printy, ptr %array.begin, i64 4
198       // 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 -1
205   // CHECK-NEXT:    call void @_ZN6PrintyD1Ev
206   // CHECK-NEXT:    %arraydestroy.done{{.*}} = icmp eq ptr %arraydestroy.element{{.*}}, %array.begin
207   // CHECK-NEXT:    br i1 %arraydestroy.done{{.*}}, label %[[ARRAY_DESTROY_DONE1:.+]], label %[[ARRAY_DESTROY_BODY1]]
208 
209   // CHECK:       [[ARRAY_DESTROY_DONE1]]:
210   // CHECK-NEXT:    ret void
211 
212   // CHECK:       cleanup:
213   // CHECK-NEXT:    %1 = load ptr, ptr %arrayinit.endOfInit, align 8
214   // CHECK-NEXT:    %arraydestroy.isempty = icmp eq ptr %arr, %1
215   // 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 -1
220   // CHECK-NEXT:    call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)
221   // CHECK-NEXT:    %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arr
222   // 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 
ArraySubobjects()228 void 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 8
236   S s{{Printy("a"), Printy("b")},
237       // CHECK: call void @_ZN6PrintyC1EPKc
238       // CHECK: call void @_ZN6PrintyC1EPKc
239       {Printy("a"),
240       // CHECK: store ptr %arr2, ptr %arrayinit.endOfInit, align 8
241       // CHECK: call void @_ZN6PrintyC1EPKc
242       // CHECK: [[ARRAYINIT_ELEMENT:%.+]] = getelementptr inbounds %struct.Printy
243       // CHECK: store ptr [[ARRAYINIT_ELEMENT]], ptr %arrayinit.endOfInit, align 8
244       ({
245          if (foo()) {
246            return;
247            // CHECK:      if.then:
248            // CHECK-NEXT:   [[V0:%.+]] = load ptr, ptr %arrayinit.endOfInit, align 8
249            // 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 @_ZN6PrintyC1EPKc
258       // CHECK:         call void @_ZN6PrintyC1EPKc
259       // CHECK-NEXT:    call void @_ZZ15ArraySubobjectsvEN1SD1Ev
260       // CHECK-NEXT:    br label %return
261     };
262     // CHECK:       return:
263     // CHECK-NEXT:    ret void
264 
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 -1
268     // CHECK-NEXT:    call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)
269     // CHECK-NEXT:    %arraydestroy.done = icmp eq ptr %arraydestroy.element, %arr2
270     // 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 0
274     // CHECK-NEXT:    [[V1:%.+]] = getelementptr inbounds %struct.Printy, ptr [[ARRAY_BEGIN]], i64 2
275     // 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 -1
280     // 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 %return
287 }
288 
LambdaInit()289 void 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 @_ZN6PrintyD1Ev
296                                  // CHECK-NEXT:    br label %return
297                                }
298                                Printy("b");
299                              })]() { return a; };
300 }
301 
302 struct PrintyRefBind {
303   const Printy &a;
304   const Printy &b;
305 };
306 
307 struct Temp {
308   Temp();
309   ~Temp();
310 };
311 Temp CreateTemp();
312 Printy CreatePrinty();
313 Printy CreatePrinty(const Temp&);
314 
LifetimeExtended()315 void LifetimeExtended() {
316   // CHECK-LABEL: define dso_local void @_Z16LifetimeExtendedv
317   PrintyRefBind ps = {Printy("a"), ({
318                         if (foo()) {
319                           return;
320                           // CHECK: if.then:
321                           // CHECK-NEXT: call void @_ZN6PrintyD1Ev
322                           // CHECK-NEXT: br label %return
323                         }
324                         Printy("b");
325                       })};
326 }
327 
ConditionalLifetimeExtended()328 void 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 at
336   // 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 1
340   // CHECK-NEXT:  store i1 false, ptr [[BRANCH1_LIFEEXT:%cleanup.cond.*]], align 1
341   PrintyRefBind ps = {
342       p1 != nullptr ? static_cast<const Printy&>(CreatePrinty())
343       // CHECK:       cond.true:
344       // CHECK-NEXT:    call void @_Z12CreatePrintyv
345       // CHECK-NEXT:    store i1 true, ptr [[BRANCH1_DEFERRED]], align 1
346       // CHECK-NEXT:    store i1 true, ptr [[BRANCH1_LIFEEXT]], align 1
347       // 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 @_ZN6PrintyC1EPKc
355         // CHECK-NEXT:    store ptr
356       })};
357       // CHECK-NEXT:      store i1 false, ptr [[BRANCH1_DEFERRED]], align 1
358       // CHECK-NEXT:      store i32 0, ptr %cleanup.dest.slot, align 4
359       // CHECK-NEXT:      br label %cleanup
360 
361 }
362 
NewArrayInit()363 void NewArrayInit() {
364   // CHECK-LABEL: define dso_local void @_Z12NewArrayInitv()
365   // CHECK: %array.init.end = alloca ptr, align 8
366   // CHECK: store ptr %0, ptr %array.init.end, align 8
367   Printy *array = new Printy[3]{
368     "a",
369     // CHECK: call void @_ZN6PrintyC1EPKc
370     // CHECK: store ptr %array.exp.next, ptr %array.init.end, align 8
371     "b",
372     // CHECK: call void @_ZN6PrintyC1EPKc
373     // CHECK: store ptr %array.exp.next1, ptr %array.init.end, align 8
374     ({
375         if (foo()) {
376           return;
377           // CHECK: if.then:
378           // CHECK:   br i1 %arraydestroy.isempty, label %arraydestroy.done{{.*}}, label %arraydestroy.body
379         }
380         "b";
381         // CHECK: if.end:
382         // CHECK:   call void @_ZN6PrintyC1EPKc
383     })};
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 -1
387   // CHECK-NEXT:    call void @_ZN6PrintyD1Ev(ptr noundef nonnull align 8 dereferenceable(8) %arraydestroy.element)
388   // CHECK-NEXT:    %arraydestroy.done = icmp eq ptr %arraydestroy.element, %0
389   // CHECK-NEXT:    br i1 %arraydestroy.done, label %arraydestroy.done{{.*}}, label %arraydestroy.body
390 
391   // CHECK:       arraydestroy.done{{.*}}:                               ; preds = %arraydestroy.body, %if.then
392   // CHECK-NEXT:    br label %return
393 }
394 
DestroyInConditionalCleanup()395 void 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 @_ZZ27DestroyInConditionalCleanupvEN1AC1Ev
416   // NOEH:      store ptr %{{.*}}, ptr %cond-cleanup.save
417 
418   // EH:   cond.true:
419   // EH:        invoke void @_ZZ27DestroyInConditionalCleanupvEN1AC1Ev
420   // EH:        store ptr %{{.*}}, ptr %cond-cleanup.save
421 }
422 
ArrayInitWithContinue()423 void ArrayInitWithContinue() {
424   // CHECK-LABEL: @_Z21ArrayInitWithContinuev
425   // Verify that we start to emit the array destructor.
426   // CHECK: %arrayinit.endOfInit = alloca ptr, align 8
427   for (int i = 0; i < 1; ++i) {
428     Printy arr[2] = {"a", ({
429                        if (foo()) {
430                          continue;
431                        }
432                        "b";
433                      })};
434   }
435 }
436 
437 struct [[clang::trivial_abi]] HasTrivialABI {
438   HasTrivialABI();
439   ~HasTrivialABI();
440 };
441 void AcceptTrivialABI(HasTrivialABI, int);
TrivialABI()442 void 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 @_ZN13HasTrivialABID1Ev
448                      // CHECK-NEXT:   br label %return
449                      0;
450                    }));
451 }
452 
453 namespace CleanupFlag {
454 struct A {
ACleanupFlag::A455   A() {}
~ACleanupFlag::A456   ~A() {}
457 };
458 
459 struct B {
BCleanupFlag::B460   B(const A&) {}
BCleanupFlag::B461   B() {}
~BCleanupFlag::B462   ~B() {}
463 };
464 
465 struct S {
466   A a;
467   B b;
468 };
469 
470 int AcceptS(S s);
471 
472 void Accept2(int x, int y);
473 
InactiveNormalCleanup()474 void InactiveNormalCleanup() {
475   // CHECK-LABEL: define {{.*}}InactiveNormalCleanupEv()
476 
477   // The first A{} below is an inactive normal cleanup which
478   // is not popped from EHStack on deactivation. This needs an
479   // "active" cleanup flag.
480 
481   // CHECK: [[ACTIVE:%cleanup.isactive.*]] = alloca i1, align 1
482   // CHECK: call void [[A_CTOR:@.*AC1Ev]]
483   // CHECK: store i1 true, ptr [[ACTIVE]], align 1
484   // CHECK: call void [[A_CTOR]]
485   // CHECK: call void [[B_CTOR:@.*BC1ERKNS_1AE]]
486   // CHECK: store i1 false, ptr [[ACTIVE]], align 1
487   // 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 %cleanup
492             0;
493             // CHECK: if.end:
494             // CHECK:   call void [[ACCEPT2:@.*Accept2Eii]]
495             // CHECK:   br label %cleanup
496           }));
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.done
502 
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 true
511   // CHECK:   call void [[A_CTOR]]
512   // CHECK:   call void [[B_CTOR]]
513   // CHECK-NOT: store i1 false
514   // 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 %return
519 }
520 }  // namespace CleanupFlag
521