1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 18:54:02 +01:00

[Attributor] Add an Attributor CGSCC pass and run it

In addition to the module pass, this patch introduces a CGSCC pass that
runs the Attributor on a strongly connected component of the call graph
(both old and new PM). The Attributor was always design to be used on a
subset of functions which makes this patch mostly mechanical.

The one change is that we give up `norecurse` deduction in the module
pass in favor of doing it during the CGSCC pass. This makes the
interfaces simpler but can be revisited if needed.

Reviewed By: hfinkel

Differential Revision: https://reviews.llvm.org/D70767
This commit is contained in:
Johannes Doerfert 2019-11-27 00:30:12 -06:00
parent 4104d396cb
commit 9354843292
30 changed files with 412 additions and 180 deletions

View File

@ -76,6 +76,7 @@ void initializeArgPromotionPass(PassRegistry&);
void initializeAssumptionCacheTrackerPass(PassRegistry&);
void initializeAtomicExpandPass(PassRegistry&);
void initializeAttributorLegacyPassPass(PassRegistry&);
void initializeAttributorCGSCCLegacyPassPass(PassRegistry &);
void initializeBDCELegacyPassPass(PassRegistry&);
void initializeBarrierNoopPass(PassRegistry&);
void initializeBasicAAWrapperPassPass(PassRegistry&);

View File

