Path: blob/main/contrib/llvm-project/llvm/lib/Analysis/InlineSizeEstimatorAnalysis.cpp
35233 views
//===- InlineSizeEstimatorAnalysis.cpp - IR to native size from ML model --===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This implements feature and label extraction for offline supervised learning9// of a IR to native size model.10//11//===----------------------------------------------------------------------===//12#include "llvm/Analysis/InlineSizeEstimatorAnalysis.h"1314#ifdef LLVM_HAVE_TFLITE15#include "llvm/Analysis/Utils/TFUtils.h"16#endif17#include "llvm/IR/Function.h"18#include "llvm/IR/PassManager.h"19#include "llvm/Support/raw_ostream.h"2021using namespace llvm;2223AnalysisKey InlineSizeEstimatorAnalysis::Key;2425#ifdef LLVM_HAVE_TFLITE26#include "llvm/Analysis/LoopInfo.h"27#include "llvm/Analysis/TargetLibraryInfo.h"28#include "llvm/Analysis/TargetTransformInfo.h"29#include "llvm/IR/BasicBlock.h"30#include "llvm/IR/Dominators.h"31#include "llvm/IR/Instructions.h"32#include "llvm/Support/Casting.h"33#include "llvm/Support/CommandLine.h"34#include <algorithm>35#include <deque>36#include <optional>3738cl::opt<std::string> TFIR2NativeModelPath(39"ml-inliner-ir2native-model", cl::Hidden,40cl::desc("Path to saved model evaluating native size from IR."));4142#define DEBUG_TYPE "inline-size-estimator"43namespace {44unsigned getMaxInstructionID() {45#define LAST_OTHER_INST(NR) return NR;46#include "llvm/IR/Instruction.def"47}4849class IRToNativeSizeLearning {50public:51enum class NamedFeatureIndex : size_t {52InitialSize,53Blocks,54Calls,55IsLocal,56IsLinkOnceODR,57IsLinkOnce,58Loops,59MaxLoopDepth,60MaxDomTreeLevel,6162NumNamedFeatures63};64static const size_t NumNamedFeatures =65static_cast<size_t>(NamedFeatureIndex::NumNamedFeatures);66struct FunctionFeatures {67static const size_t FeatureCount;6869std::array<int32_t, NumNamedFeatures> NamedFeatures = {0};70std::vector<int32_t> InstructionHistogram;71std::vector<int32_t> InstructionPairHistogram;7273void fillTensor(int32_t *Ptr) const;74int32_t &operator[](NamedFeatureIndex Pos) {75return NamedFeatures[static_cast<size_t>(Pos)];76}77};78IRToNativeSizeLearning() = default;7980static FunctionFeatures getFunctionFeatures(Function &F,81FunctionAnalysisManager &FAM);82};8384// This is a point in time - we determined including these pairs of85// consecutive instructions (in the IR layout available at inline time) as86// features improves the model performance. We want to move away from manual87// feature selection.88// The array is given in opcode pairs rather than labels because 1) labels89// weren't readily available, and 2) the successions were hand - extracted.90//91// This array must be sorted.92static const std::array<std::pair<size_t, size_t>, 137>93ImportantInstructionSuccessions{94{{1, 1}, {1, 4}, {1, 5}, {1, 7}, {1, 8}, {1, 9}, {1, 11},95{1, 12}, {1, 13}, {1, 14}, {1, 18}, {1, 20}, {1, 22}, {1, 24},96{1, 25}, {1, 26}, {1, 27}, {1, 28}, {1, 29}, {1, 30}, {1, 31},97{1, 32}, {1, 33}, {1, 34}, {1, 39}, {1, 40}, {1, 42}, {1, 45},98{2, 1}, {2, 2}, {2, 13}, {2, 28}, {2, 29}, {2, 32}, {2, 33},99{2, 34}, {2, 38}, {2, 48}, {2, 49}, {2, 53}, {2, 55}, {2, 56},100{13, 2}, {13, 13}, {13, 26}, {13, 33}, {13, 34}, {13, 56}, {15, 27},101{28, 2}, {28, 48}, {28, 53}, {29, 2}, {29, 33}, {29, 56}, {31, 31},102{31, 33}, {31, 34}, {31, 49}, {32, 1}, {32, 2}, {32, 13}, {32, 15},103{32, 28}, {32, 29}, {32, 32}, {32, 33}, {32, 34}, {32, 39}, {32, 40},104{32, 48}, {32, 49}, {32, 53}, {32, 56}, {33, 1}, {33, 2}, {33, 32},105{33, 33}, {33, 34}, {33, 49}, {33, 53}, {33, 56}, {34, 1}, {34, 2},106{34, 32}, {34, 33}, {34, 34}, {34, 49}, {34, 53}, {34, 56}, {38, 34},107{39, 57}, {40, 34}, {47, 15}, {47, 49}, {48, 2}, {48, 34}, {48, 56},108{49, 1}, {49, 2}, {49, 28}, {49, 32}, {49, 33}, {49, 34}, {49, 39},109{49, 49}, {49, 56}, {53, 1}, {53, 2}, {53, 28}, {53, 34}, {53, 53},110{53, 57}, {55, 1}, {55, 28}, {55, 34}, {55, 53}, {55, 55}, {55, 56},111{56, 1}, {56, 2}, {56, 7}, {56, 13}, {56, 32}, {56, 33}, {56, 34},112{56, 49}, {56, 53}, {56, 56}, {56, 64}, {57, 34}, {57, 56}, {57, 57},113{64, 1}, {64, 64}, {65, 1}, {65, 65}}};114115// We have: 9 calculated features (the features here); 1 feature for each116// instruction opcode; and 1 feature for each manually-identified sequence.117// For the latter 2, we build a histogram: we count the number of118// occurrences of each instruction opcode or succession of instructions,119// respectively.120// Note that instruction opcodes start from 1. For convenience, we also have an121// always 0 feature for the '0' opcode, hence the extra 1.122const size_t IRToNativeSizeLearning::FunctionFeatures::FeatureCount =123ImportantInstructionSuccessions.size() + getMaxInstructionID() + 1 +124IRToNativeSizeLearning::NumNamedFeatures;125126size_t getSize(Function &F, TargetTransformInfo &TTI) {127size_t Ret = 0;128for (const auto &BB : F)129for (const auto &I : BB)130Ret += *(TTI.getInstructionCost(131&I, TargetTransformInfo::TargetCostKind::TCK_CodeSize).getValue());132return Ret;133}134135size_t getSize(Function &F, FunctionAnalysisManager &FAM) {136auto &TTI = FAM.getResult<TargetIRAnalysis>(F);137return getSize(F, TTI);138}139140unsigned getMaxDominatorTreeDepth(const Function &F,141const DominatorTree &Tree) {142unsigned Ret = 0;143for (const auto &BB : F)144if (const auto *TN = Tree.getNode(&BB))145Ret = std::max(Ret, TN->getLevel());146return Ret;147}148} // namespace149150IRToNativeSizeLearning::FunctionFeatures151IRToNativeSizeLearning::getFunctionFeatures(Function &F,152FunctionAnalysisManager &FAM) {153assert(llvm::is_sorted(ImportantInstructionSuccessions) &&154"expected function features are sorted");155156auto &DomTree = FAM.getResult<DominatorTreeAnalysis>(F);157FunctionFeatures FF;158size_t InstrCount = getMaxInstructionID() + 1;159FF.InstructionHistogram.resize(InstrCount);160161FF.InstructionPairHistogram.resize(ImportantInstructionSuccessions.size());162163int StartID = 0;164int LastID = StartID;165auto getPairIndex = [](size_t a, size_t b) {166auto I = llvm::find(ImportantInstructionSuccessions, std::make_pair(a, b));167if (I == ImportantInstructionSuccessions.end())168return -1;169return static_cast<int>(170std::distance(ImportantInstructionSuccessions.begin(), I));171};172173// We don't want debug calls, because they'd just add noise.174for (const auto &BB : F) {175for (const auto &I : BB.instructionsWithoutDebug()) {176auto ID = I.getOpcode();177178++FF.InstructionHistogram[ID];179int PairIndex = getPairIndex(LastID, ID);180if (PairIndex >= 0)181++FF.InstructionPairHistogram[PairIndex];182LastID = ID;183if (isa<CallBase>(I))184++FF[NamedFeatureIndex::Calls];185}186}187188FF[NamedFeatureIndex::InitialSize] = getSize(F, FAM);189FF[NamedFeatureIndex::IsLocal] = F.hasLocalLinkage();190FF[NamedFeatureIndex::IsLinkOnceODR] = F.hasLinkOnceODRLinkage();191FF[NamedFeatureIndex::IsLinkOnce] = F.hasLinkOnceLinkage();192FF[NamedFeatureIndex::Blocks] = F.size();193auto &LI = FAM.getResult<LoopAnalysis>(F);194FF[NamedFeatureIndex::Loops] = std::distance(LI.begin(), LI.end());195for (auto &L : LI)196FF[NamedFeatureIndex::MaxLoopDepth] =197std::max(FF[NamedFeatureIndex::MaxLoopDepth],198static_cast<int32_t>(L->getLoopDepth()));199FF[NamedFeatureIndex::MaxDomTreeLevel] = getMaxDominatorTreeDepth(F, DomTree);200return FF;201}202203void IRToNativeSizeLearning::FunctionFeatures::fillTensor(int32_t *Ptr) const {204std::copy(NamedFeatures.begin(), NamedFeatures.end(), Ptr);205Ptr += NamedFeatures.size();206std::copy(InstructionHistogram.begin(), InstructionHistogram.end(), Ptr);207Ptr += InstructionHistogram.size();208std::copy(InstructionPairHistogram.begin(), InstructionPairHistogram.end(),209Ptr);210}211212bool InlineSizeEstimatorAnalysis::isEvaluatorRequested() {213return !TFIR2NativeModelPath.empty();214}215216InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis() {217if (!isEvaluatorRequested()) {218return;219}220std::vector<TensorSpec> InputSpecs{TensorSpec::createSpec<int32_t>(221"serving_default_input_1",222{1, static_cast<int64_t>(223IRToNativeSizeLearning::FunctionFeatures::FeatureCount)})};224std::vector<TensorSpec> OutputSpecs{225TensorSpec::createSpec<float>("StatefulPartitionedCall", {1})};226Evaluator = std::make_unique<TFModelEvaluator>(227TFIR2NativeModelPath.getValue().c_str(), InputSpecs, OutputSpecs);228if (!Evaluator || !Evaluator->isValid()) {229Evaluator.reset();230return;231}232}233234InlineSizeEstimatorAnalysis::Result235InlineSizeEstimatorAnalysis::run(const Function &F,236FunctionAnalysisManager &FAM) {237if (!Evaluator)238return std::nullopt;239auto Features = IRToNativeSizeLearning::getFunctionFeatures(240const_cast<Function &>(F), FAM);241int32_t *V = Evaluator->getInput<int32_t>(0);242Features.fillTensor(V);243auto ER = Evaluator->evaluate();244if (!ER)245return std::nullopt;246float Ret = *ER->getTensorValue<float>(0);247if (Ret < 0.0)248Ret = 0.0;249return static_cast<size_t>(Ret);250}251252InlineSizeEstimatorAnalysis::~InlineSizeEstimatorAnalysis() {}253InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis(254InlineSizeEstimatorAnalysis &&Other)255: Evaluator(std::move(Other.Evaluator)) {}256257#else258namespace llvm {259class TFModelEvaluator {};260} // namespace llvm261InlineSizeEstimatorAnalysis::InlineSizeEstimatorAnalysis() = default;262InlineSizeEstimatorAnalysis ::InlineSizeEstimatorAnalysis(263InlineSizeEstimatorAnalysis &&) {}264InlineSizeEstimatorAnalysis::~InlineSizeEstimatorAnalysis() = default;265InlineSizeEstimatorAnalysis::Result266InlineSizeEstimatorAnalysis::run(const Function &F,267FunctionAnalysisManager &FAM) {268return std::nullopt;269}270bool InlineSizeEstimatorAnalysis::isEvaluatorRequested() { return false; }271#endif272273PreservedAnalyses274InlineSizeEstimatorAnalysisPrinterPass::run(Function &F,275FunctionAnalysisManager &AM) {276OS << "[InlineSizeEstimatorAnalysis] size estimate for " << F.getName()277<< ": " << AM.getResult<InlineSizeEstimatorAnalysis>(F) << "\n";278return PreservedAnalyses::all();279}280281282