[inliner] Mandatory inlining decisions produce remarks

This also removes the need to disable the mandatory inlining phase in
tests.

In a departure from the previous remark, we don't output a 'cost' in
this case, because there's no such thing. We just report that inlining
happened because of the attribute.

Differential Revision: https://reviews.llvm.org/D110891
This commit is contained in:
Mircea Trofin 2021-09-30 16:13:05 -07:00
parent 7c9d9e4e64
commit 7d541eb4d4
10 changed files with 106 additions and 33 deletions

View File

@ -6,7 +6,7 @@
// The new PM inliner is not added to the default pipeline at O0, so we add
// some optimizations to trigger it.
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -debug-info-kind=line-tables-only -emit-llvm-only -mllvm -mandatory-inlining-first=false -verify
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -debug-info-kind=line-tables-only -emit-llvm-only -verify
int foo(int x, int y) __attribute__((always_inline));
int foo(int x, int y) { return x + y; }

View File

@ -1,8 +1,8 @@
// Verify that remarks for the inliner appear. The remarks under the new PM will
// be slightly different than those emitted by the legacy PM. The new PM inliner
// also doesnot appear to be added at O0, so we test at O1.
// RUN: %clang_cc1 %s -Rpass=inline -Rpass-analysis=inline -Rpass-missed=inline -O1 -fexperimental-new-pass-manager -emit-llvm-only -mllvm -mandatory-inlining-first=false -verify
// RUN: %clang_cc1 %s -Rpass=inline -Rpass-analysis=inline -Rpass-missed=inline -O1 -fexperimental-new-pass-manager -emit-llvm-only -debug-info-kind=line-tables-only -mllvm -mandatory-inlining-first=false -verify
// RUN: %clang_cc1 %s -Rpass=inline -Rpass-analysis=inline -Rpass-missed=inline -O1 -fexperimental-new-pass-manager -emit-llvm-only -verify
// RUN: %clang_cc1 %s -Rpass=inline -Rpass-analysis=inline -Rpass-missed=inline -O1 -fexperimental-new-pass-manager -emit-llvm-only -debug-info-kind=line-tables-only -verify
int foo(int x, int y) __attribute__((always_inline));
int foo(int x, int y) { return x + y; }

View File

