[ARM] Better OR's for MVE compares
[llvm-core.git] / test / Transforms / Inline / inline-funclets.ll
blob409310380f216fd0aa0dacc5d66fa3295f2d7f70
1 ; RUN: opt -inline -S %s | FileCheck %s
2 ; RUN: opt -passes='cgscc(inline)' -S %s | FileCheck %s
4 declare void @g()
7 ;;; Test with a call in a funclet that needs to remain a call
8 ;;; when inlined because the funclet doesn't unwind to caller.
9 ;;; CHECK-LABEL: define void @test1(
10 define void @test1() personality void ()* @g {
11 entry:
12 ; CHECK-NEXT: entry:
13   invoke void @test1_inlinee()
14     to label %exit unwind label %cleanup
15 cleanup:
16   %pad = cleanuppad within none []
17   call void @g() [ "funclet"(token %pad) ]
18   cleanupret from %pad unwind to caller
19 exit:
20   ret void
23 define void @test1_inlinee() alwaysinline personality void ()* @g {
24 entry:
25   invoke void @g()
26     to label %exit unwind label %cleanup.inner
27 ; CHECK-NEXT:  invoke void @g()
28 ; CHECK-NEXT:    unwind label %[[cleanup_inner:.+]]
30 cleanup.inner:
31   %pad.inner = cleanuppad within none []
32   call void @g() [ "funclet"(token %pad.inner) ]
33   cleanupret from %pad.inner unwind label %cleanup.outer
34 ; CHECK: [[cleanup_inner]]:
35 ; The call here needs to remain a call becuase pad.inner has a cleanupret
36 ; that stays within the inlinee.
37 ; CHECK-NEXT:  %[[pad_inner:[^ ]+]] = cleanuppad within none
38 ; CHECK-NEXT:  call void @g() [ "funclet"(token %[[pad_inner]]) ]
39 ; CHECK-NEXT:  cleanupret from %[[pad_inner]] unwind label %[[cleanup_outer:.+]]
41 cleanup.outer:
42   %pad.outer = cleanuppad within none []
43   call void @g() [ "funclet"(token %pad.outer) ]
44   cleanupret from %pad.outer unwind to caller
45 ; CHECK: [[cleanup_outer]]:
46 ; The call and cleanupret here need to be redirected to caller cleanup
47 ; CHECK-NEXT: %[[pad_outer:[^ ]+]] = cleanuppad within none
48 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[pad_outer]]) ]
49 ; CHECK-NEXT:   unwind label %cleanup
50 ; CHECK: cleanupret from %[[pad_outer]] unwind label %cleanup{{$}}
52 exit:
53   ret void
58 ;;; Test with an "unwind to caller" catchswitch in a parent funclet
59 ;;; that needs to remain "unwind to caller" because the parent
60 ;;; doesn't unwind to caller.
61 ;;; CHECK-LABEL: define void @test2(
62 define void @test2() personality void ()* @g {
63 entry:
64 ; CHECK-NEXT: entry:
65   invoke void @test2_inlinee()
66     to label %exit unwind label %cleanup
67 cleanup:
68   %pad = cleanuppad within none []
69   call void @g() [ "funclet"(token %pad) ]
70   cleanupret from %pad unwind to caller
71 exit:
72   ret void
75 define void @test2_inlinee() alwaysinline personality void ()* @g {
76 entry:
77   invoke void @g()
78     to label %exit unwind label %cleanup1
79 ; CHECK-NEXT:   invoke void @g()
80 ; CHECK-NEXT:     unwind label %[[cleanup1:.+]]
82 cleanup1:
83   %outer = cleanuppad within none []
84   invoke void @g() [ "funclet"(token %outer) ]
85     to label %ret1 unwind label %catchswitch
86 ; CHECK: [[cleanup1]]:
87 ; CHECK-NEXT: %[[outer:[^ ]+]] = cleanuppad within none
88 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[outer]]) ]
89 ; CHECK-NEXT:   unwind label %[[catchswitch:.+]]
91 catchswitch:
92   %cs = catchswitch within %outer [label %catch] unwind to caller
93 ; CHECK: [[catchswitch]]:
94 ; The catchswitch here needs to remain "unwind to caller" since %outer
95 ; has a cleanupret that remains within the inlinee.
96 ; CHECK-NEXT: %[[cs:[^ ]+]] = catchswitch within %[[outer]] [label %[[catch:.+]]] unwind to caller
98 catch:
99   %inner = catchpad within %cs []
100   call void @g() [ "funclet"(token %inner) ]
101   catchret from %inner to label %ret1
102 ; CHECK: [[catch]]:
103 ; The call here needs to remain a call since it too is within %outer
104 ; CHECK:   %[[inner:[^ ]+]] = catchpad within %[[cs]]
105 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[inner]]) ]
107 ret1:
108   cleanupret from %outer unwind label %cleanup2
109 ; CHECK: cleanupret from %[[outer]] unwind label %[[cleanup2:.+]]
111 cleanup2:
112   %later = cleanuppad within none []
113   cleanupret from %later unwind to caller
114 ; CHECK: [[cleanup2]]:
115 ; The cleanupret here needs to get redirected to the caller cleanup
116 ; CHECK-NEXT: %[[later:[^ ]+]] = cleanuppad within none
117 ; CHECK-NEXT: cleanupret from %[[later]] unwind label %cleanup{{$}}
119 exit:
120   ret void
124 ;;; Test with a call in a cleanup that has no definitive unwind
125 ;;; destination, that must be rewritten to an invoke.
126 ;;; CHECK-LABEL: define void @test3(
127 define void @test3() personality void ()* @g {
128 entry:
129 ; CHECK-NEXT: entry:
130   invoke void @test3_inlinee()
131     to label %exit unwind label %cleanup
132 cleanup:
133   %pad = cleanuppad within none []
134   call void @g() [ "funclet"(token %pad) ]
135   cleanupret from %pad unwind to caller
136 exit:
137   ret void
140 define void @test3_inlinee() alwaysinline personality void ()* @g {
141 entry:
142   invoke void @g()
143     to label %exit unwind label %cleanup
144 ; CHECK-NEXT:  invoke void @g()
145 ; CHECK-NEXT:    unwind label %[[cleanup:.+]]
147 cleanup:
148   %pad = cleanuppad within none []
149   call void @g() [ "funclet"(token %pad) ]
150   unreachable
151 ; CHECK: [[cleanup]]:
152 ; The call must be rewritten to an invoke targeting the caller cleanup
153 ; because it may well unwind to there.
154 ; CHECK-NEXT: %[[pad:[^ ]+]] = cleanuppad within none
155 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[pad]]) ]
156 ; CHECK-NEXT:   unwind label %cleanup{{$}}
158 exit:
159   ret void
163 ;;; Test with a catchswitch in a cleanup that has no definitive
164 ;;; unwind destination, that must be rewritten to unwind to the
165 ;;; inlined invoke's unwind dest
166 ;;; CHECK-LABEL: define void @test4(
167 define void @test4() personality void ()* @g {
168 entry:
169 ; CHECK-NEXT: entry:
170   invoke void @test4_inlinee()
171     to label %exit unwind label %cleanup
172 cleanup:
173   %pad = cleanuppad within none []
174   call void @g() [ "funclet"(token %pad) ]
175   cleanupret from %pad unwind to caller
176 exit:
177   ret void
180 define void @test4_inlinee() alwaysinline personality void ()* @g {
181 entry:
182   invoke void @g()
183     to label %exit unwind label %cleanup
184 ; CHECK-NEXT: invoke void @g()
185 ; CHECK-NEXT:   unwind label %[[cleanup:.+]]
187 cleanup:
188   %clean = cleanuppad within none []
189   invoke void @g() [ "funclet"(token %clean) ]
190     to label %unreachable unwind label %dispatch
191 ; CHECK: [[cleanup]]:
192 ; CHECK-NEXT: %[[clean:[^ ]+]] = cleanuppad within none
193 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[clean]]) ]
194 ; CHECK-NEXT:   unwind label %[[dispatch:.+]]
196 dispatch:
197   %cs = catchswitch within %clean [label %catch] unwind to caller
198 ; CHECK: [[dispatch]]:
199 ; The catchswitch must be rewritten to unwind to %cleanup in the caller
200 ; because it may well unwind to there.
201 ; CHECK-NEXT: %[[cs:[^ ]+]] = catchswitch within %[[clean]] [label %[[catch:.+]]] unwind label %cleanup{{$}}
203 catch:
204   catchpad within %cs []
205   br label %unreachable
206 unreachable:
207   unreachable
208 exit:
209   ret void
213 ;;; Test with multiple levels of nesting, and unwind dests
214 ;;; that need to be inferred from ancestors, descendants,
215 ;;; and cousins.
216 ;;; CHECK-LABEL: define void @test5(
217 define void @test5() personality void ()* @g {
218 entry:
219 ; CHECK-NEXT: entry:
220   invoke void @test5_inlinee()
221     to label %exit unwind label %cleanup
222 cleanup:
223   %pad = cleanuppad within none []
224   call void @g() [ "funclet"(token %pad) ]
225   cleanupret from %pad unwind to caller
226 exit:
227   ret void
230 define void @test5_inlinee() alwaysinline personality void ()* @g {
231 entry:
232   invoke void @g()
233     to label %cont unwind label %noinfo.root
234 ; CHECK-NEXT: invoke void @g()
235 ; CHECK-NEXT:   to label %[[cont:[^ ]+]] unwind label %[[noinfo_root:.+]]
237 noinfo.root:
238   %noinfo.root.pad = cleanuppad within none []
239   call void @g() [ "funclet"(token %noinfo.root.pad) ]
240   invoke void @g() [ "funclet"(token %noinfo.root.pad) ]
241     to label %noinfo.root.cont unwind label %noinfo.left
242 ; CHECK: [[noinfo_root]]:
243 ; Nothing under "noinfo.root" has a definitive unwind destination, so
244 ; we must assume all of it may actually unwind, and redirect unwinds
245 ; to the cleanup in the caller.
246 ; CHECK-NEXT: %[[noinfo_root_pad:[^ ]+]] = cleanuppad within none []
247 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
248 ; CHECK-NEXT:   to label %[[next:[^ ]+]] unwind label %cleanup{{$}}
249 ; CHECK: [[next]]:
250 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
251 ; CHECK-NEXT:   to label %[[noinfo_root_cont:[^ ]+]] unwind label %[[noinfo_left:.+]]
253 noinfo.left:
254   %noinfo.left.pad = cleanuppad within %noinfo.root.pad []
255   invoke void @g() [ "funclet"(token %noinfo.left.pad) ]
256     to label %unreachable unwind label %noinfo.left.child
257 ; CHECK: [[noinfo_left]]:
258 ; CHECK-NEXT: %[[noinfo_left_pad:[^ ]+]] = cleanuppad within %[[noinfo_root_pad]]
259 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_left_pad]]) ]
260 ; CHECK-NEXT:   unwind label %[[noinfo_left_child:.+]]
262 noinfo.left.child:
263   %noinfo.left.child.cs = catchswitch within %noinfo.left.pad [label %noinfo.left.child.catch] unwind to caller
264 ; CHECK: [[noinfo_left_child]]:
265 ; CHECK-NEXT: %[[noinfo_left_child_cs:[^ ]+]] = catchswitch within %[[noinfo_left_pad]] [label %[[noinfo_left_child_catch:[^ ]+]]] unwind label %cleanup{{$}}
267 noinfo.left.child.catch:
268   %noinfo.left.child.pad = catchpad within %noinfo.left.child.cs []
269   call void @g() [ "funclet"(token %noinfo.left.child.pad) ]
270   br label %unreachable
271 ; CHECK: [[noinfo_left_child_catch]]:
272 ; CHECK-NEXT: %[[noinfo_left_child_pad:[^ ]+]] = catchpad within %[[noinfo_left_child_cs]] []
273 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_left_child_pad]]) ]
274 ; CHECK-NEXT:   unwind label %cleanup{{$}}
276 noinfo.root.cont:
277   invoke void @g() [ "funclet"(token %noinfo.root.pad) ]
278     to label %unreachable unwind label %noinfo.right
279 ; CHECK: [[noinfo_root_cont]]:
280 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
281 ; CHECK-NEXT:   unwind label %[[noinfo_right:.+]]
283 noinfo.right:
284   %noinfo.right.cs = catchswitch within %noinfo.root.pad [label %noinfo.right.catch] unwind to caller
285 ; CHECK: [[noinfo_right]]:
286 ; CHECK-NEXT: %[[noinfo_right_cs:[^ ]+]] = catchswitch within %[[noinfo_root_pad]] [label %[[noinfo_right_catch:[^ ]+]]] unwind label %cleanup{{$}}
288 noinfo.right.catch:
289   %noinfo.right.pad = catchpad within %noinfo.right.cs []
290   invoke void @g() [ "funclet"(token %noinfo.right.pad) ]
291     to label %unreachable unwind label %noinfo.right.child
292 ; CHECK: [[noinfo_right_catch]]:
293 ; CHECK-NEXT: %[[noinfo_right_pad:[^ ]+]] = catchpad within %[[noinfo_right_cs]]
294 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_right_pad]]) ]
295 ; CHECK-NEXT:   unwind label %[[noinfo_right_child:.+]]
297 noinfo.right.child:
298   %noinfo.right.child.pad = cleanuppad within %noinfo.right.pad []
299   call void @g() [ "funclet"(token %noinfo.right.child.pad) ]
300   br label %unreachable
301 ; CHECK: [[noinfo_right_child]]:
302 ; CHECK-NEXT: %[[noinfo_right_child_pad:[^ ]+]] = cleanuppad within %[[noinfo_right_pad]]
303 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_right_child_pad]]) ]
304 ; CHECK-NEXT:   unwind label %cleanup{{$}}
306 cont:
307   invoke void @g()
308     to label %exit unwind label %implicit.root
309 ; CHECK: [[cont]]:
310 ; CHECK-NEXT: invoke void @g()
311 ; CHECK-NEXT:   unwind label %[[implicit_root:.+]]
313 implicit.root:
314   %implicit.root.pad = cleanuppad within none []
315   call void @g() [ "funclet"(token %implicit.root.pad) ]
316   invoke void @g() [ "funclet"(token %implicit.root.pad) ]
317     to label %implicit.root.cont unwind label %implicit.left
318 ; CHECK: [[implicit_root]]:
319 ; There's an unwind edge to %internal in implicit.right, and we need to propagate that
320 ; fact down to implicit.right.grandchild, up to implicit.root, and down to
321 ; implicit.left.child.catch, leaving all calls and "unwind to caller" catchswitches
322 ; alone to so they don't conflict with the unwind edge in implicit.right
323 ; CHECK-NEXT: %[[implicit_root_pad:[^ ]+]] = cleanuppad within none
324 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
325 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
326 ; CHECK-NEXT:   to label %[[implicit_root_cont:[^ ]+]] unwind label %[[implicit_left:.+]]
328 implicit.left:
329   %implicit.left.pad = cleanuppad within %implicit.root.pad []
330   invoke void @g() [ "funclet"(token %implicit.left.pad) ]
331     to label %unreachable unwind label %implicit.left.child
332 ; CHECK: [[implicit_left]]:
333 ; CHECK-NEXT: %[[implicit_left_pad:[^ ]+]] = cleanuppad within %[[implicit_root_pad:[^ ]+]]
334 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_left_pad]]) ]
335 ; CHECK-NEXT:   unwind label %[[implicit_left_child:.+]]
337 implicit.left.child:
338   %implicit.left.child.cs = catchswitch within %implicit.left.pad [label %implicit.left.child.catch] unwind to caller
339 ; CHECK: [[implicit_left_child]]:
340 ; CHECK-NEXT: %[[implicit_left_child_cs:[^ ]+]] = catchswitch within %[[implicit_left_pad]] [label %[[implicit_left_child_catch:[^ ]+]]] unwind to caller
342 implicit.left.child.catch:
343   %implicit.left.child.pad = catchpad within %implicit.left.child.cs []
344   call void @g() [ "funclet"(token %implicit.left.child.pad) ]
345   br label %unreachable
346 ; CHECK: [[implicit_left_child_catch]]:
347 ; CHECK-NEXT: %[[implicit_left_child_pad:[^ ]+]] = catchpad within %[[implicit_left_child_cs]]
348 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_left_child_pad]]) ]
350 implicit.root.cont:
351   invoke void @g() [ "funclet"(token %implicit.root.pad) ]
352     to label %unreachable unwind label %implicit.right
353 ; CHECK: [[implicit_root_cont]]:
354 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_root_pad]]) ]
355 ; CHECK-NEXT:   unwind label %[[implicit_right:.+]]
357 implicit.right:
358   %implicit.right.cs = catchswitch within %implicit.root.pad [label %implicit.right.catch] unwind label %internal
359 ; CHECK: [[implicit_right]]:
360 ; This is the unwind edge (to %internal) whose existence needs to get propagated around the "implicit" tree
361 ; CHECK-NEXT: %[[implicit_right_cs:[^ ]+]] = catchswitch within %[[implicit_root_pad]] [label %[[implicit_right_catch:[^ ]+]]] unwind label %[[internal:.+]]
363 implicit.right.catch:
364   %implicit.right.pad = catchpad within %implicit.right.cs []
365   invoke void @g() [ "funclet"(token %implicit.right.pad) ]
366     to label %unreachable unwind label %implicit.right.child
367 ; CHECK: [[implicit_right_catch]]:
368 ; CHECK-NEXT: %[[implicit_right_pad:[^ ]+]] = catchpad within %[[implicit_right_cs]]
369 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_right_pad]]) ]
370 ; CHECK-NEXT:   unwind label %[[implicit_right_child:.+]]
372 implicit.right.child:
373   %implicit.right.child.pad = cleanuppad within %implicit.right.pad []
374   invoke void @g() [ "funclet"(token %implicit.right.child.pad) ]
375     to label %unreachable unwind label %implicit.right.grandchild
376 ; CHECK: [[implicit_right_child]]:
377 ; CHECK-NEXT: %[[implicit_right_child_pad:[^ ]+]] = cleanuppad within %[[implicit_right_pad]]
378 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[implicit_right_child_pad]]) ]
379 ; CHECK-NEXT:   unwind label %[[implicit_right_grandchild:.+]]
381 implicit.right.grandchild:
382   %implicit.right.grandchild.cs = catchswitch within %implicit.right.child.pad [label %implicit.right.grandchild.catch] unwind to caller
383 ; CHECK: [[implicit_right_grandchild]]:
384 ; CHECK-NEXT: %[[implicit_right_grandchild_cs:[^ ]+]] = catchswitch within %[[implicit_right_child_pad]] [label %[[implicit_right_grandchild_catch:[^ ]+]]] unwind to caller
386 implicit.right.grandchild.catch:
387   %implicit.right.grandhcild.pad = catchpad within %implicit.right.grandchild.cs []
388   call void @g() [ "funclet"(token %implicit.right.grandhcild.pad) ]
389   br label %unreachable
390 ; CHECK: [[implicit_right_grandchild_catch]]:
391 ; CHECK-NEXT: %[[implicit_right_grandhcild_pad:[^ ]+]] = catchpad within %[[implicit_right_grandchild_cs]]
392 ; CHECK-NEXT: call void @g() [ "funclet"(token %[[implicit_right_grandhcild_pad]]) ]
394 internal:
395   %internal.pad = cleanuppad within none []
396   call void @g() [ "funclet"(token %internal.pad) ]
397   cleanupret from %internal.pad unwind to caller
398 ; CHECK: [[internal]]:
399 ; internal is a cleanup with a "return to caller" cleanuppad; that needs to get redirected
400 ; to %cleanup in the caller, and the call needs to get similarly rewritten to an invoke.
401 ; CHECK-NEXT: %[[internal_pad:[^ ]+]] = cleanuppad within none
402 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %internal.pad.i) ]
403 ; CHECK-NEXT:   to label %[[next:[^ ]+]] unwind label %cleanup{{$}}
404 ; CHECK: [[next]]:
405 ; CHECK-NEXT: cleanupret from %[[internal_pad]] unwind label %cleanup{{$}}
407 unreachable:
408   unreachable
409 exit:
410   ret void
413 ;;; Test with funclets that don't have information for themselves, but have
414 ;;; descendants which unwind to other descendants (left.left unwinds to
415 ;;; left.right, and right unwinds to far_right).  Make sure that these local
416 ;;; unwinds don't trip up processing of the ancestor nodes (left and root) that
417 ;;; ultimately have no information.
418 ;;; CHECK-LABEL: define void @test6(
419 define void @test6() personality void()* @ProcessCLRException {
420 entry:
421 ; CHECK-NEXT: entry:
422   invoke void @test6_inlinee()
423     to label %exit unwind label %cleanup
424 cleanup:
425   %pad = cleanuppad within none []
426   call void @g() [ "funclet"(token %pad) ]
427   cleanupret from %pad unwind to caller
428 exit:
429   ret void
432 define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
433 entry:
434   invoke void @g()
435     to label %exit unwind label %root
436     ; CHECK-NEXT:  invoke void @g()
437     ; CHECK-NEXT:    unwind label %[[root:.+]]
438 root:
439   %root.pad = cleanuppad within none []
440   invoke void @g() [ "funclet"(token %root.pad) ]
441     to label %root.cont unwind label %left
442 ; CHECK: [[root]]:
443 ; CHECK-NEXT: %[[root_pad:.+]] = cleanuppad within none []
444 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
445 ; CHECK-NEXT:   to label %[[root_cont:.+]] unwind label %[[left:.+]]
447 left:
448   %left.cs = catchswitch within %root.pad [label %left.catch] unwind to caller
449 ; CHECK: [[left]]:
450 ; CHECK-NEXT: %[[left_cs:.+]] = catchswitch within %[[root_pad]] [label %[[left_catch:.+]]] unwind label %cleanup
452 left.catch:
453   %left.cp = catchpad within %left.cs []
454   call void @g() [ "funclet"(token %left.cp) ]
455   invoke void @g() [ "funclet"(token %left.cp) ]
456     to label %unreach unwind label %left.left
457 ; CHECK: [[left_catch:.+]]:
458 ; CHECK-NEXT: %[[left_cp:.+]] = catchpad within %[[left_cs]] []
459 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
460 ; CHECK-NEXT:   to label %[[lc_cont:.+]] unwind label %cleanup
461 ; CHECK: [[lc_cont]]:
462 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
463 ; CHECK-NEXT:   to label %[[unreach:.+]] unwind label %[[left_left:.+]]
465 left.left:
466   %ll.pad = cleanuppad within %left.cp []
467   cleanupret from %ll.pad unwind label %left.right
468 ; CHECK: [[left_left]]:
469 ; CHECK-NEXT: %[[ll_pad:.+]] = cleanuppad within %[[left_cp]] []
470 ; CHECK-NEXT: cleanupret from %[[ll_pad]] unwind label %[[left_right:.+]]
472 left.right:
473   %lr.pad = cleanuppad within %left.cp []
474   unreachable
475 ; CHECK: [[left_right]]:
476 ; CHECK-NEXT: %[[lr_pad:.+]] = cleanuppad within %[[left_cp]] []
477 ; CHECK-NEXT: unreachable
479 root.cont:
480   call void @g() [ "funclet"(token %root.pad) ]
481   invoke void @g() [ "funclet"(token %root.pad) ]
482     to label %unreach unwind label %right
483 ; CHECK: [[root_cont]]:
484 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
485 ; CHECK-NEXT:   to label %[[root_cont_cont:.+]] unwind label %cleanup
486 ; CHECK: [[root_cont_cont]]:
487 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_pad]]) ]
488 ; CHECK-NEXT:   to label %[[unreach]] unwind label %[[right:.+]]
490 right:
491   %right.pad = cleanuppad within %root.pad []
492   invoke void @g() [ "funclet"(token %right.pad) ]
493     to label %unreach unwind label %right.child
494 ; CHECK: [[right]]:
495 ; CHECK-NEXT: %[[right_pad:.+]] = cleanuppad within %[[root_pad]] []
496 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[right_pad]]) ]
497 ; CHECK-NEXT:   to label %[[unreach]] unwind label %[[right_child:.+]]
499 right.child:
500   %rc.pad = cleanuppad within %right.pad []
501   invoke void @g() [ "funclet"(token %rc.pad) ]
502     to label %unreach unwind label %far_right
503 ; CHECK: [[right_child]]:
504 ; CHECK-NEXT: %[[rc_pad:.+]] = cleanuppad within %[[right_pad]] []
505 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[rc_pad]]) ]
506 ; CHECK-NEXT:   to label %[[unreach]] unwind label %[[far_right:.+]]
508 far_right:
509   %fr.cs = catchswitch within %root.pad [label %fr.catch] unwind to caller
510 ; CHECK: [[far_right]]:
511 ; CHECK-NEXT: %[[fr_cs:.+]] = catchswitch within %[[root_pad]] [label %[[fr_catch:.+]]] unwind label %cleanup
513 fr.catch:
514   %fr.cp = catchpad within %fr.cs []
515   unreachable
516 ; CHECK: [[fr_catch]]:
517 ; CHECK-NEXT: %[[fr_cp:.+]] = catchpad within %[[fr_cs]] []
518 ; CHECK-NEXT: unreachable
520 unreach:
521   unreachable
522 ; CHECK: [[unreach]]:
523 ; CHECK-NEXT: unreachable
525 exit:
526   ret void
530 ;;; Test with a no-info funclet (right) which has a cousin (left.left) that
531 ;;; unwinds to another cousin (left.right); make sure we don't trip over this
532 ;;; when propagating unwind destination info to "right".
533 ;;; CHECK-LABEL: define void @test7(
534 define void @test7() personality void()* @ProcessCLRException {
535 entry:
536 ; CHECK-NEXT: entry:
537   invoke void @test7_inlinee()
538     to label %exit unwind label %cleanup
539 cleanup:
540   %pad = cleanuppad within none []
541   call void @g() [ "funclet"(token %pad) ]
542   cleanupret from %pad unwind to caller
543 exit:
544   ret void
547 define void @test7_inlinee() alwaysinline personality void ()* @ProcessCLRException {
548 entry:
549   invoke void @g()
550     to label %exit unwind label %root
551 ; CHECK-NEXT:  invoke void @g()
552 ; CHECK-NEXT:    unwind label %[[root:.+]]
554 root:
555   %root.cp = cleanuppad within none []
556   invoke void @g() [ "funclet"(token %root.cp) ]
557     to label %root.cont unwind label %child
558 ; CHECK: [[root]]:
559 ; CHECK-NEXT: %[[root_cp:.+]] = cleanuppad within none []
560 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[root_cp]]) ]
561 ; CHECK-NEXT:   to label %[[root_cont:.+]] unwind label %[[child:.+]]
563 root.cont:
564   cleanupret from %root.cp unwind to caller
565 ; CHECK: [[root_cont]]:
566 ; CHECK-NEXT: cleanupret from %[[root_cp]] unwind label %cleanup
568 child:
569   %child.cp = cleanuppad within %root.cp []
570   invoke void @g() [ "funclet"(token %child.cp) ]
571     to label %child.cont unwind label %left
572 ; CHECK: [[child]]:
573 ; CHECK-NEXT: %[[child_cp:.+]] = cleanuppad within %[[root_cp]] []
574 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[child_cp]]) ]
575 ; CHECK-NEXT:   to label %[[child_cont:.+]] unwind label %[[left:.+]]
577 left:
578   %left.cp = cleanuppad within %child.cp []
579   invoke void @g() [ "funclet"(token %left.cp) ]
580     to label %left.cont unwind label %left.left
581 ; CHECK: [[left]]:
582 ; CHECK-NEXT: %[[left_cp:.+]] = cleanuppad within %[[child_cp]] []
583 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
584 ; CHECK-NEXT:   to label %[[left_cont:.+]] unwind label %[[left_left:.+]]
586 left.left:
587   %ll.cp = cleanuppad within %left.cp []
588   cleanupret from %ll.cp unwind label %left.right
589 ; CHECK: [[left_left]]:
590 ; CHECK-NEXT: %[[ll_cp:.+]] = cleanuppad within %[[left_cp]] []
591 ; CHECK-NEXT: cleanupret from %[[ll_cp]] unwind label %[[left_right:.+]]
593 left.cont:
594   invoke void @g() [ "funclet"(token %left.cp) ]
595     to label %unreach unwind label %left.right
596 ; CHECK: [[left_cont]]:
597 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[left_cp]]) ]
598 ; CHECK-NEXT:   to label %[[unreach:.+]] unwind label %[[left_right]]
600 left.right:
601   %lr.cp = cleanuppad within %left.cp []
602   unreachable
603 ; CHECK: [[left_right]]:
604 ; CHECK-NEXT: %[[lr_cp:.+]] = cleanuppad within %[[left_cp]] []
605 ; CHECK-NEXT: unreachable
607 child.cont:
608   invoke void @g() [ "funclet"(token %child.cp) ]
609     to label %unreach unwind label %right
610 ; CHECK: [[child_cont]]:
611 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[child_cp]]) ]
612 ; CHECK-NEXT:   to label %[[unreach]] unwind label %[[right:.+]]
614 right:
615   %right.cp = cleanuppad within %child.cp []
616   call void @g() [ "funclet"(token %right.cp) ]
617   unreachable
618 ; CHECK: [[right]]:
619 ; CHECK-NEXT: %[[right_cp:.+]] = cleanuppad within %[[child_cp]]
620 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[right_cp]]) ]
621 ; CHECK-NEXT:   to label %[[right_cont:.+]] unwind label %cleanup
622 ; CHECK: [[right_cont]]:
623 ; CHECK-NEXT: unreachable
625 unreach:
626   unreachable
627 ; CHECK: [[unreach]]:
628 ; CHECK-NEXT: unreachable
630 exit:
631   ret void
634 declare void @ProcessCLRException()
636 ; Make sure the logic doesn't get tripped up when the inlined invoke is
637 ; itself within a funclet in the caller.
638 ; CHECK-LABEL: define void @test8(
639 define void @test8() personality void ()* @ProcessCLRException {
640 entry:
641   invoke void @g()
642     to label %exit unwind label %callsite_parent
643 callsite_parent:
644   %callsite_parent.pad = cleanuppad within none []
645 ; CHECK: %callsite_parent.pad = cleanuppad within none
646   invoke void @test8_inlinee() [ "funclet"(token %callsite_parent.pad) ]
647     to label %ret unwind label %cleanup
648 ret:
649   cleanupret from %callsite_parent.pad unwind label %cleanup
650 cleanup:
651   %pad = cleanuppad within none []
652   call void @g() [ "funclet"(token %pad) ]
653   cleanupret from %pad unwind to caller
654 exit:
655   ret void
658 define void @test8_inlinee() alwaysinline personality void ()* @ProcessCLRException {
659 entry:
660   invoke void @g()
661     to label %exit unwind label %inlinee_cleanup
662 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %callsite_parent.pad) ]
663 ; CHECK-NEXT:   unwind label %[[inlinee_cleanup:.+]]
665 inlinee_cleanup:
666   %inlinee.pad = cleanuppad within none []
667   call void @g() [ "funclet"(token %inlinee.pad) ]
668   unreachable
669 ; CHECK: [[inlinee_cleanup]]:
670 ; CHECK-NEXT: %[[inlinee_pad:[^ ]+]] = cleanuppad within %callsite_parent.pad
671 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[inlinee_pad]]) ]
672 ; CHECK-NEXT:   unwind label %cleanup{{$}}
674 exit:
675   ret void