1 //===- bolt/Profile/YAMLProfileWriter.cpp - YAML profile serializer -------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "bolt/Profile/YAMLProfileWriter.h" 10 #include "bolt/Core/BinaryBasicBlock.h" 11 #include "bolt/Core/BinaryFunction.h" 12 #include "bolt/Profile/ProfileReaderBase.h" 13 #include "bolt/Profile/ProfileYAMLMapping.h" 14 #include "bolt/Rewrite/RewriteInstance.h" 15 #include "llvm/Support/FileSystem.h" 16 #include "llvm/Support/raw_ostream.h" 17 18 #undef DEBUG_TYPE 19 #define DEBUG_TYPE "bolt-prof" 20 21 namespace llvm { 22 namespace bolt { 23 24 namespace { 25 void convert(const BinaryFunction &BF, 26 yaml::bolt::BinaryFunctionProfile &YamlBF) { 27 const BinaryContext &BC = BF.getBinaryContext(); 28 29 const uint16_t LBRProfile = BF.getProfileFlags() & BinaryFunction::PF_LBR; 30 31 // Prepare function and block hashes 32 BF.computeHash(/*UseDFS=*/true); 33 BF.computeBlockHashes(); 34 35 YamlBF.Name = BF.getPrintName(); 36 YamlBF.Id = BF.getFunctionNumber(); 37 YamlBF.Hash = BF.getHash(); 38 YamlBF.NumBasicBlocks = BF.size(); 39 YamlBF.ExecCount = BF.getKnownExecutionCount(); 40 41 for (const BinaryBasicBlock *BB : BF.dfs()) { 42 yaml::bolt::BinaryBasicBlockProfile YamlBB; 43 YamlBB.Index = BB->getLayoutIndex(); 44 YamlBB.NumInstructions = BB->getNumNonPseudos(); 45 YamlBB.Hash = BB->getHash(); 46 47 if (!LBRProfile) { 48 YamlBB.EventCount = BB->getKnownExecutionCount(); 49 if (YamlBB.EventCount) 50 YamlBF.Blocks.emplace_back(YamlBB); 51 continue; 52 } 53 54 YamlBB.ExecCount = BB->getKnownExecutionCount(); 55 56 for (const MCInst &Instr : *BB) { 57 if (!BC.MIB->isCall(Instr) && !BC.MIB->isIndirectBranch(Instr)) 58 continue; 59 60 SmallVector<std::pair<StringRef, yaml::bolt::CallSiteInfo>> CSTargets; 61 yaml::bolt::CallSiteInfo CSI; 62 std::optional<uint32_t> Offset = BC.MIB->getOffset(Instr); 63 if (!Offset || *Offset < BB->getInputOffset()) 64 continue; 65 CSI.Offset = *Offset - BB->getInputOffset(); 66 67 if (BC.MIB->isIndirectCall(Instr) || BC.MIB->isIndirectBranch(Instr)) { 68 const auto ICSP = BC.MIB->tryGetAnnotationAs<IndirectCallSiteProfile>( 69 Instr, "CallProfile"); 70 if (!ICSP) 71 continue; 72 for (const IndirectCallProfile &CSP : ICSP.get()) { 73 StringRef TargetName = ""; 74 CSI.DestId = 0; // designated for unknown functions 75 CSI.EntryDiscriminator = 0; 76 if (CSP.Symbol) { 77 const BinaryFunction *Callee = BC.getFunctionForSymbol(CSP.Symbol); 78 if (Callee) { 79 CSI.DestId = Callee->getFunctionNumber(); 80 TargetName = Callee->getOneName(); 81 } 82 } 83 CSI.Count = CSP.Count; 84 CSI.Mispreds = CSP.Mispreds; 85 CSTargets.emplace_back(TargetName, CSI); 86 } 87 } else { // direct call or a tail call 88 uint64_t EntryID = 0; 89 CSI.DestId = 0; 90 StringRef TargetName = ""; 91 const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Instr); 92 const BinaryFunction *const Callee = 93 BC.getFunctionForSymbol(CalleeSymbol, &EntryID); 94 if (Callee) { 95 CSI.DestId = Callee->getFunctionNumber(); 96 CSI.EntryDiscriminator = EntryID; 97 TargetName = Callee->getOneName(); 98 } 99 100 if (BC.MIB->getConditionalTailCall(Instr)) { 101 auto CTCCount = 102 BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCTakenCount"); 103 if (CTCCount) { 104 CSI.Count = *CTCCount; 105 auto CTCMispreds = 106 BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCMispredCount"); 107 if (CTCMispreds) 108 CSI.Mispreds = *CTCMispreds; 109 } 110 } else { 111 auto Count = BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "Count"); 112 if (Count) 113 CSI.Count = *Count; 114 } 115 116 if (CSI.Count) 117 CSTargets.emplace_back(TargetName, CSI); 118 } 119 // Sort targets in a similar way to getBranchData, see Location::operator< 120 llvm::sort(CSTargets, [](const auto &RHS, const auto &LHS) { 121 if (RHS.first != LHS.first) 122 return RHS.first < LHS.first; 123 return RHS.second.Offset < LHS.second.Offset; 124 }); 125 for (auto &KV : CSTargets) 126 YamlBB.CallSites.push_back(KV.second); 127 } 128 129 // Skip printing if there's no profile data for non-entry basic block. 130 // Include landing pads with non-zero execution count. 131 if (YamlBB.CallSites.empty() && !BB->isEntryPoint() && 132 !(BB->isLandingPad() && BB->getKnownExecutionCount() != 0)) { 133 // Include blocks having successors or predecessors with positive counts. 134 uint64_t SuccessorExecCount = 0; 135 for (const BinaryBasicBlock::BinaryBranchInfo &BranchInfo : 136 BB->branch_info()) 137 SuccessorExecCount += BranchInfo.Count; 138 uint64_t PredecessorExecCount = 0; 139 for (auto Pred : BB->predecessors()) 140 PredecessorExecCount += Pred->getBranchInfo(*BB).Count; 141 if (!SuccessorExecCount && !PredecessorExecCount) 142 continue; 143 } 144 145 auto BranchInfo = BB->branch_info_begin(); 146 for (const BinaryBasicBlock *Successor : BB->successors()) { 147 yaml::bolt::SuccessorInfo YamlSI; 148 YamlSI.Index = Successor->getLayoutIndex(); 149 YamlSI.Count = BranchInfo->Count; 150 YamlSI.Mispreds = BranchInfo->MispredictedCount; 151 152 YamlBB.Successors.emplace_back(YamlSI); 153 154 ++BranchInfo; 155 } 156 157 YamlBF.Blocks.emplace_back(YamlBB); 158 } 159 } 160 } // end anonymous namespace 161 162 std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) { 163 const BinaryContext &BC = RI.getBinaryContext(); 164 const auto &Functions = BC.getBinaryFunctions(); 165 166 std::error_code EC; 167 OS = std::make_unique<raw_fd_ostream>(Filename, EC, sys::fs::OF_None); 168 if (EC) { 169 errs() << "BOLT-WARNING: " << EC.message() << " : unable to open " 170 << Filename << " for output.\n"; 171 return EC; 172 } 173 174 yaml::bolt::BinaryProfile BP; 175 176 // Fill out the header info. 177 BP.Header.Version = 1; 178 BP.Header.FileName = std::string(BC.getFilename()); 179 std::optional<StringRef> BuildID = BC.getFileBuildID(); 180 BP.Header.Id = BuildID ? std::string(*BuildID) : "<unknown>"; 181 BP.Header.Origin = std::string(RI.getProfileReader()->getReaderName()); 182 183 StringSet<> EventNames = RI.getProfileReader()->getEventNames(); 184 if (!EventNames.empty()) { 185 std::string Sep; 186 for (const StringMapEntry<std::nullopt_t> &EventEntry : EventNames) { 187 BP.Header.EventNames += Sep + EventEntry.first().str(); 188 Sep = ","; 189 } 190 } 191 192 // Make sure the profile is consistent across all functions. 193 uint16_t ProfileFlags = BinaryFunction::PF_NONE; 194 for (const auto &BFI : Functions) { 195 const BinaryFunction &BF = BFI.second; 196 if (BF.hasProfile() && !BF.empty()) { 197 assert(BF.getProfileFlags() != BinaryFunction::PF_NONE); 198 if (ProfileFlags == BinaryFunction::PF_NONE) 199 ProfileFlags = BF.getProfileFlags(); 200 201 assert(BF.getProfileFlags() == ProfileFlags && 202 "expected consistent profile flags across all functions"); 203 } 204 } 205 BP.Header.Flags = ProfileFlags; 206 207 // Add all function objects. 208 for (const auto &BFI : Functions) { 209 const BinaryFunction &BF = BFI.second; 210 if (BF.hasProfile()) { 211 if (!BF.hasValidProfile() && !RI.getProfileReader()->isTrustedSource()) 212 continue; 213 214 yaml::bolt::BinaryFunctionProfile YamlBF; 215 convert(BF, YamlBF); 216 BP.Functions.emplace_back(YamlBF); 217 } 218 } 219 220 // Write the profile. 221 yaml::Output Out(*OS, nullptr, 0); 222 Out << BP; 223 224 return std::error_code(); 225 } 226 227 } // namespace bolt 228 } // namespace llvm 229