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