mirror of
https://github.com/RPCSX/llvm.git
synced 2025-02-04 11:27:34 +00:00
[WinEHPrepare] Provide a cloning mode which doesn't demote
We are experimenting with a new approach to saving and restoring SSA values used across funclets: let the register allocator do the dirty work for us. However, this means that we need to be able to clone commoned blocks without relying on demotion. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@247835 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
6badbab39a
commit
ad53a65179
@ -41,6 +41,7 @@
|
||||
#include "llvm/Transforms/Utils/Cloning.h"
|
||||
#include "llvm/Transforms/Utils/Local.h"
|
||||
#include "llvm/Transforms/Utils/PromoteMemToReg.h"
|
||||
#include "llvm/Transforms/Utils/SSAUpdater.h"
|
||||
#include <memory>
|
||||
|
||||
using namespace llvm;
|
||||
@ -48,6 +49,17 @@ using namespace llvm::PatternMatch;
|
||||
|
||||
#define DEBUG_TYPE "winehprepare"
|
||||
|
||||
static cl::opt<bool> DisableDemotion(
|
||||
"disable-demotion", cl::Hidden,
|
||||
cl::desc(
|
||||
"Clone multicolor basic blocks but do not demote cross funclet values"),
|
||||
cl::init(false));
|
||||
|
||||
static cl::opt<bool> DisableCleanups(
|
||||
"disable-cleanups", cl::Hidden,
|
||||
cl::desc("Do not remove implausible terminators or other similar cleanups"),
|
||||
cl::init(false));
|
||||
|
||||
namespace {
|
||||
|
||||
// This map is used to model frame variable usage during outlining, to
|
||||
@ -3399,6 +3411,77 @@ void WinEHPrepare::cloneCommonBlocks(
|
||||
// Loop over all instructions, fixing each one as we find it...
|
||||
for (Instruction &I : *BB)
|
||||
RemapInstruction(&I, VMap, RF_IgnoreMissingEntries);
|
||||
|
||||
// Check to see if SuccBB has PHI nodes. If so, we need to add entries to
|
||||
// the PHI nodes for NewBB now.
|
||||
for (auto &BBMapping : Orig2Clone) {
|
||||
BasicBlock *OldBlock = BBMapping.first;
|
||||
BasicBlock *NewBlock = BBMapping.second;
|
||||
for (BasicBlock *SuccBB : successors(NewBlock)) {
|
||||
for (Instruction &SuccI : *SuccBB) {
|
||||
auto *SuccPN = dyn_cast<PHINode>(&SuccI);
|
||||
if (!SuccPN)
|
||||
break;
|
||||
|
||||
// Ok, we have a PHI node. Figure out what the incoming value was for
|
||||
// the OldBlock.
|
||||
int OldBlockIdx = SuccPN->getBasicBlockIndex(OldBlock);
|
||||
if (OldBlockIdx == -1)
|
||||
break;
|
||||
Value *IV = SuccPN->getIncomingValue(OldBlockIdx);
|
||||
|
||||
// Remap the value if necessary.
|
||||
if (auto *Inst = dyn_cast<Instruction>(IV)) {
|
||||
ValueToValueMapTy::iterator I = VMap.find(Inst);
|
||||
if (I != VMap.end())
|
||||
IV = I->second;
|
||||
}
|
||||
|
||||
SuccPN->addIncoming(IV, NewBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ValueToValueMapTy::value_type VT : VMap) {
|
||||
// If there were values defined in BB that are used outside the funclet,
|
||||
// then we now have to update all uses of the value to use either the
|
||||
// original value, the cloned value, or some PHI derived value. This can
|
||||
// require arbitrary PHI insertion, of which we are prepared to do, clean
|
||||
// these up now.
|
||||
SmallVector<Use *, 16> UsesToRename;
|
||||
|
||||
auto *OldI = dyn_cast<Instruction>(const_cast<Value *>(VT.first));
|
||||
if (!OldI)
|
||||
continue;
|
||||
auto *NewI = cast<Instruction>(VT.second);
|
||||
// Scan all uses of this instruction to see if it is used outside of its
|
||||
// funclet, and if so, record them in UsesToRename.
|
||||
for (Use &U : OldI->uses()) {
|
||||
Instruction *UserI = cast<Instruction>(U.getUser());
|
||||
BasicBlock *UserBB = UserI->getParent();
|
||||
std::set<BasicBlock *> &ColorsForUserBB = BlockColors[UserBB];
|
||||
assert(!ColorsForUserBB.empty());
|
||||
if (ColorsForUserBB.size() > 1 ||
|
||||
*ColorsForUserBB.begin() != FuncletPadBB)
|
||||
UsesToRename.push_back(&U);
|
||||
}
|
||||
|
||||
// If there are no uses outside the block, we're done with this
|
||||
// instruction.
|
||||
if (UsesToRename.empty())
|
||||
continue;
|
||||
|
||||
// We found a use of OldI outside of the funclet. Rename all uses of OldI
|
||||
// that are outside its funclet to be uses of the appropriate PHI node
|
||||
// etc.
|
||||
SSAUpdater SSAUpdate;
|
||||
SSAUpdate.Initialize(OldI->getType(), OldI->getName());
|
||||
SSAUpdate.AddAvailableValue(OldI->getParent(), OldI);
|
||||
SSAUpdate.AddAvailableValue(NewI->getParent(), NewI);
|
||||
|
||||
while (!UsesToRename.empty())
|
||||
SSAUpdate.RewriteUseAfterInsertions(*UsesToRename.pop_back_val());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3429,6 +3512,11 @@ void WinEHPrepare::removeImplausibleTerminators(Function &F) {
|
||||
IsUnreachableCleanupendpad = CEPI->getCleanupPad() != CleanupPad;
|
||||
if (IsUnreachableRet || IsUnreachableCatchret ||
|
||||
IsUnreachableCleanupret || IsUnreachableCleanupendpad) {
|
||||
// Loop through all of our successors and make sure they know that one
|
||||
// of their predecessors is going away.
|
||||
for (BasicBlock *SuccBB : TI->successors())
|
||||
SuccBB->removePredecessor(BB);
|
||||
|
||||
new UnreachableInst(BB->getContext(), TI);
|
||||
TI->eraseFromParent();
|
||||
}
|
||||
@ -3460,11 +3548,13 @@ void WinEHPrepare::verifyPreparedFunclets(Function &F) {
|
||||
report_fatal_error("Uncolored BB!");
|
||||
if (NumColors > 1)
|
||||
report_fatal_error("Multicolor BB!");
|
||||
if (!DisableDemotion) {
|
||||
bool EHPadHasPHI = BB.isEHPad() && isa<PHINode>(BB.begin());
|
||||
assert(!EHPadHasPHI && "EH Pad still has a PHI!");
|
||||
if (EHPadHasPHI)
|
||||
report_fatal_error("EH Pad still has a PHI!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool WinEHPrepare::prepareExplicitEH(
|
||||
@ -3477,17 +3567,21 @@ bool WinEHPrepare::prepareExplicitEH(
|
||||
// Determine which blocks are reachable from which funclet entries.
|
||||
colorFunclets(F, EntryBlocks);
|
||||
|
||||
if (!DisableDemotion) {
|
||||
demotePHIsOnFunclets(F);
|
||||
|
||||
demoteUsesBetweenFunclets(F);
|
||||
|
||||
demoteArgumentUses(F);
|
||||
}
|
||||
|
||||
cloneCommonBlocks(F, EntryBlocks);
|
||||
|
||||
if (!DisableCleanups) {
|
||||
removeImplausibleTerminators(F);
|
||||
|
||||
cleanupPreparedFunclets(F);
|
||||
}
|
||||
|
||||
verifyPreparedFunclets(F);
|
||||
|
||||
|
87
test/CodeGen/WinEH/wineh-no-demotion.ll
Normal file
87
test/CodeGen/WinEH/wineh-no-demotion.ll
Normal file
@ -0,0 +1,87 @@
|
||||
; RUN: opt -mtriple=x86_x64-pc-windows-msvc -S -winehprepare -disable-demotion < %s | FileCheck %s
|
||||
|
||||
declare i32 @__CxxFrameHandler3(...)
|
||||
|
||||
declare void @f()
|
||||
|
||||
declare i32 @g()
|
||||
|
||||
declare void @h(i32)
|
||||
|
||||
; CHECK-LABEL: @test1(
|
||||
define void @test1() personality i32 (...)* @__CxxFrameHandler3 {
|
||||
entry:
|
||||
invoke void @f()
|
||||
to label %invoke.cont1 unwind label %left
|
||||
|
||||
invoke.cont1:
|
||||
invoke void @f()
|
||||
to label %invoke.cont2 unwind label %right
|
||||
|
||||
invoke.cont2:
|
||||
invoke void @f()
|
||||
to label %exit unwind label %inner
|
||||
|
||||
left:
|
||||
%0 = cleanuppad []
|
||||
br label %shared
|
||||
|
||||
right:
|
||||
%1 = cleanuppad []
|
||||
br label %shared
|
||||
|
||||
shared:
|
||||
%x = call i32 @g()
|
||||
invoke void @f()
|
||||
to label %shared.cont unwind label %inner
|
||||
|
||||
shared.cont:
|
||||
unreachable
|
||||
|
||||
inner:
|
||||
; CHECK: %phi = phi i32 [ %x, %right ], [ 0, %invoke.cont2 ], [ %x.for.left, %left ]
|
||||
%phi = phi i32 [ %x, %shared ], [ 0, %invoke.cont2 ]
|
||||
%i = cleanuppad []
|
||||
call void @h(i32 %phi)
|
||||
unreachable
|
||||
|
||||
exit:
|
||||
unreachable
|
||||
}
|
||||
|
||||
; CHECK-LABEL: @test2(
|
||||
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:
|
||||
cleanuppad []
|
||||
br label %shared
|
||||
|
||||
shared:
|
||||
%x = call i32 @g()
|
||||
invoke void @f()
|
||||
to label %shared.cont unwind label %inner
|
||||
|
||||
shared.cont:
|
||||
unreachable
|
||||
|
||||
inner:
|
||||
; CHECK: %x1 = phi i32 [ %x.for.left, %left ], [ %x, %right ]
|
||||
; CHECK: call void @h(i32 %x1)
|
||||
%i = cleanuppad []
|
||||
call void @h(i32 %x)
|
||||
unreachable
|
||||
|
||||
exit:
|
||||
unreachable
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user