Path: blob/main/contrib/llvm-project/llvm/lib/CodeGen/BasicBlockSectionsProfileReader.cpp
35232 views
//===-- BasicBlockSectionsProfileReader.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// Implementation of the basic block sections profile reader pass. It parses9// and stores the basic block sections profile file (which is specified via the10// `-basic-block-sections` flag).11//12//===----------------------------------------------------------------------===//1314#include "llvm/CodeGen/BasicBlockSectionsProfileReader.h"15#include "llvm/ADT/DenseSet.h"16#include "llvm/ADT/SmallSet.h"17#include "llvm/ADT/SmallString.h"18#include "llvm/ADT/SmallVector.h"19#include "llvm/ADT/StringMap.h"20#include "llvm/ADT/StringRef.h"21#include "llvm/IR/DebugInfoMetadata.h"22#include "llvm/Pass.h"23#include "llvm/Support/Error.h"24#include "llvm/Support/ErrorHandling.h"25#include "llvm/Support/LineIterator.h"26#include "llvm/Support/MemoryBuffer.h"27#include "llvm/Support/Path.h"28#include <llvm/ADT/STLExtras.h>2930using namespace llvm;3132char BasicBlockSectionsProfileReaderWrapperPass::ID = 0;33INITIALIZE_PASS(BasicBlockSectionsProfileReaderWrapperPass,34"bbsections-profile-reader",35"Reads and parses a basic block sections profile.", false,36false)3738Expected<UniqueBBID>39BasicBlockSectionsProfileReader::parseUniqueBBID(StringRef S) const {40SmallVector<StringRef, 2> Parts;41S.split(Parts, '.');42if (Parts.size() > 2)43return createProfileParseError(Twine("unable to parse basic block id: '") +44S + "'");45unsigned long long BaseBBID;46if (getAsUnsignedInteger(Parts[0], 10, BaseBBID))47return createProfileParseError(48Twine("unable to parse BB id: '" + Parts[0]) +49"': unsigned integer expected");50unsigned long long CloneID = 0;51if (Parts.size() > 1 && getAsUnsignedInteger(Parts[1], 10, CloneID))52return createProfileParseError(Twine("unable to parse clone id: '") +53Parts[1] + "': unsigned integer expected");54return UniqueBBID{static_cast<unsigned>(BaseBBID),55static_cast<unsigned>(CloneID)};56}5758bool BasicBlockSectionsProfileReader::isFunctionHot(StringRef FuncName) const {59return getClusterInfoForFunction(FuncName).first;60}6162std::pair<bool, SmallVector<BBClusterInfo>>63BasicBlockSectionsProfileReader::getClusterInfoForFunction(64StringRef FuncName) const {65auto R = ProgramPathAndClusterInfo.find(getAliasName(FuncName));66return R != ProgramPathAndClusterInfo.end()67? std::pair(true, R->second.ClusterInfo)68: std::pair(false, SmallVector<BBClusterInfo>());69}7071SmallVector<SmallVector<unsigned>>72BasicBlockSectionsProfileReader::getClonePathsForFunction(73StringRef FuncName) const {74return ProgramPathAndClusterInfo.lookup(getAliasName(FuncName)).ClonePaths;75}7677// Reads the version 1 basic block sections profile. Profile for each function78// is encoded as follows:79// m <module_name>80// f <function_name_1> <function_name_2> ...81// c <bb_id_1> <bb_id_2> <bb_id_3>82// c <bb_id_4> <bb_id_5>83// ...84// Module name specifier (starting with 'm') is optional and allows85// distinguishing profile for internal-linkage functions with the same name. If86// not specified, it will apply to any function with the same name. Function87// name specifier (starting with 'f') can specify multiple function name88// aliases. Basic block clusters are specified by 'c' and specify the cluster of89// basic blocks, and the internal order in which they must be placed in the same90// section.91// This profile can also specify cloning paths which instruct the compiler to92// clone basic blocks along a path. The cloned blocks are then specified in the93// cluster information.94// The following profile lists two cloning paths (starting with 'p') for95// function bar and places the total 9 blocks within two clusters. The first two96// blocks of a cloning path specify the edge along which the path is cloned. For97// instance, path 1 (1 -> 3 -> 4) instructs that 3 and 4 must be cloned along98// the edge 1->3. Within the given clusters, each cloned block is identified by99// "<original block id>.<clone id>". For instance, 3.1 represents the first100// clone of block 3. Original blocks are specified just with their block ids. A101// block cloned multiple times appears with distinct clone ids. The CFG for bar102// is shown below before and after cloning with its final clusters labeled.103//104// f main105// f bar106// p 1 3 4 # cloning path 1107// p 4 2 # cloning path 2108// c 1 3.1 4.1 6 # basic block cluster 1109// c 0 2 3 4 2.1 5 # basic block cluster 2110// ****************************************************************************111// function bar before and after cloning with basic block clusters shown.112// ****************************************************************************113// .... ..............114// 0 -------+ : 0 :---->: 1 ---> 3.1 :115// | | : | : :........ | :116// v v : v : : v :117// +--> 2 --> 5 1 ~~~~~~> +---: 2 : : 4.1: clsuter 1118// | | | | : | : : | :119// | v | | : v ....... : v :120// | 3 <------+ | : 3 <--+ : : 6 :121// | | | : | | : :....:122// | v | : v | :123// +--- 4 ---> 6 | : 4 | :124// | : | | :125// | : v | :126// | :2.1---+ : cluster 2127// | : | ......:128// | : v :129// +-->: 5 :130// ....131// ****************************************************************************132Error BasicBlockSectionsProfileReader::ReadV1Profile() {133auto FI = ProgramPathAndClusterInfo.end();134135// Current cluster ID corresponding to this function.136unsigned CurrentCluster = 0;137// Current position in the current cluster.138unsigned CurrentPosition = 0;139140// Temporary set to ensure every basic block ID appears once in the clusters141// of a function.142DenseSet<UniqueBBID> FuncBBIDs;143144// Debug-info-based module filename for the current function. Empty string145// means no filename.146StringRef DIFilename;147148for (; !LineIt.is_at_eof(); ++LineIt) {149StringRef S(*LineIt);150char Specifier = S[0];151S = S.drop_front().trim();152SmallVector<StringRef, 4> Values;153S.split(Values, ' ');154switch (Specifier) {155case '@':156continue;157case 'm': // Module name speicifer.158if (Values.size() != 1) {159return createProfileParseError(Twine("invalid module name value: '") +160S + "'");161}162DIFilename = sys::path::remove_leading_dotslash(Values[0]);163continue;164case 'f': { // Function names specifier.165bool FunctionFound = any_of(Values, [&](StringRef Alias) {166auto It = FunctionNameToDIFilename.find(Alias);167// No match if this function name is not found in this module.168if (It == FunctionNameToDIFilename.end())169return false;170// Return a match if debug-info-filename is not specified. Otherwise,171// check for equality.172return DIFilename.empty() || It->second == DIFilename;173});174if (!FunctionFound) {175// Skip the following profile by setting the profile iterator (FI) to176// the past-the-end element.177FI = ProgramPathAndClusterInfo.end();178DIFilename = "";179continue;180}181for (size_t i = 1; i < Values.size(); ++i)182FuncAliasMap.try_emplace(Values[i], Values.front());183184// Prepare for parsing clusters of this function name.185// Start a new cluster map for this function name.186auto R = ProgramPathAndClusterInfo.try_emplace(Values.front());187// Report error when multiple profiles have been specified for the same188// function.189if (!R.second)190return createProfileParseError("duplicate profile for function '" +191Values.front() + "'");192FI = R.first;193CurrentCluster = 0;194FuncBBIDs.clear();195// We won't need DIFilename anymore. Clean it up to avoid its application196// on the next function.197DIFilename = "";198continue;199}200case 'c': // Basic block cluster specifier.201// Skip the profile when we the profile iterator (FI) refers to the202// past-the-end element.203if (FI == ProgramPathAndClusterInfo.end())204continue;205// Reset current cluster position.206CurrentPosition = 0;207for (auto BasicBlockIDStr : Values) {208auto BasicBlockID = parseUniqueBBID(BasicBlockIDStr);209if (!BasicBlockID)210return BasicBlockID.takeError();211if (!FuncBBIDs.insert(*BasicBlockID).second)212return createProfileParseError(213Twine("duplicate basic block id found '") + BasicBlockIDStr +214"'");215216FI->second.ClusterInfo.emplace_back(BBClusterInfo{217*std::move(BasicBlockID), CurrentCluster, CurrentPosition++});218}219CurrentCluster++;220continue;221case 'p': { // Basic block cloning path specifier.222// Skip the profile when we the profile iterator (FI) refers to the223// past-the-end element.224if (FI == ProgramPathAndClusterInfo.end())225continue;226SmallSet<unsigned, 5> BBsInPath;227FI->second.ClonePaths.push_back({});228for (size_t I = 0; I < Values.size(); ++I) {229auto BaseBBIDStr = Values[I];230unsigned long long BaseBBID = 0;231if (getAsUnsignedInteger(BaseBBIDStr, 10, BaseBBID))232return createProfileParseError(Twine("unsigned integer expected: '") +233BaseBBIDStr + "'");234if (I != 0 && !BBsInPath.insert(BaseBBID).second)235return createProfileParseError(236Twine("duplicate cloned block in path: '") + BaseBBIDStr + "'");237FI->second.ClonePaths.back().push_back(BaseBBID);238}239continue;240}241default:242return createProfileParseError(Twine("invalid specifier: '") +243Twine(Specifier) + "'");244}245llvm_unreachable("should not break from this switch statement");246}247return Error::success();248}249250Error BasicBlockSectionsProfileReader::ReadV0Profile() {251auto FI = ProgramPathAndClusterInfo.end();252// Current cluster ID corresponding to this function.253unsigned CurrentCluster = 0;254// Current position in the current cluster.255unsigned CurrentPosition = 0;256257// Temporary set to ensure every basic block ID appears once in the clusters258// of a function.259SmallSet<unsigned, 4> FuncBBIDs;260261for (; !LineIt.is_at_eof(); ++LineIt) {262StringRef S(*LineIt);263if (S[0] == '@')264continue;265// Check for the leading "!"266if (!S.consume_front("!") || S.empty())267break;268// Check for second "!" which indicates a cluster of basic blocks.269if (S.consume_front("!")) {270// Skip the profile when we the profile iterator (FI) refers to the271// past-the-end element.272if (FI == ProgramPathAndClusterInfo.end())273continue;274SmallVector<StringRef, 4> BBIDs;275S.split(BBIDs, ' ');276// Reset current cluster position.277CurrentPosition = 0;278for (auto BBIDStr : BBIDs) {279unsigned long long BBID;280if (getAsUnsignedInteger(BBIDStr, 10, BBID))281return createProfileParseError(Twine("unsigned integer expected: '") +282BBIDStr + "'");283if (!FuncBBIDs.insert(BBID).second)284return createProfileParseError(285Twine("duplicate basic block id found '") + BBIDStr + "'");286287FI->second.ClusterInfo.emplace_back(288BBClusterInfo({{static_cast<unsigned>(BBID), 0},289CurrentCluster,290CurrentPosition++}));291}292CurrentCluster++;293} else {294// This is a function name specifier. It may include a debug info filename295// specifier starting with `M=`.296auto [AliasesStr, DIFilenameStr] = S.split(' ');297SmallString<128> DIFilename;298if (DIFilenameStr.starts_with("M=")) {299DIFilename =300sys::path::remove_leading_dotslash(DIFilenameStr.substr(2));301if (DIFilename.empty())302return createProfileParseError("empty module name specifier");303} else if (!DIFilenameStr.empty()) {304return createProfileParseError("unknown string found: '" +305DIFilenameStr + "'");306}307// Function aliases are separated using '/'. We use the first function308// name for the cluster info mapping and delegate all other aliases to309// this one.310SmallVector<StringRef, 4> Aliases;311AliasesStr.split(Aliases, '/');312bool FunctionFound = any_of(Aliases, [&](StringRef Alias) {313auto It = FunctionNameToDIFilename.find(Alias);314// No match if this function name is not found in this module.315if (It == FunctionNameToDIFilename.end())316return false;317// Return a match if debug-info-filename is not specified. Otherwise,318// check for equality.319return DIFilename.empty() || It->second == DIFilename;320});321if (!FunctionFound) {322// Skip the following profile by setting the profile iterator (FI) to323// the past-the-end element.324FI = ProgramPathAndClusterInfo.end();325continue;326}327for (size_t i = 1; i < Aliases.size(); ++i)328FuncAliasMap.try_emplace(Aliases[i], Aliases.front());329330// Prepare for parsing clusters of this function name.331// Start a new cluster map for this function name.332auto R = ProgramPathAndClusterInfo.try_emplace(Aliases.front());333// Report error when multiple profiles have been specified for the same334// function.335if (!R.second)336return createProfileParseError("duplicate profile for function '" +337Aliases.front() + "'");338FI = R.first;339CurrentCluster = 0;340FuncBBIDs.clear();341}342}343return Error::success();344}345346// Basic Block Sections can be enabled for a subset of machine basic blocks.347// This is done by passing a file containing names of functions for which basic348// block sections are desired. Additionally, machine basic block ids of the349// functions can also be specified for a finer granularity. Moreover, a cluster350// of basic blocks could be assigned to the same section.351// Optionally, a debug-info filename can be specified for each function to allow352// distinguishing internal-linkage functions of the same name.353// A file with basic block sections for all of function main and three blocks354// for function foo (of which 1 and 2 are placed in a cluster) looks like this:355// (Profile for function foo is only loaded when its debug-info filename356// matches 'path/to/foo_file.cc').357// ----------------------------358// list.txt:359// !main360// !foo M=path/to/foo_file.cc361// !!1 2362// !!4363Error BasicBlockSectionsProfileReader::ReadProfile() {364assert(MBuf);365366unsigned long long Version = 0;367StringRef FirstLine(*LineIt);368if (FirstLine.consume_front("v")) {369if (getAsUnsignedInteger(FirstLine, 10, Version)) {370return createProfileParseError(Twine("version number expected: '") +371FirstLine + "'");372}373if (Version > 1) {374return createProfileParseError(Twine("invalid profile version: ") +375Twine(Version));376}377++LineIt;378}379380switch (Version) {381case 0:382// TODO: Deprecate V0 once V1 is fully integrated downstream.383return ReadV0Profile();384case 1:385return ReadV1Profile();386default:387llvm_unreachable("Invalid profile version.");388}389}390391bool BasicBlockSectionsProfileReaderWrapperPass::doInitialization(Module &M) {392if (!BBSPR.MBuf)393return false;394// Get the function name to debug info filename mapping.395BBSPR.FunctionNameToDIFilename.clear();396for (const Function &F : M) {397SmallString<128> DIFilename;398if (F.isDeclaration())399continue;400DISubprogram *Subprogram = F.getSubprogram();401if (Subprogram) {402llvm::DICompileUnit *CU = Subprogram->getUnit();403if (CU)404DIFilename = sys::path::remove_leading_dotslash(CU->getFilename());405}406[[maybe_unused]] bool inserted =407BBSPR.FunctionNameToDIFilename.try_emplace(F.getName(), DIFilename)408.second;409assert(inserted);410}411if (auto Err = BBSPR.ReadProfile())412report_fatal_error(std::move(Err));413return false;414}415416AnalysisKey BasicBlockSectionsProfileReaderAnalysis::Key;417418BasicBlockSectionsProfileReader419BasicBlockSectionsProfileReaderAnalysis::run(Function &F,420FunctionAnalysisManager &AM) {421return BasicBlockSectionsProfileReader(TM->getBBSectionsFuncListBuf());422}423424bool BasicBlockSectionsProfileReaderWrapperPass::isFunctionHot(425StringRef FuncName) const {426return BBSPR.isFunctionHot(FuncName);427}428429std::pair<bool, SmallVector<BBClusterInfo>>430BasicBlockSectionsProfileReaderWrapperPass::getClusterInfoForFunction(431StringRef FuncName) const {432return BBSPR.getClusterInfoForFunction(FuncName);433}434435SmallVector<SmallVector<unsigned>>436BasicBlockSectionsProfileReaderWrapperPass::getClonePathsForFunction(437StringRef FuncName) const {438return BBSPR.getClonePathsForFunction(FuncName);439}440441BasicBlockSectionsProfileReader &442BasicBlockSectionsProfileReaderWrapperPass::getBBSPR() {443return BBSPR;444}445446ImmutablePass *llvm::createBasicBlockSectionsProfileReaderWrapperPass(447const MemoryBuffer *Buf) {448return new BasicBlockSectionsProfileReaderWrapperPass(Buf);449}450451452