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
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 opts
35
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(Inst: *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(Symbol: 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(Sym: JTEntry, Size: 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(Function: 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(path: 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(first: PrintName.begin(), last: PrintName.end(), old_value: '/', new_value: '-');
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(STI: *BC.STI, MRI: *BC.MRI, Options: MCTargetOptions()));
136 int AsmPrinterVariant = BC.AsmInfo->getAssemblerDialect();
137 MCInstPrinter *InstructionPrinter(BC.TheTarget->createMCInstPrinter(
138 T: *BC.TheTriple, SyntaxVariant: AsmPrinterVariant, MAI: *BC.AsmInfo, MII: *BC.MII, MRI: *BC.MRI));
139 auto FOut = std::make_unique<formatted_raw_ostream>(args&: OS);
140 FOut->SetUnbuffered();
141 std::unique_ptr<MCStreamer> AsmStreamer(
142 createAsmStreamer(Ctx&: *LocalCtx, OS: std::move(FOut),
143 /*isVerboseAsm=*/true,
144 /*useDwarfDirectory=*/false, InstPrint: InstructionPrinter,
145 CE: std::move(MCEInstance.MCE), TAB: std::move(MAB),
146 /*ShowInst=*/false));
147 AsmStreamer->initSections(NoExecStack: true, STI: *BC.STI);
148 std::unique_ptr<TargetMachine> TM(BC.TheTarget->createTargetMachine(
149 TT: BC.TripleName, CPU: "", Features: "", Options: TargetOptions(), RM: std::nullopt));
150 std::unique_ptr<AsmPrinter> MAP(
151 BC.TheTarget->createAsmPrinter(TM&: *TM, Streamer: std::move(AsmStreamer)));
152
153 StringRef FunctionName = BF.getOneName();
154 OS << " .globl " << FunctionName << '\n';
155 OS << " .type " << FunctionName << ", %function\n";
156 OS << FunctionName << ":\n";
157
158 // FDATA for the entry point
159 if (uint64_t EntryExecCount = BF.getKnownExecutionCount())
160 OS << "# FDATA: 0 [unknown] 0 "
161 << "1 " << FunctionName << " 0 "
162 << "0 " << EntryExecCount << '\n';
163
164 // Binary data references from the function.
165 std::unordered_set<const BinaryData *> BDReferences;
166 // Function references from the function (to avoid constructing call graph).
167 std::unordered_set<const MCSymbol *> CallReferences;
168
169 MAP->OutStreamer->emitCFIStartProc(/*IsSimple=*/false);
170 for (const BinaryBasicBlock *BB : BF.getLayout().blocks()) {
171 OS << BB->getName() << ": \n";
172
173 const std::string BranchLabel = Twine(BB->getName(), "_br").str();
174 const MCInst *LastInst = BB->getLastNonPseudoInstr();
175
176 for (const MCInst &Instr : *BB) {
177 // Dump pseudo instructions (CFI)
178 if (BC.MIB->isPseudo(Inst: Instr)) {
179 if (BC.MIB->isCFI(Inst: Instr))
180 dumpCFI(BF, Instr, MAP&: *MAP.get());
181 continue;
182 }
183
184 // Analyze symbol references (data, functions) from the instruction.
185 bool IsCall = BC.MIB->isCall(Inst: Instr);
186 for (const MCOperand &Operand : MCPlus::primeOperands(Inst: Instr)) {
187 if (Operand.isExpr() &&
188 Operand.getExpr()->getKind() == MCExpr::SymbolRef) {
189 std::pair<const MCSymbol *, uint64_t> TSI =
190 BC.MIB->getTargetSymbolInfo(Expr: Operand.getExpr());
191 const MCSymbol *Symbol = TSI.first;
192 if (IsCall)
193 CallReferences.insert(x: Symbol);
194 else if (const BinaryData *BD =
195 BC.getBinaryDataByName(Name: Symbol->getName()))
196 BDReferences.insert(x: BD);
197 }
198 }
199
200 if (&Instr == LastInst && (BB->succ_size() || IsCall))
201 OS << BranchLabel << ":\n";
202
203 BC.InstPrinter->printInst(MI: &Instr, Address: 0, Annot: "", STI: *BC.STI, OS);
204 OS << '\n';
205 }
206
207 // Dump profile data in FDATA format (as parsed by link_fdata).
208 for (const BinaryBasicBlock *Succ : BB->successors()) {
209 const BinaryBasicBlock::BinaryBranchInfo BI = BB->getBranchInfo(Succ: *Succ);
210 if (!BI.MispredictedCount && !BI.Count)
211 continue;
212
213 OS << "# FDATA: 1 " << FunctionName << " #" << BranchLabel << "# "
214 << "1 " << FunctionName << " #" << Succ->getName() << "# "
215 << BI.MispredictedCount << " " << BI.Count << '\n';
216 }
217
218 OS << '\n';
219 }
220 MAP->OutStreamer->emitCFIEndProc();
221
222 OS << ".size " << FunctionName << ", .-" << FunctionName << '\n';
223
224 const BinarySection *LastSection = BF.getOriginSection();
225 // Print stubs for all target functions.
226 for (const MCSymbol *CalleeSymb : CallReferences)
227 dumpTargetFunctionStub(OS, BC, CalleeSymb, LastCS&: LastSection);
228
229 OS << "# Jump tables\n";
230 // Print all jump tables.
231 for (auto &JTI : BF.jumpTables())
232 dumpJumpTableSymbols(OS, JT: JTI.second, MAP&: *MAP.get(), LastBS&: LastSection);
233
234 OS << "# BinaryData\n";
235 // Print data references.
236 for (const BinaryData *BD : BDReferences)
237 dumpBinaryDataSymbols(OS, BD, LastBS&: LastSection);
238}
239
240Error AsmDumpPass::runOnFunctions(BinaryContext &BC) {
241 for (const auto &BFIt : BC.getBinaryFunctions())
242 dumpFunction(BF: BFIt.second);
243 return Error::success();
244}
245
246} // namespace bolt
247} // namespace llvm
248

source code of bolt/lib/Passes/AsmDump.cpp