Path: blob/main/contrib/llvm-project/llvm/lib/CGData/StableFunctionMap.cpp
213764 views
//===-- StableFunctionMap.cpp ---------------------------------------------===//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 the functionality for the StableFunctionMap class, which9// manages the mapping of stable function hashes to their metadata. It includes10// methods for inserting, merging, and finalizing function entries, as well as11// utilities for handling function names and IDs.12//13//===----------------------------------------------------------------------===//1415#include "llvm/CGData/StableFunctionMap.h"16#include "llvm/ADT/SmallSet.h"17#include "llvm/Support/CommandLine.h"18#include "llvm/Support/Debug.h"1920#define DEBUG_TYPE "stable-function-map"2122using namespace llvm;2324static cl::opt<unsigned>25GlobalMergingMinMerges("global-merging-min-merges",26cl::desc("Minimum number of similar functions with "27"the same hash required for merging."),28cl::init(2), cl::Hidden);29static cl::opt<unsigned> GlobalMergingMinInstrs(30"global-merging-min-instrs",31cl::desc("The minimum instruction count required when merging functions."),32cl::init(1), cl::Hidden);33static cl::opt<unsigned> GlobalMergingMaxParams(34"global-merging-max-params",35cl::desc(36"The maximum number of parameters allowed when merging functions."),37cl::init(std::numeric_limits<unsigned>::max()), cl::Hidden);38static cl::opt<bool> GlobalMergingSkipNoParams(39"global-merging-skip-no-params",40cl::desc("Skip merging functions with no parameters."), cl::init(true),41cl::Hidden);42static cl::opt<double> GlobalMergingInstOverhead(43"global-merging-inst-overhead",44cl::desc("The overhead cost associated with each instruction when lowering "45"to machine instruction."),46cl::init(1.2), cl::Hidden);47static cl::opt<double> GlobalMergingParamOverhead(48"global-merging-param-overhead",49cl::desc("The overhead cost associated with each parameter when merging "50"functions."),51cl::init(2.0), cl::Hidden);52static cl::opt<double>53GlobalMergingCallOverhead("global-merging-call-overhead",54cl::desc("The overhead cost associated with each "55"function call when merging functions."),56cl::init(1.0), cl::Hidden);57static cl::opt<double> GlobalMergingExtraThreshold(58"global-merging-extra-threshold",59cl::desc("An additional cost threshold that must be exceeded for merging "60"to be considered beneficial."),61cl::init(0.0), cl::Hidden);6263unsigned StableFunctionMap::getIdOrCreateForName(StringRef Name) {64auto It = NameToId.find(Name);65if (It != NameToId.end())66return It->second;67unsigned Id = IdToName.size();68assert(Id == NameToId.size() && "ID collision");69IdToName.emplace_back(Name.str());70NameToId[IdToName.back()] = Id;71return Id;72}7374std::optional<std::string> StableFunctionMap::getNameForId(unsigned Id) const {75if (Id >= IdToName.size())76return std::nullopt;77return IdToName[Id];78}7980void StableFunctionMap::insert(const StableFunction &Func) {81assert(!Finalized && "Cannot insert after finalization");82auto FuncNameId = getIdOrCreateForName(Func.FunctionName);83auto ModuleNameId = getIdOrCreateForName(Func.ModuleName);84auto IndexOperandHashMap = std::make_unique<IndexOperandHashMapType>();85for (auto &[Index, Hash] : Func.IndexOperandHashes)86(*IndexOperandHashMap)[Index] = Hash;87auto FuncEntry = std::make_unique<StableFunctionEntry>(88Func.Hash, FuncNameId, ModuleNameId, Func.InstCount,89std::move(IndexOperandHashMap));90insert(std::move(FuncEntry));91}9293void StableFunctionMap::merge(const StableFunctionMap &OtherMap) {94assert(!Finalized && "Cannot merge after finalization");95for (auto &[Hash, Funcs] : OtherMap.HashToFuncs) {96auto &ThisFuncs = HashToFuncs[Hash];97for (auto &Func : Funcs) {98auto FuncNameId =99getIdOrCreateForName(*OtherMap.getNameForId(Func->FunctionNameId));100auto ModuleNameId =101getIdOrCreateForName(*OtherMap.getNameForId(Func->ModuleNameId));102auto ClonedIndexOperandHashMap =103std::make_unique<IndexOperandHashMapType>(*Func->IndexOperandHashMap);104ThisFuncs.emplace_back(std::make_unique<StableFunctionEntry>(105Func->Hash, FuncNameId, ModuleNameId, Func->InstCount,106std::move(ClonedIndexOperandHashMap)));107}108}109}110111size_t StableFunctionMap::size(SizeType Type) const {112switch (Type) {113case UniqueHashCount:114return HashToFuncs.size();115case TotalFunctionCount: {116size_t Count = 0;117for (auto &Funcs : HashToFuncs)118Count += Funcs.second.size();119return Count;120}121case MergeableFunctionCount: {122size_t Count = 0;123for (auto &[Hash, Funcs] : HashToFuncs)124if (Funcs.size() >= 2)125Count += Funcs.size();126return Count;127}128}129llvm_unreachable("Unhandled size type");130}131132using ParamLocs = SmallVector<IndexPair>;133static void removeIdenticalIndexPair(134SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>> &SFS) {135auto &RSF = SFS[0];136unsigned StableFunctionCount = SFS.size();137138SmallVector<IndexPair> ToDelete;139for (auto &[Pair, Hash] : *(RSF->IndexOperandHashMap)) {140bool Identical = true;141for (unsigned J = 1; J < StableFunctionCount; ++J) {142auto &SF = SFS[J];143const auto &SHash = SF->IndexOperandHashMap->at(Pair);144if (Hash != SHash) {145Identical = false;146break;147}148}149150// No need to parameterize them if the hashes are identical across stable151// functions.152if (Identical)153ToDelete.emplace_back(Pair);154}155156for (auto &Pair : ToDelete)157for (auto &SF : SFS)158SF->IndexOperandHashMap->erase(Pair);159}160161static bool isProfitable(162const SmallVector<std::unique_ptr<StableFunctionMap::StableFunctionEntry>>163&SFS) {164unsigned StableFunctionCount = SFS.size();165if (StableFunctionCount < GlobalMergingMinMerges)166return false;167168unsigned InstCount = SFS[0]->InstCount;169if (InstCount < GlobalMergingMinInstrs)170return false;171172double Cost = 0.0;173SmallSet<stable_hash, 8> UniqueHashVals;174for (auto &SF : SFS) {175UniqueHashVals.clear();176for (auto &[IndexPair, Hash] : *SF->IndexOperandHashMap)177UniqueHashVals.insert(Hash);178unsigned ParamCount = UniqueHashVals.size();179if (ParamCount > GlobalMergingMaxParams)180return false;181// Theoretically, if ParamCount is 0, it results in identical code folding182// (ICF), which we can skip merging here since the linker already handles183// ICF. This pass would otherwise introduce unnecessary thunks that are184// merely direct jumps. However, enabling this could be beneficial depending185// on downstream passes, so we provide an option for it.186if (GlobalMergingSkipNoParams && ParamCount == 0)187return false;188Cost += ParamCount * GlobalMergingParamOverhead + GlobalMergingCallOverhead;189}190Cost += GlobalMergingExtraThreshold;191192double Benefit =193InstCount * (StableFunctionCount - 1) * GlobalMergingInstOverhead;194bool Result = Benefit > Cost;195LLVM_DEBUG(dbgs() << "isProfitable: Hash = " << SFS[0]->Hash << ", "196<< "StableFunctionCount = " << StableFunctionCount197<< ", InstCount = " << InstCount198<< ", Benefit = " << Benefit << ", Cost = " << Cost199<< ", Result = " << (Result ? "true" : "false") << "\n");200return Result;201}202203void StableFunctionMap::finalize(bool SkipTrim) {204for (auto It = HashToFuncs.begin(); It != HashToFuncs.end(); ++It) {205auto &[StableHash, SFS] = *It;206207// Group stable functions by ModuleIdentifier.208llvm::stable_sort(SFS, [&](const std::unique_ptr<StableFunctionEntry> &L,209const std::unique_ptr<StableFunctionEntry> &R) {210return *getNameForId(L->ModuleNameId) < *getNameForId(R->ModuleNameId);211});212213// Consider the first function as the root function.214auto &RSF = SFS[0];215216bool Invalid = false;217unsigned StableFunctionCount = SFS.size();218for (unsigned I = 1; I < StableFunctionCount; ++I) {219auto &SF = SFS[I];220assert(RSF->Hash == SF->Hash);221if (RSF->InstCount != SF->InstCount) {222Invalid = true;223break;224}225if (RSF->IndexOperandHashMap->size() != SF->IndexOperandHashMap->size()) {226Invalid = true;227break;228}229for (auto &P : *RSF->IndexOperandHashMap) {230auto &InstOpndIndex = P.first;231if (!SF->IndexOperandHashMap->count(InstOpndIndex)) {232Invalid = true;233break;234}235}236}237if (Invalid) {238HashToFuncs.erase(It);239continue;240}241242if (SkipTrim)243continue;244245// Trim the index pair that has the same operand hash across246// stable functions.247removeIdenticalIndexPair(SFS);248249if (!isProfitable(SFS))250HashToFuncs.erase(It);251}252253Finalized = true;254}255256257