[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
llvm-svn: 367961
diff --git a/llvm/lib/Transforms/IPO/Attributor.cpp b/llvm/lib/Transforms/IPO/Attributor.cpp
index 6cbe47c..3656d77 100644
--- a/llvm/lib/Transforms/IPO/Attributor.cpp
+++ b/llvm/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<AAIsDead>(*this, F);
+ auto CheckForNoUnwind = [&](Instruction &I) {
+ if (!I.mayThrow())
+ return true;
- for (unsigned Opcode : Opcodes) {
- for (Instruction *I : OpcodeInstMap[Opcode]) {
- // Skip dead instructions.
- if (LivenessAA && LivenessAA->isAssumedDead(I))
- continue;
+ auto *NoUnwindAA = A.getAAFor<AANoUnwind>(*this, I);
+ return NoUnwindAA && NoUnwindAA->isAssumedNoUnwind();
+ };
- if (!I->mayThrow())
- continue;
+ if (!A.checkForAllInstructions(F, CheckForNoUnwind, *this, InfoCache,
+ Opcodes))
+ return indicatePessimisticFixpoint();
- auto *NoUnwindAA = A.getAAFor<AANoUnwind>(*this, *I);
-
- 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};
+ 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;
- 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;
+ // non-convergent and readnone imply nosync.
+ return !ImmutableCallSite(&I).isConvergent();
+ };
- ImmutableCallSite ICS(I);
-
- // non-convergent and readnone imply nosync.
- if (!ICS.isConvergent())
- continue;
-
- return indicatePessimisticFixpoint();
- }
- }
-
+ 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<AAIsDead>(*this, F);
+ auto CheckForNoFree = [&](Instruction &I) {
+ if (ImmutableCallSite(&I).hasFnAttr(Attribute::NoFree))
+ return true;
- // The map from instruction opcodes to those instructions in the function.
- auto &OpcodeInstMap = InfoCache.getOpcodeInstMapForFunction(F);
+ auto *NoFreeAA = A.getAAFor<AANoFreeImpl>(*this, I);
+ return NoFreeAA && NoFreeAA->isAssumedNoFree();
+ };
- 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<AANoFreeImpl>(*this, *I);
-
- 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<AAIsDead>(*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<AAWillReturn>(*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<AANoRecurse>(*this, I);
+ return NoRecurseAA && NoRecurseAA->isAssumedNoRecurse();
+ };
- auto *WillReturnAA = A.getAAFor<AAWillReturn>(*this, *I);
- if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn())
- return indicatePessimisticFixpoint();
-
- auto *NoRecurseAA = A.getAAFor<AANoRecurse>(*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<AAIsDead>(*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<bool(CallSite)> &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<Instruction>(U.getUser());
Function *AnchorValue = I->getParent()->getParent();
- auto *LivenessAA = getAAFor<AAIsDead>(AA, *AnchorValue);
+ auto *LivenessAA = getAAFor<AAIsDead>(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<bool(Instruction &)> &Pred,
+ AbstractAttribute &QueryingAA, InformationCache &InfoCache,
+ const ArrayRef<unsigned> &Opcodes) {
+
+ auto *LivenessAA = getAAFor<AAIsDead>(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)