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 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
<< " "
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
);
82 OS
<< "# FDATA: 1 " << FunctionName
<< " #" << BranchLabel
<< "# "
83 << "1 " << TargetBF
->getPrintName() << " 0 "
84 << "0 " << CallFreq
<< '\n';
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())
94 if (CalleeFunc
->getOriginSection() != LastCS
) {
95 OS
<< ".section " << CalleeFunc
->getOriginSectionName() << '\n';
96 LastCS
= CalleeFunc
->getOriginSection();
98 StringRef CalleeName
= CalleeFunc
->getOneName();
99 OS
<< ".set \"" << CalleeName
<< "\", 0\n";
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();
108 OS
<< "\"" << JT
->getName() << "\":\n";
109 for (MCSymbol
*JTEntry
: JT
->Entries
)
110 MAP
.OutStreamer
->emitSymbolValue(JTEntry
, JT
->OutputEntrySize
);
114 void dumpBinaryDataSymbols(raw_ostream
&OS
, const BinaryData
*BD
,
115 const BinarySection
*&LastBS
) {
116 if (BD
->isJumpTable())
118 if (&BD
->getSection() != LastBS
) {
119 OS
<< ".section " << BD
->getSectionName() << '\n';
120 LastBS
= &BD
->getSection();
122 OS
<< "\"" << BD
->getName() << "\": ";
126 void dumpFunction(const BinaryFunction
&BF
) {
127 const BinaryContext
&BC
= BF
.getBinaryContext();
128 if (!opts::shouldPrint(BF
))
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';
140 std::string PrintName
= BF
.getPrintName();
141 std::replace(PrintName
.begin(), PrintName
.end(), '/', '-');
142 std::string Filename
=
143 opts::AsmDump
.empty()
145 : (opts::AsmDump
+ sys::path::get_separator() + PrintName
+ ".s")
147 outs() << "BOLT-INFO: Dumping function assembly to " << Filename
<< "\n";
150 raw_fd_ostream
OS(Filename
, EC
, sys::fs::OF_None
);
152 errs() << "BOLT-ERROR: " << EC
.message() << ", unable to open " << Filename
158 // Create local MC context to isolate the effect of ephemeral assembly
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
)));
182 StringRef FunctionName
= BF
.getOneName();
183 OS
<< " .globl " << FunctionName
<< '\n';
184 OS
<< " .type " << FunctionName
<< ", %function\n";
185 OS
<< FunctionName
<< ":\n";
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';
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
;
198 MAP
->OutStreamer
->emitCFIStartProc(/*IsSimple=*/false);
199 for (BinaryBasicBlock
*BB
: BF
.layout()) {
200 OS
<< BB
->getName() << ": \n";
202 const std::string BranchLabel
= Twine(BB
->getName(), "_br").str();
203 const MCInst
*LastInst
= BB
->getLastNonPseudoInstr();
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());
213 // Analyze symbol references (data, functions) from the instruction.
214 bool IsCall
= BC
.MIB
->isCall(Instr
);
215 for (const MCOperand
&Operand
: MCPlus::primeOperands(Instr
)) {
216 if (Operand
.isExpr() &&
217 Operand
.getExpr()->getKind() == MCExpr::SymbolRef
) {
218 std::pair
<const MCSymbol
*, uint64_t> TSI
=
219 BC
.MIB
->getTargetSymbolInfo(Operand
.getExpr());
220 const MCSymbol
*Symbol
= TSI
.first
;
222 CallReferences
.insert(Symbol
);
223 else if (const BinaryData
*BD
=
224 BC
.getBinaryDataByName(Symbol
->getName()))
225 BDReferences
.insert(BD
);
229 if (&Instr
== LastInst
&& (BB
->succ_size() || IsCall
))
230 OS
<< BranchLabel
<< ":\n";
232 BC
.InstPrinter
->printInst(&Instr
, 0, "", *BC
.STI
, OS
);
235 // Dump profile data in FDATA format (as parsed by link_fdata).
236 if (BC
.MIB
->getJumpTable(Instr
))
237 dumpJumpTableFdata(OS
, BF
, Instr
, BranchLabel
);
238 else if (BC
.MIB
->isTailCall(Instr
))
239 dumpTailCallFdata(OS
, BF
, Instr
, BranchLabel
);
242 // Dump profile data in FDATA format (as parsed by link_fdata).
243 for (const BinaryBasicBlock
*Succ
: BB
->successors()) {
244 const BinaryBasicBlock::BinaryBranchInfo BI
= BB
->getBranchInfo(*Succ
);
245 if (!BI
.MispredictedCount
&& !BI
.Count
)
248 OS
<< "# FDATA: 1 " << FunctionName
<< " #" << BranchLabel
<< "# "
249 << "1 " << FunctionName
<< " #" << Succ
->getName() << "# "
250 << BI
.MispredictedCount
<< " " << BI
.Count
<< '\n';
255 MAP
->OutStreamer
->emitCFIEndProc();
257 OS
<< ".size " << FunctionName
<< ", .-" << FunctionName
<< '\n';
259 const BinarySection
*LastSection
= BF
.getOriginSection();
260 // Print stubs for all target functions.
261 for (const MCSymbol
*CalleeSymb
: CallReferences
)
262 dumpTargetFunctionStub(OS
, BC
, CalleeSymb
, LastSection
);
264 OS
<< "# Jump tables\n";
265 // Print all jump tables.
266 for (auto &JTI
: BF
.jumpTables())
267 dumpJumpTableSymbols(OS
, JTI
.second
, *MAP
.get(), LastSection
);
269 OS
<< "# BinaryData\n";
270 // Print data references.
271 for (const BinaryData
*BD
: BDReferences
)
272 dumpBinaryDataSymbols(OS
, BD
, LastSection
);
275 void AsmDumpPass::runOnFunctions(BinaryContext
&BC
) {
276 for (const auto &BFIt
: BC
.getBinaryFunctions())
277 dumpFunction(BFIt
.second
);