[LICM] Make instruction sinking funclet-aware

We had two bugs here:
- We might try to sink into a catchswitch, causing verifier failures.
- We will succeed in sinking into a cleanuppad but we didn't update the
  funclet operand bundle.

This fixes PR26000.

llvm-svn: 256728
This commit is contained in:
David Majnemer 2016-01-04 03:37:39 +00:00
parent 5243e190a7
commit 42a0730c42
4 changed files with 147 additions and 18 deletions

View File

@ -16,6 +16,7 @@
#include "llvm/ADT/SmallVector.h" #include "llvm/ADT/SmallVector.h"
#include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/IR/Dominators.h" #include "llvm/IR/Dominators.h"
#include "llvm/IR/IRBuilder.h" #include "llvm/IR/IRBuilder.h"
@ -39,6 +40,8 @@ struct LICMSafetyInfo {
bool MayThrow; // The current loop contains an instruction which bool MayThrow; // The current loop contains an instruction which
// may throw. // may throw.
bool HeaderMayThrow; // Same as previous, but specific to loop header bool HeaderMayThrow; // Same as previous, but specific to loop header
// Used to update funclet bundle operands.
DenseMap<BasicBlock *, ColorVector> BlockColors;
LICMSafetyInfo() : MayThrow(false), HeaderMayThrow(false) LICMSafetyInfo() : MayThrow(false), HeaderMayThrow(false)
{} {}
}; };

View File

