1//===-- llvm-dis.cpp - The low-level LLVM disassembler --------------------===//
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 utility may be invoked in the following manner:
10// llvm-dis [options] - Read LLVM bitcode from stdin, write asm to stdout
11// llvm-dis [options] x.bc - Read LLVM bitcode from the x.bc file, write asm
12// to the x.ll file.
13// Options:
14//
15// Color Options:
16// --color - Use colors in output (default=autodetect)
17//
18// Disassembler Options:
19// -f - Enable binary output on terminals
20// --materialize-metadata - Load module without materializing metadata,
21// then materialize only the metadata
22// -o <filename> - Override output filename
23// --show-annotations - Add informational comments to the .ll file
24//
25// Generic Options:
26// --help - Display available options
27// (--help-hidden for more)
28// --help-list - Display list of available options
29// (--help-list-hidden for more)
30// --version - Display the version of this program
31//
32//===----------------------------------------------------------------------===//
33
34#include "llvm/Bitcode/BitcodeReader.h"
35#include "llvm/IR/AssemblyAnnotationWriter.h"
36#include "llvm/IR/DebugInfo.h"
37#include "llvm/IR/DiagnosticInfo.h"
38#include "llvm/IR/DiagnosticPrinter.h"
39#include "llvm/IR/IntrinsicInst.h"
40#include "llvm/IR/LLVMContext.h"
41#include "llvm/IR/Module.h"
42#include "llvm/IR/ModuleSummaryIndex.h"
43#include "llvm/IR/Type.h"
44#include "llvm/Support/CommandLine.h"
45#include "llvm/Support/Error.h"
46#include "llvm/Support/FileSystem.h"
47#include "llvm/Support/FormattedStream.h"
48#include "llvm/Support/InitLLVM.h"
49#include "llvm/Support/MemoryBuffer.h"
50#include "llvm/Support/ToolOutputFile.h"
51#include "llvm/Support/WithColor.h"
52#include <system_error>
53using namespace llvm;
54
55static cl::OptionCategory DisCategory("Disassembler Options");
56
57static cl::list<std::string> InputFilenames(cl::Positional,
58 cl::desc("[input bitcode]..."),
59 cl::cat(DisCategory));
60
61static cl::opt<std::string> OutputFilename("o",
62 cl::desc("Override output filename"),
63 cl::value_desc("filename"),
64 cl::cat(DisCategory));
65
66static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
67 cl::cat(DisCategory));
68
69static cl::opt<bool> DontPrint("disable-output",
70 cl::desc("Don't output the .ll file"),
71 cl::Hidden, cl::cat(DisCategory));
72
73static cl::opt<bool>
74 SetImporting("set-importing",
75 cl::desc("Set lazy loading to pretend to import a module"),
76 cl::Hidden, cl::cat(DisCategory));
77
78static cl::opt<bool>
79 ShowAnnotations("show-annotations",
80 cl::desc("Add informational comments to the .ll file"),
81 cl::cat(DisCategory));
82
83static cl::opt<bool>
84 MaterializeMetadata("materialize-metadata",
85 cl::desc("Load module without materializing metadata, "
86 "then materialize only the metadata"),
87 cl::cat(DisCategory));
88
89static cl::opt<bool> PrintThinLTOIndexOnly(
90 "print-thinlto-index-only",
91 cl::desc("Only read thinlto index and print the index as LLVM assembly."),
92 cl::init(Val: false), cl::Hidden, cl::cat(DisCategory));
93
94static void printDebugLoc(const DebugLoc &DL, formatted_raw_ostream &OS) {
95 OS << DL.getLine() << ":" << DL.getCol();
96 if (DILocation *IDL = DL.getInlinedAt()) {
97 OS << "@";
98 printDebugLoc(DL: IDL, OS);
99 }
100}
101
102namespace {
103class CommentWriter : public AssemblyAnnotationWriter {
104private:
105 bool canSafelyAccessUses(const Value &V) {
106 // Can't safely access uses, if module not materialized.
107 const GlobalValue *GV = dyn_cast<GlobalValue>(Val: &V);
108 return !GV || (GV->getParent() && GV->getParent()->isMaterialized());
109 }
110
111public:
112 void emitFunctionAnnot(const Function *F,
113 formatted_raw_ostream &OS) override {
114 if (!canSafelyAccessUses(V: *F))
115 return;
116
117 OS << "; [#uses=" << F->getNumUses() << ']'; // Output # uses
118 OS << '\n';
119 }
120
121 void printInfoComment(const Value &V, formatted_raw_ostream &OS) override {
122 if (!canSafelyAccessUses(V))
123 return;
124
125 bool Padded = false;
126 if (!V.getType()->isVoidTy()) {
127 OS.PadToColumn(NewCol: 50);
128 Padded = true;
129 // Output # uses and type
130 OS << "; [#uses=" << V.getNumUses() << " type=" << *V.getType() << "]";
131 }
132 if (const Instruction *I = dyn_cast<Instruction>(Val: &V)) {
133 if (const DebugLoc &DL = I->getDebugLoc()) {
134 if (!Padded) {
135 OS.PadToColumn(NewCol: 50);
136 Padded = true;
137 OS << ";";
138 }
139 OS << " [debug line = ";
140 printDebugLoc(DL,OS);
141 OS << "]";
142 }
143 }
144 }
145};
146
147struct LLVMDisDiagnosticHandler : public DiagnosticHandler {
148 char *Prefix;
149 LLVMDisDiagnosticHandler(char *PrefixPtr) : Prefix(PrefixPtr) {}
150 bool handleDiagnostics(const DiagnosticInfo &DI) override {
151 raw_ostream &OS = errs();
152 OS << Prefix << ": ";
153 switch (DI.getSeverity()) {
154 case DS_Error: WithColor::error(OS); break;
155 case DS_Warning: WithColor::warning(OS); break;
156 case DS_Remark: OS << "remark: "; break;
157 case DS_Note: WithColor::note(OS); break;
158 }
159
160 DiagnosticPrinterRawOStream DP(OS);
161 DI.print(DP);
162 OS << '\n';
163
164 if (DI.getSeverity() == DS_Error)
165 exit(status: 1);
166 return true;
167 }
168};
169} // namespace
170
171static ExitOnError ExitOnErr;
172
173int main(int argc, char **argv) {
174 InitLLVM X(argc, argv);
175
176 ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
177
178 cl::HideUnrelatedOptions(Categories: {&DisCategory, &getColorCategory()});
179 cl::ParseCommandLineOptions(argc, argv, Overview: "llvm .bc -> .ll disassembler\n");
180
181 if (InputFilenames.size() < 1) {
182 InputFilenames.push_back(value: "-");
183 } else if (InputFilenames.size() > 1 && !OutputFilename.empty()) {
184 errs()
185 << "error: output file name cannot be set for multiple input files\n";
186 return 1;
187 }
188
189 for (const auto &InputFilename : InputFilenames) {
190 // Use a fresh context for each input to avoid state
191 // cross-contamination across inputs (e.g. type name collisions).
192 LLVMContext Context;
193 Context.setDiagnosticHandler(
194 DH: std::make_unique<LLVMDisDiagnosticHandler>(args&: argv[0]));
195
196 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
197 MemoryBuffer::getFileOrSTDIN(Filename: InputFilename);
198 if (std::error_code EC = BufferOrErr.getError()) {
199 WithColor::error() << InputFilename << ": " << EC.message() << '\n';
200 return 1;
201 }
202 std::unique_ptr<MemoryBuffer> MB = std::move(BufferOrErr.get());
203
204 BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(Buffer: *MB));
205
206 const size_t N = IF.Mods.size();
207
208 if (OutputFilename == "-" && N > 1)
209 errs() << "only single module bitcode files can be written to stdout\n";
210
211 for (size_t I = 0; I < N; ++I) {
212 BitcodeModule MB = IF.Mods[I];
213
214 std::unique_ptr<Module> M;
215
216 if (!PrintThinLTOIndexOnly) {
217 M = ExitOnErr(
218 MB.getLazyModule(Context, ShouldLazyLoadMetadata: MaterializeMetadata, IsImporting: SetImporting));
219 if (MaterializeMetadata)
220 ExitOnErr(M->materializeMetadata());
221 else
222 ExitOnErr(M->materializeAll());
223 }
224
225 BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo());
226 std::unique_ptr<ModuleSummaryIndex> Index;
227 if (LTOInfo.HasSummary)
228 Index = ExitOnErr(MB.getSummary());
229
230 std::string FinalFilename(OutputFilename);
231
232 // Just use stdout. We won't actually print anything on it.
233 if (DontPrint)
234 FinalFilename = "-";
235
236 if (FinalFilename.empty()) { // Unspecified output, infer it.
237 if (InputFilename == "-") {
238 FinalFilename = "-";
239 } else {
240 StringRef IFN = InputFilename;
241 FinalFilename = (IFN.ends_with(Suffix: ".bc") ? IFN.drop_back(N: 3) : IFN).str();
242 if (N > 1)
243 FinalFilename += std::string(".") + std::to_string(val: I);
244 FinalFilename += ".ll";
245 }
246 } else {
247 if (N > 1)
248 FinalFilename += std::string(".") + std::to_string(val: I);
249 }
250
251 std::error_code EC;
252 std::unique_ptr<ToolOutputFile> Out(
253 new ToolOutputFile(FinalFilename, EC, sys::fs::OF_TextWithCRLF));
254 if (EC) {
255 errs() << EC.message() << '\n';
256 return 1;
257 }
258
259 std::unique_ptr<AssemblyAnnotationWriter> Annotator;
260 if (ShowAnnotations)
261 Annotator.reset(p: new CommentWriter());
262
263 // All that llvm-dis does is write the assembly to a file.
264 if (!DontPrint) {
265 if (M) {
266 M->removeDebugIntrinsicDeclarations();
267 M->print(OS&: Out->os(), AAW: Annotator.get(),
268 /* ShouldPreserveUseListOrder */ false);
269 }
270 if (Index)
271 Index->print(OS&: Out->os());
272 }
273
274 // Declare success.
275 Out->keep();
276 }
277 }
278
279 return 0;
280}
281