mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-12-14 07:09:08 +00:00
feb8745705
Those commits created an artificial edge from a cleanup to a synthesized catchswitch in order to get the MSVC personality routine to execute cleanups which don't cleanupret and are not wrapped by a catchswitch. This worked well enough but is not a complete solution in situations where there the cleanup infinite loops. However, the real deal breaker behind this approach comes about from a degenerate case where the cleanup is post-dominated by unreachable *and* throws an exception. This ends poorly because the catchswitch will inadvertently catch the exception. Because of this we should go back to our previous behavior of not executing certain cleanups (identical behavior with the Itanium ABI implementation in clang, GCC and ICC). N.B. I think this could be salvaged by making the catchpad rethrow the exception and properly transforming throwing calls in the cleanup into invokes. llvm-svn: 259338
357 lines
10 KiB
LLVM
357 lines
10 KiB
LLVM
; RUN: opt -mtriple=x86_64-pc-windows-msvc -S -winehprepare < %s | FileCheck %s
|
|
|
|
declare i32 @__CxxFrameHandler3(...)
|
|
|
|
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 (...)* @__CxxFrameHandler3 { 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
|
|
}
|