forked from OSchip/llvm-project
1549 lines
51 KiB
LLVM
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
|
|
|
|
|