Index: include/llvm/Analysis/GuardUtils.h =================================================================== --- include/llvm/Analysis/GuardUtils.h +++ include/llvm/Analysis/GuardUtils.h @@ -15,10 +15,25 @@ namespace llvm { +class BasicBlock; class User; +class Value; -/// Returns true iff \p U has semantics of a guard. -bool isGuard(const User *U); +/// Returns true of we can prove that execution will reach a call of an +/// llvm.experimental.deopt intrinsic and memory will not be modified before +/// that under assumption that we enter the basic block \p BB. +bool alwaysGoesToReExecutableDeoptBlock(const BasicBlock *BB); + +/// If \p V can be represented as (%cond1 & %cond2 & ... & %wc & ...), +/// where %wc is the result of call of llvm.experimental.widenable.condition, +/// return %wc. Otherwise, return nullptr. +const Value *extractWidenableCondition(const Value *V); + +/// Returns true iff \p is a guard represented as intrinsic. +bool isGuardInIntrinsicForm(const User *U); + +/// Returns true iff \p is a guard represented as explicit branch. +bool isGuardInExplicitForm(const User *U); } // llvm Index: lib/Analysis/AliasSetTracker.cpp =================================================================== --- lib/Analysis/AliasSetTracker.cpp +++ lib/Analysis/AliasSetTracker.cpp @@ -173,7 +173,7 @@ // Guards are marked as modifying memory for control flow modelling purposes, // but don't actually modify any specific memory location. using namespace PatternMatch; - bool MayWriteMemory = I->mayWriteToMemory() && !isGuard(I) && + bool MayWriteMemory = I->mayWriteToMemory() && !isGuardInIntrinsicForm(I) && !(I->use_empty() && match(I, m_Intrinsic())); if (!MayWriteMemory) { Alias = SetMayAlias; Index: lib/Analysis/GuardUtils.cpp =================================================================== --- lib/Analysis/GuardUtils.cpp +++ lib/Analysis/GuardUtils.cpp @@ -11,11 +11,82 @@ //===----------------------------------------------------------------------===// #include "llvm/Analysis/GuardUtils.h" +#include "llvm/Analysis/ValueTracking.h" #include "llvm/IR/PatternMatch.h" using namespace llvm; -bool llvm::isGuard(const User *U) { +bool llvm::alwaysGoesToReExecutableDeoptBlock(const BasicBlock *BB) { + SmallPtrSet SeenBlocks; + SeenBlocks.insert(BB); + do { + for (auto &I : *BB) { + // We are done if we have found the deopt call. + using namespace llvm::PatternMatch; + if (match(&I, m_Intrinsic())) + return true; + // If an instruction may write memory, this makes the part between the + // guard and the deoptimization not re-executable. If there is an + // instruction that may not transfer execution to successor, then we may + // not reach the deopt. In either case, conservatively return false. + if (I.mayWriteToMemory() || + !isGuaranteedToTransferExecutionToSuccessor(&I)) + return false; + } + // If there is a single successor, go analyze it. Do not deal with complex + // control flow. + if (auto *Succ = BB->getSingleSuccessor()) + BB = Succ; + // Bail if we found a loop. + } while (SeenBlocks.insert(BB).second); + // Otherwise we are dealing with something that does not always deopt, or + // it is too hard to prove that it does. + return false; +} + +const Value *llvm::extractWidenableCondition(const Value *V) { + SmallPtrSet Visited; + SmallVector WorkList; + Visited.insert(V); + WorkList.push_back(V); + + // Try to find a widenable condition among arguments of AND (recursively). + while (!WorkList.empty()) { + auto *Curr = WorkList.pop_back_val(); + using namespace llvm::PatternMatch; + if (match(Curr, m_Intrinsic())) + return Curr; + const Value *Op1 = nullptr, *Op2 = nullptr; + if (match(Curr, m_And(m_Value(Op1), m_Value(Op2)))) { + if (Visited.insert(Op1).second) + WorkList.push_back(Op1); + if (Visited.insert(Op2).second) + WorkList.push_back(Op2); + } + } + // Nothing found. + return nullptr; +} + +bool llvm::isGuardInIntrinsicForm(const User *U) { using namespace llvm::PatternMatch; return match(U, m_Intrinsic()); } + +bool llvm::isGuardInExplicitForm(const User *U){ + // Otherwise check whether it is a guard expressed in implicit control flow. + // First of all, it should be a conditional branch. + auto *BI = dyn_cast(U); + if (!BI || !BI->isConditional()) + return false; + // Make sure that the condition of this branch contains a widneable condition, + // maybe AND'ed with other operands. + if (!extractWidenableCondition(BI->getCondition())) + return false; + // Make sure that the false branch ends with a deopt which is always executed, + // and that no instruction between the guard and the deopt may change the + // memory state. + if (!alwaysGoesToReExecutableDeoptBlock(BI->getSuccessor(1))) + return false; + return true; +} Index: lib/Analysis/ValueTracking.cpp =================================================================== --- lib/Analysis/ValueTracking.cpp +++ lib/Analysis/ValueTracking.cpp @@ -1903,7 +1903,7 @@ BasicBlockEdge Edge(BI->getParent(), NonNullSuccessor); if (Edge.isSingleEdge() && DT->dominates(Edge, CtxI->getParent())) return true; - } else if (Pred == ICmpInst::ICMP_NE && isGuard(Curr) && + } else if (Pred == ICmpInst::ICMP_NE && isGuardInIntrinsicForm(Curr) && DT->dominates(cast(Curr), CtxI)) { return true; } Index: lib/Transforms/Scalar/EarlyCSE.cpp =================================================================== --- lib/Transforms/Scalar/EarlyCSE.cpp +++ lib/Transforms/Scalar/EarlyCSE.cpp @@ -865,7 +865,7 @@ continue; } - if (isGuard(Inst)) { + if (isGuardInIntrinsicForm(Inst)) { if (auto *CondI = dyn_cast(cast(Inst)->getArgOperand(0))) { if (SimpleValue::canHandle(CondI)) { Index: lib/Transforms/Scalar/ExplicifyGuards.cpp =================================================================== --- lib/Transforms/Scalar/ExplicifyGuards.cpp +++ lib/Transforms/Scalar/ExplicifyGuards.cpp @@ -56,6 +56,7 @@ } static void turnToExplicitForm(CallInst *Guard, Function *DeoptIntrinsic) { + assert(isGuardInIntrinsicForm(Guard) && "Must be!"); // Replace the guard with an explicit branch (just like in GuardWidening). BasicBlock *BB = Guard->getParent(); makeGuardControlFlowExplicit(DeoptIntrinsic, Guard); @@ -74,6 +75,7 @@ B.CreateAnd(ExplicitGuard->getCondition(), WidenableCondition); NewCond->setName("exiplicit_guard_cond"); ExplicitGuard->setCondition(NewCond); + assert(isGuardInExplicitForm(ExplicitGuard) && "Must be!"); Guard->eraseFromParent(); } @@ -87,7 +89,7 @@ SmallVector GuardIntrinsics; for (auto &I : instructions(F)) - if (isGuard(&I)) + if (isGuardInIntrinsicForm(&I)) GuardIntrinsics.push_back(cast(&I)); if (GuardIntrinsics.empty()) Index: lib/Transforms/Scalar/GuardWidening.cpp =================================================================== --- lib/Transforms/Scalar/GuardWidening.cpp +++ lib/Transforms/Scalar/GuardWidening.cpp @@ -304,7 +304,8 @@ auto &CurrentList = GuardsInBlock[BB]; for (auto &I : *BB) - if (isGuard(&I)) + if (isGuardInIntrinsicForm(&I)) + // TODO: Support explicit guards as well. CurrentList.push_back(cast(&I)); for (auto *II : CurrentList) @@ -326,7 +327,7 @@ for (auto *I : EliminatedGuardsAndBranches) if (!WidenedGuards.count(I)) { assert(isa(getCondition(I)) && "Should be!"); - if (isGuard(I)) + if (isGuardInIntrinsicForm(I)) eliminateGuard(I); else { assert(isa(I) && Index: lib/Transforms/Scalar/JumpThreading.cpp =================================================================== --- lib/Transforms/Scalar/JumpThreading.cpp +++ lib/Transforms/Scalar/JumpThreading.cpp @@ -2608,7 +2608,8 @@ if (auto *BI = dyn_cast(Parent->getTerminator())) for (auto &I : *BB) - if (isGuard(&I) && ThreadGuard(BB, cast(&I), BI)) + if (isGuardInIntrinsicForm(&I) && + ThreadGuard(BB, cast(&I), BI)) return true; return false; Index: lib/Transforms/Scalar/LICM.cpp =================================================================== --- lib/Transforms/Scalar/LICM.cpp +++ lib/Transforms/Scalar/LICM.cpp @@ -529,7 +529,7 @@ using namespace PatternMatch; if (((I.use_empty() && match(&I, m_Intrinsic())) || - isGuard(&I)) && + isGuardInIntrinsicForm(&I)) && IsMustExecute && IsMemoryNotModified && CurLoop->hasLoopInvariantOperands(&I)) { hoist(I, DT, CurLoop, SafetyInfo, ORE); Index: lib/Transforms/Scalar/LowerGuardIntrinsic.cpp =================================================================== --- lib/Transforms/Scalar/LowerGuardIntrinsic.cpp +++ lib/Transforms/Scalar/LowerGuardIntrinsic.cpp @@ -50,7 +50,8 @@ SmallVector ToLower; for (auto &I : instructions(F)) - if (isGuard(&I)) + if (isGuardInIntrinsicForm(&I)) + // TODO: Introduce lowering for guards in explicit form as well. ToLower.push_back(cast(&I)); if (ToLower.empty())