246 lines · cpp
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-exception6//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 23using namespace llvm;24 25namespace opts {26extern bool shouldPrint(const bolt::BinaryFunction &Function);27extern cl::OptionCategory BoltCategory;28extern cl::opt<unsigned> Verbosity;29 30cl::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 opts35 36namespace llvm {37namespace bolt {38 39void 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 58void 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 73void 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 85void 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 97void 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::AsmDump106 << "': " << EC.message() << '\n';107 return;108 }109 }110 111 std::string PrintName = BF.getPrintName();112 llvm::replace(PrintName, '/', '-');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 assembly130 // 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 std::unique_ptr<MCInstPrinter> InstructionPrinter(138 BC.TheTarget->createMCInstPrinter(*BC.TheTriple, AsmPrinterVariant,139 *BC.AsmInfo, *BC.MII, *BC.MRI));140 auto FOut = std::make_unique<formatted_raw_ostream>(OS);141 FOut->SetUnbuffered();142 std::unique_ptr<MCStreamer> AsmStreamer(createAsmStreamer(143 *LocalCtx, std::move(FOut), std::move(InstructionPrinter),144 std::move(MCEInstance.MCE), std::move(MAB)));145 AsmStreamer->initSections(true, *BC.STI);146 std::unique_ptr<TargetMachine> TM(BC.TheTarget->createTargetMachine(147 *BC.TheTriple, "", "", TargetOptions(), std::nullopt));148 std::unique_ptr<AsmPrinter> MAP(149 BC.TheTarget->createAsmPrinter(*TM, std::move(AsmStreamer)));150 151 StringRef FunctionName = BF.getOneName();152 OS << " .globl " << FunctionName << '\n';153 OS << " .type " << FunctionName << ", %function\n";154 OS << FunctionName << ":\n";155 156 // FDATA for the entry point157 if (uint64_t EntryExecCount = BF.getKnownExecutionCount())158 OS << "# FDATA: 0 [unknown] 0 "159 << "1 " << FunctionName << " 0 "160 << "0 " << EntryExecCount << '\n';161 162 // Binary data references from the function.163 std::unordered_set<const BinaryData *> BDReferences;164 // Function references from the function (to avoid constructing call graph).165 std::unordered_set<const MCSymbol *> CallReferences;166 167 MAP->OutStreamer->emitCFIStartProc(/*IsSimple=*/false);168 for (const BinaryBasicBlock *BB : BF.getLayout().blocks()) {169 OS << BB->getName() << ": \n";170 171 const std::string BranchLabel = Twine(BB->getName(), "_br").str();172 const MCInst *LastInst = BB->getLastNonPseudoInstr();173 174 for (const MCInst &Instr : *BB) {175 // Dump pseudo instructions (CFI)176 if (BC.MIB->isPseudo(Instr)) {177 if (BC.MIB->isCFI(Instr))178 dumpCFI(BF, Instr, *MAP);179 continue;180 }181 182 // Analyze symbol references (data, functions) from the instruction.183 bool IsCall = BC.MIB->isCall(Instr);184 for (const MCOperand &Operand : MCPlus::primeOperands(Instr)) {185 if (Operand.isExpr() &&186 Operand.getExpr()->getKind() == MCExpr::SymbolRef) {187 std::pair<const MCSymbol *, uint64_t> TSI =188 BC.MIB->getTargetSymbolInfo(Operand.getExpr());189 const MCSymbol *Symbol = TSI.first;190 if (IsCall)191 CallReferences.insert(Symbol);192 else if (const BinaryData *BD =193 BC.getBinaryDataByName(Symbol->getName()))194 BDReferences.insert(BD);195 }196 }197 198 if (&Instr == LastInst && (BB->succ_size() || IsCall))199 OS << BranchLabel << ":\n";200 201 BC.InstPrinter->printInst(&Instr, 0, "", *BC.STI, OS);202 OS << '\n';203 }204 205 // Dump profile data in FDATA format (as parsed by link_fdata).206 for (const BinaryBasicBlock *Succ : BB->successors()) {207 const BinaryBasicBlock::BinaryBranchInfo BI = BB->getBranchInfo(*Succ);208 if (!BI.MispredictedCount && !BI.Count)209 continue;210 211 OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# "212 << "1 " << FunctionName << " #" << Succ->getName() << "# "213 << BI.MispredictedCount << " " << BI.Count << '\n';214 }215 216 OS << '\n';217 }218 MAP->OutStreamer->emitCFIEndProc();219 220 OS << ".size " << FunctionName << ", .-" << FunctionName << '\n';221 222 const BinarySection *LastSection = BF.getOriginSection();223 // Print stubs for all target functions.224 for (const MCSymbol *CalleeSymb : CallReferences)225 dumpTargetFunctionStub(OS, BC, CalleeSymb, LastSection);226 227 OS << "# Jump tables\n";228 // Print all jump tables.229 for (auto &JTI : BF.jumpTables())230 dumpJumpTableSymbols(OS, JTI.second, *MAP, LastSection);231 232 OS << "# BinaryData\n";233 // Print data references.234 for (const BinaryData *BD : BDReferences)235 dumpBinaryDataSymbols(OS, BD, LastSection);236}237 238Error AsmDumpPass::runOnFunctions(BinaryContext &BC) {239 for (const auto &BFIt : BC.getBinaryFunctions())240 dumpFunction(BFIt.second);241 return Error::success();242}243 244} // namespace bolt245} // namespace llvm246