Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -1737,13 +1737,13 @@ bool isAssumedToCauseUB() const { return getAssumed(); } /// Return true if "undefined behavior" is assumed for a specific instruction. - virtual bool isAssumedToCauseUB(Instruction *I) const = 0; + virtual bool isAssumedToCauseUB(const Instruction *I) const = 0; /// Return true if "undefined behavior" is known. bool isKnownToCauseUB() const { return getKnown(); } /// Return true if "undefined behavior" is known for a specific instruction. - virtual bool isKnownToCauseUB(Instruction *I) const = 0; + virtual bool isKnownToCauseUB(const Instruction *I) const = 0; /// Return an IR position, see struct IRPosition. const IRPosition &getIRPosition() const override { return *this; } @@ -1859,10 +1859,10 @@ virtual bool isKnownDead(const BasicBlock *BB) const = 0; /// Returns true if \p I is assumed dead. - virtual bool isAssumedDead(const Instruction *I) const = 0; + virtual bool isAssumedDead(Attributor &A, const Instruction *I) const = 0; /// Returns true if \p I is known dead. - virtual bool isKnownDead(const Instruction *I) const = 0; + virtual bool isKnownDead(Attributor &A, const Instruction *I) const = 0; /// This method is used to check if at least one instruction in a collection /// of instructions is live. Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -268,7 +268,7 @@ "Expected liveness in the presence of instructions!"); for (unsigned u = 0, e = PHI->getNumIncomingValues(); u < e; u++) { const BasicBlock *IncomingBB = PHI->getIncomingBlock(u); - if (LivenessAA->isAssumedDead(IncomingBB->getTerminator())) { + if (LivenessAA->isAssumedDead(A, IncomingBB->getTerminator())) { AnyDead = true; continue; } @@ -1976,6 +1976,11 @@ struct AAUndefinedBehaviorImpl : public AAUndefinedBehavior { AAUndefinedBehaviorImpl(const IRPosition &IRP) : AAUndefinedBehavior(IRP) {} + void initialize(Attributor &A) override { + AAUndefinedBehavior::initialize(A); + Explorer = &(A.getInfoCache().getMustBeExecutedContextExplorer()); + } + /// See AbstractAttribute::updateImpl(...). // through a pointer (i.e. also branches etc.) ChangeStatus updateImpl(Attributor &A) override { @@ -2060,11 +2065,15 @@ return ChangeStatus::UNCHANGED; } - bool isKnownToCauseUB(Instruction *I) const override { - return KnownUBInsts.count(I); + bool isKnownToCauseUB(const Instruction *I) const override { + for (auto It : Explorer->range(I)) { + if (KnownUBInsts.count(It)) + return true; + } + return false; } - bool isAssumedToCauseUB(Instruction *I) const override { + bool isAssumedToCauseUB(const Instruction *I) const override { // In simple words, if an instruction is not in the assumed to _not_ // cause UB, then it is assumed UB (that includes those // in the KnownUBInsts set). The rest is boilerplate @@ -2130,6 +2139,7 @@ SmallPtrSet KnownUBInsts; private: + MustBeExecutedContextExplorer *Explorer; /// A set of all the (live) instructions that are assumed to _not_ cause UB. SmallPtrSet AssumedNoUBInsts; @@ -2515,12 +2525,12 @@ bool isKnownDead(const BasicBlock *BB) const override { return false; } /// See AAIsDead::isAssumedDead(Instruction *I). - bool isAssumedDead(const Instruction *I) const override { + bool isAssumedDead(Attributor &A, const Instruction *I) const override { return I == getCtxI() && isAssumedDead(); } /// See AAIsDead::isKnownDead(Instruction *I). - bool isKnownDead(const Instruction *I) const override { + bool isKnownDead(Attributor &A, const Instruction *I) const override { return I == getCtxI() && getKnown(); } @@ -2703,8 +2713,14 @@ void initialize(Attributor &A) override { const Function *F = getAssociatedFunction(); if (F && !F->isDeclaration()) { - ToBeExploredFrom.insert(&F->getEntryBlock().front()); - assumeLive(A, F->getEntryBlock()); + const BasicBlock &EntryBlock = F->getEntryBlock(); + const Instruction &Front = EntryBlock.front(); + const auto &AAUB = + A.getAAFor(*this, IRPosition::function(*F)); + if (!AAUB.isKnownToCauseUB(&Front)) { + ToBeExploredFrom.insert(&Front); + assumeLive(A, EntryBlock); + } } } @@ -2863,7 +2879,7 @@ } /// See AAIsDead::isAssumed(Instruction *I). - bool isAssumedDead(const Instruction *I) const override { + bool isAssumedDead(Attributor &A, const Instruction *I) const override { assert(I->getParent()->getParent() == getAssociatedFunction() && "Instruction must be in the same anchor scope function."); @@ -2886,8 +2902,8 @@ } /// See AAIsDead::isKnownDead(Instruction *I). - bool isKnownDead(const Instruction *I) const override { - return getKnown() && isAssumedDead(I); + bool isKnownDead(Attributor &A, const Instruction *I) const override { + return getKnown() && isAssumedDead(A, I); } /// Determine if \p F might catch asynchronous exceptions. @@ -2982,9 +2998,10 @@ identifyAliveSuccessors(Attributor &A, const BranchInst &BI, AbstractAttribute &AA, SmallVectorImpl &AliveSuccessors) { + SmallVector ToBePushed; bool UsedAssumedInformation = false; if (BI.getNumSuccessors() == 1) { - AliveSuccessors.push_back(&BI.getSuccessor(0)->front()); + ToBePushed.push_back(&BI.getSuccessor(0)->front()); } else { Optional CI = getAssumedConstant(A, *BI.getCondition(), AA, UsedAssumedInformation); @@ -2993,13 +3010,25 @@ } else if (CI.getValue()) { const BasicBlock *SuccBB = BI.getSuccessor(1 - CI.getValue()->getZExtValue()); - AliveSuccessors.push_back(&SuccBB->front()); + ToBePushed.push_back(&SuccBB->front()); } else { - AliveSuccessors.push_back(&BI.getSuccessor(0)->front()); - AliveSuccessors.push_back(&BI.getSuccessor(1)->front()); + ToBePushed.push_back(&BI.getSuccessor(0)->front()); + ToBePushed.push_back(&BI.getSuccessor(1)->front()); UsedAssumedInformation = false; } } + const Function *F = BI.getFunction(); + const auto &AAUB = + A.getAAFor(AA, IRPosition::function(*F)); + for (const Instruction *I : ToBePushed) { + if (!AAUB.isKnownToCauseUB(I)) { + if (AAUB.isAssumedToCauseUB(I)) + // Don't insert it, but mark that this is based on assumed info. + UsedAssumedInformation = true; + else + AliveSuccessors.push_back(I); + } + } return UsedAssumedInformation; } @@ -3882,7 +3911,7 @@ /// See CaptureTracker::shouldExplore(...). bool shouldExplore(const Use *U) override { // Check liveness. - return !IsDeadAA.isAssumedDead(cast(U->getUser())); + return !IsDeadAA.isAssumedDead(A, cast(U->getUser())); } /// Update the state according to \p CapturedInMem, \p CapturedInInt, and @@ -4961,9 +4990,9 @@ const Use *U = Uses[i]; Instruction *UserI = cast(U->getUser()); LLVM_DEBUG(dbgs() << "[AAMemoryBehavior] Use: " << **U << " in " << *UserI - << " [Dead: " << (LivenessAA.isAssumedDead(UserI)) + << " [Dead: " << (LivenessAA.isAssumedDead(A, UserI)) << "]\n"); - if (LivenessAA.isAssumedDead(UserI)) + if (LivenessAA.isAssumedDead(A, UserI)) continue; // Check if the users of UserI should also be visited. @@ -5087,7 +5116,7 @@ if (&AA == LivenessAA) return false; - if (!LivenessAA->isAssumedDead(CtxI)) + if (!LivenessAA->isAssumedDead(*this, CtxI)) return false; // We actually used liveness information so we have to record a dependence. @@ -5125,7 +5154,7 @@ continue; LLVM_DEBUG(dbgs() << "[Attributor] Check use: " << **U << "\n"); if (Instruction *UserI = dyn_cast(U->getUser())) - if (LivenessAA && LivenessAA->isAssumedDead(UserI)) { + if (LivenessAA && LivenessAA->isAssumedDead(*this, UserI)) { LLVM_DEBUG(dbgs() << "[Attributor] Dead user: " << *UserI << ": " << *LivenessAA << "\n"); AnyDead = true; @@ -5196,7 +5225,7 @@ /* TrackDependence */ false); // Skip dead calls. - if (LivenessAA && LivenessAA->isAssumedDead(I)) { + if (LivenessAA && LivenessAA->isAssumedDead(*this, I)) { // We actually used liveness information so we have to record a // dependence. if (QueryingAA) @@ -5269,15 +5298,14 @@ }); } -static bool -checkForAllInstructionsImpl(InformationCache::OpcodeInstMapTy &OpcodeInstMap, - const function_ref &Pred, - const AAIsDead *LivenessAA, bool &AnyDead, - const ArrayRef &Opcodes) { +static bool checkForAllInstructionsImpl( + Attributor &A, InformationCache::OpcodeInstMapTy &OpcodeInstMap, + const function_ref &Pred, const AAIsDead *LivenessAA, + bool &AnyDead, const ArrayRef &Opcodes) { for (unsigned Opcode : Opcodes) { for (Instruction *I : OpcodeInstMap[Opcode]) { // Skip dead instructions. - if (LivenessAA && LivenessAA->isAssumedDead(I)) { + if (LivenessAA && LivenessAA->isAssumedDead(A, I)) { AnyDead = true; continue; } @@ -5307,8 +5335,8 @@ auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(*AssociatedFunction); - if (!checkForAllInstructionsImpl(OpcodeInstMap, Pred, &LivenessAA, AnyDead, - Opcodes)) + if (!checkForAllInstructionsImpl(*this, OpcodeInstMap, Pred, &LivenessAA, + AnyDead, Opcodes)) return false; // If we actually used liveness information so we have to record a dependence. @@ -5336,7 +5364,7 @@ for (Instruction *I : InfoCache.getReadOrWriteInstsForFunction(*AssociatedFunction)) { // Skip dead instructions. - if (LivenessAA.isAssumedDead(I)) { + if (LivenessAA.isAssumedDead(*this, I)) { AnyDead = true; continue; } @@ -5866,7 +5894,7 @@ auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); bool Success, AnyDead = false; Success = checkForAllInstructionsImpl( - OpcodeInstMap, CallSitePred, nullptr, AnyDead, + *this, OpcodeInstMap, CallSitePred, nullptr, AnyDead, {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, (unsigned)Instruction::Call}); (void)Success; @@ -5882,7 +5910,7 @@ return true; }; Success = checkForAllInstructionsImpl( - OpcodeInstMap, LoadStorePred, nullptr, AnyDead, + *this, OpcodeInstMap, LoadStorePred, nullptr, AnyDead, {(unsigned)Instruction::Load, (unsigned)Instruction::Store}); (void)Success; assert(Success && !AnyDead && "Expected the check call to be successful!");