diff options
Diffstat (limited to 'llvm/lib/Transforms/IPO/AlwaysInliner.cpp')
| -rw-r--r-- | llvm/lib/Transforms/IPO/AlwaysInliner.cpp | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/llvm/lib/Transforms/IPO/AlwaysInliner.cpp b/llvm/lib/Transforms/IPO/AlwaysInliner.cpp new file mode 100644 index 000000000000..c50805692b98 --- /dev/null +++ b/llvm/lib/Transforms/IPO/AlwaysInliner.cpp @@ -0,0 +1,173 @@ +//===- InlineAlways.cpp - Code to inline always_inline functions ----------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements a custom inliner that handles only functions that +// are marked as "always inline". +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/IPO/AlwaysInliner.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/Analysis/AssumptionCache.h" +#include "llvm/Analysis/InlineCost.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/CallSite.h" +#include "llvm/IR/CallingConv.h" +#include "llvm/IR/DataLayout.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Type.h" +#include "llvm/Transforms/IPO.h" +#include "llvm/Transforms/IPO/Inliner.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ModuleUtils.h" + +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(); + std::function<AssumptionCache &(Function &)> GetAssumptionCache = + [&](Function &F) -> AssumptionCache & { + return FAM.getResult<AssumptionAnalysis>(F); + }; + InlineFunctionInfo IFI(/*cg=*/nullptr, &GetAssumptionCache); + + SmallSetVector<CallSite, 16> Calls; + bool Changed = false; + SmallVector<Function *, 16> InlinedFunctions; + for (Function &F : M) + if (!F.isDeclaration() && F.hasFnAttribute(Attribute::AlwaysInline) && + isInlineViable(F)) { + Calls.clear(); + + for (User *U : F.users()) + if (auto CS = CallSite(U)) + if (CS.getCalledFunction() == &F) + Calls.insert(CS); + + for (CallSite CS : Calls) + // FIXME: We really shouldn't be able to fail to inline at this point! + // We should do something to log or check the inline failures here. + Changed |= + InlineFunction(CS, IFI, /*CalleeAAR=*/nullptr, InsertLifetime); + + // Remember to try and delete this function afterward. This both avoids + // re-walking the rest of the module and avoids dealing with any iterator + // invalidation issues while deleting functions. + InlinedFunctions.push_back(&F); + } + + // Remove any live functions. + erase_if(InlinedFunctions, [&](Function *F) { + F->removeDeadConstantUsers(); + return !F->isDefTriviallyDead(); + }); + + // Delete the non-comdat ones from the module and also from our vector. + auto NonComdatBegin = partition( + InlinedFunctions, [&](Function *F) { return F->hasComdat(); }); + for (Function *F : make_range(NonComdatBegin, InlinedFunctions.end())) + M.getFunctionList().erase(F); + InlinedFunctions.erase(NonComdatBegin, InlinedFunctions.end()); + + if (!InlinedFunctions.empty()) { + // Now we just have the comdat functions. Filter out the ones whose comdats + // are not actually dead. + filterDeadComdatFunctions(M, InlinedFunctions); + // The remaining functions are actually dead. + for (Function *F : InlinedFunctions) + M.getFunctionList().erase(F); + } + + return Changed ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} + +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 { + +public: + AlwaysInlinerLegacyPass() : LegacyInlinerBase(ID, /*InsertLifetime*/ true) { + initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + AlwaysInlinerLegacyPass(bool InsertLifetime) + : LegacyInlinerBase(ID, InsertLifetime) { + initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry()); + } + + /// Main run interface method. We override here to avoid calling skipSCC(). + bool runOnSCC(CallGraphSCC &SCC) override { return inlineCalls(SCC); } + + static char ID; // Pass identification, replacement for typeid + + InlineCost getInlineCost(CallSite CS) override; + + using llvm::Pass::doFinalization; + bool doFinalization(CallGraph &CG) override { + return removeDeadFunctions(CG, /*AlwaysInlineOnly=*/true); + } +}; +} + +char AlwaysInlinerLegacyPass::ID = 0; +INITIALIZE_PASS_BEGIN(AlwaysInlinerLegacyPass, "always-inline", + "Inliner for always_inline functions", false, false) +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(CallSite CS) { + Function *Callee = CS.getCalledFunction(); + + // Only inline direct calls to functions with always-inline attributes + // that are viable for inlining. + if (!Callee) + return InlineCost::getNever("indirect call"); + + // FIXME: We shouldn't even get here for declarations. + if (Callee->isDeclaration()) + return InlineCost::getNever("no definition"); + + if (!CS.hasFnAttr(Attribute::AlwaysInline)) + return InlineCost::getNever("no alwaysinline attribute"); + + auto IsViable = isInlineViable(*Callee); + if (!IsViable) + return InlineCost::getNever(IsViable.message); + + return InlineCost::getAlways("always inliner"); +} |
