1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-10-21 12:02:58 +02:00
llvm-mirror/test/CodeGen/WinEH/wineh-demotion.ll
David Majnemer a2ed036c0a [WinEH] Let cleanups post-dominated by unreachable get executed
Cleanups in C++ are a little weird.  They are only guaranteed to be
reliably executed if, and only if, there is a viable catch handler which
can handle the exception.

This means that reachability of a cleanup is lexically determined by it
being nested with a try-block which unwinds to a catch.  It is *cannot*
be reasoned about by examining the control flow edges leaving a cleanup.

Usually this is not a problem.  It becomes a problem when there are *no*
edges out of a cleanup because we believed that code post-dominated by
the cleanup is dead.  In LLVM's case, this code is what informs the
personality routine about the presence of a suitable catch handler.
However, the lack of edges to that catch handler makes the handler
become unreachable which causes us to remove it.  By removing the
handler, the cleanup becomes unreachable.

Instead, inject a catch-all handler with every cleanup that has no
unwind edges.  This will allow us to properly unwind the stack.

This fixes PR25997.

llvm-svn: 258580
2016-01-22 23:20:43 +00:00

358 lines
10 KiB
LLVM

; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -winehprepare < %s | FileCheck %s
declare i32 @__CxxFrameHandler3(...)
declare i32 @__C_specific_handler(...)
declare void @f()
declare i32 @g()
declare void @h(i32)
declare i1 @i()
declare void @llvm.bar() nounwind
; CHECK-LABEL: @test1(
define void @test1(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
entry:
; Spill slot should be inserted here
; CHECK: [[Slot:%[^ ]+]] = alloca
; Can't store for %phi at these defs because the lifetimes overlap
; CHECK-NOT: store
%x = call i32 @g()
%y = call i32 @g()
br i1 %B, label %left, label %right
left:
; CHECK: left:
; CHECK-NEXT: store i32 %x, i32* [[Slot]]
; CHECK-NEXT: invoke void @f
invoke void @f()
to label %exit unwind label %merge
right:
; CHECK: right:
; CHECK-NEXT: store i32 %y, i32* [[Slot]]
; CHECK-NEXT: invoke void @f
invoke void @f()
to label %exit unwind label %merge
merge:
; CHECK: merge:
; CHECK-NOT: = phi
%phi = phi i32 [ %x, %left ], [ %y, %right ]
%cs1 = catchswitch within none [label %catch] unwind to caller
catch:
%cp = catchpad within %cs1 []
; CHECK: catch:
; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]]
; CHECK-NEXT: call void @h(i32 [[Reload]])
call void @h(i32 %phi) [ "funclet"(token %cp) ]
catchret from %cp to label %exit
exit:
ret void
}
; CHECK-LABEL: @test2(
define void @test2(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
entry:
br i1 %B, label %left, label %right
left:
; Need two stores here because %x and %y interfere so they need 2 slots
; CHECK: left:
; CHECK: store i32 1, i32* [[Slot1:%[^ ]+]]
; CHECK: store i32 1, i32* [[Slot2:%[^ ]+]]
; CHECK-NEXT: invoke void @f
invoke void @f()
to label %exit unwind label %merge.inner
right:
; Need two stores here because %x and %y interfere so they need 2 slots
; CHECK: right:
; CHECK-DAG: store i32 2, i32* [[Slot1]]
; CHECK-DAG: store i32 2, i32* [[Slot2]]
; CHECK: invoke void @f
invoke void @f()
to label %exit unwind label %merge.inner
merge.inner:
; CHECK: merge.inner:
; CHECK-NOT: = phi
; CHECK: catchswitch within none
%x = phi i32 [ 1, %left ], [ 2, %right ]
%cs1 = catchswitch within none [label %catch.inner] unwind label %merge.outer
catch.inner:
%cpinner = catchpad within %cs1 []
; Need just one store here because only %y is affected
; CHECK: catch.inner:
%z = call i32 @g() [ "funclet"(token %cpinner) ]
; CHECK: store i32 %z
; CHECK-NEXT: invoke void @f
invoke void @f() [ "funclet"(token %cpinner) ]
to label %catchret.inner unwind label %merge.outer
catchret.inner:
catchret from %cpinner to label %exit
merge.outer:
%y = phi i32 [ %x, %merge.inner ], [ %z, %catch.inner ]
; CHECK: merge.outer:
; CHECK-NOT: = phi
; CHECK: catchswitch within none
%cs2 = catchswitch within none [label %catch.outer] unwind to caller
catch.outer:
%cpouter = catchpad within %cs2 []
; CHECK: catch.outer:
; CHECK: [[CatchPad:%[^ ]+]] = catchpad within %cs2 []
; Need to load x and y from two different slots since they're both live
; and can have different values (if we came from catch.inner)
; CHECK-DAG: load i32, i32* [[Slot1]]
; CHECK-DAG: load i32, i32* [[Slot2]]
; CHECK: catchret from [[CatchPad]] to label
call void @h(i32 %x) [ "funclet"(token %cpouter) ]
call void @h(i32 %y) [ "funclet"(token %cpouter) ]
catchret from %cpouter to label %exit
exit:
ret void
}
; test4: don't need stores for %phi.inner, as its only use is to feed %phi.outer
; %phi.outer needs stores in %left, %right, and %join
; CHECK-LABEL: @test4(
define void @test4(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
entry:
; CHECK: entry:
; CHECK: [[Slot:%[^ ]+]] = alloca
; CHECK-NEXT: br
br i1 %B, label %left, label %right
left:
; CHECK: left:
; CHECK-NOT: store
; CHECK: store i32 %l, i32* [[Slot]]
; CHECK-NEXT: invoke void @f
%l = call i32 @g()
invoke void @f()
to label %join unwind label %catchpad.inner
right:
; CHECK: right:
; CHECK-NOT: store
; CHECK: store i32 %r, i32* [[Slot]]
; CHECK-NEXT: invoke void @f
%r = call i32 @g()
invoke void @f()
to label %join unwind label %catchpad.inner
catchpad.inner:
; CHECK: catchpad.inner:
; CHECK-NEXT: catchswitch within none
%phi.inner = phi i32 [ %l, %left ], [ %r, %right ]
%cs1 = catchswitch within none [label %catch.inner] unwind label %catchpad.outer
catch.inner:
%cp1 = catchpad within %cs1 []
catchret from %cp1 to label %join
join:
; CHECK: join:
; CHECK-NOT: store
; CHECK: store i32 %j, i32* [[Slot]]
; CHECK-NEXT: invoke void @f
%j = call i32 @g()
invoke void @f()
to label %exit unwind label %catchpad.outer
catchpad.outer:
; CHECK: catchpad.outer:
; CHECK-NEXT: catchswitch within none
%phi.outer = phi i32 [ %phi.inner, %catchpad.inner ], [ %j, %join ]
%cs2 = catchswitch within none [label %catch.outer] unwind to caller
catch.outer:
; CHECK: catch.outer:
; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]]
; CHECK: call void @h(i32 [[Reload]])
%cp2 = catchpad within %cs2 []
call void @h(i32 %phi.outer) [ "funclet"(token %cp2) ]
catchret from %cp2 to label %exit
exit:
ret void
}
; CHECK-LABEL: @test5(
define void @test5() personality i32 (...)* @__CxxFrameHandler3 {
entry:
; need store for %phi.cleanup
; CHECK: entry:
; CHECK: store i32 1, i32* [[CleanupSlot:%[^ ]+]]
; CHECK-NEXT: invoke void @f
invoke void @f()
to label %invoke.cont unwind label %cleanup
invoke.cont:
; need store for %phi.cleanup
; CHECK: invoke.cont:
; CHECK-NEXT: store i32 2, i32* [[CleanupSlot]]
; CHECK-NEXT: invoke void @f
invoke void @f()
to label %invoke.cont2 unwind label %cleanup
cleanup:
; cleanup phi can be loaded at cleanup entry
; CHECK: cleanup:
; CHECK-NEXT: cleanuppad within none []
; CHECK: [[CleanupReload:%[^ ]+]] = load i32, i32* [[CleanupSlot]]
%phi.cleanup = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
%cp = cleanuppad within none []
%b = call i1 @i() [ "funclet"(token %cp) ]
br i1 %b, label %left, label %right
left:
; CHECK: left:
; CHECK: call void @h(i32 [[CleanupReload]]
call void @h(i32 %phi.cleanup) [ "funclet"(token %cp) ]
br label %merge
right:
; CHECK: right:
; CHECK: call void @h(i32 [[CleanupReload]]
call void @h(i32 %phi.cleanup) [ "funclet"(token %cp) ]
br label %merge
merge:
; need store for %phi.catch
; CHECK: merge:
; CHECK-NEXT: store i32 [[CleanupReload]], i32* [[CatchSlot:%[^ ]+]]
; CHECK-NEXT: cleanupret
cleanupret from %cp unwind label %catchswitch
invoke.cont2:
; need store for %phi.catch
; CHECK: invoke.cont2:
; CHECK-NEXT: store i32 3, i32* [[CatchSlot]]
; CHECK-NEXT: invoke void @f
invoke void @f()
to label %exit unwind label %catchswitch
catchswitch:
; CHECK: catchswitch:
; CHECK-NEXT: catchswitch within none
%phi.catch = phi i32 [ %phi.cleanup, %merge ], [ 3, %invoke.cont2 ]
%cs1 = catchswitch within none [label %catch] unwind to caller
catch:
; CHECK: catch:
; CHECK: catchpad within %cs1
; CHECK: [[CatchReload:%[^ ]+]] = load i32, i32* [[CatchSlot]]
; CHECK: call void @h(i32 [[CatchReload]]
%cp2 = catchpad within %cs1 []
call void @h(i32 %phi.catch) [ "funclet"(token %cp2) ]
catchret from %cp2 to label %exit
exit:
ret void
}
; We used to demote %x, but we don't need to anymore.
; CHECK-LABEL: @test6(
define void @test6() personality i32 (...)* @__CxxFrameHandler3 {
entry:
; CHECK: entry:
; CHECK: %x = invoke i32 @g()
; CHECK-NEXT: to label %loop unwind label %to_caller
%x = invoke i32 @g()
to label %loop unwind label %to_caller
to_caller:
%cp1 = cleanuppad within none []
cleanupret from %cp1 unwind to caller
loop:
invoke void @f()
to label %loop unwind label %cleanup
cleanup:
; CHECK: cleanup:
; CHECK: call void @h(i32 %x)
%cp2 = cleanuppad within none []
call void @h(i32 %x) [ "funclet"(token %cp2) ]
cleanupret from %cp2 unwind to caller
}
; CHECK-LABEL: @test7(
define void @test7() personality i32 (...)* @__CxxFrameHandler3 {
entry:
; %x is an EH pad phi, so gets stored in pred here
; CHECK: entry:
; CHECK: store i32 1, i32* [[SlotX:%[^ ]+]]
; CHECK: invoke void @f()
invoke void @f()
to label %invoke.cont unwind label %catchpad
invoke.cont:
; %x is an EH pad phi, so gets stored in pred here
; CHECK: invoke.cont:
; CHECK: store i32 2, i32* [[SlotX]]
; CHECK: invoke void @f()
invoke void @f()
to label %exit unwind label %catchpad
catchpad:
; %x phi should be eliminated
; CHECK: catchpad:
; CHECK-NEXT: catchswitch within none
%x = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
%cs1 = catchswitch within none [label %catch] unwind to caller
catch:
; CHECK: catch:
; CHECK-NEXT: %[[CatchPad:[^ ]+]] = catchpad within %cs1 []
%cp = catchpad within %cs1 []
%b = call i1 @i() [ "funclet"(token %cp) ]
br i1 %b, label %left, label %right
left:
; Edge from %left to %join needs to be split so that
; the load of %x can be inserted *after* the catchret
; CHECK: left:
; CHECK-NEXT: catchret from %[[CatchPad]] to label %[[SplitLeft:[^ ]+]]
catchret from %cp to label %join
; CHECK: [[SplitLeft]]:
; CHECK: [[LoadX:%[^ ]+]] = load i32, i32* [[SlotX]]
; CHECK: br label %join
right:
; Edge from %right to %join needs to be split so that
; the load of %y can be inserted *after* the catchret
; CHECK: right:
; CHECK: %y = call i32 @g()
; CHECK: catchret from %[[CatchPad]] to label %join
%y = call i32 @g() [ "funclet"(token %cp) ]
catchret from %cp to label %join
join:
; CHECK: join:
; CHECK: %phi = phi i32 [ [[LoadX]], %[[SplitLeft]] ], [ %y, %right ]
%phi = phi i32 [ %x, %left ], [ %y, %right ]
call void @h(i32 %phi)
br label %exit
exit:
ret void
}
; CHECK-LABEL: @test8(
define void @test8() personality i32 (...)* @__C_specific_handler { entry:
invoke void @f()
to label %done unwind label %cleanup1
invoke void @f()
to label %done unwind label %cleanup2
done:
ret void
cleanup1:
; CHECK: [[CleanupPad1:%[^ ]+]] = cleanuppad within none []
; CHECK-NEXT: call void @llvm.bar()
; CHECK-NEXT: cleanupret from [[CleanupPad1]]
%cp0 = cleanuppad within none []
br label %cleanupexit
cleanup2:
; CHECK: cleanuppad within none []
; CHECK-NEXT: call void @llvm.bar()
; CHECK-NEXT: unreachable
%cp1 = cleanuppad within none []
br label %cleanupexit
cleanupexit:
call void @llvm.bar()
cleanupret from %cp0 unwind label %cleanup2
}