1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-21 20:12:56 +02:00
llvm-mirror/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll
Reid Kleckner ba8ad3f697 [WinEH] Disable most forms of demotion
Now that the register allocator knows about the barriers on funclet
entry and exit, testing has shown that this is unnecessary.

We still demote PHIs on unsplittable blocks due to the differences
between the IR CFG and the Machine CFG.

llvm-svn: 253619
2015-11-19 23:23:33 +00:00

1549 lines
51 KiB
LLVM

; RUN: opt -mtriple=x86_x64-pc-windows-msvc -S -winehprepare < %s | FileCheck %s
declare i32 @__CxxFrameHandler3(...)
declare void @f()
declare i32 @g()
declare void @h(i32)
declare i1 @b()
define void @test1() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
%i = cleanuppad []
call void @h(i32 %x)
cleanupret %i unwind label %right.end
exit:
ret void
}
; %inner is a cleanup which appears both as a child of
; %left and as a child of %right. Since statically we
; need each funclet to have a single parent, we need to
; clone the entire %inner funclet so we can have one
; copy under each parent. The cleanupret in %inner
; unwinds to the catchendpad for %right, so the copy
; of %inner under %right should include it; the copy
; of %inner under %left should instead have an
; `unreachable` inserted there, but the copy under
; %left still needs to be created because it's possible
; the dynamic path enters %left, then enters %inner,
; then calls @h, and that the call to @h doesn't return.
; CHECK-LABEL: define void @test1(
; CHECK: left:
; CHECK: cleanuppad
; CHECK: %x.for.left = call i32 @g()
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: to label %right.catch unwind label %right.end
; CHECK: right.catch:
; CHECK: %x = call i32 @g()
; CHECK: to label %shared.cont unwind label %[[INNER_RIGHT:.+]]
; CHECK: right.end:
; CHECK: catchendpad unwind to caller
; CHECK: shared.cont:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_R:\%.+]] = cleanuppad []
; CHECK: call void @h(i32 %x)
; CHECK: cleanupret [[I_R]] unwind label %right.end
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_L:\%.+]] = cleanuppad []
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
define void @test2() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %right.end
exit:
ret void
}
; In this case left and right are both parents of inner. This differs from
; @test1 in that inner is a catchpad rather than a cleanuppad, which makes
; inner.end a block that gets cloned so that left and right each contain a
; copy (catchendpad blocks are considered to be part of the parent funclet
; of the associated catchpad). The catchendpad in %inner.end unwinds to
; %right.end (which belongs to the entry funclet).
; CHECK-LABEL: define void @test2(
; CHECK: left:
; CHECK: cleanuppad
; CHECK: %x.for.left = call i32 @g()
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]]
; CHECK: right.catch:
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind label %[[RIGHT_END]]
define void @test3() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
%l = cleanuppad []
br label %shared
left.end:
cleanupendpad %l unwind label %right
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; In this case, %left and %right are siblings with %entry as the parent of both,
; while %left and %right are both parents of %inner. The catchendpad in
; %inner.end unwinds to %left.end. When %inner is cloned a copy of %inner.end
; will be made for both %left and %right, but because %left.end is a cleanup pad
; and %right is a catch pad the unwind edge from the copy of %inner.end for
; %right must be removed.
; CHECK-LABEL: define void @test3(
; CHECK: left:
; CHECK: %l = cleanuppad []
; CHECK: %x.for.left = call i32 @g()
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[LEFT_END:left.end.*]]:
; CHECK: cleanupendpad %l unwind label %right
; CHECK: right:
; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]]
; CHECK: right.catch:
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
define void @test4() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
catchpad []
to label %left.catch unwind label %left.end
left.catch:
br label %shared
left.end:
catchendpad unwind label %right
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This is a variation of @test3 in which both %left and %right are catch pads.
; In this case, %left and %right are siblings with %entry as the parent of both,
; while %left and %right are both parents of %inner. The catchendpad in
; %inner.end unwinds to %left.end. When %inner is cloned a copy of %inner.end
; will be made for both %left and %right, but because the catchpad in %right
; does not unwind to %left.end the unwind edge from the copy of %inner.end for
; %right must be removed.
; CHECK-LABEL: define void @test4(
; CHECK: left:
; CHECK: catchpad []
; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]]
; CHECK: left.catch:
; CHECK: %x.for.left = call i32 @g()
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind label %right
; CHECK: right:
; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]]
; CHECK: right.catch:
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
define void @test5() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
catchpad []
to label %left.catch unwind label %left.end
left.catch:
br label %shared
left.end:
catchendpad unwind label %right
right:
%r = cleanuppad []
br label %shared
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; Like @test3, %left and %right are siblings with %entry as the parent of both,
; while %left and %right are both parents of %inner. This case makes %left a
; catch and %right a cleanup so that %inner unwinds to %left.end, which is a
; block in %entry. The %inner funclet is cloned for %left and %right, but the
; copy of %inner.end for %right must have its unwind edge removed because the
; catchendpad at %left.end is not compatible with %right.
; CHECK-LABEL: define void @test5(
; CHECK: left:
; CHECK: catchpad []
; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]]
; CHECK: left.catch:
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind label %right
; CHECK: right:
; CHECK: %r = cleanuppad []
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
define void @test6() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
catchpad []
to label %left.catch unwind label %left.end
left.catch:
br label %shared
left.end:
catchendpad unwind label %middle
middle:
%m = catchpad []
to label %middle.catch unwind label %middle.end
middle.catch:
catchret %m to label %exit
middle.end:
catchendpad unwind label %right
right:
%r = cleanuppad []
br label %shared
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This is like @test5 but it inserts another sibling between %left and %right.
; In this case %left, %middle and %right are all siblings, while %left and
; %right are both parents of %inner. This checks the proper handling of the
; catchendpad in %inner.end (which will be cloned so that %left and %right both
; have copies) unwinding to a catchendpad that unwinds to a sibling.
; CHECK-LABEL: define void @test6(
; CHECK: left:
; CHECK: catchpad []
; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]]
; CHECK: left.catch:
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind label %middle
; CHECK: middle:
; CHECK: catchpad []
; CHECK: to label %middle.catch unwind label %middle.end
; CHECK: middle.catch:
; CHECK: catchret %m to label %exit
; CHECK: middle.end:
; CHECK: catchendpad unwind label %right
; CHECK: right:
; CHECK: %r = cleanuppad []
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
define void @test7() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
catchpad []
to label %left.catch unwind label %left.end
left.catch:
br label %shared
left.end:
catchendpad unwind label %right
right:
%r = cleanuppad []
br label %shared
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %inner.sibling
inner.sibling:
%is = cleanuppad []
call void @h(i32 0)
cleanupret %is unwind label %left.end
exit:
ret void
}
; This is like @test5 but instead of unwinding to %left.end, the catchendpad
; in %inner.end unwinds to a sibling cleanup pad. Both %inner (along with its
; associated blocks) and %inner.sibling must be cloned for %left and %right.
; The clones of %inner will be identical, but the copy of %inner.sibling for
; %right must end with an unreachable instruction, because it cannot unwind to
; %left.end.
; CHECK-LABEL: define void @test7(
; CHECK: left:
; CHECK: catchpad []
; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]]
; CHECK: left.catch:
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind label %[[RIGHT:.+]]
; CHECK: [[RIGHT]]:
; CHECK: [[R:\%.+]] = cleanuppad []
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind label %[[INNER_SIBLING_RIGHT:.+]]
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[INNER_SIBLING_LEFT:.+]]
; CHECK: [[INNER_SIBLING_RIGHT]]
; CHECK: [[IS_R:\%.+]] = cleanuppad []
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_SIBLING_LEFT]]
; CHECK: [[IS_L:\%.+]] = cleanuppad []
; CHECK: call void @h(i32 0)
; CHECK: cleanupret [[IS_L]] unwind label %[[LEFT_END]]
define void @test8() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %unreachable unwind label %right
left:
cleanuppad []
invoke void @f() to label %unreachable unwind label %inner
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
invoke void @f() to label %unreachable unwind label %inner
right.end:
catchendpad unwind to caller
inner:
%i = cleanuppad []
%x = call i32 @g()
call void @h(i32 %x)
cleanupret %i unwind label %right.end
unreachable:
unreachable
}
; Another case of a two-parent child (like @test1), this time
; with the join at the entry itself instead of following a
; non-pad join.
; CHECK-LABEL: define void @test8(
; CHECK: invoke.cont:
; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right
; CHECK: left:
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: to label %right.catch unwind label %right.end
; CHECK: right.catch:
; CHECK: to label %unreachable unwind label %[[INNER_RIGHT:.+]]
; CHECK: right.end:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_R:\%.+]] = cleanuppad []
; CHECK: [[X_R:\%.+]] = call i32 @g()
; CHECK: call void @h(i32 [[X_R]])
; CHECK: cleanupret [[I_R]] unwind label %right.end
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_L:\%.+]] = cleanuppad []
; CHECK: [[X_L:\%.+]] = call i32 @g()
; CHECK: call void @h(i32 [[X_L]])
; CHECK: unreachable
; CHECK: unreachable:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_LEFT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_ENTRY]]:
; CHECK: unreachable
define void @test9() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %unreachable unwind label %right
left:
cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
invoke void @f()
to label %unreachable unwind label %inner
inner:
cleanuppad []
invoke void @f()
to label %unreachable unwind label %inner.child
inner.child:
cleanuppad []
%x = call i32 @g()
call void @h(i32 %x)
unreachable
unreachable:
unreachable
}
; %inner is a two-parent child which itself has a child; need
; to make two copies of both the %inner and %inner.child.
; CHECK-LABEL: define void @test9(
; CHECK: invoke.cont:
; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %right
; CHECK: left:
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: to label %right.catch unwind label %right.end
; CHECK: right.catch:
; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: right.end:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_RIGHT]]:
; CHECK: to label %[[UNREACHABLE_INNER_RIGHT:.+]] unwind label %[[INNER_CHILD_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: to label %[[UNREACHABLE_INNER_LEFT:.+]] unwind label %[[INNER_CHILD_LEFT:.+]]
; CHECK: [[INNER_CHILD_RIGHT]]:
; CHECK: [[TMP:\%.+]] = cleanuppad []
; CHECK: [[X:\%.+]] = call i32 @g()
; CHECK: call void @h(i32 [[X]])
; CHECK: unreachable
; CHECK: [[INNER_CHILD_LEFT]]:
; CHECK: [[TMP:\%.+]] = cleanuppad []
; CHECK: [[X:\%.+]] = call i32 @g()
; CHECK: call void @h(i32 [[X]])
; CHECK: unreachable
; CHECK: [[UNREACHABLE_INNER_RIGHT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_INNER_LEFT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_RIGHT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_LEFT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_ENTRY]]:
; CHECK: unreachable
define void @test10() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %unreachable unwind label %right
left:
cleanuppad []
call void @h(i32 1)
invoke void @f()
to label %unreachable unwind label %right
right:
cleanuppad []
call void @h(i32 2)
invoke void @f()
to label %unreachable unwind label %left
unreachable:
unreachable
}
; This is an irreducible loop with two funclets that enter each other;
; need to make two copies of each funclet (one a child of root, the
; other a child of the opposite funclet), but also make sure not to
; clone self-descendants (if we tried to do that we'd need to make an
; infinite number of them). Presumably if optimizations ever generated
; such a thing it would mean that one of the two cleanups was originally
; the parent of the other, but that we'd somehow lost track in the CFG
; of which was which along the way; generating each possibility lets
; whichever case was correct execute correctly.
; CHECK-LABEL: define void @test10(
; CHECK: entry:
; CHECK: to label %invoke.cont unwind label %[[LEFT:.+]]
; CHECK: invoke.cont:
; CHECK: to label %[[UNREACHABLE_ENTRY:.+]] unwind label %[[RIGHT:.+]]
; CHECK: [[LEFT_FROM_RIGHT:.+]]:
; CHECK: call void @h(i32 1)
; CHECK: call void @f()
; CHECK: unreachable
; CHECK: [[LEFT]]:
; CHECK: call void @h(i32 1)
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[RIGHT_FROM_LEFT:.+]]
; CHECK: [[RIGHT]]:
; CHECK: call void @h(i32 2)
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[LEFT_FROM_RIGHT]]
; CHECK: [[RIGHT_FROM_LEFT]]:
; CHECK: call void @h(i32 2)
; CHECK: call void @f()
; CHECK: unreachable
; CHECK: [[UNREACHABLE_RIGHT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_LEFT]]:
; CHECK: unreachable
; CHECK: [[UNREACHABLE_ENTRY]]:
; CHECK: unreachable
define void @test11() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
catchpad []
to label %left.catch unwind label %left.sibling
left.catch:
br label %shared
left.sibling:
%ls = catchpad []
to label %left.sibling.catch unwind label %left.end
left.sibling.catch:
catchret %ls to label %exit
left.end:
catchendpad unwind label %right
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This is a variation of @test4 in which the shared child funclet unwinds to a
; catchend pad that is the unwind destination of %left.sibling rather than %left
; but is still a valid destination for %inner as reach from %left.
; When %inner is cloned a copy of %inner.end will be made for both %left and
; %right, but because the catchpad in %right does not unwind to %left.end the
; unwind edge from the copy of %inner.end for %right must be removed.
; CHECK-LABEL: define void @test11(
; CHECK: left:
; CHECK: catchpad []
; CHECK: to label %left.catch unwind label %left.sibling
; CHECK: left.catch:
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: left.sibling:
; CHECK: catchpad []
; CHECK: to label %left.sibling.catch unwind label %[[LEFT_END:.+]]
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind label %right
; CHECK: right:
; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]]
; CHECK: right.catch:
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
define void @test12() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
catchpad []
to label %left.catch unwind label %right
left.catch:
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
%x = call i32 @g()
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 %x)
unreachable
inner.end:
catchendpad unwind label %right.end
exit:
ret void
}
; In this case %left and %right are both parents of %inner, so %inner must be
; cloned but the catchendpad unwind target in %inner.end is valid for both
; parents, so the unwind edge should not be removed in either case.
; CHECK-LABEL: define void @test12(
; CHECK: left:
; CHECK: catchpad []
; CHECK: to label %left.catch unwind label %right
; CHECK: left.catch:
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: to label %right.catch unwind label %[[RIGHT_END:.+]]
; CHECK: right.catch:
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 %x)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 %x.for.left)
; CHECK: unreachable
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind label %[[RIGHT_END]]
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[RIGHT_END]]
define void @test13() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
%l = catchpad []
to label %left.cont unwind label %left.end
left.cont:
invoke void @f()
to label %left.ret unwind label %inner
left.ret:
catchret %l to label %invoke.cont
left.end:
catchendpad unwind to caller
right:
%r = catchpad []
to label %right.catch unwind label %right.end
right.catch:
invoke void @f()
to label %right.ret unwind label %inner
right.ret:
catchret %r to label %exit
right.end:
catchendpad unwind to caller
shared:
call void @h(i32 0)
unreachable
inner:
%i = catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 1)
catchret %i to label %shared
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This case tests the scenario where a funclet with multiple parents uses a
; catchret to return to a block that may exist in either parent funclets.
; Both %left and %right are parents of %inner. During common block cloning
; a clone of %shared will be made so that both %left and %right have a copy,
; but the copy of %shared for one of the parent funclets will be unreachable
; until the %inner funclet is cloned. When the %inner.catch block is cloned
; during the %inner funclet cloning, the catchret instruction should be updated
; so that the catchret in the copy %inner.catch for %left returns to the copy of
; %shared in %left and the catchret in the copy of %inner.catch for %right
; returns to the copy of %shared for %right.
; CHECK-LABEL: define void @test13(
; CHECK: left:
; CHECK: %l = catchpad []
; CHECK: to label %left.cont unwind label %left.end
; CHECK: left.cont:
; CHECK: invoke void @f()
; CHECK: to label %left.ret unwind label %[[INNER_LEFT:.+]]
; CHECK: left.ret:
; CHECK: catchret %l to label %invoke.cont
; CHECK: left.end:
; CHECK: catchendpad unwind to caller
; CHECK: right:
; CHECK: %r = catchpad []
; CHECK: to label %right.catch unwind label %right.end
; CHECK: right.catch:
; CHECK: invoke void @f()
; CHECK: to label %right.ret unwind label %[[INNER_RIGHT:.+]]
; CHECK: right.ret:
; CHECK: catchret %r to label %exit
; CHECK: right.end:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_RIGHT:.+]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[SHARED_LEFT:.+]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: %[[I_RIGHT:.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: %[[I_LEFT:.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 1)
; CHECK: catchret %[[I_RIGHT]] to label %[[SHARED_RIGHT]]
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 1)
; CHECK: catchret %[[I_LEFT]] to label %[[SHARED_LEFT]]
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
define void @test14() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
%l = catchpad []
to label %shared unwind label %left.end
left.cont:
invoke void @f()
to label %left.ret unwind label %right
left.ret:
catchret %l to label %exit
left.end:
catchendpad unwind to caller
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind label %left.end
shared:
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
%i = catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 0)
catchret %i to label %left.cont
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This case tests another scenario where a funclet with multiple parents uses a
; catchret to return to a block in one of the parent funclets. Here %right and
; %left are both parents of %inner and %left is a parent of %right. The
; catchret in %inner.catch will cause %left.cont and %left.ret to be cloned for
; both %left and %right, but the catchret in %left.ret is invalid for %right
; but the catchret instruction in the copy of %left.ret for %right will be
; removed as an implausible terminator.
; CHECK-LABEL: define void @test14(
; CHECK: left:
; CHECK: %l = catchpad []
; CHECK: to label %[[SHARED_LEFT:.+]] unwind label %[[LEFT_END:.+]]
; CHECK: [[LEFT_CONT:left.cont.*]]:
; CHECK: invoke void @f()
; CHECK: to label %[[LEFT_RET:.+]] unwind label %[[RIGHT:.+]]
; CHECK: [[LEFT_RET]]:
; CHECK: catchret %l to label %exit
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
; CHECK: [[SHARED_LEFT]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 0)
; CHECK: catchret [[I_LEFT]] to label %[[LEFT_CONT]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
define void @test15() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
%l = catchpad []
to label %left.catch unwind label %left.end
left.catch:
invoke void @f()
to label %shared unwind label %right
left.ret:
catchret %l to label %exit
left.end:
catchendpad unwind to caller
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind label %left.end
shared:
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
%i = catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 0)
catchret %i to label %left.ret
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This case is a variation of test14 but instead of returning to an invoke the
; catchret in %inner.catch returns to a catchret instruction.
; CHECK-LABEL: define void @test15(
; CHECK: left:
; CHECK: %l = catchpad []
; CHECK: to label %left.catch unwind label %[[LEFT_END:.+]]
; CHECK: left.catch:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_LEFT:.+]] unwind label %[[RIGHT:.+]]
; CHECK: [[LEFT_RET_RIGHT:.+]]:
; CHECK: unreachable
; CHECK: [[LEFT_RET_LEFT:.+]]:
; CHECK: catchret %l to label %exit
; CHECK: [[LEFT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
; CHECK: [[SHARED_LEFT]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 0)
; CHECK: catchret [[I_LEFT]] to label %[[LEFT_RET_LEFT]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 0)
; CHECK: catchret [[I_RIGHT]] to label %[[LEFT_RET_RIGHT]]
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END]]
define void @test16() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %exit unwind label %left
left:
%l = cleanuppad []
br label %shared
left.cont:
cleanupret %l unwind label %right
left.end:
cleanupendpad %l unwind label %right
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
invoke void @f()
to label %shared.cont unwind label %inner
shared.cont:
unreachable
inner:
%i = catchpad []
to label %inner.catch unwind label %inner.end
inner.catch:
call void @h(i32 0)
catchret %i to label %left.cont
inner.end:
catchendpad unwind label %left.end
exit:
ret void
}
; This case is another variation of test14 but here the catchret in %inner.catch
; returns to a cleanupret instruction.
; CHECK-LABEL: define void @test16(
; CHECK: left:
; CHECK: %l = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: [[LEFT_CONT_RIGHT:.+]]:
; CHECK: unreachable
; CHECK: [[LEFT_CONT_LEFT:.+]]:
; CHECK: cleanupret %l unwind label %[[RIGHT:.+]]
; CHECK: [[LEFT_END_LEFT:.+]]:
; CHECK: cleanupendpad %l unwind label %[[RIGHT]]
; CHECK: [[RIGHT]]:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 0)
; CHECK: catchret [[I_RIGHT]] to label %[[LEFT_CONT_RIGHT]]
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 0)
; CHECK: catchret [[I_LEFT]] to label %[[LEFT_CONT_LEFT]]
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind label %[[LEFT_END_LEFT]]
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind to caller
define void @test17() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
%l = cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
invoke void @f()
to label %unreachable unwind label %inner
unreachable:
unreachable
inner:
%i = catchpad []
to label %inner.catch unwind label %inner.sibling
inner.catch:
call void @h(i32 0)
unreachable
inner.sibling:
%is = catchpad []
to label %inner.sibling.catch unwind label %inner.end
inner.sibling.catch:
invoke void @f()
to label %unreachable unwind label %inner.end
inner.end:
catchendpad unwind label %right.end
exit:
ret void
}
; This case tests the scenario where two catchpads with the same catchendpad
; have multiple parents. Both %left and %right are parents of %inner and
; %inner.sibling so both of the inner funclets must be cloned. Because
; the catchendpad in %inner.end unwinds to the catchendpad for %right, the
; unwind edge should be removed for the copy of %inner.end that is reached
; from %left. In addition, the %inner.siblin.catch block contains an invoke
; that unwinds to the shared inner catchendpad. The unwind destination for
; this invoke should be updated to unwind to the correct cloned %inner.end
; for each path to the funclet.
; CHECK-LABEL: define void @test17(
; CHECK: left:
; CHECK: %l = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_SIBLING_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_SIBLING_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_SIBLING_RIGHT]]:
; CHECK: [[IS_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_SIBLING_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_SIBLING_LEFT]]:
; CHECK: [[IS_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_SIBLING_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_SIBLING_CATCH_RIGHT]]:
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_END_RIGHT]]
; CHECK: [[INNER_SIBLING_CATCH_LEFT]]:
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_END_LEFT]]
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind label %[[RIGHT_END]]
define void @test18() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
%l = cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
invoke void @f()
to label %unreachable unwind label %inner
unreachable:
unreachable
inner:
%i = catchpad []
to label %inner.catch unwind label %inner.sibling
inner.catch:
invoke void @f()
to label %unreachable unwind label %inner.end
inner.sibling:
%is = catchpad []
to label %inner.sibling.catch unwind label %inner.end
inner.sibling.catch:
call void @h(i32 0)
unreachable
inner.end:
catchendpad unwind label %right.end
exit:
ret void
}
; This is like test17 except that the inner invoke is moved from the
; %inner.sibling funclet to %inner so that it is unwinding to a
; catchendpad block that has not yet been cloned. The unwind destination
; of the invoke should still be updated to reach the correct copy of
; %inner.end for the path by which it is reached.
; CHECK-LABEL: define void @test18(
; CHECK: left:
; CHECK: %l = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_RIGHT:.+]] unwind label %[[INNER_SIBLING_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_CATCH_LEFT:.+]] unwind label %[[INNER_SIBLING_LEFT:.+]]
; CHECK: [[INNER_CATCH_RIGHT]]:
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_SIBLING_RIGHT]]:
; CHECK: [[IS_RIGHT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_SIBLING_CATCH_RIGHT:.+]] unwind label %[[INNER_END_RIGHT]]
; CHECK: [[INNER_SIBLING_LEFT]]:
; CHECK: [[IS_LEFT:\%.+]] = catchpad []
; CHECK: to label %[[INNER_SIBLING_CATCH_LEFT:.+]] unwind label %[[INNER_END_LEFT]]
; CHECK: [[INNER_SIBLING_CATCH_RIGHT]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_SIBLING_CATCH_LEFT]]:
; CHECK: call void @h(i32 0)
; CHECK: unreachable
; CHECK: [[INNER_END_LEFT]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: catchendpad unwind label %[[RIGHT_END]]
define void @test19() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
%l = cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
invoke void @f()
to label %unreachable unwind label %inner
unreachable:
unreachable
inner:
%i = cleanuppad []
invoke void @f()
to label %unreachable unwind label %inner.end
inner.end:
cleanupendpad %i unwind label %right.end
exit:
ret void
}
; This case tests the scenario where an invoke in a funclet with multiple
; parents unwinds to a cleanup end pad for the funclet. The unwind destination
; for the invoke should map to the correct copy of the cleanup end pad block.
; CHECK-LABEL: define void @test19(
; CHECK: left:
; CHECK: %l = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_END_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_END_LEFT:.+]]
; CHECK: [[INNER_END_RIGHT]]:
; CHECK: cleanupendpad [[I_RIGHT]] unwind label %[[RIGHT_END]]
; CHECK: [[INNER_END_LEFT]]:
; CHECK: cleanupendpad [[I_LEFT]] unwind to caller
define void @test20() personality i32 (...)* @__CxxFrameHandler3 {
entry:
invoke void @f()
to label %invoke.cont unwind label %left
invoke.cont:
invoke void @f()
to label %exit unwind label %right
left:
%l = cleanuppad []
br label %shared
right:
catchpad []
to label %right.catch unwind label %right.end
right.catch:
br label %shared
right.end:
catchendpad unwind to caller
shared:
invoke void @f()
to label %unreachable unwind label %inner
unreachable:
unreachable
inner:
%i = cleanuppad []
invoke void @f()
to label %unreachable unwind label %inner.cleanup
inner.cleanup:
cleanuppad []
call void @f()
unreachable
exit:
ret void
}
; This tests the case where a funclet with multiple parents contains an invoke
; instruction that unwinds to a child funclet. Here %left and %right are both
; parents of %inner. Initially %inner is the only parent of %inner.cleanup but
; after %inner is cloned, %inner.cleanup has multiple parents and so it must
; also be cloned.
; CHECK-LABEL: define void @test20(
; CHECK: left:
; CHECK: %l = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_LEFT:.+]] unwind label %[[INNER_LEFT:.+]]
; CHECK: right:
; CHECK: catchpad []
; CHECK: to label %[[RIGHT_CATCH:.+]] unwind label %[[RIGHT_END:.+]]
; CHECK: [[RIGHT_CATCH]]:
; CHECK: invoke void @f()
; CHECK: to label %[[SHARED_CONT_RIGHT:.+]] unwind label %[[INNER_RIGHT:.+]]
; CHECK: [[RIGHT_END]]:
; CHECK: catchendpad unwind to caller
; CHECK: [[SHARED_CONT_RIGHT]]:
; CHECK: unreachable
; CHECK: [[SHARED_CONT_LEFT]]:
; CHECK: unreachable
; CHECK: [[INNER_RIGHT]]:
; CHECK: [[I_RIGHT:\%.+]] = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_RIGHT:.+]] unwind label %[[INNER_CLEANUP_RIGHT:.+]]
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_LEFT:\%.+]] = cleanuppad []
; CHECK: invoke void @f()
; CHECK: to label %[[UNREACHABLE_LEFT:.+]] unwind label %[[INNER_CLEANUP_LEFT:.+]]
; CHECK: [[INNER_CLEANUP_RIGHT]]:
; CHECK: cleanuppad []
; CHECK: call void @f()
; CHECK: unreachable
; CHECK: [[INNER_CLEANUP_LEFT]]:
; CHECK: cleanuppad []
; CHECK: call void @f()
; CHECK: unreachable