Index: llvm/trunk/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/trunk/include/llvm/Transforms/IPO/Attributor.h +++ llvm/trunk/include/llvm/Transforms/IPO/Attributor.h @@ -266,7 +266,29 @@ /// true if \p Pred holds in every call sites. However, this is only possible /// all call sites are known, hence the function has internal linkage. bool checkForAllCallSites(Function &F, std::function &Pred, - bool RequireAllCallSites, AbstractAttribute &AA); + AbstractAttribute &QueryingAA, + bool RequireAllCallSites); + + /// Check \p Pred on all instructions with an opcode present in \p Opcodes. + /// + /// This method will evaluate \p Pred on all instructions with an opcode + /// present in \p Opcode and return true if \p Pred holds on all of them. + bool checkForAllInstructions( + const Function &F, const llvm::function_ref &Pred, + AbstractAttribute &QueryingAA, InformationCache &InfoCache, + const ArrayRef &Opcodes); + + /// Check \p Pred on all call-like instructions (=CallBased derived). + /// + /// See checkForAllCallLikeInstructions(...) for more information. + bool checkForAllCallLikeInstructions( + const Function &F, const llvm::function_ref &Pred, + AbstractAttribute &QueryingAA, InformationCache &InfoCache) { + return checkForAllInstructions(F, Pred, QueryingAA, InfoCache, + {(unsigned)Instruction::Invoke, + (unsigned)Instruction::CallBr, + (unsigned)Instruction::Call}); + } private: /// The set of all abstract attributes. Index: llvm/trunk/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/trunk/lib/Transforms/IPO/Attributor.cpp +++ llvm/trunk/lib/Transforms/IPO/Attributor.cpp @@ -422,29 +422,23 @@ Function &F = getAnchorScope(); // The map from instruction opcodes to those instructions in the function. - auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); auto Opcodes = { (unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, (unsigned)Instruction::Call, (unsigned)Instruction::CleanupRet, (unsigned)Instruction::CatchSwitch, (unsigned)Instruction::Resume}; - auto *LivenessAA = A.getAAFor(*this, F); - - for (unsigned Opcode : Opcodes) { - for (Instruction *I : OpcodeInstMap[Opcode]) { - // Skip dead instructions. - if (LivenessAA && LivenessAA->isAssumedDead(I)) - continue; + auto CheckForNoUnwind = [&](Instruction &I) { + if (!I.mayThrow()) + return true; - if (!I->mayThrow()) - continue; + auto *NoUnwindAA = A.getAAFor(*this, I); + return NoUnwindAA && NoUnwindAA->isAssumedNoUnwind(); + }; - auto *NoUnwindAA = A.getAAFor(*this, *I); + if (!A.checkForAllInstructions(F, CheckForNoUnwind, *this, InfoCache, + Opcodes)) + return indicatePessimisticFixpoint(); - if (!NoUnwindAA || !NoUnwindAA->isAssumedNoUnwind()) - return indicatePessimisticFixpoint(); - } - } return ChangeStatus::UNCHANGED; } @@ -968,30 +962,18 @@ return indicatePessimisticFixpoint(); } - auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); - auto Opcodes = {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, - (unsigned)Instruction::Call}; - - for (unsigned Opcode : Opcodes) { - for (Instruction *I : OpcodeInstMap[Opcode]) { - // Skip assumed dead instructions. - if (LivenessAA && LivenessAA->isAssumedDead(I)) - continue; - // At this point we handled all read/write effects and they are all - // nosync, so they can be skipped. - if (I->mayReadOrWriteMemory()) - continue; - - ImmutableCallSite ICS(I); - - // non-convergent and readnone imply nosync. - if (!ICS.isConvergent()) - continue; + auto CheckForNoSync = [&](Instruction &I) { + // At this point we handled all read/write effects and they are all + // nosync, so they can be skipped. + if (I.mayReadOrWriteMemory()) + return true; - return indicatePessimisticFixpoint(); - } - } + // non-convergent and readnone imply nosync. + return !ImmutableCallSite(&I).isConvergent(); + }; + if (!A.checkForAllCallLikeInstructions(F, CheckForNoSync, *this, InfoCache)) + return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -1029,26 +1011,16 @@ InformationCache &InfoCache) { Function &F = getAnchorScope(); - auto *LivenessAA = A.getAAFor(*this, F); - - // The map from instruction opcodes to those instructions in the function. - auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); + auto CheckForNoFree = [&](Instruction &I) { + if (ImmutableCallSite(&I).hasFnAttr(Attribute::NoFree)) + return true; - for (unsigned Opcode : - {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, - (unsigned)Instruction::Call}) { - for (Instruction *I : OpcodeInstMap[Opcode]) { - // Skip assumed dead instructions. - if (LivenessAA && LivenessAA->isAssumedDead(I)) - continue; - auto ICS = ImmutableCallSite(I); - auto *NoFreeAA = A.getAAFor(*this, *I); + auto *NoFreeAA = A.getAAFor(*this, I); + return NoFreeAA && NoFreeAA->isAssumedNoFree(); + }; - if ((!NoFreeAA || !NoFreeAA->isAssumedNoFree()) && - !ICS.hasFnAttr(Attribute::NoFree)) - return indicatePessimisticFixpoint(); - } - } + if (!A.checkForAllCallLikeInstructions(F, CheckForNoFree, *this, InfoCache)) + return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -1215,7 +1187,7 @@ return false; }; - if (!A.checkForAllCallSites(F, CallSiteCheck, true, *this)) + if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true)) return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -1303,41 +1275,29 @@ ChangeStatus AAWillReturnFunction::updateImpl(Attributor &A, InformationCache &InfoCache) { - Function &F = getAnchorScope(); - + const Function &F = getAnchorScope(); // The map from instruction opcodes to those instructions in the function. - auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); - auto *LivenessAA = A.getAAFor(*this, F); + auto CheckForWillReturn = [&](Instruction &I) { + ImmutableCallSite ICS(&I); + if (ICS.hasFnAttr(Attribute::WillReturn)) + return true; - for (unsigned Opcode : - {(unsigned)Instruction::Invoke, (unsigned)Instruction::CallBr, - (unsigned)Instruction::Call}) { - for (Instruction *I : OpcodeInstMap[Opcode]) { - // Skip assumed dead instructions. - if (LivenessAA && LivenessAA->isAssumedDead(I)) - continue; + auto *WillReturnAA = A.getAAFor(*this, I); + if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) + return false; - auto ICS = ImmutableCallSite(I); + // FIXME: Prohibit any recursion for now. + if (ICS.hasFnAttr(Attribute::NoRecurse)) + return true; - if (ICS.hasFnAttr(Attribute::WillReturn)) - continue; + auto *NoRecurseAA = A.getAAFor(*this, I); + return NoRecurseAA && NoRecurseAA->isAssumedNoRecurse(); + }; - auto *WillReturnAA = A.getAAFor(*this, *I); - if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) - return indicatePessimisticFixpoint(); - - auto *NoRecurseAA = A.getAAFor(*this, *I); - - // FIXME: (i) Prohibit any recursion for now. - // (ii) AANoRecurse isn't implemented yet so currently any call is - // regarded as having recursion. - // Code below should be - // if ((!NoRecurseAA || !NoRecurseAA->isAssumedNoRecurse()) && - if (!NoRecurseAA && !ICS.hasFnAttr(Attribute::NoRecurse)) - return indicatePessimisticFixpoint(); - } - } + if (!A.checkForAllCallLikeInstructions(F, CheckForWillReturn, *this, + InfoCache)) + return indicatePessimisticFixpoint(); return ChangeStatus::UNCHANGED; } @@ -1969,7 +1929,7 @@ return isValidState(); }; - if (!A.checkForAllCallSites(F, CallSiteCheck, true, *this)) + if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true)) return indicatePessimisticFixpoint(); updateAssumedNonNullGlobalState(IsNonNull, IsGlobal); @@ -2155,7 +2115,7 @@ return isValidState(); }; - if (!A.checkForAllCallSites(F, CallSiteCheck, true, *this)) + if (!A.checkForAllCallSites(F, CallSiteCheck, *this, true)) indicatePessimisticFixpoint(); return BeforeState == getAssumed() ? ChangeStatus::UNCHANGED @@ -2230,21 +2190,11 @@ /// See AbstractAttribute::updateImpl(Attributor &A). virtual ChangeStatus updateImpl(Attributor &A, InformationCache &InfoCache) override { - Function &F = getAnchorScope(); - - // The map from instruction opcodes to those instructions in the function. - auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); - - // Look at all return instructions. - auto &ReturnInsts = OpcodeInstMap[Instruction::Ret]; - if (ReturnInsts.empty()) - return indicateOptimisticFixpoint(); - - auto *LivenessAA = A.getAAFor(*this, F); - if (!LivenessAA || - LivenessAA->isLiveInstSet(ReturnInsts.begin(), ReturnInsts.end())) + const Function &F = getAnchorScope(); + auto CheckForNoReturn = [](Instruction &) { return false; }; + if (!A.checkForAllInstructions(F, CheckForNoReturn, *this, InfoCache, + {(unsigned)Instruction::Ret})) return indicatePessimisticFixpoint(); - return ChangeStatus::UNCHANGED; } }; @@ -2259,8 +2209,8 @@ bool Attributor::checkForAllCallSites(Function &F, std::function &Pred, - bool RequireAllCallSites, - AbstractAttribute &AA) { + AbstractAttribute &QueryingAA, + bool RequireAllCallSites) { // We can try to determine information from // the call sites. However, this is only possible all call sites are known, // hence the function has internal linkage. @@ -2276,7 +2226,7 @@ Instruction *I = cast(U.getUser()); Function *AnchorValue = I->getParent()->getParent(); - auto *LivenessAA = getAAFor(AA, *AnchorValue); + auto *LivenessAA = getAAFor(QueryingAA, *AnchorValue); // Skip dead calls. if (LivenessAA && LivenessAA->isAssumedDead(I)) @@ -2303,6 +2253,28 @@ return true; } +bool Attributor::checkForAllInstructions( + const Function &F, const llvm::function_ref &Pred, + AbstractAttribute &QueryingAA, InformationCache &InfoCache, + const ArrayRef &Opcodes) { + + auto *LivenessAA = getAAFor(QueryingAA, F); + + auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F); + for (unsigned Opcode : Opcodes) { + for (Instruction *I : OpcodeInstMap[Opcode]) { + // Skip dead instructions. + if (LivenessAA && LivenessAA->isAssumedDead(I)) + continue; + + if (!Pred(*I)) + return false; + } + } + + return true; +} + ChangeStatus Attributor::run(InformationCache &InfoCache) { // Initialize all abstract attributes. for (AbstractAttribute *AA : AllAbstractAttributes)