| 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> |
| 84 | MaterializeMetadata("materialize-metadata" , |
| 85 | cl::desc("Load module without materializing metadata, " |
| 86 | "then materialize only the metadata" ), |
| 87 | cl::cat(DisCategory)); |
| 88 | |
| 89 | static 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 | |
| 94 | static 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 | |
| 102 | namespace { |
| 103 | class : public AssemblyAnnotationWriter { |
| 104 | private: |
| 105 | bool (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 | |
| 111 | public: |
| 112 | void (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 (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 | |
| 147 | struct 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 | |
| 171 | static ExitOnError ExitOnErr; |
| 172 | |
| 173 | int 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 | |