@ -18,47 +18,47 @@
// RUN: -fprofile-instrument-use-path=%t.profdata -Rpass=inline \
// RUN: -fexperimental-new-pass-manager -O1 \
// RUN: -Rpass-analysis=inline -Rpass-missed=inline \
// RUN: -fdiagnostics-show-hotness -verify -mllvm -mandatory-inlining-first=false
// RUN: -fdiagnostics-show-hotness -verify
// The clang version of the previous test.
// RUN: %clang -target x86_64-apple-macosx10.9 %s -c -emit-llvm -o /dev/null \
// RUN: -fprofile-instr-use=%t.profdata -Rpass=inline \
// RUN: -fexperimental-new-pass-manager -O1 \
// RUN: -Rpass-analysis=inline -Rpass-missed=inline \
// RUN: -fdiagnostics-show-hotness -Xclang -verify -mllvm -mandatory-inlining-first=false
// RUN: -fdiagnostics-show-hotness -Xclang -verify
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name \
// RUN: optimization-remark-with-hotness.c %s -emit-llvm-only \
// RUN: -fprofile-sample-use=%t-sample.profdata -Rpass=inline \
// RUN: -fexperimental-new-pass-manager -O1 \
// RUN: -Rpass-analysis=inline -Rpass-missed=inline \
// RUN: -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=10 \
// RUN: -verify -mllvm -mandatory-inlining-first=false
// RUN: -verify
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name \
// RUN: optimization-remark-with-hotness.c %s -emit-llvm-only \
// RUN: -fprofile-instrument-use-path=%t.profdata -Rpass=inline \
// RUN: -fexperimental-new-pass-manager -O1 \
// RUN: -Rpass-analysis=inline -Rpass-missed=inline \
// RUN: -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=10 -verify -mllvm -mandatory-inlining-first=false
// RUN: -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=10 -verify
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name \
// RUN: optimization-remark-with-hotness.c %s -emit-llvm-only \
// RUN: -fprofile-instrument-use-path=%t.profdata -Rpass=inline \
// RUN: -fexperimental-new-pass-manager -O1 \
// RUN: -Rpass-analysis=inline -mllvm -mandatory-inlining-first=false 2>&1 | FileCheck -check-prefix=HOTNESS_OFF %s
// RUN: -Rpass-analysis=inline 2>&1 | FileCheck -check-prefix=HOTNESS_OFF %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name \
// RUN: optimization-remark-with-hotness.c %s -emit-llvm-only \
// RUN: -fprofile-instrument-use-path=%t.profdata -Rpass=inline \
// RUN: -fexperimental-new-pass-manager -O1 \
// RUN: -Rpass-analysis=inline -Rno-pass-with-hotness -mllvm -mandatory-inlining-first=false 2>&1 | FileCheck \
// RUN: -Rpass-analysis=inline -Rno-pass-with-hotness 2>&1 | FileCheck \
// RUN: -check-prefix=HOTNESS_OFF %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name \
// RUN: optimization-remark-with-hotness.c %s -emit-llvm-only \
// RUN: -fprofile-instrument-use-path=%t.profdata -Rpass=inline \
// RUN: -Rpass-analysis=inline -fdiagnostics-show-hotness \
// RUN: -fdiagnostics-hotness-threshold=100 -mllvm -mandatory-inlining-first=false 2>&1 \
// RUN: -fdiagnostics-hotness-threshold=100 2>&1 \
// RUN: | FileCheck -allow-empty -check-prefix=THRESHOLD %s
// RUN: %clang_cc1 -triple x86_64-apple-macosx10.9 -main-file-name \
// RUN: optimization-remark-with-hotness.c %s -emit-llvm-only \
// RUN: -Rpass=inline -Rpass-analysis=inline \
// RUN: -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=10 -mllvm -mandatory-inlining-first=false 2>&1 \
// RUN: -fdiagnostics-show-hotness -fdiagnostics-hotness-threshold=10 2>&1 \
// RUN: | FileCheck -check-prefix=NO_PGO %s
int foo(int x, int y) __attribute__((always_inline));
@ -73,7 +73,7 @@ void bar(int x) {
// THRESHOLD-NOT: hotness
// NO_PGO: '-fdiagnostics-show-hotness' requires profile-guided optimization information
// NO_PGO: '-fdiagnostics-hotness-threshold=' requires profile-guided optimization information
// expected-remark@+1 {{'foo' inlined into 'bar' with (cost=always): always inline attribute at callsite bar:8:10; (hotness:}}
// expected-remark@+1 {{'foo' inlined into 'bar': always inline attribute at callsite bar:8:10; (hotness:}}
sum += foo(x, x - 2);
}

View File

@ -20,12 +20,12 @@
//
// The inliner for the new PM does not seem to be enabled at O0, but we still
// get the same remarks with at least O1.
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -emit-llvm -mllvm -mandatory-inlining-first=false -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -Rno-everything -Reverything -emit-llvm -mllvm -mandatory-inlining-first=false -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -Rno-everything -Reverything -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
//
// Check that -w doesn't disable remarks.
// RUN: %clang_cc1 %s -Rpass=inline -fno-experimental-new-pass-manager -w -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -w -emit-llvm -mllvm -mandatory-inlining-first=false -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
// RUN: %clang_cc1 %s -Rpass=inline -fexperimental-new-pass-manager -O1 -w -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS
//
// -Reverything implies -Rpass=.*.
// RUN: %clang_cc1 %s -Reverything -emit-llvm -o - 2>&1 | FileCheck %s --check-prefix=CHECK-REMARKS

View File

