diff options
Diffstat (limited to 'lib/Transforms/Scalar/GuardWidening.cpp')
-rw-r--r-- | lib/Transforms/Scalar/GuardWidening.cpp | 212 |
1 files changed, 127 insertions, 85 deletions
diff --git a/lib/Transforms/Scalar/GuardWidening.cpp b/lib/Transforms/Scalar/GuardWidening.cpp index efc204d4f74b..e14f44bb7069 100644 --- a/lib/Transforms/Scalar/GuardWidening.cpp +++ b/lib/Transforms/Scalar/GuardWidening.cpp @@ -1,9 +1,8 @@ //===- GuardWidening.cpp - ---- Guard widening ----------------------------===// // -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // @@ -83,6 +82,11 @@ static cl::opt<unsigned> FrequentBranchThreshold( "it is considered frequently taken"), cl::init(1000)); +static cl::opt<bool> + WidenBranchGuards("guard-widening-widen-branch-guards", cl::Hidden, + cl::desc("Whether or not we should widen guards " + "expressed as branches by widenable conditions"), + cl::init(true)); namespace { @@ -93,6 +97,10 @@ static Value *getCondition(Instruction *I) { "Bad guard intrinsic?"); return GI->getArgOperand(0); } + if (isGuardAsWidenableBranch(I)) { + auto *Cond = cast<BranchInst>(I)->getCondition(); + return cast<BinaryOperator>(Cond)->getOperand(0); + } return cast<BranchInst>(I)->getCondition(); } @@ -133,12 +141,12 @@ class GuardWideningImpl { /// guards. DenseSet<Instruction *> WidenedGuards; - /// Try to eliminate guard \p Guard by widening it into an earlier dominating - /// guard. \p DFSI is the DFS iterator on the dominator tree that is - /// currently visiting the block containing \p Guard, and \p GuardsPerBlock + /// Try to eliminate instruction \p Instr by widening it into an earlier + /// dominating guard. \p DFSI is the DFS iterator on the dominator tree that + /// is currently visiting the block containing \p Guard, and \p GuardsPerBlock /// maps BasicBlocks to the set of guards seen in that block. - bool eliminateGuardViaWidening( - Instruction *Guard, const df_iterator<DomTreeNode *> &DFSI, + bool eliminateInstrViaWidening( + Instruction *Instr, const df_iterator<DomTreeNode *> &DFSI, const DenseMap<BasicBlock *, SmallVector<Instruction *, 8>> & GuardsPerBlock, bool InvertCondition = false); @@ -162,28 +170,25 @@ class GuardWideningImpl { static StringRef scoreTypeToString(WideningScore WS); - /// Compute the score for widening the condition in \p DominatedGuard - /// (contained in \p DominatedGuardLoop) into \p DominatingGuard (contained in - /// \p DominatingGuardLoop). If \p InvertCond is set, then we widen the + /// Compute the score for widening the condition in \p DominatedInstr + /// into \p DominatingGuard. If \p InvertCond is set, then we widen the /// inverted condition of the dominating guard. - WideningScore computeWideningScore(Instruction *DominatedGuard, - Loop *DominatedGuardLoop, + WideningScore computeWideningScore(Instruction *DominatedInstr, Instruction *DominatingGuard, - Loop *DominatingGuardLoop, bool InvertCond); /// Helper to check if \p V can be hoisted to \p InsertPos. - bool isAvailableAt(Value *V, Instruction *InsertPos) { - SmallPtrSet<Instruction *, 8> Visited; + bool isAvailableAt(const Value *V, const Instruction *InsertPos) const { + SmallPtrSet<const Instruction *, 8> Visited; return isAvailableAt(V, InsertPos, Visited); } - bool isAvailableAt(Value *V, Instruction *InsertPos, - SmallPtrSetImpl<Instruction *> &Visited); + bool isAvailableAt(const Value *V, const Instruction *InsertPos, + SmallPtrSetImpl<const Instruction *> &Visited) const; /// Helper to hoist \p V to \p InsertPos. Guaranteed to succeed if \c /// isAvailableAt returned true. - void makeAvailableAt(Value *V, Instruction *InsertPos); + void makeAvailableAt(Value *V, Instruction *InsertPos) const; /// Common helper used by \c widenGuard and \c isWideningCondProfitable. Try /// to generate an expression computing the logical AND of \p Cond0 and (\p @@ -200,23 +205,23 @@ class GuardWideningImpl { /// pre-existing instruction in the IR that computes the result of this range /// check. class RangeCheck { - Value *Base; - ConstantInt *Offset; - Value *Length; + const Value *Base; + const ConstantInt *Offset; + const Value *Length; ICmpInst *CheckInst; public: - explicit RangeCheck(Value *Base, ConstantInt *Offset, Value *Length, - ICmpInst *CheckInst) + explicit RangeCheck(const Value *Base, const ConstantInt *Offset, + const Value *Length, ICmpInst *CheckInst) : Base(Base), Offset(Offset), Length(Length), CheckInst(CheckInst) {} - void setBase(Value *NewBase) { Base = NewBase; } - void setOffset(ConstantInt *NewOffset) { Offset = NewOffset; } + void setBase(const Value *NewBase) { Base = NewBase; } + void setOffset(const ConstantInt *NewOffset) { Offset = NewOffset; } - Value *getBase() const { return Base; } - ConstantInt *getOffset() const { return Offset; } + const Value *getBase() const { return Base; } + const ConstantInt *getOffset() const { return Offset; } const APInt &getOffsetValue() const { return getOffset()->getValue(); } - Value *getLength() const { return Length; }; + const Value *getLength() const { return Length; }; ICmpInst *getCheckInst() const { return CheckInst; } void print(raw_ostream &OS, bool PrintTypes = false) { @@ -238,19 +243,19 @@ class GuardWideningImpl { /// append them to \p Checks. Returns true on success, may clobber \c Checks /// on failure. bool parseRangeChecks(Value *CheckCond, SmallVectorImpl<RangeCheck> &Checks) { - SmallPtrSet<Value *, 8> Visited; + SmallPtrSet<const Value *, 8> Visited; return parseRangeChecks(CheckCond, Checks, Visited); } bool parseRangeChecks(Value *CheckCond, SmallVectorImpl<RangeCheck> &Checks, - SmallPtrSetImpl<Value *> &Visited); + SmallPtrSetImpl<const Value *> &Visited); /// Combine the checks in \p Checks into a smaller set of checks and append /// them into \p CombinedChecks. Return true on success (i.e. all of checks /// in \p Checks were combined into \p CombinedChecks). Clobbers \p Checks /// and \p CombinedChecks on success and on failure. bool combineRangeChecks(SmallVectorImpl<RangeCheck> &Checks, - SmallVectorImpl<RangeCheck> &CombinedChecks); + SmallVectorImpl<RangeCheck> &CombinedChecks) const; /// Can we compute the logical AND of \p Cond0 and \p Cond1 for the price of /// computing only one of the two expressions? @@ -266,8 +271,16 @@ class GuardWideningImpl { void widenGuard(Instruction *ToWiden, Value *NewCondition, bool InvertCondition) { Value *Result; - widenCondCommon(ToWiden->getOperand(0), NewCondition, ToWiden, Result, + widenCondCommon(getCondition(ToWiden), NewCondition, ToWiden, Result, InvertCondition); + Value *WidenableCondition = nullptr; + if (isGuardAsWidenableBranch(ToWiden)) { + auto *Cond = cast<BranchInst>(ToWiden)->getCondition(); + WidenableCondition = cast<BinaryOperator>(Cond)->getOperand(1); + } + if (WidenableCondition) + Result = BinaryOperator::CreateAnd(Result, WidenableCondition, + "guard.chk", ToWiden); setCondition(ToWiden, Result); } @@ -285,6 +298,14 @@ public: }; } +static bool isSupportedGuardInstruction(const Instruction *Insn) { + if (isGuard(Insn)) + return true; + if (WidenBranchGuards && isGuardAsWidenableBranch(Insn)) + return true; + return false; +} + bool GuardWideningImpl::run() { DenseMap<BasicBlock *, SmallVector<Instruction *, 8>> GuardsInBlock; bool Changed = false; @@ -304,20 +325,20 @@ bool GuardWideningImpl::run() { auto &CurrentList = GuardsInBlock[BB]; for (auto &I : *BB) - if (isGuard(&I)) + if (isSupportedGuardInstruction(&I)) CurrentList.push_back(cast<Instruction>(&I)); for (auto *II : CurrentList) - Changed |= eliminateGuardViaWidening(II, DFI, GuardsInBlock); + Changed |= eliminateInstrViaWidening(II, DFI, GuardsInBlock); if (WidenFrequentBranches && BPI) if (auto *BI = dyn_cast<BranchInst>(BB->getTerminator())) if (BI->isConditional()) { // If one of branches of a conditional is likely taken, try to // eliminate it. if (BPI->getEdgeProbability(BB, 0U) >= *LikelyTaken) - Changed |= eliminateGuardViaWidening(BI, DFI, GuardsInBlock); + Changed |= eliminateInstrViaWidening(BI, DFI, GuardsInBlock); else if (BPI->getEdgeProbability(BB, 1U) >= *LikelyTaken) - Changed |= eliminateGuardViaWidening(BI, DFI, GuardsInBlock, + Changed |= eliminateInstrViaWidening(BI, DFI, GuardsInBlock, /*InvertCondition*/true); } } @@ -326,7 +347,7 @@ bool GuardWideningImpl::run() { for (auto *I : EliminatedGuardsAndBranches) if (!WidenedGuards.count(I)) { assert(isa<ConstantInt>(getCondition(I)) && "Should be!"); - if (isGuard(I)) + if (isSupportedGuardInstruction(I)) eliminateGuard(I); else { assert(isa<BranchInst>(I) && @@ -338,19 +359,18 @@ bool GuardWideningImpl::run() { return Changed; } -bool GuardWideningImpl::eliminateGuardViaWidening( - Instruction *GuardInst, const df_iterator<DomTreeNode *> &DFSI, +bool GuardWideningImpl::eliminateInstrViaWidening( + Instruction *Instr, const df_iterator<DomTreeNode *> &DFSI, const DenseMap<BasicBlock *, SmallVector<Instruction *, 8>> & GuardsInBlock, bool InvertCondition) { // Ignore trivial true or false conditions. These instructions will be // trivially eliminated by any cleanup pass. Do not erase them because other // guards can possibly be widened into them. - if (isa<ConstantInt>(getCondition(GuardInst))) + if (isa<ConstantInt>(getCondition(Instr))) return false; Instruction *BestSoFar = nullptr; auto BestScoreSoFar = WS_IllegalOrNegative; - auto *GuardInstLoop = LI.getLoopFor(GuardInst->getParent()); // In the set of dominating guards, find the one we can merge GuardInst with // for the most profit. @@ -358,12 +378,13 @@ bool GuardWideningImpl::eliminateGuardViaWidening( auto *CurBB = DFSI.getPath(i)->getBlock(); if (!BlockFilter(CurBB)) break; - auto *CurLoop = LI.getLoopFor(CurBB); assert(GuardsInBlock.count(CurBB) && "Must have been populated by now!"); const auto &GuardsInCurBB = GuardsInBlock.find(CurBB)->second; auto I = GuardsInCurBB.begin(); - auto E = GuardsInCurBB.end(); + auto E = Instr->getParent() == CurBB + ? std::find(GuardsInCurBB.begin(), GuardsInCurBB.end(), Instr) + : GuardsInCurBB.end(); #ifndef NDEBUG { @@ -379,21 +400,11 @@ bool GuardWideningImpl::eliminateGuardViaWidening( } #endif - assert((i == (e - 1)) == (GuardInst->getParent() == CurBB) && "Bad DFS?"); - - if (i == (e - 1) && CurBB->getTerminator() != GuardInst) { - // Corner case: make sure we're only looking at guards strictly dominating - // GuardInst when visiting GuardInst->getParent(). - auto NewEnd = std::find(I, E, GuardInst); - assert(NewEnd != E && "GuardInst not in its own block?"); - E = NewEnd; - } + assert((i == (e - 1)) == (Instr->getParent() == CurBB) && "Bad DFS?"); for (auto *Candidate : make_range(I, E)) { - auto Score = - computeWideningScore(GuardInst, GuardInstLoop, Candidate, CurLoop, - InvertCondition); - LLVM_DEBUG(dbgs() << "Score between " << *getCondition(GuardInst) + auto Score = computeWideningScore(Instr, Candidate, InvertCondition); + LLVM_DEBUG(dbgs() << "Score between " << *getCondition(Instr) << " and " << *getCondition(Candidate) << " is " << scoreTypeToString(Score) << "\n"); if (Score > BestScoreSoFar) { @@ -404,42 +415,45 @@ bool GuardWideningImpl::eliminateGuardViaWidening( } if (BestScoreSoFar == WS_IllegalOrNegative) { - LLVM_DEBUG(dbgs() << "Did not eliminate guard " << *GuardInst << "\n"); + LLVM_DEBUG(dbgs() << "Did not eliminate guard " << *Instr << "\n"); return false; } - assert(BestSoFar != GuardInst && "Should have never visited same guard!"); - assert(DT.dominates(BestSoFar, GuardInst) && "Should be!"); + assert(BestSoFar != Instr && "Should have never visited same guard!"); + assert(DT.dominates(BestSoFar, Instr) && "Should be!"); - LLVM_DEBUG(dbgs() << "Widening " << *GuardInst << " into " << *BestSoFar + LLVM_DEBUG(dbgs() << "Widening " << *Instr << " into " << *BestSoFar << " with score " << scoreTypeToString(BestScoreSoFar) << "\n"); - widenGuard(BestSoFar, getCondition(GuardInst), InvertCondition); + widenGuard(BestSoFar, getCondition(Instr), InvertCondition); auto NewGuardCondition = InvertCondition - ? ConstantInt::getFalse(GuardInst->getContext()) - : ConstantInt::getTrue(GuardInst->getContext()); - setCondition(GuardInst, NewGuardCondition); - EliminatedGuardsAndBranches.push_back(GuardInst); + ? ConstantInt::getFalse(Instr->getContext()) + : ConstantInt::getTrue(Instr->getContext()); + setCondition(Instr, NewGuardCondition); + EliminatedGuardsAndBranches.push_back(Instr); WidenedGuards.insert(BestSoFar); return true; } -GuardWideningImpl::WideningScore GuardWideningImpl::computeWideningScore( - Instruction *DominatedGuard, Loop *DominatedGuardLoop, - Instruction *DominatingGuard, Loop *DominatingGuardLoop, bool InvertCond) { +GuardWideningImpl::WideningScore +GuardWideningImpl::computeWideningScore(Instruction *DominatedInstr, + Instruction *DominatingGuard, + bool InvertCond) { + Loop *DominatedInstrLoop = LI.getLoopFor(DominatedInstr->getParent()); + Loop *DominatingGuardLoop = LI.getLoopFor(DominatingGuard->getParent()); bool HoistingOutOfLoop = false; - if (DominatingGuardLoop != DominatedGuardLoop) { + if (DominatingGuardLoop != DominatedInstrLoop) { // Be conservative and don't widen into a sibling loop. TODO: If the // sibling is colder, we should consider allowing this. if (DominatingGuardLoop && - !DominatingGuardLoop->contains(DominatedGuardLoop)) + !DominatingGuardLoop->contains(DominatedInstrLoop)) return WS_IllegalOrNegative; HoistingOutOfLoop = true; } - if (!isAvailableAt(getCondition(DominatedGuard), DominatingGuard)) + if (!isAvailableAt(getCondition(DominatedInstr), DominatingGuard)) return WS_IllegalOrNegative; // If the guard was conditional executed, it may never be reached @@ -450,7 +464,7 @@ GuardWideningImpl::WideningScore GuardWideningImpl::computeWideningScore( // here. TODO: evaluate cost model for spurious deopt // NOTE: As written, this also lets us hoist right over another guard which // is essentially just another spelling for control flow. - if (isWideningCondProfitable(getCondition(DominatedGuard), + if (isWideningCondProfitable(getCondition(DominatedInstr), getCondition(DominatingGuard), InvertCond)) return HoistingOutOfLoop ? WS_VeryPositive : WS_Positive; @@ -462,7 +476,9 @@ GuardWideningImpl::WideningScore GuardWideningImpl::computeWideningScore( // throw, etc...). That choice appears arbitrary. auto MaybeHoistingOutOfIf = [&]() { auto *DominatingBlock = DominatingGuard->getParent(); - auto *DominatedBlock = DominatedGuard->getParent(); + auto *DominatedBlock = DominatedInstr->getParent(); + if (isGuardAsWidenableBranch(DominatingGuard)) + DominatingBlock = cast<BranchInst>(DominatingGuard)->getSuccessor(0); // Same Block? if (DominatedBlock == DominatingBlock) @@ -478,8 +494,9 @@ GuardWideningImpl::WideningScore GuardWideningImpl::computeWideningScore( return MaybeHoistingOutOfIf() ? WS_IllegalOrNegative : WS_Neutral; } -bool GuardWideningImpl::isAvailableAt(Value *V, Instruction *Loc, - SmallPtrSetImpl<Instruction *> &Visited) { +bool GuardWideningImpl::isAvailableAt( + const Value *V, const Instruction *Loc, + SmallPtrSetImpl<const Instruction *> &Visited) const { auto *Inst = dyn_cast<Instruction>(V); if (!Inst || DT.dominates(Inst, Loc) || Visited.count(Inst)) return true; @@ -499,7 +516,7 @@ bool GuardWideningImpl::isAvailableAt(Value *V, Instruction *Loc, [&](Value *Op) { return isAvailableAt(Op, Loc, Visited); }); } -void GuardWideningImpl::makeAvailableAt(Value *V, Instruction *Loc) { +void GuardWideningImpl::makeAvailableAt(Value *V, Instruction *Loc) const { auto *Inst = dyn_cast<Instruction>(V); if (!Inst || DT.dominates(Inst, Loc)) return; @@ -597,7 +614,7 @@ bool GuardWideningImpl::widenCondCommon(Value *Cond0, Value *Cond1, bool GuardWideningImpl::parseRangeChecks( Value *CheckCond, SmallVectorImpl<GuardWideningImpl::RangeCheck> &Checks, - SmallPtrSetImpl<Value *> &Visited) { + SmallPtrSetImpl<const Value *> &Visited) { if (!Visited.insert(CheckCond).second) return true; @@ -616,7 +633,7 @@ bool GuardWideningImpl::parseRangeChecks( IC->getPredicate() != ICmpInst::ICMP_UGT)) return false; - Value *CmpLHS = IC->getOperand(0), *CmpRHS = IC->getOperand(1); + const Value *CmpLHS = IC->getOperand(0), *CmpRHS = IC->getOperand(1); if (IC->getPredicate() == ICmpInst::ICMP_UGT) std::swap(CmpLHS, CmpRHS); @@ -669,13 +686,13 @@ bool GuardWideningImpl::parseRangeChecks( bool GuardWideningImpl::combineRangeChecks( SmallVectorImpl<GuardWideningImpl::RangeCheck> &Checks, - SmallVectorImpl<GuardWideningImpl::RangeCheck> &RangeChecksOut) { + SmallVectorImpl<GuardWideningImpl::RangeCheck> &RangeChecksOut) const { unsigned OldCount = Checks.size(); while (!Checks.empty()) { // Pick all of the range checks with a specific base and length, and try to // merge them. - Value *CurrentBase = Checks.front().getBase(); - Value *CurrentLength = Checks.front().getLength(); + const Value *CurrentBase = Checks.front().getBase(); + const Value *CurrentLength = Checks.front().getLength(); SmallVector<GuardWideningImpl::RangeCheck, 3> CurrentChecks; @@ -704,8 +721,8 @@ bool GuardWideningImpl::combineRangeChecks( // Note: std::sort should not invalidate the ChecksStart iterator. - ConstantInt *MinOffset = CurrentChecks.front().getOffset(), - *MaxOffset = CurrentChecks.back().getOffset(); + const ConstantInt *MinOffset = CurrentChecks.front().getOffset(); + const ConstantInt *MaxOffset = CurrentChecks.back().getOffset(); unsigned BitWidth = MaxOffset->getValue().getBitWidth(); if ((MaxOffset->getValue() - MinOffset->getValue()) @@ -800,6 +817,31 @@ PreservedAnalyses GuardWideningPass::run(Function &F, return PA; } +PreservedAnalyses GuardWideningPass::run(Loop &L, LoopAnalysisManager &AM, + LoopStandardAnalysisResults &AR, + LPMUpdater &U) { + + const auto &FAM = + AM.getResult<FunctionAnalysisManagerLoopProxy>(L, AR).getManager(); + Function &F = *L.getHeader()->getParent(); + BranchProbabilityInfo *BPI = nullptr; + if (WidenFrequentBranches) + BPI = FAM.getCachedResult<BranchProbabilityAnalysis>(F); + + BasicBlock *RootBB = L.getLoopPredecessor(); + if (!RootBB) + RootBB = L.getHeader(); + auto BlockFilter = [&](BasicBlock *BB) { + return BB == RootBB || L.contains(BB); + }; + if (!GuardWideningImpl(AR.DT, nullptr, AR.LI, BPI, + AR.DT.getNode(RootBB), + BlockFilter).run()) + return PreservedAnalyses::all(); + + return getLoopPassPreservedAnalyses(); +} + namespace { struct GuardWideningLegacyPass : public FunctionPass { static char ID; |