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