Changeset View
Changeset View
Standalone View
Standalone View
lib/Transform/ScopInliner.cpp
Show All 16 Lines | |||||
#include "polly/LinkAllPasses.h" | #include "polly/LinkAllPasses.h" | ||||
#include "polly/RegisterPasses.h" | #include "polly/RegisterPasses.h" | ||||
#include "polly/ScopDetection.h" | #include "polly/ScopDetection.h" | ||||
#include "llvm/Analysis/CallGraphSCCPass.h" | #include "llvm/Analysis/CallGraphSCCPass.h" | ||||
#include "llvm/IR/LLVMContext.h" | #include "llvm/IR/LLVMContext.h" | ||||
#include "llvm/IR/PassManager.h" | #include "llvm/IR/PassManager.h" | ||||
#include "llvm/Passes/PassBuilder.h" | #include "llvm/Passes/PassBuilder.h" | ||||
#include "llvm/Transforms/IPO/AlwaysInliner.h" | #include "llvm/Transforms/IPO/AlwaysInliner.h" | ||||
#include "llvm/Transforms/IPO/Inliner.h" | |||||
#define DEBUG_TYPE "polly-scop-inliner" | #define DEBUG_TYPE "polly-scop-inliner" | ||||
using namespace polly; | using namespace polly; | ||||
extern bool polly::PollyAllowFullFunction; | extern bool polly::PollyAllowFullFunction; | ||||
namespace { | namespace { | ||||
class ScopInliner : public CallGraphSCCPass { | class ScopInliner : public LegacyInlinerBase { | ||||
private: | |||||
std::map<Function *, InlineCost> InlineCostCache; | |||||
public: | public: | ||||
static char ID; | static char ID; | ||||
ScopInliner() : CallGraphSCCPass(ID) {} | ScopInliner() : LegacyInlinerBase(ID, /*InsertLifetime*/ true) { | ||||
// initializeAlwaysInlinerLegacyPassPass(*PassRegistry::getPassRegistry()); | |||||
} | |||||
bool doInitialization(CallGraph &CG) override { | bool doInitialization(CallGraph &CG) override { | ||||
if (!polly::PollyAllowFullFunction) { | if (!polly::PollyAllowFullFunction) { | ||||
report_fatal_error( | report_fatal_error( | ||||
"Aborting from ScopInliner because it only makes sense to run with " | "Aborting from ScopInliner because it only makes sense to run with " | ||||
"-polly-allow-full-function. " | "-polly-allow-full-function. " | ||||
"The heurtistic for ScopInliner checks that the full function is a " | "The heurtistic for ScopInliner checks that the full function is a " | ||||
"Scop, which happens if and only if polly-allow-full-function is " | "Scop, which happens if and only if polly-allow-full-function is " | ||||
" enabled. " | " enabled. " | ||||
" If not, the entry block is not included in the Scop"); | " If not, the entry block is not included in the Scop"); | ||||
} | } | ||||
return true; | return LegacyInlinerBase::doInitialization(CG); | ||||
} | } | ||||
bool runOnSCC(CallGraphSCC &SCC) override { | bool doFinalization(CallGraph &CG) override { | ||||
// We do not try to inline non-trivial SCCs because this would lead to | InlineCostCache.clear(); | ||||
// "infinite" inlining if we are not careful. | return LegacyInlinerBase::doFinalization(CG); | ||||
if (SCC.size() > 1) | } | ||||
return false; | |||||
assert(SCC.size() == 1 && "found empty SCC"); | |||||
Function *F = (*SCC.begin())->getFunction(); | |||||
// If the function is a nullptr, or the function is a declaration. | InlineCost getInlineCost(CallSite CS) override { | ||||
if (!F) | Function *F = CS.getCalledFunction(); | ||||
return false; | |||||
if (F->isDeclaration()) { | if (!F || F->isDeclaration()) | ||||
DEBUG(dbgs() << "Skipping " << F->getName() | return InlineCost::getNever(); | ||||
<< "because it is a declaration.\n"); | |||||
return false; | DEBUG(dbgs() << "Scop inliner running on: " << F->getName() << " | "); | ||||
std::map<Function *, InlineCost>::iterator It; | |||||
if ((It = InlineCostCache.find(F)) != InlineCostCache.end()) { | |||||
DEBUG(dbgs() << "(cached) will inline? " << (bool)It->second << ".\n"); | |||||
return It->second; | |||||
} | } | ||||
PassBuilder PB; | PassBuilder PB; | ||||
FunctionAnalysisManager FAM; | FunctionAnalysisManager FAM; | ||||
FAM.registerPass([] { return ScopAnalysis(); }); | FAM.registerPass([] { return ScopAnalysis(); }); | ||||
PB.registerFunctionAnalyses(FAM); | PB.registerFunctionAnalyses(FAM); | ||||
RegionInfo &RI = FAM.getResult<RegionInfoAnalysis>(*F); | RegionInfo &RI = FAM.getResult<RegionInfoAnalysis>(*F); | ||||
ScopDetection &SD = FAM.getResult<ScopAnalysis>(*F); | ScopDetection &SD = FAM.getResult<ScopAnalysis>(*F); | ||||
const bool HasScopAsTopLevelRegion = | const auto TopLevelRegion = RI.getTopLevelRegion(); | ||||
SD.ValidRegions.count(RI.getTopLevelRegion()) > 0; | |||||
if (HasScopAsTopLevelRegion) { | |||||
DEBUG(dbgs() << "Skipping " << F->getName() | |||||
<< " has scop as top level region"); | |||||
F->addFnAttr(llvm::Attribute::AlwaysInline); | |||||
ModuleAnalysisManager MAM; | |||||
PB.registerModuleAnalyses(MAM); | |||||
ModulePassManager MPM; | |||||
MPM.addPass(AlwaysInlinerPass()); | |||||
Module *M = F->getParent(); | |||||
assert(M && "Function has illegal module"); | |||||
MPM.run(*M, MAM); | |||||
} else { | |||||
DEBUG(dbgs() << F->getName() | |||||
<< " does NOT have scop as top level region\n"); | |||||
} | |||||
// Whether the entire function can be modeled as a Scop. | |||||
const bool IsFullyModeledAsScop = | |||||
SD.ValidRegions.count(TopLevelRegion) > 0; | |||||
// Whether the scop contains all the children of the top-level region. | |||||
singam-sanjay: "Whether a child of the top-level region contains a SCoP" ? | |||||
const bool IsModeledByTopLevelChildren = [&] { | |||||
Not Done ReplyInline ActionsNot sure what polly convention is on this: I create a lambda and immediately evaluate it so I can mark the outer variable as const. It's a nice way to split off coherent sub-checks IMO. bollu: Not sure what polly convention is on this: I create a lambda and immediately evaluate it so I… | |||||
for (auto ScopRegion : SD.ValidRegions) | |||||
if (ScopRegion->getParent() == TopLevelRegion) | |||||
Not Done ReplyInline ActionsYou're just checking for one of the children of the TopLevelRegion to be a SCoP ? How is it fine for one of the children to not be a SCoP ? singam-sanjay: You're just checking for one of the children of the TopLevelRegion to be a SCoP ? How is it… | |||||
return true; | |||||
return false; | return false; | ||||
}; | }(); | ||||
const InlineCost AnalyzedInlineCost = [&] { | |||||
Not Done ReplyInline ActionsSame immediately-invoke-lambda pattern used here. bollu: Same immediately-invoke-lambda pattern used here. | |||||
if (IsFullyModeledAsScop || IsModeledByTopLevelChildren) | |||||
return InlineCost::getAlways(); | |||||
return InlineCost::getNever(); | |||||
}(); | |||||
assert(InlineCostCache.find(F) == InlineCostCache.end() && | |||||
"Cached inlining analysis was not used."); | |||||
// Can't use InlineCostCache[F] = AnalyzedInlineCost because | |||||
// copy-ctor of InlineCost has been deleted. Joy. | |||||
InlineCostCache.insert(std::make_pair(F, AnalyzedInlineCost)); | |||||
DEBUG(dbgs() << "will inline? " << (bool)(AnalyzedInlineCost) << ".\n"); | |||||
void getAnalysisUsage(AnalysisUsage &AU) const override { | // If we decided to inline, then invalidate call site. | ||||
CallGraphSCCPass::getAnalysisUsage(AU); | if (AnalyzedInlineCost) { | ||||
Function *Caller = CS.getCaller(); | |||||
assert(Caller && "Callsite has invalid caller"); | |||||
InlineCostCache.erase(Caller); | |||||
} | |||||
return AnalyzedInlineCost; | |||||
} | } | ||||
// Do whatever alwaysinliner does. | |||||
bool runOnSCC(CallGraphSCC &SCC) override { return inlineCalls(SCC); } | |||||
}; | }; | ||||
} // namespace | } // namespace | ||||
char ScopInliner::ID; | char ScopInliner::ID; | ||||
Pass *polly::createScopInlinerPass() { | Pass *polly::createScopInlinerPass() { | ||||
ScopInliner *pass = new ScopInliner(); | ScopInliner *pass = new ScopInliner(); | ||||
return pass; | return pass; | ||||
Show All 10 Lines |
"Whether a child of the top-level region contains a SCoP" ?