1 //===- YAMLProfileWriter.cpp - serialize profiling data in YAML -*- C++ -*-===// 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 // 10 //===----------------------------------------------------------------------===// 11 12 #include "bolt/Profile/YAMLProfileWriter.h" 13 #include "bolt/Core/BinaryBasicBlock.h" 14 #include "bolt/Core/BinaryFunction.h" 15 #include "bolt/Profile/ProfileReaderBase.h" 16 #include "bolt/Profile/ProfileYAMLMapping.h" 17 #include "bolt/Rewrite/RewriteInstance.h" 18 #include "llvm/Support/FileSystem.h" 19 #include "llvm/Support/raw_ostream.h" 20 21 #undef DEBUG_TYPE 22 #define DEBUG_TYPE "bolt-prof" 23 24 namespace llvm { 25 namespace bolt { 26 27 namespace { 28 void convert(const BinaryFunction &BF, 29 yaml::bolt::BinaryFunctionProfile &YamlBF) { 30 const BinaryContext &BC = BF.getBinaryContext(); 31 32 const uint16_t LBRProfile = BF.getProfileFlags() & BinaryFunction::PF_LBR; 33 34 YamlBF.Name = BF.getPrintName(); 35 YamlBF.Id = BF.getFunctionNumber(); 36 YamlBF.Hash = BF.computeHash(/*UseDFS=*/true); 37 YamlBF.NumBasicBlocks = BF.size(); 38 YamlBF.ExecCount = BF.getKnownExecutionCount(); 39 40 for (const BinaryBasicBlock *BB : BF.dfs()) { 41 yaml::bolt::BinaryBasicBlockProfile YamlBB; 42 YamlBB.Index = BB->getLayoutIndex(); 43 YamlBB.NumInstructions = BB->getNumNonPseudos(); 44 45 if (!LBRProfile) { 46 YamlBB.EventCount = BB->getKnownExecutionCount(); 47 if (YamlBB.EventCount) 48 YamlBF.Blocks.emplace_back(YamlBB); 49 continue; 50 } 51 52 YamlBB.ExecCount = BB->getKnownExecutionCount(); 53 54 for (const MCInst &Instr : *BB) { 55 if (!BC.MIB->isCall(Instr) && !BC.MIB->isIndirectBranch(Instr)) 56 continue; 57 58 yaml::bolt::CallSiteInfo CSI; 59 auto Offset = BC.MIB->tryGetAnnotationAs<uint32_t>(Instr, "Offset"); 60 if (!Offset || Offset.get() < BB->getInputOffset()) 61 continue; 62 CSI.Offset = Offset.get() - BB->getInputOffset(); 63 64 if (BC.MIB->isIndirectCall(Instr) || BC.MIB->isIndirectBranch(Instr)) { 65 auto ICSP = BC.MIB->tryGetAnnotationAs<IndirectCallSiteProfile>( 66 Instr, "CallProfile"); 67 if (!ICSP) 68 continue; 69 for (const IndirectCallProfile &CSP : ICSP.get()) { 70 CSI.DestId = 0; // designated for unknown functions 71 CSI.EntryDiscriminator = 0; 72 if (CSP.Symbol) { 73 const BinaryFunction *Callee = BC.getFunctionForSymbol(CSP.Symbol); 74 if (Callee) { 75 CSI.DestId = Callee->getFunctionNumber(); 76 } 77 } 78 CSI.Count = CSP.Count; 79 CSI.Mispreds = CSP.Mispreds; 80 YamlBB.CallSites.push_back(CSI); 81 } 82 } else { // direct call or a tail call 83 uint64_t EntryID = 0; 84 const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Instr); 85 const BinaryFunction *const Callee = 86 BC.getFunctionForSymbol(CalleeSymbol, &EntryID); 87 if (Callee) { 88 CSI.DestId = Callee->getFunctionNumber(); 89 CSI.EntryDiscriminator = EntryID; 90 } 91 92 if (BC.MIB->getConditionalTailCall(Instr)) { 93 auto CTCCount = 94 BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCTakenCount"); 95 if (CTCCount) { 96 CSI.Count = *CTCCount; 97 auto CTCMispreds = 98 BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCMispredCount"); 99 if (CTCMispreds) 100 CSI.Mispreds = *CTCMispreds; 101 } 102 } else { 103 auto Count = BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "Count"); 104 if (Count) 105 CSI.Count = *Count; 106 } 107 108 if (CSI.Count) 109 YamlBB.CallSites.emplace_back(CSI); 110 } 111 } 112 113 std::sort(YamlBB.CallSites.begin(), YamlBB.CallSites.end()); 114 115 // Skip printing if there's no profile data for non-entry basic block. 116 // Include landing pads with non-zero execution count. 117 if (YamlBB.CallSites.empty() && !BB->isEntryPoint() && 118 !(BB->isLandingPad() && BB->getKnownExecutionCount() != 0)) { 119 uint64_t SuccessorExecCount = 0; 120 for (const BinaryBasicBlock::BinaryBranchInfo &BranchInfo : 121 BB->branch_info()) { 122 SuccessorExecCount += BranchInfo.Count; 123 } 124 if (!SuccessorExecCount) 125 continue; 126 } 127 128 auto BranchInfo = BB->branch_info_begin(); 129 for (const BinaryBasicBlock *Successor : BB->successors()) { 130 yaml::bolt::SuccessorInfo YamlSI; 131 YamlSI.Index = Successor->getLayoutIndex(); 132 YamlSI.Count = BranchInfo->Count; 133 YamlSI.Mispreds = BranchInfo->MispredictedCount; 134 135 YamlBB.Successors.emplace_back(YamlSI); 136 137 ++BranchInfo; 138 } 139 140 YamlBF.Blocks.emplace_back(YamlBB); 141 } 142 } 143 } // end anonymous namespace 144 145 std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) { 146 const BinaryContext &BC = RI.getBinaryContext(); 147 const auto &Functions = BC.getBinaryFunctions(); 148 149 std::error_code EC; 150 OS = std::make_unique<raw_fd_ostream>(Filename, EC, sys::fs::OF_None); 151 if (EC) { 152 errs() << "BOLT-WARNING: " << EC.message() << " : unable to open " 153 << Filename << " for output.\n"; 154 return EC; 155 } 156 157 yaml::bolt::BinaryProfile BP; 158 159 // Fill out the header info. 160 BP.Header.Version = 1; 161 BP.Header.FileName = std::string(BC.getFilename()); 162 Optional<StringRef> BuildID = BC.getFileBuildID(); 163 BP.Header.Id = BuildID ? std::string(*BuildID) : "<unknown>"; 164 BP.Header.Origin = std::string(RI.getProfileReader()->getReaderName()); 165 166 StringSet<> EventNames = RI.getProfileReader()->getEventNames(); 167 if (!EventNames.empty()) { 168 std::string Sep = ""; 169 for (const StringMapEntry<NoneType> &EventEntry : EventNames) { 170 BP.Header.EventNames += Sep + EventEntry.first().str(); 171 Sep = ","; 172 } 173 } 174 175 // Make sure the profile is consistent across all functions. 176 uint16_t ProfileFlags = BinaryFunction::PF_NONE; 177 for (const auto &BFI : Functions) { 178 const BinaryFunction &BF = BFI.second; 179 if (BF.hasProfile() && !BF.empty()) { 180 assert(BF.getProfileFlags() != BinaryFunction::PF_NONE); 181 if (ProfileFlags == BinaryFunction::PF_NONE) { 182 ProfileFlags = BF.getProfileFlags(); 183 } 184 assert(BF.getProfileFlags() == ProfileFlags && 185 "expected consistent profile flags across all functions"); 186 } 187 } 188 BP.Header.Flags = ProfileFlags; 189 190 // Add all function objects. 191 for (const auto &BFI : Functions) { 192 const BinaryFunction &BF = BFI.second; 193 if (BF.hasProfile()) { 194 if (!BF.hasValidProfile() && !RI.getProfileReader()->isTrustedSource()) 195 continue; 196 197 yaml::bolt::BinaryFunctionProfile YamlBF; 198 convert(BF, YamlBF); 199 BP.Functions.emplace_back(YamlBF); 200 } 201 } 202 203 // Write the profile. 204 yaml::Output Out(*OS, nullptr, 0); 205 Out << BP; 206 207 return std::error_code(); 208 } 209 210 } // namespace bolt 211 } // namespace llvm 212