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::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 BF.getBinaryContext().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 dumpTargetFunctionStub(raw_ostream &OS, const BinaryContext &BC, 59 const MCSymbol *CalleeSymb, 60 const BinarySection *&LastCS) { 61 const BinaryFunction *CalleeFunc = BC.getFunctionForSymbol(CalleeSymb); 62 if (!CalleeFunc || CalleeFunc->isPLTFunction()) 63 return; 64 65 if (CalleeFunc->getOriginSection() != LastCS) { 66 OS << ".section " << CalleeFunc->getOriginSectionName() << '\n'; 67 LastCS = CalleeFunc->getOriginSection(); 68 } 69 StringRef CalleeName = CalleeFunc->getOneName(); 70 OS << ".set \"" << CalleeName << "\", 0\n"; 71 } 72 73 void dumpJumpTableSymbols(raw_ostream &OS, const JumpTable *JT, AsmPrinter &MAP, 74 const BinarySection *&LastBS) { 75 if (&JT->getSection() != LastBS) { 76 OS << ".section " << JT->getSectionName() << '\n'; 77 LastBS = &JT->getSection(); 78 } 79 OS << "\"" << JT->getName() << "\":\n"; 80 for (MCSymbol *JTEntry : JT->Entries) 81 MAP.OutStreamer->emitSymbolValue(JTEntry, JT->OutputEntrySize); 82 OS << '\n'; 83 } 84 85 void dumpBinaryDataSymbols(raw_ostream &OS, const BinaryData *BD, 86 const BinarySection *&LastBS) { 87 if (BD->isJumpTable()) 88 return; 89 if (&BD->getSection() != LastBS) { 90 OS << ".section " << BD->getSectionName() << '\n'; 91 LastBS = &BD->getSection(); 92 } 93 OS << "\"" << BD->getName() << "\": "; 94 OS << '\n'; 95 } 96 97 void dumpFunction(const BinaryFunction &BF) { 98 const BinaryContext &BC = BF.getBinaryContext(); 99 if (!opts::shouldPrint(BF)) 100 return; 101 102 // Make sure the new directory exists, creating it if necessary. 103 if (!opts::AsmDump.empty()) { 104 if (std::error_code EC = sys::fs::create_directories(opts::AsmDump)) { 105 BC.errs() << "BOLT-ERROR: could not create directory '" << opts::AsmDump 106 << "': " << EC.message() << '\n'; 107 return; 108 } 109 } 110 111 std::string PrintName = BF.getPrintName(); 112 std::replace(PrintName.begin(), PrintName.end(), '/', '-'); 113 std::string Filename = 114 opts::AsmDump.empty() 115 ? (PrintName + ".s") 116 : (opts::AsmDump + sys::path::get_separator() + PrintName + ".s") 117 .str(); 118 BC.outs() << "BOLT-INFO: Dumping function assembly to " << Filename << "\n"; 119 120 std::error_code EC; 121 raw_fd_ostream OS(Filename, EC, sys::fs::OF_None); 122 if (EC) { 123 BC.errs() << "BOLT-ERROR: " << EC.message() << ", unable to open " 124 << Filename << " for output.\n"; 125 return; 126 } 127 OS.SetUnbuffered(); 128 129 // Create local MC context to isolate the effect of ephemeral assembly 130 // emission. 131 BinaryContext::IndependentCodeEmitter MCEInstance = 132 BC.createIndependentMCCodeEmitter(); 133 MCContext *LocalCtx = MCEInstance.LocalCtx.get(); 134 std::unique_ptr<MCAsmBackend> MAB( 135 BC.TheTarget->createMCAsmBackend(*BC.STI, *BC.MRI, MCTargetOptions())); 136 int AsmPrinterVariant = BC.AsmInfo->getAssemblerDialect(); 137 MCInstPrinter *InstructionPrinter(BC.TheTarget->createMCInstPrinter( 138 *BC.TheTriple, AsmPrinterVariant, *BC.AsmInfo, *BC.MII, *BC.MRI)); 139 auto FOut = std::make_unique<formatted_raw_ostream>(OS); 140 FOut->SetUnbuffered(); 141 std::unique_ptr<MCStreamer> AsmStreamer( 142 createAsmStreamer(*LocalCtx, std::move(FOut), InstructionPrinter, 143 std::move(MCEInstance.MCE), std::move(MAB))); 144 AsmStreamer->initSections(true, *BC.STI); 145 std::unique_ptr<TargetMachine> TM(BC.TheTarget->createTargetMachine( 146 BC.TripleName, "", "", TargetOptions(), std::nullopt)); 147 std::unique_ptr<AsmPrinter> MAP( 148 BC.TheTarget->createAsmPrinter(*TM, std::move(AsmStreamer))); 149 150 StringRef FunctionName = BF.getOneName(); 151 OS << " .globl " << FunctionName << '\n'; 152 OS << " .type " << FunctionName << ", %function\n"; 153 OS << FunctionName << ":\n"; 154 155 // FDATA for the entry point 156 if (uint64_t EntryExecCount = BF.getKnownExecutionCount()) 157 OS << "# FDATA: 0 [unknown] 0 " 158 << "1 " << FunctionName << " 0 " 159 << "0 " << EntryExecCount << '\n'; 160 161 // Binary data references from the function. 162 std::unordered_set<const BinaryData *> BDReferences; 163 // Function references from the function (to avoid constructing call graph). 164 std::unordered_set<const MCSymbol *> CallReferences; 165 166 MAP->OutStreamer->emitCFIStartProc(/*IsSimple=*/false); 167 for (const BinaryBasicBlock *BB : BF.getLayout().blocks()) { 168 OS << BB->getName() << ": \n"; 169 170 const std::string BranchLabel = Twine(BB->getName(), "_br").str(); 171 const MCInst *LastInst = BB->getLastNonPseudoInstr(); 172 173 for (const MCInst &Instr : *BB) { 174 // Dump pseudo instructions (CFI) 175 if (BC.MIB->isPseudo(Instr)) { 176 if (BC.MIB->isCFI(Instr)) 177 dumpCFI(BF, Instr, *MAP.get()); 178 continue; 179 } 180 181 // Analyze symbol references (data, functions) from the instruction. 182 bool IsCall = BC.MIB->isCall(Instr); 183 for (const MCOperand &Operand : MCPlus::primeOperands(Instr)) { 184 if (Operand.isExpr() && 185 Operand.getExpr()->getKind() == MCExpr::SymbolRef) { 186 std::pair<const MCSymbol *, uint64_t> TSI = 187 BC.MIB->getTargetSymbolInfo(Operand.getExpr()); 188 const MCSymbol *Symbol = TSI.first; 189 if (IsCall) 190 CallReferences.insert(Symbol); 191 else if (const BinaryData *BD = 192 BC.getBinaryDataByName(Symbol->getName())) 193 BDReferences.insert(BD); 194 } 195 } 196 197 if (&Instr == LastInst && (BB->succ_size() || IsCall)) 198 OS << BranchLabel << ":\n"; 199 200 BC.InstPrinter->printInst(&Instr, 0, "", *BC.STI, OS); 201 OS << '\n'; 202 } 203 204 // Dump profile data in FDATA format (as parsed by link_fdata). 205 for (const BinaryBasicBlock *Succ : BB->successors()) { 206 const BinaryBasicBlock::BinaryBranchInfo BI = BB->getBranchInfo(*Succ); 207 if (!BI.MispredictedCount && !BI.Count) 208 continue; 209 210 OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# " 211 << "1 " << FunctionName << " #" << Succ->getName() << "# " 212 << BI.MispredictedCount << " " << BI.Count << '\n'; 213 } 214 215 OS << '\n'; 216 } 217 MAP->OutStreamer->emitCFIEndProc(); 218 219 OS << ".size " << FunctionName << ", .-" << FunctionName << '\n'; 220 221 const BinarySection *LastSection = BF.getOriginSection(); 222 // Print stubs for all target functions. 223 for (const MCSymbol *CalleeSymb : CallReferences) 224 dumpTargetFunctionStub(OS, BC, CalleeSymb, LastSection); 225 226 OS << "# Jump tables\n"; 227 // Print all jump tables. 228 for (auto &JTI : BF.jumpTables()) 229 dumpJumpTableSymbols(OS, JTI.second, *MAP.get(), LastSection); 230 231 OS << "# BinaryData\n"; 232 // Print data references. 233 for (const BinaryData *BD : BDReferences) 234 dumpBinaryDataSymbols(OS, BD, LastSection); 235 } 236 237 Error AsmDumpPass::runOnFunctions(BinaryContext &BC) { 238 for (const auto &BFIt : BC.getBinaryFunctions()) 239 dumpFunction(BFIt.second); 240 return Error::success(); 241 } 242 243 } // namespace bolt 244 } // namespace llvm 245