Index: llvm/include/llvm/Transforms/IPO/Attributor.h =================================================================== --- llvm/include/llvm/Transforms/IPO/Attributor.h +++ llvm/include/llvm/Transforms/IPO/Attributor.h @@ -98,6 +98,7 @@ #define LLVM_TRANSFORMS_IPO_ATTRIBUTOR_H #include "llvm/Analysis/LazyCallGraph.h" +#include "llvm/Analysis/LoopInfo.h" #include "llvm/IR/CallSite.h" #include "llvm/IR/PassManager.h" @@ -905,6 +906,27 @@ static constexpr Attribute::AttrKind ID = Attribute::Dereferenceable; }; +/// An abstract interface for loop attributes. +struct AALoop : public AbstractAttribute { + + /// See AbstractAttribute::AbstractAttribute(...). + AALoop(Value &V, InformationCache &InfoCache) + : AbstractAttribute(V, InfoCache) {} + + /// See AbastractState::getAttrKind(). + Attribute::AttrKind getAttrKind() const override { return ID; } + + /// Return true if a loop is assumed "never-endless". + virtual bool isAssumedNeverEndless(Loop *) const = 0; + + /// Return true if a loop is assumed "always-endless". + virtual bool isAssumedAlwaysEndless(Loop *) const = 0; + + /// The identifier used by the Attributor for this class of attributes. + static constexpr Attribute::AttrKind ID = + Attribute::AttrKind(Attribute::EndAttrKinds + 2); +}; + } // end namespace llvm #endif // LLVM_TRANSFORMS_IPO_FUNCTIONATTRS_H Index: llvm/lib/Transforms/IPO/Attributor.cpp =================================================================== --- llvm/lib/Transforms/IPO/Attributor.cpp +++ llvm/lib/Transforms/IPO/Attributor.cpp @@ -1973,6 +1973,121 @@ return BeforeState == static_cast(*this) ? ChangeStatus::UNCHANGED : ChangeStatus::CHANGED; } +/// -------------------AALoop Function Attribute----------------------- +struct AALoopFunction : AALoop, BooleanState { + + AALoopFunction(Function &F, InformationCache &InfoCache) + : AALoop(F, InfoCache) {} + + /// See AbstractAttribute::getState() + /// { + AbstractState &getState() override { return *this; } + const AbstractState &getState() const override { return *this; } + /// } + + /// See AbstractAttribute::getManifestPosition(). + ManifestPosition getManifestPosition() const override { return MP_FUNCTION; } + + const std::string getAsStr() const override { + return "LoopAA(N " + std::to_string(NoReturnCalls.size()) + "/W " + + std::to_string(WillReturnCalls.size()) + ")"; + } + + void initialize(Attributor &A) override { + Function &F = getAnchorScope(); + for (Instruction &I : instructions(F)) { + ImmutableCallSite ICS(&I); + if (ICS) { + NoReturnCalls.insert(&I); + WillReturnCalls.insert(&I); + } + } + } + /// See AbstractAttribute::updateImpl(...). + ChangeStatus updateImpl(Attributor &A) override; + + /// See AALoop::isAssumedNeverEndless(Loop*). + bool isAssumedNeverEndless(Loop *) const override; + + /// See AALoop::isAssumedAlwaysEndless(Loop*). + bool isAssumedAlwaysEndless(Loop *) const override; + + /// Collection of calls with noreturn attribute, assumed or knwon. + SmallSetVector NoReturnCalls; + + /// Collection of calls with willreturn attribute, assumed or knwon. + SmallSetVector WillReturnCalls; +}; + +ChangeStatus AALoopFunction::updateImpl(Attributor &A) { + SmallVector NoReturnBefore, WillReturnBefore; + + ChangeStatus Status = ChangeStatus::UNCHANGED; + + NoReturnBefore.assign(NoReturnCalls.begin(), NoReturnCalls.end()); + WillReturnBefore.assign(WillReturnCalls.begin(), WillReturnCalls.end()); + + for (Instruction *I : NoReturnBefore) { + auto NoReturnAA = A.getAAFor(*this, *I); + if (!NoReturnAA || !NoReturnAA->isAssumedNoReturn()) { + NoReturnCalls.remove(I); + Status = ChangeStatus::CHANGED; + } + } + + for (Instruction *I : WillReturnBefore) { + auto WillReturnAA = A.getAAFor(*this, *I); + if (!WillReturnAA || !WillReturnAA->isAssumedWillReturn()) { + WillReturnCalls.remove(I); + Status = ChangeStatus::CHANGED; + } + } + + if (WillReturnCalls.empty() && NoReturnCalls.empty()) + indicatePessimisticFixpoint(); + + return Status; +} +// "always-endless" is assumed if the loop satisfies any of following +// conditions. +// (i) There is no exit block. +// (ii) There is function call assumed no-return and guaranteed to be executed. +// (iii) Break condition would be never +// satisfied. +bool AALoopFunction::isAssumedAlwaysEndless(Loop *L) const { + + // (i) Check whether there is no exit block. + SmallVector ExitBlocks; + L->getExitBlocks(ExitBlocks); + if (ExitBlocks.size() == 0) + return true; + + // (ii) For now, only traverse instructions in Header. + BasicBlock &Header = *L->getHeader(); + for (Instruction &I : Header) { + ImmutableCallSite ICS(&I); + if (ICS && NoReturnCalls.count(&I)) + return true; + } + + // TODO: Implement break condition check(iii). + + return false; +} +// "never-endless" is assumed if the loop satisfies all of following conditions. +// (i) All calls are assumed willreturn. +// (ii) Break condition would be satisfied anyway. +bool AALoopFunction::isAssumedNeverEndless(Loop *L) const { + for (BasicBlock *BB : L->getBlocks()) { + for (Instruction &I : *BB) { + ImmutableCallSite ICS(&I); + if (ICS && !WillReturnCalls.count(&I)) + return false; + } + } + // TODO: Implement (ii) + return false; +} /// ---------------------------------------------------------------------------- /// Attributor @@ -2205,6 +2320,9 @@ // Check for dead BasicBlocks in every function. registerAA(*new AAIsDeadFunction(F, InfoCache)); + // Register AALoop for every function. + registerAA(*new AALoopFunction(F, InfoCache)); + // Walk all instructions to find more attribute opportunities and also // interesting instructions that might be queried by abstract attributes // during their initialization or update.