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

[Inliner] Inlining should honor nobuiltin attributes

Summary:
Final patch in series to fix inlining between functions with different
nobuiltin attributes/options, which was specifically an issue in LTO.
See discussion on D61634 for background.

The prior patch in this series (D67923) enabled per-Function TLI
construction that identified the nobuiltin attributes.

Here I have allowed inlining to proceed if the callee's nobuiltins are a
subset of the caller's nobuiltins, but not in the reverse case, which
should be conservatively correct. This is controlled by a new option,
-inline-caller-superset-nobuiltin, which is enabled by default.

Reviewers: hfinkel, gchatelet, chandlerc, davidxl

Subscribers: arsenm, jvesely, nhaehnle, mehdi_amini, eraman, hiraditya, haicheng, dexonsmith, kerbowa, llvm-commits

Tags: #llvm

Differential Revision: https://reviews.llvm.org/D74162
This commit is contained in:
Teresa Johnson 2020-02-06 13:28:41 -08:00
parent 9d6924a218
commit e53f3c1bec
10 changed files with 190 additions and 30 deletions

View File

@ -27,6 +27,7 @@ class DataLayout;
class Function; class Function;
class ProfileSummaryInfo; class ProfileSummaryInfo;
class TargetTransformInfo; class TargetTransformInfo;
class TargetLibraryInfo;
namespace InlineConstants { namespace InlineConstants {
// Various thresholds used by inline cost analysis. // Various thresholds used by inline cost analysis.
@ -219,6 +220,7 @@ InlineCost getInlineCost(
CallBase &Call, const InlineParams &Params, TargetTransformInfo &CalleeTTI, CallBase &Call, const InlineParams &Params, TargetTransformInfo &CalleeTTI,
std::function<AssumptionCache &(Function &)> &GetAssumptionCache, std::function<AssumptionCache &(Function &)> &GetAssumptionCache,
Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI, Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE = nullptr); ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE = nullptr);
/// Get an InlineCost with the callee explicitly specified. /// Get an InlineCost with the callee explicitly specified.
@ -231,6 +233,7 @@ getInlineCost(CallBase &Call, Function *Callee, const InlineParams &Params,
TargetTransformInfo &CalleeTTI, TargetTransformInfo &CalleeTTI,
std::function<AssumptionCache &(Function &)> &GetAssumptionCache, std::function<AssumptionCache &(Function &)> &GetAssumptionCache,
Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI, Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE); ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE);
/// Minimal filter to detect invalid constructs for inlining. /// Minimal filter to detect invalid constructs for inlining.

View File

@ -260,6 +260,21 @@ public:
return *this; return *this;
} }
/// Determine whether a callee with the given TLI can be inlined into
/// caller with this TLI, based on 'nobuiltin' attributes. When requested,
/// allow inlining into a caller with a superset of the callee's nobuiltin
/// attributes, which is conservatively correct.
bool areInlineCompatible(const TargetLibraryInfo &CalleeTLI,
bool AllowCallerSuperset) const {
if (!AllowCallerSuperset)
return OverrideAsUnavailable == CalleeTLI.OverrideAsUnavailable;
BitVector B = OverrideAsUnavailable;
B |= CalleeTLI.OverrideAsUnavailable;
// We can inline if the union of the caller and callee's nobuiltin
// attributes is no stricter than the caller's nobuiltin attributes.
return B == OverrideAsUnavailable;
}
/// Searches for a particular function name. /// Searches for a particular function name.
/// ///
/// If it is one of the known library functions, return true and set F to the /// If it is one of the known library functions, return true and set F to the

View File

@ -74,6 +74,7 @@ private:
protected: protected:
AssumptionCacheTracker *ACT; AssumptionCacheTracker *ACT;
ProfileSummaryInfo *PSI; ProfileSummaryInfo *PSI;
std::function<const TargetLibraryInfo &(Function &)> GetTLI;
ImportedFunctionsInliningStatistics ImportedFunctionsStats; ImportedFunctionsInliningStatistics ImportedFunctionsStats;
}; };

