| 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 | |