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