View File

@ -24,6 +24,7 @@
#include "llvm/Analysis/InstructionSimplify.h" #include "llvm/Analysis/InstructionSimplify.h"
#include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/LoopInfo.h"
#include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Analysis/ValueTracking.h" #include "llvm/Analysis/ValueTracking.h"
#include "llvm/Config/llvm-config.h" #include "llvm/Config/llvm-config.h"
@ -103,6 +104,12 @@ static cl::opt<bool> OptComputeFullInlineCost(
cl::desc("Compute the full inline cost of a call site even when the cost " cl::desc("Compute the full inline cost of a call site even when the cost "
"exceeds the threshold.")); "exceeds the threshold."));
static cl::opt<bool> InlineCallerSupersetNoBuiltin(
"inline-caller-superset-nobuiltin", cl::Hidden, cl::init(true),
cl::ZeroOrMore,
cl::desc("Allow inlining when caller has a superset of callee's nobuiltin "
"attributes."));
namespace { namespace {
class InlineCostCallAnalyzer; class InlineCostCallAnalyzer;
@ -2146,10 +2153,17 @@ LLVM_DUMP_METHOD void InlineCostCallAnalyzer::dump() {
/// Test that there are no attribute conflicts between Caller and Callee /// Test that there are no attribute conflicts between Caller and Callee
/// that prevent inlining. /// that prevent inlining.
static bool functionsHaveCompatibleAttributes(Function *Caller, static bool functionsHaveCompatibleAttributes(
Function *Callee, Function *Caller, Function *Callee, TargetTransformInfo &TTI,
TargetTransformInfo &TTI) { function_ref<const TargetLibraryInfo &(Function &)> &GetTLI) {
// Note that CalleeTLI must be a copy not a reference. The legacy pass manager
// caches the most recently created TLI in the TargetLibraryInfoWrapperPass
// object, and always returns the same object (which is overwritten on each
// GetTLI call). Therefore we copy the first result.
auto CalleeTLI = GetTLI(*Callee);
return TTI.areInlineCompatible(Caller, Callee) && return TTI.areInlineCompatible(Caller, Callee) &&
GetTLI(*Caller).areInlineCompatible(CalleeTLI,
InlineCallerSupersetNoBuiltin) &&
AttributeFuncs::areInlineCompatible(*Caller, *Callee); AttributeFuncs::areInlineCompatible(*Caller, *Callee);
} }
@ -2190,9 +2204,10 @@ InlineCost llvm::getInlineCost(
CallBase &Call, const InlineParams &Params, TargetTransformInfo &CalleeTTI, CallBase &Call, const InlineParams &Params, TargetTransformInfo &CalleeTTI,
std::function<AssumptionCache &(Function &)> &GetAssumptionCache, std::function<AssumptionCache &(Function &)> &GetAssumptionCache,
Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI, Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) { ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) {
return getInlineCost(Call, Call.getCalledFunction(), Params, CalleeTTI, return getInlineCost(Call, Call.getCalledFunction(), Params, CalleeTTI,
GetAssumptionCache, GetBFI, PSI, ORE); GetAssumptionCache, GetBFI, GetTLI, PSI, ORE);
} }
InlineCost llvm::getInlineCost( InlineCost llvm::getInlineCost(
@ -2200,6 +2215,7 @@ InlineCost llvm::getInlineCost(
TargetTransformInfo &CalleeTTI, TargetTransformInfo &CalleeTTI,
std::function<AssumptionCache &(Function &)> &GetAssumptionCache, std::function<AssumptionCache &(Function &)> &GetAssumptionCache,
Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI, Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI,
function_ref<const TargetLibraryInfo &(Function &)> GetTLI,
ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) { ProfileSummaryInfo *PSI, OptimizationRemarkEmitter *ORE) {
// Cannot inline indirect calls. // Cannot inline indirect calls.
@ -2232,7 +2248,7 @@ InlineCost llvm::getInlineCost(
// Never inline functions with conflicting attributes (unless callee has // Never inline functions with conflicting attributes (unless callee has
// always-inline attribute). // always-inline attribute).
Function *Caller = Call.getCaller(); Function *Caller = Call.getCaller();
if (!functionsHaveCompatibleAttributes(Caller, Callee, CalleeTTI)) if (!functionsHaveCompatibleAttributes(Caller, Callee, CalleeTTI, GetTLI))
return llvm::InlineCost::getNever("conflicting attributes"); return llvm::InlineCost::getNever("conflicting attributes");
// Don't inline this call if the caller has the optnone attribute. // Don't inline this call if the caller has the optnone attribute.

View File

@ -215,8 +215,8 @@ InlineCost AMDGPUInliner::getInlineCost(CallSite CS) {
}; };
auto IC = llvm::getInlineCost(cast<CallBase>(*CS.getInstruction()), Callee, auto IC = llvm::getInlineCost(cast<CallBase>(*CS.getInstruction()), Callee,
LocalParams, TTI, GetAssumptionCache, None, PSI, LocalParams, TTI, GetAssumptionCache, None,
RemarksEnabled ? &ORE : nullptr); GetTLI, PSI, RemarksEnabled ? &ORE : nullptr);
if (IC && !IC.isAlways() && !Callee->hasFnAttribute(Attribute::InlineHint)) { if (IC && !IC.isAlways() && !Callee->hasFnAttribute(Attribute::InlineHint)) {
// Single BB does not increase total BB amount, thus subtract 1 // Single BB does not increase total BB amount, thus subtract 1

View File

@ -71,7 +71,7 @@ public:
}; };
return llvm::getInlineCost( return llvm::getInlineCost(
cast<CallBase>(*CS.getInstruction()), Params, TTI, GetAssumptionCache, cast<CallBase>(*CS.getInstruction()), Params, TTI, GetAssumptionCache,
/*GetBFI=*/None, PSI, RemarksEnabled ? &ORE : nullptr); /*GetBFI=*/None, GetTLI, PSI, RemarksEnabled ? &ORE : nullptr);
} }
bool runOnSCC(CallGraphSCC &SCC) override; bool runOnSCC(CallGraphSCC &SCC) override;

View File

@ -528,7 +528,7 @@ static bool
inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG, inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG,
std::function<AssumptionCache &(Function &)> GetAssumptionCache, std::function<AssumptionCache &(Function &)> GetAssumptionCache,
ProfileSummaryInfo *PSI, ProfileSummaryInfo *PSI,
std::function<TargetLibraryInfo &(Function &)> GetTLI, std::function<const TargetLibraryInfo &(Function &)> GetTLI,
bool InsertLifetime, bool InsertLifetime,
function_ref<InlineCost(CallSite CS)> GetInlineCost, function_ref<InlineCost(CallSite CS)> GetInlineCost,
function_ref<AAResults &(Function &)> AARGetter, function_ref<AAResults &(Function &)> AARGetter,
@ -761,7 +761,7 @@ bool LegacyInlinerBase::inlineCalls(CallGraphSCC &SCC) {
CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph(); CallGraph &CG = getAnalysis<CallGraphWrapperPass>().getCallGraph();
ACT = &getAnalysis<AssumptionCacheTracker>(); ACT = &getAnalysis<AssumptionCacheTracker>();
PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI(); PSI = &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
auto GetTLI = [&](Function &F) -> TargetLibraryInfo & { GetTLI = [&](Function &F) -> const TargetLibraryInfo & {
return getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F); return getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
}; };
auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & { auto GetAssumptionCache = [&](Function &F) -> AssumptionCache & {
@ -1008,6 +1008,9 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
auto GetBFI = [&](Function &F) -> BlockFrequencyInfo & { auto GetBFI = [&](Function &F) -> BlockFrequencyInfo & {
return FAM.getResult<BlockFrequencyAnalysis>(F); return FAM.getResult<BlockFrequencyAnalysis>(F);
}; };
auto GetTLI = [&](Function &F) -> const TargetLibraryInfo & {
return FAM.getResult<TargetLibraryAnalysis>(F);
};
auto GetInlineCost = [&](CallSite CS) { auto GetInlineCost = [&](CallSite CS) {
Function &Callee = *CS.getCalledFunction(); Function &Callee = *CS.getCalledFunction();
@ -1016,7 +1019,7 @@ PreservedAnalyses InlinerPass::run(LazyCallGraph::SCC &InitialC,
Callee.getContext().getDiagHandlerPtr()->isMissedOptRemarkEnabled( Callee.getContext().getDiagHandlerPtr()->isMissedOptRemarkEnabled(
DEBUG_TYPE); DEBUG_TYPE);
return getInlineCost(cast<CallBase>(*CS.getInstruction()), Params, return getInlineCost(cast<CallBase>(*CS.getInstruction()), Params,
CalleeTTI, GetAssumptionCache, {GetBFI}, PSI, CalleeTTI, GetAssumptionCache, {GetBFI}, GetTLI, PSI,
RemarksEnabled ? &ORE : nullptr); RemarksEnabled ? &ORE : nullptr);
}; };

View File

@ -203,9 +203,10 @@ struct PartialInlinerImpl {
function_ref<AssumptionCache *(Function &)> LookupAC, function_ref<AssumptionCache *(Function &)> LookupAC,
std::function<TargetTransformInfo &(Function &)> *GTTI, std::function<TargetTransformInfo &(Function &)> *GTTI,
Optional<function_ref<BlockFrequencyInfo &(Function &)>> GBFI, Optional<function_ref<BlockFrequencyInfo &(Function &)>> GBFI,
std::function<const TargetLibraryInfo &(Function &)> *GTLI,
ProfileSummaryInfo *ProfSI) ProfileSummaryInfo *ProfSI)
: GetAssumptionCache(GetAC), LookupAssumptionCache(LookupAC), : GetAssumptionCache(GetAC), LookupAssumptionCache(LookupAC),
GetTTI(GTTI), GetBFI(GBFI), PSI(ProfSI) {} GetTTI(GTTI), GetBFI(GBFI), GetTLI(GTLI), PSI(ProfSI) {}
bool run(Module &M); bool run(Module &M);
// Main part of the transformation that calls helper functions to find // Main part of the transformation that calls helper functions to find
@ -274,6 +275,7 @@ private:
function_ref<AssumptionCache *(Function &)> LookupAssumptionCache; function_ref<AssumptionCache *(Function &)> LookupAssumptionCache;
std::function<TargetTransformInfo &(Function &)> *GetTTI; std::function<TargetTransformInfo &(Function &)> *GetTTI;
Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI; Optional<function_ref<BlockFrequencyInfo &(Function &)>> GetBFI;
std::function<const TargetLibraryInfo &(Function &)> *GetTLI;
ProfileSummaryInfo *PSI; ProfileSummaryInfo *PSI;
// Return the frequency of the OutlininingBB relative to F's entry point. // Return the frequency of the OutlininingBB relative to F's entry point.
@ -355,6 +357,7 @@ struct PartialInlinerLegacyPass : public ModulePass {
AU.addRequired<AssumptionCacheTracker>(); AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<ProfileSummaryInfoWrapperPass>(); AU.addRequired<ProfileSummaryInfoWrapperPass>();
AU.addRequired<TargetTransformInfoWrapperPass>(); AU.addRequired<TargetTransformInfoWrapperPass>();
AU.addRequired<TargetLibraryInfoWrapperPass>();
} }
bool runOnModule(Module &M) override { bool runOnModule(Module &M) override {
@ -381,8 +384,13 @@ struct PartialInlinerLegacyPass : public ModulePass {
return TTIWP->getTTI(F); return TTIWP->getTTI(F);
}; };
std::function<const TargetLibraryInfo &(Function &)> GetTLI =
[this](Function &F) -> TargetLibraryInfo & {
return this->getAnalysis<TargetLibraryInfoWrapperPass>().getTLI(F);
};
return PartialInlinerImpl(&GetAssumptionCache, LookupAssumptionCache, return PartialInlinerImpl(&GetAssumptionCache, LookupAssumptionCache,
&GetTTI, NoneType::None, PSI) &GetTTI, NoneType::None, &GetTLI, PSI)
.run(M); .run(M);
} }
}; };
@ -778,8 +786,8 @@ bool PartialInlinerImpl::shouldPartialInline(
DEBUG_TYPE); DEBUG_TYPE);
assert(Call && "invalid callsite for partial inline"); assert(Call && "invalid callsite for partial inline");
InlineCost IC = getInlineCost(cast<CallBase>(*Call), getInlineParams(), InlineCost IC = getInlineCost(cast<CallBase>(*Call), getInlineParams(),
CalleeTTI, *GetAssumptionCache, GetBFI, PSI, CalleeTTI, *GetAssumptionCache, GetBFI, *GetTLI,
RemarksEnabled ? &ORE : nullptr); PSI, RemarksEnabled ? &ORE : nullptr);
if (IC.isAlways()) { if (IC.isAlways()) {
ORE.emit([&]() { ORE.emit([&]() {
@ -1493,6 +1501,7 @@ INITIALIZE_PASS_BEGIN(PartialInlinerLegacyPass, "partial-inliner",
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_END(PartialInlinerLegacyPass, "partial-inliner", INITIALIZE_PASS_END(PartialInlinerLegacyPass, "partial-inliner",
"Partial Inliner", false, false) "Partial Inliner", false, false)
@ -1523,10 +1532,15 @@ PreservedAnalyses PartialInlinerPass::run(Module &M,
return FAM.getResult<TargetIRAnalysis>(F); return FAM.getResult<TargetIRAnalysis>(F);
}; };
std::function<const TargetLibraryInfo &(Function &)> GetTLI =
[&FAM](Function &F) -> TargetLibraryInfo & {
return FAM.getResult<TargetLibraryAnalysis>(F);
};
ProfileSummaryInfo *PSI = &AM.getResult<ProfileSummaryAnalysis>(M); ProfileSummaryInfo *PSI = &AM.getResult<ProfileSummaryAnalysis>(M);
if (PartialInlinerImpl(&GetAssumptionCache, LookupAssumptionCache, &GetTTI, if (PartialInlinerImpl(&GetAssumptionCache, LookupAssumptionCache, &GetTTI,
{GetBFI}, PSI) {GetBFI}, &GetTLI, PSI)
.run(M)) .run(M))
return PreservedAnalyses::none(); return PreservedAnalyses::none();
return PreservedAnalyses::all(); return PreservedAnalyses::all();

View File

@ -42,6 +42,7 @@
#include "llvm/Analysis/OptimizationRemarkEmitter.h" #include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/PostDominators.h" #include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/ProfileSummaryInfo.h" #include "llvm/Analysis/ProfileSummaryInfo.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/BasicBlock.h" #include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h" #include "llvm/IR/CFG.h"
@ -307,10 +308,12 @@ public:
SampleProfileLoader( SampleProfileLoader(
StringRef Name, StringRef RemapName, bool IsThinLTOPreLink, StringRef Name, StringRef RemapName, bool IsThinLTOPreLink,
std::function<AssumptionCache &(Function &)> GetAssumptionCache, std::function<AssumptionCache &(Function &)> GetAssumptionCache,
std::function<TargetTransformInfo &(Function &)> GetTargetTransformInfo) std::function<TargetTransformInfo &(Function &)> GetTargetTransformInfo,
std::function<const TargetLibraryInfo &(Function &)> GetTLI)
: GetAC(std::move(GetAssumptionCache)), : GetAC(std::move(GetAssumptionCache)),
GetTTI(std::move(GetTargetTransformInfo)), CoverageTracker(*this), GetTTI(std::move(GetTargetTransformInfo)), GetTLI(std::move(GetTLI)),
Filename(std::string(Name)), RemappingFilename(std::string(RemapName)), CoverageTracker(*this), Filename(std::string(Name)),
RemappingFilename(std::string(RemapName)),
IsThinLTOPreLink(IsThinLTOPreLink) {} IsThinLTOPreLink(IsThinLTOPreLink) {}
bool doInitialization(Module &M); bool doInitialization(Module &M);
@ -397,6 +400,7 @@ protected:
std::function<AssumptionCache &(Function &)> GetAC; std::function<AssumptionCache &(Function &)> GetAC;
std::function<TargetTransformInfo &(Function &)> GetTTI; std::function<TargetTransformInfo &(Function &)> GetTTI;
std::function<const TargetLibraryInfo &(Function &)> GetTLI;
/// Predecessors for each basic block in the CFG. /// Predecessors for each basic block in the CFG.
BlockEdgeMap Predecessors; BlockEdgeMap Predecessors;
@ -474,13 +478,16 @@ public:
SampleProfileLoaderLegacyPass(StringRef Name = SampleProfileFile, SampleProfileLoaderLegacyPass(StringRef Name = SampleProfileFile,
bool IsThinLTOPreLink = false) bool IsThinLTOPreLink = false)
: ModulePass(ID), : ModulePass(ID), SampleLoader(
SampleLoader(Name, SampleProfileRemappingFile, IsThinLTOPreLink, Name, SampleProfileRemappingFile, IsThinLTOPreLink,
[&](Function &F) -> AssumptionCache & { [&](Function &F) -> AssumptionCache & {
return ACT->getAssumptionCache(F); return ACT->getAssumptionCache(F);
}, },
[&](Function &F) -> TargetTransformInfo & { [&](Function &F) -> TargetTransformInfo & {
return TTIWP->getTTI(F); return TTIWP->getTTI(F);
},
[&](Function &F) -> TargetLibraryInfo & {
return TLIWP->getTLI(F);
}) { }) {
initializeSampleProfileLoaderLegacyPassPass( initializeSampleProfileLoaderLegacyPassPass(
*PassRegistry::getPassRegistry()); *PassRegistry::getPassRegistry());
@ -498,6 +505,7 @@ public:
void getAnalysisUsage(AnalysisUsage &AU) const override { void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.addRequired<AssumptionCacheTracker>(); AU.addRequired<AssumptionCacheTracker>();
AU.addRequired<TargetTransformInfoWrapperPass>(); AU.addRequired<TargetTransformInfoWrapperPass>();
AU.addRequired<TargetLibraryInfoWrapperPass>();
AU.addRequired<ProfileSummaryInfoWrapperPass>(); AU.addRequired<ProfileSummaryInfoWrapperPass>();
} }
@ -505,6 +513,7 @@ private:
SampleProfileLoader SampleLoader; SampleProfileLoader SampleLoader;
AssumptionCacheTracker *ACT = nullptr; AssumptionCacheTracker *ACT = nullptr;
TargetTransformInfoWrapperPass *TTIWP = nullptr; TargetTransformInfoWrapperPass *TTIWP = nullptr;
TargetLibraryInfoWrapperPass *TLIWP = nullptr;
}; };
} // end anonymous namespace } // end anonymous namespace
@ -902,7 +911,7 @@ bool SampleProfileLoader::inlineCallInstruction(Instruction *I) {
// see if it is legal to inline the callsite. // see if it is legal to inline the callsite.
InlineCost Cost = InlineCost Cost =
getInlineCost(cast<CallBase>(*I), Params, GetTTI(*CalledFunction), GetAC, getInlineCost(cast<CallBase>(*I), Params, GetTTI(*CalledFunction), GetAC,
None, nullptr, nullptr); None, GetTLI, nullptr, nullptr);
if (Cost.isNever()) { if (Cost.isNever()) {
ORE->emit(OptimizationRemarkAnalysis(CSINLINE_DEBUG, "InlineFail", DLoc, BB) ORE->emit(OptimizationRemarkAnalysis(CSINLINE_DEBUG, "InlineFail", DLoc, BB)
<< "incompatible inlining"); << "incompatible inlining");
@ -929,7 +938,7 @@ bool SampleProfileLoader::shouldInlineColdCallee(Instruction &CallInst) {
InlineCost Cost = InlineCost Cost =
getInlineCost(cast<CallBase>(CallInst), getInlineParams(), getInlineCost(cast<CallBase>(CallInst), getInlineParams(),
GetTTI(*Callee), GetAC, None, nullptr, nullptr); GetTTI(*Callee), GetAC, None, GetTLI, nullptr, nullptr);
return Cost.getCost() <= SampleColdCallSiteThreshold; return Cost.getCost() <= SampleColdCallSiteThreshold;
} }
@ -1770,6 +1779,7 @@ INITIALIZE_PASS_BEGIN(SampleProfileLoaderLegacyPass, "sample-profile",
"Sample Profile loader", false, false) "Sample Profile loader", false, false)
INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker) INITIALIZE_PASS_DEPENDENCY(AssumptionCacheTracker)
INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(TargetTransformInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(TargetLibraryInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass) INITIALIZE_PASS_DEPENDENCY(ProfileSummaryInfoWrapperPass)
INITIALIZE_PASS_END(SampleProfileLoaderLegacyPass, "sample-profile", INITIALIZE_PASS_END(SampleProfileLoaderLegacyPass, "sample-profile",
"Sample Profile loader", false, false) "Sample Profile loader", false, false)
@ -1890,6 +1900,7 @@ bool SampleProfileLoader::runOnModule(Module &M, ModuleAnalysisManager *AM,
bool SampleProfileLoaderLegacyPass::runOnModule(Module &M) { bool SampleProfileLoaderLegacyPass::runOnModule(Module &M) {
ACT = &getAnalysis<AssumptionCacheTracker>(); ACT = &getAnalysis<AssumptionCacheTracker>();
TTIWP = &getAnalysis<TargetTransformInfoWrapperPass>(); TTIWP = &getAnalysis<TargetTransformInfoWrapperPass>();
TLIWP = &getAnalysis<TargetLibraryInfoWrapperPass>();
ProfileSummaryInfo *PSI = ProfileSummaryInfo *PSI =
&getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI(); &getAnalysis<ProfileSummaryInfoWrapperPass>().getPSI();
return SampleLoader.runOnModule(M, nullptr, PSI, nullptr); return SampleLoader.runOnModule(M, nullptr, PSI, nullptr);
@ -1966,12 +1977,15 @@ PreservedAnalyses SampleProfileLoaderPass::run(Module &M,
auto GetTTI = [&](Function &F) -> TargetTransformInfo & { auto GetTTI = [&](Function &F) -> TargetTransformInfo & {
return FAM.getResult<TargetIRAnalysis>(F); return FAM.getResult<TargetIRAnalysis>(F);
}; };
auto GetTLI = [&](Function &F) -> const TargetLibraryInfo & {
return FAM.getResult<TargetLibraryAnalysis>(F);
};
SampleProfileLoader SampleLoader( SampleProfileLoader SampleLoader(
ProfileFileName.empty() ? SampleProfileFile : ProfileFileName, ProfileFileName.empty() ? SampleProfileFile : ProfileFileName,
ProfileRemappingFileName.empty() ? SampleProfileRemappingFile ProfileRemappingFileName.empty() ? SampleProfileRemappingFile
: ProfileRemappingFileName, : ProfileRemappingFileName,
IsThinLTOPreLink, GetAssumptionCache, GetTTI); IsThinLTOPreLink, GetAssumptionCache, GetTTI, GetTLI);
if (!SampleLoader.doInitialization(M)) if (!SampleLoader.doInitialization(M))
return PreservedAnalyses::all(); return PreservedAnalyses::all();

View File

@ -0,0 +1,94 @@
; Test to ensure no inlining is allowed into a caller with fewer nobuiltin attributes.
; RUN: opt < %s -mtriple=x86_64-unknown-linux-gnu -S -inline | FileCheck %s
; RUN: opt < %s -mtriple=x86_64-unknown-linux-gnu -S -passes='cgscc(inline)' | FileCheck %s
; Make sure we don't inline callees into a caller with a superset of the
; no builtin attributes when -inline-caller-superset-nobuiltin=false.
; RUN: opt < %s -inline-caller-superset-nobuiltin=false -mtriple=x86_64-unknown-linux-gnu -S -passes='cgscc(inline)' | FileCheck %s --check-prefix=NOSUPERSET
target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"
define i32 @allbuiltins() {
entry:
%call = call i32 (...) @externalfunc()
ret i32 %call
; CHECK-LABEL: allbuiltins
; CHECK: call i32 (...) @externalfunc()
}
declare i32 @externalfunc(...)
; We can inline a function that allows all builtins into one with a single
; nobuiltin.
define i32 @nobuiltinmemcpy() #0 {
entry:
%call = call i32 @allbuiltins()
ret i32 %call
; CHECK-LABEL: nobuiltinmemcpy
; CHECK-NOT: call i32 @allbuiltins()
; NOSUPERSET-LABEL: nobuiltinmemcpy
; NOSUPERSET: call i32 @allbuiltins()
}
; We can inline a function that allows all builtins into one with all
; nobuiltins.
define i32 @nobuiltins() #1 {
entry:
%call = call i32 @allbuiltins()
ret i32 %call
; CHECK-LABEL: nobuiltins
; CHECK-NOT: call i32 @allbuiltins()
; NOSUPERSET-LABEL: nobuiltins
; NOSUPERSET: call i32 @allbuiltins()
}
; We can inline a function with a single nobuiltin into one with all nobuiltins.
define i32 @nobuiltins2() #1 {
entry:
%call = call i32 @nobuiltinmemcpy()
ret i32 %call
; CHECK-LABEL: nobuiltins2
; CHECK-NOT: call i32 @nobuiltinmemcpy()
; NOSUPERSET-LABEL: nobuiltins2
; NOSUPERSET: call i32 @nobuiltinmemcpy()
}
; We can't inline a function with any given nobuiltin into one that allows all
; builtins.
define i32 @allbuiltins2() {
entry:
%call = call i32 @nobuiltinmemcpy()
ret i32 %call
; CHECK-LABEL: allbuiltins2
; CHECK: call i32 @nobuiltinmemcpy()
; NOSUPERSET-LABEL: allbuiltins2
; NOSUPERSET: call i32 @nobuiltinmemcpy()
}
; We can't inline a function with all nobuiltins into one that allows all
; builtins.
define i32 @allbuiltins3() {
entry:
%call = call i32 @nobuiltins()
ret i32 %call
; CHECK-LABEL: allbuiltins3
; CHECK: call i32 @nobuiltins()
; NOSUPERSET-LABEL: allbuiltins3
; NOSUPERSET: call i32 @nobuiltins()
}
; We can't inline a function with a specific nobuiltin into one with a
; different specific nobuiltin.
define i32 @nobuiltinmemset() #2 {
entry:
%call = call i32 @nobuiltinmemcpy()
ret i32 %call
; CHECK-LABEL: nobuiltinmemset
; CHECK: call i32 @nobuiltinmemcpy()
; NOSUPERSET-LABEL: nobuiltinmemset
; NOSUPERSET: call i32 @nobuiltinmemcpy()
}
attributes #0 = { "no-builtin-memcpy" }
attributes #1 = { "no-builtins" }
attributes #2 = { "no-builtin-memset" }