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