1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-22 04:22:57 +02:00
llvm-mirror/test/CodeGen/WinEH/wineh-multi-parent-cloning.ll

1558 lines
52 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: 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: store i32 %x, i32* %x.wineh.spillslot
; 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: cleanupret [[I_R]] unwind label %right.end
; CHECK: [[INNER_LEFT]]:
; CHECK: [[I_L:\%.+]] = cleanuppad []
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; 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: 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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: [[X_RELOAD_R:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_R]])
; CHECK: unreachable
; CHECK: [[INNER_CATCH_LEFT]]:
; CHECK: [[X_RELOAD_L:\%.+]] = load i32, i32* %x.wineh.spillslot
; CHECK: call void @h(i32 [[X_RELOAD_L]])
; 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