[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:
David Majnemer 2015-09-16 18:40:37 +00:00
parent 6badbab39a
commit ad53a65179
2 changed files with 190 additions and 9 deletions

View File

@ -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);

View 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
}