1 //===- bolt/Passes/AsmDump.cpp - Dump BinaryFunction into assembly --------===// 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 // This file implements the AsmDumpPass class. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "bolt/Passes/AsmDump.h" 14 #include "llvm/CodeGen/AsmPrinter.h" 15 #include "llvm/MC/TargetRegistry.h" 16 #include "llvm/Support/FileSystem.h" 17 #include "llvm/Support/Path.h" 18 #include "llvm/Target/TargetMachine.h" 19 #include <unordered_set> 20 21 #define DEBUG_TYPE "asm-dump" 22 23 using namespace llvm; 24 25 namespace opts { 26 extern bool shouldPrint(const bolt::BinaryFunction &Function); 27 extern cl::OptionCategory BoltCategory; 28 extern cl::opt<unsigned> Verbosity; 29 30 cl::opt<std::string> AsmDump("asm-dump", 31 cl::desc("dump function into assembly"), 32 cl::value_desc("dump folder"), cl::ValueOptional, 33 cl::ZeroOrMore, cl::Hidden, cl::cat(BoltCategory)); 34 } // end namespace opts 35 36 namespace llvm { 37 namespace bolt { 38 39 void dumpCFI(const BinaryFunction &BF, const MCInst &Instr, AsmPrinter &MAP) { 40 const MCCFIInstruction *CFIInstr = BF.getCFIFor(Instr); 41 switch (CFIInstr->getOperation()) { 42 // Skip unsupported CFI instructions. 43 case MCCFIInstruction::OpRememberState: 44 case MCCFIInstruction::OpRestoreState: 45 if (opts::Verbosity >= 2) 46 errs() 47 << "BOLT-WARNING: AsmDump: skipping unsupported CFI instruction in " 48 << BF << ".\n"; 49 50 return; 51 52 default: 53 // Emit regular CFI instructions. 54 MAP.emitCFIInstruction(*CFIInstr); 55 } 56 } 57 58 void dumpJumpTableFdata(raw_ostream &OS, const BinaryFunction &BF, 59 const MCInst &Instr, const std::string &BranchLabel) { 60 StringRef FunctionName = BF.getOneName(); 61 const JumpTable *JT = BF.getJumpTable(Instr); 62 for (const uint64_t EntryOffset : JT->OffsetEntries) { 63 auto LI = JT->Labels.find(EntryOffset); 64 StringRef TargetName = LI->second->getName(); 65 const uint64_t Mispreds = JT->Counts[EntryOffset].Mispreds; 66 const uint64_t Count = JT->Counts[EntryOffset].Count; 67 OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# " 68 << "1 " << FunctionName << " #" << TargetName << "# " << Mispreds << " " 69 << Count << '\n'; 70 } 71 } 72 73 void dumpTailCallFdata(raw_ostream &OS, const BinaryFunction &BF, 74 const MCInst &Instr, const std::string &BranchLabel) { 75 const BinaryContext &BC = BF.getBinaryContext(); 76 StringRef FunctionName = BF.getOneName(); 77 auto CallFreq = BC.MIB->getAnnotationWithDefault<uint64_t>(Instr, "Count"); 78 const MCSymbol *Target = BC.MIB->getTargetSymbol(Instr); 79 const BinaryFunction *TargetBF = BC.getFunctionForSymbol(Target); 80 if (!TargetBF) 81 return; 82 OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# " 83 << "1 " << TargetBF->getPrintName() << " 0 " 84 << "0 " << CallFreq << '\n'; 85 } 86 87 void dumpTargetFunctionStub(raw_ostream &OS, const BinaryContext &BC, 88 const MCSymbol *CalleeSymb, 89 const BinarySection *&LastCS) { 90 const BinaryFunction *CalleeFunc = BC.getFunctionForSymbol(CalleeSymb); 91 if (!CalleeFunc || CalleeFunc->isPLTFunction()) 92 return; 93 94 if (CalleeFunc->getOriginSection() != LastCS) { 95 OS << ".section " << CalleeFunc->getOriginSectionName() << '\n'; 96 LastCS = CalleeFunc->getOriginSection(); 97 } 98 StringRef CalleeName = CalleeFunc->getOneName(); 99 OS << ".set \"" << CalleeName << "\", 0\n"; 100 } 101 102 void dumpJumpTableSymbols(raw_ostream &OS, const JumpTable *JT, AsmPrinter &MAP, 103 const BinarySection *&LastBS) { 104 if (&JT->getSection() != LastBS) { 105 OS << ".section " << JT->getSectionName() << '\n'; 106 LastBS = &JT->getSection(); 107 } 108 OS << "\"" << JT->getName() << "\":\n"; 109 for (MCSymbol *JTEntry : JT->Entries) 110 MAP.OutStreamer->emitSymbolValue(JTEntry, JT->OutputEntrySize); 111 OS << '\n'; 112 } 113 114 void dumpBinaryDataSymbols(raw_ostream &OS, const BinaryData *BD, 115 const BinarySection *&LastBS) { 116 if (BD->isJumpTable()) 117 return; 118 if (&BD->getSection() != LastBS) { 119 OS << ".section " << BD->getSectionName() << '\n'; 120 LastBS = &BD->getSection(); 121 } 122 OS << "\"" << BD->getName() << "\": "; 123 OS << '\n'; 124 } 125 126 void dumpFunction(const BinaryFunction &BF) { 127 const BinaryContext &BC = BF.getBinaryContext(); 128 if (!opts::shouldPrint(BF)) 129 return; 130 131 // Make sure the new directory exists, creating it if necessary. 132 if (!opts::AsmDump.empty()) { 133 if (std::error_code EC = sys::fs::create_directories(opts::AsmDump)) { 134 errs() << "BOLT-ERROR: could not create directory '" << opts::AsmDump 135 << "': " << EC.message() << '\n'; 136 exit(1); 137 } 138 } 139 140 std::string PrintName = BF.getPrintName(); 141 std::replace(PrintName.begin(), PrintName.end(), '/', '-'); 142 std::string Filename = 143 opts::AsmDump.empty() 144 ? (PrintName + ".s") 145 : (opts::AsmDump + sys::path::get_separator() + PrintName + ".s") 146 .str(); 147 outs() << "BOLT-INFO: Dumping function assembly to " << Filename << "\n"; 148 149 std::error_code EC; 150 raw_fd_ostream OS(Filename, EC, sys::fs::OF_None); 151 if (EC) { 152 errs() << "BOLT-ERROR: " << EC.message() << ", unable to open " << Filename 153 << " for output.\n"; 154 exit(1); 155 } 156 OS.SetUnbuffered(); 157 158 // Create local MC context to isolate the effect of ephemeral assembly 159 // emission. 160 BinaryContext::IndependentCodeEmitter MCEInstance = 161 BC.createIndependentMCCodeEmitter(); 162 MCContext *LocalCtx = MCEInstance.LocalCtx.get(); 163 std::unique_ptr<MCAsmBackend> MAB( 164 BC.TheTarget->createMCAsmBackend(*BC.STI, *BC.MRI, MCTargetOptions())); 165 int AsmPrinterVariant = BC.AsmInfo->getAssemblerDialect(); 166 MCInstPrinter *InstructionPrinter(BC.TheTarget->createMCInstPrinter( 167 *BC.TheTriple, AsmPrinterVariant, *BC.AsmInfo, *BC.MII, *BC.MRI)); 168 auto FOut = std::make_unique<formatted_raw_ostream>(OS); 169 FOut->SetUnbuffered(); 170 std::unique_ptr<MCStreamer> AsmStreamer( 171 createAsmStreamer(*LocalCtx, std::move(FOut), 172 /*isVerboseAsm=*/true, 173 /*useDwarfDirectory=*/false, InstructionPrinter, 174 std::move(MCEInstance.MCE), std::move(MAB), 175 /*ShowInst=*/false)); 176 AsmStreamer->initSections(true, *BC.STI); 177 std::unique_ptr<TargetMachine> TM(BC.TheTarget->createTargetMachine( 178 BC.TripleName, "", "", TargetOptions(), None)); 179 std::unique_ptr<AsmPrinter> MAP( 180 BC.TheTarget->createAsmPrinter(*TM, std::move(AsmStreamer))); 181 182 StringRef FunctionName = BF.getOneName(); 183 OS << " .globl " << FunctionName << '\n'; 184 OS << " .type " << FunctionName << ", %function\n"; 185 OS << FunctionName << ":\n"; 186 187 // FDATA for the entry point 188 if (uint64_t EntryExecCount = BF.getKnownExecutionCount()) 189 OS << "# FDATA: 0 [unknown] 0 " 190 << "1 " << FunctionName << " 0 " 191 << "0 " << EntryExecCount << '\n'; 192 193 // Binary data references from the function. 194 std::unordered_set<const BinaryData *> BDReferences; 195 // Function references from the function (to avoid constructing call graph). 196 std::unordered_set<const MCSymbol *> CallReferences; 197 198 MAP->OutStreamer->emitCFIStartProc(/*IsSimple=*/false); 199 for (BinaryBasicBlock *BB : BF.layout()) { 200 OS << BB->getName() << ": \n"; 201 202 const std::string BranchLabel = Twine(BB->getName(), "_br").str(); 203 const MCInst *LastInst = BB->getLastNonPseudoInstr(); 204 205 for (const MCInst &Instr : *BB) { 206 // Dump pseudo instructions (CFI) 207 if (BC.MIB->isPseudo(Instr)) { 208 if (BC.MIB->isCFI(Instr)) 209 dumpCFI(BF, Instr, *MAP.get()); 210 continue; 211 } 212 213 // Analyze symbol references (data, functions) from the instruction. 214 bool IsCall = BC.MIB->isCall(Instr); 215 for (unsigned I = 0, E = MCPlus::getNumPrimeOperands(Instr); I != E; 216 ++I) { 217 MCOperand Operand = Instr.getOperand(I); 218 if (Operand.isExpr() && 219 Operand.getExpr()->getKind() == MCExpr::SymbolRef) { 220 std::pair<const MCSymbol *, uint64_t> TSI = 221 BC.MIB->getTargetSymbolInfo(Operand.getExpr()); 222 const MCSymbol *Symbol = TSI.first; 223 if (IsCall) 224 CallReferences.insert(Symbol); 225 else if (const BinaryData *BD = 226 BC.getBinaryDataByName(Symbol->getName())) 227 BDReferences.insert(BD); 228 } 229 } 230 231 if (&Instr == LastInst && (BB->succ_size() || IsCall)) 232 OS << BranchLabel << ":\n"; 233 234 BC.InstPrinter->printInst(&Instr, 0, "", *BC.STI, OS); 235 OS << '\n'; 236 237 // Dump profile data in FDATA format (as parsed by link_fdata). 238 if (BC.MIB->getJumpTable(Instr)) 239 dumpJumpTableFdata(OS, BF, Instr, BranchLabel); 240 else if (BC.MIB->isTailCall(Instr)) 241 dumpTailCallFdata(OS, BF, Instr, BranchLabel); 242 } 243 244 // Dump profile data in FDATA format (as parsed by link_fdata). 245 for (const BinaryBasicBlock *Succ : BB->successors()) { 246 const BinaryBasicBlock::BinaryBranchInfo BI = BB->getBranchInfo(*Succ); 247 if (!BI.MispredictedCount && !BI.Count) 248 continue; 249 250 OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# " 251 << "1 " << FunctionName << " #" << Succ->getName() << "# " 252 << BI.MispredictedCount << " " << BI.Count << '\n'; 253 } 254 255 OS << '\n'; 256 } 257 MAP->OutStreamer->emitCFIEndProc(); 258 259 OS << ".size " << FunctionName << ", .-" << FunctionName << '\n'; 260 261 const BinarySection *LastSection = BF.getOriginSection(); 262 // Print stubs for all target functions. 263 for (const MCSymbol *CalleeSymb : CallReferences) 264 dumpTargetFunctionStub(OS, BC, CalleeSymb, LastSection); 265 266 OS << "# Jump tables\n"; 267 // Print all jump tables. 268 for (auto &JTI : BF.jumpTables()) 269 dumpJumpTableSymbols(OS, JTI.second, *MAP.get(), LastSection); 270 271 OS << "# BinaryData\n"; 272 // Print data references. 273 for (const BinaryData *BD : BDReferences) 274 dumpBinaryDataSymbols(OS, BD, LastSection); 275 } 276 277 void AsmDumpPass::runOnFunctions(BinaryContext &BC) { 278 for (const auto &BFIt : BC.getBinaryFunctions()) 279 dumpFunction(BFIt.second); 280 } 281 282 } // namespace bolt 283 } // namespace llvm 284