xref: /llvm-project/llvm/lib/Analysis/InlineSizeEstimatorAnalysis.cpp (revision 057f28be3e1188de518bcbf007fee759aa7e812a)
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