|
@@ -1,1548 +0,0 @@
|
|
|
-; 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
|
|
|
-
|
|
|
-
|