@ -193,6 +193,7 @@ namespace {
(void) llvm::createInstructionNamerPass();
(void) llvm::createMetaRenamerPass();
(void) llvm::createAttributorLegacyPass();
(void) llvm::createAttributorCGSCCLegacyPass();
(void) llvm::createPostOrderFunctionAttrsLegacyPass();
(void) llvm::createReversePostOrderFunctionAttrsPass();
(void) llvm::createMergeFunctionsPass();

View File

@ -101,13 +101,16 @@
#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 {
@ -537,25 +540,16 @@ public:
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.
@ -571,20 +565,10 @@ private:
/// 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>>;
@ -624,11 +608,11 @@ struct InformationCache {
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.
@ -657,8 +641,8 @@ private:
/// 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.
@ -695,15 +679,18 @@ private:
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);
@ -717,7 +704,7 @@ struct Attributor {
/// 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
@ -1036,6 +1023,17 @@ struct Attributor {
/// 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.
///
@ -1067,9 +1065,10 @@ private:
// 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
@ -1080,6 +1079,15 @@ private:
}
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())
@ -1117,7 +1125,8 @@ private:
/// 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.
///{
@ -1154,9 +1163,19 @@ private:
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;
@ -1834,8 +1853,13 @@ raw_ostream &operator<<(raw_ostream &OS, const IntegerRangeState &State);
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

View File

@ -139,6 +139,7 @@ void LTOCodeGenerator::initializeLTOPasses() {
initializeJumpThreadingPass(R);
initializeSROALegacyPassPass(R);
initializeAttributorLegacyPassPass(R);
initializeAttributorCGSCCLegacyPassPass(R);
initializePostOrderFunctionAttrsLegacyPassPass(R);
initializeReversePostOrderFunctionAttrsLegacyPassPass(R);
initializeGlobalsAAWrapperPassPass(R);

View File

@ -748,6 +748,7 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
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.
@ -830,6 +831,8 @@ PassBuilder::buildModuleSimplificationPipeline(OptimizationLevel Level,
IP.HotCallSiteThreshold = 0;
MainCGPipeline.addPass(InlinerPass(IP));
MainCGPipeline.addPass(AttributorCGSCCPass());
// Now deduce any function attributes based in the current code.
MainCGPipeline.addPass(PostOrderFunctionAttrsPass());

View File

@ -108,6 +108,7 @@ CGSCC_ANALYSIS("pass-instrumentation", PassInstrumentationAnalysis(PIC))
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())

View File

@ -20,6 +20,8 @@
#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"
@ -245,20 +247,6 @@ Argument *IRPosition::getAssociatedArgument() const {
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) {
@ -1201,10 +1189,10 @@ ChangeStatus AAReturnedValuesImpl::manifest(Attributor &A) {
"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;
};
@ -4694,7 +4682,7 @@ struct AAHeapToStackImpl : public AAHeapToStack {
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();
@ -6378,7 +6366,7 @@ struct AAValueConstantRangeCallSiteArgument : AAValueConstantRangeFloating {
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
@ -6665,7 +6653,7 @@ bool Attributor::checkForAllReadWriteInstructions(
return true;
}
ChangeStatus Attributor::run(Module &M) {
ChangeStatus Attributor::run() {
LLVM_DEBUG(dbgs() << "[Attributor] Identified and initialized "
<< AllAbstractAttributes.size()
<< " abstract attributes.\n");
@ -6852,9 +6840,9 @@ ChangeStatus Attributor::run(Module &M) {
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.
{
@ -6874,11 +6862,12 @@ ChangeStatus Attributor::run(Module &M) {
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)) {
@ -6915,13 +6904,18 @@ ChangeStatus Attributor::run(Module &M) {
}
}
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);
@ -6935,7 +6929,10 @@ ChangeStatus Attributor::run(Module &M) {
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.
@ -6949,9 +6946,9 @@ ChangeStatus Attributor::run(Module &M) {
// 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) {
@ -6977,17 +6974,18 @@ ChangeStatus Attributor::run(Module &M) {
}
}
// 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) {
@ -7093,7 +7091,8 @@ bool Attributor::registerFunctionSignatureRewrite(
return true;
}
ChangeStatus Attributor::rewriteFunctionSignatures() {
ChangeStatus Attributor::rewriteFunctionSignatures(
SmallPtrSetImpl<Function *> &ModifiedFns) {
ChangeStatus Changed = ChangeStatus::UNCHANGED;
for (auto &It : ArgumentReplacementMap) {
@ -7254,11 +7253,20 @@ ChangeStatus Attributor::rewriteFunctionSignatures() {
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;
}
@ -7588,50 +7596,90 @@ void AbstractAttribute::print(raw_ostream &OS) const {
/// 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();
}
@ -7652,7 +7700,13 @@ struct AttributorLegacyPass : public ModulePass {
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 {
@ -7661,11 +7715,53 @@ struct AttributorLegacyPass : public ModulePass {
}
};
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;
@ -7818,3 +7914,11 @@ INITIALIZE_PASS_BEGIN(AttributorLegacyPass, "attributor",
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)

View File

@ -47,6 +47,7 @@ void llvm::initializeIPO(PassRegistry &Registry) {
initializeMergeFunctionsLegacyPassPass(Registry);
initializePartialInlinerLegacyPassPass(Registry);
initializeAttributorLegacyPassPass(Registry);
initializeAttributorCGSCCLegacyPassPass(Registry);
initializePostOrderFunctionAttrsLegacyPassPass(Registry);
initializeReversePostOrderFunctionAttrsLegacyPassPass(Registry);
initializePruneEHPass(Registry);

View File

@ -551,6 +551,9 @@ void PassManagerBuilder::populateModulePassManager(
// 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)
@ -559,9 +562,6 @@ void PassManagerBuilder::populateModulePassManager(
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());
@ -599,6 +599,9 @@ void PassManagerBuilder::populateModulePassManager(
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)
@ -935,6 +938,9 @@ void PassManagerBuilder::addLTOOptimizationPasses(legacy::PassManagerBase &PM) {
// 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)

View File

@ -113,6 +113,7 @@
; 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
@ -140,6 +141,7 @@
; 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)

View File

@ -79,6 +79,7 @@
; 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
@ -105,6 +106,7 @@
; 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)

View File

@ -48,6 +48,7 @@
; 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
@ -77,6 +78,7 @@
; 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

View File

@ -59,6 +59,7 @@
; 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
@ -85,6 +86,7 @@
; 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

View File

@ -52,6 +52,7 @@
; 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
@ -105,6 +106,7 @@
; 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

View File

@ -59,6 +59,7 @@
; 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
@ -85,6 +86,7 @@
; 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

View File

@ -29,11 +29,11 @@
; 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
@ -59,6 +59,7 @@
; 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

View File

@ -29,6 +29,7 @@
; 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
@ -36,7 +37,6 @@
; 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
@ -62,6 +62,7 @@
; 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

View File

@ -29,11 +29,11 @@
; 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
@ -59,6 +59,7 @@
; 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

View File

@ -46,6 +46,7 @@
; 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

View File

@ -30,7 +30,7 @@ define void @caller(i32** %Y, %struct.pair* %P) {
; 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

View File

@ -1,9 +1,13 @@
; 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)

View File

@ -1,5 +1,5 @@
; 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.

View File

@ -9,7 +9,7 @@ define i64 @fn2() {
; 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:

View File

@ -1,5 +1,5 @@
; 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
;
;
; /---------------------------------------|

View File

@ -1,5 +1,6 @@
; 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>
;
@ -53,40 +54,60 @@ entry:
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

View File

@ -1,5 +1,8 @@
; 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"
@ -135,9 +138,14 @@ define internal i8* @f3(i8* readnone %0) local_unnamed_addr #0 {
; 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
@ -146,18 +154,31 @@ define align 4 i32* @test7(i32* align 32 %p) #0 {
; 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
@ -262,7 +283,8 @@ define void @test8_helper() {
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)

View File

@ -1,4 +1,4 @@
; 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:

View File

@ -1,9 +1,11 @@
; 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
@ -23,7 +25,7 @@ declare i32 @bar() nosync 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
@ -47,7 +49,7 @@ define i32 @volatile_load(i32*) norecurse nounwind uwtable {
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
@ -55,7 +57,8 @@ define internal i32 @internal_load(i32*) norecurse nounwind uwtable {
; 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)
@ -792,10 +795,11 @@ define internal void @dead_e2() { ret void }
; 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*) {
@ -852,14 +856,17 @@ live_with_dead_entry:
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
}
@ -867,7 +874,8 @@ define internal void @useless_arg_almost_sink(i32* %a) {
; 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
}
@ -926,7 +934,7 @@ define i32 @switch_default_caller() {
; 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

View File

@ -1,4 +1,5 @@
; 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
@ -72,7 +73,8 @@ define void @m() norecurse {
}
; 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()
@ -138,8 +140,8 @@ define i32 @eval_func2(i32 (i32)* , i32) local_unnamed_addr "null-pointer-is-val
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:

View File

@ -1,4 +1,5 @@
; 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"
@ -8,7 +9,8 @@ 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
@ -51,7 +53,8 @@ define i32 @fib(i32 %0) local_unnamed_addr #0 {
; }
; 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 {
@ -85,7 +88,8 @@ 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
@ -223,14 +227,18 @@ define void @call_maybe_noreturn() #0 {
; 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()
@ -241,7 +249,8 @@ define void @f2() #0 {
; 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 {
@ -288,7 +297,8 @@ declare i32 @__gxx_personality_v0(...)
; }
; 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
@ -318,7 +328,9 @@ define i32 @loop_constant_trip_count(i32* nocapture readonly %0) #0 {
; }
; 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 {
@ -354,7 +366,8 @@ define i32 @loop_trip_count_unbound(i32 %0, i32 %1, i32* nocapture readonly %2,
; 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 {
@ -383,7 +396,8 @@ define i32 @loop_trip_dec(i32 %0, i32* nocapture readonly %1) local_unnamed_addr
; 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
@ -399,7 +413,8 @@ f:
; 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()
@ -411,7 +426,8 @@ unreachable_label:
}
; 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
@ -449,7 +465,8 @@ unreachable_label:
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 {