During the transition from debug intrinsics to debug records, we used several different command line options to customise handling: the printing of debug records to bitcode and textual could be independent of how the debug-info was represented inside a module, whether the autoupgrader ran could be customised. This was all valuable during development, but now that totally removing debug intrinsics is coming up, this patch removes those options in favour of a single flag (experimental-debuginfo-iterators), which enables autoupgrade, in-memory debug records, and debug record printing to bitcode and textual IR. We need to do this ahead of removing the experimental-debuginfo-iterators flag, to reduce the amount of test-juggling that happens at that time. There are quite a number of weird test behaviours related to this -- some of which I simply delete in this commit. Things like print-non-instruction-debug-info.ll , the test suite now checks for debug records in all tests, and we don't want to check we can print as intrinsics. Or the update_test_checks tests -- these are duplicated with write-experimental-debuginfo=false to ensure file writing for intrinsics is correct, but that's something we're imminently going to delete. A short survey of curious test changes: * free-intrinsics.ll: we don't need to test that debug-info is a zero cost intrinsic, because we won't be using intrinsics in the future. * undef-dbg-val.ll: apparently we pinned this to non-RemoveDIs in-memory mode while we sorted something out; it works now either way. * salvage-cast-debug-info.ll: was testing intrinsics-in-memory get salvaged, isn't necessary now * localize-constexpr-debuginfo.ll: was producing "dead metadata" intrinsics for optimised-out variable values, dbg-records takes the (correct) representation of poison/undef as an operand. Looks like we didn't update this in the past to avoid spurious test differences. * Transforms/Scalarizer/dbginfo.ll: this test was explicitly testing that debug-info affected codegen, and we deferred updating the tests until now. This is just one of those silent gnochange issues that get fixed by RemoveDIs. Finally: I've added a bitcode test, dbg-intrinsics-autoupgrade.ll.bc, that checks we can autoupgrade debug intrinsics that are in bitcode into the new debug records.
289 lines
9.7 KiB
C++
289 lines
9.7 KiB
C++
//===-- llvm-dis.cpp - The low-level LLVM disassembler --------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This utility may be invoked in the following manner:
|
|
// llvm-dis [options] - Read LLVM bitcode from stdin, write asm to stdout
|
|
// llvm-dis [options] x.bc - Read LLVM bitcode from the x.bc file, write asm
|
|
// to the x.ll file.
|
|
// Options:
|
|
//
|
|
// Color Options:
|
|
// --color - Use colors in output (default=autodetect)
|
|
//
|
|
// Disassembler Options:
|
|
// -f - Enable binary output on terminals
|
|
// --materialize-metadata - Load module without materializing metadata,
|
|
// then materialize only the metadata
|
|
// -o <filename> - Override output filename
|
|
// --show-annotations - Add informational comments to the .ll file
|
|
//
|
|
// Generic Options:
|
|
// --help - Display available options
|
|
// (--help-hidden for more)
|
|
// --help-list - Display list of available options
|
|
// (--help-list-hidden for more)
|
|
// --version - Display the version of this program
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "llvm/Bitcode/BitcodeReader.h"
|
|
#include "llvm/IR/AssemblyAnnotationWriter.h"
|
|
#include "llvm/IR/DebugInfo.h"
|
|
#include "llvm/IR/DiagnosticInfo.h"
|
|
#include "llvm/IR/DiagnosticPrinter.h"
|
|
#include "llvm/IR/IntrinsicInst.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/Module.h"
|
|
#include "llvm/IR/ModuleSummaryIndex.h"
|
|
#include "llvm/IR/Type.h"
|
|
#include "llvm/Support/CommandLine.h"
|
|
#include "llvm/Support/Error.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/FormattedStream.h"
|
|
#include "llvm/Support/InitLLVM.h"
|
|
#include "llvm/Support/MemoryBuffer.h"
|
|
#include "llvm/Support/ToolOutputFile.h"
|
|
#include "llvm/Support/WithColor.h"
|
|
#include <system_error>
|
|
using namespace llvm;
|
|
|
|
static cl::OptionCategory DisCategory("Disassembler Options");
|
|
|
|
static cl::list<std::string> InputFilenames(cl::Positional,
|
|
cl::desc("[input bitcode]..."),
|
|
cl::cat(DisCategory));
|
|
|
|
static cl::opt<std::string> OutputFilename("o",
|
|
cl::desc("Override output filename"),
|
|
cl::value_desc("filename"),
|
|
cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool> Force("f", cl::desc("Enable binary output on terminals"),
|
|
cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool> DontPrint("disable-output",
|
|
cl::desc("Don't output the .ll file"),
|
|
cl::Hidden, cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool>
|
|
SetImporting("set-importing",
|
|
cl::desc("Set lazy loading to pretend to import a module"),
|
|
cl::Hidden, cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool>
|
|
ShowAnnotations("show-annotations",
|
|
cl::desc("Add informational comments to the .ll file"),
|
|
cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool> PreserveAssemblyUseListOrder(
|
|
"preserve-ll-uselistorder",
|
|
cl::desc("Preserve use-list order when writing LLVM assembly."),
|
|
cl::init(false), cl::Hidden, cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool>
|
|
MaterializeMetadata("materialize-metadata",
|
|
cl::desc("Load module without materializing metadata, "
|
|
"then materialize only the metadata"),
|
|
cl::cat(DisCategory));
|
|
|
|
static cl::opt<bool> PrintThinLTOIndexOnly(
|
|
"print-thinlto-index-only",
|
|
cl::desc("Only read thinlto index and print the index as LLVM assembly."),
|
|
cl::init(false), cl::Hidden, cl::cat(DisCategory));
|
|
|
|
extern cl::opt<bool> UseNewDbgInfoFormat;
|
|
|
|
namespace {
|
|
|
|
static void printDebugLoc(const DebugLoc &DL, formatted_raw_ostream &OS) {
|
|
OS << DL.getLine() << ":" << DL.getCol();
|
|
if (DILocation *IDL = DL.getInlinedAt()) {
|
|
OS << "@";
|
|
printDebugLoc(IDL, OS);
|
|
}
|
|
}
|
|
class CommentWriter : public AssemblyAnnotationWriter {
|
|
public:
|
|
void emitFunctionAnnot(const Function *F,
|
|
formatted_raw_ostream &OS) override {
|
|
OS << "; [#uses=" << F->getNumUses() << ']'; // Output # uses
|
|
OS << '\n';
|
|
}
|
|
void printInfoComment(const Value &V, formatted_raw_ostream &OS) override {
|
|
bool Padded = false;
|
|
if (!V.getType()->isVoidTy()) {
|
|
OS.PadToColumn(50);
|
|
Padded = true;
|
|
// Output # uses and type
|
|
OS << "; [#uses=" << V.getNumUses() << " type=" << *V.getType() << "]";
|
|
}
|
|
if (const Instruction *I = dyn_cast<Instruction>(&V)) {
|
|
if (const DebugLoc &DL = I->getDebugLoc()) {
|
|
if (!Padded) {
|
|
OS.PadToColumn(50);
|
|
Padded = true;
|
|
OS << ";";
|
|
}
|
|
OS << " [debug line = ";
|
|
printDebugLoc(DL,OS);
|
|
OS << "]";
|
|
}
|
|
if (const DbgDeclareInst *DDI = dyn_cast<DbgDeclareInst>(I)) {
|
|
if (!Padded) {
|
|
OS.PadToColumn(50);
|
|
OS << ";";
|
|
}
|
|
OS << " [debug variable = " << DDI->getVariable()->getName() << "]";
|
|
}
|
|
else if (const DbgValueInst *DVI = dyn_cast<DbgValueInst>(I)) {
|
|
if (!Padded) {
|
|
OS.PadToColumn(50);
|
|
OS << ";";
|
|
}
|
|
OS << " [debug variable = " << DVI->getVariable()->getName() << "]";
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
struct LLVMDisDiagnosticHandler : public DiagnosticHandler {
|
|
char *Prefix;
|
|
LLVMDisDiagnosticHandler(char *PrefixPtr) : Prefix(PrefixPtr) {}
|
|
bool handleDiagnostics(const DiagnosticInfo &DI) override {
|
|
raw_ostream &OS = errs();
|
|
OS << Prefix << ": ";
|
|
switch (DI.getSeverity()) {
|
|
case DS_Error: WithColor::error(OS); break;
|
|
case DS_Warning: WithColor::warning(OS); break;
|
|
case DS_Remark: OS << "remark: "; break;
|
|
case DS_Note: WithColor::note(OS); break;
|
|
}
|
|
|
|
DiagnosticPrinterRawOStream DP(OS);
|
|
DI.print(DP);
|
|
OS << '\n';
|
|
|
|
if (DI.getSeverity() == DS_Error)
|
|
exit(1);
|
|
return true;
|
|
}
|
|
};
|
|
} // end anon namespace
|
|
|
|
static ExitOnError ExitOnErr;
|
|
|
|
int main(int argc, char **argv) {
|
|
InitLLVM X(argc, argv);
|
|
|
|
ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
|
|
|
|
cl::HideUnrelatedOptions({&DisCategory, &getColorCategory()});
|
|
cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
|
|
|
|
if (InputFilenames.size() < 1) {
|
|
InputFilenames.push_back("-");
|
|
} else if (InputFilenames.size() > 1 && !OutputFilename.empty()) {
|
|
errs()
|
|
<< "error: output file name cannot be set for multiple input files\n";
|
|
return 1;
|
|
}
|
|
|
|
for (const auto &InputFilename : InputFilenames) {
|
|
// Use a fresh context for each input to avoid state
|
|
// cross-contamination across inputs (e.g. type name collisions).
|
|
LLVMContext Context;
|
|
Context.setDiagnosticHandler(
|
|
std::make_unique<LLVMDisDiagnosticHandler>(argv[0]));
|
|
|
|
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
|
|
MemoryBuffer::getFileOrSTDIN(InputFilename);
|
|
if (std::error_code EC = BufferOrErr.getError()) {
|
|
WithColor::error() << InputFilename << ": " << EC.message() << '\n';
|
|
return 1;
|
|
}
|
|
std::unique_ptr<MemoryBuffer> MB = std::move(BufferOrErr.get());
|
|
|
|
BitcodeFileContents IF = ExitOnErr(llvm::getBitcodeFileContents(*MB));
|
|
|
|
const size_t N = IF.Mods.size();
|
|
|
|
if (OutputFilename == "-" && N > 1)
|
|
errs() << "only single module bitcode files can be written to stdout\n";
|
|
|
|
for (size_t I = 0; I < N; ++I) {
|
|
BitcodeModule MB = IF.Mods[I];
|
|
|
|
std::unique_ptr<Module> M;
|
|
|
|
if (!PrintThinLTOIndexOnly) {
|
|
M = ExitOnErr(
|
|
MB.getLazyModule(Context, MaterializeMetadata, SetImporting));
|
|
if (MaterializeMetadata)
|
|
ExitOnErr(M->materializeMetadata());
|
|
else
|
|
ExitOnErr(M->materializeAll());
|
|
}
|
|
|
|
BitcodeLTOInfo LTOInfo = ExitOnErr(MB.getLTOInfo());
|
|
std::unique_ptr<ModuleSummaryIndex> Index;
|
|
if (LTOInfo.HasSummary)
|
|
Index = ExitOnErr(MB.getSummary());
|
|
|
|
std::string FinalFilename(OutputFilename);
|
|
|
|
// Just use stdout. We won't actually print anything on it.
|
|
if (DontPrint)
|
|
FinalFilename = "-";
|
|
|
|
if (FinalFilename.empty()) { // Unspecified output, infer it.
|
|
if (InputFilename == "-") {
|
|
FinalFilename = "-";
|
|
} else {
|
|
StringRef IFN = InputFilename;
|
|
FinalFilename = (IFN.ends_with(".bc") ? IFN.drop_back(3) : IFN).str();
|
|
if (N > 1)
|
|
FinalFilename += std::string(".") + std::to_string(I);
|
|
FinalFilename += ".ll";
|
|
}
|
|
} else {
|
|
if (N > 1)
|
|
FinalFilename += std::string(".") + std::to_string(I);
|
|
}
|
|
|
|
std::error_code EC;
|
|
std::unique_ptr<ToolOutputFile> Out(
|
|
new ToolOutputFile(FinalFilename, EC, sys::fs::OF_TextWithCRLF));
|
|
if (EC) {
|
|
errs() << EC.message() << '\n';
|
|
return 1;
|
|
}
|
|
|
|
std::unique_ptr<AssemblyAnnotationWriter> Annotator;
|
|
if (ShowAnnotations)
|
|
Annotator.reset(new CommentWriter());
|
|
|
|
// All that llvm-dis does is write the assembly to a file.
|
|
if (!DontPrint) {
|
|
if (M) {
|
|
M->setIsNewDbgInfoFormat(UseNewDbgInfoFormat);
|
|
if (UseNewDbgInfoFormat)
|
|
M->removeDebugIntrinsicDeclarations();
|
|
M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder);
|
|
}
|
|
if (Index)
|
|
Index->print(Out->os());
|
|
}
|
|
|
|
// Declare success.
|
|
Out->keep();
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|