//===- llvm/CodeGen/BasicBlockMatchingAndInference.cpp ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // In Propeller's profile, we have already read the hash values of basic blocks, // as well as the weights of basic blocks and edges in the CFG. In this file, // we first match the basic blocks in the profile with those in the current // MachineFunction using the basic block hash, thereby obtaining the weights of // some basic blocks and edges. Subsequently, we infer the weights of all basic // blocks using an inference algorithm. // // TODO: Integrate part of the code in this file with BOLT's implementation into // the LLVM infrastructure, enabling both BOLT and Propeller to reuse it. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/BasicBlockMatchingAndInference.h" #include "CodeGenOptions.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseSet.h" #include "llvm/CodeGen/BasicBlockSectionsProfileReader.h" #include "llvm/CodeGen/MachineBlockHashInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/InitializePasses.h" #include "llvm/Support/Debug.h" using namespace llvm; #define DEBUG_TYPE "machine-block-match-infer" /// The object is used to identify and match basic blocks given their hashes. class StaleMatcher { public: /// Initialize stale matcher. void init(const std::vector &Blocks, const std::vector &Hashes) { assert(Blocks.size() == Hashes.size() && "incorrect matcher initialization"); for (size_t I = 0; I < Blocks.size(); I++) { MachineBasicBlock *Block = Blocks[I]; uint16_t OpHash = Hashes[I].getOpcodeHash(); OpHashToBlocks[OpHash].push_back(std::make_pair(Hashes[I], Block)); } } /// Find the most similar block for a given hash. MachineBasicBlock *matchBlock(BlendedBlockHash BlendedHash) const { auto BlockIt = OpHashToBlocks.find(BlendedHash.getOpcodeHash()); if (BlockIt == OpHashToBlocks.end()) { return nullptr; } MachineBasicBlock *BestBlock = nullptr; uint64_t BestDist = std::numeric_limits::max(); for (auto It : BlockIt->second) { MachineBasicBlock *Block = It.second; BlendedBlockHash Hash = It.first; uint64_t Dist = Hash.distance(BlendedHash); if (BestBlock == nullptr || Dist < BestDist) { BestDist = Dist; BestBlock = Block; } } return BestBlock; } private: using HashBlockPairType = std::pair; DenseMap> OpHashToBlocks; }; INITIALIZE_PASS_BEGIN(BasicBlockMatchingAndInference, "machine-block-match-infer", "Machine Block Matching and Inference Analysis", true, true) INITIALIZE_PASS_DEPENDENCY(MachineBlockHashInfo) INITIALIZE_PASS_DEPENDENCY(BasicBlockSectionsProfileReaderWrapperPass) INITIALIZE_PASS_END(BasicBlockMatchingAndInference, "machine-block-match-infer", "Machine Block Matching and Inference Analysis", true, true) char BasicBlockMatchingAndInference::ID = 0; BasicBlockMatchingAndInference::BasicBlockMatchingAndInference() : MachineFunctionPass(ID) {} void BasicBlockMatchingAndInference::getAnalysisUsage(AnalysisUsage &AU) const { AU.addRequired(); AU.addRequired(); AU.setPreservesAll(); MachineFunctionPass::getAnalysisUsage(AU); } std::optional BasicBlockMatchingAndInference::getWeightInfo(StringRef FuncName) const { auto It = ProgramWeightInfo.find(FuncName); if (It == ProgramWeightInfo.end()) { return std::nullopt; } return It->second; } BasicBlockMatchingAndInference::WeightInfo BasicBlockMatchingAndInference::initWeightInfoByMatching(MachineFunction &MF) { std::vector Blocks; std::vector Hashes; auto BSPR = &getAnalysis(); auto MBHI = &getAnalysis(); for (auto &Block : MF) { Blocks.push_back(&Block); Hashes.push_back(BlendedBlockHash(MBHI->getMBBHash(Block))); } StaleMatcher Matcher; Matcher.init(Blocks, Hashes); BasicBlockMatchingAndInference::WeightInfo MatchWeight; const CFGProfile *CFG = BSPR->getFunctionCFGProfile(MF.getName()); if (CFG == nullptr) return MatchWeight; for (auto &BlockCount : CFG->NodeCounts) { if (CFG->BBHashes.count(BlockCount.first.BaseID)) { auto Hash = CFG->BBHashes.lookup(BlockCount.first.BaseID); MachineBasicBlock *Block = Matcher.matchBlock(BlendedBlockHash(Hash)); // When a basic block has clone copies, sum their counts. if (Block != nullptr) MatchWeight.BlockWeights[Block] += BlockCount.second; } } for (auto &PredItem : CFG->EdgeCounts) { auto PredID = PredItem.first.BaseID; if (!CFG->BBHashes.count(PredID)) continue; auto PredHash = CFG->BBHashes.lookup(PredID); MachineBasicBlock *PredBlock = Matcher.matchBlock(BlendedBlockHash(PredHash)); if (PredBlock == nullptr) continue; for (auto &SuccItem : PredItem.second) { auto SuccID = SuccItem.first.BaseID; auto EdgeWeight = SuccItem.second; if (CFG->BBHashes.count(SuccID)) { auto SuccHash = CFG->BBHashes.lookup(SuccID); MachineBasicBlock *SuccBlock = Matcher.matchBlock(BlendedBlockHash(SuccHash)); // When an edge has clone copies, sum their counts. if (SuccBlock != nullptr) MatchWeight.EdgeWeights[std::make_pair(PredBlock, SuccBlock)] += EdgeWeight; } } } return MatchWeight; } BasicBlockMatchingAndInference::WeightInfo BasicBlockMatchingAndInference::initWeightInfoByBBID(MachineFunction &MF, const CFGProfile &CFG) { // Match blocks by their full UniqueBBID (including CloneID). If a cloned // block in the profile does not exist in MF (e.g., because its cloning path // was rejected), fall back to the original block so its counts are // attributed to the original block. DenseMap BBIDToMBB; for (auto &MBB : MF) BBIDToMBB[*MBB.getBBID()] = &MBB; auto FindMBB = [&](const UniqueBBID &BBID) { if (MachineBasicBlock *MBB = BBIDToMBB.lookup(BBID)) return MBB; return BBIDToMBB.lookup(UniqueBBID{BBID.BaseID, 0}); }; // A basic block is matched only if it exists in CFG.NodeCounts and all of its // edges in the CFG profile are also edges in the MIR. The profile may omit // edges without samples, so MIR edges missing from the profile are allowed. // Both ends of a profile edge that is not in the MIR are kept as unknown. DenseSet MismatchedMBBs; for (const auto &[PredID, Succs] : CFG.EdgeCounts) { MachineBasicBlock *PredMBB = FindMBB(PredID); for (const auto &[SuccID, EdgeCount] : Succs) { MachineBasicBlock *SuccMBB = FindMBB(SuccID); if (PredMBB && SuccMBB && PredMBB->isSuccessor(SuccMBB)) continue; if (PredMBB) MismatchedMBBs.insert(PredMBB); if (SuccMBB) MismatchedMBBs.insert(SuccMBB); } } DenseSet ProfiledMBBs; for (const auto &[BBID, BlockCount] : CFG.NodeCounts) if (MachineBasicBlock *MBB = FindMBB(BBID)) ProfiledMBBs.insert(MBB); DenseSet MatchedMBBs; for (auto &MBB : MF) { if (!ProfiledMBBs.contains(&MBB)) continue; if (MismatchedMBBs.contains(&MBB)) LLVM_DEBUG(dbgs() << MF.getName() << ": bb." << MBB.getNumber() << " has profile edges that are not in the CFG\n"); else MatchedMBBs.insert(&MBB); } BasicBlockMatchingAndInference::WeightInfo MatchWeight; for (const auto &[BBID, BlockCount] : CFG.NodeCounts) { MachineBasicBlock *MBB = FindMBB(BBID); if (MBB && MatchedMBBs.count(MBB)) MatchWeight.BlockWeights[MBB] += BlockCount; } for (const auto &[PredID, Succs] : CFG.EdgeCounts) { MachineBasicBlock *PredMBB = FindMBB(PredID); if (!PredMBB || !MatchedMBBs.count(PredMBB)) continue; for (const auto &[SuccID, EdgeCount] : Succs) { MachineBasicBlock *SuccMBB = FindMBB(SuccID); if (!SuccMBB || !MatchedMBBs.count(SuccMBB)) continue; MatchWeight.EdgeWeights[std::make_pair(PredMBB, SuccMBB)] += EdgeCount; } } return MatchWeight; } void BasicBlockMatchingAndInference::generateWeightInfoByInference( MachineFunction &MF, BasicBlockMatchingAndInference::WeightInfo &MatchWeight) { BlockEdgeMap Successors; for (auto &Block : MF) { for (auto *Succ : Block.successors()) Successors[&Block].push_back(Succ); } SampleProfileInference SPI( MF, Successors, MatchWeight.BlockWeights, MatchWeight.EdgeWeights); BlockWeightMap BlockWeights; EdgeWeightMap EdgeWeights; SPI.apply(BlockWeights, EdgeWeights); ProgramWeightInfo.try_emplace( MF.getName(), BasicBlockMatchingAndInference::WeightInfo{ std::move(BlockWeights), std::move(EdgeWeights)}); } bool BasicBlockMatchingAndInference::runOnMachineFunction(MachineFunction &MF) { if (MF.empty()) return false; auto *BSPR = &getAnalysis(); const CFGProfile *CFG = BSPR->getFunctionCFGProfile(MF.getName()); if (CFG == nullptr || (CFG->NodeCounts.empty() && CFG->EdgeCounts.empty())) return false; const CodeGenOptions &Opts = getCodeGenOptions(MF); BasicBlockMatchingAndInference::WeightInfo MatchWeight; if (Opts.propeller_match_bbid) { MatchWeight = initWeightInfoByBBID(MF, *CFG); } else { MatchWeight = initWeightInfoByMatching(MF); // If the ratio of the number of MBBs in matching to the total number of // MBBs in the function is less than the threshold value, the processing // should be abandoned. if (static_cast(MatchWeight.BlockWeights.size()) / MF.size() < Opts.propeller_infer_threshold) { return false; } } generateWeightInfoByInference(MF, MatchWeight); return false; } MachineFunctionPass *llvm::createBasicBlockMatchingAndInferencePass() { return new BasicBlockMatchingAndInference(); }