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