Skip to content

Commit

Permalink
[AlwaysInliner] Make legacy pass like the new pass
Browse files Browse the repository at this point in the history
The legacy pass is only used in AMDGPU codegen, which doesn't care about running it in call graph order (it actually has to work around that fact).

Make the legacy pass a module pass and share code with the new pass.

This allows us to remove the legacy inliner infrastructure.

Reviewed By: mtrofin

Differential Revision: https://reviews.llvm.org/D146446
  • Loading branch information
aeubanks committed Mar 21, 2023
1 parent 2356bf2 commit fa6ea7a
Show file tree
Hide file tree
Showing 12 changed files with 88 additions and 677 deletions.
4 changes: 0 additions & 4 deletions llvm/include/llvm/Analysis/AliasAnalysis.h
Original file line number Diff line number Diff line change
Expand Up @@ -985,10 +985,6 @@ ImmutablePass *createExternalAAWrapperPass(
/// getAnalysisUsage.
AAResults createLegacyPMAAResults(Pass &P, Function &F, BasicAAResult &BAR);

/// A helper for the legacy pass manager to populate \p AU to add uses to make
/// sure the analyses required by \p createLegacyPMAAResults are available.
void getAAResultsAnalysisUsage(AnalysisUsage &AU);

} // end namespace llvm

#endif // LLVM_ANALYSIS_ALIASANALYSIS_H
56 changes: 0 additions & 56 deletions llvm/include/llvm/Transforms/IPO/Inliner.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,62 +23,6 @@ class AssumptionCacheTracker;
class CallGraph;
class ProfileSummaryInfo;

/// This class contains all of the helper code which is used to perform the
/// inlining operations that do not depend on the policy. It contains the core
/// bottom-up inlining infrastructure that specific inliner passes use.
struct LegacyInlinerBase : public CallGraphSCCPass {
explicit LegacyInlinerBase(char &ID);
explicit LegacyInlinerBase(char &ID, bool InsertLifetime);

/// For this class, we declare that we require and preserve the call graph.
/// If the derived class implements this method, it should always explicitly
/// call the implementation here.
void getAnalysisUsage(AnalysisUsage &Info) const override;

using llvm::Pass::doInitialization;

bool doInitialization(CallGraph &CG) override;

/// Main run interface method, this implements the interface required by the
/// Pass class.
bool runOnSCC(CallGraphSCC &SCC) override;

using llvm::Pass::doFinalization;

/// Remove now-dead linkonce functions at the end of processing to avoid
/// breaking the SCC traversal.
bool doFinalization(CallGraph &CG) override;

/// This method must be implemented by the subclass to determine the cost of
/// inlining the specified call site. If the cost returned is greater than
/// the current inline threshold, the call site is not inlined.
virtual InlineCost getInlineCost(CallBase &CB) = 0;

/// Remove dead functions.
///
/// This also includes a hack in the form of the 'AlwaysInlineOnly' flag
/// which restricts it to deleting functions with an 'AlwaysInline'
/// attribute. This is useful for the InlineAlways pass that only wants to
/// deal with that subset of the functions.
bool removeDeadFunctions(CallGraph &CG, bool AlwaysInlineOnly = false);

/// This function performs the main work of the pass. The default of
/// Inlinter::runOnSCC() calls skipSCC() before calling this method, but
/// derived classes which cannot be skipped can override that method and call
/// this function unconditionally.
bool inlineCalls(CallGraphSCC &SCC);

private:
// Insert @llvm.lifetime intrinsics.
bool InsertLifetime = true;

protected:
AssumptionCacheTracker *ACT;
ProfileSummaryInfo *PSI;
std::function<const TargetLibraryInfo &(Function &)> GetTLI;
ImportedFunctionsInliningStatistics ImportedFunctionsStats;
};