@ -75,10 +75,12 @@ DisablePromotion("disable-licm-promotion", cl::Hidden,
cl::desc("Disable memory promotion in LICM pass")); cl::desc("Disable memory promotion in LICM pass"));
static bool inSubLoop(BasicBlock *BB, Loop *CurLoop, LoopInfo *LI); static bool inSubLoop(BasicBlock *BB, Loop *CurLoop, LoopInfo *LI);
static bool isNotUsedInLoop(const Instruction &I, const Loop *CurLoop); static bool isNotUsedInLoop(const Instruction &I, const Loop *CurLoop,
const LICMSafetyInfo *SafetyInfo);
static bool hoist(Instruction &I, BasicBlock *Preheader); static bool hoist(Instruction &I, BasicBlock *Preheader);
static bool sink(Instruction &I, const LoopInfo *LI, const DominatorTree *DT, static bool sink(Instruction &I, const LoopInfo *LI, const DominatorTree *DT,
const Loop *CurLoop, AliasSetTracker *CurAST ); const Loop *CurLoop, AliasSetTracker *CurAST,
const LICMSafetyInfo *SafetyInfo);
static bool isGuaranteedToExecute(const Instruction &Inst, static bool isGuaranteedToExecute(const Instruction &Inst,
const DominatorTree *DT, const DominatorTree *DT,
const Loop *CurLoop, const Loop *CurLoop,
@ -92,10 +94,10 @@ static bool isSafeToExecuteUnconditionally(const Instruction &Inst,
static bool pointerInvalidatedByLoop(Value *V, uint64_t Size, static bool pointerInvalidatedByLoop(Value *V, uint64_t Size,
const AAMDNodes &AAInfo, const AAMDNodes &AAInfo,
AliasSetTracker *CurAST); AliasSetTracker *CurAST);
static Instruction *CloneInstructionInExitBlock(const Instruction &I, static Instruction *
BasicBlock &ExitBlock, CloneInstructionInExitBlock(Instruction &I, BasicBlock &ExitBlock, PHINode &PN,
PHINode &PN, const LoopInfo *LI,
const LoopInfo *LI); const LICMSafetyInfo *SafetyInfo);
static bool canSinkOrHoistInst(Instruction &I, AliasAnalysis *AA, static bool canSinkOrHoistInst(Instruction &I, AliasAnalysis *AA,
DominatorTree *DT, TargetLibraryInfo *TLI, DominatorTree *DT, TargetLibraryInfo *TLI,
Loop *CurLoop, AliasSetTracker *CurAST, Loop *CurLoop, AliasSetTracker *CurAST,
@ -348,10 +350,10 @@ bool llvm::sinkRegion(DomTreeNode *N, AliasAnalysis *AA, LoopInfo *LI,
// outside of the loop. In this case, it doesn't even matter if the // outside of the loop. In this case, it doesn't even matter if the
// operands of the instruction are loop invariant. // operands of the instruction are loop invariant.
// //
if (isNotUsedInLoop(I, CurLoop) && if (isNotUsedInLoop(I, CurLoop, SafetyInfo) &&
canSinkOrHoistInst(I, AA, DT, TLI, CurLoop, CurAST, SafetyInfo)) { canSinkOrHoistInst(I, AA, DT, TLI, CurLoop, CurAST, SafetyInfo)) {
++II; ++II;
Changed |= sink(I, LI, DT, CurLoop, CurAST); Changed |= sink(I, LI, DT, CurLoop, CurAST, SafetyInfo);
} }
} }
return Changed; return Changed;
@ -432,6 +434,14 @@ void llvm::computeLICMSafetyInfo(LICMSafetyInfo * SafetyInfo, Loop * CurLoop) {
for (BasicBlock::iterator I = (*BB)->begin(), E = (*BB)->end(); for (BasicBlock::iterator I = (*BB)->begin(), E = (*BB)->end();
(I != E) && !SafetyInfo->MayThrow; ++I) (I != E) && !SafetyInfo->MayThrow; ++I)
SafetyInfo->MayThrow |= I->mayThrow(); SafetyInfo->MayThrow |= I->mayThrow();
// Compute funclet colors if we might sink/hoist in a function with a funclet
// personality routine.
Function *Fn = CurLoop->getHeader()->getParent();
if (Fn->hasPersonalityFn())
if (Constant *PersonalityFn = Fn->getPersonalityFn())
if (isFuncletEHPersonality(classifyEHPersonality(PersonalityFn)))
SafetyInfo->BlockColors = colorEHFunclets(*Fn);
} }
/// canSinkOrHoistInst - Return true if the hoister and sinker can handle this /// canSinkOrHoistInst - Return true if the hoister and sinker can handle this
@ -466,6 +476,10 @@ bool canSinkOrHoistInst(Instruction &I, AliasAnalysis *AA, DominatorTree *DT,
if (isa<DbgInfoIntrinsic>(I)) if (isa<DbgInfoIntrinsic>(I))
return false; return false;
// Don't sink calls which can throw.
if (CI->mayThrow())
return false;
// Handle simple cases by querying alias analysis. // Handle simple cases by querying alias analysis.
FunctionModRefBehavior Behavior = AA->getModRefBehavior(CI); FunctionModRefBehavior Behavior = AA->getModRefBehavior(CI);
if (Behavior == FMRB_DoesNotAccessMemory) if (Behavior == FMRB_DoesNotAccessMemory)
@ -534,10 +548,24 @@ static bool isTriviallyReplacablePHI(const PHINode &PN, const Instruction &I) {
/// the loop. If this is true, we can sink the instruction to the exit /// the loop. If this is true, we can sink the instruction to the exit
/// blocks of the loop. /// blocks of the loop.
/// ///
static bool isNotUsedInLoop(const Instruction &I, const Loop *CurLoop) { static bool isNotUsedInLoop(const Instruction &I, const Loop *CurLoop,
const LICMSafetyInfo *SafetyInfo) {
const auto &BlockColors = SafetyInfo->BlockColors;
for (const User *U : I.users()) { for (const User *U : I.users()) {
const Instruction *UI = cast<Instruction>(U); const Instruction *UI = cast<Instruction>(U);
if (const PHINode *PN = dyn_cast<PHINode>(UI)) { if (const PHINode *PN = dyn_cast<PHINode>(UI)) {
const BasicBlock *BB = PN->getParent();
// We cannot sink uses in catchswitches.
if (isa<CatchSwitchInst>(BB->getTerminator()))
return false;
// We need to sink a callsite to a unique funclet. Avoid sinking if the
// phi use is too muddled.
if (isa<CallInst>(I))
if (!BlockColors.empty() &&
BlockColors.find(const_cast<BasicBlock *>(BB))->second.size() != 1)
return false;
// A PHI node where all of the incoming values are this instruction are // A PHI node where all of the incoming values are this instruction are
// special -- they can just be RAUW'ed with the instruction and thus // special -- they can just be RAUW'ed with the instruction and thus
// don't require a use in the predecessor. This is a particular important // don't require a use in the predecessor. This is a particular important
@ -565,11 +593,41 @@ static bool isNotUsedInLoop(const Instruction &I, const Loop *CurLoop) {
return true; return true;
} }
static Instruction *CloneInstructionInExitBlock(const Instruction &I, static Instruction *
BasicBlock &ExitBlock, CloneInstructionInExitBlock(Instruction &I, BasicBlock &ExitBlock, PHINode &PN,
PHINode &PN, const LoopInfo *LI,
const LoopInfo *LI) { const LICMSafetyInfo *SafetyInfo) {
Instruction *New = I.clone(); Instruction *New;
if (auto *CI = dyn_cast<CallInst>(&I)) {
const auto &BlockColors = SafetyInfo->BlockColors;
// Sinking call-sites need to be handled differently from other
// instructions. The cloned call-site needs a funclet bundle operand
// appropriate for it's location in the CFG.
SmallVector<OperandBundleDef, 1> OpBundles;
for (unsigned BundleIdx = 0, BundleEnd = CI->getNumOperandBundles();
BundleIdx != BundleEnd; ++BundleIdx) {
OperandBundleUse Bundle = CI->getOperandBundleAt(BundleIdx);
if (Bundle.getTagID() == LLVMContext::OB_funclet)
continue;
OpBundles.emplace_back(Bundle);
}
if (!BlockColors.empty()) {
const ColorVector &CV = BlockColors.find(&ExitBlock)->second;
assert(CV.size() == 1 && "non-unique color for exit block!");
BasicBlock *BBColor = CV.front();
Instruction *EHPad = BBColor->getFirstNonPHI();
if (EHPad->isEHPad())
OpBundles.emplace_back("funclet", EHPad);
}
New = CallInst::Create(CI, OpBundles);
} else {
New = I.clone();
}
ExitBlock.getInstList().insert(ExitBlock.getFirstInsertionPt(), New); ExitBlock.getInstList().insert(ExitBlock.getFirstInsertionPt(), New);
if (!I.getName().empty()) New->setName(I.getName() + ".le"); if (!I.getName().empty()) New->setName(I.getName() + ".le");
@ -601,7 +659,8 @@ static Instruction *CloneInstructionInExitBlock(const Instruction &I,
/// position, and may either delete it or move it to outside of the loop. /// position, and may either delete it or move it to outside of the loop.
/// ///
static bool sink(Instruction &I, const LoopInfo *LI, const DominatorTree *DT, static bool sink(Instruction &I, const LoopInfo *LI, const DominatorTree *DT,
const Loop *CurLoop, AliasSetTracker *CurAST ) { const Loop *CurLoop, AliasSetTracker *CurAST,
const LICMSafetyInfo *SafetyInfo) {
DEBUG(dbgs() << "LICM sinking instruction: " << I << "\n"); DEBUG(dbgs() << "LICM sinking instruction: " << I << "\n");
bool Changed = false; bool Changed = false;
if (isa<LoadInst>(I)) ++NumMovedLoads; if (isa<LoadInst>(I)) ++NumMovedLoads;
@ -652,7 +711,7 @@ static bool sink(Instruction &I, const LoopInfo *LI, const DominatorTree *DT,
New = It->second; New = It->second;
else else
New = SunkCopies[ExitBlock] = New = SunkCopies[ExitBlock] =
CloneInstructionInExitBlock(I, *ExitBlock, *PN, LI); CloneInstructionInExitBlock(I, *ExitBlock, *PN, LI, SafetyInfo);
PN->replaceAllUsesWith(New); PN->replaceAllUsesWith(New);
PN->eraseFromParent(); PN->eraseFromParent();

View File

@ -0,0 +1,67 @@
; RUN: opt < %s -licm -S | FileCheck %s
target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
target triple = "i386-pc-windows-msvc18.0.0"
define void @test1(i32* %s, i1 %b) personality i32 (...)* @__CxxFrameHandler3 {
entry:
br label %while.cond
while.cond: ; preds = %while.body, %entry
%0 = call i32 @pure_computation()
br i1 %b, label %try.cont, label %while.body
while.body: ; preds = %while.cond
invoke void @may_throw()
to label %while.cond unwind label %catch.dispatch
catch.dispatch: ; preds = %while.body
%.lcssa1 = phi i32 [ %0, %while.body ]
%cs = catchswitch within none [label %catch] unwind to caller
catch: ; preds = %catch.dispatch
%cp = catchpad within %cs [i8* null, i32 64, i8* null]
store i32 %.lcssa1, i32* %s
catchret from %cp to label %try.cont
try.cont: ; preds = %catch, %while.cond
ret void
}
; CHECK-LABEL: define void @test1(
; CHECK: %[[CALL:.*]] = call i32 @pure_computation()
; CHECK: phi i32 [ %[[CALL]]
define void @test2(i32* %s, i1 %b) personality i32 (...)* @__CxxFrameHandler3 {
entry:
br label %while.cond
while.cond: ; preds = %while.body, %entry
%0 = call i32 @pure_computation()
br i1 %b, label %try.cont, label %while.body
while.body: ; preds = %while.cond
invoke void @may_throw()
to label %while.cond unwind label %catch.dispatch
catch.dispatch: ; preds = %while.body
%.lcssa1 = phi i32 [ %0, %while.body ]
%cp = cleanuppad within none []
store i32 %.lcssa1, i32* %s
cleanupret from %cp unwind to caller
try.cont: ; preds = %catch, %while.cond
ret void
}
; CHECK-LABEL: define void @test2(
; CHECK: %[[CP:.*]] = cleanuppad within none []
; CHECK-NEXT: %[[CALL:.*]] = call i32 @pure_computation() [ "funclet"(token %[[CP]]) ]
; CHECK-NEXT: store i32 %[[CALL]], i32* %s
; CHECK-NEXT: cleanupret from %[[CP]] unwind to caller
declare void @may_throw()
declare i32 @pure_computation() nounwind argmemonly readonly
declare i32 @__CxxFrameHandler3(...)

View File

@ -1,6 +1,6 @@
; RUN: opt < %s -basicaa -licm -S | FileCheck %s ; RUN: opt < %s -basicaa -licm -S | FileCheck %s
declare i32 @strlen(i8*) readonly declare i32 @strlen(i8*) readonly nounwind
declare void @foo() declare void @foo()
@ -20,7 +20,7 @@ Out: ; preds = %Loop
; CHECK-NEXT: ret i32 %A ; CHECK-NEXT: ret i32 %A
} }
declare double @sin(double) readnone declare double @sin(double) readnone nounwind
; Sink readnone function out of loop with unknown memory behavior. ; Sink readnone function out of loop with unknown memory behavior.
define double @test2(double %X) { define double @test2(double %X) {