The goal is to allow grafting an inline tree from Clang or GCC into a new compilation without affecting other functions. For GCC, we're doing this by extracting the inline tree from dwarf information and generating the equivalent remarks.
This allows easier side-by-side asm analysis and a trial way to see if a particular inlining setup provides benefits by itself.
Testing:
ninja check-all
Reviewed By: wenlei, mtrofin
Differential Revision: https://reviews.llvm.org/D110658
/// training.
enum class InliningAdvisorMode : int { Default, Release, Development };
+/// For Replay Inliner initialization
+enum class ReplayInlineScope : int { Function, Module };
+
class InlineAdvisor;
/// Capture state between an inlining decision having had been made, and
/// its impact being observable. When collecting model training data, this
/// be up-to-date wrt previous inlining decisions. \p MandatoryOnly indicates
/// only mandatory (always-inline) call sites should be recommended - this
/// allows the InlineAdvisor track such inlininings.
- /// Returns an InlineAdvice with the inlining recommendation.
+ /// Returns:
+ /// - An InlineAdvice with the inlining recommendation.
+ /// - Null when no recommendation is made (https://reviews.llvm.org/D110658).
+ /// TODO: Consider removing the Null return scenario by incorporating the
+ /// SampleProfile inliner into an InlineAdvisor
std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB,
bool MandatoryOnly = false);
return !PAC.preservedWhenStateless();
}
bool tryCreate(InlineParams Params, InliningAdvisorMode Mode,
- StringRef ReplayFile);
+ StringRef ReplayFile, ReplayInlineScope ReplayScope);
InlineAdvisor *getAdvisor() const { return Advisor.get(); }
private:
std::function<bool(CallBase &)> GetDefaultAdvice);
#endif
+std::unique_ptr<InlineAdvisor> getReplayInlineAdvisor(
+ Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context,
+ std::unique_ptr<InlineAdvisor> OriginalAdvisor, StringRef RemarksFile,
+ ReplayInlineScope Scope, bool EmitRemarks);
+
// Default (manual policy) decision making helper APIs. Shared with the legacy
// pass manager inliner.
ReplayInlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
LLVMContext &Context,
std::unique_ptr<InlineAdvisor> OriginalAdvisor,
- StringRef RemarksFile, bool EmitRemarks);
+ StringRef RemarksFile, ReplayInlineScope Scope,
+ bool EmitRemarks);
std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override;
bool areReplayRemarksLoaded() const { return HasReplayRemarks; }
private:
- StringSet<> InlineSitesFromRemarks;
std::unique_ptr<InlineAdvisor> OriginalAdvisor;
bool HasReplayRemarks = false;
+ ReplayInlineScope Scope;
bool EmitRemarks = false;
+
+ StringMap<bool> InlineSitesFromRemarks;
+ StringSet<> CallersToReplay;
};
} // namespace llvm
#endif // LLVM_ANALYSIS_REPLAYINLINEADVISOR_H
#include "llvm/Analysis/InlineAdvisor.h"
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/LazyCallGraph.h"
-#include "llvm/Analysis/ReplayInlineAdvisor.h"
#include "llvm/Analysis/Utils/ImportedFunctionsInliningStatistics.h"
#include "llvm/IR/PassManager.h"
#include <utility>
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
-#include "llvm/Analysis/ReplayInlineAdvisor.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/DebugInfoMetadata.h"
bool InlineAdvisorAnalysis::Result::tryCreate(InlineParams Params,
InliningAdvisorMode Mode,
- StringRef ReplayFile) {
+ StringRef ReplayFile,
+ ReplayInlineScope ReplayScope) {
auto &FAM = MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
switch (Mode) {
case InliningAdvisorMode::Default:
// Restrict replay to default advisor, ML advisors are stateful so
// replay will need augmentations to interleave with them correctly.
if (!ReplayFile.empty()) {
- Advisor = std::make_unique<ReplayInlineAdvisor>(
- M, FAM, M.getContext(), std::move(Advisor), ReplayFile,
+ Advisor = llvm::getReplayInlineAdvisor(
+ M, FAM, M.getContext(), std::move(Advisor), ReplayFile, ReplayScope,
/* EmitRemarks =*/true);
}
break;
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/LineIterator.h"
+#include <memory>
using namespace llvm;
-#define DEBUG_TYPE "inline-replay"
+#define DEBUG_TYPE "replay-inline"
ReplayInlineAdvisor::ReplayInlineAdvisor(
Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context,
std::unique_ptr<InlineAdvisor> OriginalAdvisor, StringRef RemarksFile,
- bool EmitRemarks)
+ ReplayInlineScope Scope, bool EmitRemarks)
: InlineAdvisor(M, FAM), OriginalAdvisor(std::move(OriginalAdvisor)),
- HasReplayRemarks(false), EmitRemarks(EmitRemarks) {
+ HasReplayRemarks(false), Scope(Scope), EmitRemarks(EmitRemarks) {
+
auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(RemarksFile);
std::error_code EC = BufferOrErr.getError();
if (EC) {
}
// Example for inline remarks to parse:
- // main:3:1.1: '_Z3subii' inlined into 'main' at callsite sum:1 @ main:3:1.1
+ // main:3:1.1: '_Z3subii' inlined into 'main' at callsite sum:1 @
+ // main:3:1.1;
// We use the callsite string after `at callsite` to replay inlining.
line_iterator LineIt(*BufferOrErr.get(), /*SkipBlanks=*/true);
for (; !LineIt.is_at_eof(); ++LineIt) {
StringRef Line = *LineIt;
auto Pair = Line.split(" at callsite ");
- StringRef Callee = Pair.first.split(" inlined into")
- .first.rsplit(": '")
- .second.drop_back();
+ auto CalleeCaller = Pair.first.split("' inlined into '");
+
+ StringRef Callee = CalleeCaller.first.rsplit(": '").second;
+ StringRef Caller = CalleeCaller.second.rsplit("'").first;
+
auto CallSite = Pair.second.split(";").first;
- if (Callee.empty() || CallSite.empty())
- continue;
+ if (Callee.empty() || Caller.empty() || CallSite.empty()) {
+ Context.emitError("Invalid remark format: " + Line);
+ return;
+ }
std::string Combined = (Callee + CallSite).str();
- InlineSitesFromRemarks.insert(Combined);
+ InlineSitesFromRemarks[Combined] = false;
+ if (Scope == ReplayInlineScope::Function)
+ CallersToReplay.insert(Caller);
}
HasReplayRemarks = true;
}
+std::unique_ptr<InlineAdvisor> llvm::getReplayInlineAdvisor(
+ Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context,
+ std::unique_ptr<InlineAdvisor> OriginalAdvisor, StringRef RemarksFile,
+ ReplayInlineScope Scope, bool EmitRemarks) {
+ auto Advisor = std::make_unique<ReplayInlineAdvisor>(
+ M, FAM, Context, std::move(OriginalAdvisor), RemarksFile, Scope,
+ EmitRemarks);
+ if (!Advisor->areReplayRemarksLoaded())
+ Advisor.reset();
+ return Advisor;
+}
+
std::unique_ptr<InlineAdvice> ReplayInlineAdvisor::getAdviceImpl(CallBase &CB) {
assert(HasReplayRemarks);
Function &Caller = *CB.getCaller();
auto &ORE = FAM.getResult<OptimizationRemarkEmitterAnalysis>(Caller);
- if (InlineSitesFromRemarks.empty())
- return std::make_unique<DefaultInlineAdvice>(this, CB, None, ORE,
- EmitRemarks);
+ Optional<InlineCost> InlineRecommended;
- std::string CallSiteLoc = getCallSiteLocation(CB.getDebugLoc());
- StringRef Callee = CB.getCalledFunction()->getName();
- std::string Combined = (Callee + CallSiteLoc).str();
- auto Iter = InlineSitesFromRemarks.find(Combined);
+ if (Scope == ReplayInlineScope::Module ||
+ CallersToReplay.count(CB.getFunction()->getName())) {
+ std::string CallSiteLoc = getCallSiteLocation(CB.getDebugLoc());
+ StringRef Callee = CB.getCalledFunction()->getName();
+ std::string Combined = (Callee + CallSiteLoc).str();
- Optional<InlineCost> InlineRecommended = None;
- if (Iter != InlineSitesFromRemarks.end()) {
- InlineRecommended = llvm::InlineCost::getAlways("found in replay");
+ auto Iter = InlineSitesFromRemarks.find(Combined);
+ if (Iter != InlineSitesFromRemarks.end()) {
+ InlineSitesFromRemarks[Combined] = true;
+ InlineRecommended = llvm::InlineCost::getAlways("previously inlined");
+ }
+ } else if (Scope == ReplayInlineScope::Function) {
+ if (OriginalAdvisor)
+ return OriginalAdvisor->getAdvice(CB);
+ return {};
}
return std::make_unique<DefaultInlineAdvice>(this, CB, InlineRecommended, ORE,
"cgscc-inline-replay", cl::init(""), cl::value_desc("filename"),
cl::desc(
"Optimization remarks file containing inline remarks to be replayed "
- "by inlining from cgscc inline remarks."),
+ "by cgscc inlining."),
+ cl::Hidden);
+
+static cl::opt<ReplayInlineScope> CGSCCInlineReplayScope(
+ "cgscc-inline-replay-scope", cl::init(ReplayInlineScope::Function),
+ cl::values(clEnumValN(ReplayInlineScope::Function, "Function",
+ "Replay on functions that have remarks associated "
+ "with them (default)"),
+ clEnumValN(ReplayInlineScope::Module, "Module",
+ "Replay on the entire module")),
+ cl::desc("Whether inline replay should be applied to the entire "
+ "Module or just the Functions (default) that are present as "
+ "callers in remarks during cgscc inlining."),
cl::Hidden);
static cl::opt<bool> InlineEnablePriorityOrder(
std::make_unique<DefaultInlineAdvisor>(M, FAM, getInlineParams());
if (!CGSCCInlineReplayFile.empty())
- OwnedAdvisor = std::make_unique<ReplayInlineAdvisor>(
+ OwnedAdvisor = getReplayInlineAdvisor(
M, FAM, M.getContext(), std::move(OwnedAdvisor),
- CGSCCInlineReplayFile,
+ CGSCCInlineReplayFile, CGSCCInlineReplayScope,
/*EmitRemarks=*/true);
return *OwnedAdvisor;
}
auto Advice = Advisor.getAdvice(*CB, OnlyMandatory);
+
// Check whether we want to inline this callsite.
- if (!Advice->isInliningRecommended()) {
+ if (!Advice || !Advice->isInliningRecommended()) {
Advice->recordUnattemptedInlining();
continue;
}
PreservedAnalyses ModuleInlinerWrapperPass::run(Module &M,
ModuleAnalysisManager &MAM) {
auto &IAA = MAM.getResult<InlineAdvisorAnalysis>(M);
- if (!IAA.tryCreate(Params, Mode, CGSCCInlineReplayFile)) {
+ if (!IAA.tryCreate(Params, Mode, CGSCCInlineReplayFile,
+ CGSCCInlineReplayScope)) {
M.getContext().emitError(
"Could not setup Inlining Advisor for the requested "
"mode and/or options");
#include "llvm/Analysis/OptimizationRemarkEmitter.h"
#include "llvm/Analysis/PostDominators.h"
#include "llvm/Analysis/ProfileSummaryInfo.h"
-#include "llvm/Analysis/ReplayInlineAdvisor.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/IR/BasicBlock.h"
"by inlining from sample profile loader."),
cl::Hidden);
+static cl::opt<ReplayInlineScope> ProfileInlineReplayScope(
+ "sample-profile-inline-replay-scope", cl::init(ReplayInlineScope::Function),
+ cl::values(clEnumValN(ReplayInlineScope::Function, "Function",
+ "Replay on functions that have remarks associated "
+ "with them (default)"),
+ clEnumValN(ReplayInlineScope::Module, "Module",
+ "Replay on the entire module")),
+ cl::desc("Whether inline replay should be applied to the entire "
+ "Module or just the Functions (default) that are present as "
+ "callers in remarks during sample profile inlining."),
+ cl::Hidden);
+
static cl::opt<unsigned>
MaxNumPromotions("sample-profile-icp-max-prom", cl::init(3), cl::Hidden,
cl::ZeroOrMore,
bool ProfAccForSymsInList;
// External inline advisor used to replay inline decision from remarks.
- std::unique_ptr<ReplayInlineAdvisor> ExternalInlineAdvisor;
+ std::unique_ptr<InlineAdvisor> ExternalInlineAdvisor;
// A pseudo probe helper to correlate the imported sample counts.
std::unique_ptr<PseudoProbeManager> ProbeManager;
std::unique_ptr<InlineAdvice> Advice = nullptr;
if (ExternalInlineAdvisor) {
Advice = ExternalInlineAdvisor->getAdvice(*Candidate.CallInstr);
- if (!Advice->isInliningRecommended()) {
- Advice->recordUnattemptedInlining();
- return InlineCost::getNever("not previously inlined");
+ if (Advice) {
+ if (!Advice->isInliningRecommended()) {
+ Advice->recordUnattemptedInlining();
+ return InlineCost::getNever("not previously inlined");
+ }
+ Advice->recordInlining();
+ return InlineCost::getAlways("previously inlined");
}
- Advice->recordInlining();
- return InlineCost::getAlways("previously inlined");
}
// Adjust threshold based on call site hotness, only do this for callsite
}
if (FAM && !ProfileInlineReplayFile.empty()) {
- ExternalInlineAdvisor = std::make_unique<ReplayInlineAdvisor>(
+ ExternalInlineAdvisor = getReplayInlineAdvisor(
M, *FAM, Ctx, /*OriginalAdvisor=*/nullptr, ProfileInlineReplayFile,
- /*EmitRemarks=*/false);
- if (!ExternalInlineAdvisor->areReplayRemarksLoaded())
- ExternalInlineAdvisor.reset();
+ ProfileInlineReplayScope, /*EmitRemarks=*/false);
}
// Apply tweaks if context-sensitive profile is available.
;; Note that this needs new pass manager for now. Passing `-cgscc-inline-replay` to legacy pass manager is a no-op.
-;; Check replay inline decisions
-; RUN: opt < %s -passes=inline -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=DEFAULT %s
-; RUN: opt < %s -passes=inline -cgscc-inline-replay=%S/Inputs/cgscc-inline-replay.txt -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=REPLAY %s
+;; Check baseline inline decisions
+; RUN: opt < %s -passes=inline -pass-remarks=inline --disable-output 2>&1 | FileCheck -check-prefix=DEFAULT %s
+
+;; Check module-scope replay inline decisions
+; RUN: opt < %s -passes=inline -cgscc-inline-replay=%S/Inputs/cgscc-inline-replay.txt -cgscc-inline-replay-scope=Module -pass-remarks=inline --disable-output 2>&1 | FileCheck -check-prefix=REPLAY %s
+
+;; Check function-scope inline replay decisions
+; RUN: opt < %s -passes=inline -cgscc-inline-replay=%S/Inputs/cgscc-inline-replay.txt -cgscc-inline-replay-scope=Function -pass-remarks=inline --disable-output 2>&1 | FileCheck -check-prefix=REPLAY-FUNCTION %s
+
+;; Check behavior on non-existent replay file
+; RUN: not opt < %s -passes=inline -cgscc-inline-replay=%S -pass-remarks=inline --disable-output 2>&1 | FileCheck -check-prefix=REPLAY-ERROR %s
+
+;; Check scope inlining errors out on non <Module|Function> inputs
+; RUN: not opt < %s -passes=inline -cgscc-inline-replay=%S/Inputs/cgscc-inline-replay.txt -cgscc-inline-replay-scope=function -pass-remarks=inline --disable-output 2>&1 | FileCheck -check-prefix=REPLAY-ERROR-SCOPE %s
+
+; DEFAULT: '_Z3subii' inlined into '_Z3sumii' with (cost={{[-0-9]+}}
+; DEFAULT: '_Z3sumii' inlined into 'main' with (cost={{[-0-9]+}}
+; DEFAULT-NOT: '_Z3subii' inlined into 'main'
+
+; REPLAY: '_Z3sumii' inlined into 'main' with (cost=always)
+; REPLAY: '_Z3subii' inlined into 'main' with (cost=always)
+
+; REPLAY-FUNCTION: '_Z3subii' inlined into '_Z3sumii' with (cost={{[-0-9]+}}
+; REPLAY-FUNCTION: '_Z3sumii' inlined into 'main' with (cost=always)
+
+; REPLAY-ERROR: error: Could not open remarks file: Is a directory
+; REPLAY-ERROR-SCOPE: opt: for the --cgscc-inline-replay-scope option: Cannot find option named 'function'!
@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1
!24 = !DILexicalBlockFile(scope: !18, file: !1, discriminator: 6)
!25 = !DILocation(line: 11, scope: !12)
!26 = !DILocation(line: 12, scope: !12)
-
-; DEFAULT: '_Z3subii' inlined into '_Z3sumii'
-; DEFAULT: '_Z3sumii' inlined into 'main'
-; DEFAULT-NOT: '_Z3subii' inlined into 'main'
-
-; REPLAY: '_Z3sumii' inlined into 'main'
-; REPLAY: '_Z3subii' inlined into 'main'
-; REPLAY-NOT: '_Z3subii' inlined into '_Z3sumii'
--- /dev/null
+remark: calls.cc:10:0: '_Z3sumii' inlined into 'main' to match profiling context with (cost=45, threshold=337) at callsite main:3:0.1;
\ No newline at end of file
remark: calls.cc:10:0: '_Z3sumii' inlined into 'main' to match profiling context with (cost=45, threshold=337) at callsite main:3:0.1;
-remark: calls.cc:4:0: '_Z3subii' inlined into 'main' to match profiling context with (cost=-5, threshold=337) at callsite _Z3sumii:1:0 @ main:3:0.1;
+remark: calls.cc:4:0: '_Z3subii' inlined into 'main' to match profiling context with (cost=-5, threshold=337) at callsite _Z3sumii:1:0 @ main:3:0.1;
\ No newline at end of file
--- /dev/null
+main:225715:0
+ 2.1: 5553
+ 3: 5391
+ 3.1: _Z3sumii:50000
+ 1: _Z3subii:50000
+ 1: 0
+
+_Z3sumii:6010:50000
+ 1: _Z3subii:60000
+ 1: 9
\ No newline at end of file
; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown.prof -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=DEFAULT %s
;; Check replay inline decisions
-; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown.prof -sample-profile-inline-replay=%S/Inputs/inline-replay.txt -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=REPLAY %s
+; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown.prof -sample-profile-inline-replay=%S/Inputs/inline-replay.txt -sample-profile-inline-replay-scope=Module -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=REPLAY %s
+
+;; Check baseline inline decisions with "inline-topdown-inline-all.prof" which inlines all sites
+; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown-inline-all.prof -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=DEFAULT-ALL %s
+
+;; Check function scope replay inline decisions with "inline-topdown-inline-all.prof" and "inline-topdown-function-scope.txt" which only contains: '_Z3sumii' inlined into 'main'
+;; 1. _Z3sumii is inlined into main, but all other inline candidates in main (e.g. _Z3subii) are not inlined
+;; 2. Inline decisions made in other functions match default sample inlining, in this case _Z3subii is inlined into _Z3sumii
+; RUN: opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown-inline-all.prof -sample-profile-inline-replay=%S/Inputs/inline-replay-function-scope.txt -sample-profile-inline-replay-scope=Function -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=REPLAY-ALL-FUNCTION %s
+
+;; Check behavior on non-existent replay file
+; RUN: not opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown.prof -sample-profile-inline-replay=%S -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=REPLAY-ERROR %s
+
+;; Check scope inlining errors out on non <Module|Function> inputs
+; RUN: not opt < %s -passes=sample-profile -sample-profile-file=%S/Inputs/inline-topdown.prof -sample-profile-inline-replay=%S/Inputs/inline-replay.txt -sample-profile-inline-replay-scope=function -sample-profile-merge-inlinee -sample-profile-top-down-load -pass-remarks=inline -S 2>&1 | FileCheck -check-prefix=REPLAY-ERROR-SCOPE %s
+
+; DEFAULT: '_Z3sumii' inlined into 'main' to match profiling context with (cost={{[-0-9]+}}
+; DEFAULT: '_Z3subii' inlined into '_Z3sumii' to match profiling context with (cost={{[-0-9]+}}
+; DEFAULT-NOT: '_Z3subii' inlined into 'main'
+
+; REPLAY: '_Z3sumii' inlined into 'main' to match profiling context with (cost=always)
+; REPLAY: '_Z3subii' inlined into 'main' to match profiling context with (cost=always)
+; REPLAY-NOT: '_Z3subii' inlined into '_Z3sumii'
+
+; DEFAULT-ALL: '_Z3sumii' inlined into 'main' to match profiling context with (cost={{[-0-9]+}}
+; DEFAULT-ALL: '_Z3subii' inlined into 'main' to match profiling context with (cost={{[-0-9]+}}
+; DEFAULT-ALL: '_Z3subii' inlined into '_Z3sumii' to match profiling context with (cost={{[-0-9]+}}
+
+; REPLAY-ALL-FUNCTION : _Z3sumii' inlined into 'main' to match profiling context with (cost=always)
+; REPLAY-ALL-FUNCTION-NOT: '_Z3subii' inlined into 'main' to match profiling context with (cost={{[-0-9]+}}
+; REPLAY-ALL-FUNCTION: '_Z3subii' inlined into '_Z3sumii' to match profiling context with (cost={{[-0-9]+}}
+
+; REPLAY-ERROR: error: Could not open remarks file: Is a directory
+; REPLAY-ERROR-SCOPE: opt: for the --sample-profile-inline-replay-scope option: Cannot find option named 'function'!
@.str = private unnamed_addr constant [11 x i8] c"sum is %d\0A\00", align 1
!24 = !DILexicalBlockFile(scope: !18, file: !1, discriminator: 6)
!25 = !DILocation(line: 11, scope: !12)
!26 = !DILocation(line: 12, scope: !12)
-
-
-; DEFAULT: '_Z3sumii' inlined into 'main'
-; DEFAULT: '_Z3subii' inlined into '_Z3sumii'
-; DEFAULT-NOT: '_Z3subii' inlined into 'main'
-
-; REPLAY: '_Z3sumii' inlined into 'main'
-; REPLAY: '_Z3subii' inlined into 'main'
-; REPLAY-NOT: '_Z3subii' inlined into '_Z3sumii'