xref: /llvm-project/llvm/test/Transforms/SimplifyCFG/X86/empty-cleanuppad.ll (revision 07b9d231ff9baa6473b0dd588a3ce5330d3e4871)
1; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals
2; RUN: opt < %s -passes=simplifycfg -simplifycfg-require-and-preserve-domtree=1 -S -hoist-common-insts=true | FileCheck %s
3
4; ModuleID = 'cppeh-simplify.cpp'
5target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
6target triple = "x86_64-pc-windows-msvc18.0.0"
7
8
9; This case arises when two objects with empty destructors are cleaned up.
10;
11; void f1() {
12;   S a;
13;   S b;
14;   g();
15; }
16;
17; In this case, both cleanup pads can be eliminated and the invoke can be
18; converted to a call.
19;
20define void @f1() personality ptr @__CxxFrameHandler3 {
21; CHECK-LABEL: @f1(
22; CHECK-NEXT:  entry:
23; CHECK-NEXT:    call void @g()
24; CHECK-NEXT:    ret void
25;
26entry:
27  invoke void @g() to label %invoke.cont unwind label %ehcleanup
28
29invoke.cont:                                      ; preds = %entry
30  ret void
31
32ehcleanup:                                        ; preds = %entry
33  %0 = cleanuppad within none []
34  cleanupret from %0 unwind label %ehcleanup.1
35
36ehcleanup.1:                                      ; preds = %ehcleanup
37  %1 = cleanuppad within none []
38  cleanupret from %1 unwind to caller
39}
40
41
42; This case arises when an object with an empty destructor must be cleaned up
43; outside of a try-block and an object with a non-empty destructor must be
44; cleaned up within the try-block.
45;
46; void f2() {
47;   S a;
48;   try {
49;     S2 b;
50;     g();
51;   } catch (...) {}
52; }
53;
54; In this case, the outermost cleanup pad can be eliminated and the catch block
55; should unwind to the caller (that is, exception handling continues with the
56; parent frame of the caller).
57;
58define void @f2() personality ptr @__CxxFrameHandler3 {
59; CHECK-LABEL: @f2(
60; CHECK-NEXT:  entry:
61; CHECK-NEXT:    [[B:%.*]] = alloca [[STRUCT_S2:%.*]], align 1
62; CHECK-NEXT:    invoke void @g()
63; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[EHCLEANUP:%.*]]
64; CHECK:       ehcleanup:
65; CHECK-NEXT:    [[TMP0:%.*]] = cleanuppad within none []
66; CHECK-NEXT:    call void @"\01??1S2@@QEAA@XZ"(ptr [[B]])
67; CHECK-NEXT:    cleanupret from [[TMP0]] unwind label [[CATCH_DISPATCH:%.*]]
68; CHECK:       catch.dispatch:
69; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
70; CHECK:       catch:
71; CHECK-NEXT:    [[TMP1:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
72; CHECK-NEXT:    catchret from [[TMP1]] to label [[TRY_CONT]]
73; CHECK:       try.cont:
74; CHECK-NEXT:    ret void
75;
76entry:
77  %b = alloca %struct.S2, align 1
78  invoke void @g() to label %invoke.cont unwind label %ehcleanup
79
80invoke.cont:                                      ; preds = %entry
81  br label %try.cont
82
83ehcleanup:                                        ; preds = %entry
84  %0 = cleanuppad within none []
85  call void @"\01??1S2@@QEAA@XZ"(ptr %b)
86  cleanupret from %0 unwind label %catch.dispatch
87
88catch.dispatch:                                   ; preds = %ehcleanup
89  %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1
90
91catch:                                            ; preds = %catch.dispatch
92  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
93  catchret from %1 to label %catchret.dest
94
95catchret.dest:                                    ; preds = %catch
96  br label %try.cont
97
98try.cont:                                         ; preds = %catchret.dest, %invoke.cont
99  ret void
100
101ehcleanup.1:
102  %2 = cleanuppad within none []
103  cleanupret from %2 unwind to caller
104}
105
106
107; This case arises when an object with a non-empty destructor must be cleaned up
108; outside of a try-block and an object with an empty destructor must be cleaned
109; within the try-block.
110;
111; void f3() {
112;   S2 a;
113;   try {
114;     S b;
115;     g();
116;   } catch (...) {}
117; }
118;
119; In this case the inner cleanup pad should be eliminated and the invoke of g()
120; should unwind directly to the catchpad.
121
122define void @f3() personality ptr @__CxxFrameHandler3 {
123; CHECK-LABEL: @f3(
124; CHECK-NEXT:  entry:
125; CHECK-NEXT:    [[A:%.*]] = alloca [[STRUCT_S2:%.*]], align 1
126; CHECK-NEXT:    invoke void @g()
127; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
128; CHECK:       catch.dispatch:
129; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind label [[EHCLEANUP_1:%.*]]
130; CHECK:       catch:
131; CHECK-NEXT:    [[CP2:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
132; CHECK-NEXT:    catchret from [[CP2]] to label [[TRY_CONT]]
133; CHECK:       try.cont:
134; CHECK-NEXT:    ret void
135; CHECK:       ehcleanup.1:
136; CHECK-NEXT:    [[CP3:%.*]] = cleanuppad within none []
137; CHECK-NEXT:    call void @"\01??1S2@@QEAA@XZ"(ptr [[A]])
138; CHECK-NEXT:    cleanupret from [[CP3]] unwind to caller
139;
140entry:
141  %a = alloca %struct.S2, align 1
142  invoke void @g() to label %invoke.cont unwind label %ehcleanup
143
144invoke.cont:                                      ; preds = %entry
145  br label %try.cont
146
147ehcleanup:                                        ; preds = %entry
148  %0 = cleanuppad within none []
149  cleanupret from %0 unwind label %catch.dispatch
150
151catch.dispatch:                                   ; preds = %ehcleanup
152  %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup.1
153
154catch:                                            ; preds = %catch.dispatch
155  %cp2 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
156  catchret from %cp2 to label %catchret.dest
157
158catchret.dest:                                    ; preds = %catch
159  br label %try.cont
160
161try.cont:                                         ; preds = %catchret.dest, %invoke.cont
162  ret void
163
164ehcleanup.1:
165  %cp3 = cleanuppad within none []
166  call void @"\01??1S2@@QEAA@XZ"(ptr %a)
167  cleanupret from %cp3 unwind to caller
168}
169
170
171; This case arises when an object with an empty destructor may require cleanup
172; from either inside or outside of a try-block.
173;
174; void f4() {
175;   S a;
176;   g();
177;   try {
178;     g();
179;   } catch (...) {}
180; }
181;
182; In this case, the cleanuppad should be eliminated, the invoke outside of the
183; catch block should be converted to a call (that is, that is, exception
184; handling continues with the parent frame of the caller).)
185;
186; Note: The cleanuppad simplification will insert an unconditional branch here
187;       but it will be eliminated, placing the following invoke in the entry BB.
188;
189define void @f4() personality ptr @__CxxFrameHandler3 {
190; CHECK-LABEL: @f4(
191; CHECK-NEXT:  entry:
192; CHECK-NEXT:    call void @g()
193; CHECK-NEXT:    invoke void @g()
194; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
195; CHECK:       catch.dispatch:
196; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
197; CHECK:       catch:
198; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
199; CHECK-NEXT:    catchret from [[TMP0]] to label [[TRY_CONT]]
200; CHECK:       try.cont:
201; CHECK-NEXT:    ret void
202;
203entry:
204  invoke void @g()
205  to label %invoke.cont unwind label %ehcleanup
206
207invoke.cont:                                      ; preds = %entry
208  invoke void @g()
209  to label %try.cont unwind label %catch.dispatch
210
211catch.dispatch:                                   ; preds = %invoke.cont
212  %cs1 = catchswitch within none [label %catch] unwind label %ehcleanup
213
214catch:                                            ; preds = %catch.dispatch
215  %0 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
216  catchret from %0 to label %try.cont
217
218try.cont:                                         ; preds = %catch, %invoke.cont
219  ret void
220
221ehcleanup:
222  %cp2 = cleanuppad within none []
223  cleanupret from %cp2 unwind to caller
224}
225
226; This case tests simplification of an otherwise empty cleanup pad that contains
227; a PHI node.
228;
229; int f6() {
230;   int state = 1;
231;   try {
232;     S a;
233;     g();
234;     state = 2;
235;     g();
236;   } catch (...) {
237;     return state;
238;   }
239;   return 0;
240; }
241;
242; In this case, the cleanup pad should be eliminated and the PHI node in the
243; cleanup pad should be sunk into the catch dispatch block.
244;
245define i32 @f6() personality ptr @__CxxFrameHandler3 {
246; CHECK-LABEL: @f6(
247; CHECK-NEXT:  entry:
248; CHECK-NEXT:    invoke void @g()
249; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
250; CHECK:       invoke.cont:
251; CHECK-NEXT:    invoke void @g()
252; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
253; CHECK:       catch.dispatch:
254; CHECK-NEXT:    [[STATE_0:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ]
255; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
256; CHECK:       catch:
257; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
258; CHECK-NEXT:    catchret from [[TMP0]] to label [[RETURN]]
259; CHECK:       return:
260; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i32 [ [[STATE_0]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT]] ]
261; CHECK-NEXT:    ret i32 [[RETVAL_0]]
262;
263entry:
264  invoke void @g()
265  to label %invoke.cont unwind label %ehcleanup
266
267invoke.cont:                                      ; preds = %entry
268  invoke void @g()
269  to label %return unwind label %ehcleanup
270
271ehcleanup:                                        ; preds = %invoke.cont, %entry
272  %state.0 = phi i32 [ 2, %invoke.cont ], [ 1, %entry ]
273  %0 = cleanuppad within none []
274  cleanupret from %0 unwind label %catch.dispatch
275
276catch.dispatch:                                   ; preds = %ehcleanup
277  %cs1 = catchswitch within none [label %catch] unwind to caller
278
279catch:                                            ; preds = %catch.dispatch
280  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
281  catchret from %1 to label %return
282
283return:                                           ; preds = %invoke.cont, %catch
284  %retval.0 = phi i32 [ %state.0, %catch ], [ 0, %invoke.cont ]
285  ret i32 %retval.0
286}
287
288; This case tests another variation of simplification of an otherwise empty
289; cleanup pad that contains a PHI node.
290;
291; int f7() {
292;   int state = 1;
293;   try {
294;     g();
295;     state = 2;
296;     S a;
297;     g();
298;     state = 3;
299;     g();
300;   } catch (...) {
301;     return state;
302;   }
303;   return 0;
304; }
305;
306; In this case, the cleanup pad should be eliminated and the PHI node in the
307; cleanup pad should be merged with the PHI node in the catch dispatch block.
308;
309define i32 @f7() personality ptr @__CxxFrameHandler3 {
310; CHECK-LABEL: @f7(
311; CHECK-NEXT:  entry:
312; CHECK-NEXT:    invoke void @g()
313; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
314; CHECK:       invoke.cont:
315; CHECK-NEXT:    invoke void @g()
316; CHECK-NEXT:            to label [[INVOKE_CONT_1:%.*]] unwind label [[CATCH_DISPATCH]]
317; CHECK:       invoke.cont.1:
318; CHECK-NEXT:    invoke void @g()
319; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
320; CHECK:       catch.dispatch:
321; CHECK-NEXT:    [[STATE_1:%.*]] = phi i32 [ 1, [[ENTRY:%.*]] ], [ 3, [[INVOKE_CONT_1]] ], [ 2, [[INVOKE_CONT]] ]
322; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
323; CHECK:       catch:
324; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
325; CHECK-NEXT:    catchret from [[TMP0]] to label [[RETURN]]
326; CHECK:       return:
327; CHECK-NEXT:    [[RETVAL_0:%.*]] = phi i32 [ [[STATE_1]], [[CATCH:%.*]] ], [ 0, [[INVOKE_CONT_1]] ]
328; CHECK-NEXT:    ret i32 [[RETVAL_0]]
329;
330entry:
331  invoke void @g()
332  to label %invoke.cont unwind label %catch.dispatch
333
334invoke.cont:                                      ; preds = %entry
335  invoke void @g()
336  to label %invoke.cont.1 unwind label %ehcleanup
337
338invoke.cont.1:                                    ; preds = %invoke.cont
339  invoke void @g()
340  to label %return unwind label %ehcleanup
341
342ehcleanup:                                        ; preds = %invoke.cont.1, %invoke.cont
343  %state.0 = phi i32 [ 3, %invoke.cont.1 ], [ 2, %invoke.cont ]
344  %0 = cleanuppad within none []
345  cleanupret from %0 unwind label %catch.dispatch
346
347catch.dispatch:                                   ; preds = %ehcleanup, %entry
348  %state.1 = phi i32 [ %state.0, %ehcleanup ], [ 1, %entry ]
349  %cs1 = catchswitch within none [label %catch] unwind to caller
350
351catch:                                            ; preds = %catch.dispatch
352  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
353  catchret from %1 to label %return
354
355return:                                           ; preds = %invoke.cont.1, %catch
356  %retval.0 = phi i32 [ %state.1, %catch ], [ 0, %invoke.cont.1 ]
357  ret i32 %retval.0
358}
359
360; This case tests a scenario where an empty cleanup pad is not dominated by all
361; of the predecessors of its successor, but the successor references a PHI node
362; in the empty cleanup pad.
363;
364; Conceptually, the case being modeled is something like this:
365;
366; int f8() {
367;   int x = 1;
368;   try {
369;     S a;
370;     g();
371;     x = 2;
372; retry:
373;     g();
374;     return
375;   } catch (...) {
376;     use_x(x);
377;   }
378;   goto retry;
379; }
380;
381; While that C++ syntax isn't legal, the IR below is.
382;
383; In this case, the PHI node that is sunk from ehcleanup to catch.dispatch
384; should have an incoming value entry for path from 'foo' that references the
385; PHI node itself.
386;
387define void @f8() personality ptr @__CxxFrameHandler3 {
388; CHECK-LABEL: @f8(
389; CHECK-NEXT:  entry:
390; CHECK-NEXT:    invoke void @g()
391; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
392; CHECK:       invoke.cont:
393; CHECK-NEXT:    invoke void @g()
394; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
395; CHECK:       catch.dispatch:
396; CHECK-NEXT:    [[X:%.*]] = phi i32 [ 2, [[INVOKE_CONT]] ], [ 1, [[ENTRY:%.*]] ], [ [[X]], [[CATCH_CONT:%.*]] ]
397; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
398; CHECK:       catch:
399; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
400; CHECK-NEXT:    call void @use_x(i32 [[X]])
401; CHECK-NEXT:    catchret from [[TMP0]] to label [[CATCH_CONT]]
402; CHECK:       catch.cont:
403; CHECK-NEXT:    invoke void @g()
404; CHECK-NEXT:            to label [[RETURN]] unwind label [[CATCH_DISPATCH]]
405; CHECK:       return:
406; CHECK-NEXT:    ret void
407;
408entry:
409  invoke void @g()
410  to label %invoke.cont unwind label %ehcleanup
411
412invoke.cont:                                      ; preds = %entry
413  invoke void @g()
414  to label %return unwind label %ehcleanup
415
416ehcleanup:                                        ; preds = %invoke.cont, %entry
417  %x = phi i32 [ 2, %invoke.cont ], [ 1, %entry ]
418  %0 = cleanuppad within none []
419  cleanupret from %0 unwind label %catch.dispatch
420
421catch.dispatch:                                   ; preds = %ehcleanup, %catch.cont
422  %cs1 = catchswitch within none [label %catch] unwind to caller
423
424catch:                                            ; preds = %catch.dispatch
425  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
426  call void @use_x(i32 %x)
427  catchret from %1 to label %catch.cont
428
429catch.cont:                                       ; preds = %catch
430  invoke void @g()
431  to label %return unwind label %catch.dispatch
432
433return:                                           ; preds = %invoke.cont, %catch.cont
434  ret void
435}
436define i32 @f9() personality ptr @__CxxFrameHandler3 {
437; CHECK-LABEL: @f9(
438; CHECK-NEXT:  entry:
439; CHECK-NEXT:    [[S:%.*]] = alloca i8, align 1
440; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 1, ptr nonnull [[S]])
441; CHECK-NEXT:    invoke void @"\01??1S2@@QEAA@XZ"(ptr [[S]])
442; CHECK-NEXT:            to label [[TRY_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
443; CHECK:       catch.dispatch:
444; CHECK-NEXT:    [[CATCH_SWITCH:%.*]] = catchswitch within none [label %catch] unwind to caller
445; CHECK:       catch:
446; CHECK-NEXT:    [[CATCH_PAD:%.*]] = catchpad within [[CATCH_SWITCH]] [ptr null, i32 0, ptr null]
447; CHECK-NEXT:    catchret from [[CATCH_PAD]] to label [[TRY_CONT]]
448; CHECK:       try.cont:
449; CHECK-NEXT:    ret i32 0
450;
451entry:
452  %s = alloca i8, align 1
453  call void @llvm.lifetime.start.p0(i64 1, ptr nonnull %s)
454  invoke void @"\01??1S2@@QEAA@XZ"(ptr %s)
455  to label %try.cont unwind label %ehcleanup
456
457ehcleanup:
458  %cleanup.pad = cleanuppad within none []
459  call void @llvm.lifetime.end.p0(i64 1, ptr nonnull %s)
460  cleanupret from %cleanup.pad unwind label %catch.dispatch
461
462catch.dispatch:
463  %catch.switch = catchswitch within none [label %catch] unwind to caller
464
465catch:
466  %catch.pad = catchpad within %catch.switch [ptr null, i32 0, ptr null]
467  catchret from %catch.pad to label %try.cont
468
469try.cont:
470  ret i32 0
471}
472
473define void @f10(i32 %V) personality ptr @__CxxFrameHandler3 {
474; CHECK-LABEL: @f10(
475; CHECK-NEXT:  entry:
476; CHECK-NEXT:    call void @g()
477; CHECK-NEXT:    unreachable
478;
479entry:
480  invoke void @g()
481  to label %unreachable unwind label %cleanup
482
483unreachable:
484  unreachable
485
486cleanup:
487  %cp = cleanuppad within none []
488  switch i32 %V, label %cleanupret1 [
489  i32 0, label %cleanupret2
490  ]
491
492cleanupret1:
493  cleanupret from %cp unwind to caller
494
495cleanupret2:
496  cleanupret from %cp unwind to caller
497}
498
499;   This case tests the handling of an empty cleanup pad that
500;   contains a lifetime_end intrinsic and does not dominate its
501;   successor.
502define void @f11() personality ptr @__CxxFrameHandler3 {
503; CHECK-LABEL: @f11(
504; CHECK-NEXT:  entry:
505; CHECK-NEXT:    invoke void @g()
506; CHECK-NEXT:            to label [[INVOKE_CONT:%.*]] unwind label [[CATCH_DISPATCH:%.*]]
507; CHECK:       invoke.cont:
508; CHECK-NEXT:    invoke void @g()
509; CHECK-NEXT:            to label [[INVOKE_CONT2:%.*]] unwind label [[CATCH_DISPATCH]]
510; CHECK:       invoke.cont2:
511; CHECK-NEXT:    invoke void @g()
512; CHECK-NEXT:            to label [[RETURN:%.*]] unwind label [[CATCH_DISPATCH]]
513; CHECK:       catch.dispatch:
514; CHECK-NEXT:    [[CS1:%.*]] = catchswitch within none [label %catch] unwind to caller
515; CHECK:       catch:
516; CHECK-NEXT:    [[TMP0:%.*]] = catchpad within [[CS1]] [ptr null, i32 64, ptr null]
517; CHECK-NEXT:    catchret from [[TMP0]] to label [[RETURN]]
518; CHECK:       return:
519; CHECK-NEXT:    ret void
520;
521entry:
522  invoke void @g()
523  to label %invoke.cont unwind label %ehcleanup
524
525invoke.cont:                                      ; preds = %entry
526  invoke void @g()
527  to label %invoke.cont2 unwind label %ehcleanup
528
529invoke.cont2:                                     ; preds = %invoke.cont
530  invoke void @g()
531  to label %return unwind label %catch.dispatch
532
533ehcleanup:                                        ; preds = %invoke.cont, %entry
534  %x = phi ptr [ undef, %invoke.cont ], [ undef, %entry ]
535  %0 = cleanuppad within none []
536  call void @llvm.lifetime.end.p0(i64 16, ptr nonnull %x)
537  cleanupret from %0 unwind label %catch.dispatch
538
539catch.dispatch:                                   ; preds = %ehcleanup, %invoke.cont
540  %cs1 = catchswitch within none [label %catch] unwind to caller
541
542catch:                                            ; preds = %catch.dispatch
543  %1 = catchpad within %cs1 [ptr null, i32 u0x40, ptr null]
544  catchret from %1 to label %return
545
546return:                                           ; preds = %invoke.cont, %catch.cont
547  ret void
548}
549
550%struct.S = type { i8 }
551%struct.S2 = type { i8 }
552declare void @"\01??1S2@@QEAA@XZ"(ptr)
553declare void @g()
554declare void @use_x(i32 %x)
555
556declare i32 @__CxxFrameHandler3(...)
557
558declare void @llvm.lifetime.start.p0(i64, ptr nocapture)
559declare void @llvm.lifetime.end.p0(i64, ptr nocapture)
560;.
561; CHECK: attributes #[[ATTR0:[0-9]+]] = { nocallback nofree nosync nounwind willreturn memory(argmem: readwrite) }
562;.
563