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

