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