@ -263,10 +263,17 @@ shouldInline(CallBase &CB, function_ref<InlineCost(CallBase &CB)> GetInlineCost,
/// Emit ORE message.
void emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
const BasicBlock *Block, const Function &Callee,
const Function &Caller, const InlineCost &IC,
bool ForProfileContext = false,
const Function &Caller, bool IsMandatory,
function_ref<void(OptimizationRemark &)> ExtraContext = {},
const char *PassName = nullptr);
/// Emit ORE message based in cost (default heuristic).
void emitInlinedIntoBasedOnCost(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
const BasicBlock *Block, const Function &Callee,
const Function &Caller, const InlineCost &IC,
bool ForProfileContext = false,
const char *PassName = nullptr);
/// get call site location as string
std::string getCallSiteLocation(DebugLoc DLoc);

View File

@ -49,6 +49,42 @@ static cl::opt<int>
extern cl::opt<InlinerFunctionImportStatsOpts> InlinerFunctionImportStats;
namespace {
using namespace llvm::ore;
class MandatoryInlineAdvice : public InlineAdvice {
public:
MandatoryInlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
OptimizationRemarkEmitter &ORE,
bool IsInliningMandatory)
: InlineAdvice(Advisor, CB, ORE, IsInliningMandatory) {}
private:
void recordInliningWithCalleeDeletedImpl() override { recordInliningImpl(); }
void recordInliningImpl() override {
if (IsInliningRecommended)
emitInlinedInto(ORE, DLoc, Block, *Callee, *Caller, IsInliningRecommended,
[&](OptimizationRemark &Remark) {
Remark << ": always inline attribute";
});
}
void recordUnsuccessfulInliningImpl(const InlineResult &Result) override {
if (IsInliningRecommended)
ORE.emit([&]() {
return OptimizationRemarkMissed(DEBUG_TYPE, "NotInlined", DLoc, Block)
<< "'" << NV("Callee", Callee) << "' is not AlwaysInline into '"
<< NV("Caller", Caller)
<< "': " << NV("Reason", Result.getFailureReason());
});
}
void recordUnattemptedInliningImpl() override {
assert(!IsInliningRecommended && "Expected to attempt inlining");
}
};
} // namespace
void DefaultInlineAdvice::recordUnsuccessfulInliningImpl(
const InlineResult &Result) {
using namespace ore;
@ -64,12 +100,12 @@ void DefaultInlineAdvice::recordUnsuccessfulInliningImpl(
void DefaultInlineAdvice::recordInliningWithCalleeDeletedImpl() {
if (EmitRemarks)
emitInlinedInto(ORE, DLoc, Block, *Callee, *Caller, *OIC);
emitInlinedIntoBasedOnCost(ORE, DLoc, Block, *Callee, *Caller, *OIC);
}
void DefaultInlineAdvice::recordInliningImpl() {
if (EmitRemarks)
emitInlinedInto(ORE, DLoc, Block, *Callee, *Caller, *OIC);
emitInlinedIntoBasedOnCost(ORE, DLoc, Block, *Callee, *Caller, *OIC);
}
llvm::Optional<llvm::InlineCost> static getDefaultInlineAdvice(
@ -435,25 +471,38 @@ void llvm::addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc) {
Remark << ";";
}
void llvm::emitInlinedInto(OptimizationRemarkEmitter &ORE, DebugLoc DLoc,
const BasicBlock *Block, const Function &Callee,
const Function &Caller, const InlineCost &IC,
bool ForProfileContext, const char *PassName) {
void llvm::emitInlinedInto(
OptimizationRemarkEmitter &ORE, DebugLoc DLoc, const BasicBlock *Block,
const Function &Callee, const Function &Caller, bool AlwaysInline,
function_ref<void(OptimizationRemark &)> ExtraContext,
const char *PassName) {
ORE.emit([&]() {
bool AlwaysInline = IC.isAlways();
StringRef RemarkName = AlwaysInline ? "AlwaysInline" : "Inlined";
OptimizationRemark Remark(PassName ? PassName : DEBUG_TYPE, RemarkName,
DLoc, Block);
Remark << "'" << ore::NV("Callee", &Callee) << "' inlined into '"
<< ore::NV("Caller", &Caller) << "'";
if (ForProfileContext)
Remark << " to match profiling context";
Remark << " with " << IC;
if (ExtraContext)
ExtraContext(Remark);
addLocationToRemarks(Remark, DLoc);
return Remark;
});
}
void llvm::emitInlinedIntoBasedOnCost(
OptimizationRemarkEmitter &ORE, DebugLoc DLoc, const BasicBlock *Block,
const Function &Callee, const Function &Caller, const InlineCost &IC,
bool ForProfileContext, const char *PassName) {
llvm::emitInlinedInto(
ORE, DLoc, Block, Callee, Caller, IC.isAlways(),
[&](OptimizationRemark &Remark) {
if (ForProfileContext)
Remark << " to match profiling context";
Remark << " with " << IC;
},
PassName);
}
InlineAdvisor::InlineAdvisor(Module &M, FunctionAnalysisManager &FAM)
: M(M), FAM(FAM) {
if (InlinerFunctionImportStats != InlinerFunctionImportStatsOpts::No) {
@ -475,7 +524,8 @@ InlineAdvisor::~InlineAdvisor() {
std::unique_ptr<InlineAdvice> InlineAdvisor::getMandatoryAdvice(CallBase &CB,
bool Advice) {
return std::make_unique<InlineAdvice>(this, CB, getCallerORE(CB), Advice);
return std::make_unique<MandatoryInlineAdvice>(this, CB, getCallerORE(CB),
Advice);
}
InlineAdvisor::MandatoryInliningKind

View File

@ -73,8 +73,8 @@ PreservedAnalyses AlwaysInlinerPass::run(Module &M,
},
ORE);
assert(OIC);
emitInlinedInto(ORE, CB->getDebugLoc(), CB->getParent(), F, *Caller,
*OIC, false, DEBUG_TYPE);
emitInlinedIntoBasedOnCost(ORE, CB->getDebugLoc(), CB->getParent(), F,
*Caller, *OIC, false, DEBUG_TYPE);
InlineFunctionInfo IFI(
/*cg=*/nullptr, GetAssumptionCache, &PSI,

View File

@ -464,7 +464,7 @@ inlineCallsImpl(CallGraphSCC &SCC, CallGraph &CG,
}
++NumInlined;
emitInlinedInto(ORE, DLoc, Block, *Callee, *Caller, *OIC);
emitInlinedIntoBasedOnCost(ORE, DLoc, Block, *Callee, *Caller, *OIC);
// If inlining this function gave us any new call sites, throw them
// onto our worklist to process. They are useful inline candidates.

View File

@ -1204,8 +1204,8 @@ bool SampleProfileLoader::tryInlineCandidate(
*CalledFunction);
// The call to InlineFunction erases I, so we can't pass it here.
emitInlinedInto(*ORE, DLoc, BB, *CalledFunction, *BB->getParent(), Cost,
true, CSINLINE_DEBUG);
emitInlinedIntoBasedOnCost(*ORE, DLoc, BB, *CalledFunction,
*BB->getParent(), Cost, true, CSINLINE_DEBUG);
// Now populate the list of newly exposed call sites.
if (InlinedCallSites) {

View File

@ -0,0 +1,16 @@
; RUN: opt -passes="cgscc(inline<only-mandatory>)" -pass-remarks-missed=inline -S < %s 2>&1 | FileCheck %s
declare void @personalityFn1();
declare void @personalityFn2();
define i32 @a() personality void ()* @personalityFn1 {
ret i32 1
}
define i32 @b() personality void ()* @personalityFn2 {
%r = call i32 @a() alwaysinline
ret i32 %r
}
; CHECK: remark: {{.*}} 'a' is not AlwaysInline into 'b': incompatible personality