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