void initializeAssumptionCacheTrackerPass(PassRegistry&);
void initializeAtomicExpandPass(PassRegistry&);
void initializeAttributorLegacyPassPass(PassRegistry&);
+void initializeAttributorCGSCCLegacyPassPass(PassRegistry &);
void initializeBDCELegacyPassPass(PassRegistry&);
void initializeBarrierNoopPass(PassRegistry&);
void initializeBasicAAWrapperPassPass(PassRegistry&);
(void) llvm::createInstructionNamerPass();
(void) llvm::createMetaRenamerPass();
(void) llvm::createAttributorLegacyPass();
+ (void) llvm::createAttributorCGSCCLegacyPass();
(void) llvm::createPostOrderFunctionAttrsLegacyPass();
(void) llvm::createReversePostOrderFunctionAttrsPass();
(void) llvm::createMergeFunctionsPass();
#include "llvm/ADT/SCCIterator.h"
#include "llvm/ADT/SetVector.h"
#include "llvm/Analysis/AliasAnalysis.h"
+#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/LazyCallGraph.h"
#include "llvm/Analysis/MustExecute.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/CallSite.h"
#include "llvm/IR/ConstantRange.h"
#include "llvm/IR/PassManager.h"
+#include "llvm/Transforms/Utils/CallGraphUpdater.h"
namespace llvm {
struct AnalysisGetter {
template <typename Analysis>
typename Analysis::Result *getAnalysis(const Function &F) {
- if (!MAM || !F.getParent())
+ if (!FAM || !F.getParent())
return nullptr;
- auto &FAM = MAM->getResult<FunctionAnalysisManagerModuleProxy>(
- const_cast<Module &>(*F.getParent()))
- .getManager();
- return &FAM.getResult<Analysis>(const_cast<Function &>(F));
+ return &FAM->getResult<Analysis>(const_cast<Function &>(F));
}
- template <typename Analysis>
- typename Analysis::Result *getAnalysis(const Module &M) {
- if (!MAM)
- return nullptr;
- return &MAM->getResult<Analysis>(const_cast<Module &>(M));
- }
- AnalysisGetter(ModuleAnalysisManager &MAM) : MAM(&MAM) {}
+ AnalysisGetter(FunctionAnalysisManager &FAM) : FAM(&FAM) {}
AnalysisGetter() {}
private:
- ModuleAnalysisManager *MAM = nullptr;
+ FunctionAnalysisManager *FAM = nullptr;
};
/// Data structure to hold cached (LLVM-IR) information.
/// reusable, it is advised to inherit from the InformationCache and cast the
/// instance down in the abstract attributes.
struct InformationCache {
- InformationCache(const Module &M, AnalysisGetter &AG)
- : DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG) {
-
- CallGraph *CG = AG.getAnalysis<CallGraphAnalysis>(M);
- if (!CG)
- return;
-
- DenseMap<const Function *, unsigned> SccSize;
- for (scc_iterator<CallGraph *> I = scc_begin(CG); !I.isAtEnd(); ++I) {
- for (CallGraphNode *Node : *I)
- SccSize[Node->getFunction()] = I->size();
- }
- SccSizeOpt = std::move(SccSize);
- }
+ InformationCache(const Module &M, AnalysisGetter &AG,
+ SetVector<Function *> *CGSCC)
+ : DL(M.getDataLayout()), Explorer(/* ExploreInterBlock */ true), AG(AG),
+ CGSCC(CGSCC) {}
/// A map type from opcodes to instructions with this opcode.
using OpcodeInstMapTy = DenseMap<unsigned, SmallVector<Instruction *, 32>>;
return AG.getAnalysis<AP>(F);
}
- /// Return SCC size on call graph for function \p F.
+ /// Return SCC size on call graph for function \p F or 0 if unknown.
unsigned getSccSize(const Function &F) {
- if (!SccSizeOpt.hasValue())
- return 0;
- return (SccSizeOpt.getValue())[&F];
+ if (CGSCC && CGSCC->count(const_cast<Function *>(&F)))
+ return CGSCC->size();
+ return 0;
}
/// Return datalayout used in the module.
/// Getters for analysis.
AnalysisGetter &AG;
- /// Cache result for scc size in the call graph
- Optional<DenseMap<const Function *, unsigned>> SccSizeOpt;
+ /// The underlying CGSCC, or null if not available.
+ SetVector<Function *> *CGSCC;
/// Give the Attributor access to the members so
/// Attributor::identifyDefaultAbstractAttributes(...) can initialize them.
struct Attributor {
/// Constructor
///
+ /// \param Functions The set of functions we are deriving attributes for.
/// \param InfoCache Cache to hold various information accessible for
/// the abstract attributes.
+ /// \param CGUpdater Helper to update an underlying call graph.
/// \param DepRecomputeInterval Number of iterations until the dependences
/// between abstract attributes are recomputed.
/// \param Whitelist If not null, a set limiting the attribute opportunities.
- Attributor(InformationCache &InfoCache, unsigned DepRecomputeInterval,
+ Attributor(SetVector<Function *> &Functions, InformationCache &InfoCache,
+ CallGraphUpdater &CGUpdater, unsigned DepRecomputeInterval,
DenseSet<const char *> *Whitelist = nullptr)
- : InfoCache(InfoCache), DepRecomputeInterval(DepRecomputeInterval),
- Whitelist(Whitelist) {}
+ : Functions(Functions), InfoCache(InfoCache), CGUpdater(CGUpdater),
+ DepRecomputeInterval(DepRecomputeInterval), Whitelist(Whitelist) {}
~Attributor() {
DeleteContainerPointers(AllAbstractAttributes);
/// as the Attributor is not destroyed (it owns the attributes now).
///
/// \Returns CHANGED if the IR was changed, otherwise UNCHANGED.
- ChangeStatus run(Module &M);
+ ChangeStatus run();
/// Lookup an abstract attribute of type \p AAType at position \p IRP. While
/// no abstract attribute is found equivalent positions are checked, see
/// Return the data layout associated with the anchor scope.
const DataLayout &getDataLayout() const { return InfoCache.DL; }
+ /// Replace all uses of \p Old with \p New and, for calls (and invokes),
+ /// update the call graph.
+ void replaceAllUsesWith(Value &Old, Value &New) {
+ if (CallBase *OldCB = dyn_cast<CallBase>(&Old)) {
+ // We do not modify the call graph here but simply reanalyze the old
+ // function. This should be revisited once the old PM is gone.
+ CGModifiedFunctions.insert(OldCB->getFunction());
+ }
+ Old.replaceAllUsesWith(&New);
+ }
+
private:
/// Check \p Pred on all call sites of \p Fn.
///
// For now we ignore naked and optnone functions.
bool Invalidate = Whitelist && !Whitelist->count(&AAType::ID);
- if (const Function *Fn = IRP.getAnchorScope())
- Invalidate |= Fn->hasFnAttribute(Attribute::Naked) ||
- Fn->hasFnAttribute(Attribute::OptimizeNone);
+ const Function *FnScope = IRP.getAnchorScope();
+ if (FnScope)
+ Invalidate |= FnScope->hasFnAttribute(Attribute::Naked) ||
+ FnScope->hasFnAttribute(Attribute::OptimizeNone);
// Bootstrap the new attribute with an initial update to propagate
// information, e.g., function -> call site. If it is not on a given
}
AA.initialize(*this);
+
+ // We can initialize (=look at) code outside the current function set but
+ // not call update because that would again spawn new abstract attributes in
+ // potentially unconnected code regions (=SCCs).
+ if (FnScope && !Functions.count(const_cast<Function *>(FnScope))) {
+ AA.getState().indicatePessimisticFixpoint();
+ return AA;
+ }
+
AA.update(*this);
if (TrackDependence && AA.getState().isValidState())
/// Apply all requested function signature rewrites
/// (\see registerFunctionSignatureRewrite) and return Changed if the module
/// was altered.
- ChangeStatus rewriteFunctionSignatures();
+ ChangeStatus
+ rewriteFunctionSignatures(SmallPtrSetImpl<Function *> &ModifiedFns);
/// The set of all abstract attributes.
///{
DenseMap<Function *, SmallVector<ArgumentReplacementInfo *, 8>>
ArgumentReplacementMap;
+ /// The set of functions we are deriving attributes for.
+ SetVector<Function *> &Functions;
+
/// The information cache that holds pre-processed (LLVM-IR) information.
InformationCache &InfoCache;
+ /// Helper to update an underlying call graph.
+ CallGraphUpdater &CGUpdater;
+
+ /// Set of functions for which we modified the content such that it might
+ /// impact the call graph.
+ SmallPtrSet<Function *, 8> CGModifiedFunctions;
+
/// Set if the attribute currently updated did query a non-fix attribute.
bool QueriedNonFixAA;
struct AttributorPass : public PassInfoMixin<AttributorPass> {
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
};
+struct AttributorCGSCCPass : public PassInfoMixin<AttributorCGSCCPass> {
+ PreservedAnalyses run(LazyCallGraph::SCC &C, CGSCCAnalysisManager &AM,
+ LazyCallGraph &CG, CGSCCUpdateResult &UR);
+};
Pass *createAttributorLegacyPass();
+Pass *createAttributorCGSCCLegacyPass();
/// ----------------------------------------------------------------------------
/// Abstract Attribute Classes
initializeJumpThreadingPass(R);
initializeSROALegacyPassPass(R);
initializeAttributorLegacyPassPass(R);
+ initializeAttributorCGSCCLegacyPassPass(R);
initializePostOrderFunctionAttrsLegacyPassPass(R);
initializeReversePostOrderFunctionAttrsLegacyPassPass(R);
initializeGlobalsAAWrapperPassPass(R);
MPM.addPass(PGOIndirectCallPromotion(Phase == ThinLTOPhase::PostLink,
true /* SamplePGO */));
}
+ MPM.addPass(AttributorPass());
// Interprocedural constant propagation now that basic cleanup has occurred
// and prior to optimizing globals.
IP.HotCallSiteThreshold = 0;
MainCGPipeline.addPass(InlinerPass(IP));
+ MainCGPipeline.addPass(AttributorCGSCCPass());
+
// Now deduce any function attributes based in the current code.
MainCGPipeline.addPass(PostOrderFunctionAttrsPass());
CGSCC_PASS("argpromotion", ArgumentPromotionPass())
CGSCC_PASS("invalidate<all>", InvalidateAllAnalysesPass())
CGSCC_PASS("function-attrs", PostOrderFunctionAttrsPass())
+CGSCC_PASS("attributor-cgscc", AttributorCGSCCPass())
CGSCC_PASS("inline", InlinerPass())
CGSCC_PASS("openmpopt", OpenMPOptPass())
CGSCC_PASS("no-op-cgscc", NoOpCGSCCPass())
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/Analysis/CallGraph.h"
+#include "llvm/Analysis/CallGraphSCCPass.h"
#include "llvm/Analysis/CaptureTracking.h"
#include "llvm/Analysis/EHPersonalities.h"
#include "llvm/Analysis/GlobalsModRef.h"
return nullptr;
}
-/// For calls (and invokes) we will only replace instruction uses to not disturb
-/// the old style call graph.
-/// TODO: Remove this once we get rid of the old PM.
-static void replaceAllInstructionUsesWith(Value &Old, Value &New) {
- if (!isa<CallBase>(Old))
- return Old.replaceAllUsesWith(&New);
- SmallVector<Use *, 8> Uses;
- for (Use &U : Old.uses())
- if (isa<Instruction>(U.getUser()))
- Uses.push_back(&U);
- for (Use *U : Uses)
- U->set(&New);
-}
-
static Optional<ConstantInt *>
getAssumedConstant(Attributor &A, const Value &V, const AbstractAttribute &AA,
bool &UsedAssumedInformation) {
"Number of function with unique return");
// Callback to replace the uses of CB with the constant C.
- auto ReplaceCallSiteUsersWith = [](CallBase &CB, Constant &C) {
+ auto ReplaceCallSiteUsersWith = [&A](CallBase &CB, Constant &C) {
if (CB.getNumUses() == 0 || CB.isMustTailCall())
return ChangeStatus::UNCHANGED;
- replaceAllInstructionUsesWith(CB, C);
+ A.replaceAllUsesWith(CB, C);
return ChangeStatus::CHANGED;
};
AI = new BitCastInst(AI, MallocCall->getType(), "malloc_bc",
AI->getNextNode());
- replaceAllInstructionUsesWith(*MallocCall, *AI);
+ A.replaceAllUsesWith(*MallocCall, *AI);
if (auto *II = dyn_cast<InvokeInst>(MallocCall)) {
auto *NBB = II->getNormalDest();
bool Attributor::isAssumedDead(const AbstractAttribute &AA,
const AAIsDead *LivenessAA) {
const Instruction *CtxI = AA.getIRPosition().getCtxI();
- if (!CtxI)
+ if (!CtxI || !Functions.count(const_cast<Function *>(CtxI->getFunction())))
return false;
// TODO: Find a good way to utilize fine and coarse grained liveness
return true;
}
-ChangeStatus Attributor::run(Module &M) {
+ChangeStatus Attributor::run() {
LLVM_DEBUG(dbgs() << "[Attributor] Identified and initialized "
<< AllAbstractAttributes.size()
<< " abstract attributes.\n");
NumAttributesValidFixpoint += NumAtFixpoint;
(void)NumFinalAAs;
- assert(
- NumFinalAAs == AllAbstractAttributes.size() &&
- "Expected the final number of abstract attributes to remain unchanged!");
+ assert(NumFinalAAs == AllAbstractAttributes.size() &&
+ "Expected the final number of abstract attributes to remain "
+ "unchanged!");
// Delete stuff at the end to avoid invalid references and a nice order.
{
LLVM_DEBUG(dbgs() << "Use " << *NewV << " in " << *U->getUser()
<< " instead of " << *OldV << "\n");
U->set(NewV);
- if (Instruction *I = dyn_cast<Instruction>(OldV))
+ if (Instruction *I = dyn_cast<Instruction>(OldV)) {
+ CGModifiedFunctions.insert(I->getFunction());
if (!isa<PHINode>(I) && !ToBeDeletedInsts.count(I) &&
- isInstructionTriviallyDead(I)) {
+ isInstructionTriviallyDead(I))
DeadInsts.push_back(I);
- }
+ }
if (isa<Constant>(NewV) && isa<BranchInst>(U->getUser())) {
Instruction *UserI = cast<Instruction>(U->getUser());
if (isa<UndefValue>(NewV)) {
}
}
for (auto &V : ToBeChangedToUnreachableInsts)
- if (Instruction *I = dyn_cast_or_null<Instruction>(V))
+ if (Instruction *I = dyn_cast_or_null<Instruction>(V)) {
+ CGModifiedFunctions.insert(I->getFunction());
changeToUnreachable(I, /* UseLLVMTrap */ false);
- for (Instruction *I : TerminatorsToFold)
+ }
+ for (Instruction *I : TerminatorsToFold) {
+ CGModifiedFunctions.insert(I->getFunction());
ConstantFoldTerminator(I->getParent());
+ }
for (auto &V : ToBeDeletedInsts) {
if (Instruction *I = dyn_cast_or_null<Instruction>(V)) {
+ CGModifiedFunctions.insert(I->getFunction());
I->replaceAllUsesWith(UndefValue::get(I->getType()));
if (!isa<PHINode>(I) && isInstructionTriviallyDead(I))
DeadInsts.push_back(I);
if (unsigned NumDeadBlocks = ToBeDeletedBlocks.size()) {
SmallVector<BasicBlock *, 8> ToBeDeletedBBs;
ToBeDeletedBBs.reserve(NumDeadBlocks);
- ToBeDeletedBBs.append(ToBeDeletedBlocks.begin(), ToBeDeletedBlocks.end());
+ for (BasicBlock *BB : ToBeDeletedBlocks) {
+ CGModifiedFunctions.insert(BB->getParent());
+ ToBeDeletedBBs.push_back(BB);
+ }
// Actually we do not delete the blocks but squash them into a single
// unreachable but untangling branches that jump here is something we need
// to do in a more generic way.
// as live to lower the number of iterations. If they happen to be dead, the
// below fixpoint loop will identify and eliminate them.
SmallVector<Function *, 8> InternalFns;
- for (Function &F : M)
- if (F.hasLocalLinkage())
- InternalFns.push_back(&F);
+ for (Function *F : Functions)
+ if (F->hasLocalLinkage())
+ InternalFns.push_back(F);
bool FoundDeadFn = true;
while (FoundDeadFn) {
}
}
+ // Rewrite the functions as requested during manifest.
+ ManifestChange =
+ ManifestChange | rewriteFunctionSignatures(CGModifiedFunctions);
+
+ for (Function *Fn : CGModifiedFunctions)
+ CGUpdater.reanalyzeFunction(*Fn);
+
STATS_DECL(AAIsDead, Function, "Number of dead functions deleted.");
BUILD_STAT_NAME(AAIsDead, Function) += ToBeDeletedFunctions.size();
- // Rewrite the functions as requested during manifest.
- ManifestChange = ManifestChange | rewriteFunctionSignatures();
-
- for (Function *Fn : ToBeDeletedFunctions) {
- Fn->deleteBody();
- Fn->replaceAllUsesWith(UndefValue::get(Fn->getType()));
- Fn->eraseFromParent();
- }
+ for (Function *Fn : ToBeDeletedFunctions)
+ CGUpdater.removeFunction(*Fn);
if (VerifyMaxFixpointIterations &&
IterationCounter != MaxFixpointIterations) {
return true;
}
-ChangeStatus Attributor::rewriteFunctionSignatures() {
+ChangeStatus Attributor::rewriteFunctionSignatures(
+ SmallPtrSetImpl<Function *> &ModifiedFns) {
ChangeStatus Changed = ChangeStatus::UNCHANGED;
for (auto &It : ArgumentReplacementMap) {
for (auto &CallSitePair : CallSitePairs) {
CallBase &OldCB = *CallSitePair.first;
CallBase &NewCB = *CallSitePair.second;
+ // We do not modify the call graph here but simply reanalyze the old
+ // function. This should be revisited once the old PM is gone.
+ ModifiedFns.insert(OldCB.getFunction());
OldCB.replaceAllUsesWith(&NewCB);
OldCB.eraseFromParent();
}
- ToBeDeletedFunctions.insert(OldFn);
+ // Replace the function in the call graph (if any).
+ CGUpdater.replaceFunctionWith(*OldFn, *NewFn);
+
+ // If the old function was modified and needed to be reanalyzed, the new one
+ // does now.
+ if (ModifiedFns.erase(OldFn))
+ ModifiedFns.insert(NewFn);
Changed = ChangeStatus::CHANGED;
}
/// Pass (Manager) Boilerplate
/// ----------------------------------------------------------------------------
-static bool runAttributorOnModule(Module &M, AnalysisGetter &AG) {
- if (DisableAttributor)
+static bool runAttributorOnFunctions(InformationCache &InfoCache,
+ SetVector<Function *> &Functions,
+ AnalysisGetter &AG,
+ CallGraphUpdater &CGUpdater) {
+ if (DisableAttributor || Functions.empty())
return false;
- LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << M.size()
+ LLVM_DEBUG(dbgs() << "[Attributor] Run on module with " << Functions.size()
<< " functions.\n");
// Create an Attributor and initially empty information cache that is filled
// while we identify default attribute opportunities.
- InformationCache InfoCache(M, AG);
- Attributor A(InfoCache, DepRecInterval);
+ Attributor A(Functions, InfoCache, CGUpdater, DepRecInterval);
- for (Function &F : M)
- A.initializeInformationCache(F);
+ for (Function *F : Functions)
+ A.initializeInformationCache(*F);
- for (Function &F : M) {
- if (F.hasExactDefinition())
+ for (Function *F : Functions) {
+ if (F->hasExactDefinition())
NumFnWithExactDefinition++;
else
NumFnWithoutExactDefinition++;
// We look at internal functions only on-demand but if any use is not a
- // direct call, we have to do it eagerly.
- if (F.hasLocalLinkage()) {
- if (llvm::all_of(F.uses(), [](const Use &U) {
- return ImmutableCallSite(U.getUser()) &&
- ImmutableCallSite(U.getUser()).isCallee(&U);
+ // direct call or outside the current set of analyzed functions, we have to
+ // do it eagerly.
+ if (F->hasLocalLinkage()) {
+ if (llvm::all_of(F->uses(), [&Functions](const Use &U) {
+ ImmutableCallSite ICS(U.getUser());
+ return ICS && ICS.isCallee(&U) &&
+ Functions.count(const_cast<Function *>(ICS.getCaller()));
}))
continue;
}
// Populate the Attributor with abstract attribute opportunities in the
// function and the information cache with IR information.
- A.identifyDefaultAbstractAttributes(F);
+ A.identifyDefaultAbstractAttributes(*F);
}
- bool Changed = A.run(M) == ChangeStatus::CHANGED;
- assert(!verifyModule(M, &errs()) && "Module verification failed!");
+ bool Changed = A.run() == ChangeStatus::CHANGED;
+ assert(!verifyModule(*Functions.front()->getParent(), &errs()) &&
+ "Module verification failed!");
return Changed;
}
PreservedAnalyses AttributorPass::run(Module &M, ModuleAnalysisManager &AM) {
- AnalysisGetter AG(AM);
- if (runAttributorOnModule(M, AG)) {
+ FunctionAnalysisManager &FAM =
+ AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
+ AnalysisGetter AG(FAM);
+
+ SetVector<Function *> Functions;
+ for (Function &F : M)
+ Functions.insert(&F);
+
+ CallGraphUpdater CGUpdater;
+ InformationCache InfoCache(M, AG, /* CGSCC */ nullptr);
+ if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) {
+ // FIXME: Think about passes we will preserve and add them here.
+ return PreservedAnalyses::none();
+ }
+ return PreservedAnalyses::all();
+}
+
+PreservedAnalyses AttributorCGSCCPass::run(LazyCallGraph::SCC &C,
+ CGSCCAnalysisManager &AM,
+ LazyCallGraph &CG,
+ CGSCCUpdateResult &UR) {
+ FunctionAnalysisManager &FAM =
+ AM.getResult<FunctionAnalysisManagerCGSCCProxy>(C, CG).getManager();
+ AnalysisGetter AG(FAM);
+
+ SetVector<Function *> Functions;
+ for (LazyCallGraph::Node &N : C)
+ Functions.insert(&N.getFunction());
+
+ if (Functions.empty())
+ return PreservedAnalyses::all();
+
+ Module &M = *Functions.back()->getParent();
+ CallGraphUpdater CGUpdater;
+ CGUpdater.initialize(CG, C, AM, UR);
+ InformationCache InfoCache(M, AG, /* CGSCC */ &Functions);
+ if (runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater)) {
// FIXME: Think about passes we will preserve and add them here.
return PreservedAnalyses::none();
}
return false;
AnalysisGetter AG;
- return runAttributorOnModule(M, AG);
+ SetVector<Function *> Functions;
+ for (Function &F : M)
+ Functions.insert(&F);
+
+ CallGraphUpdater CGUpdater;
+ InformationCache InfoCache(M, AG, /* CGSCC */ nullptr);
+ return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater);
+ }
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ // FIXME: Think about passes we will preserve and add them here.
+ AU.addRequired<TargetLibraryInfoWrapperPass>();
+ }
+};
+
+struct AttributorCGSCCLegacyPass : public CallGraphSCCPass {
+ CallGraphUpdater CGUpdater;
+ static char ID;
+
+ AttributorCGSCCLegacyPass() : CallGraphSCCPass(ID) {
+ initializeAttributorCGSCCLegacyPassPass(*PassRegistry::getPassRegistry());
+ }
+
+ bool runOnSCC(CallGraphSCC &SCC) override {
+ if (skipSCC(SCC))
+ return false;
+
+ SetVector<Function *> Functions;
+ for (CallGraphNode *CGN : SCC)
+ if (Function *Fn = CGN->getFunction())
+ if (!Fn->isDeclaration())
+ Functions.insert(Fn);
+
+ if (Functions.empty())
+ return false;
+
+ AnalysisGetter AG;
+ CallGraph &CG = const_cast<CallGraph &>(SCC.getCallGraph());
+ CGUpdater.initialize(CG, SCC);
+ Module &M = *Functions.back()->getParent();
+ InformationCache InfoCache(M, AG, /* CGSCC */ &Functions);
+ return runAttributorOnFunctions(InfoCache, Functions, AG, CGUpdater);
}
+ bool doFinalization(CallGraph &CG) override { return CGUpdater.finalize(); }
+
void getAnalysisUsage(AnalysisUsage &AU) const override {
// FIXME: Think about passes we will preserve and add them here.
AU.addRequired<TargetLibraryInfoWrapperPass>();
+ CallGraphSCCPass::getAnalysisUsage(AU);
}
};
} // end anonymous namespace
Pass *llvm::createAttributorLegacyPass() { return new AttributorLegacyPass(); }
+Pass *llvm::createAttributorCGSCCLegacyPass() {
+ return new AttributorCGSCCLegacyPass();
+}
char AttributorLegacyPass::ID = 0;
+char AttributorCGSCCLegacyPass::ID = 0;
const char AAReturnedValues::ID = 0;
const char AANoUnwind::ID = 0;
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_END(AttributorLegacyPass, "attributor",
"Deduce and propagate attributes", false, false)
+INITIALIZE_PASS_BEGIN(AttributorCGSCCLegacyPass, "attributor-cgscc",
+ "Deduce and propagate attributes (CGSCC pass)", false,
+ false)
+INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
+INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
+INITIALIZE_PASS_END(AttributorCGSCCLegacyPass, "attributor-cgscc",
+ "Deduce and propagate attributes (CGSCC pass)", false,
+ false)
initializeMergeFunctionsLegacyPassPass(Registry);
initializePartialInlinerLegacyPassPass(Registry);
initializeAttributorLegacyPassPass(Registry);
+ initializeAttributorCGSCCLegacyPassPass(Registry);
initializePostOrderFunctionAttrsLegacyPassPass(Registry);
initializeReversePostOrderFunctionAttrsLegacyPassPass(Registry);
initializePruneEHPass(Registry);
// Infer attributes about declarations if possible.
MPM.add(createInferFunctionAttrsLegacyPass());
+ // Infer attributes on declarations, call sites, arguments, etc.
+ MPM.add(createAttributorLegacyPass());
+
addExtensionsToPM(EP_ModuleOptimizerEarly, MPM);
if (OptLevel > 2)
MPM.add(createIPSCCPPass()); // IP SCCP
MPM.add(createCalledValuePropagationPass());
- // Infer attributes on declarations, call sites, arguments, etc.
- MPM.add(createAttributorLegacyPass());
-
MPM.add(createGlobalOptimizerPass()); // Optimize out global vars
// Promote any localized global vars.
MPM.add(createPromoteMemoryToRegisterPass());
RunInliner = true;
}
+ // Infer attributes on declarations, call sites, arguments, etc. for an SCC.
+ MPM.add(createAttributorCGSCCLegacyPass());
+
// Try to perform OpenMP specific optimizations. This is a (quick!) no-op if
// there are no OpenMP runtime calls present in the module.
if (OptLevel > 1)
// CSFDO instrumentation and use pass.
addPGOInstrPasses(PM, /* IsCS */ true);
+ // Infer attributes on declarations, call sites, arguments, etc. for an SCC.
+ PM.add(createAttributorCGSCCLegacyPass());
+
// Try to perform OpenMP specific optimizations. This is a (quick!) no-op if
// there are no OpenMP runtime calls present in the module.
if (OptLevel > 1)
; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass
; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass
; CHECK-O-NEXT: Finished llvm::Function pass manager run.
+; CHECK-O-NEXT: Running pass: AttributorPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
+; CHECK-O-NEXT: Running pass: AttributorCGSCCPass
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O2-NEXT: Running pass: OpenMPOptPass on (foo)
; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass
; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass
; CHECK-O-NEXT: Finished llvm::Function pass manager run.
+; CHECK-O-NEXT: Running pass: AttributorPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
+; CHECK-O-NEXT: Running pass: AttributorCGSCCPass
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O2-NEXT: Running pass: OpenMPOptPass on (foo)
; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass
; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass
; CHECK-O-NEXT: Finished {{.*}}Function pass manager run.
+; CHECK-O-NEXT: Running pass: AttributorPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
+; CHECK-O-NEXT: Running pass: AttributorCGSCCPass
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O2-NEXT: Running pass: OpenMPOptPass
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
; CHECK-O-NEXT: Running pass: PGOIndirectCallPromotion
+; CHECK-O-NEXT: Running pass: AttributorPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
+; CHECK-O-NEXT: Running pass: AttributorCGSCCPass
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O2-NEXT: Running pass: OpenMPOptPass
; CHECK-O-NEXT: Running pass: LowerExpectIntrinsicPass
; CHECK-O3-NEXT: Running pass: CallSiteSplittingPass
; CHECK-O-NEXT: Finished {{.*}}Function pass manager run.
+; CHECK-O-NEXT: Running pass: AttributorPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
+; CHECK-O-NEXT: Running pass: AttributorCGSCCPass
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O-NEXT: Running analysis: AAManager on foo
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O-NEXT: Running analysis: ProfileSummaryAnalysis
; CHECK-O-NEXT: Running analysis: CallGraphAnalysis
; CHECK-O-NEXT: Running pass: RequireAnalysisPass<{{.*}}ProfileSummaryAnalysis
+; CHECK-O-NEXT: Running pass: AttributorPass
; CHECK-O-NEXT: Running pass: IPSCCPPass
; CHECK-O-NEXT: Running pass: CalledValuePropagationPass
; CHECK-O-NEXT: Running pass: GlobalOptPass
; CHECK-O-NEXT: Running analysis: OuterAnalysisManagerProxy<{{.*}}LazyCallGraph::SCC{{.*}}>
; CHECK-O-NEXT: Starting CGSCC pass manager run.
; CHECK-O-NEXT: Running pass: InlinerPass
+; CHECK-O-NEXT: Running pass: AttributorCGSCCPass
; CHECK-O-NEXT: Running pass: PostOrderFunctionAttrsPass
; CHECK-O3-NEXT: Running pass: ArgumentPromotionPass
; CHECK-O2-NEXT: Running pass: OpenMPOptPass
; CHECK-NEXT: ModulePass Manager
; CHECK-NEXT: Force set function attributes
; CHECK-NEXT: Infer set function attributes
+; CHECK-NEXT: Deduce and propagate attributes
; CHECK-NEXT: Interprocedural Sparse Conditional Constant Propagation
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Called Value Propagation
-; CHECK-NEXT: Deduce and propagate attributes
; CHECK-NEXT: Global Variable Optimizer
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Call Graph SCC Pass Manager
; CHECK-NEXT: Remove unused exception handling info
; CHECK-NEXT: Function Integration/Inlining
+; CHECK-NEXT: Deduce and propagate attributes (CGSCC pass)
; CHECK-NEXT: OpenMP specific optimizations
; CHECK-NEXT: Deduce function attributes
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: ModulePass Manager
; CHECK-NEXT: Force set function attributes
; CHECK-NEXT: Infer set function attributes
+; CHECK-NEXT: Deduce and propagate attributes
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Call-site splitting
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Called Value Propagation
-; CHECK-NEXT: Deduce and propagate attributes
; CHECK-NEXT: Global Variable Optimizer
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Call Graph SCC Pass Manager
; CHECK-NEXT: Remove unused exception handling info
; CHECK-NEXT: Function Integration/Inlining
+; CHECK-NEXT: Deduce and propagate attributes (CGSCC pass)
; CHECK-NEXT: OpenMP specific optimizations
; CHECK-NEXT: Deduce function attributes
; CHECK-NEXT: Promote 'by reference' arguments to scalars
; CHECK-NEXT: ModulePass Manager
; CHECK-NEXT: Force set function attributes
; CHECK-NEXT: Infer set function attributes
+; CHECK-NEXT: Deduce and propagate attributes
; CHECK-NEXT: Interprocedural Sparse Conditional Constant Propagation
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Called Value Propagation
-; CHECK-NEXT: Deduce and propagate attributes
; CHECK-NEXT: Global Variable Optimizer
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Call Graph SCC Pass Manager
; CHECK-NEXT: Remove unused exception handling info
; CHECK-NEXT: Function Integration/Inlining
+; CHECK-NEXT: Deduce and propagate attributes (CGSCC pass)
; CHECK-NEXT: OpenMP specific optimizations
; CHECK-NEXT: Deduce function attributes
; CHECK-NEXT: FunctionPass Manager
; CHECK-O2-NEXT: Call Graph SCC Pass Manager
; CHECK-O2-NEXT: Remove unused exception handling info
; CHECK-O2-NEXT: Function Integration/Inlining
+; CHECK-O2-NEXT: Deduce and propagate attributes (CGSCC pass)
; CHECK-O2-NEXT: OpenMP specific optimizations
; CHECK-O2-NEXT: Deduce function attributes
; Next up is the main function pass pipeline. It shouldn't be split up and
; CHECK-LABEL: define {{[^@]+}}@caller
; CHECK-SAME: (i32** nocapture readonly [[Y:%.*]], %struct.pair* nocapture nofree readonly [[P:%.*]])
; CHECK-NEXT: call void @test(i32** nocapture readonly align 8 [[Y]]), !dbg !4
-; CHECK-NEXT: call void @test_byval() #1, !dbg !5
+; CHECK-NEXT: call void @test_byval(), !dbg !5
; CHECK-NEXT: ret void
;
call void @test(i32** %Y), !dbg !1
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
; RUN: opt -S -basicaa -attributor -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s --check-prefixes=CHECK,OLDPM_MODULE
+; RUN: opt -S -basicaa -attributor-cgscc -attributor-disable=false < %s | FileCheck %s --check-prefixes=CHECK,OLDPM_CGSCC
; RUN: opt -S -passes='attributor' -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s --check-prefixes=CHECK,NEWPM_MODULE
+; RUN: opt -S -passes='attributor-cgscc' -aa-pipeline='basic-aa' -attributor-disable=false < %s | FileCheck %s --check-prefixes=CHECK,NEWPM_CGSCC
; OLDPM_MODULE-NOT: @dead
; NEWPM_MODULE-NOT: @dead
+; OLDPM_CGSCC-NOT: @dead
+; NEWPM_CGSCC-NOT: @dead
define internal void @dead() {
call i32 @test(i32* null, i32* null)
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
-; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=2 < %s | FileCheck %s
+; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
; ArgumentPromotion should preserve the default function address space
; from the data layout.
; CHECK-NEXT: entry:
; CHECK-NEXT: [[CONV:%.*]] = sext i32 undef to i64
; CHECK-NEXT: [[DIV:%.*]] = sdiv i64 8, [[CONV]]
-; CHECK-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 [[DIV]]) #0, !range !0
+; CHECK-NEXT: [[CALL2:%.*]] = call i64 @fn1(i64 [[DIV]]) #{{[0-9]+}}, !range !0
; CHECK-NEXT: ret i64 [[CALL2]]
;
entry:
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
-; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=3 < %s | FileCheck %s
+; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
;
;
; /---------------------------------------|
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
-; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s
+; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s --check-prefixes=CHECK,MODULE
+; RUN: opt -S -passes=attributor-cgscc -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-max-iterations=1 < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
;
; #include <pthread.h>
;
declare !callback !0 dso_local i32 @pthread_create(i64*, %union.pthread_attr_t*, i8* (i8*)*, i8*)
define internal i8* @foo(i8* %arg) {
-; CHECK-LABEL: define {{[^@]+}}@foo
-; CHECK-SAME: (i8* noalias nofree readnone returned align 536870912 [[ARG:%.*]])
-; CHECK-NEXT: entry:
-; CHECK-NEXT: ret i8* null
+; MODULE-LABEL: define {{[^@]+}}@foo
+; MODULE-SAME: (i8* noalias nofree readnone returned align 536870912 [[ARG:%.*]])
+; MODULE-NEXT: entry:
+; MODULE-NEXT: ret i8* null
+;
+; CGSCC-LABEL: define {{[^@]+}}@foo
+; CGSCC-SAME: (i8* noalias nofree readnone returned [[ARG:%.*]])
+; CGSCC-NEXT: entry:
+; CGSCC-NEXT: ret i8* null
;
entry:
ret i8* %arg
}
define internal i8* @bar(i8* %arg) {
-; CHECK-LABEL: define {{[^@]+}}@bar
-; CHECK-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(8) [[ARG:%.*]])
-; CHECK-NEXT: entry:
-; CHECK-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*)
+; MODULE-LABEL: define {{[^@]+}}@bar
+; MODULE-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(8) [[ARG:%.*]])
+; MODULE-NEXT: entry:
+; MODULE-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*)
+;
+; CGSCC-LABEL: define {{[^@]+}}@bar
+; CGSCC-SAME: (i8* nofree readnone returned [[ARG:%.*]])
+; CGSCC-NEXT: entry:
+; CGSCC-NEXT: ret i8* bitcast (i8** @GlobalVPtr to i8*)
;
entry:
ret i8* %arg
}
define internal i8* @baz(i8* %arg) {
-; CHECK-LABEL: define {{[^@]+}}@baz
-; CHECK-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) [[ARG:%.*]])
-; CHECK-NEXT: entry:
-; CHECK-NEXT: ret i8* [[ARG]]
+; MODULE-LABEL: define {{[^@]+}}@baz
+; MODULE-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) [[ARG:%.*]])
+; MODULE-NEXT: entry:
+; MODULE-NEXT: ret i8* [[ARG]]
+;
+; CGSCC-LABEL: define {{[^@]+}}@baz
+; CGSCC-SAME: (i8* nofree readnone returned [[ARG:%.*]])
+; CGSCC-NEXT: entry:
+; CGSCC-NEXT: ret i8* [[ARG]]
;
entry:
ret i8* %arg
}
define internal i8* @buz(i8* %arg) {
-; CHECK-LABEL: define {{[^@]+}}@buz
-; CHECK-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) [[ARG:%.*]])
-; CHECK-NEXT: entry:
-; CHECK-NEXT: ret i8* [[ARG]]
+; MODULE-LABEL: define {{[^@]+}}@buz
+; MODULE-SAME: (i8* noalias nofree nonnull readnone returned align 8 dereferenceable(1) [[ARG:%.*]])
+; MODULE-NEXT: entry:
+; MODULE-NEXT: ret i8* [[ARG]]
+;
+; CGSCC-LABEL: define {{[^@]+}}@buz
+; CGSCC-SAME: (i8* nofree readnone returned [[ARG:%.*]])
+; CGSCC-NEXT: entry:
+; CGSCC-NEXT: ret i8* [[ARG]]
;
entry:
ret i8* %arg
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes --turn off
-; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
+; RUN: opt -attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE
+; RUN: opt -attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC
+; RUN: opt -passes=attributor -attributor-manifest-internal -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE
+; RUN: opt -passes=attributor-cgscc -attributor-manifest-internal -attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=5 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
; TEST 7
; Better than IR information
define align 4 i32* @test7(i32* align 32 %p) #0 {
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@test7
-; ATTRIBUTOR-SAME: (i32* nofree readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]])
-; ATTRIBUTOR-NEXT: ret i32* [[P]]
+; ATTRIBUTOR_MODULE-LABEL: define {{[^@]+}}@test7
+; ATTRIBUTOR_MODULE-SAME: (i32* nofree readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]])
+; ATTRIBUTOR_MODULE-NEXT: ret i32* [[P]]
+;
+; ATTRIBUTOR_CGSCC-LABEL: define {{[^@]+}}@test7
+; ATTRIBUTOR_CGSCC-SAME: (i32* nofree readnone returned align 32 "no-capture-maybe-returned" [[P:%.*]])
+; ATTRIBUTOR_CGSCC-NEXT: [[TMP1:%.*]] = tail call i8* @f1(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1)
+; ATTRIBUTOR_CGSCC-NEXT: ret i32* [[P]]
;
tail call i8* @f1(i8* align 8 dereferenceable(1) @a1)
ret i32* %p
; TEST 7b
; Function Attrs: nounwind readnone ssp uwtable
define internal i8* @f1b(i8* readnone %0) local_unnamed_addr #0 {
-; ATTRIBUTOR-LABEL: define {{[^@]+}}@f1b
-; ATTRIBUTOR-SAME: (i8* noalias nofree nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr
-; ATTRIBUTOR-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
-; ATTRIBUTOR-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
-; ATTRIBUTOR: 3:
-; ATTRIBUTOR-NEXT: [[TMP4:%.*]] = tail call align 8 i8* @f2b(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1)
-; ATTRIBUTOR-NEXT: [[L:%.*]] = load i8, i8* [[TMP4]], align 8
-; ATTRIBUTOR-NEXT: store i8 [[L]], i8* @a1, align 8
-; ATTRIBUTOR-NEXT: br label [[TMP5]]
-; ATTRIBUTOR: 5:
-; ATTRIBUTOR-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
-; ATTRIBUTOR-NEXT: ret i8* [[TMP6]]
+; ATTRIBUTOR_MODULE-LABEL: define {{[^@]+}}@f1b
+; ATTRIBUTOR_MODULE-SAME: (i8* noalias nofree nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr
+; ATTRIBUTOR_MODULE-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; ATTRIBUTOR_MODULE-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; ATTRIBUTOR_MODULE: 3:
+; ATTRIBUTOR_MODULE-NEXT: [[TMP4:%.*]] = tail call align 8 i8* @f2b(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1)
+; ATTRIBUTOR_MODULE-NEXT: [[L:%.*]] = load i8, i8* [[TMP4]], align 8
+; ATTRIBUTOR_MODULE-NEXT: store i8 [[L]], i8* @a1, align 8
+; ATTRIBUTOR_MODULE-NEXT: br label [[TMP5]]
+; ATTRIBUTOR_MODULE: 5:
+; ATTRIBUTOR_MODULE-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; ATTRIBUTOR_MODULE-NEXT: ret i8* [[TMP6]]
+;
+; ATTRIBUTOR_CGSCC-LABEL: define {{[^@]+}}@f1b
+; ATTRIBUTOR_CGSCC-SAME: (i8* nofree nonnull readnone align 8 dereferenceable(1) "no-capture-maybe-returned" [[TMP0:%.*]]) local_unnamed_addr
+; ATTRIBUTOR_CGSCC-NEXT: [[TMP2:%.*]] = icmp eq i8* [[TMP0]], null
+; ATTRIBUTOR_CGSCC-NEXT: br i1 [[TMP2]], label [[TMP3:%.*]], label [[TMP5:%.*]]
+; ATTRIBUTOR_CGSCC: 3:
+; ATTRIBUTOR_CGSCC-NEXT: [[TMP4:%.*]] = tail call align 8 i8* @f2b(i8* noalias nofree nonnull readnone align 8 dereferenceable(1) @a1)
+; ATTRIBUTOR_CGSCC-NEXT: [[L:%.*]] = load i8, i8* [[TMP4]], align 8
+; ATTRIBUTOR_CGSCC-NEXT: store i8 [[L]], i8* @a1, align 8
+; ATTRIBUTOR_CGSCC-NEXT: br label [[TMP5]]
+; ATTRIBUTOR_CGSCC: 5:
+; ATTRIBUTOR_CGSCC-NEXT: [[TMP6:%.*]] = phi i8* [ [[TMP4]], [[TMP3]] ], [ [[TMP0]], [[TMP1:%.*]] ]
+; ATTRIBUTOR_CGSCC-NEXT: ret i8* [[TMP6]]
;
%2 = icmp eq i8* %0, null
br i1 %2, label %3, label %5
declare void @user_i32_ptr(i32*) readnone nounwind
define internal void @test8(i32* %a, i32* %b, i32* %c) {
-; ATTRIBUTOR: define internal void @test8(i32* noalias nocapture readnone align 4 %a, i32* noalias nocapture readnone align 4 %b, i32* noalias nocapture readnone %c)
+; ATTRIBUTOR_MODULE: define internal void @test8(i32* noalias nocapture readnone align 4 %a, i32* noalias nocapture readnone align 4 %b, i32* noalias nocapture readnone %c)
+; ATTRIBUTOR_CGSCC: define internal void @test8(i32* nocapture readnone align 4 %a, i32* nocapture readnone align 4 %b, i32* nocapture readnone %c)
call void @user_i32_ptr(i32* %a)
call void @user_i32_ptr(i32* %b)
call void @user_i32_ptr(i32* %c)
-; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=6 < %s | FileCheck %s
+; RUN: opt -S -passes=attributor -aa-pipeline='basic-aa' -attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=5 < %s | FileCheck %s
define dso_local i32 @visible(i32* noalias %A, i32* noalias %B) #0 {
entry:
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --function-signature --scrub-attributes
-; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,OLDPM
-; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,NEWPM
+; RUN: opt -attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,MODULE,ALL_BUT_OLD_CGSCCC
+; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC
+; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,MODULE,ALL_BUT_OLD_CGSCCC
+; RUN: opt -passes='attributor-cgscc' --attributor-disable=false -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=CHECK,CGSCC,ALL_BUT_OLD_CGSCCC
; UTC_ARGS: --turn off
-; CHECK: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)]
+; ALL_BUT_OLD_CGSCCC: @dead_with_blockaddress_users.l = constant [2 x i8*] [i8* inttoptr (i32 1 to i8*), i8* inttoptr (i32 1 to i8*)]
@dead_with_blockaddress_users.l = constant [2 x i8*] [i8* blockaddress(@dead_with_blockaddress_users, %lab0), i8* blockaddress(@dead_with_blockaddress_users, %end)]
declare void @no_return_call() nofree noreturn nounwind readnone
; This internal function has no live call sites, so all its BBs are considered dead,
; and nothing should be deduced for it.
-; CHECK-NOT: define internal i32 @dead_internal_func(i32 %0)
+; MODULE-NOT: define internal i32 @dead_internal_func(i32 %0)
define internal i32 @dead_internal_func(i32 %0) {
%2 = icmp slt i32 %0, 1
br i1 %2, label %3, label %5
ret i32 %2
}
-; CHECK-NOT: internal_load
+; MODULE-NOT: internal_load
define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
%2 = load i32, i32* %0, align 4
ret i32 %2
; TEST 1: Only first block is live.
; CHECK: Function Attrs: nofree noreturn nosync nounwind
-; CHECK-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly %ptr1, i32* nocapture nofree readnone %ptr2)
+; MODULE-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly %ptr1, i32* nocapture nofree readnone %ptr2)
+; CGSCC-NEXT: define i32 @first_block_no_return(i32 %a, i32* nocapture nofree nonnull readonly align 4 dereferenceable(4) %ptr1, i32* nocapture nofree readnone %ptr2)
define i32 @first_block_no_return(i32 %a, i32* nonnull %ptr1, i32* %ptr2) #0 {
entry:
call i32 @internal_load(i32* %ptr1)
; CHECK: define internal void @non_dead_d13()
; CHECK: define internal void @non_dead_d14()
; Verify we actually deduce information for these functions.
-; OLDPM: Function Attrs: nofree nosync nounwind readnone willreturn
-; NEWPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
-; CHECK-NEXT: define internal void @non_dead_d15()
-; CHECK-NOT: define internal void @dead_e
+; MODULE: Function Attrs: nofree nosync nounwind readnone willreturn
+; MODULE-NEXT: define internal void @non_dead_d15()
+; MODULE-NOT: define internal void @dead_e
+; CGSCC: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; CGSCC-NEXT: define internal void @non_dead_d15()
declare void @blowup() noreturn
define void @live_with_dead_entry() personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*) {
ret void
}
-; CHECK: define internal void @useless_arg_sink()
+; MODULE: define internal void @useless_arg_sink()
+; CGSCC: define internal void @useless_arg_sink(i32*{{.*}} %a)
define internal void @useless_arg_sink(i32* %a) {
ret void
}
-; CHECK: define internal void @useless_arg_almost_sink()
+; MODULE: define internal void @useless_arg_almost_sink()
+; CGSCC: define internal void @useless_arg_almost_sink(i32*{{.*}} %a)
define internal void @useless_arg_almost_sink(i32* %a) {
-; CHECK: call void @useless_arg_sink()
+; MODULE: call void @useless_arg_sink()
+; CGSCC: call void @useless_arg_sink(i32* noalias nofree readnone %a)
call void @useless_arg_sink(i32* %a)
ret void
}
; Check we do not annotate the function interface of this weak function.
; CHECK: define weak_odr void @useless_arg_ext(i32* %a)
define weak_odr void @useless_arg_ext(i32* %a) {
-; CHECK: call void @useless_arg_almost_sink()
+; MODULE: call void @useless_arg_almost_sink()
+; CGSCC: call void @useless_arg_almost_sink(i32* noalias nofree readnone %a)
call void @useless_arg_almost_sink(i32* %a)
ret void
}
; UTC_ARGS: --turn off
; Allow blockaddress users
-; CHECK-NOT @dead_with_blockaddress_users
+; ALL_BUT_OLD_CGSCCC-NOT @dead_with_blockaddress_users
define internal void @dead_with_blockaddress_users(i32* nocapture %pc) nounwind readonly {
entry:
br label %indirectgoto
-; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=4 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
+; RUN: opt -attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR
+; RUN: opt -passes=attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_NPM
; Copied from Transforms/FunctoinAttrs/norecurse.ll
; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
}
; ATTRIBUTOR: Function Attrs
-; ATTRIBUTOR-SAME: norecurse nosync readnone
+; FIXME: norecurse missing
+; ATTRIBUTOR-SAME: nosync readnone
; ATTRIBUTOR-NEXT: @called_by_norecurse_indirectly
define internal i32 @called_by_norecurse_indirectly() {
%a = call i32 @k()
declare void @unknown()
; Call an unknown function in a dead block.
-; ATTRIBUTOR: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
-; ATTRIBUTOR: define i32 @call_unknown_in_dead_block()
+; ATTRIBUTOR_NPM: Function Attrs: nofree norecurse nosync nounwind readnone willreturn
+; ATTRIBUTOR_NPM: define i32 @call_unknown_in_dead_block()
define i32 @call_unknown_in_dead_block() local_unnamed_addr {
ret i32 0
Dead:
-; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefix=ATTRIBUTOR
+; RUN: opt -passes=attributor --attributor-disable=false -attributor-max-iterations-verify -attributor-annotate-decl-cs -attributor-max-iterations=2 -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_MODULE
+; RUN: opt -passes=attributor-cgscc --attributor-disable=false -attributor-annotate-decl-cs -S < %s | FileCheck %s --check-prefixes=ATTRIBUTOR,ATTRIBUTOR_CGSCC
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
; TEST 1 (positive case)
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
; ATTRIBUTOR-NEXT: define void @only_return()
define void @only_return() #0 {
ret void
; }
; fact_maybe_not(-1) doesn't stop.
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
; ATTRIBUTOR-NOT: willreturn
; ATTRIBUTOR-NEXT: define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr
define i32 @fact_maybe_not_halt(i32 %0) local_unnamed_addr #0 {
; }
; FIXME: missing willreturn
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
; ATTRIBUTOR-NEXT: define i32 @fact_loop(i32 %0) local_unnamed_addr
define i32 @fact_loop(i32 %0) local_unnamed_addr #0 {
%2 = icmp slt i32 %0, 1
; ATTRIBUTOR-NEXT: declare void @will_return()
declare void @will_return() willreturn norecurse
-; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn
+; ATTRIBUTOR_MODULE: Function Attrs: noinline nounwind uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse nounwind uwtable willreturn
; ATTRIBUTOR-NEXT: define void @f1()
define void @f1() #0 {
tail call void @will_return()
ret void
}
-; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn
+; ATTRIBUTOR_MODULE: Function Attrs: noinline nounwind uwtable
+; FIXME: Because we do not derive norecurse in the module run anymore, willreturn is missing as well.
+; ATTRIBUTOR_MODULE-NOT: willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse nounwind uwtable willreturn
; ATTRIBUTOR-NEXT: define void @f2()
define void @f2() #0 {
tail call void @f1()
; TEST 9 (negative case)
; call willreturn function in endless loop.
-; ATTRIBUTOR: Function Attrs: noinline norecurse noreturn nounwind uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: noinline noreturn nounwind uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse noreturn nounwind uwtable
; ATTRIBUTOR-NOT: willreturn
; ATTRIBUTOR-NEXT: define void @call_will_return_but_has_loop()
define void @call_will_return_but_has_loop() #0 {
; }
; FIXME: missing willreturn
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
; ATTRIBUTOR-NEXT: define i32 @loop_constant_trip_count(i32* nocapture nofree readonly %0)
define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 {
br label %3
; }
; return ans;
; }
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
+; FNATTR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
; ATTRIBUTOR-NOT: willreturn
; ATTRIBUTOR-NEXT: define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture nofree readonly %2, i32 %3) local_unnamed_addr
define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2, i32 %3) local_unnamed_addr #0 {
; FIXME: missing willreturn
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readonly uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readonly uwtable
; ATTRIBUTOR-NEXT: define i32 @loop_trip_dec(i32 %0, i32* nocapture nofree readonly %1) local_unnamed_addr
define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr #0 {
; TEST 14 (positive case)
; multiple return
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable willreturn
; ATTRIBUTOR-NEXT: define i32 @multiple_return(i32 %a)
define i32 @multiple_return(i32 %a) #0 {
%b = icmp eq i32 %a, 0
; unreachable exit
; 15.1 (positive case)
-; ATTRIBUTOR: Function Attrs: noinline norecurse nounwind uwtable willreturn
+; ATTRIBUTOR_MODULE: Function Attrs: noinline nounwind uwtable willreturn
+; ATTRIBUTOR_CGSCC: Function Attrs: noinline norecurse nounwind uwtable willreturn
; ATTRIBUTOR-NEXT: define void @unreachable_exit_positive1()
define void @unreachable_exit_positive1() #0 {
tail call void @will_return()
}
; FIXME: missing willreturn
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline nosync nounwind readnone uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse nosync nounwind readnone uwtable
; ATTRIBUTOR-NEXT: define i32 @unreachable_exit_positive2(i32 %0)
define i32 @unreachable_exit_positive2(i32) local_unnamed_addr #0 {
%2 = icmp slt i32 %0, 1
unreachable
}
-; ATTRIBUTOR: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable
+; ATTRIBUTOR_MODULE: Function Attrs: nofree noinline noreturn nosync nounwind readnone uwtable
+; ATTRIBUTOR_CGSCC: Function Attrs: nofree noinline norecurse noreturn nosync nounwind readnone uwtable
; ATTRIBUTOR-NOT: willreturn
; ATTRIBUTOR-NEXT: define void @unreachable_exit_negative2()
define void @unreachable_exit_negative2() #0 {