From ad53a65179bb143fc0f5c7846277869d6c68034f Mon Sep 17 00:00:00 2001 From: David Majnemer Date: Wed, 16 Sep 2015 18:40:37 +0000 Subject: [PATCH] [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 --- lib/CodeGen/WinEHPrepare.cpp | 112 ++++++++++++++++++++++-- test/CodeGen/WinEH/wineh-no-demotion.ll | 87 ++++++++++++++++++ 2 files changed, 190 insertions(+), 9 deletions(-) create mode 100644 test/CodeGen/WinEH/wineh-no-demotion.ll diff --git a/lib/CodeGen/WinEHPrepare.cpp b/lib/CodeGen/WinEHPrepare.cpp index 044cbe0df20..8ba6f5ef525 100644 --- a/lib/CodeGen/WinEHPrepare.cpp +++ b/lib/CodeGen/WinEHPrepare.cpp @@ -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 using namespace llvm; @@ -48,6 +49,17 @@ using namespace llvm::PatternMatch; #define DEBUG_TYPE "winehprepare" +static cl::opt DisableDemotion( + "disable-demotion", cl::Hidden, + cl::desc( + "Clone multicolor basic blocks but do not demote cross funclet values"), + cl::init(false)); + +static cl::opt 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(&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(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 UsesToRename; + + auto *OldI = dyn_cast(const_cast(VT.first)); + if (!OldI) + continue; + auto *NewI = cast(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(U.getUser()); + BasicBlock *UserBB = UserI->getParent(); + std::set &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,10 +3548,12 @@ void WinEHPrepare::verifyPreparedFunclets(Function &F) { report_fatal_error("Uncolored BB!"); if (NumColors > 1) report_fatal_error("Multicolor BB!"); - bool EHPadHasPHI = BB.isEHPad() && isa(BB.begin()); - assert(!EHPadHasPHI && "EH Pad still has a PHI!"); - if (EHPadHasPHI) - report_fatal_error("EH Pad still has a PHI!"); + if (!DisableDemotion) { + bool EHPadHasPHI = BB.isEHPad() && isa(BB.begin()); + assert(!EHPadHasPHI && "EH Pad still has a PHI!"); + if (EHPadHasPHI) + report_fatal_error("EH Pad still has a PHI!"); + } } } @@ -3477,17 +3567,21 @@ bool WinEHPrepare::prepareExplicitEH( // Determine which blocks are reachable from which funclet entries. colorFunclets(F, EntryBlocks); - demotePHIsOnFunclets(F); + if (!DisableDemotion) { + demotePHIsOnFunclets(F); - demoteUsesBetweenFunclets(F); + demoteUsesBetweenFunclets(F); - demoteArgumentUses(F); + demoteArgumentUses(F); + } cloneCommonBlocks(F, EntryBlocks); - removeImplausibleTerminators(F); + if (!DisableCleanups) { + removeImplausibleTerminators(F); - cleanupPreparedFunclets(F); + cleanupPreparedFunclets(F); + } verifyPreparedFunclets(F); diff --git a/test/CodeGen/WinEH/wineh-no-demotion.ll b/test/CodeGen/WinEH/wineh-no-demotion.ll new file mode 100644 index 00000000000..66857c08ed1 --- /dev/null +++ b/test/CodeGen/WinEH/wineh-no-demotion.ll @@ -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 +} -- 2.34.1