1 //===- bolt/Passes/AsmDump.cpp - Dump BinaryFunction into assembly --------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements the AsmDumpPass class.
11 //===----------------------------------------------------------------------===//
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>
21 #define DEBUG_TYPE "asm-dump"
26 extern bool shouldPrint(const bolt::BinaryFunction
&Function
);
27 extern cl::OptionCategory BoltCategory
;
28 extern cl::opt
<unsigned> Verbosity
;
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
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)
47 << "BOLT-WARNING: AsmDump: skipping unsupported CFI instruction in "
53 // Emit regular CFI instructions.
54 MAP
.emitCFIInstruction(*CFIInstr
);
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())
65 if (CalleeFunc
->getOriginSection() != LastCS
) {
66 OS
<< ".section " << CalleeFunc
->getOriginSectionName() << '\n';
67 LastCS
= CalleeFunc
->getOriginSection();
69 StringRef CalleeName
= CalleeFunc
->getOneName();
70 OS
<< ".set \"" << CalleeName
<< "\", 0\n";
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();
79 OS
<< "\"" << JT
->getName() << "\":\n";
80 for (MCSymbol
*JTEntry
: JT
->Entries
)
81 MAP
.OutStreamer
->emitSymbolValue(JTEntry
, JT
->OutputEntrySize
);
85 void dumpBinaryDataSymbols(raw_ostream
&OS
, const BinaryData
*BD
,
86 const BinarySection
*&LastBS
) {
87 if (BD
->isJumpTable())
89 if (&BD
->getSection() != LastBS
) {
90 OS
<< ".section " << BD
->getSectionName() << '\n';
91 LastBS
= &BD
->getSection();
93 OS
<< "\"" << BD
->getName() << "\": ";
97 void dumpFunction(const BinaryFunction
&BF
) {
98 const BinaryContext
&BC
= BF
.getBinaryContext();
99 if (!opts::shouldPrint(BF
))
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 errs() << "BOLT-ERROR: could not create directory '" << opts::AsmDump
106 << "': " << EC
.message() << '\n';
111 std::string PrintName
= BF
.getPrintName();
112 std::replace(PrintName
.begin(), PrintName
.end(), '/', '-');
113 std::string Filename
=
114 opts::AsmDump
.empty()
116 : (opts::AsmDump
+ sys::path::get_separator() + PrintName
+ ".s")
118 outs() << "BOLT-INFO: Dumping function assembly to " << Filename
<< "\n";
121 raw_fd_ostream
OS(Filename
, EC
, sys::fs::OF_None
);
123 errs() << "BOLT-ERROR: " << EC
.message() << ", unable to open " << Filename
129 // Create local MC context to isolate the effect of ephemeral assembly
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
),
143 /*isVerboseAsm=*/true,
144 /*useDwarfDirectory=*/false, InstructionPrinter
,
145 std::move(MCEInstance
.MCE
), std::move(MAB
),
146 /*ShowInst=*/false));
147 AsmStreamer
->initSections(true, *BC
.STI
);
148 std::unique_ptr
<TargetMachine
> TM(BC
.TheTarget
->createTargetMachine(
149 BC
.TripleName
, "", "", TargetOptions(), std::nullopt
));
150 std::unique_ptr
<AsmPrinter
> MAP(
151 BC
.TheTarget
->createAsmPrinter(*TM
, std::move(AsmStreamer
)));
153 StringRef FunctionName
= BF
.getOneName();
154 OS
<< " .globl " << FunctionName
<< '\n';
155 OS
<< " .type " << FunctionName
<< ", %function\n";
156 OS
<< FunctionName
<< ":\n";
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';
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
;
169 MAP
->OutStreamer
->emitCFIStartProc(/*IsSimple=*/false);
170 for (const BinaryBasicBlock
*BB
: BF
.getLayout().blocks()) {
171 OS
<< BB
->getName() << ": \n";
173 const std::string BranchLabel
= Twine(BB
->getName(), "_br").str();
174 const MCInst
*LastInst
= BB
->getLastNonPseudoInstr();
176 for (const MCInst
&Instr
: *BB
) {
177 // Dump pseudo instructions (CFI)
178 if (BC
.MIB
->isPseudo(Instr
)) {
179 if (BC
.MIB
->isCFI(Instr
))
180 dumpCFI(BF
, Instr
, *MAP
.get());
184 // Analyze symbol references (data, functions) from the instruction.
185 bool IsCall
= BC
.MIB
->isCall(Instr
);
186 for (const MCOperand
&Operand
: MCPlus::primeOperands(Instr
)) {
187 if (Operand
.isExpr() &&
188 Operand
.getExpr()->getKind() == MCExpr::SymbolRef
) {
189 std::pair
<const MCSymbol
*, uint64_t> TSI
=
190 BC
.MIB
->getTargetSymbolInfo(Operand
.getExpr());
191 const MCSymbol
*Symbol
= TSI
.first
;
193 CallReferences
.insert(Symbol
);
194 else if (const BinaryData
*BD
=
195 BC
.getBinaryDataByName(Symbol
->getName()))
196 BDReferences
.insert(BD
);
200 if (&Instr
== LastInst
&& (BB
->succ_size() || IsCall
))
201 OS
<< BranchLabel
<< ":\n";
203 BC
.InstPrinter
->printInst(&Instr
, 0, "", *BC
.STI
, OS
);
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
);
210 if (!BI
.MispredictedCount
&& !BI
.Count
)
213 OS
<< "# FDATA: 1 " << FunctionName
<< " #" << BranchLabel
<< "# "
214 << "1 " << FunctionName
<< " #" << Succ
->getName() << "# "
215 << BI
.MispredictedCount
<< " " << BI
.Count
<< '\n';
220 MAP
->OutStreamer
->emitCFIEndProc();
222 OS
<< ".size " << FunctionName
<< ", .-" << FunctionName
<< '\n';
224 const BinarySection
*LastSection
= BF
.getOriginSection();
225 // Print stubs for all target functions.
226 for (const MCSymbol
*CalleeSymb
: CallReferences
)
227 dumpTargetFunctionStub(OS
, BC
, CalleeSymb
, LastSection
);
229 OS
<< "# Jump tables\n";
230 // Print all jump tables.
231 for (auto &JTI
: BF
.jumpTables())
232 dumpJumpTableSymbols(OS
, JTI
.second
, *MAP
.get(), LastSection
);
234 OS
<< "# BinaryData\n";
235 // Print data references.
236 for (const BinaryData
*BD
: BDReferences
)
237 dumpBinaryDataSymbols(OS
, BD
, LastSection
);
240 void AsmDumpPass::runOnFunctions(BinaryContext
&BC
) {
241 for (const auto &BFIt
: BC
.getBinaryFunctions())
242 dumpFunction(BFIt
.second
);