mirror of
https://github.com/RPCS3/llvm.git
synced 2025-03-03 16:18:37 +00:00

Summary: WinEHPrepare is going to require that cleanuppad and catchpad produce values of token type which are consumed by any cleanupret or catchret exiting the pad. This change updates the signatures of those operators to require/enforce that the type produced by the pads is token type and that the rets have an appropriate argument. The catchpad argument of a `CatchReturnInst` must be a `CatchPadInst` (and similarly for `CleanupReturnInst`/`CleanupPadInst`). To accommodate that restriction, this change adds a notion of an operator constraint to both LLParser and BitcodeReader, allowing appropriate sentinels to be constructed for forward references and appropriate error messages to be emitted for illegal inputs. Also add a verifier rule (noted in LangRef) that a catchpad with a catchpad predecessor must have no other predecessors; this ensures that WinEHPrepare will see the expected linear relationship between sibling catches on the same try. Lastly, remove some superfluous/vestigial casts from instruction operand setters operating on BasicBlocks. Reviewers: rnk, majnemer Subscribers: llvm-commits Differential Revision: http://reviews.llvm.org/D12108 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@245797 91177308-0d34-0410-b5e6-96231b3b80d8
412 lines
11 KiB
LLVM
412 lines
11 KiB
LLVM
; 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 @i()
|
|
|
|
; 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 ]
|
|
%cp = catchpad [] to label %catch unwind label %catchend
|
|
|
|
catch:
|
|
; CHECK: catch:
|
|
; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]]
|
|
; CHECK-NEXT: call void @h(i32 [[Reload]])
|
|
call void @h(i32 %phi)
|
|
catchret %cp to label %exit
|
|
|
|
catchend:
|
|
catchendpad unwind to caller
|
|
|
|
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: catchpad []
|
|
%x = phi i32 [ 1, %left ], [ 2, %right ]
|
|
%cpinner = catchpad [] to label %catch.inner unwind label %catchend.inner
|
|
|
|
catch.inner:
|
|
; Need just one store here because only %y is affected
|
|
; CHECK: catch.inner:
|
|
%z = call i32 @g()
|
|
; CHECK: store i32 %z
|
|
; CHECK-NEXT: invoke void @f
|
|
invoke void @f()
|
|
to label %catchret.inner unwind label %merge.outer
|
|
|
|
catchret.inner:
|
|
catchret %cpinner to label %exit
|
|
catchend.inner:
|
|
catchendpad unwind label %merge.outer
|
|
|
|
merge.outer:
|
|
; CHECK: merge.outer:
|
|
; CHECK-NOT: = phi
|
|
; CHECK: [[CatchPad:%[^ ]+]] = catchpad []
|
|
%y = phi i32 [ %x, %catchend.inner ], [ %z, %catch.inner ]
|
|
%cpouter = catchpad [] to label %catch.outer unwind label %catchend.outer
|
|
|
|
catchend.outer:
|
|
catchendpad unwind to caller
|
|
|
|
catch.outer:
|
|
; 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: catch.outer:
|
|
; CHECK-DAG: load i32, i32* [[Slot1]]
|
|
; CHECK-DAG: load i32, i32* [[Slot2]]
|
|
; CHECK: catchret [[CatchPad]] to label
|
|
call void @h(i32 %x)
|
|
call void @h(i32 %y)
|
|
catchret %cpouter to label %exit
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: @test3(
|
|
define void @test3(i1 %B) personality i32 (...)* @__CxxFrameHandler3 {
|
|
entry:
|
|
; need to spill parameter %B and def %x since they're used in a funclet
|
|
; CHECK: entry:
|
|
; CHECK-DAG: store i1 %B, i1* [[SlotB:%[^ ]+]]
|
|
; CHECK-DAG: store i32 %x, i32* [[SlotX:%[^ ]+]]
|
|
; CHECK: invoke void @f
|
|
%x = call i32 @g()
|
|
invoke void @f()
|
|
to label %exit unwind label %catchpad
|
|
|
|
catchpad:
|
|
%cp = catchpad [] to label %catch unwind label %catchend
|
|
|
|
catch:
|
|
; Need to reload %B here
|
|
; CHECK: catch:
|
|
; CHECK: [[ReloadB:%[^ ]+]] = load i1, i1* [[SlotB]]
|
|
; CHECK: br i1 [[ReloadB]]
|
|
br i1 %B, label %left, label %right
|
|
left:
|
|
; Use of %x is in a phi, so need reload here in pred
|
|
; CHECK: left:
|
|
; CHECK: [[ReloadX:%[^ ]+]] = load i32, i32* [[SlotX]]
|
|
; CHECK: br label %merge
|
|
br label %merge
|
|
right:
|
|
br label %merge
|
|
merge:
|
|
; CHECK: merge:
|
|
; CHECK: %phi = phi i32 [ [[ReloadX]], %left ]
|
|
%phi = phi i32 [ %x, %left ], [ 42, %right ]
|
|
call void @h(i32 %phi)
|
|
catchret %cp to label %exit
|
|
|
|
catchend:
|
|
catchendpad unwind to caller
|
|
|
|
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: catchpad []
|
|
%phi.inner = phi i32 [ %l, %left ], [ %r, %right ]
|
|
%cp1 = catchpad [] to label %catch.inner unwind label %catchend.inner
|
|
catch.inner:
|
|
catchret %cp1 to label %join
|
|
catchend.inner:
|
|
catchendpad unwind label %catchpad.outer
|
|
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: catchpad []
|
|
%phi.outer = phi i32 [ %phi.inner, %catchend.inner ], [ %j, %join ]
|
|
%cp2 = catchpad [] to label %catch.outer unwind label %catchend.outer
|
|
catch.outer:
|
|
; CHECK: catch.outer:
|
|
; CHECK: [[Reload:%[^ ]+]] = load i32, i32* [[Slot]]
|
|
; CHECK: call void @h(i32 [[Reload]])
|
|
call void @h(i32 %phi.outer)
|
|
catchret %cp2 to label %exit
|
|
catchend.outer:
|
|
catchendpad unwind to caller
|
|
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 []
|
|
; CHECK: [[CleanupReload:%[^ ]+]] = load i32, i32* [[CleanupSlot]]
|
|
%phi.cleanup = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
|
|
%cp = cleanuppad []
|
|
%b = call i1 @i()
|
|
br i1 %b, label %left, label %right
|
|
|
|
left:
|
|
; CHECK: left:
|
|
; CHECK: call void @h(i32 [[CleanupReload]]
|
|
call void @h(i32 %phi.cleanup)
|
|
br label %merge
|
|
|
|
right:
|
|
; CHECK: right:
|
|
; CHECK: call void @h(i32 [[CleanupReload]]
|
|
call void @h(i32 %phi.cleanup)
|
|
br label %merge
|
|
|
|
merge:
|
|
; need store for %phi.catch
|
|
; CHECK: merge:
|
|
; CHECK-NEXT: store i32 [[CleanupReload]], i32* [[CatchSlot:%[^ ]+]]
|
|
; CHECK-NEXT: cleanupret
|
|
cleanupret %cp unwind label %catchpad
|
|
|
|
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 %catchpad
|
|
|
|
catchpad:
|
|
; CHECK: catchpad:
|
|
; CHECK-NEXT: catchpad []
|
|
%phi.catch = phi i32 [ %phi.cleanup, %merge ], [ 3, %invoke.cont2 ]
|
|
%cp2 = catchpad [] to label %catch unwind label %catchend
|
|
|
|
catch:
|
|
; CHECK: catch:
|
|
; CHECK: [[CatchReload:%[^ ]+]] = load i32, i32* [[CatchSlot]]
|
|
; CHECK: call void @h(i32 [[CatchReload]]
|
|
call void @h(i32 %phi.catch)
|
|
catchret %cp2 to label %exit
|
|
|
|
catchend:
|
|
catchendpad unwind to caller
|
|
|
|
exit:
|
|
ret void
|
|
}
|
|
|
|
; CHECK-LABEL: @test6(
|
|
define void @test6() personality i32 (...)* @__CxxFrameHandler3 {
|
|
entry:
|
|
; Since %x needs to be stored but the edge to loop is critical,
|
|
; it needs to be split
|
|
; CHECK: entry:
|
|
; CHECK: invoke i32 @g
|
|
; CHECK-NEXT: to label %[[SplitBlock:[^ ]+]] unwind label %to_caller
|
|
%x = invoke i32 @g()
|
|
to label %loop unwind label %to_caller
|
|
; The store should be in the split block
|
|
; CHECK: [[SplitBlock]]:
|
|
; CHECK: store i32 %x, i32* [[SpillSlot:%[^ ]+]]
|
|
; CHECK: br label %loop
|
|
to_caller:
|
|
%cp1 = cleanuppad []
|
|
cleanupret %cp1 unwind to caller
|
|
loop:
|
|
invoke void @f()
|
|
to label %loop unwind label %cleanup
|
|
cleanup:
|
|
; CHECK: cleanup:
|
|
; CHECK: [[Load:%[^ ]+]] = load i32, i32* [[SpillSlot]]
|
|
; CHECK: call void @h(i32 [[Load]])
|
|
%cp2 = cleanuppad []
|
|
call void @h(i32 %x)
|
|
cleanupret %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: %[[CatchPad:[^ ]+]] = catchpad []
|
|
%x = phi i32 [ 1, %entry ], [ 2, %invoke.cont ]
|
|
%cp = catchpad [] to label %catch unwind label %catchend
|
|
catch:
|
|
%b = call i1 @i()
|
|
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 %[[CatchPad]] to label %[[SplitLeft:[^ ]+]]
|
|
catchret %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: store i32 %y, i32* [[SlotY:%[^ ]+]]
|
|
; CHECK: catchret %[[CatchPad]] to label %[[SplitRight:[^ ]+]]
|
|
%y = call i32 @g()
|
|
catchret %cp to label %join
|
|
; CHECK: [[SplitRight]]:
|
|
; CHECK: [[LoadY:%[^ ]+]] = load i32, i32* [[SlotY]]
|
|
; CHECK: br label %join
|
|
catchend:
|
|
catchendpad unwind to caller
|
|
join:
|
|
; CHECK: join:
|
|
; CHECK: %phi = phi i32 [ [[LoadX]], %[[SplitLeft]] ], [ [[LoadY]], %[[SplitRight]] ]
|
|
%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 []
|
|
; CHECK-NEXT: call void @f()
|
|
; CHECK-NEXT: cleanupret [[CleanupPad1]]
|
|
%cp0 = cleanuppad []
|
|
br label %cleanupexit
|
|
|
|
cleanup2:
|
|
; CHECK: cleanuppad []
|
|
; CHECK-NEXT: call void @f()
|
|
; CHECK-NEXT: unreachable
|
|
%cp1 = cleanuppad []
|
|
br label %cleanupexit
|
|
|
|
cleanupexit:
|
|
call void @f()
|
|
cleanupret %cp0 unwind label %cleanup2
|
|
}
|