1 ; RUN: opt -inline -S %s | FileCheck %s
2 ; RUN: opt -passes='cgscc(inline)' -S %s | FileCheck %s
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 {
13 invoke void @test1_inlinee()
14 to label %exit unwind label %cleanup
16 %pad = cleanuppad within none []
17 call void @g() [ "funclet"(token %pad) ]
18 cleanupret from %pad unwind to caller
23 define void @test1_inlinee() alwaysinline personality void ()* @g {
26 to label %exit unwind label %cleanup.inner
27 ; CHECK-NEXT: invoke void @g()
28 ; CHECK-NEXT: unwind label %[[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:.+]]
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{{$}}
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 {
65 invoke void @test2_inlinee()
66 to label %exit unwind label %cleanup
68 %pad = cleanuppad within none []
69 call void @g() [ "funclet"(token %pad) ]
70 cleanupret from %pad unwind to caller
75 define void @test2_inlinee() alwaysinline personality void ()* @g {
78 to label %exit unwind label %cleanup1
79 ; CHECK-NEXT: invoke void @g()
80 ; CHECK-NEXT: unwind label %[[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:.+]]
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
99 %inner = catchpad within %cs []
100 call void @g() [ "funclet"(token %inner) ]
101 catchret from %inner to label %ret1
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]]) ]
108 cleanupret from %outer unwind label %cleanup2
109 ; CHECK: cleanupret from %[[outer]] unwind label %[[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{{$}}
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 {
130 invoke void @test3_inlinee()
131 to label %exit unwind label %cleanup
133 %pad = cleanuppad within none []
134 call void @g() [ "funclet"(token %pad) ]
135 cleanupret from %pad unwind to caller
140 define void @test3_inlinee() alwaysinline personality void ()* @g {
143 to label %exit unwind label %cleanup
144 ; CHECK-NEXT: invoke void @g()
145 ; CHECK-NEXT: unwind label %[[cleanup:.+]]
148 %pad = cleanuppad within none []
149 call void @g() [ "funclet"(token %pad) ]
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{{$}}
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 {
170 invoke void @test4_inlinee()
171 to label %exit unwind label %cleanup
173 %pad = cleanuppad within none []
174 call void @g() [ "funclet"(token %pad) ]
175 cleanupret from %pad unwind to caller
180 define void @test4_inlinee() alwaysinline personality void ()* @g {
183 to label %exit unwind label %cleanup
184 ; CHECK-NEXT: invoke void @g()
185 ; CHECK-NEXT: unwind label %[[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:.+]]
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{{$}}
204 catchpad within %cs []
205 br label %unreachable
213 ;;; Test with multiple levels of nesting, and unwind dests
214 ;;; that need to be inferred from ancestors, descendants,
216 ;;; CHECK-LABEL: define void @test5(
217 define void @test5() personality void ()* @g {
220 invoke void @test5_inlinee()
221 to label %exit unwind label %cleanup
223 %pad = cleanuppad within none []
224 call void @g() [ "funclet"(token %pad) ]
225 cleanupret from %pad unwind to caller
230 define void @test5_inlinee() alwaysinline personality 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:.+]]
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{{$}}
250 ; CHECK-NEXT: invoke void @g() [ "funclet"(token %[[noinfo_root_pad]]) ]
251 ; CHECK-NEXT: to label %[[noinfo_root_cont:[^ ]+]] unwind label %[[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:.+]]
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{{$}}
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:.+]]
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{{$}}
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:.+]]
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{{$}}
308 to label %exit unwind label %implicit.root
310 ; CHECK-NEXT: invoke void @g()
311 ; CHECK-NEXT: unwind label %[[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:.+]]
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:.+]]
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]]) ]
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:.+]]
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]]) ]
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{{$}}
405 ; CHECK-NEXT: cleanupret from %[[internal_pad]] unwind label %cleanup{{$}}
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 {
422 invoke void @test6_inlinee()
423 to label %exit unwind label %cleanup
425 %pad = cleanuppad within none []
426 call void @g() [ "funclet"(token %pad) ]
427 cleanupret from %pad unwind to caller
432 define void @test6_inlinee() alwaysinline personality void ()* @ProcessCLRException {
435 to label %exit unwind label %root
436 ; CHECK-NEXT: invoke void @g()
437 ; CHECK-NEXT: unwind label %[[root:.+]]
439 %root.pad = cleanuppad within none []
440 invoke void @g() [ "funclet"(token %root.pad) ]
441 to label %root.cont unwind label %left
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:.+]]
448 %left.cs = catchswitch within %root.pad [label %left.catch] unwind to caller
450 ; CHECK-NEXT: %[[left_cs:.+]] = catchswitch within %[[root_pad]] [label %[[left_catch:.+]]] unwind label %cleanup
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:.+]]
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:.+]]
473 %lr.pad = cleanuppad within %left.cp []
475 ; CHECK: [[left_right]]:
476 ; CHECK-NEXT: %[[lr_pad:.+]] = cleanuppad within %[[left_cp]] []
477 ; CHECK-NEXT: unreachable
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:.+]]
491 %right.pad = cleanuppad within %root.pad []
492 invoke void @g() [ "funclet"(token %right.pad) ]
493 to label %unreach unwind label %right.child
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:.+]]
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:.+]]
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
514 %fr.cp = catchpad within %fr.cs []
516 ; CHECK: [[fr_catch]]:
517 ; CHECK-NEXT: %[[fr_cp:.+]] = catchpad within %[[fr_cs]] []
518 ; CHECK-NEXT: unreachable
522 ; CHECK: [[unreach]]:
523 ; CHECK-NEXT: unreachable
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 {
537 invoke void @test7_inlinee()
538 to label %exit unwind label %cleanup
540 %pad = cleanuppad within none []
541 call void @g() [ "funclet"(token %pad) ]
542 cleanupret from %pad unwind to caller
547 define void @test7_inlinee() alwaysinline personality void ()* @ProcessCLRException {
550 to label %exit unwind label %root
551 ; CHECK-NEXT: invoke void @g()
552 ; CHECK-NEXT: unwind label %[[root:.+]]
555 %root.cp = cleanuppad within none []
556 invoke void @g() [ "funclet"(token %root.cp) ]
557 to label %root.cont unwind label %child
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:.+]]
564 cleanupret from %root.cp unwind to caller
565 ; CHECK: [[root_cont]]:
566 ; CHECK-NEXT: cleanupret from %[[root_cp]] unwind label %cleanup
569 %child.cp = cleanuppad within %root.cp []
570 invoke void @g() [ "funclet"(token %child.cp) ]
571 to label %child.cont unwind label %left
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:.+]]
578 %left.cp = cleanuppad within %child.cp []
579 invoke void @g() [ "funclet"(token %left.cp) ]
580 to label %left.cont unwind label %left.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:.+]]
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:.+]]
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]]
601 %lr.cp = cleanuppad within %left.cp []
603 ; CHECK: [[left_right]]:
604 ; CHECK-NEXT: %[[lr_cp:.+]] = cleanuppad within %[[left_cp]] []
605 ; CHECK-NEXT: unreachable
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:.+]]
615 %right.cp = cleanuppad within %child.cp []
616 call void @g() [ "funclet"(token %right.cp) ]
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
627 ; CHECK: [[unreach]]:
628 ; CHECK-NEXT: unreachable
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 {
642 to label %exit unwind label %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
649 cleanupret from %callsite_parent.pad unwind label %cleanup
651 %pad = cleanuppad within none []
652 call void @g() [ "funclet"(token %pad) ]
653 cleanupret from %pad unwind to caller
658 define void @test8_inlinee() alwaysinline personality void ()* @ProcessCLRException {
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:.+]]
666 %inlinee.pad = cleanuppad within none []
667 call void @g() [ "funclet"(token %inlinee.pad) ]
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{{$}}