llvm-project/llvm/lib/Analysis/ReplayInlineAdvisor.cpp
modimo 5caad9b5d3 [InlineAdvisor] Add fallback/format switches and negative remark processing to Replay Inliner
Adds the following switches:

1. --sample-profile-inline-replay-fallback/--cgscc-inline-replay-fallback: controls what the replay advisor does for inline sites that are not present in the replay. Options are:

 1. Original: defers to original advisor
 2. AlwaysInline: inline all sites not in replay
 3. NeverInline: inline no sites not in replay

2. --sample-profile-inline-replay-format/--cgscc-inline-replay-format: controls what format should be generated to match against the replay remarks. Options are:

  1. Line
  2. LineColumn
  3. LineDiscriminator
  4. LineColumnDiscriminator

Adds support for negative inlining decisions. These are denoted by "will not be inlined into" as compared to the positive "inlined into" in the remarks.

All of these together with the previous `--sample-profile-inline-replay-scope/--cgscc-inline-replay-scope` allow tweaking in how to apply replay. In my testing, I'm using:
1. --sample-profile-inline-replay-scope/--cgscc-inline-replay-scope = Function to only replay on a function
2. --sample-profile-inline-replay-fallback/--cgscc-inline-replay-fallback = NeverInline since I'm feeding in only positive remarks to the replay system
3. --sample-profile-inline-replay-format/--cgscc-inline-replay-format = Line since I'm generating the remarks from DWARF information from GCC which can conflict quite heavily in column number compared to Clang

An alternative configuration could be to do Function, AlwaysInline, Line fallback with negative remarks which closer matches the final call-sites. Note that this can lead to unbounded inlining if a negative remark doesn't match/exist for one reason or another.

Updated various tests to cover the new switches and negative remarks

Testing:
ninja check-all

Reviewed By: wenlei, mtrofin

Differential Revision: https://reviews.llvm.org/D112040
2021-10-29 12:32:03 -07:00

151 lines
5.7 KiB
C++

//===- ReplayInlineAdvisor.cpp - Replay InlineAdvisor ---------------------===//
//
// 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 ReplayInlineAdvisor that replays inline decisions based
// on previous inline remarks from optimization remark log. This is a best
// effort approach useful for testing compiler/source changes while holding
// inlining steady.
//
//===----------------------------------------------------------------------===//
#include "llvm/Analysis/ReplayInlineAdvisor.h"
#include "llvm/IR/DebugInfoMetadata.h"
#include "llvm/IR/Instructions.h"
#include "llvm/Support/LineIterator.h"
#include <memory>
using namespace llvm;
#define DEBUG_TYPE "replay-inline"
ReplayInlineAdvisor::ReplayInlineAdvisor(
Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context,
std::unique_ptr<InlineAdvisor> OriginalAdvisor,
const ReplayInlinerSettings &ReplaySettings, bool EmitRemarks)
: InlineAdvisor(M, FAM), OriginalAdvisor(std::move(OriginalAdvisor)),
HasReplayRemarks(false), ReplaySettings(ReplaySettings),
EmitRemarks(EmitRemarks) {
auto BufferOrErr = MemoryBuffer::getFileOrSTDIN(ReplaySettings.ReplayFile);
std::error_code EC = BufferOrErr.getError();
if (EC) {
Context.emitError("Could not open remarks file: " + EC.message());
return;
}
// Example for inline remarks to parse:
// 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);
const std::string PositiveRemark = "' inlined into '";
const std::string NegativeRemark = "' will not be inlined into '";
for (; !LineIt.is_at_eof(); ++LineIt) {
StringRef Line = *LineIt;
auto Pair = Line.split(" at callsite ");
bool IsPositiveRemark = true;
if (Pair.first.contains(NegativeRemark))
IsPositiveRemark = false;
auto CalleeCaller =
Pair.first.split(IsPositiveRemark ? PositiveRemark : NegativeRemark);
StringRef Callee = CalleeCaller.first.rsplit(": '").second;
StringRef Caller = CalleeCaller.second.rsplit("'").first;
auto CallSite = Pair.second.split(";").first;
if (Callee.empty() || Caller.empty() || CallSite.empty()) {
Context.emitError("Invalid remark format: " + Line);
return;
}
std::string Combined = (Callee + CallSite).str();
InlineSitesFromRemarks[Combined] = IsPositiveRemark;
if (ReplaySettings.ReplayScope == ReplayInlinerSettings::Scope::Function)
CallersToReplay.insert(Caller);
}
HasReplayRemarks = true;
}
std::unique_ptr<InlineAdvisor> llvm::getReplayInlineAdvisor(
Module &M, FunctionAnalysisManager &FAM, LLVMContext &Context,
std::unique_ptr<InlineAdvisor> OriginalAdvisor,
const ReplayInlinerSettings &ReplaySettings, bool EmitRemarks) {
auto Advisor = std::make_unique<ReplayInlineAdvisor>(
M, FAM, Context, std::move(OriginalAdvisor), ReplaySettings, 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);
// Decision not made by replay system
if (!hasInlineAdvice(*CB.getFunction())) {
// If there's a registered original advisor, return its decision
if (OriginalAdvisor)
return OriginalAdvisor->getAdvice(CB);
// If no decision is made above, return non-decision
return {};
}
std::string CallSiteLoc =
formatCallSiteLocation(CB.getDebugLoc(), ReplaySettings.ReplayFormat);
StringRef Callee = CB.getCalledFunction()->getName();
std::string Combined = (Callee + CallSiteLoc).str();
// Replay decision, if it has one
auto Iter = InlineSitesFromRemarks.find(Combined);
if (Iter != InlineSitesFromRemarks.end()) {
if (InlineSitesFromRemarks[Combined]) {
LLVM_DEBUG(dbgs() << "Replay Inliner: Inlined " << Callee << " @ "
<< CallSiteLoc << "\n");
return std::make_unique<DefaultInlineAdvice>(
this, CB, llvm::InlineCost::getAlways("previously inlined"), ORE,
EmitRemarks);
} else {
LLVM_DEBUG(dbgs() << "Replay Inliner: Not Inlined " << Callee << " @ "
<< CallSiteLoc << "\n");
// A negative inline is conveyed by "None" Optional<InlineCost>
return std::make_unique<DefaultInlineAdvice>(this, CB, None, ORE,
EmitRemarks);
}
}
// Fallback decisions
if (ReplaySettings.ReplayFallback ==
ReplayInlinerSettings::Fallback::AlwaysInline)
return std::make_unique<DefaultInlineAdvice>(
this, CB, llvm::InlineCost::getAlways("AlwaysInline Fallback"), ORE,
EmitRemarks);
else if (ReplaySettings.ReplayFallback ==
ReplayInlinerSettings::Fallback::NeverInline)
// A negative inline is conveyed by "None" Optional<InlineCost>
return std::make_unique<DefaultInlineAdvice>(this, CB, None, ORE,
EmitRemarks);
else {
assert(ReplaySettings.ReplayFallback ==
ReplayInlinerSettings::Fallback::Original);
// If there's a registered original advisor, return its decision
if (OriginalAdvisor)
return OriginalAdvisor->getAdvice(CB);
}
// If no decision is made above, return non-decision
return {};
}