mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-10-21 12:02:58 +02:00
a2ed036c0a
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
358 lines
10 KiB
LLVM
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
|
|
}
|