1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
|
#pragma once
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//===- InlineAdvisor.h - Inlining decision making abstraction -*- C++ ---*-===//
//
// 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
//
//===----------------------------------------------------------------------===//
//
#ifndef LLVM_INLINEADVISOR_H_
#define LLVM_INLINEADVISOR_H_
#include "llvm/Analysis/InlineCost.h"
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Analysis/Utils/ImportedFunctionsInliningStatistics.h"
#include <memory>
#include <unordered_set>
namespace llvm {
class BasicBlock;
class CallBase;
class Function;
class Module;
class OptimizationRemarkEmitter;
/// There are 3 scenarios we can use the InlineAdvisor:
/// - Default - use manual heuristics.
///
/// - Release mode, the expected mode for production, day to day deployments.
/// In this mode, when building the compiler, we also compile a pre-trained ML
/// model to native code, and link it as a static library. This mode has low
/// overhead and no additional dependencies for the compiler runtime.
///
/// - Development mode, for training new models.
/// In this mode, we trade off runtime performance for flexibility. This mode
/// requires the full C Tensorflow API library, and evaluates models
/// dynamically. This mode also permits generating training logs, for offline
/// training.
enum class InliningAdvisorMode : int {
Default,
Release,
Development
};
class InlineAdvisor;
/// Capture state between an inlining decision having had been made, and
/// its impact being observable. When collecting model training data, this
/// allows recording features/decisions/partial reward data sets.
///
/// Derivations of this type are expected to be tightly coupled with their
/// InliningAdvisors. The base type implements the minimal contractual
/// obligations.
class InlineAdvice {
public:
InlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
OptimizationRemarkEmitter &ORE, bool IsInliningRecommended);
InlineAdvice(InlineAdvice &&) = delete;
InlineAdvice(const InlineAdvice &) = delete;
virtual ~InlineAdvice() {
assert(Recorded && "InlineAdvice should have been informed of the "
"inliner's decision in all cases");
}
/// Exactly one of the record* APIs must be called. Implementers may extend
/// behavior by implementing the corresponding record*Impl.
///
/// Call after inlining succeeded, and did not result in deleting the callee.
void recordInlining();
/// Call after inlining succeeded, and resulted in deleting the callee.
void recordInliningWithCalleeDeleted();
/// Call after the decision for a call site was to not inline.
void recordUnsuccessfulInlining(const InlineResult &Result) {
markRecorded();
recordUnsuccessfulInliningImpl(Result);
}
/// Call to indicate inlining was not attempted.
void recordUnattemptedInlining() {
markRecorded();
recordUnattemptedInliningImpl();
}
/// Get the inlining recommendation.
bool isInliningRecommended() const { return IsInliningRecommended; }
const DebugLoc &getOriginalCallSiteDebugLoc() const { return DLoc; }
const BasicBlock *getOriginalCallSiteBasicBlock() const { return Block; }
protected:
virtual void recordInliningImpl() {}
virtual void recordInliningWithCalleeDeletedImpl() {}
virtual void recordUnsuccessfulInliningImpl(const InlineResult &Result) {}
virtual void recordUnattemptedInliningImpl() {}
InlineAdvisor *const Advisor;
/// Caller and Callee are pre-inlining.
Function *const Caller;
Function *const Callee;
// Capture the context of CB before inlining, as a successful inlining may
// change that context, and we want to report success or failure in the
// original context.
const DebugLoc DLoc;
const BasicBlock *const Block;
OptimizationRemarkEmitter &ORE;
const bool IsInliningRecommended;
private:
void markRecorded() {
assert(!Recorded && "Recording should happen exactly once");
Recorded = true;
}
void recordInlineStatsIfNeeded();
bool Recorded = false;
};
class DefaultInlineAdvice : public InlineAdvice {
public:
DefaultInlineAdvice(InlineAdvisor *Advisor, CallBase &CB,
Optional<InlineCost> OIC, OptimizationRemarkEmitter &ORE,
bool EmitRemarks = true)
: InlineAdvice(Advisor, CB, ORE, OIC.hasValue()), OriginalCB(&CB),
OIC(OIC), EmitRemarks(EmitRemarks) {}
private:
void recordUnsuccessfulInliningImpl(const InlineResult &Result) override;
void recordInliningWithCalleeDeletedImpl() override;
void recordInliningImpl() override;
private:
CallBase *const OriginalCB;
Optional<InlineCost> OIC;
bool EmitRemarks;
};
/// Interface for deciding whether to inline a call site or not.
class InlineAdvisor {
public:
InlineAdvisor(InlineAdvisor &&) = delete;
virtual ~InlineAdvisor();
/// Get an InlineAdvice containing a recommendation on whether to
/// inline or not. \p CB is assumed to be a direct call. \p FAM is assumed to
/// 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.
std::unique_ptr<InlineAdvice> getAdvice(CallBase &CB,
bool MandatoryOnly = false);
/// This must be called when the Inliner pass is entered, to allow the
/// InlineAdvisor update internal state, as result of function passes run
/// between Inliner pass runs (for the same module).
virtual void onPassEntry() {}
/// This must be called when the Inliner pass is exited, as function passes
/// may be run subsequently. This allows an implementation of InlineAdvisor
/// to prepare for a partial update.
virtual void onPassExit() {}
protected:
InlineAdvisor(Module &M, FunctionAnalysisManager &FAM);
virtual std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) = 0;
virtual std::unique_ptr<InlineAdvice> getMandatoryAdvice(CallBase &CB,
bool Advice);
Module &M;
FunctionAnalysisManager &FAM;
std::unique_ptr<ImportedFunctionsInliningStatistics> ImportedFunctionsStats;
/// We may want to defer deleting functions to after the inlining for a whole
/// module has finished. This allows us to reliably use function pointers as
/// unique identifiers, as an efficient implementation detail of the
/// InlineAdvisor. Otherwise, it is possible the memory allocator
/// re-allocate Function objects at the same address of a deleted Function;
/// and Functions are potentially created during the function passes called
/// after each SCC inlining (e.g. argument promotion does that).
void freeDeletedFunctions();
bool isFunctionDeleted(const Function *F) const {
return DeletedFunctions.count(F);
}
enum class MandatoryInliningKind { NotMandatory, Always, Never };
static MandatoryInliningKind getMandatoryKind(CallBase &CB,
FunctionAnalysisManager &FAM,
OptimizationRemarkEmitter &ORE);
OptimizationRemarkEmitter &getCallerORE(CallBase &CB);
private:
friend class InlineAdvice;
void markFunctionAsDeleted(Function *F);
std::unordered_set<const Function *> DeletedFunctions;
};
/// The default (manual heuristics) implementation of the InlineAdvisor. This
/// implementation does not need to keep state between inliner pass runs, and is
/// reusable as-is for inliner pass test scenarios, as well as for regular use.
class DefaultInlineAdvisor : public InlineAdvisor {
public:
DefaultInlineAdvisor(Module &M, FunctionAnalysisManager &FAM,
InlineParams Params)
: InlineAdvisor(M, FAM), Params(Params) {}
private:
std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override;
void onPassExit() override { freeDeletedFunctions(); }
InlineParams Params;
};
/// The InlineAdvisorAnalysis is a module pass because the InlineAdvisor
/// needs to capture state right before inlining commences over a module.
class InlineAdvisorAnalysis : public AnalysisInfoMixin<InlineAdvisorAnalysis> {
public:
static AnalysisKey Key;
InlineAdvisorAnalysis() = default;
struct Result {
Result(Module &M, ModuleAnalysisManager &MAM) : M(M), MAM(MAM) {}
bool invalidate(Module &, const PreservedAnalyses &,
ModuleAnalysisManager::Invalidator &) {
// InlineAdvisor must be preserved across analysis invalidations.
return false;
}
bool tryCreate(InlineParams Params, InliningAdvisorMode Mode,
StringRef ReplayFile);
InlineAdvisor *getAdvisor() const { return Advisor.get(); }
void clear() { Advisor.reset(); }
private:
Module &M;
ModuleAnalysisManager &MAM;
std::unique_ptr<InlineAdvisor> Advisor;
};
Result run(Module &M, ModuleAnalysisManager &MAM) { return Result(M, MAM); }
};
#ifdef LLVM_HAVE_TF_AOT
std::unique_ptr<InlineAdvisor>
getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM);
#endif
#ifdef LLVM_HAVE_TF_API
std::unique_ptr<InlineAdvisor>
getDevelopmentModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
std::function<bool(CallBase &)> GetDefaultAdvice);
#endif
// Default (manual policy) decision making helper APIs. Shared with the legacy
// pass manager inliner.
/// Return the cost only if the inliner should attempt to inline at the given
/// CallSite. If we return the cost, we will emit an optimisation remark later
/// using that cost, so we won't do so from this function. Return None if
/// inlining should not be attempted.
Optional<InlineCost>
shouldInline(CallBase &CB, function_ref<InlineCost(CallBase &CB)> GetInlineCost,
OptimizationRemarkEmitter &ORE, bool EnableDeferral = true);
/// 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 char *PassName = nullptr);
/// get call site location as string
std::string getCallSiteLocation(DebugLoc DLoc);
/// Add location info to ORE message.
void addLocationToRemarks(OptimizationRemark &Remark, DebugLoc DLoc);
/// Set the inline-remark attribute.
void setInlineRemark(CallBase &CB, StringRef Message);
/// Utility for extracting the inline cost message to a string.
std::string inlineCostStr(const InlineCost &IC);
} // namespace llvm
#endif // LLVM_INLINEADVISOR_H_
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
|