15ffd83dbSDimitry Andric //===- InlineSizeEstimatorAnalysis.cpp - IR to native size from ML model --===// 25ffd83dbSDimitry Andric // 3349cc55cSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4349cc55cSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5349cc55cSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 65ffd83dbSDimitry Andric // 75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 85ffd83dbSDimitry Andric // 95ffd83dbSDimitry Andric // This implements feature and label extraction for offline supervised learning 105ffd83dbSDimitry Andric // of a IR to native size model. 115ffd83dbSDimitry Andric // 125ffd83dbSDimitry Andric //===----------------------------------------------------------------------===// 135ffd83dbSDimitry Andric #include "llvm/Analysis/InlineSizeEstimatorAnalysis.h" 145ffd83dbSDimitry Andric 15*bdd1243dSDimitry Andric #ifdef LLVM_HAVE_TFLITE 165ffd83dbSDimitry Andric #include "llvm/Analysis/Utils/TFUtils.h" 175ffd83dbSDimitry Andric #endif 185ffd83dbSDimitry Andric #include "llvm/IR/Function.h" 195ffd83dbSDimitry Andric #include "llvm/IR/PassManager.h" 205ffd83dbSDimitry Andric #include "llvm/Support/raw_ostream.h" 215ffd83dbSDimitry Andric 225ffd83dbSDimitry Andric using namespace llvm; 235ffd83dbSDimitry Andric 245ffd83dbSDimitry Andric AnalysisKey InlineSizeEstimatorAnalysis::Key; 255ffd83dbSDimitry Andric 26*bdd1243dSDimitry Andric #ifdef LLVM_HAVE_TFLITE 2781ad6265SDimitry Andric #include "llvm/Analysis/LoopInfo.h" 2881ad6265SDimitry Andric #include "llvm/Analysis/TargetLibraryInfo.h" 2981ad6265SDimitry Andric #include "llvm/Analysis/TargetTransformInfo.h" 3081ad6265SDimitry Andric #include "llvm/IR/BasicBlock.h" 3181ad6265SDimitry Andric #include "llvm/IR/Dominators.h" 3281ad6265SDimitry Andric #include "llvm/IR/Instructions.h" 3381ad6265SDimitry Andric #include "llvm/Support/Casting.h" 3481ad6265SDimitry Andric #include "llvm/Support/CommandLine.h" 3581ad6265SDimitry Andric #include <algorithm> 3681ad6265SDimitry Andric #include <deque> 37*bdd1243dSDimitry Andric #include <optional> 3881ad6265SDimitry Andric 395ffd83dbSDimitry Andric cl::opt<std::string> TFIR2NativeModelPath( 405ffd83dbSDimitry Andric "ml-inliner-ir2native-model", cl::Hidden, 415ffd83dbSDimitry Andric cl::desc("Path to saved model evaluating native size from IR.")); 425ffd83dbSDimitry Andric 4381ad6265SDimitry Andric #define DEBUG_TYPE "inline-size-estimator" 445ffd83dbSDimitry Andric namespace { 455ffd83dbSDimitry Andric unsigned getMaxInstructionID() { 465ffd83dbSDimitry Andric #define LAST_OTHER_INST(NR) return NR; 475ffd83dbSDimitry Andric #include "llvm/IR/Instruction.def" 485ffd83dbSDimitry Andric } 495ffd83dbSDimitry Andric 505ffd83dbSDimitry Andric class IRToNativeSizeLearning { 515ffd83dbSDimitry Andric public: 525ffd83dbSDimitry Andric enum class NamedFeatureIndex : size_t { 535ffd83dbSDimitry Andric InitialSize, 545ffd83dbSDimitry Andric Blocks, 555ffd83dbSDimitry Andric Calls, 565ffd83dbSDimitry Andric IsLocal, 575ffd83dbSDimitry Andric IsLinkOnceODR, 585ffd83dbSDimitry Andric IsLinkOnce, 595ffd83dbSDimitry Andric Loops, 605ffd83dbSDimitry Andric MaxLoopDepth, 615ffd83dbSDimitry Andric MaxDomTreeLevel, 625ffd83dbSDimitry Andric 635ffd83dbSDimitry Andric NumNamedFeatures 645ffd83dbSDimitry Andric }; 655ffd83dbSDimitry Andric static const size_t NumNamedFeatures = 665ffd83dbSDimitry Andric static_cast<size_t>(NamedFeatureIndex::NumNamedFeatures); 675ffd83dbSDimitry Andric struct FunctionFeatures { 685ffd83dbSDimitry Andric static const size_t FeatureCount; 695ffd83dbSDimitry Andric 705ffd83dbSDimitry Andric std::array<int32_t, NumNamedFeatures> NamedFeatures = {0}; 715ffd83dbSDimitry Andric std::vector<int32_t> InstructionHistogram; 725ffd83dbSDimitry Andric std::vector<int32_t> InstructionPairHistogram; 735ffd83dbSDimitry Andric 745ffd83dbSDimitry Andric void fillTensor(int32_t *Ptr) const; 755ffd83dbSDimitry Andric int32_t &operator[](NamedFeatureIndex Pos) { 765ffd83dbSDimitry Andric return NamedFeatures[static_cast<size_t>(Pos)]; 775ffd83dbSDimitry Andric } 785ffd83dbSDimitry Andric }; 795ffd83dbSDimitry Andric IRToNativeSizeLearning() = default; 805ffd83dbSDimitry Andric 815ffd83dbSDimitry Andric static FunctionFeatures getFunctionFeatures(Function &F, 825ffd83dbSDimitry Andric FunctionAnalysisManager &FAM); 835ffd83dbSDimitry Andric }; 845ffd83dbSDimitry Andric 855ffd83dbSDimitry Andric // This is a point in time - we determined including these pairs of 865ffd83dbSDimitry Andric // consecutive instructions (in the IR layout available at inline time) as 875ffd83dbSDimitry Andric // features improves the model performance. We want to move away from manual 885ffd83dbSDimitry Andric // feature selection. 89e8d8bef9SDimitry Andric // The array is given in opcode pairs rather than labels because 1) labels 90e8d8bef9SDimitry Andric // weren't readily available, and 2) the successions were hand - extracted. 91e8d8bef9SDimitry Andric // 92e8d8bef9SDimitry Andric // This array must be sorted. 93e8d8bef9SDimitry Andric static const std::array<std::pair<size_t, size_t>, 137> 94e8d8bef9SDimitry Andric ImportantInstructionSuccessions{ 95e8d8bef9SDimitry Andric {{1, 1}, {1, 4}, {1, 5}, {1, 7}, {1, 8}, {1, 9}, {1, 11}, 96e8d8bef9SDimitry Andric {1, 12}, {1, 13}, {1, 14}, {1, 18}, {1, 20}, {1, 22}, {1, 24}, 97e8d8bef9SDimitry Andric {1, 25}, {1, 26}, {1, 27}, {1, 28}, {1, 29}, {1, 30}, {1, 31}, 98e8d8bef9SDimitry Andric {1, 32}, {1, 33}, {1, 34}, {1, 39}, {1, 40}, {1, 42}, {1, 45}, 99e8d8bef9SDimitry Andric {2, 1}, {2, 2}, {2, 13}, {2, 28}, {2, 29}, {2, 32}, {2, 33}, 100e8d8bef9SDimitry Andric {2, 34}, {2, 38}, {2, 48}, {2, 49}, {2, 53}, {2, 55}, {2, 56}, 101e8d8bef9SDimitry Andric {13, 2}, {13, 13}, {13, 26}, {13, 33}, {13, 34}, {13, 56}, {15, 27}, 102e8d8bef9SDimitry Andric {28, 2}, {28, 48}, {28, 53}, {29, 2}, {29, 33}, {29, 56}, {31, 31}, 103e8d8bef9SDimitry Andric {31, 33}, {31, 34}, {31, 49}, {32, 1}, {32, 2}, {32, 13}, {32, 15}, 104e8d8bef9SDimitry Andric {32, 28}, {32, 29}, {32, 32}, {32, 33}, {32, 34}, {32, 39}, {32, 40}, 105e8d8bef9SDimitry Andric {32, 48}, {32, 49}, {32, 53}, {32, 56}, {33, 1}, {33, 2}, {33, 32}, 106e8d8bef9SDimitry Andric {33, 33}, {33, 34}, {33, 49}, {33, 53}, {33, 56}, {34, 1}, {34, 2}, 107e8d8bef9SDimitry Andric {34, 32}, {34, 33}, {34, 34}, {34, 49}, {34, 53}, {34, 56}, {38, 34}, 108e8d8bef9SDimitry Andric {39, 57}, {40, 34}, {47, 15}, {47, 49}, {48, 2}, {48, 34}, {48, 56}, 109e8d8bef9SDimitry Andric {49, 1}, {49, 2}, {49, 28}, {49, 32}, {49, 33}, {49, 34}, {49, 39}, 110e8d8bef9SDimitry Andric {49, 49}, {49, 56}, {53, 1}, {53, 2}, {53, 28}, {53, 34}, {53, 53}, 111e8d8bef9SDimitry Andric {53, 57}, {55, 1}, {55, 28}, {55, 34}, {55, 53}, {55, 55}, {55, 56}, 112e8d8bef9SDimitry Andric {56, 1}, {56, 2}, {56, 7}, {56, 13}, {56, 32}, {56, 33}, {56, 34}, 113e8d8bef9SDimitry Andric {56, 49}, {56, 53}, {56, 56}, {56, 64}, {57, 34}, {57, 56}, {57, 57}, 114e8d8bef9SDimitry Andric {64, 1}, {64, 64}, {65, 1}, {65, 65}}}; 1155ffd83dbSDimitry Andric 1165ffd83dbSDimitry Andric // We have: 9 calculated features (the features here); 1 feature for each 1175ffd83dbSDimitry Andric // instruction opcode; and 1 feature for each manually-identified sequence. 1185ffd83dbSDimitry Andric // For the latter 2, we build a histogram: we count the number of 1195ffd83dbSDimitry Andric // occurrences of each instruction opcode or succession of instructions, 1205ffd83dbSDimitry Andric // respectively. 1215ffd83dbSDimitry Andric // Note that instruction opcodes start from 1. For convenience, we also have an 1225ffd83dbSDimitry Andric // always 0 feature for the '0' opcode, hence the extra 1. 1235ffd83dbSDimitry Andric const size_t IRToNativeSizeLearning::FunctionFeatures::FeatureCount = 124e8d8bef9SDimitry Andric ImportantInstructionSuccessions.size() + getMaxInstructionID() + 1 + 125e8d8bef9SDimitry Andric IRToNativeSizeLearning::NumNamedFeatures; 1265ffd83dbSDimitry Andric 1275ffd83dbSDimitry Andric size_t getSize(Function &F, TargetTransformInfo &TTI) { 1285ffd83dbSDimitry Andric size_t Ret = 0; 129e8d8bef9SDimitry Andric for (const auto &BB : F) 130e8d8bef9SDimitry Andric for (const auto &I : BB) 131e8d8bef9SDimitry Andric Ret += *(TTI.getInstructionCost( 132e8d8bef9SDimitry Andric &I, TargetTransformInfo::TargetCostKind::TCK_CodeSize).getValue()); 1335ffd83dbSDimitry Andric return Ret; 1345ffd83dbSDimitry Andric } 1355ffd83dbSDimitry Andric 1365ffd83dbSDimitry Andric size_t getSize(Function &F, FunctionAnalysisManager &FAM) { 1375ffd83dbSDimitry Andric auto &TTI = FAM.getResult<TargetIRAnalysis>(F); 1385ffd83dbSDimitry Andric return getSize(F, TTI); 1395ffd83dbSDimitry Andric } 1405ffd83dbSDimitry Andric 1415ffd83dbSDimitry Andric unsigned getMaxDominatorTreeDepth(const Function &F, 1425ffd83dbSDimitry Andric const DominatorTree &Tree) { 1435ffd83dbSDimitry Andric unsigned Ret = 0; 144e8d8bef9SDimitry Andric for (const auto &BB : F) 145e8d8bef9SDimitry Andric if (const auto *TN = Tree.getNode(&BB)) 1465ffd83dbSDimitry Andric Ret = std::max(Ret, TN->getLevel()); 1475ffd83dbSDimitry Andric return Ret; 1485ffd83dbSDimitry Andric } 1495ffd83dbSDimitry Andric } // namespace 1505ffd83dbSDimitry Andric 1515ffd83dbSDimitry Andric IRToNativeSizeLearning::FunctionFeatures 1525ffd83dbSDimitry Andric IRToNativeSizeLearning::getFunctionFeatures(Function &F, 1535ffd83dbSDimitry Andric FunctionAnalysisManager &FAM) { 154e8d8bef9SDimitry Andric assert(llvm::is_sorted(ImportantInstructionSuccessions) && 155e8d8bef9SDimitry Andric "expected function features are sorted"); 1565ffd83dbSDimitry Andric 1575ffd83dbSDimitry Andric auto &DomTree = FAM.getResult<DominatorTreeAnalysis>(F); 1585ffd83dbSDimitry Andric FunctionFeatures FF; 1595ffd83dbSDimitry Andric size_t InstrCount = getMaxInstructionID() + 1; 1605ffd83dbSDimitry Andric FF.InstructionHistogram.resize(InstrCount); 1615ffd83dbSDimitry Andric 162e8d8bef9SDimitry Andric FF.InstructionPairHistogram.resize(ImportantInstructionSuccessions.size()); 1635ffd83dbSDimitry Andric 164e8d8bef9SDimitry Andric int StartID = 0; 165e8d8bef9SDimitry Andric int LastID = StartID; 1665ffd83dbSDimitry Andric auto getPairIndex = [](size_t a, size_t b) { 167e8d8bef9SDimitry Andric auto I = llvm::find(ImportantInstructionSuccessions, std::make_pair(a, b)); 168e8d8bef9SDimitry Andric if (I == ImportantInstructionSuccessions.end()) 1695ffd83dbSDimitry Andric return -1; 170e8d8bef9SDimitry Andric return static_cast<int>( 171e8d8bef9SDimitry Andric std::distance(ImportantInstructionSuccessions.begin(), I)); 1725ffd83dbSDimitry Andric }; 1735ffd83dbSDimitry Andric 1745ffd83dbSDimitry Andric // We don't want debug calls, because they'd just add noise. 175e8d8bef9SDimitry Andric for (const auto &BB : F) { 176e8d8bef9SDimitry Andric for (const auto &I : BB.instructionsWithoutDebug()) { 177e8d8bef9SDimitry Andric auto ID = I.getOpcode(); 1785ffd83dbSDimitry Andric 1795ffd83dbSDimitry Andric ++FF.InstructionHistogram[ID]; 1805ffd83dbSDimitry Andric int PairIndex = getPairIndex(LastID, ID); 1815ffd83dbSDimitry Andric if (PairIndex >= 0) 1825ffd83dbSDimitry Andric ++FF.InstructionPairHistogram[PairIndex]; 1835ffd83dbSDimitry Andric LastID = ID; 184e8d8bef9SDimitry Andric if (isa<CallBase>(I)) 1855ffd83dbSDimitry Andric ++FF[NamedFeatureIndex::Calls]; 1865ffd83dbSDimitry Andric } 1875ffd83dbSDimitry Andric } 1885ffd83dbSDimitry Andric 1895ffd83dbSDimitry Andric FF[NamedFeatureIndex::InitialSize] = getSize(F, FAM); 1905ffd83dbSDimitry Andric FF[NamedFeatureIndex::IsLocal] = F.hasLocalLinkage(); 1915ffd83dbSDimitry Andric FF[NamedFeatureIndex::IsLinkOnceODR] = F.hasLinkOnceODRLinkage(); 1925ffd83dbSDimitry Andric FF[NamedFeatureIndex::IsLinkOnce] = F.hasLinkOnceLinkage(); 193*bdd1243dSDimitry Andric FF[NamedFeatureIndex::Blocks] = F.size(); 1945ffd83dbSDimitry Andric auto &LI = FAM.getResult<LoopAnalysis>(F); 1955ffd83dbSDimitry Andric FF[NamedFeatureIndex::Loops] = std::distance(LI.begin(), LI.end()); 1965ffd83dbSDimitry Andric for (auto &L : LI) 1975ffd83dbSDimitry Andric FF[NamedFeatureIndex::MaxLoopDepth] = 1985ffd83dbSDimitry Andric std::max(FF[NamedFeatureIndex::MaxLoopDepth], 1995ffd83dbSDimitry Andric static_cast<int32_t>(L->getLoopDepth())); 2005ffd83dbSDimitry Andric FF[NamedFeatureIndex::MaxDomTreeLevel] = getMaxDominatorTreeDepth(F, DomTree); 2015ffd83dbSDimitry Andric return FF; 2025ffd83dbSDimitry Andric } 2035ffd83dbSDimitry Andric 2045ffd83dbSDimitry Andric void IRToNativeSizeLearning::FunctionFeatures::fillTensor(int32_t *Ptr) const { 2055ffd83dbSDimitry Andric std::copy(NamedFeatures.begin(), NamedFeatures.end(), Ptr); 2065ffd83dbSDimitry Andric Ptr += NamedFeatures.size(); 2075ffd83dbSDimitry Andric std::copy(InstructionHistogram.begin(), InstructionHistogram.end(), Ptr); 2085ffd83dbSDimitry Andric Ptr += InstructionHistogram.size(); 2095ffd83dbSDimitry Andric std::copy(InstructionPairHistogram.begin(), InstructionPairHistogram.end(), 2105ffd83dbSDimitry Andric Ptr); 2115ffd83dbSDimitry Andric } 2125ffd83dbSDimitry Andric 2135ffd83dbSDimitry Andric bool InlineSizeEstimatorAnalysis::isEvaluatorRequested() { 2145ffd83dbSDimitry Andric return !TFIR2NativeModelPath.empty(); 2155ffd83dbSDimitry Andric } 2165ffd83dbSDimitry Andric 2175ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis() { 2185ffd83dbSDimitry Andric if (!isEvaluatorRequested()) { 2195ffd83dbSDimitry Andric return; 2205ffd83dbSDimitry Andric } 221e8d8bef9SDimitry Andric std::vector<TensorSpec> InputSpecs{TensorSpec::createSpec<int32_t>( 222e8d8bef9SDimitry Andric "serving_default_input_1", 223e8d8bef9SDimitry Andric {1, static_cast<int64_t>( 224e8d8bef9SDimitry Andric IRToNativeSizeLearning::FunctionFeatures::FeatureCount)})}; 225e8d8bef9SDimitry Andric std::vector<TensorSpec> OutputSpecs{ 226e8d8bef9SDimitry Andric TensorSpec::createSpec<float>("StatefulPartitionedCall", {1})}; 2275ffd83dbSDimitry Andric Evaluator = std::make_unique<TFModelEvaluator>( 228e8d8bef9SDimitry Andric TFIR2NativeModelPath.getValue().c_str(), InputSpecs, OutputSpecs); 2295ffd83dbSDimitry Andric if (!Evaluator || !Evaluator->isValid()) { 2305ffd83dbSDimitry Andric Evaluator.reset(); 2315ffd83dbSDimitry Andric return; 2325ffd83dbSDimitry Andric } 2335ffd83dbSDimitry Andric } 2345ffd83dbSDimitry Andric 2355ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::Result 2365ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::run(const Function &F, 2375ffd83dbSDimitry Andric FunctionAnalysisManager &FAM) { 2385ffd83dbSDimitry Andric if (!Evaluator) 239*bdd1243dSDimitry Andric return std::nullopt; 2405ffd83dbSDimitry Andric auto Features = IRToNativeSizeLearning::getFunctionFeatures( 2415ffd83dbSDimitry Andric const_cast<Function &>(F), FAM); 2425ffd83dbSDimitry Andric int32_t *V = Evaluator->getInput<int32_t>(0); 2435ffd83dbSDimitry Andric Features.fillTensor(V); 2445ffd83dbSDimitry Andric auto ER = Evaluator->evaluate(); 2455ffd83dbSDimitry Andric if (!ER) 246*bdd1243dSDimitry Andric return std::nullopt; 2475ffd83dbSDimitry Andric float Ret = *ER->getTensorValue<float>(0); 2485ffd83dbSDimitry Andric if (Ret < 0.0) 2495ffd83dbSDimitry Andric Ret = 0.0; 2505ffd83dbSDimitry Andric return static_cast<size_t>(Ret); 2515ffd83dbSDimitry Andric } 2525ffd83dbSDimitry Andric 2535ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::~InlineSizeEstimatorAnalysis() {} 2545ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis( 2555ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis &&Other) 2565ffd83dbSDimitry Andric : Evaluator(std::move(Other.Evaluator)) {} 2575ffd83dbSDimitry Andric 2585ffd83dbSDimitry Andric #else 2595ffd83dbSDimitry Andric namespace llvm { 2605ffd83dbSDimitry Andric class TFModelEvaluator {}; 2615ffd83dbSDimitry Andric } // namespace llvm 26281ad6265SDimitry Andric InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis() = default; 2635ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis ::InlineSizeEstimatorAnalysis( 2645ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis &&) {} 26581ad6265SDimitry Andric InlineSizeEstimatorAnalysis::~InlineSizeEstimatorAnalysis() = default; 2665ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::Result 2675ffd83dbSDimitry Andric InlineSizeEstimatorAnalysis::run(const Function &F, 2685ffd83dbSDimitry Andric FunctionAnalysisManager &FAM) { 269*bdd1243dSDimitry Andric return std::nullopt; 2705ffd83dbSDimitry Andric } 2715ffd83dbSDimitry Andric bool InlineSizeEstimatorAnalysis::isEvaluatorRequested() { return false; } 2725ffd83dbSDimitry Andric #endif 273e8d8bef9SDimitry Andric 274e8d8bef9SDimitry Andric PreservedAnalyses 275e8d8bef9SDimitry Andric InlineSizeEstimatorAnalysisPrinterPass::run(Function &F, 276e8d8bef9SDimitry Andric FunctionAnalysisManager &AM) { 277e8d8bef9SDimitry Andric OS << "[InlineSizeEstimatorAnalysis] size estimate for " << F.getName() 278e8d8bef9SDimitry Andric << ": " << AM.getResult<InlineSizeEstimatorAnalysis>(F) << "\n"; 279e8d8bef9SDimitry Andric return PreservedAnalyses::all(); 280e8d8bef9SDimitry Andric } 281