1caf395eeSMircea Trofin //===- InlineSizeEstimatorAnalysis.cpp - IR to native size from ML model --===//
2caf395eeSMircea Trofin //
3c874dd53SChristopher Di Bella // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4c874dd53SChristopher Di Bella // See https://llvm.org/LICENSE.txt for license information.
5c874dd53SChristopher Di Bella // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6caf395eeSMircea Trofin //
7caf395eeSMircea Trofin //===----------------------------------------------------------------------===//
8caf395eeSMircea Trofin //
9caf395eeSMircea Trofin // This implements feature and label extraction for offline supervised learning
10caf395eeSMircea Trofin // of a IR to native size model.
11caf395eeSMircea Trofin //
12caf395eeSMircea Trofin //===----------------------------------------------------------------------===//
13caf395eeSMircea Trofin #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h"
14caf395eeSMircea Trofin
15edc83a15SKazu Hirata #ifdef LLVM_HAVE_TFLITE
16caf395eeSMircea Trofin #include "llvm/Analysis/Utils/TFUtils.h"
17caf395eeSMircea Trofin #endif
18caf395eeSMircea Trofin #include "llvm/IR/Function.h"
19caf395eeSMircea Trofin #include "llvm/IR/PassManager.h"
20caf395eeSMircea Trofin #include "llvm/Support/raw_ostream.h"
21caf395eeSMircea Trofin
22caf395eeSMircea Trofin using namespace llvm;
23caf395eeSMircea Trofin
24caf395eeSMircea Trofin AnalysisKey InlineSizeEstimatorAnalysis::Key;
25caf395eeSMircea Trofin
26edc83a15SKazu Hirata #ifdef LLVM_HAVE_TFLITE
2726141927SMircea Trofin #include "llvm/Analysis/LoopInfo.h"
2826141927SMircea Trofin #include "llvm/Analysis/TargetLibraryInfo.h"
2926141927SMircea Trofin #include "llvm/Analysis/TargetTransformInfo.h"
3026141927SMircea Trofin #include "llvm/IR/BasicBlock.h"
3126141927SMircea Trofin #include "llvm/IR/Dominators.h"
3226141927SMircea Trofin #include "llvm/IR/Instructions.h"
3326141927SMircea Trofin #include "llvm/Support/Casting.h"
3426141927SMircea Trofin #include "llvm/Support/CommandLine.h"
3526141927SMircea Trofin #include <algorithm>
3626141927SMircea Trofin #include <deque>
379c444f70SKazu Hirata #include <optional>
3826141927SMircea Trofin
39caf395eeSMircea Trofin cl::opt<std::string> TFIR2NativeModelPath(
40caf395eeSMircea Trofin "ml-inliner-ir2native-model", cl::Hidden,
41caf395eeSMircea Trofin cl::desc("Path to saved model evaluating native size from IR."));
42caf395eeSMircea Trofin
4326141927SMircea Trofin #define DEBUG_TYPE "inline-size-estimator"
44caf395eeSMircea Trofin namespace {
getMaxInstructionID()45caf395eeSMircea Trofin unsigned getMaxInstructionID() {
46caf395eeSMircea Trofin #define LAST_OTHER_INST(NR) return NR;
47caf395eeSMircea Trofin #include "llvm/IR/Instruction.def"
48caf395eeSMircea Trofin }
49caf395eeSMircea Trofin
50caf395eeSMircea Trofin class IRToNativeSizeLearning {
51caf395eeSMircea Trofin public:
52caf395eeSMircea Trofin enum class NamedFeatureIndex : size_t {
53caf395eeSMircea Trofin InitialSize,
54caf395eeSMircea Trofin Blocks,
55caf395eeSMircea Trofin Calls,
56caf395eeSMircea Trofin IsLocal,
57caf395eeSMircea Trofin IsLinkOnceODR,
58caf395eeSMircea Trofin IsLinkOnce,
59caf395eeSMircea Trofin Loops,
60caf395eeSMircea Trofin MaxLoopDepth,
61caf395eeSMircea Trofin MaxDomTreeLevel,
62caf395eeSMircea Trofin
63caf395eeSMircea Trofin NumNamedFeatures
64caf395eeSMircea Trofin };
65caf395eeSMircea Trofin static const size_t NumNamedFeatures =
66caf395eeSMircea Trofin static_cast<size_t>(NamedFeatureIndex::NumNamedFeatures);
67caf395eeSMircea Trofin struct FunctionFeatures {
68caf395eeSMircea Trofin static const size_t FeatureCount;
69caf395eeSMircea Trofin
70caf395eeSMircea Trofin std::array<int32_t, NumNamedFeatures> NamedFeatures = {0};
71caf395eeSMircea Trofin std::vector<int32_t> InstructionHistogram;
72caf395eeSMircea Trofin std::vector<int32_t> InstructionPairHistogram;
73caf395eeSMircea Trofin
74caf395eeSMircea Trofin void fillTensor(int32_t *Ptr) const;
operator []__anond9efefac0111::IRToNativeSizeLearning::FunctionFeatures75caf395eeSMircea Trofin int32_t &operator[](NamedFeatureIndex Pos) {
76caf395eeSMircea Trofin return NamedFeatures[static_cast<size_t>(Pos)];
77caf395eeSMircea Trofin }
78caf395eeSMircea Trofin };
79caf395eeSMircea Trofin IRToNativeSizeLearning() = default;
80caf395eeSMircea Trofin
81caf395eeSMircea Trofin static FunctionFeatures getFunctionFeatures(Function &F,
82caf395eeSMircea Trofin FunctionAnalysisManager &FAM);
83caf395eeSMircea Trofin };
84caf395eeSMircea Trofin
85caf395eeSMircea Trofin // This is a point in time - we determined including these pairs of
86caf395eeSMircea Trofin // consecutive instructions (in the IR layout available at inline time) as
87caf395eeSMircea Trofin // features improves the model performance. We want to move away from manual
88caf395eeSMircea Trofin // feature selection.
89da924488SMircea Trofin // The array is given in opcode pairs rather than labels because 1) labels
90da924488SMircea Trofin // weren't readily available, and 2) the successions were hand - extracted.
91da924488SMircea Trofin //
92da924488SMircea Trofin // This array must be sorted.
93da924488SMircea Trofin static const std::array<std::pair<size_t, size_t>, 137>
94da924488SMircea Trofin ImportantInstructionSuccessions{
95da924488SMircea Trofin {{1, 1}, {1, 4}, {1, 5}, {1, 7}, {1, 8}, {1, 9}, {1, 11},
96da924488SMircea Trofin {1, 12}, {1, 13}, {1, 14}, {1, 18}, {1, 20}, {1, 22}, {1, 24},
97da924488SMircea Trofin {1, 25}, {1, 26}, {1, 27}, {1, 28}, {1, 29}, {1, 30}, {1, 31},
98da924488SMircea Trofin {1, 32}, {1, 33}, {1, 34}, {1, 39}, {1, 40}, {1, 42}, {1, 45},
99da924488SMircea Trofin {2, 1}, {2, 2}, {2, 13}, {2, 28}, {2, 29}, {2, 32}, {2, 33},
100da924488SMircea Trofin {2, 34}, {2, 38}, {2, 48}, {2, 49}, {2, 53}, {2, 55}, {2, 56},
101da924488SMircea Trofin {13, 2}, {13, 13}, {13, 26}, {13, 33}, {13, 34}, {13, 56}, {15, 27},
102da924488SMircea Trofin {28, 2}, {28, 48}, {28, 53}, {29, 2}, {29, 33}, {29, 56}, {31, 31},
103da924488SMircea Trofin {31, 33}, {31, 34}, {31, 49}, {32, 1}, {32, 2}, {32, 13}, {32, 15},
104da924488SMircea Trofin {32, 28}, {32, 29}, {32, 32}, {32, 33}, {32, 34}, {32, 39}, {32, 40},
105da924488SMircea Trofin {32, 48}, {32, 49}, {32, 53}, {32, 56}, {33, 1}, {33, 2}, {33, 32},
106da924488SMircea Trofin {33, 33}, {33, 34}, {33, 49}, {33, 53}, {33, 56}, {34, 1}, {34, 2},
107da924488SMircea Trofin {34, 32}, {34, 33}, {34, 34}, {34, 49}, {34, 53}, {34, 56}, {38, 34},
108da924488SMircea Trofin {39, 57}, {40, 34}, {47, 15}, {47, 49}, {48, 2}, {48, 34}, {48, 56},
109da924488SMircea Trofin {49, 1}, {49, 2}, {49, 28}, {49, 32}, {49, 33}, {49, 34}, {49, 39},
110da924488SMircea Trofin {49, 49}, {49, 56}, {53, 1}, {53, 2}, {53, 28}, {53, 34}, {53, 53},
111da924488SMircea Trofin {53, 57}, {55, 1}, {55, 28}, {55, 34}, {55, 53}, {55, 55}, {55, 56},
112da924488SMircea Trofin {56, 1}, {56, 2}, {56, 7}, {56, 13}, {56, 32}, {56, 33}, {56, 34},
113da924488SMircea Trofin {56, 49}, {56, 53}, {56, 56}, {56, 64}, {57, 34}, {57, 56}, {57, 57},
114da924488SMircea Trofin {64, 1}, {64, 64}, {65, 1}, {65, 65}}};
115caf395eeSMircea Trofin
116caf395eeSMircea Trofin // We have: 9 calculated features (the features here); 1 feature for each
117caf395eeSMircea Trofin // instruction opcode; and 1 feature for each manually-identified sequence.
118caf395eeSMircea Trofin // For the latter 2, we build a histogram: we count the number of
119caf395eeSMircea Trofin // occurrences of each instruction opcode or succession of instructions,
120caf395eeSMircea Trofin // respectively.
121caf395eeSMircea Trofin // Note that instruction opcodes start from 1. For convenience, we also have an
122caf395eeSMircea Trofin // always 0 feature for the '0' opcode, hence the extra 1.
123caf395eeSMircea Trofin const size_t IRToNativeSizeLearning::FunctionFeatures::FeatureCount =
124da924488SMircea Trofin ImportantInstructionSuccessions.size() + getMaxInstructionID() + 1 +
125da924488SMircea Trofin IRToNativeSizeLearning::NumNamedFeatures;
126caf395eeSMircea Trofin
getSize(Function & F,TargetTransformInfo & TTI)127caf395eeSMircea Trofin size_t getSize(Function &F, TargetTransformInfo &TTI) {
128caf395eeSMircea Trofin size_t Ret = 0;
129da924488SMircea Trofin for (const auto &BB : F)
130da924488SMircea Trofin for (const auto &I : BB)
131f76b7f22SMircea Trofin Ret += *(TTI.getInstructionCost(
132f76b7f22SMircea Trofin &I, TargetTransformInfo::TargetCostKind::TCK_CodeSize).getValue());
133caf395eeSMircea Trofin return Ret;
134caf395eeSMircea Trofin }
135caf395eeSMircea Trofin
getSize(Function & F,FunctionAnalysisManager & FAM)136caf395eeSMircea Trofin size_t getSize(Function &F, FunctionAnalysisManager &FAM) {
137caf395eeSMircea Trofin auto &TTI = FAM.getResult<TargetIRAnalysis>(F);
138caf395eeSMircea Trofin return getSize(F, TTI);
139caf395eeSMircea Trofin }
140caf395eeSMircea Trofin
getMaxDominatorTreeDepth(const Function & F,const DominatorTree & Tree)141caf395eeSMircea Trofin unsigned getMaxDominatorTreeDepth(const Function &F,
142caf395eeSMircea Trofin const DominatorTree &Tree) {
143caf395eeSMircea Trofin unsigned Ret = 0;
144da924488SMircea Trofin for (const auto &BB : F)
145da924488SMircea Trofin if (const auto *TN = Tree.getNode(&BB))
146caf395eeSMircea Trofin Ret = std::max(Ret, TN->getLevel());
147caf395eeSMircea Trofin return Ret;
148caf395eeSMircea Trofin }
149caf395eeSMircea Trofin } // namespace
150caf395eeSMircea Trofin
151caf395eeSMircea Trofin IRToNativeSizeLearning::FunctionFeatures
getFunctionFeatures(Function & F,FunctionAnalysisManager & FAM)152caf395eeSMircea Trofin IRToNativeSizeLearning::getFunctionFeatures(Function &F,
153caf395eeSMircea Trofin FunctionAnalysisManager &FAM) {
154da924488SMircea Trofin assert(llvm::is_sorted(ImportantInstructionSuccessions) &&
155da924488SMircea Trofin "expected function features are sorted");
156caf395eeSMircea Trofin
157caf395eeSMircea Trofin auto &DomTree = FAM.getResult<DominatorTreeAnalysis>(F);
158caf395eeSMircea Trofin FunctionFeatures FF;
159caf395eeSMircea Trofin size_t InstrCount = getMaxInstructionID() + 1;
160caf395eeSMircea Trofin FF.InstructionHistogram.resize(InstrCount);
161caf395eeSMircea Trofin
162da924488SMircea Trofin FF.InstructionPairHistogram.resize(ImportantInstructionSuccessions.size());
163caf395eeSMircea Trofin
164da924488SMircea Trofin int StartID = 0;
165da924488SMircea Trofin int LastID = StartID;
166caf395eeSMircea Trofin auto getPairIndex = [](size_t a, size_t b) {
167da924488SMircea Trofin auto I = llvm::find(ImportantInstructionSuccessions, std::make_pair(a, b));
168da924488SMircea Trofin if (I == ImportantInstructionSuccessions.end())
169caf395eeSMircea Trofin return -1;
170da924488SMircea Trofin return static_cast<int>(
171da924488SMircea Trofin std::distance(ImportantInstructionSuccessions.begin(), I));
172caf395eeSMircea Trofin };
173caf395eeSMircea Trofin
174caf395eeSMircea Trofin // We don't want debug calls, because they'd just add noise.
175da924488SMircea Trofin for (const auto &BB : F) {
176da924488SMircea Trofin for (const auto &I : BB.instructionsWithoutDebug()) {
177da924488SMircea Trofin auto ID = I.getOpcode();
178caf395eeSMircea Trofin
179caf395eeSMircea Trofin ++FF.InstructionHistogram[ID];
180caf395eeSMircea Trofin int PairIndex = getPairIndex(LastID, ID);
181caf395eeSMircea Trofin if (PairIndex >= 0)
182caf395eeSMircea Trofin ++FF.InstructionPairHistogram[PairIndex];
183caf395eeSMircea Trofin LastID = ID;
184da924488SMircea Trofin if (isa<CallBase>(I))
185caf395eeSMircea Trofin ++FF[NamedFeatureIndex::Calls];
186caf395eeSMircea Trofin }
187caf395eeSMircea Trofin }
188caf395eeSMircea Trofin
189caf395eeSMircea Trofin FF[NamedFeatureIndex::InitialSize] = getSize(F, FAM);
190caf395eeSMircea Trofin FF[NamedFeatureIndex::IsLocal] = F.hasLocalLinkage();
191caf395eeSMircea Trofin FF[NamedFeatureIndex::IsLinkOnceODR] = F.hasLinkOnceODRLinkage();
192caf395eeSMircea Trofin FF[NamedFeatureIndex::IsLinkOnce] = F.hasLinkOnceLinkage();
193*cb5ebfa2SVasileios Porpodas FF[NamedFeatureIndex::Blocks] = F.size();
194caf395eeSMircea Trofin auto &LI = FAM.getResult<LoopAnalysis>(F);
195caf395eeSMircea Trofin FF[NamedFeatureIndex::Loops] = std::distance(LI.begin(), LI.end());
196caf395eeSMircea Trofin for (auto &L : LI)
197caf395eeSMircea Trofin FF[NamedFeatureIndex::MaxLoopDepth] =
198caf395eeSMircea Trofin std::max(FF[NamedFeatureIndex::MaxLoopDepth],
199caf395eeSMircea Trofin static_cast<int32_t>(L->getLoopDepth()));
200caf395eeSMircea Trofin FF[NamedFeatureIndex::MaxDomTreeLevel] = getMaxDominatorTreeDepth(F, DomTree);
201caf395eeSMircea Trofin return FF;
202caf395eeSMircea Trofin }
203caf395eeSMircea Trofin
fillTensor(int32_t * Ptr) const204caf395eeSMircea Trofin void IRToNativeSizeLearning::FunctionFeatures::fillTensor(int32_t *Ptr) const {
205caf395eeSMircea Trofin std::copy(NamedFeatures.begin(), NamedFeatures.end(), Ptr);
206caf395eeSMircea Trofin Ptr += NamedFeatures.size();
207caf395eeSMircea Trofin std::copy(InstructionHistogram.begin(), InstructionHistogram.end(), Ptr);
208caf395eeSMircea Trofin Ptr += InstructionHistogram.size();
209caf395eeSMircea Trofin std::copy(InstructionPairHistogram.begin(), InstructionPairHistogram.end(),
210caf395eeSMircea Trofin Ptr);
211caf395eeSMircea Trofin }
212caf395eeSMircea Trofin
isEvaluatorRequested()213caf395eeSMircea Trofin bool InlineSizeEstimatorAnalysis::isEvaluatorRequested() {
214caf395eeSMircea Trofin return !TFIR2NativeModelPath.empty();
215caf395eeSMircea Trofin }
216caf395eeSMircea Trofin
InlineSizeEstimatorAnalysis()217caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis() {
218caf395eeSMircea Trofin if (!isEvaluatorRequested()) {
219caf395eeSMircea Trofin return;
220caf395eeSMircea Trofin }
22171059257SMircea Trofin std::vector<TensorSpec> InputSpecs{TensorSpec::createSpec<int32_t>(
22271059257SMircea Trofin "serving_default_input_1",
22371059257SMircea Trofin {1, static_cast<int64_t>(
22471059257SMircea Trofin IRToNativeSizeLearning::FunctionFeatures::FeatureCount)})};
22571059257SMircea Trofin std::vector<TensorSpec> OutputSpecs{
22671059257SMircea Trofin TensorSpec::createSpec<float>("StatefulPartitionedCall", {1})};
227caf395eeSMircea Trofin Evaluator = std::make_unique<TFModelEvaluator>(
22871059257SMircea Trofin TFIR2NativeModelPath.getValue().c_str(), InputSpecs, OutputSpecs);
229caf395eeSMircea Trofin if (!Evaluator || !Evaluator->isValid()) {
230caf395eeSMircea Trofin Evaluator.reset();
231caf395eeSMircea Trofin return;
232caf395eeSMircea Trofin }
233caf395eeSMircea Trofin }
234caf395eeSMircea Trofin
235caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::Result
run(const Function & F,FunctionAnalysisManager & FAM)236caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::run(const Function &F,
237caf395eeSMircea Trofin FunctionAnalysisManager &FAM) {
238caf395eeSMircea Trofin if (!Evaluator)
2399c444f70SKazu Hirata return std::nullopt;
240caf395eeSMircea Trofin auto Features = IRToNativeSizeLearning::getFunctionFeatures(
241caf395eeSMircea Trofin const_cast<Function &>(F), FAM);
2424f763b21SMircea Trofin int32_t *V = Evaluator->getInput<int32_t>(0);
243caf395eeSMircea Trofin Features.fillTensor(V);
244caf395eeSMircea Trofin auto ER = Evaluator->evaluate();
245caf395eeSMircea Trofin if (!ER)
2469c444f70SKazu Hirata return std::nullopt;
247caf395eeSMircea Trofin float Ret = *ER->getTensorValue<float>(0);
248caf395eeSMircea Trofin if (Ret < 0.0)
249caf395eeSMircea Trofin Ret = 0.0;
250caf395eeSMircea Trofin return static_cast<size_t>(Ret);
251caf395eeSMircea Trofin }
252caf395eeSMircea Trofin
~InlineSizeEstimatorAnalysis()253caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::~InlineSizeEstimatorAnalysis() {}
InlineSizeEstimatorAnalysis(InlineSizeEstimatorAnalysis && Other)254caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis(
255caf395eeSMircea Trofin InlineSizeEstimatorAnalysis &&Other)
256caf395eeSMircea Trofin : Evaluator(std::move(Other.Evaluator)) {}
257caf395eeSMircea Trofin
258caf395eeSMircea Trofin #else
259caf395eeSMircea Trofin namespace llvm {
260caf395eeSMircea Trofin class TFModelEvaluator {};
261caf395eeSMircea Trofin } // namespace llvm
2623a3cb929SKazu Hirata InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis() = default;
InlineSizeEstimatorAnalysis(InlineSizeEstimatorAnalysis &&)263caf395eeSMircea Trofin InlineSizeEstimatorAnalysis ::InlineSizeEstimatorAnalysis(
264caf395eeSMircea Trofin InlineSizeEstimatorAnalysis &&) {}
2653a3cb929SKazu Hirata InlineSizeEstimatorAnalysis::~InlineSizeEstimatorAnalysis() = default;
266caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::Result
run(const Function & F,FunctionAnalysisManager & FAM)267caf395eeSMircea Trofin InlineSizeEstimatorAnalysis::run(const Function &F,
268caf395eeSMircea Trofin FunctionAnalysisManager &FAM) {
26919aff0f3SKazu Hirata return std::nullopt;
270caf395eeSMircea Trofin }
isEvaluatorRequested()271caf395eeSMircea Trofin bool InlineSizeEstimatorAnalysis::isEvaluatorRequested() { return false; }
272caf395eeSMircea Trofin #endif
2739870f774SMircea Trofin
2749870f774SMircea Trofin PreservedAnalyses
run(Function & F,FunctionAnalysisManager & AM)2759870f774SMircea Trofin InlineSizeEstimatorAnalysisPrinterPass::run(Function &F,
2769870f774SMircea Trofin FunctionAnalysisManager &AM) {
2779870f774SMircea Trofin OS << "[InlineSizeEstimatorAnalysis] size estimate for " << F.getName()
2789870f774SMircea Trofin << ": " << AM.getResult<InlineSizeEstimatorAnalysis>(F) << "\n";
2799870f774SMircea Trofin return PreservedAnalyses::all();
2809870f774SMircea Trofin }
281