From: Johannes Doerfert Date: Tue, 6 Aug 2019 00:32:43 +0000 (+0000) Subject: [Attributor] Provide a generic interface to check live instructions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3e62aa52b04cc5c62569d9603c8b7568b24e4b00;p=llvm [Attributor] Provide a generic interface to check live instructions Summary: Similar to `Attributor::checkForAllCallSites`, we now provide such functionality for instructions of a certain opcode through `Attributor::checkForAllInstructions` and the convenient wrapper `Attributor::checkForAllCallLikeInstructions`. This cleans up code, avoids duplication, and simplifies the usage of liveness information. Reviewers: sstefan1, uenoku Subscribers: hiraditya, bollu, llvm-commits Tags: #llvm Differential Revision: https://reviews.llvm.org/D65731 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@367961 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/llvm/Transforms/IPO/Attributor.h b/include/llvm/Transforms/IPO/Attributor.h index 63293e9d145..1b6b711d2b9 100644 --- a/include/llvm/Transforms/IPO/Attributor.h +++ b/include/llvm/Transforms/IPO/Attributor.h @@ -266,7 +266,29 @@ struct Attributor { /// 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. diff --git a/lib/Transforms/IPO/Attributor.cpp b/lib/Transforms/IPO/Attributor.cpp index 6cbe47c184e..3656d77414c 100644 --- a/lib/Transforms/IPO/Attributor.cpp +++ b/lib/Transforms/IPO/Attributor.cpp @@ -422,29 +422,23 @@ ChangeStatus AANoUnwindImpl::updateImpl(Attributor &A, 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 @@ ChangeStatus AANoSyncImpl::updateImpl(Attributor &A, 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 @@ ChangeStatus AANoFreeImpl::updateImpl(Attributor &A, 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 @@ ChangeStatus AANonNullArgument::updateImpl(Attributor &A, 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 @@ void AAWillReturnFunction::initialize(Attributor &A, 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); - 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 CheckForWillReturn = [&](Instruction &I) { + ImmutableCallSite ICS(&I); + if (ICS.hasFnAttr(Attribute::WillReturn)) + return true; - if (ICS.hasFnAttr(Attribute::WillReturn)) - continue; + auto *WillReturnAA = A.getAAFor(*this, I); + if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) + return false; - auto *WillReturnAA = A.getAAFor(*this, *I); - if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) - return indicatePessimisticFixpoint(); + // FIXME: Prohibit any recursion for now. + if (ICS.hasFnAttr(Attribute::NoRecurse)) + return true; - auto *NoRecurseAA = A.getAAFor(*this, *I); + auto *NoRecurseAA = A.getAAFor(*this, I); + return NoRecurseAA && NoRecurseAA->isAssumedNoRecurse(); + }; - // 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 @@ AADereferenceableArgument::updateImpl(Attributor &A, 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 @@ ChangeStatus AAAlignArgument::updateImpl(Attributor &A, 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 @@ struct AANoReturnImpl : public AANoReturn, BooleanState { /// 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 @@ struct AANoReturnFunction final : AANoReturnImpl { 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 @@ bool Attributor::checkForAllCallSites(Function &F, 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 @@ bool Attributor::checkForAllCallSites(Function &F, 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)