/// The inliner pass for the new pass manager.
///
/// This pass wires together the inlining utilities and the inline cost
Expand Down
7 changes: 2 additions & 5 deletions llvm/include/llvm/Transforms/Utils/Cloning.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,18 +203,15 @@ void CloneAndPruneFunctionInto(Function *NewFunc, const Function *OldFunc,
class InlineFunctionInfo {
public:
explicit InlineFunctionInfo(
CallGraph *cg = nullptr,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache = nullptr,
ProfileSummaryInfo *PSI = nullptr,
BlockFrequencyInfo *CallerBFI = nullptr,
BlockFrequencyInfo *CalleeBFI = nullptr, bool UpdateProfile = true)
: CG(cg), GetAssumptionCache(GetAssumptionCache), PSI(PSI),
CallerBFI(CallerBFI), CalleeBFI(CalleeBFI),
UpdateProfile(UpdateProfile) {}
: GetAssumptionCache(GetAssumptionCache), PSI(PSI), CallerBFI(CallerBFI),
CalleeBFI(CalleeBFI), UpdateProfile(UpdateProfile) {}

/// If non-null, InlineFunction will update the callgraph to reflect the
/// changes it makes.
CallGraph *CG;
function_ref<AssumptionCache &(Function &)> GetAssumptionCache;
ProfileSummaryInfo *PSI;
BlockFrequencyInfo *CallerBFI, *CalleeBFI;
Expand Down
11 changes: 0 additions & 11 deletions llvm/lib/Analysis/AliasAnalysis.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -935,14 +935,3 @@ bool llvm::isNotVisibleOnUnwind(const Value *Object,

return false;
}

void llvm::getAAResultsAnalysisUsage(AnalysisUsage &AU) {
// This function needs to be in sync with llvm::createLegacyPMAAResults -- if
// more alias analyses are added to llvm::createLegacyPMAAResults, they need
// to be added here also.
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.addUsedIfAvailable<ScopedNoAliasAAWrapperPass>();
AU.addUsedIfAvailable<TypeBasedAAWrapperPass>();
AU.addUsedIfAvailable<GlobalsAAWrapperPass>();
AU.addUsedIfAvailable<ExternalAAWrapperPass>();
}
6 changes: 0 additions & 6 deletions llvm/lib/Target/AMDGPU/AMDGPUTargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -974,12 +974,6 @@ void AMDGPUPassConfig::addIRPasses() {
// Function calls are not supported, so make sure we inline everything.
addPass(createAMDGPUAlwaysInlinePass());
addPass(createAlwaysInlinerLegacyPass());
// We need to add the barrier noop pass, otherwise adding the function
// inlining pass will cause all of the PassConfigs passes to be run
// one function at a time, which means if we have a module with two
// functions, then we will generate code for the first function
// without ever running any passes on the second.
addPass(createBarrierNoopPass());

// Handle uses of OpenCL image2d_t, image3d_t and sampler_t arguments.
if (TM.getTargetTriple().getArch() == Triple::r600)
Expand Down
136 changes: 56 additions & 80 deletions llvm/lib/Transforms/IPO/AlwaysInliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,13 @@ using namespace llvm;

#define DEBUG_TYPE "inline"

PreservedAnalyses AlwaysInlinerPass::run(Module &M,
ModuleAnalysisManager &MAM) {
// Add inline assumptions during code generation.
FunctionAnalysisManager &FAM =
MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
return FAM.getResult<AssumptionAnalysis>(F);
};
auto &PSI = MAM.getResult<ProfileSummaryAnalysis>(M);
namespace {

bool AlwaysInlineImpl(
Module &M, bool InsertLifetime, ProfileSummaryInfo &PSI,
function_ref<AssumptionCache &(Function &)> GetAssumptionCache,
function_ref<AAResults &(Function &)> GetAAR,
function_ref<BlockFrequencyInfo &(Function &)> GetBFI) {
SmallSetVector<CallBase *, 16> Calls;
bool Changed = false;
SmallVector<Function *, 16> InlinedFunctions;
Expand Down Expand Up @@ -65,14 +62,12 @@ PreservedAnalyses AlwaysInlinerPass::run(Module &M,
DebugLoc DLoc = CB->getDebugLoc();
BasicBlock *Block = CB->getParent();

InlineFunctionInfo IFI(
/*cg=*/nullptr, GetAssumptionCache, &PSI,
&FAM.getResult<BlockFrequencyAnalysis>(*Caller),
&FAM.getResult<BlockFrequencyAnalysis>(F));
InlineFunctionInfo IFI(GetAssumptionCache, &PSI,
GetBFI ? &GetBFI(*Caller) : nullptr,
GetBFI ? &GetBFI(F) : nullptr);

InlineResult Res =
InlineFunction(*CB, IFI, /*MergeAttributes=*/true,
&FAM.getResult<AAManager>(F), InsertLifetime);
InlineResult Res = InlineFunction(*CB, IFI, /*MergeAttributes=*/true,
&GetAAR(F), InsertLifetime);
if (!Res.isSuccess()) {
ORE.emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc,
Expand Down Expand Up @@ -127,95 +122,76 @@ PreservedAnalyses AlwaysInlinerPass::run(Module &M,
}
}

return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
return Changed;
}

namespace {

/// Inliner pass which only handles "always inline" functions.
///
/// Unlike the \c AlwaysInlinerPass, this uses the more heavyweight \c Inliner
/// base class to provide several facilities such as array alloca merging.
class AlwaysInlinerLegacyPass : public LegacyInlinerBase {
struct AlwaysInlinerLegacyPass : public ModulePass {
bool InsertLifetime;

public:
AlwaysInlinerLegacyPass() : LegacyInlinerBase(ID, /*InsertLifetime*/ true) {
initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry());
}
AlwaysInlinerLegacyPass()
: AlwaysInlinerLegacyPass(/*InsertLifetime*/ true) {}

AlwaysInlinerLegacyPass(bool InsertLifetime)
: LegacyInlinerBase(ID, InsertLifetime) {
: ModulePass(ID), InsertLifetime(InsertLifetime) {
initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry());
}

/// Main run interface method. We override here to avoid calling skipSCC().
bool runOnSCC(CallGraphSCC &SCC) override { return inlineCalls(SCC); }
bool runOnModule(Module &M) override {

auto &PSI = getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
auto GetAAR = [&](Function &F) -> AAResults & {
return getAnalysis<AAResultsWrapperPass>(F).getAAResults();
};
auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
return getAnalysis<AssumptionCacheTracker>().getAssumptionCache(F);
};

return AlwaysInlineImpl(M, InsertLifetime, PSI, GetAssumptionCache, GetAAR,
/*GetBFI*/ nullptr);
}

static char ID; // Pass identification, replacement for typeid

InlineCost getInlineCost(CallBase &CB) override;

using llvm::Pass::doFinalization;
bool doFinalization(CallGraph &CG) override {
return removeDeadFunctions(CG, /*AlwaysInlineOnly=*/true);
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<AAResultsWrapperPass>();
AU.addRequired<ProfileSummaryInfoWrapperPass>();
}
};
}

} // namespace

char AlwaysInlinerLegacyPass::ID = 0;
INITIALIZE_PASS_BEGIN(AlwaysInlinerLegacyPass, "always-inline",
"Inliner for always_inline functions", false, false)
INITIALIZE_PASS_DEPENDENCY(AAResultsWrapperPass)
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(CallGraphWrapperPass)
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_END(AlwaysInlinerLegacyPass, "always-inline",
"Inliner for always_inline functions", false, false)

Pass *llvm::createAlwaysInlinerLegacyPass(bool InsertLifetime) {
return new AlwaysInlinerLegacyPass(InsertLifetime);
}

/// Get the inline cost for the always-inliner.
///
/// The always inliner *only* handles functions which are marked with the
/// attribute to force inlining. As such, it is dramatically simpler and avoids
/// using the powerful (but expensive) inline cost analysis. Instead it uses
/// a very simple and boring direct walk of the instructions looking for
/// impossible-to-inline constructs.
///
/// Note, it would be possible to go to some lengths to cache the information
/// computed here, but as we only expect to do this for relatively few and
/// small functions which have the explicit attribute to force inlining, it is
/// likely not worth it in practice.
InlineCost AlwaysInlinerLegacyPass::getInlineCost(CallBase &CB) {
Function *Callee = CB.getCalledFunction();

// Only inline direct calls to functions with always-inline attributes
// that are viable for inlining.
if (!Callee)
return InlineCost::getNever("indirect call");

// When callee coroutine function is inlined into caller coroutine function
// before coro-split pass,
// coro-early pass can not handle this quiet well.
// So we won't inline the coroutine function if it have not been unsplited
if (Callee->isPresplitCoroutine())
return InlineCost::getNever("unsplited coroutine call");

// FIXME: We shouldn't even get here for declarations.
if (Callee->isDeclaration())
return InlineCost::getNever("no definition");

if (!CB.hasFnAttr(Attribute::AlwaysInline))
return InlineCost::getNever("no alwaysinline attribute");

if (Callee->hasFnAttribute(Attribute::AlwaysInline) && CB.isNoInline())
return InlineCost::getNever("noinline call site attribute");

auto IsViable = isInlineViable(*Callee);
if (!IsViable.isSuccess())
return InlineCost::getNever(IsViable.getFailureReason());

return InlineCost::getAlways("always inliner");
PreservedAnalyses AlwaysInlinerPass::run(Module &M,
ModuleAnalysisManager &MAM) {
FunctionAnalysisManager &FAM =
MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
return FAM.getResult<AssumptionAnalysis>(F);
};
auto GetBFI = [&](Function &F) -> BlockFrequencyInfo & {
return FAM.getResult<BlockFrequencyAnalysis>(F);
};
auto GetAAR = [&](Function &F) -> AAResults & {
return FAM.getResult<AAManager>(F);
};
auto &PSI = MAM.getResult<ProfileSummaryAnalysis>(M);

bool Changed = AlwaysInlineImpl(M, InsertLifetime, PSI, GetAssumptionCache,
GetAAR, GetBFI);

return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all();
}
Loading

0 comments on commit fa6ea7a

Please sign in to comment.