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/BoltAddressTranslation.h" 13 #include "bolt/Profile/DataAggregator.h" 14 #include "bolt/Profile/ProfileReaderBase.h" 15 #include "bolt/Rewrite/RewriteInstance.h" 16 #include "llvm/Support/CommandLine.h" 17 #include "llvm/Support/FileSystem.h" 18 #include "llvm/Support/raw_ostream.h" 19 20 #undef DEBUG_TYPE 21 #define DEBUG_TYPE "bolt-prof" 22 23 namespace opts { 24 extern llvm::cl::opt<bool> ProfileUseDFS; 25 extern llvm::cl::opt<bool> ProfileUsePseudoProbes; 26 } // namespace opts 27 28 namespace llvm { 29 namespace bolt { 30 31 const BinaryFunction *YAMLProfileWriter::setCSIDestination( 32 const BinaryContext &BC, yaml::bolt::CallSiteInfo &CSI, 33 const MCSymbol *Symbol, const BoltAddressTranslation *BAT, 34 uint32_t Offset) { 35 CSI.DestId = 0; // designated for unknown functions 36 CSI.EntryDiscriminator = 0; 37 38 if (Symbol) { 39 uint64_t EntryID = 0; 40 if (const BinaryFunction *Callee = 41 BC.getFunctionForSymbol(Symbol, &EntryID)) { 42 if (BAT && BAT->isBATFunction(Callee->getAddress())) 43 std::tie(Callee, EntryID) = BAT->translateSymbol(BC, *Symbol, Offset); 44 else if (const BinaryBasicBlock *BB = 45 Callee->getBasicBlockContainingOffset(Offset)) 46 BC.getFunctionForSymbol(Callee->getSecondaryEntryPointSymbol(*BB), 47 &EntryID); 48 CSI.DestId = Callee->getFunctionNumber(); 49 CSI.EntryDiscriminator = EntryID; 50 return Callee; 51 } 52 } 53 return nullptr; 54 } 55 56 yaml::bolt::BinaryFunctionProfile 57 YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS, 58 const BoltAddressTranslation *BAT) { 59 yaml::bolt::BinaryFunctionProfile YamlBF; 60 const BinaryContext &BC = BF.getBinaryContext(); 61 const MCPseudoProbeDecoder *PseudoProbeDecoder = 62 opts::ProfileUsePseudoProbes ? BC.getPseudoProbeDecoder() : nullptr; 63 64 const uint16_t LBRProfile = BF.getProfileFlags() & BinaryFunction::PF_LBR; 65 66 // Prepare function and block hashes 67 BF.computeHash(UseDFS); 68 BF.computeBlockHashes(); 69 70 YamlBF.Name = DataAggregator::getLocationName(BF, BAT); 71 YamlBF.Id = BF.getFunctionNumber(); 72 YamlBF.Hash = BF.getHash(); 73 YamlBF.NumBasicBlocks = BF.size(); 74 YamlBF.ExecCount = BF.getKnownExecutionCount(); 75 if (PseudoProbeDecoder) { 76 if ((YamlBF.GUID = BF.getGUID())) { 77 const MCPseudoProbeFuncDesc *FuncDesc = 78 PseudoProbeDecoder->getFuncDescForGUID(YamlBF.GUID); 79 YamlBF.PseudoProbeDescHash = FuncDesc->FuncHash; 80 } 81 } 82 83 BinaryFunction::BasicBlockOrderType Order; 84 llvm::copy(UseDFS ? BF.dfs() : BF.getLayout().blocks(), 85 std::back_inserter(Order)); 86 87 const FunctionLayout Layout = BF.getLayout(); 88 Layout.updateLayoutIndices(Order); 89 90 for (const BinaryBasicBlock *BB : Order) { 91 yaml::bolt::BinaryBasicBlockProfile YamlBB; 92 YamlBB.Index = BB->getLayoutIndex(); 93 YamlBB.NumInstructions = BB->getNumNonPseudos(); 94 YamlBB.Hash = BB->getHash(); 95 96 if (!LBRProfile) { 97 YamlBB.EventCount = BB->getKnownExecutionCount(); 98 if (YamlBB.EventCount) 99 YamlBF.Blocks.emplace_back(YamlBB); 100 continue; 101 } 102 103 YamlBB.ExecCount = BB->getKnownExecutionCount(); 104 105 for (const MCInst &Instr : *BB) { 106 if (!BC.MIB->isCall(Instr) && !BC.MIB->isIndirectBranch(Instr)) 107 continue; 108 109 SmallVector<std::pair<StringRef, yaml::bolt::CallSiteInfo>> CSTargets; 110 yaml::bolt::CallSiteInfo CSI; 111 std::optional<uint32_t> Offset = BC.MIB->getOffset(Instr); 112 if (!Offset || *Offset < BB->getInputOffset()) 113 continue; 114 CSI.Offset = *Offset - BB->getInputOffset(); 115 116 if (BC.MIB->isIndirectCall(Instr) || BC.MIB->isIndirectBranch(Instr)) { 117 const auto ICSP = BC.MIB->tryGetAnnotationAs<IndirectCallSiteProfile>( 118 Instr, "CallProfile"); 119 if (!ICSP) 120 continue; 121 for (const IndirectCallProfile &CSP : ICSP.get()) { 122 StringRef TargetName = ""; 123 const BinaryFunction *Callee = 124 setCSIDestination(BC, CSI, CSP.Symbol, BAT); 125 if (Callee) 126 TargetName = Callee->getOneName(); 127 CSI.Count = CSP.Count; 128 CSI.Mispreds = CSP.Mispreds; 129 CSTargets.emplace_back(TargetName, CSI); 130 } 131 } else { // direct call or a tail call 132 StringRef TargetName = ""; 133 const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Instr); 134 const BinaryFunction *const Callee = 135 setCSIDestination(BC, CSI, CalleeSymbol, BAT); 136 if (Callee) 137 TargetName = Callee->getOneName(); 138 139 auto getAnnotationWithDefault = [&](const MCInst &Inst, StringRef Ann) { 140 return BC.MIB->getAnnotationWithDefault(Instr, Ann, 0ull); 141 }; 142 if (BC.MIB->getConditionalTailCall(Instr)) { 143 CSI.Count = getAnnotationWithDefault(Instr, "CTCTakenCount"); 144 CSI.Mispreds = getAnnotationWithDefault(Instr, "CTCMispredCount"); 145 } else { 146 CSI.Count = getAnnotationWithDefault(Instr, "Count"); 147 } 148 149 if (CSI.Count) 150 CSTargets.emplace_back(TargetName, CSI); 151 } 152 // Sort targets in a similar way to getBranchData, see Location::operator< 153 llvm::sort(CSTargets, [](const auto &RHS, const auto &LHS) { 154 if (RHS.first != LHS.first) 155 return RHS.first < LHS.first; 156 return RHS.second.Offset < LHS.second.Offset; 157 }); 158 for (auto &KV : CSTargets) 159 YamlBB.CallSites.push_back(KV.second); 160 } 161 162 // Skip printing if there's no profile data for non-entry basic block. 163 // Include landing pads with non-zero execution count. 164 if (YamlBB.CallSites.empty() && !BB->isEntryPoint() && 165 !(BB->isLandingPad() && BB->getKnownExecutionCount() != 0)) { 166 // Include blocks having successors or predecessors with positive counts. 167 uint64_t SuccessorExecCount = 0; 168 for (const BinaryBasicBlock::BinaryBranchInfo &BranchInfo : 169 BB->branch_info()) 170 SuccessorExecCount += BranchInfo.Count; 171 uint64_t PredecessorExecCount = 0; 172 for (auto Pred : BB->predecessors()) 173 PredecessorExecCount += Pred->getBranchInfo(*BB).Count; 174 if (!SuccessorExecCount && !PredecessorExecCount) 175 continue; 176 } 177 178 auto BranchInfo = BB->branch_info_begin(); 179 for (const BinaryBasicBlock *Successor : BB->successors()) { 180 yaml::bolt::SuccessorInfo YamlSI; 181 YamlSI.Index = Successor->getLayoutIndex(); 182 YamlSI.Count = BranchInfo->Count; 183 YamlSI.Mispreds = BranchInfo->MispredictedCount; 184 185 YamlBB.Successors.emplace_back(YamlSI); 186 187 ++BranchInfo; 188 } 189 190 if (PseudoProbeDecoder) { 191 const AddressProbesMap &ProbeMap = 192 PseudoProbeDecoder->getAddress2ProbesMap(); 193 const uint64_t FuncAddr = BF.getAddress(); 194 const std::pair<uint64_t, uint64_t> &BlockRange = 195 BB->getInputAddressRange(); 196 for (const MCDecodedPseudoProbe &Probe : ProbeMap.find( 197 FuncAddr + BlockRange.first, FuncAddr + BlockRange.second)) 198 YamlBB.PseudoProbes.emplace_back(yaml::bolt::PseudoProbeInfo{ 199 Probe.getGuid(), Probe.getIndex(), Probe.getType()}); 200 } 201 202 YamlBF.Blocks.emplace_back(YamlBB); 203 } 204 return YamlBF; 205 } 206 207 std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) { 208 const BinaryContext &BC = RI.getBinaryContext(); 209 const auto &Functions = BC.getBinaryFunctions(); 210 211 std::error_code EC; 212 OS = std::make_unique<raw_fd_ostream>(Filename, EC, sys::fs::OF_None); 213 if (EC) { 214 errs() << "BOLT-WARNING: " << EC.message() << " : unable to open " 215 << Filename << " for output.\n"; 216 return EC; 217 } 218 219 yaml::bolt::BinaryProfile BP; 220 221 // Fill out the header info. 222 BP.Header.Version = 1; 223 BP.Header.FileName = std::string(BC.getFilename()); 224 std::optional<StringRef> BuildID = BC.getFileBuildID(); 225 BP.Header.Id = BuildID ? std::string(*BuildID) : "<unknown>"; 226 BP.Header.Origin = std::string(RI.getProfileReader()->getReaderName()); 227 BP.Header.IsDFSOrder = opts::ProfileUseDFS; 228 BP.Header.HashFunction = HashFunction::Default; 229 230 StringSet<> EventNames = RI.getProfileReader()->getEventNames(); 231 if (!EventNames.empty()) { 232 std::string Sep; 233 for (const StringMapEntry<std::nullopt_t> &EventEntry : EventNames) { 234 BP.Header.EventNames += Sep + EventEntry.first().str(); 235 Sep = ","; 236 } 237 } 238 239 // Make sure the profile is consistent across all functions. 240 uint16_t ProfileFlags = BinaryFunction::PF_NONE; 241 for (const auto &BFI : Functions) { 242 const BinaryFunction &BF = BFI.second; 243 if (BF.hasProfile() && !BF.empty()) { 244 assert(BF.getProfileFlags() != BinaryFunction::PF_NONE); 245 if (ProfileFlags == BinaryFunction::PF_NONE) 246 ProfileFlags = BF.getProfileFlags(); 247 248 assert(BF.getProfileFlags() == ProfileFlags && 249 "expected consistent profile flags across all functions"); 250 } 251 } 252 BP.Header.Flags = ProfileFlags; 253 254 // Add all function objects. 255 for (const auto &BFI : Functions) { 256 const BinaryFunction &BF = BFI.second; 257 if (BF.hasProfile()) { 258 if (!BF.hasValidProfile() && !RI.getProfileReader()->isTrustedSource()) 259 continue; 260 261 BP.Functions.emplace_back(convert(BF, opts::ProfileUseDFS)); 262 } 263 } 264 265 // Write the profile. 266 yaml::Output Out(*OS, nullptr, 0); 267 Out << BP; 268 269 return std::error_code(); 270 } 271 272 } // namespace bolt 273 } // namespace llvm 274