Skip to content

Commit c878baf

Browse files
authored
[mlgo][inliner] Fix potential concurrency issue in local ThinLTO + IR2Vec cases (#156120)
The inliner's `FeatureMap` used to be immutable, but in IR2Vec cases we don't know the shapes of the embedding vectors until later, so we need to initialize it at the time we construct the advisor. In non-distributed ThinLTO cases, for example, this means we'd mutate shared state. The feature set is also needed when constructing the underlying model runner. The alternative here is to postpone the creation of the model runner to the time we construct the advisor, and also make the feature map a member of the advisor object. (issue identified by @efriedma-quic in PR #154541)
1 parent 38832a8 commit c878baf

File tree

4 files changed

+85
-58
lines changed

4 files changed

+85
-58
lines changed

llvm/include/llvm/Analysis/InlineModelFeatureMaps.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,6 @@ inlineCostFeatureToMlFeature(InlineCostFeatureIndex Feature) {
160160
return static_cast<FeatureIndex>(static_cast<size_t>(Feature));
161161
}
162162

163-
LLVM_ABI extern std::vector<TensorSpec> &getFeatureMap();
164-
165163
LLVM_ABI extern const char *const DecisionName;
166164
LLVM_ABI extern const TensorSpec InlineDecisionSpec;
167165
LLVM_ABI extern const char *const DefaultDecisionName;

llvm/include/llvm/Analysis/MLInlineAdvisor.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ class ProfileSummaryInfo;
2828
class MLInlineAdvisor : public InlineAdvisor {
2929
public:
3030
MLInlineAdvisor(Module &M, ModuleAnalysisManager &MAM,
31-
std::unique_ptr<MLModelRunner> ModelRunner,
31+
std::function<std::unique_ptr<MLModelRunner>(
32+
const std::vector<TensorSpec> &)>
33+
GetModelRunner,
3234
std::function<bool(CallBase &)> GetDefaultAdvice);
3335

3436
virtual ~MLInlineAdvisor() = default;
@@ -46,6 +48,8 @@ class MLInlineAdvisor : public InlineAdvisor {
4648
int64_t getLocalCalls(Function &F);
4749
const MLModelRunner &getModelRunner() const { return *ModelRunner; }
4850
FunctionPropertiesInfo &getCachedFPI(Function &) const;
51+
const std::vector<TensorSpec> &getFeatureMap() const { return FeatureMap; };
52+
static const std::vector<TensorSpec> &getInitialFeatureMap();
4953

5054
protected:
5155
std::unique_ptr<InlineAdvice> getAdviceImpl(CallBase &CB) override;
@@ -65,6 +69,7 @@ class MLInlineAdvisor : public InlineAdvisor {
6569

6670
std::unique_ptr<MLModelRunner> ModelRunner;
6771
std::function<bool(CallBase &)> GetDefaultAdvice;
72+
std::vector<TensorSpec> FeatureMap;
6873

6974
private:
7075
int64_t getModuleIRSize() const;

llvm/lib/Analysis/DevelopmentModeInlineAdvisor.cpp

Lines changed: 43 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,8 @@ struct InlineEvent {
9797
/// Collect data we may use for training a model.
9898
class TrainingLogger final {
9999
public:
100-
TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR);
100+
TrainingLogger(StringRef LogFileName, const ModelUnderTrainingRunner *MUTR,
101+
const std::vector<TensorSpec> &FeatureMap);
101102

102103
/// Log one inlining event.
103104
void logInlineEvent(const InlineEvent &Event,
@@ -106,6 +107,8 @@ class TrainingLogger final {
106107
private:
107108
StringRef LogFileName;
108109
const ModelUnderTrainingRunner *const MUTR;
110+
const std::vector<TensorSpec> &FeatureMap;
111+
109112
std::unique_ptr<Logger> L;
110113
BitVector Effects;
111114
/// Set these 2 clearly OOB, to make sure we set them later.
@@ -142,9 +145,10 @@ class DevelopmentModeMLInlineAdvisor : public MLInlineAdvisor {
142145
public:
143146
DevelopmentModeMLInlineAdvisor(
144147
Module &M, ModuleAnalysisManager &MAM,
145-
std::unique_ptr<MLModelRunner> ModelRunner,
146-
std::function<bool(CallBase &)> GetDefaultAdvice,
147-
std::unique_ptr<TrainingLogger> Logger);
148+
std::function<
149+
std::unique_ptr<MLModelRunner>(const std::vector<TensorSpec> &)>
150+
GetModelRunner,
151+
std::function<bool(CallBase &)> GetDefaultAdvice);
148152

149153
size_t getTotalSizeEstimate();
150154

@@ -258,9 +262,13 @@ static const std::vector<TensorSpec> TrainingOnlyFeatures{
258262
TensorSpec::createSpec<float>(TFFeedPrefix + "reward", {1}),
259263
TensorSpec::createSpec<int32_t>(TFFeedPrefix + "step_type", {1})};
260264

261-
static const std::vector<TensorSpec> getInputFeatures() {
265+
// add TFFeedPrefix to the names and also add the "TrainingOnlyFeatures" which
266+
// the model runner needs to see present. We don't set them ourselves or
267+
// interact with them.
268+
static const std::vector<TensorSpec>
269+
convertInputFeatures(const std::vector<TensorSpec> &OriginalFeatures) {
262270
std::vector<TensorSpec> InputSpecs;
263-
for (const auto &Feature : getFeatureMap())
271+
for (const auto &Feature : OriginalFeatures)
264272
InputSpecs.push_back(TensorSpec(TFFeedPrefix + Feature.name(), Feature));
265273
append_range(InputSpecs, TrainingOnlyFeatures);
266274
return InputSpecs;
@@ -269,8 +277,9 @@ static const std::vector<TensorSpec> getInputFeatures() {
269277
} // namespace
270278

271279
TrainingLogger::TrainingLogger(StringRef LogFileName,
272-
const ModelUnderTrainingRunner *MUTR)
273-
: LogFileName(LogFileName), MUTR(MUTR) {
280+
const ModelUnderTrainingRunner *MUTR,
281+
const std::vector<TensorSpec> &FeatureMap)
282+
: LogFileName(LogFileName), MUTR(MUTR), FeatureMap(FeatureMap) {
274283
// The first output is the inlining decision.
275284
std::vector<TensorSpec> FT(getFeatureMap().begin(), getFeatureMap().end());
276285

@@ -327,15 +336,19 @@ void TrainingLogger::logInlineEvent(const InlineEvent &Event,
327336

328337
DevelopmentModeMLInlineAdvisor::DevelopmentModeMLInlineAdvisor(
329338
Module &M, ModuleAnalysisManager &MAM,
330-
std::unique_ptr<MLModelRunner> ModelRunner,
331-
std::function<bool(CallBase &)> GetDefaultAdvice,
332-
std::unique_ptr<TrainingLogger> Logger)
333-
: MLInlineAdvisor(M, MAM, std::move(ModelRunner), GetDefaultAdvice),
339+
std::function<
340+
std::unique_ptr<MLModelRunner>(const std::vector<TensorSpec> &)>
341+
GetModelRunner,
342+
std::function<bool(CallBase &)> GetDefaultAdvice)
343+
: MLInlineAdvisor(M, MAM, GetModelRunner, GetDefaultAdvice),
334344
IsDoingInference(isa<ModelUnderTrainingRunner>(getModelRunner())),
335-
Logger(std::move(Logger)),
336345
InitialNativeSize(isLogging() ? getTotalSizeEstimate() : 0),
337346
CurrentNativeSize(InitialNativeSize) {
338347
// We cannot have the case of neither inference nor logging.
348+
if (!TrainingLog.empty())
349+
Logger = std::make_unique<TrainingLogger>(
350+
TrainingLog, dyn_cast<ModelUnderTrainingRunner>(ModelRunner.get()),
351+
getFeatureMap());
339352
assert(IsDoingInference || isLogging());
340353
}
341354

@@ -401,21 +414,22 @@ std::unique_ptr<InlineAdvisor> llvm::getDevelopmentModeAdvisor(
401414
Module &M, ModuleAnalysisManager &MAM,
402415
std::function<bool(CallBase &)> GetDefaultAdvice) {
403416
auto &Ctx = M.getContext();
404-
std::unique_ptr<MLModelRunner> Runner;
405-
if (TFModelUnderTrainingPath.empty())
406-
Runner.reset(new NoInferenceModelRunner(Ctx, getInputFeatures()));
407-
else
408-
Runner = ModelUnderTrainingRunner::createAndEnsureValid(
409-
Ctx, TFModelUnderTrainingPath, DecisionName, getInputFeatures(),
410-
TFOutputSpecOverride);
411-
if (!Runner)
412-
return nullptr;
413-
std::unique_ptr<TrainingLogger> Logger;
414-
if (!TrainingLog.empty())
415-
Logger = std::make_unique<TrainingLogger>(
416-
TrainingLog, dyn_cast<ModelUnderTrainingRunner>(Runner.get()));
417-
418-
return std::make_unique<DevelopmentModeMLInlineAdvisor>(
419-
M, MAM, std::move(Runner), GetDefaultAdvice, std::move(Logger));
417+
auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
418+
-> std::unique_ptr<MLModelRunner> {
419+
std::unique_ptr<MLModelRunner> Runner;
420+
const std::vector<TensorSpec> ConvertedFeatures =
421+
convertInputFeatures(InputFeatures);
422+
if (TFModelUnderTrainingPath.empty())
423+
Runner.reset(new NoInferenceModelRunner(Ctx, ConvertedFeatures));
424+
else
425+
Runner = ModelUnderTrainingRunner::createAndEnsureValid(
426+
Ctx, TFModelUnderTrainingPath, DecisionName, ConvertedFeatures,
427+
TFOutputSpecOverride);
428+
if (!Runner)
429+
return nullptr;
430+
return Runner;
431+
};
432+
return std::make_unique<DevelopmentModeMLInlineAdvisor>(M, MAM, RunnerFactory,
433+
GetDefaultAdvice);
420434
}
421435
#endif // defined(LLVM_HAVE_TFLITE)

llvm/lib/Analysis/MLInlineAdvisor.cpp

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -75,21 +75,22 @@ llvm::getReleaseModeAdvisor(Module &M, ModuleAnalysisManager &MAM,
7575
if (!llvm::isEmbeddedModelEvaluatorValid<CompiledModelType>() &&
7676
InteractiveChannelBaseName.empty())
7777
return nullptr;
78-
std::unique_ptr<MLModelRunner> AOTRunner;
79-
if (InteractiveChannelBaseName.empty())
80-
AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
81-
M.getContext(), getFeatureMap(), DecisionName,
82-
EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
83-
else {
84-
auto Features = getFeatureMap();
85-
if (InteractiveIncludeDefault)
86-
Features.push_back(DefaultDecisionSpec);
87-
AOTRunner = std::make_unique<InteractiveModelRunner>(
88-
M.getContext(), Features, InlineDecisionSpec,
89-
InteractiveChannelBaseName + ".out",
90-
InteractiveChannelBaseName + ".in");
91-
}
92-
return std::make_unique<MLInlineAdvisor>(M, MAM, std::move(AOTRunner),
78+
auto RunnerFactory = [&](const std::vector<TensorSpec> &InputFeatures)
79+
-> std::unique_ptr<MLModelRunner> {
80+
std::unique_ptr<MLModelRunner> AOTRunner;
81+
if (InteractiveChannelBaseName.empty())
82+
AOTRunner = std::make_unique<ReleaseModeModelRunner<CompiledModelType>>(
83+
M.getContext(), InputFeatures, DecisionName,
84+
EmbeddedModelRunnerOptions().setModelSelector(ModelSelector));
85+
else {
86+
AOTRunner = std::make_unique<InteractiveModelRunner>(
87+
M.getContext(), InputFeatures, InlineDecisionSpec,
88+
InteractiveChannelBaseName + ".out",
89+
InteractiveChannelBaseName + ".in");
90+
}
91+
return AOTRunner;
92+
};
93+
return std::make_unique<MLInlineAdvisor>(M, MAM, RunnerFactory,
9394
GetDefaultAdvice);
9495
}
9596

@@ -107,7 +108,7 @@ static cl::opt<bool> KeepFPICache(
107108
"For test - keep the ML Inline advisor's FunctionPropertiesInfo cache"),
108109
cl::init(false));
109110

110-
std::vector<TensorSpec> &llvm::getFeatureMap() {
111+
const std::vector<TensorSpec> &MLInlineAdvisor::getInitialFeatureMap() {
111112
// clang-format off
112113
static std::vector<TensorSpec> FeatureMap{
113114
#define POPULATE_NAMES(DTYPE, SHAPE, NAME, __) TensorSpec::createSpec<DTYPE>(#NAME, SHAPE),
@@ -142,17 +143,17 @@ CallBase *getInlinableCS(Instruction &I) {
142143

143144
MLInlineAdvisor::MLInlineAdvisor(
144145
Module &M, ModuleAnalysisManager &MAM,
145-
std::unique_ptr<MLModelRunner> Runner,
146+
std::function<
147+
std::unique_ptr<MLModelRunner>(const std::vector<TensorSpec> &)>
148+
GetModelRunner,
146149
std::function<bool(CallBase &)> GetDefaultAdvice)
147150
: InlineAdvisor(
148151
M, MAM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager()),
149-
ModelRunner(std::move(Runner)), GetDefaultAdvice(GetDefaultAdvice),
152+
GetDefaultAdvice(GetDefaultAdvice), FeatureMap(getInitialFeatureMap()),
150153
CG(MAM.getResult<LazyCallGraphAnalysis>(M)),
151154
UseIR2Vec(MAM.getCachedResult<IR2VecVocabAnalysis>(M) != nullptr),
152155
InitialIRSize(getModuleIRSize()), CurrentIRSize(InitialIRSize),
153156
PSI(MAM.getResult<ProfileSummaryAnalysis>(M)) {
154-
assert(ModelRunner);
155-
ModelRunner->switchContext("");
156157
// Extract the 'call site height' feature - the position of a call site
157158
// relative to the farthest statically reachable SCC node. We don't mutate
158159
// this value while inlining happens. Empirically, this feature proved
@@ -192,18 +193,27 @@ MLInlineAdvisor::MLInlineAdvisor(
192193
}
193194
NodeCount = AllNodes.size();
194195

195-
if (auto IR2VecVocabResult = MAM.getCachedResult<IR2VecVocabAnalysis>(M)) {
196+
if (auto *IR2VecVocabResult = MAM.getCachedResult<IR2VecVocabAnalysis>(M)) {
196197
if (!IR2VecVocabResult->isValid()) {
197198
M.getContext().emitError("IR2VecVocabAnalysis is not valid");
198199
return;
199200
}
200201
// Add the IR2Vec features to the feature map
201202
auto IR2VecDim = IR2VecVocabResult->getDimension();
202-
getFeatureMap().push_back(
203+
FeatureMap.push_back(
203204
TensorSpec::createSpec<float>("callee_embedding", {IR2VecDim}));
204-
getFeatureMap().push_back(
205+
FeatureMap.push_back(
205206
TensorSpec::createSpec<float>("caller_embedding", {IR2VecDim}));
206207
}
208+
if (InteractiveIncludeDefault)
209+
FeatureMap.push_back(DefaultDecisionSpec);
210+
211+
ModelRunner = GetModelRunner(getFeatureMap());
212+
if (!ModelRunner) {
213+
M.getContext().emitError("Could not create model runner");
214+
return;
215+
}
216+
ModelRunner->switchContext("");
207217
}
208218

209219
unsigned MLInlineAdvisor::getInitialFunctionLevel(const Function &F) const {
@@ -475,7 +485,7 @@ std::unique_ptr<InlineAdvice> MLInlineAdvisor::getAdviceImpl(CallBase &CB) {
475485
}
476486
// This one would have been set up to be right at the end.
477487
if (!InteractiveChannelBaseName.empty() && InteractiveIncludeDefault)
478-
*ModelRunner->getTensor<int64_t>(getFeatureMap().size()) =
488+
*ModelRunner->getTensor<int64_t>(getFeatureMap().size() - 1) =
479489
GetDefaultAdvice(CB);
480490
return getAdviceFromModel(CB, ORE);
481491
}
@@ -554,8 +564,8 @@ void MLInlineAdvice::reportContextForRemark(
554564
DiagnosticInfoOptimizationBase &OR) {
555565
using namespace ore;
556566
OR << NV("Callee", Callee->getName());
557-
for (size_t I = 0; I < getFeatureMap().size(); ++I)
558-
OR << NV(getFeatureMap()[I].name(),
567+
for (size_t I = 0; I < getAdvisor()->getFeatureMap().size(); ++I)
568+
OR << NV(getAdvisor()->getFeatureMap()[I].name(),
559569
*getAdvisor()->getModelRunner().getTensor<int64_t>(I));
560570
OR << NV("ShouldInline", isInliningRecommended());
561571
}

0 commit comments

Comments
 (0)