aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm/tools
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools')
-rw-r--r--contrib/llvm/tools/bugpoint/BugDriver.cpp28
-rw-r--r--contrib/llvm/tools/bugpoint/BugDriver.h84
-rw-r--r--contrib/llvm/tools/bugpoint/CrashDebugger.cpp342
-rw-r--r--contrib/llvm/tools/bugpoint/ExecutionDriver.cpp49
-rw-r--r--contrib/llvm/tools/bugpoint/ExtractFunction.cpp39
-rw-r--r--contrib/llvm/tools/bugpoint/FindBugs.cpp8
-rw-r--r--contrib/llvm/tools/bugpoint/Miscompilation.cpp132
-rw-r--r--contrib/llvm/tools/bugpoint/OptimizerDriver.cpp54
-rw-r--r--contrib/llvm/tools/bugpoint/ToolRunner.cpp231
-rw-r--r--contrib/llvm/tools/bugpoint/bugpoint.cpp8
-rw-r--r--contrib/llvm/tools/llc/llc.cpp96
-rw-r--r--contrib/llvm/tools/lli/OrcLazyJIT.cpp166
-rw-r--r--contrib/llvm/tools/lli/OrcLazyJIT.h201
-rw-r--r--contrib/llvm/tools/lli/RemoteJITUtils.h4
-rw-r--r--contrib/llvm/tools/lli/lli.cpp219
-rw-r--r--contrib/llvm/tools/llvm-ar/llvm-ar.cpp298
-rw-r--r--contrib/llvm/tools/llvm-as/llvm-as.cpp51
-rw-r--r--contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp85
-rw-r--r--contrib/llvm/tools/llvm-cov/CodeCoverage.cpp137
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageExporter.h52
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp578
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageExporterJson.h112
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageFilters.cpp12
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageFilters.h33
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageReport.cpp94
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageReport.h15
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h42
-rw-r--r--contrib/llvm/tools/llvm-cov/CoverageViewOptions.h16
-rw-r--r--contrib/llvm/tools/llvm-cov/RenderingSupport.h4
-rw-r--r--contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp3
-rw-r--r--contrib/llvm/tools/llvm-cov/SourceCoverageView.h64
-rw-r--r--contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp90
-rw-r--r--contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h4
-rw-r--r--contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp4
-rw-r--r--contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h4
-rw-r--r--contrib/llvm/tools/llvm-cov/TestingSupport.cpp3
-rw-r--r--contrib/llvm/tools/llvm-cov/llvm-cov.cpp22
-rw-r--r--contrib/llvm/tools/llvm-cxxdump/Error.cpp2
-rw-r--r--contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp8
-rw-r--r--contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp6
-rw-r--r--contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp37
-rw-r--r--contrib/llvm/tools/llvm-dis/llvm-dis.cpp50
-rw-r--r--contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp33
-rw-r--r--contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp96
-rw-r--r--contrib/llvm/tools/llvm-extract/llvm-extract.cpp49
-rw-r--r--contrib/llvm/tools/llvm-link/llvm-link.cpp43
-rw-r--r--contrib/llvm/tools/llvm-lto/llvm-lto.cpp33
-rw-r--r--contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp18
-rw-r--r--contrib/llvm/tools/llvm-mc/llvm-mc.cpp261
-rw-r--r--contrib/llvm/tools/llvm-mca/CodeRegion.cpp66
-rw-r--r--contrib/llvm/tools/llvm-mca/CodeRegion.h131
-rw-r--r--contrib/llvm/tools/llvm-mca/Context.cpp63
-rw-r--r--contrib/llvm/tools/llvm-mca/Context.h68
-rw-r--r--contrib/llvm/tools/llvm-mca/DispatchStage.cpp149
-rw-r--r--contrib/llvm/tools/llvm-mca/DispatchStage.h106
-rw-r--r--contrib/llvm/tools/llvm-mca/DispatchStatistics.cpp71
-rw-r--r--contrib/llvm/tools/llvm-mca/DispatchStatistics.h84
-rw-r--r--contrib/llvm/tools/llvm-mca/ExecuteStage.cpp210
-rw-r--r--contrib/llvm/tools/llvm-mca/ExecuteStage.h67
-rw-r--r--contrib/llvm/tools/llvm-mca/FetchStage.cpp46
-rw-r--r--contrib/llvm/tools/llvm-mca/FetchStage.h45
-rw-r--r--contrib/llvm/tools/llvm-mca/HWEventListener.cpp21
-rw-r--r--contrib/llvm/tools/llvm-mca/HWEventListener.h141
-rw-r--r--contrib/llvm/tools/llvm-mca/HardwareUnit.cpp23
-rw-r--r--contrib/llvm/tools/llvm-mca/HardwareUnit.h31
-rw-r--r--contrib/llvm/tools/llvm-mca/InstrBuilder.cpp465
-rw-r--r--contrib/llvm/tools/llvm-mca/InstrBuilder.h85
-rw-r--r--contrib/llvm/tools/llvm-mca/Instruction.cpp177
-rw-r--r--contrib/llvm/tools/llvm-mca/Instruction.h427
-rw-r--r--contrib/llvm/tools/llvm-mca/InstructionInfoView.cpp91
-rw-r--r--contrib/llvm/tools/llvm-mca/InstructionInfoView.h66
-rw-r--r--contrib/llvm/tools/llvm-mca/InstructionTables.cpp70
-rw-r--r--contrib/llvm/tools/llvm-mca/InstructionTables.h43
-rw-r--r--contrib/llvm/tools/llvm-mca/LSUnit.cpp148
-rw-r--r--contrib/llvm/tools/llvm-mca/LSUnit.h147
-rw-r--r--contrib/llvm/tools/llvm-mca/Pipeline.cpp99
-rw-r--r--contrib/llvm/tools/llvm-mca/Pipeline.h79
-rw-r--r--contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp26
-rw-r--r--contrib/llvm/tools/llvm-mca/PipelinePrinter.h52
-rw-r--r--contrib/llvm/tools/llvm-mca/README.txt865
-rw-r--r--contrib/llvm/tools/llvm-mca/RegisterFile.cpp343
-rw-r--r--contrib/llvm/tools/llvm-mca/RegisterFile.h172
-rw-r--r--contrib/llvm/tools/llvm-mca/RegisterFileStatistics.cpp107
-rw-r--r--contrib/llvm/tools/llvm-mca/RegisterFileStatistics.h67
-rw-r--r--contrib/llvm/tools/llvm-mca/ResourcePressureView.cpp171
-rw-r--r--contrib/llvm/tools/llvm-mca/ResourcePressureView.h109
-rw-r--r--contrib/llvm/tools/llvm-mca/RetireControlUnit.cpp87
-rw-r--r--contrib/llvm/tools/llvm-mca/RetireControlUnit.h98
-rw-r--r--contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.cpp49
-rw-r--r--contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.h60
-rw-r--r--contrib/llvm/tools/llvm-mca/RetireStage.cpp55
-rw-r--r--contrib/llvm/tools/llvm-mca/RetireStage.h48
-rw-r--r--contrib/llvm/tools/llvm-mca/Scheduler.cpp403
-rw-r--r--contrib/llvm/tools/llvm-mca/Scheduler.h515
-rw-r--r--contrib/llvm/tools/llvm-mca/SchedulerStatistics.cpp94
-rw-r--r--contrib/llvm/tools/llvm-mca/SchedulerStatistics.h91
-rw-r--r--contrib/llvm/tools/llvm-mca/SourceMgr.h63
-rw-r--r--contrib/llvm/tools/llvm-mca/Stage.cpp27
-rw-r--r--contrib/llvm/tools/llvm-mca/Stage.h76
-rw-r--r--contrib/llvm/tools/llvm-mca/SummaryView.cpp85
-rw-r--r--contrib/llvm/tools/llvm-mca/SummaryView.h76
-rw-r--r--contrib/llvm/tools/llvm-mca/Support.cpp79
-rw-r--r--contrib/llvm/tools/llvm-mca/Support.h58
-rw-r--r--contrib/llvm/tools/llvm-mca/TimelineView.cpp240
-rw-r--r--contrib/llvm/tools/llvm-mca/TimelineView.h189
-rw-r--r--contrib/llvm/tools/llvm-mca/View.cpp20
-rw-r--r--contrib/llvm/tools/llvm-mca/View.h32
-rw-r--r--contrib/llvm/tools/llvm-mca/llvm-mca.cpp552
-rw-r--r--contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp2
-rw-r--r--contrib/llvm/tools/llvm-nm/llvm-nm.cpp125
-rw-r--r--contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td99
-rw-r--r--contrib/llvm/tools/llvm-objcopy/Object.cpp1118
-rw-r--r--contrib/llvm/tools/llvm-objcopy/Object.h508
-rw-r--r--contrib/llvm/tools/llvm-objcopy/StripOpts.td49
-rw-r--r--contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp675
-rw-r--r--contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h5
-rw-r--r--contrib/llvm/tools/llvm-objdump/COFFDump.cpp4
-rw-r--r--contrib/llvm/tools/llvm-objdump/ELFDump.cpp91
-rw-r--r--contrib/llvm/tools/llvm-objdump/MachODump.cpp407
-rw-r--r--contrib/llvm/tools/llvm-objdump/WasmDump.cpp2
-rw-r--r--contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp1073
-rw-r--r--contrib/llvm/tools/llvm-objdump/llvm-objdump.h7
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/Analyze.cpp2
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/Diff.cpp644
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/Diff.h45
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp147
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h172
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp389
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h4
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp469
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h68
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/InputFile.cpp44
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/InputFile.h11
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp13
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h10
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp25
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp9
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp7
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp95
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp9
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp5
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp3
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp3
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp6
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp81
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/StreamUtil.h15
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp478
-rw-r--r--contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h25
-rw-r--r--contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp44
-rw-r--r--contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h13
-rw-r--r--contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp2
-rw-r--r--contrib/llvm/tools/llvm-readobj/COFFDumper.cpp29
-rw-r--r--contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp40
-rw-r--r--contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h245
-rw-r--r--contrib/llvm/tools/llvm-readobj/ELFDumper.cpp734
-rw-r--r--contrib/llvm/tools/llvm-readobj/MachODumper.cpp9
-rw-r--r--contrib/llvm/tools/llvm-readobj/ObjDumper.cpp122
-rw-r--r--contrib/llvm/tools/llvm-readobj/ObjDumper.h13
-rw-r--r--contrib/llvm/tools/llvm-readobj/StackMapPrinter.h49
-rw-r--r--contrib/llvm/tools/llvm-readobj/WasmDumper.cpp40
-rw-r--r--contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp102
-rw-r--r--contrib/llvm/tools/llvm-readobj/llvm-readobj.h1
-rw-r--r--contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp11
-rw-r--r--contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp26
-rw-r--r--contrib/llvm/tools/llvm-xray/func-id-helper.cpp (renamed from contrib/llvm/tools/llvm-xray/func-id-helper.cc)10
-rw-r--r--contrib/llvm/tools/llvm-xray/func-id-helper.h2
-rw-r--r--contrib/llvm/tools/llvm-xray/llvm-xray.cpp (renamed from contrib/llvm/tools/llvm-xray/llvm-xray.cc)2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-account.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-account.cc)145
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-account.h8
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-color-helper.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-color-helper.cc)2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-converter.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-converter.cc)55
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-extract.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-extract.cc)2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-graph-diff.cc)2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-graph.cc)2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-graph.h2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-registry.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-registry.cc)2
-rw-r--r--contrib/llvm/tools/llvm-xray/xray-stacks.cpp (renamed from contrib/llvm/tools/llvm-xray/xray-stacks.cc)2
-rw-r--r--contrib/llvm/tools/opt/BreakpointPrinter.cpp2
-rw-r--r--contrib/llvm/tools/opt/BreakpointPrinter.h2
-rw-r--r--contrib/llvm/tools/opt/Debugify.cpp357
-rw-r--r--contrib/llvm/tools/opt/Debugify.h75
-rw-r--r--contrib/llvm/tools/opt/NewPMDriver.cpp53
-rw-r--r--contrib/llvm/tools/opt/NewPMDriver.h7
-rw-r--r--contrib/llvm/tools/opt/PassPrinters.cpp2
-rw-r--r--contrib/llvm/tools/opt/PassPrinters.h5
-rw-r--r--contrib/llvm/tools/opt/opt.cpp158
186 files changed, 16384 insertions, 5085 deletions
diff --git a/contrib/llvm/tools/bugpoint/BugDriver.cpp b/contrib/llvm/tools/bugpoint/BugDriver.cpp
index 37bdb7bc96b6..3832e075a693 100644
--- a/contrib/llvm/tools/bugpoint/BugDriver.cpp
+++ b/contrib/llvm/tools/bugpoint/BugDriver.cpp
@@ -55,12 +55,11 @@ cl::opt<std::string> OutputFile("output",
"(for miscompilation detection)"));
}
-/// setNewProgram - If we reduce or update the program somehow, call this method
-/// to update bugdriver with it. This deletes the old module and sets the
-/// specified one as the current program.
-void BugDriver::setNewProgram(Module *M) {
- delete Program;
- Program = M;
+/// If we reduce or update the program somehow, call this method to update
+/// bugdriver with it. This deletes the old module and sets the specified one
+/// as the current program.
+void BugDriver::setNewProgram(std::unique_ptr<Module> M) {
+ Program = std::move(M);
}
/// getPassesString - Turn a list of passes into a string which indicates the
@@ -85,7 +84,6 @@ BugDriver::BugDriver(const char *toolname, bool find_bugs, unsigned timeout,
MemoryLimit(memlimit), UseValgrind(use_valgrind) {}
BugDriver::~BugDriver() {
- delete Program;
if (Interpreter != SafeInterpreter)
delete Interpreter;
delete SafeInterpreter;
@@ -121,6 +119,12 @@ std::unique_ptr<Module> llvm::parseInputFile(StringRef Filename,
return Result;
}
+std::unique_ptr<Module> BugDriver::swapProgramIn(std::unique_ptr<Module> M) {
+ std::unique_ptr<Module> OldProgram = std::move(Program);
+ Program = std::move(M);
+ return OldProgram;
+}
+
// This method takes the specified list of LLVM input files, attempts to load
// them, either as assembly or bitcode, then link them together. It returns
// true on failure (if, for example, an input bitcode file could not be
@@ -131,7 +135,7 @@ bool BugDriver::addSources(const std::vector<std::string> &Filenames) {
assert(!Filenames.empty() && "Must specify at least on input filename!");
// Load the first input file.
- Program = parseInputFile(Filenames[0], Context).release();
+ Program = parseInputFile(Filenames[0], Context);
if (!Program)
return true;
@@ -172,7 +176,7 @@ Error BugDriver::run() {
// miscompilation.
if (!PassesToRun.empty()) {
outs() << "Running selected passes on program to test for crash: ";
- if (runPasses(Program, PassesToRun))
+ if (runPasses(*Program, PassesToRun))
return debugOptimizerCrash();
}
@@ -182,7 +186,7 @@ Error BugDriver::run() {
// Test to see if we have a code generator crash.
outs() << "Running the code generator to test for a crash: ";
- if (Error E = compileProgram(Program)) {
+ if (Error E = compileProgram(*Program)) {
outs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
@@ -195,7 +199,7 @@ Error BugDriver::run() {
bool CreatedOutput = false;
if (ReferenceOutputFile.empty()) {
outs() << "Generating reference output from raw program: ";
- if (Error E = createReferenceFile(Program)) {
+ if (Error E = createReferenceFile(*Program)) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
}
@@ -211,7 +215,7 @@ Error BugDriver::run() {
// matches, then we assume there is a miscompilation bug and try to
// diagnose it.
outs() << "*** Checking the code generator...\n";
- Expected<bool> Diff = diffProgram(Program, "", "", false);
+ Expected<bool> Diff = diffProgram(*Program, "", "", false);
if (Error E = Diff.takeError()) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
diff --git a/contrib/llvm/tools/bugpoint/BugDriver.h b/contrib/llvm/tools/bugpoint/BugDriver.h
index 0e6a9b4f2f38..bc60ae753548 100644
--- a/contrib/llvm/tools/bugpoint/BugDriver.h
+++ b/contrib/llvm/tools/bugpoint/BugDriver.h
@@ -50,7 +50,7 @@ class BugDriver {
LLVMContext &Context;
const char *ToolName; // argv[0] of bugpoint
std::string ReferenceOutputFile; // Name of `good' output file
- Module *Program; // The raw program, linked together
+ std::unique_ptr<Module> Program; // The raw program, linked together
std::vector<std::string> PassesToRun;
AbstractInterpreter *Interpreter; // How to run the program
AbstractInterpreter *SafeInterpreter; // To generate reference output, etc.
@@ -128,15 +128,10 @@ public:
///
bool isExecutingJIT();
- Module *getProgram() const { return Program; }
+ Module &getProgram() const { return *Program; }
- /// swapProgramIn - Set the current module to the specified module, returning
- /// the old one.
- Module *swapProgramIn(Module *M) {
- Module *OldProgram = Program;
- Program = M;
- return OldProgram;
- }
+ /// Set the current module to the specified module, returning the old one.
+ std::unique_ptr<Module> swapProgramIn(std::unique_ptr<Module> M);
AbstractInterpreter *switchToSafeInterpreter() {
AbstractInterpreter *Old = Interpreter;
@@ -146,55 +141,47 @@ public:
void switchToInterpreter(AbstractInterpreter *AI) { Interpreter = AI; }
- /// setNewProgram - If we reduce or update the program somehow, call this
- /// method to update bugdriver with it. This deletes the old module and sets
- /// the specified one as the current program.
- void setNewProgram(Module *M);
+ /// If we reduce or update the program somehow, call this method to update
+ /// bugdriver with it. This deletes the old module and sets the specified one
+ /// as the current program.
+ void setNewProgram(std::unique_ptr<Module> M);
/// Try to compile the specified module. This is used for code generation
/// crash testing.
- Error compileProgram(Module *M) const;
+ Error compileProgram(Module &M) const;
- /// executeProgram - This method runs "Program", capturing the output of the
- /// program to a file. A recommended filename may be optionally specified.
- ///
- Expected<std::string> executeProgram(const Module *Program,
+ /// This method runs "Program", capturing the output of the program to a file.
+ /// A recommended filename may be optionally specified.
+ Expected<std::string> executeProgram(const Module &Program,
std::string OutputFilename,
std::string Bitcode,
const std::string &SharedObjects,
AbstractInterpreter *AI) const;
- /// executeProgramSafely - Used to create reference output with the "safe"
- /// backend, if reference output is not provided. If there is a problem with
- /// the code generator (e.g., llc crashes), this will return false and set
- /// Error.
- ///
+ /// Used to create reference output with the "safe" backend, if reference
+ /// output is not provided. If there is a problem with the code generator
+ /// (e.g., llc crashes), this will return false and set Error.
Expected<std::string>
- executeProgramSafely(const Module *Program,
+ executeProgramSafely(const Module &Program,
const std::string &OutputFile) const;
- /// createReferenceFile - calls compileProgram and then records the output
- /// into ReferenceOutputFile. Returns true if reference file created, false
- /// otherwise. Note: initializeExecutionEnvironment should be called BEFORE
- /// this function.
- ///
- Error createReferenceFile(Module *M, const std::string &Filename =
+ /// Calls compileProgram and then records the output into ReferenceOutputFile.
+ /// Returns true if reference file created, false otherwise. Note:
+ /// initializeExecutionEnvironment should be called BEFORE this function.
+ Error createReferenceFile(Module &M, const std::string &Filename =
"bugpoint.reference.out-%%%%%%%");
- /// diffProgram - This method executes the specified module and diffs the
- /// output against the file specified by ReferenceOutputFile. If the output
- /// is different, 1 is returned. If there is a problem with the code
- /// generator (e.g., llc crashes), this will return -1 and set Error.
- ///
- Expected<bool> diffProgram(const Module *Program,
+ /// This method executes the specified module and diffs the output against the
+ /// file specified by ReferenceOutputFile. If the output is different, 1 is
+ /// returned. If there is a problem with the code generator (e.g., llc
+ /// crashes), this will return -1 and set Error.
+ Expected<bool> diffProgram(const Module &Program,
const std::string &BitcodeFile = "",
const std::string &SharedObj = "",
bool RemoveBitcode = false) const;
- /// EmitProgressBitcode - This function is used to output M to a file named
- /// "bugpoint-ID.bc".
- ///
- void EmitProgressBitcode(const Module *M, const std::string &ID,
+ /// This function is used to output M to a file named "bugpoint-ID.bc".
+ void EmitProgressBitcode(const Module &M, const std::string &ID,
bool NoFlyer = false) const;
/// This method clones the current Program and deletes the specified
@@ -210,7 +197,7 @@ public:
/// MayModifySemantics argument is true, then the cleanups is allowed to
/// modify how the code behaves.
///
- std::unique_ptr<Module> performFinalCleanups(Module *M,
+ std::unique_ptr<Module> performFinalCleanups(std::unique_ptr<Module> M,
bool MayModifySemantics = false);
/// Given a module, extract up to one loop from it into a new function. This
@@ -243,7 +230,7 @@ public:
/// or failed, unless Quiet is set. ExtraArgs specifies additional arguments
/// to pass to the child bugpoint instance.
///
- bool runPasses(Module *Program, const std::vector<std::string> &PassesToRun,
+ bool runPasses(Module &Program, const std::vector<std::string> &PassesToRun,
std::string &OutputFilename, bool DeleteOutput = false,
bool Quiet = false, unsigned NumExtraArgs = 0,
const char *const *ExtraArgs = nullptr) const;
@@ -252,7 +239,7 @@ public:
/// false indicating whether or not the optimizer crashed on the specified
/// input (true = crashed). Does not produce any output.
///
- bool runPasses(Module *M, const std::vector<std::string> &PassesToRun) const {
+ bool runPasses(Module &M, const std::vector<std::string> &PassesToRun) const {
std::string Filename;
return runPasses(M, PassesToRun, Filename, true);
}
@@ -265,13 +252,12 @@ public:
/// failure.
Error runManyPasses(const std::vector<std::string> &AllPasses);
- /// writeProgramToFile - This writes the current "Program" to the named
- /// bitcode file. If an error occurs, true is returned.
- ///
- bool writeProgramToFile(const std::string &Filename, const Module *M) const;
+ /// This writes the current "Program" to the named bitcode file. If an error
+ /// occurs, true is returned.
+ bool writeProgramToFile(const std::string &Filename, const Module &M) const;
bool writeProgramToFile(const std::string &Filename, int FD,
- const Module *M) const;
- bool writeProgramToFile(int FD, const Module *M) const;
+ const Module &M) const;
+ bool writeProgramToFile(int FD, const Module &M) const;
private:
/// initializeExecutionEnvironment - This method is used to set up the
diff --git a/contrib/llvm/tools/bugpoint/CrashDebugger.cpp b/contrib/llvm/tools/bugpoint/CrashDebugger.cpp
index 9097917d5fef..a5b31e1ab321 100644
--- a/contrib/llvm/tools/bugpoint/CrashDebugger.cpp
+++ b/contrib/llvm/tools/bugpoint/CrashDebugger.cpp
@@ -17,6 +17,7 @@
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/Analysis/TargetTransformInfo.h"
+#include "llvm/Transforms/Utils/Local.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/DebugInfo.h"
@@ -32,7 +33,6 @@
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
#include "llvm/Transforms/Utils/Cloning.h"
-#include "llvm/Transforms/Utils/Local.h"
#include <set>
using namespace llvm;
@@ -85,16 +85,16 @@ Expected<ReducePassList::TestResult>
ReducePassList::doTest(std::vector<std::string> &Prefix,
std::vector<std::string> &Suffix) {
std::string PrefixOutput;
- Module *OrigProgram = nullptr;
+ std::unique_ptr<Module> OrigProgram;
if (!Prefix.empty()) {
outs() << "Checking to see if these passes crash: "
<< getPassesString(Prefix) << ": ";
if (BD.runPasses(BD.getProgram(), Prefix, PrefixOutput))
return KeepPrefix;
- OrigProgram = BD.Program;
+ OrigProgram = std::move(BD.Program);
- BD.Program = parseInputFile(PrefixOutput, BD.getContext()).release();
+ BD.Program = parseInputFile(PrefixOutput, BD.getContext());
if (BD.Program == nullptr) {
errs() << BD.getToolName() << ": Error reading bitcode file '"
<< PrefixOutput << "'!\n";
@@ -106,31 +106,27 @@ ReducePassList::doTest(std::vector<std::string> &Prefix,
outs() << "Checking to see if these passes crash: " << getPassesString(Suffix)
<< ": ";
- if (BD.runPasses(BD.getProgram(), Suffix)) {
- delete OrigProgram; // The suffix crashes alone...
- return KeepSuffix;
- }
+ if (BD.runPasses(BD.getProgram(), Suffix))
+ return KeepSuffix; // The suffix crashes alone...
// Nothing failed, restore state...
- if (OrigProgram) {
- delete BD.Program;
- BD.Program = OrigProgram;
- }
+ if (OrigProgram)
+ BD.Program = std::move(OrigProgram);
return NoFailure;
}
+using BugTester = bool (*)(const BugDriver &, Module *);
+
namespace {
-/// ReduceCrashingGlobalVariables - This works by removing the global
-/// variable's initializer and seeing if the program still crashes. If it
-/// does, then we keep that program and try again.
-///
-class ReduceCrashingGlobalVariables : public ListReducer<GlobalVariable *> {
+/// ReduceCrashingGlobalInitializers - This works by removing global variable
+/// initializers and seeing if the program still crashes. If it does, then we
+/// keep that program and try again.
+class ReduceCrashingGlobalInitializers : public ListReducer<GlobalVariable *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
public:
- ReduceCrashingGlobalVariables(BugDriver &bd,
- bool (*testFn)(const BugDriver &, Module *))
+ ReduceCrashingGlobalInitializers(BugDriver &bd, BugTester testFn)
: BD(bd), TestFn(testFn) {}
Expected<TestResult> doTest(std::vector<GlobalVariable *> &Prefix,
@@ -146,11 +142,11 @@ public:
};
}
-bool ReduceCrashingGlobalVariables::TestGlobalVariables(
+bool ReduceCrashingGlobalInitializers::TestGlobalVariables(
std::vector<GlobalVariable *> &GVs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// Convert list to set for fast lookup...
std::set<GlobalVariable *> GVSet;
@@ -175,8 +171,8 @@ bool ReduceCrashingGlobalVariables::TestGlobalVariables(
}
// Try running the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
// Make sure to use global variable pointers that point into the now-current
// module.
@@ -184,7 +180,6 @@ bool ReduceCrashingGlobalVariables::TestGlobalVariables(
return true;
}
- delete M;
return false;
}
@@ -195,11 +190,10 @@ namespace {
///
class ReduceCrashingFunctions : public ListReducer<Function *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
public:
- ReduceCrashingFunctions(BugDriver &bd,
- bool (*testFn)(const BugDriver &, Module *))
+ ReduceCrashingFunctions(BugDriver &bd, BugTester testFn)
: BD(bd), TestFn(testFn) {}
Expected<TestResult> doTest(std::vector<Function *> &Prefix,
@@ -241,12 +235,12 @@ static void RemoveFunctionReferences(Module *M, const char *Name) {
bool ReduceCrashingFunctions::TestFuncs(std::vector<Function *> &Funcs) {
// If main isn't present, claim there is no problem.
- if (KeepMain && !is_contained(Funcs, BD.getProgram()->getFunction("main")))
+ if (KeepMain && !is_contained(Funcs, BD.getProgram().getFunction("main")))
return false;
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// Convert list to set for fast lookup...
std::set<Function *> Functions;
@@ -305,19 +299,18 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function *> &Funcs) {
}
// Finally, remove any null members from any global intrinsic.
- RemoveFunctionReferences(M, "llvm.used");
- RemoveFunctionReferences(M, "llvm.compiler.used");
+ RemoveFunctionReferences(M.get(), "llvm.used");
+ RemoveFunctionReferences(M.get(), "llvm.compiler.used");
}
// Try running the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
// Make sure to use function pointers that point into the now-current
// module.
Funcs.assign(Functions.begin(), Functions.end());
return true;
}
- delete M;
return false;
}
@@ -368,11 +361,10 @@ void simpleSimplifyCfg(Function &F, SmallVectorImpl<BasicBlock *> &BBs) {
///
class ReduceCrashingBlocks : public ListReducer<const BasicBlock *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
public:
- ReduceCrashingBlocks(BugDriver &BD,
- bool (*testFn)(const BugDriver &, Module *))
+ ReduceCrashingBlocks(BugDriver &BD, BugTester testFn)
: BD(BD), TestFn(testFn) {}
Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix,
@@ -391,7 +383,7 @@ public:
bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// Convert list to set for fast lookup...
SmallPtrSet<BasicBlock *, 8> Blocks;
@@ -409,31 +401,32 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) {
outs() << ": ";
// Loop over and delete any hack up any blocks that are not listed...
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
- for (Function::iterator BB = I->begin(), E = I->end(); BB != E; ++BB)
- if (!Blocks.count(&*BB) && BB->getTerminator()->getNumSuccessors()) {
+ for (Function &F : M->functions()) {
+ for (BasicBlock &BB : F) {
+ if (!Blocks.count(&BB) && BB.getTerminator()->getNumSuccessors()) {
// Loop over all of the successors of this block, deleting any PHI nodes
// that might include it.
- for (succ_iterator SI = succ_begin(&*BB), E = succ_end(&*BB); SI != E;
- ++SI)
- (*SI)->removePredecessor(&*BB);
+ for (BasicBlock *Succ : successors(&BB))
+ Succ->removePredecessor(&BB);
- TerminatorInst *BBTerm = BB->getTerminator();
+ TerminatorInst *BBTerm = BB.getTerminator();
if (BBTerm->isEHPad() || BBTerm->getType()->isTokenTy())
continue;
if (!BBTerm->getType()->isVoidTy())
BBTerm->replaceAllUsesWith(Constant::getNullValue(BBTerm->getType()));
// Replace the old terminator instruction.
- BB->getInstList().pop_back();
- new UnreachableInst(BB->getContext(), &*BB);
+ BB.getInstList().pop_back();
+ new UnreachableInst(BB.getContext(), &BB);
}
+ }
+ }
// The CFG Simplifier pass may delete one of the basic blocks we are
// interested in. If it does we need to take the block out of the list. Make
// a "persistent mapping" by turning basic blocks into <function, name> pairs.
// This won't work well if blocks are unnamed, but that is just the risk we
- // have to take.
+ // have to take. FIXME: Can we just name the blocks?
std::vector<std::pair<std::string, std::string>> BlockInfo;
for (BasicBlock *BB : Blocks)
@@ -450,31 +443,30 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) {
// Verify we didn't break anything
std::vector<std::string> Passes;
Passes.push_back("verify");
- std::unique_ptr<Module> New = BD.runPassesOn(M, Passes);
- delete M;
+ std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes);
if (!New) {
errs() << "verify failed!\n";
exit(1);
}
- M = New.release();
+ M = std::move(New);
// Try running on the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
// Make sure to use basic block pointers that point into the now-current
// module, and that they don't include any deleted blocks.
BBs.clear();
- const ValueSymbolTable &GST = M->getValueSymbolTable();
- for (unsigned i = 0, e = BlockInfo.size(); i != e; ++i) {
- Function *F = cast<Function>(GST.lookup(BlockInfo[i].first));
- Value *V = F->getValueSymbolTable()->lookup(BlockInfo[i].second);
+ const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable();
+ for (const auto &BI : BlockInfo) {
+ Function *F = cast<Function>(GST.lookup(BI.first));
+ Value *V = F->getValueSymbolTable()->lookup(BI.second);
if (V && V->getType() == Type::getLabelTy(V->getContext()))
BBs.push_back(cast<BasicBlock>(V));
}
return true;
}
- delete M; // It didn't crash, try something else.
+ // It didn't crash, try something else.
return false;
}
@@ -486,13 +478,11 @@ namespace {
///
class ReduceCrashingConditionals : public ListReducer<const BasicBlock *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
bool Direction;
public:
- ReduceCrashingConditionals(BugDriver &bd,
- bool (*testFn)(const BugDriver &, Module *),
- bool Direction)
+ ReduceCrashingConditionals(BugDriver &bd, BugTester testFn, bool Direction)
: BD(bd), TestFn(testFn), Direction(Direction) {}
Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix,
@@ -512,7 +502,7 @@ bool ReduceCrashingConditionals::TestBlocks(
std::vector<const BasicBlock *> &BBs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// Convert list to set for fast lookup...
SmallPtrSet<const BasicBlock *, 8> Blocks;
@@ -560,22 +550,21 @@ bool ReduceCrashingConditionals::TestBlocks(
// Verify we didn't break anything
std::vector<std::string> Passes;
Passes.push_back("verify");
- std::unique_ptr<Module> New = BD.runPassesOn(M, Passes);
- delete M;
+ std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes);
if (!New) {
errs() << "verify failed!\n";
exit(1);
}
- M = New.release();
+ M = std::move(New);
// Try running on the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
// Make sure to use basic block pointers that point into the now-current
// module, and that they don't include any deleted blocks.
BBs.clear();
- const ValueSymbolTable &GST = M->getValueSymbolTable();
+ const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable();
for (auto &BI : BlockInfo) {
auto *F = cast<Function>(GST.lookup(BI.first));
Value *V = F->getValueSymbolTable()->lookup(BI.second);
@@ -584,7 +573,7 @@ bool ReduceCrashingConditionals::TestBlocks(
}
return true;
}
- delete M; // It didn't crash, try something else.
+ // It didn't crash, try something else.
return false;
}
@@ -594,12 +583,12 @@ namespace {
class ReduceSimplifyCFG : public ListReducer<const BasicBlock *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
TargetTransformInfo TTI;
public:
- ReduceSimplifyCFG(BugDriver &bd, bool (*testFn)(const BugDriver &, Module *))
- : BD(bd), TestFn(testFn), TTI(bd.getProgram()->getDataLayout()) {}
+ ReduceSimplifyCFG(BugDriver &bd, BugTester testFn)
+ : BD(bd), TestFn(testFn), TTI(bd.getProgram().getDataLayout()) {}
Expected<TestResult> doTest(std::vector<const BasicBlock *> &Prefix,
std::vector<const BasicBlock *> &Kept) override {
@@ -617,7 +606,7 @@ public:
bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// Convert list to set for fast lookup...
SmallPtrSet<const BasicBlock *, 8> Blocks;
@@ -653,22 +642,21 @@ bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) {
// Verify we didn't break anything
std::vector<std::string> Passes;
Passes.push_back("verify");
- std::unique_ptr<Module> New = BD.runPassesOn(M, Passes);
- delete M;
+ std::unique_ptr<Module> New = BD.runPassesOn(M.get(), Passes);
if (!New) {
errs() << "verify failed!\n";
exit(1);
}
- M = New.release();
+ M = std::move(New);
// Try running on the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
// Make sure to use basic block pointers that point into the now-current
// module, and that they don't include any deleted blocks.
BBs.clear();
- const ValueSymbolTable &GST = M->getValueSymbolTable();
+ const ValueSymbolTable &GST = BD.getProgram().getValueSymbolTable();
for (auto &BI : BlockInfo) {
auto *F = cast<Function>(GST.lookup(BI.first));
Value *V = F->getValueSymbolTable()->lookup(BI.second);
@@ -677,7 +665,7 @@ bool ReduceSimplifyCFG::TestBlocks(std::vector<const BasicBlock *> &BBs) {
}
return true;
}
- delete M; // It didn't crash, try something else.
+ // It didn't crash, try something else.
return false;
}
@@ -687,11 +675,10 @@ namespace {
///
class ReduceCrashingInstructions : public ListReducer<const Instruction *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
public:
- ReduceCrashingInstructions(BugDriver &bd,
- bool (*testFn)(const BugDriver &, Module *))
+ ReduceCrashingInstructions(BugDriver &bd, BugTester testFn)
: BD(bd), TestFn(testFn) {}
Expected<TestResult> doTest(std::vector<const Instruction *> &Prefix,
@@ -711,7 +698,7 @@ bool ReduceCrashingInstructions::TestInsts(
std::vector<const Instruction *> &Insts) {
// Clone the program to try hacking it apart...
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// Convert list to set for fast lookup...
SmallPtrSet<Instruction *, 32> Instructions;
@@ -745,8 +732,8 @@ bool ReduceCrashingInstructions::TestInsts(
Passes.run(*M);
// Try running on the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
// Make sure to use instruction pointers that point into the now-current
// module, and that they don't include any deleted blocks.
@@ -755,7 +742,7 @@ bool ReduceCrashingInstructions::TestInsts(
Insts.push_back(Inst);
return true;
}
- delete M; // It didn't crash, try something else.
+ // It didn't crash, try something else.
return false;
}
@@ -764,11 +751,10 @@ namespace {
// names to avoid having to convert back and forth every time.
class ReduceCrashingNamedMD : public ListReducer<std::string> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
public:
- ReduceCrashingNamedMD(BugDriver &bd,
- bool (*testFn)(const BugDriver &, Module *))
+ ReduceCrashingNamedMD(BugDriver &bd, BugTester testFn)
: BD(bd), TestFn(testFn) {}
Expected<TestResult> doTest(std::vector<std::string> &Prefix,
@@ -787,7 +773,7 @@ public:
bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) {
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
outs() << "Checking for crash with only these named metadata nodes:";
unsigned NumPrint = std::min<size_t>(NamedMDs.size(), 10);
@@ -821,11 +807,10 @@ bool ReduceCrashingNamedMD::TestNamedMDs(std::vector<std::string> &NamedMDs) {
Passes.run(*M);
// Try running on the hacked up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ if (TestFn(BD, M.get())) {
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
return true;
}
- delete M; // It didn't crash, try something else.
return false;
}
@@ -833,11 +818,10 @@ namespace {
// Reduce the list of operands to named metadata nodes
class ReduceCrashingNamedMDOps : public ListReducer<const MDNode *> {
BugDriver &BD;
- bool (*TestFn)(const BugDriver &, Module *);
+ BugTester TestFn;
public:
- ReduceCrashingNamedMDOps(BugDriver &bd,
- bool (*testFn)(const BugDriver &, Module *))
+ ReduceCrashingNamedMDOps(BugDriver &bd, BugTester testFn)
: BD(bd), TestFn(testFn) {}
Expected<TestResult> doTest(std::vector<const MDNode *> &Prefix,
@@ -868,11 +852,11 @@ bool ReduceCrashingNamedMDOps::TestNamedMDOps(
outs() << " named metadata operands: ";
ValueToValueMapTy VMap;
- Module *M = CloneModule(BD.getProgram(), VMap).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram(), VMap);
// This is a little wasteful. In the future it might be good if we could have
// these dropped during cloning.
- for (auto &NamedMD : BD.getProgram()->named_metadata()) {
+ for (auto &NamedMD : BD.getProgram().named_metadata()) {
// Drop the old one and create a new one
M->eraseNamedMetadata(M->getNamedMetadata(NamedMD.getName()));
NamedMDNode *NewNamedMDNode =
@@ -888,85 +872,82 @@ bool ReduceCrashingNamedMDOps::TestNamedMDOps(
Passes.run(*M);
// Try running on the hacked up program...
- if (TestFn(BD, M)) {
+ if (TestFn(BD, M.get())) {
// Make sure to use instruction pointers that point into the now-current
// module, and that they don't include any deleted blocks.
NamedMDOps.clear();
for (const MDNode *Node : OldMDNodeOps)
NamedMDOps.push_back(cast<MDNode>(*VMap.getMappedMD(Node)));
- BD.setNewProgram(M); // It crashed, keep the trimmed version...
+ BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version...
return true;
}
- delete M; // It didn't crash, try something else.
+ // It didn't crash, try something else.
return false;
}
-static Error ReduceGlobalInitializers(BugDriver &BD,
- bool (*TestFn)(const BugDriver &,
- Module *)) {
- if (BD.getProgram()->global_begin() != BD.getProgram()->global_end()) {
- // Now try to reduce the number of global variable initializers in the
- // module to something small.
- Module *M = CloneModule(BD.getProgram()).release();
- bool DeletedInit = false;
-
- for (Module::global_iterator I = M->global_begin(), E = M->global_end();
- I != E; ++I)
- if (I->hasInitializer()) {
- DeleteGlobalInitializer(&*I);
- I->setLinkage(GlobalValue::ExternalLinkage);
- I->setComdat(nullptr);
- DeletedInit = true;
- }
-
- if (!DeletedInit) {
- delete M; // No change made...
- } else {
- // See if the program still causes a crash...
- outs() << "\nChecking to see if we can delete global inits: ";
-
- if (TestFn(BD, M)) { // Still crashes?
- BD.setNewProgram(M);
- outs() << "\n*** Able to remove all global initializers!\n";
- } else { // No longer crashes?
- outs() << " - Removing all global inits hides problem!\n";
- delete M;
-
- std::vector<GlobalVariable *> GVs;
-
- for (Module::global_iterator I = BD.getProgram()->global_begin(),
- E = BD.getProgram()->global_end();
- I != E; ++I)
- if (I->hasInitializer())
- GVs.push_back(&*I);
-
- if (GVs.size() > 1 && !BugpointIsInterrupted) {
- outs() << "\n*** Attempting to reduce the number of global "
- << "variables in the testcase\n";
-
- unsigned OldSize = GVs.size();
- Expected<bool> Result =
- ReduceCrashingGlobalVariables(BD, TestFn).reduceList(GVs);
- if (Error E = Result.takeError())
- return E;
-
- if (GVs.size() < OldSize)
- BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables");
- }
- }
+/// Attempt to eliminate as many global initializers as possible.
+static Error ReduceGlobalInitializers(BugDriver &BD, BugTester TestFn) {
+ Module &OrigM = BD.getProgram();
+ if (OrigM.global_empty())
+ return Error::success();
+
+ // Now try to reduce the number of global variable initializers in the
+ // module to something small.
+ std::unique_ptr<Module> M = CloneModule(OrigM);
+ bool DeletedInit = false;
+
+ for (GlobalVariable &GV : M->globals()) {
+ if (GV.hasInitializer()) {
+ DeleteGlobalInitializer(&GV);
+ GV.setLinkage(GlobalValue::ExternalLinkage);
+ GV.setComdat(nullptr);
+ DeletedInit = true;
}
}
+
+ if (!DeletedInit)
+ return Error::success();
+
+ // See if the program still causes a crash...
+ outs() << "\nChecking to see if we can delete global inits: ";
+
+ if (TestFn(BD, M.get())) { // Still crashes?
+ BD.setNewProgram(std::move(M));
+ outs() << "\n*** Able to remove all global initializers!\n";
+ return Error::success();
+ }
+
+ // No longer crashes.
+ outs() << " - Removing all global inits hides problem!\n";
+
+ std::vector<GlobalVariable *> GVs;
+ for (GlobalVariable &GV : OrigM.globals())
+ if (GV.hasInitializer())
+ GVs.push_back(&GV);
+
+ if (GVs.size() > 1 && !BugpointIsInterrupted) {
+ outs() << "\n*** Attempting to reduce the number of global initializers "
+ << "in the testcase\n";
+
+ unsigned OldSize = GVs.size();
+ Expected<bool> Result =
+ ReduceCrashingGlobalInitializers(BD, TestFn).reduceList(GVs);
+ if (Error E = Result.takeError())
+ return E;
+
+ if (GVs.size() < OldSize)
+ BD.EmitProgressBitcode(BD.getProgram(), "reduced-global-variables");
+ }
return Error::success();
}
-static Error ReduceInsts(BugDriver &BD,
- bool (*TestFn)(const BugDriver &, Module *)) {
+static Error ReduceInsts(BugDriver &BD, BugTester TestFn) {
// Attempt to delete instructions using bisection. This should help out nasty
// cases with large basic blocks where the problem is at one end.
if (!BugpointIsInterrupted) {
std::vector<const Instruction *> Insts;
- for (const Function &F : *BD.getProgram())
+ for (const Function &F : BD.getProgram())
for (const BasicBlock &BB : F)
for (const Instruction &I : BB)
if (!isa<TerminatorInst>(&I))
@@ -1001,8 +982,8 @@ static Error ReduceInsts(BugDriver &BD,
// Loop over all of the (non-terminator) instructions remaining in the
// function, attempting to delete them.
unsigned CurInstructionNum = 0;
- for (Module::const_iterator FI = BD.getProgram()->begin(),
- E = BD.getProgram()->end();
+ for (Module::const_iterator FI = BD.getProgram().begin(),
+ E = BD.getProgram().end();
FI != E; ++FI)
if (!FI->isDeclaration())
for (Function::const_iterator BI = FI->begin(), E = FI->end(); BI != E;
@@ -1028,7 +1009,7 @@ static Error ReduceInsts(BugDriver &BD,
if (TestFn(BD, M.get())) {
// Yup, it does, we delete the old module, and continue trying
// to reduce the testcase...
- BD.setNewProgram(M.release());
+ BD.setNewProgram(std::move(M));
InstructionsToSkipBeforeDeleting = CurInstructionNum;
goto TryAgain; // I wish I had a multi-level break here!
}
@@ -1048,8 +1029,7 @@ static Error ReduceInsts(BugDriver &BD,
/// DebugACrash - Given a predicate that determines whether a component crashes
/// on a program, try to destructively reduce the program while still keeping
/// the predicate true.
-static Error DebugACrash(BugDriver &BD,
- bool (*TestFn)(const BugDriver &, Module *)) {
+static Error DebugACrash(BugDriver &BD, BugTester TestFn) {
// See if we can get away with nuking some of the global variable initializers
// in the program...
if (!NoGlobalRM)
@@ -1058,7 +1038,7 @@ static Error DebugACrash(BugDriver &BD,
// Now try to reduce the number of functions in the module to something small.
std::vector<Function *> Functions;
- for (Function &F : *BD.getProgram())
+ for (Function &F : BD.getProgram())
if (!F.isDeclaration())
Functions.push_back(&F);
@@ -1080,7 +1060,7 @@ static Error DebugACrash(BugDriver &BD,
// eliminate blocks.
if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
std::vector<const BasicBlock *> Blocks;
- for (Function &F : *BD.getProgram())
+ for (Function &F : BD.getProgram())
for (BasicBlock &BB : F)
Blocks.push_back(&BB);
unsigned OldSize = Blocks.size();
@@ -1102,7 +1082,7 @@ static Error DebugACrash(BugDriver &BD,
//
if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
std::vector<const BasicBlock *> Blocks;
- for (Function &F : *BD.getProgram())
+ for (Function &F : BD.getProgram())
for (BasicBlock &BB : F)
Blocks.push_back(&BB);
unsigned OldSize = Blocks.size();
@@ -1115,7 +1095,7 @@ static Error DebugACrash(BugDriver &BD,
if (!DisableSimplifyCFG && !BugpointIsInterrupted) {
std::vector<const BasicBlock *> Blocks;
- for (Function &F : *BD.getProgram())
+ for (Function &F : BD.getProgram())
for (BasicBlock &BB : F)
Blocks.push_back(&BB);
unsigned OldSize = Blocks.size();
@@ -1137,7 +1117,7 @@ static Error DebugACrash(BugDriver &BD,
std::unique_ptr<Module> M = CloneModule(BD.getProgram());
strip(*M);
if (TestFn(BD, M.get()))
- BD.setNewProgram(M.release());
+ BD.setNewProgram(std::move(M));
};
if (!NoStripDebugInfo && !BugpointIsInterrupted) {
outs() << "\n*** Attempting to strip the debug info: ";
@@ -1154,7 +1134,7 @@ static Error DebugACrash(BugDriver &BD,
// by dropping global named metadata that anchors them
outs() << "\n*** Attempting to remove named metadata: ";
std::vector<std::string> NamedMDNames;
- for (auto &NamedMD : BD.getProgram()->named_metadata())
+ for (auto &NamedMD : BD.getProgram().named_metadata())
NamedMDNames.push_back(NamedMD.getName().str());
Expected<bool> Result =
ReduceCrashingNamedMD(BD, TestFn).reduceList(NamedMDNames);
@@ -1166,7 +1146,7 @@ static Error DebugACrash(BugDriver &BD,
// Now that we quickly dropped all the named metadata that doesn't
// contribute to the crash, bisect the operands of the remaining ones
std::vector<const MDNode *> NamedMDOps;
- for (auto &NamedMD : BD.getProgram()->named_metadata())
+ for (auto &NamedMD : BD.getProgram().named_metadata())
for (auto op : NamedMD.operands())
NamedMDOps.push_back(op);
Expected<bool> Result =
@@ -1180,15 +1160,13 @@ static Error DebugACrash(BugDriver &BD,
// Try to clean up the testcase by running funcresolve and globaldce...
if (!BugpointIsInterrupted) {
outs() << "\n*** Attempting to perform final cleanups: ";
- Module *M = CloneModule(BD.getProgram()).release();
- M = BD.performFinalCleanups(M, true).release();
+ std::unique_ptr<Module> M = CloneModule(BD.getProgram());
+ M = BD.performFinalCleanups(std::move(M), true);
// Find out if the pass still crashes on the cleaned up program...
- if (TestFn(BD, M)) {
- BD.setNewProgram(M); // Yup, it does, keep the reduced version...
- } else {
- delete M;
- }
+ if (M && TestFn(BD, M.get()))
+ BD.setNewProgram(
+ std::move(M)); // Yup, it does, keep the reduced version...
}
BD.EmitProgressBitcode(BD.getProgram(), "reduced-simplified");
@@ -1197,7 +1175,7 @@ static Error DebugACrash(BugDriver &BD,
}
static bool TestForOptimizerCrash(const BugDriver &BD, Module *M) {
- return BD.runPasses(M, BD.getPassesToRun());
+ return BD.runPasses(*M, BD.getPassesToRun());
}
/// debugOptimizerCrash - This method is called when some pass crashes on input.
@@ -1218,13 +1196,13 @@ Error BugDriver::debugOptimizerCrash(const std::string &ID) {
<< (PassesToRun.size() == 1 ? ": " : "es: ")
<< getPassesString(PassesToRun) << '\n';
- EmitProgressBitcode(Program, ID);
+ EmitProgressBitcode(*Program, ID);
return DebugACrash(*this, TestForOptimizerCrash);
}
static bool TestForCodeGenCrash(const BugDriver &BD, Module *M) {
- if (Error E = BD.compileProgram(M)) {
+ if (Error E = BD.compileProgram(*M)) {
if (VerboseErrors)
errs() << toString(std::move(E)) << "\n";
else {
diff --git a/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp b/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp
index 7562aa603bbb..773bad69fae0 100644
--- a/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp
+++ b/contrib/llvm/tools/bugpoint/ExecutionDriver.cpp
@@ -265,11 +265,9 @@ Error BugDriver::initializeExecutionEnvironment() {
return Error::success();
}
-/// compileProgram - Try to compile the specified module, returning false and
-/// setting Error if an error occurs. This is used for code generation
-/// crash testing.
-///
-Error BugDriver::compileProgram(Module *M) const {
+/// Try to compile the specified module, returning false and setting Error if an
+/// error occurs. This is used for code generation crash testing.
+Error BugDriver::compileProgram(Module &M) const {
// Emit the program to a bitcode file...
auto Temp =
sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc");
@@ -290,11 +288,10 @@ Error BugDriver::compileProgram(Module *M) const {
return Interpreter->compileProgram(Temp->TmpName, Timeout, MemoryLimit);
}
-/// executeProgram - This method runs "Program", capturing the output of the
-/// program to a file, returning the filename of the file. A recommended
-/// filename may be optionally specified.
-///
-Expected<std::string> BugDriver::executeProgram(const Module *Program,
+/// This method runs "Program", capturing the output of the program to a file,
+/// returning the filename of the file. A recommended filename may be
+/// optionally specified.
+Expected<std::string> BugDriver::executeProgram(const Module &Program,
std::string OutputFile,
std::string BitcodeFile,
const std::string &SharedObj,
@@ -373,11 +370,10 @@ Expected<std::string> BugDriver::executeProgram(const Module *Program,
return OutputFile;
}
-/// executeProgramSafely - Used to create reference output with the "safe"
-/// backend, if reference output is not provided.
-///
+/// Used to create reference output with the "safe" backend, if reference output
+/// is not provided.
Expected<std::string>
-BugDriver::executeProgramSafely(const Module *Program,
+BugDriver::executeProgramSafely(const Module &Program,
const std::string &OutputFile) const {
return executeProgram(Program, OutputFile, "", "", SafeInterpreter);
}
@@ -404,16 +400,14 @@ BugDriver::compileSharedObject(const std::string &BitcodeFile) {
return SharedObjectFile;
}
-/// createReferenceFile - calls compileProgram and then records the output
-/// into ReferenceOutputFile. Returns true if reference file created, false
-/// otherwise. Note: initializeExecutionEnvironment should be called BEFORE
-/// this function.
-///
-Error BugDriver::createReferenceFile(Module *M, const std::string &Filename) {
- if (Error E = compileProgram(Program))
+/// Calls compileProgram and then records the output into ReferenceOutputFile.
+/// Returns true if reference file created, false otherwise. Note:
+/// initializeExecutionEnvironment should be called BEFORE this function.
+Error BugDriver::createReferenceFile(Module &M, const std::string &Filename) {
+ if (Error E = compileProgram(*Program))
return E;
- Expected<std::string> Result = executeProgramSafely(Program, Filename);
+ Expected<std::string> Result = executeProgramSafely(*Program, Filename);
if (Error E = Result.takeError()) {
if (Interpreter != SafeInterpreter) {
E = joinErrors(
@@ -432,12 +426,11 @@ Error BugDriver::createReferenceFile(Module *M, const std::string &Filename) {
return Error::success();
}
-/// diffProgram - This method executes the specified module and diffs the
-/// output against the file specified by ReferenceOutputFile. If the output
-/// is different, 1 is returned. If there is a problem with the code
-/// generator (e.g., llc crashes), this will set ErrMsg.
-///
-Expected<bool> BugDriver::diffProgram(const Module *Program,
+/// This method executes the specified module and diffs the output against the
+/// file specified by ReferenceOutputFile. If the output is different, 1 is
+/// returned. If there is a problem with the code generator (e.g., llc
+/// crashes), this will set ErrMsg.
+Expected<bool> BugDriver::diffProgram(const Module &Program,
const std::string &BitcodeFile,
const std::string &SharedObject,
bool RemoveBitcode) const {
diff --git a/contrib/llvm/tools/bugpoint/ExtractFunction.cpp b/contrib/llvm/tools/bugpoint/ExtractFunction.cpp
index 431dcedfe203..48f1575c25eb 100644
--- a/contrib/llvm/tools/bugpoint/ExtractFunction.cpp
+++ b/contrib/llvm/tools/bugpoint/ExtractFunction.cpp
@@ -85,7 +85,7 @@ std::unique_ptr<Module>
BugDriver::deleteInstructionFromProgram(const Instruction *I,
unsigned Simplification) {
// FIXME, use vmap?
- Module *Clone = CloneModule(Program).release();
+ std::unique_ptr<Module> Clone = CloneModule(*Program);
const BasicBlock *PBB = I->getParent();
const Function *PF = PBB->getParent();
@@ -118,8 +118,7 @@ BugDriver::deleteInstructionFromProgram(const Instruction *I,
Passes.push_back("simplifycfg"); // Delete dead control flow
Passes.push_back("verify");
- std::unique_ptr<Module> New = runPassesOn(Clone, Passes);
- delete Clone;
+ std::unique_ptr<Module> New = runPassesOn(Clone.get(), Passes);
if (!New) {
errs() << "Instruction removal failed. Sorry. :( Please report a bug!\n";
exit(1);
@@ -128,7 +127,8 @@ BugDriver::deleteInstructionFromProgram(const Instruction *I,
}
std::unique_ptr<Module>
-BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) {
+BugDriver::performFinalCleanups(std::unique_ptr<Module> M,
+ bool MayModifySemantics) {
// Make all functions external, so GlobalDCE doesn't delete them...
for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
I->setLinkage(GlobalValue::ExternalLinkage);
@@ -141,12 +141,11 @@ BugDriver::performFinalCleanups(Module *M, bool MayModifySemantics) {
else
CleanupPasses.push_back("deadargelim");
- std::unique_ptr<Module> New = runPassesOn(M, CleanupPasses);
+ std::unique_ptr<Module> New = runPassesOn(M.get(), CleanupPasses);
if (!New) {
errs() << "Final cleanups failed. Sorry. :( Please report a bug!\n";
return nullptr;
}
- delete M;
return New;
}
@@ -157,7 +156,7 @@ std::unique_ptr<Module> BugDriver::extractLoop(Module *M) {
std::unique_ptr<Module> NewM = runPassesOn(M, LoopExtractPasses);
if (!NewM) {
outs() << "*** Loop extraction failed: ";
- EmitProgressBitcode(M, "loopextraction", true);
+ EmitProgressBitcode(*M, "loopextraction", true);
outs() << "*** Sorry. :( Please report a bug!\n";
return nullptr;
}
@@ -319,15 +318,15 @@ llvm::SplitFunctionsOutOfModule(Module *M, const std::vector<Function *> &F,
}
ValueToValueMapTy NewVMap;
- std::unique_ptr<Module> New = CloneModule(M, NewVMap);
+ std::unique_ptr<Module> New = CloneModule(*M, NewVMap);
// Remove the Test functions from the Safe module
std::set<Function *> TestFunctions;
for (unsigned i = 0, e = F.size(); i != e; ++i) {
Function *TNOF = cast<Function>(VMap[F[i]]);
- DEBUG(errs() << "Removing function ");
- DEBUG(TNOF->printAsOperand(errs(), false));
- DEBUG(errs() << "\n");
+ LLVM_DEBUG(errs() << "Removing function ");
+ LLVM_DEBUG(TNOF->printAsOperand(errs(), false));
+ LLVM_DEBUG(errs() << "\n");
TestFunctions.insert(cast<Function>(NewVMap[TNOF]));
DeleteFunctionBody(TNOF); // Function is now external in this module!
}
@@ -378,15 +377,21 @@ BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
outs() << "*** Basic Block extraction failed!\n";
errs() << "Error creating temporary file: " << toString(Temp.takeError())
<< "\n";
- EmitProgressBitcode(M, "basicblockextractfail", true);
+ EmitProgressBitcode(*M, "basicblockextractfail", true);
return nullptr;
}
DiscardTemp Discard{*Temp};
+ // Extract all of the blocks except the ones in BBs.
+ SmallVector<BasicBlock *, 32> BlocksToExtract;
+ for (Function &F : *M)
+ for (BasicBlock &BB : F)
+ // Check if this block is going to be extracted.
+ if (std::find(BBs.begin(), BBs.end(), &BB) == BBs.end())
+ BlocksToExtract.push_back(&BB);
+
raw_fd_ostream OS(Temp->FD, /*shouldClose*/ false);
- for (std::vector<BasicBlock *>::const_iterator I = BBs.begin(), E = BBs.end();
- I != E; ++I) {
- BasicBlock *BB = *I;
+ for (BasicBlock *BB : BBs) {
// If the BB doesn't have a name, give it one so we have something to key
// off of.
if (!BB->hasName())
@@ -396,7 +401,7 @@ BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
OS.flush();
if (OS.has_error()) {
errs() << "Error writing list of blocks to not extract\n";
- EmitProgressBitcode(M, "basicblockextractfail", true);
+ EmitProgressBitcode(*M, "basicblockextractfail", true);
OS.clear_error();
return nullptr;
}
@@ -411,7 +416,7 @@ BugDriver::extractMappedBlocksFromModule(const std::vector<BasicBlock *> &BBs,
if (!Ret) {
outs() << "*** Basic Block extraction failed, please report a bug!\n";
- EmitProgressBitcode(M, "basicblockextractfail", true);
+ EmitProgressBitcode(*M, "basicblockextractfail", true);
}
return Ret;
}
diff --git a/contrib/llvm/tools/bugpoint/FindBugs.cpp b/contrib/llvm/tools/bugpoint/FindBugs.cpp
index 40502cbf9495..a695e875b787 100644
--- a/contrib/llvm/tools/bugpoint/FindBugs.cpp
+++ b/contrib/llvm/tools/bugpoint/FindBugs.cpp
@@ -32,7 +32,7 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
outs() << "\n";
if (ReferenceOutputFile.empty()) {
outs() << "Generating reference output from raw program: \n";
- if (Error E = createReferenceFile(Program))
+ if (Error E = createReferenceFile(*Program))
return E;
}
@@ -53,7 +53,7 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
}
std::string Filename;
- if (runPasses(Program, PassesToRun, Filename, false)) {
+ if (runPasses(*Program, PassesToRun, Filename, false)) {
outs() << "\n";
outs() << "Optimizer passes caused failure!\n\n";
return debugOptimizerCrash();
@@ -65,7 +65,7 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
// Step 3: Compile the optimized code.
//
outs() << "Running the code generator to test for a crash: ";
- if (Error E = compileProgram(Program)) {
+ if (Error E = compileProgram(*Program)) {
outs() << "\n*** compileProgram threw an exception: ";
outs() << toString(std::move(E));
return debugCodeGeneratorCrash();
@@ -77,7 +77,7 @@ BugDriver::runManyPasses(const std::vector<std::string> &AllPasses) {
// output (created above).
//
outs() << "*** Checking if passes caused miscompliation:\n";
- Expected<bool> Diff = diffProgram(Program, Filename, "", false);
+ Expected<bool> Diff = diffProgram(*Program, Filename, "", false);
if (Error E = Diff.takeError()) {
errs() << toString(std::move(E));
return debugCodeGeneratorCrash();
diff --git a/contrib/llvm/tools/bugpoint/Miscompilation.cpp b/contrib/llvm/tools/bugpoint/Miscompilation.cpp
index 80f4cea23481..375bee7a0d50 100644
--- a/contrib/llvm/tools/bugpoint/Miscompilation.cpp
+++ b/contrib/llvm/tools/bugpoint/Miscompilation.cpp
@@ -152,8 +152,8 @@ ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix,
<< "' passes compile correctly after the '" << getPassesString(Prefix)
<< "' passes: ";
- std::unique_ptr<Module> OriginalInput(
- BD.swapProgramIn(PrefixOutput.release()));
+ std::unique_ptr<Module> OriginalInput =
+ BD.swapProgramIn(std::move(PrefixOutput));
if (BD.runPasses(BD.getProgram(), Suffix, BitcodeResult, false /*delete*/,
true /*quiet*/)) {
errs() << " Error running this sequence of passes"
@@ -179,7 +179,7 @@ ReduceMiscompilingPasses::doTest(std::vector<std::string> &Prefix,
// Otherwise, we must not be running the bad pass anymore.
outs() << " yup.\n"; // No miscompilation!
// Restore orig program & free test.
- delete BD.swapProgramIn(OriginalInput.release());
+ BD.setNewProgram(std::move(OriginalInput));
return NoFailure;
}
@@ -230,23 +230,22 @@ static Expected<std::unique_ptr<Module>> testMergedProgram(const BugDriver &BD,
const Module &M2,
bool &Broken) {
// Resulting merge of M1 and M2.
- auto Merged = CloneModule(&M1);
- if (Linker::linkModules(*Merged, CloneModule(&M2)))
+ auto Merged = CloneModule(M1);
+ if (Linker::linkModules(*Merged, CloneModule(M2)))
// TODO: Shouldn't we thread the error up instead of exiting?
exit(1);
// Execute the program.
- Expected<bool> Diff = BD.diffProgram(Merged.get(), "", "", false);
+ Expected<bool> Diff = BD.diffProgram(*Merged, "", "", false);
if (Error E = Diff.takeError())
return std::move(E);
Broken = *Diff;
return std::move(Merged);
}
-/// TestFuncs - split functions in a Module into two groups: those that are
-/// under consideration for miscompilation vs. those that are not, and test
+/// split functions in a Module into two groups: those that are under
+/// consideration for miscompilation vs. those that are not, and test
/// accordingly. Each group of functions becomes a separate Module.
-///
Expected<bool>
ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function *> &Funcs) {
// Test to see if the function is misoptimized if we ONLY run it on the
@@ -266,8 +265,8 @@ ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function *> &Funcs) {
// we can conclude that a function triggers the bug when in fact one
// needs a larger set of original functions to do so.
ValueToValueMapTy VMap;
- Module *Clone = CloneModule(BD.getProgram(), VMap).release();
- Module *Orig = BD.swapProgramIn(Clone);
+ std::unique_ptr<Module> Clone = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> Orig = BD.swapProgramIn(std::move(Clone));
std::vector<Function *> FuncsOnClone;
for (unsigned i = 0, e = Funcs.size(); i != e; ++i) {
@@ -284,19 +283,18 @@ ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function *> &Funcs) {
Expected<bool> Broken =
TestFn(BD, std::move(ToOptimize), std::move(ToNotOptimize));
- delete BD.swapProgramIn(Orig);
+ BD.setNewProgram(std::move(Orig));
return Broken;
}
-/// DisambiguateGlobalSymbols - Give anonymous global values names.
-///
-static void DisambiguateGlobalSymbols(Module *M) {
- for (Module::global_iterator I = M->global_begin(), E = M->global_end();
- I != E; ++I)
+/// Give anonymous global values names.
+static void DisambiguateGlobalSymbols(Module &M) {
+ for (Module::global_iterator I = M.global_begin(), E = M.global_end(); I != E;
+ ++I)
if (!I->hasName())
I->setName("anon_global");
- for (Module::iterator I = M->begin(), E = M->end(); I != E; ++I)
+ for (Module::iterator I = M.begin(), E = M.end(); I != E; ++I)
if (!I->hasName())
I->setName("anon_fn");
}
@@ -317,17 +315,14 @@ ExtractLoops(BugDriver &BD,
ValueToValueMapTy VMap;
std::unique_ptr<Module> ToNotOptimize = CloneModule(BD.getProgram(), VMap);
- Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize.get(),
- MiscompiledFunctions, VMap)
- .release();
+ std::unique_ptr<Module> ToOptimize = SplitFunctionsOutOfModule(
+ ToNotOptimize.get(), MiscompiledFunctions, VMap);
std::unique_ptr<Module> ToOptimizeLoopExtracted =
- BD.extractLoop(ToOptimize);
- if (!ToOptimizeLoopExtracted) {
+ BD.extractLoop(ToOptimize.get());
+ if (!ToOptimizeLoopExtracted)
// If the loop extractor crashed or if there were no extractible loops,
// then this chapter of our odyssey is over with.
- delete ToOptimize;
return MadeChange;
- }
errs() << "Extracted a loop from the breaking portion of the program.\n";
@@ -346,10 +341,9 @@ ExtractLoops(BugDriver &BD,
return false;
// Delete the original and set the new program.
- Module *Old = BD.swapProgramIn(New->release());
+ std::unique_ptr<Module> Old = BD.swapProgramIn(std::move(*New));
for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]);
- delete Old;
if (Failure) {
BD.switchToInterpreter(AI);
@@ -360,25 +354,23 @@ ExtractLoops(BugDriver &BD,
errs() << " Continuing on with un-loop-extracted version.\n";
BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-tno.bc",
- ToNotOptimize.get());
+ *ToNotOptimize);
BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to.bc",
- ToOptimize);
+ *ToOptimize);
BD.writeProgramToFile(OutputPrefix + "-loop-extract-fail-to-le.bc",
- ToOptimizeLoopExtracted.get());
+ *ToOptimizeLoopExtracted);
errs() << "Please submit the " << OutputPrefix
<< "-loop-extract-fail-*.bc files.\n";
- delete ToOptimize;
return MadeChange;
}
- delete ToOptimize;
BD.switchToInterpreter(AI);
outs() << " Testing after loop extraction:\n";
// Clone modules, the tester function will free them.
std::unique_ptr<Module> TOLEBackup =
- CloneModule(ToOptimizeLoopExtracted.get(), VMap);
- std::unique_ptr<Module> TNOBackup = CloneModule(ToNotOptimize.get(), VMap);
+ CloneModule(*ToOptimizeLoopExtracted, VMap);
+ std::unique_ptr<Module> TNOBackup = CloneModule(*ToNotOptimize, VMap);
for (unsigned i = 0, e = MiscompiledFunctions.size(); i != e; ++i)
MiscompiledFunctions[i] = cast<Function>(VMap[MiscompiledFunctions[i]]);
@@ -413,7 +405,7 @@ ExtractLoops(BugDriver &BD,
MiscompiledFunctions.push_back(NewF);
}
- BD.setNewProgram(ToNotOptimize.release());
+ BD.setNewProgram(std::move(ToNotOptimize));
return MadeChange;
}
@@ -444,7 +436,7 @@ ExtractLoops(BugDriver &BD,
MiscompiledFunctions.push_back(NewF);
}
- BD.setNewProgram(ToNotOptimize.release());
+ BD.setNewProgram(std::move(ToNotOptimize));
MadeChange = true;
}
}
@@ -508,8 +500,8 @@ ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock *> &BBs) {
// Split the module into the two halves of the program we want.
ValueToValueMapTy VMap;
- Module *Clone = CloneModule(BD.getProgram(), VMap).release();
- Module *Orig = BD.swapProgramIn(Clone);
+ std::unique_ptr<Module> Clone = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> Orig = BD.swapProgramIn(std::move(Clone));
std::vector<Function *> FuncsOnClone;
std::vector<BasicBlock *> BBsOnClone;
for (unsigned i = 0, e = FunctionsBeingTested.size(); i != e; ++i) {
@@ -531,10 +523,10 @@ ReduceMiscompiledBlocks::TestFuncs(const std::vector<BasicBlock *> &BBs) {
if (std::unique_ptr<Module> New =
BD.extractMappedBlocksFromModule(BBsOnClone, ToOptimize.get())) {
Expected<bool> Ret = TestFn(BD, std::move(New), std::move(ToNotOptimize));
- delete BD.swapProgramIn(Orig);
+ BD.setNewProgram(std::move(Orig));
return Ret;
}
- delete BD.swapProgramIn(Orig);
+ BD.setNewProgram(std::move(Orig));
return false;
}
@@ -577,23 +569,19 @@ ExtractBlocks(BugDriver &BD,
}
ValueToValueMapTy VMap;
- Module *ProgClone = CloneModule(BD.getProgram(), VMap).release();
- Module *ToExtract =
- SplitFunctionsOutOfModule(ProgClone, MiscompiledFunctions, VMap)
- .release();
+ std::unique_ptr<Module> ProgClone = CloneModule(BD.getProgram(), VMap);
+ std::unique_ptr<Module> ToExtract =
+ SplitFunctionsOutOfModule(ProgClone.get(), MiscompiledFunctions, VMap);
std::unique_ptr<Module> Extracted =
- BD.extractMappedBlocksFromModule(Blocks, ToExtract);
+ BD.extractMappedBlocksFromModule(Blocks, ToExtract.get());
if (!Extracted) {
// Weird, extraction should have worked.
errs() << "Nondeterministic problem extracting blocks??\n";
- delete ProgClone;
- delete ToExtract;
return false;
}
// Otherwise, block extraction succeeded. Link the two program fragments back
// together.
- delete ToExtract;
std::vector<std::pair<std::string, FunctionType *>> MisCompFunctions;
for (Module::iterator I = Extracted->begin(), E = Extracted->end(); I != E;
@@ -605,7 +593,7 @@ ExtractBlocks(BugDriver &BD,
exit(1);
// Set the new program and delete the old one.
- BD.setNewProgram(ProgClone);
+ BD.setNewProgram(std::move(ProgClone));
// Update the list of miscompiled functions.
MiscompiledFunctions.clear();
@@ -631,8 +619,8 @@ static Expected<std::vector<Function *>> DebugAMiscompilation(
// miscompiled... first build a list of all of the non-external functions in
// the program.
std::vector<Function *> MiscompiledFunctions;
- Module *Prog = BD.getProgram();
- for (Function &F : *Prog)
+ Module &Prog = BD.getProgram();
+ for (Function &F : Prog)
if (!F.isDeclaration())
MiscompiledFunctions.push_back(&F);
@@ -718,8 +706,8 @@ static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
if (!Optimized) {
errs() << " Error running this sequence of passes"
<< " on the input program!\n";
- delete BD.swapProgramIn(Test.get());
- BD.EmitProgressBitcode(Test.get(), "pass-error", false);
+ BD.setNewProgram(std::move(Test));
+ BD.EmitProgressBitcode(*Test, "pass-error", false);
if (Error E = BD.debugOptimizerCrash())
return std::move(E);
return false;
@@ -734,7 +722,7 @@ static Expected<bool> TestOptimizer(BugDriver &BD, std::unique_ptr<Module> Test,
if (auto New = std::move(*Result)) {
outs() << (Broken ? " nope.\n" : " yup.\n");
// Delete the original and set the new program.
- delete BD.swapProgramIn(New.release());
+ BD.setNewProgram(std::move(New));
}
return Broken;
}
@@ -760,7 +748,7 @@ Error BugDriver::debugMiscompilation() {
outs() << "\n*** Found miscompiling pass"
<< (getPassesToRun().size() == 1 ? "" : "es") << ": "
<< getPassesString(getPassesToRun()) << '\n';
- EmitProgressBitcode(Program, "passinput");
+ EmitProgressBitcode(*Program, "passinput");
Expected<std::vector<Function *>> MiscompiledFunctions =
DebugAMiscompilation(*this, TestOptimizer);
@@ -776,11 +764,11 @@ Error BugDriver::debugMiscompilation() {
.release();
outs() << " Non-optimized portion: ";
- EmitProgressBitcode(ToNotOptimize, "tonotoptimize", true);
+ EmitProgressBitcode(*ToNotOptimize, "tonotoptimize", true);
delete ToNotOptimize; // Delete hacked module.
outs() << " Portion that is input to optimizer: ";
- EmitProgressBitcode(ToOptimize, "tooptimize");
+ EmitProgressBitcode(*ToOptimize, "tooptimize");
delete ToOptimize; // Delete hacked module.
return Error::success();
@@ -788,15 +776,15 @@ Error BugDriver::debugMiscompilation() {
/// Get the specified modules ready for code generator testing.
///
-static void CleanupAndPrepareModules(BugDriver &BD,
- std::unique_ptr<Module> &Test,
- Module *Safe) {
+static std::unique_ptr<Module>
+CleanupAndPrepareModules(BugDriver &BD, std::unique_ptr<Module> Test,
+ Module *Safe) {
// Clean up the modules, removing extra cruft that we don't need anymore...
- Test = BD.performFinalCleanups(Test.get());
+ Test = BD.performFinalCleanups(std::move(Test));
// If we are executing the JIT, we have several nasty issues to take care of.
if (!BD.isExecutingJIT())
- return;
+ return Test;
// First, if the main function is in the Safe module, we must add a stub to
// the Test module to call into it. Thus, we create a new function `main'
@@ -942,6 +930,8 @@ static void CleanupAndPrepareModules(BugDriver &BD,
errs() << "Bugpoint has a bug, which corrupted a module!!\n";
abort();
}
+
+ return Test;
}
/// This is the predicate function used to check to see if the "Test" portion of
@@ -951,7 +941,7 @@ static void CleanupAndPrepareModules(BugDriver &BD,
static Expected<bool> TestCodeGenerator(BugDriver &BD,
std::unique_ptr<Module> Test,
std::unique_ptr<Module> Safe) {
- CleanupAndPrepareModules(BD, Test, Safe.get());
+ Test = CleanupAndPrepareModules(BD, std::move(Test), Safe.get());
SmallString<128> TestModuleBC;
int TestModuleFD;
@@ -962,7 +952,7 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
<< "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
- if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, Test.get())) {
+ if (BD.writeProgramToFile(TestModuleBC.str(), TestModuleFD, *Test)) {
errs() << "Error writing bitcode to `" << TestModuleBC.str()
<< "'\nExiting.";
exit(1);
@@ -981,7 +971,7 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
exit(1);
}
- if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, Safe.get())) {
+ if (BD.writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, *Safe)) {
errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting.";
exit(1);
}
@@ -1015,7 +1005,7 @@ static Expected<bool> TestCodeGenerator(BugDriver &BD,
Error BugDriver::debugCodeGenerator() {
if ((void *)SafeInterpreter == (void *)Interpreter) {
Expected<std::string> Result =
- executeProgramSafely(Program, "bugpoint.safe.out");
+ executeProgramSafely(*Program, "bugpoint.safe.out");
if (Result) {
outs() << "\n*** The \"safe\" i.e. 'known good' backend cannot match "
<< "the reference diff. This may be due to a\n front-end "
@@ -1028,7 +1018,7 @@ Error BugDriver::debugCodeGenerator() {
return Error::success();
}
- DisambiguateGlobalSymbols(Program);
+ DisambiguateGlobalSymbols(*Program);
Expected<std::vector<Function *>> Funcs =
DebugAMiscompilation(*this, TestCodeGenerator);
@@ -1042,7 +1032,8 @@ Error BugDriver::debugCodeGenerator() {
SplitFunctionsOutOfModule(ToNotCodeGen.get(), *Funcs, VMap);
// Condition the modules
- CleanupAndPrepareModules(*this, ToCodeGen, ToNotCodeGen.get());
+ ToCodeGen =
+ CleanupAndPrepareModules(*this, std::move(ToCodeGen), ToNotCodeGen.get());
SmallString<128> TestModuleBC;
int TestModuleFD;
@@ -1054,7 +1045,7 @@ Error BugDriver::debugCodeGenerator() {
exit(1);
}
- if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, ToCodeGen.get())) {
+ if (writeProgramToFile(TestModuleBC.str(), TestModuleFD, *ToCodeGen)) {
errs() << "Error writing bitcode to `" << TestModuleBC << "'\nExiting.";
exit(1);
}
@@ -1070,8 +1061,7 @@ Error BugDriver::debugCodeGenerator() {
exit(1);
}
- if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD,
- ToNotCodeGen.get())) {
+ if (writeProgramToFile(SafeModuleBC.str(), SafeModuleFD, *ToNotCodeGen)) {
errs() << "Error writing bitcode to `" << SafeModuleBC << "'\nExiting.";
exit(1);
}
diff --git a/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp b/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp
index ee3f2f0174d2..cbb048db8fe7 100644
--- a/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp
+++ b/contrib/llvm/tools/bugpoint/OptimizerDriver.cpp
@@ -43,18 +43,14 @@ static cl::opt<bool> PreserveBitcodeUseListOrder(
cl::desc("Preserve use-list order when writing LLVM bitcode."),
cl::init(true), cl::Hidden);
-// ChildOutput - This option captures the name of the child output file that
-// is set up by the parent bugpoint process
-static cl::opt<std::string> ChildOutput("child-output", cl::ReallyHidden);
static cl::opt<std::string>
OptCmd("opt-command", cl::init(""),
cl::desc("Path to opt. (default: search path "
"for 'opt'.)"));
-/// writeProgramToFile - This writes the current "Program" to the named bitcode
-/// file. If an error occurs, true is returned.
-///
-static bool writeProgramToFileAux(ToolOutputFile &Out, const Module *M) {
+/// This writes the current "Program" to the named bitcode file. If an error
+/// occurs, true is returned.
+static bool writeProgramToFileAux(ToolOutputFile &Out, const Module &M) {
WriteBitcodeToFile(M, Out.os(), PreserveBitcodeUseListOrder);
Out.os().close();
if (!Out.os().has_error()) {
@@ -65,12 +61,12 @@ static bool writeProgramToFileAux(ToolOutputFile &Out, const Module *M) {
}
bool BugDriver::writeProgramToFile(const std::string &Filename, int FD,
- const Module *M) const {
+ const Module &M) const {
ToolOutputFile Out(Filename, FD);
return writeProgramToFileAux(Out, M);
}
-bool BugDriver::writeProgramToFile(int FD, const Module *M) const {
+bool BugDriver::writeProgramToFile(int FD, const Module &M) const {
raw_fd_ostream OS(FD, /*shouldClose*/ false);
WriteBitcodeToFile(M, OS, PreserveBitcodeUseListOrder);
OS.flush();
@@ -81,7 +77,7 @@ bool BugDriver::writeProgramToFile(int FD, const Module *M) const {
}
bool BugDriver::writeProgramToFile(const std::string &Filename,
- const Module *M) const {
+ const Module &M) const {
std::error_code EC;
ToolOutputFile Out(Filename, EC, sys::fs::F_None);
if (!EC)
@@ -89,10 +85,9 @@ bool BugDriver::writeProgramToFile(const std::string &Filename,
return true;
}
-/// EmitProgressBitcode - This function is used to output the current Program
-/// to a file named "bugpoint-ID.bc".
-///
-void BugDriver::EmitProgressBitcode(const Module *M, const std::string &ID,
+/// This function is used to output the current Program to a file named
+/// "bugpoint-ID.bc".
+void BugDriver::EmitProgressBitcode(const Module &M, const std::string &ID,
bool NoFlyer) const {
// Output the input to the current pass to a bitcode file, emit a message
// telling the user how to reproduce it: opt -foo blah.bc
@@ -132,7 +127,7 @@ static cl::list<std::string> OptArgs("opt-args", cl::Positional,
/// outs() a single line message indicating whether compilation was successful
/// or failed.
///
-bool BugDriver::runPasses(Module *Program,
+bool BugDriver::runPasses(Module &Program,
const std::vector<std::string> &Passes,
std::string &OutputFilename, bool DeleteOutput,
bool Quiet, unsigned NumExtraArgs,
@@ -180,6 +175,10 @@ bool BugDriver::runPasses(Module *Program,
errs() << "Cannot find `opt' in PATH!\n";
return 1;
}
+ if (!sys::fs::exists(tool)) {
+ errs() << "Specified `opt' binary does not exist: " << tool << "\n";
+ return 1;
+ }
std::string Prog;
if (UseValgrind) {
@@ -195,20 +194,20 @@ bool BugDriver::runPasses(Module *Program,
}
// setup the child process' arguments
- SmallVector<const char *, 8> Args;
+ SmallVector<StringRef, 8> Args;
if (UseValgrind) {
Args.push_back("valgrind");
Args.push_back("--error-exitcode=1");
Args.push_back("-q");
- Args.push_back(tool.c_str());
+ Args.push_back(tool);
} else
- Args.push_back(tool.c_str());
+ Args.push_back(tool);
for (unsigned i = 0, e = OptArgs.size(); i != e; ++i)
- Args.push_back(OptArgs[i].c_str());
+ Args.push_back(OptArgs[i]);
Args.push_back("-disable-symbolication");
Args.push_back("-o");
- Args.push_back(OutputFilename.c_str());
+ Args.push_back(OutputFilename);
std::vector<std::string> pass_args;
for (unsigned i = 0, e = PluginLoader::getNumPlugins(); i != e; ++i) {
pass_args.push_back(std::string("-load"));
@@ -225,12 +224,11 @@ bool BugDriver::runPasses(Module *Program,
Args.push_back(Temp->TmpName.c_str());
for (unsigned i = 0; i < NumExtraArgs; ++i)
Args.push_back(*ExtraArgs);
- Args.push_back(nullptr);
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()
- << " " << Args[i];
- errs() << "\n";);
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = Args.size() - 1; i != e; ++i) errs()
+ << " " << Args[i];
+ errs() << "\n";);
Optional<StringRef> Redirects[3] = {None, None, None};
// Redirect stdout and stderr to nowhere if SilencePasses is given.
@@ -240,8 +238,8 @@ bool BugDriver::runPasses(Module *Program,
}
std::string ErrMsg;
- int result = sys::ExecuteAndWait(Prog, Args.data(), nullptr, Redirects,
- Timeout, MemoryLimit, &ErrMsg);
+ int result = sys::ExecuteAndWait(Prog, Args, None, Redirects, Timeout,
+ MemoryLimit, &ErrMsg);
// If we are supposed to delete the bitcode file or if the passes crashed,
// remove it now. This may fail if the file was never created, but that's ok.
@@ -271,7 +269,7 @@ std::unique_ptr<Module>
BugDriver::runPassesOn(Module *M, const std::vector<std::string> &Passes,
unsigned NumExtraArgs, const char *const *ExtraArgs) {
std::string BitcodeResult;
- if (runPasses(M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,
+ if (runPasses(*M, Passes, BitcodeResult, false /*delete*/, true /*quiet*/,
NumExtraArgs, ExtraArgs)) {
return nullptr;
}
diff --git a/contrib/llvm/tools/bugpoint/ToolRunner.cpp b/contrib/llvm/tools/bugpoint/ToolRunner.cpp
index 8094dfdd78fa..812e8e3bbae5 100644
--- a/contrib/llvm/tools/bugpoint/ToolRunner.cpp
+++ b/contrib/llvm/tools/bugpoint/ToolRunner.cpp
@@ -53,13 +53,14 @@ cl::opt<std::string>
/// RunProgramWithTimeout - This function provides an alternate interface
/// to the sys::Program::ExecuteAndWait interface.
/// @see sys::Program::ExecuteAndWait
-static int RunProgramWithTimeout(StringRef ProgramPath, const char **Args,
- StringRef StdInFile, StringRef StdOutFile,
- StringRef StdErrFile, unsigned NumSeconds = 0,
+static int RunProgramWithTimeout(StringRef ProgramPath,
+ ArrayRef<StringRef> Args, StringRef StdInFile,
+ StringRef StdOutFile, StringRef StdErrFile,
+ unsigned NumSeconds = 0,
unsigned MemoryLimit = 0,
std::string *ErrMsg = nullptr) {
Optional<StringRef> Redirects[3] = {StdInFile, StdOutFile, StdErrFile};
- return sys::ExecuteAndWait(ProgramPath, Args, nullptr, Redirects, NumSeconds,
+ return sys::ExecuteAndWait(ProgramPath, Args, None, Redirects, NumSeconds,
MemoryLimit, ErrMsg);
}
@@ -69,24 +70,22 @@ static int RunProgramWithTimeout(StringRef ProgramPath, const char **Args,
/// fails. Remote client is required to return 255 if it failed or program exit
/// code otherwise.
/// @see sys::Program::ExecuteAndWait
-static int RunProgramRemotelyWithTimeout(StringRef RemoteClientPath,
- const char **Args, StringRef StdInFile,
- StringRef StdOutFile,
- StringRef StdErrFile,
- unsigned NumSeconds = 0,
- unsigned MemoryLimit = 0) {
+static int RunProgramRemotelyWithTimeout(
+ StringRef RemoteClientPath, ArrayRef<StringRef> Args, StringRef StdInFile,
+ StringRef StdOutFile, StringRef StdErrFile, unsigned NumSeconds = 0,
+ unsigned MemoryLimit = 0) {
Optional<StringRef> Redirects[3] = {StdInFile, StdOutFile, StdErrFile};
// Run the program remotely with the remote client
- int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, nullptr,
- Redirects, NumSeconds, MemoryLimit);
+ int ReturnCode = sys::ExecuteAndWait(RemoteClientPath, Args, None, Redirects,
+ NumSeconds, MemoryLimit);
// Has the remote client fail?
if (255 == ReturnCode) {
std::ostringstream OS;
OS << "\nError running remote client:\n ";
- for (const char **Arg = Args; *Arg; ++Arg)
- OS << " " << *Arg;
+ for (StringRef Arg : Args)
+ OS << " " << Arg.str();
OS << "\n";
// The error message is in the output file, let's print it out from there.
@@ -105,12 +104,12 @@ static int RunProgramRemotelyWithTimeout(StringRef RemoteClientPath,
return ReturnCode;
}
-static Error ProcessFailure(StringRef ProgPath, const char **Args,
+static Error ProcessFailure(StringRef ProgPath, ArrayRef<StringRef> Args,
unsigned Timeout = 0, unsigned MemoryLimit = 0) {
std::ostringstream OS;
OS << "\nError running tool:\n ";
- for (const char **Arg = Args; *Arg; ++Arg)
- OS << " " << *Arg;
+ for (StringRef Arg : Args)
+ OS << " " << Arg.str();
OS << "\n";
// Rerun the compiler, capturing any error messages to print them.
@@ -171,7 +170,7 @@ Expected<int> LLI::ExecuteProgram(const std::string &Bitcode,
const std::vector<std::string> &CCArgs,
const std::vector<std::string> &SharedLibs,
unsigned Timeout, unsigned MemoryLimit) {
- std::vector<const char *> LLIArgs;
+ std::vector<StringRef> LLIArgs;
LLIArgs.push_back(LLIPath.c_str());
LLIArgs.push_back("-force-interpreter=true");
@@ -179,26 +178,25 @@ Expected<int> LLI::ExecuteProgram(const std::string &Bitcode,
e = SharedLibs.end();
i != e; ++i) {
LLIArgs.push_back("-load");
- LLIArgs.push_back((*i).c_str());
+ LLIArgs.push_back(*i);
}
// Add any extra LLI args.
for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i)
- LLIArgs.push_back(ToolArgs[i].c_str());
+ LLIArgs.push_back(ToolArgs[i]);
- LLIArgs.push_back(Bitcode.c_str());
+ LLIArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
for (unsigned i = 0, e = Args.size(); i != e; ++i)
- LLIArgs.push_back(Args[i].c_str());
- LLIArgs.push_back(nullptr);
+ LLIArgs.push_back(Args[i]);
outs() << "<lli>";
outs().flush();
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = LLIArgs.size() - 1; i != e; ++i) errs()
- << " " << LLIArgs[i];
- errs() << "\n";);
- return RunProgramWithTimeout(LLIPath, &LLIArgs[0], InputFile, OutputFile,
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = LLIArgs.size() - 1; i != e; ++i) errs()
+ << " " << LLIArgs[i];
+ errs() << "\n";);
+ return RunProgramWithTimeout(LLIPath, LLIArgs, InputFile, OutputFile,
OutputFile, Timeout, MemoryLimit);
}
@@ -206,7 +204,7 @@ void AbstractInterpreter::anchor() {}
#if defined(LLVM_ON_UNIX)
const char EXESuffix[] = "";
-#elif defined(LLVM_ON_WIN32)
+#elif defined(_WIN32)
const char EXESuffix[] = "exe";
#endif
@@ -215,7 +213,7 @@ const char EXESuffix[] = "exe";
/// itself. This allows us to find another LLVM tool if it is built in the same
/// directory. An empty string is returned on error; note that this function
/// just mainpulates the path and doesn't check for executability.
-/// @brief Find a named executable.
+/// Find a named executable.
static std::string PrependMainExecutablePath(const std::string &ExeName,
const char *Argv0,
void *MainAddr) {
@@ -285,22 +283,20 @@ public:
Error CustomCompiler::compileProgram(const std::string &Bitcode,
unsigned Timeout, unsigned MemoryLimit) {
- std::vector<const char *> ProgramArgs;
+ std::vector<StringRef> ProgramArgs;
ProgramArgs.push_back(CompilerCommand.c_str());
for (std::size_t i = 0; i < CompilerArgs.size(); ++i)
ProgramArgs.push_back(CompilerArgs.at(i).c_str());
- ProgramArgs.push_back(Bitcode.c_str());
- ProgramArgs.push_back(nullptr);
+ ProgramArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
for (unsigned i = 0, e = CompilerArgs.size(); i != e; ++i)
ProgramArgs.push_back(CompilerArgs[i].c_str());
- if (RunProgramWithTimeout(CompilerCommand, &ProgramArgs[0], "", "", "",
- Timeout, MemoryLimit))
- return ProcessFailure(CompilerCommand, &ProgramArgs[0], Timeout,
- MemoryLimit);
+ if (RunProgramWithTimeout(CompilerCommand, ProgramArgs, "", "", "", Timeout,
+ MemoryLimit))
+ return ProcessFailure(CompilerCommand, ProgramArgs, Timeout, MemoryLimit);
return Error::success();
}
@@ -336,19 +332,18 @@ Expected<int> CustomExecutor::ExecuteProgram(
const std::vector<std::string> &SharedLibs, unsigned Timeout,
unsigned MemoryLimit) {
- std::vector<const char *> ProgramArgs;
- ProgramArgs.push_back(ExecutionCommand.c_str());
+ std::vector<StringRef> ProgramArgs;
+ ProgramArgs.push_back(ExecutionCommand);
for (std::size_t i = 0; i < ExecutorArgs.size(); ++i)
- ProgramArgs.push_back(ExecutorArgs.at(i).c_str());
- ProgramArgs.push_back(Bitcode.c_str());
- ProgramArgs.push_back(nullptr);
+ ProgramArgs.push_back(ExecutorArgs[i]);
+ ProgramArgs.push_back(Bitcode);
// Add optional parameters to the running program from Argv
for (unsigned i = 0, e = Args.size(); i != e; ++i)
- ProgramArgs.push_back(Args[i].c_str());
+ ProgramArgs.push_back(Args[i]);
- return RunProgramWithTimeout(ExecutionCommand, &ProgramArgs[0], InputFile,
+ return RunProgramWithTimeout(ExecutionCommand, ProgramArgs, InputFile,
OutputFile, OutputFile, Timeout, MemoryLimit);
}
@@ -463,31 +458,28 @@ Expected<CC::FileType> LLC::OutputCode(const std::string &Bitcode,
exit(1);
}
OutputAsmFile = UniqueFile.str();
- std::vector<const char *> LLCArgs;
- LLCArgs.push_back(LLCPath.c_str());
+ std::vector<StringRef> LLCArgs;
+ LLCArgs.push_back(LLCPath);
// Add any extra LLC args.
for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i)
- LLCArgs.push_back(ToolArgs[i].c_str());
+ LLCArgs.push_back(ToolArgs[i]);
LLCArgs.push_back("-o");
- LLCArgs.push_back(OutputAsmFile.c_str()); // Output to the Asm file
- LLCArgs.push_back(Bitcode.c_str()); // This is the input bitcode
+ LLCArgs.push_back(OutputAsmFile); // Output to the Asm file
+ LLCArgs.push_back(Bitcode); // This is the input bitcode
if (UseIntegratedAssembler)
LLCArgs.push_back("-filetype=obj");
- LLCArgs.push_back(nullptr);
-
outs() << (UseIntegratedAssembler ? "<llc-ia>" : "<llc>");
outs().flush();
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = LLCArgs.size() - 1; i != e; ++i) errs()
- << " " << LLCArgs[i];
- errs() << "\n";);
- if (RunProgramWithTimeout(LLCPath, &LLCArgs[0], "", "", "", Timeout,
- MemoryLimit))
- return ProcessFailure(LLCPath, &LLCArgs[0], Timeout, MemoryLimit);
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = LLCArgs.size() - 1; i != e; ++i) errs()
+ << " " << LLCArgs[i];
+ errs() << "\n";);
+ if (RunProgramWithTimeout(LLCPath, LLCArgs, "", "", "", Timeout, MemoryLimit))
+ return ProcessFailure(LLCPath, LLCArgs, Timeout, MemoryLimit);
return UseIntegratedAssembler ? CC::ObjectFile : CC::AsmFile;
}
@@ -581,32 +573,31 @@ Expected<int> JIT::ExecuteProgram(const std::string &Bitcode,
const std::vector<std::string> &SharedLibs,
unsigned Timeout, unsigned MemoryLimit) {
// Construct a vector of parameters, incorporating those from the command-line
- std::vector<const char *> JITArgs;
+ std::vector<StringRef> JITArgs;
JITArgs.push_back(LLIPath.c_str());
JITArgs.push_back("-force-interpreter=false");
// Add any extra LLI args.
for (unsigned i = 0, e = ToolArgs.size(); i != e; ++i)
- JITArgs.push_back(ToolArgs[i].c_str());
+ JITArgs.push_back(ToolArgs[i]);
for (unsigned i = 0, e = SharedLibs.size(); i != e; ++i) {
JITArgs.push_back("-load");
- JITArgs.push_back(SharedLibs[i].c_str());
+ JITArgs.push_back(SharedLibs[i]);
}
JITArgs.push_back(Bitcode.c_str());
// Add optional parameters to the running program from Argv
for (unsigned i = 0, e = Args.size(); i != e; ++i)
- JITArgs.push_back(Args[i].c_str());
- JITArgs.push_back(nullptr);
+ JITArgs.push_back(Args[i]);
outs() << "<jit>";
outs().flush();
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = JITArgs.size() - 1; i != e; ++i) errs()
- << " " << JITArgs[i];
- errs() << "\n";);
- DEBUG(errs() << "\nSending output to " << OutputFile << "\n");
- return RunProgramWithTimeout(LLIPath, &JITArgs[0], InputFile, OutputFile,
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = JITArgs.size() - 1; i != e; ++i) errs()
+ << " " << JITArgs[i];
+ errs() << "\n";);
+ LLVM_DEBUG(errs() << "\nSending output to " << OutputFile << "\n");
+ return RunProgramWithTimeout(LLIPath, JITArgs, InputFile, OutputFile,
OutputFile, Timeout, MemoryLimit);
}
@@ -630,15 +621,15 @@ AbstractInterpreter::createJIT(const char *Argv0, std::string &Message,
// CC abstraction
//
-static bool IsARMArchitecture(std::vector<const char *> Args) {
- for (std::vector<const char *>::const_iterator I = Args.begin(),
- E = Args.end();
- I != E; ++I) {
- if (StringRef(*I).equals_lower("-arch")) {
- ++I;
- if (I != E && StringRef(*I).startswith_lower("arm"))
- return true;
- }
+static bool IsARMArchitecture(std::vector<StringRef> Args) {
+ for (size_t I = 0; I < Args.size(); ++I) {
+ if (!Args[I].equals_lower("-arch"))
+ continue;
+ ++I;
+ if (I == Args.size())
+ break;
+ if (Args[I].startswith_lower("arm"))
+ return true;
}
return false;
@@ -651,9 +642,9 @@ Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
const std::string &OutputFile,
const std::vector<std::string> &ArgsForCC,
unsigned Timeout, unsigned MemoryLimit) {
- std::vector<const char *> CCArgs;
+ std::vector<StringRef> CCArgs;
- CCArgs.push_back(CCPath.c_str());
+ CCArgs.push_back(CCPath);
if (TargetTriple.getArch() == Triple::x86)
CCArgs.push_back("-m32");
@@ -661,7 +652,7 @@ Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
for (std::vector<std::string>::const_iterator I = ccArgs.begin(),
E = ccArgs.end();
I != E; ++I)
- CCArgs.push_back(I->c_str());
+ CCArgs.push_back(*I);
// Specify -x explicitly in case the extension is wonky
if (fileType != ObjectFile) {
@@ -680,7 +671,7 @@ Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
}
}
- CCArgs.push_back(ProgramFile.c_str()); // Specify the input filename.
+ CCArgs.push_back(ProgramFile); // Specify the input filename.
CCArgs.push_back("-x");
CCArgs.push_back("none");
@@ -693,51 +684,50 @@ Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
errs() << "Error making unique filename: " << EC.message() << "\n";
exit(1);
}
- CCArgs.push_back(OutputBinary.c_str()); // Output to the right file...
+ CCArgs.push_back(OutputBinary); // Output to the right file...
// Add any arguments intended for CC. We locate them here because this is
// most likely -L and -l options that need to come before other libraries but
// after the source. Other options won't be sensitive to placement on the
// command line, so this should be safe.
for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i)
- CCArgs.push_back(ArgsForCC[i].c_str());
+ CCArgs.push_back(ArgsForCC[i]);
CCArgs.push_back("-lm"); // Hard-code the math library...
CCArgs.push_back("-O2"); // Optimize the program a bit...
if (TargetTriple.getArch() == Triple::sparc)
CCArgs.push_back("-mcpu=v9");
- CCArgs.push_back(nullptr); // NULL terminator
outs() << "<CC>";
outs().flush();
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = CCArgs.size() - 1; i != e; ++i) errs()
- << " " << CCArgs[i];
- errs() << "\n";);
- if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", ""))
- return ProcessFailure(CCPath, &CCArgs[0]);
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = CCArgs.size() - 1; i != e; ++i) errs()
+ << " " << CCArgs[i];
+ errs() << "\n";);
+ if (RunProgramWithTimeout(CCPath, CCArgs, "", "", ""))
+ return ProcessFailure(CCPath, CCArgs);
- std::vector<const char *> ProgramArgs;
+ std::vector<StringRef> ProgramArgs;
// Declared here so that the destructor only runs after
// ProgramArgs is used.
std::string Exec;
if (RemoteClientPath.empty())
- ProgramArgs.push_back(OutputBinary.c_str());
+ ProgramArgs.push_back(OutputBinary);
else {
- ProgramArgs.push_back(RemoteClientPath.c_str());
- ProgramArgs.push_back(RemoteHost.c_str());
+ ProgramArgs.push_back(RemoteClientPath);
+ ProgramArgs.push_back(RemoteHost);
if (!RemoteUser.empty()) {
ProgramArgs.push_back("-l");
- ProgramArgs.push_back(RemoteUser.c_str());
+ ProgramArgs.push_back(RemoteUser);
}
if (!RemotePort.empty()) {
ProgramArgs.push_back("-p");
- ProgramArgs.push_back(RemotePort.c_str());
+ ProgramArgs.push_back(RemotePort);
}
if (!RemoteExtra.empty()) {
- ProgramArgs.push_back(RemoteExtra.c_str());
+ ProgramArgs.push_back(RemoteExtra);
}
// Full path to the binary. We need to cd to the exec directory because
@@ -747,28 +737,28 @@ Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
Exec += env_pwd;
Exec += "; ./";
Exec += OutputBinary.c_str();
- ProgramArgs.push_back(Exec.c_str());
+ ProgramArgs.push_back(Exec);
}
// Add optional parameters to the running program from Argv
for (unsigned i = 0, e = Args.size(); i != e; ++i)
- ProgramArgs.push_back(Args[i].c_str());
- ProgramArgs.push_back(nullptr); // NULL terminator
+ ProgramArgs.push_back(Args[i]);
// Now that we have a binary, run it!
outs() << "<program>";
outs().flush();
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = ProgramArgs.size() - 1; i != e; ++i) errs()
- << " " << ProgramArgs[i];
- errs() << "\n";);
+ LLVM_DEBUG(
+ errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = ProgramArgs.size() - 1; i != e; ++i) errs()
+ << " " << ProgramArgs[i];
+ errs() << "\n";);
FileRemover OutputBinaryRemover(OutputBinary.str(), !SaveTemps);
if (RemoteClientPath.empty()) {
- DEBUG(errs() << "<run locally>");
+ LLVM_DEBUG(errs() << "<run locally>");
std::string Error;
- int ExitCode = RunProgramWithTimeout(OutputBinary.str(), &ProgramArgs[0],
+ int ExitCode = RunProgramWithTimeout(OutputBinary.str(), ProgramArgs,
InputFile, OutputFile, OutputFile,
Timeout, MemoryLimit, &Error);
// Treat a signal (usually SIGSEGV) or timeout as part of the program output
@@ -782,7 +772,7 @@ Expected<int> CC::ExecuteProgram(const std::string &ProgramFile,
} else {
outs() << "<run remotely>";
outs().flush();
- return RunProgramRemotelyWithTimeout(RemoteClientPath, &ProgramArgs[0],
+ return RunProgramRemotelyWithTimeout(RemoteClientPath, ProgramArgs,
InputFile, OutputFile, OutputFile,
Timeout, MemoryLimit);
}
@@ -800,9 +790,9 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
}
OutputFile = UniqueFilename.str();
- std::vector<const char *> CCArgs;
+ std::vector<StringRef> CCArgs;
- CCArgs.push_back(CCPath.c_str());
+ CCArgs.push_back(CCPath);
if (TargetTriple.getArch() == Triple::x86)
CCArgs.push_back("-m32");
@@ -810,7 +800,7 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
for (std::vector<std::string>::const_iterator I = ccArgs.begin(),
E = ccArgs.end();
I != E; ++I)
- CCArgs.push_back(I->c_str());
+ CCArgs.push_back(*I);
// Compile the C/asm file into a shared object
if (fileType != ObjectFile) {
@@ -818,7 +808,7 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
CCArgs.push_back(fileType == AsmFile ? "assembler" : "c");
}
CCArgs.push_back("-fno-strict-aliasing");
- CCArgs.push_back(InputFile.c_str()); // Specify the input filename.
+ CCArgs.push_back(InputFile); // Specify the input filename.
CCArgs.push_back("-x");
CCArgs.push_back("none");
if (TargetTriple.getArch() == Triple::sparc)
@@ -842,7 +832,7 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
CCArgs.push_back("-mcpu=v9");
CCArgs.push_back("-o");
- CCArgs.push_back(OutputFile.c_str()); // Output to the right filename.
+ CCArgs.push_back(OutputFile); // Output to the right filename.
CCArgs.push_back("-O2"); // Optimize the program a bit.
// Add any arguments intended for CC. We locate them here because this is
@@ -850,17 +840,16 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType,
// after the source. Other options won't be sensitive to placement on the
// command line, so this should be safe.
for (unsigned i = 0, e = ArgsForCC.size(); i != e; ++i)
- CCArgs.push_back(ArgsForCC[i].c_str());
- CCArgs.push_back(nullptr); // NULL terminator
+ CCArgs.push_back(ArgsForCC[i]);
outs() << "<CC>";
outs().flush();
- DEBUG(errs() << "\nAbout to run:\t";
- for (unsigned i = 0, e = CCArgs.size() - 1; i != e; ++i) errs()
- << " " << CCArgs[i];
- errs() << "\n";);
- if (RunProgramWithTimeout(CCPath, &CCArgs[0], "", "", ""))
- return ProcessFailure(CCPath, &CCArgs[0]);
+ LLVM_DEBUG(errs() << "\nAbout to run:\t";
+ for (unsigned i = 0, e = CCArgs.size() - 1; i != e; ++i) errs()
+ << " " << CCArgs[i];
+ errs() << "\n";);
+ if (RunProgramWithTimeout(CCPath, CCArgs, "", "", ""))
+ return ProcessFailure(CCPath, CCArgs);
return Error::success();
}
diff --git a/contrib/llvm/tools/bugpoint/bugpoint.cpp b/contrib/llvm/tools/bugpoint/bugpoint.cpp
index ec1ca2e54968..f6b7d08455d4 100644
--- a/contrib/llvm/tools/bugpoint/bugpoint.cpp
+++ b/contrib/llvm/tools/bugpoint/bugpoint.cpp
@@ -15,17 +15,18 @@
#include "BugDriver.h"
#include "ToolRunner.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/IR/LegacyPassNameParser.h"
#include "llvm/LinkAllIR.h"
#include "llvm/LinkAllPasses.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/Valgrind.h"
#include "llvm/Transforms/IPO/AlwaysInliner.h"
@@ -117,9 +118,7 @@ void initializePollyPasses(llvm::PassRegistry &Registry);
int main(int argc, char **argv) {
#ifndef DEBUG_BUGPOINT
- llvm::sys::PrintStackTraceOnErrorSignal(argv[0]);
- llvm::PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
#endif
// Initialize passes
@@ -132,6 +131,7 @@ int main(int argc, char **argv) {
initializeAnalysis(Registry);
initializeTransformUtils(Registry);
initializeInstCombine(Registry);
+ initializeAggressiveInstCombine(Registry);
initializeInstrumentation(Registry);
initializeTarget(Registry);
diff --git a/contrib/llvm/tools/llc/llc.cpp b/contrib/llvm/tools/llc/llc.cpp
index a4810890f9b4..2329fb3e87c9 100644
--- a/contrib/llvm/tools/llc/llc.cpp
+++ b/contrib/llvm/tools/llc/llc.cpp
@@ -16,7 +16,7 @@
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Analysis/TargetLibraryInfo.h"
-#include "llvm/CodeGen/CommandFlags.def"
+#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/CodeGen/LinkAllAsmWriterComponents.h"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
@@ -24,6 +24,7 @@
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetPassConfig.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
+#include "llvm/IR/AutoUpgrade.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
@@ -40,14 +41,14 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/PluginLoader.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include <memory>
@@ -66,6 +67,11 @@ InputLanguage("x", cl::desc("Input language ('ir' or 'mir')"));
static cl::opt<std::string>
OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename"));
+static cl::opt<std::string>
+ SplitDwarfOutputFile("split-dwarf-output",
+ cl::desc(".dwo output filename"),
+ cl::value_desc("filename"));
+
static cl::opt<unsigned>
TimeCompilations("time-compilations", cl::Hidden, cl::init(1u),
cl::value_desc("N"),
@@ -226,7 +232,7 @@ static std::unique_ptr<ToolOutputFile> GetOutputStream(const char *TargetName,
OpenFlags |= sys::fs::F_Text;
auto FDOut = llvm::make_unique<ToolOutputFile>(OutputFilename, EC, OpenFlags);
if (EC) {
- errs() << EC.message() << '\n';
+ WithColor::error() << EC.message() << '\n';
return nullptr;
}
@@ -262,20 +268,18 @@ static void InlineAsmDiagHandler(const SMDiagnostic &SMD, void *Context,
// For testing purposes, we print the LocCookie here.
if (LocCookie)
- errs() << "note: !srcloc = " << LocCookie << "\n";
+ WithColor::note() << "!srcloc = " << LocCookie << "\n";
}
// main - Entry point for the llc compiler.
//
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
+ InitLLVM X(argc, argv);
// Enable debug stream buffering.
EnableDebugBuffering = true;
LLVMContext Context;
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
// Initialize targets first, so that --version shows registered targets.
InitializeAllTargets();
@@ -327,7 +331,7 @@ int main(int argc, char **argv) {
YamlFile =
llvm::make_unique<ToolOutputFile>(RemarksFilename, EC, sys::fs::F_None);
if (EC) {
- errs() << EC.message() << '\n';
+ WithColor::error(errs(), argv[0]) << EC.message() << '\n';
return 1;
}
Context.setDiagnosticsOutputFile(
@@ -336,7 +340,8 @@ int main(int argc, char **argv) {
if (InputLanguage != "" && InputLanguage != "ir" &&
InputLanguage != "mir") {
- errs() << argv[0] << "Input language must be '', 'IR' or 'MIR'\n";
+ WithColor::error(errs(), argv[0])
+ << "input language must be '', 'IR' or 'MIR'\n";
return 1;
}
@@ -359,7 +364,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0,
const PassRegistry *PR = PassRegistry::getPassRegistry();
const PassInfo *PI = PR->getPassInfo(PassName);
if (!PI) {
- errs() << argv0 << ": run-pass " << PassName << " is not registered.\n";
+ WithColor::error(errs(), argv0)
+ << "run-pass " << PassName << " is not registered.\n";
return true;
}
@@ -367,7 +373,8 @@ static bool addPass(PassManagerBase &PM, const char *argv0,
if (PI->getNormalCtor())
P = PI->getNormalCtor()();
else {
- errs() << argv0 << ": cannot create pass: " << PI->getPassName() << "\n";
+ WithColor::error(errs(), argv0)
+ << "cannot create pass: " << PI->getPassName() << "\n";
return true;
}
std::string Banner = std::string("After ") + std::string(P->getPassName());
@@ -395,17 +402,9 @@ static int compileModule(char **argv, LLVMContext &Context) {
if (MIR)
M = MIR->parseIRModule();
} else
- M = parseIRFile(InputFilename, Err, Context);
+ M = parseIRFile(InputFilename, Err, Context, false);
if (!M) {
- Err.print(argv[0], errs());
- return 1;
- }
-
- // Verify module immediately to catch problems before doInitialization() is
- // called on any passes.
- if (!NoVerify && verifyModule(*M, &errs())) {
- errs() << argv[0] << ": " << InputFilename
- << ": error: input module is broken!\n";
+ Err.print(argv[0], WithColor::error(errs(), argv[0]));
return 1;
}
@@ -425,7 +424,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
const Target *TheTarget = TargetRegistry::lookupTarget(MArch, TheTriple,
Error);
if (!TheTarget) {
- errs() << argv[0] << ": " << Error;
+ WithColor::error(errs(), argv[0]) << Error;
return 1;
}
@@ -434,7 +433,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
CodeGenOpt::Level OLvl = CodeGenOpt::Default;
switch (OptLevel) {
default:
- errs() << argv[0] << ": invalid optimization level.\n";
+ WithColor::error(errs(), argv[0]) << "invalid optimization level.\n";
return 1;
case ' ': break;
case '0': OLvl = CodeGenOpt::None; break;
@@ -473,6 +472,17 @@ static int compileModule(char **argv, LLVMContext &Context) {
GetOutputStream(TheTarget->getName(), TheTriple.getOS(), argv[0]);
if (!Out) return 1;
+ std::unique_ptr<ToolOutputFile> DwoOut;
+ if (!SplitDwarfOutputFile.empty()) {
+ std::error_code EC;
+ DwoOut = llvm::make_unique<ToolOutputFile>(SplitDwarfOutputFile, EC,
+ sys::fs::F_None);
+ if (EC) {
+ WithColor::error(errs(), argv[0]) << EC.message() << '\n';
+ return 1;
+ }
+ }
+
// Build up all of the passes that we want to do to the module.
legacy::PassManager PM;
@@ -487,14 +497,27 @@ static int compileModule(char **argv, LLVMContext &Context) {
// Add the target data from the target machine, if it exists, or the module.
M->setDataLayout(Target->createDataLayout());
+ // This needs to be done after setting datalayout since it calls verifier
+ // to check debug info whereas verifier relies on correct datalayout.
+ UpgradeDebugInfo(*M);
+
+ // Verify module immediately to catch problems before doInitialization() is
+ // called on any passes.
+ if (!NoVerify && verifyModule(*M, &errs())) {
+ std::string Prefix =
+ (Twine(argv[0]) + Twine(": ") + Twine(InputFilename)).str();
+ WithColor::error(errs(), Prefix) << "input module is broken!\n";
+ return 1;
+ }
+
// Override function attributes based on CPUStr, FeaturesStr, and command line
// flags.
setFunctionAttributes(CPUStr, FeaturesStr, *M);
if (RelaxAll.getNumOccurrences() > 0 &&
FileType != TargetMachine::CGFT_ObjectFile)
- errs() << argv[0]
- << ": warning: ignoring -mc-relax-all because filetype != obj";
+ WithColor::warning(errs(), argv[0])
+ << ": warning: ignoring -mc-relax-all because filetype != obj";
{
raw_pwrite_stream *OS = &Out->os();
@@ -518,13 +541,15 @@ static int compileModule(char **argv, LLVMContext &Context) {
// selection.
if (!RunPassNames->empty()) {
if (!MIR) {
- errs() << argv0 << ": run-pass is for .mir file only.\n";
+ WithColor::warning(errs(), argv[0])
+ << "run-pass is for .mir file only.\n";
return 1;
}
TargetPassConfig &TPC = *LLVMTM.createPassConfig(PM);
if (TPC.hasLimitedCodeGenPipeline()) {
- errs() << argv0 << ": run-pass cannot be used with "
- << TPC.getLimitedCodeGenPipelineReason(" and ") << ".\n";
+ WithColor::warning(errs(), argv[0])
+ << "run-pass cannot be used with "
+ << TPC.getLimitedCodeGenPipelineReason(" and ") << ".\n";
return 1;
}
@@ -539,9 +564,12 @@ static int compileModule(char **argv, LLVMContext &Context) {
TPC.setInitialized();
PM.add(createPrintMIRPass(*OS));
PM.add(createFreeMachineFunctionPass());
- } else if (Target->addPassesToEmitFile(PM, *OS, FileType, NoVerify, MMI)) {
- errs() << argv0 << ": target does not support generation of this"
- << " file type!\n";
+ } else if (Target->addPassesToEmitFile(PM, *OS,
+ DwoOut ? &DwoOut->os() : nullptr,
+ FileType, NoVerify, MMI)) {
+ WithColor::warning(errs(), argv[0])
+ << "target does not support generation of this"
+ << " file type!\n";
return 1;
}
@@ -560,7 +588,7 @@ static int compileModule(char **argv, LLVMContext &Context) {
// in the future.
SmallVector<char, 0> CompileTwiceBuffer;
if (CompileTwice) {
- std::unique_ptr<Module> M2(llvm::CloneModule(M.get()));
+ std::unique_ptr<Module> M2(llvm::CloneModule(*M));
PM.run(*M2);
CompileTwiceBuffer = Buffer;
Buffer.clear();
@@ -596,6 +624,8 @@ static int compileModule(char **argv, LLVMContext &Context) {
// Declare success.
Out->keep();
+ if (DwoOut)
+ DwoOut->keep();
return 0;
}
diff --git a/contrib/llvm/tools/lli/OrcLazyJIT.cpp b/contrib/llvm/tools/lli/OrcLazyJIT.cpp
deleted file mode 100644
index f1a752e0790d..000000000000
--- a/contrib/llvm/tools/lli/OrcLazyJIT.cpp
+++ /dev/null
@@ -1,166 +0,0 @@
-//===- OrcLazyJIT.cpp - Basic Orc-based JIT for lazy execution ------------===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "OrcLazyJIT.h"
-#include "llvm/ADT/Triple.h"
-#include "llvm/ExecutionEngine/ExecutionEngine.h"
-#include "llvm/Support/CodeGen.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/DynamicLibrary.h"
-#include "llvm/Support/ErrorHandling.h"
-#include "llvm/Support/FileSystem.h"
-#include <cstdint>
-#include <cstdio>
-#include <cstdlib>
-#include <system_error>
-
-using namespace llvm;
-
-namespace {
-
-enum class DumpKind {
- NoDump,
- DumpFuncsToStdOut,
- DumpModsToStdOut,
- DumpModsToDisk
-};
-
-} // end anonymous namespace
-
-static cl::opt<DumpKind> OrcDumpKind(
- "orc-lazy-debug", cl::desc("Debug dumping for the orc-lazy JIT."),
- cl::init(DumpKind::NoDump),
- cl::values(clEnumValN(DumpKind::NoDump, "no-dump", "Don't dump anything."),
- clEnumValN(DumpKind::DumpFuncsToStdOut, "funcs-to-stdout",
- "Dump function names to stdout."),
- clEnumValN(DumpKind::DumpModsToStdOut, "mods-to-stdout",
- "Dump modules to stdout."),
- clEnumValN(DumpKind::DumpModsToDisk, "mods-to-disk",
- "Dump modules to the current "
- "working directory. (WARNING: "
- "will overwrite existing files).")),
- cl::Hidden);
-
-static cl::opt<bool> OrcInlineStubs("orc-lazy-inline-stubs",
- cl::desc("Try to inline stubs"),
- cl::init(true), cl::Hidden);
-
-OrcLazyJIT::TransformFtor OrcLazyJIT::createDebugDumper() {
- switch (OrcDumpKind) {
- case DumpKind::NoDump:
- return [](std::shared_ptr<Module> M) { return M; };
-
- case DumpKind::DumpFuncsToStdOut:
- return [](std::shared_ptr<Module> M) {
- printf("[ ");
-
- for (const auto &F : *M) {
- if (F.isDeclaration())
- continue;
-
- if (F.hasName()) {
- std::string Name(F.getName());
- printf("%s ", Name.c_str());
- } else
- printf("<anon> ");
- }
-
- printf("]\n");
- return M;
- };
-
- case DumpKind::DumpModsToStdOut:
- return [](std::shared_ptr<Module> M) {
- outs() << "----- Module Start -----\n" << *M
- << "----- Module End -----\n";
-
- return M;
- };
-
- case DumpKind::DumpModsToDisk:
- return [](std::shared_ptr<Module> M) {
- std::error_code EC;
- raw_fd_ostream Out(M->getModuleIdentifier() + ".ll", EC,
- sys::fs::F_Text);
- if (EC) {
- errs() << "Couldn't open " << M->getModuleIdentifier()
- << " for dumping.\nError:" << EC.message() << "\n";
- exit(1);
- }
- Out << *M;
- return M;
- };
- }
- llvm_unreachable("Unknown DumpKind");
-}
-
-// Defined in lli.cpp.
-CodeGenOpt::Level getOptLevel();
-
-template <typename PtrTy>
-static PtrTy fromTargetAddress(JITTargetAddress Addr) {
- return reinterpret_cast<PtrTy>(static_cast<uintptr_t>(Addr));
-}
-
-int llvm::runOrcLazyJIT(std::vector<std::unique_ptr<Module>> Ms,
- const std::vector<std::string> &Args) {
- // Add the program's symbols into the JIT's search space.
- if (sys::DynamicLibrary::LoadLibraryPermanently(nullptr)) {
- errs() << "Error loading program symbols.\n";
- return 1;
- }
-
- // Grab a target machine and try to build a factory function for the
- // target-specific Orc callback manager.
- EngineBuilder EB;
- EB.setOptLevel(getOptLevel());
- auto TM = std::unique_ptr<TargetMachine>(EB.selectTarget());
- Triple T(TM->getTargetTriple());
- auto CompileCallbackMgr = orc::createLocalCompileCallbackManager(T, 0);
-
- // If we couldn't build the factory function then there must not be a callback
- // manager for this target. Bail out.
- if (!CompileCallbackMgr) {
- errs() << "No callback manager available for target '"
- << TM->getTargetTriple().str() << "'.\n";
- return 1;
- }
-
- auto IndirectStubsMgrBuilder = orc::createLocalIndirectStubsManagerBuilder(T);
-
- // If we couldn't build a stubs-manager-builder for this target then bail out.
- if (!IndirectStubsMgrBuilder) {
- errs() << "No indirect stubs manager available for target '"
- << TM->getTargetTriple().str() << "'.\n";
- return 1;
- }
-
- // Everything looks good. Build the JIT.
- OrcLazyJIT J(std::move(TM), std::move(CompileCallbackMgr),
- std::move(IndirectStubsMgrBuilder),
- OrcInlineStubs);
-
- // Add the module, look up main and run it.
- for (auto &M : Ms)
- cantFail(J.addModule(std::shared_ptr<Module>(std::move(M))));
-
- if (auto MainSym = J.findSymbol("main")) {
- typedef int (*MainFnPtr)(int, const char*[]);
- std::vector<const char *> ArgV;
- for (auto &Arg : Args)
- ArgV.push_back(Arg.c_str());
- auto Main = fromTargetAddress<MainFnPtr>(cantFail(MainSym.getAddress()));
- return Main(ArgV.size(), (const char**)ArgV.data());
- } else if (auto Err = MainSym.takeError())
- logAllUnhandledErrors(std::move(Err), llvm::errs(), "");
- else
- errs() << "Could not find main function.\n";
-
- return 1;
-}
diff --git a/contrib/llvm/tools/lli/OrcLazyJIT.h b/contrib/llvm/tools/lli/OrcLazyJIT.h
deleted file mode 100644
index a5cc804bb045..000000000000
--- a/contrib/llvm/tools/lli/OrcLazyJIT.h
+++ /dev/null
@@ -1,201 +0,0 @@
-//===- OrcLazyJIT.h - Basic Orc-based JIT for lazy execution ----*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-//
-// Simple Orc-based JIT. Uses the compile-on-demand layer to break up and
-// lazily compile modules.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLI_ORCLAZYJIT_H
-#define LLVM_TOOLS_LLI_ORCLAZYJIT_H
-
-#include "llvm/ADT/Optional.h"
-#include "llvm/ADT/STLExtras.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/ExecutionEngine/JITSymbol.h"
-#include "llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h"
-#include "llvm/ExecutionEngine/Orc/CompileUtils.h"
-#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
-#include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
-#include "llvm/ExecutionEngine/Orc/IRTransformLayer.h"
-#include "llvm/ExecutionEngine/Orc/IndirectionUtils.h"
-#include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
-#include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
-#include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
-#include "llvm/ExecutionEngine/SectionMemoryManager.h"
-#include "llvm/IR/DataLayout.h"
-#include "llvm/IR/GlobalValue.h"
-#include "llvm/IR/Mangler.h"
-#include "llvm/IR/Module.h"
-#include "llvm/Support/raw_ostream.h"
-#include "llvm/Target/TargetMachine.h"
-#include <algorithm>
-#include <functional>
-#include <memory>
-#include <set>
-#include <string>
-#include <vector>
-
-namespace llvm {
-
-class OrcLazyJIT {
-public:
-
- using CompileCallbackMgr = orc::JITCompileCallbackManager;
- using ObjLayerT = orc::RTDyldObjectLinkingLayer;
- using CompileLayerT = orc::IRCompileLayer<ObjLayerT, orc::SimpleCompiler>;
- using TransformFtor =
- std::function<std::shared_ptr<Module>(std::shared_ptr<Module>)>;
- using IRDumpLayerT = orc::IRTransformLayer<CompileLayerT, TransformFtor>;
- using CODLayerT = orc::CompileOnDemandLayer<IRDumpLayerT, CompileCallbackMgr>;
- using IndirectStubsManagerBuilder = CODLayerT::IndirectStubsManagerBuilderT;
- using ModuleHandleT = CODLayerT::ModuleHandleT;
-
- OrcLazyJIT(std::unique_ptr<TargetMachine> TM,
- std::unique_ptr<CompileCallbackMgr> CCMgr,
- IndirectStubsManagerBuilder IndirectStubsMgrBuilder,
- bool InlineStubs)
- : TM(std::move(TM)), DL(this->TM->createDataLayout()),
- CCMgr(std::move(CCMgr)),
- ObjectLayer([]() { return std::make_shared<SectionMemoryManager>(); }),
- CompileLayer(ObjectLayer, orc::SimpleCompiler(*this->TM)),
- IRDumpLayer(CompileLayer, createDebugDumper()),
- CODLayer(IRDumpLayer, extractSingleFunction, *this->CCMgr,
- std::move(IndirectStubsMgrBuilder), InlineStubs),
- CXXRuntimeOverrides(
- [this](const std::string &S) { return mangle(S); }) {}
-
- ~OrcLazyJIT() {
- // Run any destructors registered with __cxa_atexit.
- CXXRuntimeOverrides.runDestructors();
- // Run any IR destructors.
- for (auto &DtorRunner : IRStaticDestructorRunners)
- if (auto Err = DtorRunner.runViaLayer(CODLayer)) {
- // FIXME: OrcLazyJIT should probably take a "shutdownError" callback to
- // report these errors on.
- report_fatal_error(std::move(Err));
- }
- }
-
- Error addModule(std::shared_ptr<Module> M) {
- if (M->getDataLayout().isDefault())
- M->setDataLayout(DL);
-
- // Rename, bump linkage and record static constructors and destructors.
- // We have to do this before we hand over ownership of the module to the
- // JIT.
- std::vector<std::string> CtorNames, DtorNames;
- {
- unsigned CtorId = 0, DtorId = 0;
- for (auto Ctor : orc::getConstructors(*M)) {
- std::string NewCtorName = ("$static_ctor." + Twine(CtorId++)).str();
- Ctor.Func->setName(NewCtorName);
- Ctor.Func->setLinkage(GlobalValue::ExternalLinkage);
- Ctor.Func->setVisibility(GlobalValue::HiddenVisibility);
- CtorNames.push_back(mangle(NewCtorName));
- }
- for (auto Dtor : orc::getDestructors(*M)) {
- std::string NewDtorName = ("$static_dtor." + Twine(DtorId++)).str();
- Dtor.Func->setLinkage(GlobalValue::ExternalLinkage);
- Dtor.Func->setVisibility(GlobalValue::HiddenVisibility);
- DtorNames.push_back(mangle(Dtor.Func->getName()));
- Dtor.Func->setName(NewDtorName);
- }
- }
-
- // Symbol resolution order:
- // 1) Search the JIT symbols.
- // 2) Check for C++ runtime overrides.
- // 3) Search the host process (LLI)'s symbol table.
- if (!ModulesHandle) {
- auto Resolver =
- orc::createLambdaResolver(
- [this](const std::string &Name) -> JITSymbol {
- if (auto Sym = CODLayer.findSymbol(Name, true))
- return Sym;
- return CXXRuntimeOverrides.searchOverrides(Name);
- },
- [](const std::string &Name) {
- if (auto Addr =
- RTDyldMemoryManager::getSymbolAddressInProcess(Name))
- return JITSymbol(Addr, JITSymbolFlags::Exported);
- return JITSymbol(nullptr);
- }
- );
-
- // Add the module to the JIT.
- if (auto ModulesHandleOrErr =
- CODLayer.addModule(std::move(M), std::move(Resolver)))
- ModulesHandle = std::move(*ModulesHandleOrErr);
- else
- return ModulesHandleOrErr.takeError();
-
- } else if (auto Err = CODLayer.addExtraModule(*ModulesHandle, std::move(M)))
- return Err;
-
- // Run the static constructors, and save the static destructor runner for
- // execution when the JIT is torn down.
- orc::CtorDtorRunner<CODLayerT> CtorRunner(std::move(CtorNames),
- *ModulesHandle);
- if (auto Err = CtorRunner.runViaLayer(CODLayer))
- return Err;
-
- IRStaticDestructorRunners.emplace_back(std::move(DtorNames),
- *ModulesHandle);
-
- return Error::success();
- }
-
- JITSymbol findSymbol(const std::string &Name) {
- return CODLayer.findSymbol(mangle(Name), true);
- }
-
- JITSymbol findSymbolIn(ModuleHandleT H, const std::string &Name) {
- return CODLayer.findSymbolIn(H, mangle(Name), true);
- }
-
-private:
- std::string mangle(const std::string &Name) {
- std::string MangledName;
- {
- raw_string_ostream MangledNameStream(MangledName);
- Mangler::getNameWithPrefix(MangledNameStream, Name, DL);
- }
- return MangledName;
- }
-
- static std::set<Function*> extractSingleFunction(Function &F) {
- std::set<Function*> Partition;
- Partition.insert(&F);
- return Partition;
- }
-
- static TransformFtor createDebugDumper();
-
- std::unique_ptr<TargetMachine> TM;
- DataLayout DL;
- SectionMemoryManager CCMgrMemMgr;
-
- std::unique_ptr<CompileCallbackMgr> CCMgr;
- ObjLayerT ObjectLayer;
- CompileLayerT CompileLayer;
- IRDumpLayerT IRDumpLayer;
- CODLayerT CODLayer;
-
- orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides;
- std::vector<orc::CtorDtorRunner<CODLayerT>> IRStaticDestructorRunners;
- llvm::Optional<CODLayerT::ModuleHandleT> ModulesHandle;
-};
-
-int runOrcLazyJIT(std::vector<std::unique_ptr<Module>> Ms,
- const std::vector<std::string> &Args);
-
-} // end namespace llvm
-
-#endif // LLVM_TOOLS_LLI_ORCLAZYJIT_H
diff --git a/contrib/llvm/tools/lli/RemoteJITUtils.h b/contrib/llvm/tools/lli/RemoteJITUtils.h
index 4e948413865c..944881070c70 100644
--- a/contrib/llvm/tools/lli/RemoteJITUtils.h
+++ b/contrib/llvm/tools/lli/RemoteJITUtils.h
@@ -84,7 +84,7 @@ public:
this->MemMgr = std::move(MemMgr);
}
- void setResolver(std::shared_ptr<JITSymbolResolver> Resolver) {
+ void setResolver(std::shared_ptr<LegacyJITSymbolResolver> Resolver) {
this->Resolver = std::move(Resolver);
}
@@ -145,7 +145,7 @@ public:
private:
std::unique_ptr<RuntimeDyld::MemoryManager> MemMgr;
- std::shared_ptr<JITSymbolResolver> Resolver;
+ std::shared_ptr<LegacyJITSymbolResolver> Resolver;
};
}
diff --git a/contrib/llvm/tools/lli/lli.cpp b/contrib/llvm/tools/lli/lli.cpp
index a33c51d77877..1940dbd848cc 100644
--- a/contrib/llvm/tools/lli/lli.cpp
+++ b/contrib/llvm/tools/lli/lli.cpp
@@ -13,18 +13,20 @@
//
//===----------------------------------------------------------------------===//
-#include "OrcLazyJIT.h"
#include "RemoteJITUtils.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Bitcode/BitcodeReader.h"
-#include "llvm/CodeGen/CommandFlags.def"
+#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/CodeGen/LinkAllCodegenComponents.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/ExecutionEngine/GenericValue.h"
#include "llvm/ExecutionEngine/Interpreter.h"
#include "llvm/ExecutionEngine/JITEventListener.h"
#include "llvm/ExecutionEngine/MCJIT.h"
#include "llvm/ExecutionEngine/ObjectCache.h"
+#include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
+#include "llvm/ExecutionEngine/Orc/LLJIT.h"
#include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h"
#include "llvm/ExecutionEngine/OrcMCJITReplacement.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
@@ -33,6 +35,7 @@
#include "llvm/IR/Module.h"
#include "llvm/IR/Type.h"
#include "llvm/IR/TypeBuilder.h"
+#include "llvm/IR/Verifier.h"
#include "llvm/IRReader/IRReader.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ObjectFile.h"
@@ -40,18 +43,18 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/DynamicLibrary.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PluginLoader.h"
-#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Transforms/Instrumentation.h"
#include <cerrno>
@@ -176,6 +179,28 @@ namespace {
cl::desc("Generate software floating point library calls"),
cl::init(false));
+ enum class DumpKind {
+ NoDump,
+ DumpFuncsToStdOut,
+ DumpModsToStdOut,
+ DumpModsToDisk
+ };
+
+ cl::opt<DumpKind> OrcDumpKind(
+ "orc-lazy-debug", cl::desc("Debug dumping for the orc-lazy JIT."),
+ cl::init(DumpKind::NoDump),
+ cl::values(clEnumValN(DumpKind::NoDump, "no-dump",
+ "Don't dump anything."),
+ clEnumValN(DumpKind::DumpFuncsToStdOut, "funcs-to-stdout",
+ "Dump function names to stdout."),
+ clEnumValN(DumpKind::DumpModsToStdOut, "mods-to-stdout",
+ "Dump modules to stdout."),
+ clEnumValN(DumpKind::DumpModsToDisk, "mods-to-disk",
+ "Dump modules to the current "
+ "working directory. (WARNING: "
+ "will overwrite existing files).")),
+ cl::Hidden);
+
ExitOnError ExitOnErr;
}
@@ -295,7 +320,7 @@ static void addCygMingExtraModule(ExecutionEngine &EE, LLVMContext &Context,
CodeGenOpt::Level getOptLevel() {
switch (OptLevel) {
default:
- errs() << "lli: Invalid optimization level.\n";
+ WithColor::error(errs(), "lli") << "invalid optimization level.\n";
exit(1);
case '0': return CodeGenOpt::None;
case '1': return CodeGenOpt::Less;
@@ -312,14 +337,14 @@ static void reportError(SMDiagnostic Err, const char *ProgName) {
exit(1);
}
+int runOrcLazyJIT(LLVMContext &Ctx, std::vector<std::unique_ptr<Module>> Ms,
+ const std::vector<std::string> &Args);
+
//===----------------------------------------------------------------------===//
// main Driver function
//
int main(int argc, char **argv, char * const *envp) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
-
- atexit(llvm_shutdown); // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
if (argc > 1)
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
@@ -358,7 +383,7 @@ int main(int argc, char **argv, char * const *envp) {
Args.push_back(InputFile);
for (auto &Arg : InputArgv)
Args.push_back(Arg);
- return runOrcLazyJIT(std::move(Ms), Args);
+ return runOrcLazyJIT(Context, std::move(Ms), Args);
}
if (EnableCacheManager) {
@@ -378,8 +403,8 @@ int main(int argc, char **argv, char * const *envp) {
std::string ErrorMsg;
EngineBuilder builder(std::move(Owner));
builder.setMArch(MArch);
- builder.setMCPU(MCPU);
- builder.setMAttrs(MAttrs);
+ builder.setMCPU(getCPUStr());
+ builder.setMAttrs(getFeatureList());
if (RelocModel.getNumOccurrences())
builder.setRelocationModel(RelocModel);
if (CMModel.getNumOccurrences())
@@ -407,8 +432,8 @@ int main(int argc, char **argv, char * const *envp) {
builder.setMCJITMemoryManager(
std::unique_ptr<RTDyldMemoryManager>(RTDyldMM));
} else if (RemoteMCJIT) {
- errs() << "error: Remote process execution does not work with the "
- "interpreter.\n";
+ WithColor::error(errs(), argv[0])
+ << "remote process execution does not work with the interpreter.\n";
exit(1);
}
@@ -423,9 +448,10 @@ int main(int argc, char **argv, char * const *envp) {
std::unique_ptr<ExecutionEngine> EE(builder.create());
if (!EE) {
if (!ErrorMsg.empty())
- errs() << argv[0] << ": error creating EE: " << ErrorMsg << "\n";
+ WithColor::error(errs(), argv[0])
+ << "error creating EE: " << ErrorMsg << "\n";
else
- errs() << argv[0] << ": unknown error creating EE!\n";
+ WithColor::error(errs(), argv[0]) << "unknown error creating EE!\n";
exit(1);
}
@@ -496,9 +522,13 @@ int main(int argc, char **argv, char * const *envp) {
JITEventListener::createOProfileJITEventListener());
EE->RegisterJITEventListener(
JITEventListener::createIntelJITEventListener());
+ if (!RemoteMCJIT)
+ EE->RegisterJITEventListener(
+ JITEventListener::createPerfJITEventListener());
if (!NoLazyCompilation && RemoteMCJIT) {
- errs() << "warning: remote mcjit does not support lazy compilation\n";
+ WithColor::warning(errs(), argv[0])
+ << "remote mcjit does not support lazy compilation\n";
NoLazyCompilation = true;
}
EE->DisableLazyCompilation(NoLazyCompilation);
@@ -524,7 +554,8 @@ int main(int argc, char **argv, char * const *envp) {
//
Function *EntryFn = Mod->getFunction(EntryFunc);
if (!EntryFn) {
- errs() << '\'' << EntryFunc << "\' function not found in module.\n";
+ WithColor::error(errs(), argv[0])
+ << '\'' << EntryFunc << "\' function not found in module.\n";
return -1;
}
@@ -537,16 +568,19 @@ int main(int argc, char **argv, char * const *envp) {
// remote JIT on Unix platforms.
if (RemoteMCJIT) {
#ifndef LLVM_ON_UNIX
- errs() << "Warning: host does not support external remote targets.\n"
- << " Defaulting to local execution\n";
+ WithColor::warning(errs(), argv[0])
+ << "host does not support external remote targets.\n";
+ WithColor::note() << "defaulting to local execution\n";
return -1;
#else
if (ChildExecPath.empty()) {
- errs() << "-remote-mcjit requires -mcjit-remote-process.\n";
+ WithColor::error(errs(), argv[0])
+ << "-remote-mcjit requires -mcjit-remote-process.\n";
exit(1);
} else if (!sys::fs::can_execute(ChildExecPath)) {
- errs() << "Unable to find usable child executable: '" << ChildExecPath
- << "'\n";
+ WithColor::error(errs(), argv[0])
+ << "unable to find usable child executable: '" << ChildExecPath
+ << "'\n";
return -1;
}
#endif
@@ -586,10 +620,11 @@ int main(int argc, char **argv, char * const *envp) {
ResultGV.IntVal = APInt(32, Result);
Args.push_back(ResultGV);
EE->runFunction(ExitF, Args);
- errs() << "ERROR: exit(" << Result << ") returned!\n";
+ WithColor::error(errs(), argv[0]) << "exit(" << Result << ") returned!\n";
abort();
} else {
- errs() << "ERROR: exit defined with wrong prototype!\n";
+ WithColor::error(errs(), argv[0])
+ << "exit defined with wrong prototype!\n";
abort();
}
} else {
@@ -602,13 +637,15 @@ int main(int argc, char **argv, char * const *envp) {
// Lanch the remote process and get a channel to it.
std::unique_ptr<FDRawChannel> C = launchRemote();
if (!C) {
- errs() << "Failed to launch remote JIT.\n";
+ WithColor::error(errs(), argv[0]) << "failed to launch remote JIT.\n";
exit(1);
}
// Create a remote target client running over the channel.
+ llvm::orc::ExecutionSession ES;
+ ES.setErrorReporter([&](Error Err) { ExitOnErr(std::move(Err)); });
typedef orc::remote::OrcRemoteTargetClient MyRemote;
- auto R = ExitOnErr(MyRemote::Create(*C, ExitOnErr));
+ auto R = ExitOnErr(MyRemote::Create(*C, ES));
// Create a remote memory manager.
auto RemoteMM = ExitOnErr(R->createRemoteMemoryManager());
@@ -632,8 +669,8 @@ int main(int argc, char **argv, char * const *envp) {
// FIXME: argv and envp handling.
JITTargetAddress Entry = EE->getFunctionAddress(EntryFn->getName().str());
EE->finalizeObject();
- DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x"
- << format("%llx", Entry) << "\n");
+ LLVM_DEBUG(dbgs() << "Executing '" << EntryFn->getName() << "' at 0x"
+ << format("%llx", Entry) << "\n");
Result = ExitOnErr(R->callIntVoid(Entry));
// Like static constructors, the remote target MCJIT support doesn't handle
@@ -651,6 +688,130 @@ int main(int argc, char **argv, char * const *envp) {
return Result;
}
+static orc::IRTransformLayer2::TransformFunction createDebugDumper() {
+ switch (OrcDumpKind) {
+ case DumpKind::NoDump:
+ return [](std::unique_ptr<Module> M) { return M; };
+
+ case DumpKind::DumpFuncsToStdOut:
+ return [](std::unique_ptr<Module> M) {
+ printf("[ ");
+
+ for (const auto &F : *M) {
+ if (F.isDeclaration())
+ continue;
+
+ if (F.hasName()) {
+ std::string Name(F.getName());
+ printf("%s ", Name.c_str());
+ } else
+ printf("<anon> ");
+ }
+
+ printf("]\n");
+ return M;
+ };
+
+ case DumpKind::DumpModsToStdOut:
+ return [](std::unique_ptr<Module> M) {
+ outs() << "----- Module Start -----\n"
+ << *M << "----- Module End -----\n";
+
+ return M;
+ };
+
+ case DumpKind::DumpModsToDisk:
+ return [](std::unique_ptr<Module> M) {
+ std::error_code EC;
+ raw_fd_ostream Out(M->getModuleIdentifier() + ".ll", EC, sys::fs::F_Text);
+ if (EC) {
+ errs() << "Couldn't open " << M->getModuleIdentifier()
+ << " for dumping.\nError:" << EC.message() << "\n";
+ exit(1);
+ }
+ Out << *M;
+ return M;
+ };
+ }
+ llvm_unreachable("Unknown DumpKind");
+}
+
+int runOrcLazyJIT(LLVMContext &Ctx, std::vector<std::unique_ptr<Module>> Ms,
+ const std::vector<std::string> &Args) {
+ // Bail out early if no modules loaded.
+ if (Ms.empty())
+ return 0;
+
+ // Add lli's symbols into the JIT's search space.
+ std::string ErrMsg;
+ sys::DynamicLibrary LibLLI =
+ sys::DynamicLibrary::getPermanentLibrary(nullptr, &ErrMsg);
+ if (!LibLLI.isValid()) {
+ errs() << "Error loading lli symbols: " << ErrMsg << ".\n";
+ return 1;
+ }
+
+ const auto &TT = Ms.front()->getTargetTriple();
+ orc::JITTargetMachineBuilder TMD =
+ TT.empty() ? ExitOnErr(orc::JITTargetMachineBuilder::detectHost())
+ : orc::JITTargetMachineBuilder(Triple(TT));
+
+ TMD.setArch(MArch)
+ .setCPU(getCPUStr())
+ .addFeatures(getFeatureList())
+ .setRelocationModel(RelocModel.getNumOccurrences()
+ ? Optional<Reloc::Model>(RelocModel)
+ : None)
+ .setCodeModel(CMModel.getNumOccurrences()
+ ? Optional<CodeModel::Model>(CMModel)
+ : None);
+ auto TM = ExitOnErr(TMD.createTargetMachine());
+ auto DL = TM->createDataLayout();
+ auto ES = llvm::make_unique<orc::ExecutionSession>();
+ auto J =
+ ExitOnErr(orc::LLLazyJIT::Create(std::move(ES), std::move(TM), DL, Ctx));
+
+ auto Dump = createDebugDumper();
+
+ J->setLazyCompileTransform(
+ [&](std::unique_ptr<Module> M) {
+ if (verifyModule(*M, &dbgs())) {
+ dbgs() << "Bad module: " << *M << "\n";
+ exit(1);
+ }
+ return Dump(std::move(M));
+ });
+ J->getMainVSO().setFallbackDefinitionGenerator(
+ orc::DynamicLibraryFallbackGenerator(
+ std::move(LibLLI), DL, [](orc::SymbolStringPtr) { return true; }));
+
+ orc::MangleAndInterner Mangle(J->getExecutionSession(), DL);
+ orc::LocalCXXRuntimeOverrides2 CXXRuntimeOverrides;
+ ExitOnErr(CXXRuntimeOverrides.enable(J->getMainVSO(), Mangle));
+
+ for (auto &M : Ms) {
+ orc::makeAllSymbolsExternallyAccessible(*M);
+ ExitOnErr(J->addLazyIRModule(std::move(M)));
+ }
+
+ ExitOnErr(J->runConstructors());
+
+ auto MainSym = ExitOnErr(J->lookup("main"));
+ typedef int (*MainFnPtr)(int, const char *[]);
+ std::vector<const char *> ArgV;
+ for (auto &Arg : Args)
+ ArgV.push_back(Arg.c_str());
+ auto Main =
+ reinterpret_cast<MainFnPtr>(static_cast<uintptr_t>(MainSym.getAddress()));
+ auto Result = Main(ArgV.size(), (const char **)ArgV.data());
+
+ ExitOnErr(J->runDestructors());
+
+ CXXRuntimeOverrides.runDestructors();
+
+ return Result;
+}
+
std::unique_ptr<FDRawChannel> launchRemote() {
#ifndef LLVM_ON_UNIX
llvm_unreachable("launchRemote not supported on non-Unix platforms");
diff --git a/contrib/llvm/tools/llvm-ar/llvm-ar.cpp b/contrib/llvm/tools/llvm-ar/llvm-ar.cpp
index ae7d1a7f1b7a..9023bdd1a0d6 100644
--- a/contrib/llvm/tools/llvm-ar/llvm-ar.cpp
+++ b/contrib/llvm/tools/llvm-ar/llvm-ar.cpp
@@ -15,8 +15,6 @@
#include "llvm/ADT/StringSwitch.h"
#include "llvm/ADT/Triple.h"
#include "llvm/IR/LLVMContext.h"
-#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
-#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/MachO.h"
@@ -26,15 +24,17 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h"
-#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/Process.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"
+#include "llvm/ToolDrivers/llvm-lib/LibDriver.h"
#if !defined(_MSC_VER) && !defined(__MINGW32__)
#include <unistd.h>
@@ -47,10 +47,76 @@ using namespace llvm;
// The name this program was invoked as.
static StringRef ToolName;
+// The basename of this program.
+static StringRef Stem;
+
+const char RanlibHelp[] = R"(
+OVERVIEW: LLVM Ranlib (llvm-ranlib)
+
+ This program generates an index to speed access to archives
+
+USAGE: llvm-ranlib <archive-file>
+
+OPTIONS:
+ -help - Display available options
+ -version - Display the version of this program
+)";
+
+const char ArHelp[] = R"(
+OVERVIEW: LLVM Archiver (llvm-ar)
+
+ This program archives bitcode files into single libraries
+
+USAGE: llvm-ar [options] [relpos] [count] <archive-file> [members]...
+
+OPTIONS:
+ -M -
+ -format - Archive format to create
+ =default - default
+ =gnu - gnu
+ =darwin - darwin
+ =bsd - bsd
+ -plugin=<string> - plugin (ignored for compatibility
+ -help - Display available options
+ -version - Display the version of this program
+
+OPERATIONS:
+ d[NsS] - delete file(s) from the archive
+ m[abiSs] - move file(s) in the archive
+ p[kN] - print file(s) found in the archive
+ q[ufsS] - quick append file(s) to the archive
+ r[abfiuRsS] - replace or insert file(s) into the archive
+ t - display contents of archive
+ x[No] - extract file(s) from the archive
+
+MODIFIERS (operation specific):
+ [a] - put file(s) after [relpos]
+ [b] - put file(s) before [relpos] (same as [i])
+ [D] - use zero for timestamps and uids/gids (default)
+ [i] - put file(s) before [relpos] (same as [b])
+ [o] - preserve original dates
+ [s] - create an archive index (cf. ranlib)
+ [S] - do not build a symbol table
+ [T] - create a thin archive
+ [u] - update only files newer than archive contents
+ [U] - use actual timestamps and uids/gids
+
+MODIFIERS (generic):
+ [c] - do not warn if the library had to be created
+ [v] - be verbose about actions taken
+)";
+
+void printHelpMessage() {
+ if (Stem.contains_lower("ranlib"))
+ outs() << RanlibHelp;
+ else if (Stem.contains_lower("ar"))
+ outs() << ArHelp;
+}
+
// Show the error message and exit.
LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) {
errs() << ToolName << ": " << Error << ".\n";
- cl::PrintHelpMessage();
+ printHelpMessage();
exit(1);
}
@@ -76,55 +142,18 @@ static void failIfError(Error E, Twine Context = "") {
});
}
-// llvm-ar/llvm-ranlib remaining positional arguments.
-static cl::list<std::string>
- RestOfArgs(cl::Positional, cl::ZeroOrMore,
- cl::desc("[relpos] [count] <archive-file> [members]..."));
+static SmallVector<const char *, 256> PositionalArgs;
-static cl::opt<bool> MRI("M", cl::desc(""));
-static cl::opt<std::string> Plugin("plugin", cl::desc("plugin (ignored for compatibility"));
+static bool MRI;
namespace {
-enum Format { Default, GNU, BSD, DARWIN };
+enum Format { Default, GNU, BSD, DARWIN, Unknown };
}
-static cl::opt<Format>
- FormatOpt("format", cl::desc("Archive format to create"),
- cl::values(clEnumValN(Default, "default", "default"),
- clEnumValN(GNU, "gnu", "gnu"),
- clEnumValN(DARWIN, "darwin", "darwin"),
- clEnumValN(BSD, "bsd", "bsd")));
+static Format FormatType = Default;
static std::string Options;
-// Provide additional help output explaining the operations and modifiers of
-// llvm-ar. This object instructs the CommandLine library to print the text of
-// the constructor when the --help option is given.
-static cl::extrahelp MoreHelp(
- "\nOPERATIONS:\n"
- " d[NsS] - delete file(s) from the archive\n"
- " m[abiSs] - move file(s) in the archive\n"
- " p[kN] - print file(s) found in the archive\n"
- " q[ufsS] - quick append file(s) to the archive\n"
- " r[abfiuRsS] - replace or insert file(s) into the archive\n"
- " t - display contents of archive\n"
- " x[No] - extract file(s) from the archive\n"
- "\nMODIFIERS (operation specific):\n"
- " [a] - put file(s) after [relpos]\n"
- " [b] - put file(s) before [relpos] (same as [i])\n"
- " [i] - put file(s) before [relpos] (same as [b])\n"
- " [o] - preserve original dates\n"
- " [s] - create an archive index (cf. ranlib)\n"
- " [S] - do not build a symbol table\n"
- " [T] - create a thin archive\n"
- " [u] - update only files newer than archive contents\n"
- "\nMODIFIERS (generic):\n"
- " [c] - do not warn if the library had to be created\n"
- " [v] - be verbose about actions taken\n"
-);
-
-static const char OptionChars[] = "dmpqrtxabiosSTucv";
-
// This enumeration delineates the kinds of operations on an archive
// that are permitted.
enum ArchiveOperation {
@@ -166,30 +195,23 @@ static std::vector<StringRef> Members;
// Extract the member filename from the command line for the [relpos] argument
// associated with a, b, and i modifiers
static void getRelPos() {
- if(RestOfArgs.size() == 0)
+ if (PositionalArgs.size() == 0)
fail("Expected [relpos] for a, b, or i modifier");
- RelPos = RestOfArgs[0];
- RestOfArgs.erase(RestOfArgs.begin());
-}
-
-static void getOptions() {
- if(RestOfArgs.size() == 0)
- fail("Expected options");
- Options = RestOfArgs[0];
- RestOfArgs.erase(RestOfArgs.begin());
+ RelPos = PositionalArgs[0];
+ PositionalArgs.erase(PositionalArgs.begin());
}
// Get the archive file name from the command line
static void getArchive() {
- if(RestOfArgs.size() == 0)
+ if (PositionalArgs.size() == 0)
fail("An archive name must be specified");
- ArchiveName = RestOfArgs[0];
- RestOfArgs.erase(RestOfArgs.begin());
+ ArchiveName = PositionalArgs[0];
+ PositionalArgs.erase(PositionalArgs.begin());
}
-// Copy over remaining items in RestOfArgs to our Members vector
+// Copy over remaining items in PositionalArgs to our Members vector
static void getMembers() {
- for (auto &Arg : RestOfArgs)
+ for (auto &Arg : PositionalArgs)
Members.push_back(Arg);
}
@@ -200,13 +222,11 @@ static void runMRIScript();
// modifier/operation pairs have not been violated.
static ArchiveOperation parseCommandLine() {
if (MRI) {
- if (!RestOfArgs.empty())
+ if (!PositionalArgs.empty() || !Options.empty())
fail("Cannot mix -M and other options");
runMRIScript();
}
- getOptions();
-
// Keep track of number of operations. We can only specify one
// per execution.
unsigned NumOperations = 0;
@@ -370,6 +390,7 @@ static void doExtract(StringRef Name, const object::Archive::Child &C) {
int FD;
failIfError(sys::fs::openFileForWrite(sys::path::filename(Name), FD,
+ sys::fs::CD_CreateAlways,
sys::fs::F_None, Mode),
Name);
@@ -651,7 +672,7 @@ performWriteOperation(ArchiveOperation Operation,
NewMembers = computeNewArchiveMembers(Operation, OldArchive);
object::Archive::Kind Kind;
- switch (FormatOpt) {
+ switch (FormatType) {
case Default:
if (Thin)
Kind = object::Archive::K_GNU;
@@ -677,6 +698,8 @@ performWriteOperation(ArchiveOperation Operation,
fail("Only the gnu format has a thin mode");
Kind = object::Archive::K_DARWIN;
break;
+ case Unknown:
+ llvm_unreachable("");
}
Error E =
@@ -758,7 +781,7 @@ static int performOperation(ArchiveOperation Operation,
}
static void runMRIScript() {
- enum class MRICommand { AddLib, AddMod, Create, Save, End, Invalid };
+ enum class MRICommand { AddLib, AddMod, Create, Delete, Save, End, Invalid };
ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getSTDIN();
failIfError(Buf.getError());
@@ -779,6 +802,7 @@ static void runMRIScript() {
.Case("addlib", MRICommand::AddLib)
.Case("addmod", MRICommand::AddMod)
.Case("create", MRICommand::Create)
+ .Case("delete", MRICommand::Delete)
.Case("save", MRICommand::Save)
.Case("end", MRICommand::End)
.Default(MRICommand::Invalid);
@@ -813,6 +837,12 @@ static void runMRIScript() {
fail("File already saved");
ArchiveName = Rest;
break;
+ case MRICommand::Delete: {
+ StringRef Name = sys::path::filename(Rest);
+ llvm::erase_if(NewMembers,
+ [=](NewArchiveMember &M) { return M.MemberName == Name; });
+ break;
+ }
case MRICommand::Save:
Saved = true;
break;
@@ -829,67 +859,113 @@ static void runMRIScript() {
exit(0);
}
-static int ar_main() {
- // Do our own parsing of the command line because the CommandLine utility
- // can't handle the grouped positional parameters without a dash.
+static bool handleGenericOption(StringRef arg) {
+ if (arg == "-help" || arg == "--help") {
+ printHelpMessage();
+ return true;
+ }
+ if (arg == "-version" || arg == "--version") {
+ cl::PrintVersionMessage();
+ return true;
+ }
+ return false;
+}
+
+static int ar_main(int argc, char **argv) {
+ SmallVector<const char *, 0> Argv(argv, argv + argc);
+ BumpPtrAllocator Alloc;
+ StringSaver Saver(Alloc);
+ cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv);
+ for(size_t i = 1; i < Argv.size(); ++i) {
+ StringRef Arg = Argv[i];
+ const char *match;
+ auto MatchFlagWithArg = [&](const char *expected) {
+ size_t len = strlen(expected);
+ if (Arg == expected) {
+ if (++i >= Argv.size())
+ fail(std::string(expected) + " requires an argument");
+ match = Argv[i];
+ return true;
+ }
+ if (Arg.startswith(expected) && Arg.size() > len &&
+ Arg[len] == '=') {
+ match = Arg.data() + len + 1;
+ return true;
+ }
+ return false;
+ };
+ if (handleGenericOption(Argv[i]))
+ return 0;
+ if (Arg == "--") {
+ for(; i < Argv.size(); ++i)
+ PositionalArgs.push_back(Argv[i]);
+ break;
+ }
+ if (Arg[0] == '-') {
+ if (Arg.startswith("--"))
+ Arg = Argv[i] + 2;
+ else
+ Arg = Argv[i] + 1;
+ if (Arg == "M") {
+ MRI = true;
+ } else if (MatchFlagWithArg("format")) {
+ FormatType = StringSwitch<Format>(match)
+ .Case("default", Default)
+ .Case("gnu", GNU)
+ .Case("darwin", DARWIN)
+ .Case("bsd", BSD)
+ .Default(Unknown);
+ if (FormatType == Unknown)
+ fail(std::string("Invalid format ") + match);
+ } else if (MatchFlagWithArg("plugin")) {
+ // Ignored.
+ } else {
+ Options += Argv[i] + 1;
+ }
+ } else if (Options.empty()) {
+ Options += Argv[i];
+ } else {
+ PositionalArgs.push_back(Argv[i]);
+ }
+ }
ArchiveOperation Operation = parseCommandLine();
return performOperation(Operation, nullptr);
}
-static int ranlib_main() {
- if (RestOfArgs.size() != 1)
- fail(ToolName + " takes just one archive as an argument");
- ArchiveName = RestOfArgs[0];
+static int ranlib_main(int argc, char **argv) {
+ bool ArchiveSpecified = false;
+ for(int i = 1; i < argc; ++i) {
+ if (handleGenericOption(argv[i])) {
+ return 0;
+ } else {
+ if (ArchiveSpecified)
+ fail("Exactly one archive should be specified");
+ ArchiveSpecified = true;
+ ArchiveName = argv[i];
+ }
+ }
return performOperation(CreateSymTab, nullptr);
}
int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
ToolName = argv[0];
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
llvm::InitializeAllAsmParsers();
- StringRef Stem = sys::path::stem(ToolName);
- if (Stem.find("dlltool") != StringRef::npos)
+ Stem = sys::path::stem(ToolName);
+ if (Stem.contains_lower("dlltool"))
return dlltoolDriverMain(makeArrayRef(argv, argc));
- if (Stem.find("ranlib") == StringRef::npos &&
- Stem.find("lib") != StringRef::npos)
- return libDriverMain(makeArrayRef(argv, argc));
+ if (Stem.contains_lower("ranlib"))
+ return ranlib_main(argc, argv);
- for (int i = 1; i < argc; i++) {
- // If an argument starts with a dash and only contains chars
- // that belong to the options chars set, remove the dash.
- // We can't handle it after the command line options parsing
- // is done, since it will error out on an unrecognized string
- // starting with a dash.
- // Make sure this doesn't match the actual llvm-ar specific options
- // that start with a dash.
- StringRef S = argv[i];
- if (S.startswith("-") &&
- S.find_first_not_of(OptionChars, 1) == StringRef::npos) {
- argv[i]++;
- break;
- }
- if (S == "--")
- break;
- }
+ if (Stem.contains_lower("lib"))
+ return libDriverMain(makeArrayRef(argv, argc));
- // Have the command line options parsed and handle things
- // like --help and --version.
- cl::ParseCommandLineOptions(argc, argv,
- "LLVM Archiver (llvm-ar)\n\n"
- " This program archives bitcode files into single libraries\n"
- );
-
- if (Stem.find("ranlib") != StringRef::npos)
- return ranlib_main();
- if (Stem.find("ar") != StringRef::npos)
- return ar_main();
+ if (Stem.contains_lower("ar"))
+ return ar_main(argc, argv);
fail("Not ranlib, ar, lib or dlltool!");
}
diff --git a/contrib/llvm/tools/llvm-as/llvm-as.cpp b/contrib/llvm/tools/llvm-as/llvm-as.cpp
index 9f0f162b74f8..bb4233aa9ba0 100644
--- a/contrib/llvm/tools/llvm-as/llvm-as.cpp
+++ b/contrib/llvm/tools/llvm-as/llvm-as.cpp
@@ -19,12 +19,12 @@
#include "llvm/Bitcode/BitcodeWriter.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
+#include "llvm/IR/ModuleSummaryIndex.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -59,7 +59,12 @@ static cl::opt<bool> PreserveBitcodeUseListOrder(
cl::desc("Preserve use-list order when writing LLVM bitcode."),
cl::init(true), cl::Hidden);
-static void WriteOutputFile(const Module *M) {
+static cl::opt<std::string> ClDataLayout("data-layout",
+ cl::desc("data layout string to use"),
+ cl::value_desc("layout-string"),
+ cl::init(""));
+
+static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) {
// Infer the output filename if needed.
if (OutputFilename.empty()) {
if (InputFilename == "-") {
@@ -79,30 +84,44 @@ static void WriteOutputFile(const Module *M) {
exit(1);
}
- if (Force || !CheckBitcodeOutputToConsole(Out->os(), true))
- WriteBitcodeToFile(M, Out->os(), PreserveBitcodeUseListOrder, nullptr,
- EmitModuleHash);
+ if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) {
+ const ModuleSummaryIndex *IndexToWrite = nullptr;
+ // Don't attempt to write a summary index unless it contains any entries.
+ // Otherwise we get an empty summary section.
+ if (Index && Index->begin() != Index->end())
+ IndexToWrite = Index;
+ if (!IndexToWrite || (M && (!M->empty() || !M->global_empty())))
+ // If we have a non-empty Module, then we write the Module plus
+ // any non-null Index along with it as a per-module Index.
+ // If both are empty, this will give an empty module block, which is
+ // the expected behavior.
+ WriteBitcodeToFile(*M, Out->os(), PreserveBitcodeUseListOrder,
+ IndexToWrite, EmitModuleHash);
+ else
+ // Otherwise, with an empty Module but non-empty Index, we write a
+ // combined index.
+ WriteIndexToFile(*IndexToWrite, Out->os());
+ }
// Declare success.
Out->keep();
}
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
+ InitLLVM X(argc, argv);
LLVMContext Context;
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm .ll -> .bc assembler\n");
// Parse the file now...
SMDiagnostic Err;
- std::unique_ptr<Module> M =
- parseAssemblyFile(InputFilename, Err, Context, nullptr, !DisableVerify);
+ auto ModuleAndIndex = parseAssemblyFileWithIndex(
+ InputFilename, Err, Context, nullptr, !DisableVerify, ClDataLayout);
+ std::unique_ptr<Module> M = std::move(ModuleAndIndex.Mod);
if (!M.get()) {
Err.print(argv[0], errs());
return 1;
}
+ std::unique_ptr<ModuleSummaryIndex> Index = std::move(ModuleAndIndex.Index);
if (!DisableVerify) {
std::string ErrorStr;
@@ -113,13 +132,17 @@ int main(int argc, char **argv) {
errs() << OS.str();
return 1;
}
+ // TODO: Implement and call summary index verifier.
}
- if (DumpAsm)
+ if (DumpAsm) {
errs() << "Here's the assembly:\n" << *M.get();
+ if (Index.get() && Index->begin() != Index->end())
+ Index->print(errs());
+ }
if (!DisableOutput)
- WriteOutputFile(M.get());
+ WriteOutputFile(M.get(), Index.get());
return 0;
}
diff --git a/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
index 7f20e136eefd..1939dc6440fe 100644
--- a/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
+++ b/contrib/llvm/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
@@ -33,11 +33,11 @@
#include "llvm/Bitcode/LLVMBitCodes.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/SHA1.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -75,7 +75,9 @@ namespace {
/// CurStreamTypeType - A type for CurStreamType
enum CurStreamTypeType {
UnknownBitstream,
- LLVMIRBitstream
+ LLVMIRBitstream,
+ ClangSerializedASTBitstream,
+ ClangSerializedDiagnosticsBitstream,
};
}
@@ -306,6 +308,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
return nullptr;
STRINGIFY_CODE(FS, PERMODULE)
STRINGIFY_CODE(FS, PERMODULE_PROFILE)
+ STRINGIFY_CODE(FS, PERMODULE_RELBF)
STRINGIFY_CODE(FS, PERMODULE_GLOBALVAR_INIT_REFS)
STRINGIFY_CODE(FS, COMBINED)
STRINGIFY_CODE(FS, COMBINED_PROFILE)
@@ -314,6 +317,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FS, COMBINED_ALIAS)
STRINGIFY_CODE(FS, COMBINED_ORIGINAL_NAME)
STRINGIFY_CODE(FS, VERSION)
+ STRINGIFY_CODE(FS, FLAGS)
STRINGIFY_CODE(FS, TYPE_TESTS)
STRINGIFY_CODE(FS, TYPE_TEST_ASSUME_VCALLS)
STRINGIFY_CODE(FS, TYPE_CHECKED_LOAD_VCALLS)
@@ -322,6 +326,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
STRINGIFY_CODE(FS, VALUE_GUID)
STRINGIFY_CODE(FS, CFI_FUNCTION_DEFS)
STRINGIFY_CODE(FS, CFI_FUNCTION_DECLS)
+ STRINGIFY_CODE(FS, TYPE_ID)
}
case bitc::METADATA_ATTACHMENT_ID:
switch(CodeID) {
@@ -442,7 +447,7 @@ static std::map<unsigned, PerBlockIDStats> BlockIDStats;
/// ReportError - All bitcode analysis errors go through this function, making this a
/// good place to breakpoint if debugging.
static bool ReportError(const Twine &Err) {
- errs() << Err << "\n";
+ WithColor::error() << Err << "\n";
return true;
}
@@ -597,7 +602,7 @@ static bool ParseBlock(BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo,
++BlockStats.NumRecords;
StringRef Blob;
- unsigned CurrentRecordPos = Stream.GetCurrentBitNo();
+ uint64_t CurrentRecordPos = Stream.GetCurrentBitNo();
unsigned Code = Stream.readRecord(Entry.ID, Record, &Blob);
// Increment the # occurrences of this code.
@@ -694,7 +699,7 @@ static bool ParseBlock(BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo,
std::string Str;
bool ArrayIsPrintable = true;
for (unsigned j = i - 1, je = Record.size(); j != je; ++j) {
- if (!isprint(static_cast<unsigned char>(Record[j]))) {
+ if (!isPrint(static_cast<unsigned char>(Record[j]))) {
ArrayIsPrintable = false;
break;
}
@@ -714,7 +719,7 @@ static bool ParseBlock(BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo,
} else {
bool BlobIsPrintable = true;
for (unsigned i = 0, e = Blob.size(); i != e; ++i)
- if (!isprint(static_cast<unsigned char>(Blob[i]))) {
+ if (!isPrint(static_cast<unsigned char>(Blob[i]))) {
BlobIsPrintable = false;
break;
}
@@ -743,6 +748,35 @@ static void PrintSize(uint64_t Bits) {
(double)Bits/8, (unsigned long)(Bits/32));
}
+static CurStreamTypeType ReadSignature(BitstreamCursor &Stream) {
+ char Signature[6];
+ Signature[0] = Stream.Read(8);
+ Signature[1] = Stream.Read(8);
+
+ // Autodetect the file contents, if it is one we know.
+ if (Signature[0] == 'C' && Signature[1] == 'P') {
+ Signature[2] = Stream.Read(8);
+ Signature[3] = Stream.Read(8);
+ if (Signature[2] == 'C' && Signature[3] == 'H')
+ return ClangSerializedASTBitstream;
+ } else if (Signature[0] == 'D' && Signature[1] == 'I') {
+ Signature[2] = Stream.Read(8);
+ Signature[3] = Stream.Read(8);
+ if (Signature[2] == 'A' && Signature[3] == 'G')
+ return ClangSerializedDiagnosticsBitstream;
+ } else {
+ Signature[2] = Stream.Read(4);
+ Signature[3] = Stream.Read(4);
+ Signature[4] = Stream.Read(4);
+ Signature[5] = Stream.Read(4);
+ if (Signature[0] == 'B' && Signature[1] == 'C' &&
+ Signature[2] == 0x0 && Signature[3] == 0xC &&
+ Signature[4] == 0xE && Signature[5] == 0xD)
+ return LLVMIRBitstream;
+ }
+ return UnknownBitstream;
+}
+
static bool openBitcodeFile(StringRef Path,
std::unique_ptr<MemoryBuffer> &MemBuf,
BitstreamCursor &Stream,
@@ -786,22 +820,7 @@ static bool openBitcodeFile(StringRef Path,
}
Stream = BitstreamCursor(ArrayRef<uint8_t>(BufPtr, EndBufPtr));
-
- // Read the stream signature.
- char Signature[6];
- Signature[0] = Stream.Read(8);
- Signature[1] = Stream.Read(8);
- Signature[2] = Stream.Read(4);
- Signature[3] = Stream.Read(4);
- Signature[4] = Stream.Read(4);
- Signature[5] = Stream.Read(4);
-
- // Autodetect the file contents, if it is one we know.
- CurStreamType = UnknownBitstream;
- if (Signature[0] == 'B' && Signature[1] == 'C' &&
- Signature[2] == 0x0 && Signature[3] == 0xC &&
- Signature[4] == 0xE && Signature[5] == 0xD)
- CurStreamType = LLVMIRBitstream;
+ CurStreamType = ReadSignature(Stream);
return false;
}
@@ -870,8 +889,18 @@ static int AnalyzeBitcode() {
outs() << "\n";
outs() << " Stream type: ";
switch (CurStreamType) {
- case UnknownBitstream: outs() << "unknown\n"; break;
- case LLVMIRBitstream: outs() << "LLVM IR\n"; break;
+ case UnknownBitstream:
+ outs() << "unknown\n";
+ break;
+ case LLVMIRBitstream:
+ outs() << "LLVM IR\n";
+ break;
+ case ClangSerializedASTBitstream:
+ outs() << "Clang Serialized AST\n";
+ break;
+ case ClangSerializedDiagnosticsBitstream:
+ outs() << "Clang Serialized Diagnostics\n";
+ break;
}
outs() << " # Toplevel Blocks: " << NumTopBlocks << "\n";
outs() << "\n";
@@ -961,11 +990,7 @@ static int AnalyzeBitcode() {
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, "llvm-bcanalyzer file analyzer\n");
-
return AnalyzeBitcode();
}
diff --git a/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp b/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp
index c5ea50bff273..e93b63d388e0 100644
--- a/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp
+++ b/contrib/llvm/tools/llvm-cov/CodeCoverage.cpp
@@ -13,6 +13,7 @@
//
//===----------------------------------------------------------------------===//
+#include "CoverageExporterJson.h"
#include "CoverageFilters.h"
#include "CoverageReport.h"
#include "CoverageSummaryInfo.h"
@@ -32,8 +33,8 @@
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/ScopedPrinter.h"
-#include "llvm/Support/Threading.h"
#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
#include "llvm/Support/ToolOutputFile.h"
#include <functional>
@@ -48,83 +49,84 @@ void exportCoverageDataToJson(const coverage::CoverageMapping &CoverageMapping,
raw_ostream &OS);
namespace {
-/// \brief The implementation of the coverage tool.
+/// The implementation of the coverage tool.
class CodeCoverageTool {
public:
enum Command {
- /// \brief The show command.
+ /// The show command.
Show,
- /// \brief The report command.
+ /// The report command.
Report,
- /// \brief The export command.
+ /// The export command.
Export
};
int run(Command Cmd, int argc, const char **argv);
private:
- /// \brief Print the error message to the error output stream.
+ /// Print the error message to the error output stream.
void error(const Twine &Message, StringRef Whence = "");
- /// \brief Print the warning message to the error output stream.
+ /// Print the warning message to the error output stream.
void warning(const Twine &Message, StringRef Whence = "");
- /// \brief Convert \p Path into an absolute path and append it to the list
+ /// Convert \p Path into an absolute path and append it to the list
/// of collected paths.
void addCollectedPath(const std::string &Path);
- /// \brief If \p Path is a regular file, collect the path. If it's a
+ /// If \p Path is a regular file, collect the path. If it's a
/// directory, recursively collect all of the paths within the directory.
void collectPaths(const std::string &Path);
- /// \brief Return a memory buffer for the given source file.
+ /// Return a memory buffer for the given source file.
ErrorOr<const MemoryBuffer &> getSourceFile(StringRef SourceFile);
- /// \brief Create source views for the expansions of the view.
+ /// Create source views for the expansions of the view.
void attachExpansionSubViews(SourceCoverageView &View,
ArrayRef<ExpansionRecord> Expansions,
const CoverageMapping &Coverage);
- /// \brief Create the source view of a particular function.
+ /// Create the source view of a particular function.
std::unique_ptr<SourceCoverageView>
createFunctionView(const FunctionRecord &Function,
const CoverageMapping &Coverage);
- /// \brief Create the main source view of a particular source file.
+ /// Create the main source view of a particular source file.
std::unique_ptr<SourceCoverageView>
createSourceFileView(StringRef SourceFile, const CoverageMapping &Coverage);
- /// \brief Load the coverage mapping data. Return nullptr if an error occurred.
+ /// Load the coverage mapping data. Return nullptr if an error occurred.
std::unique_ptr<CoverageMapping> load();
- /// \brief Create a mapping from files in the Coverage data to local copies
+ /// Create a mapping from files in the Coverage data to local copies
/// (path-equivalence).
void remapPathNames(const CoverageMapping &Coverage);
- /// \brief Remove input source files which aren't mapped by \p Coverage.
+ /// Remove input source files which aren't mapped by \p Coverage.
void removeUnmappedInputs(const CoverageMapping &Coverage);
- /// \brief If a demangler is available, demangle all symbol names.
+ /// If a demangler is available, demangle all symbol names.
void demangleSymbols(const CoverageMapping &Coverage);
- /// \brief Write out a source file view to the filesystem.
+ /// Write out a source file view to the filesystem.
void writeSourceFileView(StringRef SourceFile, CoverageMapping *Coverage,
CoveragePrinter *Printer, bool ShowFilenames);
typedef llvm::function_ref<int(int, const char **)> CommandLineParserType;
- int show(int argc, const char **argv,
- CommandLineParserType commandLineParser);
-
- int report(int argc, const char **argv,
+ int doShow(int argc, const char **argv,
CommandLineParserType commandLineParser);
- int export_(int argc, const char **argv,
- CommandLineParserType commandLineParser);
+ int doReport(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
+
+ int doExport(int argc, const char **argv,
+ CommandLineParserType commandLineParser);
std::vector<StringRef> ObjectFilenames;
CoverageViewOptions ViewOpts;
CoverageFiltersMatchAll Filters;
+ CoverageFilters IgnoreFilenameFilters;
/// The path to the indexed profile.
std::string PGOFilename;
@@ -188,7 +190,8 @@ void CodeCoverageTool::addCollectedPath(const std::string &Path) {
return;
}
sys::path::remove_dots(EffectivePath, /*remove_dot_dots=*/true);
- SourceFiles.emplace_back(EffectivePath.str());
+ if (!IgnoreFilenameFilters.matchesFilename(EffectivePath))
+ SourceFiles.emplace_back(EffectivePath.str());
}
void CodeCoverageTool::collectPaths(const std::string &Path) {
@@ -198,7 +201,7 @@ void CodeCoverageTool::collectPaths(const std::string &Path) {
if (PathRemapping)
addCollectedPath(Path);
else
- error("Missing source file", Path);
+ warning("Source file doesn't exist, proceeded by ignoring it.", Path);
return;
}
@@ -210,12 +213,16 @@ void CodeCoverageTool::collectPaths(const std::string &Path) {
if (llvm::sys::fs::is_directory(Status)) {
std::error_code EC;
for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E;
- F != E && !EC; F.increment(EC)) {
+ F != E; F.increment(EC)) {
+
+ if (EC) {
+ warning(EC.message(), F->path());
+ continue;
+ }
+
if (llvm::sys::fs::is_regular_file(F->path()))
addCollectedPath(F->path());
}
- if (EC)
- warning(EC.message(), Path);
}
}
@@ -471,14 +478,13 @@ void CodeCoverageTool::demangleSymbols(const CoverageMapping &Coverage) {
OutputTOF.os().close();
// Invoke the demangler.
- std::vector<const char *> ArgsV;
- for (const std::string &Arg : ViewOpts.DemanglerOpts)
- ArgsV.push_back(Arg.c_str());
- ArgsV.push_back(nullptr);
+ std::vector<StringRef> ArgsV;
+ for (StringRef Arg : ViewOpts.DemanglerOpts)
+ ArgsV.push_back(Arg);
Optional<StringRef> Redirects[] = {InputPath.str(), OutputPath.str(), {""}};
std::string ErrMsg;
- int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV.data(),
- /*env=*/nullptr, Redirects, /*secondsToWait=*/0,
+ int RC = sys::ExecuteAndWait(ViewOpts.DemanglerOpts[0], ArgsV,
+ /*env=*/None, Redirects, /*secondsToWait=*/0,
/*memoryLimit=*/0, &ErrMsg);
if (RC) {
error(ErrMsg, ViewOpts.DemanglerOpts[0]);
@@ -592,6 +598,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
"regular expression"),
cl::ZeroOrMore, cl::cat(FilteringCategory));
+ cl::list<std::string> IgnoreFilenameRegexFilters(
+ "ignore-filename-regex", cl::Optional,
+ cl::desc("Skip source code files with file paths that match the given "
+ "regular expression"),
+ cl::ZeroOrMore, cl::cat(FilteringCategory));
+
cl::opt<double> RegionCoverageLtFilter(
"region-coverage-lt", cl::Optional,
cl::desc("Show code coverage only for functions with region coverage "
@@ -636,6 +648,12 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
"summary-only", cl::Optional,
cl::desc("Export only summary information for each source file"));
+ cl::opt<unsigned> NumThreads(
+ "num-threads", cl::init(0),
+ cl::desc("Number of merge threads to use (default: autodetect)"));
+ cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
+ cl::aliasopt(NumThreads));
+
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
ViewOpts.Debug = DebugDump;
@@ -703,6 +721,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
llvm::make_unique<NameRegexCoverageFilter>(Regex));
Filters.push_back(std::move(NameFilterer));
}
+
if (RegionCoverageLtFilter.getNumOccurrences() ||
RegionCoverageGtFilter.getNumOccurrences() ||
LineCoverageLtFilter.getNumOccurrences() ||
@@ -723,6 +742,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
Filters.push_back(std::move(StatFilterer));
}
+ // Create the ignore filename filters.
+ for (const auto &RE : IgnoreFilenameRegexFilters)
+ IgnoreFilenameFilters.push_back(
+ llvm::make_unique<NameRegexCoverageFilter>(RE));
+
if (!Arches.empty()) {
for (const std::string &Arch : Arches) {
if (Triple(Arch).getArch() == llvm::Triple::ArchType::UnknownArch) {
@@ -737,6 +761,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
}
}
+ // IgnoreFilenameFilters are applied even when InputSourceFiles specified.
for (const std::string &File : InputSourceFiles)
collectPaths(File);
@@ -749,23 +774,24 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
ViewOpts.ShowRegionSummary = RegionSummary;
ViewOpts.ShowInstantiationSummary = InstantiationSummary;
ViewOpts.ExportSummaryOnly = SummaryOnly;
+ ViewOpts.NumThreads = NumThreads;
return 0;
};
switch (Cmd) {
case Show:
- return show(argc, argv, commandLineParser);
+ return doShow(argc, argv, commandLineParser);
case Report:
- return report(argc, argv, commandLineParser);
+ return doReport(argc, argv, commandLineParser);
case Export:
- return export_(argc, argv, commandLineParser);
+ return doExport(argc, argv, commandLineParser);
}
return 0;
}
-int CodeCoverageTool::show(int argc, const char **argv,
- CommandLineParserType commandLineParser) {
+int CodeCoverageTool::doShow(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
cl::OptionCategory ViewCategory("Viewing options");
@@ -808,12 +834,6 @@ int CodeCoverageTool::show(int argc, const char **argv,
"project-title", cl::Optional,
cl::desc("Set project title for the coverage report"));
- cl::opt<unsigned> NumThreads(
- "num-threads", cl::init(0),
- cl::desc("Number of merge threads to use (default: autodetect)"));
- cl::alias NumThreadsA("j", cl::desc("Alias for --num-threads"),
- cl::aliasopt(NumThreads));
-
auto Err = commandLineParser(argc, argv);
if (Err)
return Err;
@@ -856,8 +876,10 @@ int CodeCoverageTool::show(int argc, const char **argv,
if (SourceFiles.empty())
// Get the source files from the function coverage mapping.
- for (StringRef Filename : Coverage->getUniqueSourceFiles())
- SourceFiles.push_back(Filename);
+ for (StringRef Filename : Coverage->getUniqueSourceFiles()) {
+ if (!IgnoreFilenameFilters.matchesFilename(Filename))
+ SourceFiles.push_back(Filename);
+ }
// Create an index out of the source files.
if (ViewOpts.hasOutputDirectory()) {
@@ -910,6 +932,8 @@ int CodeCoverageTool::show(int argc, const char **argv,
(SourceFiles.size() != 1) || ViewOpts.hasOutputDirectory() ||
(ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML);
+ auto NumThreads = ViewOpts.NumThreads;
+
// If NumThreads is not specified, auto-detect a good default.
if (NumThreads == 0)
NumThreads =
@@ -932,8 +956,8 @@ int CodeCoverageTool::show(int argc, const char **argv,
return 0;
}
-int CodeCoverageTool::report(int argc, const char **argv,
- CommandLineParserType commandLineParser) {
+int CodeCoverageTool::doReport(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
cl::opt<bool> ShowFunctionSummaries(
"show-functions", cl::Optional, cl::init(false),
cl::desc("Show coverage summaries for each function"));
@@ -954,7 +978,7 @@ int CodeCoverageTool::report(int argc, const char **argv,
CoverageReport Report(ViewOpts, *Coverage.get());
if (!ShowFunctionSummaries) {
if (SourceFiles.empty())
- Report.renderFileReports(llvm::outs());
+ Report.renderFileReports(llvm::outs(), IgnoreFilenameFilters);
else
Report.renderFileReports(llvm::outs(), SourceFiles);
} else {
@@ -969,8 +993,8 @@ int CodeCoverageTool::report(int argc, const char **argv,
return 0;
}
-int CodeCoverageTool::export_(int argc, const char **argv,
- CommandLineParserType commandLineParser) {
+int CodeCoverageTool::doExport(int argc, const char **argv,
+ CommandLineParserType commandLineParser) {
auto Err = commandLineParser(argc, argv);
if (Err)
@@ -987,7 +1011,12 @@ int CodeCoverageTool::export_(int argc, const char **argv,
return 1;
}
- exportCoverageDataToJson(*Coverage.get(), ViewOpts, outs());
+ auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs());
+
+ if (SourceFiles.empty())
+ Exporter.renderRoot(IgnoreFilenameFilters);
+ else
+ Exporter.renderRoot(SourceFiles);
return 0;
}
diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporter.h b/contrib/llvm/tools/llvm-cov/CoverageExporter.h
new file mode 100644
index 000000000000..884fba96d618
--- /dev/null
+++ b/contrib/llvm/tools/llvm-cov/CoverageExporter.h
@@ -0,0 +1,52 @@
+//===- CoverageExporter.h - Code coverage exporter ------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class defines a code coverage exporter interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEEXPORTER_H
+#define LLVM_COV_COVERAGEEXPORTER_H
+
+#include "CoverageFilters.h"
+#include "CoverageSummaryInfo.h"
+#include "CoverageViewOptions.h"
+#include "llvm/ProfileData/Coverage/CoverageMapping.h"
+
+namespace llvm {
+
+/// Exports the code coverage information.
+class CoverageExporter {
+protected:
+ /// The full CoverageMapping object to export.
+ const coverage::CoverageMapping &Coverage;
+
+ /// The options passed to the tool.
+ const CoverageViewOptions &Options;
+
+ /// Output stream to print JSON to.
+ raw_ostream &OS;
+
+ CoverageExporter(const coverage::CoverageMapping &CoverageMapping,
+ const CoverageViewOptions &Options, raw_ostream &OS)
+ : Coverage(CoverageMapping), Options(Options), OS(OS) {}
+
+public:
+ virtual ~CoverageExporter(){};
+
+ /// Render the CoverageMapping object.
+ virtual void renderRoot(const CoverageFilters &IgnoreFilenameFilters) = 0;
+
+ /// Render the CoverageMapping object for specified source files.
+ virtual void renderRoot(const std::vector<std::string> &SourceFiles) = 0;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_COV_COVERAGEEXPORTER_H
diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp
index 7b700908968d..56c3a0003b02 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp
+++ b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.cpp
@@ -41,394 +41,346 @@
//
//===----------------------------------------------------------------------===//
+#include "CoverageExporterJson.h"
#include "CoverageReport.h"
-#include "CoverageSummaryInfo.h"
-#include "CoverageViewOptions.h"
-#include "llvm/ProfileData/Coverage/CoverageMapping.h"
-#include <stack>
-/// \brief The semantic version combined as a string.
+/// The semantic version combined as a string.
#define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0"
-/// \brief Unique type identifier for JSON coverage export.
+/// Unique type identifier for JSON coverage export.
#define LLVM_COVERAGE_EXPORT_JSON_TYPE_STR "llvm.coverage.json.export"
using namespace llvm;
-using namespace coverage;
-class CoverageExporterJson {
- const CoverageViewOptions &Options;
-
- /// \brief Output stream to print JSON to.
- raw_ostream &OS;
-
- /// \brief The full CoverageMapping object to export.
- const CoverageMapping &Coverage;
-
- /// \brief States that the JSON rendering machine can be in.
- enum JsonState { None, NonEmptyElement, EmptyElement };
-
- /// \brief Tracks state of the JSON output.
- std::stack<JsonState> State;
-
- /// \brief Emit a serialized scalar.
- void emitSerialized(const int64_t Value) { OS << Value; }
-
- /// \brief Emit a serialized string.
- void emitSerialized(const std::string &Value) {
- OS << "\"";
- for (char C : Value) {
- if (C != '\\')
- OS << C;
- else
- OS << "\\\\";
- }
- OS << "\"";
- }
-
- /// \brief Emit a comma if there is a previous element to delimit.
- void emitComma() {
- if (State.top() == JsonState::NonEmptyElement) {
- OS << ",";
- } else if (State.top() == JsonState::EmptyElement) {
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
- State.push(JsonState::NonEmptyElement);
- }
- }
-
- /// \brief Emit a starting dictionary/object character.
- void emitDictStart() {
- emitComma();
- State.push(JsonState::EmptyElement);
- OS << "{";
- }
-
- /// \brief Emit a dictionary/object key but no value.
- void emitDictKey(const std::string &Key) {
- emitComma();
- emitSerialized(Key);
- OS << ":";
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
+CoverageExporterJson::CoverageExporterJson(
+ const coverage::CoverageMapping &CoverageMapping,
+ const CoverageViewOptions &Options, raw_ostream &OS)
+ : CoverageExporter(CoverageMapping, Options, OS) {
+ State.push(JsonState::None);
+}
- // We do not want to emit a comma after this key.
- State.push(JsonState::EmptyElement);
- }
+void CoverageExporterJson::emitSerialized(const int64_t Value) { OS << Value; }
- /// \brief Emit a dictionary/object key/value pair.
- template <typename V>
- void emitDictElement(const std::string &Key, const V &Value) {
- emitComma();
- emitSerialized(Key);
- OS << ":";
- emitSerialized(Value);
+void CoverageExporterJson::emitSerialized(const std::string &Value) {
+ OS << "\"";
+ for (char C : Value) {
+ if (C != '\\')
+ OS << C;
+ else
+ OS << "\\\\";
}
+ OS << "\"";
+}
- /// \brief Emit a closing dictionary/object character.
- void emitDictEnd() {
+void CoverageExporterJson::emitComma() {
+ if (State.top() == JsonState::NonEmptyElement) {
+ OS << ",";
+ } else if (State.top() == JsonState::EmptyElement) {
State.pop();
assert((State.size() >= 1) && "Closed too many JSON elements");
- OS << "}";
- }
-
- /// \brief Emit a starting array character.
- void emitArrayStart() {
- emitComma();
- State.push(JsonState::EmptyElement);
- OS << "[";
- }
-
- /// \brief Emit an array element.
- template <typename V> void emitArrayElement(const V &Value) {
- emitComma();
- emitSerialized(Value);
+ State.push(JsonState::NonEmptyElement);
}
+}
- /// \brief emit a closing array character.
- void emitArrayEnd() {
- State.pop();
- assert((State.size() >= 1) && "Closed too many JSON elements");
- OS << "]";
- }
+void CoverageExporterJson::emitDictStart() {
+ emitComma();
+ State.push(JsonState::EmptyElement);
+ OS << "{";
+}
- /// \brief Render the CoverageMapping object.
- void renderRoot() {
- // Start Root of JSON object.
- emitDictStart();
+void CoverageExporterJson::emitDictKey(const std::string &Key) {
+ emitComma();
+ emitSerialized(Key);
+ OS << ":";
+ State.pop();
+ assert((State.size() >= 1) && "Closed too many JSON elements");
- emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
- emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
- emitDictKey("data");
+ // We do not want to emit a comma after this key.
+ State.push(JsonState::EmptyElement);
+}
- // Start List of Exports.
- emitArrayStart();
+void CoverageExporterJson::emitDictEnd() {
+ State.pop();
+ assert((State.size() >= 1) && "Closed too many JSON elements");
+ OS << "}";
+}
- // Start Export.
- emitDictStart();
+void CoverageExporterJson::emitArrayStart() {
+ emitComma();
+ State.push(JsonState::EmptyElement);
+ OS << "[";
+}
- emitDictKey("files");
+void CoverageExporterJson::emitArrayEnd() {
+ State.pop();
+ assert((State.size() >= 1) && "Closed too many JSON elements");
+ OS << "]";
+}
- FileCoverageSummary Totals = FileCoverageSummary("Totals");
- std::vector<std::string> SourceFiles;
- for (StringRef SF : Coverage.getUniqueSourceFiles())
+void CoverageExporterJson::renderRoot(
+ const CoverageFilters &IgnoreFilenameFilters) {
+ std::vector<std::string> SourceFiles;
+ for (StringRef SF : Coverage.getUniqueSourceFiles()) {
+ if (!IgnoreFilenameFilters.matchesFilename(SF))
SourceFiles.emplace_back(SF);
- auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
- SourceFiles, Options);
- renderFiles(SourceFiles, FileReports);
-
- // Skip functions-level information for summary-only export mode.
- if (!Options.ExportSummaryOnly) {
- emitDictKey("functions");
- renderFunctions(Coverage.getCoveredFunctions());
- }
-
- emitDictKey("totals");
- renderSummary(Totals);
-
- // End Export.
- emitDictEnd();
-
- // End List of Exports.
- emitArrayEnd();
-
- // End Root of JSON Object.
- emitDictEnd();
-
- assert((State.top() == JsonState::None) &&
- "All Elements In JSON were Closed");
}
+ renderRoot(SourceFiles);
+}
- /// \brief Render an array of all the given functions.
- void
- renderFunctions(const iterator_range<FunctionRecordIterator> &Functions) {
- // Start List of Functions.
- emitArrayStart();
-
- for (const auto &Function : Functions) {
- // Start Function.
- emitDictStart();
-
- emitDictElement("name", Function.Name);
- emitDictElement("count", Function.ExecutionCount);
- emitDictKey("regions");
-
- renderRegions(Function.CountedRegions);
+void CoverageExporterJson::renderRoot(
+ const std::vector<std::string> &SourceFiles) {
+ // Start Root of JSON object.
+ emitDictStart();
- emitDictKey("filenames");
+ emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR);
+ emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR);
+ emitDictKey("data");
- // Start Filenames for Function.
- emitArrayStart();
+ // Start List of Exports.
+ emitArrayStart();
- for (const auto &FileName : Function.Filenames)
- emitArrayElement(FileName);
+ // Start Export.
+ emitDictStart();
- // End Filenames for Function.
- emitArrayEnd();
+ emitDictKey("files");
- // End Function.
- emitDictEnd();
- }
+ FileCoverageSummary Totals = FileCoverageSummary("Totals");
+ auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals,
+ SourceFiles, Options);
+ renderFiles(SourceFiles, FileReports);
- // End List of Functions.
- emitArrayEnd();
+ // Skip functions-level information for summary-only export mode.
+ if (!Options.ExportSummaryOnly) {
+ emitDictKey("functions");
+ renderFunctions(Coverage.getCoveredFunctions());
}
- /// \brief Render an array of all the source files, also pass back a Summary.
- void renderFiles(ArrayRef<std::string> SourceFiles,
- ArrayRef<FileCoverageSummary> FileReports) {
- // Start List of Files.
- emitArrayStart();
-
- for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
- // Render the file.
- auto FileCoverage = Coverage.getCoverageForFile(SourceFiles[I]);
- renderFile(FileCoverage, FileReports[I]);
- }
+ emitDictKey("totals");
+ renderSummary(Totals);
- // End List of Files.
- emitArrayEnd();
- }
+ // End Export.
+ emitDictEnd();
- /// \brief Render a single file.
- void renderFile(const CoverageData &FileCoverage,
- const FileCoverageSummary &FileReport) {
- // Start File.
- emitDictStart();
+ // End List of Exports.
+ emitArrayEnd();
- emitDictElement("filename", FileCoverage.getFilename());
+ // End Root of JSON Object.
+ emitDictEnd();
- // Skip segments and expansions for summary-only export mode.
- if (!Options.ExportSummaryOnly) {
- emitDictKey("segments");
+ assert((State.top() == JsonState::None) &&
+ "All Elements In JSON were Closed");
+}
- // Start List of Segments.
- emitArrayStart();
+void CoverageExporterJson::renderFunctions(
+ const iterator_range<coverage::FunctionRecordIterator> &Functions) {
+ // Start List of Functions.
+ emitArrayStart();
- for (const auto &Segment : FileCoverage)
- renderSegment(Segment);
+ for (const auto &Function : Functions) {
+ // Start Function.
+ emitDictStart();
- // End List of Segments.
- emitArrayEnd();
+ emitDictElement("name", Function.Name);
+ emitDictElement("count", Function.ExecutionCount);
+ emitDictKey("regions");
- emitDictKey("expansions");
+ renderRegions(Function.CountedRegions);
- // Start List of Expansions.
- emitArrayStart();
+ emitDictKey("filenames");
- for (const auto &Expansion : FileCoverage.getExpansions())
- renderExpansion(Expansion);
+ // Start Filenames for Function.
+ emitArrayStart();
- // End List of Expansions.
- emitArrayEnd();
- }
+ for (const auto &FileName : Function.Filenames)
+ emitArrayElement(FileName);
- emitDictKey("summary");
- renderSummary(FileReport);
+ // End Filenames for Function.
+ emitArrayEnd();
- // End File.
+ // End Function.
emitDictEnd();
}
- /// \brief Render a CoverageSegment.
- void renderSegment(const CoverageSegment &Segment) {
- // Start Segment.
- emitArrayStart();
+ // End List of Functions.
+ emitArrayEnd();
+}
- emitArrayElement(Segment.Line);
- emitArrayElement(Segment.Col);
- emitArrayElement(Segment.Count);
- emitArrayElement(Segment.HasCount);
- emitArrayElement(Segment.IsRegionEntry);
+void CoverageExporterJson::renderFiles(
+ ArrayRef<std::string> SourceFiles,
+ ArrayRef<FileCoverageSummary> FileReports) {
+ // Start List of Files.
+ emitArrayStart();
- // End Segment.
- emitArrayEnd();
+ for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) {
+ renderFile(SourceFiles[I], FileReports[I]);
}
- /// \brief Render an ExpansionRecord.
- void renderExpansion(const ExpansionRecord &Expansion) {
- // Start Expansion.
- emitDictStart();
-
- // Mark the beginning and end of this expansion in the source file.
- emitDictKey("source_region");
- renderRegion(Expansion.Region);
+ // End List of Files.
+ emitArrayEnd();
+}
- // Enumerate the coverage information for the expansion.
- emitDictKey("target_regions");
- renderRegions(Expansion.Function.CountedRegions);
+void CoverageExporterJson::renderFile(const std::string &Filename,
+ const FileCoverageSummary &FileReport) {
+ // Start File.
+ emitDictStart();
- emitDictKey("filenames");
- // Start List of Filenames to map the fileIDs.
- emitArrayStart();
- for (const auto &Filename : Expansion.Function.Filenames)
- emitArrayElement(Filename);
- // End List of Filenames.
- emitArrayEnd();
+ emitDictElement("filename", Filename);
- // End Expansion.
- emitDictEnd();
+ if (!Options.ExportSummaryOnly) {
+ // Calculate and render detailed coverage information for given file.
+ auto FileCoverage = Coverage.getCoverageForFile(Filename);
+ renderFileCoverage(FileCoverage, FileReport);
}
- /// \brief Render a list of CountedRegions.
- void renderRegions(ArrayRef<CountedRegion> Regions) {
- // Start List of Regions.
- emitArrayStart();
+ emitDictKey("summary");
+ renderSummary(FileReport);
- for (const auto &Region : Regions)
- renderRegion(Region);
+ // End File.
+ emitDictEnd();
+}
- // End List of Regions.
- emitArrayEnd();
- }
- /// \brief Render a single CountedRegion.
- void renderRegion(const CountedRegion &Region) {
- // Start CountedRegion.
- emitArrayStart();
+void CoverageExporterJson::renderFileCoverage(
+ const coverage::CoverageData &FileCoverage,
+ const FileCoverageSummary &FileReport) {
+ emitDictKey("segments");
- emitArrayElement(Region.LineStart);
- emitArrayElement(Region.ColumnStart);
- emitArrayElement(Region.LineEnd);
- emitArrayElement(Region.ColumnEnd);
- emitArrayElement(Region.ExecutionCount);
- emitArrayElement(Region.FileID);
- emitArrayElement(Region.ExpandedFileID);
- emitArrayElement(Region.Kind);
+ // Start List of Segments.
+ emitArrayStart();
- // End CountedRegion.
- emitArrayEnd();
- }
+ for (const auto &Segment : FileCoverage)
+ renderSegment(Segment);
- /// \brief Render a FileCoverageSummary.
- void renderSummary(const FileCoverageSummary &Summary) {
- // Start Summary for the file.
- emitDictStart();
+ // End List of Segments.
+ emitArrayEnd();
- emitDictKey("lines");
+ emitDictKey("expansions");
- // Start Line Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.LineCoverage.getNumLines());
- emitDictElement("covered", Summary.LineCoverage.getCovered());
- emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
- // End Line Coverage Summary.
- emitDictEnd();
+ // Start List of Expansions.
+ emitArrayStart();
- emitDictKey("functions");
+ for (const auto &Expansion : FileCoverage.getExpansions())
+ renderExpansion(Expansion);
- // Start Function Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.FunctionCoverage.getNumFunctions());
- emitDictElement("covered", Summary.FunctionCoverage.getExecuted());
- emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
- // End Function Coverage Summary.
- emitDictEnd();
+ // End List of Expansions.
+ emitArrayEnd();
+}
- emitDictKey("instantiations");
+void CoverageExporterJson::renderSegment(
+ const coverage::CoverageSegment &Segment) {
+ // Start Segment.
+ emitArrayStart();
- // Start Instantiation Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.InstantiationCoverage.getNumFunctions());
- emitDictElement("covered", Summary.InstantiationCoverage.getExecuted());
- emitDictElement("percent",
- Summary.InstantiationCoverage.getPercentCovered());
- // End Function Coverage Summary.
- emitDictEnd();
+ emitArrayElement(Segment.Line);
+ emitArrayElement(Segment.Col);
+ emitArrayElement(Segment.Count);
+ emitArrayElement(Segment.HasCount);
+ emitArrayElement(Segment.IsRegionEntry);
- emitDictKey("regions");
+ // End Segment.
+ emitArrayEnd();
+}
- // Start Region Coverage Summary.
- emitDictStart();
- emitDictElement("count", Summary.RegionCoverage.getNumRegions());
- emitDictElement("covered", Summary.RegionCoverage.getCovered());
- emitDictElement("notcovered",
- Summary.RegionCoverage.getNumRegions() -
- Summary.RegionCoverage.getCovered());
- emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
- // End Region Coverage Summary.
- emitDictEnd();
+void CoverageExporterJson::renderExpansion(
+ const coverage::ExpansionRecord &Expansion) {
+ // Start Expansion.
+ emitDictStart();
+
+ // Mark the beginning and end of this expansion in the source file.
+ emitDictKey("source_region");
+ renderRegion(Expansion.Region);
+
+ // Enumerate the coverage information for the expansion.
+ emitDictKey("target_regions");
+ renderRegions(Expansion.Function.CountedRegions);
+
+ emitDictKey("filenames");
+ // Start List of Filenames to map the fileIDs.
+ emitArrayStart();
+ for (const auto &Filename : Expansion.Function.Filenames)
+ emitArrayElement(Filename);
+ // End List of Filenames.
+ emitArrayEnd();
+
+ // End Expansion.
+ emitDictEnd();
+}
- // End Summary for the file.
- emitDictEnd();
- }
+void CoverageExporterJson::renderRegions(
+ ArrayRef<coverage::CountedRegion> Regions) {
+ // Start List of Regions.
+ emitArrayStart();
-public:
- CoverageExporterJson(const CoverageMapping &CoverageMapping,
- const CoverageViewOptions &Options, raw_ostream &OS)
- : Options(Options), OS(OS), Coverage(CoverageMapping) {
- State.push(JsonState::None);
- }
+ for (const auto &Region : Regions)
+ renderRegion(Region);
- /// \brief Print the CoverageMapping.
- void print() { renderRoot(); }
-};
+ // End List of Regions.
+ emitArrayEnd();
+}
-/// \brief Export the given CoverageMapping to a JSON Format.
-void exportCoverageDataToJson(const CoverageMapping &CoverageMapping,
- const CoverageViewOptions &Options,
- raw_ostream &OS) {
- auto Exporter = CoverageExporterJson(CoverageMapping, Options, OS);
+void CoverageExporterJson::renderRegion(const coverage::CountedRegion &Region) {
+ // Start CountedRegion.
+ emitArrayStart();
+
+ emitArrayElement(Region.LineStart);
+ emitArrayElement(Region.ColumnStart);
+ emitArrayElement(Region.LineEnd);
+ emitArrayElement(Region.ColumnEnd);
+ emitArrayElement(Region.ExecutionCount);
+ emitArrayElement(Region.FileID);
+ emitArrayElement(Region.ExpandedFileID);
+ emitArrayElement(Region.Kind);
+
+ // End CountedRegion.
+ emitArrayEnd();
+}
- Exporter.print();
+void CoverageExporterJson::renderSummary(const FileCoverageSummary &Summary) {
+ // Start Summary for the file.
+ emitDictStart();
+
+ emitDictKey("lines");
+
+ // Start Line Coverage Summary.
+ emitDictStart();
+ emitDictElement("count", Summary.LineCoverage.getNumLines());
+ emitDictElement("covered", Summary.LineCoverage.getCovered());
+ emitDictElement("percent", Summary.LineCoverage.getPercentCovered());
+ // End Line Coverage Summary.
+ emitDictEnd();
+
+ emitDictKey("functions");
+
+ // Start Function Coverage Summary.
+ emitDictStart();
+ emitDictElement("count", Summary.FunctionCoverage.getNumFunctions());
+ emitDictElement("covered", Summary.FunctionCoverage.getExecuted());
+ emitDictElement("percent", Summary.FunctionCoverage.getPercentCovered());
+ // End Function Coverage Summary.
+ emitDictEnd();
+
+ emitDictKey("instantiations");
+
+ // Start Instantiation Coverage Summary.
+ emitDictStart();
+ emitDictElement("count", Summary.InstantiationCoverage.getNumFunctions());
+ emitDictElement("covered", Summary.InstantiationCoverage.getExecuted());
+ emitDictElement("percent", Summary.InstantiationCoverage.getPercentCovered());
+ // End Function Coverage Summary.
+ emitDictEnd();
+
+ emitDictKey("regions");
+
+ // Start Region Coverage Summary.
+ emitDictStart();
+ emitDictElement("count", Summary.RegionCoverage.getNumRegions());
+ emitDictElement("covered", Summary.RegionCoverage.getCovered());
+ emitDictElement("notcovered", Summary.RegionCoverage.getNumRegions() -
+ Summary.RegionCoverage.getCovered());
+ emitDictElement("percent", Summary.RegionCoverage.getPercentCovered());
+ // End Region Coverage Summary.
+ emitDictEnd();
+
+ // End Summary for the file.
+ emitDictEnd();
}
diff --git a/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h
new file mode 100644
index 000000000000..f88dffa0ebea
--- /dev/null
+++ b/contrib/llvm/tools/llvm-cov/CoverageExporterJson.h
@@ -0,0 +1,112 @@
+//===- CoverageExporterJson.h - Code coverage JSON exporter ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This class implements a code coverage exporter for JSON format.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_COV_COVERAGEEXPORTERJSON_H
+#define LLVM_COV_COVERAGEEXPORTERJSON_H
+
+#include "CoverageExporter.h"
+#include <stack>
+
+namespace llvm {
+
+class CoverageExporterJson : public CoverageExporter {
+ /// States that the JSON rendering machine can be in.
+ enum JsonState { None, NonEmptyElement, EmptyElement };
+
+ /// Tracks state of the JSON output.
+ std::stack<JsonState> State;
+
+ /// Emit a serialized scalar.
+ void emitSerialized(const int64_t Value);
+
+ /// Emit a serialized string.
+ void emitSerialized(const std::string &Value);
+
+ /// Emit a comma if there is a previous element to delimit.
+ void emitComma();
+
+ /// Emit a starting dictionary/object character.
+ void emitDictStart();
+
+ /// Emit a dictionary/object key but no value.
+ void emitDictKey(const std::string &Key);
+
+ /// Emit a dictionary/object key/value pair.
+ template <typename V>
+ void emitDictElement(const std::string &Key, const V &Value) {
+ emitComma();
+ emitSerialized(Key);
+ OS << ":";
+ emitSerialized(Value);
+ }
+
+ /// Emit a closing dictionary/object character.
+ void emitDictEnd();
+
+ /// Emit a starting array character.
+ void emitArrayStart();
+
+ /// Emit an array element.
+ template <typename V> void emitArrayElement(const V &Value) {
+ emitComma();
+ emitSerialized(Value);
+ }
+
+ /// emit a closing array character.
+ void emitArrayEnd();
+
+ /// Render an array of all the given functions.
+ void renderFunctions(
+ const iterator_range<coverage::FunctionRecordIterator> &Functions);
+
+ /// Render an array of all the source files, also pass back a Summary.
+ void renderFiles(ArrayRef<std::string> SourceFiles,
+ ArrayRef<FileCoverageSummary> FileReports);
+
+ /// Render a single file.
+ void renderFile(const std::string &Filename,
+ const FileCoverageSummary &FileReport);
+
+ /// Render summary for a single file.
+ void renderFileCoverage(const coverage::CoverageData &FileCoverage,
+ const FileCoverageSummary &FileReport);
+
+ /// Render a CoverageSegment.
+ void renderSegment(const coverage::CoverageSegment &Segment);
+
+ /// Render an ExpansionRecord.
+ void renderExpansion(const coverage::ExpansionRecord &Expansion);
+
+ /// Render a list of CountedRegions.
+ void renderRegions(ArrayRef<coverage::CountedRegion> Regions);
+
+ /// Render a single CountedRegion.
+ void renderRegion(const coverage::CountedRegion &Region);
+
+ /// Render a FileCoverageSummary.
+ void renderSummary(const FileCoverageSummary &Summary);
+
+public:
+ CoverageExporterJson(const coverage::CoverageMapping &CoverageMapping,
+ const CoverageViewOptions &Options, raw_ostream &OS);
+
+ /// Render the CoverageMapping object.
+ void renderRoot(const CoverageFilters &IgnoreFilenameFilters) override;
+
+ /// Render the CoverageMapping object for specified source files.
+ void renderRoot(const std::vector<std::string> &SourceFiles) override;
+};
+
+} // end namespace llvm
+
+#endif // LLVM_COV_COVERAGEEXPORTERJSON_H
diff --git a/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp b/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp
index 441179601dcc..4dd0f552c7e0 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp
+++ b/contrib/llvm/tools/llvm-cov/CoverageFilters.cpp
@@ -30,6 +30,10 @@ bool NameRegexCoverageFilter::matches(
return llvm::Regex(Regex).match(Function.Name);
}
+bool NameRegexCoverageFilter::matchesFilename(StringRef Filename) const {
+ return llvm::Regex(Regex).match(Filename);
+}
+
bool NameWhitelistCoverageFilter::matches(
const coverage::CoverageMapping &,
const coverage::FunctionRecord &Function) const {
@@ -63,6 +67,14 @@ bool CoverageFilters::matches(const coverage::CoverageMapping &CM,
return false;
}
+bool CoverageFilters::matchesFilename(StringRef Filename) const {
+ for (const auto &Filter : Filters) {
+ if (Filter->matchesFilename(Filename))
+ return true;
+ }
+ return false;
+}
+
bool CoverageFiltersMatchAll::matches(
const coverage::CoverageMapping &CM,
const coverage::FunctionRecord &Function) const {
diff --git a/contrib/llvm/tools/llvm-cov/CoverageFilters.h b/contrib/llvm/tools/llvm-cov/CoverageFilters.h
index aeaf61de1730..6424ca5a8081 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageFilters.h
+++ b/contrib/llvm/tools/llvm-cov/CoverageFilters.h
@@ -22,19 +22,24 @@
namespace llvm {
-/// \brief Matches specific functions that pass the requirement of this filter.
+/// Matches specific functions that pass the requirement of this filter.
class CoverageFilter {
public:
virtual ~CoverageFilter() {}
- /// \brief Return true if the function passes the requirements of this filter.
+ /// Return true if the function passes the requirements of this filter.
virtual bool matches(const coverage::CoverageMapping &CM,
const coverage::FunctionRecord &Function) const {
return true;
}
+
+ /// Return true if the filename passes the requirements of this filter.
+ virtual bool matchesFilename(StringRef Filename) const {
+ return true;
+ }
};
-/// \brief Matches functions that contain a specific string in their name.
+/// Matches functions that contain a specific string in their name.
class NameCoverageFilter : public CoverageFilter {
StringRef Name;
@@ -45,7 +50,7 @@ public:
const coverage::FunctionRecord &Function) const override;
};
-/// \brief Matches functions whose name matches a certain regular expression.
+/// Matches functions whose name matches a certain regular expression.
class NameRegexCoverageFilter : public CoverageFilter {
StringRef Regex;
@@ -54,9 +59,11 @@ public:
bool matches(const coverage::CoverageMapping &CM,
const coverage::FunctionRecord &Function) const override;
+
+ bool matchesFilename(StringRef Filename) const override;
};
-/// \brief Matches functions whose name appears in a SpecialCaseList in the
+/// Matches functions whose name appears in a SpecialCaseList in the
/// whitelist_fun section.
class NameWhitelistCoverageFilter : public CoverageFilter {
const SpecialCaseList &Whitelist;
@@ -69,7 +76,7 @@ public:
const coverage::FunctionRecord &Function) const override;
};
-/// \brief Matches numbers that pass a certain threshold.
+/// Matches numbers that pass a certain threshold.
template <typename T> class StatisticThresholdFilter {
public:
enum Operation { LessThan, GreaterThan };
@@ -81,7 +88,7 @@ protected:
StatisticThresholdFilter(Operation Op, T Threshold)
: Op(Op), Threshold(Threshold) {}
- /// \brief Return true if the given number is less than
+ /// Return true if the given number is less than
/// or greater than the certain threshold.
bool PassesThreshold(T Value) const {
switch (Op) {
@@ -94,7 +101,7 @@ protected:
}
};
-/// \brief Matches functions whose region coverage percentage
+/// Matches functions whose region coverage percentage
/// is above/below a certain percentage.
class RegionCoverageFilter : public CoverageFilter,
public StatisticThresholdFilter<double> {
@@ -106,7 +113,7 @@ public:
const coverage::FunctionRecord &Function) const override;
};
-/// \brief Matches functions whose line coverage percentage
+/// Matches functions whose line coverage percentage
/// is above/below a certain percentage.
class LineCoverageFilter : public CoverageFilter,
public StatisticThresholdFilter<double> {
@@ -118,7 +125,7 @@ public:
const coverage::FunctionRecord &Function) const override;
};
-/// \brief A collection of filters.
+/// A collection of filters.
/// Matches functions that match any filters contained
/// in an instance of this class.
class CoverageFilters : public CoverageFilter {
@@ -126,16 +133,18 @@ protected:
std::vector<std::unique_ptr<CoverageFilter>> Filters;
public:
- /// \brief Append a filter to this collection.
+ /// Append a filter to this collection.
void push_back(std::unique_ptr<CoverageFilter> Filter);
bool empty() const { return Filters.empty(); }
bool matches(const coverage::CoverageMapping &CM,
const coverage::FunctionRecord &Function) const override;
+
+ bool matchesFilename(StringRef Filename) const override;
};
-/// \brief A collection of filters.
+/// A collection of filters.
/// Matches functions that match all of the filters contained
/// in an instance of this class.
class CoverageFiltersMatchAll : public CoverageFilters {
diff --git a/contrib/llvm/tools/llvm-cov/CoverageReport.cpp b/contrib/llvm/tools/llvm-cov/CoverageReport.cpp
index 9c553a7f64c7..607a3ceb30cb 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageReport.cpp
+++ b/contrib/llvm/tools/llvm-cov/CoverageReport.cpp
@@ -16,13 +16,15 @@
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/Path.h"
+#include "llvm/Support/ThreadPool.h"
+#include "llvm/Support/Threading.h"
#include <numeric>
using namespace llvm;
namespace {
-/// \brief Helper struct which prints trimmed and aligned columns.
+/// Helper struct which prints trimmed and aligned columns.
struct Column {
enum TrimKind { NoTrim, WidthTrim, RightTrim };
@@ -89,7 +91,7 @@ size_t FileReportColumns[] = {25, 12, 18, 10, 12, 18, 10,
16, 16, 10, 12, 18, 10};
size_t FunctionReportColumns[] = {25, 10, 8, 8, 10, 8, 8};
-/// \brief Adjust column widths to fit long file paths and function names.
+/// Adjust column widths to fit long file paths and function names.
void adjustColumnWidths(ArrayRef<StringRef> Files,
ArrayRef<StringRef> Functions) {
for (StringRef Filename : Files)
@@ -99,7 +101,7 @@ void adjustColumnWidths(ArrayRef<StringRef> Files,
std::max(FunctionReportColumns[0], Funcname.size());
}
-/// \brief Prints a horizontal divider long enough to cover the given column
+/// Prints a horizontal divider long enough to cover the given column
/// widths.
void renderDivider(ArrayRef<size_t> ColumnWidths, raw_ostream &OS) {
size_t Length = std::accumulate(ColumnWidths.begin(), ColumnWidths.end(), 0);
@@ -107,7 +109,7 @@ void renderDivider(ArrayRef<size_t> ColumnWidths, raw_ostream &OS) {
OS << '-';
}
-/// \brief Return the color which correponds to the coverage percentage of a
+/// Return the color which correponds to the coverage percentage of a
/// certain metric.
template <typename T>
raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
@@ -117,7 +119,7 @@ raw_ostream::Colors determineCoveragePercentageColor(const T &Info) {
: raw_ostream::RED;
}
-/// \brief Get the number of redundant path components in each path in \p Paths.
+/// Get the number of redundant path components in each path in \p Paths.
unsigned getNumRedundantPathComponents(ArrayRef<std::string> Paths) {
// To start, set the number of redundant path components to the maximum
// possible value.
@@ -146,7 +148,7 @@ unsigned getNumRedundantPathComponents(ArrayRef<std::string> Paths) {
return NumRedundant;
}
-/// \brief Determine the length of the longest redundant prefix of the paths in
+/// Determine the length of the longest redundant prefix of the paths in
/// \p Paths.
unsigned getRedundantPrefixLen(ArrayRef<std::string> Paths) {
// If there's at most one path, no path components are redundant.
@@ -319,50 +321,72 @@ void CoverageReport::renderFunctionReports(ArrayRef<std::string> Files,
}
}
+void CoverageReport::prepareSingleFileReport(const StringRef Filename,
+ const coverage::CoverageMapping *Coverage,
+ const CoverageViewOptions &Options, const unsigned LCP,
+ FileCoverageSummary *FileReport, const CoverageFilter *Filters) {
+ for (const auto &Group : Coverage->getInstantiationGroups(Filename)) {
+ std::vector<FunctionCoverageSummary> InstantiationSummaries;
+ for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
+ if (!Filters->matches(*Coverage, *F))
+ continue;
+ auto InstantiationSummary = FunctionCoverageSummary::get(*Coverage, *F);
+ FileReport->addInstantiation(InstantiationSummary);
+ InstantiationSummaries.push_back(InstantiationSummary);
+ }
+ if (InstantiationSummaries.empty())
+ continue;
+
+ auto GroupSummary =
+ FunctionCoverageSummary::get(Group, InstantiationSummaries);
+
+ if (Options.Debug)
+ outs() << "InstantiationGroup: " << GroupSummary.Name << " with "
+ << "size = " << Group.size() << "\n";
+
+ FileReport->addFunction(GroupSummary);
+ }
+}
+
std::vector<FileCoverageSummary> CoverageReport::prepareFileReports(
const coverage::CoverageMapping &Coverage, FileCoverageSummary &Totals,
ArrayRef<std::string> Files, const CoverageViewOptions &Options,
const CoverageFilter &Filters) {
- std::vector<FileCoverageSummary> FileReports;
unsigned LCP = getRedundantPrefixLen(Files);
+ auto NumThreads = Options.NumThreads;
- for (StringRef Filename : Files) {
- FileCoverageSummary Summary(Filename.drop_front(LCP));
-
- for (const auto &Group : Coverage.getInstantiationGroups(Filename)) {
- std::vector<FunctionCoverageSummary> InstantiationSummaries;
- for (const coverage::FunctionRecord *F : Group.getInstantiations()) {
- if (!Filters.matches(Coverage, *F))
- continue;
- auto InstantiationSummary = FunctionCoverageSummary::get(Coverage, *F);
- Summary.addInstantiation(InstantiationSummary);
- Totals.addInstantiation(InstantiationSummary);
- InstantiationSummaries.push_back(InstantiationSummary);
- }
- if (InstantiationSummaries.empty())
- continue;
-
- auto GroupSummary =
- FunctionCoverageSummary::get(Group, InstantiationSummaries);
+ // If NumThreads is not specified, auto-detect a good default.
+ if (NumThreads == 0)
+ NumThreads =
+ std::max(1U, std::min(llvm::heavyweight_hardware_concurrency(),
+ unsigned(Files.size())));
- if (Options.Debug)
- outs() << "InstantiationGroup: " << GroupSummary.Name << " with "
- << "size = " << Group.size() << "\n";
+ ThreadPool Pool(NumThreads);
- Summary.addFunction(GroupSummary);
- Totals.addFunction(GroupSummary);
- }
+ std::vector<FileCoverageSummary> FileReports;
+ FileReports.reserve(Files.size());
- FileReports.push_back(Summary);
+ for (StringRef Filename : Files) {
+ FileReports.emplace_back(Filename.drop_front(LCP));
+ Pool.async(&CoverageReport::prepareSingleFileReport, Filename,
+ &Coverage, Options, LCP, &FileReports.back(), &Filters);
}
+ Pool.wait();
+
+ for (const auto &FileReport : FileReports)
+ Totals += FileReport;
return FileReports;
}
-void CoverageReport::renderFileReports(raw_ostream &OS) const {
+void CoverageReport::renderFileReports(
+ raw_ostream &OS, const CoverageFilters &IgnoreFilenameFilters) const {
std::vector<std::string> UniqueSourceFiles;
- for (StringRef SF : Coverage.getUniqueSourceFiles())
- UniqueSourceFiles.emplace_back(SF.str());
+ for (StringRef SF : Coverage.getUniqueSourceFiles()) {
+ // Apply ignore source files filters.
+ if (!IgnoreFilenameFilters.matchesFilename(SF))
+ UniqueSourceFiles.emplace_back(SF.str());
+ }
renderFileReports(OS, UniqueSourceFiles);
}
diff --git a/contrib/llvm/tools/llvm-cov/CoverageReport.h b/contrib/llvm/tools/llvm-cov/CoverageReport.h
index 1c9e68e832f3..4a6527e9fe5d 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageReport.h
+++ b/contrib/llvm/tools/llvm-cov/CoverageReport.h
@@ -1,4 +1,4 @@
-//===- CoverageReport.h - Code coverage report ---------------------------===//
+//===- CoverageReport.h - Code coverage report ----------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -20,7 +20,7 @@
namespace llvm {
-/// \brief Displays the code coverage report.
+/// Displays the code coverage report.
class CoverageReport {
const CoverageViewOptions &Options;
const coverage::CoverageMapping &Coverage;
@@ -44,8 +44,17 @@ public:
const CoverageViewOptions &Options,
const CoverageFilter &Filters = CoverageFiltersMatchAll());
+ static void
+ prepareSingleFileReport(const StringRef Filename,
+ const coverage::CoverageMapping *Coverage,
+ const CoverageViewOptions &Options,
+ const unsigned LCP,
+ FileCoverageSummary *FileReport,
+ const CoverageFilter *Filters);
+
/// Render file reports for every unique file in the coverage mapping.
- void renderFileReports(raw_ostream &OS) const;
+ void renderFileReports(raw_ostream &OS,
+ const CoverageFilters &IgnoreFilenameFilters) const;
/// Render file reports for the files specified in \p Files.
void renderFileReports(raw_ostream &OS, ArrayRef<std::string> Files) const;
diff --git a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h
index 8eae0b7fec97..0845e2ce2e77 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h
+++ b/contrib/llvm/tools/llvm-cov/CoverageSummaryInfo.h
@@ -20,12 +20,12 @@
namespace llvm {
-/// \brief Provides information about region coverage for a function/file.
+/// Provides information about region coverage for a function/file.
class RegionCoverageInfo {
- /// \brief The number of regions that were executed at least once.
+ /// The number of regions that were executed at least once.
size_t Covered;
- /// \brief The total number of regions in a function/file.
+ /// The total number of regions in a function/file.
size_t NumRegions;
public:
@@ -61,12 +61,12 @@ public:
}
};
-/// \brief Provides information about line coverage for a function/file.
+/// Provides information about line coverage for a function/file.
class LineCoverageInfo {
- /// \brief The number of lines that were executed at least once.
+ /// The number of lines that were executed at least once.
size_t Covered;
- /// \brief The total number of lines in a function/file.
+ /// The total number of lines in a function/file.
size_t NumLines;
public:
@@ -102,12 +102,12 @@ public:
}
};
-/// \brief Provides information about function coverage for a file.
+/// Provides information about function coverage for a file.
class FunctionCoverageInfo {
- /// \brief The number of functions that were executed.
+ /// The number of functions that were executed.
size_t Executed;
- /// \brief The total number of functions in this file.
+ /// The total number of functions in this file.
size_t NumFunctions;
public:
@@ -116,6 +116,12 @@ public:
FunctionCoverageInfo(size_t Executed, size_t NumFunctions)
: Executed(Executed), NumFunctions(NumFunctions) {}
+ FunctionCoverageInfo &operator+=(const FunctionCoverageInfo &RHS) {
+ Executed += RHS.Executed;
+ NumFunctions += RHS.NumFunctions;
+ return *this;
+ }
+
void addFunction(bool Covered) {
if (Covered)
++Executed;
@@ -136,7 +142,7 @@ public:
}
};
-/// \brief A summary of function's code coverage.
+/// A summary of function's code coverage.
struct FunctionCoverageSummary {
std::string Name;
uint64_t ExecutionCount;
@@ -152,7 +158,7 @@ struct FunctionCoverageSummary {
: Name(Name), ExecutionCount(ExecutionCount),
RegionCoverage(RegionCoverage), LineCoverage(LineCoverage) {}
- /// \brief Compute the code coverage summary for the given function coverage
+ /// Compute the code coverage summary for the given function coverage
/// mapping record.
static FunctionCoverageSummary get(const coverage::CoverageMapping &CM,
const coverage::FunctionRecord &Function);
@@ -164,7 +170,7 @@ struct FunctionCoverageSummary {
ArrayRef<FunctionCoverageSummary> Summaries);
};
-/// \brief A summary of file's code coverage.
+/// A summary of file's code coverage.
struct FileCoverageSummary {
StringRef Name;
RegionCoverageInfo RegionCoverage;
@@ -176,6 +182,14 @@ struct FileCoverageSummary {
: Name(Name), RegionCoverage(), LineCoverage(), FunctionCoverage(),
InstantiationCoverage() {}
+ FileCoverageSummary &operator+=(const FileCoverageSummary &RHS) {
+ RegionCoverage += RHS.RegionCoverage;
+ LineCoverage += RHS.LineCoverage;
+ FunctionCoverage += RHS.FunctionCoverage;
+ InstantiationCoverage += RHS.InstantiationCoverage;
+ return *this;
+ }
+
void addFunction(const FunctionCoverageSummary &Function) {
RegionCoverage += Function.RegionCoverage;
LineCoverage += Function.LineCoverage;
@@ -187,11 +201,11 @@ struct FileCoverageSummary {
}
};
-/// \brief A cache for demangled symbols.
+/// A cache for demangled symbols.
struct DemangleCache {
StringMap<std::string> DemangledNames;
- /// \brief Demangle \p Sym if possible. Otherwise, just return \p Sym.
+ /// Demangle \p Sym if possible. Otherwise, just return \p Sym.
StringRef demangle(StringRef Sym) const {
const auto DemangledName = DemangledNames.find(Sym);
if (DemangledName == DemangledNames.end())
diff --git a/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h b/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h
index 17614c4e9ba2..20085a957bb5 100644
--- a/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h
+++ b/contrib/llvm/tools/llvm-cov/CoverageViewOptions.h
@@ -10,12 +10,13 @@
#ifndef LLVM_COV_COVERAGEVIEWOPTIONS_H
#define LLVM_COV_COVERAGEVIEWOPTIONS_H
+#include "llvm/Config/llvm-config.h"
#include "RenderingSupport.h"
#include <vector>
namespace llvm {
-/// \brief The options for displaying the code coverage information.
+/// The options for displaying the code coverage information.
struct CoverageViewOptions {
enum class OutputFormat {
Text,
@@ -39,26 +40,27 @@ struct CoverageViewOptions {
uint32_t TabSize;
std::string ProjectTitle;
std::string CreatedTimeStr;
+ unsigned NumThreads;
- /// \brief Change the output's stream color if the colors are enabled.
+ /// Change the output's stream color if the colors are enabled.
ColoredRawOstream colored_ostream(raw_ostream &OS,
raw_ostream::Colors Color) const {
return llvm::colored_ostream(OS, Color, Colors);
}
- /// \brief Check if an output directory has been specified.
+ /// Check if an output directory has been specified.
bool hasOutputDirectory() const { return !ShowOutputDirectory.empty(); }
- /// \brief Check if a demangler has been specified.
+ /// Check if a demangler has been specified.
bool hasDemangler() const { return !DemanglerOpts.empty(); }
- /// \brief Check if a project title has been specified.
+ /// Check if a project title has been specified.
bool hasProjectTitle() const { return !ProjectTitle.empty(); }
- /// \brief Check if the created time of the profile data file is available.
+ /// Check if the created time of the profile data file is available.
bool hasCreatedTime() const { return !CreatedTimeStr.empty(); }
- /// \brief Get the LLVM version string.
+ /// Get the LLVM version string.
std::string getLLVMVersionString() const {
std::string VersionString = "Generated by llvm-cov -- llvm version ";
VersionString += LLVM_VERSION_STRING;
diff --git a/contrib/llvm/tools/llvm-cov/RenderingSupport.h b/contrib/llvm/tools/llvm-cov/RenderingSupport.h
index aa70fbc23e3c..2cfe24919142 100644
--- a/contrib/llvm/tools/llvm-cov/RenderingSupport.h
+++ b/contrib/llvm/tools/llvm-cov/RenderingSupport.h
@@ -15,7 +15,7 @@
namespace llvm {
-/// \brief A helper class that resets the output stream's color if needed
+/// A helper class that resets the output stream's color if needed
/// when destroyed.
class ColoredRawOstream {
ColoredRawOstream(const ColoredRawOstream &OS) = delete;
@@ -45,7 +45,7 @@ inline raw_ostream &operator<<(const ColoredRawOstream &OS, T &&Value) {
return OS.OS << std::forward<T>(Value);
}
-/// \brief Change the color of the output stream if the `IsColorUsed` flag
+/// Change the color of the output stream if the `IsColorUsed` flag
/// is true. Returns an object that resets the color when destroyed.
inline ColoredRawOstream colored_ostream(raw_ostream &OS,
raw_ostream::Colors Color,
diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp
index 8c39dab580de..a5a8fa9a4814 100644
--- a/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp
+++ b/contrib/llvm/tools/llvm-cov/SourceCoverageView.cpp
@@ -65,7 +65,8 @@ CoveragePrinter::createOutputStream(StringRef Path, StringRef Extension,
return errorCodeToError(E);
std::error_code E;
- raw_ostream *RawStream = new raw_fd_ostream(FullPath, E, sys::fs::F_RW);
+ raw_ostream *RawStream =
+ new raw_fd_ostream(FullPath, E, sys::fs::FA_Read | sys::fs::FA_Write);
auto OS = CoveragePrinter::OwnedStream(RawStream);
if (E)
return errorCodeToError(E);
diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageView.h b/contrib/llvm/tools/llvm-cov/SourceCoverageView.h
index 7f58ea5d7be8..e3a2f9e5c0b4 100644
--- a/contrib/llvm/tools/llvm-cov/SourceCoverageView.h
+++ b/contrib/llvm/tools/llvm-cov/SourceCoverageView.h
@@ -27,7 +27,7 @@ using namespace coverage;
class CoverageFiltersMatchAll;
class SourceCoverageView;
-/// \brief A view that represents a macro or include expansion.
+/// A view that represents a macro or include expansion.
struct ExpansionView {
CounterMappingRegion Region;
std::unique_ptr<SourceCoverageView> View;
@@ -52,7 +52,7 @@ struct ExpansionView {
}
};
-/// \brief A view that represents a function instantiation.
+/// A view that represents a function instantiation.
struct InstantiationView {
StringRef FunctionName;
unsigned Line;
@@ -68,7 +68,7 @@ struct InstantiationView {
}
};
-/// \brief A file manager that handles format-aware file creation.
+/// A file manager that handles format-aware file creation.
class CoveragePrinter {
public:
struct StreamDestructor {
@@ -82,18 +82,18 @@ protected:
CoveragePrinter(const CoverageViewOptions &Opts) : Opts(Opts) {}
- /// \brief Return `OutputDir/ToplevelDir/Path.Extension`. If \p InToplevel is
+ /// Return `OutputDir/ToplevelDir/Path.Extension`. If \p InToplevel is
/// false, skip the ToplevelDir component. If \p Relative is false, skip the
/// OutputDir component.
std::string getOutputPath(StringRef Path, StringRef Extension,
bool InToplevel, bool Relative = true) const;
- /// \brief If directory output is enabled, create a file in that directory
+ /// If directory output is enabled, create a file in that directory
/// at the path given by getOutputPath(). Otherwise, return stdout.
Expected<OwnedStream> createOutputStream(StringRef Path, StringRef Extension,
bool InToplevel) const;
- /// \brief Return the sub-directory name for file coverage reports.
+ /// Return the sub-directory name for file coverage reports.
static StringRef getCoverageDir() { return "coverage"; }
public:
@@ -105,14 +105,14 @@ public:
/// @name File Creation Interface
/// @{
- /// \brief Create a file to print a coverage view into.
+ /// Create a file to print a coverage view into.
virtual Expected<OwnedStream> createViewFile(StringRef Path,
bool InToplevel) = 0;
- /// \brief Close a file which has been used to print a coverage view.
+ /// Close a file which has been used to print a coverage view.
virtual void closeViewFile(OwnedStream OS) = 0;
- /// \brief Create an index which lists reports for the given source files.
+ /// Create an index which lists reports for the given source files.
virtual Error createIndexFile(ArrayRef<std::string> SourceFiles,
const CoverageMapping &Coverage,
const CoverageFiltersMatchAll &Filters) = 0;
@@ -120,7 +120,7 @@ public:
/// @}
};
-/// \brief A code coverage view of a source file or function.
+/// A code coverage view of a source file or function.
///
/// A source coverage view and its nested sub-views form a file-oriented
/// representation of code coverage data. This view can be printed out by a
@@ -161,73 +161,73 @@ protected:
/// @name Rendering Interface
/// @{
- /// \brief Render a header for the view.
+ /// Render a header for the view.
virtual void renderViewHeader(raw_ostream &OS) = 0;
- /// \brief Render a footer for the view.
+ /// Render a footer for the view.
virtual void renderViewFooter(raw_ostream &OS) = 0;
- /// \brief Render the source name for the view.
+ /// Render the source name for the view.
virtual void renderSourceName(raw_ostream &OS, bool WholeFile) = 0;
- /// \brief Render the line prefix at the given \p ViewDepth.
+ /// Render the line prefix at the given \p ViewDepth.
virtual void renderLinePrefix(raw_ostream &OS, unsigned ViewDepth) = 0;
- /// \brief Render the line suffix at the given \p ViewDepth.
+ /// Render the line suffix at the given \p ViewDepth.
virtual void renderLineSuffix(raw_ostream &OS, unsigned ViewDepth) = 0;
- /// \brief Render a view divider at the given \p ViewDepth.
+ /// Render a view divider at the given \p ViewDepth.
virtual void renderViewDivider(raw_ostream &OS, unsigned ViewDepth) = 0;
- /// \brief Render a source line with highlighting.
+ /// Render a source line with highlighting.
virtual void renderLine(raw_ostream &OS, LineRef L,
const LineCoverageStats &LCS, unsigned ExpansionCol,
unsigned ViewDepth) = 0;
- /// \brief Render the line's execution count column.
+ /// Render the line's execution count column.
virtual void renderLineCoverageColumn(raw_ostream &OS,
const LineCoverageStats &Line) = 0;
- /// \brief Render the line number column.
+ /// Render the line number column.
virtual void renderLineNumberColumn(raw_ostream &OS, unsigned LineNo) = 0;
- /// \brief Render all the region's execution counts on a line.
+ /// Render all the region's execution counts on a line.
virtual void renderRegionMarkers(raw_ostream &OS,
const LineCoverageStats &Line,
unsigned ViewDepth) = 0;
- /// \brief Render the site of an expansion.
+ /// Render the site of an expansion.
virtual void renderExpansionSite(raw_ostream &OS, LineRef L,
const LineCoverageStats &LCS,
unsigned ExpansionCol,
unsigned ViewDepth) = 0;
- /// \brief Render an expansion view and any nested views.
+ /// Render an expansion view and any nested views.
virtual void renderExpansionView(raw_ostream &OS, ExpansionView &ESV,
unsigned ViewDepth) = 0;
- /// \brief Render an instantiation view and any nested views.
+ /// Render an instantiation view and any nested views.
virtual void renderInstantiationView(raw_ostream &OS, InstantiationView &ISV,
unsigned ViewDepth) = 0;
- /// \brief Render \p Title, a project title if one is available, and the
+ /// Render \p Title, a project title if one is available, and the
/// created time.
virtual void renderTitle(raw_ostream &OS, StringRef CellText) = 0;
- /// \brief Render the table header for a given source file.
+ /// Render the table header for a given source file.
virtual void renderTableHeader(raw_ostream &OS, unsigned FirstUncoveredLineNo,
unsigned IndentLevel) = 0;
/// @}
- /// \brief Format a count using engineering notation with 3 significant
+ /// Format a count using engineering notation with 3 significant
/// digits.
static std::string formatCount(uint64_t N);
- /// \brief Check if region marker output is expected for a line.
+ /// Check if region marker output is expected for a line.
bool shouldRenderRegionMarkers(const LineCoverageStats &LCS) const;
- /// \brief Check if there are any sub-views attached to this view.
+ /// Check if there are any sub-views attached to this view.
bool hasSubViews() const;
SourceCoverageView(StringRef SourceName, const MemoryBuffer &File,
@@ -243,20 +243,20 @@ public:
virtual ~SourceCoverageView() {}
- /// \brief Return the source name formatted for the host OS.
+ /// Return the source name formatted for the host OS.
std::string getSourceName() const;
const CoverageViewOptions &getOptions() const { return Options; }
- /// \brief Add an expansion subview to this view.
+ /// Add an expansion subview to this view.
void addExpansion(const CounterMappingRegion &Region,
std::unique_ptr<SourceCoverageView> View);
- /// \brief Add a function instantiation subview to this view.
+ /// Add a function instantiation subview to this view.
void addInstantiation(StringRef FunctionName, unsigned Line,
std::unique_ptr<SourceCoverageView> View);
- /// \brief Print the code coverage information for a specific portion of a
+ /// Print the code coverage information for a specific portion of a
/// source file to the output stream.
void print(raw_ostream &OS, bool WholeFile, bool ShowSourceName,
bool ShowTitle, unsigned ViewDepth = 0);
diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
index e45c6f4cb473..acb67aa5cfc7 100644
--- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
+++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.cpp
@@ -25,31 +25,29 @@ namespace {
// Return a string with the special characters in \p Str escaped.
std::string escape(StringRef Str, const CoverageViewOptions &Opts) {
- std::string Result;
+ std::string TabExpandedResult;
unsigned ColNum = 0; // Record the column number.
for (char C : Str) {
- ++ColNum;
- if (C == '&')
- Result += "&amp;";
- else if (C == '<')
- Result += "&lt;";
- else if (C == '>')
- Result += "&gt;";
- else if (C == '\"')
- Result += "&quot;";
- else if (C == '\n' || C == '\r') {
- Result += C;
- ColNum = 0;
- } else if (C == '\t') {
- // Replace '\t' with TabSize spaces.
- unsigned NumSpaces = Opts.TabSize - (--ColNum % Opts.TabSize);
+ if (C == '\t') {
+ // Replace '\t' with up to TabSize spaces.
+ unsigned NumSpaces = Opts.TabSize - (ColNum % Opts.TabSize);
for (unsigned I = 0; I < NumSpaces; ++I)
- Result += "&nbsp;";
+ TabExpandedResult += ' ';
ColNum += NumSpaces;
- } else
- Result += C;
+ } else {
+ TabExpandedResult += C;
+ if (C == '\n' || C == '\r')
+ ColNum = 0;
+ else
+ ++ColNum;
+ }
+ }
+ std::string EscapedHTML;
+ {
+ raw_string_ostream OS{EscapedHTML};
+ printHTMLEscaped(TabExpandedResult, OS);
}
- return Result;
+ return EscapedHTML;
}
// Create a \p Name tag around \p Str, and optionally set its \p ClassName.
@@ -116,24 +114,39 @@ table {
background: #ffffff;
border: 1px solid #dbdbdb;
}
+.light-row-bold {
+ background: #ffffff;
+ border: 1px solid #dbdbdb;
+ font-weight: bold;
+}
.column-entry {
- text-align: right;
+ text-align: left;
}
-.column-entry-left {
+.column-entry-bold {
+ font-weight: bold;
text-align: left;
}
.column-entry-yellow {
- text-align: right;
+ text-align: left;
background-color: #ffffd0;
}
+.column-entry-yellow:hover {
+ background-color: #fffff0;
+}
.column-entry-red {
- text-align: right;
+ text-align: left;
background-color: #ffd0d0;
}
+.column-entry-red:hover {
+ background-color: #fff0f0;
+}
.column-entry-green {
- text-align: right;
+ text-align: left;
background-color: #d0ffd0;
}
+.column-entry-green:hover {
+ background-color: #f0fff0;
+}
.line-number {
text-align: right;
color: #aaa;
@@ -184,10 +197,14 @@ table {
}
th, td {
vertical-align: top;
- padding: 2px 5px;
+ padding: 2px 8px;
border-collapse: collapse;
border-right: solid 1px #eee;
border-left: solid 1px #eee;
+ text-align: left;
+}
+td pre {
+ display: inline-block;
}
td:first-child {
border-left: none;
@@ -195,6 +212,9 @@ td:first-child {
td:last-child {
border-right: none;
}
+tr:hover {
+ background-color: #f0f0f0;
+}
)";
const char *EndHeader = "</head>";
@@ -287,13 +307,14 @@ void CoveragePrinterHTML::closeViewFile(OwnedStream OS) {
static void emitColumnLabelsForIndex(raw_ostream &OS,
const CoverageViewOptions &Opts) {
SmallVector<std::string, 4> Columns;
- Columns.emplace_back(tag("td", "Filename", "column-entry-left"));
- Columns.emplace_back(tag("td", "Function Coverage", "column-entry"));
+ Columns.emplace_back(tag("td", "Filename", "column-entry-bold"));
+ Columns.emplace_back(tag("td", "Function Coverage", "column-entry-bold"));
if (Opts.ShowInstantiationSummary)
- Columns.emplace_back(tag("td", "Instantiation Coverage", "column-entry"));
- Columns.emplace_back(tag("td", "Line Coverage", "column-entry"));
+ Columns.emplace_back(
+ tag("td", "Instantiation Coverage", "column-entry-bold"));
+ Columns.emplace_back(tag("td", "Line Coverage", "column-entry-bold"));
if (Opts.ShowRegionSummary)
- Columns.emplace_back(tag("td", "Region Coverage", "column-entry"));
+ Columns.emplace_back(tag("td", "Region Coverage", "column-entry-bold"));
OS << tag("tr", join(Columns.begin(), Columns.end(), ""));
}
@@ -339,7 +360,7 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
// Simplify the display file path, and wrap it in a link if requested.
std::string Filename;
if (IsTotals) {
- Filename = "TOTALS";
+ Filename = SF;
} else {
Filename = buildLinkToFile(SF, FCS);
}
@@ -360,7 +381,10 @@ void CoveragePrinterHTML::emitFileSummary(raw_ostream &OS, StringRef SF,
FCS.RegionCoverage.getNumRegions(),
FCS.RegionCoverage.getPercentCovered());
- OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
+ if (IsTotals)
+ OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row-bold");
+ else
+ OS << tag("tr", join(Columns.begin(), Columns.end(), ""), "light-row");
}
Error CoveragePrinterHTML::createIndexFile(
diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h
index 91b4ad4e220c..cb41fcaf37b9 100644
--- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h
+++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewHTML.h
@@ -22,7 +22,7 @@ using namespace coverage;
struct FileCoverageSummary;
-/// \brief A coverage printer for html output.
+/// A coverage printer for html output.
class CoveragePrinterHTML : public CoveragePrinter {
public:
Expected<OwnedStream> createViewFile(StringRef Path,
@@ -45,7 +45,7 @@ private:
const FileCoverageSummary &FCS) const;
};
-/// \brief A code coverage view which supports html-based rendering.
+/// A code coverage view which supports html-based rendering.
class SourceCoverageViewHTML : public SourceCoverageView {
void renderViewHeader(raw_ostream &OS) override;
diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
index 2480ee9f416a..aac70baed613 100644
--- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
+++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.cpp
@@ -51,13 +51,13 @@ namespace {
static const unsigned LineCoverageColumnWidth = 7;
static const unsigned LineNumberColumnWidth = 5;
-/// \brief Get the width of the leading columns.
+/// Get the width of the leading columns.
unsigned getCombinedColumnWidth(const CoverageViewOptions &Opts) {
return (Opts.ShowLineStats ? LineCoverageColumnWidth + 1 : 0) +
(Opts.ShowLineNumbers ? LineNumberColumnWidth + 1 : 0);
}
-/// \brief The width of the line that is used to divide between the view and
+/// The width of the line that is used to divide between the view and
/// the subviews.
unsigned getDividerWidth(const CoverageViewOptions &Opts) {
return getCombinedColumnWidth(Opts) + 4;
diff --git a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h
index cabf91975df3..a46f35cc6495 100644
--- a/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h
+++ b/contrib/llvm/tools/llvm-cov/SourceCoverageViewText.h
@@ -20,7 +20,7 @@ namespace llvm {
using namespace coverage;
-/// \brief A coverage printer for text output.
+/// A coverage printer for text output.
class CoveragePrinterText : public CoveragePrinter {
public:
Expected<OwnedStream> createViewFile(StringRef Path,
@@ -36,7 +36,7 @@ public:
: CoveragePrinter(Opts) {}
};
-/// \brief A code coverage view which supports text-based rendering.
+/// A code coverage view which supports text-based rendering.
class SourceCoverageViewText : public SourceCoverageView {
void renderViewHeader(raw_ostream &OS) override;
diff --git a/contrib/llvm/tools/llvm-cov/TestingSupport.cpp b/contrib/llvm/tools/llvm-cov/TestingSupport.cpp
index 4713d75f17dd..e07abdbd17f1 100644
--- a/contrib/llvm/tools/llvm-cov/TestingSupport.cpp
+++ b/contrib/llvm/tools/llvm-cov/TestingSupport.cpp
@@ -75,8 +75,7 @@ int convertForTestingMain(int argc, const char *argv[]) {
return 1;
int FD;
- if (auto Err =
- sys::fs::openFileForWrite(OutputFilename, FD, sys::fs::F_None)) {
+ if (auto Err = sys::fs::openFileForWrite(OutputFilename, FD)) {
errs() << "error: " << Err.message() << "\n";
return 1;
}
diff --git a/contrib/llvm/tools/llvm-cov/llvm-cov.cpp b/contrib/llvm/tools/llvm-cov/llvm-cov.cpp
index 158415870250..4c3b574451c3 100644
--- a/contrib/llvm/tools/llvm-cov/llvm-cov.cpp
+++ b/contrib/llvm/tools/llvm-cov/llvm-cov.cpp
@@ -14,32 +14,31 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Process.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <string>
using namespace llvm;
-/// \brief The main entry point for the 'show' subcommand.
+/// The main entry point for the 'show' subcommand.
int showMain(int argc, const char *argv[]);
-/// \brief The main entry point for the 'report' subcommand.
+/// The main entry point for the 'report' subcommand.
int reportMain(int argc, const char *argv[]);
-/// \brief The main entry point for the 'export' subcommand.
+/// The main entry point for the 'export' subcommand.
int exportMain(int argc, const char *argv[]);
-/// \brief The main entry point for the 'convert-for-testing' subcommand.
+/// The main entry point for the 'convert-for-testing' subcommand.
int convertForTestingMain(int argc, const char *argv[]);
-/// \brief The main entry point for the gcov compatible coverage tool.
+/// The main entry point for the gcov compatible coverage tool.
int gcovMain(int argc, const char *argv[]);
-/// \brief Top level help.
+/// Top level help.
static int helpMain(int argc, const char *argv[]) {
errs() << "Usage: llvm-cov {export|gcov|report|show} [OPTION]...\n\n"
<< "Shows code coverage information.\n\n"
@@ -52,17 +51,14 @@ static int helpMain(int argc, const char *argv[]) {
return 0;
}
-/// \brief Top level version information.
+/// Top level version information.
static int versionMain(int argc, const char *argv[]) {
cl::PrintVersionMessage();
return 0;
}
int main(int argc, const char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
// If argv[0] is or ends with 'gcov', always be gcov compatible
if (sys::path::stem(argv[0]).endswith_lower("gcov"))
diff --git a/contrib/llvm/tools/llvm-cxxdump/Error.cpp b/contrib/llvm/tools/llvm-cxxdump/Error.cpp
index d59547e3a2ce..54207fad32af 100644
--- a/contrib/llvm/tools/llvm-cxxdump/Error.cpp
+++ b/contrib/llvm/tools/llvm-cxxdump/Error.cpp
@@ -1,4 +1,4 @@
-//===- Error.cxx - system_error extensions for llvm-cxxdump -----*- C++ -*-===//
+//===- Error.cpp - system_error extensions for llvm-cxxdump -----*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp b/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
index 9b687e4fbe22..09e40d9b0db7 100644
--- a/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
+++ b/contrib/llvm/tools/llvm-cxxdump/llvm-cxxdump.cpp
@@ -20,9 +20,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/Endian.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
@@ -533,9 +531,7 @@ static void dumpInput(StringRef File) {
}
int main(int argc, const char *argv[]) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y;
+ InitLLVM X(argc, argv);
// Initialize targets.
llvm::InitializeAllTargetInfos();
diff --git a/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
index 9c6a1612fa08..afc1e4a8d128 100644
--- a/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
+++ b/contrib/llvm/tools/llvm-cxxfilt/llvm-cxxfilt.cpp
@@ -9,8 +9,7 @@
#include "llvm/Demangle/Demangle.h"
#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdlib>
#include <iostream>
@@ -81,8 +80,7 @@ static void demangle(llvm::raw_ostream &OS, const std::string &Mangled) {
}
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
+ InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, "llvm symbol undecoration tool\n");
diff --git a/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp b/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp
index 95a63d7f9c83..af0a055ea21f 100644
--- a/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp
+++ b/contrib/llvm/tools/llvm-diff/DifferenceEngine.cpp
@@ -303,6 +303,26 @@ class FunctionDifferenceEngine {
if (TryUnify) tryUnify(LI->getSuccessor(0), RI->getSuccessor(0));
return false;
+ } else if (isa<IndirectBrInst>(L)) {
+ IndirectBrInst *LI = cast<IndirectBrInst>(L);
+ IndirectBrInst *RI = cast<IndirectBrInst>(R);
+ if (LI->getNumDestinations() != RI->getNumDestinations()) {
+ if (Complain) Engine.log("indirectbr # of destinations differ");
+ return true;
+ }
+
+ if (!equivalentAsOperands(LI->getAddress(), RI->getAddress())) {
+ if (Complain) Engine.log("indirectbr addresses differ");
+ return true;
+ }
+
+ if (TryUnify) {
+ for (unsigned i = 0; i < LI->getNumDestinations(); i++) {
+ tryUnify(LI->getDestination(i), RI->getDestination(i));
+ }
+ }
+ return false;
+
} else if (isa<SwitchInst>(L)) {
SwitchInst *LI = cast<SwitchInst>(L);
SwitchInst *RI = cast<SwitchInst>(R);
@@ -377,9 +397,9 @@ class FunctionDifferenceEngine {
return equivalentAsOperands(cast<ConstantExpr>(L),
cast<ConstantExpr>(R));
- // Nulls of the "same type" don't always actually have the same
+ // Constants of the "same type" don't always actually have the same
// type; I don't know why. Just white-list them.
- if (isa<ConstantPointerNull>(L))
+ if (isa<ConstantPointerNull>(L) || isa<UndefValue>(L) || isa<ConstantAggregateZero>(L))
return true;
// Block addresses only match if we've already encountered the
@@ -388,6 +408,19 @@ class FunctionDifferenceEngine {
return Blocks[cast<BlockAddress>(L)->getBasicBlock()]
== cast<BlockAddress>(R)->getBasicBlock();
+ // If L and R are ConstantVectors, compare each element
+ if (isa<ConstantVector>(L)) {
+ ConstantVector *CVL = cast<ConstantVector>(L);
+ ConstantVector *CVR = cast<ConstantVector>(R);
+ if (CVL->getType()->getNumElements() != CVR->getType()->getNumElements())
+ return false;
+ for (unsigned i = 0; i < CVL->getType()->getNumElements(); i++) {
+ if (!equivalentAsOperands(CVL->getOperand(i), CVR->getOperand(i)))
+ return false;
+ }
+ return true;
+ }
+
return false;
}
diff --git a/contrib/llvm/tools/llvm-dis/llvm-dis.cpp b/contrib/llvm/tools/llvm-dis/llvm-dis.cpp
index c91aa1c71a15..8143a2a5a934 100644
--- a/contrib/llvm/tools/llvm-dis/llvm-dis.cpp
+++ b/contrib/llvm/tools/llvm-dis/llvm-dis.cpp
@@ -16,24 +16,23 @@
//
//===----------------------------------------------------------------------===//
-#include "llvm/IR/LLVMContext.h"
#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/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/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
#include <system_error>
using namespace llvm;
@@ -129,10 +128,10 @@ struct LLVMDisDiagnosticHandler : public DiagnosticHandler {
raw_ostream &OS = errs();
OS << Prefix << ": ";
switch (DI.getSeverity()) {
- case DS_Error: OS << "error: "; break;
- case DS_Warning: OS << "warning: "; break;
+ case DS_Error: WithColor::error(OS); break;
+ case DS_Warning: WithColor::warning(OS); break;
case DS_Remark: OS << "remark: "; break;
- case DS_Note: OS << "note: "; break;
+ case DS_Note: WithColor::note(OS); break;
}
DiagnosticPrinterRawOStream DP(OS);
@@ -148,33 +147,29 @@ struct LLVMDisDiagnosticHandler : public DiagnosticHandler {
static ExitOnError ExitOnErr;
-static std::unique_ptr<Module> openInputFile(LLVMContext &Context) {
- std::unique_ptr<MemoryBuffer> MB =
- ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
- std::unique_ptr<Module> M = ExitOnErr(getOwningLazyBitcodeModule(
- std::move(MB), Context,
- /*ShouldLazyLoadMetadata=*/true, SetImporting));
- if (MaterializeMetadata)
- ExitOnErr(M->materializeMetadata());
- else
- ExitOnErr(M->materializeAll());
- return M;
-}
-
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
+ InitLLVM X(argc, argv);
ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
LLVMContext Context;
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
Context.setDiagnosticHandler(
llvm::make_unique<LLVMDisDiagnosticHandler>(argv[0]));
cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n");
- std::unique_ptr<Module> M = openInputFile(Context);
+ std::unique_ptr<MemoryBuffer> MB =
+ ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename)));
+ std::unique_ptr<Module> M = ExitOnErr(getLazyBitcodeModule(
+ *MB, Context, /*ShouldLazyLoadMetadata=*/true, SetImporting));
+ if (MaterializeMetadata)
+ ExitOnErr(M->materializeMetadata());
+ else
+ ExitOnErr(M->materializeAll());
+
+ BitcodeLTOInfo LTOInfo = ExitOnErr(getBitcodeLTOInfo(*MB));
+ std::unique_ptr<ModuleSummaryIndex> Index;
+ if (LTOInfo.HasSummary)
+ Index = ExitOnErr(getModuleSummaryIndex(*MB));
// Just use stdout. We won't actually print anything on it.
if (DontPrint)
@@ -203,8 +198,11 @@ int main(int argc, char **argv) {
Annotator.reset(new CommentWriter());
// All that llvm-dis does is write the assembly to a file.
- if (!DontPrint)
+ if (!DontPrint) {
M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder);
+ if (Index)
+ Index->print(Out->os());
+ }
// Declare success.
Out->keep();
diff --git a/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp b/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp
index 9a7454a52624..5af853d4ef28 100644
--- a/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp
+++ b/contrib/llvm/tools/llvm-dwarfdump/Statistics.cpp
@@ -34,8 +34,14 @@ struct GlobalStats {
/// Extract the low pc from a Die.
static uint64_t getLowPC(DWARFDie Die) {
- if (Die.getAddressRanges().size())
- return Die.getAddressRanges()[0].LowPC;
+ auto RangesOrError = Die.getAddressRanges();
+ DWARFAddressRangesVector Ranges;
+ if (RangesOrError)
+ Ranges = RangesOrError.get();
+ else
+ llvm::consumeError(RangesOrError.takeError());
+ if (Ranges.size())
+ return Ranges[0].LowPC;
return dwarf::toAddress(Die.find(dwarf::DW_AT_low_pc), 0);
}
@@ -137,7 +143,13 @@ static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
}
// PC Ranges.
- auto Ranges = Die.getAddressRanges();
+ auto RangesOrError = Die.getAddressRanges();
+ if (!RangesOrError) {
+ llvm::consumeError(RangesOrError.takeError());
+ return;
+ }
+
+ auto Ranges = RangesOrError.get();
uint64_t BytesInThisScope = 0;
for (auto Range : Ranges)
BytesInThisScope += Range.HighPC - Range.LowPC;
@@ -165,11 +177,11 @@ static void collectStatsRecursive(DWARFDie Die, std::string Prefix,
/// \{
static void printDatum(raw_ostream &OS, const char *Key, StringRef Value) {
OS << ",\"" << Key << "\":\"" << Value << '"';
- DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
+ LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
}
static void printDatum(raw_ostream &OS, const char *Key, uint64_t Value) {
OS << ",\"" << Key << "\":" << Value;
- DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
+ LLVM_DEBUG(llvm::dbgs() << Key << ": " << Value << '\n');
}
/// \}
@@ -206,8 +218,9 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
VarWithLoc += Stats.TotalVarWithLoc + Constants;
VarTotal += TotalVars + Constants;
VarUnique += Stats.VarsInFunction.size();
- DEBUG(for (auto V : Stats.VarsInFunction)
- llvm::dbgs() << Entry.getKey() << ": " << V << "\n");
+ LLVM_DEBUG(for (auto V
+ : Stats.VarsInFunction) llvm::dbgs()
+ << Entry.getKey() << ": " << V << "\n");
NumFunctions += Stats.IsFunction;
NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined;
}
@@ -215,8 +228,8 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
// Print summary.
OS.SetBufferSize(1024);
OS << "{\"version\":\"" << Version << '"';
- DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
- llvm::dbgs() << "---------------------------------\n");
+ LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n";
+ llvm::dbgs() << "---------------------------------\n");
printDatum(OS, "file", Filename.str());
printDatum(OS, "format", FormatName);
printDatum(OS, "source functions", NumFunctions);
@@ -228,7 +241,7 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx,
GlobalStats.ScopeBytesFromFirstDefinition);
printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered);
OS << "}\n";
- DEBUG(
+ LLVM_DEBUG(
llvm::dbgs() << "Total Availability: "
<< (int)std::round((VarWithLoc * 100.0) / VarTotal) << "%\n";
llvm::dbgs() << "PC Ranges covered: "
diff --git a/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
index 12c005de6005..d75f33906098 100644
--- a/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
+++ b/contrib/llvm/tools/llvm-dwarfdump/llvm-dwarfdump.cpp
@@ -22,14 +22,13 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/Format.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Regex.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;
@@ -310,6 +309,62 @@ static void filterByName(const StringSet<> &Names,
}
+static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel,
+ StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
+ for (const auto &Entry : Accel.equal_range(Name)) {
+ if (llvm::Optional<uint64_t> Off = Entry.getDIESectionOffset()) {
+ if (DWARFDie Die = DICtx.getDIEForOffset(*Off))
+ Dies.push_back(Die);
+ }
+ }
+}
+
+static DWARFDie toDie(const DWARFDebugNames::Entry &Entry,
+ DWARFContext &DICtx) {
+ llvm::Optional<uint64_t> CUOff = Entry.getCUOffset();
+ llvm::Optional<uint64_t> Off = Entry.getDIEUnitOffset();
+ if (!CUOff || !Off)
+ return DWARFDie();
+
+ DWARFCompileUnit *CU = DICtx.getCompileUnitForOffset(*CUOff);
+ if (!CU)
+ return DWARFDie();
+
+ if (llvm::Optional<uint64_t> DWOId = CU->getDWOId()) {
+ // This is a skeleton unit. Look up the DIE in the DWO unit.
+ CU = DICtx.getDWOCompileUnitForHash(*DWOId);
+ if (!CU)
+ return DWARFDie();
+ }
+
+ return CU->getDIEForOffset(CU->getOffset() + *Off);
+}
+
+static void getDies(DWARFContext &DICtx, const DWARFDebugNames &Accel,
+ StringRef Name, SmallVectorImpl<DWARFDie> &Dies) {
+ for (const auto &Entry : Accel.equal_range(Name)) {
+ if (DWARFDie Die = toDie(Entry, DICtx))
+ Dies.push_back(Die);
+ }
+}
+
+/// Print only DIEs that have a certain name.
+static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx,
+ raw_ostream &OS) {
+ SmallVector<DWARFDie, 4> Dies;
+ for (const auto &Name : Names) {
+ getDies(DICtx, DICtx.getAppleNames(), Name, Dies);
+ getDies(DICtx, DICtx.getAppleTypes(), Name, Dies);
+ getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies);
+ getDies(DICtx, DICtx.getDebugNames(), Name, Dies);
+ }
+ llvm::sort(Dies.begin(), Dies.end());
+ Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end());
+
+ for (DWARFDie Die : Dies)
+ Die.dump(OS, 0, getDumpOpts());
+}
+
/// Handle the --lookup option and dump the DIEs and line info for the given
/// address.
static bool lookup(DWARFContext &DICtx, uint64_t Address, raw_ostream &OS) {
@@ -361,28 +416,8 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename,
// Handle the --find option and lower it to --debug-info=<offset>.
if (!Find.empty()) {
- DumpOffsets[DIDT_ID_DebugInfo] = [&]() -> llvm::Optional<uint64_t> {
- for (auto Name : Find) {
- auto find = [&](const DWARFAcceleratorTable &Accel)
- -> llvm::Optional<uint64_t> {
- for (auto Entry : Accel.equal_range(Name))
- for (auto Atom : Entry)
- if (auto Offset = Atom.getAsSectionOffset())
- return Offset;
- return None;
- };
- if (auto Offset = find(DICtx.getAppleNames()))
- return DumpOffsets[DIDT_ID_DebugInfo] = *Offset;
- if (auto Offset = find(DICtx.getAppleTypes()))
- return DumpOffsets[DIDT_ID_DebugInfo] = *Offset;
- if (auto Offset = find(DICtx.getAppleNamespaces()))
- return DumpOffsets[DIDT_ID_DebugInfo] = *Offset;
- }
- return None;
- }();
- // Early exit if --find was specified but the current file doesn't have it.
- if (!DumpOffsets[DIDT_ID_DebugInfo])
- return true;
+ filterByAccelName(Find, DICtx, OS);
+ return true;
}
// Dump the complete DWARF structure.
@@ -477,6 +512,8 @@ static bool handleFile(StringRef Filename, HandlerFn HandleObj,
static std::vector<std::string> expandBundle(const std::string &InputPath) {
std::vector<std::string> BundlePaths;
SmallString<256> BundlePath(InputPath);
+ // Normalize input path. This is necessary to accept `bundle.dSYM/`.
+ sys::path::remove_dots(BundlePath);
// Manually open up the bundle to avoid introducing additional dependencies.
if (sys::fs::is_directory(BundlePath) &&
sys::path::extension(BundlePath) == ".dSYM") {
@@ -505,15 +542,12 @@ static std::vector<std::string> expandBundle(const std::string &InputPath) {
}
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
- HideUnrelatedOptions({&DwarfDumpCategory, &SectionCategory});
+ HideUnrelatedOptions({&DwarfDumpCategory, &SectionCategory, &ColorCategory});
cl::ParseCommandLineOptions(
argc, argv,
"pretty-print DWARF debug information in object files"
@@ -565,7 +599,7 @@ int main(int argc, char **argv) {
ShowChildren = true;
// Defaults to a.out if no filenames specified.
- if (InputFilenames.size() == 0)
+ if (InputFilenames.empty())
InputFilenames.push_back("a.out");
// Expand any .dSYM bundles to the individual object files contained therein.
diff --git a/contrib/llvm/tools/llvm-extract/llvm-extract.cpp b/contrib/llvm/tools/llvm-extract/llvm-extract.cpp
index c39ffa58fbf7..94aaa2f52eb5 100644
--- a/contrib/llvm/tools/llvm-extract/llvm-extract.cpp
+++ b/contrib/llvm/tools/llvm-extract/llvm-extract.cpp
@@ -25,10 +25,8 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/PrettyStackTrace.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Regex.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -67,6 +65,12 @@ ExtractRegExpFuncs("rfunc", cl::desc("Specify function(s) to extract using a "
"regular expression"),
cl::ZeroOrMore, cl::value_desc("rfunction"));
+// ExtractBlocks - The blocks to extract from the module.
+static cl::list<std::string>
+ ExtractBlocks("bb",
+ cl::desc("Specify <function, basic block> pairs to extract"),
+ cl::ZeroOrMore, cl::value_desc("function:bb"));
+
// ExtractAlias - The alias to extract from the module.
static cl::list<std::string>
ExtractAliases("alias", cl::desc("Specify alias to extract"),
@@ -107,12 +111,9 @@ static cl::opt<bool> PreserveAssemblyUseListOrder(
cl::init(false), cl::Hidden);
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
+ InitLLVM X(argc, argv);
LLVMContext Context;
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm extractor\n");
// Use lazy loading, since we only care about selected global values.
@@ -228,6 +229,32 @@ int main(int argc, char **argv) {
}
}
+ // Figure out which BasicBlocks we should extract.
+ SmallVector<BasicBlock *, 4> BBs;
+ for (StringRef StrPair : ExtractBlocks) {
+ auto BBInfo = StrPair.split(':');
+ // Get the function.
+ Function *F = M->getFunction(BBInfo.first);
+ if (!F) {
+ errs() << argv[0] << ": program doesn't contain a function named '"
+ << BBInfo.first << "'!\n";
+ return 1;
+ }
+ // Do not materialize this function.
+ GVs.insert(F);
+ // Get the basic block.
+ auto Res = llvm::find_if(*F, [&](const BasicBlock &BB) {
+ return BB.getName().equals(BBInfo.second);
+ });
+ if (Res == F->end()) {
+ errs() << argv[0] << ": function " << F->getName()
+ << " doesn't contain a basic block named '" << BBInfo.second
+ << "'!\n";
+ return 1;
+ }
+ BBs.push_back(&*Res);
+ }
+
// Use *argv instead of argv[0] to work around a wrong GCC warning.
ExitOnError ExitOnErr(std::string(*argv) + ": error reading input: ");
@@ -286,6 +313,14 @@ int main(int argc, char **argv) {
ExitOnErr(M->materializeAll());
}
+ // Extract the specified basic blocks from the module and erase the existing
+ // functions.
+ if (!ExtractBlocks.empty()) {
+ legacy::PassManager PM;
+ PM.add(createBlockExtractorPass(BBs, true));
+ PM.run(*M);
+ }
+
// In addition to deleting all other functions, we also want to spiff it
// up a little bit. Do this now.
legacy::PassManager Passes;
diff --git a/contrib/llvm/tools/llvm-link/llvm-link.cpp b/contrib/llvm/tools/llvm-link/llvm-link.cpp
index 50f506aeaae9..b7a888375b3d 100644
--- a/contrib/llvm/tools/llvm-link/llvm-link.cpp
+++ b/contrib/llvm/tools/llvm-link/llvm-link.cpp
@@ -26,13 +26,12 @@
#include "llvm/Linker/Linker.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Transforms/IPO/FunctionImport.h"
#include "llvm/Transforms/IPO/Internalize.h"
#include "llvm/Transforms/Utils/FunctionImportUtils.h"
@@ -120,7 +119,8 @@ static std::unique_ptr<Module> loadFile(const char *argv0,
LLVMContext &Context,
bool MaterializeMetadata = true) {
SMDiagnostic Err;
- if (Verbose) errs() << "Loading '" << FN << "'\n";
+ if (Verbose)
+ errs() << "Loading '" << FN << "'\n";
std::unique_ptr<Module> Result;
if (DisableLazyLoad)
Result = parseIRFile(FN, Err, Context);
@@ -188,12 +188,12 @@ struct LLVMLinkDiagnosticHandler : public DiagnosticHandler {
unsigned Severity = DI.getSeverity();
switch (Severity) {
case DS_Error:
- errs() << "ERROR: ";
+ WithColor::error();
break;
case DS_Warning:
if (SuppressWarnings)
return true;
- errs() << "WARNING: ";
+ WithColor::warning();
break;
case DS_Remark:
case DS_Note:
@@ -238,8 +238,8 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
auto &SrcModule = ModuleLoaderCache(argv0, FileName);
if (verifyModule(SrcModule, &errs())) {
- errs() << argv0 << ": " << FileName
- << ": error: input module is broken!\n";
+ errs() << argv0 << ": " << FileName;
+ WithColor::error() << "input module is broken!\n";
return false;
}
@@ -262,7 +262,7 @@ static bool importFunctions(const char *argv0, Module &DestModule) {
errs() << "Importing " << FunctionName << " from " << FileName << "\n";
auto &Entry = ImportList[FileName];
- Entry.insert(std::make_pair(F->getGUID(), /* (Unused) threshold */ 1.0));
+ Entry.insert(F->getGUID());
}
auto CachedModuleLoader = [&](StringRef Identifier) {
return ModuleLoaderCache.takeModule(Identifier);
@@ -283,7 +283,8 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
for (const auto &File : Files) {
std::unique_ptr<Module> M = loadFile(argv0, File, Context);
if (!M.get()) {
- errs() << argv0 << ": error loading file '" << File << "'\n";
+ errs() << argv0 << ": ";
+ WithColor::error() << " loading file '" << File << "'\n";
return false;
}
@@ -291,7 +292,8 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
// doing that debug metadata in the src module might already be pointing to
// the destination.
if (DisableDITypeMap && verifyModule(*M, &errs())) {
- errs() << argv0 << ": " << File << ": error: input module is broken!\n";
+ errs() << argv0 << ": " << File << ": ";
+ WithColor::error() << "input module is broken!\n";
return false;
}
@@ -345,16 +347,12 @@ static bool linkFiles(const char *argv0, LLVMContext &Context, Linker &L,
}
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
-
+ InitLLVM X(argc, argv);
ExitOnErr.setBanner(std::string(argv[0]) + ": ");
LLVMContext Context;
Context.setDiagnosticHandler(
llvm::make_unique<LLVMLinkDiagnosticHandler>(), true);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
cl::ParseCommandLineOptions(argc, argv, "llvm linker\n");
if (!DisableDITypeMap)
@@ -380,25 +378,28 @@ int main(int argc, char **argv) {
if (!importFunctions(argv[0], *Composite))
return 1;
- if (DumpAsm) errs() << "Here's the assembly:\n" << *Composite;
+ if (DumpAsm)
+ errs() << "Here's the assembly:\n" << *Composite;
std::error_code EC;
ToolOutputFile Out(OutputFilename, EC, sys::fs::F_None);
if (EC) {
- errs() << EC.message() << '\n';
+ WithColor::error() << EC.message() << '\n';
return 1;
}
if (verifyModule(*Composite, &errs())) {
- errs() << argv[0] << ": error: linked module is broken!\n";
+ errs() << argv[0] << ": ";
+ WithColor::error() << "linked module is broken!\n";
return 1;
}
- if (Verbose) errs() << "Writing bitcode...\n";
+ if (Verbose)
+ errs() << "Writing bitcode...\n";
if (OutputAssembly) {
Composite->print(Out.os(), nullptr, PreserveAssemblyUseListOrder);
} else if (Force || !CheckBitcodeOutputToConsole(Out.os(), true))
- WriteBitcodeToFile(Composite.get(), Out.os(), PreserveBitcodeUseListOrder);
+ WriteBitcodeToFile(*Composite, Out.os(), PreserveBitcodeUseListOrder);
// Declare success.
Out.keep();
diff --git a/contrib/llvm/tools/llvm-lto/llvm-lto.cpp b/contrib/llvm/tools/llvm-lto/llvm-lto.cpp
index 7d71a3e8dfe3..75668a9dd8b6 100644
--- a/contrib/llvm/tools/llvm-lto/llvm-lto.cpp
+++ b/contrib/llvm/tools/llvm-lto/llvm-lto.cpp
@@ -22,7 +22,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Bitcode/BitcodeReader.h"
#include "llvm/Bitcode/BitcodeWriter.h"
-#include "llvm/CodeGen/CommandFlags.def"
+#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/IR/DiagnosticInfo.h"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/IR/LLVMContext.h"
@@ -40,11 +40,9 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -157,7 +155,16 @@ static cl::opt<std::string>
ThinLTOCacheDir("thinlto-cache-dir", cl::desc("Enable ThinLTO caching."));
static cl::opt<int>
- ThinLTOCachePruningInterval("thinlto-cache-pruning-interval", cl::desc("Set ThinLTO cache pruning interval."));
+ ThinLTOCachePruningInterval("thinlto-cache-pruning-interval",
+ cl::init(1200), cl::desc("Set ThinLTO cache pruning interval."));
+
+static cl::opt<int>
+ ThinLTOCacheMaxSizeBytes("thinlto-cache-max-size-bytes",
+ cl::desc("Set ThinLTO cache pruning directory maximum size in bytes."));
+
+static cl::opt<int>
+ ThinLTOCacheMaxSizeFiles("thinlto-cache-max-size-files", cl::init(1000000),
+ cl::desc("Set ThinLTO cache pruning directory maximum number of files."));
static cl::opt<std::string> ThinLTOSaveTempsPrefix(
"thinlto-save-temps",
@@ -343,7 +350,7 @@ void printIndexStats() {
}
}
-/// \brief List symbols in each IR file.
+/// List symbols in each IR file.
///
/// The main point here is to provide lit-testable coverage for the LTOModule
/// functionality that's exposed by the C API to list symbols. Moreover, this
@@ -367,13 +374,13 @@ static void listSymbols(const TargetOptions &Options) {
/// This is meant to enable testing of ThinLTO combined index generation,
/// currently available via the gold plugin via -thinlto.
static void createCombinedModuleSummaryIndex() {
- ModuleSummaryIndex CombinedIndex;
+ ModuleSummaryIndex CombinedIndex(/*HaveGVs=*/false);
uint64_t NextModuleId = 0;
for (auto &Filename : InputFilenames) {
ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': ");
std::unique_ptr<MemoryBuffer> MB =
ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename)));
- ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, ++NextModuleId));
+ ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, NextModuleId++));
}
std::error_code EC;
assert(!OutputFilename.empty());
@@ -462,7 +469,7 @@ static void writeModuleToFile(Module &TheModule, StringRef Filename) {
raw_fd_ostream OS(Filename, EC, sys::fs::OpenFlags::F_None);
error(EC, "error opening the file '" + Filename + "'");
maybeVerifyModule(TheModule);
- WriteBitcodeToFile(&TheModule, OS, /* ShouldPreserveUseListOrder */ true);
+ WriteBitcodeToFile(TheModule, OS, /* ShouldPreserveUseListOrder */ true);
}
class ThinLTOProcessing {
@@ -474,6 +481,8 @@ public:
ThinGenerator.setTargetOptions(Options);
ThinGenerator.setCacheDir(ThinLTOCacheDir);
ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval);
+ ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles);
+ ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes);
ThinGenerator.setFreestanding(EnableFreestanding);
// Add all the exported symbols to the table of symbols to preserve.
@@ -788,11 +797,7 @@ private:
} // end namespace thinlto
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
-
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, "llvm LTO linker\n");
if (OptLevel < '0' || OptLevel > '3')
diff --git a/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp b/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp
index 70aae0f41507..442973f90209 100644
--- a/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp
+++ b/contrib/llvm/tools/llvm-lto2/llvm-lto2.cpp
@@ -17,7 +17,7 @@
//===----------------------------------------------------------------------===//
#include "llvm/Bitcode/BitcodeReader.h"
-#include "llvm/CodeGen/CommandFlags.def"
+#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/IR/DiagnosticPrinter.h"
#include "llvm/LTO/Caching.h"
#include "llvm/LTO/LTO.h"
@@ -113,6 +113,9 @@ static cl::opt<bool>
DebugPassManager("debug-pass-manager", cl::init(false), cl::Hidden,
cl::desc("Print pass management debugging information"));
+static cl::opt<std::string>
+ StatsFile("stats-file", cl::desc("Filename to write statistics to"));
+
static void check(Error E, std::string Msg) {
if (!E)
return;
@@ -189,7 +192,8 @@ static int run(int argc, char **argv) {
DiagnosticPrinterRawOStream DP(errs());
DI.print(DP);
errs() << '\n';
- exit(1);
+ if (DI.getSeverity() == DS_Error)
+ exit(1);
};
Conf.CPU = MCPU;
@@ -240,10 +244,15 @@ static int run(int argc, char **argv) {
Conf.OverrideTriple = OverrideTriple;
Conf.DefaultTriple = DefaultTriple;
+ Conf.StatsFile = StatsFile;
ThinBackend Backend;
if (ThinLTODistributedIndexes)
- Backend = createWriteIndexesThinBackend("", "", true, "");
+ Backend = createWriteIndexesThinBackend(/* OldPrefix */ "",
+ /* NewPrefix */ "",
+ /* ShouldEmitImportsFiles */ true,
+ /* LinkedObjectsFile */ nullptr,
+ /* OnWrite */ {});
else
Backend = createInProcessThinBackend(Threads);
LTO Lto(std::move(Conf), std::move(Backend));
@@ -296,8 +305,7 @@ static int run(int argc, char **argv) {
return llvm::make_unique<lto::NativeObjectStream>(std::move(S));
};
- auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB,
- StringRef Path) {
+ auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) {
*AddStream(Task)->OS << MB->getBuffer();
};
diff --git a/contrib/llvm/tools/llvm-mc/llvm-mc.cpp b/contrib/llvm/tools/llvm-mc/llvm-mc.cpp
index 3987be2bd688..f494d02f3bca 100644
--- a/contrib/llvm/tools/llvm-mc/llvm-mc.cpp
+++ b/contrib/llvm/tools/llvm-mc/llvm-mc.cpp
@@ -20,34 +20,38 @@
#include "llvm/MC/MCInstPrinter.h"
#include "llvm/MC/MCInstrInfo.h"
#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCParser/AsmLexer.h"
#include "llvm/MC/MCParser/MCTargetAsmParser.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSubtargetInfo.h"
-#include "llvm/MC/MCTargetOptionsCommandFlags.def"
+#include "llvm/MC/MCTargetOptionsCommandFlags.inc"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compression.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/Host.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
using namespace llvm;
static cl::opt<std::string>
InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-"));
-static cl::opt<std::string>
-OutputFilename("o", cl::desc("Output filename"),
- cl::value_desc("filename"));
+static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
+ cl::value_desc("filename"),
+ cl::init("-"));
+
+static cl::opt<std::string> SplitDwarfFile("split-dwarf-file",
+ cl::desc("DWO output filename"),
+ cl::value_desc("filename"));
static cl::opt<bool>
ShowEncoding("show-encoding", cl::desc("Show instruction encodings"));
@@ -148,6 +152,11 @@ static cl::opt<std::string>
DebugCompilationDir("fdebug-compilation-dir",
cl::desc("Specifies the debug info's compilation dir"));
+static cl::list<std::string>
+DebugPrefixMap("fdebug-prefix-map",
+ cl::desc("Map file source paths in debug info"),
+ cl::value_desc("= separated key-value pairs"));
+
static cl::opt<std::string>
MainFileName("main-file-name",
cl::desc("Specifies the name we should consider the input file"));
@@ -188,7 +197,7 @@ static const Target *GetTarget(const char *ProgName) {
const Target *TheTarget = TargetRegistry::lookupTarget(ArchName, TheTriple,
Error);
if (!TheTarget) {
- errs() << ProgName << ": " << Error;
+ WithColor::error(errs(), ProgName) << Error;
return nullptr;
}
@@ -197,15 +206,11 @@ static const Target *GetTarget(const char *ProgName) {
return TheTarget;
}
-static std::unique_ptr<ToolOutputFile> GetOutputStream() {
- if (OutputFilename == "")
- OutputFilename = "-";
-
+static std::unique_ptr<ToolOutputFile> GetOutputStream(StringRef Path) {
std::error_code EC;
- auto Out =
- llvm::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::F_None);
+ auto Out = llvm::make_unique<ToolOutputFile>(Path, EC, sys::fs::F_None);
if (EC) {
- errs() << EC.message() << '\n';
+ WithColor::error() << EC.message() << '\n';
return nullptr;
}
@@ -238,144 +243,10 @@ static int AsLexInput(SourceMgr &SrcMgr, MCAsmInfo &MAI,
bool Error = false;
while (Lexer.Lex().isNot(AsmToken::Eof)) {
- const AsmToken &Tok = Lexer.getTok();
-
- switch (Tok.getKind()) {
- default:
- SrcMgr.PrintMessage(Lexer.getLoc(), SourceMgr::DK_Warning,
- "unknown token");
+ Lexer.getTok().dump(OS);
+ OS << "\n";
+ if (Lexer.getTok().getKind() == AsmToken::Error)
Error = true;
- break;
- case AsmToken::Error:
- Error = true; // error already printed.
- break;
- case AsmToken::Identifier:
- OS << "identifier: " << Lexer.getTok().getString();
- break;
- case AsmToken::Integer:
- OS << "int: " << Lexer.getTok().getString();
- break;
- case AsmToken::Real:
- OS << "real: " << Lexer.getTok().getString();
- break;
- case AsmToken::String:
- OS << "string: " << Lexer.getTok().getString();
- break;
-
- case AsmToken::Amp: OS << "Amp"; break;
- case AsmToken::AmpAmp: OS << "AmpAmp"; break;
- case AsmToken::At: OS << "At"; break;
- case AsmToken::Caret: OS << "Caret"; break;
- case AsmToken::Colon: OS << "Colon"; break;
- case AsmToken::Comma: OS << "Comma"; break;
- case AsmToken::Dollar: OS << "Dollar"; break;
- case AsmToken::Dot: OS << "Dot"; break;
- case AsmToken::EndOfStatement: OS << "EndOfStatement"; break;
- case AsmToken::Eof: OS << "Eof"; break;
- case AsmToken::Equal: OS << "Equal"; break;
- case AsmToken::EqualEqual: OS << "EqualEqual"; break;
- case AsmToken::Exclaim: OS << "Exclaim"; break;
- case AsmToken::ExclaimEqual: OS << "ExclaimEqual"; break;
- case AsmToken::Greater: OS << "Greater"; break;
- case AsmToken::GreaterEqual: OS << "GreaterEqual"; break;
- case AsmToken::GreaterGreater: OS << "GreaterGreater"; break;
- case AsmToken::Hash: OS << "Hash"; break;
- case AsmToken::LBrac: OS << "LBrac"; break;
- case AsmToken::LCurly: OS << "LCurly"; break;
- case AsmToken::LParen: OS << "LParen"; break;
- case AsmToken::Less: OS << "Less"; break;
- case AsmToken::LessEqual: OS << "LessEqual"; break;
- case AsmToken::LessGreater: OS << "LessGreater"; break;
- case AsmToken::LessLess: OS << "LessLess"; break;
- case AsmToken::Minus: OS << "Minus"; break;
- case AsmToken::Percent: OS << "Percent"; break;
- case AsmToken::Pipe: OS << "Pipe"; break;
- case AsmToken::PipePipe: OS << "PipePipe"; break;
- case AsmToken::Plus: OS << "Plus"; break;
- case AsmToken::RBrac: OS << "RBrac"; break;
- case AsmToken::RCurly: OS << "RCurly"; break;
- case AsmToken::RParen: OS << "RParen"; break;
- case AsmToken::Slash: OS << "Slash"; break;
- case AsmToken::Star: OS << "Star"; break;
- case AsmToken::Tilde: OS << "Tilde"; break;
- case AsmToken::PercentCall16:
- OS << "PercentCall16";
- break;
- case AsmToken::PercentCall_Hi:
- OS << "PercentCall_Hi";
- break;
- case AsmToken::PercentCall_Lo:
- OS << "PercentCall_Lo";
- break;
- case AsmToken::PercentDtprel_Hi:
- OS << "PercentDtprel_Hi";
- break;
- case AsmToken::PercentDtprel_Lo:
- OS << "PercentDtprel_Lo";
- break;
- case AsmToken::PercentGot:
- OS << "PercentGot";
- break;
- case AsmToken::PercentGot_Disp:
- OS << "PercentGot_Disp";
- break;
- case AsmToken::PercentGot_Hi:
- OS << "PercentGot_Hi";
- break;
- case AsmToken::PercentGot_Lo:
- OS << "PercentGot_Lo";
- break;
- case AsmToken::PercentGot_Ofst:
- OS << "PercentGot_Ofst";
- break;
- case AsmToken::PercentGot_Page:
- OS << "PercentGot_Page";
- break;
- case AsmToken::PercentGottprel:
- OS << "PercentGottprel";
- break;
- case AsmToken::PercentGp_Rel:
- OS << "PercentGp_Rel";
- break;
- case AsmToken::PercentHi:
- OS << "PercentHi";
- break;
- case AsmToken::PercentHigher:
- OS << "PercentHigher";
- break;
- case AsmToken::PercentHighest:
- OS << "PercentHighest";
- break;
- case AsmToken::PercentLo:
- OS << "PercentLo";
- break;
- case AsmToken::PercentNeg:
- OS << "PercentNeg";
- break;
- case AsmToken::PercentPcrel_Hi:
- OS << "PercentPcrel_Hi";
- break;
- case AsmToken::PercentPcrel_Lo:
- OS << "PercentPcrel_Lo";
- break;
- case AsmToken::PercentTlsgd:
- OS << "PercentTlsgd";
- break;
- case AsmToken::PercentTlsldm:
- OS << "PercentTlsldm";
- break;
- case AsmToken::PercentTprel_Hi:
- OS << "PercentTprel_Hi";
- break;
- case AsmToken::PercentTprel_Lo:
- OS << "PercentTprel_Lo";
- break;
- }
-
- // Print the token string.
- OS << " (\"";
- OS.write_escaped(Tok.getString());
- OS << "\")\n";
}
return Error;
@@ -388,12 +259,13 @@ static int fillCommandLineSymbols(MCAsmParser &Parser) {
auto Val = Pair.second;
if (Sym.empty() || Val.empty()) {
- errs() << "error: defsym must be of the form: sym=value: " << I << "\n";
+ WithColor::error() << "defsym must be of the form: sym=value: " << I
+ << "\n";
return 1;
}
int64_t Value;
if (Val.getAsInteger(0, Value)) {
- errs() << "error: Value is not an integer: " << Val << "\n";
+ WithColor::error() << "value is not an integer: " << Val << "\n";
return 1;
}
Parser.getContext().setSymbolValue(Parser.getStreamer(), Sym, Value);
@@ -411,8 +283,8 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget,
TheTarget->createMCAsmParser(STI, *Parser, MCII, MCOptions));
if (!TAP) {
- errs() << ProgName
- << ": error: this target does not support assembly parsing.\n";
+ WithColor::error(errs(), ProgName)
+ << "this target does not support assembly parsing.\n";
return 1;
}
@@ -428,10 +300,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget,
}
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
// Initialize targets and assembly printers/parsers.
llvm::InitializeAllTargetInfos();
@@ -460,7 +329,8 @@ int main(int argc, char **argv) {
ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
MemoryBuffer::getFileOrSTDIN(InputFilename);
if (std::error_code EC = BufferPtr.getError()) {
- errs() << InputFilename << ": " << EC.message() << '\n';
+ WithColor::error(errs(), ProgName)
+ << InputFilename << ": " << EC.message() << '\n';
return 1;
}
MemoryBuffer *Buffer = BufferPtr->get();
@@ -484,8 +354,8 @@ int main(int argc, char **argv) {
if (CompressDebugSections != DebugCompressionType::None) {
if (!zlib::isAvailable()) {
- errs() << ProgName
- << ": build tools with zlib to enable -compress-debug-sections";
+ WithColor::error(errs(), ProgName)
+ << "build tools with zlib to enable -compress-debug-sections";
return 1;
}
MAI->setCompressDebugSections(CompressDebugSections);
@@ -522,8 +392,24 @@ int main(int argc, char **argv) {
if (!sys::fs::current_path(CWD))
Ctx.setCompilationDir(CWD);
}
+ for (const auto &Arg : DebugPrefixMap) {
+ const auto &KV = StringRef(Arg).split('=');
+ Ctx.addDebugPrefixMapEntry(KV.first, KV.second);
+ }
if (!MainFileName.empty())
Ctx.setMainFileName(MainFileName);
+ if (GenDwarfForAssembly && DwarfVersion >= 5) {
+ // DWARF v5 needs the root file as well as the compilation directory.
+ // If we find a '.file 0' directive that will supersede these values.
+ MD5 Hash;
+ MD5::MD5Result *Cksum =
+ (MD5::MD5Result *)Ctx.allocate(sizeof(MD5::MD5Result), 1);
+ Hash.update(Buffer->getBuffer());
+ Hash.final(*Cksum);
+ Ctx.setMCLineTableRootFile(
+ /*CUID=*/0, Ctx.getCompilationDir(),
+ !MainFileName.empty() ? MainFileName : InputFilename, Cksum, None);
+ }
// Package up features to be passed to target/subtarget
std::string FeaturesStr;
@@ -534,10 +420,21 @@ int main(int argc, char **argv) {
FeaturesStr = Features.getString();
}
- std::unique_ptr<ToolOutputFile> Out = GetOutputStream();
+ std::unique_ptr<ToolOutputFile> Out = GetOutputStream(OutputFilename);
if (!Out)
return 1;
+ std::unique_ptr<ToolOutputFile> DwoOut;
+ if (!SplitDwarfFile.empty()) {
+ if (FileType != OFT_ObjectFile) {
+ WithColor::error() << "dwo output only supported with object files\n";
+ return 1;
+ }
+ DwoOut = GetOutputStream(SplitDwarfFile);
+ if (!DwoOut)
+ return 1;
+ }
+
std::unique_ptr<buffer_ostream> BOS;
raw_pwrite_stream *OS = &Out->os();
std::unique_ptr<MCStreamer> Str;
@@ -552,8 +449,8 @@ int main(int argc, char **argv) {
*MAI, *MCII, *MRI);
if (!IP) {
- errs()
- << "error: unable to create instruction printer for target triple '"
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
<< TheTriple.normalize() << "' with assembly variant "
<< OutputAsmVariant << ".\n";
return 1;
@@ -563,16 +460,17 @@ int main(int argc, char **argv) {
IP->setPrintImmHex(PrintImmHex);
// Set up the AsmStreamer.
- MCCodeEmitter *CE = nullptr;
- MCAsmBackend *MAB = nullptr;
- if (ShowEncoding) {
- CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx);
- MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions);
- }
+ std::unique_ptr<MCCodeEmitter> CE;
+ if (ShowEncoding)
+ CE.reset(TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx));
+
+ std::unique_ptr<MCAsmBackend> MAB(
+ TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions));
auto FOut = llvm::make_unique<formatted_raw_ostream>(*OS);
- Str.reset(TheTarget->createAsmStreamer(
- Ctx, std::move(FOut), /*asmverbose*/ true,
- /*useDwarfDirectory*/ true, IP, CE, MAB, ShowInst));
+ Str.reset(
+ TheTarget->createAsmStreamer(Ctx, std::move(FOut), /*asmverbose*/ true,
+ /*useDwarfDirectory*/ true, IP,
+ std::move(CE), std::move(MAB), ShowInst));
} else if (FileType == OFT_Null) {
Str.reset(TheTarget->createNullStreamer(Ctx));
@@ -590,7 +488,9 @@ int main(int argc, char **argv) {
MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx);
MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions);
Str.reset(TheTarget->createMCObjectStreamer(
- TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), *OS,
+ TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB),
+ DwoOut ? MAB->createDwoObjectWriter(*OS, DwoOut->os())
+ : MAB->createObjectWriter(*OS),
std::unique_ptr<MCCodeEmitter>(CE), *STI, MCOptions.MCRelaxAll,
MCOptions.MCIncrementalLinkerCompatible,
/*DWARFMustBeAtTheEnd*/ false));
@@ -598,6 +498,9 @@ int main(int argc, char **argv) {
Str->InitSections(true);
}
+ // Use Assembler information for parsing.
+ Str->setUseAssemblerInfoForParsing(true);
+
int Res = 1;
bool disassemble = false;
switch (Action) {
@@ -622,6 +525,10 @@ int main(int argc, char **argv) {
*Buffer, SrcMgr, Out->os());
// Keep output if no errors.
- if (Res == 0) Out->keep();
+ if (Res == 0) {
+ Out->keep();
+ if (DwoOut)
+ DwoOut->keep();
+ }
return Res;
}
diff --git a/contrib/llvm/tools/llvm-mca/CodeRegion.cpp b/contrib/llvm/tools/llvm-mca/CodeRegion.cpp
new file mode 100644
index 000000000000..896865996504
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/CodeRegion.cpp
@@ -0,0 +1,66 @@
+//===-------------------------- CodeRegion.cpp -----------------*- C++ -* -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements methods from the CodeRegions interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "CodeRegion.h"
+
+using namespace llvm;
+
+namespace mca {
+
+bool CodeRegion::isLocInRange(SMLoc Loc) const {
+ if (RangeEnd.isValid() && Loc.getPointer() > RangeEnd.getPointer())
+ return false;
+ if (RangeStart.isValid() && Loc.getPointer() < RangeStart.getPointer())
+ return false;
+ return true;
+}
+
+void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) {
+ assert(!Regions.empty() && "Missing Default region");
+ const CodeRegion &CurrentRegion = *Regions.back();
+ if (CurrentRegion.startLoc().isValid() && !CurrentRegion.endLoc().isValid()) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Warning,
+ "Ignoring invalid region start");
+ return;
+ }
+
+ // Remove the default region if there are user defined regions.
+ if (!CurrentRegion.startLoc().isValid())
+ Regions.erase(Regions.begin());
+ addRegion(Description, Loc);
+}
+
+void CodeRegions::endRegion(SMLoc Loc) {
+ assert(!Regions.empty() && "Missing Default region");
+ CodeRegion &CurrentRegion = *Regions.back();
+ if (CurrentRegion.endLoc().isValid()) {
+ SM.PrintMessage(Loc, SourceMgr::DK_Warning, "Ignoring invalid region end");
+ return;
+ }
+
+ CurrentRegion.setEndLocation(Loc);
+}
+
+void CodeRegions::addInstruction(std::unique_ptr<const MCInst> Instruction) {
+ const SMLoc &Loc = Instruction->getLoc();
+ const auto It =
+ std::find_if(Regions.rbegin(), Regions.rend(),
+ [Loc](const std::unique_ptr<CodeRegion> &Region) {
+ return Region->isLocInRange(Loc);
+ });
+ if (It != Regions.rend())
+ (*It)->addInstruction(std::move(Instruction));
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/CodeRegion.h b/contrib/llvm/tools/llvm-mca/CodeRegion.h
new file mode 100644
index 000000000000..7f0025e4884c
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/CodeRegion.h
@@ -0,0 +1,131 @@
+//===-------------------------- CodeRegion.h -------------------*- C++ -* -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements class CodeRegion and CodeRegions.
+///
+/// A CodeRegion describes a region of assembly code guarded by special LLVM-MCA
+/// comment directives.
+///
+/// # LLVM-MCA-BEGIN foo
+/// ... ## asm
+/// # LLVM-MCA-END
+///
+/// A comment starting with substring LLVM-MCA-BEGIN marks the beginning of a
+/// new region of code.
+/// A comment starting with substring LLVM-MCA-END marks the end of the
+/// last-seen region of code.
+///
+/// Code regions are not allowed to overlap. Each region can have a optional
+/// description; internally, regions are described by a range of source
+/// locations (SMLoc objects).
+///
+/// An instruction (a MCInst) is added to a region R only if its location is in
+/// range [R.RangeStart, R.RangeEnd].
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_H
+#define LLVM_TOOLS_LLVM_MCA_CODEREGION_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/SMLoc.h"
+#include "llvm/Support/SourceMgr.h"
+#include <vector>
+
+namespace mca {
+
+/// A region of assembly code.
+///
+/// It identifies a sequence of machine instructions.
+class CodeRegion {
+ // An optional descriptor for this region.
+ llvm::StringRef Description;
+ // Instructions that form this region.
+ std::vector<std::unique_ptr<const llvm::MCInst>> Instructions;
+ // Source location range.
+ llvm::SMLoc RangeStart;
+ llvm::SMLoc RangeEnd;
+
+ CodeRegion(const CodeRegion &) = delete;
+ CodeRegion &operator=(const CodeRegion &) = delete;
+
+public:
+ CodeRegion(llvm::StringRef Desc, llvm::SMLoc Start)
+ : Description(Desc), RangeStart(Start), RangeEnd() {}
+
+ void addInstruction(std::unique_ptr<const llvm::MCInst> Instruction) {
+ Instructions.emplace_back(std::move(Instruction));
+ }
+
+ llvm::SMLoc startLoc() const { return RangeStart; }
+ llvm::SMLoc endLoc() const { return RangeEnd; }
+
+ void setEndLocation(llvm::SMLoc End) { RangeEnd = End; }
+ bool empty() const { return Instructions.empty(); }
+ bool isLocInRange(llvm::SMLoc Loc) const;
+
+ const std::vector<std::unique_ptr<const llvm::MCInst>> &
+ getInstructions() const {
+ return Instructions;
+ }
+
+ llvm::StringRef getDescription() const { return Description; }
+};
+
+class CodeRegions {
+ // A source manager. Used by the tool to generate meaningful warnings.
+ llvm::SourceMgr &SM;
+
+ std::vector<std::unique_ptr<CodeRegion>> Regions;
+
+ // Construct a new region of code guarded by LLVM-MCA comments.
+ void addRegion(llvm::StringRef Description, llvm::SMLoc Loc) {
+ Regions.emplace_back(llvm::make_unique<CodeRegion>(Description, Loc));
+ }
+
+ CodeRegions(const CodeRegions &) = delete;
+ CodeRegions &operator=(const CodeRegions &) = delete;
+
+public:
+ typedef std::vector<std::unique_ptr<CodeRegion>>::iterator iterator;
+ typedef std::vector<std::unique_ptr<CodeRegion>>::const_iterator
+ const_iterator;
+
+ iterator begin() { return Regions.begin(); }
+ iterator end() { return Regions.end(); }
+ const_iterator begin() const { return Regions.cbegin(); }
+ const_iterator end() const { return Regions.cend(); }
+
+ void beginRegion(llvm::StringRef Description, llvm::SMLoc Loc);
+ void endRegion(llvm::SMLoc Loc);
+ void addInstruction(std::unique_ptr<const llvm::MCInst> Instruction);
+
+ CodeRegions(llvm::SourceMgr &S) : SM(S) {
+ // Create a default region for the input code sequence.
+ addRegion("Default", llvm::SMLoc());
+ }
+
+ const std::vector<std::unique_ptr<const llvm::MCInst>> &
+ getInstructionSequence(unsigned Idx) const {
+ return Regions[Idx]->getInstructions();
+ }
+
+ bool empty() const {
+ return std::all_of(Regions.begin(), Regions.end(),
+ [](const std::unique_ptr<CodeRegion> &Region) {
+ return Region->empty();
+ });
+ }
+};
+
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/Context.cpp b/contrib/llvm/tools/llvm-mca/Context.cpp
new file mode 100644
index 000000000000..685714e64b92
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Context.cpp
@@ -0,0 +1,63 @@
+//===---------------------------- Context.cpp -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a class for holding ownership of various simulated
+/// hardware units. A Context also provides a utility routine for constructing
+/// a default out-of-order pipeline with fetch, dispatch, execute, and retire
+/// stages).
+///
+//===----------------------------------------------------------------------===//
+
+#include "Context.h"
+#include "DispatchStage.h"
+#include "ExecuteStage.h"
+#include "FetchStage.h"
+#include "RegisterFile.h"
+#include "RetireControlUnit.h"
+#include "RetireStage.h"
+#include "Scheduler.h"
+
+namespace mca {
+
+using namespace llvm;
+
+std::unique_ptr<Pipeline>
+Context::createDefaultPipeline(const PipelineOptions &Opts, InstrBuilder &IB,
+ SourceMgr &SrcMgr) {
+ const MCSchedModel &SM = STI.getSchedModel();
+
+ // Create the hardware units defining the backend.
+ auto RCU = llvm::make_unique<RetireControlUnit>(SM);
+ auto PRF = llvm::make_unique<RegisterFile>(SM, MRI, Opts.RegisterFileSize);
+ auto HWS = llvm::make_unique<Scheduler>(
+ SM, Opts.LoadQueueSize, Opts.StoreQueueSize, Opts.AssumeNoAlias);
+
+ // Create the pipeline and its stages.
+ auto P = llvm::make_unique<Pipeline>();
+ auto F = llvm::make_unique<FetchStage>(IB, SrcMgr);
+ auto D = llvm::make_unique<DispatchStage>(
+ STI, MRI, Opts.RegisterFileSize, Opts.DispatchWidth, *RCU, *PRF, *HWS);
+ auto R = llvm::make_unique<RetireStage>(*RCU, *PRF);
+ auto E = llvm::make_unique<ExecuteStage>(*RCU, *HWS);
+
+ // Add the hardware to the context.
+ addHardwareUnit(std::move(RCU));
+ addHardwareUnit(std::move(PRF));
+ addHardwareUnit(std::move(HWS));
+
+ // Build the pipeline.
+ P->appendStage(std::move(F));
+ P->appendStage(std::move(D));
+ P->appendStage(std::move(R));
+ P->appendStage(std::move(E));
+ return P;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/Context.h b/contrib/llvm/tools/llvm-mca/Context.h
new file mode 100644
index 000000000000..cf483fa7b37d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Context.h
@@ -0,0 +1,68 @@
+//===---------------------------- Context.h ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a class for holding ownership of various simulated
+/// hardware units. A Context also provides a utility routine for constructing
+/// a default out-of-order pipeline with fetch, dispatch, execute, and retire
+/// stages).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_CONTEXT_H
+#define LLVM_TOOLS_LLVM_MCA_CONTEXT_H
+#include "HardwareUnit.h"
+#include "InstrBuilder.h"
+#include "Pipeline.h"
+#include "SourceMgr.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSchedule.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include <memory>
+
+namespace mca {
+
+/// This is a convenience struct to hold the parameters necessary for creating
+/// the pre-built "default" out-of-order pipeline.
+struct PipelineOptions {
+ PipelineOptions(unsigned DW, unsigned RFS, unsigned LQS, unsigned SQS,
+ bool NoAlias)
+ : DispatchWidth(DW), RegisterFileSize(RFS), LoadQueueSize(LQS),
+ StoreQueueSize(SQS), AssumeNoAlias(NoAlias) {}
+ unsigned DispatchWidth;
+ unsigned RegisterFileSize;
+ unsigned LoadQueueSize;
+ unsigned StoreQueueSize;
+ bool AssumeNoAlias;
+};
+
+class Context {
+ llvm::SmallVector<std::unique_ptr<HardwareUnit>, 4> Hardware;
+ const llvm::MCRegisterInfo &MRI;
+ const llvm::MCSubtargetInfo &STI;
+
+public:
+ Context(const llvm::MCRegisterInfo &R, const llvm::MCSubtargetInfo &S)
+ : MRI(R), STI(S) {}
+ Context(const Context &C) = delete;
+ Context &operator=(const Context &C) = delete;
+
+ void addHardwareUnit(std::unique_ptr<HardwareUnit> H) {
+ Hardware.push_back(std::move(H));
+ }
+
+ /// Construct a basic pipeline for simulating an out-of-order pipeline.
+ /// This pipeline consists of Fetch, Dispatch, Execute, and Retire stages.
+ std::unique_ptr<Pipeline> createDefaultPipeline(const PipelineOptions &Opts,
+ InstrBuilder &IB,
+ SourceMgr &SrcMgr);
+};
+
+} // namespace mca
+#endif // LLVM_TOOLS_LLVM_MCA_CONTEXT_H
diff --git a/contrib/llvm/tools/llvm-mca/DispatchStage.cpp b/contrib/llvm/tools/llvm-mca/DispatchStage.cpp
new file mode 100644
index 000000000000..be6f1f89be5c
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/DispatchStage.cpp
@@ -0,0 +1,149 @@
+//===--------------------- DispatchStage.cpp --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file models the dispatch component of an instruction pipeline.
+///
+/// The DispatchStage is responsible for updating instruction dependencies
+/// and communicating to the simulated instruction scheduler that an instruction
+/// is ready to be scheduled for execution.
+///
+//===----------------------------------------------------------------------===//
+
+#include "DispatchStage.h"
+#include "HWEventListener.h"
+#include "Scheduler.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+void DispatchStage::notifyInstructionDispatched(const InstRef &IR,
+ ArrayRef<unsigned> UsedRegs) {
+ LLVM_DEBUG(dbgs() << "[E] Instruction Dispatched: #" << IR << '\n');
+ notifyEvent<HWInstructionEvent>(HWInstructionDispatchedEvent(IR, UsedRegs));
+}
+
+bool DispatchStage::checkPRF(const InstRef &IR) {
+ SmallVector<unsigned, 4> RegDefs;
+ for (const std::unique_ptr<WriteState> &RegDef :
+ IR.getInstruction()->getDefs())
+ RegDefs.emplace_back(RegDef->getRegisterID());
+
+ const unsigned RegisterMask = PRF.isAvailable(RegDefs);
+ // A mask with all zeroes means: register files are available.
+ if (RegisterMask) {
+ notifyEvent<HWStallEvent>(
+ HWStallEvent(HWStallEvent::RegisterFileStall, IR));
+ return false;
+ }
+
+ return true;
+}
+
+bool DispatchStage::checkRCU(const InstRef &IR) {
+ const unsigned NumMicroOps = IR.getInstruction()->getDesc().NumMicroOps;
+ if (RCU.isAvailable(NumMicroOps))
+ return true;
+ notifyEvent<HWStallEvent>(
+ HWStallEvent(HWStallEvent::RetireControlUnitStall, IR));
+ return false;
+}
+
+bool DispatchStage::checkScheduler(const InstRef &IR) {
+ HWStallEvent::GenericEventType Event;
+ const bool Ready = SC.canBeDispatched(IR, Event);
+ if (!Ready)
+ notifyEvent<HWStallEvent>(HWStallEvent(Event, IR));
+ return Ready;
+}
+
+void DispatchStage::updateRAWDependencies(ReadState &RS,
+ const MCSubtargetInfo &STI) {
+ SmallVector<WriteRef, 4> DependentWrites;
+
+ collectWrites(DependentWrites, RS.getRegisterID());
+ RS.setDependentWrites(DependentWrites.size());
+ // We know that this read depends on all the writes in DependentWrites.
+ // For each write, check if we have ReadAdvance information, and use it
+ // to figure out in how many cycles this read becomes available.
+ const ReadDescriptor &RD = RS.getDescriptor();
+ const MCSchedModel &SM = STI.getSchedModel();
+ const MCSchedClassDesc *SC = SM.getSchedClassDesc(RD.SchedClassID);
+ for (WriteRef &WR : DependentWrites) {
+ WriteState &WS = *WR.getWriteState();
+ unsigned WriteResID = WS.getWriteResourceID();
+ int ReadAdvance = STI.getReadAdvanceCycles(SC, RD.UseIndex, WriteResID);
+ WS.addUser(&RS, ReadAdvance);
+ }
+}
+
+void DispatchStage::dispatch(InstRef IR) {
+ assert(!CarryOver && "Cannot dispatch another instruction!");
+ Instruction &IS = *IR.getInstruction();
+ const InstrDesc &Desc = IS.getDesc();
+ const unsigned NumMicroOps = Desc.NumMicroOps;
+ if (NumMicroOps > DispatchWidth) {
+ assert(AvailableEntries == DispatchWidth);
+ AvailableEntries = 0;
+ CarryOver = NumMicroOps - DispatchWidth;
+ } else {
+ assert(AvailableEntries >= NumMicroOps);
+ AvailableEntries -= NumMicroOps;
+ }
+
+ // A dependency-breaking instruction doesn't have to wait on the register
+ // input operands, and it is often optimized at register renaming stage.
+ // Update RAW dependencies if this instruction is not a dependency-breaking
+ // instruction. A dependency-breaking instruction is a zero-latency
+ // instruction that doesn't consume hardware resources.
+ // An example of dependency-breaking instruction on X86 is a zero-idiom XOR.
+ if (!Desc.isZeroLatency())
+ for (std::unique_ptr<ReadState> &RS : IS.getUses())
+ updateRAWDependencies(*RS, STI);
+
+ // By default, a dependency-breaking zero-latency instruction is expected to
+ // be optimized at register renaming stage. That means, no physical register
+ // is allocated to the instruction.
+ SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles());
+ for (std::unique_ptr<WriteState> &WS : IS.getDefs())
+ PRF.addRegisterWrite(WriteRef(IR.first, WS.get()), RegisterFiles,
+ !Desc.isZeroLatency());
+
+ // Reserve slots in the RCU, and notify the instruction that it has been
+ // dispatched to the schedulers for execution.
+ IS.dispatch(RCU.reserveSlot(IR, NumMicroOps));
+
+ // Notify listeners of the "instruction dispatched" event.
+ notifyInstructionDispatched(IR, RegisterFiles);
+}
+
+void DispatchStage::cycleStart() {
+ AvailableEntries = CarryOver >= DispatchWidth ? 0 : DispatchWidth - CarryOver;
+ CarryOver = CarryOver >= DispatchWidth ? CarryOver - DispatchWidth : 0U;
+}
+
+bool DispatchStage::execute(InstRef &IR) {
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ if (!isAvailable(Desc.NumMicroOps) || !canDispatch(IR))
+ return false;
+ dispatch(IR);
+ return true;
+}
+
+#ifndef NDEBUG
+void DispatchStage::dump() const {
+ PRF.dump();
+ RCU.dump();
+}
+#endif
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/DispatchStage.h b/contrib/llvm/tools/llvm-mca/DispatchStage.h
new file mode 100644
index 000000000000..f21789a29c50
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/DispatchStage.h
@@ -0,0 +1,106 @@
+//===----------------------- DispatchStage.h --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file models the dispatch component of an instruction pipeline.
+///
+/// The DispatchStage is responsible for updating instruction dependencies
+/// and communicating to the simulated instruction scheduler that an instruction
+/// is ready to be scheduled for execution.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_DISPATCH_STAGE_H
+#define LLVM_TOOLS_LLVM_MCA_DISPATCH_STAGE_H
+
+#include "HWEventListener.h"
+#include "Instruction.h"
+#include "RegisterFile.h"
+#include "RetireControlUnit.h"
+#include "Stage.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+
+namespace mca {
+
+class Scheduler;
+
+// Implements the hardware dispatch logic.
+//
+// This class is responsible for the dispatch stage, in which instructions are
+// dispatched in groups to the Scheduler. An instruction can be dispatched if
+// the following conditions are met:
+// 1) There are enough entries in the reorder buffer (see class
+// RetireControlUnit) to write the opcodes associated with the instruction.
+// 2) There are enough temporaries to rename output register operands.
+// 3) There are enough entries available in the used buffered resource(s).
+//
+// The number of micro opcodes that can be dispatched in one cycle is limited by
+// the value of field 'DispatchWidth'. A "dynamic dispatch stall" occurs when
+// processor resources are not available. Dispatch stall events are counted
+// during the entire execution of the code, and displayed by the performance
+// report when flag '-dispatch-stats' is specified.
+//
+// If the number of micro opcodes exceedes DispatchWidth, then the instruction
+// is dispatched in multiple cycles.
+class DispatchStage : public Stage {
+ unsigned DispatchWidth;
+ unsigned AvailableEntries;
+ unsigned CarryOver;
+ const llvm::MCSubtargetInfo &STI;
+ RetireControlUnit &RCU;
+ RegisterFile &PRF;
+ Scheduler &SC;
+
+ bool checkRCU(const InstRef &IR);
+ bool checkPRF(const InstRef &IR);
+ bool checkScheduler(const InstRef &IR);
+ void dispatch(InstRef IR);
+ void updateRAWDependencies(ReadState &RS, const llvm::MCSubtargetInfo &STI);
+
+ void notifyInstructionDispatched(const InstRef &IR,
+ llvm::ArrayRef<unsigned> UsedPhysRegs);
+
+ bool isAvailable(unsigned NumEntries) const {
+ return NumEntries <= AvailableEntries || AvailableEntries == DispatchWidth;
+ }
+
+ bool canDispatch(const InstRef &IR) {
+ assert(isAvailable(IR.getInstruction()->getDesc().NumMicroOps));
+ return checkRCU(IR) && checkPRF(IR) && checkScheduler(IR);
+ }
+
+ void collectWrites(llvm::SmallVectorImpl<WriteRef> &Vec,
+ unsigned RegID) const {
+ return PRF.collectWrites(Vec, RegID);
+ }
+
+public:
+ DispatchStage(const llvm::MCSubtargetInfo &Subtarget,
+ const llvm::MCRegisterInfo &MRI, unsigned RegisterFileSize,
+ unsigned MaxDispatchWidth, RetireControlUnit &R,
+ RegisterFile &F, Scheduler &Sched)
+ : DispatchWidth(MaxDispatchWidth), AvailableEntries(MaxDispatchWidth),
+ CarryOver(0U), STI(Subtarget), RCU(R), PRF(F), SC(Sched) {}
+
+ // We can always try to dispatch, so returning false is okay in this case.
+ // The retire stage, which controls the RCU, might have items to complete but
+ // RetireStage::hasWorkToComplete will check for that case.
+ virtual bool hasWorkToComplete() const override final { return false; }
+ virtual void cycleStart() override final;
+ virtual bool execute(InstRef &IR) override final;
+ void notifyDispatchStall(const InstRef &IR, unsigned EventType);
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+};
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_DISPATCH_STAGE_H
diff --git a/contrib/llvm/tools/llvm-mca/DispatchStatistics.cpp b/contrib/llvm/tools/llvm-mca/DispatchStatistics.cpp
new file mode 100644
index 000000000000..4bddbef9a0c8
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/DispatchStatistics.cpp
@@ -0,0 +1,71 @@
+//===--------------------- DispatchStatistics.cpp ---------------------*- C++
+//-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the DispatchStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "DispatchStatistics.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+namespace mca {
+
+void DispatchStatistics::onEvent(const HWStallEvent &Event) {
+ if (Event.Type < HWStallEvent::LastGenericEvent)
+ HWStalls[Event.Type]++;
+}
+
+void DispatchStatistics::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Dispatched)
+ ++NumDispatched;
+}
+
+void DispatchStatistics::printDispatchHistogram(llvm::raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nDispatch Logic - "
+ << "number of cycles where we saw N instructions dispatched:\n";
+ TempStream << "[# dispatched], [# cycles]\n";
+ for (const std::pair<unsigned, unsigned> &Entry : DispatchGroupSizePerCycle) {
+ TempStream << " " << Entry.first << ", " << Entry.second
+ << " ("
+ << format("%.1f", ((double)Entry.second / NumCycles) * 100.0)
+ << "%)\n";
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+void DispatchStatistics::printDispatchStalls(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nDynamic Dispatch Stall Cycles:\n";
+ TempStream << "RAT - Register unavailable: "
+ << HWStalls[HWStallEvent::RegisterFileStall];
+ TempStream << "\nRCU - Retire tokens unavailable: "
+ << HWStalls[HWStallEvent::RetireControlUnitStall];
+ TempStream << "\nSCHEDQ - Scheduler full: "
+ << HWStalls[HWStallEvent::SchedulerQueueFull];
+ TempStream << "\nLQ - Load queue full: "
+ << HWStalls[HWStallEvent::LoadQueueFull];
+ TempStream << "\nSQ - Store queue full: "
+ << HWStalls[HWStallEvent::StoreQueueFull];
+ TempStream << "\nGROUP - Static restrictions on the dispatch group: "
+ << HWStalls[HWStallEvent::DispatchGroupStall];
+ TempStream << '\n';
+ TempStream.flush();
+ OS << Buffer;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/DispatchStatistics.h b/contrib/llvm/tools/llvm-mca/DispatchStatistics.h
new file mode 100644
index 000000000000..1e389d54766b
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/DispatchStatistics.h
@@ -0,0 +1,84 @@
+//===--------------------- DispatchStatistics.h -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements a view that prints a few statistics related to the
+/// dispatch logic. It collects and analyzes instruction dispatch events as
+/// well as static/dynamic dispatch stall events.
+///
+/// Example:
+/// ========
+///
+/// Dynamic Dispatch Stall Cycles:
+/// RAT - Register unavailable: 0
+/// RCU - Retire tokens unavailable: 0
+/// SCHEDQ - Scheduler full: 42
+/// LQ - Load queue full: 0
+/// SQ - Store queue full: 0
+/// GROUP - Static restrictions on the dispatch group: 0
+///
+///
+/// Dispatch Logic - number of cycles where we saw N instructions dispatched:
+/// [# dispatched], [# cycles]
+/// 0, 15 (11.5%)
+/// 2, 4 (3.1%)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H
+
+#include "View.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include <map>
+
+namespace mca {
+
+class DispatchStatistics : public View {
+ unsigned NumDispatched;
+ unsigned NumCycles;
+
+ // Counts dispatch stall events caused by unavailability of resources. There
+ // is one counter for every generic stall kind (see class HWStallEvent).
+ llvm::SmallVector<unsigned, 8> HWStalls;
+
+ using Histogram = std::map<unsigned, unsigned>;
+ Histogram DispatchGroupSizePerCycle;
+
+ void updateHistograms() {
+ DispatchGroupSizePerCycle[NumDispatched]++;
+ NumDispatched = 0;
+ }
+
+ void printDispatchHistogram(llvm::raw_ostream &OS) const;
+
+ void printDispatchStalls(llvm::raw_ostream &OS) const;
+
+public:
+ DispatchStatistics()
+ : NumDispatched(0), NumCycles(0),
+ HWStalls(HWStallEvent::LastGenericEvent) {}
+
+ void onEvent(const HWStallEvent &Event) override;
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void onCycleBegin() override { NumCycles++; }
+
+ void onCycleEnd() override { updateHistograms(); }
+
+ void printView(llvm::raw_ostream &OS) const override {
+ printDispatchStalls(OS);
+ printDispatchHistogram(OS);
+ }
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/ExecuteStage.cpp b/contrib/llvm/tools/llvm-mca/ExecuteStage.cpp
new file mode 100644
index 000000000000..437f864b072c
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/ExecuteStage.cpp
@@ -0,0 +1,210 @@
+//===---------------------- ExecuteStage.cpp --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the execution stage of an instruction pipeline.
+///
+/// The ExecuteStage is responsible for managing the hardware scheduler
+/// and issuing notifications that an instruction has been executed.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ExecuteStage.h"
+#include "Scheduler.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Debug.h"
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+using namespace llvm;
+
+// Reclaim the simulated resources used by the scheduler.
+void ExecuteStage::reclaimSchedulerResources() {
+ SmallVector<ResourceRef, 8> ResourcesFreed;
+ HWS.reclaimSimulatedResources(ResourcesFreed);
+ for (const ResourceRef &RR : ResourcesFreed)
+ notifyResourceAvailable(RR);
+}
+
+// Update the scheduler's instruction queues.
+void ExecuteStage::updateSchedulerQueues() {
+ SmallVector<InstRef, 4> InstructionIDs;
+ HWS.updateIssuedQueue(InstructionIDs);
+ for (const InstRef &IR : InstructionIDs)
+ notifyInstructionExecuted(IR);
+ InstructionIDs.clear();
+
+ HWS.updatePendingQueue(InstructionIDs);
+ for (const InstRef &IR : InstructionIDs)
+ notifyInstructionReady(IR);
+}
+
+// Issue instructions that are waiting in the scheduler's ready queue.
+void ExecuteStage::issueReadyInstructions() {
+ SmallVector<InstRef, 4> InstructionIDs;
+ InstRef IR = HWS.select();
+ while (IR.isValid()) {
+ SmallVector<std::pair<ResourceRef, double>, 4> Used;
+ HWS.issueInstruction(IR, Used);
+
+ // Reclaim instruction resources and perform notifications.
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ notifyReleasedBuffers(Desc.Buffers);
+ notifyInstructionIssued(IR, Used);
+ if (IR.getInstruction()->isExecuted())
+ notifyInstructionExecuted(IR);
+
+ // Instructions that have been issued during this cycle might have unblocked
+ // other dependent instructions. Dependent instructions may be issued during
+ // this same cycle if operands have ReadAdvance entries. Promote those
+ // instructions to the ReadyQueue and tell to the caller that we need
+ // another round of 'issue()'.
+ HWS.promoteToReadyQueue(InstructionIDs);
+ for (const InstRef &I : InstructionIDs)
+ notifyInstructionReady(I);
+ InstructionIDs.clear();
+
+ // Select the next instruction to issue.
+ IR = HWS.select();
+ }
+}
+
+// The following routine is the maintenance routine of the ExecuteStage.
+// It is responsible for updating the hardware scheduler (HWS), including
+// reclaiming the HWS's simulated hardware resources, as well as updating the
+// HWS's queues.
+//
+// This routine also processes the instructions that are ready for issuance.
+// These instructions are managed by the HWS's ready queue and can be accessed
+// via the Scheduler::select() routine.
+//
+// Notifications are issued to this stage's listeners when instructions are
+// moved between the HWS's queues. In particular, when an instruction becomes
+// ready or executed.
+void ExecuteStage::cycleStart() {
+ reclaimSchedulerResources();
+ updateSchedulerQueues();
+ issueReadyInstructions();
+}
+
+// Schedule the instruction for execution on the hardware.
+bool ExecuteStage::execute(InstRef &IR) {
+#ifndef NDEBUG
+ // Ensure that the HWS has not stored this instruction in its queues.
+ HWS.sanityCheck(IR);
+#endif
+ // Reserve a slot in each buffered resource. Also, mark units with
+ // BufferSize=0 as reserved. Resources with a buffer size of zero will only
+ // be released after MCIS is issued, and all the ResourceCycles for those
+ // units have been consumed.
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ HWS.reserveBuffers(Desc.Buffers);
+ notifyReservedBuffers(Desc.Buffers);
+
+ // Obtain a slot in the LSU. If we cannot reserve resources, return true, so
+ // that succeeding stages can make progress.
+ if (!HWS.reserveResources(IR))
+ return true;
+
+ // If we did not return early, then the scheduler is ready for execution.
+ notifyInstructionReady(IR);
+
+ // Don't add a zero-latency instruction to the Wait or Ready queue.
+ // A zero-latency instruction doesn't consume any scheduler resources. That is
+ // because it doesn't need to be executed, and it is often removed at register
+ // renaming stage. For example, register-register moves are often optimized at
+ // register renaming stage by simply updating register aliases. On some
+ // targets, zero-idiom instructions (for example: a xor that clears the value
+ // of a register) are treated specially, and are often eliminated at register
+ // renaming stage.
+ //
+ // Instructions that use an in-order dispatch/issue processor resource must be
+ // issued immediately to the pipeline(s). Any other in-order buffered
+ // resources (i.e. BufferSize=1) is consumed.
+ //
+ // If we cannot issue immediately, the HWS will add IR to its ready queue for
+ // execution later, so we must return early here.
+ if (!HWS.issueImmediately(IR))
+ return true;
+
+ LLVM_DEBUG(dbgs() << "[SCHEDULER] Instruction #" << IR
+ << " issued immediately\n");
+
+ // Issue IR. The resources for this issuance will be placed in 'Used.'
+ SmallVector<std::pair<ResourceRef, double>, 4> Used;
+ HWS.issueInstruction(IR, Used);
+
+ // Perform notifications.
+ notifyReleasedBuffers(Desc.Buffers);
+ notifyInstructionIssued(IR, Used);
+ if (IR.getInstruction()->isExecuted())
+ notifyInstructionExecuted(IR);
+
+ return true;
+}
+
+void ExecuteStage::notifyInstructionExecuted(const InstRef &IR) {
+ HWS.onInstructionExecuted(IR);
+ LLVM_DEBUG(dbgs() << "[E] Instruction Executed: #" << IR << '\n');
+ notifyEvent<HWInstructionEvent>(
+ HWInstructionEvent(HWInstructionEvent::Executed, IR));
+ RCU.onInstructionExecuted(IR.getInstruction()->getRCUTokenID());
+}
+
+void ExecuteStage::notifyInstructionReady(const InstRef &IR) {
+ LLVM_DEBUG(dbgs() << "[E] Instruction Ready: #" << IR << '\n');
+ notifyEvent<HWInstructionEvent>(
+ HWInstructionEvent(HWInstructionEvent::Ready, IR));
+}
+
+void ExecuteStage::notifyResourceAvailable(const ResourceRef &RR) {
+ LLVM_DEBUG(dbgs() << "[E] Resource Available: [" << RR.first << '.'
+ << RR.second << "]\n");
+ for (HWEventListener *Listener : getListeners())
+ Listener->onResourceAvailable(RR);
+}
+
+void ExecuteStage::notifyInstructionIssued(
+ const InstRef &IR, ArrayRef<std::pair<ResourceRef, double>> Used) {
+ LLVM_DEBUG({
+ dbgs() << "[E] Instruction Issued: #" << IR << '\n';
+ for (const std::pair<ResourceRef, unsigned> &Resource : Used) {
+ dbgs() << "[E] Resource Used: [" << Resource.first.first << '.'
+ << Resource.first.second << "], ";
+ dbgs() << "cycles: " << Resource.second << '\n';
+ }
+ });
+ notifyEvent<HWInstructionEvent>(HWInstructionIssuedEvent(IR, Used));
+}
+
+void ExecuteStage::notifyReservedBuffers(ArrayRef<uint64_t> Buffers) {
+ if (Buffers.empty())
+ return;
+
+ SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
+ std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(),
+ [&](uint64_t Op) { return HWS.getResourceID(Op); });
+ for (HWEventListener *Listener : getListeners())
+ Listener->onReservedBuffers(BufferIDs);
+}
+
+void ExecuteStage::notifyReleasedBuffers(ArrayRef<uint64_t> Buffers) {
+ if (Buffers.empty())
+ return;
+
+ SmallVector<unsigned, 4> BufferIDs(Buffers.begin(), Buffers.end());
+ std::transform(Buffers.begin(), Buffers.end(), BufferIDs.begin(),
+ [&](uint64_t Op) { return HWS.getResourceID(Op); });
+ for (HWEventListener *Listener : getListeners())
+ Listener->onReleasedBuffers(BufferIDs);
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/ExecuteStage.h b/contrib/llvm/tools/llvm-mca/ExecuteStage.h
new file mode 100644
index 000000000000..4914a9373e7c
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/ExecuteStage.h
@@ -0,0 +1,67 @@
+//===---------------------- ExecuteStage.h ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the execution stage of an instruction pipeline.
+///
+/// The ExecuteStage is responsible for managing the hardware scheduler
+/// and issuing notifications that an instruction has been executed.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_EXECUTE_STAGE_H
+#define LLVM_TOOLS_LLVM_MCA_EXECUTE_STAGE_H
+
+#include "Instruction.h"
+#include "RetireControlUnit.h"
+#include "Scheduler.h"
+#include "Stage.h"
+#include "llvm/ADT/ArrayRef.h"
+
+namespace mca {
+
+class ExecuteStage : public Stage {
+ // Owner will go away when we move listeners/eventing to the stages.
+ RetireControlUnit &RCU;
+ Scheduler &HWS;
+
+ // The following routines are used to maintain the HWS.
+ void reclaimSchedulerResources();
+ void updateSchedulerQueues();
+ void issueReadyInstructions();
+
+public:
+ ExecuteStage(RetireControlUnit &R, Scheduler &S) : Stage(), RCU(R), HWS(S) {}
+ ExecuteStage(const ExecuteStage &Other) = delete;
+ ExecuteStage &operator=(const ExecuteStage &Other) = delete;
+
+ // The ExecuteStage will always complete all of its work per call to
+ // execute(), so it is never left in a 'to-be-processed' state.
+ virtual bool hasWorkToComplete() const override final { return false; }
+
+ virtual void cycleStart() override final;
+ virtual bool execute(InstRef &IR) override final;
+
+ void
+ notifyInstructionIssued(const InstRef &IR,
+ llvm::ArrayRef<std::pair<ResourceRef, double>> Used);
+ void notifyInstructionExecuted(const InstRef &IR);
+ void notifyInstructionReady(const InstRef &IR);
+ void notifyResourceAvailable(const ResourceRef &RR);
+
+ // Notify listeners that buffered resources were consumed.
+ void notifyReservedBuffers(llvm::ArrayRef<uint64_t> Buffers);
+
+ // Notify listeners that buffered resources were freed.
+ void notifyReleasedBuffers(llvm::ArrayRef<uint64_t> Buffers);
+};
+
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_EXECUTE_STAGE_H
diff --git a/contrib/llvm/tools/llvm-mca/FetchStage.cpp b/contrib/llvm/tools/llvm-mca/FetchStage.cpp
new file mode 100644
index 000000000000..3da117c0abc1
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/FetchStage.cpp
@@ -0,0 +1,46 @@
+//===---------------------- FetchStage.cpp ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the Fetch stage of an instruction pipeline. Its sole
+/// purpose in life is to produce instructions for the rest of the pipeline.
+///
+//===----------------------------------------------------------------------===//
+
+#include "FetchStage.h"
+
+namespace mca {
+
+bool FetchStage::hasWorkToComplete() const { return SM.hasNext(); }
+
+bool FetchStage::execute(InstRef &IR) {
+ if (!SM.hasNext())
+ return false;
+ const SourceRef SR = SM.peekNext();
+ std::unique_ptr<Instruction> I = IB.createInstruction(*SR.second);
+ IR = InstRef(SR.first, I.get());
+ Instructions[IR.getSourceIndex()] = std::move(I);
+ return true;
+}
+
+void FetchStage::postExecute() { SM.updateNext(); }
+
+void FetchStage::cycleEnd() {
+ // Find the first instruction which hasn't been retired.
+ const InstMap::iterator It =
+ llvm::find_if(Instructions, [](const InstMap::value_type &KeyValuePair) {
+ return !KeyValuePair.second->isRetired();
+ });
+
+ // Erase instructions up to the first that hasn't been retired.
+ if (It != Instructions.begin())
+ Instructions.erase(Instructions.begin(), It);
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/FetchStage.h b/contrib/llvm/tools/llvm-mca/FetchStage.h
new file mode 100644
index 000000000000..620075d24fea
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/FetchStage.h
@@ -0,0 +1,45 @@
+//===---------------------- FetchStage.h ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the Fetch stage of an instruction pipeline. Its sole
+/// purpose in life is to produce instructions for the rest of the pipeline.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_FETCH_STAGE_H
+#define LLVM_TOOLS_LLVM_MCA_FETCH_STAGE_H
+
+#include "InstrBuilder.h"
+#include "SourceMgr.h"
+#include "Stage.h"
+#include <map>
+
+namespace mca {
+
+class FetchStage : public Stage {
+ using InstMap = std::map<unsigned, std::unique_ptr<Instruction>>;
+ InstMap Instructions;
+ InstrBuilder &IB;
+ SourceMgr &SM;
+
+public:
+ FetchStage(InstrBuilder &IB, SourceMgr &SM) : IB(IB), SM(SM) {}
+ FetchStage(const FetchStage &Other) = delete;
+ FetchStage &operator=(const FetchStage &Other) = delete;
+
+ bool hasWorkToComplete() const override final;
+ bool execute(InstRef &IR) override final;
+ void postExecute() override final;
+ void cycleEnd() override final;
+};
+
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_FETCH_STAGE_H
diff --git a/contrib/llvm/tools/llvm-mca/HWEventListener.cpp b/contrib/llvm/tools/llvm-mca/HWEventListener.cpp
new file mode 100644
index 000000000000..f27a04a9a980
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/HWEventListener.cpp
@@ -0,0 +1,21 @@
+//===----------------------- HWEventListener.cpp ----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a vtable anchor for class HWEventListener.
+///
+//===----------------------------------------------------------------------===//
+
+#include "HWEventListener.h"
+
+namespace mca {
+
+// Anchor the vtable here.
+void HWEventListener::anchor() {}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/HWEventListener.h b/contrib/llvm/tools/llvm-mca/HWEventListener.h
new file mode 100644
index 000000000000..aa3e6dcf19a0
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/HWEventListener.h
@@ -0,0 +1,141 @@
+//===----------------------- HWEventListener.h ------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the main interface for hardware event listeners.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
+#define LLVM_TOOLS_LLVM_MCA_HWEVENTLISTENER_H
+
+#include "Instruction.h"
+#include "llvm/ADT/ArrayRef.h"
+#include <utility>
+
+namespace mca {
+
+// An HWInstructionEvent represents state changes of instructions that
+// listeners might be interested in. Listeners can choose to ignore any event
+// they are not interested in.
+class HWInstructionEvent {
+public:
+ // This is the list of event types that are shared by all targets, that
+ // generic subtarget-agnostic classes (e.g., Pipeline, HWInstructionEvent,
+ // ...) and generic Views can manipulate.
+ // Subtargets are free to define additional event types, that are goin to be
+ // handled by generic components as opaque values, but can still be
+ // emitted by subtarget-specific pipeline stages (e.g., ExecuteStage,
+ // DispatchStage, ...) and interpreted by subtarget-specific EventListener
+ // implementations.
+ enum GenericEventType {
+ Invalid = 0,
+ // Events generated by the Retire Control Unit.
+ Retired,
+ // Events generated by the Scheduler.
+ Ready,
+ Issued,
+ Executed,
+ // Events generated by the Dispatch logic.
+ Dispatched,
+
+ LastGenericEventType,
+ };
+
+ HWInstructionEvent(unsigned type, const InstRef &Inst)
+ : Type(type), IR(Inst) {}
+
+ // The event type. The exact meaning depends on the subtarget.
+ const unsigned Type;
+
+ // The instruction this event was generated for.
+ const InstRef &IR;
+};
+
+class HWInstructionIssuedEvent : public HWInstructionEvent {
+public:
+ using ResourceRef = std::pair<uint64_t, uint64_t>;
+ HWInstructionIssuedEvent(const InstRef &IR,
+ llvm::ArrayRef<std::pair<ResourceRef, double>> UR)
+ : HWInstructionEvent(HWInstructionEvent::Issued, IR), UsedResources(UR) {}
+
+ llvm::ArrayRef<std::pair<ResourceRef, double>> UsedResources;
+};
+
+class HWInstructionDispatchedEvent : public HWInstructionEvent {
+public:
+ HWInstructionDispatchedEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
+ : HWInstructionEvent(HWInstructionEvent::Dispatched, IR),
+ UsedPhysRegs(Regs) {}
+ // Number of physical register allocated for this instruction. There is one
+ // entry per register file.
+ llvm::ArrayRef<unsigned> UsedPhysRegs;
+};
+
+class HWInstructionRetiredEvent : public HWInstructionEvent {
+public:
+ HWInstructionRetiredEvent(const InstRef &IR, llvm::ArrayRef<unsigned> Regs)
+ : HWInstructionEvent(HWInstructionEvent::Retired, IR),
+ FreedPhysRegs(Regs) {}
+ // Number of register writes that have been architecturally committed. There
+ // is one entry per register file.
+ llvm::ArrayRef<unsigned> FreedPhysRegs;
+};
+
+// A HWStallEvent represents a pipeline stall caused by the lack of hardware
+// resources.
+class HWStallEvent {
+public:
+ enum GenericEventType {
+ Invalid = 0,
+ // Generic stall events generated by the DispatchStage.
+ RegisterFileStall,
+ RetireControlUnitStall,
+ // Generic stall events generated by the Scheduler.
+ DispatchGroupStall,
+ SchedulerQueueFull,
+ LoadQueueFull,
+ StoreQueueFull,
+ LastGenericEvent
+ };
+
+ HWStallEvent(unsigned type, const InstRef &Inst) : Type(type), IR(Inst) {}
+
+ // The exact meaning of the stall event type depends on the subtarget.
+ const unsigned Type;
+
+ // The instruction this event was generated for.
+ const InstRef &IR;
+};
+
+class HWEventListener {
+public:
+ // Generic events generated by the pipeline.
+ virtual void onCycleBegin() {}
+ virtual void onCycleEnd() {}
+
+ virtual void onEvent(const HWInstructionEvent &Event) {}
+ virtual void onEvent(const HWStallEvent &Event) {}
+
+ using ResourceRef = std::pair<uint64_t, uint64_t>;
+ virtual void onResourceAvailable(const ResourceRef &RRef) {}
+
+ // Events generated by the Scheduler when buffered resources are
+ // consumed/freed.
+ virtual void onReservedBuffers(llvm::ArrayRef<unsigned> Buffers) {}
+ virtual void onReleasedBuffers(llvm::ArrayRef<unsigned> Buffers) {}
+
+ virtual ~HWEventListener() {}
+
+private:
+ virtual void anchor();
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/HardwareUnit.cpp b/contrib/llvm/tools/llvm-mca/HardwareUnit.cpp
new file mode 100644
index 000000000000..103cde9afcc8
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/HardwareUnit.cpp
@@ -0,0 +1,23 @@
+//===------------------------- HardwareUnit.cpp -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the anchor for the base class that describes
+/// simulated hardware units.
+///
+//===----------------------------------------------------------------------===//
+
+#include "HardwareUnit.h"
+
+namespace mca {
+
+// Pin the vtable with this method.
+HardwareUnit::~HardwareUnit() = default;
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/HardwareUnit.h b/contrib/llvm/tools/llvm-mca/HardwareUnit.h
new file mode 100644
index 000000000000..e8c496ab967a
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/HardwareUnit.h
@@ -0,0 +1,31 @@
+//===-------------------------- HardwareUnit.h ------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a base class for describing a simulated hardware
+/// unit. These units are used to construct a simulated backend.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_HARDWAREUNIT_H
+#define LLVM_TOOLS_LLVM_MCA_HARDWAREUNIT_H
+
+namespace mca {
+
+class HardwareUnit {
+ HardwareUnit(const HardwareUnit &H) = delete;
+ HardwareUnit &operator=(const HardwareUnit &H) = delete;
+
+public:
+ HardwareUnit() = default;
+ virtual ~HardwareUnit();
+};
+
+} // namespace mca
+#endif // LLVM_TOOLS_LLVM_MCA_HARDWAREUNIT_H
diff --git a/contrib/llvm/tools/llvm-mca/InstrBuilder.cpp b/contrib/llvm/tools/llvm-mca/InstrBuilder.cpp
new file mode 100644
index 000000000000..dbd457196f9d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/InstrBuilder.cpp
@@ -0,0 +1,465 @@
+//===--------------------- InstrBuilder.cpp ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the InstrBuilder interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "InstrBuilder.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCInst.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/WithColor.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+using namespace llvm;
+
+static void initializeUsedResources(InstrDesc &ID,
+ const MCSchedClassDesc &SCDesc,
+ const MCSubtargetInfo &STI,
+ ArrayRef<uint64_t> ProcResourceMasks) {
+ const MCSchedModel &SM = STI.getSchedModel();
+
+ // Populate resources consumed.
+ using ResourcePlusCycles = std::pair<uint64_t, ResourceUsage>;
+ std::vector<ResourcePlusCycles> Worklist;
+
+ // Track cycles contributed by resources that are in a "Super" relationship.
+ // This is required if we want to correctly match the behavior of method
+ // SubtargetEmitter::ExpandProcResource() in Tablegen. When computing the set
+ // of "consumed" processor resources and resource cycles, the logic in
+ // ExpandProcResource() doesn't update the number of resource cycles
+ // contributed by a "Super" resource to a group.
+ // We need to take this into account when we find that a processor resource is
+ // part of a group, and it is also used as the "Super" of other resources.
+ // This map stores the number of cycles contributed by sub-resources that are
+ // part of a "Super" resource. The key value is the "Super" resource mask ID.
+ DenseMap<uint64_t, unsigned> SuperResources;
+
+ for (unsigned I = 0, E = SCDesc.NumWriteProcResEntries; I < E; ++I) {
+ const MCWriteProcResEntry *PRE = STI.getWriteProcResBegin(&SCDesc) + I;
+ const MCProcResourceDesc &PR = *SM.getProcResource(PRE->ProcResourceIdx);
+ uint64_t Mask = ProcResourceMasks[PRE->ProcResourceIdx];
+ if (PR.BufferSize != -1)
+ ID.Buffers.push_back(Mask);
+ CycleSegment RCy(0, PRE->Cycles, false);
+ Worklist.emplace_back(ResourcePlusCycles(Mask, ResourceUsage(RCy)));
+ if (PR.SuperIdx) {
+ uint64_t Super = ProcResourceMasks[PR.SuperIdx];
+ SuperResources[Super] += PRE->Cycles;
+ }
+ }
+
+ // Sort elements by mask popcount, so that we prioritize resource units over
+ // resource groups, and smaller groups over larger groups.
+ llvm::sort(Worklist.begin(), Worklist.end(),
+ [](const ResourcePlusCycles &A, const ResourcePlusCycles &B) {
+ unsigned popcntA = countPopulation(A.first);
+ unsigned popcntB = countPopulation(B.first);
+ if (popcntA < popcntB)
+ return true;
+ if (popcntA > popcntB)
+ return false;
+ return A.first < B.first;
+ });
+
+ uint64_t UsedResourceUnits = 0;
+
+ // Remove cycles contributed by smaller resources.
+ for (unsigned I = 0, E = Worklist.size(); I < E; ++I) {
+ ResourcePlusCycles &A = Worklist[I];
+ if (!A.second.size()) {
+ A.second.NumUnits = 0;
+ A.second.setReserved();
+ ID.Resources.emplace_back(A);
+ continue;
+ }
+
+ ID.Resources.emplace_back(A);
+ uint64_t NormalizedMask = A.first;
+ if (countPopulation(A.first) == 1) {
+ UsedResourceUnits |= A.first;
+ } else {
+ // Remove the leading 1 from the resource group mask.
+ NormalizedMask ^= PowerOf2Floor(NormalizedMask);
+ }
+
+ for (unsigned J = I + 1; J < E; ++J) {
+ ResourcePlusCycles &B = Worklist[J];
+ if ((NormalizedMask & B.first) == NormalizedMask) {
+ B.second.CS.Subtract(A.second.size() - SuperResources[A.first]);
+ if (countPopulation(B.first) > 1)
+ B.second.NumUnits++;
+ }
+ }
+ }
+
+ // A SchedWrite may specify a number of cycles in which a resource group
+ // is reserved. For example (on target x86; cpu Haswell):
+ //
+ // SchedWriteRes<[HWPort0, HWPort1, HWPort01]> {
+ // let ResourceCycles = [2, 2, 3];
+ // }
+ //
+ // This means:
+ // Resource units HWPort0 and HWPort1 are both used for 2cy.
+ // Resource group HWPort01 is the union of HWPort0 and HWPort1.
+ // Since this write touches both HWPort0 and HWPort1 for 2cy, HWPort01
+ // will not be usable for 2 entire cycles from instruction issue.
+ //
+ // On top of those 2cy, SchedWriteRes explicitly specifies an extra latency
+ // of 3 cycles for HWPort01. This tool assumes that the 3cy latency is an
+ // extra delay on top of the 2 cycles latency.
+ // During those extra cycles, HWPort01 is not usable by other instructions.
+ for (ResourcePlusCycles &RPC : ID.Resources) {
+ if (countPopulation(RPC.first) > 1 && !RPC.second.isReserved()) {
+ // Remove the leading 1 from the resource group mask.
+ uint64_t Mask = RPC.first ^ PowerOf2Floor(RPC.first);
+ if ((Mask & UsedResourceUnits) == Mask)
+ RPC.second.setReserved();
+ }
+ }
+
+ LLVM_DEBUG({
+ for (const std::pair<uint64_t, ResourceUsage> &R : ID.Resources)
+ dbgs() << "\t\tMask=" << R.first << ", cy=" << R.second.size() << '\n';
+ for (const uint64_t R : ID.Buffers)
+ dbgs() << "\t\tBuffer Mask=" << R << '\n';
+ });
+}
+
+static void computeMaxLatency(InstrDesc &ID, const MCInstrDesc &MCDesc,
+ const MCSchedClassDesc &SCDesc,
+ const MCSubtargetInfo &STI) {
+ if (MCDesc.isCall()) {
+ // We cannot estimate how long this call will take.
+ // Artificially set an arbitrarily high latency (100cy).
+ ID.MaxLatency = 100U;
+ return;
+ }
+
+ int Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
+ // If latency is unknown, then conservatively assume a MaxLatency of 100cy.
+ ID.MaxLatency = Latency < 0 ? 100U : static_cast<unsigned>(Latency);
+}
+
+void InstrBuilder::populateWrites(InstrDesc &ID, const MCInst &MCI,
+ unsigned SchedClassID) {
+ const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
+ const MCSchedModel &SM = STI.getSchedModel();
+ const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
+
+ // These are for now the (strong) assumptions made by this algorithm:
+ // * The number of explicit and implicit register definitions in a MCInst
+ // matches the number of explicit and implicit definitions according to
+ // the opcode descriptor (MCInstrDesc).
+ // * Register definitions take precedence over register uses in the operands
+ // list.
+ // * If an opcode specifies an optional definition, then the optional
+ // definition is always the last operand in the sequence, and it can be
+ // set to zero (i.e. "no register").
+ //
+ // These assumptions work quite well for most out-of-order in-tree targets
+ // like x86. This is mainly because the vast majority of instructions is
+ // expanded to MCInst using a straightforward lowering logic that preserves
+ // the ordering of the operands.
+ unsigned NumExplicitDefs = MCDesc.getNumDefs();
+ unsigned NumImplicitDefs = MCDesc.getNumImplicitDefs();
+ unsigned NumWriteLatencyEntries = SCDesc.NumWriteLatencyEntries;
+ unsigned TotalDefs = NumExplicitDefs + NumImplicitDefs;
+ if (MCDesc.hasOptionalDef())
+ TotalDefs++;
+ ID.Writes.resize(TotalDefs);
+ // Iterate over the operands list, and skip non-register operands.
+ // The first NumExplictDefs register operands are expected to be register
+ // definitions.
+ unsigned CurrentDef = 0;
+ unsigned i = 0;
+ for (; i < MCI.getNumOperands() && CurrentDef < NumExplicitDefs; ++i) {
+ const MCOperand &Op = MCI.getOperand(i);
+ if (!Op.isReg())
+ continue;
+
+ WriteDescriptor &Write = ID.Writes[CurrentDef];
+ Write.OpIndex = i;
+ if (CurrentDef < NumWriteLatencyEntries) {
+ const MCWriteLatencyEntry &WLE =
+ *STI.getWriteLatencyEntry(&SCDesc, CurrentDef);
+ // Conservatively default to MaxLatency.
+ Write.Latency =
+ WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
+ Write.SClassOrWriteResourceID = WLE.WriteResourceID;
+ } else {
+ // Assign a default latency for this write.
+ Write.Latency = ID.MaxLatency;
+ Write.SClassOrWriteResourceID = 0;
+ }
+ Write.IsOptionalDef = false;
+ LLVM_DEBUG({
+ dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
+ << ", Latency=" << Write.Latency
+ << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
+ });
+ CurrentDef++;
+ }
+
+ if (CurrentDef != NumExplicitDefs)
+ llvm::report_fatal_error(
+ "error: Expected more register operand definitions. ");
+
+ CurrentDef = 0;
+ for (CurrentDef = 0; CurrentDef < NumImplicitDefs; ++CurrentDef) {
+ unsigned Index = NumExplicitDefs + CurrentDef;
+ WriteDescriptor &Write = ID.Writes[Index];
+ Write.OpIndex = ~CurrentDef;
+ Write.RegisterID = MCDesc.getImplicitDefs()[CurrentDef];
+ if (Index < NumWriteLatencyEntries) {
+ const MCWriteLatencyEntry &WLE =
+ *STI.getWriteLatencyEntry(&SCDesc, Index);
+ // Conservatively default to MaxLatency.
+ Write.Latency =
+ WLE.Cycles < 0 ? ID.MaxLatency : static_cast<unsigned>(WLE.Cycles);
+ Write.SClassOrWriteResourceID = WLE.WriteResourceID;
+ } else {
+ // Assign a default latency for this write.
+ Write.Latency = ID.MaxLatency;
+ Write.SClassOrWriteResourceID = 0;
+ }
+
+ Write.IsOptionalDef = false;
+ assert(Write.RegisterID != 0 && "Expected a valid phys register!");
+ LLVM_DEBUG({
+ dbgs() << "\t\t[Def] OpIdx=" << Write.OpIndex
+ << ", PhysReg=" << MRI.getName(Write.RegisterID)
+ << ", Latency=" << Write.Latency
+ << ", WriteResourceID=" << Write.SClassOrWriteResourceID << '\n';
+ });
+ }
+
+ if (MCDesc.hasOptionalDef()) {
+ // Always assume that the optional definition is the last operand of the
+ // MCInst sequence.
+ const MCOperand &Op = MCI.getOperand(MCI.getNumOperands() - 1);
+ if (i == MCI.getNumOperands() || !Op.isReg())
+ llvm::report_fatal_error(
+ "error: expected a register operand for an optional "
+ "definition. Instruction has not be correctly analyzed.\n",
+ false);
+
+ WriteDescriptor &Write = ID.Writes[TotalDefs - 1];
+ Write.OpIndex = MCI.getNumOperands() - 1;
+ // Assign a default latency for this write.
+ Write.Latency = ID.MaxLatency;
+ Write.SClassOrWriteResourceID = 0;
+ Write.IsOptionalDef = true;
+ }
+}
+
+void InstrBuilder::populateReads(InstrDesc &ID, const MCInst &MCI,
+ unsigned SchedClassID) {
+ const MCInstrDesc &MCDesc = MCII.get(MCI.getOpcode());
+ unsigned NumExplicitDefs = MCDesc.getNumDefs();
+
+ // Skip explicit definitions.
+ unsigned i = 0;
+ for (; i < MCI.getNumOperands() && NumExplicitDefs; ++i) {
+ const MCOperand &Op = MCI.getOperand(i);
+ if (Op.isReg())
+ NumExplicitDefs--;
+ }
+
+ if (NumExplicitDefs)
+ llvm::report_fatal_error(
+ "error: Expected more register operand definitions. ", false);
+
+ unsigned NumExplicitUses = MCI.getNumOperands() - i;
+ unsigned NumImplicitUses = MCDesc.getNumImplicitUses();
+ if (MCDesc.hasOptionalDef()) {
+ assert(NumExplicitUses);
+ NumExplicitUses--;
+ }
+ unsigned TotalUses = NumExplicitUses + NumImplicitUses;
+ if (!TotalUses)
+ return;
+
+ ID.Reads.resize(TotalUses);
+ for (unsigned CurrentUse = 0; CurrentUse < NumExplicitUses; ++CurrentUse) {
+ ReadDescriptor &Read = ID.Reads[CurrentUse];
+ Read.OpIndex = i + CurrentUse;
+ Read.UseIndex = CurrentUse;
+ Read.SchedClassID = SchedClassID;
+ LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex
+ << ", UseIndex=" << Read.UseIndex << '\n');
+ }
+
+ for (unsigned CurrentUse = 0; CurrentUse < NumImplicitUses; ++CurrentUse) {
+ ReadDescriptor &Read = ID.Reads[NumExplicitUses + CurrentUse];
+ Read.OpIndex = ~CurrentUse;
+ Read.UseIndex = NumExplicitUses + CurrentUse;
+ Read.RegisterID = MCDesc.getImplicitUses()[CurrentUse];
+ Read.SchedClassID = SchedClassID;
+ LLVM_DEBUG(dbgs() << "\t\t[Use] OpIdx=" << Read.OpIndex << ", RegisterID="
+ << MRI.getName(Read.RegisterID) << '\n');
+ }
+}
+
+const InstrDesc &InstrBuilder::createInstrDescImpl(const MCInst &MCI) {
+ assert(STI.getSchedModel().hasInstrSchedModel() &&
+ "Itineraries are not yet supported!");
+
+ // Obtain the instruction descriptor from the opcode.
+ unsigned short Opcode = MCI.getOpcode();
+ const MCInstrDesc &MCDesc = MCII.get(Opcode);
+ const MCSchedModel &SM = STI.getSchedModel();
+
+ // Then obtain the scheduling class information from the instruction.
+ unsigned SchedClassID = MCDesc.getSchedClass();
+ unsigned CPUID = SM.getProcessorID();
+
+ // Try to solve variant scheduling classes.
+ if (SchedClassID) {
+ while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
+ SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &MCI, CPUID);
+
+ if (!SchedClassID)
+ llvm::report_fatal_error("unable to resolve this variant class.");
+ }
+
+ // Check if this instruction is supported. Otherwise, report a fatal error.
+ const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
+ if (SCDesc.NumMicroOps == MCSchedClassDesc::InvalidNumMicroOps) {
+ std::string ToString;
+ llvm::raw_string_ostream OS(ToString);
+ WithColor::error() << "found an unsupported instruction in the input"
+ << " assembly sequence.\n";
+ MCIP.printInst(&MCI, OS, "", STI);
+ OS.flush();
+
+ WithColor::note() << "instruction: " << ToString << '\n';
+ llvm::report_fatal_error(
+ "Don't know how to analyze unsupported instructions.");
+ }
+
+ // Create a new empty descriptor.
+ std::unique_ptr<InstrDesc> ID = llvm::make_unique<InstrDesc>();
+ ID->NumMicroOps = SCDesc.NumMicroOps;
+
+ if (MCDesc.isCall()) {
+ // We don't correctly model calls.
+ WithColor::warning() << "found a call in the input assembly sequence.\n";
+ WithColor::note() << "call instructions are not correctly modeled. "
+ << "Assume a latency of 100cy.\n";
+ }
+
+ if (MCDesc.isReturn()) {
+ WithColor::warning() << "found a return instruction in the input"
+ << " assembly sequence.\n";
+ WithColor::note() << "program counter updates are ignored.\n";
+ }
+
+ ID->MayLoad = MCDesc.mayLoad();
+ ID->MayStore = MCDesc.mayStore();
+ ID->HasSideEffects = MCDesc.hasUnmodeledSideEffects();
+
+ initializeUsedResources(*ID, SCDesc, STI, ProcResourceMasks);
+ computeMaxLatency(*ID, MCDesc, SCDesc, STI);
+ populateWrites(*ID, MCI, SchedClassID);
+ populateReads(*ID, MCI, SchedClassID);
+
+ LLVM_DEBUG(dbgs() << "\t\tMaxLatency=" << ID->MaxLatency << '\n');
+ LLVM_DEBUG(dbgs() << "\t\tNumMicroOps=" << ID->NumMicroOps << '\n');
+
+ // Now add the new descriptor.
+ SchedClassID = MCDesc.getSchedClass();
+ if (!SM.getSchedClassDesc(SchedClassID)->isVariant()) {
+ Descriptors[MCI.getOpcode()] = std::move(ID);
+ return *Descriptors[MCI.getOpcode()];
+ }
+
+ VariantDescriptors[&MCI] = std::move(ID);
+ return *VariantDescriptors[&MCI];
+}
+
+const InstrDesc &InstrBuilder::getOrCreateInstrDesc(const MCInst &MCI) {
+ if (Descriptors.find_as(MCI.getOpcode()) != Descriptors.end())
+ return *Descriptors[MCI.getOpcode()];
+
+ if (VariantDescriptors.find(&MCI) != VariantDescriptors.end())
+ return *VariantDescriptors[&MCI];
+
+ return createInstrDescImpl(MCI);
+}
+
+std::unique_ptr<Instruction>
+InstrBuilder::createInstruction(const MCInst &MCI) {
+ const InstrDesc &D = getOrCreateInstrDesc(MCI);
+ std::unique_ptr<Instruction> NewIS = llvm::make_unique<Instruction>(D);
+
+ // Initialize Reads first.
+ for (const ReadDescriptor &RD : D.Reads) {
+ int RegID = -1;
+ if (!RD.isImplicitRead()) {
+ // explicit read.
+ const MCOperand &Op = MCI.getOperand(RD.OpIndex);
+ // Skip non-register operands.
+ if (!Op.isReg())
+ continue;
+ RegID = Op.getReg();
+ } else {
+ // Implicit read.
+ RegID = RD.RegisterID;
+ }
+
+ // Skip invalid register operands.
+ if (!RegID)
+ continue;
+
+ // Okay, this is a register operand. Create a ReadState for it.
+ assert(RegID > 0 && "Invalid register ID found!");
+ NewIS->getUses().emplace_back(llvm::make_unique<ReadState>(RD, RegID));
+ }
+
+ // Early exit if there are no writes.
+ if (D.Writes.empty())
+ return NewIS;
+
+ // Track register writes that implicitly clear the upper portion of the
+ // underlying super-registers using an APInt.
+ APInt WriteMask(D.Writes.size(), 0);
+
+ // Now query the MCInstrAnalysis object to obtain information about which
+ // register writes implicitly clear the upper portion of a super-register.
+ MCIA.clearsSuperRegisters(MRI, MCI, WriteMask);
+
+ // Initialize writes.
+ unsigned WriteIndex = 0;
+ for (const WriteDescriptor &WD : D.Writes) {
+ unsigned RegID = WD.isImplicitWrite() ? WD.RegisterID
+ : MCI.getOperand(WD.OpIndex).getReg();
+ // Check if this is a optional definition that references NoReg.
+ if (WD.IsOptionalDef && !RegID) {
+ ++WriteIndex;
+ continue;
+ }
+
+ assert(RegID && "Expected a valid register ID!");
+ NewIS->getDefs().emplace_back(llvm::make_unique<WriteState>(
+ WD, RegID, /* ClearsSuperRegs */ WriteMask[WriteIndex]));
+ ++WriteIndex;
+ }
+
+ return NewIS;
+}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/InstrBuilder.h b/contrib/llvm/tools/llvm-mca/InstrBuilder.h
new file mode 100644
index 000000000000..69a53b6fec21
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/InstrBuilder.h
@@ -0,0 +1,85 @@
+//===--------------------- InstrBuilder.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// A builder class for instructions that are statically analyzed by llvm-mca.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRBUILDER_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRBUILDER_H
+
+#include "Instruction.h"
+#include "Support.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrAnalysis.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+
+namespace mca {
+
+class DispatchUnit;
+
+/// A builder class that knows how to construct Instruction objects.
+///
+/// Every llvm-mca Instruction is described by an object of class InstrDesc.
+/// An InstrDesc describes which registers are read/written by the instruction,
+/// as well as the instruction latency and hardware resources consumed.
+///
+/// This class is used by the tool to construct Instructions and instruction
+/// descriptors (i.e. InstrDesc objects).
+/// Information from the machine scheduling model is used to identify processor
+/// resources that are consumed by an instruction.
+class InstrBuilder {
+ const llvm::MCSubtargetInfo &STI;
+ const llvm::MCInstrInfo &MCII;
+ const llvm::MCRegisterInfo &MRI;
+ const llvm::MCInstrAnalysis &MCIA;
+ llvm::MCInstPrinter &MCIP;
+ llvm::SmallVector<uint64_t, 8> ProcResourceMasks;
+
+ llvm::DenseMap<unsigned short, std::unique_ptr<const InstrDesc>> Descriptors;
+ llvm::DenseMap<const llvm::MCInst *, std::unique_ptr<const InstrDesc>>
+ VariantDescriptors;
+
+ const InstrDesc &createInstrDescImpl(const llvm::MCInst &MCI);
+ InstrBuilder(const InstrBuilder &) = delete;
+ InstrBuilder &operator=(const InstrBuilder &) = delete;
+
+ void populateWrites(InstrDesc &ID, const llvm::MCInst &MCI,
+ unsigned SchedClassID);
+ void populateReads(InstrDesc &ID, const llvm::MCInst &MCI,
+ unsigned SchedClassID);
+
+public:
+ InstrBuilder(const llvm::MCSubtargetInfo &sti, const llvm::MCInstrInfo &mcii,
+ const llvm::MCRegisterInfo &mri,
+ const llvm::MCInstrAnalysis &mcia, llvm::MCInstPrinter &mcip)
+ : STI(sti), MCII(mcii), MRI(mri), MCIA(mcia), MCIP(mcip),
+ ProcResourceMasks(STI.getSchedModel().getNumProcResourceKinds()) {
+ computeProcResourceMasks(STI.getSchedModel(), ProcResourceMasks);
+ }
+
+ const InstrDesc &getOrCreateInstrDesc(const llvm::MCInst &MCI);
+ // Returns an array of processor resource masks.
+ // Masks are computed by function mca::computeProcResourceMasks. see
+ // Support.h for a description of how masks are computed and how masks can be
+ // used to solve set membership problems.
+ llvm::ArrayRef<uint64_t> getProcResourceMasks() const {
+ return ProcResourceMasks;
+ }
+
+ void clear() { VariantDescriptors.shrink_and_clear(); }
+
+ std::unique_ptr<Instruction> createInstruction(const llvm::MCInst &MCI);
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/Instruction.cpp b/contrib/llvm/tools/llvm-mca/Instruction.cpp
new file mode 100644
index 000000000000..0c8476705572
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Instruction.cpp
@@ -0,0 +1,177 @@
+//===--------------------- Instruction.cpp ----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines abstractions used by the Pipeline to model register reads,
+// register writes and instructions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Instruction.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace mca {
+
+using namespace llvm;
+
+void ReadState::writeStartEvent(unsigned Cycles) {
+ assert(DependentWrites);
+ assert(CyclesLeft == UNKNOWN_CYCLES);
+
+ // This read may be dependent on more than one write. This typically occurs
+ // when a definition is the result of multiple writes where at least one
+ // write does a partial register update.
+ // The HW is forced to do some extra bookkeeping to track of all the
+ // dependent writes, and implement a merging scheme for the partial writes.
+ --DependentWrites;
+ TotalCycles = std::max(TotalCycles, Cycles);
+
+ if (!DependentWrites) {
+ CyclesLeft = TotalCycles;
+ IsReady = !CyclesLeft;
+ }
+}
+
+void WriteState::onInstructionIssued() {
+ assert(CyclesLeft == UNKNOWN_CYCLES);
+ // Update the number of cycles left based on the WriteDescriptor info.
+ CyclesLeft = getLatency();
+
+ // Now that the time left before write-back is known, notify
+ // all the users.
+ for (const std::pair<ReadState *, int> &User : Users) {
+ ReadState *RS = User.first;
+ unsigned ReadCycles = std::max(0, CyclesLeft - User.second);
+ RS->writeStartEvent(ReadCycles);
+ }
+}
+
+void WriteState::addUser(ReadState *User, int ReadAdvance) {
+ // If CyclesLeft is different than -1, then we don't need to
+ // update the list of users. We can just notify the user with
+ // the actual number of cycles left (which may be zero).
+ if (CyclesLeft != UNKNOWN_CYCLES) {
+ unsigned ReadCycles = std::max(0, CyclesLeft - ReadAdvance);
+ User->writeStartEvent(ReadCycles);
+ return;
+ }
+
+ std::pair<ReadState *, int> NewPair(User, ReadAdvance);
+ Users.insert(NewPair);
+}
+
+void WriteState::cycleEvent() {
+ // Note: CyclesLeft can be a negative number. It is an error to
+ // make it an unsigned quantity because users of this write may
+ // specify a negative ReadAdvance.
+ if (CyclesLeft != UNKNOWN_CYCLES)
+ CyclesLeft--;
+}
+
+void ReadState::cycleEvent() {
+ // Update the total number of cycles.
+ if (DependentWrites && TotalCycles) {
+ --TotalCycles;
+ return;
+ }
+
+ // Bail out immediately if we don't know how many cycles are left.
+ if (CyclesLeft == UNKNOWN_CYCLES)
+ return;
+
+ if (CyclesLeft) {
+ --CyclesLeft;
+ IsReady = !CyclesLeft;
+ }
+}
+
+#ifndef NDEBUG
+void WriteState::dump() const {
+ dbgs() << "{ OpIdx=" << WD.OpIndex << ", Lat=" << getLatency() << ", RegID "
+ << getRegisterID() << ", Cycles Left=" << getCyclesLeft() << " }";
+}
+
+void WriteRef::dump() const {
+ dbgs() << "IID=" << getSourceIndex() << ' ';
+ if (isValid())
+ getWriteState()->dump();
+ else
+ dbgs() << "(null)";
+}
+#endif
+
+void Instruction::dispatch(unsigned RCUToken) {
+ assert(Stage == IS_INVALID);
+ Stage = IS_AVAILABLE;
+ RCUTokenID = RCUToken;
+
+ // Check if input operands are already available.
+ update();
+}
+
+void Instruction::execute() {
+ assert(Stage == IS_READY);
+ Stage = IS_EXECUTING;
+
+ // Set the cycles left before the write-back stage.
+ CyclesLeft = Desc.MaxLatency;
+
+ for (UniqueDef &Def : Defs)
+ Def->onInstructionIssued();
+
+ // Transition to the "executed" stage if this is a zero-latency instruction.
+ if (!CyclesLeft)
+ Stage = IS_EXECUTED;
+}
+
+void Instruction::update() {
+ assert(isDispatched() && "Unexpected instruction stage found!");
+
+ if (!llvm::all_of(Uses, [](const UniqueUse &Use) { return Use->isReady(); }))
+ return;
+
+ // A partial register write cannot complete before a dependent write.
+ auto IsDefReady = [&](const UniqueDef &Def) {
+ if (const WriteState *Write = Def->getDependentWrite()) {
+ int WriteLatency = Write->getCyclesLeft();
+ if (WriteLatency == UNKNOWN_CYCLES)
+ return false;
+ return static_cast<unsigned>(WriteLatency) < Desc.MaxLatency;
+ }
+ return true;
+ };
+
+ if (llvm::all_of(Defs, IsDefReady))
+ Stage = IS_READY;
+}
+
+void Instruction::cycleEvent() {
+ if (isReady())
+ return;
+
+ if (isDispatched()) {
+ for (UniqueUse &Use : Uses)
+ Use->cycleEvent();
+
+ update();
+ return;
+ }
+
+ assert(isExecuting() && "Instruction not in-flight?");
+ assert(CyclesLeft && "Instruction already executed?");
+ for (UniqueDef &Def : Defs)
+ Def->cycleEvent();
+ CyclesLeft--;
+ if (!CyclesLeft)
+ Stage = IS_EXECUTED;
+}
+
+const unsigned WriteRef::INVALID_IID = std::numeric_limits<unsigned>::max();
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/Instruction.h b/contrib/llvm/tools/llvm-mca/Instruction.h
new file mode 100644
index 000000000000..ddf5c3a5e33f
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Instruction.h
@@ -0,0 +1,427 @@
+//===--------------------- Instruction.h ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines abstractions used by the Pipeline to model register reads,
+/// register writes and instructions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRUCTION_H
+
+#include "llvm/Support/MathExtras.h"
+
+#ifndef NDEBUG
+#include "llvm/Support/raw_ostream.h"
+#endif
+
+#include <memory>
+#include <set>
+#include <vector>
+
+namespace mca {
+
+constexpr int UNKNOWN_CYCLES = -512;
+
+/// A register write descriptor.
+struct WriteDescriptor {
+ // Operand index. The index is negative for implicit writes only.
+ // For implicit writes, the actual operand index is computed performing
+ // a bitwise not of the OpIndex.
+ int OpIndex;
+ // Write latency. Number of cycles before write-back stage.
+ unsigned Latency;
+ // This field is set to a value different than zero only if this
+ // is an implicit definition.
+ unsigned RegisterID;
+ // Instruction itineraries would set this field to the SchedClass ID.
+ // Otherwise, it defaults to the WriteResourceID from the MCWriteLatencyEntry
+ // element associated to this write.
+ // When computing read latencies, this value is matched against the
+ // "ReadAdvance" information. The hardware backend may implement
+ // dedicated forwarding paths to quickly propagate write results to dependent
+ // instructions waiting in the reservation station (effectively bypassing the
+ // write-back stage).
+ unsigned SClassOrWriteResourceID;
+ // True only if this is a write obtained from an optional definition.
+ // Optional definitions are allowed to reference regID zero (i.e. "no
+ // register").
+ bool IsOptionalDef;
+
+ bool isImplicitWrite() const { return OpIndex < 0; };
+};
+
+/// A register read descriptor.
+struct ReadDescriptor {
+ // A MCOperand index. This is used by the Dispatch logic to identify register
+ // reads. Implicit reads have negative indices. The actual operand index of an
+ // implicit read is the bitwise not of field OpIndex.
+ int OpIndex;
+ // The actual "UseIdx". This is used to query the ReadAdvance table. Explicit
+ // uses always come first in the sequence of uses.
+ unsigned UseIndex;
+ // This field is only set if this is an implicit read.
+ unsigned RegisterID;
+ // Scheduling Class Index. It is used to query the scheduling model for the
+ // MCSchedClassDesc object.
+ unsigned SchedClassID;
+
+ bool isImplicitRead() const { return OpIndex < 0; };
+};
+
+class ReadState;
+
+/// Tracks uses of a register definition (e.g. register write).
+///
+/// Each implicit/explicit register write is associated with an instance of
+/// this class. A WriteState object tracks the dependent users of a
+/// register write. It also tracks how many cycles are left before the write
+/// back stage.
+class WriteState {
+ const WriteDescriptor &WD;
+ // On instruction issue, this field is set equal to the write latency.
+ // Before instruction issue, this field defaults to -512, a special
+ // value that represents an "unknown" number of cycles.
+ int CyclesLeft;
+
+ // Actual register defined by this write. This field is only used
+ // to speedup queries on the register file.
+ // For implicit writes, this field always matches the value of
+ // field RegisterID from WD.
+ unsigned RegisterID;
+
+ // True if this write implicitly clears the upper portion of RegisterID's
+ // super-registers.
+ bool ClearsSuperRegs;
+
+ // This field is set if this is a partial register write, and it has a false
+ // dependency on any previous write of the same register (or a portion of it).
+ // DependentWrite must be able to complete before this write completes, so
+ // that we don't break the WAW, and the two writes can be merged together.
+ const WriteState *DependentWrite;
+
+ // A list of dependent reads. Users is a set of dependent
+ // reads. A dependent read is added to the set only if CyclesLeft
+ // is "unknown". As soon as CyclesLeft is 'known', each user in the set
+ // gets notified with the actual CyclesLeft.
+
+ // The 'second' element of a pair is a "ReadAdvance" number of cycles.
+ std::set<std::pair<ReadState *, int>> Users;
+
+public:
+ WriteState(const WriteDescriptor &Desc, unsigned RegID,
+ bool clearsSuperRegs = false)
+ : WD(Desc), CyclesLeft(UNKNOWN_CYCLES), RegisterID(RegID),
+ ClearsSuperRegs(clearsSuperRegs), DependentWrite(nullptr) {}
+ WriteState(const WriteState &Other) = delete;
+ WriteState &operator=(const WriteState &Other) = delete;
+
+ int getCyclesLeft() const { return CyclesLeft; }
+ unsigned getWriteResourceID() const { return WD.SClassOrWriteResourceID; }
+ unsigned getRegisterID() const { return RegisterID; }
+ unsigned getLatency() const { return WD.Latency; }
+
+ void addUser(ReadState *Use, int ReadAdvance);
+ unsigned getNumUsers() const { return Users.size(); }
+ bool clearsSuperRegisters() const { return ClearsSuperRegs; }
+
+ const WriteState *getDependentWrite() const { return DependentWrite; }
+ void setDependentWrite(const WriteState *Write) { DependentWrite = Write; }
+
+ // On every cycle, update CyclesLeft and notify dependent users.
+ void cycleEvent();
+ void onInstructionIssued();
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+};
+
+/// Tracks register operand latency in cycles.
+///
+/// A read may be dependent on more than one write. This occurs when some
+/// writes only partially update the register associated to this read.
+class ReadState {
+ const ReadDescriptor &RD;
+ // Physical register identified associated to this read.
+ unsigned RegisterID;
+ // Number of writes that contribute to the definition of RegisterID.
+ // In the absence of partial register updates, the number of DependentWrites
+ // cannot be more than one.
+ unsigned DependentWrites;
+ // Number of cycles left before RegisterID can be read. This value depends on
+ // the latency of all the dependent writes. It defaults to UNKNOWN_CYCLES.
+ // It gets set to the value of field TotalCycles only when the 'CyclesLeft' of
+ // every dependent write is known.
+ int CyclesLeft;
+ // This field is updated on every writeStartEvent(). When the number of
+ // dependent writes (i.e. field DependentWrite) is zero, this value is
+ // propagated to field CyclesLeft.
+ unsigned TotalCycles;
+ // This field is set to true only if there are no dependent writes, and
+ // there are no `CyclesLeft' to wait.
+ bool IsReady;
+
+public:
+ bool isReady() const { return IsReady; }
+
+ ReadState(const ReadDescriptor &Desc, unsigned RegID)
+ : RD(Desc), RegisterID(RegID), DependentWrites(0),
+ CyclesLeft(UNKNOWN_CYCLES), TotalCycles(0), IsReady(true) {}
+ ReadState(const ReadState &Other) = delete;
+ ReadState &operator=(const ReadState &Other) = delete;
+
+ const ReadDescriptor &getDescriptor() const { return RD; }
+ unsigned getSchedClass() const { return RD.SchedClassID; }
+ unsigned getRegisterID() const { return RegisterID; }
+
+ void cycleEvent();
+ void writeStartEvent(unsigned Cycles);
+ void setDependentWrites(unsigned Writes) {
+ DependentWrites = Writes;
+ IsReady = !Writes;
+ }
+};
+
+/// A sequence of cycles.
+///
+/// This class can be used as a building block to construct ranges of cycles.
+class CycleSegment {
+ unsigned Begin; // Inclusive.
+ unsigned End; // Exclusive.
+ bool Reserved; // Resources associated to this segment must be reserved.
+
+public:
+ CycleSegment(unsigned StartCycle, unsigned EndCycle, bool IsReserved = false)
+ : Begin(StartCycle), End(EndCycle), Reserved(IsReserved) {}
+
+ bool contains(unsigned Cycle) const { return Cycle >= Begin && Cycle < End; }
+ bool startsAfter(const CycleSegment &CS) const { return End <= CS.Begin; }
+ bool endsBefore(const CycleSegment &CS) const { return Begin >= CS.End; }
+ bool overlaps(const CycleSegment &CS) const {
+ return !startsAfter(CS) && !endsBefore(CS);
+ }
+ bool isExecuting() const { return Begin == 0 && End != 0; }
+ bool isExecuted() const { return End == 0; }
+ bool operator<(const CycleSegment &Other) const {
+ return Begin < Other.Begin;
+ }
+ CycleSegment &operator--(void) {
+ if (Begin)
+ Begin--;
+ if (End)
+ End--;
+ return *this;
+ }
+
+ bool isValid() const { return Begin <= End; }
+ unsigned size() const { return End - Begin; };
+ void Subtract(unsigned Cycles) {
+ assert(End >= Cycles);
+ End -= Cycles;
+ }
+
+ unsigned begin() const { return Begin; }
+ unsigned end() const { return End; }
+ void setEnd(unsigned NewEnd) { End = NewEnd; }
+ bool isReserved() const { return Reserved; }
+ void setReserved() { Reserved = true; }
+};
+
+/// Helper used by class InstrDesc to describe how hardware resources
+/// are used.
+///
+/// This class describes how many resource units of a specific resource kind
+/// (and how many cycles) are "used" by an instruction.
+struct ResourceUsage {
+ CycleSegment CS;
+ unsigned NumUnits;
+ ResourceUsage(CycleSegment Cycles, unsigned Units = 1)
+ : CS(Cycles), NumUnits(Units) {}
+ unsigned size() const { return CS.size(); }
+ bool isReserved() const { return CS.isReserved(); }
+ void setReserved() { CS.setReserved(); }
+};
+
+/// An instruction descriptor
+struct InstrDesc {
+ std::vector<WriteDescriptor> Writes; // Implicit writes are at the end.
+ std::vector<ReadDescriptor> Reads; // Implicit reads are at the end.
+
+ // For every resource used by an instruction of this kind, this vector
+ // reports the number of "consumed cycles".
+ std::vector<std::pair<uint64_t, ResourceUsage>> Resources;
+
+ // A list of buffered resources consumed by this instruction.
+ std::vector<uint64_t> Buffers;
+ unsigned MaxLatency;
+ // Number of MicroOps for this instruction.
+ unsigned NumMicroOps;
+
+ bool MayLoad;
+ bool MayStore;
+ bool HasSideEffects;
+
+ // A zero latency instruction doesn't consume any scheduler resources.
+ bool isZeroLatency() const { return !MaxLatency && Resources.empty(); }
+};
+
+/// An instruction propagated through the simulated instruction pipeline.
+///
+/// This class is used to monitor changes to the internal state of instructions
+/// that are sent to the various components of the simulated hardware pipeline.
+class Instruction {
+ const InstrDesc &Desc;
+
+ enum InstrStage {
+ IS_INVALID, // Instruction in an invalid state.
+ IS_AVAILABLE, // Instruction dispatched but operands are not ready.
+ IS_READY, // Instruction dispatched and operands ready.
+ IS_EXECUTING, // Instruction issued.
+ IS_EXECUTED, // Instruction executed. Values are written back.
+ IS_RETIRED // Instruction retired.
+ };
+
+ // The current instruction stage.
+ enum InstrStage Stage;
+
+ // This value defaults to the instruction latency. This instruction is
+ // considered executed when field CyclesLeft goes to zero.
+ int CyclesLeft;
+
+ // Retire Unit token ID for this instruction.
+ unsigned RCUTokenID;
+
+ using UniqueDef = std::unique_ptr<WriteState>;
+ using UniqueUse = std::unique_ptr<ReadState>;
+ using VecDefs = std::vector<UniqueDef>;
+ using VecUses = std::vector<UniqueUse>;
+
+ // Output dependencies.
+ // One entry per each implicit and explicit register definition.
+ VecDefs Defs;
+
+ // Input dependencies.
+ // One entry per each implicit and explicit register use.
+ VecUses Uses;
+
+public:
+ Instruction(const InstrDesc &D)
+ : Desc(D), Stage(IS_INVALID), CyclesLeft(UNKNOWN_CYCLES) {}
+ Instruction(const Instruction &Other) = delete;
+ Instruction &operator=(const Instruction &Other) = delete;
+
+ VecDefs &getDefs() { return Defs; }
+ const VecDefs &getDefs() const { return Defs; }
+ VecUses &getUses() { return Uses; }
+ const VecUses &getUses() const { return Uses; }
+ const InstrDesc &getDesc() const { return Desc; }
+ unsigned getRCUTokenID() const { return RCUTokenID; }
+ int getCyclesLeft() const { return CyclesLeft; }
+
+ unsigned getNumUsers() const {
+ unsigned NumUsers = 0;
+ for (const UniqueDef &Def : Defs)
+ NumUsers += Def->getNumUsers();
+ return NumUsers;
+ }
+
+ // Transition to the dispatch stage, and assign a RCUToken to this
+ // instruction. The RCUToken is used to track the completion of every
+ // register write performed by this instruction.
+ void dispatch(unsigned RCUTokenID);
+
+ // Instruction issued. Transition to the IS_EXECUTING state, and update
+ // all the definitions.
+ void execute();
+
+ // Force a transition from the IS_AVAILABLE state to the IS_READY state if
+ // input operands are all ready. State transitions normally occur at the
+ // beginning of a new cycle (see method cycleEvent()). However, the scheduler
+ // may decide to promote instructions from the wait queue to the ready queue
+ // as the result of another issue event. This method is called every time the
+ // instruction might have changed in state.
+ void update();
+
+ bool isDispatched() const { return Stage == IS_AVAILABLE; }
+ bool isReady() const { return Stage == IS_READY; }
+ bool isExecuting() const { return Stage == IS_EXECUTING; }
+ bool isExecuted() const { return Stage == IS_EXECUTED; }
+ bool isRetired() const { return Stage == IS_RETIRED; }
+
+ void retire() {
+ assert(isExecuted() && "Instruction is in an invalid state!");
+ Stage = IS_RETIRED;
+ }
+
+ void cycleEvent();
+};
+
+/// An InstRef contains both a SourceMgr index and Instruction pair. The index
+/// is used as a unique identifier for the instruction. MCA will make use of
+/// this index as a key throughout MCA.
+class InstRef : public std::pair<unsigned, Instruction *> {
+public:
+ InstRef() : std::pair<unsigned, Instruction *>(0, nullptr) {}
+ InstRef(unsigned Index, Instruction *I)
+ : std::pair<unsigned, Instruction *>(Index, I) {}
+
+ unsigned getSourceIndex() const { return first; }
+ Instruction *getInstruction() { return second; }
+ const Instruction *getInstruction() const { return second; }
+
+ /// Returns true if this InstRef has been populated.
+ bool isValid() const { return second != nullptr; }
+
+#ifndef NDEBUG
+ void print(llvm::raw_ostream &OS) const { OS << getSourceIndex(); }
+#endif
+};
+
+#ifndef NDEBUG
+inline llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const InstRef &IR) {
+ IR.print(OS);
+ return OS;
+}
+#endif
+
+/// A reference to a register write.
+///
+/// This class is mainly used by the register file to describe register
+/// mappings. It correlates a register write to the source index of the
+/// defining instruction.
+class WriteRef {
+ std::pair<unsigned, WriteState *> Data;
+ static const unsigned INVALID_IID;
+
+public:
+ WriteRef() : Data(INVALID_IID, nullptr) {}
+ WriteRef(unsigned SourceIndex, WriteState *WS) : Data(SourceIndex, WS) {}
+
+ unsigned getSourceIndex() const { return Data.first; }
+ const WriteState *getWriteState() const { return Data.second; }
+ WriteState *getWriteState() { return Data.second; }
+ void invalidate() { Data = std::make_pair(INVALID_IID, nullptr); }
+
+ bool isValid() const {
+ return Data.first != INVALID_IID && Data.second != nullptr;
+ }
+ bool operator==(const WriteRef &Other) const {
+ return Data == Other.Data;
+ }
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+};
+
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/InstructionInfoView.cpp b/contrib/llvm/tools/llvm-mca/InstructionInfoView.cpp
new file mode 100644
index 000000000000..0e50a96d19c1
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/InstructionInfoView.cpp
@@ -0,0 +1,91 @@
+//===--------------------- InstructionInfoView.cpp --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the InstructionInfoView API.
+///
+//===----------------------------------------------------------------------===//
+
+#include "InstructionInfoView.h"
+
+namespace mca {
+
+using namespace llvm;
+
+void InstructionInfoView::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ const MCSchedModel &SM = STI.getSchedModel();
+ unsigned Instructions = Source.size();
+
+ std::string Instruction;
+ raw_string_ostream InstrStream(Instruction);
+
+ TempStream << "\n\nInstruction Info:\n";
+ TempStream << "[1]: #uOps\n[2]: Latency\n[3]: RThroughput\n"
+ << "[4]: MayLoad\n[5]: MayStore\n[6]: HasSideEffects (U)\n\n";
+
+ TempStream << "[1] [2] [3] [4] [5] [6] Instructions:\n";
+ for (unsigned I = 0, E = Instructions; I < E; ++I) {
+ const MCInst &Inst = Source.getMCInstFromIndex(I);
+ const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode());
+
+ // Obtain the scheduling class information from the instruction.
+ unsigned SchedClassID = MCDesc.getSchedClass();
+ unsigned CPUID = SM.getProcessorID();
+
+ // Try to solve variant scheduling classes.
+ while (SchedClassID && SM.getSchedClassDesc(SchedClassID)->isVariant())
+ SchedClassID = STI.resolveVariantSchedClass(SchedClassID, &Inst, CPUID);
+
+ const MCSchedClassDesc &SCDesc = *SM.getSchedClassDesc(SchedClassID);
+ unsigned NumMicroOpcodes = SCDesc.NumMicroOps;
+ unsigned Latency = MCSchedModel::computeInstrLatency(STI, SCDesc);
+ Optional<double> RThroughput =
+ MCSchedModel::getReciprocalThroughput(STI, SCDesc);
+
+ TempStream << ' ' << NumMicroOpcodes << " ";
+ if (NumMicroOpcodes < 10)
+ TempStream << " ";
+ else if (NumMicroOpcodes < 100)
+ TempStream << ' ';
+ TempStream << Latency << " ";
+ if (Latency < 10)
+ TempStream << " ";
+ else if (Latency < 100)
+ TempStream << ' ';
+
+ if (RThroughput.hasValue()) {
+ double RT = RThroughput.getValue();
+ TempStream << format("%.2f", RT) << ' ';
+ if (RT < 10.0)
+ TempStream << " ";
+ else if (RT < 100.0)
+ TempStream << ' ';
+ } else {
+ TempStream << " - ";
+ }
+ TempStream << (MCDesc.mayLoad() ? " * " : " ");
+ TempStream << (MCDesc.mayStore() ? " * " : " ");
+ TempStream << (MCDesc.hasUnmodeledSideEffects() ? " U " : " ");
+
+ MCIP.printInst(&Inst, InstrStream, "", STI);
+ InstrStream.flush();
+
+ // Consume any tabs or spaces at the beginning of the string.
+ StringRef Str(Instruction);
+ Str = Str.ltrim();
+ TempStream << " " << Str << '\n';
+ Instruction = "";
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+} // namespace mca.
diff --git a/contrib/llvm/tools/llvm-mca/InstructionInfoView.h b/contrib/llvm/tools/llvm-mca/InstructionInfoView.h
new file mode 100644
index 000000000000..0770ae3d2b57
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/InstructionInfoView.h
@@ -0,0 +1,66 @@
+//===--------------------- InstructionInfoView.h ----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the instruction info view.
+///
+/// The goal fo the instruction info view is to print the latency and reciprocal
+/// throughput information for every instruction in the input sequence.
+/// This section also reports extra information related to the number of micro
+/// opcodes, and opcode properties (i.e. 'MayLoad', 'MayStore', 'HasSideEffects)
+///
+/// Example:
+///
+/// Instruction Info:
+/// [1]: #uOps
+/// [2]: Latency
+/// [3]: RThroughput
+/// [4]: MayLoad
+/// [5]: MayStore
+/// [6]: HasSideEffects
+///
+/// [1] [2] [3] [4] [5] [6] Instructions:
+/// 1 2 1.00 vmulps %xmm0, %xmm1, %xmm2
+/// 1 3 1.00 vhaddps %xmm2, %xmm2, %xmm3
+/// 1 3 1.00 vhaddps %xmm3, %xmm3, %xmm4
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H
+
+#include "SourceMgr.h"
+#include "View.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCInstrInfo.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+/// A view that prints out generic instruction information.
+class InstructionInfoView : public View {
+ const llvm::MCSubtargetInfo &STI;
+ const llvm::MCInstrInfo &MCII;
+ const SourceMgr &Source;
+ llvm::MCInstPrinter &MCIP;
+
+public:
+ InstructionInfoView(const llvm::MCSubtargetInfo &sti,
+ const llvm::MCInstrInfo &mcii, const SourceMgr &S,
+ llvm::MCInstPrinter &IP)
+ : STI(sti), MCII(mcii), Source(S), MCIP(IP) {}
+
+ void printView(llvm::raw_ostream &OS) const override;
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/InstructionTables.cpp b/contrib/llvm/tools/llvm-mca/InstructionTables.cpp
new file mode 100644
index 000000000000..9b9dbc37fbdb
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/InstructionTables.cpp
@@ -0,0 +1,70 @@
+//===--------------------- InstructionTables.cpp ----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the method InstructionTables::execute().
+/// Method execute() prints a theoretical resource pressure distribution based
+/// on the information available in the scheduling model, and without running
+/// the pipeline.
+///
+//===----------------------------------------------------------------------===//
+
+#include "InstructionTables.h"
+
+namespace mca {
+
+using namespace llvm;
+
+bool InstructionTables::execute(InstRef &IR) {
+ ArrayRef<uint64_t> Masks = IB.getProcResourceMasks();
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ UsedResources.clear();
+
+ // Identify the resources consumed by this instruction.
+ for (const std::pair<uint64_t, ResourceUsage> Resource : Desc.Resources) {
+ // Skip zero-cycle resources (i.e., unused resources).
+ if (!Resource.second.size())
+ continue;
+ double Cycles = static_cast<double>(Resource.second.size());
+ unsigned Index = std::distance(
+ Masks.begin(), std::find(Masks.begin(), Masks.end(), Resource.first));
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(Index);
+ unsigned NumUnits = ProcResource.NumUnits;
+ if (!ProcResource.SubUnitsIdxBegin) {
+ // The number of cycles consumed by each unit.
+ Cycles /= NumUnits;
+ for (unsigned I = 0, E = NumUnits; I < E; ++I) {
+ ResourceRef ResourceUnit = std::make_pair(Index, 1U << I);
+ UsedResources.emplace_back(std::make_pair(ResourceUnit, Cycles));
+ }
+ continue;
+ }
+
+ // This is a group. Obtain the set of resources contained in this
+ // group. Some of these resources may implement multiple units.
+ // Uniformly distribute Cycles across all of the units.
+ for (unsigned I1 = 0; I1 < NumUnits; ++I1) {
+ unsigned SubUnitIdx = ProcResource.SubUnitsIdxBegin[I1];
+ const MCProcResourceDesc &SubUnit = *SM.getProcResource(SubUnitIdx);
+ // Compute the number of cycles consumed by each resource unit.
+ double RUCycles = Cycles / (NumUnits * SubUnit.NumUnits);
+ for (unsigned I2 = 0, E2 = SubUnit.NumUnits; I2 < E2; ++I2) {
+ ResourceRef ResourceUnit = std::make_pair(SubUnitIdx, 1U << I2);
+ UsedResources.emplace_back(std::make_pair(ResourceUnit, RUCycles));
+ }
+ }
+ }
+
+ // Send a fake instruction issued event to all the views.
+ HWInstructionIssuedEvent Event(IR, UsedResources);
+ notifyEvent<HWInstructionIssuedEvent>(Event);
+ return true;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/InstructionTables.h b/contrib/llvm/tools/llvm-mca/InstructionTables.h
new file mode 100644
index 000000000000..18e019988430
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/InstructionTables.h
@@ -0,0 +1,43 @@
+//===--------------------- InstructionTables.h ------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements a custom stage to generate instruction tables.
+/// See the description of command-line flag -instruction-tables in
+/// docs/CommandGuide/lvm-mca.rst
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONTABLES_H
+#define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONTABLES_H
+
+#include "InstrBuilder.h"
+#include "Scheduler.h"
+#include "Stage.h"
+#include "View.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSchedule.h"
+
+namespace mca {
+
+class InstructionTables : public Stage {
+ const llvm::MCSchedModel &SM;
+ InstrBuilder &IB;
+ llvm::SmallVector<std::pair<ResourceRef, double>, 4> UsedResources;
+
+public:
+ InstructionTables(const llvm::MCSchedModel &Model, InstrBuilder &Builder)
+ : Stage(), SM(Model), IB(Builder) {}
+
+ bool hasWorkToComplete() const override final { return false; }
+ bool execute(InstRef &IR) override final;
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/LSUnit.cpp b/contrib/llvm/tools/llvm-mca/LSUnit.cpp
new file mode 100644
index 000000000000..9ee3b6171893
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/LSUnit.cpp
@@ -0,0 +1,148 @@
+//===----------------------- LSUnit.cpp --------------------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// A Load-Store Unit for the llvm-mca tool.
+///
+//===----------------------------------------------------------------------===//
+
+#include "LSUnit.h"
+#include "Instruction.h"
+
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+#ifndef NDEBUG
+void LSUnit::dump() const {
+ dbgs() << "[LSUnit] LQ_Size = " << LQ_Size << '\n';
+ dbgs() << "[LSUnit] SQ_Size = " << SQ_Size << '\n';
+ dbgs() << "[LSUnit] NextLQSlotIdx = " << LoadQueue.size() << '\n';
+ dbgs() << "[LSUnit] NextSQSlotIdx = " << StoreQueue.size() << '\n';
+}
+#endif
+
+void LSUnit::assignLQSlot(unsigned Index) {
+ assert(!isLQFull());
+ assert(LoadQueue.count(Index) == 0);
+
+ LLVM_DEBUG(dbgs() << "[LSUnit] - AssignLQSlot <Idx=" << Index
+ << ",slot=" << LoadQueue.size() << ">\n");
+ LoadQueue.insert(Index);
+}
+
+void LSUnit::assignSQSlot(unsigned Index) {
+ assert(!isSQFull());
+ assert(StoreQueue.count(Index) == 0);
+
+ LLVM_DEBUG(dbgs() << "[LSUnit] - AssignSQSlot <Idx=" << Index
+ << ",slot=" << StoreQueue.size() << ">\n");
+ StoreQueue.insert(Index);
+}
+
+bool LSUnit::reserve(const InstRef &IR) {
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ unsigned MayLoad = Desc.MayLoad;
+ unsigned MayStore = Desc.MayStore;
+ unsigned IsMemBarrier = Desc.HasSideEffects;
+ if (!MayLoad && !MayStore)
+ return false;
+
+ const unsigned Index = IR.getSourceIndex();
+ if (MayLoad) {
+ if (IsMemBarrier)
+ LoadBarriers.insert(Index);
+ assignLQSlot(Index);
+ }
+ if (MayStore) {
+ if (IsMemBarrier)
+ StoreBarriers.insert(Index);
+ assignSQSlot(Index);
+ }
+ return true;
+}
+
+bool LSUnit::isReady(const InstRef &IR) const {
+ const unsigned Index = IR.getSourceIndex();
+ bool IsALoad = LoadQueue.count(Index) != 0;
+ bool IsAStore = StoreQueue.count(Index) != 0;
+ assert((IsALoad || IsAStore) && "Instruction is not in queue!");
+
+ if (IsALoad && !LoadBarriers.empty()) {
+ unsigned LoadBarrierIndex = *LoadBarriers.begin();
+ if (Index > LoadBarrierIndex)
+ return false;
+ if (Index == LoadBarrierIndex && Index != *LoadQueue.begin())
+ return false;
+ }
+
+ if (IsAStore && !StoreBarriers.empty()) {
+ unsigned StoreBarrierIndex = *StoreBarriers.begin();
+ if (Index > StoreBarrierIndex)
+ return false;
+ if (Index == StoreBarrierIndex && Index != *StoreQueue.begin())
+ return false;
+ }
+
+ if (NoAlias && IsALoad)
+ return true;
+
+ if (StoreQueue.size()) {
+ // Check if this memory operation is younger than the older store.
+ if (Index > *StoreQueue.begin())
+ return false;
+ }
+
+ // Okay, we are older than the oldest store in the queue.
+ // If there are no pending loads, then we can say for sure that this
+ // instruction is ready.
+ if (isLQEmpty())
+ return true;
+
+ // Check if there are no older loads.
+ if (Index <= *LoadQueue.begin())
+ return true;
+
+ // There is at least one younger load.
+ return !IsAStore;
+}
+
+void LSUnit::onInstructionExecuted(const InstRef &IR) {
+ const unsigned Index = IR.getSourceIndex();
+ std::set<unsigned>::iterator it = LoadQueue.find(Index);
+ if (it != LoadQueue.end()) {
+ LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
+ << " has been removed from the load queue.\n");
+ LoadQueue.erase(it);
+ }
+
+ it = StoreQueue.find(Index);
+ if (it != StoreQueue.end()) {
+ LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
+ << " has been removed from the store queue.\n");
+ StoreQueue.erase(it);
+ }
+
+ if (!StoreBarriers.empty() && Index == *StoreBarriers.begin()) {
+ LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
+ << " has been removed from the set of store barriers.\n");
+ StoreBarriers.erase(StoreBarriers.begin());
+ }
+ if (!LoadBarriers.empty() && Index == *LoadBarriers.begin()) {
+ LLVM_DEBUG(dbgs() << "[LSUnit]: Instruction idx=" << Index
+ << " has been removed from the set of load barriers.\n");
+ LoadBarriers.erase(LoadBarriers.begin());
+ }
+}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/LSUnit.h b/contrib/llvm/tools/llvm-mca/LSUnit.h
new file mode 100644
index 000000000000..817522190589
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/LSUnit.h
@@ -0,0 +1,147 @@
+//===------------------------- LSUnit.h --------------------------*- C++-*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// A Load/Store unit class that models load/store queues and that implements
+/// a simple weak memory consistency model.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_LSUNIT_H
+#define LLVM_TOOLS_LLVM_MCA_LSUNIT_H
+
+#include <set>
+
+namespace mca {
+
+class InstRef;
+struct InstrDesc;
+
+/// A Load/Store Unit implementing a load and store queues.
+///
+/// This class implements a load queue and a store queue to emulate the
+/// out-of-order execution of memory operations.
+/// Each load (or store) consumes an entry in the load (or store) queue.
+///
+/// Rules are:
+/// 1) A younger load is allowed to pass an older load only if there are no
+/// stores nor barriers in between the two loads.
+/// 2) An younger store is not allowed to pass an older store.
+/// 3) A younger store is not allowed to pass an older load.
+/// 4) A younger load is allowed to pass an older store only if the load does
+/// not alias with the store.
+///
+/// This class optimistically assumes that loads don't alias store operations.
+/// Under this assumption, younger loads are always allowed to pass older
+/// stores (this would only affects rule 4).
+/// Essentially, this LSUnit doesn't attempt to run any sort alias analysis to
+/// predict when loads and stores don't alias with eachother.
+///
+/// To enforce aliasing between loads and stores, flag `AssumeNoAlias` must be
+/// set to `false` by the constructor of LSUnit.
+///
+/// In the case of write-combining memory, rule 2. could be relaxed to allow
+/// reordering of non-aliasing store operations. At the moment, this is not
+/// allowed.
+/// To put it in another way, there is no option to specify a different memory
+/// type for memory operations (example: write-through, write-combining, etc.).
+/// Also, there is no way to weaken the memory model, and this unit currently
+/// doesn't support write-combining behavior.
+///
+/// No assumptions are made on the size of the store buffer.
+/// As mentioned before, this class doesn't perform alias analysis.
+/// Consequently, LSUnit doesn't know how to identify cases where
+/// store-to-load forwarding may occur.
+///
+/// LSUnit doesn't attempt to predict whether a load or store hits or misses
+/// the L1 cache. To be more specific, LSUnit doesn't know anything about
+/// the cache hierarchy and memory types.
+/// It only knows if an instruction "mayLoad" and/or "mayStore". For loads, the
+/// scheduling model provides an "optimistic" load-to-use latency (which usually
+/// matches the load-to-use latency for when there is a hit in the L1D).
+///
+/// Class MCInstrDesc in LLVM doesn't know about serializing operations, nor
+/// memory-barrier like instructions.
+/// LSUnit conservatively assumes that an instruction which `mayLoad` and has
+/// `unmodeled side effects` behave like a "soft" load-barrier. That means, it
+/// serializes loads without forcing a flush of the load queue.
+/// Similarly, instructions that both `mayStore` and have `unmodeled side
+/// effects` are treated like store barriers. A full memory
+/// barrier is a 'mayLoad' and 'mayStore' instruction with unmodeled side
+/// effects. This is obviously inaccurate, but this is the best that we can do
+/// at the moment.
+///
+/// Each load/store barrier consumes one entry in the load/store queue. A
+/// load/store barrier enforces ordering of loads/stores:
+/// - A younger load cannot pass a load barrier.
+/// - A younger store cannot pass a store barrier.
+///
+/// A younger load has to wait for the memory load barrier to execute.
+/// A load/store barrier is "executed" when it becomes the oldest entry in
+/// the load/store queue(s). That also means, all the older loads/stores have
+/// already been executed.
+class LSUnit {
+ // Load queue size.
+ // LQ_Size == 0 means that there are infinite slots in the load queue.
+ unsigned LQ_Size;
+
+ // Store queue size.
+ // SQ_Size == 0 means that there are infinite slots in the store queue.
+ unsigned SQ_Size;
+
+ // If true, loads will never alias with stores. This is the default.
+ bool NoAlias;
+
+ std::set<unsigned> LoadQueue;
+ std::set<unsigned> StoreQueue;
+
+ void assignLQSlot(unsigned Index);
+ void assignSQSlot(unsigned Index);
+ bool isReadyNoAlias(unsigned Index) const;
+
+ // An instruction that both 'mayStore' and 'HasUnmodeledSideEffects' is
+ // conservatively treated as a store barrier. It forces older store to be
+ // executed before newer stores are issued.
+ std::set<unsigned> StoreBarriers;
+
+ // An instruction that both 'MayLoad' and 'HasUnmodeledSideEffects' is
+ // conservatively treated as a load barrier. It forces older loads to execute
+ // before newer loads are issued.
+ std::set<unsigned> LoadBarriers;
+
+public:
+ LSUnit(unsigned LQ = 0, unsigned SQ = 0, bool AssumeNoAlias = false)
+ : LQ_Size(LQ), SQ_Size(SQ), NoAlias(AssumeNoAlias) {}
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+
+ bool isSQEmpty() const { return StoreQueue.empty(); }
+ bool isLQEmpty() const { return LoadQueue.empty(); }
+ bool isSQFull() const { return SQ_Size != 0 && StoreQueue.size() == SQ_Size; }
+ bool isLQFull() const { return LQ_Size != 0 && LoadQueue.size() == LQ_Size; }
+
+ // Returns true if this instruction has been successfully enqueued.
+ bool reserve(const InstRef &IR);
+
+ // The rules are:
+ // 1. A store may not pass a previous store.
+ // 2. A load may not pass a previous store unless flag 'NoAlias' is set.
+ // 3. A load may pass a previous load.
+ // 4. A store may not pass a previous load (regardless of flag 'NoAlias').
+ // 5. A load has to wait until an older load barrier is fully executed.
+ // 6. A store has to wait until an older store barrier is fully executed.
+ bool isReady(const InstRef &IR) const;
+ void onInstructionExecuted(const InstRef &IR);
+};
+
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/Pipeline.cpp b/contrib/llvm/tools/llvm-mca/Pipeline.cpp
new file mode 100644
index 000000000000..7c937e7b48b5
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Pipeline.cpp
@@ -0,0 +1,99 @@
+//===--------------------- Pipeline.cpp -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements an ordered container of stages that simulate the
+/// pipeline of a hardware backend.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Pipeline.h"
+#include "HWEventListener.h"
+#include "llvm/CodeGen/TargetSchedule.h"
+#include "llvm/Support/Debug.h"
+
+namespace mca {
+
+#define DEBUG_TYPE "llvm-mca"
+
+using namespace llvm;
+
+void Pipeline::addEventListener(HWEventListener *Listener) {
+ if (Listener)
+ Listeners.insert(Listener);
+ for (auto &S : Stages)
+ S->addListener(Listener);
+}
+
+bool Pipeline::hasWorkToProcess() {
+ const auto It = llvm::find_if(Stages, [](const std::unique_ptr<Stage> &S) {
+ return S->hasWorkToComplete();
+ });
+ return It != Stages.end();
+}
+
+// This routine returns early if any stage returns 'false' after execute() is
+// called on it.
+bool Pipeline::executeStages(InstRef &IR) {
+ for (const std::unique_ptr<Stage> &S : Stages)
+ if (!S->execute(IR))
+ return false;
+ return true;
+}
+
+void Pipeline::preExecuteStages() {
+ for (const std::unique_ptr<Stage> &S : Stages)
+ S->preExecute();
+}
+
+void Pipeline::postExecuteStages() {
+ for (const std::unique_ptr<Stage> &S : Stages)
+ S->postExecute();
+}
+
+void Pipeline::run() {
+ while (hasWorkToProcess()) {
+ notifyCycleBegin();
+ runCycle();
+ notifyCycleEnd();
+ ++Cycles;
+ }
+}
+
+void Pipeline::runCycle() {
+ // Update the stages before we do any processing for this cycle.
+ InstRef IR;
+ for (auto &S : Stages)
+ S->cycleStart();
+
+ // Continue executing this cycle until any stage claims it cannot make
+ // progress.
+ while (true) {
+ preExecuteStages();
+ if (!executeStages(IR))
+ break;
+ postExecuteStages();
+ }
+
+ for (auto &S : Stages)
+ S->cycleEnd();
+}
+
+void Pipeline::notifyCycleBegin() {
+ LLVM_DEBUG(dbgs() << "[E] Cycle begin: " << Cycles << '\n');
+ for (HWEventListener *Listener : Listeners)
+ Listener->onCycleBegin();
+}
+
+void Pipeline::notifyCycleEnd() {
+ LLVM_DEBUG(dbgs() << "[E] Cycle end: " << Cycles << "\n\n");
+ for (HWEventListener *Listener : Listeners)
+ Listener->onCycleEnd();
+}
+} // namespace mca.
diff --git a/contrib/llvm/tools/llvm-mca/Pipeline.h b/contrib/llvm/tools/llvm-mca/Pipeline.h
new file mode 100644
index 000000000000..6916e422be39
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Pipeline.h
@@ -0,0 +1,79 @@
+//===--------------------- Pipeline.h ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements an ordered container of stages that simulate the
+/// pipeline of a hardware backend.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_PIPELINE_H
+#define LLVM_TOOLS_LLVM_MCA_PIPELINE_H
+
+#include "Scheduler.h"
+#include "Stage.h"
+#include "llvm/ADT/SmallVector.h"
+
+namespace mca {
+
+class HWEventListener;
+class HWInstructionEvent;
+class HWStallEvent;
+
+/// A pipeline for a specific subtarget.
+///
+/// It emulates an out-of-order execution of instructions. Instructions are
+/// fetched from a MCInst sequence managed by an initial 'Fetch' stage.
+/// Instructions are firstly fetched, then dispatched to the schedulers, and
+/// then executed.
+///
+/// This class tracks the lifetime of an instruction from the moment where
+/// it gets dispatched to the schedulers, to the moment where it finishes
+/// executing and register writes are architecturally committed.
+/// In particular, it monitors changes in the state of every instruction
+/// in flight.
+///
+/// Instructions are executed in a loop of iterations. The number of iterations
+/// is defined by the SourceMgr object, which is managed by the initial stage
+/// of the instruction pipeline.
+///
+/// The Pipeline entry point is method 'run()' which executes cycles in a loop
+/// until there are new instructions to dispatch, and not every instruction
+/// has been retired.
+///
+/// Internally, the Pipeline collects statistical information in the form of
+/// histograms. For example, it tracks how the dispatch group size changes
+/// over time.
+class Pipeline {
+ Pipeline(const Pipeline &P) = delete;
+ Pipeline &operator=(const Pipeline &P) = delete;
+
+ /// An ordered list of stages that define this instruction pipeline.
+ llvm::SmallVector<std::unique_ptr<Stage>, 8> Stages;
+ std::set<HWEventListener *> Listeners;
+ unsigned Cycles;
+
+ void preExecuteStages();
+ bool executeStages(InstRef &IR);
+ void postExecuteStages();
+ void runCycle();
+
+ bool hasWorkToProcess();
+ void notifyCycleBegin();
+ void notifyCycleEnd();
+
+public:
+ Pipeline() : Cycles(0) {}
+ void appendStage(std::unique_ptr<Stage> S) { Stages.push_back(std::move(S)); }
+ void run();
+ void addEventListener(HWEventListener *Listener);
+};
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_PIPELINE_H
diff --git a/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp b/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp
new file mode 100644
index 000000000000..c5b1a12b792f
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/PipelinePrinter.cpp
@@ -0,0 +1,26 @@
+//===--------------------- PipelinePrinter.cpp ------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the PipelinePrinter interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "PipelinePrinter.h"
+#include "View.h"
+
+namespace mca {
+
+using namespace llvm;
+
+void PipelinePrinter::printReport(llvm::raw_ostream &OS) const {
+ for (const auto &V : Views)
+ V->printView(OS);
+}
+} // namespace mca.
diff --git a/contrib/llvm/tools/llvm-mca/PipelinePrinter.h b/contrib/llvm/tools/llvm-mca/PipelinePrinter.h
new file mode 100644
index 000000000000..fe871414418f
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/PipelinePrinter.h
@@ -0,0 +1,52 @@
+//===--------------------- PipelinePrinter.h --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements class PipelinePrinter.
+///
+/// PipelinePrinter allows the customization of the performance report.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
+#define LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
+
+#include "Pipeline.h"
+#include "View.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+/// A printer class that knows how to collects statistics on the
+/// code analyzed by the llvm-mca tool.
+///
+/// This class knows how to print out the analysis information collected
+/// during the execution of the code. Internally, it delegates to other
+/// classes the task of printing out timeline information as well as
+/// resource pressure.
+class PipelinePrinter {
+ Pipeline &P;
+ llvm::SmallVector<std::unique_ptr<View>, 8> Views;
+
+public:
+ PipelinePrinter(Pipeline &pipeline) : P(pipeline) {}
+
+ void addView(std::unique_ptr<View> V) {
+ P.addEventListener(V.get());
+ Views.emplace_back(std::move(V));
+ }
+
+ void printReport(llvm::raw_ostream &OS) const;
+};
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H
diff --git a/contrib/llvm/tools/llvm-mca/README.txt b/contrib/llvm/tools/llvm-mca/README.txt
new file mode 100644
index 000000000000..8b1670db0fca
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/README.txt
@@ -0,0 +1,865 @@
+llvm-mca - LLVM Machine Code Analyzer
+-------------------------------------
+
+llvm-mca is a performance analysis tool that uses information which is already
+available in LLVM (e.g., scheduling models) to statically measure the
+performance of machine code in a specific cpu.
+
+Performance is measured in terms of throughput as well as processor resource
+consumption. The tool currently works for processors with an out-of-order
+backend, for which there is a scheduling model available in LLVM.
+
+The main goal of this tool is not just to predict the performance of the code
+when run on the target, but also help with diagnosing potential performance
+issues.
+
+Given an assembly code sequence, llvm-mca estimates the IPC (instructions Per
+cycle), as well as hardware resources pressure. The analysis and reporting style
+were inspired by the IACA tool from Intel.
+
+The presence of long data dependency chains, as well as poor usage of hardware
+resources may lead to bottlenecks in the backend. The tool is able to generate
+a detailed report which should help with identifying and analyzing sources of
+bottlenecks.
+
+Scheduling models are mostly used to compute instruction latencies, to obtain
+read-advance information, and understand how processor resources are used by
+instructions. By design, the quality of the performance analysis conducted by
+the tool is inevitably affected by the quality of the target scheduling models.
+However, scheduling models intentionally do not describe all processor details,
+since the goal is just to enable the scheduling of machine instructions during
+compilation. That means, there are processor details which are not important for
+the purpose of scheduling instructions (and therefore not described by the
+scheduling model), but are very important for this tool.
+
+A few examples of details that are missing in scheduling models are:
+ - Actual dispatch width (it often differs from the issue width).
+ - Number of read/write ports in the register file(s).
+ - Length of the load/store queue in the LSUnit.
+
+It is also very difficult to find a "good" abstract model to describe the
+behavior of out-of-order processors. So, we have to keep in mind that all of
+these aspects are going to affect the quality of the static analysis performed
+by the tool.
+
+An extensive list of known limitations is reported in one of the last sections
+of this document. There is also a section related to design problems which must
+be addressed (hopefully with the help of the community). At the moment, the
+tool has been mostly tested for x86 targets, but there are still several
+limitations, some of which could be overcome by integrating extra information
+into the scheduling models.
+
+How the tool works
+------------------
+
+The tool takes assembly code as input. Assembly code is parsed into a sequence
+of MCInst with the help of the existing LLVM target assembly parsers. The parsed
+sequence of MCInst is then analyzed by a 'Pipeline' module to generate a
+performance report.
+
+The Pipeline module internally emulates the execution of the machine code
+sequence in a loop of iterations (which by default is 100). At the end of this
+process, the pipeline collects a number of statistics which are then printed out
+in the form of a report.
+
+Here is an example of performance report generated by the tool for a dot-product
+of two packed float vectors of four elements. The analysis is conducted for
+target x86, cpu btver2:
+
+///////////////////
+
+Iterations: 300
+Instructions: 900
+Total Cycles: 610
+Dispatch Width: 2
+IPC: 1.48
+
+
+Resources:
+[0] - JALU0
+[1] - JALU1
+[2] - JDiv
+[3] - JFPM
+[4] - JFPU0
+[5] - JFPU1
+[6] - JLAGU
+[7] - JSAGU
+[8] - JSTC
+[9] - JVIMUL
+
+
+Resource pressure per iteration:
+[0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
+ - - - - 2.00 1.00 - - - -
+
+Resource pressure by instruction:
+[0] [1] [2] [3] [4] [5] [6] [7] [8] [9] Instructions:
+ - - - - - 1.00 - - - - vmulps %xmm0, %xmm1, %xmm2
+ - - - - 1.00 - - - - - vhaddps %xmm2, %xmm2, %xmm3
+ - - - - 1.00 - - - - - vhaddps %xmm3, %xmm3, %xmm4
+
+
+Instruction Info:
+[1]: #uOps
+[2]: Latency
+[3]: RThroughput
+[4]: MayLoad
+[5]: MayStore
+[6]: HasSideEffects
+
+[1] [2] [3] [4] [5] [6] Instructions:
+ 1 2 1.00 vmulps %xmm0, %xmm1, %xmm2
+ 1 3 1.00 vhaddps %xmm2, %xmm2, %xmm3
+ 1 3 1.00 vhaddps %xmm3, %xmm3, %xmm4
+
+///////////////////
+
+According to this report, the dot-product kernel has been executed 300 times,
+for a total of 900 instructions dynamically executed.
+
+The report is structured in three main sections. A first section collects a few
+performance numbers; the goal of this section is to give a very quick overview
+of the performance throughput. In this example, the two important performance
+indicators are a) the predicted total number of cycles, and b) the IPC.
+IPC is probably the most important throughput indicator. A big delta between the
+Dispatch Width and the computed IPC is an indicator of potential performance
+issues.
+
+The second section is the so-called "resource pressure view". This view reports
+the average number of resource cycles consumed every iteration by instructions
+for every processor resource unit available on the target. Information is
+structured in two tables. The first table reports the number of resource cycles
+spent on average every iteration. The second table correlates the resource
+cycles to the machine instruction in the sequence. For example, every iteration
+of the dot-product, instruction 'vmulps' always executes on resource unit [5]
+(JFPU1 - floating point pipeline #1), consuming an average of 1 resource cycle
+per iteration. Note that on Jaguar, vector FP multiply can only be issued to
+pipeline JFPU1, while horizontal FP adds can only be issued to pipeline JFPU0.
+
+The third (and last) section of the report shows the latency and reciprocal
+throughput of every instruction in the sequence. That section also reports extra
+information related to the number of micro opcodes, and opcode properties (i.e.,
+'MayLoad', 'MayStore', and 'UnmodeledSideEffects').
+
+The resource pressure view helps with identifying bottlenecks caused by high
+usage of specific hardware resources. Situations with resource pressure mainly
+concentrated on a few resources should, in general, be avoided. Ideally,
+pressure should be uniformly distributed between multiple resources.
+
+Timeline View
+-------------
+
+A detailed report of each instruction's state transitions over time can be
+enabled using the command line flag '-timeline'. This prints an extra section
+in the report which contains the so-called "timeline view". Below is the
+timeline view for the dot-product example from the previous section.
+
+///////////////
+Timeline view:
+ 012345
+Index 0123456789
+
+[0,0] DeeER. . . vmulps %xmm0, %xmm1, %xmm2
+[0,1] D==eeeER . . vhaddps %xmm2, %xmm2, %xmm3
+[0,2] .D====eeeER . vhaddps %xmm3, %xmm3, %xmm4
+
+[1,0] .DeeE-----R . vmulps %xmm0, %xmm1, %xmm2
+[1,1] . D=eeeE---R . vhaddps %xmm2, %xmm2, %xmm3
+[1,2] . D====eeeER . vhaddps %xmm3, %xmm3, %xmm4
+
+[2,0] . DeeE-----R . vmulps %xmm0, %xmm1, %xmm2
+[2,1] . D====eeeER . vhaddps %xmm2, %xmm2, %xmm3
+[2,2] . D======eeeER vhaddps %xmm3, %xmm3, %xmm4
+
+
+Average Wait times (based on the timeline view):
+[0]: Executions
+[1]: Average time spent waiting in a scheduler's queue
+[2]: Average time spent waiting in a scheduler's queue while ready
+[3]: Average time elapsed from WB until retire stage
+
+ [0] [1] [2] [3]
+0. 3 1.0 1.0 3.3 vmulps %xmm0, %xmm1, %xmm2
+1. 3 3.3 0.7 1.0 vhaddps %xmm2, %xmm2, %xmm3
+2. 3 5.7 0.0 0.0 vhaddps %xmm3, %xmm3, %xmm4
+///////////////
+
+The timeline view is very interesting because it shows how instructions changed
+in state during execution. It also gives an idea of how the tool "sees"
+instructions executed on the target.
+
+The timeline view is structured in two tables. The first table shows how
+instructions change in state over time (measured in cycles); the second table
+(named "Average Wait times") reports useful timing statistics which should help
+diagnose performance bottlenecks caused by long data dependencies and
+sub-optimal usage of hardware resources.
+
+An instruction in the timeline view is identified by a pair of indices, where
+the 'first' index identifies an iteration, and the 'second' index is the actual
+instruction index (i.e., where it appears in the code sequence).
+
+Excluding the first and last column, the remaining columns are in cycles.
+Cycles are numbered sequentially starting from 0. The following characters are
+used to describe the state of an instruction:
+
+ D : Instruction dispatched.
+ e : Instruction executing.
+ E : Instruction executed.
+ R : Instruction retired.
+ = : Instruction already dispatched, waiting to be executed.
+ - : Instruction executed, waiting to be retired.
+
+Based on the timeline view from the example, we know that:
+ - Instruction [1, 0] was dispatched at cycle 1.
+ - Instruction [1, 0] started executing at cycle 2.
+ - Instruction [1, 0] reached the write back stage at cycle 4.
+ - Instruction [1, 0] was retired at cycle 10.
+
+Instruction [1, 0] (i.e., the vmulps from iteration #1) doesn't have to wait in
+the scheduler's queue for the operands to become available. By the time the
+vmulps is dispatched, operands are already available, and pipeline JFPU1 is
+ready to serve another instruction. So the instruction can be immediately
+issued on the JFPU1 pipeline. That is demonstrated by the fact that the
+instruction only spent 1cy in the scheduler's queue.
+
+There is a gap of 5 cycles between the write-back stage and the retire event.
+That is because instructions must retire in program order, so [1,0] has to wait
+for [0, 2] to be retired first (i.e., it has to wait until cycle 10).
+
+In the dot-product example, all instructions are in a RAW (Read After Write)
+dependency chain. Register %xmm2 written by the vmulps is immediately used by
+the first vhaddps, and register %xmm3 written by the first vhaddps is used by
+the second vhaddps. Long data dependencies negatively affect the ILP
+(Instruction Level Parallelism).
+
+In the dot-product example, there are anti-dependencies introduced by
+instructions from different iterations. However, those dependencies can be
+removed at register renaming stage (at the cost of allocating register aliases,
+and therefore consuming temporary registers).
+
+Table "Average Wait times" helps diagnose performance issues that are caused by
+the presence of long latency instructions and potentially long data dependencies
+which may limit the ILP. Note that the tool by default assumes at least 1cy
+between the dispatch event and the issue event.
+
+When the performance is limited by data dependencies and/or long latency
+instructions, the number of cycles spent while in the "ready" state is expected
+to be very small when compared with the total number of cycles spent in the
+scheduler's queue. So the difference between the two counters is a good
+indicator of how big of an impact data dependencies had on the execution of
+instructions. When performance is mostly limited by the lack of hardware
+resources, the delta between the two counters is small. However, the number of
+cycles spent in the queue tends to be bigger (i.e., more than 1-3cy) especially
+when compared with other low latency instructions.
+
+Extra statistics to further diagnose performance issues.
+--------------------------------------------------------
+
+Flag '-verbose' enables extra statistics and performance counters for the
+dispatch logic, the reorder buffer, the retire control unit and the register
+file.
+
+Below is an example of verbose output generated by the tool for the dot-product
+example discussed in the previous sections.
+
+///////////////////
+Iterations: 300
+Instructions: 900
+Total Cycles: 610
+Dispatch Width: 2
+IPC: 1.48
+
+
+Dynamic Dispatch Stall Cycles:
+RAT - Register unavailable: 0
+RCU - Retire tokens unavailable: 0
+SCHEDQ - Scheduler full: 272
+LQ - Load queue full: 0
+SQ - Store queue full: 0
+GROUP - Static restrictions on the dispatch group: 0
+
+
+Register Alias Table:
+Total number of mappings created: 900
+Max number of mappings used: 35
+
+
+Dispatch Logic - number of cycles where we saw N instructions dispatched:
+[# dispatched], [# cycles]
+ 0, 24 (3.9%)
+ 1, 272 (44.6%)
+ 2, 314 (51.5%)
+
+
+Schedulers - number of cycles where we saw N instructions issued:
+[# issued], [# cycles]
+ 0, 7 (1.1%)
+ 1, 306 (50.2%)
+ 2, 297 (48.7%)
+
+
+Retire Control Unit - number of cycles where we saw N instructions retired:
+[# retired], [# cycles]
+ 0, 109 (17.9%)
+ 1, 102 (16.7%)
+ 2, 399 (65.4%)
+
+
+Scheduler's queue usage:
+JALU01, 0/20
+JFPU01, 18/18
+JLSAGU, 0/12
+///////////////////
+
+Based on the verbose report, the pipeline was only able to dispatch two
+instructions 51.5% of the time. The dispatch group was limited to one
+instruction 44.6% of the cycles, which corresponds to 272 cycles.
+
+If we look at section "Dynamic Dispatch Stall Cycles", we can see how counter
+SCHEDQ reports 272 cycles. Counter SCHEDQ is incremented every time the
+dispatch logic is unable to dispatch a full group of two instructions because
+the scheduler's queue is full.
+
+Section "Scheduler's queue usage" shows how the maximum number of buffer entries
+(i.e., scheduler's queue entries) used at runtime for resource JFPU01 reached
+its maximum. Note that AMD Jaguar implements three schedulers:
+ * JALU01 - A scheduler for ALU instructions
+ * JLSAGU - A scheduler for address generation
+ * JFPU01 - A scheduler floating point operations.
+
+The dot-product is a kernel of three floating point instructions (a vector
+multiply followed by two horizontal adds). That explains why only the floating
+point scheduler appears to be used according to section "Scheduler's queue
+usage".
+
+A full scheduler's queue is either caused by data dependency chains, or by a
+sub-optimal usage of hardware resources. Sometimes, resource pressure can be
+mitigated by rewriting the kernel using different instructions that consume
+different scheduler resources. Schedulers with a small queue are less resilient
+to bottlenecks caused by the presence of long data dependencies.
+
+In this example, we can conclude that the IPC is mostly limited by data
+dependencies, and not by resource pressure.
+
+LLVM-MCA instruction flow
+-------------------------
+
+This section describes the instruction flow through the out-of-order backend,
+as well as the functional units involved in the process.
+
+An instruction goes through a default sequence of stages:
+ - Dispatch (Instruction is dispatched to the schedulers).
+ - Issue (Instruction is issued to the processor pipelines).
+ - Write Back (Instruction is executed, and results are written back).
+ - Retire (Instruction is retired; writes are architecturally committed).
+
+The tool only models the out-of-order portion of a processor. Therefore, the
+instruction fetch and decode stages are not modeled. Performance bottlenecks in
+the frontend are not diagnosed by this tool. The tool assumes that instructions
+have all been decoded and placed in a queue. Also, the tool doesn't know
+anything about branch prediction.
+
+The long term plan is to make the process customizable, so that processors can
+define their own. This is a future work.
+
+Instruction Dispatch
+--------------------
+
+During the Dispatch stage, instructions are picked in program order from a queue
+of already decoded instructions, and dispatched in groups to the hardware
+schedulers. The dispatch logic is implemented by class DispatchStage in file
+DispatchStage.h.
+
+The size of a dispatch group depends on the availability of hardware resources,
+and it cannot exceed the value of field 'DispatchWidth' in class DispatchStage.
+Note that field DispatchWidth defaults to the value of field 'IssueWidth' from
+the scheduling model.
+
+Users can override the DispatchWidth value with flag "-dispatch=<N>" (where 'N'
+is an unsigned quantity).
+
+An instruction can be dispatched if:
+ - The size of the dispatch group is smaller than DispatchWidth
+ - There are enough entries in the reorder buffer
+ - There are enough temporary registers to do register renaming
+ - Schedulers are not full.
+
+Since r329067, scheduling models can now optionally specify which register
+files are available on the processor. Class DispatchStage(see DispatchStage.h)
+would use that information to initialize register file descriptors.
+
+By default, if the model doesn't describe register files, the tool
+(optimistically) assumes a single register file with an unbounded number of
+temporary registers. Users can limit the number of temporary registers that
+are globally available for register renaming using flag
+`-register-file-size=<N>`, where N is the number of temporaries. A value of
+zero for N means 'unbounded'. Knowing how many temporaries are available for
+register renaming, the tool can predict dispatch stalls caused by the lack of
+temporaries.
+
+The number of reorder buffer entries consumed by an instruction depends on the
+number of micro-opcodes it specifies in the target scheduling model (see field
+'NumMicroOpcodes' of TableGen class ProcWriteResources and its derived classes;
+TargetSchedule.td).
+
+The reorder buffer is implemented by class RetireControlUnit (see
+DispatchStage.h). Its goal is to track the progress of instructions that are
+"in-flight", and retire instructions in program order. The number of entries
+in the reorder buffer defaults to the value of field 'MicroOpBufferSize' from
+the target scheduling model.
+
+Instructions that are dispatched to the schedulers consume scheduler buffer
+entries. The tool queries the scheduling model to figure out the set of
+buffered resources consumed by an instruction. Buffered resources are treated
+like "scheduler" resources, and the field 'BufferSize' (from the processor
+resource TableGen definition) defines the size of the scheduler's queue.
+
+Zero latency instructions (for example NOP instructions) don't consume scheduler
+resources. However, those instructions still reserve a number of slots in the
+reorder buffer.
+
+Instruction Issue
+-----------------
+
+As mentioned in the previous section, each scheduler resource implements a queue
+of instructions. An instruction has to wait in the scheduler's queue until
+input register operands become available. Only at that point, does the
+instruction becomes eligible for execution and may be issued (potentially
+out-of-order) to a pipeline for execution.
+
+Instruction latencies can be computed by the tool with the help of the
+scheduling model; latency values are defined by the scheduling model through
+ProcWriteResources objects.
+
+Class Scheduler (see file Scheduler.h) knows how to emulate multiple processor
+schedulers. A Scheduler is responsible for tracking data dependencies, and
+dynamically select which processor resources are consumed/used by instructions.
+
+Internally, the Scheduler class delegates the management of processor resource
+units and resource groups to the ResourceManager class. ResourceManager is also
+responsible for selecting resource units that are effectively consumed by
+instructions. For example, if an instruction consumes 1cy of a resource group,
+the ResourceManager object selects one of the available units from the group; by
+default, it uses a round-robin selector to guarantee that resource usage is
+uniformly distributed between all units of a group.
+
+Internally, class Scheduler implements three instruction queues:
+ - WaitQueue: a queue of instructions whose operands are not ready yet.
+ - ReadyQueue: a queue of instructions ready to execute.
+ - IssuedQueue: a queue of instructions executing.
+
+Depending on the operands availability, instructions that are dispatched to the
+Scheduler are either placed into the WaitQueue or into the ReadyQueue.
+
+Every cycle, class Scheduler checks if instructions can be moved from the
+WaitQueue to the ReadyQueue, and if instructions from the ReadyQueue can be
+issued to the underlying pipelines. The algorithm prioritizes older
+instructions over younger instructions.
+
+Objects of class ResourceState (see Scheduler.h) describe processor resources.
+There is an instance of class ResourceState for each single processor resource
+specified by the scheduling model. A ResourceState object for a processor
+resource with multiple units dynamically tracks the availability of every single
+unit. For example, the ResourceState of a resource group tracks the
+availability of every resource in that group. Internally, ResourceState
+implements a round-robin selector to dynamically pick the next unit to use from
+the group.
+
+Write-Back and Retire Stage
+---------------------------
+
+Issued instructions are moved from the ReadyQueue to the IssuedQueue. There,
+instructions wait until they reach the write-back stage. At that point, they
+get removed from the queue and the retire control unit is notified.
+
+On the event of "instruction executed", the retire control unit flags the
+instruction as "ready to retire".
+
+Instruction are retired in program order; an "instruction retired" event is sent
+to the register file which frees the temporary registers allocated for the
+instruction at register renaming stage.
+
+Load/Store Unit and Memory Consistency Model
+--------------------------------------------
+
+The tool attempts to emulate out-of-order execution of memory operations. Class
+LSUnit (see file LSUnit.h) emulates a load/store unit implementing queues for
+speculative execution of loads and stores.
+
+Each load (or store) consumes an entry in the load (or store) queue. The number
+of slots in the load/store queues is unknown by the tool, since there is no
+mention of it in the scheduling model. In practice, users can specify flag
+`-lqueue=N` (vic. `-squeue=N`) to limit the number of entries in the queue to be
+equal to exactly N (an unsigned value). If N is zero, then the tool assumes an
+unbounded queue (this is the default).
+
+LSUnit implements a relaxed consistency model for memory loads and stores. The
+rules are:
+1) A younger load is allowed to pass an older load only if there is no
+ intervening store in between the two loads.
+2) An younger store is not allowed to pass an older store.
+3) A younger store is not allowed to pass an older load.
+4) A younger load is allowed to pass an older store provided that the load does
+ not alias with the store.
+
+By default, this class conservatively (i.e., pessimistically) assumes that loads
+always may-alias store operations. Essentially, this LSUnit doesn't perform
+any sort of alias analysis to rule out cases where loads and stores don't
+overlap with each other. The downside of this approach however is that younger
+loads are never allowed to pass older stores. To make it possible for a
+younger load to pass an older store, users can use the command line flag
+-noalias. Under 'noalias', a younger load is always allowed to pass an older
+store.
+
+Note that, in the case of write-combining memory, rule 2. could be relaxed a bit
+to allow reordering of non-aliasing store operations. That being said, at the
+moment, there is no way to further relax the memory model (flag -noalias is the
+only option). Essentially, there is no option to specify a different memory
+type (for example: write-back, write-combining, write-through; etc.) and
+consequently to weaken or strengthen the memory model.
+
+Other limitations are:
+ * LSUnit doesn't know when store-to-load forwarding may occur.
+ * LSUnit doesn't know anything about the cache hierarchy and memory types.
+ * LSUnit doesn't know how to identify serializing operations and memory fences.
+
+No assumption is made on the store buffer size. As mentioned before, LSUnit
+conservatively assumes a may-alias relation between loads and stores, and it
+doesn't attempt to identify cases where store-to-load forwarding would occur in
+practice.
+
+LSUnit doesn't attempt to predict whether a load or store hits or misses the L1
+cache. It only knows if an instruction "MayLoad" and/or "MayStore". For loads,
+the scheduling model provides an "optimistic" load-to-use latency (which usually
+matches the load-to-use latency for when there is a hit in the L1D).
+
+Class MCInstrDesc in LLVM doesn't know about serializing operations, nor
+memory-barrier like instructions. LSUnit conservatively assumes that an
+instruction which has both 'MayLoad' and 'UnmodeledSideEffects' behaves like a
+"soft" load-barrier. That means, it serializes loads without forcing a flush of
+the load queue. Similarly, instructions flagged with both 'MayStore' and
+'UnmodeledSideEffects' are treated like store barriers. A full memory barrier
+is a 'MayLoad' and 'MayStore' instruction with 'UnmodeledSideEffects'. This is
+inaccurate, but it is the best that we can do at the moment with the current
+information available in LLVM.
+
+A load/store barrier consumes one entry of the load/store queue. A load/store
+barrier enforces ordering of loads/stores. A younger load cannot pass a load
+barrier. Also, a younger store cannot pass a store barrier. A younger load has
+to wait for the memory/load barrier to execute. A load/store barrier is
+"executed" when it becomes the oldest entry in the load/store queue(s). That
+also means, by construction, all the older loads/stores have been executed.
+
+In conclusion the full set of rules is:
+ 1. A store may not pass a previous store.
+ 2. A load may not pass a previous store unless flag 'NoAlias' is set.
+ 3. A load may pass a previous load.
+ 4. A store may not pass a previous load (regardless of flag 'NoAlias').
+ 5. A load has to wait until an older load barrier is fully executed.
+ 6. A store has to wait until an older store barrier is fully executed.
+
+Known limitations
+-----------------
+Previous sections described cases where the tool is missing information to give
+an accurate report. For example, the first sections of this document explained
+how the lack of knowledge about the processor negatively affects the performance
+analysis. The lack of knowledge is often a consequence of how scheduling models
+are defined; as mentioned before, scheduling models intentionally don't describe
+processors in fine details. That being said, the LLVM machine model can be
+extended to expose more details, as long as they are opt-in for targets.
+
+The accuracy of the performance analysis is also affected by assumptions made by
+the processor model used by the tool.
+
+Most recent Intel and AMD processors implement dedicated LoopBuffer/OpCache in
+the hardware frontend to speedup the throughput in the presence of tight loops.
+The presence of these buffers complicates the decoding logic, and requires
+knowledge on the branch predictor too. Class 'SchedMachineModel' in TableGen
+provides a field named 'LoopMicroOpBufferSize' which is used to describe loop
+buffers. However, the purpose of that field is to enable loop unrolling of
+tight loops; essentially, it affects the cost model used by pass loop-unroll.
+
+At the current state, the tool only describes the out-of-order portion of a
+processor, and consequently doesn't try to predict the frontend throughput. That
+being said, this tool could be definitely extended in future to also account for
+the hardware frontend when doing performance analysis. This would inevitably
+require extra (extensive) processor knowledge related to all the available
+decoding paths in the hardware frontend, as well as branch prediction.
+
+Currently, the tool assumes a zero-latency "perfect" fetch&decode
+stage; the full sequence of decoded instructions is immediately visible to the
+dispatch logic from the start.
+
+The tool doesn't know about simultaneous mutithreading. According to the tool,
+processor resources are not statically/dynamically partitioned. Processor
+resources are fully available to the hardware thread executing the
+microbenchmark.
+
+The execution model implemented by this tool assumes that instructions are
+firstly dispatched in groups to hardware schedulers, and then issued to
+pipelines for execution. The model assumes dynamic scheduling of instructions.
+Instructions are placed in a queue and potentially executed out-of-order (based
+on the operand availability). The dispatch stage is definitely distinct from the
+issue stage. This will change in future; as mentioned in the first section, the
+end goal is to let processors customize the process.
+
+This model doesn't correctly describe processors where the dispatch/issue is a
+single stage. This is what happens for example in VLIW processors, where
+instructions are packaged and statically scheduled at compile time; it is up to
+the compiler to predict the latency of instructions and package issue groups
+accordingly. For such targets, there is no dynamic scheduling done by the
+hardware.
+
+Existing classes (DispatchStage, Scheduler, etc.) could be extended/adapted to
+support processors with a single dispatch/issue stage. The execution flow would
+require some changes in the way how existing components (i.e., DispatchStage,
+Scheduler, etc.) interact. This can be a future development.
+
+The following sections describes other known limitations. The goal is not to
+provide an extensive list of limitations; we want to report what we believe are
+the most important limitations, and suggest possible methods to overcome them.
+
+Load/Store barrier instructions and serializing operations
+----------------------------------------------------------
+Section "Load/Store Unit and Memory Consistency Model" already mentioned how
+LLVM doesn't know about serializing operations and memory barriers. Most of it
+boils down to the fact that class MCInstrDesc (intentionally) doesn't expose
+those properties. Instead, both serializing operations and memory barriers
+"have side-effects" according to MCInstrDesc. That is because, at least for
+scheduling purposes, knowing that an instruction has unmodeled side effects is
+often enough to treat the instruction like a compiler scheduling barrier.
+
+A performance analysis tool could use the extra knowledge on barriers and
+serializing operations to generate a more accurate performance report. One way
+to improve this is by reserving a couple of bits in field 'Flags' from class
+MCInstrDesc: one bit for barrier operations, and another bit to mark
+instructions as serializing operations.
+
+Lack of support for instruction itineraries
+-------------------------------------------
+The current version of the tool doesn't know how to process instruction
+itineraries. This is probably one of the most important limitations, since it
+affects a few out-of-order processors in LLVM.
+
+As mentioned in section 'Instruction Issue', class Scheduler delegates to an
+instance of class ResourceManager the handling of processor resources.
+ResourceManager is where most of the scheduling logic is implemented.
+
+Adding support for instruction itineraries requires that we teach
+ResourceManager how to handle functional units and instruction stages. This
+development can be a future extension, and it would probably require a few
+changes to the ResourceManager interface.
+
+Instructions that affect control flow are not correctly modeled
+---------------------------------------------------------------
+Examples of instructions that affect the control flow are: return, indirect
+branches, calls, etc. The tool doesn't try to predict/evaluate branch targets.
+In particular, the tool doesn't model any sort of branch prediction, nor does it
+attempt to track changes to the program counter. The tool always assumes that
+the input assembly sequence is the body of a microbenchmark (a simple loop
+executed for a number of iterations). The "next" instruction in sequence is
+always the next instruction to dispatch.
+
+Call instructions default to an arbitrary high latency of 100cy. A warning is
+generated if the tool encounters a call instruction in the sequence. Return
+instructions are not evaluated, and therefore control flow is not affected.
+However, the tool still queries the processor scheduling model to obtain latency
+information for instructions that affect the control flow.
+
+Known limitations on X86 processors
+-----------------------------------
+
+1) Partial register updates versus full register updates.
+
+On x86-64, a 32-bit GPR write fully updates the super-register. Example:
+ add %edi %eax ## eax += edi
+
+Here, register %eax aliases the lower half of 64-bit register %rax. On x86-64,
+register %rax is fully updated by the 'add' (the upper half of %rax is zeroed).
+Essentially, it "kills" any previous definition of (the upper half of) register
+%rax.
+
+On the other hand, 8/16 bit register writes only perform a so-called "partial
+register update". Example:
+ add %di, %ax ## ax += di
+
+Here, register %eax is only partially updated. To be more specific, the lower
+half of %eax is set, and the upper half is left unchanged. There is also no
+change in the upper 48 bits of register %rax.
+
+To get accurate performance analysis, the tool has to know which instructions
+perform a partial register update, and which instructions fully update the
+destination's super-register.
+
+One way to expose this information is (again) via TableGen. For example, we
+could add a flag in the TableGen instruction class to tag instructions that
+perform partial register updates. Something like this: 'bit
+hasPartialRegisterUpdate = 1'. However, this would force a `let
+hasPartialRegisterUpdate = 0` on several instruction definitions.
+
+Another approach is to have a MCSubtargetInfo hook similar to this:
+ virtual bool updatesSuperRegisters(unsigned short opcode) { return false; }
+
+Targets will be able to override this method if needed. Again, this is just an
+idea. But the plan is to have this fixed as a future development.
+
+2) Macro Op fusion.
+
+The tool doesn't know about macro-op fusion. On modern x86 processors, a
+'cmp/test' followed by a 'jmp' is fused into a single macro operation. The
+advantage is that the fused pair only consumes a single slot in the dispatch
+group.
+
+As a future development, the tool should be extended to address macro-fusion.
+Ideally, we could have LLVM generate a table enumerating all the opcode pairs
+that can be fused together. That table could be exposed to the tool via the
+MCSubtargetInfo interface. This is just an idea; there may be better ways to
+implement this.
+
+3) Intel processors: mixing legacy SSE with AVX instructions.
+
+On modern Intel processors with AVX, mixing legacy SSE code with AVX code
+negatively impacts the performance. The tool is not aware of this issue, and
+the performance penalty is not accounted when doing the analysis. This is
+something that we would like to improve in future.
+
+4) Zero-latency register moves and Zero-idioms.
+
+Most modern AMD/Intel processors know how to optimize out register-register
+moves and zero idioms at register renaming stage. The tool doesn't know
+about these patterns, and this may negatively impact the performance analysis.
+
+Known design problems
+---------------------
+This section describes two design issues that are currently affecting the tool.
+The long term plan is to "fix" these issues.
+Both limitations would be easily fixed if we teach the tool how to directly
+manipulate MachineInstr objects (instead of MCInst objects).
+
+1) Variant instructions not correctly modeled.
+
+The tool doesn't know how to analyze instructions with a "variant" scheduling
+class descriptor. A variant scheduling class needs to be resolved dynamically.
+The "actual" scheduling class often depends on the subtarget, as well as
+properties of the specific MachineInstr object.
+
+Unfortunately, the tool manipulates MCInst, and it doesn't know anything about
+MachineInstr. As a consequence, the tool cannot use the existing machine
+subtarget hooks that are normally used to resolve the variant scheduling class.
+This is a major design issue which mostly affects ARM/AArch64 targets. It
+mostly boils down to the fact that the existing scheduling framework was meant
+to work for MachineInstr.
+
+When the tool encounters a "variant" instruction, it assumes a generic 1cy
+latency. However, the tool would not be able to tell which processor resources
+are effectively consumed by the variant instruction.
+
+2) MCInst and MCInstrDesc.
+
+Performance analysis tools require data dependency information to correctly
+predict the runtime performance of the code. This tool must always be able to
+obtain the set of implicit/explicit register defs/uses for every instruction of
+the input assembly sequence.
+
+In the first section of this document, it was mentioned how the tool takes as
+input an assembly sequence. That sequence is parsed into a MCInst sequence with
+the help of assembly parsers available from the targets.
+
+A MCInst is a very low-level instruction representation. The tool can inspect
+the MCOperand sequence of an MCInst to identify register operands. However,
+there is no way to tell register operands that are definitions from register
+operands that are uses.
+
+In LLVM, class MCInstrDesc is used to fully describe target instructions and
+their operands. The opcode of a machine instruction (a MachineInstr object) can
+be used to query the instruction set through method `MCInstrInfo::get' to obtain
+the associated MCInstrDesc object.
+
+However class MCInstrDesc describes properties and operands of MachineInstr
+objects. Essentially, MCInstrDesc is not meant to be used to describe MCInst
+objects. To be more specific, MCInstrDesc objects are automatically generated
+via TableGen from the instruction set description in the target .td files. For
+example, field `MCInstrDesc::NumDefs' is always equal to the cardinality of the
+`(outs)` set from the TableGen instruction definition.
+
+By construction, register definitions always appear at the beginning of the
+MachineOperands list in MachineInstr. Basically, the (outs) are the first
+operands of a MachineInstr, and the (ins) will come after in the machine operand
+list. Knowing the number of register definitions is enough to identify
+all the register operands that are definitions.
+
+In a normal compilation process, MCInst objects are generated from MachineInstr
+objects through a lowering step. By default the lowering logic simply iterates
+over the machine operands of a MachineInstr, and converts/expands them into
+equivalent MCOperand objects.
+
+The default lowering strategy has the advantage of preserving all of the above
+mentioned assumptions on the machine operand sequence. That means, register
+definitions would still be at the beginning of the MCOperand sequence, and
+register uses would come after.
+
+Targets may still define custom lowering routines for specific opcodes. Some of
+these routines may lower operands in a way that potentially breaks (some of) the
+assumptions on the machine operand sequence which were valid for MachineInstr.
+Luckily, this is not the most common form of lowering done by the targets, and
+the vast majority of the MachineInstr are lowered based on the default strategy
+which preserves the original machine operand sequence. This is especially true
+for x86, where the custom lowering logic always preserves the original (i.e.,
+from the MachineInstr) operand sequence.
+
+This tool currently works under the strong (and potentially incorrect)
+assumption that register def/uses in a MCInst can always be identified by
+querying the machine instruction descriptor for the opcode. This assumption made
+it possible to develop this tool and get good numbers at least for the
+processors available in the x86 backend.
+
+That being said, the analysis is still potentially incorrect for other targets.
+So we plan (with the help of the community) to find a proper mechanism to map
+when possible MCOperand indices back to MachineOperand indices of the equivalent
+MachineInstr. This would be equivalent to describing changes made by the
+lowering step which affected the operand sequence. For example, we could have an
+index for every register MCOperand (or -1, if the operand didn't exist in the
+original MachineInstr). The mapping could look like this <0,1,3,2>. Here,
+MCOperand #2 was obtained from the lowering of MachineOperand #3. etc.
+
+This information could be automatically generated via TableGen for all the
+instructions whose custom lowering step breaks assumptions made by the tool on
+the register operand sequence (In general, these instructions should be the
+minority of a target's instruction set). Unfortunately, we don't have that
+information now. As a consequence, we assume that the number of explicit
+register definitions is the same number specified in MCInstrDesc. We also
+assume that register definitions always come first in the operand sequence.
+
+In conclusion: these are for now the strong assumptions made by the tool:
+ * The number of explicit and implicit register definitions in a MCInst
+ matches the number of explicit and implicit definitions specified by the
+ MCInstrDesc object.
+ * Register uses always come after register definitions.
+ * If an opcode specifies an optional definition, then the optional
+ definition is always the last register operand in the sequence.
+
+Note that some of the information accessible from the MCInstrDesc is always
+valid for MCInst. For example: implicit register defs, implicit register uses
+and 'MayLoad/MayStore/HasUnmodeledSideEffects' opcode properties still apply to
+MCInst. The tool knows about this, and uses that information during its
+analysis.
+
+Future work
+-----------
+ * Address limitations (described in section "Known limitations").
+ * Let processors specify the selection strategy for processor resource groups
+ and resources with multiple units. The tool currently uses a round-robin
+ selector to pick the next resource to use.
+ * Address limitations specifically described in section "Known limitations on
+ X86 processors".
+ * Address design issues identified in section "Known design problems".
+ * Define a standard interface for "Views". This would let users customize the
+ performance report generated by the tool.
+
+When interfaces are mature/stable:
+ * Move the logic into a library. This will enable a number of other
+ interesting use cases.
+
+Work is currently tracked on https://bugs.llvm.org. llvm-mca bugs are tagged
+with prefix [llvm-mca]. You can easily find the full list of open bugs if you
+search for that tag.
diff --git a/contrib/llvm/tools/llvm-mca/RegisterFile.cpp b/contrib/llvm/tools/llvm-mca/RegisterFile.cpp
new file mode 100644
index 000000000000..44de105b8996
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RegisterFile.cpp
@@ -0,0 +1,343 @@
+//===--------------------- RegisterFile.cpp ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a register mapping file class. This class is responsible
+/// for managing hardware register files and the tracking of data dependencies
+/// between registers.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RegisterFile.h"
+#include "Instruction.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+RegisterFile::RegisterFile(const llvm::MCSchedModel &SM,
+ const llvm::MCRegisterInfo &mri, unsigned NumRegs)
+ : MRI(mri), RegisterMappings(mri.getNumRegs(),
+ {WriteRef(), {IndexPlusCostPairTy(0, 1), 0}}) {
+ initialize(SM, NumRegs);
+}
+
+void RegisterFile::initialize(const MCSchedModel &SM, unsigned NumRegs) {
+ // Create a default register file that "sees" all the machine registers
+ // declared by the target. The number of physical registers in the default
+ // register file is set equal to `NumRegs`. A value of zero for `NumRegs`
+ // means: this register file has an unbounded number of physical registers.
+ addRegisterFile({} /* all registers */, NumRegs);
+ if (!SM.hasExtraProcessorInfo())
+ return;
+
+ // For each user defined register file, allocate a RegisterMappingTracker
+ // object. The size of every register file, as well as the mapping between
+ // register files and register classes is specified via tablegen.
+ const MCExtraProcessorInfo &Info = SM.getExtraProcessorInfo();
+ for (unsigned I = 0, E = Info.NumRegisterFiles; I < E; ++I) {
+ const MCRegisterFileDesc &RF = Info.RegisterFiles[I];
+ // Skip invalid register files with zero physical registers.
+ unsigned Length = RF.NumRegisterCostEntries;
+ if (!RF.NumPhysRegs)
+ continue;
+ // The cost of a register definition is equivalent to the number of
+ // physical registers that are allocated at register renaming stage.
+ const MCRegisterCostEntry *FirstElt =
+ &Info.RegisterCostTable[RF.RegisterCostEntryIdx];
+ addRegisterFile(ArrayRef<MCRegisterCostEntry>(FirstElt, Length),
+ RF.NumPhysRegs);
+ }
+}
+
+void RegisterFile::addRegisterFile(ArrayRef<MCRegisterCostEntry> Entries,
+ unsigned NumPhysRegs) {
+ // A default register file is always allocated at index #0. That register file
+ // is mainly used to count the total number of mappings created by all
+ // register files at runtime. Users can limit the number of available physical
+ // registers in register file #0 through the command line flag
+ // `-register-file-size`.
+ unsigned RegisterFileIndex = RegisterFiles.size();
+ RegisterFiles.emplace_back(NumPhysRegs);
+
+ // Special case where there is no register class identifier in the set.
+ // An empty set of register classes means: this register file contains all
+ // the physical registers specified by the target.
+ // We optimistically assume that a register can be renamed at the cost of a
+ // single physical register. The constructor of RegisterFile ensures that
+ // a RegisterMapping exists for each logical register defined by the Target.
+ if (Entries.empty())
+ return;
+
+ // Now update the cost of individual registers.
+ for (const MCRegisterCostEntry &RCE : Entries) {
+ const MCRegisterClass &RC = MRI.getRegClass(RCE.RegisterClassID);
+ for (const MCPhysReg Reg : RC) {
+ RegisterRenamingInfo &Entry = RegisterMappings[Reg].second;
+ IndexPlusCostPairTy &IPC = Entry.IndexPlusCost;
+ if (IPC.first && IPC.first != RegisterFileIndex) {
+ // The only register file that is allowed to overlap is the default
+ // register file at index #0. The analysis is inaccurate if register
+ // files overlap.
+ errs() << "warning: register " << MRI.getName(Reg)
+ << " defined in multiple register files.";
+ }
+ IPC = std::make_pair(RegisterFileIndex, RCE.Cost);
+ Entry.RenameAs = Reg;
+
+ // Assume the same cost for each sub-register.
+ for (MCSubRegIterator I(Reg, &MRI); I.isValid(); ++I) {
+ RegisterRenamingInfo &OtherEntry = RegisterMappings[*I].second;
+ if (!OtherEntry.IndexPlusCost.first &&
+ (!OtherEntry.RenameAs ||
+ MRI.isSuperRegister(*I, OtherEntry.RenameAs))) {
+ OtherEntry.IndexPlusCost = IPC;
+ OtherEntry.RenameAs = Reg;
+ }
+ }
+ }
+ }
+}
+
+void RegisterFile::allocatePhysRegs(const RegisterRenamingInfo &Entry,
+ MutableArrayRef<unsigned> UsedPhysRegs) {
+ unsigned RegisterFileIndex = Entry.IndexPlusCost.first;
+ unsigned Cost = Entry.IndexPlusCost.second;
+ if (RegisterFileIndex) {
+ RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
+ RMT.NumUsedPhysRegs += Cost;
+ UsedPhysRegs[RegisterFileIndex] += Cost;
+ }
+
+ // Now update the default register mapping tracker.
+ RegisterFiles[0].NumUsedPhysRegs += Cost;
+ UsedPhysRegs[0] += Cost;
+}
+
+void RegisterFile::freePhysRegs(const RegisterRenamingInfo &Entry,
+ MutableArrayRef<unsigned> FreedPhysRegs) {
+ unsigned RegisterFileIndex = Entry.IndexPlusCost.first;
+ unsigned Cost = Entry.IndexPlusCost.second;
+ if (RegisterFileIndex) {
+ RegisterMappingTracker &RMT = RegisterFiles[RegisterFileIndex];
+ RMT.NumUsedPhysRegs -= Cost;
+ FreedPhysRegs[RegisterFileIndex] += Cost;
+ }
+
+ // Now update the default register mapping tracker.
+ RegisterFiles[0].NumUsedPhysRegs -= Cost;
+ FreedPhysRegs[0] += Cost;
+}
+
+void RegisterFile::addRegisterWrite(WriteRef Write,
+ MutableArrayRef<unsigned> UsedPhysRegs,
+ bool ShouldAllocatePhysRegs) {
+ WriteState &WS = *Write.getWriteState();
+ unsigned RegID = WS.getRegisterID();
+ assert(RegID && "Adding an invalid register definition?");
+
+ LLVM_DEBUG({
+ dbgs() << "RegisterFile: addRegisterWrite [ " << Write.getSourceIndex()
+ << ", " << MRI.getName(RegID) << "]\n";
+ });
+
+ // If RenameAs is equal to RegID, then RegID is subject to register renaming
+ // and false dependencies on RegID are all eliminated.
+
+ // If RenameAs references the invalid register, then we optimistically assume
+ // that it can be renamed. In the absence of tablegen descriptors for register
+ // files, RenameAs is always set to the invalid register ID. In all other
+ // cases, RenameAs must be either equal to RegID, or it must reference a
+ // super-register of RegID.
+
+ // If RenameAs is a super-register of RegID, then a write to RegID has always
+ // a false dependency on RenameAs. The only exception is for when the write
+ // implicitly clears the upper portion of the underlying register.
+ // If a write clears its super-registers, then it is renamed as `RenameAs`.
+ const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
+ if (RRI.RenameAs && RRI.RenameAs != RegID) {
+ RegID = RRI.RenameAs;
+ const WriteRef &OtherWrite = RegisterMappings[RegID].first;
+
+ if (!WS.clearsSuperRegisters()) {
+ // The processor keeps the definition of `RegID` together with register
+ // `RenameAs`. Since this partial write is not renamed, no physical
+ // register is allocated.
+ ShouldAllocatePhysRegs = false;
+
+ if (OtherWrite.getSourceIndex() != Write.getSourceIndex()) {
+ // This partial write has a false dependency on RenameAs.
+ WS.setDependentWrite(OtherWrite.getWriteState());
+ }
+ }
+ }
+
+ // Update the mapping for register RegID including its sub-registers.
+ RegisterMappings[RegID].first = Write;
+ for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I)
+ RegisterMappings[*I].first = Write;
+
+ // No physical registers are allocated for instructions that are optimized in
+ // hardware. For example, zero-latency data-dependency breaking instructions
+ // don't consume physical registers.
+ if (ShouldAllocatePhysRegs)
+ allocatePhysRegs(RegisterMappings[RegID].second, UsedPhysRegs);
+
+ if (!WS.clearsSuperRegisters())
+ return;
+
+ for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I)
+ RegisterMappings[*I].first = Write;
+}
+
+void RegisterFile::removeRegisterWrite(const WriteState &WS,
+ MutableArrayRef<unsigned> FreedPhysRegs,
+ bool ShouldFreePhysRegs) {
+ unsigned RegID = WS.getRegisterID();
+
+ assert(RegID != 0 && "Invalidating an already invalid register?");
+ assert(WS.getCyclesLeft() != UNKNOWN_CYCLES &&
+ "Invalidating a write of unknown cycles!");
+ assert(WS.getCyclesLeft() <= 0 && "Invalid cycles left for this write!");
+
+ unsigned RenameAs = RegisterMappings[RegID].second.RenameAs;
+ if (RenameAs && RenameAs != RegID) {
+ RegID = RenameAs;
+
+ if (!WS.clearsSuperRegisters()) {
+ // Keep the definition of `RegID` together with register `RenameAs`.
+ ShouldFreePhysRegs = false;
+ }
+ }
+
+ if (ShouldFreePhysRegs)
+ freePhysRegs(RegisterMappings[RegID].second, FreedPhysRegs);
+
+ WriteRef &WR = RegisterMappings[RegID].first;
+ if (WR.getWriteState() == &WS)
+ WR.invalidate();
+
+ for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
+ WriteRef &OtherWR = RegisterMappings[*I].first;
+ if (OtherWR.getWriteState() == &WS)
+ OtherWR.invalidate();
+ }
+
+ if (!WS.clearsSuperRegisters())
+ return;
+
+ for (MCSuperRegIterator I(RegID, &MRI); I.isValid(); ++I) {
+ WriteRef &OtherWR = RegisterMappings[*I].first;
+ if (OtherWR.getWriteState() == &WS)
+ OtherWR.invalidate();
+ }
+}
+
+void RegisterFile::collectWrites(SmallVectorImpl<WriteRef> &Writes,
+ unsigned RegID) const {
+ assert(RegID && RegID < RegisterMappings.size());
+ LLVM_DEBUG(dbgs() << "RegisterFile: collecting writes for register "
+ << MRI.getName(RegID) << '\n');
+ const WriteRef &WR = RegisterMappings[RegID].first;
+ if (WR.isValid())
+ Writes.push_back(WR);
+
+ // Handle potential partial register updates.
+ for (MCSubRegIterator I(RegID, &MRI); I.isValid(); ++I) {
+ const WriteRef &WR = RegisterMappings[*I].first;
+ if (WR.isValid())
+ Writes.push_back(WR);
+ }
+
+ // Remove duplicate entries and resize the input vector.
+ llvm::sort(Writes.begin(), Writes.end(),
+ [](const WriteRef &Lhs, const WriteRef &Rhs) {
+ return Lhs.getWriteState() < Rhs.getWriteState();
+ });
+ auto It = std::unique(Writes.begin(), Writes.end());
+ Writes.resize(std::distance(Writes.begin(), It));
+
+ LLVM_DEBUG({
+ for (const WriteRef &WR : Writes) {
+ const WriteState &WS = *WR.getWriteState();
+ dbgs() << "[PRF] Found a dependent use of Register "
+ << MRI.getName(WS.getRegisterID()) << " (defined by intruction #"
+ << WR.getSourceIndex() << ")\n";
+ }
+ });
+}
+
+unsigned RegisterFile::isAvailable(ArrayRef<unsigned> Regs) const {
+ SmallVector<unsigned, 4> NumPhysRegs(getNumRegisterFiles());
+
+ // Find how many new mappings must be created for each register file.
+ for (const unsigned RegID : Regs) {
+ const RegisterRenamingInfo &RRI = RegisterMappings[RegID].second;
+ const IndexPlusCostPairTy &Entry = RRI.IndexPlusCost;
+ if (Entry.first)
+ NumPhysRegs[Entry.first] += Entry.second;
+ NumPhysRegs[0] += Entry.second;
+ }
+
+ unsigned Response = 0;
+ for (unsigned I = 0, E = getNumRegisterFiles(); I < E; ++I) {
+ unsigned NumRegs = NumPhysRegs[I];
+ if (!NumRegs)
+ continue;
+
+ const RegisterMappingTracker &RMT = RegisterFiles[I];
+ if (!RMT.NumPhysRegs) {
+ // The register file has an unbounded number of microarchitectural
+ // registers.
+ continue;
+ }
+
+ if (RMT.NumPhysRegs < NumRegs) {
+ // The current register file is too small. This may occur if the number of
+ // microarchitectural registers in register file #0 was changed by the
+ // users via flag -reg-file-size. Alternatively, the scheduling model
+ // specified a too small number of registers for this register file.
+ report_fatal_error(
+ "Not enough microarchitectural registers in the register file");
+ }
+
+ if (RMT.NumPhysRegs < (RMT.NumUsedPhysRegs + NumRegs))
+ Response |= (1U << I);
+ }
+
+ return Response;
+}
+
+#ifndef NDEBUG
+void RegisterFile::dump() const {
+ for (unsigned I = 0, E = MRI.getNumRegs(); I < E; ++I) {
+ const RegisterMapping &RM = RegisterMappings[I];
+ if (!RM.first.getWriteState())
+ continue;
+ const RegisterRenamingInfo &RRI = RM.second;
+ dbgs() << MRI.getName(I) << ", " << I << ", PRF=" << RRI.IndexPlusCost.first
+ << ", Cost=" << RRI.IndexPlusCost.second
+ << ", RenameAs=" << RRI.RenameAs << ", ";
+ RM.first.dump();
+ dbgs() << '\n';
+ }
+
+ for (unsigned I = 0, E = getNumRegisterFiles(); I < E; ++I) {
+ dbgs() << "Register File #" << I;
+ const RegisterMappingTracker &RMT = RegisterFiles[I];
+ dbgs() << "\n TotalMappings: " << RMT.NumPhysRegs
+ << "\n NumUsedMappings: " << RMT.NumUsedPhysRegs << '\n';
+ }
+}
+#endif
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/RegisterFile.h b/contrib/llvm/tools/llvm-mca/RegisterFile.h
new file mode 100644
index 000000000000..349e9789b6ee
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RegisterFile.h
@@ -0,0 +1,172 @@
+//===--------------------- RegisterFile.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a register mapping file class. This class is responsible
+/// for managing hardware register files and the tracking of data dependencies
+/// between registers.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
+#define LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
+
+#include "HardwareUnit.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCSchedule.h"
+
+namespace mca {
+
+class ReadState;
+class WriteState;
+class WriteRef;
+
+/// Manages hardware register files, and tracks register definitions for
+/// register renaming purposes.
+class RegisterFile : public HardwareUnit {
+ const llvm::MCRegisterInfo &MRI;
+
+ // Each register file is associated with an instance of
+ // RegisterMappingTracker.
+ // A RegisterMappingTracker keeps track of the number of physical registers
+ // which have been dynamically allocated by the simulator.
+ struct RegisterMappingTracker {
+ // The total number of physical registers that are available in this
+ // register file for register renaming purpouses. A value of zero for this
+ // field means: this register file has an unbounded number of physical
+ // registers.
+ const unsigned NumPhysRegs;
+ // Number of physical registers that are currently in use.
+ unsigned NumUsedPhysRegs;
+
+ RegisterMappingTracker(unsigned NumPhysRegisters)
+ : NumPhysRegs(NumPhysRegisters), NumUsedPhysRegs(0) {}
+ };
+
+ // A vector of register file descriptors. This set always contains at least
+ // one entry. Entry at index #0 is reserved. That entry describes a register
+ // file with an unbounded number of physical registers that "sees" all the
+ // hardware registers declared by the target (i.e. all the register
+ // definitions in the target specific `XYZRegisterInfo.td` - where `XYZ` is
+ // the target name).
+ //
+ // Users can limit the number of physical registers that are available in
+ // regsiter file #0 specifying command line flag `-register-file-size=<uint>`.
+ llvm::SmallVector<RegisterMappingTracker, 4> RegisterFiles;
+
+ // This type is used to propagate information about the owner of a register,
+ // and the cost of allocating it in the PRF. Register cost is defined as the
+ // number of physical registers consumed by the PRF to allocate a user
+ // register.
+ //
+ // For example: on X86 BtVer2, a YMM register consumes 2 128-bit physical
+ // registers. So, the cost of allocating a YMM register in BtVer2 is 2.
+ using IndexPlusCostPairTy = std::pair<unsigned, unsigned>;
+
+ // Struct RegisterRenamingInfo maps registers to register files.
+ // There is a RegisterRenamingInfo object for every register defined by
+ // the target. RegisteRenamingInfo objects are stored into vector
+ // RegisterMappings, and register IDs can be used to reference them.
+ struct RegisterRenamingInfo {
+ IndexPlusCostPairTy IndexPlusCost;
+ llvm::MCPhysReg RenameAs;
+ };
+
+ // RegisterMapping objects are mainly used to track physical register
+ // definitions. There is a RegisterMapping for every register defined by the
+ // Target. For each register, a RegisterMapping pair contains a descriptor of
+ // the last register write (in the form of a WriteRef object), as well as a
+ // RegisterRenamingInfo to quickly identify owning register files.
+ //
+ // This implementation does not allow overlapping register files. The only
+ // register file that is allowed to overlap with other register files is
+ // register file #0. If we exclude register #0, every register is "owned" by
+ // at most one register file.
+ using RegisterMapping = std::pair<WriteRef, RegisterRenamingInfo>;
+
+ // This map contains one entry for each register defined by the target.
+ std::vector<RegisterMapping> RegisterMappings;
+
+ // This method creates a new register file descriptor.
+ // The new register file owns all of the registers declared by register
+ // classes in the 'RegisterClasses' set.
+ //
+ // Processor models allow the definition of RegisterFile(s) via tablegen. For
+ // example, this is a tablegen definition for a x86 register file for
+ // XMM[0-15] and YMM[0-15], that allows up to 60 renames (each rename costs 1
+ // physical register).
+ //
+ // def FPRegisterFile : RegisterFile<60, [VR128RegClass, VR256RegClass]>
+ //
+ // Here FPRegisterFile contains all the registers defined by register class
+ // VR128RegClass and VR256RegClass. FPRegisterFile implements 60
+ // registers which can be used for register renaming purpose.
+ void
+ addRegisterFile(llvm::ArrayRef<llvm::MCRegisterCostEntry> RegisterClasses,
+ unsigned NumPhysRegs);
+
+ // Consumes physical registers in each register file specified by the
+ // `IndexPlusCostPairTy`. This method is called from `addRegisterMapping()`.
+ void allocatePhysRegs(const RegisterRenamingInfo &Entry,
+ llvm::MutableArrayRef<unsigned> UsedPhysRegs);
+
+ // Releases previously allocated physical registers from the register file(s).
+ // This method is called from `invalidateRegisterMapping()`.
+ void freePhysRegs(const RegisterRenamingInfo &Entry,
+ llvm::MutableArrayRef<unsigned> FreedPhysRegs);
+
+ // Create an instance of RegisterMappingTracker for every register file
+ // specified by the processor model.
+ // If no register file is specified, then this method creates a default
+ // register file with an unbounded number of physical registers.
+ void initialize(const llvm::MCSchedModel &SM, unsigned NumRegs);
+
+public:
+ RegisterFile(const llvm::MCSchedModel &SM, const llvm::MCRegisterInfo &mri,
+ unsigned NumRegs = 0);
+
+ // This method updates the register mappings inserting a new register
+ // definition. This method is also responsible for updating the number of
+ // allocated physical registers in each register file modified by the write.
+ // No physical regiser is allocated when flag ShouldAllocatePhysRegs is set.
+ void addRegisterWrite(WriteRef Write,
+ llvm::MutableArrayRef<unsigned> UsedPhysRegs,
+ bool ShouldAllocatePhysRegs = true);
+
+ // Removes write \param WS from the register mappings.
+ // Physical registers may be released to reflect this update.
+ void removeRegisterWrite(const WriteState &WS,
+ llvm::MutableArrayRef<unsigned> FreedPhysRegs,
+ bool ShouldFreePhysRegs = true);
+
+ // Checks if there are enough physical registers in the register files.
+ // Returns a "response mask" where each bit represents the response from a
+ // different register file. A mask of all zeroes means that all register
+ // files are available. Otherwise, the mask can be used to identify which
+ // register file was busy. This sematic allows us to classify dispatch
+ // stalls caused by the lack of register file resources.
+ //
+ // Current implementation can simulate up to 32 register files (including the
+ // special register file at index #0).
+ unsigned isAvailable(llvm::ArrayRef<unsigned> Regs) const;
+ void collectWrites(llvm::SmallVectorImpl<WriteRef> &Writes,
+ unsigned RegID) const;
+ void updateOnRead(ReadState &RS, unsigned RegID);
+
+ unsigned getNumRegisterFiles() const { return RegisterFiles.size(); }
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+};
+
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_REGISTER_FILE_H
diff --git a/contrib/llvm/tools/llvm-mca/RegisterFileStatistics.cpp b/contrib/llvm/tools/llvm-mca/RegisterFileStatistics.cpp
new file mode 100644
index 000000000000..1b07bf9a3b33
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RegisterFileStatistics.cpp
@@ -0,0 +1,107 @@
+//===--------------------- RegisterFileStatistics.cpp -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the RegisterFileStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RegisterFileStatistics.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+namespace mca {
+
+void RegisterFileStatistics::initializeRegisterFileInfo() {
+ const MCSchedModel &SM = STI.getSchedModel();
+ RegisterFileUsage Empty = {0, 0, 0};
+ if (!SM.hasExtraProcessorInfo()) {
+ // Assume a single register file.
+ RegisterFiles.emplace_back(Empty);
+ return;
+ }
+
+ // Initialize a RegisterFileUsage for every user defined register file, plus
+ // the default register file which is always at index #0.
+ const MCExtraProcessorInfo &PI = SM.getExtraProcessorInfo();
+ // There is always an "InvalidRegisterFile" entry in tablegen. That entry can
+ // be skipped. If there are no user defined register files, then reserve a
+ // single entry for the default register file at index #0.
+ unsigned NumRegFiles = std::max(PI.NumRegisterFiles, 1U);
+ RegisterFiles.resize(NumRegFiles);
+ std::fill(RegisterFiles.begin(), RegisterFiles.end(), Empty);
+}
+
+void RegisterFileStatistics::onEvent(const HWInstructionEvent &Event) {
+ switch (Event.Type) {
+ default:
+ break;
+ case HWInstructionEvent::Retired: {
+ const auto &RE = static_cast<const HWInstructionRetiredEvent &>(Event);
+ for (unsigned I = 0, E = RegisterFiles.size(); I < E; ++I)
+ RegisterFiles[I].CurrentlyUsedMappings -= RE.FreedPhysRegs[I];
+ break;
+ }
+ case HWInstructionEvent::Dispatched: {
+ const auto &DE = static_cast<const HWInstructionDispatchedEvent &>(Event);
+ for (unsigned I = 0, E = RegisterFiles.size(); I < E; ++I) {
+ RegisterFileUsage &RFU = RegisterFiles[I];
+ unsigned NumUsedPhysRegs = DE.UsedPhysRegs[I];
+ RFU.CurrentlyUsedMappings += NumUsedPhysRegs;
+ RFU.TotalMappings += NumUsedPhysRegs;
+ RFU.MaxUsedMappings =
+ std::max(RFU.MaxUsedMappings, RFU.CurrentlyUsedMappings);
+ }
+ }
+ }
+}
+
+void RegisterFileStatistics::printView(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+
+ TempStream << "\n\nRegister File statistics:";
+ const RegisterFileUsage &GlobalUsage = RegisterFiles[0];
+ TempStream << "\nTotal number of mappings created: "
+ << GlobalUsage.TotalMappings;
+ TempStream << "\nMax number of mappings used: "
+ << GlobalUsage.MaxUsedMappings << '\n';
+
+ for (unsigned I = 1, E = RegisterFiles.size(); I < E; ++I) {
+ const RegisterFileUsage &RFU = RegisterFiles[I];
+ // Obtain the register file descriptor from the scheduling model.
+ assert(STI.getSchedModel().hasExtraProcessorInfo() &&
+ "Unable to find register file info!");
+ const MCExtraProcessorInfo &PI =
+ STI.getSchedModel().getExtraProcessorInfo();
+ assert(I <= PI.NumRegisterFiles && "Unexpected register file index!");
+ const MCRegisterFileDesc &RFDesc = PI.RegisterFiles[I];
+ // Skip invalid register files.
+ if (!RFDesc.NumPhysRegs)
+ continue;
+
+ TempStream << "\n* Register File #" << I;
+ TempStream << " -- " << StringRef(RFDesc.Name) << ':';
+ TempStream << "\n Number of physical registers: ";
+ if (!RFDesc.NumPhysRegs)
+ TempStream << "unbounded";
+ else
+ TempStream << RFDesc.NumPhysRegs;
+ TempStream << "\n Total number of mappings created: "
+ << RFU.TotalMappings;
+ TempStream << "\n Max number of mappings used: "
+ << RFU.MaxUsedMappings << '\n';
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/RegisterFileStatistics.h b/contrib/llvm/tools/llvm-mca/RegisterFileStatistics.h
new file mode 100644
index 000000000000..cbe816cd3332
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RegisterFileStatistics.h
@@ -0,0 +1,67 @@
+//===--------------------- RegisterFileStatistics.h -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This view collects and prints register file usage statistics.
+///
+/// Example (-mcpu=btver2):
+/// ========================
+///
+/// Register File statistics:
+/// Total number of mappings created: 6
+/// Max number of mappings used: 3
+///
+/// * Register File #1 -- FpuPRF:
+/// Number of physical registers: 72
+/// Total number of mappings created: 0
+/// Max number of mappings used: 0
+///
+/// * Register File #2 -- IntegerPRF:
+/// Number of physical registers: 64
+/// Total number of mappings created: 6
+/// Max number of mappings used: 3
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H
+#define LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H
+
+#include "View.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+
+namespace mca {
+
+class RegisterFileStatistics : public View {
+ const llvm::MCSubtargetInfo &STI;
+
+ // Used to track the number of physical registers used in a register file.
+ struct RegisterFileUsage {
+ unsigned TotalMappings;
+ unsigned MaxUsedMappings;
+ unsigned CurrentlyUsedMappings;
+ };
+
+ // There is one entry for each register file implemented by the processor.
+ llvm::SmallVector<RegisterFileUsage, 4> RegisterFiles;
+
+ void initializeRegisterFileInfo();
+
+public:
+ RegisterFileStatistics(const llvm::MCSubtargetInfo &sti) : STI(sti) {
+ initializeRegisterFileInfo();
+ }
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void printView(llvm::raw_ostream &OS) const override;
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/ResourcePressureView.cpp b/contrib/llvm/tools/llvm-mca/ResourcePressureView.cpp
new file mode 100644
index 000000000000..fe9d5b7fabc8
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/ResourcePressureView.cpp
@@ -0,0 +1,171 @@
+//===--------------------- ResourcePressureView.cpp -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements methods in the ResourcePressureView interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "ResourcePressureView.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace mca {
+
+using namespace llvm;
+
+void ResourcePressureView::initialize() {
+ // Populate the map of resource descriptors.
+ unsigned R2VIndex = 0;
+ const MCSchedModel &SM = STI.getSchedModel();
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ // Skip groups and invalid resources with zero units.
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ Resource2VecIndex.insert(std::pair<unsigned, unsigned>(I, R2VIndex));
+ R2VIndex += ProcResource.NumUnits;
+ }
+
+ NumResourceUnits = R2VIndex;
+ ResourceUsage.resize(NumResourceUnits * (Source.size() + 1));
+ std::fill(ResourceUsage.begin(), ResourceUsage.end(), 0.0);
+}
+
+void ResourcePressureView::onEvent(const HWInstructionEvent &Event) {
+ // We're only interested in Issue events.
+ if (Event.Type != HWInstructionEvent::Issued)
+ return;
+ const auto &IssueEvent = static_cast<const HWInstructionIssuedEvent &>(Event);
+ const unsigned SourceIdx = Event.IR.getSourceIndex() % Source.size();
+ for (const std::pair<ResourceRef, double> &Use : IssueEvent.UsedResources) {
+ const ResourceRef &RR = Use.first;
+ assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end());
+ unsigned R2VIndex = Resource2VecIndex[RR.first];
+ R2VIndex += countTrailingZeros(RR.second);
+ ResourceUsage[R2VIndex + NumResourceUnits * SourceIdx] += Use.second;
+ ResourceUsage[R2VIndex + NumResourceUnits * Source.size()] += Use.second;
+ }
+}
+
+static void printColumnNames(formatted_raw_ostream &OS,
+ const MCSchedModel &SM) {
+ unsigned Column = OS.getColumn();
+ for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
+ I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ // Skip groups and invalid resources with zero units.
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ for (unsigned J = 0; J < NumUnits; ++J) {
+ Column += 7;
+ OS << "[" << ResourceIndex;
+ if (NumUnits > 1)
+ OS << '.' << J;
+ OS << ']';
+ OS.PadToColumn(Column);
+ }
+
+ ResourceIndex++;
+ }
+}
+
+static void printResourcePressure(formatted_raw_ostream &OS, double Pressure,
+ unsigned Col) {
+ if (!Pressure || Pressure < 0.005) {
+ OS << " - ";
+ } else {
+ // Round to the value to the nearest hundredth and then print it.
+ OS << format("%.2f", floor((Pressure * 100) + 0.5) / 100);
+ }
+ OS.PadToColumn(Col);
+}
+
+void ResourcePressureView::printResourcePressurePerIteration(
+ raw_ostream &OS, unsigned Executions) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ formatted_raw_ostream FOS(TempStream);
+
+ FOS << "\n\nResources:\n";
+ const MCSchedModel &SM = STI.getSchedModel();
+ for (unsigned I = 1, ResourceIndex = 0, E = SM.getNumProcResourceKinds();
+ I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ unsigned NumUnits = ProcResource.NumUnits;
+ // Skip groups and invalid resources with zero units.
+ if (ProcResource.SubUnitsIdxBegin || !NumUnits)
+ continue;
+
+ for (unsigned J = 0; J < NumUnits; ++J) {
+ FOS << '[' << ResourceIndex;
+ if (NumUnits > 1)
+ FOS << '.' << J;
+ FOS << ']';
+ FOS.PadToColumn(6);
+ FOS << "- " << ProcResource.Name << '\n';
+ }
+
+ ResourceIndex++;
+ }
+
+ FOS << "\n\nResource pressure per iteration:\n";
+ FOS.flush();
+ printColumnNames(FOS, SM);
+ FOS << '\n';
+ FOS.flush();
+
+ for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) {
+ double Usage = ResourceUsage[I + Source.size() * E];
+ printResourcePressure(FOS, Usage / Executions, (I + 1) * 7);
+ }
+
+ FOS.flush();
+ OS << Buffer;
+}
+
+void ResourcePressureView::printResourcePressurePerInstruction(
+ raw_ostream &OS, unsigned Executions) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ formatted_raw_ostream FOS(TempStream);
+
+ FOS << "\n\nResource pressure by instruction:\n";
+ printColumnNames(FOS, STI.getSchedModel());
+ FOS << "Instructions:\n";
+
+ std::string Instruction;
+ raw_string_ostream InstrStream(Instruction);
+
+ for (unsigned I = 0, E = Source.size(); I < E; ++I) {
+ for (unsigned J = 0; J < NumResourceUnits; ++J) {
+ double Usage = ResourceUsage[J + I * NumResourceUnits];
+ printResourcePressure(FOS, Usage / Executions, (J + 1) * 7);
+ }
+
+ MCIP.printInst(&Source.getMCInstFromIndex(I), InstrStream, "", STI);
+ InstrStream.flush();
+ StringRef Str(Instruction);
+
+ // Remove any tabs or spaces at the beginning of the instruction.
+ Str = Str.ltrim();
+
+ FOS << Str << '\n';
+ Instruction = "";
+
+ FOS.flush();
+ OS << Buffer;
+ Buffer = "";
+ }
+}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/ResourcePressureView.h b/contrib/llvm/tools/llvm-mca/ResourcePressureView.h
new file mode 100644
index 000000000000..fe1c6af5e6f6
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/ResourcePressureView.h
@@ -0,0 +1,109 @@
+//===--------------------- ResourcePressureView.h ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file define class ResourcePressureView.
+/// Class ResourcePressureView observes hardware events generated by
+/// the Pipeline object and collects statistics related to resource usage at
+/// instruction granularity.
+/// Resource pressure information is then printed out to a stream in the
+/// form of a table like the one from the example below:
+///
+/// Resources:
+/// [0] - JALU0
+/// [1] - JALU1
+/// [2] - JDiv
+/// [3] - JFPM
+/// [4] - JFPU0
+/// [5] - JFPU1
+/// [6] - JLAGU
+/// [7] - JSAGU
+/// [8] - JSTC
+/// [9] - JVIMUL
+///
+/// Resource pressure per iteration:
+/// [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
+/// 0.00 0.00 0.00 0.00 2.00 2.00 0.00 0.00 0.00 0.00
+///
+/// Resource pressure by instruction:
+/// [0] [1] [2] [3] [4] [5] [6] [7] [8] [9] Instructions:
+/// - - - - - 1.00 - - - - vpermilpd $1, %xmm0,
+/// %xmm1
+/// - - - - 1.00 - - - - - vaddps %xmm0, %xmm1,
+/// %xmm2
+/// - - - - - 1.00 - - - - vmovshdup %xmm2, %xmm3
+/// - - - - 1.00 - - - - - vaddss %xmm2, %xmm3,
+/// %xmm4
+///
+/// In this example, we have AVX code executed on AMD Jaguar (btver2).
+/// Both shuffles and vector floating point add operations on XMM registers have
+/// a reciprocal throughput of 1cy.
+/// Each add is issued to pipeline JFPU0, while each shuffle is issued to
+/// pipeline JFPU1. The overall pressure per iteration is reported by two
+/// tables: the first smaller table is the resource pressure per iteration;
+/// the second table reports resource pressure per instruction. Values are the
+/// average resource cycles consumed by an instruction.
+/// Every vector add from the example uses resource JFPU0 for an average of 1cy
+/// per iteration. Consequently, the resource pressure on JFPU0 is of 2cy per
+/// iteration.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H
+
+#include "SourceMgr.h"
+#include "View.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include <map>
+
+namespace mca {
+
+/// This class collects resource pressure statistics and it is able to print
+/// out all the collected information as a table to an output stream.
+class ResourcePressureView : public View {
+ const llvm::MCSubtargetInfo &STI;
+ llvm::MCInstPrinter &MCIP;
+ const SourceMgr &Source;
+
+ // Map to quickly obtain the ResourceUsage column index from a processor
+ // resource ID.
+ llvm::DenseMap<unsigned, unsigned> Resource2VecIndex;
+
+ // Table of resources used by instructions.
+ std::vector<double> ResourceUsage;
+ unsigned NumResourceUnits;
+
+ const llvm::MCInst &GetMCInstFromIndex(unsigned Index) const;
+ void printResourcePressurePerIteration(llvm::raw_ostream &OS,
+ unsigned Executions) const;
+ void printResourcePressurePerInstruction(llvm::raw_ostream &OS,
+ unsigned Executions) const;
+ void initialize();
+
+public:
+ ResourcePressureView(const llvm::MCSubtargetInfo &sti,
+ llvm::MCInstPrinter &Printer, const SourceMgr &SM)
+ : STI(sti), MCIP(Printer), Source(SM) {
+ initialize();
+ }
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void printView(llvm::raw_ostream &OS) const override {
+ unsigned Executions = Source.getNumIterations();
+ printResourcePressurePerIteration(OS, Executions);
+ printResourcePressurePerInstruction(OS, Executions);
+ }
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/RetireControlUnit.cpp b/contrib/llvm/tools/llvm-mca/RetireControlUnit.cpp
new file mode 100644
index 000000000000..123058541f28
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RetireControlUnit.cpp
@@ -0,0 +1,87 @@
+//===---------------------- RetireControlUnit.cpp ---------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file simulates the hardware responsible for retiring instructions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RetireControlUnit.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+RetireControlUnit::RetireControlUnit(const llvm::MCSchedModel &SM)
+ : NextAvailableSlotIdx(0), CurrentInstructionSlotIdx(0),
+ AvailableSlots(SM.MicroOpBufferSize), MaxRetirePerCycle(0) {
+ // Check if the scheduling model provides extra information about the machine
+ // processor. If so, then use that information to set the reorder buffer size
+ // and the maximum number of instructions retired per cycle.
+ if (SM.hasExtraProcessorInfo()) {
+ const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo();
+ if (EPI.ReorderBufferSize)
+ AvailableSlots = EPI.ReorderBufferSize;
+ MaxRetirePerCycle = EPI.MaxRetirePerCycle;
+ }
+
+ assert(AvailableSlots && "Invalid reorder buffer size!");
+ Queue.resize(AvailableSlots);
+}
+
+// Reserves a number of slots, and returns a new token.
+unsigned RetireControlUnit::reserveSlot(const InstRef &IR,
+ unsigned NumMicroOps) {
+ assert(isAvailable(NumMicroOps));
+ unsigned NormalizedQuantity =
+ std::min(NumMicroOps, static_cast<unsigned>(Queue.size()));
+ // Zero latency instructions may have zero mOps. Artificially bump this
+ // value to 1. Although zero latency instructions don't consume scheduler
+ // resources, they still consume one slot in the retire queue.
+ NormalizedQuantity = std::max(NormalizedQuantity, 1U);
+ unsigned TokenID = NextAvailableSlotIdx;
+ Queue[NextAvailableSlotIdx] = {IR, NormalizedQuantity, false};
+ NextAvailableSlotIdx += NormalizedQuantity;
+ NextAvailableSlotIdx %= Queue.size();
+ AvailableSlots -= NormalizedQuantity;
+ return TokenID;
+}
+
+const RetireControlUnit::RUToken &RetireControlUnit::peekCurrentToken() const {
+ return Queue[CurrentInstructionSlotIdx];
+}
+
+void RetireControlUnit::consumeCurrentToken() {
+ const RetireControlUnit::RUToken &Current = peekCurrentToken();
+ assert(Current.NumSlots && "Reserved zero slots?");
+ assert(Current.IR.isValid() && "Invalid RUToken in the RCU queue.");
+
+ // Update the slot index to be the next item in the circular queue.
+ CurrentInstructionSlotIdx += Current.NumSlots;
+ CurrentInstructionSlotIdx %= Queue.size();
+ AvailableSlots += Current.NumSlots;
+}
+
+void RetireControlUnit::onInstructionExecuted(unsigned TokenID) {
+ assert(Queue.size() > TokenID);
+ assert(Queue[TokenID].Executed == false && Queue[TokenID].IR.isValid());
+ Queue[TokenID].Executed = true;
+}
+
+#ifndef NDEBUG
+void RetireControlUnit::dump() const {
+ dbgs() << "Retire Unit: { Total Slots=" << Queue.size()
+ << ", Available Slots=" << AvailableSlots << " }\n";
+}
+#endif
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/RetireControlUnit.h b/contrib/llvm/tools/llvm-mca/RetireControlUnit.h
new file mode 100644
index 000000000000..3530ff21ba0d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RetireControlUnit.h
@@ -0,0 +1,98 @@
+//===---------------------- RetireControlUnit.h -----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file simulates the hardware responsible for retiring instructions.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_RETIRE_CONTROL_UNIT_H
+#define LLVM_TOOLS_LLVM_MCA_RETIRE_CONTROL_UNIT_H
+
+#include "HardwareUnit.h"
+#include "Instruction.h"
+#include "llvm/MC/MCSchedule.h"
+#include <vector>
+
+namespace mca {
+
+/// This class tracks which instructions are in-flight (i.e., dispatched but not
+/// retired) in the OoO backend.
+//
+/// This class checks on every cycle if/which instructions can be retired.
+/// Instructions are retired in program order.
+/// In the event of an instruction being retired, the pipeline that owns
+/// this RetireControlUnit (RCU) gets notified.
+///
+/// On instruction retired, register updates are all architecturally
+/// committed, and any temporary registers originally allocated for the
+/// retired instruction are freed.
+struct RetireControlUnit : public HardwareUnit {
+ // A RUToken is created by the RCU for every instruction dispatched to the
+ // schedulers. These "tokens" are managed by the RCU in its token Queue.
+ //
+ // On every cycle ('cycleEvent'), the RCU iterates through the token queue
+ // looking for any token with its 'Executed' flag set. If a token has that
+ // flag set, then the instruction has reached the write-back stage and will
+ // be retired by the RCU.
+ //
+ // 'NumSlots' represents the number of entries consumed by the instruction in
+ // the reorder buffer. Those entries will become available again once the
+ // instruction is retired.
+ //
+ // Note that the size of the reorder buffer is defined by the scheduling
+ // model via field 'NumMicroOpBufferSize'.
+ struct RUToken {
+ InstRef IR;
+ unsigned NumSlots; // Slots reserved to this instruction.
+ bool Executed; // True if the instruction is past the WB stage.
+ };
+
+private:
+ unsigned NextAvailableSlotIdx;
+ unsigned CurrentInstructionSlotIdx;
+ unsigned AvailableSlots;
+ unsigned MaxRetirePerCycle; // 0 means no limit.
+ std::vector<RUToken> Queue;
+
+public:
+ RetireControlUnit(const llvm::MCSchedModel &SM);
+
+ bool isFull() const { return !AvailableSlots; }
+ bool isEmpty() const { return AvailableSlots == Queue.size(); }
+ bool isAvailable(unsigned Quantity = 1) const {
+ // Some instructions may declare a number of uOps which exceeds the size
+ // of the reorder buffer. To avoid problems, cap the amount of slots to
+ // the size of the reorder buffer.
+ Quantity = std::min(Quantity, static_cast<unsigned>(Queue.size()));
+ return AvailableSlots >= Quantity;
+ }
+
+ unsigned getMaxRetirePerCycle() const { return MaxRetirePerCycle; }
+
+ // Reserves a number of slots, and returns a new token.
+ unsigned reserveSlot(const InstRef &IS, unsigned NumMicroOps);
+
+ // Return the current token from the RCU's circular token queue.
+ const RUToken &peekCurrentToken() const;
+
+ // Advance the pointer to the next token in the circular token queue.
+ void consumeCurrentToken();
+
+ // Update the RCU token to represent the executed state.
+ void onInstructionExecuted(unsigned TokenID);
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+};
+
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_RETIRE_CONTROL_UNIT_H
diff --git a/contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.cpp b/contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.cpp
new file mode 100644
index 000000000000..edb855e11e84
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.cpp
@@ -0,0 +1,49 @@
+//===--------------------- RetireControlUnitStatistics.cpp ------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the RetireControlUnitStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RetireControlUnitStatistics.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+namespace mca {
+
+void RetireControlUnitStatistics::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Retired)
+ ++NumRetired;
+}
+
+void RetireControlUnitStatistics::printView(llvm::raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nRetire Control Unit - "
+ << "number of cycles where we saw N instructions retired:\n";
+ TempStream << "[# retired], [# cycles]\n";
+
+ for (const std::pair<unsigned, unsigned> &Entry : RetiredPerCycle) {
+ TempStream << " " << Entry.first;
+ if (Entry.first < 10)
+ TempStream << ", ";
+ else
+ TempStream << ", ";
+ TempStream << Entry.second << " ("
+ << format("%.1f", ((double)Entry.second / NumCycles) * 100.0)
+ << "%)\n";
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.h b/contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.h
new file mode 100644
index 000000000000..1f03e7efe889
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RetireControlUnitStatistics.h
@@ -0,0 +1,60 @@
+//===--------------------- RetireControlUnitStatistics.h --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines class RetireControlUnitStatistics: a view that knows how
+/// to print general statistics related to the retire control unit.
+///
+/// Example:
+/// ========
+///
+/// Retire Control Unit - number of cycles where we saw N instructions retired:
+/// [# retired], [# cycles]
+/// 0, 9 (6.9%)
+/// 1, 6 (4.6%)
+/// 2, 1 (0.8%)
+/// 4, 3 (2.3%)
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_RETIRECONTROLUNITSTATISTICS_H
+#define LLVM_TOOLS_LLVM_MCA_RETIRECONTROLUNITSTATISTICS_H
+
+#include "View.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include <map>
+
+namespace mca {
+
+class RetireControlUnitStatistics : public View {
+ using Histogram = std::map<unsigned, unsigned>;
+ Histogram RetiredPerCycle;
+
+ unsigned NumRetired;
+ unsigned NumCycles;
+
+ void updateHistograms() {
+ RetiredPerCycle[NumRetired]++;
+ NumRetired = 0;
+ }
+
+public:
+ RetireControlUnitStatistics() : NumRetired(0), NumCycles(0) {}
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void onCycleBegin() override { NumCycles++; }
+
+ void onCycleEnd() override { updateHistograms(); }
+
+ void printView(llvm::raw_ostream &OS) const override;
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/RetireStage.cpp b/contrib/llvm/tools/llvm-mca/RetireStage.cpp
new file mode 100644
index 000000000000..386ec54d7ba3
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RetireStage.cpp
@@ -0,0 +1,55 @@
+//===---------------------- RetireStage.cpp ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the retire stage of an instruction pipeline.
+/// The RetireStage represents the process logic that interacts with the
+/// simulated RetireControlUnit hardware.
+///
+//===----------------------------------------------------------------------===//
+
+#include "RetireStage.h"
+#include "HWEventListener.h"
+#include "llvm/Support/Debug.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+namespace mca {
+
+void RetireStage::cycleStart() {
+ if (RCU.isEmpty())
+ return;
+
+ const unsigned MaxRetirePerCycle = RCU.getMaxRetirePerCycle();
+ unsigned NumRetired = 0;
+ while (!RCU.isEmpty()) {
+ if (MaxRetirePerCycle != 0 && NumRetired == MaxRetirePerCycle)
+ break;
+ const RetireControlUnit::RUToken &Current = RCU.peekCurrentToken();
+ if (!Current.Executed)
+ break;
+ RCU.consumeCurrentToken();
+ notifyInstructionRetired(Current.IR);
+ NumRetired++;
+ }
+}
+
+void RetireStage::notifyInstructionRetired(const InstRef &IR) {
+ LLVM_DEBUG(dbgs() << "[E] Instruction Retired: #" << IR << '\n');
+ SmallVector<unsigned, 4> FreedRegs(PRF.getNumRegisterFiles());
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+
+ for (const std::unique_ptr<WriteState> &WS : IR.getInstruction()->getDefs())
+ PRF.removeRegisterWrite(*WS.get(), FreedRegs, !Desc.isZeroLatency());
+ notifyEvent<HWInstructionEvent>(HWInstructionRetiredEvent(IR, FreedRegs));
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/RetireStage.h b/contrib/llvm/tools/llvm-mca/RetireStage.h
new file mode 100644
index 000000000000..8cf672d92c6e
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/RetireStage.h
@@ -0,0 +1,48 @@
+//===---------------------- RetireStage.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the retire stage of an instruction pipeline.
+/// The RetireStage represents the process logic that interacts with the
+/// simulated RetireControlUnit hardware.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
+#define LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
+
+#include "RegisterFile.h"
+#include "RetireControlUnit.h"
+#include "Stage.h"
+
+namespace mca {
+
+class RetireStage : public Stage {
+ // Owner will go away when we move listeners/eventing to the stages.
+ RetireControlUnit &RCU;
+ RegisterFile &PRF;
+
+public:
+ RetireStage(RetireControlUnit &R, RegisterFile &F)
+ : Stage(), RCU(R), PRF(F) {}
+ RetireStage(const RetireStage &Other) = delete;
+ RetireStage &operator=(const RetireStage &Other) = delete;
+
+ virtual bool hasWorkToComplete() const override final {
+ return !RCU.isEmpty();
+ }
+ virtual void cycleStart() override final;
+ virtual bool execute(InstRef &IR) override final { return true; }
+ void notifyInstructionRetired(const InstRef &IR);
+ void onInstructionExecuted(unsigned TokenID);
+};
+
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_RETIRE_STAGE_H
diff --git a/contrib/llvm/tools/llvm-mca/Scheduler.cpp b/contrib/llvm/tools/llvm-mca/Scheduler.cpp
new file mode 100644
index 000000000000..975a50e4b638
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Scheduler.cpp
@@ -0,0 +1,403 @@
+//===--------------------- Scheduler.cpp ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// A scheduler for processor resource units and processor resource groups.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Scheduler.h"
+#include "Support.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace mca {
+
+using namespace llvm;
+
+#define DEBUG_TYPE "llvm-mca"
+
+uint64_t ResourceState::selectNextInSequence() {
+ assert(isReady());
+ uint64_t Next = getNextInSequence();
+ while (!isSubResourceReady(Next)) {
+ updateNextInSequence();
+ Next = getNextInSequence();
+ }
+ return Next;
+}
+
+#ifndef NDEBUG
+void ResourceState::dump() const {
+ dbgs() << "MASK: " << ResourceMask << ", SIZE_MASK: " << ResourceSizeMask
+ << ", NEXT: " << NextInSequenceMask << ", RDYMASK: " << ReadyMask
+ << ", BufferSize=" << BufferSize
+ << ", AvailableSlots=" << AvailableSlots
+ << ", Reserved=" << Unavailable << '\n';
+}
+#endif
+
+void ResourceManager::initialize(const llvm::MCSchedModel &SM) {
+ computeProcResourceMasks(SM, ProcResID2Mask);
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I)
+ addResource(*SM.getProcResource(I), I, ProcResID2Mask[I]);
+}
+
+// Adds a new resource state in Resources, as well as a new descriptor in
+// ResourceDescriptor. Map 'Resources' allows to quickly obtain ResourceState
+// objects from resource mask identifiers.
+void ResourceManager::addResource(const MCProcResourceDesc &Desc,
+ unsigned Index, uint64_t Mask) {
+ assert(Resources.find(Mask) == Resources.end() && "Resource already added!");
+ Resources[Mask] = llvm::make_unique<ResourceState>(Desc, Index, Mask);
+}
+
+// Returns the actual resource consumed by this Use.
+// First, is the primary resource ID.
+// Second, is the specific sub-resource ID.
+std::pair<uint64_t, uint64_t> ResourceManager::selectPipe(uint64_t ResourceID) {
+ ResourceState &RS = *Resources[ResourceID];
+ uint64_t SubResourceID = RS.selectNextInSequence();
+ if (RS.isAResourceGroup())
+ return selectPipe(SubResourceID);
+ return std::pair<uint64_t, uint64_t>(ResourceID, SubResourceID);
+}
+
+void ResourceState::removeFromNextInSequence(uint64_t ID) {
+ assert(NextInSequenceMask);
+ assert(countPopulation(ID) == 1);
+ if (ID > getNextInSequence())
+ RemovedFromNextInSequence |= ID;
+ NextInSequenceMask = NextInSequenceMask & (~ID);
+ if (!NextInSequenceMask) {
+ NextInSequenceMask = ResourceSizeMask;
+ assert(NextInSequenceMask != RemovedFromNextInSequence);
+ NextInSequenceMask ^= RemovedFromNextInSequence;
+ RemovedFromNextInSequence = 0;
+ }
+}
+
+void ResourceManager::use(ResourceRef RR) {
+ // Mark the sub-resource referenced by RR as used.
+ ResourceState &RS = *Resources[RR.first];
+ RS.markSubResourceAsUsed(RR.second);
+ // If there are still available units in RR.first,
+ // then we are done.
+ if (RS.isReady())
+ return;
+
+ // Notify to other resources that RR.first is no longer available.
+ for (const std::pair<uint64_t, UniqueResourceState> &Res : Resources) {
+ ResourceState &Current = *Res.second.get();
+ if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
+ continue;
+
+ if (Current.containsResource(RR.first)) {
+ Current.markSubResourceAsUsed(RR.first);
+ Current.removeFromNextInSequence(RR.first);
+ }
+ }
+}
+
+void ResourceManager::release(ResourceRef RR) {
+ ResourceState &RS = *Resources[RR.first];
+ bool WasFullyUsed = !RS.isReady();
+ RS.releaseSubResource(RR.second);
+ if (!WasFullyUsed)
+ return;
+
+ for (const std::pair<uint64_t, UniqueResourceState> &Res : Resources) {
+ ResourceState &Current = *Res.second.get();
+ if (!Current.isAResourceGroup() || Current.getResourceMask() == RR.first)
+ continue;
+
+ if (Current.containsResource(RR.first))
+ Current.releaseSubResource(RR.first);
+ }
+}
+
+ResourceStateEvent
+ResourceManager::canBeDispatched(ArrayRef<uint64_t> Buffers) const {
+ ResourceStateEvent Result = ResourceStateEvent::RS_BUFFER_AVAILABLE;
+ for (uint64_t Buffer : Buffers) {
+ Result = isBufferAvailable(Buffer);
+ if (Result != ResourceStateEvent::RS_BUFFER_AVAILABLE)
+ break;
+ }
+ return Result;
+}
+
+void ResourceManager::reserveBuffers(ArrayRef<uint64_t> Buffers) {
+ for (const uint64_t R : Buffers) {
+ reserveBuffer(R);
+ ResourceState &Resource = *Resources[R];
+ if (Resource.isADispatchHazard()) {
+ assert(!Resource.isReserved());
+ Resource.setReserved();
+ }
+ }
+}
+
+void ResourceManager::releaseBuffers(ArrayRef<uint64_t> Buffers) {
+ for (const uint64_t R : Buffers)
+ releaseBuffer(R);
+}
+
+bool ResourceManager::canBeIssued(const InstrDesc &Desc) const {
+ return std::all_of(Desc.Resources.begin(), Desc.Resources.end(),
+ [&](const std::pair<uint64_t, const ResourceUsage> &E) {
+ unsigned NumUnits =
+ E.second.isReserved() ? 0U : E.second.NumUnits;
+ return isReady(E.first, NumUnits);
+ });
+}
+
+// Returns true if all resources are in-order, and there is at least one
+// resource which is a dispatch hazard (BufferSize = 0).
+bool ResourceManager::mustIssueImmediately(const InstrDesc &Desc) {
+ if (!canBeIssued(Desc))
+ return false;
+ bool AllInOrderResources = all_of(Desc.Buffers, [&](uint64_t BufferMask) {
+ const ResourceState &Resource = *Resources[BufferMask];
+ return Resource.isInOrder() || Resource.isADispatchHazard();
+ });
+ if (!AllInOrderResources)
+ return false;
+
+ return any_of(Desc.Buffers, [&](uint64_t BufferMask) {
+ return Resources[BufferMask]->isADispatchHazard();
+ });
+}
+
+void ResourceManager::issueInstruction(
+ const InstrDesc &Desc,
+ SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes) {
+ for (const std::pair<uint64_t, ResourceUsage> &R : Desc.Resources) {
+ const CycleSegment &CS = R.second.CS;
+ if (!CS.size()) {
+ releaseResource(R.first);
+ continue;
+ }
+
+ assert(CS.begin() == 0 && "Invalid {Start, End} cycles!");
+ if (!R.second.isReserved()) {
+ ResourceRef Pipe = selectPipe(R.first);
+ use(Pipe);
+ BusyResources[Pipe] += CS.size();
+ // Replace the resource mask with a valid processor resource index.
+ const ResourceState &RS = *Resources[Pipe.first];
+ Pipe.first = RS.getProcResourceID();
+ Pipes.emplace_back(
+ std::pair<ResourceRef, double>(Pipe, static_cast<double>(CS.size())));
+ } else {
+ assert((countPopulation(R.first) > 1) && "Expected a group!");
+ // Mark this group as reserved.
+ assert(R.second.isReserved());
+ reserveResource(R.first);
+ BusyResources[ResourceRef(R.first, R.first)] += CS.size();
+ }
+ }
+}
+
+void ResourceManager::cycleEvent(SmallVectorImpl<ResourceRef> &ResourcesFreed) {
+ for (std::pair<ResourceRef, unsigned> &BR : BusyResources) {
+ if (BR.second)
+ BR.second--;
+ if (!BR.second) {
+ // Release this resource.
+ const ResourceRef &RR = BR.first;
+
+ if (countPopulation(RR.first) == 1)
+ release(RR);
+
+ releaseResource(RR.first);
+ ResourcesFreed.push_back(RR);
+ }
+ }
+
+ for (const ResourceRef &RF : ResourcesFreed)
+ BusyResources.erase(RF);
+}
+
+#ifndef NDEBUG
+void Scheduler::dump() const {
+ dbgs() << "[SCHEDULER]: WaitQueue size is: " << WaitQueue.size() << '\n';
+ dbgs() << "[SCHEDULER]: ReadyQueue size is: " << ReadyQueue.size() << '\n';
+ dbgs() << "[SCHEDULER]: IssuedQueue size is: " << IssuedQueue.size() << '\n';
+ Resources->dump();
+}
+#endif
+
+bool Scheduler::canBeDispatched(const InstRef &IR,
+ HWStallEvent::GenericEventType &Event) const {
+ Event = HWStallEvent::Invalid;
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+
+ if (Desc.MayLoad && LSU->isLQFull())
+ Event = HWStallEvent::LoadQueueFull;
+ else if (Desc.MayStore && LSU->isSQFull())
+ Event = HWStallEvent::StoreQueueFull;
+ else {
+ switch (Resources->canBeDispatched(Desc.Buffers)) {
+ default:
+ return true;
+ case ResourceStateEvent::RS_BUFFER_UNAVAILABLE:
+ Event = HWStallEvent::SchedulerQueueFull;
+ break;
+ case ResourceStateEvent::RS_RESERVED:
+ Event = HWStallEvent::DispatchGroupStall;
+ }
+ }
+
+ return false;
+}
+
+void Scheduler::issueInstructionImpl(
+ InstRef &IR,
+ SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
+ Instruction *IS = IR.getInstruction();
+ const InstrDesc &D = IS->getDesc();
+
+ // Issue the instruction and collect all the consumed resources
+ // into a vector. That vector is then used to notify the listener.
+ Resources->issueInstruction(D, UsedResources);
+
+ // Notify the instruction that it started executing.
+ // This updates the internal state of each write.
+ IS->execute();
+
+ if (IS->isExecuting())
+ IssuedQueue[IR.getSourceIndex()] = IS;
+}
+
+// Release the buffered resources and issue the instruction.
+void Scheduler::issueInstruction(
+ InstRef &IR,
+ SmallVectorImpl<std::pair<ResourceRef, double>> &UsedResources) {
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ releaseBuffers(Desc.Buffers);
+ issueInstructionImpl(IR, UsedResources);
+}
+
+void Scheduler::promoteToReadyQueue(SmallVectorImpl<InstRef> &Ready) {
+ // Scan the set of waiting instructions and promote them to the
+ // ready queue if operands are all ready.
+ for (auto I = WaitQueue.begin(), E = WaitQueue.end(); I != E;) {
+ const unsigned IID = I->first;
+ Instruction *IS = I->second;
+
+ // Check if this instruction is now ready. In case, force
+ // a transition in state using method 'update()'.
+ if (!IS->isReady())
+ IS->update();
+
+ const InstrDesc &Desc = IS->getDesc();
+ bool IsMemOp = Desc.MayLoad || Desc.MayStore;
+ if (!IS->isReady() || (IsMemOp && !LSU->isReady({IID, IS}))) {
+ ++I;
+ continue;
+ }
+
+ Ready.emplace_back(IID, IS);
+ ReadyQueue[IID] = IS;
+ auto ToRemove = I;
+ ++I;
+ WaitQueue.erase(ToRemove);
+ }
+}
+
+InstRef Scheduler::select() {
+ // Find the oldest ready-to-issue instruction in the ReadyQueue.
+ auto It = std::find_if(ReadyQueue.begin(), ReadyQueue.end(),
+ [&](const QueueEntryTy &Entry) {
+ const InstrDesc &D = Entry.second->getDesc();
+ return Resources->canBeIssued(D);
+ });
+
+ if (It == ReadyQueue.end())
+ return {0, nullptr};
+
+ // We want to prioritize older instructions over younger instructions to
+ // minimize the pressure on the reorder buffer. We also want to
+ // rank higher the instructions with more users to better expose ILP.
+
+ // Compute a rank value based on the age of an instruction (i.e. its source
+ // index) and its number of users. The lower the rank value, the better.
+ int Rank = It->first - It->second->getNumUsers();
+ for (auto I = It, E = ReadyQueue.end(); I != E; ++I) {
+ int CurrentRank = I->first - I->second->getNumUsers();
+ if (CurrentRank < Rank) {
+ const InstrDesc &D = I->second->getDesc();
+ if (Resources->canBeIssued(D))
+ It = I;
+ }
+ }
+
+ // We found an instruction to issue.
+ InstRef IR(It->first, It->second);
+ ReadyQueue.erase(It);
+ return IR;
+}
+
+void Scheduler::updatePendingQueue(SmallVectorImpl<InstRef> &Ready) {
+ // Notify to instructions in the pending queue that a new cycle just
+ // started.
+ for (QueueEntryTy Entry : WaitQueue)
+ Entry.second->cycleEvent();
+ promoteToReadyQueue(Ready);
+}
+
+void Scheduler::updateIssuedQueue(SmallVectorImpl<InstRef> &Executed) {
+ for (auto I = IssuedQueue.begin(), E = IssuedQueue.end(); I != E;) {
+ const QueueEntryTy Entry = *I;
+ Instruction *IS = Entry.second;
+ IS->cycleEvent();
+ if (IS->isExecuted()) {
+ Executed.push_back({Entry.first, Entry.second});
+ auto ToRemove = I;
+ ++I;
+ IssuedQueue.erase(ToRemove);
+ } else {
+ LLVM_DEBUG(dbgs() << "[SCHEDULER]: Instruction #" << Entry.first
+ << " is still executing.\n");
+ ++I;
+ }
+ }
+}
+
+void Scheduler::onInstructionExecuted(const InstRef &IR) {
+ LSU->onInstructionExecuted(IR);
+}
+
+void Scheduler::reclaimSimulatedResources(SmallVectorImpl<ResourceRef> &Freed) {
+ Resources->cycleEvent(Freed);
+}
+
+bool Scheduler::reserveResources(InstRef &IR) {
+ // If necessary, reserve queue entries in the load-store unit (LSU).
+ const bool Reserved = LSU->reserve(IR);
+ if (!IR.getInstruction()->isReady() || (Reserved && !LSU->isReady(IR))) {
+ LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR << " to the Wait Queue\n");
+ WaitQueue[IR.getSourceIndex()] = IR.getInstruction();
+ return false;
+ }
+ return true;
+}
+
+bool Scheduler::issueImmediately(InstRef &IR) {
+ const InstrDesc &Desc = IR.getInstruction()->getDesc();
+ if (!Desc.isZeroLatency() && !Resources->mustIssueImmediately(Desc)) {
+ LLVM_DEBUG(dbgs() << "[SCHEDULER] Adding #" << IR
+ << " to the Ready Queue\n");
+ ReadyQueue[IR.getSourceIndex()] = IR.getInstruction();
+ return false;
+ }
+ return true;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/Scheduler.h b/contrib/llvm/tools/llvm-mca/Scheduler.h
new file mode 100644
index 000000000000..428fbc01707d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Scheduler.h
@@ -0,0 +1,515 @@
+//===--------------------- Scheduler.h ------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// A scheduler for Processor Resource Units and Processor Resource Groups.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_SCHEDULER_H
+#define LLVM_TOOLS_LLVM_MCA_SCHEDULER_H
+
+#include "HWEventListener.h"
+#include "HardwareUnit.h"
+#include "Instruction.h"
+#include "LSUnit.h"
+#include "RetireControlUnit.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include <map>
+
+namespace mca {
+
+/// Used to notify the internal state of a processor resource.
+///
+/// A processor resource is available if it is not reserved, and there are
+/// available slots in the buffer. A processor resource is unavailable if it
+/// is either reserved, or the associated buffer is full. A processor resource
+/// with a buffer size of -1 is always available if it is not reserved.
+///
+/// Values of type ResourceStateEvent are returned by method
+/// ResourceState::isBufferAvailable(), which is used to query the internal
+/// state of a resource.
+///
+/// The naming convention for resource state events is:
+/// * Event names start with prefix RS_
+/// * Prefix RS_ is followed by a string describing the actual resource state.
+enum ResourceStateEvent {
+ RS_BUFFER_AVAILABLE,
+ RS_BUFFER_UNAVAILABLE,
+ RS_RESERVED
+};
+
+/// A descriptor for processor resources.
+///
+/// Each object of class ResourceState is associated to a specific processor
+/// resource. There is an instance of this class for every processor resource
+/// defined by the scheduling model.
+/// A ResourceState dynamically tracks the availability of units of a processor
+/// resource. For example, the ResourceState of a ProcResGroup tracks the
+/// availability of resource units which are part of the group.
+///
+/// Internally, ResourceState uses a round-robin selector to identify
+/// which unit of the group shall be used next.
+class ResourceState {
+ // Index to the MCProcResourceDesc in the processor Model.
+ unsigned ProcResourceDescIndex;
+ // A resource mask. This is generated by the tool with the help of
+ // function `mca::createProcResourceMasks' (see Support.h).
+ uint64_t ResourceMask;
+
+ // A ProcResource can specify a number of units. For the purpose of dynamic
+ // scheduling, a processor resource with more than one unit behaves like a
+ // group. This field has one bit set for every unit/resource that is part of
+ // the group.
+ // For groups, this field defaults to 'ResourceMask'. For non-group
+ // resources, the number of bits set in this mask is equivalent to the
+ // number of units (i.e. field 'NumUnits' in 'ProcResourceUnits').
+ uint64_t ResourceSizeMask;
+
+ // A simple round-robin selector for processor resources.
+ // Each bit of the mask identifies a sub resource within this group.
+ //
+ // As an example, lets assume that this ResourceState describes a
+ // processor resource group composed of the following three units:
+ // ResourceA -- 0b001
+ // ResourceB -- 0b010
+ // ResourceC -- 0b100
+ //
+ // Each unit is identified by a ResourceMask which always contains a
+ // single bit set. Field NextInSequenceMask is initially set to value
+ // 0xb111. That value is obtained by OR'ing the resource masks of
+ // processor resource that are part of the group.
+ //
+ // NextInSequenceMask -- 0b111
+ //
+ // Field NextInSequenceMask is used by the resource manager (i.e.
+ // an object of class ResourceManager) to select the "next available resource"
+ // from the set. The algorithm would prioritize resources with a bigger
+ // ResourceMask value.
+ //
+ // In this example, there are three resources in the set, and 'ResourceC'
+ // has the highest mask value. The round-robin selector would firstly select
+ // 'ResourceC', then 'ResourceB', and eventually 'ResourceA'.
+ //
+ // When a resource R is used, its corresponding bit is cleared from the set.
+ //
+ // Back to the example:
+ // If 'ResourceC' is selected, then the new value of NextInSequenceMask
+ // becomes 0xb011.
+ //
+ // When NextInSequenceMask becomes zero, it is reset to its original value
+ // (in this example, that value would be 0b111).
+ uint64_t NextInSequenceMask;
+
+ // Some instructions can only be issued on very specific pipeline resources.
+ // For those instructions, we know exactly which resource would be consumed
+ // without having to dynamically select it using field 'NextInSequenceMask'.
+ //
+ // The resource mask bit associated to the (statically) selected
+ // processor resource is still cleared from the 'NextInSequenceMask'.
+ // If that bit was already zero in NextInSequenceMask, then we update
+ // mask 'RemovedFromNextInSequence'.
+ //
+ // When NextInSequenceMask is reset back to its initial value, the algorithm
+ // removes any bits which are set in RemoveFromNextInSequence.
+ uint64_t RemovedFromNextInSequence;
+
+ // A mask of ready units.
+ uint64_t ReadyMask;
+
+ // Buffered resources will have this field set to a positive number bigger
+ // than 0. A buffered resource behaves like a separate reservation station
+ // implementing its own buffer for out-of-order execution.
+ // A buffer of 1 is for units that force in-order execution.
+ // A value of 0 is treated specially. In particular, a resource with
+ // A BufferSize = 0 is for an in-order issue/dispatch resource.
+ // That means, this resource is reserved starting from the dispatch event,
+ // until all the "resource cycles" are consumed after the issue event.
+ // While this resource is reserved, no other instruction may be dispatched.
+ int BufferSize;
+
+ // Available slots in the buffer (zero, if this is not a buffered resource).
+ unsigned AvailableSlots;
+
+ // True if this is resource is currently unavailable.
+ // An instruction may "reserve" a resource for a number of cycles.
+ // During those cycles, the reserved resource cannot be used for other
+ // instructions, even if the ReadyMask is set.
+ bool Unavailable;
+
+ bool isSubResourceReady(uint64_t ID) const { return ReadyMask & ID; }
+
+ /// Returns the mask identifier of the next available resource in the set.
+ uint64_t getNextInSequence() const {
+ assert(NextInSequenceMask);
+ return llvm::PowerOf2Floor(NextInSequenceMask);
+ }
+
+ /// Returns the mask of the next available resource within the set,
+ /// and updates the resource selector.
+ void updateNextInSequence() {
+ NextInSequenceMask ^= getNextInSequence();
+ if (!NextInSequenceMask)
+ NextInSequenceMask = ResourceSizeMask;
+ }
+
+ uint64_t computeResourceSizeMaskForGroup(uint64_t ResourceMask) {
+ assert(llvm::countPopulation(ResourceMask) > 1);
+ return ResourceMask ^ llvm::PowerOf2Floor(ResourceMask);
+ }
+
+public:
+ ResourceState(const llvm::MCProcResourceDesc &Desc, unsigned Index,
+ uint64_t Mask)
+ : ProcResourceDescIndex(Index), ResourceMask(Mask) {
+ bool IsAGroup = llvm::countPopulation(ResourceMask) > 1;
+ ResourceSizeMask = IsAGroup ? computeResourceSizeMaskForGroup(ResourceMask)
+ : ((1ULL << Desc.NumUnits) - 1);
+ NextInSequenceMask = ResourceSizeMask;
+ RemovedFromNextInSequence = 0;
+ ReadyMask = ResourceSizeMask;
+ BufferSize = Desc.BufferSize;
+ AvailableSlots = BufferSize == -1 ? 0U : static_cast<unsigned>(BufferSize);
+ Unavailable = false;
+ }
+
+ unsigned getProcResourceID() const { return ProcResourceDescIndex; }
+ uint64_t getResourceMask() const { return ResourceMask; }
+ int getBufferSize() const { return BufferSize; }
+
+ bool isBuffered() const { return BufferSize > 0; }
+ bool isInOrder() const { return BufferSize == 1; }
+ bool isADispatchHazard() const { return BufferSize == 0; }
+ bool isReserved() const { return Unavailable; }
+
+ void setReserved() { Unavailable = true; }
+ void clearReserved() { Unavailable = false; }
+
+ // A resource is ready if it is not reserved, and if there are enough
+ // available units.
+ // If a resource is also a dispatch hazard, then we don't check if
+ // it is reserved because that check would always return true.
+ // A resource marked as "dispatch hazard" is always reserved at
+ // dispatch time. When this method is called, the assumption is that
+ // the user of this resource has been already dispatched.
+ bool isReady(unsigned NumUnits = 1) const {
+ return (!isReserved() || isADispatchHazard()) &&
+ llvm::countPopulation(ReadyMask) >= NumUnits;
+ }
+ bool isAResourceGroup() const {
+ return llvm::countPopulation(ResourceMask) > 1;
+ }
+
+ bool containsResource(uint64_t ID) const { return ResourceMask & ID; }
+
+ void markSubResourceAsUsed(uint64_t ID) {
+ assert(isSubResourceReady(ID));
+ ReadyMask ^= ID;
+ }
+
+ void releaseSubResource(uint64_t ID) {
+ assert(!isSubResourceReady(ID));
+ ReadyMask ^= ID;
+ }
+
+ unsigned getNumUnits() const {
+ return isAResourceGroup() ? 1U : llvm::countPopulation(ResourceSizeMask);
+ }
+
+ uint64_t selectNextInSequence();
+ void removeFromNextInSequence(uint64_t ID);
+
+ ResourceStateEvent isBufferAvailable() const {
+ if (isADispatchHazard() && isReserved())
+ return RS_RESERVED;
+ if (!isBuffered() || AvailableSlots)
+ return RS_BUFFER_AVAILABLE;
+ return RS_BUFFER_UNAVAILABLE;
+ }
+
+ void reserveBuffer() {
+ if (AvailableSlots)
+ AvailableSlots--;
+ }
+
+ void releaseBuffer() {
+ if (BufferSize > 0)
+ AvailableSlots++;
+ assert(AvailableSlots <= static_cast<unsigned>(BufferSize));
+ }
+
+#ifndef NDEBUG
+ void dump() const;
+#endif
+};
+
+/// A resource unit identifier.
+///
+/// This is used to identify a specific processor resource unit using a pair
+/// of indices where the 'first' index is a processor resource mask, and the
+/// 'second' index is an index for a "sub-resource" (i.e. unit).
+typedef std::pair<uint64_t, uint64_t> ResourceRef;
+
+// First: a MCProcResourceDesc index identifying a buffered resource.
+// Second: max number of buffer entries used in this resource.
+typedef std::pair<unsigned, unsigned> BufferUsageEntry;
+
+/// A resource manager for processor resource units and groups.
+///
+/// This class owns all the ResourceState objects, and it is responsible for
+/// acting on requests from a Scheduler by updating the internal state of
+/// ResourceState objects.
+/// This class doesn't know about instruction itineraries and functional units.
+/// In future, it can be extended to support itineraries too through the same
+/// public interface.
+class ResourceManager {
+ // The resource manager owns all the ResourceState.
+ using UniqueResourceState = std::unique_ptr<ResourceState>;
+ llvm::SmallDenseMap<uint64_t, UniqueResourceState> Resources;
+
+ // Keeps track of which resources are busy, and how many cycles are left
+ // before those become usable again.
+ llvm::SmallDenseMap<ResourceRef, unsigned> BusyResources;
+
+ // A table to map processor resource IDs to processor resource masks.
+ llvm::SmallVector<uint64_t, 8> ProcResID2Mask;
+
+ // Adds a new resource state in Resources, as well as a new descriptor in
+ // ResourceDescriptor.
+ void addResource(const llvm::MCProcResourceDesc &Desc, unsigned Index,
+ uint64_t Mask);
+
+ // Populate resource descriptors.
+ void initialize(const llvm::MCSchedModel &SM);
+
+ // Returns the actual resource unit that will be used.
+ ResourceRef selectPipe(uint64_t ResourceID);
+
+ void use(ResourceRef RR);
+ void release(ResourceRef RR);
+
+ unsigned getNumUnits(uint64_t ResourceID) const {
+ assert(Resources.find(ResourceID) != Resources.end());
+ return Resources.find(ResourceID)->getSecond()->getNumUnits();
+ }
+
+ // Reserve a specific Resource kind.
+ void reserveBuffer(uint64_t ResourceID) {
+ assert(isBufferAvailable(ResourceID) ==
+ ResourceStateEvent::RS_BUFFER_AVAILABLE);
+ ResourceState &Resource = *Resources[ResourceID];
+ Resource.reserveBuffer();
+ }
+
+ void releaseBuffer(uint64_t ResourceID) {
+ Resources[ResourceID]->releaseBuffer();
+ }
+
+ ResourceStateEvent isBufferAvailable(uint64_t ResourceID) const {
+ const ResourceState &Resource = *Resources.find(ResourceID)->second;
+ return Resource.isBufferAvailable();
+ }
+
+ bool isReady(uint64_t ResourceID, unsigned NumUnits) const {
+ const ResourceState &Resource = *Resources.find(ResourceID)->second;
+ return Resource.isReady(NumUnits);
+ }
+
+public:
+ ResourceManager(const llvm::MCSchedModel &SM)
+ : ProcResID2Mask(SM.getNumProcResourceKinds()) {
+ initialize(SM);
+ }
+
+ // Returns RS_BUFFER_AVAILABLE if buffered resources are not reserved, and if
+ // there are enough available slots in the buffers.
+ ResourceStateEvent canBeDispatched(llvm::ArrayRef<uint64_t> Buffers) const;
+
+ // Return the processor resource identifier associated to this Mask.
+ unsigned resolveResourceMask(uint64_t Mask) const {
+ return Resources.find(Mask)->second->getProcResourceID();
+ }
+
+ // Consume a slot in every buffered resource from array 'Buffers'. Resource
+ // units that are dispatch hazards (i.e. BufferSize=0) are marked as reserved.
+ void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers);
+
+ // Release buffer entries previously allocated by method reserveBuffers.
+ void releaseBuffers(llvm::ArrayRef<uint64_t> Buffers);
+
+ void reserveResource(uint64_t ResourceID) {
+ ResourceState &Resource = *Resources[ResourceID];
+ assert(!Resource.isReserved());
+ Resource.setReserved();
+ }
+
+ void releaseResource(uint64_t ResourceID) {
+ ResourceState &Resource = *Resources[ResourceID];
+ Resource.clearReserved();
+ }
+
+ // Returns true if all resources are in-order, and there is at least one
+ // resource which is a dispatch hazard (BufferSize = 0).
+ bool mustIssueImmediately(const InstrDesc &Desc);
+
+ bool canBeIssued(const InstrDesc &Desc) const;
+
+ void issueInstruction(
+ const InstrDesc &Desc,
+ llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
+
+ void cycleEvent(llvm::SmallVectorImpl<ResourceRef> &ResourcesFreed);
+
+#ifndef NDEBUG
+ void dump() const {
+ for (const std::pair<uint64_t, UniqueResourceState> &Resource : Resources)
+ Resource.second->dump();
+ }
+#endif
+}; // namespace mca
+
+/// Class Scheduler is responsible for issuing instructions to pipeline
+/// resources. Internally, it delegates to a ResourceManager the management of
+/// processor resources.
+/// This class is also responsible for tracking the progress of instructions
+/// from the dispatch stage, until the write-back stage.
+///
+/// An nstruction dispatched to the Scheduler is initially placed into either
+/// the 'WaitQueue' or the 'ReadyQueue' depending on the availability of the
+/// input operands. Instructions in the WaitQueue are ordered by instruction
+/// index. An instruction is moved from the WaitQueue to the ReadyQueue when
+/// register operands become available, and all memory dependencies are met.
+/// Instructions that are moved from the WaitQueue to the ReadyQueue transition
+/// from state 'IS_AVAILABLE' to state 'IS_READY'.
+///
+/// At the beginning of each cycle, the Scheduler checks if there are
+/// instructions in the WaitQueue that can be moved to the ReadyQueue. If the
+/// ReadyQueue is not empty, then older instructions from the queue are issued
+/// to the processor pipelines, and the underlying ResourceManager is updated
+/// accordingly. The ReadyQueue is ordered by instruction index to guarantee
+/// that the first instructions in the set are also the oldest.
+///
+/// An Instruction is moved from the ReadyQueue the `IssuedQueue` when it is
+/// issued to a (one or more) pipeline(s). This event also causes an instruction
+/// state transition (i.e. from state IS_READY, to state IS_EXECUTING).
+/// An Instruction leaves the IssuedQueue when it reaches the write-back stage.
+class Scheduler : public HardwareUnit {
+ const llvm::MCSchedModel &SM;
+
+ // Hardware resources that are managed by this scheduler.
+ std::unique_ptr<ResourceManager> Resources;
+ std::unique_ptr<LSUnit> LSU;
+
+ using QueueEntryTy = std::pair<unsigned, Instruction *>;
+ std::map<unsigned, Instruction *> WaitQueue;
+ std::map<unsigned, Instruction *> ReadyQueue;
+ std::map<unsigned, Instruction *> IssuedQueue;
+
+ /// Issue an instruction without updating the ready queue.
+ void issueInstructionImpl(
+ InstRef &IR,
+ llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Pipes);
+
+public:
+ Scheduler(const llvm::MCSchedModel &Model, unsigned LoadQueueSize,
+ unsigned StoreQueueSize, bool AssumeNoAlias)
+ : SM(Model), Resources(llvm::make_unique<ResourceManager>(SM)),
+ LSU(llvm::make_unique<LSUnit>(LoadQueueSize, StoreQueueSize,
+ AssumeNoAlias)) {}
+
+ /// Check if the instruction in 'IR' can be dispatched.
+ ///
+ /// The DispatchStage is responsible for querying the Scheduler before
+ /// dispatching new instructions. This routine is used for performing such
+ /// a query. If the instruction 'IR' can be dispatched, then true is
+ /// returned, otherwise false is returned with Event set to the stall type.
+ bool canBeDispatched(const InstRef &IR,
+ HWStallEvent::GenericEventType &Event) const;
+
+ /// Returns true if there is availibility for IR in the LSU.
+ bool isReady(const InstRef &IR) const { return LSU->isReady(IR); }
+
+ /// Issue an instruction. The Used container is populated with
+ /// the resource objects consumed on behalf of issuing this instruction.
+ void
+ issueInstruction(InstRef &IR,
+ llvm::SmallVectorImpl<std::pair<ResourceRef, double>> &Used);
+
+ /// This routine will attempt to issue an instruction immediately (for
+ /// zero-latency instructions).
+ ///
+ /// Returns true if the instruction is issued immediately. If this does not
+ /// occur, then the instruction will be added to the Scheduler's ReadyQueue.
+ bool issueImmediately(InstRef &IR);
+
+ /// Reserve one entry in each buffered resource.
+ void reserveBuffers(llvm::ArrayRef<uint64_t> Buffers) {
+ Resources->reserveBuffers(Buffers);
+ }
+
+ /// Release buffer entries previously allocated by method reserveBuffers.
+ void releaseBuffers(llvm::ArrayRef<uint64_t> Buffers) {
+ Resources->releaseBuffers(Buffers);
+ }
+
+ /// Update the resources managed by the scheduler.
+ /// This routine is to be called at the start of a new cycle, and is
+ /// responsible for updating scheduler resources. Resources are released
+ /// once they have been fully consumed.
+ void reclaimSimulatedResources(llvm::SmallVectorImpl<ResourceRef> &Freed);
+
+ /// Move instructions from the WaitQueue to the ReadyQueue if input operands
+ /// are all available.
+ void promoteToReadyQueue(llvm::SmallVectorImpl<InstRef> &Ready);
+
+ /// Update the ready queue.
+ void updatePendingQueue(llvm::SmallVectorImpl<InstRef> &Ready);
+
+ /// Update the issued queue.
+ void updateIssuedQueue(llvm::SmallVectorImpl<InstRef> &Executed);
+
+ /// Updates the Scheduler's resources to reflect that an instruction has just
+ /// been executed.
+ void onInstructionExecuted(const InstRef &IR);
+
+ /// Obtain the processor's resource identifier for the given
+ /// resource mask.
+ unsigned getResourceID(uint64_t Mask) {
+ return Resources->resolveResourceMask(Mask);
+ }
+
+ /// Reserve resources necessary to issue the instruction.
+ /// Returns true if the resources are ready and the (LSU) can
+ /// execute the given instruction immediately.
+ bool reserveResources(InstRef &IR);
+
+ /// Select the next instruction to issue from the ReadyQueue.
+ /// This method gives priority to older instructions.
+ InstRef select();
+
+#ifndef NDEBUG
+ // Update the ready queues.
+ void dump() const;
+
+ // This routine performs a sanity check. This routine should only be called
+ // when we know that 'IR' is not in the scheduler's instruction queues.
+ void sanityCheck(const InstRef &IR) const {
+ const unsigned Idx = IR.getSourceIndex();
+ assert(WaitQueue.find(Idx) == WaitQueue.end());
+ assert(ReadyQueue.find(Idx) == ReadyQueue.end());
+ assert(IssuedQueue.find(Idx) == IssuedQueue.end());
+ }
+#endif // !NDEBUG
+};
+} // namespace mca
+
+#endif // LLVM_TOOLS_LLVM_MCA_SCHEDULER_H
diff --git a/contrib/llvm/tools/llvm-mca/SchedulerStatistics.cpp b/contrib/llvm/tools/llvm-mca/SchedulerStatistics.cpp
new file mode 100644
index 000000000000..5c6d22a71812
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/SchedulerStatistics.cpp
@@ -0,0 +1,94 @@
+//===--------------------- SchedulerStatistics.cpp --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the SchedulerStatistics interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SchedulerStatistics.h"
+#include "llvm/Support/Format.h"
+
+using namespace llvm;
+
+namespace mca {
+
+void SchedulerStatistics::onEvent(const HWInstructionEvent &Event) {
+ if (Event.Type == HWInstructionEvent::Issued)
+ ++NumIssued;
+}
+
+void SchedulerStatistics::onReservedBuffers(ArrayRef<unsigned> Buffers) {
+ for (const unsigned Buffer : Buffers) {
+ if (BufferedResources.find(Buffer) != BufferedResources.end()) {
+ BufferUsage &BU = BufferedResources[Buffer];
+ BU.SlotsInUse++;
+ BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse);
+ continue;
+ }
+
+ BufferedResources.insert(
+ std::pair<unsigned, BufferUsage>(Buffer, {1U, 1U}));
+ }
+}
+
+void SchedulerStatistics::onReleasedBuffers(ArrayRef<unsigned> Buffers) {
+ for (const unsigned Buffer : Buffers) {
+ assert(BufferedResources.find(Buffer) != BufferedResources.end() &&
+ "Buffered resource not in map?");
+ BufferUsage &BU = BufferedResources[Buffer];
+ BU.SlotsInUse--;
+ }
+}
+
+void SchedulerStatistics::printSchedulerStatistics(
+ llvm::raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nSchedulers - number of cycles where we saw N instructions "
+ "issued:\n";
+ TempStream << "[# issued], [# cycles]\n";
+ for (const std::pair<unsigned, unsigned> &Entry : IssuedPerCycle) {
+ TempStream << " " << Entry.first << ", " << Entry.second << " ("
+ << format("%.1f", ((double)Entry.second / NumCycles) * 100)
+ << "%)\n";
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+
+void SchedulerStatistics::printSchedulerUsage(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "\n\nScheduler's queue usage:\n";
+ // Early exit if no buffered resources were consumed.
+ if (BufferedResources.empty()) {
+ TempStream << "No scheduler resources used.\n";
+ TempStream.flush();
+ OS << Buffer;
+ return;
+ }
+
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &ProcResource = *SM.getProcResource(I);
+ if (ProcResource.BufferSize <= 0)
+ continue;
+
+ const auto It = BufferedResources.find(I);
+ unsigned MaxUsedSlots =
+ It == BufferedResources.end() ? 0 : It->second.MaxUsedSlots;
+ TempStream << ProcResource.Name << ", " << MaxUsedSlots << '/'
+ << ProcResource.BufferSize << '\n';
+ }
+
+ TempStream.flush();
+ OS << Buffer;
+}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/SchedulerStatistics.h b/contrib/llvm/tools/llvm-mca/SchedulerStatistics.h
new file mode 100644
index 000000000000..7383c54a1615
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/SchedulerStatistics.h
@@ -0,0 +1,91 @@
+//===--------------------- SchedulerStatistics.h ----------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines class SchedulerStatistics. Class SchedulerStatistics is a
+/// View that listens to instruction issue events in order to print general
+/// statistics related to the hardware schedulers.
+///
+/// Example:
+/// ========
+///
+/// Schedulers - number of cycles where we saw N instructions issued:
+/// [# issued], [# cycles]
+/// 0, 7 (5.4%)
+/// 1, 4 (3.1%)
+/// 2, 8 (6.2%)
+///
+/// Scheduler's queue usage:
+/// JALU01, 0/20
+/// JFPU01, 18/18
+/// JLSAGU, 0/12
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_SCHEDULERSTATISTICS_H
+#define LLVM_TOOLS_LLVM_MCA_SCHEDULERSTATISTICS_H
+
+#include "View.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include <map>
+
+namespace mca {
+
+class SchedulerStatistics : public View {
+ const llvm::MCSchedModel &SM;
+
+ using Histogram = std::map<unsigned, unsigned>;
+ Histogram IssuedPerCycle;
+
+ unsigned NumIssued;
+ unsigned NumCycles;
+
+ // Tracks the usage of a scheduler's queue.
+ struct BufferUsage {
+ unsigned SlotsInUse;
+ unsigned MaxUsedSlots;
+ };
+
+ std::map<unsigned, BufferUsage> BufferedResources;
+
+ void updateHistograms() {
+ IssuedPerCycle[NumIssued]++;
+ NumIssued = 0;
+ }
+
+ void printSchedulerStatistics(llvm::raw_ostream &OS) const;
+ void printSchedulerUsage(llvm::raw_ostream &OS) const;
+
+public:
+ SchedulerStatistics(const llvm::MCSubtargetInfo &STI)
+ : SM(STI.getSchedModel()), NumIssued(0), NumCycles(0) {}
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void onCycleBegin() override { NumCycles++; }
+
+ void onCycleEnd() override { updateHistograms(); }
+
+ // Increases the number of used scheduler queue slots of every buffered
+ // resource in the Buffers set.
+ void onReservedBuffers(llvm::ArrayRef<unsigned> Buffers) override;
+
+ // Decreases by one the number of used scheduler queue slots of every
+ // buffered resource in the Buffers set.
+ void onReleasedBuffers(llvm::ArrayRef<unsigned> Buffers) override;
+
+ void printView(llvm::raw_ostream &OS) const override {
+ printSchedulerStatistics(OS);
+ printSchedulerUsage(OS);
+ }
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/SourceMgr.h b/contrib/llvm/tools/llvm-mca/SourceMgr.h
new file mode 100644
index 000000000000..15a85a69569f
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/SourceMgr.h
@@ -0,0 +1,63 @@
+//===--------------------- SourceMgr.h --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file implements class SourceMgr. Class SourceMgr abstracts the input
+/// code sequence (a sequence of MCInst), and assings unique identifiers to
+/// every instruction in the sequence.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_SOURCEMGR_H
+#define LLVM_TOOLS_LLVM_MCA_SOURCEMGR_H
+
+#include "llvm/MC/MCInst.h"
+#include <vector>
+
+namespace mca {
+
+typedef std::pair<unsigned, const llvm::MCInst *> SourceRef;
+
+class SourceMgr {
+ using InstVec = std::vector<std::unique_ptr<const llvm::MCInst>>;
+ const InstVec &Sequence;
+ unsigned Current;
+ unsigned Iterations;
+ static const unsigned DefaultIterations = 100;
+
+public:
+ SourceMgr(const InstVec &MCInstSequence, unsigned NumIterations)
+ : Sequence(MCInstSequence), Current(0),
+ Iterations(NumIterations ? NumIterations : DefaultIterations) {}
+
+ unsigned getCurrentIteration() const { return Current / Sequence.size(); }
+ unsigned getNumIterations() const { return Iterations; }
+ unsigned size() const { return Sequence.size(); }
+ const InstVec &getSequence() const { return Sequence; }
+
+ bool hasNext() const { return Current < (Iterations * size()); }
+ void updateNext() { Current++; }
+
+ const SourceRef peekNext() const {
+ unsigned Index = getCurrentInstructionIndex();
+ return SourceRef(Current, Sequence[Index].get());
+ }
+
+ unsigned getCurrentInstructionIndex() const {
+ return Current % Sequence.size();
+ }
+
+ const llvm::MCInst &getMCInstFromIndex(unsigned Index) const {
+ return *Sequence[Index % size()];
+ }
+
+ bool isEmpty() const { return size() == 0; }
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/Stage.cpp b/contrib/llvm/tools/llvm-mca/Stage.cpp
new file mode 100644
index 000000000000..7ead940e63c1
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Stage.cpp
@@ -0,0 +1,27 @@
+//===---------------------- Stage.cpp ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a stage.
+/// A chain of stages compose an instruction pipeline.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Stage.h"
+
+namespace mca {
+
+// Pin the vtable here in the implementation file.
+Stage::Stage() {}
+
+void Stage::addListener(HWEventListener *Listener) {
+ Listeners.insert(Listener);
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/Stage.h b/contrib/llvm/tools/llvm-mca/Stage.h
new file mode 100644
index 000000000000..9dbdcd89a33b
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Stage.h
@@ -0,0 +1,76 @@
+//===---------------------- Stage.h -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines a stage.
+/// A chain of stages compose an instruction pipeline.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_STAGE_H
+#define LLVM_TOOLS_LLVM_MCA_STAGE_H
+
+#include "HWEventListener.h"
+#include <set>
+
+namespace mca {
+
+class InstRef;
+
+class Stage {
+ Stage(const Stage &Other) = delete;
+ Stage &operator=(const Stage &Other) = delete;
+ std::set<HWEventListener *> Listeners;
+
+protected:
+ const std::set<HWEventListener *> &getListeners() const { return Listeners; }
+
+public:
+ Stage();
+ virtual ~Stage() = default;
+
+ /// Called prior to preExecute to ensure that the stage has items that it
+ /// is to process. For example, a FetchStage might have more instructions
+ /// that need to be processed, or a RCU might have items that have yet to
+ /// retire.
+ virtual bool hasWorkToComplete() const = 0;
+
+ /// Called once at the start of each cycle. This can be used as a setup
+ /// phase to prepare for the executions during the cycle.
+ virtual void cycleStart() {}
+
+ /// Called once at the end of each cycle.
+ virtual void cycleEnd() {}
+
+ /// Called prior to executing the list of stages.
+ /// This can be called multiple times per cycle.
+ virtual void preExecute() {}
+
+ /// Called as a cleanup and finalization phase after each execution.
+ /// This will only be called if all stages return a success from their
+ /// execute callback. This can be called multiple times per cycle.
+ virtual void postExecute() {}
+
+ /// The primary action that this stage performs.
+ /// Returning false prevents successor stages from having their 'execute'
+ /// routine called. This can be called multiple times during a single cycle.
+ virtual bool execute(InstRef &IR) = 0;
+
+ /// Add a listener to receive callbacks during the execution of this stage.
+ void addListener(HWEventListener *Listener);
+
+ /// Notify listeners of a particular hardware event.
+ template <typename EventT> void notifyEvent(const EventT &Event) {
+ for (HWEventListener *Listener : Listeners)
+ Listener->onEvent(Event);
+ }
+};
+
+} // namespace mca
+#endif // LLVM_TOOLS_LLVM_MCA_STAGE_H
diff --git a/contrib/llvm/tools/llvm-mca/SummaryView.cpp b/contrib/llvm/tools/llvm-mca/SummaryView.cpp
new file mode 100644
index 000000000000..01399055c4fd
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/SummaryView.cpp
@@ -0,0 +1,85 @@
+//===--------------------- SummaryView.cpp -------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the functionalities used by the SummaryView to print
+/// the report information.
+///
+//===----------------------------------------------------------------------===//
+
+#include "SummaryView.h"
+#include "Support.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Format.h"
+
+namespace mca {
+
+#define DEBUG_TYPE "llvm-mca"
+
+using namespace llvm;
+
+SummaryView::SummaryView(const llvm::MCSchedModel &Model, const SourceMgr &S,
+ unsigned Width)
+ : SM(Model), Source(S), DispatchWidth(Width), TotalCycles(0),
+ NumMicroOps(0), ProcResourceUsage(Model.getNumProcResourceKinds(), 0),
+ ProcResourceMasks(Model.getNumProcResourceKinds(), 0) {
+ computeProcResourceMasks(SM, ProcResourceMasks);
+}
+
+void SummaryView::onEvent(const HWInstructionEvent &Event) {
+ // We are only interested in the "instruction dispatched" events generated by
+ // the dispatch stage for instructions that are part of iteration #0.
+ if (Event.Type != HWInstructionEvent::Dispatched)
+ return;
+
+ if (Event.IR.getSourceIndex() >= Source.size())
+ return;
+
+ // Update the cumulative number of resource cycles based on the processor
+ // resource usage information available from the instruction descriptor. We
+ // need to compute the cumulative number of resource cycles for every
+ // processor resource which is consumed by an instruction of the block.
+ const Instruction &Inst = *Event.IR.getInstruction();
+ const InstrDesc &Desc = Inst.getDesc();
+ NumMicroOps += Desc.NumMicroOps;
+ for (const std::pair<uint64_t, const ResourceUsage> &RU : Desc.Resources) {
+ if (RU.second.size()) {
+ const auto It = find(ProcResourceMasks, RU.first);
+ assert(It != ProcResourceMasks.end() &&
+ "Invalid processor resource mask!");
+ ProcResourceUsage[std::distance(ProcResourceMasks.begin(), It)] +=
+ RU.second.size();
+ }
+ }
+}
+
+void SummaryView::printView(raw_ostream &OS) const {
+ unsigned Iterations = Source.getNumIterations();
+ unsigned Instructions = Source.size();
+ unsigned TotalInstructions = Instructions * Iterations;
+ double IPC = (double)TotalInstructions / TotalCycles;
+ double BlockRThroughput = computeBlockRThroughput(
+ SM, DispatchWidth, NumMicroOps, ProcResourceUsage);
+
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ TempStream << "Iterations: " << Iterations;
+ TempStream << "\nInstructions: " << TotalInstructions;
+ TempStream << "\nTotal Cycles: " << TotalCycles;
+ TempStream << "\nDispatch Width: " << DispatchWidth;
+ TempStream << "\nIPC: " << format("%.2f", IPC);
+
+ // Round to the block reciprocal throughput to the nearest tenth.
+ TempStream << "\nBlock RThroughput: "
+ << format("%.1f", floor((BlockRThroughput * 10) + 0.5) / 10)
+ << '\n';
+ TempStream.flush();
+ OS << Buffer;
+}
+} // namespace mca.
diff --git a/contrib/llvm/tools/llvm-mca/SummaryView.h b/contrib/llvm/tools/llvm-mca/SummaryView.h
new file mode 100644
index 000000000000..b799ce3aa747
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/SummaryView.h
@@ -0,0 +1,76 @@
+//===--------------------- SummaryView.h ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements the summary view.
+///
+/// The goal of the summary view is to give a very quick overview of the
+/// performance throughput. Below is an example of summary view:
+///
+///
+/// Iterations: 300
+/// Instructions: 900
+/// Total Cycles: 610
+/// Dispatch Width: 2
+/// IPC: 1.48
+/// Block RThroughput: 2.0
+///
+/// The summary view collects a few performance numbers. The two main
+/// performance indicators are 'Total Cycles' and IPC (Instructions Per Cycle).
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H
+
+#include "SourceMgr.h"
+#include "View.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/MC/MCSchedule.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace mca {
+
+/// A view that collects and prints a few performance numbers.
+class SummaryView : public View {
+ const llvm::MCSchedModel &SM;
+ const SourceMgr &Source;
+ const unsigned DispatchWidth;
+ unsigned TotalCycles;
+ // The total number of micro opcodes contributed by a block of instructions.
+ unsigned NumMicroOps;
+ // For each processor resource, this vector stores the cumulative number of
+ // resource cycles consumed by the analyzed code block.
+ llvm::SmallVector<unsigned, 8> ProcResourceUsage;
+
+ // Each processor resource is associated with a so-called processor resource
+ // mask. This vector allows to correlate processor resource IDs with processor
+ // resource masks. There is exactly one element per each processor resource
+ // declared by the scheduling model.
+ llvm::SmallVector<uint64_t, 8> ProcResourceMasks;
+
+ // Compute the reciprocal throughput for the analyzed code block.
+ // The reciprocal block throughput is computed as the MAX between:
+ // - NumMicroOps / DispatchWidth
+ // - Total Resource Cycles / #Units (for every resource consumed).
+ double getBlockRThroughput() const;
+
+public:
+ SummaryView(const llvm::MCSchedModel &Model, const SourceMgr &S,
+ unsigned Width);
+
+ void onCycleEnd() override { ++TotalCycles; }
+
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ void printView(llvm::raw_ostream &OS) const override;
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/Support.cpp b/contrib/llvm/tools/llvm-mca/Support.cpp
new file mode 100644
index 000000000000..8f6b8a91f38f
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Support.cpp
@@ -0,0 +1,79 @@
+//===--------------------- Support.cpp --------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file implements a few helper functions used by various pipeline
+/// components.
+///
+//===----------------------------------------------------------------------===//
+
+#include "Support.h"
+#include "llvm/MC/MCSchedule.h"
+
+namespace mca {
+
+using namespace llvm;
+
+void computeProcResourceMasks(const MCSchedModel &SM,
+ SmallVectorImpl<uint64_t> &Masks) {
+ unsigned ProcResourceID = 0;
+
+ // Create a unique bitmask for every processor resource unit.
+ // Skip resource at index 0, since it always references 'InvalidUnit'.
+ Masks.resize(SM.getNumProcResourceKinds());
+ for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &Desc = *SM.getProcResource(I);
+ if (Desc.SubUnitsIdxBegin)
+ continue;
+ Masks[I] = 1ULL << ProcResourceID;
+ ProcResourceID++;
+ }
+
+ // Create a unique bitmask for every processor resource group.
+ for (unsigned I = 1, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ const MCProcResourceDesc &Desc = *SM.getProcResource(I);
+ if (!Desc.SubUnitsIdxBegin)
+ continue;
+ Masks[I] = 1ULL << ProcResourceID;
+ for (unsigned U = 0; U < Desc.NumUnits; ++U) {
+ uint64_t OtherMask = Masks[Desc.SubUnitsIdxBegin[U]];
+ Masks[I] |= OtherMask;
+ }
+ ProcResourceID++;
+ }
+}
+
+double computeBlockRThroughput(const MCSchedModel &SM, unsigned DispatchWidth,
+ unsigned NumMicroOps,
+ ArrayRef<unsigned> ProcResourceUsage) {
+ // The block throughput is bounded from above by the hardware dispatch
+ // throughput. That is because the DispatchWidth is an upper bound on the
+ // number of opcodes that can be part of a single dispatch group.
+ double Max = static_cast<double>(NumMicroOps) / DispatchWidth;
+
+ // The block throughput is also limited by the amount of hardware parallelism.
+ // The number of available resource units affects the resource pressure
+ // distribution, as well as how many blocks can be executed every cycle.
+ for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
+ unsigned ResourceCycles = ProcResourceUsage[I];
+ if (!ResourceCycles)
+ continue;
+
+ const MCProcResourceDesc &MCDesc = *SM.getProcResource(I);
+ double Throughput = static_cast<double>(ResourceCycles) / MCDesc.NumUnits;
+ Max = std::max(Max, Throughput);
+ }
+
+ // The block reciprocal throughput is computed as the MAX of:
+ // - (NumMicroOps / DispatchWidth)
+ // - (NumUnits / ResourceCycles) for every consumed processor resource.
+ return Max;
+}
+
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/Support.h b/contrib/llvm/tools/llvm-mca/Support.h
new file mode 100644
index 000000000000..fd8d8b5a23b3
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/Support.h
@@ -0,0 +1,58 @@
+//===--------------------- Support.h ----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// Helper functions used by various pipeline components.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_SUPPORT_H
+#define LLVM_TOOLS_LLVM_MCA_SUPPORT_H
+
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/MC/MCSchedule.h"
+
+namespace mca {
+
+/// Populates vector Masks with processor resource masks.
+///
+/// The number of bits set in a mask depends on the processor resource type.
+/// Each processor resource mask has at least one bit set. For groups, the
+/// number of bits set in the mask is equal to the cardinality of the group plus
+/// one. Excluding the most significant bit, the remaining bits in the mask
+/// identify processor resources that are part of the group.
+///
+/// Example:
+///
+/// ResourceA -- Mask: 0b001
+/// ResourceB -- Mask: 0b010
+/// ResourceAB -- Mask: 0b100 U (ResourceA::Mask | ResourceB::Mask) == 0b111
+///
+/// ResourceAB is a processor resource group containing ResourceA and ResourceB.
+/// Each resource mask uniquely identifies a resource; both ResourceA and
+/// ResourceB only have one bit set.
+/// ResourceAB is a group; excluding the most significant bit in the mask, the
+/// remaining bits identify the composition of the group.
+///
+/// Resource masks are used by the ResourceManager to solve set membership
+/// problems with simple bit manipulation operations.
+void computeProcResourceMasks(const llvm::MCSchedModel &SM,
+ llvm::SmallVectorImpl<uint64_t> &Masks);
+
+/// Compute the reciprocal block throughput from a set of processor resource
+/// cycles. The reciprocal block throughput is computed as the MAX between:
+/// - NumMicroOps / DispatchWidth
+/// - ProcResourceCycles / #ProcResourceUnits (for every consumed resource).
+double computeBlockRThroughput(const llvm::MCSchedModel &SM,
+ unsigned DispatchWidth, unsigned NumMicroOps,
+ llvm::ArrayRef<unsigned> ProcResourceUsage);
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/TimelineView.cpp b/contrib/llvm/tools/llvm-mca/TimelineView.cpp
new file mode 100644
index 000000000000..6e75cac0d432
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/TimelineView.cpp
@@ -0,0 +1,240 @@
+//===--------------------- TimelineView.cpp ---------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \brief
+///
+/// This file implements the TimelineView interface.
+///
+//===----------------------------------------------------------------------===//
+
+#include "TimelineView.h"
+
+using namespace llvm;
+
+namespace mca {
+
+void TimelineView::initialize(unsigned MaxIterations) {
+ unsigned NumInstructions =
+ AsmSequence.getNumIterations() * AsmSequence.size();
+ if (!MaxIterations)
+ MaxIterations = DEFAULT_ITERATIONS;
+ unsigned NumEntries =
+ std::min(NumInstructions, MaxIterations * AsmSequence.size());
+ Timeline.resize(NumEntries);
+ TimelineViewEntry NullTVEntry = {0, 0, 0, 0, 0};
+ std::fill(Timeline.begin(), Timeline.end(), NullTVEntry);
+
+ WaitTime.resize(AsmSequence.size());
+ WaitTimeEntry NullWTEntry = {0, 0, 0, 0};
+ std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry);
+}
+
+void TimelineView::onEvent(const HWInstructionEvent &Event) {
+ const unsigned Index = Event.IR.getSourceIndex();
+ if (CurrentCycle >= MaxCycle || Index >= Timeline.size())
+ return;
+ switch (Event.Type) {
+ case HWInstructionEvent::Retired: {
+ TimelineViewEntry &TVEntry = Timeline[Index];
+ TVEntry.CycleRetired = CurrentCycle;
+
+ // Update the WaitTime entry which corresponds to this Index.
+ WaitTimeEntry &WTEntry = WaitTime[Index % AsmSequence.size()];
+ WTEntry.Executions++;
+ WTEntry.CyclesSpentInSchedulerQueue +=
+ TVEntry.CycleIssued - TVEntry.CycleDispatched;
+ assert(TVEntry.CycleDispatched <= TVEntry.CycleReady);
+ WTEntry.CyclesSpentInSQWhileReady +=
+ TVEntry.CycleIssued - TVEntry.CycleReady;
+ WTEntry.CyclesSpentAfterWBAndBeforeRetire +=
+ (TVEntry.CycleRetired - 1) - TVEntry.CycleExecuted;
+ break;
+ }
+ case HWInstructionEvent::Ready:
+ Timeline[Index].CycleReady = CurrentCycle;
+ break;
+ case HWInstructionEvent::Issued:
+ Timeline[Index].CycleIssued = CurrentCycle;
+ break;
+ case HWInstructionEvent::Executed:
+ Timeline[Index].CycleExecuted = CurrentCycle;
+ break;
+ case HWInstructionEvent::Dispatched:
+ Timeline[Index].CycleDispatched = CurrentCycle;
+ break;
+ default:
+ return;
+ }
+ LastCycle = std::max(LastCycle, CurrentCycle);
+}
+
+void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS,
+ const WaitTimeEntry &Entry,
+ unsigned SourceIndex) const {
+ OS << SourceIndex << '.';
+ OS.PadToColumn(7);
+
+ if (Entry.Executions == 0) {
+ OS << "- - - - ";
+ } else {
+ double AverageTime1, AverageTime2, AverageTime3;
+ unsigned Executions = Entry.Executions;
+ AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions;
+ AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions;
+ AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions;
+
+ OS << Executions;
+ OS.PadToColumn(13);
+
+ OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10);
+ OS.PadToColumn(20);
+ OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10);
+ OS.PadToColumn(27);
+ OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10);
+ OS.PadToColumn(34);
+ }
+}
+
+void TimelineView::printAverageWaitTimes(raw_ostream &OS) const {
+ if (WaitTime.empty())
+ return;
+
+ std::string Buffer;
+ raw_string_ostream TempStream(Buffer);
+ formatted_raw_ostream FOS(TempStream);
+
+ FOS << "\n\nAverage Wait times (based on the timeline view):\n"
+ << "[0]: Executions\n"
+ << "[1]: Average time spent waiting in a scheduler's queue\n"
+ << "[2]: Average time spent waiting in a scheduler's queue while ready\n"
+ << "[3]: Average time elapsed from WB until retire stage\n\n";
+ FOS << " [0] [1] [2] [3]\n";
+
+ // Use a different string stream for the instruction.
+ std::string Instruction;
+ raw_string_ostream InstrStream(Instruction);
+
+ for (unsigned I = 0, E = WaitTime.size(); I < E; ++I) {
+ printWaitTimeEntry(FOS, WaitTime[I], I);
+ // Append the instruction info at the end of the line.
+ const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
+
+ MCIP.printInst(&Inst, InstrStream, "", STI);
+ InstrStream.flush();
+
+ // Consume any tabs or spaces at the beginning of the string.
+ StringRef Str(Instruction);
+ Str = Str.ltrim();
+ FOS << " " << Str << '\n';
+ FOS.flush();
+ Instruction = "";
+
+ OS << Buffer;
+ Buffer = "";
+ }
+}
+
+void TimelineView::printTimelineViewEntry(formatted_raw_ostream &OS,
+ const TimelineViewEntry &Entry,
+ unsigned Iteration,
+ unsigned SourceIndex) const {
+ if (Iteration == 0 && SourceIndex == 0)
+ OS << '\n';
+ OS << '[' << Iteration << ',' << SourceIndex << ']';
+ OS.PadToColumn(10);
+ for (unsigned I = 0, E = Entry.CycleDispatched; I < E; ++I)
+ OS << ((I % 5 == 0) ? '.' : ' ');
+ OS << TimelineView::DisplayChar::Dispatched;
+ if (Entry.CycleDispatched != Entry.CycleExecuted) {
+ // Zero latency instructions have the same value for CycleDispatched,
+ // CycleIssued and CycleExecuted.
+ for (unsigned I = Entry.CycleDispatched + 1, E = Entry.CycleIssued; I < E;
+ ++I)
+ OS << TimelineView::DisplayChar::Waiting;
+ if (Entry.CycleIssued == Entry.CycleExecuted)
+ OS << TimelineView::DisplayChar::DisplayChar::Executed;
+ else {
+ if (Entry.CycleDispatched != Entry.CycleIssued)
+ OS << TimelineView::DisplayChar::Executing;
+ for (unsigned I = Entry.CycleIssued + 1, E = Entry.CycleExecuted; I < E;
+ ++I)
+ OS << TimelineView::DisplayChar::Executing;
+ OS << TimelineView::DisplayChar::Executed;
+ }
+ }
+
+ for (unsigned I = Entry.CycleExecuted + 1, E = Entry.CycleRetired; I < E; ++I)
+ OS << TimelineView::DisplayChar::RetireLag;
+ OS << TimelineView::DisplayChar::Retired;
+
+ // Skip other columns.
+ for (unsigned I = Entry.CycleRetired + 1, E = LastCycle; I <= E; ++I)
+ OS << ((I % 5 == 0 || I == LastCycle) ? '.' : ' ');
+}
+
+static void printTimelineHeader(formatted_raw_ostream &OS, unsigned Cycles) {
+ OS << "\n\nTimeline view:\n";
+ if (Cycles >= 10) {
+ OS.PadToColumn(10);
+ for (unsigned I = 0; I <= Cycles; ++I) {
+ if (((I / 10) & 1) == 0)
+ OS << ' ';
+ else
+ OS << I % 10;
+ }
+ OS << '\n';
+ }
+
+ OS << "Index";
+ OS.PadToColumn(10);
+ for (unsigned I = 0; I <= Cycles; ++I) {
+ if (((I / 10) & 1) == 0)
+ OS << I % 10;
+ else
+ OS << ' ';
+ }
+ OS << '\n';
+}
+
+void TimelineView::printTimeline(raw_ostream &OS) const {
+ std::string Buffer;
+ raw_string_ostream StringStream(Buffer);
+ formatted_raw_ostream FOS(StringStream);
+
+ printTimelineHeader(FOS, LastCycle);
+ FOS.flush();
+ OS << Buffer;
+
+ // Use a different string stream for the instruction.
+ std::string Instruction;
+ raw_string_ostream InstrStream(Instruction);
+
+ for (unsigned I = 0, E = Timeline.size(); I < E; ++I) {
+ Buffer = "";
+ const TimelineViewEntry &Entry = Timeline[I];
+ if (Entry.CycleRetired == 0)
+ return;
+
+ unsigned Iteration = I / AsmSequence.size();
+ unsigned SourceIndex = I % AsmSequence.size();
+ printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex);
+ // Append the instruction info at the end of the line.
+ const MCInst &Inst = AsmSequence.getMCInstFromIndex(I);
+ MCIP.printInst(&Inst, InstrStream, "", STI);
+ InstrStream.flush();
+
+ // Consume any tabs or spaces at the beginning of the string.
+ StringRef Str(Instruction);
+ Str = Str.ltrim();
+ FOS << " " << Str << '\n';
+ FOS.flush();
+ Instruction = "";
+ OS << Buffer;
+ }
+}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/TimelineView.h b/contrib/llvm/tools/llvm-mca/TimelineView.h
new file mode 100644
index 000000000000..e53c23ec1cc2
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/TimelineView.h
@@ -0,0 +1,189 @@
+//===--------------------- TimelineView.h -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \brief
+///
+/// This file implements a timeline view for the llvm-mca tool.
+///
+/// Class TimelineView observes events generated by the pipeline. For every
+/// instruction executed by the pipeline, it stores information related to
+/// state transition. It then plots that information in the form of a table
+/// as reported by the example below:
+///
+/// Timeline view:
+/// 0123456
+/// Index 0123456789
+///
+/// [0,0] DeER . . .. vmovshdup %xmm0, %xmm1
+/// [0,1] DeER . . .. vpermilpd $1, %xmm0, %xmm2
+/// [0,2] .DeER. . .. vpermilps $231, %xmm0, %xmm5
+/// [0,3] .DeeeER . .. vaddss %xmm1, %xmm0, %xmm3
+/// [0,4] . D==eeeER. .. vaddss %xmm3, %xmm2, %xmm4
+/// [0,5] . D=====eeeER .. vaddss %xmm4, %xmm5, %xmm6
+///
+/// [1,0] . DeE------R .. vmovshdup %xmm0, %xmm1
+/// [1,1] . DeE------R .. vpermilpd $1, %xmm0, %xmm2
+/// [1,2] . DeE-----R .. vpermilps $231, %xmm0, %xmm5
+/// [1,3] . D=eeeE--R .. vaddss %xmm1, %xmm0, %xmm3
+/// [1,4] . D===eeeER .. vaddss %xmm3, %xmm2, %xmm4
+/// [1,5] . D======eeeER vaddss %xmm4, %xmm5, %xmm6
+///
+/// There is an entry for every instruction in the input assembly sequence.
+/// The first field is a pair of numbers obtained from the instruction index.
+/// The first element of the pair is the iteration index, while the second
+/// element of the pair is a sequence number (i.e. a position in the assembly
+/// sequence).
+/// The second field of the table is the actual timeline information; each
+/// column is the information related to a specific cycle of execution.
+/// The timeline of an instruction is described by a sequence of character
+/// where each character represents the instruction state at a specific cycle.
+///
+/// Possible instruction states are:
+/// D: Instruction Dispatched
+/// e: Instruction Executing
+/// E: Instruction Executed (write-back stage)
+/// R: Instruction retired
+/// =: Instruction waiting in the Scheduler's queue
+/// -: Instruction executed, waiting to retire in order.
+///
+/// dots ('.') and empty spaces are cycles where the instruction is not
+/// in-flight.
+///
+/// The last column is the assembly instruction associated to the entry.
+///
+/// Based on the timeline view information from the example, instruction 0
+/// at iteration 0 was dispatched at cycle 0, and was retired at cycle 3.
+/// Instruction [0,1] was also dispatched at cycle 0, and it retired at
+/// the same cycle than instruction [0,0].
+/// Instruction [0,4] has been dispatched at cycle 2. However, it had to
+/// wait for two cycles before being issued. That is because operands
+/// became ready only at cycle 5.
+///
+/// This view helps further understanding bottlenecks and the impact of
+/// resource pressure on the code.
+///
+/// To better understand why instructions had to wait for multiple cycles in
+/// the scheduler's queue, class TimelineView also reports extra timing info
+/// in another table named "Average Wait times" (see example below).
+///
+///
+/// Average Wait times (based on the timeline view):
+/// [0]: Executions
+/// [1]: Average time spent waiting in a scheduler's queue
+/// [2]: Average time spent waiting in a scheduler's queue while ready
+/// [3]: Average time elapsed from WB until retire stage
+///
+/// [0] [1] [2] [3]
+/// 0. 2 1.0 1.0 3.0 vmovshdup %xmm0, %xmm1
+/// 1. 2 1.0 1.0 3.0 vpermilpd $1, %xmm0, %xmm2
+/// 2. 2 1.0 1.0 2.5 vpermilps $231, %xmm0, %xmm5
+/// 3. 2 1.5 0.5 1.0 vaddss %xmm1, %xmm0, %xmm3
+/// 4. 2 3.5 0.0 0.0 vaddss %xmm3, %xmm2, %xmm4
+/// 5. 2 6.5 0.0 0.0 vaddss %xmm4, %xmm5, %xmm6
+///
+/// By comparing column [2] with column [1], we get an idea about how many
+/// cycles were spent in the scheduler's queue due to data dependencies.
+///
+/// In this example, instruction 5 spent an average of ~6 cycles in the
+/// scheduler's queue. As soon as operands became ready, the instruction
+/// was immediately issued to the pipeline(s).
+/// That is expected because instruction 5 cannot transition to the "ready"
+/// state until %xmm4 is written by instruction 4.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
+#define LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H
+
+#include "SourceMgr.h"
+#include "View.h"
+#include "llvm/MC/MCInstPrinter.h"
+#include "llvm/MC/MCSubtargetInfo.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+#include <map>
+
+namespace mca {
+
+/// This class listens to instruction state transition events
+/// in order to construct a timeline information.
+///
+/// For every instruction executed by the Pipeline, this class constructs
+/// a TimelineViewEntry object. TimelineViewEntry objects are then used
+/// to print the timeline information, as well as the "average wait times"
+/// for every instruction in the input assembly sequence.
+class TimelineView : public View {
+ const llvm::MCSubtargetInfo &STI;
+ llvm::MCInstPrinter &MCIP;
+ const SourceMgr &AsmSequence;
+
+ unsigned CurrentCycle;
+ unsigned MaxCycle;
+ unsigned LastCycle;
+
+ struct TimelineViewEntry {
+ unsigned CycleDispatched;
+ unsigned CycleReady;
+ unsigned CycleIssued;
+ unsigned CycleExecuted;
+ unsigned CycleRetired;
+ };
+ std::vector<TimelineViewEntry> Timeline;
+
+ struct WaitTimeEntry {
+ unsigned Executions;
+ unsigned CyclesSpentInSchedulerQueue;
+ unsigned CyclesSpentInSQWhileReady;
+ unsigned CyclesSpentAfterWBAndBeforeRetire;
+ };
+ std::vector<WaitTimeEntry> WaitTime;
+
+ void printTimelineViewEntry(llvm::formatted_raw_ostream &OS,
+ const TimelineViewEntry &E, unsigned Iteration,
+ unsigned SourceIndex) const;
+ void printWaitTimeEntry(llvm::formatted_raw_ostream &OS,
+ const WaitTimeEntry &E, unsigned Index) const;
+
+ const unsigned DEFAULT_ITERATIONS = 10;
+
+ void initialize(unsigned MaxIterations);
+
+ // Display characters for the TimelineView report output.
+ struct DisplayChar {
+ static const char Dispatched = 'D';
+ static const char Executed = 'E';
+ static const char Retired = 'R';
+ static const char Waiting = '='; // Instruction is waiting in the scheduler.
+ static const char Executing = 'e';
+ static const char RetireLag = '-'; // The instruction is waiting to retire.
+ };
+
+public:
+ TimelineView(const llvm::MCSubtargetInfo &sti, llvm::MCInstPrinter &Printer,
+ const SourceMgr &Sequence, unsigned MaxIterations,
+ unsigned Cycles)
+ : STI(sti), MCIP(Printer), AsmSequence(Sequence), CurrentCycle(0),
+ MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0) {
+ initialize(MaxIterations);
+ }
+
+ // Event handlers.
+ void onCycleEnd() override { ++CurrentCycle; }
+ void onEvent(const HWInstructionEvent &Event) override;
+
+ // print functionalities.
+ void printTimeline(llvm::raw_ostream &OS) const;
+ void printAverageWaitTimes(llvm::raw_ostream &OS) const;
+ void printView(llvm::raw_ostream &OS) const override {
+ printTimeline(OS);
+ printAverageWaitTimes(OS);
+ }
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/View.cpp b/contrib/llvm/tools/llvm-mca/View.cpp
new file mode 100644
index 000000000000..390a7aeb3b9d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/View.cpp
@@ -0,0 +1,20 @@
+//===----------------------- View.cpp ---------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the virtual anchor method in View.h to pin the vtable.
+///
+//===----------------------------------------------------------------------===//
+
+#include "View.h"
+
+namespace mca {
+
+void View::anchor() {}
+} // namespace mca
diff --git a/contrib/llvm/tools/llvm-mca/View.h b/contrib/llvm/tools/llvm-mca/View.h
new file mode 100644
index 000000000000..9ba94a5da977
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/View.h
@@ -0,0 +1,32 @@
+//===----------------------- View.h -----------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+///
+/// This file defines the main interface for Views. Each view contributes a
+/// portion of the final report generated by the tool.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_MCA_VIEW_H
+#define LLVM_TOOLS_LLVM_MCA_VIEW_H
+
+#include "HWEventListener.h"
+#include "llvm/Support/raw_ostream.h"
+
+namespace mca {
+
+class View : public HWEventListener {
+public:
+ virtual void printView(llvm::raw_ostream &OS) const = 0;
+ virtual ~View() = default;
+ void anchor() override;
+};
+} // namespace mca
+
+#endif
diff --git a/contrib/llvm/tools/llvm-mca/llvm-mca.cpp b/contrib/llvm/tools/llvm-mca/llvm-mca.cpp
new file mode 100644
index 000000000000..2d292f375e6e
--- /dev/null
+++ b/contrib/llvm/tools/llvm-mca/llvm-mca.cpp
@@ -0,0 +1,552 @@
+//===-- llvm-mca.cpp - Machine Code Analyzer -------------------*- C++ -* -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This utility is a simple driver that allows static performance analysis on
+// machine code similarly to how IACA (Intel Architecture Code Analyzer) works.
+//
+// llvm-mca [options] <file-name>
+// -march <type>
+// -mcpu <cpu>
+// -o <file>
+//
+// The target defaults to the host target.
+// The cpu defaults to the 'native' host cpu.
+// The output defaults to standard output.
+//
+//===----------------------------------------------------------------------===//
+
+#include "CodeRegion.h"
+#include "Context.h"
+#include "DispatchStatistics.h"
+#include "FetchStage.h"
+#include "InstructionInfoView.h"
+#include "InstructionTables.h"
+#include "Pipeline.h"
+#include "PipelinePrinter.h"
+#include "RegisterFileStatistics.h"
+#include "ResourcePressureView.h"
+#include "RetireControlUnitStatistics.h"
+#include "SchedulerStatistics.h"
+#include "SummaryView.h"
+#include "TimelineView.h"
+#include "llvm/MC/MCAsmInfo.h"
+#include "llvm/MC/MCContext.h"
+#include "llvm/MC/MCObjectFileInfo.h"
+#include "llvm/MC/MCParser/MCTargetAsmParser.h"
+#include "llvm/MC/MCRegisterInfo.h"
+#include "llvm/MC/MCStreamer.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/ErrorOr.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Host.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/TargetRegistry.h"
+#include "llvm/Support/TargetSelect.h"
+#include "llvm/Support/ToolOutputFile.h"
+#include "llvm/Support/WithColor.h"
+
+using namespace llvm;
+
+static cl::OptionCategory ToolOptions("Tool Options");
+static cl::OptionCategory ViewOptions("View Options");
+
+static cl::opt<std::string> InputFilename(cl::Positional,
+ cl::desc("<input file>"),
+ cl::cat(ToolOptions), cl::init("-"));
+
+static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"),
+ cl::init("-"), cl::cat(ToolOptions),
+ cl::value_desc("filename"));
+
+static cl::opt<std::string>
+ ArchName("march", cl::desc("Target arch to assemble for, "
+ "see -version for available targets"),
+ cl::cat(ToolOptions));
+
+static cl::opt<std::string>
+ TripleName("mtriple", cl::desc("Target triple to assemble for, "
+ "see -version for available targets"),
+ cl::cat(ToolOptions));
+
+static cl::opt<std::string>
+ MCPU("mcpu",
+ cl::desc("Target a specific cpu type (-mcpu=help for details)"),
+ cl::value_desc("cpu-name"), cl::cat(ToolOptions), cl::init("native"));
+
+static cl::opt<int>
+ OutputAsmVariant("output-asm-variant",
+ cl::desc("Syntax variant to use for output printing"),
+ cl::cat(ToolOptions), cl::init(-1));
+
+static cl::opt<unsigned> Iterations("iterations",
+ cl::desc("Number of iterations to run"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ DispatchWidth("dispatch", cl::desc("Override the processor dispatch width"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ RegisterFileSize("register-file-size",
+ cl::desc("Maximum number of temporary registers which can "
+ "be used for register mappings"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<bool>
+ PrintRegisterFileStats("register-file-stats",
+ cl::desc("Print register file statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> PrintDispatchStats("dispatch-stats",
+ cl::desc("Print dispatch statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool>
+ PrintSummaryView("summary-view", cl::Hidden,
+ cl::desc("Print summary view (enabled by default)"),
+ cl::cat(ViewOptions), cl::init(true));
+
+static cl::opt<bool> PrintSchedulerStats("scheduler-stats",
+ cl::desc("Print scheduler statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool>
+ PrintRetireStats("retire-stats",
+ cl::desc("Print retire control unit statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool> PrintResourcePressureView(
+ "resource-pressure",
+ cl::desc("Print the resource pressure view (enabled by default)"),
+ cl::cat(ViewOptions), cl::init(true));
+
+static cl::opt<bool> PrintTimelineView("timeline",
+ cl::desc("Print the timeline view"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<unsigned> TimelineMaxIterations(
+ "timeline-max-iterations",
+ cl::desc("Maximum number of iterations to print in timeline view"),
+ cl::cat(ViewOptions), cl::init(0));
+
+static cl::opt<unsigned> TimelineMaxCycles(
+ "timeline-max-cycles",
+ cl::desc(
+ "Maximum number of cycles in the timeline view. Defaults to 80 cycles"),
+ cl::cat(ViewOptions), cl::init(80));
+
+static cl::opt<bool>
+ AssumeNoAlias("noalias",
+ cl::desc("If set, assume that loads and stores do not alias"),
+ cl::cat(ToolOptions), cl::init(true));
+
+static cl::opt<unsigned>
+ LoadQueueSize("lqueue",
+ cl::desc("Size of the load queue (unbound by default)"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<unsigned>
+ StoreQueueSize("squeue",
+ cl::desc("Size of the store queue (unbound by default)"),
+ cl::cat(ToolOptions), cl::init(0));
+
+static cl::opt<bool>
+ PrintInstructionTables("instruction-tables",
+ cl::desc("Print instruction tables"),
+ cl::cat(ToolOptions), cl::init(false));
+
+static cl::opt<bool> PrintInstructionInfoView(
+ "instruction-info",
+ cl::desc("Print the instruction info view (enabled by default)"),
+ cl::cat(ViewOptions), cl::init(true));
+
+static cl::opt<bool> EnableAllStats("all-stats",
+ cl::desc("Print all hardware statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+static cl::opt<bool>
+ EnableAllViews("all-views",
+ cl::desc("Print all views including hardware statistics"),
+ cl::cat(ViewOptions), cl::init(false));
+
+namespace {
+
+const Target *getTarget(const char *ProgName) {
+ TripleName = Triple::normalize(TripleName);
+ if (TripleName.empty())
+ TripleName = Triple::normalize(sys::getDefaultTargetTriple());
+ Triple TheTriple(TripleName);
+
+ // Get the target specific parser.
+ std::string Error;
+ const Target *TheTarget =
+ TargetRegistry::lookupTarget(ArchName, TheTriple, Error);
+ if (!TheTarget) {
+ errs() << ProgName << ": " << Error;
+ return nullptr;
+ }
+
+ // Return the found target.
+ return TheTarget;
+}
+
+// A comment consumer that parses strings.
+// The only valid tokens are strings.
+class MCACommentConsumer : public AsmCommentConsumer {
+public:
+ mca::CodeRegions &Regions;
+
+ MCACommentConsumer(mca::CodeRegions &R) : Regions(R) {}
+ void HandleComment(SMLoc Loc, StringRef CommentText) override {
+ // Skip empty comments.
+ StringRef Comment(CommentText);
+ if (Comment.empty())
+ return;
+
+ // Skip spaces and tabs
+ unsigned Position = Comment.find_first_not_of(" \t");
+ if (Position >= Comment.size())
+ // we reached the end of the comment. Bail out.
+ return;
+
+ Comment = Comment.drop_front(Position);
+ if (Comment.consume_front("LLVM-MCA-END")) {
+ Regions.endRegion(Loc);
+ return;
+ }
+
+ // Now try to parse string LLVM-MCA-BEGIN
+ if (!Comment.consume_front("LLVM-MCA-BEGIN"))
+ return;
+
+ // Skip spaces and tabs
+ Position = Comment.find_first_not_of(" \t");
+ if (Position < Comment.size())
+ Comment = Comment.drop_front(Position);
+ // Use the rest of the string as a descriptor for this code snippet.
+ Regions.beginRegion(Comment, Loc);
+ }
+};
+
+int AssembleInput(const char *ProgName, MCAsmParser &Parser,
+ const Target *TheTarget, MCSubtargetInfo &STI,
+ MCInstrInfo &MCII, MCTargetOptions &MCOptions) {
+ std::unique_ptr<MCTargetAsmParser> TAP(
+ TheTarget->createMCAsmParser(STI, Parser, MCII, MCOptions));
+
+ if (!TAP) {
+ WithColor::error() << "this target does not support assembly parsing.\n";
+ return 1;
+ }
+
+ Parser.setTargetParser(*TAP);
+ return Parser.Run(false);
+}
+
+ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
+ if (OutputFilename == "")
+ OutputFilename = "-";
+ std::error_code EC;
+ auto Out =
+ llvm::make_unique<ToolOutputFile>(OutputFilename, EC, sys::fs::F_None);
+ if (!EC)
+ return std::move(Out);
+ return EC;
+}
+
+class MCStreamerWrapper final : public MCStreamer {
+ mca::CodeRegions &Regions;
+
+public:
+ MCStreamerWrapper(MCContext &Context, mca::CodeRegions &R)
+ : MCStreamer(Context), Regions(R) {}
+
+ // We only want to intercept the emission of new instructions.
+ virtual void EmitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI,
+ bool /* unused */) override {
+ Regions.addInstruction(llvm::make_unique<const MCInst>(Inst));
+ }
+
+ bool EmitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override {
+ return true;
+ }
+
+ void EmitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
+ unsigned ByteAlignment) override {}
+ void EmitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr,
+ uint64_t Size = 0, unsigned ByteAlignment = 0,
+ SMLoc Loc = SMLoc()) override {}
+ void EmitGPRel32Value(const MCExpr *Value) override {}
+ void BeginCOFFSymbolDef(const MCSymbol *Symbol) override {}
+ void EmitCOFFSymbolStorageClass(int StorageClass) override {}
+ void EmitCOFFSymbolType(int Type) override {}
+ void EndCOFFSymbolDef() override {}
+
+ const std::vector<std::unique_ptr<const MCInst>> &
+ GetInstructionSequence(unsigned Index) const {
+ return Regions.getInstructionSequence(Index);
+ }
+};
+} // end of anonymous namespace
+
+static void processOptionImpl(cl::opt<bool> &O, const cl::opt<bool> &Default) {
+ if (!O.getNumOccurrences() || O.getPosition() < Default.getPosition())
+ O = Default.getValue();
+}
+
+static void processViewOptions() {
+ if (!EnableAllViews.getNumOccurrences() &&
+ !EnableAllStats.getNumOccurrences())
+ return;
+
+ if (EnableAllViews.getNumOccurrences()) {
+ processOptionImpl(PrintSummaryView, EnableAllViews);
+ processOptionImpl(PrintResourcePressureView, EnableAllViews);
+ processOptionImpl(PrintTimelineView, EnableAllViews);
+ processOptionImpl(PrintInstructionInfoView, EnableAllViews);
+ }
+
+ const cl::opt<bool> &Default =
+ EnableAllViews.getPosition() < EnableAllStats.getPosition()
+ ? EnableAllStats
+ : EnableAllViews;
+ processOptionImpl(PrintSummaryView, Default);
+ processOptionImpl(PrintRegisterFileStats, Default);
+ processOptionImpl(PrintDispatchStats, Default);
+ processOptionImpl(PrintSchedulerStats, Default);
+ processOptionImpl(PrintRetireStats, Default);
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+
+ // Initialize targets and assembly parsers.
+ llvm::InitializeAllTargetInfos();
+ llvm::InitializeAllTargetMCs();
+ llvm::InitializeAllAsmParsers();
+
+ // Enable printing of available targets when flag --version is specified.
+ cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
+
+ cl::HideUnrelatedOptions({&ToolOptions, &ViewOptions});
+
+ // Parse flags and initialize target options.
+ cl::ParseCommandLineOptions(argc, argv,
+ "llvm machine code performance analyzer.\n");
+
+ MCTargetOptions MCOptions;
+ MCOptions.PreserveAsmComments = false;
+
+ // Get the target from the triple. If a triple is not specified, then select
+ // the default triple for the host. If the triple doesn't correspond to any
+ // registered target, then exit with an error message.
+ const char *ProgName = argv[0];
+ const Target *TheTarget = getTarget(ProgName);
+ if (!TheTarget)
+ return 1;
+
+ // GetTarget() may replaced TripleName with a default triple.
+ // For safety, reconstruct the Triple object.
+ Triple TheTriple(TripleName);
+
+ ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr =
+ MemoryBuffer::getFileOrSTDIN(InputFilename);
+ if (std::error_code EC = BufferPtr.getError()) {
+ WithColor::error() << InputFilename << ": " << EC.message() << '\n';
+ return 1;
+ }
+
+ // Apply overrides to llvm-mca specific options.
+ processViewOptions();
+
+ SourceMgr SrcMgr;
+
+ // Tell SrcMgr about this buffer, which is what the parser will pick up.
+ SrcMgr.AddNewSourceBuffer(std::move(*BufferPtr), SMLoc());
+
+ std::unique_ptr<MCRegisterInfo> MRI(TheTarget->createMCRegInfo(TripleName));
+ assert(MRI && "Unable to create target register info!");
+
+ std::unique_ptr<MCAsmInfo> MAI(TheTarget->createMCAsmInfo(*MRI, TripleName));
+ assert(MAI && "Unable to create target asm info!");
+
+ MCObjectFileInfo MOFI;
+ MCContext Ctx(MAI.get(), MRI.get(), &MOFI, &SrcMgr);
+ MOFI.InitMCObjectFileInfo(TheTriple, /* PIC= */ false, Ctx);
+
+ std::unique_ptr<buffer_ostream> BOS;
+
+ mca::CodeRegions Regions(SrcMgr);
+ MCStreamerWrapper Str(Ctx, Regions);
+
+ std::unique_ptr<MCInstrInfo> MCII(TheTarget->createMCInstrInfo());
+
+ std::unique_ptr<MCInstrAnalysis> MCIA(
+ TheTarget->createMCInstrAnalysis(MCII.get()));
+
+ if (!MCPU.compare("native"))
+ MCPU = llvm::sys::getHostCPUName();
+
+ std::unique_ptr<MCSubtargetInfo> STI(
+ TheTarget->createMCSubtargetInfo(TripleName, MCPU, /* FeaturesStr */ ""));
+ if (!STI->isCPUStringValid(MCPU))
+ return 1;
+
+ if (!PrintInstructionTables && !STI->getSchedModel().isOutOfOrder()) {
+ WithColor::error() << "please specify an out-of-order cpu. '" << MCPU
+ << "' is an in-order cpu.\n";
+ return 1;
+ }
+
+ if (!STI->getSchedModel().hasInstrSchedModel()) {
+ WithColor::error()
+ << "unable to find instruction-level scheduling information for"
+ << " target triple '" << TheTriple.normalize() << "' and cpu '" << MCPU
+ << "'.\n";
+
+ if (STI->getSchedModel().InstrItineraries)
+ WithColor::note()
+ << "cpu '" << MCPU << "' provides itineraries. However, "
+ << "instruction itineraries are currently unsupported.\n";
+ return 1;
+ }
+
+ std::unique_ptr<MCAsmParser> P(createMCAsmParser(SrcMgr, Ctx, Str, *MAI));
+ MCAsmLexer &Lexer = P->getLexer();
+ MCACommentConsumer CC(Regions);
+ Lexer.setCommentConsumer(&CC);
+
+ if (AssembleInput(ProgName, *P, TheTarget, *STI, *MCII, MCOptions))
+ return 1;
+
+ if (Regions.empty()) {
+ WithColor::error() << "no assembly instructions found.\n";
+ return 1;
+ }
+
+ // Now initialize the output file.
+ auto OF = getOutputStream();
+ if (std::error_code EC = OF.getError()) {
+ WithColor::error() << EC.message() << '\n';
+ return 1;
+ }
+
+ unsigned AssemblerDialect = P->getAssemblerDialect();
+ if (OutputAsmVariant >= 0)
+ AssemblerDialect = static_cast<unsigned>(OutputAsmVariant);
+ std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter(
+ Triple(TripleName), AssemblerDialect, *MAI, *MCII, *MRI));
+ if (!IP) {
+ WithColor::error()
+ << "unable to create instruction printer for target triple '"
+ << TheTriple.normalize() << "' with assembly variant "
+ << AssemblerDialect << ".\n";
+ return 1;
+ }
+
+ std::unique_ptr<llvm::ToolOutputFile> TOF = std::move(*OF);
+
+ const MCSchedModel &SM = STI->getSchedModel();
+
+ unsigned Width = SM.IssueWidth;
+ if (DispatchWidth)
+ Width = DispatchWidth;
+
+ // Create an instruction builder.
+ mca::InstrBuilder IB(*STI, *MCII, *MRI, *MCIA, *IP);
+
+ // Create a context to control ownership of the pipeline hardware.
+ mca::Context MCA(*MRI, *STI);
+
+ mca::PipelineOptions PO(Width, RegisterFileSize, LoadQueueSize,
+ StoreQueueSize, AssumeNoAlias);
+
+ // Number each region in the sequence.
+ unsigned RegionIdx = 0;
+ for (const std::unique_ptr<mca::CodeRegion> &Region : Regions) {
+ // Skip empty code regions.
+ if (Region->empty())
+ continue;
+
+ // Don't print the header of this region if it is the default region, and
+ // it doesn't have an end location.
+ if (Region->startLoc().isValid() || Region->endLoc().isValid()) {
+ TOF->os() << "\n[" << RegionIdx++ << "] Code Region";
+ StringRef Desc = Region->getDescription();
+ if (!Desc.empty())
+ TOF->os() << " - " << Desc;
+ TOF->os() << "\n\n";
+ }
+
+ mca::SourceMgr S(Region->getInstructions(),
+ PrintInstructionTables ? 1 : Iterations);
+
+ if (PrintInstructionTables) {
+ // Create a pipeline, stages, and a printer.
+ auto P = llvm::make_unique<mca::Pipeline>();
+ P->appendStage(llvm::make_unique<mca::FetchStage>(IB, S));
+ P->appendStage(llvm::make_unique<mca::InstructionTables>(SM, IB));
+ mca::PipelinePrinter Printer(*P);
+
+ // Create the views for this pipeline, execute, and emit a report.
+ if (PrintInstructionInfoView) {
+ Printer.addView(
+ llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, S, *IP));
+ }
+ Printer.addView(
+ llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S));
+ P->run();
+ Printer.printReport(TOF->os());
+ continue;
+ }
+
+ // Create a basic pipeline simulating an out-of-order backend.
+ auto P = MCA.createDefaultPipeline(PO, IB, S);
+ mca::PipelinePrinter Printer(*P);
+
+ if (PrintSummaryView)
+ Printer.addView(llvm::make_unique<mca::SummaryView>(SM, S, Width));
+
+ if (PrintInstructionInfoView)
+ Printer.addView(
+ llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, S, *IP));
+
+ if (PrintDispatchStats)
+ Printer.addView(llvm::make_unique<mca::DispatchStatistics>());
+
+ if (PrintSchedulerStats)
+ Printer.addView(llvm::make_unique<mca::SchedulerStatistics>(*STI));
+
+ if (PrintRetireStats)
+ Printer.addView(llvm::make_unique<mca::RetireControlUnitStatistics>());
+
+ if (PrintRegisterFileStats)
+ Printer.addView(llvm::make_unique<mca::RegisterFileStatistics>(*STI));
+
+ if (PrintResourcePressureView)
+ Printer.addView(
+ llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S));
+
+ if (PrintTimelineView) {
+ Printer.addView(llvm::make_unique<mca::TimelineView>(
+ *STI, *IP, S, TimelineMaxIterations, TimelineMaxCycles));
+ }
+
+ P->run();
+ Printer.printReport(TOF->os());
+
+ // Clear the InstrBuilder internal state in preparation for another round.
+ IB.clear();
+ }
+
+ TOF->keep();
+ return 0;
+}
diff --git a/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp b/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp
index b2d21c23a094..9fd8340505aa 100644
--- a/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp
+++ b/contrib/llvm/tools/llvm-modextract/llvm-modextract.cpp
@@ -70,7 +70,7 @@ int main(int argc, char **argv) {
}
std::unique_ptr<Module> M = ExitOnErr(Ms[ModuleIndex].parseModule(Context));
- WriteBitcodeToFile(M.get(), Out->os());
+ WriteBitcodeToFile(*M, Out->os());
Out->keep();
return 0;
diff --git a/contrib/llvm/tools/llvm-nm/llvm-nm.cpp b/contrib/llvm/tools/llvm-nm/llvm-nm.cpp
index b6ac9c20a946..37c1bf85809e 100644
--- a/contrib/llvm/tools/llvm-nm/llvm-nm.cpp
+++ b/contrib/llvm/tools/llvm-nm/llvm-nm.cpp
@@ -33,9 +33,8 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetSelect.h"
@@ -82,6 +81,11 @@ cl::alias ExternalOnly2("g", cl::desc("Alias for --extern-only"),
cl::aliasopt(ExternalOnly), cl::Grouping,
cl::ZeroOrMore);
+cl::opt<bool> NoWeakSymbols("no-weak",
+ cl::desc("Show only non-weak symbols"));
+cl::alias NoWeakSymbols2("W", cl::desc("Alias for --no-weak"),
+ cl::aliasopt(NoWeakSymbols), cl::Grouping);
+
cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"),
cl::Grouping);
cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"),
@@ -270,8 +274,16 @@ struct NMSymbol {
} // anonymous namespace
static bool compareSymbolAddress(const NMSymbol &A, const NMSymbol &B) {
- bool ADefined = !(A.Sym.getFlags() & SymbolRef::SF_Undefined);
- bool BDefined = !(B.Sym.getFlags() & SymbolRef::SF_Undefined);
+ bool ADefined;
+ if (A.Sym.getRawDataRefImpl().p)
+ ADefined = !(A.Sym.getFlags() & SymbolRef::SF_Undefined);
+ else
+ ADefined = A.TypeChar != 'U';
+ bool BDefined;
+ if (B.Sym.getRawDataRefImpl().p)
+ BDefined = !(B.Sym.getFlags() & SymbolRef::SF_Undefined);
+ else
+ BDefined = B.TypeChar != 'U';
return std::make_tuple(ADefined, A.Address, A.Name, A.Size) <
std::make_tuple(BDefined, B.Address, B.Name, B.Size);
}
@@ -697,7 +709,7 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
if (ReverseSort)
Cmp = [=](const NMSymbol &A, const NMSymbol &B) { return Cmp(B, A); };
- std::sort(SymbolList.begin(), SymbolList.end(), Cmp);
+ llvm::sort(SymbolList.begin(), SymbolList.end(), Cmp);
}
if (!PrintFileName) {
@@ -761,8 +773,10 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName,
bool Undefined = SymFlags & SymbolRef::SF_Undefined;
bool Global = SymFlags & SymbolRef::SF_Global;
+ bool Weak = SymFlags & SymbolRef::SF_Weak;
if ((!Undefined && UndefinedOnly) || (Undefined && DefinedOnly) ||
- (!Global && ExternalOnly) || (SizeSort && !PrintAddress))
+ (!Global && ExternalOnly) || (SizeSort && !PrintAddress) ||
+ (Weak && NoWeakSymbols))
continue;
if (PrintFileName) {
if (!ArchitectureName.empty())
@@ -1004,6 +1018,10 @@ static char getSymbolNMTypeChar(MachOObjectFile &Obj, basic_symbol_iterator I) {
StringRef SectionName;
Obj.getSectionName(Ref, SectionName);
StringRef SegmentName = Obj.getSectionFinalSegmentName(Ref);
+ if (Obj.is64Bit() &&
+ Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE &&
+ SegmentName == "__TEXT_EXEC" && SectionName == "__text")
+ return 't';
if (SegmentName == "__TEXT" && SectionName == "__text")
return 't';
if (SegmentName == "__DATA" && SectionName == "__data")
@@ -1203,6 +1221,8 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
raw_string_ostream LOS(LazysNameBuffer);
std::string WeaksNameBuffer;
raw_string_ostream WOS(WeaksNameBuffer);
+ std::string FunctionStartsNameBuffer;
+ raw_string_ostream FOS(FunctionStartsNameBuffer);
if (MachO && !NoDyldInfo) {
MachO::mach_header H;
MachO::mach_header_64 H_64;
@@ -1573,6 +1593,93 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName,
I++;
}
}
+
+ // Trying adding symbol from the function starts table and LC_MAIN entry
+ // point.
+ SmallVector<uint64_t, 8> FoundFns;
+ uint64_t lc_main_offset = UINT64_MAX;
+ for (const auto &Command : MachO->load_commands()) {
+ if (Command.C.cmd == MachO::LC_FUNCTION_STARTS) {
+ // We found a function starts segment, parse the addresses for
+ // consumption.
+ MachO::linkedit_data_command LLC =
+ MachO->getLinkeditDataLoadCommand(Command);
+
+ MachO->ReadULEB128s(LLC.dataoff, FoundFns);
+ } else if (Command.C.cmd == MachO::LC_MAIN) {
+ MachO::entry_point_command LCmain =
+ MachO->getEntryPointCommand(Command);
+ lc_main_offset = LCmain.entryoff;
+ }
+ }
+ // See if these addresses are already in the symbol table.
+ unsigned FunctionStartsAdded = 0;
+ for (uint64_t f = 0; f < FoundFns.size(); f++) {
+ bool found = false;
+ for (unsigned J = 0; J < SymbolList.size() && !found; ++J) {
+ if (SymbolList[J].Address == FoundFns[f] + BaseSegmentAddress)
+ found = true;
+ }
+ // See this address is not already in the symbol table fake up an
+ // nlist for it.
+ if (!found) {
+ NMSymbol F;
+ memset(&F, '\0', sizeof(NMSymbol));
+ F.Name = "<redacted function X>";
+ F.Address = FoundFns[f] + BaseSegmentAddress;
+ F.Size = 0;
+ // There is no symbol in the nlist symbol table for this so we set
+ // Sym effectivly to null and the rest of code in here must test for
+ // it and not do things like Sym.getFlags() for it.
+ F.Sym = BasicSymbolRef();
+ F.SymFlags = 0;
+ F.NType = MachO::N_SECT;
+ F.NSect = 0;
+ StringRef SegmentName = StringRef();
+ StringRef SectionName = StringRef();
+ for (const SectionRef &Section : MachO->sections()) {
+ Section.getName(SectionName);
+ SegmentName = MachO->getSectionFinalSegmentName(
+ Section.getRawDataRefImpl());
+ F.NSect++;
+ if (F.Address >= Section.getAddress() &&
+ F.Address < Section.getAddress() + Section.getSize()) {
+ F.Section = Section;
+ break;
+ }
+ }
+ if (SegmentName == "__TEXT" && SectionName == "__text")
+ F.TypeChar = 't';
+ else if (SegmentName == "__DATA" && SectionName == "__data")
+ F.TypeChar = 'd';
+ else if (SegmentName == "__DATA" && SectionName == "__bss")
+ F.TypeChar = 'b';
+ else
+ F.TypeChar = 's';
+ F.NDesc = 0;
+ F.IndirectName = StringRef();
+ SymbolList.push_back(F);
+ if (FoundFns[f] == lc_main_offset)
+ FOS << "<redacted LC_MAIN>";
+ else
+ FOS << "<redacted function " << f << ">";
+ FOS << '\0';
+ FunctionStartsAdded++;
+ }
+ }
+ if (FunctionStartsAdded) {
+ FOS.flush();
+ const char *Q = FunctionStartsNameBuffer.c_str();
+ for (unsigned K = 0; K < FunctionStartsAdded; K++) {
+ SymbolList[I].Name = Q;
+ Q += strlen(Q) + 1;
+ if (SymbolList[I].TypeChar == 'I') {
+ SymbolList[I].IndirectName = Q;
+ Q += strlen(Q) + 1;
+ }
+ I++;
+ }
+ }
}
}
@@ -1915,11 +2022,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) {
}
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
-
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
cl::ParseCommandLineOptions(argc, argv, "llvm symbol table dumper\n");
// llvm-nm only reads binary files.
diff --git a/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td b/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td
new file mode 100644
index 000000000000..2af2108d98d3
--- /dev/null
+++ b/contrib/llvm/tools/llvm-objcopy/ObjcopyOpts.td
@@ -0,0 +1,99 @@
+include "llvm/Option/OptParser.td"
+
+multiclass Eq<string name> {
+ def NAME: Separate<["--", "-"], name>;
+ def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+}
+
+def help : Flag<["-", "--"], "help">;
+defm binary_architecture : Eq<"binary-architecture">,
+ HelpText<"Used when transforming an architecture-less format (such as binary) to another format">;
+def B : JoinedOrSeparate<["-"], "B">,
+ Alias<binary_architecture>;
+defm input_target : Eq<"input-target">,
+ HelpText<"Format of the input file">,
+ Values<"binary">;
+defm output_target : Eq<"output-target">,
+ HelpText<"Format of the output file">,
+ Values<"binary">;
+def O : JoinedOrSeparate<["-"], "O">,
+ Alias<output_target>;
+defm split_dwo : Eq<"split-dwo">,
+ MetaVarName<"dwo-file">,
+ HelpText<"Equivalent to extract-dwo on the input file to <dwo-file>, then strip-dwo on the input file">;
+defm add_gnu_debuglink : Eq<"add-gnu-debuglink">,
+ MetaVarName<"debug-file">,
+ HelpText<"Add a .gnu_debuglink for <debug-file>">;
+defm remove_section : Eq<"remove-section">,
+ MetaVarName<"section">,
+ HelpText<"Remove <section>">;
+defm rename_section : Eq<"rename-section">,
+ MetaVarName<"old=new">,
+ HelpText<"Renames a section from old to new">;
+defm redefine_symbol : Eq<"redefine-sym">,
+ MetaVarName<"old=new">,
+ HelpText<"Change the name of a symbol old to new">;
+def R : JoinedOrSeparate<["-"], "R">,
+ Alias<remove_section>;
+defm keep : Eq<"keep">,
+ MetaVarName<"section">,
+ HelpText<"Keep <section>">;
+defm only_keep : Eq<"only-keep">,
+ MetaVarName<"section">,
+ HelpText<"Remove all but <section>">;
+def j : JoinedOrSeparate<["-"], "j">,
+ Alias<only_keep>;
+defm add_section : Eq<"add-section">,
+ MetaVarName<"section=file">,
+ HelpText<"Make a section named <section> with the contents of <file>.">;
+def strip_all : Flag<["-", "--"], "strip-all">,
+ HelpText<"Remove non-allocated sections other than .gnu.warning* sections">;
+def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">,
+ HelpText<"Compaitable with GNU objcopy's --strip-all">;
+def strip_debug : Flag<["-", "--"], "strip-debug">,
+ HelpText<"Remove all debug information">;
+def strip_dwo : Flag<["-", "--"], "strip-dwo">,
+ HelpText<"Remove all DWARF .dwo sections from file">;
+def strip_sections : Flag<["-", "--"], "strip-sections">,
+ HelpText<"Remove all section headers">;
+def strip_non_alloc : Flag<["-", "--"], "strip-non-alloc">,
+ HelpText<"Remove all non-allocated sections">;
+def extract_dwo : Flag<["-", "--"], "extract-dwo">,
+ HelpText<"Remove all sections that are not DWARF .dwo sections from file">;
+def localize_hidden : Flag<["-", "--"], "localize-hidden">,
+ HelpText<"Mark all symbols that have hidden or internal visibility as local">;
+defm localize_symbol : Eq<"localize-symbol">,
+ MetaVarName<"symbol">,
+ HelpText<"Mark <symbol> as local">;
+def L : JoinedOrSeparate<["-"], "L">,
+ Alias<localize_symbol>;
+defm globalize_symbol : Eq<"globalize-symbol">,
+ MetaVarName<"symbol">,
+ HelpText<"Mark <symbol> as global">;
+defm weaken_symbol : Eq<"weaken-symbol">,
+ MetaVarName<"symbol">,
+ HelpText<"Mark <symbol> as weak">;
+def W : JoinedOrSeparate<["-"], "W">,
+ Alias<weaken_symbol>;
+def weaken : Flag<["-", "--"], "weaken">,
+ HelpText<"Mark all global symbols as weak">;
+def discard_all : Flag<["-", "--"], "discard-all">,
+ HelpText<"Remove all local symbols except file and section symbols">;
+def x : Flag<["-"], "x">,
+ Alias<discard_all>;
+defm strip_symbol : Eq<"strip-symbol">,
+ MetaVarName<"symbol">,
+ HelpText<"Remove symbol <symbol>">;
+def N : JoinedOrSeparate<["-"], "N">,
+ Alias<strip_symbol>;
+defm keep_symbol : Eq<"keep-symbol">,
+ MetaVarName<"symbol">,
+ HelpText<"Do not remove symbol <symbol>">;
+def K : JoinedOrSeparate<["-"], "K">,
+ Alias<keep_symbol>;
+def only_keep_debug : Flag<["-", "--"], "only-keep-debug">,
+ HelpText<"Currently ignored. Only for compaitability with GNU objcopy.">;
+def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
+ HelpText<"Remove all symbols not needed by relocations">;
+def keep_file_symbols : Flag<["-", "--"], "keep-file-symbols">,
+ HelpText<"Do not remove file symbols">;
diff --git a/contrib/llvm/tools/llvm-objcopy/Object.cpp b/contrib/llvm/tools/llvm-objcopy/Object.cpp
index 9e82448187ea..7e88f5263a39 100644
--- a/contrib/llvm/tools/llvm-objcopy/Object.cpp
+++ b/contrib/llvm/tools/llvm-objcopy/Object.cpp
@@ -18,6 +18,7 @@
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/Path.h"
#include <algorithm>
#include <cstddef>
#include <cstdint>
@@ -26,64 +27,117 @@
#include <vector>
using namespace llvm;
+using namespace llvm::objcopy;
using namespace object;
using namespace ELF;
-template <class ELFT> void Segment::writeHeader(FileOutputBuffer &Out) const {
- using Elf_Ehdr = typename ELFT::Ehdr;
- using Elf_Phdr = typename ELFT::Phdr;
+Buffer::~Buffer() {}
- uint8_t *Buf = Out.getBufferStart();
- Buf += sizeof(Elf_Ehdr) + Index * sizeof(Elf_Phdr);
- Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(Buf);
- Phdr.p_type = Type;
- Phdr.p_flags = Flags;
- Phdr.p_offset = Offset;
- Phdr.p_vaddr = VAddr;
- Phdr.p_paddr = PAddr;
- Phdr.p_filesz = FileSize;
- Phdr.p_memsz = MemSize;
- Phdr.p_align = Align;
+void FileBuffer::allocate(size_t Size) {
+ Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
+ FileOutputBuffer::create(getName(), Size, FileOutputBuffer::F_executable);
+ handleAllErrors(BufferOrErr.takeError(), [this](const ErrorInfoBase &E) {
+ error("failed to open " + getName() + ": " + E.message());
+ });
+ Buf = std::move(*BufferOrErr);
+}
+
+Error FileBuffer::commit() { return Buf->commit(); }
+
+uint8_t *FileBuffer::getBufferStart() {
+ return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
}
-void Segment::writeSegment(FileOutputBuffer &Out) const {
- uint8_t *Buf = Out.getBufferStart() + Offset;
- // We want to maintain segments' interstitial data and contents exactly.
- // This lets us just copy segments directly.
- std::copy(std::begin(Contents), std::end(Contents), Buf);
+void MemBuffer::allocate(size_t Size) {
+ Buf = WritableMemoryBuffer::getNewMemBuffer(Size, getName());
+}
+
+Error MemBuffer::commit() { return Error::success(); }
+
+uint8_t *MemBuffer::getBufferStart() {
+ return reinterpret_cast<uint8_t *>(Buf->getBufferStart());
+}
+
+std::unique_ptr<WritableMemoryBuffer> MemBuffer::releaseMemoryBuffer() {
+ return std::move(Buf);
+}
+
+template <class ELFT> void ELFWriter<ELFT>::writePhdr(const Segment &Seg) {
+ using Elf_Phdr = typename ELFT::Phdr;
+
+ uint8_t *B = Buf.getBufferStart();
+ B += Obj.ProgramHdrSegment.Offset + Seg.Index * sizeof(Elf_Phdr);
+ Elf_Phdr &Phdr = *reinterpret_cast<Elf_Phdr *>(B);
+ Phdr.p_type = Seg.Type;
+ Phdr.p_flags = Seg.Flags;
+ Phdr.p_offset = Seg.Offset;
+ Phdr.p_vaddr = Seg.VAddr;
+ Phdr.p_paddr = Seg.PAddr;
+ Phdr.p_filesz = Seg.FileSize;
+ Phdr.p_memsz = Seg.MemSize;
+ Phdr.p_align = Seg.Align;
}
void SectionBase::removeSectionReferences(const SectionBase *Sec) {}
+void SectionBase::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {}
void SectionBase::initialize(SectionTableRef SecTable) {}
void SectionBase::finalize() {}
+void SectionBase::markSymbols() {}
+
+template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) {
+ uint8_t *B = Buf.getBufferStart();
+ B += Sec.HeaderOffset;
+ typename ELFT::Shdr &Shdr = *reinterpret_cast<typename ELFT::Shdr *>(B);
+ Shdr.sh_name = Sec.NameIndex;
+ Shdr.sh_type = Sec.Type;
+ Shdr.sh_flags = Sec.Flags;
+ Shdr.sh_addr = Sec.Addr;
+ Shdr.sh_offset = Sec.Offset;
+ Shdr.sh_size = Sec.Size;
+ Shdr.sh_link = Sec.Link;
+ Shdr.sh_info = Sec.Info;
+ Shdr.sh_addralign = Sec.Align;
+ Shdr.sh_entsize = Sec.EntrySize;
+}
-template <class ELFT>
-void SectionBase::writeHeader(FileOutputBuffer &Out) const {
- uint8_t *Buf = Out.getBufferStart();
- Buf += HeaderOffset;
- typename ELFT::Shdr &Shdr = *reinterpret_cast<typename ELFT::Shdr *>(Buf);
- Shdr.sh_name = NameIndex;
- Shdr.sh_type = Type;
- Shdr.sh_flags = Flags;
- Shdr.sh_addr = Addr;
- Shdr.sh_offset = Offset;
- Shdr.sh_size = Size;
- Shdr.sh_link = Link;
- Shdr.sh_info = Info;
- Shdr.sh_addralign = Align;
- Shdr.sh_entsize = EntrySize;
-}
-
-void Section::writeSection(FileOutputBuffer &Out) const {
- if (Type == SHT_NOBITS)
+SectionVisitor::~SectionVisitor() {}
+
+void BinarySectionWriter::visit(const SectionIndexSection &Sec) {
+ error("Cannot write symbol section index table '" + Sec.Name + "' ");
+}
+
+void BinarySectionWriter::visit(const SymbolTableSection &Sec) {
+ error("Cannot write symbol table '" + Sec.Name + "' out to binary");
+}
+
+void BinarySectionWriter::visit(const RelocationSection &Sec) {
+ error("Cannot write relocation section '" + Sec.Name + "' out to binary");
+}
+
+void BinarySectionWriter::visit(const GnuDebugLinkSection &Sec) {
+ error("Cannot write '" + Sec.Name + "' out to binary");
+}
+
+void BinarySectionWriter::visit(const GroupSection &Sec) {
+ error("Cannot write '" + Sec.Name + "' out to binary");
+}
+
+void SectionWriter::visit(const Section &Sec) {
+ if (Sec.Type == SHT_NOBITS)
return;
- uint8_t *Buf = Out.getBufferStart() + Offset;
- std::copy(std::begin(Contents), std::end(Contents), Buf);
+ uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), Buf);
}
-void OwnedDataSection::writeSection(FileOutputBuffer &Out) const {
- uint8_t *Buf = Out.getBufferStart() + Offset;
- std::copy(std::begin(Data), std::end(Data), Buf);
+void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); }
+
+void SectionWriter::visit(const OwnedDataSection &Sec) {
+ uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ std::copy(std::begin(Sec.Data), std::end(Sec.Data), Buf);
+}
+
+void OwnedDataSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
}
void StringTableSection::addString(StringRef Name) {
@@ -97,8 +151,35 @@ uint32_t StringTableSection::findIndex(StringRef Name) const {
void StringTableSection::finalize() { StrTabBuilder.finalize(); }
-void StringTableSection::writeSection(FileOutputBuffer &Out) const {
- StrTabBuilder.write(Out.getBufferStart() + Offset);
+void SectionWriter::visit(const StringTableSection &Sec) {
+ Sec.StrTabBuilder.write(Out.getBufferStart() + Sec.Offset);
+}
+
+void StringTableSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
+
+template <class ELFT>
+void ELFSectionWriter<ELFT>::visit(const SectionIndexSection &Sec) {
+ uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ auto *IndexesBuffer = reinterpret_cast<typename ELFT::Word *>(Buf);
+ std::copy(std::begin(Sec.Indexes), std::end(Sec.Indexes), IndexesBuffer);
+}
+
+void SectionIndexSection::initialize(SectionTableRef SecTable) {
+ Size = 0;
+ setSymTab(SecTable.getSectionOfType<SymbolTableSection>(
+ Link,
+ "Link field value " + Twine(Link) + " in section " + Name + " is invalid",
+ "Link field value " + Twine(Link) + " in section " + Name +
+ " is not a symbol table"));
+ Symbols->setShndxTable(this);
+}
+
+void SectionIndexSection::finalize() { Link = Symbols->Index; }
+
+void SectionIndexSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
}
static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) {
@@ -119,8 +200,13 @@ static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) {
return false;
}
+// Large indexes force us to clarify exactly what this function should do. This
+// function should return the value that will appear in st_shndx when written
+// out.
uint16_t Symbol::getShndx() const {
if (DefinedIn != nullptr) {
+ if (DefinedIn->Index >= SHN_LORESERVE)
+ return SHN_XINDEX;
return DefinedIn->Index;
}
switch (ShndxType) {
@@ -134,11 +220,18 @@ uint16_t Symbol::getShndx() const {
case SYMBOL_HEXAGON_SCOMMON_2:
case SYMBOL_HEXAGON_SCOMMON_4:
case SYMBOL_HEXAGON_SCOMMON_8:
+ case SYMBOL_XINDEX:
return static_cast<uint16_t>(ShndxType);
}
llvm_unreachable("Symbol with invalid ShndxType encountered");
}
+void SymbolTableSection::assignIndices() {
+ uint32_t Index = 0;
+ for (auto &Sym : Symbols)
+ Sym->Index = Index++;
+}
+
void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
SectionBase *DefinedIn, uint64_t Value,
uint8_t Visibility, uint16_t Shndx,
@@ -148,6 +241,8 @@ void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
Sym.Binding = Bind;
Sym.Type = Type;
Sym.DefinedIn = DefinedIn;
+ if (DefinedIn != nullptr)
+ DefinedIn->HasSymbol = true;
if (DefinedIn == nullptr) {
if (Shndx >= SHN_LORESERVE)
Sym.ShndxType = static_cast<SymbolShndxType>(Shndx);
@@ -163,16 +258,33 @@ void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
}
void SymbolTableSection::removeSectionReferences(const SectionBase *Sec) {
+ if (SectionIndexTable == Sec)
+ SectionIndexTable = nullptr;
if (SymbolNames == Sec) {
error("String table " + SymbolNames->Name +
" cannot be removed because it is referenced by the symbol table " +
this->Name);
}
- auto Iter =
- std::remove_if(std::begin(Symbols), std::end(Symbols),
- [=](const SymPtr &Sym) { return Sym->DefinedIn == Sec; });
- Size -= (std::end(Symbols) - Iter) * this->EntrySize;
- Symbols.erase(Iter, std::end(Symbols));
+ removeSymbols([Sec](const Symbol &Sym) { return Sym.DefinedIn == Sec; });
+}
+
+void SymbolTableSection::updateSymbols(function_ref<void(Symbol &)> Callable) {
+ std::for_each(std::begin(Symbols) + 1, std::end(Symbols),
+ [Callable](SymPtr &Sym) { Callable(*Sym); });
+ std::stable_partition(
+ std::begin(Symbols), std::end(Symbols),
+ [](const SymPtr &Sym) { return Sym->Binding == STB_LOCAL; });
+ assignIndices();
+}
+
+void SymbolTableSection::removeSymbols(
+ function_ref<bool(const Symbol &)> ToRemove) {
+ Symbols.erase(
+ std::remove_if(std::begin(Symbols) + 1, std::end(Symbols),
+ [ToRemove](const SymPtr &Sym) { return ToRemove(*Sym); }),
+ std::end(Symbols));
+ Size = Symbols.size() * EntrySize;
+ assignIndices();
}
void SymbolTableSection::initialize(SectionTableRef SecTable) {
@@ -200,7 +312,17 @@ void SymbolTableSection::finalize() {
Info = MaxLocalIndex + 1;
}
-void SymbolTableSection::addSymbolNames() {
+void SymbolTableSection::prepareForLayout() {
+ // Add all potential section indexes before file layout so that the section
+ // index section has the approprite size.
+ if (SectionIndexTable != nullptr) {
+ for (const auto &Sym : Symbols) {
+ if (Sym->DefinedIn != nullptr && Sym->DefinedIn->Index >= SHN_LORESERVE)
+ SectionIndexTable->addIndex(Sym->DefinedIn->Index);
+ else
+ SectionIndexTable->addIndex(SHN_UNDEF);
+ }
+ }
// Add all of our strings to SymbolNames so that SymbolNames has the right
// size before layout is decided.
for (auto &Sym : Symbols)
@@ -213,13 +335,18 @@ const Symbol *SymbolTableSection::getSymbolByIndex(uint32_t Index) const {
return Symbols[Index].get();
}
+Symbol *SymbolTableSection::getSymbolByIndex(uint32_t Index) {
+ return const_cast<Symbol *>(
+ static_cast<const SymbolTableSection *>(this)->getSymbolByIndex(Index));
+}
+
template <class ELFT>
-void SymbolTableSectionImpl<ELFT>::writeSection(FileOutputBuffer &Out) const {
+void ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) {
uint8_t *Buf = Out.getBufferStart();
- Buf += Offset;
+ Buf += Sec.Offset;
typename ELFT::Sym *Sym = reinterpret_cast<typename ELFT::Sym *>(Buf);
// Loop though symbols setting each entry of the symbol table.
- for (auto &Symbol : Symbols) {
+ for (auto &Symbol : Sec.Symbols) {
Sym->st_name = Symbol->NameIndex;
Sym->st_value = Symbol->Value;
Sym->st_size = Symbol->Size;
@@ -231,13 +358,18 @@ void SymbolTableSectionImpl<ELFT>::writeSection(FileOutputBuffer &Out) const {
}
}
+void SymbolTableSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
+
template <class SymTabType>
void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences(
const SectionBase *Sec) {
if (Symbols == Sec) {
- error("Symbol table " + Symbols->Name + " cannot be removed because it is "
- "referenced by the relocation "
- "section " +
+ error("Symbol table " + Symbols->Name +
+ " cannot be removed because it is "
+ "referenced by the relocation "
+ "section " +
this->Name);
}
}
@@ -252,9 +384,9 @@ void RelocSectionWithSymtabBase<SymTabType>::initialize(
" is not a symbol table"));
if (Info != SHN_UNDEF)
- setSection(SecTable.getSection(Info,
- "Info field value " + Twine(Info) +
- " in section " + Name + " is invalid"));
+ setSection(SecTable.getSection(Info, "Info field value " + Twine(Info) +
+ " in section " + Name +
+ " is invalid"));
else
setSection(nullptr);
}
@@ -267,16 +399,15 @@ void RelocSectionWithSymtabBase<SymTabType>::finalize() {
}
template <class ELFT>
-void setAddend(Elf_Rel_Impl<ELFT, false> &Rel, uint64_t Addend) {}
+static void setAddend(Elf_Rel_Impl<ELFT, false> &Rel, uint64_t Addend) {}
template <class ELFT>
-void setAddend(Elf_Rel_Impl<ELFT, true> &Rela, uint64_t Addend) {
+static void setAddend(Elf_Rel_Impl<ELFT, true> &Rela, uint64_t Addend) {
Rela.r_addend = Addend;
}
-template <class ELFT>
-template <class T>
-void RelocationSection<ELFT>::writeRel(T *Buf) const {
+template <class RelRange, class T>
+static void writeRel(const RelRange &Relocations, T *Buf) {
for (const auto &Reloc : Relocations) {
Buf->r_offset = Reloc.Offset;
setAddend(*Buf, Reloc.Addend);
@@ -286,43 +417,138 @@ void RelocationSection<ELFT>::writeRel(T *Buf) const {
}
template <class ELFT>
-void RelocationSection<ELFT>::writeSection(FileOutputBuffer &Out) const {
- uint8_t *Buf = Out.getBufferStart() + Offset;
- if (Type == SHT_REL)
- writeRel(reinterpret_cast<Elf_Rel *>(Buf));
+void ELFSectionWriter<ELFT>::visit(const RelocationSection &Sec) {
+ uint8_t *Buf = Out.getBufferStart() + Sec.Offset;
+ if (Sec.Type == SHT_REL)
+ writeRel(Sec.Relocations, reinterpret_cast<Elf_Rel *>(Buf));
else
- writeRel(reinterpret_cast<Elf_Rela *>(Buf));
+ writeRel(Sec.Relocations, reinterpret_cast<Elf_Rela *>(Buf));
}
-void DynamicRelocationSection::writeSection(FileOutputBuffer &Out) const {
- std::copy(std::begin(Contents), std::end(Contents),
- Out.getBufferStart() + Offset);
+void RelocationSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
}
-void SectionWithStrTab::removeSectionReferences(const SectionBase *Sec) {
- if (StrTab == Sec) {
- error("String table " + StrTab->Name + " cannot be removed because it is "
- "referenced by the section " +
+void RelocationSection::removeSymbols(
+ function_ref<bool(const Symbol &)> ToRemove) {
+ for (const Relocation &Reloc : Relocations)
+ if (ToRemove(*Reloc.RelocSymbol))
+ error("not stripping symbol `" + Reloc.RelocSymbol->Name +
+ "' because it is named in a relocation");
+}
+
+void RelocationSection::markSymbols() {
+ for (const Relocation &Reloc : Relocations)
+ Reloc.RelocSymbol->Referenced = true;
+}
+
+void SectionWriter::visit(const DynamicRelocationSection &Sec) {
+ std::copy(std::begin(Sec.Contents), std::end(Sec.Contents),
+ Out.getBufferStart() + Sec.Offset);
+}
+
+void DynamicRelocationSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
+
+void Section::removeSectionReferences(const SectionBase *Sec) {
+ if (LinkSection == Sec) {
+ error("Section " + LinkSection->Name +
+ " cannot be removed because it is "
+ "referenced by the section " +
this->Name);
}
}
-bool SectionWithStrTab::classof(const SectionBase *S) {
- return isa<DynamicSymbolTableSection>(S) || isa<DynamicSection>(S);
+void GroupSection::finalize() {
+ this->Info = Sym->Index;
+ this->Link = SymTab->Index;
}
-void SectionWithStrTab::initialize(SectionTableRef SecTable) {
- auto StrTab = SecTable.getSection(Link,
- "Link field value " + Twine(Link) +
- " in section " + Name + " is invalid");
- if (StrTab->Type != SHT_STRTAB) {
- error("Link field value " + Twine(Link) + " in section " + Name +
- " is not a string table");
+void GroupSection::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
+ if (ToRemove(*Sym)) {
+ error("Symbol " + Sym->Name +
+ " cannot be removed because it is "
+ "referenced by the section " +
+ this->Name + "[" + Twine(this->Index) + "]");
}
- setStrTab(StrTab);
}
-void SectionWithStrTab::finalize() { this->Link = StrTab->Index; }
+void GroupSection::markSymbols() {
+ if (Sym)
+ Sym->Referenced = true;
+}
+
+void Section::initialize(SectionTableRef SecTable) {
+ if (Link != ELF::SHN_UNDEF) {
+ LinkSection =
+ SecTable.getSection(Link, "Link field value " + Twine(Link) +
+ " in section " + Name + " is invalid");
+ if (LinkSection->Type == ELF::SHT_SYMTAB)
+ LinkSection = nullptr;
+ }
+}
+
+void Section::finalize() { this->Link = LinkSection ? LinkSection->Index : 0; }
+
+void GnuDebugLinkSection::init(StringRef File, StringRef Data) {
+ FileName = sys::path::filename(File);
+ // The format for the .gnu_debuglink starts with the file name and is
+ // followed by a null terminator and then the CRC32 of the file. The CRC32
+ // should be 4 byte aligned. So we add the FileName size, a 1 for the null
+ // byte, and then finally push the size to alignment and add 4.
+ Size = alignTo(FileName.size() + 1, 4) + 4;
+ // The CRC32 will only be aligned if we align the whole section.
+ Align = 4;
+ Type = ELF::SHT_PROGBITS;
+ Name = ".gnu_debuglink";
+ // For sections not found in segments, OriginalOffset is only used to
+ // establish the order that sections should go in. By using the maximum
+ // possible offset we cause this section to wind up at the end.
+ OriginalOffset = std::numeric_limits<uint64_t>::max();
+ JamCRC crc;
+ crc.update(ArrayRef<char>(Data.data(), Data.size()));
+ // The CRC32 value needs to be complemented because the JamCRC dosn't
+ // finalize the CRC32 value. It also dosn't negate the initial CRC32 value
+ // but it starts by default at 0xFFFFFFFF which is the complement of zero.
+ CRC32 = ~crc.getCRC();
+}
+
+GnuDebugLinkSection::GnuDebugLinkSection(StringRef File) : FileName(File) {
+ // Read in the file to compute the CRC of it.
+ auto DebugOrErr = MemoryBuffer::getFile(File);
+ if (!DebugOrErr)
+ error("'" + File + "': " + DebugOrErr.getError().message());
+ auto Debug = std::move(*DebugOrErr);
+ init(File, Debug->getBuffer());
+}
+
+template <class ELFT>
+void ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) {
+ auto Buf = Out.getBufferStart() + Sec.Offset;
+ char *File = reinterpret_cast<char *>(Buf);
+ Elf_Word *CRC =
+ reinterpret_cast<Elf_Word *>(Buf + Sec.Size - sizeof(Elf_Word));
+ *CRC = Sec.CRC32;
+ std::copy(std::begin(Sec.FileName), std::end(Sec.FileName), File);
+}
+
+void GnuDebugLinkSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
+
+template <class ELFT>
+void ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) {
+ ELF::Elf32_Word *Buf =
+ reinterpret_cast<ELF::Elf32_Word *>(Out.getBufferStart() + Sec.Offset);
+ *Buf++ = Sec.FlagWord;
+ for (const auto *S : Sec.GroupMembers)
+ support::endian::write32<ELFT::TargetEndianness>(Buf++, S->Index);
+}
+
+void GroupSection::accept(SectionVisitor &Visitor) const {
+ Visitor.visit(*this);
+}
// Returns true IFF a section is wholly inside the range of a segment
static bool sectionWithinSegment(const SectionBase &Section,
@@ -345,7 +571,7 @@ static bool segmentOverlapsSegment(const Segment &Child,
Parent.OriginalOffset + Parent.FileSize > Child.OriginalOffset;
}
-static bool compareSegments(const Segment *A, const Segment *B) {
+static bool compareSegmentsByOffset(const Segment *A, const Segment *B) {
// Any segment without a parent segment should come before a segment
// that has a parent segment.
if (A->OriginalOffset < B->OriginalOffset)
@@ -355,14 +581,36 @@ static bool compareSegments(const Segment *A, const Segment *B) {
return A->Index < B->Index;
}
-template <class ELFT>
-void Object<ELFT>::readProgramHeaders(const ELFFile<ELFT> &ElfFile) {
+static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) {
+ if (A->PAddr < B->PAddr)
+ return true;
+ if (A->PAddr > B->PAddr)
+ return false;
+ return A->Index < B->Index;
+}
+
+template <class ELFT> void ELFBuilder<ELFT>::setParentSegment(Segment &Child) {
+ for (auto &Parent : Obj.segments()) {
+ // Every segment will overlap with itself but we don't want a segment to
+ // be it's own parent so we avoid that situation.
+ if (&Child != &Parent && segmentOverlapsSegment(Child, Parent)) {
+ // We want a canonical "most parental" segment but this requires
+ // inspecting the ParentSegment.
+ if (compareSegmentsByOffset(&Parent, &Child))
+ if (Child.ParentSegment == nullptr ||
+ compareSegmentsByOffset(&Parent, Child.ParentSegment)) {
+ Child.ParentSegment = &Parent;
+ }
+ }
+ }
+}
+
+template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() {
uint32_t Index = 0;
for (const auto &Phdr : unwrapOrError(ElfFile.program_headers())) {
ArrayRef<uint8_t> Data{ElfFile.base() + Phdr.p_offset,
(size_t)Phdr.p_filesz};
- Segments.emplace_back(llvm::make_unique<Segment>(Data));
- Segment &Seg = *Segments.back();
+ Segment &Seg = Obj.addSegment(Data);
Seg.Type = Phdr.p_type;
Seg.Flags = Phdr.p_flags;
Seg.OriginalOffset = Phdr.p_offset;
@@ -373,58 +621,124 @@ void Object<ELFT>::readProgramHeaders(const ELFFile<ELFT> &ElfFile) {
Seg.MemSize = Phdr.p_memsz;
Seg.Align = Phdr.p_align;
Seg.Index = Index++;
- for (auto &Section : Sections) {
- if (sectionWithinSegment(*Section, Seg)) {
- Seg.addSection(&*Section);
- if (!Section->ParentSegment ||
- Section->ParentSegment->Offset > Seg.Offset) {
- Section->ParentSegment = &Seg;
+ for (auto &Section : Obj.sections()) {
+ if (sectionWithinSegment(Section, Seg)) {
+ Seg.addSection(&Section);
+ if (!Section.ParentSegment ||
+ Section.ParentSegment->Offset > Seg.Offset) {
+ Section.ParentSegment = &Seg;
}
}
}
}
+
+ auto &ElfHdr = Obj.ElfHdrSegment;
+ // Creating multiple PT_PHDR segments technically is not valid, but PT_LOAD
+ // segments must not overlap, and other types fit even less.
+ ElfHdr.Type = PT_PHDR;
+ ElfHdr.Flags = 0;
+ ElfHdr.OriginalOffset = ElfHdr.Offset = 0;
+ ElfHdr.VAddr = 0;
+ ElfHdr.PAddr = 0;
+ ElfHdr.FileSize = ElfHdr.MemSize = sizeof(Elf_Ehdr);
+ ElfHdr.Align = 0;
+ ElfHdr.Index = Index++;
+
+ const auto &Ehdr = *ElfFile.getHeader();
+ auto &PrHdr = Obj.ProgramHdrSegment;
+ PrHdr.Type = PT_PHDR;
+ PrHdr.Flags = 0;
+ // The spec requires us to have p_vaddr % p_align == p_offset % p_align.
+ // Whereas this works automatically for ElfHdr, here OriginalOffset is
+ // always non-zero and to ensure the equation we assign the same value to
+ // VAddr as well.
+ PrHdr.OriginalOffset = PrHdr.Offset = PrHdr.VAddr = Ehdr.e_phoff;
+ PrHdr.PAddr = 0;
+ PrHdr.FileSize = PrHdr.MemSize = Ehdr.e_phentsize * Ehdr.e_phnum;
+ // The spec requires us to naturally align all the fields.
+ PrHdr.Align = sizeof(Elf_Addr);
+ PrHdr.Index = Index++;
+
// Now we do an O(n^2) loop through the segments in order to match up
// segments.
- for (auto &Child : Segments) {
- for (auto &Parent : Segments) {
- // Every segment will overlap with itself but we don't want a segment to
- // be it's own parent so we avoid that situation.
- if (&Child != &Parent && segmentOverlapsSegment(*Child, *Parent)) {
- // We want a canonical "most parental" segment but this requires
- // inspecting the ParentSegment.
- if (compareSegments(Parent.get(), Child.get()))
- if (Child->ParentSegment == nullptr ||
- compareSegments(Parent.get(), Child->ParentSegment)) {
- Child->ParentSegment = Parent.get();
- }
- }
- }
+ for (auto &Child : Obj.segments())
+ setParentSegment(Child);
+ setParentSegment(ElfHdr);
+ setParentSegment(PrHdr);
+}
+
+template <class ELFT>
+void ELFBuilder<ELFT>::initGroupSection(GroupSection *GroupSec) {
+ auto SecTable = Obj.sections();
+ auto SymTab = SecTable.template getSectionOfType<SymbolTableSection>(
+ GroupSec->Link,
+ "Link field value " + Twine(GroupSec->Link) + " in section " +
+ GroupSec->Name + " is invalid",
+ "Link field value " + Twine(GroupSec->Link) + " in section " +
+ GroupSec->Name + " is not a symbol table");
+ auto Sym = SymTab->getSymbolByIndex(GroupSec->Info);
+ if (!Sym)
+ error("Info field value " + Twine(GroupSec->Info) + " in section " +
+ GroupSec->Name + " is not a valid symbol index");
+ GroupSec->setSymTab(SymTab);
+ GroupSec->setSymbol(Sym);
+ if (GroupSec->Contents.size() % sizeof(ELF::Elf32_Word) ||
+ GroupSec->Contents.empty())
+ error("The content of the section " + GroupSec->Name + " is malformed");
+ const ELF::Elf32_Word *Word =
+ reinterpret_cast<const ELF::Elf32_Word *>(GroupSec->Contents.data());
+ const ELF::Elf32_Word *End =
+ Word + GroupSec->Contents.size() / sizeof(ELF::Elf32_Word);
+ GroupSec->setFlagWord(*Word++);
+ for (; Word != End; ++Word) {
+ uint32_t Index = support::endian::read32<ELFT::TargetEndianness>(Word);
+ GroupSec->addMember(SecTable.getSection(
+ Index, "Group member index " + Twine(Index) + " in section " +
+ GroupSec->Name + " is invalid"));
}
}
template <class ELFT>
-void Object<ELFT>::initSymbolTable(const object::ELFFile<ELFT> &ElfFile,
- SymbolTableSection *SymTab,
- SectionTableRef SecTable) {
+void ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) {
const Elf_Shdr &Shdr = *unwrapOrError(ElfFile.getSection(SymTab->Index));
StringRef StrTabData = unwrapOrError(ElfFile.getStringTableForSymtab(Shdr));
+ ArrayRef<Elf_Word> ShndxData;
- for (const auto &Sym : unwrapOrError(ElfFile.symbols(&Shdr))) {
+ auto Symbols = unwrapOrError(ElfFile.symbols(&Shdr));
+ for (const auto &Sym : Symbols) {
SectionBase *DefSection = nullptr;
StringRef Name = unwrapOrError(Sym.getName(StrTabData));
- if (Sym.st_shndx >= SHN_LORESERVE) {
- if (!isValidReservedSectionIndex(Sym.st_shndx, Machine)) {
+ if (Sym.st_shndx == SHN_XINDEX) {
+ if (SymTab->getShndxTable() == nullptr)
+ error("Symbol '" + Name +
+ "' has index SHN_XINDEX but no SHT_SYMTAB_SHNDX section exists.");
+ if (ShndxData.data() == nullptr) {
+ const Elf_Shdr &ShndxSec =
+ *unwrapOrError(ElfFile.getSection(SymTab->getShndxTable()->Index));
+ ShndxData = unwrapOrError(
+ ElfFile.template getSectionContentsAsArray<Elf_Word>(&ShndxSec));
+ if (ShndxData.size() != Symbols.size())
+ error("Symbol section index table does not have the same number of "
+ "entries as the symbol table.");
+ }
+ Elf_Word Index = ShndxData[&Sym - Symbols.begin()];
+ DefSection = Obj.sections().getSection(
+ Index,
+ "Symbol '" + Name + "' has invalid section index " +
+ Twine(Index));
+ } else if (Sym.st_shndx >= SHN_LORESERVE) {
+ if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) {
error(
"Symbol '" + Name +
"' has unsupported value greater than or equal to SHN_LORESERVE: " +
Twine(Sym.st_shndx));
}
} else if (Sym.st_shndx != SHN_UNDEF) {
- DefSection = SecTable.getSection(
- Sym.st_shndx,
- "Symbol '" + Name + "' is defined in invalid section with index " +
- Twine(Sym.st_shndx));
+ DefSection = Obj.sections().getSection(
+ Sym.st_shndx, "Symbol '" + Name +
+ "' is defined has invalid section index " +
+ Twine(Sym.st_shndx));
}
SymTab->addSymbol(Name, Sym.getBinding(), Sym.getType(), DefSection,
@@ -440,9 +754,9 @@ static void getAddend(uint64_t &ToSet, const Elf_Rel_Impl<ELFT, true> &Rela) {
ToSet = Rela.r_addend;
}
-template <class ELFT, class T>
-void initRelocations(RelocationSection<ELFT> *Relocs,
- SymbolTableSection *SymbolTable, T RelRange) {
+template <class T>
+static void initRelocations(RelocationSection *Relocs,
+ SymbolTableSection *SymbolTable, T RelRange) {
for (const auto &Rel : RelRange) {
Relocation ToAdd;
ToAdd.Offset = Rel.r_offset;
@@ -453,14 +767,14 @@ void initRelocations(RelocationSection<ELFT> *Relocs,
}
}
-SectionBase *SectionTableRef::getSection(uint16_t Index, Twine ErrMsg) {
+SectionBase *SectionTableRef::getSection(uint32_t Index, Twine ErrMsg) {
if (Index == SHN_UNDEF || Index > Sections.size())
error(ErrMsg);
return Sections[Index - 1].get();
}
template <class T>
-T *SectionTableRef::getSectionOfType(uint16_t Index, Twine IndexErrMsg,
+T *SectionTableRef::getSectionOfType(uint32_t Index, Twine IndexErrMsg,
Twine TypeErrMsg) {
if (T *Sec = dyn_cast<T>(getSection(Index, IndexErrMsg)))
return Sec;
@@ -468,147 +782,221 @@ T *SectionTableRef::getSectionOfType(uint16_t Index, Twine IndexErrMsg,
}
template <class ELFT>
-std::unique_ptr<SectionBase>
-Object<ELFT>::makeSection(const object::ELFFile<ELFT> &ElfFile,
- const Elf_Shdr &Shdr) {
+SectionBase &ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) {
ArrayRef<uint8_t> Data;
switch (Shdr.sh_type) {
case SHT_REL:
case SHT_RELA:
if (Shdr.sh_flags & SHF_ALLOC) {
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
- return llvm::make_unique<DynamicRelocationSection>(Data);
+ return Obj.addSection<DynamicRelocationSection>(Data);
}
- return llvm::make_unique<RelocationSection<ELFT>>();
+ return Obj.addSection<RelocationSection>();
case SHT_STRTAB:
// If a string table is allocated we don't want to mess with it. That would
// mean altering the memory image. There are no special link types or
// anything so we can just use a Section.
if (Shdr.sh_flags & SHF_ALLOC) {
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
- return llvm::make_unique<Section>(Data);
+ return Obj.addSection<Section>(Data);
}
- return llvm::make_unique<StringTableSection>();
+ return Obj.addSection<StringTableSection>();
case SHT_HASH:
case SHT_GNU_HASH:
// Hash tables should refer to SHT_DYNSYM which we're not going to change.
// Because of this we don't need to mess with the hash tables either.
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
- return llvm::make_unique<Section>(Data);
+ return Obj.addSection<Section>(Data);
+ case SHT_GROUP:
+ Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
+ return Obj.addSection<GroupSection>(Data);
case SHT_DYNSYM:
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
- return llvm::make_unique<DynamicSymbolTableSection>(Data);
+ return Obj.addSection<DynamicSymbolTableSection>(Data);
case SHT_DYNAMIC:
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
- return llvm::make_unique<DynamicSection>(Data);
+ return Obj.addSection<DynamicSection>(Data);
case SHT_SYMTAB: {
- auto SymTab = llvm::make_unique<SymbolTableSectionImpl<ELFT>>();
- SymbolTable = SymTab.get();
- return std::move(SymTab);
+ auto &SymTab = Obj.addSection<SymbolTableSection>();
+ Obj.SymbolTable = &SymTab;
+ return SymTab;
+ }
+ case SHT_SYMTAB_SHNDX: {
+ auto &ShndxSection = Obj.addSection<SectionIndexSection>();
+ Obj.SectionIndexTable = &ShndxSection;
+ return ShndxSection;
}
case SHT_NOBITS:
- return llvm::make_unique<Section>(Data);
+ return Obj.addSection<Section>(Data);
default:
Data = unwrapOrError(ElfFile.getSectionContents(&Shdr));
- return llvm::make_unique<Section>(Data);
+ return Obj.addSection<Section>(Data);
}
}
-template <class ELFT>
-SectionTableRef Object<ELFT>::readSectionHeaders(const ELFFile<ELFT> &ElfFile) {
+template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() {
uint32_t Index = 0;
for (const auto &Shdr : unwrapOrError(ElfFile.sections())) {
if (Index == 0) {
++Index;
continue;
}
- SecPtr Sec = makeSection(ElfFile, Shdr);
- Sec->Name = unwrapOrError(ElfFile.getSectionName(&Shdr));
- Sec->Type = Shdr.sh_type;
- Sec->Flags = Shdr.sh_flags;
- Sec->Addr = Shdr.sh_addr;
- Sec->Offset = Shdr.sh_offset;
- Sec->OriginalOffset = Shdr.sh_offset;
- Sec->Size = Shdr.sh_size;
- Sec->Link = Shdr.sh_link;
- Sec->Info = Shdr.sh_info;
- Sec->Align = Shdr.sh_addralign;
- Sec->EntrySize = Shdr.sh_entsize;
- Sec->Index = Index++;
- Sections.push_back(std::move(Sec));
- }
-
- SectionTableRef SecTable(Sections);
+ auto &Sec = makeSection(Shdr);
+ Sec.Name = unwrapOrError(ElfFile.getSectionName(&Shdr));
+ Sec.Type = Shdr.sh_type;
+ Sec.Flags = Shdr.sh_flags;
+ Sec.Addr = Shdr.sh_addr;
+ Sec.Offset = Shdr.sh_offset;
+ Sec.OriginalOffset = Shdr.sh_offset;
+ Sec.Size = Shdr.sh_size;
+ Sec.Link = Shdr.sh_link;
+ Sec.Info = Shdr.sh_info;
+ Sec.Align = Shdr.sh_addralign;
+ Sec.EntrySize = Shdr.sh_entsize;
+ Sec.Index = Index++;
+ }
+
+ // If a section index table exists we'll need to initialize it before we
+ // initialize the symbol table because the symbol table might need to
+ // reference it.
+ if (Obj.SectionIndexTable)
+ Obj.SectionIndexTable->initialize(Obj.sections());
// Now that all of the sections have been added we can fill out some extra
// details about symbol tables. We need the symbol table filled out before
// any relocations.
- if (SymbolTable) {
- SymbolTable->initialize(SecTable);
- initSymbolTable(ElfFile, SymbolTable, SecTable);
+ if (Obj.SymbolTable) {
+ Obj.SymbolTable->initialize(Obj.sections());
+ initSymbolTable(Obj.SymbolTable);
}
// Now that all sections and symbols have been added we can add
// relocations that reference symbols and set the link and info fields for
// relocation sections.
- for (auto &Section : Sections) {
- if (Section.get() == SymbolTable)
+ for (auto &Section : Obj.sections()) {
+ if (&Section == Obj.SymbolTable)
continue;
- Section->initialize(SecTable);
- if (auto RelSec = dyn_cast<RelocationSection<ELFT>>(Section.get())) {
+ Section.initialize(Obj.sections());
+ if (auto RelSec = dyn_cast<RelocationSection>(&Section)) {
auto Shdr = unwrapOrError(ElfFile.sections()).begin() + RelSec->Index;
if (RelSec->Type == SHT_REL)
- initRelocations(RelSec, SymbolTable, unwrapOrError(ElfFile.rels(Shdr)));
+ initRelocations(RelSec, Obj.SymbolTable,
+ unwrapOrError(ElfFile.rels(Shdr)));
else
- initRelocations(RelSec, SymbolTable,
+ initRelocations(RelSec, Obj.SymbolTable,
unwrapOrError(ElfFile.relas(Shdr)));
+ } else if (auto GroupSec = dyn_cast<GroupSection>(&Section)) {
+ initGroupSection(GroupSec);
}
}
-
- return SecTable;
}
-template <class ELFT> Object<ELFT>::Object(const ELFObjectFile<ELFT> &Obj) {
- const auto &ElfFile = *Obj.getELFFile();
+template <class ELFT> void ELFBuilder<ELFT>::build() {
const auto &Ehdr = *ElfFile.getHeader();
- std::copy(Ehdr.e_ident, Ehdr.e_ident + 16, Ident);
- Type = Ehdr.e_type;
- Machine = Ehdr.e_machine;
- Version = Ehdr.e_version;
- Entry = Ehdr.e_entry;
- Flags = Ehdr.e_flags;
+ std::copy(Ehdr.e_ident, Ehdr.e_ident + 16, Obj.Ident);
+ Obj.Type = Ehdr.e_type;
+ Obj.Machine = Ehdr.e_machine;
+ Obj.Version = Ehdr.e_version;
+ Obj.Entry = Ehdr.e_entry;
+ Obj.Flags = Ehdr.e_flags;
+
+ readSectionHeaders();
+ readProgramHeaders();
+
+ uint32_t ShstrIndex = Ehdr.e_shstrndx;
+ if (ShstrIndex == SHN_XINDEX)
+ ShstrIndex = unwrapOrError(ElfFile.getSection(0))->sh_link;
+
+ Obj.SectionNames =
+ Obj.sections().template getSectionOfType<StringTableSection>(
+ ShstrIndex,
+ "e_shstrndx field value " + Twine(Ehdr.e_shstrndx) +
+ " in elf header " + " is invalid",
+ "e_shstrndx field value " + Twine(Ehdr.e_shstrndx) +
+ " in elf header " + " is not a string table");
+}
+
+// A generic size function which computes sizes of any random access range.
+template <class R> size_t size(R &&Range) {
+ return static_cast<size_t>(std::end(Range) - std::begin(Range));
+}
+
+Writer::~Writer() {}
- SectionTableRef SecTable = readSectionHeaders(ElfFile);
- readProgramHeaders(ElfFile);
+Reader::~Reader() {}
- SectionNames = SecTable.getSectionOfType<StringTableSection>(
- Ehdr.e_shstrndx,
- "e_shstrndx field value " + Twine(Ehdr.e_shstrndx) + " in elf header " +
- " is invalid",
- "e_shstrndx field value " + Twine(Ehdr.e_shstrndx) + " in elf header " +
- " is not a string table");
+ElfType ELFReader::getElfType() const {
+ if (isa<ELFObjectFile<ELF32LE>>(Bin))
+ return ELFT_ELF32LE;
+ if (isa<ELFObjectFile<ELF64LE>>(Bin))
+ return ELFT_ELF64LE;
+ if (isa<ELFObjectFile<ELF32BE>>(Bin))
+ return ELFT_ELF32BE;
+ if (isa<ELFObjectFile<ELF64BE>>(Bin))
+ return ELFT_ELF64BE;
+ llvm_unreachable("Invalid ELFType");
}
-template <class ELFT>
-void Object<ELFT>::writeHeader(FileOutputBuffer &Out) const {
- uint8_t *Buf = Out.getBufferStart();
- Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(Buf);
- std::copy(Ident, Ident + 16, Ehdr.e_ident);
- Ehdr.e_type = Type;
- Ehdr.e_machine = Machine;
- Ehdr.e_version = Version;
- Ehdr.e_entry = Entry;
- Ehdr.e_phoff = sizeof(Elf_Ehdr);
- Ehdr.e_flags = Flags;
+std::unique_ptr<Object> ELFReader::create() const {
+ auto Obj = llvm::make_unique<Object>();
+ if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) {
+ ELFBuilder<ELF32LE> Builder(*o, *Obj);
+ Builder.build();
+ return Obj;
+ } else if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) {
+ ELFBuilder<ELF64LE> Builder(*o, *Obj);
+ Builder.build();
+ return Obj;
+ } else if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) {
+ ELFBuilder<ELF32BE> Builder(*o, *Obj);
+ Builder.build();
+ return Obj;
+ } else if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) {
+ ELFBuilder<ELF64BE> Builder(*o, *Obj);
+ Builder.build();
+ return Obj;
+ }
+ error("Invalid file type");
+}
+
+template <class ELFT> void ELFWriter<ELFT>::writeEhdr() {
+ uint8_t *B = Buf.getBufferStart();
+ Elf_Ehdr &Ehdr = *reinterpret_cast<Elf_Ehdr *>(B);
+ std::copy(Obj.Ident, Obj.Ident + 16, Ehdr.e_ident);
+ Ehdr.e_type = Obj.Type;
+ Ehdr.e_machine = Obj.Machine;
+ Ehdr.e_version = Obj.Version;
+ Ehdr.e_entry = Obj.Entry;
+ Ehdr.e_phoff = Obj.ProgramHdrSegment.Offset;
+ Ehdr.e_flags = Obj.Flags;
Ehdr.e_ehsize = sizeof(Elf_Ehdr);
Ehdr.e_phentsize = sizeof(Elf_Phdr);
- Ehdr.e_phnum = Segments.size();
+ Ehdr.e_phnum = size(Obj.segments());
Ehdr.e_shentsize = sizeof(Elf_Shdr);
if (WriteSectionHeaders) {
- Ehdr.e_shoff = SHOffset;
- Ehdr.e_shnum = Sections.size() + 1;
- Ehdr.e_shstrndx = SectionNames->Index;
+ Ehdr.e_shoff = Obj.SHOffset;
+ // """
+ // If the number of sections is greater than or equal to
+ // SHN_LORESERVE (0xff00), this member has the value zero and the actual
+ // number of section header table entries is contained in the sh_size field
+ // of the section header at index 0.
+ // """
+ auto Shnum = size(Obj.sections()) + 1;
+ if (Shnum >= SHN_LORESERVE)
+ Ehdr.e_shnum = 0;
+ else
+ Ehdr.e_shnum = Shnum;
+ // """
+ // If the section name string table section index is greater than or equal
+ // to SHN_LORESERVE (0xff00), this member has the value SHN_XINDEX (0xffff)
+ // and the actual index of the section name string table section is
+ // contained in the sh_link field of the section header at index 0.
+ // """
+ if (Obj.SectionNames->Index >= SHN_LORESERVE)
+ Ehdr.e_shstrndx = SHN_XINDEX;
+ else
+ Ehdr.e_shstrndx = Obj.SectionNames->Index;
} else {
Ehdr.e_shoff = 0;
Ehdr.e_shnum = 0;
@@ -616,42 +1004,46 @@ void Object<ELFT>::writeHeader(FileOutputBuffer &Out) const {
}
}
-template <class ELFT>
-void Object<ELFT>::writeProgramHeaders(FileOutputBuffer &Out) const {
- for (auto &Phdr : Segments)
- Phdr->template writeHeader<ELFT>(Out);
+template <class ELFT> void ELFWriter<ELFT>::writePhdrs() {
+ for (auto &Seg : Obj.segments())
+ writePhdr(Seg);
}
-template <class ELFT>
-void Object<ELFT>::writeSectionHeaders(FileOutputBuffer &Out) const {
- uint8_t *Buf = Out.getBufferStart() + SHOffset;
+template <class ELFT> void ELFWriter<ELFT>::writeShdrs() {
+ uint8_t *B = Buf.getBufferStart() + Obj.SHOffset;
// This reference serves to write the dummy section header at the begining
// of the file. It is not used for anything else
- Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(Buf);
+ Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B);
Shdr.sh_name = 0;
Shdr.sh_type = SHT_NULL;
Shdr.sh_flags = 0;
Shdr.sh_addr = 0;
Shdr.sh_offset = 0;
- Shdr.sh_size = 0;
- Shdr.sh_link = 0;
+ // See writeEhdr for why we do this.
+ uint64_t Shnum = size(Obj.sections()) + 1;
+ if (Shnum >= SHN_LORESERVE)
+ Shdr.sh_size = Shnum;
+ else
+ Shdr.sh_size = 0;
+ // See writeEhdr for why we do this.
+ if (Obj.SectionNames != nullptr && Obj.SectionNames->Index >= SHN_LORESERVE)
+ Shdr.sh_link = Obj.SectionNames->Index;
+ else
+ Shdr.sh_link = 0;
Shdr.sh_info = 0;
Shdr.sh_addralign = 0;
Shdr.sh_entsize = 0;
- for (auto &Section : Sections)
- Section->template writeHeader<ELFT>(Out);
+ for (auto &Sec : Obj.sections())
+ writeShdr(Sec);
}
-template <class ELFT>
-void Object<ELFT>::writeSectionData(FileOutputBuffer &Out) const {
- for (auto &Section : Sections)
- Section->writeSection(Out);
+template <class ELFT> void ELFWriter<ELFT>::writeSectionData() {
+ for (auto &Sec : Obj.sections())
+ Sec.accept(*SecWriter);
}
-template <class ELFT>
-void Object<ELFT>::removeSections(
- std::function<bool(const SectionBase &)> ToRemove) {
+void Object::removeSections(std::function<bool(const SectionBase &)> ToRemove) {
auto Iter = std::stable_partition(
std::begin(Sections), std::end(Sections), [=](const SecPtr &Sec) {
@@ -665,12 +1057,10 @@ void Object<ELFT>::removeSections(
});
if (SymbolTable != nullptr && ToRemove(*SymbolTable))
SymbolTable = nullptr;
- if (ToRemove(*SectionNames)) {
- if (WriteSectionHeaders)
- error("Cannot remove " + SectionNames->Name +
- " because it is the section header string table.");
+ if (SectionNames != nullptr && ToRemove(*SectionNames))
SectionNames = nullptr;
- }
+ if (SectionIndexTable != nullptr && ToRemove(*SectionIndexTable))
+ SectionIndexTable = nullptr;
// Now make sure there are no remaining references to the sections that will
// be removed. Sometimes it is impossible to remove a reference so we emit
// an error here instead.
@@ -684,14 +1074,15 @@ void Object<ELFT>::removeSections(
Sections.erase(Iter, std::end(Sections));
}
-template <class ELFT>
-void Object<ELFT>::addSection(StringRef SecName, ArrayRef<uint8_t> Data) {
- auto Sec = llvm::make_unique<OwnedDataSection>(SecName, Data);
- Sec->OriginalOffset = ~0ULL;
- Sections.push_back(std::move(Sec));
+void Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) {
+ if (!SymbolTable)
+ return;
+
+ for (const SecPtr &Sec : Sections)
+ Sec->removeSymbols(ToRemove);
}
-template <class ELFT> void ELFObject<ELFT>::sortSections() {
+void Object::sortSections() {
// Put all sections in offset order. Maintain the ordering as closely as
// possible while meeting that demand however.
auto CompareSections = [](const SecPtr &A, const SecPtr &B) {
@@ -716,7 +1107,8 @@ static uint64_t alignToAddr(uint64_t Offset, uint64_t Addr, uint64_t Align) {
// Orders segments such that if x = y->ParentSegment then y comes before x.
static void OrderSegments(std::vector<Segment *> &Segments) {
- std::stable_sort(std::begin(Segments), std::end(Segments), compareSegments);
+ std::stable_sort(std::begin(Segments), std::end(Segments),
+ compareSegmentsByOffset);
}
// This function finds a consistent layout for a list of segments starting from
@@ -725,7 +1117,7 @@ static void OrderSegments(std::vector<Segment *> &Segments) {
static uint64_t LayoutSegments(std::vector<Segment *> &Segments,
uint64_t Offset) {
assert(std::is_sorted(std::begin(Segments), std::end(Segments),
- compareSegments));
+ compareSegmentsByOffset));
// The only way a segment should move is if a section was between two
// segments and that section was removed. If that section isn't in a segment
// then it's acceptable, but not ideal, to simply move it to after the
@@ -755,8 +1147,8 @@ static uint64_t LayoutSegments(std::vector<Segment *> &Segments,
// does not have a ParentSegment. It returns either the offset given if all
// sections had a ParentSegment or an offset one past the last section if there
// was a section that didn't have a ParentSegment.
-template <class SecPtr>
-static uint64_t LayoutSections(std::vector<SecPtr> &Sections, uint64_t Offset) {
+template <class Range>
+static uint64_t LayoutSections(Range Sections, uint64_t Offset) {
// Now the offset of every segment has been set we can assign the offsets
// of each section. For sections that are covered by a segment we should use
// the segment's original offset and the section's original offset to compute
@@ -765,106 +1157,154 @@ static uint64_t LayoutSections(std::vector<SecPtr> &Sections, uint64_t Offset) {
// covered by segments we can just bump Offset to the next valid location.
uint32_t Index = 1;
for (auto &Section : Sections) {
- Section->Index = Index++;
- if (Section->ParentSegment != nullptr) {
- auto Segment = Section->ParentSegment;
- Section->Offset =
- Segment->Offset + (Section->OriginalOffset - Segment->OriginalOffset);
+ Section.Index = Index++;
+ if (Section.ParentSegment != nullptr) {
+ auto Segment = *Section.ParentSegment;
+ Section.Offset =
+ Segment.Offset + (Section.OriginalOffset - Segment.OriginalOffset);
} else {
- Offset = alignTo(Offset, Section->Align == 0 ? 1 : Section->Align);
- Section->Offset = Offset;
- if (Section->Type != SHT_NOBITS)
- Offset += Section->Size;
+ Offset = alignTo(Offset, Section.Align == 0 ? 1 : Section.Align);
+ Section.Offset = Offset;
+ if (Section.Type != SHT_NOBITS)
+ Offset += Section.Size;
}
}
return Offset;
}
-template <class ELFT> void ELFObject<ELFT>::assignOffsets() {
+template <class ELFT> void ELFWriter<ELFT>::assignOffsets() {
// We need a temporary list of segments that has a special order to it
// so that we know that anytime ->ParentSegment is set that segment has
// already had its offset properly set.
std::vector<Segment *> OrderedSegments;
- for (auto &Segment : this->Segments)
- OrderedSegments.push_back(Segment.get());
+ for (auto &Segment : Obj.segments())
+ OrderedSegments.push_back(&Segment);
+ OrderedSegments.push_back(&Obj.ElfHdrSegment);
+ OrderedSegments.push_back(&Obj.ProgramHdrSegment);
OrderSegments(OrderedSegments);
- // The size of ELF + program headers will not change so it is ok to assume
- // that the first offset of the first segment is a good place to start
- // outputting sections. This covers both the standard case and the PT_PHDR
- // case.
- uint64_t Offset;
- if (!OrderedSegments.empty()) {
- Offset = OrderedSegments[0]->Offset;
- } else {
- Offset = sizeof(Elf_Ehdr);
- }
+ // Offset is used as the start offset of the first segment to be laid out.
+ // Since the ELF Header (ElfHdrSegment) must be at the start of the file,
+ // we start at offset 0.
+ uint64_t Offset = 0;
Offset = LayoutSegments(OrderedSegments, Offset);
- Offset = LayoutSections(this->Sections, Offset);
+ Offset = LayoutSections(Obj.sections(), Offset);
// If we need to write the section header table out then we need to align the
// Offset so that SHOffset is valid.
- if (this->WriteSectionHeaders)
+ if (WriteSectionHeaders)
Offset = alignTo(Offset, sizeof(typename ELFT::Addr));
- this->SHOffset = Offset;
+ Obj.SHOffset = Offset;
}
-template <class ELFT> size_t ELFObject<ELFT>::totalSize() const {
+template <class ELFT> size_t ELFWriter<ELFT>::totalSize() const {
// We already have the section header offset so we can calculate the total
// size by just adding up the size of each section header.
- auto NullSectionSize = this->WriteSectionHeaders ? sizeof(Elf_Shdr) : 0;
- return this->SHOffset + this->Sections.size() * sizeof(Elf_Shdr) +
+ auto NullSectionSize = WriteSectionHeaders ? sizeof(Elf_Shdr) : 0;
+ return Obj.SHOffset + size(Obj.sections()) * sizeof(Elf_Shdr) +
NullSectionSize;
}
-template <class ELFT> void ELFObject<ELFT>::write(FileOutputBuffer &Out) const {
- this->writeHeader(Out);
- this->writeProgramHeaders(Out);
- this->writeSectionData(Out);
- if (this->WriteSectionHeaders)
- this->writeSectionHeaders(Out);
+template <class ELFT> void ELFWriter<ELFT>::write() {
+ writeEhdr();
+ writePhdrs();
+ writeSectionData();
+ if (WriteSectionHeaders)
+ writeShdrs();
+ if (auto E = Buf.commit())
+ reportError(Buf.getName(), errorToErrorCode(std::move(E)));
}
-template <class ELFT> void ELFObject<ELFT>::finalize() {
- // Make sure we add the names of all the sections.
- if (this->SectionNames != nullptr)
- for (const auto &Section : this->Sections) {
- this->SectionNames->addString(Section->Name);
+template <class ELFT> void ELFWriter<ELFT>::finalize() {
+ // It could happen that SectionNames has been removed and yet the user wants
+ // a section header table output. We need to throw an error if a user tries
+ // to do that.
+ if (Obj.SectionNames == nullptr && WriteSectionHeaders)
+ error("Cannot write section header table because section header string "
+ "table was removed.");
+
+ Obj.sortSections();
+
+ // We need to assign indexes before we perform layout because we need to know
+ // if we need large indexes or not. We can assign indexes first and check as
+ // we go to see if we will actully need large indexes.
+ bool NeedsLargeIndexes = false;
+ if (size(Obj.sections()) >= SHN_LORESERVE) {
+ auto Sections = Obj.sections();
+ NeedsLargeIndexes =
+ std::any_of(Sections.begin() + SHN_LORESERVE, Sections.end(),
+ [](const SectionBase &Sec) { return Sec.HasSymbol; });
+ // TODO: handle case where only one section needs the large index table but
+ // only needs it because the large index table hasn't been removed yet.
+ }
+
+ if (NeedsLargeIndexes) {
+ // This means we definitely need to have a section index table but if we
+ // already have one then we should use it instead of making a new one.
+ if (Obj.SymbolTable != nullptr && Obj.SectionIndexTable == nullptr) {
+ // Addition of a section to the end does not invalidate the indexes of
+ // other sections and assigns the correct index to the new section.
+ auto &Shndx = Obj.addSection<SectionIndexSection>();
+ Obj.SymbolTable->setShndxTable(&Shndx);
+ Shndx.setSymTab(Obj.SymbolTable);
+ }
+ } else {
+ // Since we don't need SectionIndexTable we should remove it and all
+ // references to it.
+ if (Obj.SectionIndexTable != nullptr) {
+ Obj.removeSections([this](const SectionBase &Sec) {
+ return &Sec == Obj.SectionIndexTable;
+ });
+ }
+ }
+
+ // Make sure we add the names of all the sections. Importantly this must be
+ // done after we decide to add or remove SectionIndexes.
+ if (Obj.SectionNames != nullptr)
+ for (const auto &Section : Obj.sections()) {
+ Obj.SectionNames->addString(Section.Name);
}
- // Make sure we add the names of all the symbols.
- if (this->SymbolTable != nullptr)
- this->SymbolTable->addSymbolNames();
- sortSections();
+ // Before we can prepare for layout the indexes need to be finalized.
+ uint64_t Index = 0;
+ for (auto &Sec : Obj.sections())
+ Sec.Index = Index++;
+
+ // The symbol table does not update all other sections on update. For
+ // instance, symbol names are not added as new symbols are added. This means
+ // that some sections, like .strtab, don't yet have their final size.
+ if (Obj.SymbolTable != nullptr)
+ Obj.SymbolTable->prepareForLayout();
+
assignOffsets();
// Finalize SectionNames first so that we can assign name indexes.
- if (this->SectionNames != nullptr)
- this->SectionNames->finalize();
+ if (Obj.SectionNames != nullptr)
+ Obj.SectionNames->finalize();
// Finally now that all offsets and indexes have been set we can finalize any
// remaining issues.
- uint64_t Offset = this->SHOffset + sizeof(Elf_Shdr);
- for (auto &Section : this->Sections) {
- Section->HeaderOffset = Offset;
+ uint64_t Offset = Obj.SHOffset + sizeof(Elf_Shdr);
+ for (auto &Section : Obj.sections()) {
+ Section.HeaderOffset = Offset;
Offset += sizeof(Elf_Shdr);
- if (this->WriteSectionHeaders)
- Section->NameIndex = this->SectionNames->findIndex(Section->Name);
- Section->finalize();
+ if (WriteSectionHeaders)
+ Section.NameIndex = Obj.SectionNames->findIndex(Section.Name);
+ Section.finalize();
}
-}
-template <class ELFT> size_t BinaryObject<ELFT>::totalSize() const {
- return TotalSize;
+ Buf.allocate(totalSize());
+ SecWriter = llvm::make_unique<ELFSectionWriter<ELFT>>(Buf);
}
-template <class ELFT>
-void BinaryObject<ELFT>::write(FileOutputBuffer &Out) const {
- for (auto &Section : this->Sections) {
- if ((Section->Flags & SHF_ALLOC) == 0)
+void BinaryWriter::write() {
+ for (auto &Section : Obj.sections()) {
+ if ((Section.Flags & SHF_ALLOC) == 0)
continue;
- Section->writeSection(Out);
+ Section.accept(*SecWriter);
}
+ if (auto E = Buf.commit())
+ reportError(Buf.getName(), errorToErrorCode(std::move(E)));
}
-template <class ELFT> void BinaryObject<ELFT>::finalize() {
+void BinaryWriter::finalize() {
// TODO: Create a filter range to construct OrderedSegments from so that this
// code can be deduped with assignOffsets above. This should also solve the
// todo below for LayoutSections.
@@ -873,13 +1313,25 @@ template <class ELFT> void BinaryObject<ELFT>::finalize() {
// already had it's offset properly set. We only want to consider the segments
// that will affect layout of allocated sections so we only add those.
std::vector<Segment *> OrderedSegments;
- for (auto &Section : this->Sections) {
- if ((Section->Flags & SHF_ALLOC) != 0 &&
- Section->ParentSegment != nullptr) {
- OrderedSegments.push_back(Section->ParentSegment);
+ for (auto &Section : Obj.sections()) {
+ if ((Section.Flags & SHF_ALLOC) != 0 && Section.ParentSegment != nullptr) {
+ OrderedSegments.push_back(Section.ParentSegment);
}
}
- OrderSegments(OrderedSegments);
+
+ // For binary output, we're going to use physical addresses instead of
+ // virtual addresses, since a binary output is used for cases like ROM
+ // loading and physical addresses are intended for ROM loading.
+ // However, if no segment has a physical address, we'll fallback to using
+ // virtual addresses for all.
+ if (std::all_of(std::begin(OrderedSegments), std::end(OrderedSegments),
+ [](const Segment *Segment) { return Segment->PAddr == 0; }))
+ for (const auto &Segment : OrderedSegments)
+ Segment->PAddr = Segment->VAddr;
+
+ std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments),
+ compareSegmentsByPAddr);
+
// Because we add a ParentSegment for each section we might have duplicate
// segments in OrderedSegments. If there were duplicates then LayoutSegments
// would do very strange things.
@@ -887,6 +1339,8 @@ template <class ELFT> void BinaryObject<ELFT>::finalize() {
std::unique(std::begin(OrderedSegments), std::end(OrderedSegments));
OrderedSegments.erase(End, std::end(OrderedSegments));
+ uint64_t Offset = 0;
+
// Modify the first segment so that there is no gap at the start. This allows
// our layout algorithm to proceed as expected while not out writing out the
// gap at the start.
@@ -895,30 +1349,29 @@ template <class ELFT> void BinaryObject<ELFT>::finalize() {
auto Sec = Seg->firstSection();
auto Diff = Sec->OriginalOffset - Seg->OriginalOffset;
Seg->OriginalOffset += Diff;
- // The size needs to be shrunk as well
+ // The size needs to be shrunk as well.
Seg->FileSize -= Diff;
- Seg->MemSize -= Diff;
- // The VAddr needs to be adjusted so that the alignment is correct as well
- Seg->VAddr += Diff;
- Seg->PAddr = Seg->VAddr;
- // We don't want this to be shifted by alignment so we need to set the
- // alignment to zero.
- Seg->Align = 0;
+ // The PAddr needs to be increased to remove the gap before the first
+ // section.
+ Seg->PAddr += Diff;
+ uint64_t LowestPAddr = Seg->PAddr;
+ for (auto &Segment : OrderedSegments) {
+ Segment->Offset = Segment->PAddr - LowestPAddr;
+ Offset = std::max(Offset, Segment->Offset + Segment->FileSize);
+ }
}
- uint64_t Offset = LayoutSegments(OrderedSegments, 0);
-
// TODO: generalize LayoutSections to take a range. Pass a special range
// constructed from an iterator that skips values for which a predicate does
// not hold. Then pass such a range to LayoutSections instead of constructing
// AllocatedSections here.
std::vector<SectionBase *> AllocatedSections;
- for (auto &Section : this->Sections) {
- if ((Section->Flags & SHF_ALLOC) == 0)
+ for (auto &Section : Obj.sections()) {
+ if ((Section.Flags & SHF_ALLOC) == 0)
continue;
- AllocatedSections.push_back(Section.get());
+ AllocatedSections.push_back(&Section);
}
- LayoutSections(AllocatedSections, Offset);
+ LayoutSections(make_pointee_range(AllocatedSections), Offset);
// Now that every section has been laid out we just need to compute the total
// file size. This might not be the same as the offset returned by
@@ -929,23 +1382,22 @@ template <class ELFT> void BinaryObject<ELFT>::finalize() {
if (Section->Type != SHT_NOBITS)
TotalSize = std::max(TotalSize, Section->Offset + Section->Size);
}
+
+ Buf.allocate(TotalSize);
+ SecWriter = llvm::make_unique<BinarySectionWriter>(Buf);
}
namespace llvm {
-
-template class Object<ELF64LE>;
-template class Object<ELF64BE>;
-template class Object<ELF32LE>;
-template class Object<ELF32BE>;
-
-template class ELFObject<ELF64LE>;
-template class ELFObject<ELF64BE>;
-template class ELFObject<ELF32LE>;
-template class ELFObject<ELF32BE>;
-
-template class BinaryObject<ELF64LE>;
-template class BinaryObject<ELF64BE>;
-template class BinaryObject<ELF32LE>;
-template class BinaryObject<ELF32BE>;
-
+namespace objcopy {
+
+template class ELFBuilder<ELF64LE>;
+template class ELFBuilder<ELF64BE>;
+template class ELFBuilder<ELF32LE>;
+template class ELFBuilder<ELF32BE>;
+
+template class ELFWriter<ELF64LE>;
+template class ELFWriter<ELF64BE>;
+template class ELFWriter<ELF32LE>;
+template class ELFWriter<ELF32BE>;
+} // end namespace objcopy
} // end namespace llvm
diff --git a/contrib/llvm/tools/llvm-objcopy/Object.h b/contrib/llvm/tools/llvm-objcopy/Object.h
index 639f0f29ceba..76748d5fc641 100644
--- a/contrib/llvm/tools/llvm-objcopy/Object.h
+++ b/contrib/llvm/tools/llvm-objcopy/Object.h
@@ -16,6 +16,8 @@
#include "llvm/BinaryFormat/ELF.h"
#include "llvm/MC/StringTableBuilder.h"
#include "llvm/Object/ELFObjectFile.h"
+#include "llvm/Support/FileOutputBuffer.h"
+#include "llvm/Support/JamCRC.h"
#include <cstddef>
#include <cstdint>
#include <functional>
@@ -24,24 +26,209 @@
#include <vector>
namespace llvm {
+namespace objcopy {
-class FileOutputBuffer;
+class Buffer;
class SectionBase;
+class Section;
+class OwnedDataSection;
+class StringTableSection;
+class SymbolTableSection;
+class RelocationSection;
+class DynamicRelocationSection;
+class GnuDebugLinkSection;
+class GroupSection;
+class SectionIndexSection;
class Segment;
+class Object;
+struct Symbol;
class SectionTableRef {
-private:
- ArrayRef<std::unique_ptr<SectionBase>> Sections;
+ MutableArrayRef<std::unique_ptr<SectionBase>> Sections;
public:
- SectionTableRef(ArrayRef<std::unique_ptr<SectionBase>> Secs)
+ using iterator = pointee_iterator<std::unique_ptr<SectionBase> *>;
+
+ explicit SectionTableRef(MutableArrayRef<std::unique_ptr<SectionBase>> Secs)
: Sections(Secs) {}
SectionTableRef(const SectionTableRef &) = default;
- SectionBase *getSection(uint16_t Index, Twine ErrMsg);
+ iterator begin() { return iterator(Sections.data()); }
+ iterator end() { return iterator(Sections.data() + Sections.size()); }
+
+ SectionBase *getSection(uint32_t Index, Twine ErrMsg);
template <class T>
- T *getSectionOfType(uint16_t Index, Twine IndexErrMsg, Twine TypeErrMsg);
+ T *getSectionOfType(uint32_t Index, Twine IndexErrMsg, Twine TypeErrMsg);
+};
+
+enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE };
+
+class SectionVisitor {
+public:
+ virtual ~SectionVisitor();
+
+ virtual void visit(const Section &Sec) = 0;
+ virtual void visit(const OwnedDataSection &Sec) = 0;
+ virtual void visit(const StringTableSection &Sec) = 0;
+ virtual void visit(const SymbolTableSection &Sec) = 0;
+ virtual void visit(const RelocationSection &Sec) = 0;
+ virtual void visit(const DynamicRelocationSection &Sec) = 0;
+ virtual void visit(const GnuDebugLinkSection &Sec) = 0;
+ virtual void visit(const GroupSection &Sec) = 0;
+ virtual void visit(const SectionIndexSection &Sec) = 0;
+};
+
+class SectionWriter : public SectionVisitor {
+protected:
+ Buffer &Out;
+
+public:
+ virtual ~SectionWriter(){};
+
+ void visit(const Section &Sec) override;
+ void visit(const OwnedDataSection &Sec) override;
+ void visit(const StringTableSection &Sec) override;
+ void visit(const DynamicRelocationSection &Sec) override;
+ virtual void visit(const SymbolTableSection &Sec) override = 0;
+ virtual void visit(const RelocationSection &Sec) override = 0;
+ virtual void visit(const GnuDebugLinkSection &Sec) override = 0;
+ virtual void visit(const GroupSection &Sec) override = 0;
+ virtual void visit(const SectionIndexSection &Sec) override = 0;
+
+ explicit SectionWriter(Buffer &Buf) : Out(Buf) {}
+};
+
+template <class ELFT> class ELFSectionWriter : public SectionWriter {
+private:
+ using Elf_Word = typename ELFT::Word;
+ using Elf_Rel = typename ELFT::Rel;
+ using Elf_Rela = typename ELFT::Rela;
+
+public:
+ virtual ~ELFSectionWriter() {}
+ void visit(const SymbolTableSection &Sec) override;
+ void visit(const RelocationSection &Sec) override;
+ void visit(const GnuDebugLinkSection &Sec) override;
+ void visit(const GroupSection &Sec) override;
+ void visit(const SectionIndexSection &Sec) override;
+
+ explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
+};
+
+#define MAKE_SEC_WRITER_FRIEND \
+ friend class SectionWriter; \
+ template <class ELFT> friend class ELFSectionWriter;
+
+class BinarySectionWriter : public SectionWriter {
+public:
+ virtual ~BinarySectionWriter() {}
+
+ void visit(const SymbolTableSection &Sec) override;
+ void visit(const RelocationSection &Sec) override;
+ void visit(const GnuDebugLinkSection &Sec) override;
+ void visit(const GroupSection &Sec) override;
+ void visit(const SectionIndexSection &Sec) override;
+
+ explicit BinarySectionWriter(Buffer &Buf) : SectionWriter(Buf) {}
+};
+
+// The class Buffer abstracts out the common interface of FileOutputBuffer and
+// WritableMemoryBuffer so that the hierarchy of Writers depends on this
+// abstract interface and doesn't depend on a particular implementation.
+// TODO: refactor the buffer classes in LLVM to enable us to use them here
+// directly.
+class Buffer {
+ StringRef Name;
+
+public:
+ virtual ~Buffer();
+ virtual void allocate(size_t Size) = 0;
+ virtual uint8_t *getBufferStart() = 0;
+ virtual Error commit() = 0;
+
+ explicit Buffer(StringRef Name) : Name(Name) {}
+ StringRef getName() const { return Name; }
+};
+
+class FileBuffer : public Buffer {
+ std::unique_ptr<FileOutputBuffer> Buf;
+
+public:
+ void allocate(size_t Size) override;
+ uint8_t *getBufferStart() override;
+ Error commit() override;
+
+ explicit FileBuffer(StringRef FileName) : Buffer(FileName) {}
+};
+
+class MemBuffer : public Buffer {
+ std::unique_ptr<WritableMemoryBuffer> Buf;
+
+public:
+ void allocate(size_t Size) override;
+ uint8_t *getBufferStart() override;
+ Error commit() override;
+
+ explicit MemBuffer(StringRef Name) : Buffer(Name) {}
+
+ std::unique_ptr<WritableMemoryBuffer> releaseMemoryBuffer();
+};
+
+class Writer {
+protected:
+ Object &Obj;
+ Buffer &Buf;
+
+public:
+ virtual ~Writer();
+ virtual void finalize() = 0;
+ virtual void write() = 0;
+
+ Writer(Object &O, Buffer &B) : Obj(O), Buf(B) {}
+};
+
+template <class ELFT> class ELFWriter : public Writer {
+private:
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Phdr = typename ELFT::Phdr;
+ using Elf_Ehdr = typename ELFT::Ehdr;
+
+ void writeEhdr();
+ void writePhdr(const Segment &Seg);
+ void writeShdr(const SectionBase &Sec);
+
+ void writePhdrs();
+ void writeShdrs();
+ void writeSectionData();
+
+ void assignOffsets();
+
+ std::unique_ptr<ELFSectionWriter<ELFT>> SecWriter;
+
+ size_t totalSize() const;
+
+public:
+ virtual ~ELFWriter() {}
+ bool WriteSectionHeaders = true;
+
+ void finalize() override;
+ void write() override;
+ ELFWriter(Object &Obj, Buffer &Buf, bool WSH)
+ : Writer(Obj, Buf), WriteSectionHeaders(WSH) {}
+};
+
+class BinaryWriter : public Writer {
+private:
+ std::unique_ptr<BinarySectionWriter> SecWriter;
+
+ uint64_t TotalSize;
+
+public:
+ ~BinaryWriter() {}
+ void finalize() override;
+ void write() override;
+ BinaryWriter(Object &Obj, Buffer &Buf) : Writer(Obj, Buf) {}
};
class SectionBase {
@@ -49,8 +236,9 @@ public:
StringRef Name;
Segment *ParentSegment = nullptr;
uint64_t HeaderOffset;
- uint64_t OriginalOffset;
+ uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max();
uint32_t Index;
+ bool HasSymbol = false;
uint64_t Addr = 0;
uint64_t Align = 1;
@@ -68,8 +256,9 @@ public:
virtual void initialize(SectionTableRef SecTable);
virtual void finalize();
virtual void removeSectionReferences(const SectionBase *Sec);
- template <class ELFT> void writeHeader(FileOutputBuffer &Out) const;
- virtual void writeSection(FileOutputBuffer &Out) const = 0;
+ virtual void removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
+ virtual void accept(SectionVisitor &Visitor) const = 0;
+ virtual void markSymbols();
};
class Segment {
@@ -102,7 +291,8 @@ public:
uint64_t OriginalOffset;
Segment *ParentSegment = nullptr;
- Segment(ArrayRef<uint8_t> Data) : Contents(Data) {}
+ explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {}
+ Segment() {}
const SectionBase *firstSection() const {
if (!Sections.empty())
@@ -112,22 +302,26 @@ public:
void removeSection(const SectionBase *Sec) { Sections.erase(Sec); }
void addSection(const SectionBase *Sec) { Sections.insert(Sec); }
- template <class ELFT> void writeHeader(FileOutputBuffer &Out) const;
- void writeSegment(FileOutputBuffer &Out) const;
};
class Section : public SectionBase {
-private:
+ MAKE_SEC_WRITER_FRIEND
+
ArrayRef<uint8_t> Contents;
+ SectionBase *LinkSection = nullptr;
public:
- Section(ArrayRef<uint8_t> Data) : Contents(Data) {}
+ explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {}
- void writeSection(FileOutputBuffer &Out) const override;
+ void accept(SectionVisitor &Visitor) const override;
+ void removeSectionReferences(const SectionBase *Sec) override;
+ void initialize(SectionTableRef SecTable) override;
+ void finalize() override;
};
class OwnedDataSection : public SectionBase {
-private:
+ MAKE_SEC_WRITER_FRIEND
+
std::vector<uint8_t> Data;
public:
@@ -136,8 +330,10 @@ public:
Name = SecName;
Type = ELF::SHT_PROGBITS;
Size = Data.size();
+ OriginalOffset = std::numeric_limits<uint64_t>::max();
}
- void writeSection(FileOutputBuffer &Out) const override;
+
+ void accept(SectionVisitor &Sec) const override;
};
// There are two types of string tables that can exist, dynamic and not dynamic.
@@ -149,7 +345,8 @@ public:
// classof method checks that the particular instance is not allocated. This
// then agrees with the makeSection method used to construct most sections.
class StringTableSection : public SectionBase {
-private:
+ MAKE_SEC_WRITER_FRIEND
+
StringTableBuilder StrTabBuilder;
public:
@@ -160,7 +357,7 @@ public:
void addString(StringRef Name);
uint32_t findIndex(StringRef Name) const;
void finalize() override;
- void writeSection(FileOutputBuffer &Out) const override;
+ void accept(SectionVisitor &Visitor) const override;
static bool classof(const SectionBase *S) {
if (S->Flags & ELF::SHF_ALLOC)
@@ -181,6 +378,7 @@ enum SymbolShndxType {
SYMBOL_HEXAGON_SCOMMON_2 = ELF::SHN_HEXAGON_SCOMMON_2,
SYMBOL_HEXAGON_SCOMMON_4 = ELF::SHN_HEXAGON_SCOMMON_4,
SYMBOL_HEXAGON_SCOMMON_8 = ELF::SHN_HEXAGON_SCOMMON_8,
+ SYMBOL_XINDEX = ELF::SHN_XINDEX,
};
struct Symbol {
@@ -194,41 +392,79 @@ struct Symbol {
uint8_t Type;
uint64_t Value;
uint8_t Visibility;
+ bool Referenced = false;
uint16_t getShndx() const;
};
+class SectionIndexSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+private:
+ std::vector<uint32_t> Indexes;
+ SymbolTableSection *Symbols = nullptr;
+
+public:
+ virtual ~SectionIndexSection() {}
+ void addIndex(uint32_t Index) {
+ Indexes.push_back(Index);
+ Size += 4;
+ }
+ void setSymTab(SymbolTableSection *SymTab) { Symbols = SymTab; }
+ void initialize(SectionTableRef SecTable) override;
+ void finalize() override;
+ void accept(SectionVisitor &Visitor) const override;
+
+ SectionIndexSection() {
+ Name = ".symtab_shndx";
+ Align = 4;
+ EntrySize = 4;
+ Type = ELF::SHT_SYMTAB_SHNDX;
+ }
+};
+
class SymbolTableSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+
+ void setStrTab(StringTableSection *StrTab) { SymbolNames = StrTab; }
+ void assignIndices();
+
protected:
std::vector<std::unique_ptr<Symbol>> Symbols;
StringTableSection *SymbolNames = nullptr;
+ SectionIndexSection *SectionIndexTable = nullptr;
using SymPtr = std::unique_ptr<Symbol>;
public:
- void setStrTab(StringTableSection *StrTab) { SymbolNames = StrTab; }
void addSymbol(StringRef Name, uint8_t Bind, uint8_t Type,
SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility,
uint16_t Shndx, uint64_t Sz);
- void addSymbolNames();
+ void prepareForLayout();
+ // An 'empty' symbol table still contains a null symbol.
+ bool empty() const { return Symbols.size() == 1; }
+ void setShndxTable(SectionIndexSection *ShndxTable) {
+ SectionIndexTable = ShndxTable;
+ }
+ const SectionIndexSection *getShndxTable() const { return SectionIndexTable; }
const SectionBase *getStrTab() const { return SymbolNames; }
const Symbol *getSymbolByIndex(uint32_t Index) const;
+ Symbol *getSymbolByIndex(uint32_t Index);
+ void updateSymbols(function_ref<void(Symbol &)> Callable);
+
void removeSectionReferences(const SectionBase *Sec) override;
void initialize(SectionTableRef SecTable) override;
void finalize() override;
+ void accept(SectionVisitor &Visitor) const override;
+ void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
static bool classof(const SectionBase *S) {
return S->Type == ELF::SHT_SYMTAB;
}
};
-// Only writeSection depends on the ELF type so we implement it in a subclass.
-template <class ELFT> class SymbolTableSectionImpl : public SymbolTableSection {
- void writeSection(FileOutputBuffer &Out) const override;
-};
-
struct Relocation {
- const Symbol *RelocSymbol = nullptr;
+ Symbol *RelocSymbol = nullptr;
uint64_t Offset;
uint64_t Addend;
uint32_t Type;
@@ -260,33 +496,29 @@ public:
// that code between the two symbol table types.
template <class SymTabType>
class RelocSectionWithSymtabBase : public RelocationSectionBase {
-private:
SymTabType *Symbols = nullptr;
+ void setSymTab(SymTabType *SymTab) { Symbols = SymTab; }
protected:
RelocSectionWithSymtabBase() = default;
public:
- void setSymTab(SymTabType *StrTab) { Symbols = StrTab; }
void removeSectionReferences(const SectionBase *Sec) override;
void initialize(SectionTableRef SecTable) override;
void finalize() override;
};
-template <class ELFT>
class RelocationSection
: public RelocSectionWithSymtabBase<SymbolTableSection> {
-private:
- using Elf_Rel = typename ELFT::Rel;
- using Elf_Rela = typename ELFT::Rela;
+ MAKE_SEC_WRITER_FRIEND
std::vector<Relocation> Relocations;
- template <class T> void writeRel(T *Buf) const;
-
public:
void addRelocation(Relocation Rel) { Relocations.push_back(Rel); }
- void writeSection(FileOutputBuffer &Out) const override;
+ void accept(SectionVisitor &Visitor) const override;
+ void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
+ void markSymbols() override;
static bool classof(const SectionBase *S) {
if (S->Flags & ELF::SHF_ALLOC)
@@ -295,32 +527,51 @@ public:
}
};
-class SectionWithStrTab : public Section {
-private:
- const SectionBase *StrTab = nullptr;
+// TODO: The way stripping and groups interact is complicated
+// and still needs to be worked on.
+
+class GroupSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
+ const SymbolTableSection *SymTab = nullptr;
+ Symbol *Sym = nullptr;
+ ELF::Elf32_Word FlagWord;
+ SmallVector<SectionBase *, 3> GroupMembers;
public:
- SectionWithStrTab(ArrayRef<uint8_t> Data) : Section(Data) {}
+ // TODO: Contents is present in several classes of the hierarchy.
+ // This needs to be refactored to avoid duplication.
+ ArrayRef<uint8_t> Contents;
- void setStrTab(const SectionBase *StringTable) { StrTab = StringTable; }
- void removeSectionReferences(const SectionBase *Sec) override;
- void initialize(SectionTableRef SecTable) override;
+ explicit GroupSection(ArrayRef<uint8_t> Data) : Contents(Data) {}
+
+ void setSymTab(const SymbolTableSection *SymTabSec) { SymTab = SymTabSec; }
+ void setSymbol(Symbol *S) { Sym = S; }
+ void setFlagWord(ELF::Elf32_Word W) { FlagWord = W; }
+ void addMember(SectionBase *Sec) { GroupMembers.push_back(Sec); }
+
+ void initialize(SectionTableRef SecTable) override{};
+ void accept(SectionVisitor &) const override;
void finalize() override;
- static bool classof(const SectionBase *S);
+ void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override;
+ void markSymbols() override;
+
+ static bool classof(const SectionBase *S) {
+ return S->Type == ELF::SHT_GROUP;
+ }
};
-class DynamicSymbolTableSection : public SectionWithStrTab {
+class DynamicSymbolTableSection : public Section {
public:
- DynamicSymbolTableSection(ArrayRef<uint8_t> Data) : SectionWithStrTab(Data) {}
+ explicit DynamicSymbolTableSection(ArrayRef<uint8_t> Data) : Section(Data) {}
static bool classof(const SectionBase *S) {
return S->Type == ELF::SHT_DYNSYM;
}
};
-class DynamicSection : public SectionWithStrTab {
+class DynamicSection : public Section {
public:
- DynamicSection(ArrayRef<uint8_t> Data) : SectionWithStrTab(Data) {}
+ explicit DynamicSection(ArrayRef<uint8_t> Data) : Section(Data) {}
static bool classof(const SectionBase *S) {
return S->Type == ELF::SHT_DYNAMIC;
@@ -329,13 +580,15 @@ public:
class DynamicRelocationSection
: public RelocSectionWithSymtabBase<DynamicSymbolTableSection> {
+ MAKE_SEC_WRITER_FRIEND
+
private:
ArrayRef<uint8_t> Contents;
public:
- DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {}
+ explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {}
- void writeSection(FileOutputBuffer &Out) const override;
+ void accept(SectionVisitor &) const override;
static bool classof(const SectionBase *S) {
if (!(S->Flags & ELF::SHF_ALLOC))
@@ -344,90 +597,125 @@ public:
}
};
-template <class ELFT> class Object {
-private:
- using SecPtr = std::unique_ptr<SectionBase>;
- using SegPtr = std::unique_ptr<Segment>;
-
- using Elf_Shdr = typename ELFT::Shdr;
- using Elf_Ehdr = typename ELFT::Ehdr;
- using Elf_Phdr = typename ELFT::Phdr;
-
- void initSymbolTable(const object::ELFFile<ELFT> &ElfFile,
- SymbolTableSection *SymTab, SectionTableRef SecTable);
- SecPtr makeSection(const object::ELFFile<ELFT> &ElfFile,
- const Elf_Shdr &Shdr);
- void readProgramHeaders(const object::ELFFile<ELFT> &ElfFile);
- SectionTableRef readSectionHeaders(const object::ELFFile<ELFT> &ElfFile);
+class GnuDebugLinkSection : public SectionBase {
+ MAKE_SEC_WRITER_FRIEND
-protected:
- StringTableSection *SectionNames = nullptr;
- SymbolTableSection *SymbolTable = nullptr;
- std::vector<SecPtr> Sections;
- std::vector<SegPtr> Segments;
+private:
+ StringRef FileName;
+ uint32_t CRC32;
- void writeHeader(FileOutputBuffer &Out) const;
- void writeProgramHeaders(FileOutputBuffer &Out) const;
- void writeSectionData(FileOutputBuffer &Out) const;
- void writeSectionHeaders(FileOutputBuffer &Out) const;
+ void init(StringRef File, StringRef Data);
public:
- uint8_t Ident[16];
- uint64_t Entry;
- uint64_t SHOffset;
- uint32_t Type;
- uint32_t Machine;
- uint32_t Version;
- uint32_t Flags;
- bool WriteSectionHeaders = true;
-
- Object(const object::ELFObjectFile<ELFT> &Obj);
- virtual ~Object() = default;
+ // If we add this section from an external source we can use this ctor.
+ explicit GnuDebugLinkSection(StringRef File);
+ void accept(SectionVisitor &Visitor) const override;
+};
- const SymbolTableSection *getSymTab() const { return SymbolTable; }
- const SectionBase *getSectionHeaderStrTab() const { return SectionNames; }
- void removeSections(std::function<bool(const SectionBase &)> ToRemove);
- void addSection(StringRef SecName, ArrayRef<uint8_t> Data);
- virtual size_t totalSize() const = 0;
- virtual void finalize() = 0;
- virtual void write(FileOutputBuffer &Out) const = 0;
+class Reader {
+public:
+ virtual ~Reader();
+ virtual std::unique_ptr<Object> create() const = 0;
};
-template <class ELFT> class ELFObject : public Object<ELFT> {
-private:
- using SecPtr = std::unique_ptr<SectionBase>;
- using SegPtr = std::unique_ptr<Segment>;
+using object::Binary;
+using object::ELFFile;
+using object::ELFObjectFile;
+using object::OwningBinary;
+template <class ELFT> class ELFBuilder {
+private:
+ using Elf_Addr = typename ELFT::Addr;
using Elf_Shdr = typename ELFT::Shdr;
using Elf_Ehdr = typename ELFT::Ehdr;
- using Elf_Phdr = typename ELFT::Phdr;
+ using Elf_Word = typename ELFT::Word;
- void sortSections();
- void assignOffsets();
+ const ELFFile<ELFT> &ElfFile;
+ Object &Obj;
+
+ void setParentSegment(Segment &Child);
+ void readProgramHeaders();
+ void initGroupSection(GroupSection *GroupSec);
+ void initSymbolTable(SymbolTableSection *SymTab);
+ void readSectionHeaders();
+ SectionBase &makeSection(const Elf_Shdr &Shdr);
public:
- ELFObject(const object::ELFObjectFile<ELFT> &Obj) : Object<ELFT>(Obj) {}
+ ELFBuilder(const ELFObjectFile<ELFT> &ElfObj, Object &Obj)
+ : ElfFile(*ElfObj.getELFFile()), Obj(Obj) {}
- void finalize() override;
- size_t totalSize() const override;
- void write(FileOutputBuffer &Out) const override;
+ void build();
};
-template <class ELFT> class BinaryObject : public Object<ELFT> {
+class ELFReader : public Reader {
+ Binary *Bin;
+
+public:
+ ElfType getElfType() const;
+ std::unique_ptr<Object> create() const override;
+ explicit ELFReader(Binary *B) : Bin(B){};
+};
+
+class Object {
private:
using SecPtr = std::unique_ptr<SectionBase>;
using SegPtr = std::unique_ptr<Segment>;
- uint64_t TotalSize;
+ std::vector<SecPtr> Sections;
+ std::vector<SegPtr> Segments;
public:
- BinaryObject(const object::ELFObjectFile<ELFT> &Obj) : Object<ELFT>(Obj) {}
+ template <class T>
+ using Range = iterator_range<
+ pointee_iterator<typename std::vector<std::unique_ptr<T>>::iterator>>;
- void finalize() override;
- size_t totalSize() const override;
- void write(FileOutputBuffer &Out) const override;
-};
+ template <class T>
+ using ConstRange = iterator_range<pointee_iterator<
+ typename std::vector<std::unique_ptr<T>>::const_iterator>>;
+
+ // It is often the case that the ELF header and the program header table are
+ // not present in any segment. This could be a problem during file layout,
+ // because other segments may get assigned an offset where either of the
+ // two should reside, which will effectively corrupt the resulting binary.
+ // Other than that we use these segments to track program header offsets
+ // when they may not follow the ELF header.
+ Segment ElfHdrSegment;
+ Segment ProgramHdrSegment;
+ uint8_t Ident[16];
+ uint64_t Entry;
+ uint64_t SHOffset;
+ uint32_t Type;
+ uint32_t Machine;
+ uint32_t Version;
+ uint32_t Flags;
+
+ StringTableSection *SectionNames = nullptr;
+ SymbolTableSection *SymbolTable = nullptr;
+ SectionIndexSection *SectionIndexTable = nullptr;
+
+ void sortSections();
+ SectionTableRef sections() { return SectionTableRef(Sections); }
+ ConstRange<SectionBase> sections() const {
+ return make_pointee_range(Sections);
+ }
+ Range<Segment> segments() { return make_pointee_range(Segments); }
+ ConstRange<Segment> segments() const { return make_pointee_range(Segments); }
+
+ void removeSections(std::function<bool(const SectionBase &)> ToRemove);
+ void removeSymbols(function_ref<bool(const Symbol &)> ToRemove);
+ template <class T, class... Ts> T &addSection(Ts &&... Args) {
+ auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...);
+ auto Ptr = Sec.get();
+ Sections.emplace_back(std::move(Sec));
+ return *Ptr;
+ }
+ Segment &addSegment(ArrayRef<uint8_t> Data) {
+ Segments.emplace_back(llvm::make_unique<Segment>(Data));
+ return *Segments.back();
+ }
+};
+} // end namespace objcopy
} // end namespace llvm
#endif // LLVM_TOOLS_OBJCOPY_OBJECT_H
diff --git a/contrib/llvm/tools/llvm-objcopy/StripOpts.td b/contrib/llvm/tools/llvm-objcopy/StripOpts.td
new file mode 100644
index 000000000000..333b0d288efa
--- /dev/null
+++ b/contrib/llvm/tools/llvm-objcopy/StripOpts.td
@@ -0,0 +1,49 @@
+include "llvm/Option/OptParser.td"
+
+multiclass Eq<string name> {
+ def NAME: Separate<["--", "-"], name>;
+ def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>;
+}
+
+def help : Flag<["-", "--"], "help">;
+
+defm output : Eq<"o">,
+ MetaVarName<"output">,
+ HelpText<"Write output to <file>">;
+
+def strip_all : Flag<["-", "--"], "strip-all">,
+ HelpText<"Remove non-allocated sections other than .gnu.warning* sections">;
+
+def strip_debug : Flag<["-", "--"], "strip-debug">,
+ HelpText<"Remove debugging symbols only">;
+
+def d : Flag<["-"], "d">,
+ Alias<strip_debug>;
+
+def g : Flag<["-"], "g">,
+ Alias<strip_debug>;
+
+def S : Flag<["-"], "S">,
+ Alias<strip_debug>;
+
+defm remove_section : Eq<"remove-section">,
+ MetaVarName<"section">,
+ HelpText<"Remove <section>">;
+
+def R : JoinedOrSeparate<["-"], "R">,
+ Alias<remove_section>;
+
+defm keep_symbol : Eq<"keep-symbol">,
+ MetaVarName<"symbol">,
+ HelpText<"Do not remove symbol <symbol>">;
+
+def K : JoinedOrSeparate<["-"], "K">,
+ Alias<keep_symbol>;
+
+def discard_all : Flag<["-", "--"], "discard-all">,
+ HelpText<"Remove all local symbols except file and section symbols">;
+def x : Flag<["-"], "x">,
+ Alias<discard_all>;
+
+def strip_unneeded : Flag<["-", "--"], "strip-unneeded">,
+ HelpText<"Remove all symbols not needed by relocations">;
diff --git a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
index 20ce93bb40e8..4ccc67cc75db 100644
--- a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
+++ b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.cpp
@@ -13,10 +13,15 @@
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Twine.h"
#include "llvm/BinaryFormat/ELF.h"
+#include "llvm/Object/Archive.h"
+#include "llvm/Object/ArchiveWriter.h"
#include "llvm/Object/Binary.h"
#include "llvm/Object/ELFObjectFile.h"
#include "llvm/Object/ELFTypes.h"
#include "llvm/Object/Error.h"
+#include "llvm/Option/Arg.h"
+#include "llvm/Option/ArgList.h"
+#include "llvm/Option/Option.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
@@ -24,9 +29,8 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ErrorOr.h"
#include "llvm/Support/FileOutputBuffer.h"
-#include "llvm/Support/ManagedStatic.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/InitLLVM.h"
+#include "llvm/Support/Path.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
#include <cassert>
@@ -39,13 +43,122 @@
#include <utility>
using namespace llvm;
+using namespace llvm::objcopy;
using namespace object;
using namespace ELF;
-// The name this program was invoked as.
-static StringRef ToolName;
+namespace {
+
+enum ObjcopyID {
+ OBJCOPY_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ OBJCOPY_##ID,
+#include "ObjcopyOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const OBJCOPY_##NAME[] = VALUE;
+#include "ObjcopyOpts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info ObjcopyInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {OBJCOPY_##PREFIX, \
+ NAME, \
+ HELPTEXT, \
+ METAVAR, \
+ OBJCOPY_##ID, \
+ opt::Option::KIND##Class, \
+ PARAM, \
+ FLAGS, \
+ OBJCOPY_##GROUP, \
+ OBJCOPY_##ALIAS, \
+ ALIASARGS, \
+ VALUES},
+#include "ObjcopyOpts.inc"
+#undef OPTION
+};
+
+class ObjcopyOptTable : public opt::OptTable {
+public:
+ ObjcopyOptTable() : OptTable(ObjcopyInfoTable, true) {}
+};
+
+enum StripID {
+ STRIP_INVALID = 0, // This is not an option ID.
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ STRIP_##ID,
+#include "StripOpts.inc"
+#undef OPTION
+};
+
+#define PREFIX(NAME, VALUE) const char *const STRIP_##NAME[] = VALUE;
+#include "StripOpts.inc"
+#undef PREFIX
+
+static const opt::OptTable::Info StripInfoTable[] = {
+#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
+ HELPTEXT, METAVAR, VALUES) \
+ {STRIP_##PREFIX, NAME, HELPTEXT, \
+ METAVAR, STRIP_##ID, opt::Option::KIND##Class, \
+ PARAM, FLAGS, STRIP_##GROUP, \
+ STRIP_##ALIAS, ALIASARGS, VALUES},
+#include "StripOpts.inc"
+#undef OPTION
+};
+
+class StripOptTable : public opt::OptTable {
+public:
+ StripOptTable() : OptTable(StripInfoTable, true) {}
+};
+
+struct CopyConfig {
+ StringRef OutputFilename;
+ StringRef InputFilename;
+ StringRef OutputFormat;
+ StringRef InputFormat;
+ StringRef BinaryArch;
+
+ StringRef SplitDWO;
+ StringRef AddGnuDebugLink;
+ std::vector<StringRef> ToRemove;
+ std::vector<StringRef> Keep;
+ std::vector<StringRef> OnlyKeep;
+ std::vector<StringRef> AddSection;
+ std::vector<StringRef> SymbolsToLocalize;
+ std::vector<StringRef> SymbolsToGlobalize;
+ std::vector<StringRef> SymbolsToWeaken;
+ std::vector<StringRef> SymbolsToRemove;
+ std::vector<StringRef> SymbolsToKeep;
+ StringMap<StringRef> SectionsToRename;
+ StringMap<StringRef> SymbolsToRename;
+ bool StripAll = false;
+ bool StripAllGNU = false;
+ bool StripDebug = false;
+ bool StripSections = false;
+ bool StripNonAlloc = false;
+ bool StripDWO = false;
+ bool StripUnneeded = false;
+ bool ExtractDWO = false;
+ bool LocalizeHidden = false;
+ bool Weaken = false;
+ bool DiscardAll = false;
+ bool OnlyKeepDebug = false;
+ bool KeepFileSymbols = false;
+};
+
+using SectionPred = std::function<bool(const SectionBase &Sec)>;
+
+} // namespace
namespace llvm {
+namespace objcopy {
+
+// The name this program was invoked as.
+StringRef ToolName;
LLVM_ATTRIBUTE_NORETURN void error(Twine Message) {
errs() << ToolName << ": " << Message << ".\n";
@@ -69,95 +182,55 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) {
exit(1);
}
+} // end namespace objcopy
} // end namespace llvm
-static cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input>"));
-static cl::opt<std::string> OutputFilename(cl::Positional, cl::desc("<output>"),
- cl::init("-"));
-static cl::opt<std::string>
- OutputFormat("O", cl::desc("Set output format to one of the following:"
- "\n\tbinary"));
-static cl::list<std::string> ToRemove("remove-section",
- cl::desc("Remove <section>"),
- cl::value_desc("section"));
-static cl::alias ToRemoveA("R", cl::desc("Alias for remove-section"),
- cl::aliasopt(ToRemove));
-static cl::opt<bool> StripAll(
- "strip-all",
- cl::desc(
- "Removes non-allocated sections other than .gnu.warning* sections"));
-static cl::opt<bool>
- StripAllGNU("strip-all-gnu",
- cl::desc("Removes symbol, relocation, and debug information"));
-static cl::list<std::string> Keep("keep", cl::desc("Keep <section>"),
- cl::value_desc("section"));
-static cl::list<std::string> OnlyKeep("only-keep",
- cl::desc("Remove all but <section>"),
- cl::value_desc("section"));
-static cl::alias OnlyKeepA("j", cl::desc("Alias for only-keep"),
- cl::aliasopt(OnlyKeep));
-static cl::opt<bool> StripDebug("strip-debug",
- cl::desc("Removes all debug information"));
-static cl::opt<bool> StripSections("strip-sections",
- cl::desc("Remove all section headers"));
-static cl::opt<bool>
- StripNonAlloc("strip-non-alloc",
- cl::desc("Remove all non-allocated sections"));
-static cl::opt<bool>
- StripDWO("strip-dwo", cl::desc("Remove all DWARF .dwo sections from file"));
-static cl::opt<bool> ExtractDWO(
- "extract-dwo",
- cl::desc("Remove all sections that are not DWARF .dwo sections from file"));
-static cl::opt<std::string>
- SplitDWO("split-dwo",
- cl::desc("Equivalent to extract-dwo on the input file to "
- "<dwo-file>, then strip-dwo on the input file"),
- cl::value_desc("dwo-file"));
-static cl::list<std::string> AddSection(
- "add-section",
- cl::desc("Make a section named <section> with the contents of <file>."),
- cl::value_desc("section=file"));
-
-using SectionPred = std::function<bool(const SectionBase &Sec)>;
-
-bool IsDWOSection(const SectionBase &Sec) { return Sec.Name.endswith(".dwo"); }
+static bool IsDWOSection(const SectionBase &Sec) {
+ return Sec.Name.endswith(".dwo");
+}
-template <class ELFT>
-bool OnlyKeepDWOPred(const Object<ELFT> &Obj, const SectionBase &Sec) {
+static bool OnlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) {
// We can't remove the section header string table.
- if (&Sec == Obj.getSectionHeaderStrTab())
+ if (&Sec == Obj.SectionNames)
return false;
// Short of keeping the string table we want to keep everything that is a DWO
// section and remove everything else.
return !IsDWOSection(Sec);
}
-template <class ELFT>
-void WriteObjectFile(const Object<ELFT> &Obj, StringRef File) {
- std::unique_ptr<FileOutputBuffer> Buffer;
- Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
- FileOutputBuffer::create(File, Obj.totalSize(),
- FileOutputBuffer::F_executable);
- handleAllErrors(BufferOrErr.takeError(), [](const ErrorInfoBase &) {
- error("failed to open " + OutputFilename);
- });
- Buffer = std::move(*BufferOrErr);
-
- Obj.write(*Buffer);
- if (auto E = Buffer->commit())
- reportError(File, errorToErrorCode(std::move(E)));
+static std::unique_ptr<Writer> CreateWriter(const CopyConfig &Config,
+ Object &Obj, Buffer &Buf,
+ ElfType OutputElfType) {
+ if (Config.OutputFormat == "binary") {
+ return llvm::make_unique<BinaryWriter>(Obj, Buf);
+ }
+ // Depending on the initial ELFT and OutputFormat we need a different Writer.
+ switch (OutputElfType) {
+ case ELFT_ELF32LE:
+ return llvm::make_unique<ELFWriter<ELF32LE>>(Obj, Buf,
+ !Config.StripSections);
+ case ELFT_ELF64LE:
+ return llvm::make_unique<ELFWriter<ELF64LE>>(Obj, Buf,
+ !Config.StripSections);
+ case ELFT_ELF32BE:
+ return llvm::make_unique<ELFWriter<ELF32BE>>(Obj, Buf,
+ !Config.StripSections);
+ case ELFT_ELF64BE:
+ return llvm::make_unique<ELFWriter<ELF64BE>>(Obj, Buf,
+ !Config.StripSections);
+ }
+ llvm_unreachable("Invalid output format");
}
-template <class ELFT>
-void SplitDWOToFile(const ELFObjectFile<ELFT> &ObjFile, StringRef File) {
- // Construct a second output file for the DWO sections.
- ELFObject<ELFT> DWOFile(ObjFile);
-
- DWOFile.removeSections([&](const SectionBase &Sec) {
- return OnlyKeepDWOPred<ELFT>(DWOFile, Sec);
- });
- DWOFile.finalize();
- WriteObjectFile(DWOFile, File);
+static void SplitDWOToFile(const CopyConfig &Config, const Reader &Reader,
+ StringRef File, ElfType OutputElfType) {
+ auto DWOFile = Reader.create();
+ DWOFile->removeSections(
+ [&](const SectionBase &Sec) { return OnlyKeepDWOPred(*DWOFile, Sec); });
+ FileBuffer FB(File);
+ auto Writer = CreateWriter(Config, *DWOFile, FB, OutputElfType);
+ Writer->finalize();
+ Writer->write();
}
// This function handles the high level operations of GNU objcopy including
@@ -167,47 +240,104 @@ void SplitDWOToFile(const ELFObjectFile<ELFT> &ObjFile, StringRef File) {
// any previous removals. Lastly whether or not something is removed shouldn't
// depend a) on the order the options occur in or b) on some opaque priority
// system. The only priority is that keeps/copies overrule removes.
-template <class ELFT> void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
- std::unique_ptr<Object<ELFT>> Obj;
+static void HandleArgs(const CopyConfig &Config, Object &Obj,
+ const Reader &Reader, ElfType OutputElfType) {
- if (!OutputFormat.empty() && OutputFormat != "binary")
- error("invalid output format '" + OutputFormat + "'");
- if (!OutputFormat.empty() && OutputFormat == "binary")
- Obj = llvm::make_unique<BinaryObject<ELFT>>(ObjFile);
- else
- Obj = llvm::make_unique<ELFObject<ELFT>>(ObjFile);
+ if (!Config.SplitDWO.empty()) {
+ SplitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType);
+ }
+
+ // TODO: update or remove symbols only if there is an option that affects
+ // them.
+ if (Obj.SymbolTable) {
+ Obj.SymbolTable->updateSymbols([&](Symbol &Sym) {
+ if ((Config.LocalizeHidden &&
+ (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) ||
+ (!Config.SymbolsToLocalize.empty() &&
+ is_contained(Config.SymbolsToLocalize, Sym.Name)))
+ Sym.Binding = STB_LOCAL;
+
+ if (!Config.SymbolsToGlobalize.empty() &&
+ is_contained(Config.SymbolsToGlobalize, Sym.Name))
+ Sym.Binding = STB_GLOBAL;
+
+ if (!Config.SymbolsToWeaken.empty() &&
+ is_contained(Config.SymbolsToWeaken, Sym.Name) &&
+ Sym.Binding == STB_GLOBAL)
+ Sym.Binding = STB_WEAK;
+
+ if (Config.Weaken && Sym.Binding == STB_GLOBAL &&
+ Sym.getShndx() != SHN_UNDEF)
+ Sym.Binding = STB_WEAK;
+
+ const auto I = Config.SymbolsToRename.find(Sym.Name);
+ if (I != Config.SymbolsToRename.end())
+ Sym.Name = I->getValue();
+ });
+
+ // The purpose of this loop is to mark symbols referenced by sections
+ // (like GroupSection or RelocationSection). This way, we know which
+ // symbols are still 'needed' and wich are not.
+ if (Config.StripUnneeded) {
+ for (auto &Section : Obj.sections())
+ Section.markSymbols();
+ }
+
+ Obj.removeSymbols([&](const Symbol &Sym) {
+ if ((!Config.SymbolsToKeep.empty() &&
+ is_contained(Config.SymbolsToKeep, Sym.Name)) ||
+ (Config.KeepFileSymbols && Sym.Type == STT_FILE))
+ return false;
+
+ if (Config.DiscardAll && Sym.Binding == STB_LOCAL &&
+ Sym.getShndx() != SHN_UNDEF && Sym.Type != STT_FILE &&
+ Sym.Type != STT_SECTION)
+ return true;
- if (!SplitDWO.empty())
- SplitDWOToFile<ELFT>(ObjFile, SplitDWO.getValue());
+ if (Config.StripAll || Config.StripAllGNU)
+ return true;
+
+ if (!Config.SymbolsToRemove.empty() &&
+ is_contained(Config.SymbolsToRemove, Sym.Name)) {
+ return true;
+ }
+
+ if (Config.StripUnneeded && !Sym.Referenced &&
+ (Sym.Binding == STB_LOCAL || Sym.getShndx() == SHN_UNDEF) &&
+ Sym.Type != STT_FILE && Sym.Type != STT_SECTION)
+ return true;
+
+ return false;
+ });
+ }
SectionPred RemovePred = [](const SectionBase &) { return false; };
// Removes:
-
- if (!ToRemove.empty()) {
- RemovePred = [&](const SectionBase &Sec) {
- return std::find(std::begin(ToRemove), std::end(ToRemove), Sec.Name) !=
- std::end(ToRemove);
+ if (!Config.ToRemove.empty()) {
+ RemovePred = [&Config](const SectionBase &Sec) {
+ return std::find(std::begin(Config.ToRemove), std::end(Config.ToRemove),
+ Sec.Name) != std::end(Config.ToRemove);
};
}
- if (StripDWO || !SplitDWO.empty())
+ if (Config.StripDWO || !Config.SplitDWO.empty())
RemovePred = [RemovePred](const SectionBase &Sec) {
return IsDWOSection(Sec) || RemovePred(Sec);
};
- if (ExtractDWO)
+ if (Config.ExtractDWO)
RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
- return OnlyKeepDWOPred(*Obj, Sec) || RemovePred(Sec);
+ return OnlyKeepDWOPred(Obj, Sec) || RemovePred(Sec);
};
- if (StripAllGNU)
+ if (Config.StripAllGNU)
RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
if (RemovePred(Sec))
return true;
if ((Sec.Flags & SHF_ALLOC) != 0)
return false;
- if (&Sec == Obj->getSectionHeaderStrTab())
+ if (&Sec == Obj.SectionNames)
return false;
switch (Sec.Type) {
case SHT_SYMTAB:
@@ -219,33 +349,32 @@ template <class ELFT> void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
return Sec.Name.startswith(".debug");
};
- if (StripSections) {
+ if (Config.StripSections) {
RemovePred = [RemovePred](const SectionBase &Sec) {
return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0;
};
- Obj->WriteSectionHeaders = false;
}
- if (StripDebug) {
+ if (Config.StripDebug) {
RemovePred = [RemovePred](const SectionBase &Sec) {
return RemovePred(Sec) || Sec.Name.startswith(".debug");
};
}
- if (StripNonAlloc)
+ if (Config.StripNonAlloc)
RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
if (RemovePred(Sec))
return true;
- if (&Sec == Obj->getSectionHeaderStrTab())
+ if (&Sec == Obj.SectionNames)
return false;
return (Sec.Flags & SHF_ALLOC) == 0;
};
- if (StripAll)
+ if (Config.StripAll)
RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
if (RemovePred(Sec))
return true;
- if (&Sec == Obj->getSectionHeaderStrTab())
+ if (&Sec == Obj.SectionNames)
return false;
if (Sec.Name.startswith(".gnu.warning"))
return false;
@@ -253,47 +382,67 @@ template <class ELFT> void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
};
// Explicit copies:
-
- if (!OnlyKeep.empty()) {
- RemovePred = [RemovePred, &Obj](const SectionBase &Sec) {
+ if (!Config.OnlyKeep.empty()) {
+ RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) {
// Explicitly keep these sections regardless of previous removes.
- if (std::find(std::begin(OnlyKeep), std::end(OnlyKeep), Sec.Name) !=
- std::end(OnlyKeep))
+ if (std::find(std::begin(Config.OnlyKeep), std::end(Config.OnlyKeep),
+ Sec.Name) != std::end(Config.OnlyKeep))
return false;
// Allow all implicit removes.
- if (RemovePred(Sec)) {
+ if (RemovePred(Sec))
return true;
- }
// Keep special sections.
- if (Obj->getSectionHeaderStrTab() == &Sec) {
+ if (Obj.SectionNames == &Sec)
return false;
- }
- if (Obj->getSymTab() == &Sec || Obj->getSymTab()->getStrTab() == &Sec) {
+ if (Obj.SymbolTable == &Sec ||
+ (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec))
return false;
- }
+
// Remove everything else.
return true;
};
}
- if (!Keep.empty()) {
- RemovePred = [RemovePred](const SectionBase &Sec) {
+ if (!Config.Keep.empty()) {
+ RemovePred = [Config, RemovePred](const SectionBase &Sec) {
// Explicitly keep these sections regardless of previous removes.
- if (std::find(std::begin(Keep), std::end(Keep), Sec.Name) !=
- std::end(Keep))
+ if (std::find(std::begin(Config.Keep), std::end(Config.Keep), Sec.Name) !=
+ std::end(Config.Keep))
return false;
// Otherwise defer to RemovePred.
return RemovePred(Sec);
};
}
- Obj->removeSections(RemovePred);
+ // This has to be the last predicate assignment.
+ // If the option --keep-symbol has been specified
+ // and at least one of those symbols is present
+ // (equivalently, the updated symbol table is not empty)
+ // the symbol table and the string table should not be removed.
+ if ((!Config.SymbolsToKeep.empty() || Config.KeepFileSymbols) &&
+ Obj.SymbolTable && !Obj.SymbolTable->empty()) {
+ RemovePred = [&Obj, RemovePred](const SectionBase &Sec) {
+ if (&Sec == Obj.SymbolTable || &Sec == Obj.SymbolTable->getStrTab())
+ return false;
+ return RemovePred(Sec);
+ };
+ }
- if (!AddSection.empty()) {
- for (const auto &Flag : AddSection) {
- auto SecPair = StringRef(Flag).split("=");
+ Obj.removeSections(RemovePred);
+
+ if (!Config.SectionsToRename.empty()) {
+ for (auto &Sec : Obj.sections()) {
+ const auto Iter = Config.SectionsToRename.find(Sec.Name);
+ if (Iter != Config.SectionsToRename.end())
+ Sec.Name = Iter->second;
+ }
+ }
+
+ if (!Config.AddSection.empty()) {
+ for (const auto &Flag : Config.AddSection) {
+ auto SecPair = Flag.split("=");
auto SecName = SecPair.first;
auto File = SecPair.second;
auto BufOrErr = MemoryBuffer::getFile(File);
@@ -302,44 +451,256 @@ template <class ELFT> void CopyBinary(const ELFObjectFile<ELFT> &ObjFile) {
auto Buf = std::move(*BufOrErr);
auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart());
auto BufSize = Buf->getBufferSize();
- Obj->addSection(SecName, ArrayRef<uint8_t>(BufPtr, BufSize));
+ Obj.addSection<OwnedDataSection>(SecName,
+ ArrayRef<uint8_t>(BufPtr, BufSize));
}
}
- Obj->finalize();
- WriteObjectFile(*Obj, OutputFilename.getValue());
+ if (!Config.AddGnuDebugLink.empty())
+ Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink);
}
-int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
- cl::ParseCommandLineOptions(argc, argv, "llvm objcopy utility\n");
- ToolName = argv[0];
- if (InputFilename.empty()) {
- cl::PrintHelpMessage();
- return 2;
+static void ExecuteElfObjcopyOnBinary(const CopyConfig &Config, Binary &Binary,
+ Buffer &Out) {
+ ELFReader Reader(&Binary);
+ std::unique_ptr<Object> Obj = Reader.create();
+
+ HandleArgs(Config, *Obj, Reader, Reader.getElfType());
+
+ std::unique_ptr<Writer> Writer =
+ CreateWriter(Config, *Obj, Out, Reader.getElfType());
+ Writer->finalize();
+ Writer->write();
+}
+
+// For regular archives this function simply calls llvm::writeArchive,
+// For thin archives it writes the archive file itself as well as its members.
+static Error deepWriteArchive(StringRef ArcName,
+ ArrayRef<NewArchiveMember> NewMembers,
+ bool WriteSymtab, object::Archive::Kind Kind,
+ bool Deterministic, bool Thin) {
+ Error E =
+ writeArchive(ArcName, NewMembers, WriteSymtab, Kind, Deterministic, Thin);
+ if (!Thin || E)
+ return E;
+ for (const NewArchiveMember &Member : NewMembers) {
+ // Internally, FileBuffer will use the buffer created by
+ // FileOutputBuffer::create, for regular files (that is the case for
+ // deepWriteArchive) FileOutputBuffer::create will return OnDiskBuffer.
+ // OnDiskBuffer uses a temporary file and then renames it. So in reality
+ // there is no inefficiency / duplicated in-memory buffers in this case. For
+ // now in-memory buffers can not be completely avoided since
+ // NewArchiveMember still requires them even though writeArchive does not
+ // write them on disk.
+ FileBuffer FB(Member.MemberName);
+ FB.allocate(Member.Buf->getBufferSize());
+ std::copy(Member.Buf->getBufferStart(), Member.Buf->getBufferEnd(),
+ FB.getBufferStart());
+ if (auto E = FB.commit())
+ return E;
+ }
+ return Error::success();
+}
+
+static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive &Ar) {
+ std::vector<NewArchiveMember> NewArchiveMembers;
+ Error Err = Error::success();
+ for (const Archive::Child &Child : Ar.children(Err)) {
+ Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
+ if (!ChildOrErr)
+ reportError(Ar.getFileName(), ChildOrErr.takeError());
+ Expected<StringRef> ChildNameOrErr = Child.getName();
+ if (!ChildNameOrErr)
+ reportError(Ar.getFileName(), ChildNameOrErr.takeError());
+
+ MemBuffer MB(ChildNameOrErr.get());
+ ExecuteElfObjcopyOnBinary(Config, **ChildOrErr, MB);
+
+ Expected<NewArchiveMember> Member =
+ NewArchiveMember::getOldMember(Child, true);
+ if (!Member)
+ reportError(Ar.getFileName(), Member.takeError());
+ Member->Buf = MB.releaseMemoryBuffer();
+ Member->MemberName = Member->Buf->getBufferIdentifier();
+ NewArchiveMembers.push_back(std::move(*Member));
}
- Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(InputFilename);
+
+ if (Err)
+ reportError(Config.InputFilename, std::move(Err));
+ if (Error E =
+ deepWriteArchive(Config.OutputFilename, NewArchiveMembers,
+ Ar.hasSymbolTable(), Ar.kind(), true, Ar.isThin()))
+ reportError(Config.OutputFilename, std::move(E));
+}
+
+static void ExecuteElfObjcopy(const CopyConfig &Config) {
+ Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr =
+ createBinary(Config.InputFilename);
if (!BinaryOrErr)
- reportError(InputFilename, BinaryOrErr.takeError());
- Binary &Binary = *BinaryOrErr.get().getBinary();
- if (auto *o = dyn_cast<ELFObjectFile<ELF64LE>>(&Binary)) {
- CopyBinary(*o);
- return 0;
+ reportError(Config.InputFilename, BinaryOrErr.takeError());
+
+ if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary()))
+ return ExecuteElfObjcopyOnArchive(Config, *Ar);
+
+ FileBuffer FB(Config.OutputFilename);
+ ExecuteElfObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB);
+}
+
+// ParseObjcopyOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseObjcopyOptions will print the help messege and
+// exit.
+static CopyConfig ParseObjcopyOptions(ArrayRef<const char *> ArgsArr) {
+ ObjcopyOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0) {
+ T.PrintHelp(errs(), "llvm-objcopy <input> [ <output> ]", "objcopy tool");
+ exit(1);
+ }
+
+ if (InputArgs.hasArg(OBJCOPY_help)) {
+ T.PrintHelp(outs(), "llvm-objcopy <input> [ <output> ]", "objcopy tool");
+ exit(0);
+ }
+
+ SmallVector<const char *, 2> Positional;
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_UNKNOWN))
+ error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_INPUT))
+ Positional.push_back(Arg->getValue());
+
+ if (Positional.empty())
+ error("No input file specified");
+
+ if (Positional.size() > 2)
+ error("Too many positional arguments");
+
+ CopyConfig Config;
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename = Positional[Positional.size() == 1 ? 0 : 1];
+ Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target);
+ Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target);
+ Config.BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture);
+
+ Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo);
+ Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink);
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_redefine_symbol)) {
+ if (!StringRef(Arg->getValue()).contains('='))
+ error("Bad format for --redefine-sym");
+ auto Old2New = StringRef(Arg->getValue()).split('=');
+ if (!Config.SymbolsToRename.insert(Old2New).second)
+ error("Multiple redefinition of symbol " + Old2New.first);
}
- if (auto *o = dyn_cast<ELFObjectFile<ELF32LE>>(&Binary)) {
- CopyBinary(*o);
- return 0;
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) {
+ if (!StringRef(Arg->getValue()).contains('='))
+ error("Bad format for --rename-section");
+ auto Old2New = StringRef(Arg->getValue()).split('=');
+ if (!Config.SectionsToRename.insert(Old2New).second)
+ error("Already have a section rename for " + Old2New.first);
}
- if (auto *o = dyn_cast<ELFObjectFile<ELF64BE>>(&Binary)) {
- CopyBinary(*o);
- return 0;
+
+ for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section))
+ Config.ToRemove.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep))
+ Config.Keep.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_only_keep))
+ Config.OnlyKeep.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_add_section))
+ Config.AddSection.push_back(Arg->getValue());
+ Config.StripAll = InputArgs.hasArg(OBJCOPY_strip_all);
+ Config.StripAllGNU = InputArgs.hasArg(OBJCOPY_strip_all_gnu);
+ Config.StripDebug = InputArgs.hasArg(OBJCOPY_strip_debug);
+ Config.StripDWO = InputArgs.hasArg(OBJCOPY_strip_dwo);
+ Config.StripSections = InputArgs.hasArg(OBJCOPY_strip_sections);
+ Config.StripNonAlloc = InputArgs.hasArg(OBJCOPY_strip_non_alloc);
+ Config.StripUnneeded = InputArgs.hasArg(OBJCOPY_strip_unneeded);
+ Config.ExtractDWO = InputArgs.hasArg(OBJCOPY_extract_dwo);
+ Config.LocalizeHidden = InputArgs.hasArg(OBJCOPY_localize_hidden);
+ Config.Weaken = InputArgs.hasArg(OBJCOPY_weaken);
+ Config.DiscardAll = InputArgs.hasArg(OBJCOPY_discard_all);
+ Config.OnlyKeepDebug = InputArgs.hasArg(OBJCOPY_only_keep_debug);
+ Config.KeepFileSymbols = InputArgs.hasArg(OBJCOPY_keep_file_symbols);
+ for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol))
+ Config.SymbolsToLocalize.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_globalize_symbol))
+ Config.SymbolsToGlobalize.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_weaken_symbol))
+ Config.SymbolsToWeaken.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_strip_symbol))
+ Config.SymbolsToRemove.push_back(Arg->getValue());
+ for (auto Arg : InputArgs.filtered(OBJCOPY_keep_symbol))
+ Config.SymbolsToKeep.push_back(Arg->getValue());
+
+ return Config;
+}
+
+// ParseStripOptions returns the config and sets the input arguments. If a
+// help flag is set then ParseStripOptions will print the help messege and
+// exit.
+static CopyConfig ParseStripOptions(ArrayRef<const char *> ArgsArr) {
+ StripOptTable T;
+ unsigned MissingArgumentIndex, MissingArgumentCount;
+ llvm::opt::InputArgList InputArgs =
+ T.ParseArgs(ArgsArr, MissingArgumentIndex, MissingArgumentCount);
+
+ if (InputArgs.size() == 0) {
+ T.PrintHelp(errs(), "llvm-strip <input> [ <output> ]", "strip tool");
+ exit(1);
}
- if (auto *o = dyn_cast<ELFObjectFile<ELF32BE>>(&Binary)) {
- CopyBinary(*o);
- return 0;
+
+ if (InputArgs.hasArg(STRIP_help)) {
+ T.PrintHelp(outs(), "llvm-strip <input> [ <output> ]", "strip tool");
+ exit(0);
}
- reportError(InputFilename, object_error::invalid_file_type);
+
+ SmallVector<const char *, 2> Positional;
+ for (auto Arg : InputArgs.filtered(STRIP_UNKNOWN))
+ error("unknown argument '" + Arg->getAsString(InputArgs) + "'");
+ for (auto Arg : InputArgs.filtered(STRIP_INPUT))
+ Positional.push_back(Arg->getValue());
+
+ if (Positional.empty())
+ error("No input file specified");
+
+ if (Positional.size() > 2)
+ error("Support for multiple input files is not implemented yet");
+
+ CopyConfig Config;
+ Config.InputFilename = Positional[0];
+ Config.OutputFilename =
+ InputArgs.getLastArgValue(STRIP_output, Positional[0]);
+
+ Config.StripDebug = InputArgs.hasArg(STRIP_strip_debug);
+
+ Config.DiscardAll = InputArgs.hasArg(STRIP_discard_all);
+ Config.StripUnneeded = InputArgs.hasArg(STRIP_strip_unneeded);
+ Config.StripAll = InputArgs.hasArg(STRIP_strip_all);
+
+ if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll)
+ Config.StripAll = true;
+
+ for (auto Arg : InputArgs.filtered(STRIP_remove_section))
+ Config.ToRemove.push_back(Arg->getValue());
+
+ for (auto Arg : InputArgs.filtered(STRIP_keep_symbol))
+ Config.SymbolsToKeep.push_back(Arg->getValue());
+
+ return Config;
+}
+
+int main(int argc, char **argv) {
+ InitLLVM X(argc, argv);
+ ToolName = argv[0];
+ CopyConfig Config;
+ if (sys::path::stem(ToolName).endswith_lower("strip"))
+ Config = ParseStripOptions(makeArrayRef(argv + 1, argc));
+ else
+ Config = ParseObjcopyOptions(makeArrayRef(argv + 1, argc));
+ ExecuteElfObjcopy(Config);
}
diff --git a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h
index 6732e410d8e0..e222b65dc78f 100644
--- a/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h
+++ b/contrib/llvm/tools/llvm-objcopy/llvm-objcopy.h
@@ -17,8 +17,12 @@
#include <string>
namespace llvm {
+namespace objcopy {
LLVM_ATTRIBUTE_NORETURN extern void error(Twine Message);
+LLVM_ATTRIBUTE_NORETURN extern void reportError(StringRef File, Error E);
+LLVM_ATTRIBUTE_NORETURN extern void reportError(StringRef File,
+ std::error_code EC);
// This is taken from llvm-readobj.
// [see here](llvm/tools/llvm-readobj/llvm-readobj.h:38)
@@ -32,6 +36,7 @@ template <class T> T unwrapOrError(Expected<T> EO) {
error(Buf);
}
+} // end namespace objcopy
} // end namespace llvm
#endif // LLVM_TOOLS_OBJCOPY_OBJCOPY_H
diff --git a/contrib/llvm/tools/llvm-objdump/COFFDump.cpp b/contrib/llvm/tools/llvm-objdump/COFFDump.cpp
index 780d1e9e6111..7ca5d04593ff 100644
--- a/contrib/llvm/tools/llvm-objdump/COFFDump.cpp
+++ b/contrib/llvm/tools/llvm-objdump/COFFDump.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements the COFF-specific dumper for llvm-objdump.
+/// This file implements the COFF-specific dumper for llvm-objdump.
/// It outputs the Win64 EH data structures as plain text.
/// The encoding of the unwind codes is described in MSDN:
/// http://msdn.microsoft.com/en-us/library/ck9asaa9.aspx
@@ -453,7 +453,7 @@ static bool getPDataSection(const COFFObjectFile *Obj,
Rels.push_back(Reloc);
// Sort relocations by address.
- std::sort(Rels.begin(), Rels.end(), RelocAddressLess);
+ llvm::sort(Rels.begin(), Rels.end(), RelocAddressLess);
ArrayRef<uint8_t> Contents;
error(Obj->getSectionContents(Pdata, Contents));
diff --git a/contrib/llvm/tools/llvm-objdump/ELFDump.cpp b/contrib/llvm/tools/llvm-objdump/ELFDump.cpp
index 7f5fe5a9d3b8..f4d36656a6c4 100644
--- a/contrib/llvm/tools/llvm-objdump/ELFDump.cpp
+++ b/contrib/llvm/tools/llvm-objdump/ELFDump.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements the ELF-specific dumper for llvm-objdump.
+/// This file implements the ELF-specific dumper for llvm-objdump.
///
//===----------------------------------------------------------------------===//
@@ -21,6 +21,77 @@
using namespace llvm;
using namespace llvm::object;
+template <class ELFT>
+Expected<StringRef> getDynamicStrTab(const ELFFile<ELFT> *Elf) {
+ typedef ELFFile<ELFT> ELFO;
+
+ auto DynamicEntriesOrError = Elf->dynamicEntries();
+ if (!DynamicEntriesOrError)
+ return DynamicEntriesOrError.takeError();
+
+ for (const typename ELFO::Elf_Dyn &Dyn : *DynamicEntriesOrError) {
+ if (Dyn.d_tag == ELF::DT_STRTAB) {
+ auto MappedAddrOrError = Elf->toMappedAddr(Dyn.getPtr());
+ if (!MappedAddrOrError)
+ consumeError(MappedAddrOrError.takeError());
+ return StringRef(reinterpret_cast<const char *>(*MappedAddrOrError));
+ }
+ }
+
+ // If the dynamic segment is not present, we fall back on the sections.
+ auto SectionsOrError = Elf->sections();
+ if (!SectionsOrError)
+ return SectionsOrError.takeError();
+
+ for (const typename ELFO::Elf_Shdr &Sec : *SectionsOrError) {
+ if (Sec.sh_type == ELF::SHT_DYNSYM)
+ return Elf->getStringTableForSymtab(Sec);
+ }
+
+ return createError("dynamic string table not found");
+}
+
+template <class ELFT>
+void printDynamicSection(const ELFFile<ELFT> *Elf, StringRef Filename) {
+ auto ProgramHeaderOrError = Elf->program_headers();
+ if (!ProgramHeaderOrError)
+ report_error(Filename, ProgramHeaderOrError.takeError());
+
+ auto DynamicEntriesOrError = Elf->dynamicEntries();
+ if (!DynamicEntriesOrError)
+ report_error(Filename, DynamicEntriesOrError.takeError());
+
+ outs() << "Dynamic Section:\n";
+ for (const auto &Dyn : *DynamicEntriesOrError) {
+ if (Dyn.d_tag == ELF::DT_NULL)
+ continue;
+
+ StringRef Str = StringRef(Elf->getDynamicTagAsString(Dyn.d_tag));
+
+ if (Str.empty()) {
+ std::string HexStr = utohexstr(static_cast<uint64_t>(Dyn.d_tag), true);
+ outs() << format(" 0x%-19s", HexStr.c_str());
+ } else {
+ // We use "-21" in order to match GNU objdump's output.
+ outs() << format(" %-21s", Str.data());
+ }
+
+ const char *Fmt =
+ ELFT::Is64Bits ? "0x%016" PRIx64 "\n" : "0x%08" PRIx64 "\n";
+ if (Dyn.d_tag == ELF::DT_NEEDED) {
+ Expected<StringRef> StrTabOrErr = getDynamicStrTab(Elf);
+ if (StrTabOrErr) {
+ const char *Data = StrTabOrErr.get().data();
+ outs() << (Data + Dyn.d_un.d_val) << "\n";
+ continue;
+ }
+ warn(errorToErrorCode(StrTabOrErr.takeError()).message());
+ consumeError(StrTabOrErr.takeError());
+ }
+ outs() << format(Fmt, (uint64_t)Dyn.d_un.d_val);
+ }
+}
+
template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) {
typedef ELFFile<ELFT> ELFO;
outs() << "Program Header:\n";
@@ -103,3 +174,21 @@ void llvm::printELFFileHeader(const object::ObjectFile *Obj) {
if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
printProgramHeaders(ELFObj->getELFFile());
}
+
+void llvm::printELFDynamicSection(const object::ObjectFile *Obj) {
+ // Little-endian 32-bit
+ if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+
+ // Big-endian 32-bit
+ if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+
+ // Little-endian 64-bit
+ if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+
+ // Big-endian 64-bit
+ if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj))
+ printDynamicSection(ELFObj->getELFFile(), Obj->getFileName());
+}
diff --git a/contrib/llvm/tools/llvm-objdump/MachODump.cpp b/contrib/llvm/tools/llvm-objdump/MachODump.cpp
index 9908c2f2d016..bdf80c73b999 100644
--- a/contrib/llvm/tools/llvm-objdump/MachODump.cpp
+++ b/contrib/llvm/tools/llvm-objdump/MachODump.cpp
@@ -76,11 +76,6 @@ cl::opt<bool> llvm::UniversalHeaders("universal-headers",
"(requires -macho)"));
cl::opt<bool>
- llvm::ArchiveHeaders("archive-headers",
- cl::desc("Print archive headers for Mach-O archives "
- "(requires -macho)"));
-
-cl::opt<bool>
ArchiveMemberOffsets("archive-member-offsets",
cl::desc("Print the offset to each archive member for "
"Mach-O archives (requires -macho and "
@@ -1284,14 +1279,35 @@ static void DumpLiteralPointerSection(MachOObjectFile *O,
}
}
-static void DumpInitTermPointerSection(MachOObjectFile *O, const char *sect,
+static void DumpInitTermPointerSection(MachOObjectFile *O,
+ const SectionRef &Section,
+ const char *sect,
uint32_t sect_size, uint64_t sect_addr,
SymbolAddressMap *AddrMap,
bool verbose) {
uint32_t stride;
stride = (O->is64Bit()) ? sizeof(uint64_t) : sizeof(uint32_t);
+
+ // Collect the external relocation symbols for the pointers.
+ std::vector<std::pair<uint64_t, SymbolRef>> Relocs;
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ DataRefImpl Rel;
+ MachO::any_relocation_info RE;
+ bool isExtern = false;
+ Rel = Reloc.getRawDataRefImpl();
+ RE = O->getRelocation(Rel);
+ isExtern = O->getPlainRelocationExternal(RE);
+ if (isExtern) {
+ uint64_t RelocOffset = Reloc.getOffset();
+ symbol_iterator RelocSym = Reloc.getSymbol();
+ Relocs.push_back(std::make_pair(RelocOffset, *RelocSym));
+ }
+ }
+ array_pod_sort(Relocs.begin(), Relocs.end());
+
for (uint32_t i = 0; i < sect_size; i += stride) {
const char *SymbolName = nullptr;
+ uint64_t p;
if (O->is64Bit()) {
outs() << format("0x%016" PRIx64, sect_addr + i * stride) << " ";
uint64_t pointer_value;
@@ -1299,8 +1315,7 @@ static void DumpInitTermPointerSection(MachOObjectFile *O, const char *sect,
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(pointer_value);
outs() << format("0x%016" PRIx64, pointer_value);
- if (verbose)
- SymbolName = GuessSymbolName(pointer_value, AddrMap);
+ p = pointer_value;
} else {
outs() << format("0x%08" PRIx64, sect_addr + i * stride) << " ";
uint32_t pointer_value;
@@ -1308,11 +1323,25 @@ static void DumpInitTermPointerSection(MachOObjectFile *O, const char *sect,
if (O->isLittleEndian() != sys::IsLittleEndianHost)
sys::swapByteOrder(pointer_value);
outs() << format("0x%08" PRIx32, pointer_value);
- if (verbose)
- SymbolName = GuessSymbolName(pointer_value, AddrMap);
+ p = pointer_value;
+ }
+ if (verbose) {
+ // First look for an external relocation entry for this pointer.
+ auto Reloc = find_if(Relocs, [&](const std::pair<uint64_t, SymbolRef> &P) {
+ return P.first == i;
+ });
+ if (Reloc != Relocs.end()) {
+ symbol_iterator RelocSym = Reloc->second;
+ Expected<StringRef> SymName = RelocSym->getName();
+ if (!SymName)
+ report_error(O->getFileName(), SymName.takeError());
+ outs() << " " << *SymName;
+ } else {
+ SymbolName = GuessSymbolName(p, AddrMap);
+ if (SymbolName)
+ outs() << " " << SymbolName;
+ }
}
- if (SymbolName)
- outs() << " " << SymbolName;
outs() << "\n";
}
}
@@ -1463,8 +1492,8 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O,
break;
case MachO::S_MOD_INIT_FUNC_POINTERS:
case MachO::S_MOD_TERM_FUNC_POINTERS:
- DumpInitTermPointerSection(O, sect, sect_size, sect_addr, &AddrMap,
- verbose);
+ DumpInitTermPointerSection(O, Section, sect, sect_size, sect_addr,
+ &AddrMap, verbose);
break;
default:
outs() << "Unknown section type ("
@@ -2149,19 +2178,22 @@ void llvm::ParseInputMachO(StringRef Filename) {
// The block of info used by the Symbolizer call backs.
struct DisassembleInfo {
+ DisassembleInfo(MachOObjectFile *O, SymbolAddressMap *AddrMap,
+ std::vector<SectionRef> *Sections, bool verbose)
+ : verbose(verbose), O(O), AddrMap(AddrMap), Sections(Sections) {}
bool verbose;
MachOObjectFile *O;
SectionRef S;
SymbolAddressMap *AddrMap;
std::vector<SectionRef> *Sections;
- const char *class_name;
- const char *selector_name;
- char *method;
- char *demangled_name;
- uint64_t adrp_addr;
- uint32_t adrp_inst;
+ const char *class_name = nullptr;
+ const char *selector_name = nullptr;
+ std::unique_ptr<char[]> method = nullptr;
+ char *demangled_name = nullptr;
+ uint64_t adrp_addr = 0;
+ uint32_t adrp_inst = 0;
std::unique_ptr<SymbolAddressMap> bindtable;
- uint32_t depth;
+ uint32_t depth = 0;
};
// SymbolizerGetOpInfo() is the operand information call back function.
@@ -2756,32 +2788,33 @@ static void method_reference(struct DisassembleInfo *info,
if (*ReferenceName != nullptr) {
if (strcmp(*ReferenceName, "_objc_msgSend") == 0) {
if (info->selector_name != nullptr) {
- if (info->method != nullptr)
- free(info->method);
if (info->class_name != nullptr) {
- info->method = (char *)malloc(5 + strlen(info->class_name) +
- strlen(info->selector_name));
- if (info->method != nullptr) {
- strcpy(info->method, "+[");
- strcat(info->method, info->class_name);
- strcat(info->method, " ");
- strcat(info->method, info->selector_name);
- strcat(info->method, "]");
- *ReferenceName = info->method;
+ info->method = llvm::make_unique<char[]>(
+ 5 + strlen(info->class_name) + strlen(info->selector_name));
+ char *method = info->method.get();
+ if (method != nullptr) {
+ strcpy(method, "+[");
+ strcat(method, info->class_name);
+ strcat(method, " ");
+ strcat(method, info->selector_name);
+ strcat(method, "]");
+ *ReferenceName = method;
*ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message;
}
} else {
- info->method = (char *)malloc(9 + strlen(info->selector_name));
- if (info->method != nullptr) {
+ info->method =
+ llvm::make_unique<char[]>(9 + strlen(info->selector_name));
+ char *method = info->method.get();
+ if (method != nullptr) {
if (Arch == Triple::x86_64)
- strcpy(info->method, "-[%rdi ");
+ strcpy(method, "-[%rdi ");
else if (Arch == Triple::aarch64)
- strcpy(info->method, "-[x0 ");
+ strcpy(method, "-[x0 ");
else
- strcpy(info->method, "-[r? ");
- strcat(info->method, info->selector_name);
- strcat(info->method, "]");
- *ReferenceName = info->method;
+ strcpy(method, "-[r? ");
+ strcat(method, info->selector_name);
+ strcat(method, "]");
+ *ReferenceName = method;
*ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message;
}
}
@@ -2789,19 +2822,19 @@ static void method_reference(struct DisassembleInfo *info,
}
} else if (strcmp(*ReferenceName, "_objc_msgSendSuper2") == 0) {
if (info->selector_name != nullptr) {
- if (info->method != nullptr)
- free(info->method);
- info->method = (char *)malloc(17 + strlen(info->selector_name));
- if (info->method != nullptr) {
+ info->method =
+ llvm::make_unique<char[]>(17 + strlen(info->selector_name));
+ char *method = info->method.get();
+ if (method != nullptr) {
if (Arch == Triple::x86_64)
- strcpy(info->method, "-[[%rdi super] ");
+ strcpy(method, "-[[%rdi super] ");
else if (Arch == Triple::aarch64)
- strcpy(info->method, "-[[x0 super] ");
+ strcpy(method, "-[[x0 super] ");
else
- strcpy(info->method, "-[[r? super] ");
- strcat(info->method, info->selector_name);
- strcat(info->method, "]");
- *ReferenceName = info->method;
+ strcpy(method, "-[[r? super] ");
+ strcat(method, info->selector_name);
+ strcat(method, "]");
+ *ReferenceName = method;
*ReferenceType = LLVMDisassembler_ReferenceType_Out_Objc_Message;
}
info->class_name = nullptr;
@@ -3196,6 +3229,8 @@ struct imageInfo_t {
/* masks for objc_image_info.flags */
#define OBJC_IMAGE_IS_REPLACEMENT (1 << 0)
#define OBJC_IMAGE_SUPPORTS_GC (1 << 1)
+#define OBJC_IMAGE_IS_SIMULATED (1 << 5)
+#define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES (1 << 6)
struct message_ref64 {
uint64_t imp; /* IMP (64-bit pointer) */
@@ -5557,12 +5592,24 @@ static void print_image_info64(SectionRef S, struct DisassembleInfo *info) {
outs() << " OBJC_IMAGE_IS_REPLACEMENT";
if (o.flags & OBJC_IMAGE_SUPPORTS_GC)
outs() << " OBJC_IMAGE_SUPPORTS_GC";
+ if (o.flags & OBJC_IMAGE_IS_SIMULATED)
+ outs() << " OBJC_IMAGE_IS_SIMULATED";
+ if (o.flags & OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES)
+ outs() << " OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES";
swift_version = (o.flags >> 8) & 0xff;
if (swift_version != 0) {
if (swift_version == 1)
outs() << " Swift 1.0";
else if (swift_version == 2)
outs() << " Swift 1.1";
+ else if(swift_version == 3)
+ outs() << " Swift 2.0";
+ else if(swift_version == 4)
+ outs() << " Swift 3.0";
+ else if(swift_version == 5)
+ outs() << " Swift 4.0";
+ else if(swift_version == 6)
+ outs() << " Swift 4.1";
else
outs() << " unknown future Swift version (" << swift_version << ")";
}
@@ -5606,6 +5653,14 @@ static void print_image_info32(SectionRef S, struct DisassembleInfo *info) {
outs() << " Swift 1.0";
else if (swift_version == 2)
outs() << " Swift 1.1";
+ else if(swift_version == 3)
+ outs() << " Swift 2.0";
+ else if(swift_version == 4)
+ outs() << " Swift 3.0";
+ else if(swift_version == 5)
+ outs() << " Swift 4.0";
+ else if(swift_version == 6)
+ outs() << " Swift 4.1";
else
outs() << " unknown future Swift version (" << swift_version << ")";
}
@@ -5659,21 +5714,8 @@ static void printObjc2_64bit_MetaData(MachOObjectFile *O, bool verbose) {
Sections.push_back(Section);
}
- struct DisassembleInfo info;
- // Set up the block of info used by the Symbolizer call backs.
- info.verbose = verbose;
- info.O = O;
- info.AddrMap = &AddrMap;
- info.Sections = &Sections;
- info.class_name = nullptr;
- info.selector_name = nullptr;
- info.method = nullptr;
- info.demangled_name = nullptr;
- info.bindtable = nullptr;
- info.adrp_addr = 0;
- info.adrp_inst = 0;
-
- info.depth = 0;
+ struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
+
SectionRef CL = get_section(O, "__OBJC2", "__class_list");
if (CL == SectionRef())
CL = get_section(O, "__DATA", "__objc_classlist");
@@ -5757,19 +5799,7 @@ static void printObjc2_32bit_MetaData(MachOObjectFile *O, bool verbose) {
Sections.push_back(Section);
}
- struct DisassembleInfo info;
- // Set up the block of info used by the Symbolizer call backs.
- info.verbose = verbose;
- info.O = O;
- info.AddrMap = &AddrMap;
- info.Sections = &Sections;
- info.class_name = nullptr;
- info.selector_name = nullptr;
- info.method = nullptr;
- info.demangled_name = nullptr;
- info.bindtable = nullptr;
- info.adrp_addr = 0;
- info.adrp_inst = 0;
+ struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
SectionRef CL = get_section(O, "__OBJC2", "__class_list");
if (CL == SectionRef())
@@ -5867,19 +5897,7 @@ static bool printObjc1_32bit_MetaData(MachOObjectFile *O, bool verbose) {
Sections.push_back(Section);
}
- struct DisassembleInfo info;
- // Set up the block of info used by the Symbolizer call backs.
- info.verbose = verbose;
- info.O = O;
- info.AddrMap = &AddrMap;
- info.Sections = &Sections;
- info.class_name = nullptr;
- info.selector_name = nullptr;
- info.method = nullptr;
- info.demangled_name = nullptr;
- info.bindtable = nullptr;
- info.adrp_addr = 0;
- info.adrp_inst = 0;
+ struct DisassembleInfo info(O, &AddrMap, &Sections, verbose);
for (i = 0; i < S.getSize(); i += sizeof(struct objc_module_t)) {
p = S.getAddress() + i;
@@ -6040,19 +6058,7 @@ static void DumpProtocolSection(MachOObjectFile *O, const char *sect,
Sections.push_back(Section);
}
- struct DisassembleInfo info;
- // Set up the block of info used by the Symbolizer call backs.
- info.verbose = true;
- info.O = O;
- info.AddrMap = &AddrMap;
- info.Sections = &Sections;
- info.class_name = nullptr;
- info.selector_name = nullptr;
- info.method = nullptr;
- info.demangled_name = nullptr;
- info.bindtable = nullptr;
- info.adrp_addr = 0;
- info.adrp_inst = 0;
+ struct DisassembleInfo info(O, &AddrMap, &Sections, true);
const char *p;
struct objc_protocol_t protocol;
@@ -6748,7 +6754,7 @@ static const char *SymbolizerSymbolLookUp(void *DisInfo,
return SymbolName;
}
-/// \brief Emits the comments that are stored in the CommentStream.
+/// Emits the comments that are stored in the CommentStream.
/// Each comment in the CommentStream must end with a newline.
static void emitComments(raw_svector_ostream &CommentStream,
SmallString<128> &CommentsToEmit,
@@ -6817,7 +6823,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
std::unique_ptr<MCDisassembler> DisAsm(
TheTarget->createMCDisassembler(*STI, Ctx));
std::unique_ptr<MCSymbolizer> Symbolizer;
- struct DisassembleInfo SymbolizerInfo;
+ struct DisassembleInfo SymbolizerInfo(nullptr, nullptr, nullptr, false);
std::unique_ptr<MCRelocationInfo> RelInfo(
TheTarget->createMCRelocationInfo(TripleName, Ctx));
if (RelInfo) {
@@ -6855,7 +6861,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
std::unique_ptr<MCInstPrinter> ThumbIP;
std::unique_ptr<MCContext> ThumbCtx;
std::unique_ptr<MCSymbolizer> ThumbSymbolizer;
- struct DisassembleInfo ThumbSymbolizerInfo;
+ struct DisassembleInfo ThumbSymbolizerInfo(nullptr, nullptr, nullptr, false);
std::unique_ptr<MCRelocationInfo> ThumbRelInfo;
if (ThumbTarget) {
ThumbMRI.reset(ThumbTarget->createMCRegInfo(ThumbTripleName));
@@ -6904,7 +6910,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
BaseSegmentAddress);
// Sort the symbols by address, just in case they didn't come in that way.
- std::sort(Symbols.begin(), Symbols.end(), SymbolSorter());
+ llvm::sort(Symbols.begin(), Symbols.end(), SymbolSorter());
// Build a data in code table that is sorted on by the address of each entry.
uint64_t BaseAddress = 0;
@@ -6940,10 +6946,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
errs() << "llvm-objdump: " << Filename << ": " << EC.message() << '\n';
return;
}
- DbgObj =
- ObjectFile::createMachOObjectFile(BufOrErr.get()->getMemBufferRef())
- .get()
- .release();
+ Expected<std::unique_ptr<MachOObjectFile>> DbgObjCheck =
+ ObjectFile::createMachOObjectFile(BufOrErr.get()->getMemBufferRef());
+
+ if (DbgObjCheck.takeError())
+ report_error(MachOOF->getFileName(), DbgObjCheck.takeError());
+ DbgObj = DbgObjCheck.get().release();
}
// Setup the DIContext
@@ -7003,26 +7011,12 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
SymbolizerInfo.S = Sections[SectIdx];
SymbolizerInfo.AddrMap = &AddrMap;
SymbolizerInfo.Sections = &Sections;
- SymbolizerInfo.class_name = nullptr;
- SymbolizerInfo.selector_name = nullptr;
- SymbolizerInfo.method = nullptr;
- SymbolizerInfo.demangled_name = nullptr;
- SymbolizerInfo.bindtable = nullptr;
- SymbolizerInfo.adrp_addr = 0;
- SymbolizerInfo.adrp_inst = 0;
// Same for the ThumbSymbolizer
ThumbSymbolizerInfo.verbose = !NoSymbolicOperands;
ThumbSymbolizerInfo.O = MachOOF;
ThumbSymbolizerInfo.S = Sections[SectIdx];
ThumbSymbolizerInfo.AddrMap = &AddrMap;
ThumbSymbolizerInfo.Sections = &Sections;
- ThumbSymbolizerInfo.class_name = nullptr;
- ThumbSymbolizerInfo.selector_name = nullptr;
- ThumbSymbolizerInfo.method = nullptr;
- ThumbSymbolizerInfo.demangled_name = nullptr;
- ThumbSymbolizerInfo.bindtable = nullptr;
- ThumbSymbolizerInfo.adrp_addr = 0;
- ThumbSymbolizerInfo.adrp_inst = 0;
unsigned int Arch = MachOOF->getArch();
@@ -7293,12 +7287,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
TripleName = "";
ThumbTripleName = "";
- if (SymbolizerInfo.method != nullptr)
- free(SymbolizerInfo.method);
if (SymbolizerInfo.demangled_name != nullptr)
free(SymbolizerInfo.demangled_name);
- if (ThumbSymbolizerInfo.method != nullptr)
- free(ThumbSymbolizerInfo.method);
if (ThumbSymbolizerInfo.demangled_name != nullptr)
free(ThumbSymbolizerInfo.demangled_name);
}
@@ -7310,12 +7300,25 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF,
namespace {
-template <typename T> static uint64_t readNext(const char *&Buf) {
+template <typename T>
+static uint64_t read(StringRef Contents, ptrdiff_t Offset) {
using llvm::support::little;
using llvm::support::unaligned;
- uint64_t Val = support::endian::read<T, little, unaligned>(Buf);
- Buf += sizeof(T);
+ if (Offset + sizeof(T) > Contents.size()) {
+ outs() << "warning: attempt to read past end of buffer\n";
+ return T();
+ }
+
+ uint64_t Val =
+ support::endian::read<T, little, unaligned>(Contents.data() + Offset);
+ return Val;
+}
+
+template <typename T>
+static uint64_t readNext(StringRef Contents, ptrdiff_t &Offset) {
+ T Val = read<T>(Contents, Offset);
+ Offset += sizeof(T);
return Val;
}
@@ -7335,18 +7338,18 @@ struct CompactUnwindEntry {
CompactUnwindEntry(StringRef Contents, unsigned Offset, bool Is64)
: OffsetInSection(Offset) {
if (Is64)
- read<uint64_t>(Contents.data() + Offset);
+ read<uint64_t>(Contents, Offset);
else
- read<uint32_t>(Contents.data() + Offset);
+ read<uint32_t>(Contents, Offset);
}
private:
- template <typename UIntPtr> void read(const char *Buf) {
- FunctionAddr = readNext<UIntPtr>(Buf);
- Length = readNext<uint32_t>(Buf);
- CompactEncoding = readNext<uint32_t>(Buf);
- PersonalityAddr = readNext<UIntPtr>(Buf);
- LSDAAddr = readNext<UIntPtr>(Buf);
+ template <typename UIntPtr> void read(StringRef Contents, ptrdiff_t Offset) {
+ FunctionAddr = readNext<UIntPtr>(Contents, Offset);
+ Length = readNext<uint32_t>(Contents, Offset);
+ CompactEncoding = readNext<uint32_t>(Contents, Offset);
+ PersonalityAddr = readNext<UIntPtr>(Contents, Offset);
+ LSDAAddr = readNext<UIntPtr>(Contents, Offset);
}
};
}
@@ -7448,7 +7451,7 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj,
// First populate the initial raw offsets, encodings and so on from the entry.
for (unsigned Offset = 0; Offset < Contents.size(); Offset += EntrySize) {
- CompactUnwindEntry Entry(Contents.data(), Offset, Is64);
+ CompactUnwindEntry Entry(Contents, Offset, Is64);
CompactUnwinds.push_back(Entry);
}
@@ -7515,19 +7518,19 @@ printMachOCompactUnwindSection(const MachOObjectFile *Obj,
// __unwind_info section dumping
//===----------------------------------------------------------------------===//
-static void printRegularSecondLevelUnwindPage(const char *PageStart) {
- const char *Pos = PageStart;
- uint32_t Kind = readNext<uint32_t>(Pos);
+static void printRegularSecondLevelUnwindPage(StringRef PageData) {
+ ptrdiff_t Pos = 0;
+ uint32_t Kind = readNext<uint32_t>(PageData, Pos);
(void)Kind;
assert(Kind == 2 && "kind for a regular 2nd level index should be 2");
- uint16_t EntriesStart = readNext<uint16_t>(Pos);
- uint16_t NumEntries = readNext<uint16_t>(Pos);
+ uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos);
+ uint16_t NumEntries = readNext<uint16_t>(PageData, Pos);
- Pos = PageStart + EntriesStart;
+ Pos = EntriesStart;
for (unsigned i = 0; i < NumEntries; ++i) {
- uint32_t FunctionOffset = readNext<uint32_t>(Pos);
- uint32_t Encoding = readNext<uint32_t>(Pos);
+ uint32_t FunctionOffset = readNext<uint32_t>(PageData, Pos);
+ uint32_t Encoding = readNext<uint32_t>(PageData, Pos);
outs() << " [" << i << "]: "
<< "function offset=" << format("0x%08" PRIx32, FunctionOffset)
@@ -7537,24 +7540,23 @@ static void printRegularSecondLevelUnwindPage(const char *PageStart) {
}
static void printCompressedSecondLevelUnwindPage(
- const char *PageStart, uint32_t FunctionBase,
+ StringRef PageData, uint32_t FunctionBase,
const SmallVectorImpl<uint32_t> &CommonEncodings) {
- const char *Pos = PageStart;
- uint32_t Kind = readNext<uint32_t>(Pos);
+ ptrdiff_t Pos = 0;
+ uint32_t Kind = readNext<uint32_t>(PageData, Pos);
(void)Kind;
assert(Kind == 3 && "kind for a compressed 2nd level index should be 3");
- uint16_t EntriesStart = readNext<uint16_t>(Pos);
- uint16_t NumEntries = readNext<uint16_t>(Pos);
+ uint16_t EntriesStart = readNext<uint16_t>(PageData, Pos);
+ uint16_t NumEntries = readNext<uint16_t>(PageData, Pos);
- uint16_t EncodingsStart = readNext<uint16_t>(Pos);
- readNext<uint16_t>(Pos);
- const auto *PageEncodings = reinterpret_cast<const support::ulittle32_t *>(
- PageStart + EncodingsStart);
+ uint16_t EncodingsStart = readNext<uint16_t>(PageData, Pos);
+ readNext<uint16_t>(PageData, Pos);
+ StringRef PageEncodings = PageData.substr(EncodingsStart, StringRef::npos);
- Pos = PageStart + EntriesStart;
+ Pos = EntriesStart;
for (unsigned i = 0; i < NumEntries; ++i) {
- uint32_t Entry = readNext<uint32_t>(Pos);
+ uint32_t Entry = readNext<uint32_t>(PageData, Pos);
uint32_t FunctionOffset = FunctionBase + (Entry & 0xffffff);
uint32_t EncodingIdx = Entry >> 24;
@@ -7562,7 +7564,9 @@ static void printCompressedSecondLevelUnwindPage(
if (EncodingIdx < CommonEncodings.size())
Encoding = CommonEncodings[EncodingIdx];
else
- Encoding = PageEncodings[EncodingIdx - CommonEncodings.size()];
+ Encoding = read<uint32_t>(PageEncodings,
+ sizeof(uint32_t) *
+ (EncodingIdx - CommonEncodings.size()));
outs() << " [" << i << "]: "
<< "function offset=" << format("0x%08" PRIx32, FunctionOffset)
@@ -7585,13 +7589,13 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
StringRef Contents;
UnwindInfo.getContents(Contents);
- const char *Pos = Contents.data();
+ ptrdiff_t Pos = 0;
//===----------------------------------
// Section header
//===----------------------------------
- uint32_t Version = readNext<uint32_t>(Pos);
+ uint32_t Version = readNext<uint32_t>(Contents, Pos);
outs() << " Version: "
<< format("0x%" PRIx32, Version) << '\n';
if (Version != 1) {
@@ -7599,24 +7603,24 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
return;
}
- uint32_t CommonEncodingsStart = readNext<uint32_t>(Pos);
+ uint32_t CommonEncodingsStart = readNext<uint32_t>(Contents, Pos);
outs() << " Common encodings array section offset: "
<< format("0x%" PRIx32, CommonEncodingsStart) << '\n';
- uint32_t NumCommonEncodings = readNext<uint32_t>(Pos);
+ uint32_t NumCommonEncodings = readNext<uint32_t>(Contents, Pos);
outs() << " Number of common encodings in array: "
<< format("0x%" PRIx32, NumCommonEncodings) << '\n';
- uint32_t PersonalitiesStart = readNext<uint32_t>(Pos);
+ uint32_t PersonalitiesStart = readNext<uint32_t>(Contents, Pos);
outs() << " Personality function array section offset: "
<< format("0x%" PRIx32, PersonalitiesStart) << '\n';
- uint32_t NumPersonalities = readNext<uint32_t>(Pos);
+ uint32_t NumPersonalities = readNext<uint32_t>(Contents, Pos);
outs() << " Number of personality functions in array: "
<< format("0x%" PRIx32, NumPersonalities) << '\n';
- uint32_t IndicesStart = readNext<uint32_t>(Pos);
+ uint32_t IndicesStart = readNext<uint32_t>(Contents, Pos);
outs() << " Index array section offset: "
<< format("0x%" PRIx32, IndicesStart) << '\n';
- uint32_t NumIndices = readNext<uint32_t>(Pos);
+ uint32_t NumIndices = readNext<uint32_t>(Contents, Pos);
outs() << " Number of indices in array: "
<< format("0x%" PRIx32, NumIndices) << '\n';
@@ -7631,9 +7635,9 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
SmallVector<uint32_t, 64> CommonEncodings;
outs() << " Common encodings: (count = " << NumCommonEncodings << ")\n";
- Pos = Contents.data() + CommonEncodingsStart;
+ Pos = CommonEncodingsStart;
for (unsigned i = 0; i < NumCommonEncodings; ++i) {
- uint32_t Encoding = readNext<uint32_t>(Pos);
+ uint32_t Encoding = readNext<uint32_t>(Contents, Pos);
CommonEncodings.push_back(Encoding);
outs() << " encoding[" << i << "]: " << format("0x%08" PRIx32, Encoding)
@@ -7648,9 +7652,9 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
// roughly). Particularly since they only get 2 bits in the compact encoding.
outs() << " Personality functions: (count = " << NumPersonalities << ")\n";
- Pos = Contents.data() + PersonalitiesStart;
+ Pos = PersonalitiesStart;
for (unsigned i = 0; i < NumPersonalities; ++i) {
- uint32_t PersonalityFn = readNext<uint32_t>(Pos);
+ uint32_t PersonalityFn = readNext<uint32_t>(Contents, Pos);
outs() << " personality[" << i + 1
<< "]: " << format("0x%08" PRIx32, PersonalityFn) << '\n';
}
@@ -7671,13 +7675,13 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
SmallVector<IndexEntry, 4> IndexEntries;
outs() << " Top level indices: (count = " << NumIndices << ")\n";
- Pos = Contents.data() + IndicesStart;
+ Pos = IndicesStart;
for (unsigned i = 0; i < NumIndices; ++i) {
IndexEntry Entry;
- Entry.FunctionOffset = readNext<uint32_t>(Pos);
- Entry.SecondLevelPageStart = readNext<uint32_t>(Pos);
- Entry.LSDAStart = readNext<uint32_t>(Pos);
+ Entry.FunctionOffset = readNext<uint32_t>(Contents, Pos);
+ Entry.SecondLevelPageStart = readNext<uint32_t>(Contents, Pos);
+ Entry.LSDAStart = readNext<uint32_t>(Contents, Pos);
IndexEntries.push_back(Entry);
outs() << " [" << i << "]: "
@@ -7696,12 +7700,14 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
// the first top-level index's LSDAOffset to the last (sentinel).
outs() << " LSDA descriptors:\n";
- Pos = Contents.data() + IndexEntries[0].LSDAStart;
- int NumLSDAs = (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) /
- (2 * sizeof(uint32_t));
+ Pos = IndexEntries[0].LSDAStart;
+ const uint32_t LSDASize = 2 * sizeof(uint32_t);
+ int NumLSDAs =
+ (IndexEntries.back().LSDAStart - IndexEntries[0].LSDAStart) / LSDASize;
+
for (int i = 0; i < NumLSDAs; ++i) {
- uint32_t FunctionOffset = readNext<uint32_t>(Pos);
- uint32_t LSDAOffset = readNext<uint32_t>(Pos);
+ uint32_t FunctionOffset = readNext<uint32_t>(Contents, Pos);
+ uint32_t LSDAOffset = readNext<uint32_t>(Contents, Pos);
outs() << " [" << i << "]: "
<< "function offset=" << format("0x%08" PRIx32, FunctionOffset)
<< ", "
@@ -7729,12 +7735,19 @@ static void printMachOUnwindInfoSection(const MachOObjectFile *Obj,
<< "base function offset="
<< format("0x%08" PRIx32, IndexEntries[i].FunctionOffset) << '\n';
- Pos = Contents.data() + IndexEntries[i].SecondLevelPageStart;
- uint32_t Kind = *reinterpret_cast<const support::ulittle32_t *>(Pos);
+ Pos = IndexEntries[i].SecondLevelPageStart;
+ if (Pos + sizeof(uint32_t) > Contents.size()) {
+ outs() << "warning: invalid offset for second level page: " << Pos << '\n';
+ continue;
+ }
+
+ uint32_t Kind =
+ *reinterpret_cast<const support::ulittle32_t *>(Contents.data() + Pos);
if (Kind == 2)
- printRegularSecondLevelUnwindPage(Pos);
+ printRegularSecondLevelUnwindPage(Contents.substr(Pos, 4096));
else if (Kind == 3)
- printCompressedSecondLevelUnwindPage(Pos, IndexEntries[i].FunctionOffset,
+ printCompressedSecondLevelUnwindPage(Contents.substr(Pos, 4096),
+ IndexEntries[i].FunctionOffset,
CommonEncodings);
else
outs() << " Skipping 2nd level page with unknown kind " << Kind
@@ -9352,6 +9365,26 @@ static void PrintThreadCommand(MachO::thread_command t, const char *Ptr,
outs() << "\t esh.flavor " << es.esh.flavor << " esh.count "
<< es.esh.count << "\n";
}
+ } else if (flavor == MachO::x86_EXCEPTION_STATE64) {
+ outs() << " flavor x86_EXCEPTION_STATE64\n";
+ if (count == MachO::x86_EXCEPTION_STATE64_COUNT)
+ outs() << " count x86_EXCEPTION_STATE64_COUNT\n";
+ else
+ outs() << " count " << count
+ << " (not x86_EXCEPTION_STATE64_COUNT)\n";
+ struct MachO::x86_exception_state64_t es64;
+ left = end - begin;
+ if (left >= sizeof(MachO::x86_exception_state64_t)) {
+ memcpy(&es64, begin, sizeof(MachO::x86_exception_state64_t));
+ begin += sizeof(MachO::x86_exception_state64_t);
+ } else {
+ memset(&es64, '\0', sizeof(MachO::x86_exception_state64_t));
+ memcpy(&es64, begin, left);
+ begin += left;
+ }
+ if (isLittleEndian != sys::IsLittleEndianHost)
+ swapStruct(es64);
+ Print_x86_exception_state_t(es64);
} else {
outs() << " flavor " << flavor << " (unknown)\n";
outs() << " count " << count << "\n";
diff --git a/contrib/llvm/tools/llvm-objdump/WasmDump.cpp b/contrib/llvm/tools/llvm-objdump/WasmDump.cpp
index 0d8ffba6ba45..045002cd4b34 100644
--- a/contrib/llvm/tools/llvm-objdump/WasmDump.cpp
+++ b/contrib/llvm/tools/llvm-objdump/WasmDump.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements the wasm-specific dumper for llvm-objdump.
+/// This file implements the wasm-specific dumper for llvm-objdump.
///
//===----------------------------------------------------------------------===//
diff --git a/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp b/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 3a9112423cff..8041e6f59940 100644
--- a/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp
+++ b/contrib/llvm/tools/llvm-objdump/llvm-objdump.cpp
@@ -20,10 +20,12 @@
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSet.h"
#include "llvm/ADT/Triple.h"
#include "llvm/CodeGen/FaultMaps.h"
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
+#include "llvm/Demangle/Demangle.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCDisassembler/MCDisassembler.h"
@@ -50,10 +52,8 @@
#include "llvm/Support/Format.h"
#include "llvm/Support/GraphWriter.h"
#include "llvm/Support/Host.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
@@ -68,6 +68,12 @@
using namespace llvm;
using namespace object;
+cl::opt<bool>
+ llvm::AllHeaders("all-headers",
+ cl::desc("Display all available header information"));
+static cl::alias AllHeadersShort("x", cl::desc("Alias for --all-headers"),
+ cl::aliasopt(AllHeaders));
+
static cl::list<std::string>
InputFilenames(cl::Positional, cl::desc("<input object files>"),cl::ZeroOrMore);
@@ -85,10 +91,30 @@ static cl::alias
DisassembleAlld("D", cl::desc("Alias for --disassemble-all"),
cl::aliasopt(DisassembleAll));
+cl::opt<std::string> llvm::Demangle("demangle",
+ cl::desc("Demangle symbols names"),
+ cl::ValueOptional, cl::init("none"));
+
+static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"),
+ cl::aliasopt(Demangle));
+
+static cl::list<std::string>
+DisassembleFunctions("df",
+ cl::CommaSeparated,
+ cl::desc("List of functions to disassemble"));
+static StringSet<> DisasmFuncsSet;
+
cl::opt<bool>
llvm::Relocations("r", cl::desc("Display the relocation entries in the file"));
cl::opt<bool>
+llvm::DynamicRelocations("dynamic-reloc",
+ cl::desc("Display the dynamic relocation entries in the file"));
+static cl::alias
+DynamicRelocationsd("R", cl::desc("Alias for --dynamic-reloc"),
+ cl::aliasopt(DynamicRelocations));
+
+cl::opt<bool>
llvm::SectionContents("s", cl::desc("Display the content of each section"));
cl::opt<bool>
@@ -182,6 +208,21 @@ static cl::alias
PrivateHeadersShort("p", cl::desc("Alias for --private-headers"),
cl::aliasopt(PrivateHeaders));
+cl::opt<bool> llvm::FileHeaders(
+ "file-headers",
+ cl::desc("Display the contents of the overall file header"));
+
+static cl::alias FileHeadersShort("f", cl::desc("Alias for --file-headers"),
+ cl::aliasopt(FileHeaders));
+
+cl::opt<bool>
+ llvm::ArchiveHeaders("archive-headers",
+ cl::desc("Display archive header information"));
+
+cl::alias
+ArchiveHeadersShort("a", cl::desc("Alias for --archive-headers"),
+ cl::aliasopt(ArchiveHeaders));
+
cl::opt<bool>
llvm::PrintImmHex("print-imm-hex",
cl::desc("Use hex format for immediate values"));
@@ -196,7 +237,7 @@ cl::opt<DIDumpType> llvm::DwarfDumpType(
cl::opt<bool> PrintSource(
"source",
cl::desc(
- "Display source inlined with disassembly. Implies disassmble object"));
+ "Display source inlined with disassembly. Implies disassemble object"));
cl::alias PrintSourceShort("S", cl::desc("Alias for -source"),
cl::aliasopt(PrintSource));
@@ -297,6 +338,11 @@ LLVM_ATTRIBUTE_NORETURN void llvm::error(Twine Message) {
exit(1);
}
+void llvm::warn(StringRef Message) {
+ errs() << ToolName << ": warning: " << Message << ".\n";
+ errs().flush();
+}
+
LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File,
Twine Message) {
errs() << ToolName << ": '" << File << "': " << Message << ".\n";
@@ -396,252 +442,6 @@ bool llvm::RelocAddressLess(RelocationRef a, RelocationRef b) {
return a.getOffset() < b.getOffset();
}
-namespace {
-class SourcePrinter {
-protected:
- DILineInfo OldLineInfo;
- const ObjectFile *Obj = nullptr;
- std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
- // File name to file contents of source
- std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache;
- // Mark the line endings of the cached source
- std::unordered_map<std::string, std::vector<StringRef>> LineCache;
-
-private:
- bool cacheSource(const std::string& File);
-
-public:
- SourcePrinter() = default;
- SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) {
- symbolize::LLVMSymbolizer::Options SymbolizerOpts(
- DILineInfoSpecifier::FunctionNameKind::None, true, false, false,
- DefaultArch);
- Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
- }
- virtual ~SourcePrinter() = default;
- virtual void printSourceLine(raw_ostream &OS, uint64_t Address,
- StringRef Delimiter = "; ");
-};
-
-bool SourcePrinter::cacheSource(const std::string& File) {
- auto BufferOrError = MemoryBuffer::getFile(File);
- if (!BufferOrError)
- return false;
- // Chomp the file to get lines
- size_t BufferSize = (*BufferOrError)->getBufferSize();
- const char *BufferStart = (*BufferOrError)->getBufferStart();
- for (const char *Start = BufferStart, *End = BufferStart;
- End < BufferStart + BufferSize; End++)
- if (*End == '\n' || End == BufferStart + BufferSize - 1 ||
- (*End == '\r' && *(End + 1) == '\n')) {
- LineCache[File].push_back(StringRef(Start, End - Start));
- if (*End == '\r')
- End++;
- Start = End + 1;
- }
- SourceCache[File] = std::move(*BufferOrError);
- return true;
-}
-
-void SourcePrinter::printSourceLine(raw_ostream &OS, uint64_t Address,
- StringRef Delimiter) {
- if (!Symbolizer)
- return;
- DILineInfo LineInfo = DILineInfo();
- auto ExpectecLineInfo =
- Symbolizer->symbolizeCode(Obj->getFileName(), Address);
- if (!ExpectecLineInfo)
- consumeError(ExpectecLineInfo.takeError());
- else
- LineInfo = *ExpectecLineInfo;
-
- if ((LineInfo.FileName == "<invalid>") || OldLineInfo.Line == LineInfo.Line ||
- LineInfo.Line == 0)
- return;
-
- if (PrintLines)
- OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n";
- if (PrintSource) {
- if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
- if (!cacheSource(LineInfo.FileName))
- return;
- auto FileBuffer = SourceCache.find(LineInfo.FileName);
- if (FileBuffer != SourceCache.end()) {
- auto LineBuffer = LineCache.find(LineInfo.FileName);
- if (LineBuffer != LineCache.end()) {
- if (LineInfo.Line > LineBuffer->second.size())
- return;
- // Vector begins at 0, line numbers are non-zero
- OS << Delimiter << LineBuffer->second[LineInfo.Line - 1].ltrim()
- << "\n";
- }
- }
- }
- OldLineInfo = LineInfo;
-}
-
-static bool isArmElf(const ObjectFile *Obj) {
- return (Obj->isELF() &&
- (Obj->getArch() == Triple::aarch64 ||
- Obj->getArch() == Triple::aarch64_be ||
- Obj->getArch() == Triple::arm || Obj->getArch() == Triple::armeb ||
- Obj->getArch() == Triple::thumb ||
- Obj->getArch() == Triple::thumbeb));
-}
-
-class PrettyPrinter {
-public:
- virtual ~PrettyPrinter() = default;
- virtual void printInst(MCInstPrinter &IP, const MCInst *MI,
- ArrayRef<uint8_t> Bytes, uint64_t Address,
- raw_ostream &OS, StringRef Annot,
- MCSubtargetInfo const &STI, SourcePrinter *SP) {
- if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address);
- if (!NoLeadingAddr)
- OS << format("%8" PRIx64 ":", Address);
- if (!NoShowRawInsn) {
- OS << "\t";
- dumpBytes(Bytes, OS);
- }
- if (MI)
- IP.printInst(MI, OS, "", STI);
- else
- OS << " <unknown>";
- }
-};
-PrettyPrinter PrettyPrinterInst;
-class HexagonPrettyPrinter : public PrettyPrinter {
-public:
- void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address,
- raw_ostream &OS) {
- uint32_t opcode =
- (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
- if (!NoLeadingAddr)
- OS << format("%8" PRIx64 ":", Address);
- if (!NoShowRawInsn) {
- OS << "\t";
- dumpBytes(Bytes.slice(0, 4), OS);
- OS << format("%08" PRIx32, opcode);
- }
- }
- void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- uint64_t Address, raw_ostream &OS, StringRef Annot,
- MCSubtargetInfo const &STI, SourcePrinter *SP) override {
- if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, "");
- if (!MI) {
- printLead(Bytes, Address, OS);
- OS << " <unknown>";
- return;
- }
- std::string Buffer;
- {
- raw_string_ostream TempStream(Buffer);
- IP.printInst(MI, TempStream, "", STI);
- }
- StringRef Contents(Buffer);
- // Split off bundle attributes
- auto PacketBundle = Contents.rsplit('\n');
- // Split off first instruction from the rest
- auto HeadTail = PacketBundle.first.split('\n');
- auto Preamble = " { ";
- auto Separator = "";
- while(!HeadTail.first.empty()) {
- OS << Separator;
- Separator = "\n";
- if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address, "");
- printLead(Bytes, Address, OS);
- OS << Preamble;
- Preamble = " ";
- StringRef Inst;
- auto Duplex = HeadTail.first.split('\v');
- if(!Duplex.second.empty()){
- OS << Duplex.first;
- OS << "; ";
- Inst = Duplex.second;
- }
- else
- Inst = HeadTail.first;
- OS << Inst;
- Bytes = Bytes.slice(4);
- Address += 4;
- HeadTail = HeadTail.second.split('\n');
- }
- OS << " } " << PacketBundle.second;
- }
-};
-HexagonPrettyPrinter HexagonPrettyPrinterInst;
-
-class AMDGCNPrettyPrinter : public PrettyPrinter {
-public:
- void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- uint64_t Address, raw_ostream &OS, StringRef Annot,
- MCSubtargetInfo const &STI, SourcePrinter *SP) override {
- if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address);
-
- if (!MI) {
- OS << " <unknown>";
- return;
- }
-
- SmallString<40> InstStr;
- raw_svector_ostream IS(InstStr);
-
- IP.printInst(MI, IS, "", STI);
-
- OS << left_justify(IS.str(), 60) << format("// %012" PRIX64 ": ", Address);
- typedef support::ulittle32_t U32;
- for (auto D : makeArrayRef(reinterpret_cast<const U32*>(Bytes.data()),
- Bytes.size() / sizeof(U32)))
- // D should be explicitly casted to uint32_t here as it is passed
- // by format to snprintf as vararg.
- OS << format("%08" PRIX32 " ", static_cast<uint32_t>(D));
-
- if (!Annot.empty())
- OS << "// " << Annot;
- }
-};
-AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst;
-
-class BPFPrettyPrinter : public PrettyPrinter {
-public:
- void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
- uint64_t Address, raw_ostream &OS, StringRef Annot,
- MCSubtargetInfo const &STI, SourcePrinter *SP) override {
- if (SP && (PrintSource || PrintLines))
- SP->printSourceLine(OS, Address);
- if (!NoLeadingAddr)
- OS << format("%8" PRId64 ":", Address / 8);
- if (!NoShowRawInsn) {
- OS << "\t";
- dumpBytes(Bytes, OS);
- }
- if (MI)
- IP.printInst(MI, OS, "", STI);
- else
- OS << " <unknown>";
- }
-};
-BPFPrettyPrinter BPFPrettyPrinterInst;
-
-PrettyPrinter &selectPrettyPrinter(Triple const &Triple) {
- switch(Triple.getArch()) {
- default:
- return PrettyPrinterInst;
- case Triple::hexagon:
- return HexagonPrettyPrinterInst;
- case Triple::amdgcn:
- return AMDGCNPrettyPrinterInst;
- case Triple::bpfel:
- case Triple::bpfeb:
- return BPFPrettyPrinterInst;
- }
-}
-}
-
template <class ELFT>
static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
const RelocationRef &RelRef,
@@ -671,9 +471,11 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
if (!StrTabOrErr)
return errorToErrorCode(StrTabOrErr.takeError());
StringRef StrTab = *StrTabOrErr;
- uint8_t type = RelRef.getType();
- StringRef res;
int64_t addend = 0;
+ // If there is no Symbol associated with the relocation, we set the undef
+ // boolean value to 'true'. This will prevent us from calling functions that
+ // requires the relocation to be associated with a symbol.
+ bool undef = false;
switch (Sec->sh_type) {
default:
return object_error::parse_failed;
@@ -684,97 +486,41 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj,
case ELF::SHT_RELA: {
const Elf_Rela *ERela = Obj->getRela(Rel);
addend = ERela->r_addend;
+ undef = ERela->getSymbol(false) == 0;
break;
}
}
- symbol_iterator SI = RelRef.getSymbol();
- const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl());
StringRef Target;
- if (symb->getType() == ELF::STT_SECTION) {
- Expected<section_iterator> SymSI = SI->getSection();
- if (!SymSI)
- return errorToErrorCode(SymSI.takeError());
- const Elf_Shdr *SymSec = Obj->getSection((*SymSI)->getRawDataRefImpl());
- auto SecName = EF.getSectionName(SymSec);
- if (!SecName)
- return errorToErrorCode(SecName.takeError());
- Target = *SecName;
- } else {
- Expected<StringRef> SymName = symb->getName(StrTab);
- if (!SymName)
- return errorToErrorCode(SymName.takeError());
- Target = *SymName;
- }
- switch (EF.getHeader()->e_machine) {
- case ELF::EM_X86_64:
- switch (type) {
- case ELF::R_X86_64_PC8:
- case ELF::R_X86_64_PC16:
- case ELF::R_X86_64_PC32: {
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
- fmt << Target << (addend < 0 ? "" : "+") << addend << "-P";
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
- } break;
- case ELF::R_X86_64_8:
- case ELF::R_X86_64_16:
- case ELF::R_X86_64_32:
- case ELF::R_X86_64_32S:
- case ELF::R_X86_64_64: {
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
- fmt << Target << (addend < 0 ? "" : "+") << addend;
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
- } break;
- default:
- res = "Unknown";
- }
- break;
- case ELF::EM_LANAI:
- case ELF::EM_AVR:
- case ELF::EM_AARCH64: {
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
- fmt << Target;
- if (addend != 0)
- fmt << (addend < 0 ? "" : "+") << addend;
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
- break;
- }
- case ELF::EM_386:
- case ELF::EM_IAMCU:
- case ELF::EM_ARM:
- case ELF::EM_HEXAGON:
- case ELF::EM_MIPS:
- case ELF::EM_BPF:
- case ELF::EM_RISCV:
- res = Target;
- break;
- case ELF::EM_WEBASSEMBLY:
- switch (type) {
- case ELF::R_WEBASSEMBLY_DATA: {
- std::string fmtbuf;
- raw_string_ostream fmt(fmtbuf);
- fmt << Target << (addend < 0 ? "" : "+") << addend;
- fmt.flush();
- Result.append(fmtbuf.begin(), fmtbuf.end());
- break;
- }
- case ELF::R_WEBASSEMBLY_FUNCTION:
- res = Target;
- break;
- default:
- res = "Unknown";
+ if (!undef) {
+ symbol_iterator SI = RelRef.getSymbol();
+ const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl());
+ if (symb->getType() == ELF::STT_SECTION) {
+ Expected<section_iterator> SymSI = SI->getSection();
+ if (!SymSI)
+ return errorToErrorCode(SymSI.takeError());
+ const Elf_Shdr *SymSec = Obj->getSection((*SymSI)->getRawDataRefImpl());
+ auto SecName = EF.getSectionName(SymSec);
+ if (!SecName)
+ return errorToErrorCode(SecName.takeError());
+ Target = *SecName;
+ } else {
+ Expected<StringRef> SymName = symb->getName(StrTab);
+ if (!SymName)
+ return errorToErrorCode(SymName.takeError());
+ Target = *SymName;
}
- break;
- default:
- res = "Unknown";
- }
- if (Result.empty())
- Result.append(res.begin(), res.end());
+ } else
+ Target = "*ABS*";
+
+ // Default scheme is to print Target, as well as "+ <addend>" for nonzero
+ // addend. Should be acceptable for all normal purposes.
+ std::string fmtbuf;
+ raw_string_ostream fmt(fmtbuf);
+ fmt << Target;
+ if (addend != 0)
+ fmt << (addend < 0 ? "" : "+") << addend;
+ fmt.flush();
+ Result.append(fmtbuf.begin(), fmtbuf.end());
return std::error_code();
}
@@ -887,9 +633,21 @@ static std::error_code getRelocationValueString(const WasmObjectFile *Obj,
const RelocationRef &RelRef,
SmallVectorImpl<char> &Result) {
const wasm::WasmRelocation& Rel = Obj->getWasmRelocation(RelRef);
+ symbol_iterator SI = RelRef.getSymbol();
std::string fmtbuf;
raw_string_ostream fmt(fmtbuf);
- fmt << Rel.Index << (Rel.Addend < 0 ? "" : "+") << Rel.Addend;
+ if (SI == Obj->symbol_end()) {
+ // Not all wasm relocations have symbols associated with them.
+ // In particular R_WEBASSEMBLY_TYPE_INDEX_LEB.
+ fmt << Rel.Index;
+ } else {
+ Expected<StringRef> SymNameOrErr = SI->getName();
+ if (!SymNameOrErr)
+ return errorToErrorCode(SymNameOrErr.takeError());
+ StringRef SymName = *SymNameOrErr;
+ Result.append(SymName.begin(), SymName.end());
+ }
+ fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend;
fmt.flush();
Result.append(fmtbuf.begin(), fmtbuf.end());
return std::error_code();
@@ -1087,7 +845,7 @@ static std::error_code getRelocationValueString(const RelocationRef &Rel,
llvm_unreachable("unknown object file format");
}
-/// @brief Indicates whether this relocation should hidden when listing
+/// Indicates whether this relocation should hidden when listing
/// relocations, usually because it is the trailing part of a multipart
/// relocation that will be printed as part of the leading relocation.
static bool getHidden(RelocationRef RelRef) {
@@ -1120,6 +878,304 @@ static bool getHidden(RelocationRef RelRef) {
return false;
}
+namespace {
+class SourcePrinter {
+protected:
+ DILineInfo OldLineInfo;
+ const ObjectFile *Obj = nullptr;
+ std::unique_ptr<symbolize::LLVMSymbolizer> Symbolizer;
+ // File name to file contents of source
+ std::unordered_map<std::string, std::unique_ptr<MemoryBuffer>> SourceCache;
+ // Mark the line endings of the cached source
+ std::unordered_map<std::string, std::vector<StringRef>> LineCache;
+
+private:
+ bool cacheSource(const DILineInfo& LineInfoFile);
+
+public:
+ SourcePrinter() = default;
+ SourcePrinter(const ObjectFile *Obj, StringRef DefaultArch) : Obj(Obj) {
+ symbolize::LLVMSymbolizer::Options SymbolizerOpts(
+ DILineInfoSpecifier::FunctionNameKind::None, true, false, false,
+ DefaultArch);
+ Symbolizer.reset(new symbolize::LLVMSymbolizer(SymbolizerOpts));
+ }
+ virtual ~SourcePrinter() = default;
+ virtual void printSourceLine(raw_ostream &OS, uint64_t Address,
+ StringRef Delimiter = "; ");
+};
+
+bool SourcePrinter::cacheSource(const DILineInfo &LineInfo) {
+ std::unique_ptr<MemoryBuffer> Buffer;
+ if (LineInfo.Source) {
+ Buffer = MemoryBuffer::getMemBuffer(*LineInfo.Source);
+ } else {
+ auto BufferOrError = MemoryBuffer::getFile(LineInfo.FileName);
+ if (!BufferOrError)
+ return false;
+ Buffer = std::move(*BufferOrError);
+ }
+ // Chomp the file to get lines
+ size_t BufferSize = Buffer->getBufferSize();
+ const char *BufferStart = Buffer->getBufferStart();
+ for (const char *Start = BufferStart, *End = BufferStart;
+ End < BufferStart + BufferSize; End++)
+ if (*End == '\n' || End == BufferStart + BufferSize - 1 ||
+ (*End == '\r' && *(End + 1) == '\n')) {
+ LineCache[LineInfo.FileName].push_back(StringRef(Start, End - Start));
+ if (*End == '\r')
+ End++;
+ Start = End + 1;
+ }
+ SourceCache[LineInfo.FileName] = std::move(Buffer);
+ return true;
+}
+
+void SourcePrinter::printSourceLine(raw_ostream &OS, uint64_t Address,
+ StringRef Delimiter) {
+ if (!Symbolizer)
+ return;
+ DILineInfo LineInfo = DILineInfo();
+ auto ExpectecLineInfo =
+ Symbolizer->symbolizeCode(Obj->getFileName(), Address);
+ if (!ExpectecLineInfo)
+ consumeError(ExpectecLineInfo.takeError());
+ else
+ LineInfo = *ExpectecLineInfo;
+
+ if ((LineInfo.FileName == "<invalid>") || OldLineInfo.Line == LineInfo.Line ||
+ LineInfo.Line == 0)
+ return;
+
+ if (PrintLines)
+ OS << Delimiter << LineInfo.FileName << ":" << LineInfo.Line << "\n";
+ if (PrintSource) {
+ if (SourceCache.find(LineInfo.FileName) == SourceCache.end())
+ if (!cacheSource(LineInfo))
+ return;
+ auto FileBuffer = SourceCache.find(LineInfo.FileName);
+ if (FileBuffer != SourceCache.end()) {
+ auto LineBuffer = LineCache.find(LineInfo.FileName);
+ if (LineBuffer != LineCache.end()) {
+ if (LineInfo.Line > LineBuffer->second.size())
+ return;
+ // Vector begins at 0, line numbers are non-zero
+ OS << Delimiter << LineBuffer->second[LineInfo.Line - 1].ltrim()
+ << "\n";
+ }
+ }
+ }
+ OldLineInfo = LineInfo;
+}
+
+static bool isArmElf(const ObjectFile *Obj) {
+ return (Obj->isELF() &&
+ (Obj->getArch() == Triple::aarch64 ||
+ Obj->getArch() == Triple::aarch64_be ||
+ Obj->getArch() == Triple::arm || Obj->getArch() == Triple::armeb ||
+ Obj->getArch() == Triple::thumb ||
+ Obj->getArch() == Triple::thumbeb));
+}
+
+class PrettyPrinter {
+public:
+ virtual ~PrettyPrinter() = default;
+ virtual void printInst(MCInstPrinter &IP, const MCInst *MI,
+ ArrayRef<uint8_t> Bytes, uint64_t Address,
+ raw_ostream &OS, StringRef Annot,
+ MCSubtargetInfo const &STI, SourcePrinter *SP,
+ std::vector<RelocationRef> *Rels = nullptr) {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address);
+ if (!NoLeadingAddr)
+ OS << format("%8" PRIx64 ":", Address);
+ if (!NoShowRawInsn) {
+ OS << "\t";
+ dumpBytes(Bytes, OS);
+ }
+ if (MI)
+ IP.printInst(MI, OS, "", STI);
+ else
+ OS << " <unknown>";
+ }
+};
+PrettyPrinter PrettyPrinterInst;
+class HexagonPrettyPrinter : public PrettyPrinter {
+public:
+ void printLead(ArrayRef<uint8_t> Bytes, uint64_t Address,
+ raw_ostream &OS) {
+ uint32_t opcode =
+ (Bytes[3] << 24) | (Bytes[2] << 16) | (Bytes[1] << 8) | Bytes[0];
+ if (!NoLeadingAddr)
+ OS << format("%8" PRIx64 ":", Address);
+ if (!NoShowRawInsn) {
+ OS << "\t";
+ dumpBytes(Bytes.slice(0, 4), OS);
+ OS << format("%08" PRIx32, opcode);
+ }
+ }
+ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ uint64_t Address, raw_ostream &OS, StringRef Annot,
+ MCSubtargetInfo const &STI, SourcePrinter *SP,
+ std::vector<RelocationRef> *Rels) override {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, "");
+ if (!MI) {
+ printLead(Bytes, Address, OS);
+ OS << " <unknown>";
+ return;
+ }
+ std::string Buffer;
+ {
+ raw_string_ostream TempStream(Buffer);
+ IP.printInst(MI, TempStream, "", STI);
+ }
+ StringRef Contents(Buffer);
+ // Split off bundle attributes
+ auto PacketBundle = Contents.rsplit('\n');
+ // Split off first instruction from the rest
+ auto HeadTail = PacketBundle.first.split('\n');
+ auto Preamble = " { ";
+ auto Separator = "";
+ StringRef Fmt = "\t\t\t%08" PRIx64 ": ";
+ std::vector<RelocationRef>::const_iterator rel_cur = Rels->begin();
+ std::vector<RelocationRef>::const_iterator rel_end = Rels->end();
+
+ // Hexagon's packets require relocations to be inline rather than
+ // clustered at the end of the packet.
+ auto PrintReloc = [&]() -> void {
+ while ((rel_cur != rel_end) && (rel_cur->getOffset() <= Address)) {
+ if (rel_cur->getOffset() == Address) {
+ SmallString<16> name;
+ SmallString<32> val;
+ rel_cur->getTypeName(name);
+ error(getRelocationValueString(*rel_cur, val));
+ OS << Separator << format(Fmt.data(), Address) << name << "\t" << val
+ << "\n";
+ return;
+ }
+ rel_cur++;
+ }
+ };
+
+ while(!HeadTail.first.empty()) {
+ OS << Separator;
+ Separator = "\n";
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address, "");
+ printLead(Bytes, Address, OS);
+ OS << Preamble;
+ Preamble = " ";
+ StringRef Inst;
+ auto Duplex = HeadTail.first.split('\v');
+ if(!Duplex.second.empty()){
+ OS << Duplex.first;
+ OS << "; ";
+ Inst = Duplex.second;
+ }
+ else
+ Inst = HeadTail.first;
+ OS << Inst;
+ HeadTail = HeadTail.second.split('\n');
+ if (HeadTail.first.empty())
+ OS << " } " << PacketBundle.second;
+ PrintReloc();
+ Bytes = Bytes.slice(4);
+ Address += 4;
+ }
+ }
+};
+HexagonPrettyPrinter HexagonPrettyPrinterInst;
+
+class AMDGCNPrettyPrinter : public PrettyPrinter {
+public:
+ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ uint64_t Address, raw_ostream &OS, StringRef Annot,
+ MCSubtargetInfo const &STI, SourcePrinter *SP,
+ std::vector<RelocationRef> *Rels) override {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address);
+
+ typedef support::ulittle32_t U32;
+
+ if (MI) {
+ SmallString<40> InstStr;
+ raw_svector_ostream IS(InstStr);
+
+ IP.printInst(MI, IS, "", STI);
+
+ OS << left_justify(IS.str(), 60);
+ } else {
+ // an unrecognized encoding - this is probably data so represent it
+ // using the .long directive, or .byte directive if fewer than 4 bytes
+ // remaining
+ if (Bytes.size() >= 4) {
+ OS << format("\t.long 0x%08" PRIx32 " ",
+ static_cast<uint32_t>(*reinterpret_cast<const U32*>(Bytes.data())));
+ OS.indent(42);
+ } else {
+ OS << format("\t.byte 0x%02" PRIx8, Bytes[0]);
+ for (unsigned int i = 1; i < Bytes.size(); i++)
+ OS << format(", 0x%02" PRIx8, Bytes[i]);
+ OS.indent(55 - (6 * Bytes.size()));
+ }
+ }
+
+ OS << format("// %012" PRIX64 ": ", Address);
+ if (Bytes.size() >=4) {
+ for (auto D : makeArrayRef(reinterpret_cast<const U32*>(Bytes.data()),
+ Bytes.size() / sizeof(U32)))
+ // D should be explicitly casted to uint32_t here as it is passed
+ // by format to snprintf as vararg.
+ OS << format("%08" PRIX32 " ", static_cast<uint32_t>(D));
+ } else {
+ for (unsigned int i = 0; i < Bytes.size(); i++)
+ OS << format("%02" PRIX8 " ", Bytes[i]);
+ }
+
+ if (!Annot.empty())
+ OS << "// " << Annot;
+ }
+};
+AMDGCNPrettyPrinter AMDGCNPrettyPrinterInst;
+
+class BPFPrettyPrinter : public PrettyPrinter {
+public:
+ void printInst(MCInstPrinter &IP, const MCInst *MI, ArrayRef<uint8_t> Bytes,
+ uint64_t Address, raw_ostream &OS, StringRef Annot,
+ MCSubtargetInfo const &STI, SourcePrinter *SP,
+ std::vector<RelocationRef> *Rels) override {
+ if (SP && (PrintSource || PrintLines))
+ SP->printSourceLine(OS, Address);
+ if (!NoLeadingAddr)
+ OS << format("%8" PRId64 ":", Address / 8);
+ if (!NoShowRawInsn) {
+ OS << "\t";
+ dumpBytes(Bytes, OS);
+ }
+ if (MI)
+ IP.printInst(MI, OS, "", STI);
+ else
+ OS << " <unknown>";
+ }
+};
+BPFPrettyPrinter BPFPrettyPrinterInst;
+
+PrettyPrinter &selectPrettyPrinter(Triple const &Triple) {
+ switch(Triple.getArch()) {
+ default:
+ return PrettyPrinterInst;
+ case Triple::hexagon:
+ return HexagonPrettyPrinterInst;
+ case Triple::amdgcn:
+ return AMDGCNPrettyPrinterInst;
+ case Triple::bpfel:
+ case Triple::bpfeb:
+ return BPFPrettyPrinterInst;
+ }
+}
+}
+
static uint8_t getElfSymbolType(const ObjectFile *Obj, const SymbolRef &Sym) {
assert(Obj->isELF());
if (auto *Elf32LEObj = dyn_cast<ELF32LEObjectFile>(Obj))
@@ -1254,6 +1310,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// Create a mapping from virtual address to symbol name. This is used to
// pretty print the symbols while disassembling.
std::map<SectionRef, SectionSymbolsTy> AllSymbols;
+ SectionSymbolsTy AbsoluteSymbols;
for (const SymbolRef &Symbol : Obj->symbols()) {
Expected<uint64_t> AddressOrErr = Symbol.getAddress();
if (!AddressOrErr)
@@ -1269,15 +1326,17 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
Expected<section_iterator> SectionOrErr = Symbol.getSection();
if (!SectionOrErr)
report_error(Obj->getFileName(), SectionOrErr.takeError());
- section_iterator SecI = *SectionOrErr;
- if (SecI == Obj->section_end())
- continue;
uint8_t SymbolType = ELF::STT_NOTYPE;
if (Obj->isELF())
SymbolType = getElfSymbolType(Obj, Symbol);
- AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType);
+ section_iterator SecI = *SectionOrErr;
+ if (SecI != Obj->section_end())
+ AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType);
+ else
+ AbsoluteSymbols.emplace_back(Address, *Name, SymbolType);
+
}
if (AllSymbols.empty() && Obj->isELF())
@@ -1313,6 +1372,8 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
if (Sec != SectionAddresses.end())
AllSymbols[Sec->second].emplace_back(VA, Name, ELF::STT_NOTYPE);
+ else
+ AbsoluteSymbols.emplace_back(VA, Name, ELF::STT_NOTYPE);
}
}
@@ -1320,6 +1381,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
// a symbol near an address.
for (std::pair<const SectionRef, SectionSymbolsTy> &SecSyms : AllSymbols)
array_pod_sort(SecSyms.second.begin(), SecSyms.second.end());
+ array_pod_sort(AbsoluteSymbols.begin(), AbsoluteSymbols.end());
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
if (!DisassembleAll && (!Section.isText() || Section.isVirtual()))
@@ -1349,8 +1411,8 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
}
- std::sort(DataMappingSymsAddr.begin(), DataMappingSymsAddr.end());
- std::sort(TextMappingSymsAddr.begin(), TextMappingSymsAddr.end());
+ llvm::sort(DataMappingSymsAddr.begin(), DataMappingSymsAddr.end());
+ llvm::sort(TextMappingSymsAddr.begin(), TextMappingSymsAddr.end());
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
// AMDGPU disassembler uses symbolizer for printing labels
@@ -1375,30 +1437,22 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
// Sort relocations by address.
- std::sort(Rels.begin(), Rels.end(), RelocAddressLess);
+ llvm::sort(Rels.begin(), Rels.end(), RelocAddressLess);
StringRef SegmentName = "";
if (const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj)) {
DataRefImpl DR = Section.getRawDataRefImpl();
SegmentName = MachO->getSectionFinalSegmentName(DR);
}
- StringRef name;
- error(Section.getName(name));
-
- if ((SectionAddr <= StopAddress) &&
- (SectionAddr + SectSize) >= StartAddress) {
- outs() << "Disassembly of section ";
- if (!SegmentName.empty())
- outs() << SegmentName << ",";
- outs() << name << ':';
- }
+ StringRef SectionName;
+ error(Section.getName(SectionName));
// If the section has no symbol at the start, just insert a dummy one.
if (Symbols.empty() || std::get<0>(Symbols[0]) != 0) {
- Symbols.insert(Symbols.begin(),
- std::make_tuple(SectionAddr, name, Section.isText()
- ? ELF::STT_FUNC
- : ELF::STT_OBJECT));
+ Symbols.insert(
+ Symbols.begin(),
+ std::make_tuple(SectionAddr, SectionName,
+ Section.isText() ? ELF::STT_FUNC : ELF::STT_OBJECT));
}
SmallString<40> Comments;
@@ -1411,6 +1465,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
uint64_t Size;
uint64_t Index;
+ bool PrintedSection = false;
std::vector<RelocationRef>::const_iterator rel_cur = Rels.begin();
std::vector<RelocationRef>::const_iterator rel_end = Rels.end();
@@ -1435,13 +1490,24 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
continue;
}
+ /// Skip if user requested specific symbols and this is not in the list
+ if (!DisasmFuncsSet.empty() &&
+ !DisasmFuncsSet.count(std::get<1>(Symbols[si])))
+ continue;
+
+ if (!PrintedSection) {
+ PrintedSection = true;
+ outs() << "Disassembly of section ";
+ if (!SegmentName.empty())
+ outs() << SegmentName << ",";
+ outs() << SectionName << ':';
+ }
+
// Stop disassembly at the stop address specified
if (End + SectionAddr > StopAddress)
End = StopAddress - SectionAddr;
if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) {
- // make size 4 bytes folded
- End = Start + ((End - Start) & ~0x3ull);
if (std::get<2>(Symbols[si]) == ELF::STT_AMDGPU_HSA_KERNEL) {
// skip amd_kernel_code_t at the begining of kernel symbol (256 bytes)
Start += 256;
@@ -1458,7 +1524,32 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
}
- outs() << '\n' << std::get<1>(Symbols[si]) << ":\n";
+ auto PrintSymbol = [](StringRef Name) {
+ outs() << '\n' << Name << ":\n";
+ };
+ StringRef SymbolName = std::get<1>(Symbols[si]);
+ if (Demangle.getValue() == "" || Demangle.getValue() == "itanium") {
+ char *DemangledSymbol = nullptr;
+ size_t Size = 0;
+ int Status;
+ DemangledSymbol =
+ itaniumDemangle(SymbolName.data(), DemangledSymbol, &Size, &Status);
+ if (Status == 0)
+ PrintSymbol(StringRef(DemangledSymbol));
+ else
+ PrintSymbol(SymbolName);
+
+ if (Size != 0)
+ free(DemangledSymbol);
+ } else
+ PrintSymbol(SymbolName);
+
+ // Don't print raw contents of a virtual section. A virtual section
+ // doesn't have any contents in the file.
+ if (Section.isVirtual()) {
+ outs() << "...\n";
+ continue;
+ }
#ifndef NDEBUG
raw_ostream &DebugOut = DebugFlag ? dbgs() : nulls();
@@ -1560,7 +1651,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
}
Byte = Bytes.slice(Index)[0];
outs() << format(" %02x", Byte);
- AsciiData[NumBytes] = isprint(Byte) ? Byte : '.';
+ AsciiData[NumBytes] = isPrint(Byte) ? Byte : '.';
uint8_t IndentOffset = 0;
NumBytes++;
@@ -1594,7 +1685,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
PIP.printInst(*IP, Disassembled ? &Inst : nullptr,
Bytes.slice(Index, Size), SectionAddr + Index, outs(), "",
- *STI, &SP);
+ *STI, &SP, &Rels);
outs() << CommentStream.str();
Comments.clear();
@@ -1623,55 +1714,65 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) {
--SectionAddress;
TargetSectionSymbols = &AllSymbols[SectionAddress->second];
} else {
- TargetSectionSymbols = nullptr;
+ TargetSectionSymbols = &AbsoluteSymbols;
}
}
// Find the first symbol in the section whose offset is less than
- // or equal to the target.
- if (TargetSectionSymbols) {
- auto TargetSym = std::upper_bound(
- TargetSectionSymbols->begin(), TargetSectionSymbols->end(),
+ // or equal to the target. If there isn't a section that contains
+ // the target, find the nearest preceding absolute symbol.
+ auto TargetSym = std::upper_bound(
+ TargetSectionSymbols->begin(), TargetSectionSymbols->end(),
+ Target, [](uint64_t LHS,
+ const std::tuple<uint64_t, StringRef, uint8_t> &RHS) {
+ return LHS < std::get<0>(RHS);
+ });
+ if (TargetSym == TargetSectionSymbols->begin()) {
+ TargetSectionSymbols = &AbsoluteSymbols;
+ TargetSym = std::upper_bound(
+ AbsoluteSymbols.begin(), AbsoluteSymbols.end(),
Target, [](uint64_t LHS,
const std::tuple<uint64_t, StringRef, uint8_t> &RHS) {
- return LHS < std::get<0>(RHS);
- });
- if (TargetSym != TargetSectionSymbols->begin()) {
- --TargetSym;
- uint64_t TargetAddress = std::get<0>(*TargetSym);
- StringRef TargetName = std::get<1>(*TargetSym);
- outs() << " <" << TargetName;
- uint64_t Disp = Target - TargetAddress;
- if (Disp)
- outs() << "+0x" << Twine::utohexstr(Disp);
- outs() << '>';
- }
+ return LHS < std::get<0>(RHS);
+ });
+ }
+ if (TargetSym != TargetSectionSymbols->begin()) {
+ --TargetSym;
+ uint64_t TargetAddress = std::get<0>(*TargetSym);
+ StringRef TargetName = std::get<1>(*TargetSym);
+ outs() << " <" << TargetName;
+ uint64_t Disp = Target - TargetAddress;
+ if (Disp)
+ outs() << "+0x" << Twine::utohexstr(Disp);
+ outs() << '>';
}
}
}
outs() << "\n";
- // Print relocation for instruction.
- while (rel_cur != rel_end) {
- bool hidden = getHidden(*rel_cur);
- uint64_t addr = rel_cur->getOffset();
- SmallString<16> name;
- SmallString<32> val;
+ // Hexagon does this in pretty printer
+ if (Obj->getArch() != Triple::hexagon)
+ // Print relocation for instruction.
+ while (rel_cur != rel_end) {
+ bool hidden = getHidden(*rel_cur);
+ uint64_t addr = rel_cur->getOffset();
+ SmallString<16> name;
+ SmallString<32> val;
+
+ // If this relocation is hidden, skip it.
+ if (hidden || ((SectionAddr + addr) < StartAddress)) {
+ ++rel_cur;
+ continue;
+ }
- // If this relocation is hidden, skip it.
- if (hidden || ((SectionAddr + addr) < StartAddress)) {
+ // Stop when rel_cur's address is past the current instruction.
+ if (addr >= Index + Size) break;
+ rel_cur->getTypeName(name);
+ error(getRelocationValueString(*rel_cur, val));
+ outs() << format(Fmt.data(), SectionAddr + addr) << name
+ << "\t" << val << "\n";
++rel_cur;
- continue;
}
-
- // Stop when rel_cur's address is past the current instruction.
- if (addr >= Index + Size) break;
- rel_cur->getTypeName(name);
- error(getRelocationValueString(*rel_cur, val));
- outs() << format(Fmt.data(), SectionAddr + addr) << name
- << "\t" << val << "\n";
- ++rel_cur;
- }
}
}
}
@@ -1707,10 +1808,44 @@ void llvm::PrintRelocations(const ObjectFile *Obj) {
}
}
+void llvm::PrintDynamicRelocations(const ObjectFile *Obj) {
+
+ // For the moment, this option is for ELF only
+ if (!Obj->isELF())
+ return;
+
+ const auto *Elf = dyn_cast<ELFObjectFileBase>(Obj);
+
+ if (!Elf || Elf->getEType() != ELF::ET_DYN) {
+ error("not a dynamic object");
+ return;
+ }
+
+ StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64;
+
+ std::vector<SectionRef> DynRelSec = Obj->dynamic_relocation_sections();
+ if (DynRelSec.empty())
+ return;
+
+ outs() << "DYNAMIC RELOCATION RECORDS\n";
+ for (const SectionRef &Section : DynRelSec) {
+ if (Section.relocation_begin() == Section.relocation_end())
+ continue;
+ for (const RelocationRef &Reloc : Section.relocations()) {
+ uint64_t address = Reloc.getOffset();
+ SmallString<32> relocname;
+ SmallString<32> valuestr;
+ Reloc.getTypeName(relocname);
+ error(getRelocationValueString(Reloc, valuestr));
+ outs() << format(Fmt.data(), address) << " " << relocname << " "
+ << valuestr << "\n";
+ }
+ }
+}
+
void llvm::PrintSectionHeaders(const ObjectFile *Obj) {
outs() << "Sections:\n"
"Idx Name Size Address Type\n";
- unsigned i = 0;
for (const SectionRef &Section : ToolSectionFilter(*Obj)) {
StringRef Name;
error(Section.getName(Name));
@@ -1721,9 +1856,9 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) {
bool BSS = Section.isBSS();
std::string Type = (std::string(Text ? "TEXT " : "") +
(Data ? "DATA " : "") + (BSS ? "BSS" : ""));
- outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n", i,
- Name.str().c_str(), Size, Address, Type.c_str());
- ++i;
+ outs() << format("%3d %-13s %08" PRIx64 " %016" PRIx64 " %s\n",
+ (unsigned)Section.getIndex(), Name.str().c_str(), Size,
+ Address, Type.c_str());
}
}
@@ -1764,7 +1899,7 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) {
// Print ascii.
outs() << " ";
for (std::size_t i = 0; i < 16 && addr + i < end; ++i) {
- if (std::isprint(static_cast<unsigned char>(Contents[addr + i]) & 0xFF))
+ if (isPrint(static_cast<unsigned char>(Contents[addr + i]) & 0xFF))
outs() << Contents[addr + i];
else
outs() << ".";
@@ -2018,8 +2153,10 @@ static void printFaultMaps(const ObjectFile *Obj) {
}
static void printPrivateFileHeaders(const ObjectFile *o, bool onlyFirst) {
- if (o->isELF())
- return printELFFileHeader(o);
+ if (o->isELF()) {
+ printELFFileHeader(o);
+ return printELFDynamicSection(o);
+ }
if (o->isCOFF())
return printCOFFFileHeader(o);
if (o->isWasm())
@@ -2033,7 +2170,86 @@ static void printPrivateFileHeaders(const ObjectFile *o, bool onlyFirst) {
report_error(o->getFileName(), "Invalid/Unsupported object file format");
}
-static void DumpObject(ObjectFile *o, const Archive *a = nullptr) {
+static void printFileHeaders(const ObjectFile *o) {
+ if (!o->isELF() && !o->isCOFF())
+ report_error(o->getFileName(), "Invalid/Unsupported object file format");
+
+ Triple::ArchType AT = o->getArch();
+ outs() << "architecture: " << Triple::getArchTypeName(AT) << "\n";
+ Expected<uint64_t> StartAddrOrErr = o->getStartAddress();
+ if (!StartAddrOrErr)
+ report_error(o->getFileName(), StartAddrOrErr.takeError());
+ outs() << "start address: "
+ << format("0x%0*x", o->getBytesInAddress(), StartAddrOrErr.get())
+ << "\n";
+}
+
+static void printArchiveChild(StringRef Filename, const Archive::Child &C) {
+ Expected<sys::fs::perms> ModeOrErr = C.getAccessMode();
+ if (!ModeOrErr) {
+ errs() << "ill-formed archive entry.\n";
+ consumeError(ModeOrErr.takeError());
+ return;
+ }
+ sys::fs::perms Mode = ModeOrErr.get();
+ outs() << ((Mode & sys::fs::owner_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::owner_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::owner_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::group_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::group_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::group_exe) ? "x" : "-");
+ outs() << ((Mode & sys::fs::others_read) ? "r" : "-");
+ outs() << ((Mode & sys::fs::others_write) ? "w" : "-");
+ outs() << ((Mode & sys::fs::others_exe) ? "x" : "-");
+
+ outs() << " ";
+
+ Expected<unsigned> UIDOrErr = C.getUID();
+ if (!UIDOrErr)
+ report_error(Filename, UIDOrErr.takeError());
+ unsigned UID = UIDOrErr.get();
+ outs() << format("%d/", UID);
+
+ Expected<unsigned> GIDOrErr = C.getGID();
+ if (!GIDOrErr)
+ report_error(Filename, GIDOrErr.takeError());
+ unsigned GID = GIDOrErr.get();
+ outs() << format("%-d ", GID);
+
+ Expected<uint64_t> Size = C.getRawSize();
+ if (!Size)
+ report_error(Filename, Size.takeError());
+ outs() << format("%6" PRId64, Size.get()) << " ";
+
+ StringRef RawLastModified = C.getRawLastModified();
+ unsigned Seconds;
+ if (RawLastModified.getAsInteger(10, Seconds))
+ outs() << "(date: \"" << RawLastModified
+ << "\" contains non-decimal chars) ";
+ else {
+ // Since ctime(3) returns a 26 character string of the form:
+ // "Sun Sep 16 01:03:52 1973\n\0"
+ // just print 24 characters.
+ time_t t = Seconds;
+ outs() << format("%.24s ", ctime(&t));
+ }
+
+ StringRef Name = "";
+ Expected<StringRef> NameOrErr = C.getName();
+ if (!NameOrErr) {
+ consumeError(NameOrErr.takeError());
+ Expected<StringRef> RawNameOrErr = C.getRawName();
+ if (!RawNameOrErr)
+ report_error(Filename, NameOrErr.takeError());
+ Name = RawNameOrErr.get();
+ } else {
+ Name = NameOrErr.get();
+ }
+ outs() << Name << "\n";
+}
+
+static void DumpObject(ObjectFile *o, const Archive *a = nullptr,
+ const Archive::Child *c = nullptr) {
StringRef ArchiveName = a != nullptr ? a->getFileName() : "";
// Avoid other output when using a raw option.
if (!RawClangAST) {
@@ -2045,10 +2261,14 @@ static void DumpObject(ObjectFile *o, const Archive *a = nullptr) {
outs() << ":\tfile format " << o->getFileFormatName() << "\n\n";
}
+ if (ArchiveHeaders && !MachOOpt)
+ printArchiveChild(a->getFileName(), *c);
if (Disassemble)
DisassembleObject(o, Relocations);
if (Relocations && !Disassemble)
PrintRelocations(o);
+ if (DynamicRelocations)
+ PrintDynamicRelocations(o);
if (SectionHeaders)
PrintSectionHeaders(o);
if (SectionContents)
@@ -2059,6 +2279,8 @@ static void DumpObject(ObjectFile *o, const Archive *a = nullptr) {
PrintUnwindInfo(o);
if (PrivateHeaders || FirstPrivateHeader)
printPrivateFileHeaders(o, FirstPrivateHeader);
+ if (FileHeaders)
+ printFileHeaders(o);
if (ExportsTrie)
printExportsTrie(o);
if (Rebase)
@@ -2082,7 +2304,8 @@ static void DumpObject(ObjectFile *o, const Archive *a = nullptr) {
}
}
-static void DumpObject(const COFFImportFile *I, const Archive *A) {
+static void DumpObject(const COFFImportFile *I, const Archive *A,
+ const Archive::Child *C = nullptr) {
StringRef ArchiveName = A ? A->getFileName() : "";
// Avoid other output when using a raw option.
@@ -2092,11 +2315,13 @@ static void DumpObject(const COFFImportFile *I, const Archive *A) {
<< ":\tfile format COFF-import-file"
<< "\n\n";
+ if (ArchiveHeaders && !MachOOpt)
+ printArchiveChild(A->getFileName(), *C);
if (SymbolTable)
printCOFFSymbolTable(I);
}
-/// @brief Dump each object file in \a a;
+/// Dump each object file in \a a;
static void DumpArchive(const Archive *a) {
Error Err = Error::success();
for (auto &C : a->children(Err)) {
@@ -2107,9 +2332,9 @@ static void DumpArchive(const Archive *a) {
continue;
}
if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
- DumpObject(o, a);
+ DumpObject(o, a, &C);
else if (COFFImportFile *I = dyn_cast<COFFImportFile>(&*ChildOrErr.get()))
- DumpObject(I, a);
+ DumpObject(I, a, &C);
else
report_error(a->getFileName(), object_error::invalid_file_type);
}
@@ -2117,7 +2342,7 @@ static void DumpArchive(const Archive *a) {
report_error(a->getFileName(), std::move(Err));
}
-/// @brief Open file and figure out how to dump it.
+/// Open file and figure out how to dump it.
static void DumpInput(StringRef file) {
// If we are using the Mach-O specific object file parser, then let it parse
@@ -2143,10 +2368,7 @@ static void DumpInput(StringRef file) {
}
int main(int argc, char **argv) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
// Initialize targets and assembly printers/parsers.
llvm::InitializeAllTargetInfos();
@@ -2165,15 +2387,25 @@ int main(int argc, char **argv) {
if (InputFilenames.size() == 0)
InputFilenames.push_back("a.out");
+ if (AllHeaders)
+ PrivateHeaders = Relocations = SectionHeaders = SymbolTable = true;
+
if (DisassembleAll || PrintSource || PrintLines)
Disassemble = true;
+
+ if (Demangle.getValue() != "none" && Demangle.getValue() != "" &&
+ Demangle.getValue() != "itanium")
+ warn("Unsupported demangling style");
+
if (!Disassemble
&& !Relocations
+ && !DynamicRelocations
&& !SectionHeaders
&& !SectionContents
&& !SymbolTable
&& !UnwindInfo
&& !PrivateHeaders
+ && !FileHeaders
&& !FirstPrivateHeader
&& !ExportsTrie
&& !Rebase
@@ -2182,7 +2414,7 @@ int main(int argc, char **argv) {
&& !WeakBind
&& !RawClangAST
&& !(UniversalHeaders && MachOOpt)
- && !(ArchiveHeaders && MachOOpt)
+ && !ArchiveHeaders
&& !(IndirectSymbols && MachOOpt)
&& !(DataInCode && MachOOpt)
&& !(LinkOptHints && MachOOpt)
@@ -2197,6 +2429,9 @@ int main(int argc, char **argv) {
return 2;
}
+ DisasmFuncsSet.insert(DisassembleFunctions.begin(),
+ DisassembleFunctions.end());
+
llvm::for_each(InputFilenames, DumpInput);
return EXIT_SUCCESS;
diff --git a/contrib/llvm/tools/llvm-objdump/llvm-objdump.h b/contrib/llvm/tools/llvm-objdump/llvm-objdump.h
index 2fcd506884b1..b2eb6e9d7771 100644
--- a/contrib/llvm/tools/llvm-objdump/llvm-objdump.h
+++ b/contrib/llvm/tools/llvm-objdump/llvm-objdump.h
@@ -30,13 +30,16 @@ namespace object {
extern cl::opt<std::string> TripleName;
extern cl::opt<std::string> ArchName;
extern cl::opt<std::string> MCPU;
+extern cl::opt<std::string> Demangle;
extern cl::list<std::string> MAttrs;
extern cl::list<std::string> FilterSections;
+extern cl::opt<bool> AllHeaders;
extern cl::opt<bool> Disassemble;
extern cl::opt<bool> DisassembleAll;
extern cl::opt<bool> NoShowRawInsn;
extern cl::opt<bool> NoLeadingAddr;
extern cl::opt<bool> PrivateHeaders;
+extern cl::opt<bool> FileHeaders;
extern cl::opt<bool> FirstPrivateHeader;
extern cl::opt<bool> ExportsTrie;
extern cl::opt<bool> Rebase;
@@ -56,6 +59,7 @@ extern cl::opt<bool> ObjcMetaData;
extern cl::opt<std::string> DisSymName;
extern cl::opt<bool> NonVerbose;
extern cl::opt<bool> Relocations;
+extern cl::opt<bool> DynamicRelocations;
extern cl::opt<bool> SectionHeaders;
extern cl::opt<bool> SectionContents;
extern cl::opt<bool> SymbolTable;
@@ -75,6 +79,7 @@ void printMachOBindTable(object::MachOObjectFile* o);
void printMachOLazyBindTable(object::MachOObjectFile* o);
void printMachOWeakBindTable(object::MachOObjectFile* o);
void printELFFileHeader(const object::ObjectFile *o);
+void printELFDynamicSection(const object::ObjectFile *Obj);
void printCOFFFileHeader(const object::ObjectFile *o);
void printCOFFSymbolTable(const object::COFFImportFile *i);
void printCOFFSymbolTable(const object::COFFObjectFile *o);
@@ -88,10 +93,12 @@ void printLazyBindTable(object::ObjectFile *o);
void printWeakBindTable(object::ObjectFile *o);
void printRawClangAST(const object::ObjectFile *o);
void PrintRelocations(const object::ObjectFile *o);
+void PrintDynamicRelocations(const object::ObjectFile *o);
void PrintSectionHeaders(const object::ObjectFile *o);
void PrintSectionContents(const object::ObjectFile *o);
void PrintSymbolTable(const object::ObjectFile *o, StringRef ArchiveName,
StringRef ArchitectureName = StringRef());
+void warn(StringRef Message);
LLVM_ATTRIBUTE_NORETURN void error(Twine Message);
LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, Twine Message);
LLVM_ATTRIBUTE_NORETURN void report_error(StringRef File, std::error_code EC);
diff --git a/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp b/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp
index 6c603dd8542b..974ab49d9440 100644
--- a/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/Analyze.cpp
@@ -125,7 +125,7 @@ Error AnalysisStyle::dump() {
const auto &Collisions = CollisionsIter->second;
outs() << TypeName << "\n";
- outs() << formatv(" [HEAD] {0:x} {1} {2}\n", A.second,
+ outs() << formatv(" [HEAD] {0:x} {1} {2}\n", uint32_t(A.second),
getLeafTypeName(HeadRecord.Type), TypeName);
for (const auto &Chain : Collisions) {
if (Chain.TI == TI)
diff --git a/contrib/llvm/tools/llvm-pdbutil/Diff.cpp b/contrib/llvm/tools/llvm-pdbutil/Diff.cpp
deleted file mode 100644
index 286dc51c29b6..000000000000
--- a/contrib/llvm/tools/llvm-pdbutil/Diff.cpp
+++ /dev/null
@@ -1,644 +0,0 @@
-//===- Diff.cpp - PDB diff utility ------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#include "Diff.h"
-
-#include "DiffPrinter.h"
-#include "FormatUtil.h"
-#include "StreamUtil.h"
-#include "llvm-pdbutil.h"
-
-#include "llvm/ADT/StringSet.h"
-
-#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
-#include "llvm/DebugInfo/PDB/Native/Formatters.h"
-#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
-#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
-#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
-#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
-
-#include "llvm/Support/FormatAdapters.h"
-#include "llvm/Support/FormatProviders.h"
-#include "llvm/Support/FormatVariadic.h"
-#include "llvm/Support/Path.h"
-
-using namespace llvm;
-using namespace llvm::pdb;
-
-namespace {
-// Compare and format two stream numbers. Stream numbers are considered
-// identical if they contain the same value, equivalent if they are both
-// the invalid stream or neither is the invalid stream, and different if
-// one is the invalid stream and another isn't.
-struct StreamNumberProvider {
- static DiffResult compare(uint16_t L, uint16_t R) {
- if (L == R)
- return DiffResult::IDENTICAL;
- bool LP = L != kInvalidStreamIndex;
- bool RP = R != kInvalidStreamIndex;
- if (LP != RP)
- return DiffResult::DIFFERENT;
- return DiffResult::EQUIVALENT;
- }
-
- static std::string format(uint16_t SN, bool Right) {
- if (SN == kInvalidStreamIndex)
- return "(not present)";
- return formatv("{0}", SN).str();
- }
-};
-
-// Compares and formats two module indices. Modis are considered identical
-// if they are identical, equivalent if they either both contain a value or
-// both don't contain a value, and different if one contains a value and the
-// other doesn't.
-struct ModiProvider {
- DiffResult compare(Optional<uint32_t> L, Optional<uint32_t> R) {
- if (L == R)
- return DiffResult::IDENTICAL;
- if (L.hasValue() != R.hasValue())
- return DiffResult::DIFFERENT;
- return DiffResult::EQUIVALENT;
- }
-
- std::string format(Optional<uint32_t> Modi, bool Right) {
- if (!Modi.hasValue())
- return "(not present)";
- return formatv("{0}", *Modi).str();
- }
-};
-
-// Compares and formats two paths embedded in the PDB, ignoring the beginning
-// of the path if the user specified it as a "root path" on the command line.
-struct BinaryPathProvider {
- explicit BinaryPathProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
-
- DiffResult compare(StringRef L, StringRef R) {
- if (L == R)
- return DiffResult::IDENTICAL;
-
- SmallString<64> LN = removeRoot(L, false);
- SmallString<64> RN = removeRoot(R, true);
-
- return (LN.equals_lower(RN)) ? DiffResult::EQUIVALENT
- : DiffResult::DIFFERENT;
- }
-
- std::string format(StringRef S, bool Right) {
- if (S.empty())
- return "(empty)";
-
- SmallString<64> Native = removeRoot(S, Right);
- return truncateStringFront(Native.str(), MaxLen);
- }
-
- SmallString<64> removeRoot(StringRef Path, bool IsRight) const {
- SmallString<64> Native(Path);
- auto &RootOpt = IsRight ? opts::diff::RightRoot : opts::diff::LeftRoot;
- SmallString<64> Root(static_cast<std::string>(RootOpt));
- // pdb paths always use windows syntax, convert slashes to backslashes.
- sys::path::native(Root, sys::path::Style::windows);
- if (sys::path::has_stem(Root, sys::path::Style::windows))
- sys::path::append(Root, sys::path::Style::windows,
- sys::path::get_separator(sys::path::Style::windows));
-
- sys::path::replace_path_prefix(Native, Root, "", sys::path::Style::windows);
- return Native;
- }
- uint32_t MaxLen;
-};
-
-// Compare and format two stream purposes. For general streams, this just
-// compares the description. For module streams it uses the path comparison
-// algorithm taking into consideration the binary root, described above.
-// Formatting stream purposes just prints the stream purpose, except for
-// module streams and named streams, where it prefixes the name / module
-// with an identifier. Example:
-//
-// Named Stream "\names"
-// Module Stream "foo.obj"
-//
-// If a named stream is too long to fit in a column, it is truncated at the
-// end, and if a module is too long to fit in a column, it is truncated at the
-// beginning. Example:
-//
-// Named Stream "\Really Long Str..."
-// Module Stream "...puts\foo.obj"
-//
-struct StreamPurposeProvider {
- explicit StreamPurposeProvider(uint32_t MaxLen) : MaxLen(MaxLen) {}
-
- DiffResult compare(const StreamInfo &L, const StreamInfo &R) {
- if (L.getPurpose() != R.getPurpose())
- return DiffResult::DIFFERENT;
- if (L.getPurpose() == StreamPurpose::ModuleStream) {
- BinaryPathProvider PathProvider(MaxLen);
- return PathProvider.compare(L.getShortName(), R.getShortName());
- }
- return (L.getShortName() == R.getShortName()) ? DiffResult::IDENTICAL
- : DiffResult::DIFFERENT;
- }
-
- std::string format(const StreamInfo &P, bool Right) {
- if (P.getPurpose() == StreamPurpose::Other ||
- P.getPurpose() == StreamPurpose::Symbols)
- return truncateStringBack(P.getShortName(), MaxLen);
- if (P.getPurpose() == StreamPurpose::NamedStream)
- return truncateQuotedNameBack("Named Stream", P.getShortName(), MaxLen);
-
- assert(P.getPurpose() == StreamPurpose::ModuleStream);
- uint32_t ExtraChars = strlen("Module \"\"");
- BinaryPathProvider PathProvider(MaxLen - ExtraChars);
- std::string Result = PathProvider.format(P.getShortName(), Right);
- return formatv("Module \"{0}\"", Result);
- }
-
- uint32_t MaxLen;
-};
-} // namespace
-
-namespace llvm {
-template <> struct format_provider<PdbRaw_FeatureSig> {
- static void format(const PdbRaw_FeatureSig &Sig, raw_ostream &Stream,
- StringRef Style) {
- switch (Sig) {
- case PdbRaw_FeatureSig::MinimalDebugInfo:
- Stream << "MinimalDebugInfo";
- break;
- case PdbRaw_FeatureSig::NoTypeMerge:
- Stream << "NoTypeMerge";
- break;
- case PdbRaw_FeatureSig::VC110:
- Stream << "VC110";
- break;
- case PdbRaw_FeatureSig::VC140:
- Stream << "VC140";
- break;
- }
- }
-};
-}
-
-template <typename R> using ValueOfRange = llvm::detail::ValueOfRange<R>;
-
-DiffStyle::DiffStyle(PDBFile &File1, PDBFile &File2)
- : File1(File1), File2(File2) {}
-
-Error DiffStyle::dump() {
- if (auto EC = diffSuperBlock())
- return EC;
-
- if (auto EC = diffFreePageMap())
- return EC;
-
- if (auto EC = diffStreamDirectory())
- return EC;
-
- if (auto EC = diffStringTable())
- return EC;
-
- if (auto EC = diffInfoStream())
- return EC;
-
- if (auto EC = diffDbiStream())
- return EC;
-
- if (auto EC = diffSectionContribs())
- return EC;
-
- if (auto EC = diffSectionMap())
- return EC;
-
- if (auto EC = diffFpoStream())
- return EC;
-
- if (auto EC = diffTpiStream(StreamTPI))
- return EC;
-
- if (auto EC = diffTpiStream(StreamIPI))
- return EC;
-
- if (auto EC = diffPublics())
- return EC;
-
- if (auto EC = diffGlobals())
- return EC;
-
- return Error::success();
-}
-
-Error DiffStyle::diffSuperBlock() {
- DiffPrinter D(2, "MSF Super Block", 16, 20, opts::diff::PrintResultColumn,
- opts::diff::PrintValueColumns, outs());
- D.printExplicit("File", DiffResult::UNSPECIFIED,
- truncateStringFront(File1.getFilePath(), 18),
- truncateStringFront(File2.getFilePath(), 18));
- D.print("Block Size", File1.getBlockSize(), File2.getBlockSize());
- D.print("Block Count", File1.getBlockCount(), File2.getBlockCount());
- D.print("Unknown 1", File1.getUnknown1(), File2.getUnknown1());
- D.print("Directory Size", File1.getNumDirectoryBytes(),
- File2.getNumDirectoryBytes());
- return Error::success();
-}
-
-Error DiffStyle::diffStreamDirectory() {
- DiffPrinter D(2, "Stream Directory", 30, 20, opts::diff::PrintResultColumn,
- opts::diff::PrintValueColumns, outs());
- D.printExplicit("File", DiffResult::UNSPECIFIED,
- truncateStringFront(File1.getFilePath(), 18),
- truncateStringFront(File2.getFilePath(), 18));
-
- SmallVector<StreamInfo, 32> P;
- SmallVector<StreamInfo, 32> Q;
- discoverStreamPurposes(File1, P);
- discoverStreamPurposes(File2, Q);
- D.print("Stream Count", File1.getNumStreams(), File2.getNumStreams());
- auto PI = to_vector<32>(enumerate(P));
- auto QI = to_vector<32>(enumerate(Q));
-
- // Scan all streams in the left hand side, looking for ones that are also
- // in the right. Each time we find one, remove it. When we're done, Q
- // should contain all the streams that are in the right but not in the left.
- StreamPurposeProvider StreamProvider(28);
- for (const auto &P : PI) {
- typedef decltype(PI) ContainerType;
- typedef typename ContainerType::value_type value_type;
-
- auto Iter = llvm::find_if(QI, [P, &StreamProvider](const value_type &V) {
- DiffResult Result = StreamProvider.compare(P.value(), V.value());
- return Result == DiffResult::EQUIVALENT ||
- Result == DiffResult::IDENTICAL;
- });
-
- if (Iter == QI.end()) {
- D.printExplicit(StreamProvider.format(P.value(), false),
- DiffResult::DIFFERENT, P.index(), "(not present)");
- continue;
- }
-
- D.print<EquivalentDiffProvider>(StreamProvider.format(P.value(), false),
- P.index(), Iter->index());
- QI.erase(Iter);
- }
-
- for (const auto &Q : QI) {
- D.printExplicit(StreamProvider.format(Q.value(), true),
- DiffResult::DIFFERENT, "(not present)", Q.index());
- }
-
- return Error::success();
-}
-
-Error DiffStyle::diffStringTable() {
- DiffPrinter D(2, "String Table", 30, 20, opts::diff::PrintResultColumn,
- opts::diff::PrintValueColumns, outs());
- D.printExplicit("File", DiffResult::UNSPECIFIED,
- truncateStringFront(File1.getFilePath(), 18),
- truncateStringFront(File2.getFilePath(), 18));
-
- auto ExpectedST1 = File1.getStringTable();
- auto ExpectedST2 = File2.getStringTable();
- bool Has1 = !!ExpectedST1;
- bool Has2 = !!ExpectedST2;
- std::string Count1 = Has1 ? llvm::utostr(ExpectedST1->getNameCount())
- : "(string table not present)";
- std::string Count2 = Has2 ? llvm::utostr(ExpectedST2->getNameCount())
- : "(string table not present)";
- D.print("Number of Strings", Count1, Count2);
-
- if (!Has1 || !Has2) {
- consumeError(ExpectedST1.takeError());
- consumeError(ExpectedST2.takeError());
- return Error::success();
- }
-
- auto &ST1 = *ExpectedST1;
- auto &ST2 = *ExpectedST2;
-
- D.print("Hash Version", ST1.getHashVersion(), ST2.getHashVersion());
- D.print("Byte Size", ST1.getByteSize(), ST2.getByteSize());
- D.print("Signature", ST1.getSignature(), ST2.getSignature());
-
- // Both have a valid string table, dive in and compare individual strings.
-
- auto IdList1 = ST1.name_ids();
- auto IdList2 = ST2.name_ids();
- StringSet<> LS;
- StringSet<> RS;
- uint32_t Empty1 = 0;
- uint32_t Empty2 = 0;
- for (auto ID : IdList1) {
- auto S = ST1.getStringForID(ID);
- if (!S)
- return S.takeError();
- if (S->empty())
- ++Empty1;
- else
- LS.insert(*S);
- }
- for (auto ID : IdList2) {
- auto S = ST2.getStringForID(ID);
- if (!S)
- return S.takeError();
- if (S->empty())
- ++Empty2;
- else
- RS.insert(*S);
- }
- D.print("Empty Strings", Empty1, Empty2);
-
- for (const auto &S : LS) {
- auto R = RS.find(S.getKey());
- std::string Truncated = truncateStringMiddle(S.getKey(), 28);
- uint32_t I = cantFail(ST1.getIDForString(S.getKey()));
- if (R == RS.end()) {
- D.printExplicit(Truncated, DiffResult::DIFFERENT, I, "(not present)");
- continue;
- }
-
- uint32_t J = cantFail(ST2.getIDForString(R->getKey()));
- D.print<EquivalentDiffProvider>(Truncated, I, J);
- RS.erase(R);
- }
-
- for (const auto &S : RS) {
- auto L = LS.find(S.getKey());
- std::string Truncated = truncateStringMiddle(S.getKey(), 28);
- uint32_t J = cantFail(ST2.getIDForString(S.getKey()));
- if (L == LS.end()) {
- D.printExplicit(Truncated, DiffResult::DIFFERENT, "(not present)", J);
- continue;
- }
-
- uint32_t I = cantFail(ST1.getIDForString(L->getKey()));
- D.print<EquivalentDiffProvider>(Truncated, I, J);
- }
- return Error::success();
-}
-
-Error DiffStyle::diffFreePageMap() { return Error::success(); }
-
-Error DiffStyle::diffInfoStream() {
- DiffPrinter D(2, "PDB Stream", 22, 40, opts::diff::PrintResultColumn,
- opts::diff::PrintValueColumns, outs());
- D.printExplicit("File", DiffResult::UNSPECIFIED,
- truncateStringFront(File1.getFilePath(), 38),
- truncateStringFront(File2.getFilePath(), 38));
-
- auto ExpectedInfo1 = File1.getPDBInfoStream();
- auto ExpectedInfo2 = File2.getPDBInfoStream();
-
- bool Has1 = !!ExpectedInfo1;
- bool Has2 = !!ExpectedInfo2;
- if (!(Has1 && Has2)) {
- std::string L = Has1 ? "(present)" : "(not present)";
- std::string R = Has2 ? "(present)" : "(not present)";
- D.print("Stream", L, R);
-
- consumeError(ExpectedInfo1.takeError());
- consumeError(ExpectedInfo2.takeError());
- return Error::success();
- }
-
- auto &IS1 = *ExpectedInfo1;
- auto &IS2 = *ExpectedInfo2;
- D.print("Stream Size", IS1.getStreamSize(), IS2.getStreamSize());
- D.print("Age", IS1.getAge(), IS2.getAge());
- D.print("Guid", IS1.getGuid(), IS2.getGuid());
- D.print("Signature", IS1.getSignature(), IS2.getSignature());
- D.print("Version", IS1.getVersion(), IS2.getVersion());
- D.diffUnorderedArray("Feature", IS1.getFeatureSignatures(),
- IS2.getFeatureSignatures());
- D.print("Named Stream Size", IS1.getNamedStreamMapByteSize(),
- IS2.getNamedStreamMapByteSize());
- StringMap<uint32_t> NSL = IS1.getNamedStreams().getStringMap();
- StringMap<uint32_t> NSR = IS2.getNamedStreams().getStringMap();
- D.diffUnorderedMap<EquivalentDiffProvider>("Named Stream", NSL, NSR);
- return Error::success();
-}
-
-typedef std::pair<uint32_t, DbiModuleDescriptor> IndexedModuleDescriptor;
-typedef std::vector<IndexedModuleDescriptor> IndexedModuleDescriptorList;
-
-static IndexedModuleDescriptorList
-getModuleDescriptors(const DbiModuleList &ML) {
- IndexedModuleDescriptorList List;
- List.reserve(ML.getModuleCount());
- for (uint32_t I = 0; I < ML.getModuleCount(); ++I)
- List.emplace_back(I, ML.getModuleDescriptor(I));
- return List;
-}
-
-static IndexedModuleDescriptorList::iterator
-findOverrideEquivalentModule(uint32_t Modi,
- IndexedModuleDescriptorList &OtherList) {
- auto &EqMap = opts::diff::Equivalences;
-
- auto Iter = EqMap.find(Modi);
- if (Iter == EqMap.end())
- return OtherList.end();
-
- uint32_t EqValue = Iter->second;
-
- return llvm::find_if(OtherList,
- [EqValue](const IndexedModuleDescriptor &Desc) {
- return Desc.first == EqValue;
- });
-}
-
-static IndexedModuleDescriptorList::iterator
-findEquivalentModule(const IndexedModuleDescriptor &Item,
- IndexedModuleDescriptorList &OtherList, bool ItemIsRight) {
-
- if (!ItemIsRight) {
- uint32_t Modi = Item.first;
- auto OverrideIter = findOverrideEquivalentModule(Modi, OtherList);
- if (OverrideIter != OtherList.end())
- return OverrideIter;
- }
-
- BinaryPathProvider PathProvider(28);
-
- auto Iter = OtherList.begin();
- auto End = OtherList.end();
- for (; Iter != End; ++Iter) {
- const IndexedModuleDescriptor *Left = &Item;
- const IndexedModuleDescriptor *Right = &*Iter;
- if (ItemIsRight)
- std::swap(Left, Right);
- DiffResult Result = PathProvider.compare(Left->second.getModuleName(),
- Right->second.getModuleName());
- if (Result == DiffResult::EQUIVALENT || Result == DiffResult::IDENTICAL)
- return Iter;
- }
- return OtherList.end();
-}
-
-static void diffOneModule(DiffPrinter &D, const IndexedModuleDescriptor &Item,
- IndexedModuleDescriptorList &Other,
- bool ItemIsRight) {
- StreamPurposeProvider HeaderProvider(70);
- StreamInfo Info = StreamInfo::createModuleStream(
- Item.second.getModuleName(), Item.second.getModuleStreamIndex(),
- Item.first);
- D.printFullRow(HeaderProvider.format(Info, ItemIsRight));
-
- const auto *L = &Item;
-
- auto Iter = findEquivalentModule(Item, Other, ItemIsRight);
- if (Iter == Other.end()) {
- // We didn't find this module at all on the other side. Just print one row
- // and continue.
- if (ItemIsRight)
- D.print<ModiProvider>("- Modi", None, Item.first);
- else
- D.print<ModiProvider>("- Modi", Item.first, None);
- return;
- }
-
- // We did find this module. Go through and compare each field.
- const auto *R = &*Iter;
- if (ItemIsRight)
- std::swap(L, R);
-
- BinaryPathProvider PathProvider(28);
- D.print<ModiProvider>("- Modi", L->first, R->first);
- D.print<BinaryPathProvider>("- Obj File Name", L->second.getObjFileName(),
- R->second.getObjFileName(), PathProvider);
- D.print<StreamNumberProvider>("- Debug Stream",
- L->second.getModuleStreamIndex(),
- R->second.getModuleStreamIndex());
- D.print("- C11 Byte Size", L->second.getC11LineInfoByteSize(),
- R->second.getC11LineInfoByteSize());
- D.print("- C13 Byte Size", L->second.getC13LineInfoByteSize(),
- R->second.getC13LineInfoByteSize());
- D.print("- # of files", L->second.getNumberOfFiles(),
- R->second.getNumberOfFiles());
- D.print("- Pdb File Path Index", L->second.getPdbFilePathNameIndex(),
- R->second.getPdbFilePathNameIndex());
- D.print("- Source File Name Index", L->second.getSourceFileNameIndex(),
- R->second.getSourceFileNameIndex());
- D.print("- Symbol Byte Size", L->second.getSymbolDebugInfoByteSize(),
- R->second.getSymbolDebugInfoByteSize());
- Other.erase(Iter);
-}
-
-Error DiffStyle::diffDbiStream() {
- DiffPrinter D(2, "DBI Stream", 40, 30, opts::diff::PrintResultColumn,
- opts::diff::PrintValueColumns, outs());
- D.printExplicit("File", DiffResult::UNSPECIFIED,
- truncateStringFront(File1.getFilePath(), 28),
- truncateStringFront(File2.getFilePath(), 28));
-
- auto ExpectedDbi1 = File1.getPDBDbiStream();
- auto ExpectedDbi2 = File2.getPDBDbiStream();
-
- bool Has1 = !!ExpectedDbi1;
- bool Has2 = !!ExpectedDbi2;
- if (!(Has1 && Has2)) {
- std::string L = Has1 ? "(present)" : "(not present)";
- std::string R = Has2 ? "(present)" : "(not present)";
- D.print("Stream", L, R);
-
- consumeError(ExpectedDbi1.takeError());
- consumeError(ExpectedDbi2.takeError());
- return Error::success();
- }
-
- auto &DL = *ExpectedDbi1;
- auto &DR = *ExpectedDbi2;
-
- D.print("Dbi Version", (uint32_t)DL.getDbiVersion(),
- (uint32_t)DR.getDbiVersion());
- D.print("Age", DL.getAge(), DR.getAge());
- D.print("Machine", (uint16_t)DL.getMachineType(),
- (uint16_t)DR.getMachineType());
- D.print("Flags", DL.getFlags(), DR.getFlags());
- D.print("Build Major", DL.getBuildMajorVersion(), DR.getBuildMajorVersion());
- D.print("Build Minor", DL.getBuildMinorVersion(), DR.getBuildMinorVersion());
- D.print("Build Number", DL.getBuildNumber(), DR.getBuildNumber());
- D.print("PDB DLL Version", DL.getPdbDllVersion(), DR.getPdbDllVersion());
- D.print("PDB DLL RBLD", DL.getPdbDllRbld(), DR.getPdbDllRbld());
- D.print<StreamNumberProvider>("DBG (FPO)",
- DL.getDebugStreamIndex(DbgHeaderType::FPO),
- DR.getDebugStreamIndex(DbgHeaderType::FPO));
- D.print<StreamNumberProvider>(
- "DBG (Exception)", DL.getDebugStreamIndex(DbgHeaderType::Exception),
- DR.getDebugStreamIndex(DbgHeaderType::Exception));
- D.print<StreamNumberProvider>("DBG (Fixup)",
- DL.getDebugStreamIndex(DbgHeaderType::Fixup),
- DR.getDebugStreamIndex(DbgHeaderType::Fixup));
- D.print<StreamNumberProvider>(
- "DBG (OmapToSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapToSrc),
- DR.getDebugStreamIndex(DbgHeaderType::OmapToSrc));
- D.print<StreamNumberProvider>(
- "DBG (OmapFromSrc)", DL.getDebugStreamIndex(DbgHeaderType::OmapFromSrc),
- DR.getDebugStreamIndex(DbgHeaderType::OmapFromSrc));
- D.print<StreamNumberProvider>(
- "DBG (SectionHdr)", DL.getDebugStreamIndex(DbgHeaderType::SectionHdr),
- DR.getDebugStreamIndex(DbgHeaderType::SectionHdr));
- D.print<StreamNumberProvider>(
- "DBG (TokenRidMap)", DL.getDebugStreamIndex(DbgHeaderType::TokenRidMap),
- DR.getDebugStreamIndex(DbgHeaderType::TokenRidMap));
- D.print<StreamNumberProvider>("DBG (Xdata)",
- DL.getDebugStreamIndex(DbgHeaderType::Xdata),
- DR.getDebugStreamIndex(DbgHeaderType::Xdata));
- D.print<StreamNumberProvider>("DBG (Pdata)",
- DL.getDebugStreamIndex(DbgHeaderType::Pdata),
- DR.getDebugStreamIndex(DbgHeaderType::Pdata));
- D.print<StreamNumberProvider>("DBG (NewFPO)",
- DL.getDebugStreamIndex(DbgHeaderType::NewFPO),
- DR.getDebugStreamIndex(DbgHeaderType::NewFPO));
- D.print<StreamNumberProvider>(
- "DBG (SectionHdrOrig)",
- DL.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig),
- DR.getDebugStreamIndex(DbgHeaderType::SectionHdrOrig));
- D.print<StreamNumberProvider>("Globals Stream",
- DL.getGlobalSymbolStreamIndex(),
- DR.getGlobalSymbolStreamIndex());
- D.print<StreamNumberProvider>("Publics Stream",
- DL.getPublicSymbolStreamIndex(),
- DR.getPublicSymbolStreamIndex());
- D.print<StreamNumberProvider>("Symbol Records", DL.getSymRecordStreamIndex(),
- DR.getSymRecordStreamIndex());
- D.print("Has CTypes", DL.hasCTypes(), DR.hasCTypes());
- D.print("Is Incrementally Linked", DL.isIncrementallyLinked(),
- DR.isIncrementallyLinked());
- D.print("Is Stripped", DL.isStripped(), DR.isStripped());
- const DbiModuleList &ML = DL.modules();
- const DbiModuleList &MR = DR.modules();
- D.print("Module Count", ML.getModuleCount(), MR.getModuleCount());
- D.print("Source File Count", ML.getSourceFileCount(),
- MR.getSourceFileCount());
- auto MDL = getModuleDescriptors(ML);
- auto MDR = getModuleDescriptors(MR);
- // Scan all module descriptors from the left, and look for corresponding
- // module descriptors on the right.
- for (const auto &L : MDL)
- diffOneModule(D, L, MDR, false);
-
- for (const auto &R : MDR)
- diffOneModule(D, R, MDL, true);
-
- return Error::success();
-}
-
-Error DiffStyle::diffSectionContribs() { return Error::success(); }
-
-Error DiffStyle::diffSectionMap() { return Error::success(); }
-
-Error DiffStyle::diffFpoStream() { return Error::success(); }
-
-Error DiffStyle::diffTpiStream(int Index) { return Error::success(); }
-
-Error DiffStyle::diffModuleInfoStream(int Index) { return Error::success(); }
-
-Error DiffStyle::diffPublics() { return Error::success(); }
-
-Error DiffStyle::diffGlobals() { return Error::success(); }
diff --git a/contrib/llvm/tools/llvm-pdbutil/Diff.h b/contrib/llvm/tools/llvm-pdbutil/Diff.h
deleted file mode 100644
index 6037576e21bb..000000000000
--- a/contrib/llvm/tools/llvm-pdbutil/Diff.h
+++ /dev/null
@@ -1,45 +0,0 @@
-//===- Diff.h - PDB diff utility --------------------------------*- C++ -*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVMPDBDUMP_DIFF_H
-#define LLVM_TOOLS_LLVMPDBDUMP_DIFF_H
-
-#include "OutputStyle.h"
-
-namespace llvm {
-namespace pdb {
-class PDBFile;
-class DiffStyle : public OutputStyle {
-public:
- explicit DiffStyle(PDBFile &File1, PDBFile &File2);
-
- Error dump() override;
-
-private:
- Error diffSuperBlock();
- Error diffStreamDirectory();
- Error diffStringTable();
- Error diffFreePageMap();
- Error diffInfoStream();
- Error diffDbiStream();
- Error diffSectionContribs();
- Error diffSectionMap();
- Error diffFpoStream();
- Error diffTpiStream(int Index);
- Error diffModuleInfoStream(int Index);
- Error diffPublics();
- Error diffGlobals();
-
- PDBFile &File1;
- PDBFile &File2;
-};
-}
-}
-
-#endif
diff --git a/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp b/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp
deleted file mode 100644
index dd61cc182593..000000000000
--- a/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.cpp
+++ /dev/null
@@ -1,147 +0,0 @@
-
-#include "DiffPrinter.h"
-
-#include "llvm/Support/FormatAdapters.h"
-
-using namespace llvm;
-using namespace llvm::pdb;
-
-namespace {
-struct Colorize {
- Colorize(raw_ostream &OS, DiffResult Result) : OS(OS) {
- if (!OS.has_colors())
- return;
- switch (Result) {
- case DiffResult::IDENTICAL:
- OS.changeColor(raw_ostream::Colors::GREEN, false);
- break;
- case DiffResult::EQUIVALENT:
- OS.changeColor(raw_ostream::Colors::YELLOW, true);
- break;
- default:
- OS.changeColor(raw_ostream::Colors::RED, false);
- break;
- }
- }
-
- ~Colorize() {
- if (OS.has_colors())
- OS.resetColor();
- }
-
- raw_ostream &OS;
-};
-}
-
-DiffPrinter::DiffPrinter(uint32_t Indent, StringRef Header,
- uint32_t PropertyWidth, uint32_t FieldWidth,
- bool Result, bool Fields, raw_ostream &Stream)
- : PrintResult(Result), PrintValues(Fields), Indent(Indent),
- PropertyWidth(PropertyWidth), FieldWidth(FieldWidth), OS(Stream) {
- printHeaderRow();
- printFullRow(Header);
-}
-
-DiffPrinter::~DiffPrinter() {}
-
-uint32_t DiffPrinter::tableWidth() const {
- // `|`
- uint32_t W = 1;
-
- // `<width>|`
- W += PropertyWidth + 1;
-
- if (PrintResult) {
- // ` I |`
- W += 4;
- }
-
- if (PrintValues) {
- // `<width>|<width>|`
- W += 2 * (FieldWidth + 1);
- }
- return W;
-}
-
-void DiffPrinter::printFullRow(StringRef Text) {
- newLine();
- printValue(Text, DiffResult::UNSPECIFIED, AlignStyle::Center,
- tableWidth() - 2, true);
- printSeparatorRow();
-}
-
-void DiffPrinter::printSeparatorRow() {
- newLine();
- OS << formatv("{0}", fmt_repeat('-', PropertyWidth));
- if (PrintResult) {
- OS << '+';
- OS << formatv("{0}", fmt_repeat('-', 3));
- }
- if (PrintValues) {
- OS << '+';
- OS << formatv("{0}", fmt_repeat('-', FieldWidth));
- OS << '+';
- OS << formatv("{0}", fmt_repeat('-', FieldWidth));
- }
- OS << '|';
-}
-
-void DiffPrinter::printHeaderRow() {
- newLine('-');
- OS << formatv("{0}", fmt_repeat('-', tableWidth() - 1));
-}
-
-void DiffPrinter::newLine(char InitialChar) {
- OS << "\n";
- OS.indent(Indent) << InitialChar;
-}
-
-void DiffPrinter::printExplicit(StringRef Property, DiffResult C,
- StringRef Left, StringRef Right) {
- newLine();
- printValue(Property, DiffResult::UNSPECIFIED, AlignStyle::Right,
- PropertyWidth, true);
- printResult(C);
- printValue(Left, C, AlignStyle::Center, FieldWidth, false);
- printValue(Right, C, AlignStyle::Center, FieldWidth, false);
- printSeparatorRow();
-}
-
-void DiffPrinter::printResult(DiffResult Result) {
- if (!PrintResult)
- return;
- switch (Result) {
- case DiffResult::DIFFERENT:
- printValue("D", Result, AlignStyle::Center, 3, true);
- break;
- case DiffResult::EQUIVALENT:
- printValue("E", Result, AlignStyle::Center, 3, true);
- break;
- case DiffResult::IDENTICAL:
- printValue("I", Result, AlignStyle::Center, 3, true);
- break;
- case DiffResult::UNSPECIFIED:
- printValue(" ", Result, AlignStyle::Center, 3, true);
- break;
- }
-}
-
-void DiffPrinter::printValue(StringRef Value, DiffResult C, AlignStyle Style,
- uint32_t Width, bool Force) {
- if (!Force && !PrintValues)
- return;
-
- if (Style == AlignStyle::Right)
- --Width;
-
- std::string FormattedItem =
- formatv("{0}", fmt_align(Value, Style, Width)).str();
- if (C != DiffResult::UNSPECIFIED) {
- Colorize Color(OS, C);
- OS << FormattedItem;
- } else
- OS << FormattedItem;
- if (Style == AlignStyle::Right)
- OS << ' ';
- OS << '|';
-}
diff --git a/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h b/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h
deleted file mode 100644
index 475747d8dc11..000000000000
--- a/contrib/llvm/tools/llvm-pdbutil/DiffPrinter.h
+++ /dev/null
@@ -1,172 +0,0 @@
-//===- DiffPrinter.h ------------------------------------------ *- C++ --*-===//
-//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H
-#define LLVM_TOOLS_LLVMPDBDUMP_DIFFPRINTER_H
-
-#include "llvm/ADT/ArrayRef.h"
-#include "llvm/ADT/StringMap.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/DebugInfo/PDB/Native/RawConstants.h"
-#include "llvm/Support/FormatVariadic.h"
-#include "llvm/Support/raw_ostream.h"
-
-#include <list>
-#include <unordered_set>
-
-namespace std {
-template <> struct hash<llvm::pdb::PdbRaw_FeatureSig> {
- typedef llvm::pdb::PdbRaw_FeatureSig argument_type;
- typedef std::size_t result_type;
- result_type operator()(argument_type Item) const {
- return std::hash<uint32_t>{}(uint32_t(Item));
- }
-};
-} // namespace std
-
-namespace llvm {
-namespace pdb {
-
-class PDBFile;
-
-enum class DiffResult { UNSPECIFIED, IDENTICAL, EQUIVALENT, DIFFERENT };
-
-struct IdenticalDiffProvider {
- template <typename T, typename U>
- DiffResult compare(const T &Left, const U &Right) {
- return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::DIFFERENT;
- }
-
- template <typename T> std::string format(const T &Item, bool Right) {
- return formatv("{0}", Item).str();
- }
-};
-
-struct EquivalentDiffProvider {
- template <typename T, typename U>
- DiffResult compare(const T &Left, const U &Right) {
- return (Left == Right) ? DiffResult::IDENTICAL : DiffResult::EQUIVALENT;
- }
-
- template <typename T> std::string format(const T &Item, bool Right) {
- return formatv("{0}", Item).str();
- }
-};
-
-class DiffPrinter {
-public:
- DiffPrinter(uint32_t Indent, StringRef Header, uint32_t PropertyWidth,
- uint32_t FieldWidth, bool Result, bool Values,
- raw_ostream &Stream);
- ~DiffPrinter();
-
- template <typename T, typename U> struct Identical {};
-
- template <typename Provider = IdenticalDiffProvider, typename T, typename U>
- void print(StringRef Property, const T &Left, const U &Right,
- Provider P = Provider()) {
- std::string L = P.format(Left, false);
- std::string R = P.format(Right, true);
-
- DiffResult Result = P.compare(Left, Right);
- printExplicit(Property, Result, L, R);
- }
-
- void printExplicit(StringRef Property, DiffResult C, StringRef Left,
- StringRef Right);
-
- template <typename T, typename U>
- void printExplicit(StringRef Property, DiffResult C, const T &Left,
- const U &Right) {
- std::string L = formatv("{0}", Left).str();
- std::string R = formatv("{0}", Right).str();
- printExplicit(Property, C, StringRef(L), StringRef(R));
- }
-
- template <typename T, typename U>
- void diffUnorderedArray(StringRef Property, ArrayRef<T> Left,
- ArrayRef<U> Right) {
- std::unordered_set<T> LS(Left.begin(), Left.end());
- std::unordered_set<U> RS(Right.begin(), Right.end());
- std::string Count1 = formatv("{0} element(s)", Left.size());
- std::string Count2 = formatv("{0} element(s)", Right.size());
- print(std::string(Property) + "s (set)", Count1, Count2);
- for (const auto &L : LS) {
- auto Iter = RS.find(L);
- std::string Text = formatv("{0}", L).str();
- if (Iter == RS.end()) {
- print(Property, Text, "(not present)");
- continue;
- }
- print(Property, Text, Text);
- RS.erase(Iter);
- }
- for (const auto &R : RS) {
- auto Iter = LS.find(R);
- std::string Text = formatv("{0}", R).str();
- if (Iter == LS.end()) {
- print(Property, "(not present)", Text);
- continue;
- }
- print(Property, Text, Text);
- }
- }
-
- template <typename ValueProvider = IdenticalDiffProvider, typename T,
- typename U>
- void diffUnorderedMap(StringRef Property, const StringMap<T> &Left,
- const StringMap<U> &Right,
- ValueProvider P = ValueProvider()) {
- StringMap<U> RightCopy(Right);
-
- std::string Count1 = formatv("{0} element(s)", Left.size());
- std::string Count2 = formatv("{0} element(s)", Right.size());
- print(std::string(Property) + "s (map)", Count1, Count2);
-
- for (const auto &L : Left) {
- auto Iter = RightCopy.find(L.getKey());
- if (Iter == RightCopy.end()) {
- printExplicit(L.getKey(), DiffResult::DIFFERENT, L.getValue(),
- "(not present)");
- continue;
- }
-
- print(L.getKey(), L.getValue(), Iter->getValue(), P);
- RightCopy.erase(Iter);
- }
-
- for (const auto &R : RightCopy) {
- printExplicit(R.getKey(), DiffResult::DIFFERENT, "(not present)",
- R.getValue());
- }
- }
-
- void printFullRow(StringRef Text);
-
-private:
- uint32_t tableWidth() const;
-
- void printHeaderRow();
- void printSeparatorRow();
- void newLine(char InitialChar = '|');
- void printValue(StringRef Value, DiffResult C, AlignStyle Style,
- uint32_t Width, bool Force);
- void printResult(DiffResult Result);
-
- bool PrintResult;
- bool PrintValues;
- uint32_t Indent;
- uint32_t PropertyWidth;
- uint32_t FieldWidth;
- raw_ostream &OS;
-};
-} // namespace pdb
-} // namespace llvm
-
-#endif
diff --git a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
index dd8436728baf..9e59adc71967 100644
--- a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.cpp
@@ -90,7 +90,13 @@ Error DumpOutputStyle::dump() {
P.NewLine();
}
- if (opts::dump::DumpStringTable) {
+ if (opts::dump::DumpNamedStreams) {
+ if (auto EC = dumpNamedStreams())
+ return EC;
+ P.NewLine();
+ }
+
+ if (opts::dump::DumpStringTable || opts::dump::DumpStringTableDetails) {
if (auto EC = dumpStringTable())
return EC;
P.NewLine();
@@ -145,6 +151,11 @@ Error DumpOutputStyle::dump() {
}
}
+ if (opts::dump::DumpGSIRecords) {
+ if (auto EC = dumpGSIRecords())
+ return EC;
+ }
+
if (opts::dump::DumpGlobals) {
if (auto EC = dumpGlobals())
return EC;
@@ -434,6 +445,86 @@ static void iterateModuleSubsections(
});
}
+static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
+ ArrayRef<llvm::object::coff_section>>>
+loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {
+ if (!File.hasPDBDbiStream())
+ return make_error<StringError>(
+ "Section headers require a DBI Stream, which could not be loaded",
+ inconvertibleErrorCode());
+
+ auto &Dbi = cantFail(File.getPDBDbiStream());
+ uint32_t SI = Dbi.getDebugStreamIndex(Type);
+
+ if (SI == kInvalidStreamIndex)
+ return make_error<StringError>(
+ "PDB does not contain the requested image section header type",
+ inconvertibleErrorCode());
+
+ auto Stream = File.createIndexedStream(SI);
+ if (!Stream)
+ return make_error<StringError>("Could not load the required stream data",
+ inconvertibleErrorCode());
+
+ ArrayRef<object::coff_section> Headers;
+ if (Stream->getLength() % sizeof(object::coff_section) != 0)
+ return make_error<StringError>(
+ "Section header array size is not a multiple of section header size",
+ inconvertibleErrorCode());
+
+ uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);
+ BinaryStreamReader Reader(*Stream);
+ cantFail(Reader.readArray(Headers, NumHeaders));
+ return std::make_pair(std::move(Stream), Headers);
+}
+
+static std::vector<std::string> getSectionNames(PDBFile &File) {
+ auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);
+ if (!ExpectedHeaders)
+ return {};
+
+ std::unique_ptr<MappedBlockStream> Stream;
+ ArrayRef<object::coff_section> Headers;
+ std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
+ std::vector<std::string> Names;
+ for (const auto &H : Headers)
+ Names.push_back(H.Name);
+ return Names;
+}
+
+static void dumpSectionContrib(LinePrinter &P, const SectionContrib &SC,
+ ArrayRef<std::string> SectionNames,
+ uint32_t FieldWidth) {
+ std::string NameInsert;
+ if (SC.ISect > 0 && SC.ISect <= SectionNames.size()) {
+ StringRef SectionName = SectionNames[SC.ISect - 1];
+ NameInsert = formatv("[{0}]", SectionName).str();
+ } else
+ NameInsert = "[???]";
+ P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
+ "crc = {4}",
+ formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),
+ fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),
+ fmt_align(NameInsert, AlignStyle::Left, FieldWidth + 2));
+ AutoIndent Indent(P, FieldWidth + 2);
+ P.formatLine(" {0}",
+ formatSectionCharacteristics(P.getIndentLevel() + 6,
+ SC.Characteristics, 3, " | "));
+}
+
+static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC,
+ ArrayRef<std::string> SectionNames,
+ uint32_t FieldWidth) {
+ P.formatLine("SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
+ "crc = {4}, coff section = {5}",
+ formatSegmentOffset(SC.Base.ISect, SC.Base.Off),
+ fmtle(SC.Base.Size), fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc),
+ fmtle(SC.Base.RelocCrc), fmtle(SC.ISectCoff));
+ P.formatLine(" {0}",
+ formatSectionCharacteristics(P.getIndentLevel() + 6,
+ SC.Base.Characteristics, 3, " | "));
+}
+
Error DumpOutputStyle::dumpModules() {
printHeader(P, "Modules");
AutoIndent Indent(P);
@@ -456,6 +547,10 @@ Error DumpOutputStyle::dumpModules() {
iterateSymbolGroups(
File, PrintScope{P, 11}, [&](uint32_t Modi, const SymbolGroup &Strings) {
auto Desc = Modules.getModuleDescriptor(Modi);
+ if (opts::dump::DumpSectionContribs) {
+ std::vector<std::string> Sections = getSectionNames(getPdb());
+ dumpSectionContrib(P, Desc.getSectionContrib(), Sections, 0);
+ }
P.formatLine("Obj: `{0}`: ", Desc.getObjFileName());
P.formatLine("debug stream: {0}, # files: {1}, has ec info: {2}",
Desc.getModuleStreamIndex(), Desc.getNumberOfFiles(),
@@ -848,14 +943,7 @@ Error DumpOutputStyle::dumpXme() {
return Error::success();
}
-Error DumpOutputStyle::dumpStringTable() {
- printHeader(P, "String Table");
-
- if (File.isObj()) {
- P.formatLine("Dumping string table is not supported for object files");
- return Error::success();
- }
-
+Error DumpOutputStyle::dumpStringTableFromPdb() {
AutoIndent Indent(P);
auto IS = getPdb().getStringTable();
if (!IS) {
@@ -864,37 +952,121 @@ Error DumpOutputStyle::dumpStringTable() {
return Error::success();
}
- if (IS->name_ids().empty()) {
- P.formatLine("Empty");
- return Error::success();
+ if (opts::dump::DumpStringTable) {
+ if (IS->name_ids().empty())
+ P.formatLine("Empty");
+ else {
+ auto MaxID =
+ std::max_element(IS->name_ids().begin(), IS->name_ids().end());
+ uint32_t Digits = NumDigits(*MaxID);
+
+ P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
+ "String");
+
+ std::vector<uint32_t> SortedIDs(IS->name_ids().begin(),
+ IS->name_ids().end());
+ llvm::sort(SortedIDs.begin(), SortedIDs.end());
+ for (uint32_t I : SortedIDs) {
+ auto ES = IS->getStringForID(I);
+ llvm::SmallString<32> Str;
+ if (!ES) {
+ consumeError(ES.takeError());
+ Str = "Error reading string";
+ } else if (!ES->empty()) {
+ Str.append("'");
+ Str.append(*ES);
+ Str.append("'");
+ }
+
+ if (!Str.empty())
+ P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits),
+ Str);
+ }
+ }
}
- auto MaxID = std::max_element(IS->name_ids().begin(), IS->name_ids().end());
- uint32_t Digits = NumDigits(*MaxID);
-
- P.formatLine("{0} | {1}", fmt_align("ID", AlignStyle::Right, Digits),
- "String");
-
- std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end());
- std::sort(SortedIDs.begin(), SortedIDs.end());
- for (uint32_t I : SortedIDs) {
- auto ES = IS->getStringForID(I);
- llvm::SmallString<32> Str;
- if (!ES) {
- consumeError(ES.takeError());
- Str = "Error reading string";
- } else if (!ES->empty()) {
- Str.append("'");
- Str.append(*ES);
- Str.append("'");
+ if (opts::dump::DumpStringTableDetails) {
+ P.NewLine();
+ {
+ P.printLine("String Table Header:");
+ AutoIndent Indent(P);
+ P.formatLine("Signature: {0}", IS->getSignature());
+ P.formatLine("Hash Version: {0}", IS->getHashVersion());
+ P.formatLine("Name Buffer Size: {0}", IS->getByteSize());
+ P.NewLine();
}
- if (!Str.empty())
- P.formatLine("{0} | {1}", fmt_align(I, AlignStyle::Right, Digits), Str);
+ BinaryStreamRef NameBuffer = IS->getStringTable().getBuffer();
+ ArrayRef<uint8_t> Contents;
+ cantFail(NameBuffer.readBytes(0, NameBuffer.getLength(), Contents));
+ P.formatBinary("Name Buffer", Contents, 0);
+ P.NewLine();
+ {
+ P.printLine("Hash Table:");
+ AutoIndent Indent(P);
+ P.formatLine("Bucket Count: {0}", IS->name_ids().size());
+ for (const auto &Entry : enumerate(IS->name_ids()))
+ P.formatLine("Bucket[{0}] : {1}", Entry.index(),
+ uint32_t(Entry.value()));
+ P.formatLine("Name Count: {0}", IS->getNameCount());
+ }
}
return Error::success();
}
+Error DumpOutputStyle::dumpStringTableFromObj() {
+ iterateModuleSubsections<DebugStringTableSubsectionRef>(
+ File, PrintScope{P, 4},
+ [&](uint32_t Modi, const SymbolGroup &Strings,
+ DebugStringTableSubsectionRef &Strings2) {
+ BinaryStreamRef StringTableBuffer = Strings2.getBuffer();
+ BinaryStreamReader Reader(StringTableBuffer);
+ while (Reader.bytesRemaining() > 0) {
+ StringRef Str;
+ uint32_t Offset = Reader.getOffset();
+ cantFail(Reader.readCString(Str));
+ if (Str.empty())
+ continue;
+
+ P.formatLine("{0} | {1}", fmt_align(Offset, AlignStyle::Right, 4),
+ Str);
+ }
+ });
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpNamedStreams() {
+ printHeader(P, "Named Streams");
+ AutoIndent Indent(P, 2);
+
+ if (File.isObj()) {
+ P.formatLine("Dumping Named Streams is only supported for PDB files.");
+ return Error::success();
+ }
+ ExitOnError Err("Invalid PDB File: ");
+
+ auto &IS = Err(File.pdb().getPDBInfoStream());
+ const NamedStreamMap &NS = IS.getNamedStreams();
+ for (const auto &Entry : NS.entries()) {
+ P.printLine(Entry.getKey());
+ AutoIndent Indent2(P, 2);
+ P.formatLine("Index: {0}", Entry.getValue());
+ P.formatLine("Size in bytes: {0}",
+ File.pdb().getStreamByteSize(Entry.getValue()));
+ }
+
+ return Error::success();
+}
+
+Error DumpOutputStyle::dumpStringTable() {
+ printHeader(P, "String Table");
+
+ if (File.isPdb())
+ return dumpStringTableFromPdb();
+
+ return dumpStringTableFromObj();
+}
+
static void buildDepSet(LazyRandomTypeCollection &Types,
ArrayRef<TypeIndex> Indices,
std::map<TypeIndex, CVType> &DepSet) {
@@ -975,8 +1147,15 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() {
if (auto EC = S.getName(SectionName))
return errorCodeToError(EC);
- if (SectionName != ".debug$T")
+ // .debug$T is a standard CodeView type section, while .debug$P is the same
+ // format but used for MSVC precompiled header object files.
+ if (SectionName == ".debug$T")
+ printHeader(P, "Types (.debug$T)");
+ else if (SectionName == ".debug$P")
+ printHeader(P, "Precompiled Types (.debug$P)");
+ else
continue;
+
StringRef Contents;
if (auto EC = S.getContents(Contents))
return errorCodeToError(EC);
@@ -1124,6 +1303,7 @@ Error DumpOutputStyle::dumpModuleSymsForObj() {
File, PrintScope{P, 2},
[&](uint32_t Modi, const SymbolGroup &Strings,
DebugSymbolsSubsectionRef &Symbols) {
+ Dumper.setSymbolGroup(&Strings);
for (auto Symbol : Symbols) {
if (auto EC = Visitor.visitSymbolRecord(Symbol)) {
SymbolError = llvm::make_unique<Error>(std::move(EC));
@@ -1165,8 +1345,8 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() {
SymbolVisitorCallbackPipeline Pipeline;
SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
- MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids,
- Types);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Strings,
+ Ids, Types);
Pipeline.addCallbackToPipeline(Deserializer);
Pipeline.addCallbackToPipeline(Dumper);
@@ -1182,6 +1362,39 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() {
return Error::success();
}
+Error DumpOutputStyle::dumpGSIRecords() {
+ printHeader(P, "GSI Records");
+ AutoIndent Indent(P);
+
+ if (File.isObj()) {
+ P.formatLine("Dumping Globals is not supported for object files");
+ return Error::success();
+ }
+
+ if (!getPdb().hasPDBSymbolStream()) {
+ P.formatLine("GSI Common Symbol Stream not present");
+ return Error::success();
+ }
+
+ auto &Records = cantFail(getPdb().getPDBSymbolStream());
+ auto &Types = File.types();
+ auto &Ids = File.ids();
+
+ P.printLine("Records");
+ SymbolVisitorCallbackPipeline Pipeline;
+ SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb);
+ MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types);
+
+ Pipeline.addCallbackToPipeline(Deserializer);
+ Pipeline.addCallbackToPipeline(Dumper);
+ CVSymbolVisitor Visitor(Pipeline);
+
+ BinaryStreamRef SymStream = Records.getSymbolArray().getUnderlyingStream();
+ if (auto E = Visitor.visitSymbolStream(Records.getSymbolArray(), 0))
+ return E;
+ return Error::success();
+}
+
Error DumpOutputStyle::dumpGlobals() {
printHeader(P, "Global Symbols");
AutoIndent Indent(P);
@@ -1287,6 +1500,7 @@ Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
Pipeline.addCallbackToPipeline(Dumper);
CVSymbolVisitor Visitor(Pipeline);
+
BinaryStreamRef SymStream =
ExpectedSyms->getSymbolArray().getUnderlyingStream();
for (uint32_t PubSymOff : Table) {
@@ -1299,24 +1513,23 @@ Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table,
}
// Return early if we aren't dumping public hash table and address map info.
- if (!HashExtras)
- return Error::success();
-
- P.formatLine("Hash Entries");
- {
- AutoIndent Indent2(P);
- for (const PSHashRecord &HR : Table.HashRecords)
- P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),
- uint32_t(HR.CRef));
- }
+ if (HashExtras) {
+ P.formatBinary("Hash Bitmap", Table.HashBitmap, 0);
- // FIXME: Dump the bitmap.
+ P.formatLine("Hash Entries");
+ {
+ AutoIndent Indent2(P);
+ for (const PSHashRecord &HR : Table.HashRecords)
+ P.formatLine("off = {0}, refcnt = {1}", uint32_t(HR.Off),
+ uint32_t(HR.CRef));
+ }
- P.formatLine("Hash Buckets");
- {
- AutoIndent Indent2(P);
- for (uint32_t Hash : Table.HashBuckets)
- P.formatLine("{0:x8}", Hash);
+ P.formatLine("Hash Buckets");
+ {
+ AutoIndent Indent2(P);
+ for (uint32_t Hash : Table.HashBuckets)
+ P.formatLine("{0:x8}", Hash);
+ }
}
return Error::success();
@@ -1344,39 +1557,6 @@ Error DumpOutputStyle::dumpSectionHeaders() {
return Error::success();
}
-static Expected<std::pair<std::unique_ptr<MappedBlockStream>,
- ArrayRef<llvm::object::coff_section>>>
-loadSectionHeaders(PDBFile &File, DbgHeaderType Type) {
- if (!File.hasPDBDbiStream())
- return make_error<StringError>(
- "Section headers require a DBI Stream, which could not be loaded",
- inconvertibleErrorCode());
-
- auto &Dbi = cantFail(File.getPDBDbiStream());
- uint32_t SI = Dbi.getDebugStreamIndex(Type);
-
- if (SI == kInvalidStreamIndex)
- return make_error<StringError>(
- "PDB does not contain the requested image section header type",
- inconvertibleErrorCode());
-
- auto Stream = File.createIndexedStream(SI);
- if (!Stream)
- return make_error<StringError>("Could not load the required stream data",
- inconvertibleErrorCode());
-
- ArrayRef<object::coff_section> Headers;
- if (Stream->getLength() % sizeof(object::coff_section) != 0)
- return make_error<StringError>(
- "Section header array size is not a multiple of section header size",
- inconvertibleErrorCode());
-
- uint32_t NumHeaders = Stream->getLength() / sizeof(object::coff_section);
- BinaryStreamReader Reader(*Stream);
- cantFail(Reader.readArray(Headers, NumHeaders));
- return std::make_pair(std::move(Stream), Headers);
-}
-
void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
printHeader(P, Label);
@@ -1423,20 +1603,6 @@ void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) {
return;
}
-std::vector<std::string> getSectionNames(PDBFile &File) {
- auto ExpectedHeaders = loadSectionHeaders(File, DbgHeaderType::SectionHdr);
- if (!ExpectedHeaders)
- return {};
-
- std::unique_ptr<MappedBlockStream> Stream;
- ArrayRef<object::coff_section> Headers;
- std::tie(Stream, Headers) = std::move(*ExpectedHeaders);
- std::vector<std::string> Names;
- for (const auto &H : Headers)
- Names.push_back(H.Name);
- return Names;
-}
-
Error DumpOutputStyle::dumpSectionContribs() {
printHeader(P, "Section Contributions");
@@ -1465,33 +1631,10 @@ Error DumpOutputStyle::dumpSectionContribs() {
MaxNameLen = (Max == Names.end() ? 0 : Max->size());
}
void visit(const SectionContrib &SC) override {
- assert(SC.ISect > 0);
- std::string NameInsert;
- if (SC.ISect < Names.size()) {
- StringRef SectionName = Names[SC.ISect - 1];
- NameInsert = formatv("[{0}]", SectionName).str();
- } else
- NameInsert = "[???]";
- P.formatLine("SC{5} | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
- "crc = {4}",
- formatSegmentOffset(SC.ISect, SC.Off), fmtle(SC.Size),
- fmtle(SC.Imod), fmtle(SC.DataCrc), fmtle(SC.RelocCrc),
- fmt_align(NameInsert, AlignStyle::Left, MaxNameLen + 2));
- AutoIndent Indent(P, MaxNameLen + 2);
- P.formatLine(" {0}",
- formatSectionCharacteristics(P.getIndentLevel() + 6,
- SC.Characteristics, 3, " | "));
+ dumpSectionContrib(P, SC, Names, MaxNameLen);
}
void visit(const SectionContrib2 &SC) override {
- P.formatLine(
- "SC2[{6}] | mod = {2}, {0}, size = {1}, data crc = {3}, reloc "
- "crc = {4}, coff section = {5}",
- formatSegmentOffset(SC.Base.ISect, SC.Base.Off), fmtle(SC.Base.Size),
- fmtle(SC.Base.Imod), fmtle(SC.Base.DataCrc), fmtle(SC.Base.RelocCrc),
- fmtle(SC.ISectCoff));
- P.formatLine(" {0}", formatSectionCharacteristics(
- P.getIndentLevel() + 6,
- SC.Base.Characteristics, 3, " | "));
+ dumpSectionContrib(P, SC, Names, MaxNameLen);
}
private:
diff --git a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h
index 3ce2884b2712..e7e9252f2fa9 100644
--- a/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h
+++ b/contrib/llvm/tools/llvm-pdbutil/DumpOutputStyle.h
@@ -74,7 +74,10 @@ private:
Error dumpStreamSummary();
Error dumpSymbolStats();
Error dumpUdtStats();
+ Error dumpNamedStreams();
Error dumpStringTable();
+ Error dumpStringTableFromPdb();
+ Error dumpStringTableFromObj();
Error dumpLines();
Error dumpInlineeLines();
Error dumpXmi();
@@ -85,6 +88,7 @@ private:
Error dumpModuleFiles();
Error dumpModuleSymsForPdb();
Error dumpModuleSymsForObj();
+ Error dumpGSIRecords();
Error dumpGlobals();
Error dumpPublics();
Error dumpSymbolsFromGSI(const GSIHashTable &Table, bool HashExtras);
diff --git a/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp
new file mode 100644
index 000000000000..d16bfa480e1d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.cpp
@@ -0,0 +1,469 @@
+//===- ExplainOutputStyle.cpp --------------------------------- *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ExplainOutputStyle.h"
+
+#include "FormatUtil.h"
+#include "InputFile.h"
+#include "StreamUtil.h"
+#include "llvm-pdbutil.h"
+
+#include "llvm/DebugInfo/CodeView/Formatters.h"
+#include "llvm/DebugInfo/MSF/MappedBlockStream.h"
+#include "llvm/DebugInfo/PDB/Native/DbiStream.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
+#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
+#include "llvm/DebugInfo/PDB/Native/RawTypes.h"
+#include "llvm/Support/BinaryByteStream.h"
+#include "llvm/Support/BinaryStreamArray.h"
+#include "llvm/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::codeview;
+using namespace llvm::msf;
+using namespace llvm::pdb;
+
+ExplainOutputStyle::ExplainOutputStyle(InputFile &File, uint64_t FileOffset)
+ : File(File), FileOffset(FileOffset), P(2, false, outs()) {}
+
+Error ExplainOutputStyle::dump() {
+ P.formatLine("Explaining file offset {0} of file '{1}'.", FileOffset,
+ File.getFilePath());
+
+ if (File.isPdb())
+ return explainPdbFile();
+
+ return explainBinaryFile();
+}
+
+Error ExplainOutputStyle::explainPdbFile() {
+ bool IsAllocated = explainPdbBlockStatus();
+ if (!IsAllocated)
+ return Error::success();
+
+ AutoIndent Indent(P);
+ if (isPdbSuperBlock())
+ explainPdbSuperBlockOffset();
+ else if (isPdbFpmBlock())
+ explainPdbFpmBlockOffset();
+ else if (isPdbBlockMapBlock())
+ explainPdbBlockMapOffset();
+ else if (isPdbStreamDirectoryBlock())
+ explainPdbStreamDirectoryOffset();
+ else if (auto Index = getPdbBlockStreamIndex())
+ explainPdbStreamOffset(*Index);
+ else
+ explainPdbUnknownBlock();
+ return Error::success();
+}
+
+Error ExplainOutputStyle::explainBinaryFile() {
+ std::unique_ptr<BinaryByteStream> Stream =
+ llvm::make_unique<BinaryByteStream>(File.unknown().getBuffer(),
+ llvm::support::little);
+ switch (opts::explain::InputType) {
+ case opts::explain::InputFileType::DBIStream: {
+ DbiStream Dbi(std::move(Stream));
+ if (auto EC = Dbi.reload(nullptr))
+ return EC;
+ explainStreamOffset(Dbi, FileOffset);
+ break;
+ }
+ case opts::explain::InputFileType::PDBStream: {
+ InfoStream Info(std::move(Stream));
+ if (auto EC = Info.reload())
+ return EC;
+ explainStreamOffset(Info, FileOffset);
+ break;
+ }
+ default:
+ llvm_unreachable("Invalid input file type!");
+ }
+ return Error::success();
+}
+
+uint32_t ExplainOutputStyle::pdbBlockIndex() const {
+ return FileOffset / File.pdb().getBlockSize();
+}
+
+uint32_t ExplainOutputStyle::pdbBlockOffset() const {
+ uint64_t BlockStart = pdbBlockIndex() * File.pdb().getBlockSize();
+ assert(FileOffset >= BlockStart);
+ return FileOffset - BlockStart;
+}
+
+bool ExplainOutputStyle::isPdbSuperBlock() const {
+ return pdbBlockIndex() == 0;
+}
+
+bool ExplainOutputStyle::isPdbFpm1() const {
+ return ((pdbBlockIndex() - 1) % File.pdb().getBlockSize() == 0);
+}
+bool ExplainOutputStyle::isPdbFpm2() const {
+ return ((pdbBlockIndex() - 2) % File.pdb().getBlockSize() == 0);
+}
+
+bool ExplainOutputStyle::isPdbFpmBlock() const {
+ return isPdbFpm1() || isPdbFpm2();
+}
+
+bool ExplainOutputStyle::isPdbBlockMapBlock() const {
+ return pdbBlockIndex() == File.pdb().getBlockMapIndex();
+}
+
+bool ExplainOutputStyle::isPdbStreamDirectoryBlock() const {
+ const auto &Layout = File.pdb().getMsfLayout();
+ return llvm::is_contained(Layout.DirectoryBlocks, pdbBlockIndex());
+}
+
+Optional<uint32_t> ExplainOutputStyle::getPdbBlockStreamIndex() const {
+ const auto &Layout = File.pdb().getMsfLayout();
+ for (const auto &Entry : enumerate(Layout.StreamMap)) {
+ if (!llvm::is_contained(Entry.value(), pdbBlockIndex()))
+ continue;
+ return Entry.index();
+ }
+ return None;
+}
+
+bool ExplainOutputStyle::explainPdbBlockStatus() {
+ if (FileOffset >= File.pdb().getFileSize()) {
+ P.formatLine("Address {0} is not in the file (file size = {1}).",
+ FileOffset, File.pdb().getFileSize());
+ return false;
+ }
+ P.formatLine("Block:Offset = {2:X-}:{1:X-4}.", FileOffset, pdbBlockOffset(),
+ pdbBlockIndex());
+
+ bool IsFree = File.pdb().getMsfLayout().FreePageMap[pdbBlockIndex()];
+ P.formatLine("Address is in block {0} ({1}allocated).", pdbBlockIndex(),
+ IsFree ? "un" : "");
+ return !IsFree;
+}
+
+#define endof(Class, Field) (offsetof(Class, Field) + sizeof(Class::Field))
+
+void ExplainOutputStyle::explainPdbSuperBlockOffset() {
+ P.formatLine("This corresponds to offset {0} of the MSF super block, ",
+ pdbBlockOffset());
+ if (pdbBlockOffset() < endof(SuperBlock, MagicBytes))
+ P.printLine("which is part of the MSF file magic.");
+ else if (pdbBlockOffset() < endof(SuperBlock, BlockSize)) {
+ P.printLine("which contains the block size of the file.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->BlockSize));
+ } else if (pdbBlockOffset() < endof(SuperBlock, FreeBlockMapBlock)) {
+ P.printLine("which contains the index of the FPM block (e.g. 1 or 2).");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->FreeBlockMapBlock));
+ } else if (pdbBlockOffset() < endof(SuperBlock, NumBlocks)) {
+ P.printLine("which contains the number of blocks in the file.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->NumBlocks));
+ } else if (pdbBlockOffset() < endof(SuperBlock, NumDirectoryBytes)) {
+ P.printLine("which contains the number of bytes in the stream directory.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->NumDirectoryBytes));
+ } else if (pdbBlockOffset() < endof(SuperBlock, Unknown1)) {
+ P.printLine("whose purpose is unknown.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->Unknown1));
+ } else if (pdbBlockOffset() < endof(SuperBlock, BlockMapAddr)) {
+ P.printLine("which contains the file offset of the block map.");
+ P.formatLine("The current value is {0}.",
+ uint32_t(File.pdb().getMsfLayout().SB->BlockMapAddr));
+ } else {
+ assert(pdbBlockOffset() > sizeof(SuperBlock));
+ P.printLine(
+ "which is outside the range of valid data for the super block.");
+ }
+}
+
+static std::string toBinaryString(uint8_t Byte) {
+ char Result[9] = {0};
+ for (int I = 0; I < 8; ++I) {
+ char C = (Byte & 1) ? '1' : '0';
+ Result[I] = C;
+ Byte >>= 1;
+ }
+ return std::string(Result);
+}
+
+void ExplainOutputStyle::explainPdbFpmBlockOffset() {
+ const MSFLayout &Layout = File.pdb().getMsfLayout();
+ uint32_t MainFpm = Layout.mainFpmBlock();
+ uint32_t AltFpm = Layout.alternateFpmBlock();
+
+ assert(isPdbFpmBlock());
+ uint32_t Fpm = isPdbFpm1() ? 1 : 2;
+ uint32_t FpmChunk = pdbBlockIndex() / File.pdb().getBlockSize();
+ assert((Fpm == MainFpm) || (Fpm == AltFpm));
+ (void)AltFpm;
+ bool IsMain = (Fpm == MainFpm);
+ P.formatLine("Address is in FPM{0} ({1} FPM)", Fpm, IsMain ? "Main" : "Alt");
+ uint32_t DescribedBlockStart =
+ 8 * (FpmChunk * File.pdb().getBlockSize() + pdbBlockOffset());
+ if (DescribedBlockStart > File.pdb().getBlockCount()) {
+ P.printLine("Address is in extraneous FPM space.");
+ return;
+ }
+
+ P.formatLine("Address describes the allocation status of blocks [{0},{1})",
+ DescribedBlockStart, DescribedBlockStart + 8);
+ ArrayRef<uint8_t> Bytes;
+ cantFail(File.pdb().getMsfBuffer().readBytes(FileOffset, 1, Bytes));
+ P.formatLine("Status = {0} (Note: 0 = allocated, 1 = free)",
+ toBinaryString(Bytes[0]));
+}
+
+void ExplainOutputStyle::explainPdbBlockMapOffset() {
+ uint64_t BlockMapOffset = File.pdb().getBlockMapOffset();
+ uint32_t OffsetInBlock = FileOffset - BlockMapOffset;
+ P.formatLine("Address is at offset {0} of the directory block list",
+ OffsetInBlock);
+}
+
+static uint32_t getOffsetInStream(ArrayRef<support::ulittle32_t> StreamBlocks,
+ uint64_t FileOffset, uint32_t BlockSize) {
+ uint32_t BlockIndex = FileOffset / BlockSize;
+ uint32_t OffsetInBlock = FileOffset - BlockIndex * BlockSize;
+
+ auto Iter = llvm::find(StreamBlocks, BlockIndex);
+ assert(Iter != StreamBlocks.end());
+ uint32_t StreamBlockIndex = std::distance(StreamBlocks.begin(), Iter);
+ return StreamBlockIndex * BlockSize + OffsetInBlock;
+}
+
+void ExplainOutputStyle::explainPdbStreamOffset(uint32_t Stream) {
+ SmallVector<StreamInfo, 12> Streams;
+ discoverStreamPurposes(File.pdb(), Streams);
+
+ assert(Stream <= Streams.size());
+ const StreamInfo &S = Streams[Stream];
+ const auto &Layout = File.pdb().getStreamLayout(Stream);
+ uint32_t StreamOff =
+ getOffsetInStream(Layout.Blocks, FileOffset, File.pdb().getBlockSize());
+ P.formatLine("Address is at offset {0}/{1} of Stream {2} ({3}){4}.",
+ StreamOff, Layout.Length, Stream, S.getLongName(),
+ (StreamOff > Layout.Length) ? " in unused space" : "");
+ switch (S.getPurpose()) {
+ case StreamPurpose::DBI: {
+ DbiStream &Dbi = cantFail(File.pdb().getPDBDbiStream());
+ explainStreamOffset(Dbi, StreamOff);
+ break;
+ }
+ case StreamPurpose::PDB: {
+ InfoStream &Info = cantFail(File.pdb().getPDBInfoStream());
+ explainStreamOffset(Info, StreamOff);
+ break;
+ }
+ case StreamPurpose::IPI:
+ case StreamPurpose::TPI:
+ case StreamPurpose::ModuleStream:
+ case StreamPurpose::NamedStream:
+ default:
+ break;
+ }
+}
+
+void ExplainOutputStyle::explainPdbStreamDirectoryOffset() {
+ auto DirectoryBlocks = File.pdb().getDirectoryBlockArray();
+ const auto &Layout = File.pdb().getMsfLayout();
+ uint32_t StreamOff =
+ getOffsetInStream(DirectoryBlocks, FileOffset, File.pdb().getBlockSize());
+ P.formatLine("Address is at offset {0}/{1} of Stream Directory{2}.",
+ StreamOff, uint32_t(Layout.SB->NumDirectoryBytes),
+ uint32_t(StreamOff > Layout.SB->NumDirectoryBytes)
+ ? " in unused space"
+ : "");
+}
+
+void ExplainOutputStyle::explainPdbUnknownBlock() {
+ P.formatLine("Address has unknown purpose.");
+}
+
+template <typename T>
+static void printStructField(LinePrinter &P, StringRef Label, T Value) {
+ P.formatLine("which contains {0}.", Label);
+ P.formatLine("The current value is {0}.", Value);
+}
+
+static void explainDbiHeaderOffset(LinePrinter &P, DbiStream &Dbi,
+ uint32_t Offset) {
+ const DbiStreamHeader *Header = Dbi.getHeader();
+ assert(Header != nullptr);
+
+ if (Offset < endof(DbiStreamHeader, VersionSignature))
+ printStructField(P, "the DBI Stream Version Signature",
+ int32_t(Header->VersionSignature));
+ else if (Offset < endof(DbiStreamHeader, VersionHeader))
+ printStructField(P, "the DBI Stream Version Header",
+ uint32_t(Header->VersionHeader));
+ else if (Offset < endof(DbiStreamHeader, Age))
+ printStructField(P, "the age of the DBI Stream", uint32_t(Header->Age));
+ else if (Offset < endof(DbiStreamHeader, GlobalSymbolStreamIndex))
+ printStructField(P, "the index of the Global Symbol Stream",
+ uint16_t(Header->GlobalSymbolStreamIndex));
+ else if (Offset < endof(DbiStreamHeader, BuildNumber))
+ printStructField(P, "the build number", uint16_t(Header->BuildNumber));
+ else if (Offset < endof(DbiStreamHeader, PublicSymbolStreamIndex))
+ printStructField(P, "the index of the Public Symbol Stream",
+ uint16_t(Header->PublicSymbolStreamIndex));
+ else if (Offset < endof(DbiStreamHeader, PdbDllVersion))
+ printStructField(P, "the version of mspdb.dll",
+ uint16_t(Header->PdbDllVersion));
+ else if (Offset < endof(DbiStreamHeader, SymRecordStreamIndex))
+ printStructField(P, "the index of the Symbol Record Stream",
+ uint16_t(Header->SymRecordStreamIndex));
+ else if (Offset < endof(DbiStreamHeader, PdbDllRbld))
+ printStructField(P, "the rbld of mspdb.dll", uint16_t(Header->PdbDllRbld));
+ else if (Offset < endof(DbiStreamHeader, ModiSubstreamSize))
+ printStructField(P, "the size of the Module Info Substream",
+ int32_t(Header->ModiSubstreamSize));
+ else if (Offset < endof(DbiStreamHeader, SecContrSubstreamSize))
+ printStructField(P, "the size of the Section Contribution Substream",
+ int32_t(Header->SecContrSubstreamSize));
+ else if (Offset < endof(DbiStreamHeader, SectionMapSize))
+ printStructField(P, "the size of the Section Map Substream",
+ int32_t(Header->SectionMapSize));
+ else if (Offset < endof(DbiStreamHeader, FileInfoSize))
+ printStructField(P, "the size of the File Info Substream",
+ int32_t(Header->FileInfoSize));
+ else if (Offset < endof(DbiStreamHeader, TypeServerSize))
+ printStructField(P, "the size of the Type Server Map",
+ int32_t(Header->TypeServerSize));
+ else if (Offset < endof(DbiStreamHeader, MFCTypeServerIndex))
+ printStructField(P, "the index of the MFC Type Server stream",
+ uint32_t(Header->MFCTypeServerIndex));
+ else if (Offset < endof(DbiStreamHeader, OptionalDbgHdrSize))
+ printStructField(P, "the size of the Optional Debug Stream array",
+ int32_t(Header->OptionalDbgHdrSize));
+ else if (Offset < endof(DbiStreamHeader, ECSubstreamSize))
+ printStructField(P, "the size of the Edit & Continue Substream",
+ int32_t(Header->ECSubstreamSize));
+ else if (Offset < endof(DbiStreamHeader, Flags))
+ printStructField(P, "the DBI Stream flags", uint16_t(Header->Flags));
+ else if (Offset < endof(DbiStreamHeader, MachineType))
+ printStructField(P, "the machine type", uint16_t(Header->MachineType));
+ else if (Offset < endof(DbiStreamHeader, Reserved))
+ printStructField(P, "reserved data", uint32_t(Header->Reserved));
+}
+
+static void explainDbiModiSubstreamOffset(LinePrinter &P, DbiStream &Dbi,
+ uint32_t Offset) {
+ VarStreamArray<DbiModuleDescriptor> ModuleDescriptors;
+ BinaryStreamRef ModiSubstreamData = Dbi.getModiSubstreamData().StreamData;
+ BinaryStreamReader Reader(ModiSubstreamData);
+
+ cantFail(Reader.readArray(ModuleDescriptors, ModiSubstreamData.getLength()));
+ auto Prev = ModuleDescriptors.begin();
+ assert(Prev.offset() == 0);
+ auto Current = Prev;
+ uint32_t Index = 0;
+ while (true) {
+ Prev = Current;
+ ++Current;
+ if (Current == ModuleDescriptors.end() || Offset < Current.offset())
+ break;
+ ++Index;
+ }
+
+ DbiModuleDescriptor &Descriptor = *Prev;
+ P.formatLine("which contains the descriptor for module {0} ({1}).", Index,
+ Descriptor.getModuleName());
+}
+
+template <typename T>
+static void dontExplain(LinePrinter &Printer, T &Stream, uint32_t Offset) {}
+
+template <typename T, typename SubstreamRangeT>
+static void explainSubstreamOffset(LinePrinter &P, uint32_t OffsetInStream,
+ T &Stream,
+ const SubstreamRangeT &Substreams) {
+ uint32_t SubOffset = OffsetInStream;
+ for (const auto &Entry : Substreams) {
+ if (Entry.Size <= 0)
+ continue;
+ uint32_t S = static_cast<uint32_t>(Entry.Size);
+ if (SubOffset < S) {
+ P.formatLine("address is at offset {0}/{1} of the {2}.", SubOffset, S,
+ Entry.Label);
+ Entry.Explain(P, Stream, SubOffset);
+ return;
+ }
+ SubOffset -= S;
+ }
+}
+
+void ExplainOutputStyle::explainStreamOffset(DbiStream &Dbi,
+ uint32_t OffsetInStream) {
+ P.printLine("Within the DBI stream:");
+ AutoIndent Indent(P);
+ const DbiStreamHeader *Header = Dbi.getHeader();
+ assert(Header != nullptr);
+
+ struct SubstreamInfo {
+ int32_t Size;
+ StringRef Label;
+ void (*Explain)(LinePrinter &, DbiStream &, uint32_t);
+ } Substreams[] = {
+ {sizeof(DbiStreamHeader), "DBI Stream Header", explainDbiHeaderOffset},
+ {int32_t(Header->ModiSubstreamSize), "Module Info Substream",
+ explainDbiModiSubstreamOffset},
+ {int32_t(Header->SecContrSubstreamSize), "Section Contribution Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->SectionMapSize), "Section Map", dontExplain<DbiStream>},
+ {int32_t(Header->FileInfoSize), "File Info Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->TypeServerSize), "Type Server Map Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->ECSubstreamSize), "Edit & Continue Substream",
+ dontExplain<DbiStream>},
+ {int32_t(Header->OptionalDbgHdrSize), "Optional Debug Stream Array",
+ dontExplain<DbiStream>},
+ };
+
+ explainSubstreamOffset(P, OffsetInStream, Dbi, Substreams);
+}
+
+static void explainPdbStreamHeaderOffset(LinePrinter &P, InfoStream &Info,
+ uint32_t Offset) {
+ const InfoStreamHeader *Header = Info.getHeader();
+ assert(Header != nullptr);
+
+ if (Offset < endof(InfoStreamHeader, Version))
+ printStructField(P, "the PDB Stream Version Signature",
+ uint32_t(Header->Version));
+ else if (Offset < endof(InfoStreamHeader, Signature))
+ printStructField(P, "the signature of the PDB Stream",
+ uint32_t(Header->Signature));
+ else if (Offset < endof(InfoStreamHeader, Age))
+ printStructField(P, "the age of the PDB", uint32_t(Header->Age));
+ else if (Offset < endof(InfoStreamHeader, Guid))
+ printStructField(P, "the guid of the PDB", fmt_guid(Header->Guid.Guid));
+}
+
+void ExplainOutputStyle::explainStreamOffset(InfoStream &Info,
+ uint32_t OffsetInStream) {
+ P.printLine("Within the PDB stream:");
+ AutoIndent Indent(P);
+
+ struct SubstreamInfo {
+ uint32_t Size;
+ StringRef Label;
+ void (*Explain)(LinePrinter &, InfoStream &, uint32_t);
+ } Substreams[] = {{sizeof(InfoStreamHeader), "PDB Stream Header",
+ explainPdbStreamHeaderOffset},
+ {Info.getNamedStreamMapByteSize(), "Named Stream Map",
+ dontExplain<InfoStream>},
+ {Info.getStreamSize(), "PDB Feature Signatures",
+ dontExplain<InfoStream>}};
+
+ explainSubstreamOffset(P, OffsetInStream, Info, Substreams);
+}
diff --git a/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h
new file mode 100644
index 000000000000..9a497accb812
--- /dev/null
+++ b/contrib/llvm/tools/llvm-pdbutil/ExplainOutputStyle.h
@@ -0,0 +1,68 @@
+//===- ExplainOutputStyle.h ----------------------------------- *- C++ --*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
+#define LLVM_TOOLS_LLVMPDBDUMP_EXPLAINOUTPUTSTYLE_H
+
+#include "LinePrinter.h"
+#include "OutputStyle.h"
+
+#include <string>
+
+namespace llvm {
+
+namespace pdb {
+
+class DbiStream;
+class InfoStream;
+class InputFile;
+
+class ExplainOutputStyle : public OutputStyle {
+
+public:
+ ExplainOutputStyle(InputFile &File, uint64_t FileOffset);
+
+ Error dump() override;
+
+private:
+ Error explainPdbFile();
+ Error explainBinaryFile();
+
+ bool explainPdbBlockStatus();
+
+ bool isPdbFpm1() const;
+ bool isPdbFpm2() const;
+
+ bool isPdbSuperBlock() const;
+ bool isPdbFpmBlock() const;
+ bool isPdbBlockMapBlock() const;
+ bool isPdbStreamDirectoryBlock() const;
+ Optional<uint32_t> getPdbBlockStreamIndex() const;
+
+ void explainPdbSuperBlockOffset();
+ void explainPdbFpmBlockOffset();
+ void explainPdbBlockMapOffset();
+ void explainPdbStreamDirectoryOffset();
+ void explainPdbStreamOffset(uint32_t Stream);
+ void explainPdbUnknownBlock();
+
+ void explainStreamOffset(DbiStream &Stream, uint32_t OffsetInStream);
+ void explainStreamOffset(InfoStream &Stream, uint32_t OffsetInStream);
+
+ uint32_t pdbBlockIndex() const;
+ uint32_t pdbBlockOffset() const;
+
+ InputFile &File;
+ const uint64_t FileOffset;
+ LinePrinter P;
+};
+} // namespace pdb
+} // namespace llvm
+
+#endif
diff --git a/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp b/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp
index 8b05381174df..7b5af7e96920 100644
--- a/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/InputFile.cpp
@@ -95,7 +95,8 @@ static inline bool isDebugSSection(object::SectionRef Section,
static bool isDebugTSection(SectionRef Section, CVTypeArray &Types) {
BinaryStreamReader Reader;
- if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader))
+ if (!isCodeViewDebugSubsection(Section, ".debug$T", Reader) &&
+ !isCodeViewDebugSubsection(Section, ".debug$P", Reader))
return false;
cantFail(Reader.readArray(Types, Reader.bytesRemaining()));
return true;
@@ -242,7 +243,7 @@ void SymbolGroup::formatFromChecksumsOffset(LinePrinter &Printer,
}
}
-Expected<InputFile> InputFile::open(StringRef Path) {
+Expected<InputFile> InputFile::open(StringRef Path, bool AllowUnknownFile) {
InputFile IF;
if (!llvm::sys::fs::exists(Path))
return make_error<StringError>(formatv("File {0} not found", Path),
@@ -263,7 +264,7 @@ Expected<InputFile> InputFile::open(StringRef Path) {
return std::move(IF);
}
- if (Magic == file_magic::unknown) {
+ if (Magic == file_magic::pdb) {
std::unique_ptr<IPDBSession> Session;
if (auto Err = loadDataForPDB(PDB_ReaderType::Native, Path, Session))
return std::move(Err);
@@ -274,9 +275,19 @@ Expected<InputFile> InputFile::open(StringRef Path) {
return std::move(IF);
}
- return make_error<StringError>(
- formatv("File {0} is not a supported file type", Path),
- inconvertibleErrorCode());
+ if (!AllowUnknownFile)
+ return make_error<StringError>(
+ formatv("File {0} is not a supported file type", Path),
+ inconvertibleErrorCode());
+
+ auto Result = MemoryBuffer::getFile(Path, -1LL, false);
+ if (!Result)
+ return make_error<StringError>(
+ formatv("File {0} could not be opened", Path), Result.getError());
+
+ IF.UnknownFile = std::move(*Result);
+ IF.PdbOrObj = IF.UnknownFile.get();
+ return std::move(IF);
}
PDBFile &InputFile::pdb() {
@@ -299,6 +310,25 @@ const object::COFFObjectFile &InputFile::obj() const {
return *PdbOrObj.get<object::COFFObjectFile *>();
}
+MemoryBuffer &InputFile::unknown() {
+ assert(isUnknown());
+ return *PdbOrObj.get<MemoryBuffer *>();
+}
+
+const MemoryBuffer &InputFile::unknown() const {
+ assert(isUnknown());
+ return *PdbOrObj.get<MemoryBuffer *>();
+}
+
+StringRef InputFile::getFilePath() const {
+ if (isPdb())
+ return pdb().getFilePath();
+ if (isObj())
+ return obj().getFileName();
+ assert(isUnknown());
+ return unknown().getBufferIdentifier();
+}
+
bool InputFile::hasTypes() const {
if (isPdb())
return pdb().hasPDBTpiStream();
@@ -323,6 +353,8 @@ bool InputFile::isObj() const {
return PdbOrObj.is<object::COFFObjectFile *>();
}
+bool InputFile::isUnknown() const { return PdbOrObj.is<MemoryBuffer *>(); }
+
codeview::LazyRandomTypeCollection &
InputFile::getOrCreateTypeCollection(TypeCollectionKind Kind) {
if (Types && Kind == kTypes)
diff --git a/contrib/llvm/tools/llvm-pdbutil/InputFile.h b/contrib/llvm/tools/llvm-pdbutil/InputFile.h
index 8063439133c2..552f3a3b2127 100644
--- a/contrib/llvm/tools/llvm-pdbutil/InputFile.h
+++ b/contrib/llvm/tools/llvm-pdbutil/InputFile.h
@@ -43,7 +43,8 @@ class InputFile {
std::unique_ptr<NativeSession> PdbSession;
object::OwningBinary<object::Binary> CoffObject;
- PointerUnion<PDBFile *, object::COFFObjectFile *> PdbOrObj;
+ std::unique_ptr<MemoryBuffer> UnknownFile;
+ PointerUnion3<PDBFile *, object::COFFObjectFile *, MemoryBuffer *> PdbOrObj;
using TypeCollectionPtr = std::unique_ptr<codeview::LazyRandomTypeCollection>;
@@ -58,12 +59,17 @@ public:
~InputFile();
InputFile(InputFile &&Other) = default;
- static Expected<InputFile> open(StringRef Path);
+ static Expected<InputFile> open(StringRef Path,
+ bool AllowUnknownFile = false);
PDBFile &pdb();
const PDBFile &pdb() const;
object::COFFObjectFile &obj();
const object::COFFObjectFile &obj() const;
+ MemoryBuffer &unknown();
+ const MemoryBuffer &unknown() const;
+
+ StringRef getFilePath() const;
bool hasTypes() const;
bool hasIds() const;
@@ -77,6 +83,7 @@ public:
bool isPdb() const;
bool isObj() const;
+ bool isUnknown() const;
};
class SymbolGroup {
diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
index 40a0e46efd48..b454ab345456 100644
--- a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.cpp
@@ -10,6 +10,7 @@
#include "MinimalSymbolDumper.h"
#include "FormatUtil.h"
+#include "InputFile.h"
#include "LinePrinter.h"
#include "llvm/DebugInfo/CodeView/CVRecord.h"
@@ -18,6 +19,7 @@
#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h"
#include "llvm/DebugInfo/CodeView/SymbolRecord.h"
#include "llvm/DebugInfo/CodeView/TypeRecord.h"
+#include "llvm/DebugInfo/PDB/Native/PDBStringTable.h"
#include "llvm/Support/FormatVariadic.h"
using namespace llvm;
@@ -450,6 +452,17 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR,
Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FileStaticSym &FS) {
P.format(" `{0}`", FS.Name);
AutoIndent Indent(P, 7);
+ if (SymGroup) {
+ Expected<StringRef> FileName =
+ SymGroup->getNameFromStringTable(FS.ModFilenameOffset);
+ if (FileName) {
+ P.formatLine("type = {0}, file name = {1} ({2}), flags = {3}",
+ typeIndex(FS.Index), FS.ModFilenameOffset, *FileName,
+ formatLocalSymFlags(P.getIndentLevel() + 9, FS.Flags));
+ }
+ return Error::success();
+ }
+
P.formatLine("type = {0}, file name offset = {1}, flags = {2}",
typeIndex(FS.Index), FS.ModFilenameOffset,
formatLocalSymFlags(P.getIndentLevel() + 9, FS.Flags));
diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h
index d9e9861d5b30..1c26a85a4eaf 100644
--- a/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h
+++ b/contrib/llvm/tools/llvm-pdbutil/MinimalSymbolDumper.h
@@ -19,6 +19,7 @@ class LazyRandomTypeCollection;
namespace pdb {
class LinePrinter;
+class SymbolGroup;
class MinimalSymbolDumper : public codeview::SymbolVisitorCallbacks {
public:
@@ -26,11 +27,19 @@ public:
codeview::LazyRandomTypeCollection &Ids,
codeview::LazyRandomTypeCollection &Types)
: P(P), RecordBytes(RecordBytes), Ids(Ids), Types(Types) {}
+ MinimalSymbolDumper(LinePrinter &P, bool RecordBytes,
+ const SymbolGroup &SymGroup,
+ codeview::LazyRandomTypeCollection &Ids,
+ codeview::LazyRandomTypeCollection &Types)
+ : P(P), RecordBytes(RecordBytes), SymGroup(&SymGroup), Ids(Ids),
+ Types(Types) {}
Error visitSymbolBegin(codeview::CVSymbol &Record) override;
Error visitSymbolBegin(codeview::CVSymbol &Record, uint32_t Offset) override;
Error visitSymbolEnd(codeview::CVSymbol &Record) override;
+ void setSymbolGroup(const SymbolGroup *Group) { SymGroup = Group; }
+
#define SYMBOL_RECORD(EnumName, EnumVal, Name) \
virtual Error visitKnownRecord(codeview::CVSymbol &CVR, \
codeview::Name &Record) override;
@@ -45,6 +54,7 @@ private:
LinePrinter &P;
bool RecordBytes;
+ const SymbolGroup *SymGroup = nullptr;
codeview::LazyRandomTypeCollection &Ids;
codeview::LazyRandomTypeCollection &Types;
};
diff --git a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
index fae89920e0b8..569bca7490fa 100644
--- a/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/MinimalTypeDumper.cpp
@@ -303,8 +303,9 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
P.formatLine("unique name: `{0}`", Class.UniqueName);
P.formatLine("vtable: {0}, base list: {1}, field list: {2}",
Class.VTableShape, Class.DerivationList, Class.FieldList);
- P.formatLine("options: {0}",
- formatClassOptions(P.getIndentLevel(), Class.Options));
+ P.formatLine("options: {0}, sizeof {1}",
+ formatClassOptions(P.getIndentLevel(), Class.Options),
+ Class.Size);
return Error::success();
}
@@ -314,8 +315,9 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
if (Union.hasUniqueName())
P.formatLine("unique name: `{0}`", Union.UniqueName);
P.formatLine("field list: {0}", Union.FieldList);
- P.formatLine("options: {0}",
- formatClassOptions(P.getIndentLevel(), Union.Options));
+ P.formatLine("options: {0}, sizeof {1}",
+ formatClassOptions(P.getIndentLevel(), Union.Options),
+ Union.Size);
return Error::success();
}
@@ -467,6 +469,21 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, LabelRecord &R) {
return Error::success();
}
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ PrecompRecord &Precomp) {
+ P.format(" start index = {0:X+}, types count = {1:X+}, signature = {2:X+},"
+ " precomp path = {3}",
+ Precomp.StartTypeIndex, Precomp.TypesCount, Precomp.Signature,
+ Precomp.PrecompFilePath);
+ return Error::success();
+}
+
+Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR,
+ EndPrecompRecord &EP) {
+ P.format(" signature = {0:X+}", EP.Signature);
+ return Error::success();
+}
+
Error MinimalTypeDumpVisitor::visitKnownMember(CVMemberRecord &CVR,
NestedTypeRecord &Nested) {
P.format(" [name = `{0}`, parent = {1}]", Nested.Name, Nested.Type);
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp
index 10b3d9ee7304..bcdecca81aec 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp
@@ -87,7 +87,12 @@ StringRef BuiltinDumper::getTypeName(const PDBSymbolTypeBuiltin &Symbol) {
return "HRESULT";
case PDB_BuiltinType::BCD:
return "HRESULT";
- default:
- return "void";
+ case PDB_BuiltinType::Char16:
+ return "char16_t";
+ case PDB_BuiltinType::Char32:
+ return "char32_t";
+ case PDB_BuiltinType::None:
+ return "...";
}
+ llvm_unreachable("Unknown PDB_BuiltinType");
}
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp
index 66c29fc5d4ee..a572522c8cd7 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp
@@ -50,12 +50,9 @@ bool PrettyClassLayoutGraphicalDumper::start(const UDTLayoutBase &Layout) {
uint32_t RelativeOffset = Item->getOffsetInParent();
CurrentAbsoluteOffset = ClassOffsetZero + RelativeOffset;
- // Since there is storage there, it should be set! However, this might
- // be an empty base, in which case it could extend outside the bounds of
- // the parent class.
+ // This might be an empty base, in which case it could extend outside the
+ // bounds of the parent class.
if (RelativeOffset < UseMap.size() && (Item->getSize() > 0)) {
- assert(UseMap.test(RelativeOffset));
-
// If there is any remaining padding in this class, and the offset of the
// new item is after the padding, then we must have just jumped over some
// padding. Print a padding row and then look for where the next block
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
index 65e8badbc99a..0d99c9b1245c 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyCompilandDumper.cpp
@@ -55,62 +55,73 @@ void CompilandDumper::start(const PDBSymbolCompiland &Symbol,
if (opts & Flags::Lines) {
const IPDBSession &Session = Symbol.getSession();
- auto Files = Session.getSourceFilesForCompiland(Symbol);
- Printer.Indent();
- while (auto File = Files->getNext()) {
- Printer.NewLine();
- WithColor(Printer, PDB_ColorItem::Path).get() << File->getFileName();
-
- auto Lines = Session.findLineNumbers(Symbol, *File);
+ if (auto Files = Session.getSourceFilesForCompiland(Symbol)) {
Printer.Indent();
- while (auto Line = Lines->getNext()) {
+ while (auto File = Files->getNext()) {
Printer.NewLine();
- uint32_t LineStart = Line->getLineNumber();
- uint32_t LineEnd = Line->getLineNumberEnd();
-
- Printer << "Line ";
- PDB_ColorItem StatementColor = Line->isStatement()
- ? PDB_ColorItem::Keyword
- : PDB_ColorItem::LiteralValue;
- WithColor(Printer, StatementColor).get() << LineStart;
- if (LineStart != LineEnd)
- WithColor(Printer, StatementColor).get() << " - " << LineEnd;
-
- uint32_t ColumnStart = Line->getColumnNumber();
- uint32_t ColumnEnd = Line->getColumnNumberEnd();
- if (ColumnStart != 0 || ColumnEnd != 0) {
- Printer << ", Column: ";
- WithColor(Printer, StatementColor).get() << ColumnStart;
- if (ColumnEnd != ColumnStart)
- WithColor(Printer, StatementColor).get() << " - " << ColumnEnd;
+ WithColor(Printer, PDB_ColorItem::Path).get() << File->getFileName();
+ if (File->getChecksumType() != PDB_Checksum::None) {
+ auto ChecksumType = File->getChecksumType();
+ auto ChecksumHexString = toHex(File->getChecksum());
+ WithColor(Printer, PDB_ColorItem::Comment).get()
+ << " (" << ChecksumType << ": " << ChecksumHexString << ")";
}
- Printer << ", Address: ";
- if (Line->getLength() > 0) {
- uint64_t AddrStart = Line->getVirtualAddress();
- uint64_t AddrEnd = AddrStart + Line->getLength() - 1;
- WithColor(Printer, PDB_ColorItem::Address).get()
+ auto Lines = Session.findLineNumbers(Symbol, *File);
+ if (!Lines)
+ continue;
+
+ Printer.Indent();
+ while (auto Line = Lines->getNext()) {
+ Printer.NewLine();
+ uint32_t LineStart = Line->getLineNumber();
+ uint32_t LineEnd = Line->getLineNumberEnd();
+
+ Printer << "Line ";
+ PDB_ColorItem StatementColor = Line->isStatement()
+ ? PDB_ColorItem::Keyword
+ : PDB_ColorItem::LiteralValue;
+ WithColor(Printer, StatementColor).get() << LineStart;
+ if (LineStart != LineEnd)
+ WithColor(Printer, StatementColor).get() << " - " << LineEnd;
+
+ uint32_t ColumnStart = Line->getColumnNumber();
+ uint32_t ColumnEnd = Line->getColumnNumberEnd();
+ if (ColumnStart != 0 || ColumnEnd != 0) {
+ Printer << ", Column: ";
+ WithColor(Printer, StatementColor).get() << ColumnStart;
+ if (ColumnEnd != ColumnStart)
+ WithColor(Printer, StatementColor).get() << " - " << ColumnEnd;
+ }
+
+ Printer << ", Address: ";
+ if (Line->getLength() > 0) {
+ uint64_t AddrStart = Line->getVirtualAddress();
+ uint64_t AddrEnd = AddrStart + Line->getLength() - 1;
+ WithColor(Printer, PDB_ColorItem::Address).get()
<< "[" << format_hex(AddrStart, 10) << " - "
<< format_hex(AddrEnd, 10) << "]";
- Printer << " (" << Line->getLength() << " bytes)";
- } else {
- uint64_t AddrStart = Line->getVirtualAddress();
- WithColor(Printer, PDB_ColorItem::Address).get()
+ Printer << " (" << Line->getLength() << " bytes)";
+ } else {
+ uint64_t AddrStart = Line->getVirtualAddress();
+ WithColor(Printer, PDB_ColorItem::Address).get()
<< "[" << format_hex(AddrStart, 10) << "] ";
- Printer << "(0 bytes)";
+ Printer << "(0 bytes)";
+ }
}
+ Printer.Unindent();
}
Printer.Unindent();
}
- Printer.Unindent();
}
if (opts & Flags::Children) {
- auto ChildrenEnum = Symbol.findAllChildren();
- Printer.Indent();
- while (auto Child = ChildrenEnum->getNext())
- Child->dump(*this);
- Printer.Unindent();
+ if (auto ChildrenEnum = Symbol.findAllChildren()) {
+ Printer.Indent();
+ while (auto Child = ChildrenEnum->getNext())
+ Child->dump(*this);
+ Printer.Unindent();
+ }
}
}
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp
index fc40d90cee96..1270223b1c78 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp
@@ -21,9 +21,10 @@ ExternalSymbolDumper::ExternalSymbolDumper(LinePrinter &P)
: PDBSymDumper(true), Printer(P) {}
void ExternalSymbolDumper::start(const PDBSymbolExe &Symbol) {
- auto Vars = Symbol.findAllChildren<PDBSymbolPublicSymbol>();
- while (auto Var = Vars->getNext())
- Var->dump(*this);
+ if (auto Vars = Symbol.findAllChildren<PDBSymbolPublicSymbol>()) {
+ while (auto Var = Vars->getNext())
+ Var->dump(*this);
+ }
}
void ExternalSymbolDumper::dump(const PDBSymbolPublicSymbol &Symbol) {
@@ -34,7 +35,7 @@ void ExternalSymbolDumper::dump(const PDBSymbolPublicSymbol &Symbol) {
Printer.NewLine();
uint64_t Addr = Symbol.getVirtualAddress();
- Printer << "[";
+ Printer << "public [";
WithColor(Printer, PDB_ColorItem::Address).get() << format_hex(Addr, 10);
Printer << "] ";
WithColor(Printer, PDB_ColorItem::Identifier).get() << LinkageName;
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
index 0bffc73f6c74..177d8a009a2b 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyFunctionDumper.cpp
@@ -189,6 +189,8 @@ void FunctionDumper::start(const PDBSymbolFunc &Symbol, PointerType Pointer) {
if (++Index < Arguments->getChildCount())
Printer << ", ";
}
+ if (Signature->isCVarArgs())
+ Printer << ", ...";
}
Printer << ")";
if (Symbol.isConstType())
@@ -250,6 +252,9 @@ void FunctionDumper::dump(const PDBSymbolTypePointer &Symbol) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile ";
PointeeType->dump(*this);
Printer << (Symbol.isReference() ? "&" : "*");
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
}
}
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp
index 0f6086395ad1..663a608fe429 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypeDumper.cpp
@@ -128,14 +128,13 @@ filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E,
}
if (Comp)
- std::sort(Filtered.begin(), Filtered.end(), Comp);
+ llvm::sort(Filtered.begin(), Filtered.end(), Comp);
return Filtered;
}
TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {}
void TypeDumper::start(const PDBSymbolExe &Exe) {
- auto Children = Exe.findAllChildren();
if (opts::pretty::Enums) {
if (auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>()) {
Printer.NewLine();
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
index ba3b4c8035c5..65443d6bca90 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyTypedefDumper.cpp
@@ -63,6 +63,9 @@ void TypedefDumper::dump(const PDBSymbolTypePointer &Symbol) {
PointeeType->dump(*this);
Printer << ((Symbol.isReference()) ? "&" : "*");
}
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
}
void TypedefDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) {
diff --git a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp
index 4884fc8ee5a4..ddac8cf0da4a 100644
--- a/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/PrettyVariableDumper.cpp
@@ -169,6 +169,9 @@ void VariableDumper::dumpRight(const PDBSymbolTypeFunctionSig &Symbol) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << " const";
if (Symbol.isVolatileType())
WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile";
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict";
}
void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) {
@@ -189,6 +192,9 @@ void VariableDumper::dump(const PDBSymbolTypePointer &Symbol) {
WithColor(Printer, PDB_ColorItem::Keyword).get() << " const ";
if (Symbol.isVolatileType())
WithColor(Printer, PDB_ColorItem::Keyword).get() << " volatile ";
+
+ if (Symbol.getRawSymbol().isRestrictedType())
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << " __restrict ";
}
void VariableDumper::dumpRight(const PDBSymbolTypePointer &Symbol) {
diff --git a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp
index 991c99aa8686..367d947d25ee 100644
--- a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.cpp
@@ -49,16 +49,9 @@ StreamInfo StreamInfo::createModuleStream(StringRef Module,
return Result;
}
-static inline StreamInfo otherStream(StringRef Label, uint32_t Idx) {
- return StreamInfo::createStream(StreamPurpose::Other, Label, Idx);
-}
-
-static inline StreamInfo namedStream(StringRef Label, uint32_t Idx) {
- return StreamInfo::createStream(StreamPurpose::NamedStream, Label, Idx);
-}
-
-static inline StreamInfo symbolStream(StringRef Label, uint32_t Idx) {
- return StreamInfo::createStream(StreamPurpose::Symbols, Label, Idx);
+static inline StreamInfo stream(StreamPurpose Purpose, StringRef Label,
+ uint32_t Idx) {
+ return StreamInfo::createStream(Purpose, Label, Idx);
}
static inline StreamInfo moduleStream(StringRef Label, uint32_t StreamIdx,
@@ -105,60 +98,75 @@ void llvm::pdb::discoverStreamPurposes(PDBFile &File,
Streams.resize(StreamCount);
for (uint16_t StreamIdx = 0; StreamIdx < StreamCount; ++StreamIdx) {
if (StreamIdx == OldMSFDirectory)
- Streams[StreamIdx] = otherStream("Old MSF Directory", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Old MSF Directory", StreamIdx);
else if (StreamIdx == StreamPDB)
- Streams[StreamIdx] = otherStream("PDB Stream", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::PDB, "PDB Stream", StreamIdx);
else if (StreamIdx == StreamDBI)
- Streams[StreamIdx] = otherStream("DBI Stream", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::DBI, "DBI Stream", StreamIdx);
else if (StreamIdx == StreamTPI)
- Streams[StreamIdx] = otherStream("TPI Stream", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::TPI, "TPI Stream", StreamIdx);
else if (StreamIdx == StreamIPI)
- Streams[StreamIdx] = otherStream("IPI Stream", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::IPI, "IPI Stream", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getGlobalSymbolStreamIndex())
- Streams[StreamIdx] = otherStream("Global Symbol Hash", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::GlobalHash, "Global Symbol Hash", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getPublicSymbolStreamIndex())
- Streams[StreamIdx] = otherStream("Public Symbol Hash", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::PublicHash, "Public Symbol Hash", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getSymRecordStreamIndex())
- Streams[StreamIdx] = symbolStream("Symbol Records", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Symbols, "Symbol Records", StreamIdx);
else if (Tpi && StreamIdx == Tpi->getTypeHashStreamIndex())
- Streams[StreamIdx] = otherStream("TPI Hash", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::TpiHash, "TPI Hash", StreamIdx);
else if (Tpi && StreamIdx == Tpi->getTypeHashStreamAuxIndex())
- Streams[StreamIdx] = otherStream("TPI Aux Hash", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "TPI Aux Hash", StreamIdx);
else if (Ipi && StreamIdx == Ipi->getTypeHashStreamIndex())
- Streams[StreamIdx] = otherStream("IPI Hash", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::IpiHash, "IPI Hash", StreamIdx);
else if (Ipi && StreamIdx == Ipi->getTypeHashStreamAuxIndex())
- Streams[StreamIdx] = otherStream("IPI Aux Hash", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "IPI Aux Hash", StreamIdx);
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Exception))
- Streams[StreamIdx] = otherStream("Exception Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Exception Data", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Fixup))
- Streams[StreamIdx] = otherStream("Fixup Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Fixup Data", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::FPO))
- Streams[StreamIdx] = otherStream("FPO Data", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "FPO Data", StreamIdx);
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::NewFPO))
- Streams[StreamIdx] = otherStream("New FPO Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "New FPO Data", StreamIdx);
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapFromSrc))
- Streams[StreamIdx] = otherStream("Omap From Source Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Omap From Source Data", StreamIdx);
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::OmapToSrc))
- Streams[StreamIdx] = otherStream("Omap To Source Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Omap To Source Data", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Pdata))
- Streams[StreamIdx] = otherStream("Pdata", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "Pdata", StreamIdx);
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdr))
- Streams[StreamIdx] = otherStream("Section Header Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Section Header Data", StreamIdx);
else if (Dbi &&
StreamIdx ==
Dbi->getDebugStreamIndex(DbgHeaderType::SectionHdrOrig))
- Streams[StreamIdx] =
- otherStream("Section Header Original Data", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::Other,
+ "Section Header Original Data", StreamIdx);
else if (Dbi &&
StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::TokenRidMap))
- Streams[StreamIdx] = otherStream("Token Rid Data", StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::Other, "Token Rid Data", StreamIdx);
else if (Dbi && StreamIdx == Dbi->getDebugStreamIndex(DbgHeaderType::Xdata))
- Streams[StreamIdx] = otherStream("Xdata", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "Xdata", StreamIdx);
else {
auto ModIter = ModStreams.find(StreamIdx);
auto NSIter = NamedStreams.find(StreamIdx);
@@ -167,9 +175,10 @@ void llvm::pdb::discoverStreamPurposes(PDBFile &File,
moduleStream(ModIter->second.Descriptor.getModuleName(), StreamIdx,
ModIter->second.Modi);
} else if (NSIter != NamedStreams.end()) {
- Streams[StreamIdx] = namedStream(NSIter->second, StreamIdx);
+ Streams[StreamIdx] =
+ stream(StreamPurpose::NamedStream, NSIter->second, StreamIdx);
} else {
- Streams[StreamIdx] = otherStream("???", StreamIdx);
+ Streams[StreamIdx] = stream(StreamPurpose::Other, "???", StreamIdx);
}
}
}
diff --git a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h
index 443267ca3290..0e2e80707361 100644
--- a/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h
+++ b/contrib/llvm/tools/llvm-pdbutil/StreamUtil.h
@@ -19,7 +19,20 @@
namespace llvm {
namespace pdb {
class PDBFile;
-enum class StreamPurpose { NamedStream, ModuleStream, Symbols, Other };
+enum class StreamPurpose {
+ NamedStream,
+ ModuleStream,
+ Symbols,
+ PDB,
+ DBI,
+ TPI,
+ IPI,
+ GlobalHash,
+ PublicHash,
+ TpiHash,
+ IpiHash,
+ Other
+};
struct StreamInfo {
public:
diff --git a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
index 089f7256536f..5b0d21f83db7 100644
--- a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
+++ b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.cpp
@@ -15,15 +15,18 @@
#include "Analyze.h"
#include "BytesOutputStyle.h"
-#include "Diff.h"
#include "DumpOutputStyle.h"
+#include "ExplainOutputStyle.h"
#include "InputFile.h"
#include "LinePrinter.h"
#include "OutputStyle.h"
+#include "PrettyClassDefinitionDumper.h"
#include "PrettyCompilandDumper.h"
+#include "PrettyEnumDumper.h"
#include "PrettyExternalSymbolDumper.h"
#include "PrettyFunctionDumper.h"
#include "PrettyTypeDumper.h"
+#include "PrettyTypedefDumper.h"
#include "PrettyVariableDumper.h"
#include "YAMLOutputStyle.h"
@@ -45,10 +48,12 @@
#include "llvm/DebugInfo/MSF/MSFBuilder.h"
#include "llvm/DebugInfo/PDB/GenericError.h"
#include "llvm/DebugInfo/PDB/IPDBEnumChildren.h"
+#include "llvm/DebugInfo/PDB/IPDBInjectedSource.h"
#include "llvm/DebugInfo/PDB/IPDBRawSymbol.h"
#include "llvm/DebugInfo/PDB/IPDBSession.h"
#include "llvm/DebugInfo/PDB/Native/DbiModuleDescriptorBuilder.h"
#include "llvm/DebugInfo/PDB/Native/DbiStreamBuilder.h"
+#include "llvm/DebugInfo/PDB/Native/InfoStream.h"
#include "llvm/DebugInfo/PDB/Native/InfoStreamBuilder.h"
#include "llvm/DebugInfo/PDB/Native/NativeSession.h"
#include "llvm/DebugInfo/PDB/Native/PDBFile.h"
@@ -63,7 +68,11 @@
#include "llvm/DebugInfo/PDB/PDBSymbolData.h"
#include "llvm/DebugInfo/PDB/PDBSymbolExe.h"
#include "llvm/DebugInfo/PDB/PDBSymbolFunc.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h"
#include "llvm/DebugInfo/PDB/PDBSymbolThunk.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h"
+#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h"
#include "llvm/Support/BinaryByteStream.h"
#include "llvm/Support/COM.h"
#include "llvm/Support/CommandLine.h"
@@ -71,6 +80,7 @@
#include "llvm/Support/FileOutputBuffer.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
@@ -82,7 +92,6 @@
#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
-
using namespace llvm;
using namespace llvm::codeview;
using namespace llvm::msf;
@@ -97,8 +106,6 @@ cl::SubCommand
PrettySubcommand("pretty",
"Dump semantic information about types and symbols");
-cl::SubCommand DiffSubcommand("diff", "Diff the contents of 2 PDB files");
-
cl::SubCommand
YamlToPdbSubcommand("yaml2pdb",
"Generate a PDB file from a YAML description");
@@ -113,6 +120,12 @@ cl::SubCommand
cl::SubCommand MergeSubcommand("merge",
"Merge multiple PDBs into a single PDB");
+cl::SubCommand ExplainSubcommand("explain",
+ "Explain the meaning of a file offset");
+
+cl::SubCommand ExportSubcommand("export",
+ "Write binary data from a stream to a file");
+
cl::OptionCategory TypeCategory("Symbol Type Options");
cl::OptionCategory FilterCategory("Filtering and Sorting Options");
cl::OptionCategory OtherOptions("Other Options");
@@ -147,6 +160,19 @@ cl::list<std::string> InputFilenames(cl::Positional,
cl::desc("<input PDB files>"),
cl::OneOrMore, cl::sub(PrettySubcommand));
+cl::opt<bool> InjectedSources("injected-sources",
+ cl::desc("Display injected sources"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+cl::opt<bool> ShowInjectedSourceContent(
+ "injected-source-content",
+ cl::desc("When displaying an injected source, display the file content"),
+ cl::cat(OtherOptions), cl::sub(PrettySubcommand));
+
+cl::list<std::string> WithName(
+ "with-name",
+ cl::desc("Display any symbol or type with the specified exact name"),
+ cl::cat(TypeCategory), cl::ZeroOrMore, cl::sub(PrettySubcommand));
+
cl::opt<bool> Compilands("compilands", cl::desc("Display compilands"),
cl::cat(TypeCategory), cl::sub(PrettySubcommand));
cl::opt<bool> Symbols("module-syms",
@@ -286,44 +312,6 @@ cl::opt<bool> NoEnumDefs("no-enum-definitions",
cl::cat(FilterCategory), cl::sub(PrettySubcommand));
}
-namespace diff {
-cl::opt<bool> PrintValueColumns(
- "values", cl::init(true),
- cl::desc("Print one column for each PDB with the field value"),
- cl::Optional, cl::sub(DiffSubcommand));
-cl::opt<bool>
- PrintResultColumn("result", cl::init(false),
- cl::desc("Print a column with the result status"),
- cl::Optional, cl::sub(DiffSubcommand));
-
-cl::list<std::string>
- RawModiEquivalences("modi-equivalence", cl::ZeroOrMore,
- cl::value_desc("left,right"),
- cl::desc("Modules with the specified indices will be "
- "treated as referring to the same module"),
- cl::sub(DiffSubcommand));
-
-cl::opt<std::string> LeftRoot(
- "left-bin-root", cl::Optional,
- cl::desc("Treats the specified path as the root of the tree containing "
- "binaries referenced by the left PDB. The root is stripped from "
- "embedded paths when doing equality comparisons."),
- cl::sub(DiffSubcommand));
-cl::opt<std::string> RightRoot(
- "right-bin-root", cl::Optional,
- cl::desc("Treats the specified path as the root of the tree containing "
- "binaries referenced by the right PDB. The root is stripped from "
- "embedded paths when doing equality comparisons"),
- cl::sub(DiffSubcommand));
-
-cl::opt<std::string> Left(cl::Positional, cl::desc("<left>"),
- cl::sub(DiffSubcommand));
-cl::opt<std::string> Right(cl::Positional, cl::desc("<right>"),
- cl::sub(DiffSubcommand));
-
-llvm::DenseMap<uint32_t, uint32_t> Equivalences;
-}
-
cl::OptionCategory FileOptions("Module & File Options");
namespace bytes {
@@ -482,6 +470,10 @@ cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"),
cl::opt<bool> DumpPublicExtras("public-extras",
cl::desc("dump Publics hashes and address maps"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
+cl::opt<bool>
+ DumpGSIRecords("gsi-records",
+ cl::desc("dump public / global common record stream"),
+ cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
cl::opt<bool> DumpSymbols("symbols", cl::desc("dump module symbols"),
cl::cat(SymbolOptions), cl::sub(DumpSubcommand));
@@ -525,8 +517,16 @@ cl::opt<bool> JustMyCode("jmc", cl::Optional,
cl::cat(FileOptions), cl::sub(DumpSubcommand));
// MISCELLANEOUS OPTIONS
+cl::opt<bool> DumpNamedStreams("named-streams",
+ cl::desc("dump PDB named stream table"),
+ cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+
cl::opt<bool> DumpStringTable("string-table", cl::desc("dump PDB String Table"),
cl::cat(MiscOptions), cl::sub(DumpSubcommand));
+cl::opt<bool> DumpStringTableDetails("string-table-details",
+ cl::desc("dump PDB String Table Details"),
+ cl::cat(MiscOptions),
+ cl::sub(DumpSubcommand));
cl::opt<bool> DumpSectionContribs("section-contribs",
cl::desc("dump section contributions"),
@@ -629,6 +629,47 @@ cl::opt<std::string>
PdbOutputFile("pdb", cl::desc("the name of the PDB file to write"),
cl::sub(MergeSubcommand));
}
+
+namespace explain {
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(ExplainSubcommand));
+
+cl::list<uint64_t> Offsets("offset", cl::desc("The file offset to explain"),
+ cl::sub(ExplainSubcommand), cl::OneOrMore);
+
+cl::opt<InputFileType> InputType(
+ "input-type", cl::desc("Specify how to interpret the input file"),
+ cl::init(InputFileType::PDBFile), cl::Optional, cl::sub(ExplainSubcommand),
+ cl::values(clEnumValN(InputFileType::PDBFile, "pdb-file",
+ "Treat input as a PDB file (default)"),
+ clEnumValN(InputFileType::PDBStream, "pdb-stream",
+ "Treat input as raw contents of PDB stream"),
+ clEnumValN(InputFileType::DBIStream, "dbi-stream",
+ "Treat input as raw contents of DBI stream"),
+ clEnumValN(InputFileType::Names, "names-stream",
+ "Treat input as raw contents of /names named stream"),
+ clEnumValN(InputFileType::ModuleStream, "mod-stream",
+ "Treat input as raw contents of a module stream")));
+} // namespace explain
+
+namespace exportstream {
+cl::list<std::string> InputFilename(cl::Positional,
+ cl::desc("<input PDB file>"), cl::Required,
+ cl::sub(ExportSubcommand));
+cl::opt<std::string> OutputFile("out",
+ cl::desc("The file to write the stream to"),
+ cl::Required, cl::sub(ExportSubcommand));
+cl::opt<std::string>
+ Stream("stream", cl::Required,
+ cl::desc("The index or name of the stream whose contents to export"),
+ cl::sub(ExportSubcommand));
+cl::opt<bool> ForceName("name",
+ cl::desc("Force the interpretation of -stream as a "
+ "string, even if it is a valid integer"),
+ cl::sub(ExportSubcommand), cl::Optional,
+ cl::init(false));
+} // namespace exportstream
}
static ExitOnError ExitOnErr;
@@ -761,7 +802,6 @@ static void pdb2Yaml(StringRef Path) {
}
static void dumpRaw(StringRef Path) {
-
InputFile IF = ExitOnErr(InputFile::open(Path));
auto O = llvm::make_unique<DumpOutputStyle>(IF);
@@ -785,18 +825,6 @@ static void dumpAnalysis(StringRef Path) {
ExitOnErr(O->dump());
}
-static void diff(StringRef Path1, StringRef Path2) {
- std::unique_ptr<IPDBSession> Session1;
- std::unique_ptr<IPDBSession> Session2;
-
- auto &File1 = loadPDB(Path1, Session1);
- auto &File2 = loadPDB(Path2, Session2);
-
- auto O = llvm::make_unique<DiffStyle>(File1, File2);
-
- ExitOnErr(O->dump());
-}
-
bool opts::pretty::shouldDumpSymLevel(SymLevel Search) {
if (SymTypes.empty())
return true;
@@ -840,6 +868,62 @@ bool opts::pretty::compareDataSymbols(
return getTypeLength(*F1) > getTypeLength(*F2);
}
+static std::string stringOr(std::string Str, std::string IfEmpty) {
+ return (Str.empty()) ? IfEmpty : Str;
+}
+
+static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) {
+ auto Sources = Session.getInjectedSources();
+ if (0 == Sources->getChildCount()) {
+ Printer.printLine("There are no injected sources.");
+ return;
+ }
+
+ while (auto IS = Sources->getNext()) {
+ Printer.NewLine();
+ std::string File = stringOr(IS->getFileName(), "<null>");
+ uint64_t Size = IS->getCodeByteSize();
+ std::string Obj = stringOr(IS->getObjectFileName(), "<null>");
+ std::string VFName = stringOr(IS->getVirtualFileName(), "<null>");
+ uint32_t CRC = IS->getCrc32();
+
+ std::string CompressionStr;
+ llvm::raw_string_ostream Stream(CompressionStr);
+ Stream << IS->getCompression();
+ WithColor(Printer, PDB_ColorItem::Path).get() << File;
+ Printer << " (";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Size;
+ Printer << " bytes): ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "obj";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::Path).get() << Obj;
+ Printer << ", ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "vname";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::Path).get() << VFName;
+ Printer << ", ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "crc";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << CRC;
+ Printer << ", ";
+ WithColor(Printer, PDB_ColorItem::Keyword).get() << "compression";
+ Printer << "=";
+ WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Stream.str();
+
+ if (!opts::pretty::ShowInjectedSourceContent)
+ continue;
+
+ // Set the indent level to 0 when printing file content.
+ int Indent = Printer.getIndentLevel();
+ Printer.Unindent(Indent);
+
+ Printer.printLine(IS->getCode());
+
+ // Re-indent back to the original level.
+ Printer.Indent(Indent);
+ }
+}
+
static void dumpPretty(StringRef Path) {
std::unique_ptr<IPDBSession> Session;
@@ -857,6 +941,8 @@ static void dumpPretty(StringRef Path) {
LinePrinter Printer(2, UseColor, Stream);
auto GlobalScope(Session->getGlobalScope());
+ if (!GlobalScope)
+ return;
std::string FileName(GlobalScope->getSymbolsFileName());
WithColor(Printer, PDB_ColorItem::None).get() << "Summary for ";
@@ -889,19 +975,96 @@ static void dumpPretty(StringRef Path) {
outs() << "HasPrivateSymbols ";
Printer.Unindent();
+ if (!opts::pretty::WithName.empty()) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get()
+ << "---SYMBOLS & TYPES BY NAME---";
+
+ for (StringRef Name : opts::pretty::WithName) {
+ auto Symbols = GlobalScope->findChildren(
+ PDB_SymType::None, Name, PDB_NameSearchFlags::NS_CaseSensitive);
+ if (!Symbols || Symbols->getChildCount() == 0) {
+ Printer.formatLine("[not found] - {0}", Name);
+ continue;
+ }
+ Printer.formatLine("[{0} occurrences] - {1}", Symbols->getChildCount(),
+ Name);
+
+ AutoIndent Indent(Printer);
+ Printer.NewLine();
+
+ while (auto Symbol = Symbols->getNext()) {
+ switch (Symbol->getSymTag()) {
+ case PDB_SymType::Typedef: {
+ TypedefDumper TD(Printer);
+ std::unique_ptr<PDBSymbolTypeTypedef> T =
+ llvm::unique_dyn_cast<PDBSymbolTypeTypedef>(std::move(Symbol));
+ TD.start(*T);
+ break;
+ }
+ case PDB_SymType::Enum: {
+ EnumDumper ED(Printer);
+ std::unique_ptr<PDBSymbolTypeEnum> E =
+ llvm::unique_dyn_cast<PDBSymbolTypeEnum>(std::move(Symbol));
+ ED.start(*E);
+ break;
+ }
+ case PDB_SymType::UDT: {
+ ClassDefinitionDumper CD(Printer);
+ std::unique_ptr<PDBSymbolTypeUDT> C =
+ llvm::unique_dyn_cast<PDBSymbolTypeUDT>(std::move(Symbol));
+ CD.start(*C);
+ break;
+ }
+ case PDB_SymType::BaseClass:
+ case PDB_SymType::Friend: {
+ TypeDumper TD(Printer);
+ Symbol->dump(TD);
+ break;
+ }
+ case PDB_SymType::Function: {
+ FunctionDumper FD(Printer);
+ std::unique_ptr<PDBSymbolFunc> F =
+ llvm::unique_dyn_cast<PDBSymbolFunc>(std::move(Symbol));
+ FD.start(*F, FunctionDumper::PointerType::None);
+ break;
+ }
+ case PDB_SymType::Data: {
+ VariableDumper VD(Printer);
+ std::unique_ptr<PDBSymbolData> D =
+ llvm::unique_dyn_cast<PDBSymbolData>(std::move(Symbol));
+ VD.start(*D);
+ break;
+ }
+ case PDB_SymType::PublicSymbol: {
+ ExternalSymbolDumper ED(Printer);
+ std::unique_ptr<PDBSymbolPublicSymbol> PS =
+ llvm::unique_dyn_cast<PDBSymbolPublicSymbol>(std::move(Symbol));
+ ED.dump(*PS);
+ break;
+ }
+ default:
+ llvm_unreachable("Unexpected symbol tag!");
+ }
+ }
+ }
+ llvm::outs().flush();
+ }
+
if (opts::pretty::Compilands) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get()
<< "---COMPILANDS---";
- Printer.Indent();
- auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>();
- CompilandDumper Dumper(Printer);
- CompilandDumpFlags options = CompilandDumper::Flags::None;
- if (opts::pretty::Lines)
- options = options | CompilandDumper::Flags::Lines;
- while (auto Compiland = Compilands->getNext())
- Dumper.start(*Compiland, options);
- Printer.Unindent();
+ if (auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>()) {
+ Printer.Indent();
+ CompilandDumper Dumper(Printer);
+ CompilandDumpFlags options = CompilandDumper::Flags::None;
+ if (opts::pretty::Lines)
+ options = options | CompilandDumper::Flags::Lines;
+ while (auto Compiland = Compilands->getNext())
+ Dumper.start(*Compiland, options);
+ Printer.Unindent();
+ }
}
if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) {
@@ -916,12 +1079,13 @@ static void dumpPretty(StringRef Path) {
if (opts::pretty::Symbols) {
Printer.NewLine();
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---SYMBOLS---";
- Printer.Indent();
- auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>();
- CompilandDumper Dumper(Printer);
- while (auto Compiland = Compilands->getNext())
- Dumper.start(*Compiland, true);
- Printer.Unindent();
+ if (auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>()) {
+ Printer.Indent();
+ CompilandDumper Dumper(Printer);
+ while (auto Compiland = Compilands->getNext())
+ Dumper.start(*Compiland, true);
+ Printer.Unindent();
+ }
}
if (opts::pretty::Globals) {
@@ -929,45 +1093,49 @@ static void dumpPretty(StringRef Path) {
WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---GLOBALS---";
Printer.Indent();
if (shouldDumpSymLevel(opts::pretty::SymLevel::Functions)) {
- FunctionDumper Dumper(Printer);
- auto Functions = GlobalScope->findAllChildren<PDBSymbolFunc>();
- if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) {
- while (auto Function = Functions->getNext()) {
- Printer.NewLine();
- Dumper.start(*Function, FunctionDumper::PointerType::None);
- }
- } else {
- std::vector<std::unique_ptr<PDBSymbolFunc>> Funcs;
- while (auto Func = Functions->getNext())
- Funcs.push_back(std::move(Func));
- std::sort(Funcs.begin(), Funcs.end(),
- opts::pretty::compareFunctionSymbols);
- for (const auto &Func : Funcs) {
- Printer.NewLine();
- Dumper.start(*Func, FunctionDumper::PointerType::None);
+ if (auto Functions = GlobalScope->findAllChildren<PDBSymbolFunc>()) {
+ FunctionDumper Dumper(Printer);
+ if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) {
+ while (auto Function = Functions->getNext()) {
+ Printer.NewLine();
+ Dumper.start(*Function, FunctionDumper::PointerType::None);
+ }
+ } else {
+ std::vector<std::unique_ptr<PDBSymbolFunc>> Funcs;
+ while (auto Func = Functions->getNext())
+ Funcs.push_back(std::move(Func));
+ llvm::sort(Funcs.begin(), Funcs.end(),
+ opts::pretty::compareFunctionSymbols);
+ for (const auto &Func : Funcs) {
+ Printer.NewLine();
+ Dumper.start(*Func, FunctionDumper::PointerType::None);
+ }
}
}
}
if (shouldDumpSymLevel(opts::pretty::SymLevel::Data)) {
- auto Vars = GlobalScope->findAllChildren<PDBSymbolData>();
- VariableDumper Dumper(Printer);
- if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) {
- while (auto Var = Vars->getNext())
- Dumper.start(*Var);
- } else {
- std::vector<std::unique_ptr<PDBSymbolData>> Datas;
- while (auto Var = Vars->getNext())
- Datas.push_back(std::move(Var));
- std::sort(Datas.begin(), Datas.end(), opts::pretty::compareDataSymbols);
- for (const auto &Var : Datas)
- Dumper.start(*Var);
+ if (auto Vars = GlobalScope->findAllChildren<PDBSymbolData>()) {
+ VariableDumper Dumper(Printer);
+ if (opts::pretty::SymbolOrder == opts::pretty::SymbolSortMode::None) {
+ while (auto Var = Vars->getNext())
+ Dumper.start(*Var);
+ } else {
+ std::vector<std::unique_ptr<PDBSymbolData>> Datas;
+ while (auto Var = Vars->getNext())
+ Datas.push_back(std::move(Var));
+ llvm::sort(Datas.begin(), Datas.end(),
+ opts::pretty::compareDataSymbols);
+ for (const auto &Var : Datas)
+ Dumper.start(*Var);
+ }
}
}
if (shouldDumpSymLevel(opts::pretty::SymLevel::Thunks)) {
- auto Thunks = GlobalScope->findAllChildren<PDBSymbolThunk>();
- CompilandDumper Dumper(Printer);
- while (auto Thunk = Thunks->getNext())
- Dumper.dump(*Thunk);
+ if (auto Thunks = GlobalScope->findAllChildren<PDBSymbolThunk>()) {
+ CompilandDumper Dumper(Printer);
+ while (auto Thunk = Thunks->getNext())
+ Dumper.dump(*Thunk);
+ }
}
Printer.Unindent();
}
@@ -981,6 +1149,19 @@ static void dumpPretty(StringRef Path) {
if (opts::pretty::Lines) {
Printer.NewLine();
}
+ if (opts::pretty::InjectedSources) {
+ Printer.NewLine();
+ WithColor(Printer, PDB_ColorItem::SectionHeader).get()
+ << "---INJECTED SOURCES---";
+ AutoIndent Indent1(Printer);
+
+ if (ReaderType == PDB_ReaderType::Native)
+ Printer.printLine(
+ "Injected sources are not supported with the native reader.");
+ else
+ dumpInjectedSources(Printer, *Session);
+ }
+
outs().flush();
}
@@ -1033,6 +1214,58 @@ static void mergePdbs() {
ExitOnErr(Builder.commit(OutFile));
}
+static void explain() {
+ std::unique_ptr<IPDBSession> Session;
+ InputFile IF =
+ ExitOnErr(InputFile::open(opts::explain::InputFilename.front(), true));
+
+ for (uint64_t Off : opts::explain::Offsets) {
+ auto O = llvm::make_unique<ExplainOutputStyle>(IF, Off);
+
+ ExitOnErr(O->dump());
+ }
+}
+
+static void exportStream() {
+ std::unique_ptr<IPDBSession> Session;
+ PDBFile &File = loadPDB(opts::exportstream::InputFilename.front(), Session);
+
+ std::unique_ptr<MappedBlockStream> SourceStream;
+ uint32_t Index = 0;
+ bool Success = false;
+ std::string OutFileName = opts::exportstream::OutputFile;
+
+ if (!opts::exportstream::ForceName) {
+ // First try to parse it as an integer, if it fails fall back to treating it
+ // as a named stream.
+ if (to_integer(opts::exportstream::Stream, Index)) {
+ if (Index >= File.getNumStreams()) {
+ errs() << "Error: " << Index << " is not a valid stream index.\n";
+ exit(1);
+ }
+ Success = true;
+ outs() << "Dumping contents of stream index " << Index << " to file "
+ << OutFileName << ".\n";
+ }
+ }
+
+ if (!Success) {
+ InfoStream &IS = cantFail(File.getPDBInfoStream());
+ Index = ExitOnErr(IS.getNamedStreamIndex(opts::exportstream::Stream));
+ outs() << "Dumping contents of stream '" << opts::exportstream::Stream
+ << "' (index " << Index << ") to file " << OutFileName << ".\n";
+ }
+
+ SourceStream = MappedBlockStream::createIndexedStream(
+ File.getMsfLayout(), File.getMsfBuffer(), Index, File.getAllocator());
+ auto OutFile = ExitOnErr(
+ FileOutputBuffer::create(OutFileName, SourceStream->getLength()));
+ FileBufferByteStream DestStream(std::move(OutFile), llvm::support::little);
+ BinaryStreamWriter Writer(DestStream);
+ ExitOnErr(Writer.writeStreamRef(*SourceStream));
+ ExitOnErr(DestStream.commit());
+}
+
static bool parseRange(StringRef Str,
Optional<opts::bytes::NumberRange> &Parsed) {
if (Str.empty())
@@ -1064,21 +1297,11 @@ static void simplifyChunkList(llvm::cl::list<opts::ModuleSubsection> &Chunks) {
Chunks.push_back(opts::ModuleSubsection::All);
}
-int main(int argc_, const char *argv_[]) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv_[0]);
- PrettyStackTraceProgram X(argc_, argv_);
-
+int main(int Argc, const char **Argv) {
+ InitLLVM X(Argc, Argv);
ExitOnErr.setBanner("llvm-pdbutil: ");
- SmallVector<const char *, 256> argv;
- SpecificBumpPtrAllocator<char> ArgAllocator;
- ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector(
- argv, makeArrayRef(argv_, argc_), ArgAllocator)));
-
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
-
- cl::ParseCommandLineOptions(argv.size(), argv.data(), "LLVM PDB Dumper\n");
+ cl::ParseCommandLineOptions(Argc, Argv, "LLVM PDB Dumper\n");
if (opts::BytesSubcommand) {
if (!parseRange(opts::bytes::DumpBlockRangeOpt,
@@ -1113,6 +1336,7 @@ int main(int argc_, const char *argv_[]) {
opts::dump::DumpStreams = true;
opts::dump::DumpStreamBlocks = true;
opts::dump::DumpStringTable = true;
+ opts::dump::DumpStringTableDetails = true;
opts::dump::DumpSummary = true;
opts::dump::DumpSymbols = true;
opts::dump::DumpSymbolStats = true;
@@ -1146,11 +1370,6 @@ int main(int argc_, const char *argv_[]) {
if (opts::pdb2yaml::DumpModules)
opts::pdb2yaml::DbiStream = true;
}
- if (opts::DiffSubcommand) {
- if (!opts::diff::PrintResultColumn && !opts::diff::PrintValueColumns) {
- llvm::errs() << "WARNING: No diff columns specified\n";
- }
- }
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
@@ -1205,27 +1424,16 @@ int main(int argc_, const char *argv_[]) {
llvm::for_each(opts::dump::InputFilenames, dumpRaw);
} else if (opts::BytesSubcommand) {
llvm::for_each(opts::bytes::InputFilenames, dumpBytes);
- } else if (opts::DiffSubcommand) {
- for (StringRef S : opts::diff::RawModiEquivalences) {
- StringRef Left;
- StringRef Right;
- std::tie(Left, Right) = S.split(',');
- uint32_t X, Y;
- if (!to_integer(Left, X) || !to_integer(Right, Y)) {
- errs() << formatv("invalid value {0} specified for modi equivalence\n",
- S);
- exit(1);
- }
- opts::diff::Equivalences[X] = Y;
- }
-
- diff(opts::diff::Left, opts::diff::Right);
} else if (opts::MergeSubcommand) {
if (opts::merge::InputFilenames.size() < 2) {
errs() << "merge subcommand requires at least 2 input files.\n";
exit(1);
}
mergePdbs();
+ } else if (opts::ExplainSubcommand) {
+ explain();
+ } else if (opts::ExportSubcommand) {
+ exportStream();
}
outs().flush();
diff --git a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
index 3ce03d5880af..7496adaeb62f 100644
--- a/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
+++ b/contrib/llvm/tools/llvm-pdbutil/llvm-pdbutil.h
@@ -75,6 +75,8 @@ bool compareFunctionSymbols(
bool compareDataSymbols(const std::unique_ptr<llvm::pdb::PDBSymbolData> &F1,
const std::unique_ptr<llvm::pdb::PDBSymbolData> &F2);
+extern llvm::cl::list<std::string> WithName;
+
extern llvm::cl::opt<bool> Compilands;
extern llvm::cl::opt<bool> Symbols;
extern llvm::cl::opt<bool> Globals;
@@ -142,7 +144,9 @@ extern llvm::cl::opt<bool> DumpLines;
extern llvm::cl::opt<bool> DumpInlineeLines;
extern llvm::cl::opt<bool> DumpXmi;
extern llvm::cl::opt<bool> DumpXme;
+extern llvm::cl::opt<bool> DumpNamedStreams;
extern llvm::cl::opt<bool> DumpStringTable;
+extern llvm::cl::opt<bool> DumpStringTableDetails;
extern llvm::cl::opt<bool> DumpTypes;
extern llvm::cl::opt<bool> DumpTypeData;
extern llvm::cl::opt<bool> DumpTypeExtras;
@@ -158,6 +162,7 @@ extern llvm::cl::opt<uint32_t> DumpModi;
extern llvm::cl::opt<bool> JustMyCode;
extern llvm::cl::opt<bool> DumpSymbols;
extern llvm::cl::opt<bool> DumpSymRecordBytes;
+extern llvm::cl::opt<bool> DumpGSIRecords;
extern llvm::cl::opt<bool> DumpGlobals;
extern llvm::cl::opt<bool> DumpGlobalExtras;
extern llvm::cl::opt<bool> DumpPublics;
@@ -187,13 +192,19 @@ extern llvm::cl::list<ModuleSubsection> DumpModuleSubsections;
extern llvm::cl::opt<bool> DumpModuleSyms;
} // namespace pdb2yaml
-namespace diff {
-extern llvm::cl::opt<bool> PrintValueColumns;
-extern llvm::cl::opt<bool> PrintResultColumn;
-extern llvm::DenseMap<uint32_t, uint32_t> Equivalences;
-extern llvm::cl::opt<std::string> LeftRoot;
-extern llvm::cl::opt<std::string> RightRoot;
-} // namespace diff
+namespace explain {
+enum class InputFileType { PDBFile, PDBStream, DBIStream, Names, ModuleStream };
+
+extern llvm::cl::list<std::string> InputFilename;
+extern llvm::cl::list<uint64_t> Offsets;
+extern llvm::cl::opt<InputFileType> InputType;
+} // namespace explain
+
+namespace exportstream {
+extern llvm::cl::opt<std::string> OutputFile;
+extern llvm::cl::opt<std::string> Stream;
+extern llvm::cl::opt<bool> ForceName;
+} // namespace exportstream
}
#endif
diff --git a/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp b/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp
index 9afd0ae92eae..1a0b9e127bbc 100644
--- a/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp
+++ b/contrib/llvm/tools/llvm-profdata/llvm-profdata.cpp
@@ -24,32 +24,42 @@
#include "llvm/Support/Errc.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Format.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
+#include "llvm/Support/WithColor.h"
#include "llvm/Support/ThreadPool.h"
#include "llvm/Support/raw_ostream.h"
#include <algorithm>
using namespace llvm;
-enum ProfileFormat { PF_None = 0, PF_Text, PF_Binary, PF_GCC };
+enum ProfileFormat {
+ PF_None = 0,
+ PF_Text,
+ PF_Compact_Binary,
+ PF_GCC,
+ PF_Binary
+};
-static void warn(StringRef Prefix, Twine Message, std::string Whence = "",
+static void warn(Twine Message, std::string Whence = "",
std::string Hint = "") {
- errs() << Prefix;
+ WithColor::warning();
if (!Whence.empty())
errs() << Whence << ": ";
errs() << Message << "\n";
if (!Hint.empty())
- errs() << Hint << "\n";
+ WithColor::note() << Hint << "\n";
}
static void exitWithError(Twine Message, std::string Whence = "",
std::string Hint = "") {
- warn("error: ", Message, Whence, Hint);
+ WithColor::error();
+ if (!Whence.empty())
+ errs() << Whence << ": ";
+ errs() << Message << "\n";
+ if (!Hint.empty())
+ WithColor::note() << Hint << "\n";
::exit(1);
}
@@ -232,7 +242,8 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
if (OutputFilename.compare("-") == 0)
exitWithError("Cannot write indexed profdata format to stdout.");
- if (OutputFormat != PF_Binary && OutputFormat != PF_Text)
+ if (OutputFormat != PF_Binary && OutputFormat != PF_Compact_Binary &&
+ OutputFormat != PF_Text)
exitWithError("Unknown format is specified.");
std::error_code EC;
@@ -298,7 +309,7 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
if (isFatalError(IPE))
exitWithError(make_error<InstrProfError>(IPE), WC->ErrWhence);
else
- warn("warning: ", toString(make_error<InstrProfError>(IPE)),
+ warn(toString(make_error<InstrProfError>(IPE)),
WC->ErrWhence);
}
@@ -312,8 +323,8 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs,
}
static sampleprof::SampleProfileFormat FormatMap[] = {
- sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Binary,
- sampleprof::SPF_GCC};
+ sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary,
+ sampleprof::SPF_GCC, sampleprof::SPF_Binary};
static void mergeSampleProfile(const WeightedFileVector &Inputs,
StringRef OutputFilename,
@@ -462,6 +473,8 @@ static int merge_main(int argc, const char *argv[]) {
cl::opt<ProfileFormat> OutputFormat(
cl::desc("Format of output profile"), cl::init(PF_Binary),
cl::values(clEnumValN(PF_Binary, "binary", "Binary encoding (default)"),
+ clEnumValN(PF_Compact_Binary, "compbinary",
+ "Compact binary encoding"),
clEnumValN(PF_Text, "text", "Text encoding"),
clEnumValN(PF_GCC, "gcc",
"GCC encoding (only meaningful for -sample)")));
@@ -787,7 +800,7 @@ static int show_main(int argc, const char *argv[]) {
exitWithErrorCode(EC, OutputFilename);
if (ShowAllFunctions && !ShowFunction.empty())
- errs() << "warning: -function argument ignored: showing all functions\n";
+ WithColor::warning() << "-function argument ignored: showing all functions\n";
std::vector<uint32_t> Cutoffs(DetailedSummaryCutoffs.begin(),
DetailedSummaryCutoffs.end());
@@ -802,10 +815,7 @@ static int show_main(int argc, const char *argv[]) {
}
int main(int argc, const char *argv[]) {
- // Print a stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
StringRef ProgName(sys::path::filename(argv[0]));
if (argc > 1) {
diff --git a/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h b/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h
index 4417aa60fe90..51128f113c4c 100644
--- a/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h
+++ b/contrib/llvm/tools/llvm-readobj/ARMEHABIPrinter.h
@@ -323,10 +323,10 @@ inline void OpcodeDecoder::Decode(const uint8_t *Opcodes, off_t Offset,
template <typename ET>
class PrinterContext {
- typedef typename object::ELFFile<ET>::Elf_Sym Elf_Sym;
- typedef typename object::ELFFile<ET>::Elf_Shdr Elf_Shdr;
- typedef typename object::ELFFile<ET>::Elf_Rel Elf_Rel;
- typedef typename object::ELFFile<ET>::Elf_Word Elf_Word;
+ typedef typename ET::Sym Elf_Sym;
+ typedef typename ET::Shdr Elf_Shdr;
+ typedef typename ET::Rel Elf_Rel;
+ typedef typename ET::Word Elf_Word;
ScopedPrinter &SW;
const object::ELFFile<ET> *ELF;
@@ -386,7 +386,7 @@ PrinterContext<ET>::FunctionAtAddress(unsigned Section,
}
template <typename ET>
-const typename object::ELFFile<ET>::Elf_Shdr *
+const typename ET::Shdr *
PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex,
off_t IndexTableOffset) const {
/// Iterate through the sections, searching for the relocation section
@@ -410,7 +410,7 @@ PrinterContext<ET>::FindExceptionTable(unsigned IndexSectionIndex,
if (R.r_offset != static_cast<unsigned>(IndexTableOffset))
continue;
- typename object::ELFFile<ET>::Elf_Rela RelA;
+ typename ET::Rela RelA;
RelA.r_offset = R.r_offset;
RelA.r_info = R.r_info;
RelA.r_addend = 0;
@@ -586,4 +586,3 @@ void PrinterContext<ET>::PrintUnwindInformation() const {
}
#endif
-
diff --git a/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp b/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
index 1a033b1eb42e..a90840b22c8d 100644
--- a/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
+++ b/contrib/llvm/tools/llvm-readobj/ARMWinEHPrinter.cpp
@@ -33,7 +33,7 @@
// (.pdata) entry.
//
// The exception data contains information about the frame setup, all of the
-// epilouge scopes (for functions for which there are multiple exit points) and
+// epilogue scopes (for functions for which there are multiple exit points) and
// the associated exception handler. Additionally, the entry contains byte-code
// describing how to unwind the function (c.f. Decoder::decodeOpcodes).
//
diff --git a/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp b/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp
index 0e76e75c085d..0ed4ccd09f6f 100644
--- a/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp
+++ b/contrib/llvm/tools/llvm-readobj/COFFDumper.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements the COFF-specific dumper for llvm-readobj.
+/// This file implements the COFF-specific dumper for llvm-readobj.
///
//===----------------------------------------------------------------------===//
@@ -67,6 +67,8 @@ struct LoadConfigTables {
uint32_t GuardFlags = 0;
uint64_t GuardFidTableVA = 0;
uint64_t GuardFidTableCount = 0;
+ uint64_t GuardLJmpTableVA = 0;
+ uint64_t GuardLJmpTableCount = 0;
};
class COFFDumper : public ObjDumper {
@@ -242,7 +244,7 @@ std::error_code createCOFFDumper(const object::ObjectFile *Obj,
} // namespace llvm
-// Given a a section and an offset into this section the function returns the
+// Given a section and an offset into this section the function returns the
// symbol used for the relocation at the offset.
std::error_code COFFDumper::resolveSymbol(const coff_section *Section,
uint64_t Offset, SymbolRef &Sym) {
@@ -605,8 +607,8 @@ void COFFDumper::cacheRelocations() {
RelocMap[Section].push_back(Reloc);
// Sort relocations by address.
- std::sort(RelocMap[Section].begin(), RelocMap[Section].end(),
- relocAddressLess);
+ llvm::sort(RelocMap[Section].begin(), RelocMap[Section].end(),
+ relocAddressLess);
}
}
@@ -767,7 +769,7 @@ void COFFDumper::printRVATable(uint64_t TableVA, uint64_t Count,
for (uintptr_t I = TableStart; I < TableEnd; I += EntrySize) {
uint32_t RVA = *reinterpret_cast<const ulittle32_t *>(I);
raw_ostream &OS = W.startLine();
- OS << "0x" << W.hex(Obj->getImageBase() + RVA);
+ OS << W.hex(Obj->getImageBase() + RVA);
if (PrintExtra)
PrintExtra(OS, reinterpret_cast<const uint8_t *>(I));
OS << '\n';
@@ -800,6 +802,11 @@ void COFFDumper::printCOFFLoadConfig() {
printRVATable(Tables.GuardFidTableVA, Tables.GuardFidTableCount, 4);
}
}
+
+ if (Tables.GuardLJmpTableVA) {
+ ListScope LS(W, "GuardLJmpTable");
+ printRVATable(Tables.GuardLJmpTableVA, Tables.GuardLJmpTableCount, 4);
+ }
}
template <typename T>
@@ -879,6 +886,9 @@ void COFFDumper::printCOFFLoadConfig(const T *Conf, LoadConfigTables &Tables) {
W.printHex("GuardRFVerifyStackPointerFunctionPointer",
Conf->GuardRFVerifyStackPointerFunctionPointer);
W.printHex("HotPatchTableOffset", Conf->HotPatchTableOffset);
+
+ Tables.GuardLJmpTableVA = Conf->GuardLongJumpTargetTable;
+ Tables.GuardLJmpTableCount = Conf->GuardLongJumpTargetCount;
}
void COFFDumper::printBaseOfDataField(const pe32_header *Hdr) {
@@ -892,7 +902,9 @@ void COFFDumper::printCodeViewDebugInfo() {
for (const SectionRef &S : Obj->sections()) {
StringRef SectionName;
error(S.getName(SectionName));
- if (SectionName == ".debug$T")
+ // .debug$T is a standard CodeView type section, while .debug$P is the same
+ // format but used for MSVC precompiled header object files.
+ if (SectionName == ".debug$T" || SectionName == ".debug$P")
printCodeViewTypeSection(SectionName, S);
}
for (const SectionRef &S : Obj->sections()) {
@@ -1812,10 +1824,9 @@ void COFFDumper::printStackMap() const {
if (Obj->isLittleEndian())
prettyPrintStackMap(
- llvm::outs(),
- StackMapV2Parser<support::little>(StackMapContentsArray));
+ W, StackMapV2Parser<support::little>(StackMapContentsArray));
else
- prettyPrintStackMap(llvm::outs(),
+ prettyPrintStackMap(W,
StackMapV2Parser<support::big>(StackMapContentsArray));
}
diff --git a/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp b/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp
index 3b546b3ef508..18010c34f0f3 100644
--- a/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp
+++ b/contrib/llvm/tools/llvm-readobj/COFFImportDumper.cpp
@@ -8,41 +8,51 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements the COFF import library dumper for llvm-readobj.
+/// This file implements the COFF import library dumper for llvm-readobj.
///
//===----------------------------------------------------------------------===//
#include "llvm/BinaryFormat/COFF.h"
#include "llvm/Object/COFF.h"
#include "llvm/Object/COFFImportFile.h"
+#include "llvm/Support/ScopedPrinter.h"
using namespace llvm::object;
namespace llvm {
-void dumpCOFFImportFile(const COFFImportFile *File) {
- outs() << '\n';
- outs() << "File: " << File->getFileName() << "\n";
- outs() << "Format: COFF-import-file\n";
+void dumpCOFFImportFile(const COFFImportFile *File, ScopedPrinter &Writer) {
+ Writer.startLine() << '\n';
+ Writer.printString("File", File->getFileName());
+ Writer.printString("Format", "COFF-import-file");
const coff_import_header *H = File->getCOFFImportHeader();
switch (H->getType()) {
- case COFF::IMPORT_CODE: outs() << "Type: code\n"; break;
- case COFF::IMPORT_DATA: outs() << "Type: data\n"; break;
- case COFF::IMPORT_CONST: outs() << "Type: const\n"; break;
+ case COFF::IMPORT_CODE: Writer.printString("Type", "code"); break;
+ case COFF::IMPORT_DATA: Writer.printString("Type", "data"); break;
+ case COFF::IMPORT_CONST: Writer.printString("Type", "const"); break;
}
switch (H->getNameType()) {
- case COFF::IMPORT_ORDINAL: outs() << "Name type: ordinal\n"; break;
- case COFF::IMPORT_NAME: outs() << "Name type: name\n"; break;
- case COFF::IMPORT_NAME_NOPREFIX: outs() << "Name type: noprefix\n"; break;
- case COFF::IMPORT_NAME_UNDECORATE: outs() << "Name type: undecorate\n"; break;
+ case COFF::IMPORT_ORDINAL:
+ Writer.printString("Name type", "ordinal");
+ break;
+ case COFF::IMPORT_NAME:
+ Writer.printString("Name type", "name");
+ break;
+ case COFF::IMPORT_NAME_NOPREFIX:
+ Writer.printString("Name type", "noprefix");
+ break;
+ case COFF::IMPORT_NAME_UNDECORATE:
+ Writer.printString("Name type", "undecorate");
+ break;
}
for (const object::BasicSymbolRef &Sym : File->symbols()) {
- outs() << "Symbol: ";
- Sym.printName(outs());
- outs() << "\n";
+ raw_ostream &OS = Writer.startLine();
+ OS << "Symbol: ";
+ Sym.printName(OS);
+ OS << "\n";
}
}
diff --git a/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h
new file mode 100644
index 000000000000..5a1eef1d007d
--- /dev/null
+++ b/contrib/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h
@@ -0,0 +1,245 @@
+//===--- DwarfCFIEHPrinter.h - DWARF-based Unwind Information Printer -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H
+#define LLVM_TOOLS_LLVM_READOBJ_DWARFCFIEHPRINTER_H
+
+#include "Error.h"
+#include "llvm-readobj.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/BinaryFormat/Dwarf.h"
+#include "llvm/Object/ELF.h"
+#include "llvm/Object/ELFTypes.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/DebugInfo/DWARF/DWARFDataExtractor.h"
+#include "llvm/DebugInfo/DWARF/DWARFDebugFrame.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/Format.h"
+#include "llvm/Support/type_traits.h"
+
+namespace llvm {
+namespace DwarfCFIEH {
+
+template <typename ELFT>
+class PrinterContext {
+ ScopedPrinter &W;
+ const object::ELFFile<ELFT> *Obj;
+
+ void printEHFrameHdr(uint64_t Offset, uint64_t Address, uint64_t Size) const;
+
+ void printEHFrame(const typename ELFT::Shdr *EHFrameShdr) const;
+
+public:
+ PrinterContext(ScopedPrinter &W, const object::ELFFile<ELFT> *Obj)
+ : W(W), Obj(Obj) {}
+
+ void printUnwindInformation() const;
+};
+
+template <class ELFO>
+static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj,
+ uint64_t Addr) {
+ auto Sections = Obj->sections();
+ if (Error E = Sections.takeError())
+ reportError(toString(std::move(E)));
+
+ for (const auto &Shdr : *Sections)
+ if (Shdr.sh_addr == Addr)
+ return &Shdr;
+ return nullptr;
+}
+
+template <typename ELFT>
+void PrinterContext<ELFT>::printUnwindInformation() const {
+ const typename ELFT::Phdr *EHFramePhdr = nullptr;
+
+ auto PHs = Obj->program_headers();
+ if (Error E = PHs.takeError())
+ reportError(toString(std::move(E)));
+
+ for (const auto &Phdr : *PHs) {
+ if (Phdr.p_type == ELF::PT_GNU_EH_FRAME) {
+ EHFramePhdr = &Phdr;
+ if (Phdr.p_memsz != Phdr.p_filesz)
+ reportError("p_memsz does not match p_filesz for GNU_EH_FRAME");
+ break;
+ }
+ }
+
+ if (EHFramePhdr)
+ printEHFrameHdr(EHFramePhdr->p_offset, EHFramePhdr->p_vaddr,
+ EHFramePhdr->p_memsz);
+
+ auto Sections = Obj->sections();
+ if (Error E = Sections.takeError())
+ reportError(toString(std::move(E)));
+
+ for (const auto &Shdr : *Sections) {
+ auto SectionName = Obj->getSectionName(&Shdr);
+ if (Error E = SectionName.takeError())
+ reportError(toString(std::move(E)));
+
+ if (*SectionName == ".eh_frame")
+ printEHFrame(&Shdr);
+ }
+}
+
+template <typename ELFT>
+void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset,
+ uint64_t EHFrameHdrAddress,
+ uint64_t EHFrameHdrSize) const {
+ ListScope L(W, "EH_FRAME Header");
+ W.startLine() << format("Address: 0x%" PRIx64 "\n", EHFrameHdrAddress);
+ W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset);
+ W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize);
+
+ const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress);
+ if (EHFrameHdrShdr) {
+ auto SectionName = Obj->getSectionName(EHFrameHdrShdr);
+ if (Error E = SectionName.takeError())
+ reportError(toString(std::move(E)));
+
+ W.printString("Corresponding Section", *SectionName);
+ }
+
+ DataExtractor DE(
+ StringRef(reinterpret_cast<const char *>(Obj->base()) + EHFrameHdrOffset,
+ EHFrameHdrSize),
+ ELFT::TargetEndianness == support::endianness::little,
+ ELFT::Is64Bits ? 8 : 4);
+
+ DictScope D(W, "Header");
+ uint32_t Offset = 0;
+
+ auto Version = DE.getU8(&Offset);
+ W.printNumber("version", Version);
+ if (Version != 1)
+ reportError("only version 1 of .eh_frame_hdr is supported");
+
+ uint64_t EHFramePtrEnc = DE.getU8(&Offset);
+ W.startLine() << format("eh_frame_ptr_enc: 0x%" PRIx64 "\n", EHFramePtrEnc);
+ if (EHFramePtrEnc != (dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata4))
+ reportError("unexpected encoding eh_frame_ptr_enc");
+
+ uint64_t FDECountEnc = DE.getU8(&Offset);
+ W.startLine() << format("fde_count_enc: 0x%" PRIx64 "\n", FDECountEnc);
+ if (FDECountEnc != dwarf::DW_EH_PE_udata4)
+ reportError("unexpected encoding fde_count_enc");
+
+ uint64_t TableEnc = DE.getU8(&Offset);
+ W.startLine() << format("table_enc: 0x%" PRIx64 "\n", TableEnc);
+ if (TableEnc != (dwarf::DW_EH_PE_datarel | dwarf::DW_EH_PE_sdata4))
+ reportError("unexpected encoding table_enc");
+
+ auto EHFramePtr = DE.getSigned(&Offset, 4) + EHFrameHdrAddress + 4;
+ W.startLine() << format("eh_frame_ptr: 0x%" PRIx64 "\n", EHFramePtr);
+
+ auto FDECount = DE.getUnsigned(&Offset, 4);
+ W.printNumber("fde_count", FDECount);
+
+ unsigned NumEntries = 0;
+ uint64_t PrevPC = 0;
+ while (Offset + 8 <= EHFrameHdrSize && NumEntries < FDECount) {
+ DictScope D(W, std::string("entry ") + std::to_string(NumEntries));
+
+ auto InitialPC = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
+ W.startLine() << format("initial_location: 0x%" PRIx64 "\n", InitialPC);
+ auto Address = DE.getSigned(&Offset, 4) + EHFrameHdrAddress;
+ W.startLine() << format("address: 0x%" PRIx64 "\n", Address);
+
+ if (InitialPC < PrevPC)
+ reportError("initial_location is out of order");
+
+ PrevPC = InitialPC;
+ ++NumEntries;
+ }
+}
+
+template <typename ELFT>
+void PrinterContext<ELFT>::printEHFrame(
+ const typename ELFT::Shdr *EHFrameShdr) const {
+ uint64_t Address = EHFrameShdr->sh_addr;
+ uint64_t ShOffset = EHFrameShdr->sh_offset;
+ W.startLine() << format(".eh_frame section at offset 0x%" PRIx64
+ " address 0x%" PRIx64 ":\n",
+ ShOffset, Address);
+ W.indent();
+
+ auto Result = Obj->getSectionContents(EHFrameShdr);
+ if (Error E = Result.takeError())
+ reportError(toString(std::move(E)));
+
+ auto Contents = Result.get();
+ DWARFDataExtractor DE(
+ StringRef(reinterpret_cast<const char *>(Contents.data()),
+ Contents.size()),
+ ELFT::TargetEndianness == support::endianness::little,
+ ELFT::Is64Bits ? 8 : 4);
+ DWARFDebugFrame EHFrame(/*IsEH=*/true, /*EHFrameAddress=*/Address);
+ EHFrame.parse(DE);
+
+ for (const auto &Entry : EHFrame) {
+ if (const auto *CIE = dyn_cast<dwarf::CIE>(&Entry)) {
+ W.startLine() << format("[0x%" PRIx64 "] CIE length=%" PRIu64 "\n",
+ Address + CIE->getOffset(),
+ CIE->getLength());
+ W.indent();
+
+ W.printNumber("version", CIE->getVersion());
+ W.printString("augmentation", CIE->getAugmentationString());
+ W.printNumber("code_alignment_factor", CIE->getCodeAlignmentFactor());
+ W.printNumber("data_alignment_factor", CIE->getDataAlignmentFactor());
+ W.printNumber("return_address_register", CIE->getReturnAddressRegister());
+
+ W.getOStream() << "\n";
+ W.startLine() << "Program:\n";
+ W.indent();
+ CIE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
+ W.unindent();
+
+ W.unindent();
+ W.getOStream() << "\n";
+
+ } else if (const auto *FDE = dyn_cast<dwarf::FDE>(&Entry)) {
+ W.startLine() << format("[0x%" PRIx64 "] FDE length=%" PRIu64
+ " cie=[0x%" PRIx64 "]\n",
+ Address + FDE->getOffset(),
+ FDE->getLength(),
+ Address + FDE->getLinkedCIE()->getOffset());
+ W.indent();
+
+ W.startLine() << format("initial_location: 0x%" PRIx64 "\n",
+ FDE->getInitialLocation());
+ W.startLine()
+ << format("address_range: 0x%" PRIx64 " (end : 0x%" PRIx64 ")\n",
+ FDE->getAddressRange(),
+ FDE->getInitialLocation() + FDE->getAddressRange());
+
+ W.getOStream() << "\n";
+ W.startLine() << "Program:\n";
+ W.indent();
+ FDE->cfis().dump(W.getOStream(), nullptr, W.getIndentLevel());
+ W.unindent();
+
+ W.unindent();
+ W.getOStream() << "\n";
+ } else {
+ llvm_unreachable("unexpected DWARF frame kind");
+ }
+ }
+
+ W.unindent();
+}
+
+}
+}
+
+#endif
diff --git a/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp b/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp
index 5605eaea7555..645ec2d7e04b 100644
--- a/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp
+++ b/contrib/llvm/tools/llvm-readobj/ELFDumper.cpp
@@ -8,11 +8,12 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements the ELF-specific dumper for llvm-readobj.
+/// This file implements the ELF-specific dumper for llvm-readobj.
///
//===----------------------------------------------------------------------===//
#include "ARMEHABIPrinter.h"
+#include "DwarfCFIEHPrinter.h"
#include "Error.h"
#include "ObjDumper.h"
#include "StackMapPrinter.h"
@@ -43,6 +44,7 @@
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/LEB128.h"
#include "llvm/Support/MathExtras.h"
#include "llvm/Support/MipsABIFlags.h"
#include "llvm/Support/ScopedPrinter.h"
@@ -77,28 +79,32 @@ using namespace ELF;
#define TYPEDEF_ELF_TYPES(ELFT) \
using ELFO = ELFFile<ELFT>; \
- using Elf_Addr = typename ELFO::Elf_Addr; \
- using Elf_Shdr = typename ELFO::Elf_Shdr; \
- using Elf_Sym = typename ELFO::Elf_Sym; \
- using Elf_Dyn = typename ELFO::Elf_Dyn; \
- using Elf_Dyn_Range = typename ELFO::Elf_Dyn_Range; \
- using Elf_Rel = typename ELFO::Elf_Rel; \
- using Elf_Rela = typename ELFO::Elf_Rela; \
- using Elf_Rel_Range = typename ELFO::Elf_Rel_Range; \
- using Elf_Rela_Range = typename ELFO::Elf_Rela_Range; \
- using Elf_Phdr = typename ELFO::Elf_Phdr; \
- using Elf_Half = typename ELFO::Elf_Half; \
- using Elf_Ehdr = typename ELFO::Elf_Ehdr; \
- using Elf_Word = typename ELFO::Elf_Word; \
- using Elf_Hash = typename ELFO::Elf_Hash; \
- using Elf_GnuHash = typename ELFO::Elf_GnuHash; \
- using Elf_Sym_Range = typename ELFO::Elf_Sym_Range; \
- using Elf_Versym = typename ELFO::Elf_Versym; \
- using Elf_Verneed = typename ELFO::Elf_Verneed; \
- using Elf_Vernaux = typename ELFO::Elf_Vernaux; \
- using Elf_Verdef = typename ELFO::Elf_Verdef; \
- using Elf_Verdaux = typename ELFO::Elf_Verdaux; \
- using uintX_t = typename ELFO::uintX_t;
+ using Elf_Addr = typename ELFT::Addr; \
+ using Elf_Shdr = typename ELFT::Shdr; \
+ using Elf_Sym = typename ELFT::Sym; \
+ using Elf_Dyn = typename ELFT::Dyn; \
+ using Elf_Dyn_Range = typename ELFT::DynRange; \
+ using Elf_Rel = typename ELFT::Rel; \
+ using Elf_Rela = typename ELFT::Rela; \
+ using Elf_Relr = typename ELFT::Relr; \
+ using Elf_Rel_Range = typename ELFT::RelRange; \
+ using Elf_Rela_Range = typename ELFT::RelaRange; \
+ using Elf_Relr_Range = typename ELFT::RelrRange; \
+ using Elf_Phdr = typename ELFT::Phdr; \
+ using Elf_Half = typename ELFT::Half; \
+ using Elf_Ehdr = typename ELFT::Ehdr; \
+ using Elf_Word = typename ELFT::Word; \
+ using Elf_Hash = typename ELFT::Hash; \
+ using Elf_GnuHash = typename ELFT::GnuHash; \
+ using Elf_Note = typename ELFT::Note; \
+ using Elf_Sym_Range = typename ELFT::SymRange; \
+ using Elf_Versym = typename ELFT::Versym; \
+ using Elf_Verneed = typename ELFT::Verneed; \
+ using Elf_Vernaux = typename ELFT::Vernaux; \
+ using Elf_Verdef = typename ELFT::Verdef; \
+ using Elf_Verdaux = typename ELFT::Verdaux; \
+ using Elf_CGProfile = typename ELFT::CGProfile; \
+ using uintX_t = typename ELFT::uint;
namespace {
@@ -113,11 +119,11 @@ struct DynRegionInfo {
DynRegionInfo(const void *A, uint64_t S, uint64_t ES)
: Addr(A), Size(S), EntSize(ES) {}
- /// \brief Address in current address space.
+ /// Address in current address space.
const void *Addr = nullptr;
- /// \brief Size in bytes of the region.
+ /// Size in bytes of the region.
uint64_t Size = 0;
- /// \brief Size of each entity in the region.
+ /// Size of each entity in the region.
uint64_t EntSize = 0;
template <typename Type> ArrayRef<Type> getAsArrayRef() const {
@@ -162,8 +168,13 @@ public:
void printHashHistogram() override;
+ void printCGProfile() override;
+ void printAddrsig() override;
+
void printNotes() override;
+ void printELFLinkerOptions() override;
+
private:
std::unique_ptr<DumpStyle<ELFT>> ELFDumperStyle;
@@ -198,6 +209,7 @@ private:
const ELFO *Obj;
DynRegionInfo DynRelRegion;
DynRegionInfo DynRelaRegion;
+ DynRegionInfo DynRelrRegion;
DynRegionInfo DynPLTRelRegion;
DynRegionInfo DynSymRegion;
DynRegionInfo DynamicTable;
@@ -206,6 +218,8 @@ private:
const Elf_Hash *HashTable = nullptr;
const Elf_GnuHash *GnuHashTable = nullptr;
const Elf_Shdr *DotSymtabSec = nullptr;
+ const Elf_Shdr *DotCGProfileSec = nullptr;
+ const Elf_Shdr *DotAddrsigSec = nullptr;
StringRef DynSymtabName;
ArrayRef<Elf_Word> ShndxTable;
@@ -248,18 +262,23 @@ public:
Elf_Rel_Range dyn_rels() const;
Elf_Rela_Range dyn_relas() const;
+ Elf_Relr_Range dyn_relrs() const;
std::string getFullSymbolName(const Elf_Sym *Symbol, StringRef StrTable,
bool IsDynamic) const;
void getSectionNameIndex(const Elf_Sym *Symbol, const Elf_Sym *FirstSym,
StringRef &SectionName,
unsigned &SectionIndex) const;
+ StringRef getStaticSymbolName(uint32_t Index) const;
void printSymbolsHelper(bool IsDynamic) const;
const Elf_Shdr *getDotSymtabSec() const { return DotSymtabSec; }
+ const Elf_Shdr *getDotCGProfileSec() const { return DotCGProfileSec; }
+ const Elf_Shdr *getDotAddrsigSec() const { return DotAddrsigSec; }
ArrayRef<Elf_Word> getShndxTable() const { return ShndxTable; }
StringRef getDynamicStringTable() const { return DynamicStringTable; }
const DynRegionInfo &getDynRelRegion() const { return DynRelRegion; }
const DynRegionInfo &getDynRelaRegion() const { return DynRelaRegion; }
+ const DynRegionInfo &getDynRelrRegion() const { return DynRelrRegion; }
const DynRegionInfo &getDynPLTRelRegion() const { return DynPLTRelRegion; }
const Elf_Hash *getHashTable() const { return HashTable; }
const Elf_GnuHash *getGnuHashTable() const { return GnuHashTable; }
@@ -295,8 +314,8 @@ template <class ELFT> class MipsGOTParser;
template <typename ELFT> class DumpStyle {
public:
- using Elf_Shdr = typename ELFFile<ELFT>::Elf_Shdr;
- using Elf_Sym = typename ELFFile<ELFT>::Elf_Sym;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
DumpStyle(ELFDumper<ELFT> *Dumper) : Dumper(Dumper) {}
virtual ~DumpStyle() = default;
@@ -315,7 +334,10 @@ public:
bool IsDynamic) = 0;
virtual void printProgramHeaders(const ELFFile<ELFT> *Obj) = 0;
virtual void printHashHistogram(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printCGProfile(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printAddrsig(const ELFFile<ELFT> *Obj) = 0;
virtual void printNotes(const ELFFile<ELFT> *Obj) = 0;
+ virtual void printELFLinkerOptions(const ELFFile<ELFT> *Obj) = 0;
virtual void printMipsGOT(const MipsGOTParser<ELFT> &Parser) = 0;
virtual void printMipsPLT(const MipsGOTParser<ELFT> &Parser) = 0;
const ELFDumper<ELFT> *dumper() const { return Dumper; }
@@ -344,7 +366,10 @@ public:
size_t Offset) override;
void printProgramHeaders(const ELFO *Obj) override;
void printHashHistogram(const ELFFile<ELFT> *Obj) override;
+ void printCGProfile(const ELFFile<ELFT> *Obj) override;
+ void printAddrsig(const ELFFile<ELFT> *Obj) override;
void printNotes(const ELFFile<ELFT> *Obj) override;
+ void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override;
void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -374,6 +399,7 @@ private:
}
void printHashedSymbol(const ELFO *Obj, const Elf_Sym *FirstSym, uint32_t Sym,
StringRef StrTable, uint32_t Bucket);
+ void printRelocHeader(unsigned SType);
void printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
const Elf_Rela &R, bool IsRela);
void printSymbol(const ELFO *Obj, const Elf_Sym *Symbol, const Elf_Sym *First,
@@ -404,7 +430,10 @@ public:
void printDynamicRelocations(const ELFO *Obj) override;
void printProgramHeaders(const ELFO *Obj) override;
void printHashHistogram(const ELFFile<ELFT> *Obj) override;
+ void printCGProfile(const ELFFile<ELFT> *Obj) override;
+ void printAddrsig(const ELFFile<ELFT> *Obj) override;
void printNotes(const ELFFile<ELFT> *Obj) override;
+ void printELFLinkerOptions(const ELFFile<ELFT> *Obj) override;
void printMipsGOT(const MipsGOTParser<ELFT> &Parser) override;
void printMipsPLT(const MipsGOTParser<ELFT> &Parser) override;
@@ -730,6 +759,16 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab,
}
template <typename ELFT>
+StringRef ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const {
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec));
+ Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec));
+ if (Index >= Syms.size())
+ reportError("Invalid symbol index");
+ const Elf_Sym *Sym = &Syms[Index];
+ return unwrapOrError(Sym->getName(StrTable));
+}
+
+template <typename ELFT>
std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol,
StringRef StrTable,
bool IsDynamic) const {
@@ -1007,7 +1046,6 @@ static const EnumEntry<unsigned> ElfMachineType[] = {
ENUM_ENT(EM_56800EX, "EM_56800EX"),
ENUM_ENT(EM_AMDGPU, "EM_AMDGPU"),
ENUM_ENT(EM_RISCV, "RISC-V"),
- ENUM_ENT(EM_WEBASSEMBLY, "EM_WEBASSEMBLY"),
ENUM_ENT(EM_LANAI, "EM_LANAI"),
ENUM_ENT(EM_BPF, "EM_BPF"),
};
@@ -1255,9 +1293,39 @@ static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = {
};
static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = {
- LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_ARCH_NONE),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_ARCH_R600),
- LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_ARCH_GCN)
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_NONE),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_R630),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RS880),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV670),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV710),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV730),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_RV770),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CEDAR),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CYPRESS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_JUNIPER),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_REDWOOD),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_SUMO),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_BARTS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAICOS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_CAYMAN),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_R600_TURKS),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX600),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX601),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX700),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX701),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX702),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX703),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX704),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX801),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX802),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX803),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX810),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX900),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX902),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX904),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX906),
+ LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK)
};
static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = {
@@ -1353,6 +1421,16 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer)
reportError("Multiple SHT_GNU_verneed");
dot_gnu_version_r_sec = &Sec;
break;
+ case ELF::SHT_LLVM_CALL_GRAPH_PROFILE:
+ if (DotCGProfileSec != nullptr)
+ reportError("Multiple .note.llvm.cgprofile");
+ DotCGProfileSec = &Sec;
+ break;
+ case ELF::SHT_LLVM_ADDRSIG:
+ if (DotAddrsigSec != nullptr)
+ reportError("Multiple .llvm_addrsig");
+ DotAddrsigSec = &Sec;
+ break;
}
}
@@ -1427,6 +1505,18 @@ void ELFDumper<ELFT>::parseDynamicTable(
case ELF::DT_RELENT:
DynRelRegion.EntSize = Dyn.getVal();
break;
+ case ELF::DT_RELR:
+ case ELF::DT_ANDROID_RELR:
+ DynRelrRegion.Addr = toMappedAddr(Dyn.getPtr());
+ break;
+ case ELF::DT_RELRSZ:
+ case ELF::DT_ANDROID_RELRSZ:
+ DynRelrRegion.Size = Dyn.getVal();
+ break;
+ case ELF::DT_RELRENT:
+ case ELF::DT_ANDROID_RELRENT:
+ DynRelrRegion.EntSize = Dyn.getVal();
+ break;
case ELF::DT_PLTREL:
if (Dyn.getVal() == DT_REL)
DynPLTRelRegion.EntSize = sizeof(Elf_Rel);
@@ -1460,6 +1550,11 @@ typename ELFDumper<ELFT>::Elf_Rela_Range ELFDumper<ELFT>::dyn_relas() const {
return DynRelaRegion.getAsArrayRef<Elf_Rela>();
}
+template <typename ELFT>
+typename ELFDumper<ELFT>::Elf_Relr_Range ELFDumper<ELFT>::dyn_relrs() const {
+ return DynRelrRegion.getAsArrayRef<Elf_Relr>();
+}
+
template<class ELFT>
void ELFDumper<ELFT>::printFileHeaders() {
ELFDumperStyle->printFileHeaders(Obj);
@@ -1497,93 +1592,69 @@ template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() {
ELFDumperStyle->printHashHistogram(Obj);
}
+template <class ELFT> void ELFDumper<ELFT>::printCGProfile() {
+ ELFDumperStyle->printCGProfile(Obj);
+}
+
template <class ELFT> void ELFDumper<ELFT>::printNotes() {
ELFDumperStyle->printNotes(Obj);
}
-#define LLVM_READOBJ_TYPE_CASE(name) \
- case DT_##name: return #name
+template <class ELFT> void ELFDumper<ELFT>::printELFLinkerOptions() {
+ ELFDumperStyle->printELFLinkerOptions(Obj);
+}
static const char *getTypeString(unsigned Arch, uint64_t Type) {
+#define DYNAMIC_TAG(n, v)
switch (Arch) {
case EM_HEXAGON:
switch (Type) {
- LLVM_READOBJ_TYPE_CASE(HEXAGON_SYMSZ);
- LLVM_READOBJ_TYPE_CASE(HEXAGON_VER);
- LLVM_READOBJ_TYPE_CASE(HEXAGON_PLT);
+#define HEXAGON_DYNAMIC_TAG(name, value) \
+ case DT_##name: \
+ return #name;
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef HEXAGON_DYNAMIC_TAG
}
+
case EM_MIPS:
switch (Type) {
- LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP_REL);
- LLVM_READOBJ_TYPE_CASE(MIPS_RLD_VERSION);
- LLVM_READOBJ_TYPE_CASE(MIPS_FLAGS);
- LLVM_READOBJ_TYPE_CASE(MIPS_BASE_ADDRESS);
- LLVM_READOBJ_TYPE_CASE(MIPS_LOCAL_GOTNO);
- LLVM_READOBJ_TYPE_CASE(MIPS_SYMTABNO);
- LLVM_READOBJ_TYPE_CASE(MIPS_UNREFEXTNO);
- LLVM_READOBJ_TYPE_CASE(MIPS_GOTSYM);
- LLVM_READOBJ_TYPE_CASE(MIPS_RLD_MAP);
- LLVM_READOBJ_TYPE_CASE(MIPS_PLTGOT);
- LLVM_READOBJ_TYPE_CASE(MIPS_OPTIONS);
+#define MIPS_DYNAMIC_TAG(name, value) \
+ case DT_##name: \
+ return #name;
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef MIPS_DYNAMIC_TAG
+ }
+
+ case EM_PPC64:
+ switch(Type) {
+#define PPC64_DYNAMIC_TAG(name, value) \
+ case DT_##name: \
+ return #name;
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef PPC64_DYNAMIC_TAG
}
}
+#undef DYNAMIC_TAG
switch (Type) {
- LLVM_READOBJ_TYPE_CASE(ANDROID_REL);
- LLVM_READOBJ_TYPE_CASE(ANDROID_RELSZ);
- LLVM_READOBJ_TYPE_CASE(ANDROID_RELA);
- LLVM_READOBJ_TYPE_CASE(ANDROID_RELASZ);
- LLVM_READOBJ_TYPE_CASE(BIND_NOW);
- LLVM_READOBJ_TYPE_CASE(DEBUG);
- LLVM_READOBJ_TYPE_CASE(FINI);
- LLVM_READOBJ_TYPE_CASE(FINI_ARRAY);
- LLVM_READOBJ_TYPE_CASE(FINI_ARRAYSZ);
- LLVM_READOBJ_TYPE_CASE(FLAGS);
- LLVM_READOBJ_TYPE_CASE(FLAGS_1);
- LLVM_READOBJ_TYPE_CASE(HASH);
- LLVM_READOBJ_TYPE_CASE(INIT);
- LLVM_READOBJ_TYPE_CASE(INIT_ARRAY);
- LLVM_READOBJ_TYPE_CASE(INIT_ARRAYSZ);
- LLVM_READOBJ_TYPE_CASE(PREINIT_ARRAY);
- LLVM_READOBJ_TYPE_CASE(PREINIT_ARRAYSZ);
- LLVM_READOBJ_TYPE_CASE(JMPREL);
- LLVM_READOBJ_TYPE_CASE(NEEDED);
- LLVM_READOBJ_TYPE_CASE(NULL);
- LLVM_READOBJ_TYPE_CASE(PLTGOT);
- LLVM_READOBJ_TYPE_CASE(PLTREL);
- LLVM_READOBJ_TYPE_CASE(PLTRELSZ);
- LLVM_READOBJ_TYPE_CASE(REL);
- LLVM_READOBJ_TYPE_CASE(RELA);
- LLVM_READOBJ_TYPE_CASE(RELENT);
- LLVM_READOBJ_TYPE_CASE(RELSZ);
- LLVM_READOBJ_TYPE_CASE(RELAENT);
- LLVM_READOBJ_TYPE_CASE(RELASZ);
- LLVM_READOBJ_TYPE_CASE(RPATH);
- LLVM_READOBJ_TYPE_CASE(RUNPATH);
- LLVM_READOBJ_TYPE_CASE(SONAME);
- LLVM_READOBJ_TYPE_CASE(STRSZ);
- LLVM_READOBJ_TYPE_CASE(STRTAB);
- LLVM_READOBJ_TYPE_CASE(SYMBOLIC);
- LLVM_READOBJ_TYPE_CASE(SYMENT);
- LLVM_READOBJ_TYPE_CASE(SYMTAB);
- LLVM_READOBJ_TYPE_CASE(TEXTREL);
- LLVM_READOBJ_TYPE_CASE(VERDEF);
- LLVM_READOBJ_TYPE_CASE(VERDEFNUM);
- LLVM_READOBJ_TYPE_CASE(VERNEED);
- LLVM_READOBJ_TYPE_CASE(VERNEEDNUM);
- LLVM_READOBJ_TYPE_CASE(VERSYM);
- LLVM_READOBJ_TYPE_CASE(RELACOUNT);
- LLVM_READOBJ_TYPE_CASE(RELCOUNT);
- LLVM_READOBJ_TYPE_CASE(GNU_HASH);
- LLVM_READOBJ_TYPE_CASE(TLSDESC_PLT);
- LLVM_READOBJ_TYPE_CASE(TLSDESC_GOT);
- LLVM_READOBJ_TYPE_CASE(AUXILIARY);
- LLVM_READOBJ_TYPE_CASE(FILTER);
+// Now handle all dynamic tags except the architecture specific ones
+#define MIPS_DYNAMIC_TAG(name, value)
+#define HEXAGON_DYNAMIC_TAG(name, value)
+#define PPC64_DYNAMIC_TAG(name, value)
+// Also ignore marker tags such as DT_HIOS (maps to DT_VERNEEDNUM), etc.
+#define DYNAMIC_TAG_MARKER(name, value)
+#define DYNAMIC_TAG(name, value) \
+ case DT_##name: \
+ return #name;
+#include "llvm/BinaryFormat/DynamicTags.def"
+#undef DYNAMIC_TAG
+#undef MIPS_DYNAMIC_TAG
+#undef HEXAGON_DYNAMIC_TAG
+#undef PPC64_DYNAMIC_TAG
+#undef DYNAMIC_TAG_MARKER
default: return "unknown";
}
}
-#undef LLVM_READOBJ_TYPE_CASE
-
#define LLVM_READOBJ_DT_FLAG_ENT(prefix, enum) \
{ #enum, prefix##_##enum }
@@ -1771,16 +1842,20 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) {
template<class ELFT>
void ELFDumper<ELFT>::printUnwindInfo() {
+ const unsigned Machine = Obj->getHeader()->e_machine;
+ if (Machine == EM_386 || Machine == EM_X86_64) {
+ DwarfCFIEH::PrinterContext<ELFT> Ctx(W, Obj);
+ return Ctx.printUnwindInformation();
+ }
W.startLine() << "UnwindInfo not implemented.\n";
}
namespace {
-template <> void ELFDumper<ELFType<support::little, false>>::printUnwindInfo() {
+template <> void ELFDumper<ELF32LE>::printUnwindInfo() {
const unsigned Machine = Obj->getHeader()->e_machine;
if (Machine == EM_ARM) {
- ARM::EHABI::PrinterContext<ELFType<support::little, false>> Ctx(
- W, Obj, DotSymtabSec);
+ ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, DotSymtabSec);
return Ctx.PrintUnwindInformation();
}
W.startLine() << "UnwindInfo not implemented.\n";
@@ -1841,9 +1916,8 @@ void ELFDumper<ELFT>::printNeededLibraries() {
std::stable_sort(Libs.begin(), Libs.end());
- for (const auto &L : Libs) {
- outs() << " " << L << "\n";
- }
+ for (const auto &L : Libs)
+ W.startLine() << L << "\n";
}
@@ -1877,7 +1951,7 @@ void ELFDumper<ELFT>::printGnuHashTable() {
}
template <typename ELFT> void ELFDumper<ELFT>::printLoadName() {
- outs() << "LoadName: " << SOName << '\n';
+ W.printString("LoadName", SOName);
}
template <class ELFT>
@@ -1887,7 +1961,7 @@ void ELFDumper<ELFT>::printAttributes() {
namespace {
-template <> void ELFDumper<ELFType<support::little, false>>::printAttributes() {
+template <> void ELFDumper<ELF32LE>::printAttributes() {
if (Obj->getHeader()->e_machine != EM_ARM) {
W.startLine() << "Attributes not implemented.\n";
return;
@@ -2219,7 +2293,9 @@ static const EnumEntry<unsigned> ElfMipsASEFlags[] = {
{"MSA", Mips::AFL_ASE_MSA},
{"MIPS16", Mips::AFL_ASE_MIPS16},
{"microMIPS", Mips::AFL_ASE_MICROMIPS},
- {"XPA", Mips::AFL_ASE_XPA}
+ {"XPA", Mips::AFL_ASE_XPA},
+ {"CRC", Mips::AFL_ASE_CRC},
+ {"GINV", Mips::AFL_ASE_GINV},
};
static const EnumEntry<unsigned> ElfMipsFpABIType[] = {
@@ -2361,14 +2437,18 @@ template <class ELFT> void ELFDumper<ELFT>::printStackMap() const {
ArrayRef<uint8_t> StackMapContentsArray =
unwrapOrError(Obj->getSectionContents(StackMapSection));
- prettyPrintStackMap(outs(), StackMapV2Parser<ELFT::TargetEndianness>(
- StackMapContentsArray));
+ prettyPrintStackMap(
+ W, StackMapV2Parser<ELFT::TargetEndianness>(StackMapContentsArray));
}
template <class ELFT> void ELFDumper<ELFT>::printGroupSections() {
ELFDumperStyle->printGroupSections(Obj);
}
+template <class ELFT> void ELFDumper<ELFT>::printAddrsig() {
+ ELFDumperStyle->printAddrsig(Obj);
+}
+
static inline void printFields(formatted_raw_ostream &OS, StringRef Str1,
StringRef Str2) {
OS.PadToColumn(2u);
@@ -2378,6 +2458,30 @@ static inline void printFields(formatted_raw_ostream &OS, StringRef Str1,
OS.flush();
}
+template <class ELFT>
+static std::string getSectionHeadersNumString(const ELFFile<ELFT> *Obj) {
+ const typename ELFT::Ehdr *ElfHeader = Obj->getHeader();
+ if (ElfHeader->e_shnum != 0)
+ return to_string(ElfHeader->e_shnum);
+
+ ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(Obj->sections());
+ if (Arr.empty())
+ return "0";
+ return "0 (" + to_string(Arr[0].sh_size) + ")";
+}
+
+template <class ELFT>
+static std::string getSectionHeaderTableIndexString(const ELFFile<ELFT> *Obj) {
+ const typename ELFT::Ehdr *ElfHeader = Obj->getHeader();
+ if (ElfHeader->e_shstrndx != SHN_XINDEX)
+ return to_string(ElfHeader->e_shstrndx);
+
+ ArrayRef<typename ELFT::Shdr> Arr = unwrapOrError(Obj->sections());
+ if (Arr.empty())
+ return "65535 (corrupt: out of range)";
+ return to_string(ElfHeader->e_shstrndx) + " (" + to_string(Arr[0].sh_link) + ")";
+}
+
template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
const Elf_Ehdr *e = Obj->getHeader();
OS << "ELF Header:\n";
@@ -2423,9 +2527,9 @@ template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
printFields(OS, "Number of program headers:", Str);
Str = to_string(e->e_shentsize) + " (bytes)";
printFields(OS, "Size of section headers:", Str);
- Str = to_string(e->e_shnum);
+ Str = getSectionHeadersNumString(Obj);
printFields(OS, "Number of section headers:", Str);
- Str = to_string(e->e_shstrndx);
+ Str = getSectionHeaderTableIndexString(Obj);
printFields(OS, "Section header string table index:", Str);
}
@@ -2440,15 +2544,17 @@ struct GroupSection {
StringRef Signature;
uint64_t ShName;
uint64_t Index;
+ uint32_t Link;
+ uint32_t Info;
uint32_t Type;
std::vector<GroupMember> Members;
};
template <class ELFT>
std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj) {
- using Elf_Shdr = typename ELFFile<ELFT>::Elf_Shdr;
- using Elf_Sym = typename ELFFile<ELFT>::Elf_Sym;
- using Elf_Word = typename ELFFile<ELFT>::Elf_Word;
+ using Elf_Shdr = typename ELFT::Shdr;
+ using Elf_Sym = typename ELFT::Sym;
+ using Elf_Word = typename ELFT::Word;
std::vector<GroupSection> Ret;
uint64_t I = 0;
@@ -2466,7 +2572,14 @@ std::vector<GroupSection> getGroups(const ELFFile<ELFT> *Obj) {
StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
StringRef Signature = StrTable.data() + Sym->st_name;
- Ret.push_back({Name, Signature, Sec.sh_name, I - 1, Data[0], {}});
+ Ret.push_back({Name,
+ Signature,
+ Sec.sh_name,
+ I - 1,
+ Sec.sh_link,
+ Sec.sh_info,
+ Data[0],
+ {}});
std::vector<GroupMember> &GM = Ret.back().Members;
for (uint32_t Ndx : Data.slice(1)) {
@@ -2522,7 +2635,6 @@ void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
const Elf_Rela &R, bool IsRela) {
std::string Offset, Info, Addend, Value;
SmallString<32> RelocName;
- StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab));
StringRef TargetName;
const Elf_Sym *Sym = nullptr;
unsigned Width = ELFT::Is64Bits ? 16 : 8;
@@ -2538,6 +2650,7 @@ void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
Obj->getSection(Sym, SymTab, this->dumper()->getShndxTable()));
TargetName = unwrapOrError(Obj->getSectionName(Sec));
} else if (Sym) {
+ StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*SymTab));
TargetName = unwrapOrError(Sym->getName(StrTable));
}
@@ -2569,35 +2682,62 @@ void GNUStyle<ELFT>::printRelocation(const ELFO *Obj, const Elf_Shdr *SymTab,
OS << "\n";
}
-static inline void printRelocHeader(raw_ostream &OS, bool Is64, bool IsRela) {
- if (Is64)
- OS << " Offset Info Type"
+template <class ELFT> void GNUStyle<ELFT>::printRelocHeader(unsigned SType) {
+ bool IsRela = SType == ELF::SHT_RELA || SType == ELF::SHT_ANDROID_RELA;
+ bool IsRelr = SType == ELF::SHT_RELR || SType == ELF::SHT_ANDROID_RELR;
+ if (ELFT::Is64Bits)
+ OS << " ";
+ else
+ OS << " ";
+ if (IsRelr && opts::RawRelr)
+ OS << "Data ";
+ else
+ OS << "Offset";
+ if (ELFT::Is64Bits)
+ OS << " Info Type"
<< " Symbol's Value Symbol's Name";
else
- OS << " Offset Info Type Sym. Value "
- << "Symbol's Name";
+ OS << " Info Type Sym. Value Symbol's Name";
if (IsRela)
- OS << (IsRela ? " + Addend" : "");
+ OS << " + Addend";
OS << "\n";
}
template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
bool HasRelocSections = false;
for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
- if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA &&
+ if (Sec.sh_type != ELF::SHT_REL &&
+ Sec.sh_type != ELF::SHT_RELA &&
+ Sec.sh_type != ELF::SHT_RELR &&
Sec.sh_type != ELF::SHT_ANDROID_REL &&
- Sec.sh_type != ELF::SHT_ANDROID_RELA)
+ Sec.sh_type != ELF::SHT_ANDROID_RELA &&
+ Sec.sh_type != ELF::SHT_ANDROID_RELR)
continue;
HasRelocSections = true;
StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
unsigned Entries = Sec.getEntityCount();
+ std::vector<Elf_Rela> AndroidRelas;
+ if (Sec.sh_type == ELF::SHT_ANDROID_REL ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELA) {
+ // Android's packed relocation section needs to be unpacked first
+ // to get the actual number of entries.
+ AndroidRelas = unwrapOrError(Obj->android_relas(&Sec));
+ Entries = AndroidRelas.size();
+ }
+ std::vector<Elf_Rela> RelrRelas;
+ if (!opts::RawRelr && (Sec.sh_type == ELF::SHT_RELR ||
+ Sec.sh_type == ELF::SHT_ANDROID_RELR)) {
+ // .relr.dyn relative relocation section needs to be unpacked first
+ // to get the actual number of entries.
+ Elf_Relr_Range Relrs = unwrapOrError(Obj->relrs(&Sec));
+ RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs));
+ Entries = RelrRelas.size();
+ }
uintX_t Offset = Sec.sh_offset;
OS << "\nRelocation section '" << Name << "' at offset 0x"
<< to_hexString(Offset, false) << " contains " << Entries
<< " entries:\n";
- printRelocHeader(OS, ELFT::Is64Bits,
- Sec.sh_type == ELF::SHT_RELA ||
- Sec.sh_type == ELF::SHT_ANDROID_RELA);
+ printRelocHeader(Sec.sh_type);
const Elf_Shdr *SymTab = unwrapOrError(Obj->getSection(Sec.sh_link));
switch (Sec.sh_type) {
case ELF::SHT_REL:
@@ -2613,9 +2753,19 @@ template <class ELFT> void GNUStyle<ELFT>::printRelocations(const ELFO *Obj) {
for (const auto &R : unwrapOrError(Obj->relas(&Sec)))
printRelocation(Obj, SymTab, R, true);
break;
+ case ELF::SHT_RELR:
+ case ELF::SHT_ANDROID_RELR:
+ if (opts::RawRelr)
+ for (const auto &R : unwrapOrError(Obj->relrs(&Sec)))
+ OS << to_string(format_hex_no_prefix(R, ELFT::Is64Bits ? 16 : 8))
+ << "\n";
+ else
+ for (const auto &R : RelrRelas)
+ printRelocation(Obj, SymTab, R, false);
+ break;
case ELF::SHT_ANDROID_REL:
case ELF::SHT_ANDROID_RELA:
- for (const auto &R : unwrapOrError(Obj->android_relas(&Sec)))
+ for (const auto &R : AndroidRelas)
printRelocation(Obj, SymTab, R, Sec.sh_type == ELF::SHT_ANDROID_RELA);
break;
}
@@ -2694,8 +2844,17 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) {
return "GROUP";
case SHT_SYMTAB_SHNDX:
return "SYMTAB SECTION INDICES";
+ case SHT_RELR:
+ case SHT_ANDROID_RELR:
+ return "RELR";
case SHT_LLVM_ODRTAB:
return "LLVM_ODRTAB";
+ case SHT_LLVM_LINKER_OPTIONS:
+ return "LLVM_LINKER_OPTIONS";
+ case SHT_LLVM_CALL_GRAPH_PROFILE:
+ return "LLVM_CALL_GRAPH_PROFILE";
+ case SHT_LLVM_ADDRSIG:
+ return "LLVM_ADDRSIG";
// FIXME: Parse processor specific GNU attributes
case SHT_GNU_ATTRIBUTES:
return "ATTRIBUTES";
@@ -2727,7 +2886,9 @@ template <class ELFT> void GNUStyle<ELFT>::printSections(const ELFO *Obj) {
Bias = 8;
Width = 8;
}
- OS << "There are " << to_string(Obj->getHeader()->e_shnum)
+
+ ArrayRef<Elf_Shdr> Sections = unwrapOrError(Obj->sections());
+ OS << "There are " << to_string(Sections.size())
<< " section headers, starting at offset "
<< "0x" << to_hexString(Obj->getHeader()->e_shoff, false) << ":\n\n";
OS << "Section Headers:\n";
@@ -2746,7 +2907,7 @@ template <class ELFT> void GNUStyle<ELFT>::printSections(const ELFO *Obj) {
printField(f);
OS << "\n";
- for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
+ for (const Elf_Shdr &Sec : Sections) {
Number = to_string(SectionIndex);
Fields[0].Str = Number;
Fields[1].Str = unwrapOrError(Obj->getSectionName(&Sec));
@@ -3198,13 +3359,14 @@ template <class ELFT>
void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion();
const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion();
+ const DynRegionInfo &DynRelrRegion = this->dumper()->getDynRelrRegion();
const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion();
if (DynRelaRegion.Size > 0) {
OS << "\n'RELA' relocation section at offset "
<< format_hex(reinterpret_cast<const uint8_t *>(DynRelaRegion.Addr) -
Obj->base(),
1) << " contains " << DynRelaRegion.Size << " bytes:\n";
- printRelocHeader(OS, ELFT::Is64Bits, true);
+ printRelocHeader(ELF::SHT_RELA);
for (const Elf_Rela &Rela : this->dumper()->dyn_relas())
printDynamicRelocation(Obj, Rela, true);
}
@@ -3213,7 +3375,7 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
<< format_hex(reinterpret_cast<const uint8_t *>(DynRelRegion.Addr) -
Obj->base(),
1) << " contains " << DynRelRegion.Size << " bytes:\n";
- printRelocHeader(OS, ELFT::Is64Bits, false);
+ printRelocHeader(ELF::SHT_REL);
for (const Elf_Rel &Rel : this->dumper()->dyn_rels()) {
Elf_Rela Rela;
Rela.r_offset = Rel.r_offset;
@@ -3222,6 +3384,18 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
printDynamicRelocation(Obj, Rela, false);
}
}
+ if (DynRelrRegion.Size > 0) {
+ OS << "\n'RELR' relocation section at offset "
+ << format_hex(reinterpret_cast<const uint8_t *>(DynRelrRegion.Addr) -
+ Obj->base(),
+ 1) << " contains " << DynRelrRegion.Size << " bytes:\n";
+ printRelocHeader(ELF::SHT_REL);
+ Elf_Relr_Range Relrs = this->dumper()->dyn_relrs();
+ std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs));
+ for (const Elf_Rela &Rela : RelrRelas) {
+ printDynamicRelocation(Obj, Rela, false);
+ }
+ }
if (DynPLTRelRegion.Size) {
OS << "\n'PLT' relocation section at offset "
<< format_hex(reinterpret_cast<const uint8_t *>(DynPLTRelRegion.Addr) -
@@ -3229,11 +3403,11 @@ void GNUStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
1) << " contains " << DynPLTRelRegion.Size << " bytes:\n";
}
if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela)) {
- printRelocHeader(OS, ELFT::Is64Bits, true);
+ printRelocHeader(ELF::SHT_RELA);
for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>())
printDynamicRelocation(Obj, Rela, true);
} else {
- printRelocHeader(OS, ELFT::Is64Bits, false);
+ printRelocHeader(ELF::SHT_REL);
for (const Elf_Rel &Rel : DynPLTRelRegion.getAsArrayRef<Elf_Rel>()) {
Elf_Rela Rela;
Rela.r_offset = Rel.r_offset;
@@ -3349,6 +3523,16 @@ void GNUStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
}
}
+template <class ELFT>
+void GNUStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) {
+ OS << "GNUStyle::printCGProfile not implemented\n";
+}
+
+template <class ELFT>
+void GNUStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) {
+ OS << "GNUStyle::printAddrsig not implemented\n";
+}
+
static std::string getGNUNoteTypeName(const uint32_t NT) {
static const struct {
uint32_t ID;
@@ -3358,6 +3542,7 @@ static std::string getGNUNoteTypeName(const uint32_t NT) {
{ELF::NT_GNU_HWCAP, "NT_GNU_HWCAP (DSO-supplied software HWCAP info)"},
{ELF::NT_GNU_BUILD_ID, "NT_GNU_BUILD_ID (unique build ID bitstring)"},
{ELF::NT_GNU_GOLD_VERSION, "NT_GNU_GOLD_VERSION (gold version)"},
+ {ELF::NT_GNU_PROPERTY_TYPE_0, "NT_GNU_PROPERTY_TYPE_0 (property note)"},
};
for (const auto &Note : Notes)
@@ -3422,9 +3607,65 @@ static std::string getAMDGPUNoteTypeName(const uint32_t NT) {
}
template <typename ELFT>
+static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize,
+ ArrayRef<uint8_t> Data) {
+ switch (Type) {
+ default:
+ OS << format(" <application-specific type 0x%x>\n", Type);
+ return;
+ case GNU_PROPERTY_STACK_SIZE: {
+ OS << " stack size: ";
+ if (DataSize == sizeof(typename ELFT::uint))
+ OS << format("0x%llx\n",
+ (uint64_t)(*(const typename ELFT::Addr *)Data.data()));
+ else
+ OS << format("<corrupt length: 0x%x>\n", DataSize);
+ break;
+ }
+ case GNU_PROPERTY_NO_COPY_ON_PROTECTED:
+ OS << " no copy on protected";
+ if (DataSize)
+ OS << format(" <corrupt length: 0x%x>", DataSize);
+ OS << "\n";
+ break;
+ case GNU_PROPERTY_X86_FEATURE_1_AND:
+ OS << " X86 features: ";
+ if (DataSize != 4 && DataSize != 8) {
+ OS << format("<corrupt length: 0x%x>\n", DataSize);
+ break;
+ }
+ uint64_t CFProtection =
+ (DataSize == 4)
+ ? support::endian::read32<ELFT::TargetEndianness>(Data.data())
+ : support::endian::read64<ELFT::TargetEndianness>(Data.data());
+ if (CFProtection == 0) {
+ OS << "none\n";
+ break;
+ }
+ if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_IBT) {
+ OS << "IBT";
+ CFProtection &= ~GNU_PROPERTY_X86_FEATURE_1_IBT;
+ if (CFProtection)
+ OS << ", ";
+ }
+ if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_SHSTK) {
+ OS << "SHSTK";
+ CFProtection &= ~GNU_PROPERTY_X86_FEATURE_1_SHSTK;
+ if (CFProtection)
+ OS << ", ";
+ }
+ if (CFProtection)
+ OS << format("<unknown flags: 0x%llx>", CFProtection);
+ OS << "\n";
+ break;
+ }
+}
+
+template <typename ELFT>
static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
- ArrayRef<typename ELFFile<ELFT>::Elf_Word> Words,
- size_t Size) {
+ ArrayRef<typename ELFT::Word> Words, size_t Size) {
+ using Elf_Word = typename ELFT::Word;
+
switch (NoteType) {
default:
return;
@@ -3456,15 +3697,37 @@ static void printGNUNote(raw_ostream &OS, uint32_t NoteType,
OS << " Version: "
<< StringRef(reinterpret_cast<const char *>(Words.data()), Size);
break;
- }
+ case ELF::NT_GNU_PROPERTY_TYPE_0:
+ OS << " Properties:";
+
+ ArrayRef<uint8_t> Arr(reinterpret_cast<const uint8_t *>(Words.data()),
+ Size);
+ while (Arr.size() >= 8) {
+ uint32_t Type = *reinterpret_cast<const Elf_Word *>(Arr.data());
+ uint32_t DataSize = *reinterpret_cast<const Elf_Word *>(Arr.data() + 4);
+ Arr = Arr.drop_front(8);
+
+ // Take padding size into account if present.
+ uint64_t PaddedSize = alignTo(DataSize, sizeof(typename ELFT::uint));
+ if (Arr.size() < PaddedSize) {
+ OS << format(" <corrupt type (0x%x) datasz: 0x%x>\n", Type,
+ DataSize);
+ break;
+ }
+ printGNUProperty<ELFT>(OS, Type, DataSize, Arr.take_front(PaddedSize));
+ Arr = Arr.drop_front(PaddedSize);
+ }
+ if (!Arr.empty())
+ OS << " <corrupted GNU_PROPERTY_TYPE_0>";
+ break;
+ }
OS << '\n';
}
template <typename ELFT>
static void printAMDGPUNote(raw_ostream &OS, uint32_t NoteType,
- ArrayRef<typename ELFFile<ELFT>::Elf_Word> Words,
- size_t Size) {
+ ArrayRef<typename ELFT::Word> Words, size_t Size) {
switch (NoteType) {
default:
return;
@@ -3499,66 +3762,66 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
const Elf_Ehdr *e = Obj->getHeader();
bool IsCore = e->e_type == ELF::ET_CORE;
- auto process = [&](const typename ELFFile<ELFT>::Elf_Off Offset,
- const typename ELFFile<ELFT>::Elf_Addr Size) {
- if (Size <= 0)
- return;
-
- const auto *P = static_cast<const uint8_t *>(Obj->base() + Offset);
- const auto *E = P + Size;
-
+ auto PrintHeader = [&](const typename ELFT::Off Offset,
+ const typename ELFT::Addr Size) {
OS << "Displaying notes found at file offset " << format_hex(Offset, 10)
<< " with length " << format_hex(Size, 10) << ":\n"
<< " Owner Data size\tDescription\n";
+ };
- while (P < E) {
- const Elf_Word *Words = reinterpret_cast<const Elf_Word *>(&P[0]);
-
- uint32_t NameSize = Words[0];
- uint32_t DescriptorSize = Words[1];
- uint32_t Type = Words[2];
-
- ArrayRef<Elf_Word> Descriptor(&Words[3 + (alignTo<4>(NameSize) / 4)],
- alignTo<4>(DescriptorSize) / 4);
-
- StringRef Name;
- if (NameSize)
- Name =
- StringRef(reinterpret_cast<const char *>(&Words[3]), NameSize - 1);
-
- OS << " " << Name << std::string(22 - NameSize, ' ')
- << format_hex(DescriptorSize, 10) << '\t';
-
- if (Name == "GNU") {
- OS << getGNUNoteTypeName(Type) << '\n';
- printGNUNote<ELFT>(OS, Type, Descriptor, DescriptorSize);
- } else if (Name == "FreeBSD") {
- OS << getFreeBSDNoteTypeName(Type) << '\n';
- } else if (Name == "AMD") {
- OS << getAMDGPUNoteTypeName(Type) << '\n';
- printAMDGPUNote<ELFT>(OS, Type, Descriptor, DescriptorSize);
- } else {
- OS << "Unknown note type: (" << format_hex(Type, 10) << ')';
- }
- OS << '\n';
-
- P = P + 3 * sizeof(Elf_Word) + alignTo<4>(NameSize) +
- alignTo<4>(DescriptorSize);
+ auto ProcessNote = [&](const Elf_Note &Note) {
+ StringRef Name = Note.getName();
+ ArrayRef<Elf_Word> Descriptor = Note.getDesc();
+ Elf_Word Type = Note.getType();
+
+ OS << " " << Name << std::string(22 - Name.size(), ' ')
+ << format_hex(Descriptor.size(), 10) << '\t';
+
+ if (Name == "GNU") {
+ OS << getGNUNoteTypeName(Type) << '\n';
+ printGNUNote<ELFT>(OS, Type, Descriptor, Descriptor.size());
+ } else if (Name == "FreeBSD") {
+ OS << getFreeBSDNoteTypeName(Type) << '\n';
+ } else if (Name == "AMD") {
+ OS << getAMDGPUNoteTypeName(Type) << '\n';
+ printAMDGPUNote<ELFT>(OS, Type, Descriptor, Descriptor.size());
+ } else {
+ OS << "Unknown note type: (" << format_hex(Type, 10) << ')';
}
+ OS << '\n';
};
if (IsCore) {
- for (const auto &P : unwrapOrError(Obj->program_headers()))
- if (P.p_type == PT_NOTE)
- process(P.p_offset, P.p_filesz);
+ for (const auto &P : unwrapOrError(Obj->program_headers())) {
+ if (P.p_type != PT_NOTE)
+ continue;
+ PrintHeader(P.p_offset, P.p_filesz);
+ Error Err = Error::success();
+ for (const auto &Note : Obj->notes(P, Err))
+ ProcessNote(Note);
+ if (Err)
+ error(std::move(Err));
+ }
} else {
- for (const auto &S : unwrapOrError(Obj->sections()))
- if (S.sh_type == SHT_NOTE)
- process(S.sh_offset, S.sh_size);
+ for (const auto &S : unwrapOrError(Obj->sections())) {
+ if (S.sh_type != SHT_NOTE)
+ continue;
+ PrintHeader(S.sh_offset, S.sh_size);
+ Error Err = Error::success();
+ for (const auto &Note : Obj->notes(S, Err))
+ ProcessNote(Note);
+ if (Err)
+ error(std::move(Err));
+ }
}
}
template <class ELFT>
+void GNUStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) {
+ OS << "printELFLinkerOptions not implemented!\n";
+}
+
+template <class ELFT>
void GNUStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) {
size_t Bias = ELFT::Is64Bits ? 8 : 0;
auto PrintEntry = [&](const Elf_Addr *E, StringRef Purpose) {
@@ -3715,7 +3978,7 @@ template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
unsigned(ELF::EF_MIPS_MACH));
else if (e->e_machine == EM_AMDGPU)
W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderAMDGPUFlags),
- unsigned(ELF::EF_AMDGPU_ARCH));
+ unsigned(ELF::EF_AMDGPU_MACH));
else if (e->e_machine == EM_RISCV)
W.printFlags("Flags", e->e_flags, makeArrayRef(ElfHeaderRISCVFlags));
else
@@ -3724,8 +3987,8 @@ template <class ELFT> void LLVMStyle<ELFT>::printFileHeaders(const ELFO *Obj) {
W.printNumber("ProgramHeaderEntrySize", e->e_phentsize);
W.printNumber("ProgramHeaderCount", e->e_phnum);
W.printNumber("SectionHeaderEntrySize", e->e_shentsize);
- W.printNumber("SectionHeaderCount", e->e_shnum);
- W.printNumber("StringTableSectionIndex", e->e_shstrndx);
+ W.printString("SectionHeaderCount", getSectionHeadersNumString(Obj));
+ W.printString("StringTableSectionIndex", getSectionHeaderTableIndexString(Obj));
}
}
@@ -3738,6 +4001,8 @@ void LLVMStyle<ELFT>::printGroupSections(const ELFO *Obj) {
DictScope D(W, "Group");
W.printNumber("Name", G.Name, G.ShName);
W.printNumber("Index", G.Index);
+ W.printNumber("Link", G.Link);
+ W.printNumber("Info", G.Info);
W.printHex("Type", getGroupType(G.Type), G.Type);
W.startLine() << "Signature: " << G.Signature << "\n";
@@ -3768,9 +4033,12 @@ template <class ELFT> void LLVMStyle<ELFT>::printRelocations(const ELFO *Obj) {
for (const Elf_Shdr &Sec : unwrapOrError(Obj->sections())) {
++SectionNumber;
- if (Sec.sh_type != ELF::SHT_REL && Sec.sh_type != ELF::SHT_RELA &&
+ if (Sec.sh_type != ELF::SHT_REL &&
+ Sec.sh_type != ELF::SHT_RELA &&
+ Sec.sh_type != ELF::SHT_RELR &&
Sec.sh_type != ELF::SHT_ANDROID_REL &&
- Sec.sh_type != ELF::SHT_ANDROID_RELA)
+ Sec.sh_type != ELF::SHT_ANDROID_RELA &&
+ Sec.sh_type != ELF::SHT_ANDROID_RELR)
continue;
StringRef Name = unwrapOrError(Obj->getSectionName(&Sec));
@@ -3803,6 +4071,19 @@ void LLVMStyle<ELFT>::printRelocations(const Elf_Shdr *Sec, const ELFO *Obj) {
for (const Elf_Rela &R : unwrapOrError(Obj->relas(Sec)))
printRelocation(Obj, R, SymTab);
break;
+ case ELF::SHT_RELR:
+ case ELF::SHT_ANDROID_RELR: {
+ Elf_Relr_Range Relrs = unwrapOrError(Obj->relrs(Sec));
+ if (opts::RawRelr) {
+ for (const Elf_Relr &R : Relrs)
+ W.startLine() << W.hex(R) << "\n";
+ } else {
+ std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs));
+ for (const Elf_Rela &R : RelrRelas)
+ printRelocation(Obj, R, SymTab);
+ }
+ break;
+ }
case ELF::SHT_ANDROID_REL:
case ELF::SHT_ANDROID_RELA:
for (const Elf_Rela &R : unwrapOrError(Obj->android_relas(Sec)))
@@ -3983,6 +4264,7 @@ template <class ELFT>
void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
const DynRegionInfo &DynRelRegion = this->dumper()->getDynRelRegion();
const DynRegionInfo &DynRelaRegion = this->dumper()->getDynRelaRegion();
+ const DynRegionInfo &DynRelrRegion = this->dumper()->getDynRelrRegion();
const DynRegionInfo &DynPLTRelRegion = this->dumper()->getDynPLTRelRegion();
if (DynRelRegion.Size && DynRelaRegion.Size)
report_fatal_error("There are both REL and RELA dynamic relocations");
@@ -3999,6 +4281,12 @@ void LLVMStyle<ELFT>::printDynamicRelocations(const ELFO *Obj) {
Rela.r_addend = 0;
printDynamicRelocation(Obj, Rela);
}
+ if (DynRelrRegion.Size > 0) {
+ Elf_Relr_Range Relrs = this->dumper()->dyn_relrs();
+ std::vector<Elf_Rela> RelrRelas = unwrapOrError(Obj->decode_relrs(Relrs));
+ for (const Elf_Rela &Rela : RelrRelas)
+ printDynamicRelocation(Obj, Rela);
+ }
if (DynPLTRelRegion.EntSize == sizeof(Elf_Rela))
for (const Elf_Rela &Rela : DynPLTRelRegion.getAsArrayRef<Elf_Rela>())
printDynamicRelocation(Obj, Rela);
@@ -4062,11 +4350,71 @@ void LLVMStyle<ELFT>::printHashHistogram(const ELFFile<ELFT> *Obj) {
}
template <class ELFT>
+void LLVMStyle<ELFT>::printCGProfile(const ELFFile<ELFT> *Obj) {
+ ListScope L(W, "CGProfile");
+ if (!this->dumper()->getDotCGProfileSec())
+ return;
+ auto CGProfile =
+ unwrapOrError(Obj->template getSectionContentsAsArray<Elf_CGProfile>(
+ this->dumper()->getDotCGProfileSec()));
+ for (const Elf_CGProfile &CGPE : CGProfile) {
+ DictScope D(W, "CGProfileEntry");
+ W.printNumber("From", this->dumper()->getStaticSymbolName(CGPE.cgp_from),
+ CGPE.cgp_from);
+ W.printNumber("To", this->dumper()->getStaticSymbolName(CGPE.cgp_to),
+ CGPE.cgp_to);
+ W.printNumber("Weight", CGPE.cgp_weight);
+ }
+}
+
+template <class ELFT>
+void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) {
+ ListScope L(W, "Addrsig");
+ if (!this->dumper()->getDotAddrsigSec())
+ return;
+ ArrayRef<uint8_t> Contents = unwrapOrError(
+ Obj->getSectionContents(this->dumper()->getDotAddrsigSec()));
+ const uint8_t *Cur = Contents.begin();
+ const uint8_t *End = Contents.end();
+ while (Cur != End) {
+ unsigned Size;
+ const char *Err;
+ uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err);
+ if (Err)
+ reportError(Err);
+ W.printNumber("Sym", this->dumper()->getStaticSymbolName(SymIndex),
+ SymIndex);
+ Cur += Size;
+ }
+}
+
+template <class ELFT>
void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) {
W.startLine() << "printNotes not implemented!\n";
}
template <class ELFT>
+void LLVMStyle<ELFT>::printELFLinkerOptions(const ELFFile<ELFT> *Obj) {
+ ListScope L(W, "LinkerOptions");
+
+ for (const Elf_Shdr &Shdr : unwrapOrError(Obj->sections())) {
+ if (Shdr.sh_type != ELF::SHT_LLVM_LINKER_OPTIONS)
+ continue;
+
+ ArrayRef<uint8_t> Contents = unwrapOrError(Obj->getSectionContents(&Shdr));
+ for (const uint8_t *P = Contents.begin(), *E = Contents.end(); P < E; ) {
+ StringRef Key = StringRef(reinterpret_cast<const char *>(P));
+ StringRef Value =
+ StringRef(reinterpret_cast<const char *>(P) + Key.size() + 1);
+
+ W.printString(Key, Value);
+
+ P = P + Key.size() + Value.size() + 2;
+ }
+ }
+}
+
+template <class ELFT>
void LLVMStyle<ELFT>::printMipsGOT(const MipsGOTParser<ELFT> &Parser) {
auto PrintEntry = [&](const Elf_Addr *E) {
W.printHex("Address", Parser.getGotAddress(E));
diff --git a/contrib/llvm/tools/llvm-readobj/MachODumper.cpp b/contrib/llvm/tools/llvm-readobj/MachODumper.cpp
index 64178d7b33ad..69ef1556f78d 100644
--- a/contrib/llvm/tools/llvm-readobj/MachODumper.cpp
+++ b/contrib/llvm/tools/llvm-readobj/MachODumper.cpp
@@ -669,12 +669,11 @@ void MachODumper::printStackMap() const {
StackMapContents.size());
if (Obj->isLittleEndian())
- prettyPrintStackMap(
- llvm::outs(),
- StackMapV2Parser<support::little>(StackMapContentsArray));
+ prettyPrintStackMap(
+ W, StackMapV2Parser<support::little>(StackMapContentsArray));
else
- prettyPrintStackMap(llvm::outs(),
- StackMapV2Parser<support::big>(StackMapContentsArray));
+ prettyPrintStackMap(W,
+ StackMapV2Parser<support::big>(StackMapContentsArray));
}
void MachODumper::printNeededLibraries() {
diff --git a/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp b/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp
index 2a0a90e5cfd5..a725140c9d33 100644
--- a/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp
+++ b/contrib/llvm/tools/llvm-readobj/ObjDumper.cpp
@@ -8,13 +8,15 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief This file implements ObjDumper.
+/// This file implements ObjDumper.
///
//===----------------------------------------------------------------------===//
#include "ObjDumper.h"
#include "Error.h"
+#include "llvm-readobj.h"
#include "llvm/Object/ObjectFile.h"
+#include "llvm/Support/Error.h"
#include "llvm/Support/ScopedPrinter.h"
#include "llvm/Support/raw_ostream.h"
@@ -25,4 +27,122 @@ ObjDumper::ObjDumper(ScopedPrinter &Writer) : W(Writer) {}
ObjDumper::~ObjDumper() {
}
+static void printAsPrintable(raw_ostream &W, const uint8_t *Start, size_t Len) {
+ for (size_t i = 0; i < Len; i++)
+ W << (isPrint(Start[i]) ? static_cast<char>(Start[i]) : '.');
+}
+
+static Expected<object::SectionRef>
+getSecNameOrIndexAsSecRef(const object::ObjectFile *Obj, StringRef SecName) {
+ char *StrPtr;
+ long SectionIndex = strtol(SecName.data(), &StrPtr, 10);
+ object::SectionRef Section;
+ long SecIndex;
+ if (Obj->isELF())
+ SecIndex = 0;
+ else
+ SecIndex = 1;
+ for (object::SectionRef SecRef : Obj->sections()) {
+ if (*StrPtr) {
+ StringRef SectionName;
+
+ if (std::error_code E = SecRef.getName(SectionName))
+ return errorCodeToError(E);
+
+ if (SectionName == SecName)
+ return SecRef;
+ } else if (SecIndex == SectionIndex)
+ return SecRef;
+
+ SecIndex++;
+ }
+ return make_error<StringError>("invalid section reference",
+ object::object_error::parse_failed);
+}
+
+void ObjDumper::printSectionAsString(const object::ObjectFile *Obj,
+ StringRef SecName) {
+ Expected<object::SectionRef> SectionRefOrError =
+ getSecNameOrIndexAsSecRef(Obj, SecName);
+ if (!SectionRefOrError)
+ error(std::move(SectionRefOrError));
+ object::SectionRef Section = *SectionRefOrError;
+ StringRef SectionName;
+
+ if (std::error_code E = Section.getName(SectionName))
+ error(E);
+ W.startLine() << "String dump of section '" << SectionName << "':\n";
+
+ StringRef SectionContent;
+ Section.getContents(SectionContent);
+
+ const uint8_t *SecContent = SectionContent.bytes_begin();
+ const uint8_t *CurrentWord = SecContent;
+ const uint8_t *SecEnd = SectionContent.bytes_end();
+
+ while (CurrentWord <= SecEnd) {
+ size_t WordSize = strnlen(reinterpret_cast<const char *>(CurrentWord),
+ SecEnd - CurrentWord);
+ if (!WordSize) {
+ CurrentWord++;
+ continue;
+ }
+ W.startLine() << format("[%6tx] ", CurrentWord - SecContent);
+ printAsPrintable(W.startLine(), CurrentWord, WordSize);
+ W.startLine() << '\n';
+ CurrentWord += WordSize + 1;
+ }
+}
+
+void ObjDumper::printSectionAsHex(const object::ObjectFile *Obj,
+ StringRef SecName) {
+ Expected<object::SectionRef> SectionRefOrError =
+ getSecNameOrIndexAsSecRef(Obj, SecName);
+ if (!SectionRefOrError)
+ error(std::move(SectionRefOrError));
+ object::SectionRef Section = *SectionRefOrError;
+ StringRef SectionName;
+
+ if (std::error_code E = Section.getName(SectionName))
+ error(E);
+ W.startLine() << "Hex dump of section '" << SectionName << "':\n";
+
+ StringRef SectionContent;
+ Section.getContents(SectionContent);
+ const uint8_t *SecContent = SectionContent.bytes_begin();
+ const uint8_t *SecEnd = SecContent + SectionContent.size();
+
+ for (const uint8_t *SecPtr = SecContent; SecPtr < SecEnd; SecPtr += 16) {
+ const uint8_t *TmpSecPtr = SecPtr;
+ uint8_t i;
+ uint8_t k;
+
+ W.startLine() << format_hex(SecPtr - SecContent, 10);
+ W.startLine() << ' ';
+ for (i = 0; TmpSecPtr < SecEnd && i < 4; ++i) {
+ for (k = 0; TmpSecPtr < SecEnd && k < 4; k++, TmpSecPtr++) {
+ uint8_t Val = *(reinterpret_cast<const uint8_t *>(TmpSecPtr));
+ W.startLine() << format_hex_no_prefix(Val, 2);
+ }
+ W.startLine() << ' ';
+ }
+
+ // We need to print the correct amount of spaces to match the format.
+ // We are adding the (4 - i) last rows that are 8 characters each.
+ // Then, the (4 - i) spaces that are in between the rows.
+ // Least, if we cut in a middle of a row, we add the remaining characters,
+ // which is (8 - (k * 2))
+ if (i < 4)
+ W.startLine() << format("%*c", (4 - i) * 8 + (4 - i) + (8 - (k * 2)),
+ ' ');
+
+ TmpSecPtr = SecPtr;
+ for (i = 0; TmpSecPtr + i < SecEnd && i < 16; ++i)
+ W.startLine() << (isPrint(TmpSecPtr[i]) ? static_cast<char>(TmpSecPtr[i])
+ : '.');
+
+ W.startLine() << '\n';
+ }
+}
+
} // namespace llvm
diff --git a/contrib/llvm/tools/llvm-readobj/ObjDumper.h b/contrib/llvm/tools/llvm-readobj/ObjDumper.h
index c5b331d944a2..8c3a7bec73be 100644
--- a/contrib/llvm/tools/llvm-readobj/ObjDumper.h
+++ b/contrib/llvm/tools/llvm-readobj/ObjDumper.h
@@ -13,6 +13,9 @@
#include <memory>
#include <system_error>
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/ObjectFile.h"
+
namespace llvm {
namespace object {
class COFFImportFile;
@@ -41,13 +44,17 @@ public:
virtual void printDynamicTable() { }
virtual void printNeededLibraries() { }
virtual void printProgramHeaders() { }
+ virtual void printSectionAsHex(StringRef SectionName) {}
virtual void printHashTable() { }
virtual void printGnuHashTable() { }
virtual void printLoadName() {}
virtual void printVersionInfo() {}
virtual void printGroupSections() {}
virtual void printHashHistogram() {}
+ virtual void printCGProfile() {}
+ virtual void printAddrsig() {}
virtual void printNotes() {}
+ virtual void printELFLinkerOptions() {}
// Only implemented for ARM ELF at this time.
virtual void printAttributes() { }
@@ -81,6 +88,9 @@ public:
virtual void printStackMap() const = 0;
+ void printSectionAsString(const object::ObjectFile *Obj, StringRef SecName);
+ void printSectionAsHex(const object::ObjectFile *Obj, StringRef SecName);
+
protected:
ScopedPrinter &W;
};
@@ -101,7 +111,8 @@ std::error_code createWasmDumper(const object::ObjectFile *Obj,
ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result);
-void dumpCOFFImportFile(const object::COFFImportFile *File);
+void dumpCOFFImportFile(const object::COFFImportFile *File,
+ ScopedPrinter &Writer);
void dumpCodeViewMergedTypes(
ScopedPrinter &Writer, llvm::codeview::MergingTypeTableBuilder &IDTable,
diff --git a/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h b/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h
index f4ed68e92d78..77a054b178a5 100644
--- a/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h
+++ b/contrib/llvm/tools/llvm-readobj/StackMapPrinter.h
@@ -11,69 +11,70 @@
#define LLVM_TOOLS_LLVM_READOBJ_STACKMAPPRINTER_H
#include "llvm/Object/StackMapParser.h"
+#include "llvm/Support/ScopedPrinter.h"
namespace llvm {
// Pretty print a stackmap to the given ostream.
-template <typename OStreamT, typename StackMapParserT>
-void prettyPrintStackMap(OStreamT &OS, const StackMapParserT &SMP) {
+template <typename StackMapParserT>
+void prettyPrintStackMap(ScopedPrinter &W, const StackMapParserT &SMP) {
- OS << "LLVM StackMap Version: " << SMP.getVersion()
- << "\nNum Functions: " << SMP.getNumFunctions();
+ W.printNumber("LLVM StackMap Version", SMP.getVersion());
+ W.printNumber("Num Functions", SMP.getNumFunctions());
// Functions:
for (const auto &F : SMP.functions())
- OS << "\n Function address: " << F.getFunctionAddress()
+ W.startLine() << " Function address: " << F.getFunctionAddress()
<< ", stack size: " << F.getStackSize()
- << ", callsite record count: " << F.getRecordCount();
+ << ", callsite record count: " << F.getRecordCount() << "\n";
// Constants:
- OS << "\nNum Constants: " << SMP.getNumConstants();
+ W.printNumber("Num Constants", SMP.getNumConstants());
unsigned ConstantIndex = 0;
for (const auto &C : SMP.constants())
- OS << "\n #" << ++ConstantIndex << ": " << C.getValue();
+ W.startLine() << " #" << ++ConstantIndex << ": " << C.getValue() << "\n";
// Records:
- OS << "\nNum Records: " << SMP.getNumRecords();
+ W.printNumber("Num Records", SMP.getNumRecords());
for (const auto &R : SMP.records()) {
- OS << "\n Record ID: " << R.getID()
- << ", instruction offset: " << R.getInstructionOffset()
- << "\n " << R.getNumLocations() << " locations:";
+ W.startLine() << " Record ID: " << R.getID()
+ << ", instruction offset: " << R.getInstructionOffset()
+ << "\n";
+ W.startLine() << " " << R.getNumLocations() << " locations:\n";
unsigned LocationIndex = 0;
for (const auto &Loc : R.locations()) {
- OS << "\n #" << ++LocationIndex << ": ";
+ raw_ostream &OS = W.startLine();
+ OS << " #" << ++LocationIndex << ": ";
switch (Loc.getKind()) {
case StackMapParserT::LocationKind::Register:
- OS << "Register R#" << Loc.getDwarfRegNum();
+ OS << "Register R#" << Loc.getDwarfRegNum() << "\n";
break;
case StackMapParserT::LocationKind::Direct:
- OS << "Direct R#" << Loc.getDwarfRegNum() << " + "
- << Loc.getOffset();
+ OS << "Direct R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset()
+ << "\n";
break;
case StackMapParserT::LocationKind::Indirect:
- OS << "Indirect [R#" << Loc.getDwarfRegNum() << " + "
- << Loc.getOffset() << "]";
+ OS << "Indirect [R#" << Loc.getDwarfRegNum() << " + " << Loc.getOffset()
+ << "]\n";
break;
case StackMapParserT::LocationKind::Constant:
- OS << "Constant " << Loc.getSmallConstant();
+ OS << "Constant " << Loc.getSmallConstant() << "\n";
break;
case StackMapParserT::LocationKind::ConstantIndex:
OS << "ConstantIndex #" << Loc.getConstantIndex() << " ("
- << SMP.getConstant(Loc.getConstantIndex()).getValue() << ")";
+ << SMP.getConstant(Loc.getConstantIndex()).getValue() << ")\n";
break;
}
}
- OS << "\n " << R.getNumLiveOuts() << " live-outs: [ ";
+ raw_ostream &OS = W.startLine();
+ OS << " " << R.getNumLiveOuts() << " live-outs: [ ";
for (const auto &LO : R.liveouts())
OS << "R#" << LO.getDwarfRegNum() << " ("
<< LO.getSizeInBytes() << "-bytes) ";
OS << "]\n";
}
-
- OS << "\n";
-
}
}
diff --git a/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp b/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp
index 223c1c752469..ce224836225e 100644
--- a/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp
+++ b/contrib/llvm/tools/llvm-readobj/WasmDumper.cpp
@@ -23,12 +23,11 @@ using namespace object;
namespace {
static const EnumEntry<unsigned> WasmSymbolTypes[] = {
-#define ENUM_ENTRY(X) { #X, static_cast<unsigned>(WasmSymbol::SymbolType::X) }
- ENUM_ENTRY(FUNCTION_IMPORT),
- ENUM_ENTRY(FUNCTION_EXPORT),
- ENUM_ENTRY(GLOBAL_IMPORT),
- ENUM_ENTRY(GLOBAL_EXPORT),
- ENUM_ENTRY(DEBUG_FUNCTION_NAME),
+#define ENUM_ENTRY(X) { #X, wasm::WASM_SYMBOL_TYPE_##X }
+ ENUM_ENTRY(FUNCTION),
+ ENUM_ENTRY(DATA),
+ ENUM_ENTRY(GLOBAL),
+ ENUM_ENTRY(SECTION),
#undef ENUM_ENTRY
};
@@ -81,11 +80,18 @@ void WasmDumper::printRelocation(const SectionRef &Section,
Reloc.getTypeName(RelocTypeName);
const wasm::WasmRelocation &WasmReloc = Obj->getWasmRelocation(Reloc);
+ StringRef SymName;
+ symbol_iterator SI = Reloc.getSymbol();
+ if (SI != Obj->symbol_end())
+ SymName = error(SI->getName());
+
bool HasAddend = false;
switch (RelocType) {
case wasm::R_WEBASSEMBLY_MEMORY_ADDR_LEB:
case wasm::R_WEBASSEMBLY_MEMORY_ADDR_SLEB:
case wasm::R_WEBASSEMBLY_MEMORY_ADDR_I32:
+ case wasm::R_WEBASSEMBLY_FUNCTION_OFFSET_I32:
+ case wasm::R_WEBASSEMBLY_SECTION_OFFSET_I32:
HasAddend = true;
break;
default:
@@ -95,13 +101,19 @@ void WasmDumper::printRelocation(const SectionRef &Section,
DictScope Group(W, "Relocation");
W.printNumber("Type", RelocTypeName, RelocType);
W.printHex("Offset", Reloc.getOffset());
- W.printHex("Index", WasmReloc.Index);
+ if (!SymName.empty())
+ W.printString("Symbol", SymName);
+ else
+ W.printHex("Index", WasmReloc.Index);
if (HasAddend)
W.printNumber("Addend", WasmReloc.Addend);
} else {
raw_ostream& OS = W.startLine();
- OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << "["
- << WasmReloc.Index << "]";
+ OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " ";
+ if (!SymName.empty())
+ OS << SymName;
+ else
+ OS << WasmReloc.Index;
if (HasAddend)
OS << " " << WasmReloc.Addend;
OS << "\n";
@@ -155,12 +167,10 @@ void WasmDumper::printSections() {
W.printString("Name", WasmSec.Name);
if (WasmSec.Name == "linking") {
const wasm::WasmLinkingData &LinkingData = Obj->linkingData();
- W.printNumber("DataSize", LinkingData.DataSize);
if (!LinkingData.InitFunctions.empty()) {
ListScope Group(W, "InitFunctions");
for (const wasm::WasmInitFunc &F: LinkingData.InitFunctions)
- W.startLine() << F.FunctionIndex << " (priority=" << F.Priority
- << ")\n";
+ W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n";
}
}
break;
@@ -204,9 +214,9 @@ void WasmDumper::printSections() {
void WasmDumper::printSymbol(const SymbolRef &Sym) {
DictScope D(W, "Symbol");
WasmSymbol Symbol = Obj->getWasmSymbol(Sym.getRawDataRefImpl());
- W.printString("Name", Symbol.Name);
- W.printEnum("Type", static_cast<unsigned>(Symbol.Type), makeArrayRef(WasmSymbolTypes));
- W.printHex("Flags", Symbol.Flags);
+ W.printString("Name", Symbol.Info.Name);
+ W.printEnum("Type", Symbol.Info.Kind, makeArrayRef(WasmSymbolTypes));
+ W.printHex("Flags", Symbol.Info.Flags);
}
}
diff --git a/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp b/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp
index c076582794fe..a7236c02b8ae 100644
--- a/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp
+++ b/contrib/llvm/tools/llvm-readobj/llvm-readobj.cpp
@@ -34,11 +34,10 @@
#include "llvm/Support/DataTypes.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/FormatVariadic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/ScopedPrinter.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetRegistry.h"
using namespace llvm;
@@ -147,6 +146,18 @@ namespace opts {
cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"),
cl::aliasopt(ProgramHeaders));
+ // -string-dump
+ cl::list<std::string> StringDump("string-dump", cl::desc("<number|name>"),
+ cl::ZeroOrMore);
+ cl::alias StringDumpShort("p", cl::desc("Alias for --string-dump"),
+ cl::aliasopt(StringDump));
+
+ // -hex-dump
+ cl::list<std::string> HexDump("hex-dump", cl::desc("<number|name>"),
+ cl::ZeroOrMore);
+ cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"),
+ cl::aliasopt(HexDump));
+
// -hash-table
cl::opt<bool> HashTable("hash-table",
cl::desc("Display ELF hash table"));
@@ -159,6 +170,10 @@ namespace opts {
cl::opt<bool> ExpandRelocs("expand-relocs",
cl::desc("Expand each shown relocation to multiple lines"));
+ // -raw-relr
+ cl::opt<bool> RawRelr("raw-relr",
+ cl::desc("Do not decode relocations in SHT_RELR section, display raw contents"));
+
// -codeview
cl::opt<bool> CodeView("codeview",
cl::desc("Display CodeView debug information"));
@@ -228,6 +243,11 @@ namespace opts {
COFFLoadConfig("coff-load-config",
cl::desc("Display the PE/COFF load config"));
+ // -elf-linker-options
+ cl::opt<bool>
+ ELFLinkerOptions("elf-linker-options",
+ cl::desc("Display the ELF .linker-options section"));
+
// -macho-data-in-code
cl::opt<bool>
MachODataInCode("macho-data-in-code",
@@ -280,6 +300,11 @@ namespace opts {
cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"),
cl::aliasopt(HashHistogram));
+ cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section"));
+
+ cl::opt<bool> Addrsig("elf-addrsig",
+ cl::desc("Display address-significance table"));
+
cl::opt<OutputStyleTy>
Output("elf-output-style", cl::desc("Specify ELF dump style"),
cl::values(clEnumVal(LLVM, "LLVM default style"),
@@ -355,7 +380,7 @@ struct ReadObjTypeTableBuilder {
}
static ReadObjTypeTableBuilder CVTypes;
-/// @brief Creates an format-specific object file dumper.
+/// Creates an format-specific object file dumper.
static std::error_code createDumper(const ObjectFile *Obj,
ScopedPrinter &Writer,
std::unique_ptr<ObjDumper> &Result) {
@@ -374,20 +399,20 @@ static std::error_code createDumper(const ObjectFile *Obj,
return readobj_error::unsupported_obj_file_format;
}
-/// @brief Dumps the specified object file.
-static void dumpObject(const ObjectFile *Obj) {
- ScopedPrinter Writer(outs());
+/// Dumps the specified object file.
+static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) {
std::unique_ptr<ObjDumper> Dumper;
if (std::error_code EC = createDumper(Obj, Writer, Dumper))
reportError(Obj->getFileName(), EC);
if (opts::Output == opts::LLVM) {
- outs() << '\n';
- outs() << "File: " << Obj->getFileName() << "\n";
- outs() << "Format: " << Obj->getFileFormatName() << "\n";
- outs() << "Arch: " << Triple::getArchTypeName(
- (llvm::Triple::ArchType)Obj->getArch()) << "\n";
- outs() << "AddressSize: " << (8 * Obj->getBytesInAddress()) << "bit\n";
+ Writer.startLine() << "\n";
+ Writer.printString("File", Obj->getFileName());
+ Writer.printString("Format", Obj->getFileFormatName());
+ Writer.printString("Arch", Triple::getArchTypeName(
+ (llvm::Triple::ArchType)Obj->getArch()));
+ Writer.printString("AddressSize",
+ formatv("{0}bit", 8 * Obj->getBytesInAddress()));
Dumper->printLoadName();
}
@@ -411,6 +436,14 @@ static void dumpObject(const ObjectFile *Obj) {
Dumper->printNeededLibraries();
if (opts::ProgramHeaders)
Dumper->printProgramHeaders();
+ if (!opts::StringDump.empty())
+ llvm::for_each(opts::StringDump, [&Dumper, Obj](StringRef SectionName) {
+ Dumper->printSectionAsString(Obj, SectionName);
+ });
+ if (!opts::HexDump.empty())
+ llvm::for_each(opts::HexDump, [&Dumper, Obj](StringRef SectionName) {
+ Dumper->printSectionAsHex(Obj, SectionName);
+ });
if (opts::HashTable)
Dumper->printHashTable();
if (opts::GnuHashTable)
@@ -418,6 +451,8 @@ static void dumpObject(const ObjectFile *Obj) {
if (opts::VersionInfo)
Dumper->printVersionInfo();
if (Obj->isELF()) {
+ if (opts::ELFLinkerOptions)
+ Dumper->printELFLinkerOptions();
if (Obj->getArch() == llvm::Triple::arm)
if (opts::ARMAttributes)
Dumper->printAttributes();
@@ -435,6 +470,10 @@ static void dumpObject(const ObjectFile *Obj) {
Dumper->printGroupSections();
if (opts::HashHistogram)
Dumper->printHashHistogram();
+ if (opts::CGProfile)
+ Dumper->printCGProfile();
+ if (opts::Addrsig)
+ Dumper->printAddrsig();
if (opts::Notes)
Dumper->printNotes();
}
@@ -476,8 +515,8 @@ static void dumpObject(const ObjectFile *Obj) {
Dumper->printStackMap();
}
-/// @brief Dumps each object file in \a Arc;
-static void dumpArchive(const Archive *Arc) {
+/// Dumps each object file in \a Arc;
+static void dumpArchive(const Archive *Arc, ScopedPrinter &Writer) {
Error Err = Error::success();
for (auto &Child : Arc->children(Err)) {
Expected<std::unique_ptr<Binary>> ChildOrErr = Child.getAsBinary();
@@ -488,9 +527,9 @@ static void dumpArchive(const Archive *Arc) {
continue;
}
if (ObjectFile *Obj = dyn_cast<ObjectFile>(&*ChildOrErr.get()))
- dumpObject(Obj);
+ dumpObject(Obj, Writer);
else if (COFFImportFile *Imp = dyn_cast<COFFImportFile>(&*ChildOrErr.get()))
- dumpCOFFImportFile(Imp);
+ dumpCOFFImportFile(Imp, Writer);
else
reportError(Arc->getFileName(), readobj_error::unrecognized_file_format);
}
@@ -498,21 +537,22 @@ static void dumpArchive(const Archive *Arc) {
reportError(Arc->getFileName(), std::move(Err));
}
-/// @brief Dumps each object file in \a MachO Universal Binary;
-static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary) {
+/// Dumps each object file in \a MachO Universal Binary;
+static void dumpMachOUniversalBinary(const MachOUniversalBinary *UBinary,
+ ScopedPrinter &Writer) {
for (const MachOUniversalBinary::ObjectForArch &Obj : UBinary->objects()) {
Expected<std::unique_ptr<MachOObjectFile>> ObjOrErr = Obj.getAsObjectFile();
if (ObjOrErr)
- dumpObject(&*ObjOrErr.get());
+ dumpObject(&*ObjOrErr.get(), Writer);
else if (auto E = isNotObjectErrorInvalidFileType(ObjOrErr.takeError())) {
reportError(UBinary->getFileName(), ObjOrErr.takeError());
}
else if (Expected<std::unique_ptr<Archive>> AOrErr = Obj.getAsArchive())
- dumpArchive(&*AOrErr.get());
+ dumpArchive(&*AOrErr.get(), Writer);
}
}
-/// @brief Dumps \a WinRes, Windows Resource (.res) file;
+/// Dumps \a WinRes, Windows Resource (.res) file;
static void dumpWindowsResourceFile(WindowsResource *WinRes) {
ScopedPrinter Printer{outs()};
WindowsRes::Dumper Dumper(WinRes, Printer);
@@ -521,8 +561,9 @@ static void dumpWindowsResourceFile(WindowsResource *WinRes) {
}
-/// @brief Opens \a File and dumps it.
+/// Opens \a File and dumps it.
static void dumpInput(StringRef File) {
+ ScopedPrinter Writer(outs());
// Attempt to open the binary.
Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(File);
@@ -531,14 +572,14 @@ static void dumpInput(StringRef File) {
Binary &Binary = *BinaryOrErr.get().getBinary();
if (Archive *Arc = dyn_cast<Archive>(&Binary))
- dumpArchive(Arc);
+ dumpArchive(Arc, Writer);
else if (MachOUniversalBinary *UBinary =
dyn_cast<MachOUniversalBinary>(&Binary))
- dumpMachOUniversalBinary(UBinary);
+ dumpMachOUniversalBinary(UBinary, Writer);
else if (ObjectFile *Obj = dyn_cast<ObjectFile>(&Binary))
- dumpObject(Obj);
+ dumpObject(Obj, Writer);
else if (COFFImportFile *Import = dyn_cast<COFFImportFile>(&Binary))
- dumpCOFFImportFile(Import);
+ dumpCOFFImportFile(Import, Writer);
else if (WindowsResource *WinRes = dyn_cast<WindowsResource>(&Binary))
dumpWindowsResourceFile(WinRes);
else
@@ -546,17 +587,14 @@ static void dumpInput(StringRef File) {
}
int main(int argc, const char *argv[]) {
- StringRef ToolName = argv[0];
- sys::PrintStackTraceOnErrorSignal(ToolName);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y;
+ InitLLVM X(argc, argv);
// Register the target printer for --version.
cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion);
opts::WideOutput.setHiddenFlag(cl::Hidden);
- if (sys::path::stem(ToolName).find("readelf") != StringRef::npos)
+ if (sys::path::stem(argv[0]).find("readelf") != StringRef::npos)
opts::Output = opts::GNU;
cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n");
diff --git a/contrib/llvm/tools/llvm-readobj/llvm-readobj.h b/contrib/llvm/tools/llvm-readobj/llvm-readobj.h
index 840ddbabdc59..374ffd03e13a 100644
--- a/contrib/llvm/tools/llvm-readobj/llvm-readobj.h
+++ b/contrib/llvm/tools/llvm-readobj/llvm-readobj.h
@@ -60,6 +60,7 @@ namespace opts {
extern llvm::cl::opt<bool> DynamicSymbols;
extern llvm::cl::opt<bool> UnwindInfo;
extern llvm::cl::opt<bool> ExpandRelocs;
+ extern llvm::cl::opt<bool> RawRelr;
extern llvm::cl::opt<bool> CodeView;
extern llvm::cl::opt<bool> CodeViewSubsectionBytes;
extern llvm::cl::opt<bool> ARMAttributes;
diff --git a/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp b/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
index b09594622ca9..54db1ec113fc 100644
--- a/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
+++ b/contrib/llvm/tools/llvm-rtdyld/llvm-rtdyld.cpp
@@ -27,11 +27,9 @@
#include "llvm/Object/SymbolSize.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/DynamicLibrary.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Memory.h"
#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Support/raw_ostream.h"
@@ -42,7 +40,7 @@ using namespace llvm::object;
static cl::list<std::string>
InputFileList(cl::Positional, cl::ZeroOrMore,
- cl::desc("<input file>"));
+ cl::desc("<input files>"));
enum ActionType {
AC_Execute,
@@ -736,11 +734,8 @@ static int linkAndVerify() {
}
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
-
+ InitLLVM X(argc, argv);
ProgramName = argv[0];
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
llvm::InitializeAllTargetInfos();
llvm::InitializeAllTargetMCs();
diff --git a/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
index b51ec513f23b..6d40a5403504 100644
--- a/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/contrib/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -22,10 +22,8 @@
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/Path.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/raw_ostream.h"
#include <cstdio>
#include <cstring>
@@ -103,24 +101,19 @@ static bool error(Expected<T> &ResOrErr) {
static bool parseCommand(StringRef InputString, bool &IsData,
std::string &ModuleName, uint64_t &ModuleOffset) {
- const char *kDataCmd = "DATA ";
- const char *kCodeCmd = "CODE ";
const char kDelimiters[] = " \n\r";
- IsData = false;
ModuleName = "";
- const char *pos = InputString.data();
- if (strncmp(pos, kDataCmd, strlen(kDataCmd)) == 0) {
- IsData = true;
- pos += strlen(kDataCmd);
- } else if (strncmp(pos, kCodeCmd, strlen(kCodeCmd)) == 0) {
+ if (InputString.consume_front("CODE ")) {
IsData = false;
- pos += strlen(kCodeCmd);
+ } else if (InputString.consume_front("DATA ")) {
+ IsData = true;
} else {
// If no cmd, assume it's CODE.
IsData = false;
}
+ const char *pos = InputString.data();
// Skip delimiters and parse input filename (if needed).
- if (ClBinaryName == "") {
+ if (ClBinaryName.empty()) {
pos += strspn(pos, kDelimiters);
if (*pos == '"' || *pos == '\'') {
char quote = *pos;
@@ -145,10 +138,7 @@ static bool parseCommand(StringRef InputString, bool &IsData,
}
int main(int argc, char **argv) {
- // Print stack trace if we signal out.
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- PrettyStackTraceProgram X(argc, argv);
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
+ InitLLVM X(argc, argv);
llvm::sys::InitializeCOMRAII COM(llvm::sys::COMThreadingMode::MultiThreaded);
@@ -188,7 +178,7 @@ int main(int argc, char **argv) {
if (ClPrintAddress) {
outs() << "0x";
outs().write_hex(ModuleOffset);
- StringRef Delimiter = (ClPrettyPrint == true) ? ": " : "\n";
+ StringRef Delimiter = ClPrettyPrint ? ": " : "\n";
outs() << Delimiter;
}
if (IsData) {
diff --git a/contrib/llvm/tools/llvm-xray/func-id-helper.cc b/contrib/llvm/tools/llvm-xray/func-id-helper.cpp
index 3234010695b2..c2bef6ddfb39 100644
--- a/contrib/llvm/tools/llvm-xray/func-id-helper.cc
+++ b/contrib/llvm/tools/llvm-xray/func-id-helper.cpp
@@ -1,4 +1,4 @@
-//===- xray-fc-account.cc - XRay Function Call Accounting Tool ------------===//
+//===- xray-fc-account.cpp: XRay Function Call Accounting Tool ------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -19,6 +19,10 @@ using namespace llvm;
using namespace xray;
std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const {
+ auto CacheIt = CachedNames.find(FuncId);
+ if (CacheIt != CachedNames.end())
+ return CacheIt->second;
+
std::ostringstream F;
auto It = FunctionAddresses.find(FuncId);
if (It == FunctionAddresses.end()) {
@@ -37,7 +41,9 @@ std::string FuncIdConversionHelper::SymbolOrNumber(int32_t FuncId) const {
F << "@(" << std::hex << It->second << ")";
});
- return F.str();
+ auto S = F.str();
+ CachedNames[FuncId] = S;
+ return S;
}
std::string FuncIdConversionHelper::FileLineAndColumn(int32_t FuncId) const {
diff --git a/contrib/llvm/tools/llvm-xray/func-id-helper.h b/contrib/llvm/tools/llvm-xray/func-id-helper.h
index 7348a7100b05..3e0780d54f90 100644
--- a/contrib/llvm/tools/llvm-xray/func-id-helper.h
+++ b/contrib/llvm/tools/llvm-xray/func-id-helper.h
@@ -13,6 +13,7 @@
#ifndef LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
#define LLVM_TOOLS_LLVM_XRAY_FUNC_ID_HELPER_H
+#include "llvm/ADT/DenseMap.h"
#include "llvm/DebugInfo/Symbolize/Symbolize.h"
#include <unordered_map>
@@ -28,6 +29,7 @@ private:
std::string BinaryInstrMap;
symbolize::LLVMSymbolizer &Symbolizer;
const FunctionAddressMap &FunctionAddresses;
+ mutable llvm::DenseMap<int32_t, std::string> CachedNames;
public:
FuncIdConversionHelper(std::string BinaryInstrMap,
diff --git a/contrib/llvm/tools/llvm-xray/llvm-xray.cc b/contrib/llvm/tools/llvm-xray/llvm-xray.cpp
index 17cc9f90dd71..e74628f5025f 100644
--- a/contrib/llvm/tools/llvm-xray/llvm-xray.cc
+++ b/contrib/llvm/tools/llvm-xray/llvm-xray.cpp
@@ -1,4 +1,4 @@
-//===- llvm-xray.cc - XRay Tool Main Program ------------------------------===//
+//===- llvm-xray.cpp: XRay Tool Main Program ------------------------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-xray/xray-account.cc b/contrib/llvm/tools/llvm-xray/xray-account.cpp
index 7b684aad693d..2776a8888481 100644
--- a/contrib/llvm/tools/llvm-xray/xray-account.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-account.cpp
@@ -237,16 +237,19 @@ ResultRow getStats(std::vector<uint64_t> &Timings) {
auto MinMax = std::minmax_element(Timings.begin(), Timings.end());
R.Min = *MinMax.first;
R.Max = *MinMax.second;
+ R.Count = Timings.size();
+
auto MedianOff = Timings.size() / 2;
std::nth_element(Timings.begin(), Timings.begin() + MedianOff, Timings.end());
R.Median = Timings[MedianOff];
+
auto Pct90Off = std::floor(Timings.size() * 0.9);
std::nth_element(Timings.begin(), Timings.begin() + Pct90Off, Timings.end());
R.Pct90 = Timings[Pct90Off];
+
auto Pct99Off = std::floor(Timings.size() * 0.99);
- std::nth_element(Timings.begin(), Timings.begin() + Pct90Off, Timings.end());
+ std::nth_element(Timings.begin(), Timings.begin() + Pct99Off, Timings.end());
R.Pct99 = Timings[Pct99Off];
- R.Count = Timings.size();
return R;
}
@@ -279,79 +282,79 @@ void LatencyAccountant::exportStats(const XRayFileHeader &Header, F Fn) const {
// Sort the data according to user-provided flags.
switch (AccountSortOutput) {
case SortField::FUNCID:
- std::sort(Results.begin(), Results.end(),
- [](const TupleType &L, const TupleType &R) {
- if (AccountSortOrder == SortDirection::ASCENDING)
- return std::get<0>(L) < std::get<0>(R);
- if (AccountSortOrder == SortDirection::DESCENDING)
- return std::get<0>(L) > std::get<0>(R);
- llvm_unreachable("Unknown sort direction");
- });
+ llvm::sort(Results.begin(), Results.end(),
+ [](const TupleType &L, const TupleType &R) {
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return std::get<0>(L) < std::get<0>(R);
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return std::get<0>(L) > std::get<0>(R);
+ llvm_unreachable("Unknown sort direction");
+ });
break;
case SortField::COUNT:
- std::sort(Results.begin(), Results.end(),
- [](const TupleType &L, const TupleType &R) {
- if (AccountSortOrder == SortDirection::ASCENDING)
- return std::get<1>(L) < std::get<1>(R);
- if (AccountSortOrder == SortDirection::DESCENDING)
- return std::get<1>(L) > std::get<1>(R);
- llvm_unreachable("Unknown sort direction");
- });
+ llvm::sort(Results.begin(), Results.end(),
+ [](const TupleType &L, const TupleType &R) {
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return std::get<1>(L) < std::get<1>(R);
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return std::get<1>(L) > std::get<1>(R);
+ llvm_unreachable("Unknown sort direction");
+ });
break;
default:
// Here we need to look into the ResultRow for the rest of the data that
// we want to sort by.
- std::sort(Results.begin(), Results.end(),
- [&](const TupleType &L, const TupleType &R) {
- auto &LR = std::get<2>(L);
- auto &RR = std::get<2>(R);
- switch (AccountSortOutput) {
- case SortField::COUNT:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Count < RR.Count;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Count > RR.Count;
- llvm_unreachable("Unknown sort direction");
- case SortField::MIN:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Min < RR.Min;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Min > RR.Min;
- llvm_unreachable("Unknown sort direction");
- case SortField::MED:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Median < RR.Median;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Median > RR.Median;
- llvm_unreachable("Unknown sort direction");
- case SortField::PCT90:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Pct90 < RR.Pct90;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Pct90 > RR.Pct90;
- llvm_unreachable("Unknown sort direction");
- case SortField::PCT99:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Pct99 < RR.Pct99;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Pct99 > RR.Pct99;
- llvm_unreachable("Unknown sort direction");
- case SortField::MAX:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Max < RR.Max;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Max > RR.Max;
- llvm_unreachable("Unknown sort direction");
- case SortField::SUM:
- if (AccountSortOrder == SortDirection::ASCENDING)
- return LR.Sum < RR.Sum;
- if (AccountSortOrder == SortDirection::DESCENDING)
- return LR.Sum > RR.Sum;
- llvm_unreachable("Unknown sort direction");
- default:
- llvm_unreachable("Unsupported sort order");
- }
- });
+ llvm::sort(Results.begin(), Results.end(),
+ [&](const TupleType &L, const TupleType &R) {
+ auto &LR = std::get<2>(L);
+ auto &RR = std::get<2>(R);
+ switch (AccountSortOutput) {
+ case SortField::COUNT:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Count < RR.Count;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Count > RR.Count;
+ llvm_unreachable("Unknown sort direction");
+ case SortField::MIN:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Min < RR.Min;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Min > RR.Min;
+ llvm_unreachable("Unknown sort direction");
+ case SortField::MED:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Median < RR.Median;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Median > RR.Median;
+ llvm_unreachable("Unknown sort direction");
+ case SortField::PCT90:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Pct90 < RR.Pct90;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Pct90 > RR.Pct90;
+ llvm_unreachable("Unknown sort direction");
+ case SortField::PCT99:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Pct99 < RR.Pct99;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Pct99 > RR.Pct99;
+ llvm_unreachable("Unknown sort direction");
+ case SortField::MAX:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Max < RR.Max;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Max > RR.Max;
+ llvm_unreachable("Unknown sort direction");
+ case SortField::SUM:
+ if (AccountSortOrder == SortDirection::ASCENDING)
+ return LR.Sum < RR.Sum;
+ if (AccountSortOrder == SortDirection::DESCENDING)
+ return LR.Sum > RR.Sum;
+ llvm_unreachable("Unknown sort direction");
+ default:
+ llvm_unreachable("Unsupported sort order");
+ }
+ });
break;
}
@@ -473,9 +476,9 @@ static CommandRegistration Unused(&Account, []() -> Error {
errs()
<< "Error processing record: "
<< llvm::formatv(
- R"({{type: {0}; cpu: {1}; record-type: {2}; function-id: {3}; tsc: {4}; thread-id: {5}}})",
+ R"({{type: {0}; cpu: {1}; record-type: {2}; function-id: {3}; tsc: {4}; thread-id: {5}; process-id: {6}}})",
Record.RecordType, Record.CPU, Record.Type, Record.FuncId,
- Record.TId)
+ Record.TSC, Record.TId, Record.PId)
<< '\n';
for (const auto &ThreadStack : FCA.getPerThreadFunctionStack()) {
errs() << "Thread ID: " << ThreadStack.first << "\n";
diff --git a/contrib/llvm/tools/llvm-xray/xray-account.h b/contrib/llvm/tools/llvm-xray/xray-account.h
index cc9ba897e537..5c457f178166 100644
--- a/contrib/llvm/tools/llvm-xray/xray-account.h
+++ b/contrib/llvm/tools/llvm-xray/xray-account.h
@@ -29,12 +29,11 @@ namespace xray {
class LatencyAccountant {
public:
typedef std::map<int32_t, std::vector<uint64_t>> FunctionLatencyMap;
- typedef std::map<llvm::sys::ProcessInfo::ProcessId,
- std::pair<uint64_t, uint64_t>>
+ typedef std::map<llvm::sys::procid_t, std::pair<uint64_t, uint64_t>>
PerThreadMinMaxTSCMap;
typedef std::map<uint8_t, std::pair<uint64_t, uint64_t>> PerCPUMinMaxTSCMap;
typedef std::vector<std::pair<int32_t, uint64_t>> FunctionStack;
- typedef std::map<llvm::sys::ProcessInfo::ProcessId, FunctionStack>
+ typedef std::map<llvm::sys::procid_t, FunctionStack>
PerThreadFunctionStackMap;
private:
@@ -79,8 +78,7 @@ public:
///
bool accountRecord(const XRayRecord &Record);
- const FunctionStack *
- getThreadFunctionStack(llvm::sys::ProcessInfo::ProcessId TId) const {
+ const FunctionStack *getThreadFunctionStack(llvm::sys::procid_t TId) const {
auto I = PerThreadFunctionStack.find(TId);
if (I == PerThreadFunctionStack.end())
return nullptr;
diff --git a/contrib/llvm/tools/llvm-xray/xray-color-helper.cc b/contrib/llvm/tools/llvm-xray/xray-color-helper.cpp
index 61314d3c766a..78a264b73d8f 100644
--- a/contrib/llvm/tools/llvm-xray/xray-color-helper.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-color-helper.cpp
@@ -1,4 +1,4 @@
-//===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===//
+//===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-xray/xray-converter.cc b/contrib/llvm/tools/llvm-xray/xray-converter.cpp
index aa0da55207b3..90e14d0d8896 100644
--- a/contrib/llvm/tools/llvm-xray/xray-converter.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-converter.cpp
@@ -1,4 +1,4 @@
-//===- xray-converter.cc - XRay Trace Conversion --------------------------===//
+//===- xray-converter.cpp: XRay Trace Conversion --------------------------===//
//
// The LLVM Compiler Infrastructure
//
@@ -91,7 +91,7 @@ void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
Trace.Records.push_back({R.RecordType, R.CPU, R.Type, R.FuncId,
Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId)
: llvm::to_string(R.FuncId),
- R.TSC, R.TId, R.CallArgs});
+ R.TSC, R.TId, R.PId, R.CallArgs});
}
Output Out(OS, nullptr, 0);
Out << Trace;
@@ -100,7 +100,7 @@ void TraceConverter::exportAsYAML(const Trace &Records, raw_ostream &OS) {
void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) {
// First write out the file header, in the correct endian-appropriate format
// (XRay assumes currently little endian).
- support::endian::Writer<support::endianness::little> Writer(OS);
+ support::endian::Writer Writer(OS, support::endianness::little);
const auto &FH = Records.getFileHeader();
Writer.write(FH.Version);
Writer.write(FH.Type);
@@ -141,7 +141,12 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) {
Writer.write(R.FuncId);
Writer.write(R.TSC);
Writer.write(R.TId);
- Writer.write(Padding4B);
+
+ if (FH.Version >= 3)
+ Writer.write(R.PId);
+ else
+ Writer.write(Padding4B);
+
Writer.write(Padding4B);
Writer.write(Padding4B);
}
@@ -229,19 +234,29 @@ StackTrieNode *findOrCreateStackNode(
return CurrentStack;
}
-void writeTraceViewerRecord(raw_ostream &OS, int32_t FuncId, uint32_t TId,
- bool Symbolize,
+void writeTraceViewerRecord(uint16_t Version, raw_ostream &OS, int32_t FuncId,
+ uint32_t TId, uint32_t PId, bool Symbolize,
const FuncIdConversionHelper &FuncIdHelper,
double EventTimestampUs,
const StackTrieNode &StackCursor,
StringRef FunctionPhenotype) {
OS << " ";
- OS << llvm::formatv(
- R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "1", )"
- R"("ts" : "{3:f3}", "sf" : "{4}" })",
- (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId)
- : llvm::to_string(FuncId)),
- FunctionPhenotype, TId, EventTimestampUs, StackCursor.ExtraData.id);
+ if (Version >= 3) {
+ OS << llvm::formatv(
+ R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "{3}", )"
+ R"("ts" : "{4:f4}", "sf" : "{5}" })",
+ (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId)
+ : llvm::to_string(FuncId)),
+ FunctionPhenotype, TId, PId, EventTimestampUs,
+ StackCursor.ExtraData.id);
+ } else {
+ OS << llvm::formatv(
+ R"({ "name" : "{0}", "ph" : "{1}", "tid" : "{2}", "pid" : "1", )"
+ R"("ts" : "{3:f3}", "sf" : "{4}" })",
+ (Symbolize ? FuncIdHelper.SymbolOrNumber(FuncId)
+ : llvm::to_string(FuncId)),
+ FunctionPhenotype, TId, EventTimestampUs, StackCursor.ExtraData.id);
+ }
}
} // namespace
@@ -249,6 +264,7 @@ void writeTraceViewerRecord(raw_ostream &OS, int32_t FuncId, uint32_t TId,
void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
raw_ostream &OS) {
const auto &FH = Records.getFileHeader();
+ auto Version = FH.Version;
auto CycleFreq = FH.CycleFrequency;
unsigned id_counter = 0;
@@ -282,11 +298,11 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
StackRootsByThreadId, StacksByStackId,
&id_counter, NodeStore);
// Each record is represented as a json dictionary with function name,
- // type of B for begin or E for end, thread id, process id (faked),
+ // type of B for begin or E for end, thread id, process id,
// timestamp in microseconds, and a stack frame id. The ids are logged
// in an id dictionary after the events.
- writeTraceViewerRecord(OS, R.FuncId, R.TId, Symbolize, FuncIdHelper,
- EventTimestampUs, *StackCursor, "B");
+ writeTraceViewerRecord(Version, OS, R.FuncId, R.TId, R.PId, Symbolize,
+ FuncIdHelper, EventTimestampUs, *StackCursor, "B");
break;
case RecordTypes::EXIT:
case RecordTypes::TAIL_EXIT:
@@ -297,9 +313,12 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records,
// (And/Or in loop termination below)
StackTrieNode *PreviousCursor = nullptr;
do {
- writeTraceViewerRecord(OS, StackCursor->FuncId, R.TId, Symbolize,
- FuncIdHelper, EventTimestampUs, *StackCursor,
- "E");
+ if (PreviousCursor != nullptr) {
+ OS << ",\n";
+ }
+ writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId,
+ Symbolize, FuncIdHelper, EventTimestampUs,
+ *StackCursor, "E");
PreviousCursor = StackCursor;
StackCursor = StackCursor->Parent;
} while (PreviousCursor->FuncId != R.FuncId && StackCursor != nullptr);
diff --git a/contrib/llvm/tools/llvm-xray/xray-extract.cc b/contrib/llvm/tools/llvm-xray/xray-extract.cpp
index cd87798d0e60..10fe7d8d6209 100644
--- a/contrib/llvm/tools/llvm-xray/xray-extract.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-extract.cpp
@@ -1,4 +1,4 @@
-//===- xray-extract.cc - XRay Instrumentation Map Extraction --------------===//
+//===- xray-extract.cpp: XRay Instrumentation Map Extraction --------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph-diff.cc b/contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp
index 3c69b3fb0751..a22f2a99811d 100644
--- a/contrib/llvm/tools/llvm-xray/xray-graph-diff.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-graph-diff.cpp
@@ -1,4 +1,4 @@
-//===-- xray-graph-diff.cc - XRay Function Call Graph Renderer ------------===//
+//===-- xray-graph-diff.cpp: XRay Function Call Graph Renderer ------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph.cc b/contrib/llvm/tools/llvm-xray/xray-graph.cpp
index feb676331f89..c619bf86299b 100644
--- a/contrib/llvm/tools/llvm-xray/xray-graph.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-graph.cpp
@@ -1,4 +1,4 @@
-//===-- xray-graph.cc - XRay Function Call Graph Renderer -----------------===//
+//===-- xray-graph.cpp: XRay Function Call Graph Renderer -----------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-xray/xray-graph.h b/contrib/llvm/tools/llvm-xray/xray-graph.h
index a43df265d0e1..fc7f8bb470f2 100644
--- a/contrib/llvm/tools/llvm-xray/xray-graph.h
+++ b/contrib/llvm/tools/llvm-xray/xray-graph.h
@@ -80,7 +80,7 @@ public:
using FunctionStack = SmallVector<FunctionAttr, 4>;
using PerThreadFunctionStackMap =
- DenseMap<llvm::sys::ProcessInfo::ProcessId, FunctionStack>;
+ DenseMap<llvm::sys::procid_t, FunctionStack>;
class GraphT : public Graph<FunctionStats, CallStats, int32_t> {
public:
diff --git a/contrib/llvm/tools/llvm-xray/xray-registry.cc b/contrib/llvm/tools/llvm-xray/xray-registry.cpp
index 36d3a2e58f97..fe58e4deaa1e 100644
--- a/contrib/llvm/tools/llvm-xray/xray-registry.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-registry.cpp
@@ -1,4 +1,4 @@
-//===- xray-registry.cc - Implement a command registry. -------------------===//
+//===- xray-registry.cpp: Implement a command registry. -------------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/llvm-xray/xray-stacks.cc b/contrib/llvm/tools/llvm-xray/xray-stacks.cpp
index 9474de047990..1a6069780a31 100644
--- a/contrib/llvm/tools/llvm-xray/xray-stacks.cc
+++ b/contrib/llvm/tools/llvm-xray/xray-stacks.cpp
@@ -1,4 +1,4 @@
-//===- xray-stacks.cc - XRay Function Call Stack Accounting ---------------===//
+//===- xray-stacks.cpp: XRay Function Call Stack Accounting ---------------===//
//
// The LLVM Compiler Infrastructure
//
diff --git a/contrib/llvm/tools/opt/BreakpointPrinter.cpp b/contrib/llvm/tools/opt/BreakpointPrinter.cpp
index e5614ed061e3..d3f54c034f55 100644
--- a/contrib/llvm/tools/opt/BreakpointPrinter.cpp
+++ b/contrib/llvm/tools/opt/BreakpointPrinter.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief Breakpoint location printer.
+/// Breakpoint location printer.
///
//===----------------------------------------------------------------------===//
#include "BreakpointPrinter.h"
diff --git a/contrib/llvm/tools/opt/BreakpointPrinter.h b/contrib/llvm/tools/opt/BreakpointPrinter.h
index 81c88e19199e..57670e5ee8d8 100644
--- a/contrib/llvm/tools/opt/BreakpointPrinter.h
+++ b/contrib/llvm/tools/opt/BreakpointPrinter.h
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief Breakpoint location printer.
+/// Breakpoint location printer.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_OPT_BREAKPOINTPRINTER_H
diff --git a/contrib/llvm/tools/opt/Debugify.cpp b/contrib/llvm/tools/opt/Debugify.cpp
index 40ee545c098d..6c3cdc75e334 100644
--- a/contrib/llvm/tools/opt/Debugify.cpp
+++ b/contrib/llvm/tools/opt/Debugify.cpp
@@ -12,6 +12,7 @@
///
//===----------------------------------------------------------------------===//
+#include "Debugify.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/IR/BasicBlock.h"
@@ -34,10 +35,37 @@ using namespace llvm;
namespace {
-bool applyDebugifyMetadata(Module &M) {
+cl::opt<bool> Quiet("debugify-quiet",
+ cl::desc("Suppress verbose debugify output"));
+
+raw_ostream &dbg() { return Quiet ? nulls() : errs(); }
+
+uint64_t getAllocSizeInBits(Module &M, Type *Ty) {
+ return Ty->isSized() ? M.getDataLayout().getTypeAllocSizeInBits(Ty) : 0;
+}
+
+bool isFunctionSkipped(Function &F) {
+ return F.isDeclaration() || !F.hasExactDefinition();
+}
+
+/// Find the basic block's terminating instruction.
+///
+/// Special care is needed to handle musttail and deopt calls, as these behave
+/// like (but are in fact not) terminators.
+Instruction *findTerminatingInstruction(BasicBlock &BB) {
+ if (auto *I = BB.getTerminatingMustTailCall())
+ return I;
+ if (auto *I = BB.getTerminatingDeoptimizeCall())
+ return I;
+ return BB.getTerminator();
+}
+
+bool applyDebugifyMetadata(Module &M,
+ iterator_range<Module::iterator> Functions,
+ StringRef Banner) {
// Skip modules with debug info.
if (M.getNamedMetadata("llvm.dbg.cu")) {
- errs() << "Debugify: Skipping module with debug info\n";
+ dbg() << Banner << "Skipping module with debug info\n";
return false;
}
@@ -47,7 +75,7 @@ bool applyDebugifyMetadata(Module &M) {
// Get a DIType which corresponds to Ty.
DenseMap<uint64_t, DIType *> TypeCache;
auto getCachedDIType = [&](Type *Ty) -> DIType * {
- uint64_t Size = M.getDataLayout().getTypeAllocSizeInBits(Ty);
+ uint64_t Size = getAllocSizeInBits(M, Ty);
DIType *&DTy = TypeCache[Size];
if (!DTy) {
std::string Name = "ty" + utostr(Size);
@@ -59,20 +87,19 @@ bool applyDebugifyMetadata(Module &M) {
unsigned NextLine = 1;
unsigned NextVar = 1;
auto File = DIB.createFile(M.getName(), "/");
- auto CU =
- DIB.createCompileUnit(dwarf::DW_LANG_C, DIB.createFile(M.getName(), "/"),
- "debugify", /*isOptimized=*/true, "", 0);
+ auto CU = DIB.createCompileUnit(dwarf::DW_LANG_C, File, "debugify",
+ /*isOptimized=*/true, "", 0);
// Visit each instruction.
- for (Function &F : M) {
- if (F.isDeclaration())
+ for (Function &F : Functions) {
+ if (isFunctionSkipped(F))
continue;
auto SPType = DIB.createSubroutineType(DIB.getOrCreateTypeArray(None));
bool IsLocalToUnit = F.hasPrivateLinkage() || F.hasInternalLinkage();
auto SP =
DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, SPType,
- IsLocalToUnit, F.hasExactDefinition(), NextLine,
+ IsLocalToUnit, /*isDefinition=*/true, NextLine,
DINode::FlagZero, /*isOptimized=*/true);
F.setSubprogram(SP);
for (BasicBlock &BB : F) {
@@ -80,23 +107,39 @@ bool applyDebugifyMetadata(Module &M) {
for (Instruction &I : BB)
I.setDebugLoc(DILocation::get(Ctx, NextLine++, 1, SP));
+ // Inserting debug values into EH pads can break IR invariants.
+ if (BB.isEHPad())
+ continue;
+
+ // Find the terminating instruction, after which no debug values are
+ // attached.
+ Instruction *LastInst = findTerminatingInstruction(BB);
+ assert(LastInst && "Expected basic block with a terminator");
+
+ // Maintain an insertion point which can't be invalidated when updates
+ // are made.
+ BasicBlock::iterator InsertPt = BB.getFirstInsertionPt();
+ assert(InsertPt != BB.end() && "Expected to find an insertion point");
+ Instruction *InsertBefore = &*InsertPt;
+
// Attach debug values.
- for (Instruction &I : BB) {
+ for (Instruction *I = &*BB.begin(); I != LastInst; I = I->getNextNode()) {
// Skip void-valued instructions.
- if (I.getType()->isVoidTy())
+ if (I->getType()->isVoidTy())
continue;
- // Skip the terminator instruction and any just-inserted intrinsics.
- if (isa<TerminatorInst>(&I) || isa<DbgValueInst>(&I))
- break;
+ // Phis and EH pads must be grouped at the beginning of the block.
+ // Only advance the insertion point when we finish visiting these.
+ if (!isa<PHINode>(I) && !I->isEHPad())
+ InsertBefore = I->getNextNode();
std::string Name = utostr(NextVar++);
- const DILocation *Loc = I.getDebugLoc().get();
+ const DILocation *Loc = I->getDebugLoc().get();
auto LocalVar = DIB.createAutoVariable(SP, Name, File, Loc->getLine(),
- getCachedDIType(I.getType()),
+ getCachedDIType(I->getType()),
/*AlwaysPreserve=*/true);
- DIB.insertDbgValueIntrinsic(&I, LocalVar, DIB.createExpression(), Loc,
- BB.getTerminator());
+ DIB.insertDbgValueIntrinsic(I, LocalVar, DIB.createExpression(), Loc,
+ InsertBefore);
}
}
DIB.finalizeSubprogram(SP);
@@ -112,48 +155,110 @@ bool applyDebugifyMetadata(Module &M) {
};
addDebugifyOperand(NextLine - 1); // Original number of lines.
addDebugifyOperand(NextVar - 1); // Original number of variables.
+ assert(NMD->getNumOperands() == 2 &&
+ "llvm.debugify should have exactly 2 operands!");
+
+ // Claim that this synthetic debug info is valid.
+ StringRef DIVersionKey = "Debug Info Version";
+ if (!M.getModuleFlag(DIVersionKey))
+ M.addModuleFlag(Module::Warning, DIVersionKey, DEBUG_METADATA_VERSION);
+
return true;
}
-void checkDebugifyMetadata(Module &M) {
+/// Return true if a mis-sized diagnostic is issued for \p DVI.
+bool diagnoseMisSizedDbgValue(Module &M, DbgValueInst *DVI) {
+ // The size of a dbg.value's value operand should match the size of the
+ // variable it corresponds to.
+ //
+ // TODO: This, along with a check for non-null value operands, should be
+ // promoted to verifier failures.
+ Value *V = DVI->getValue();
+ if (!V)
+ return false;
+
+ // For now, don't try to interpret anything more complicated than an empty
+ // DIExpression. Eventually we should try to handle OP_deref and fragments.
+ if (DVI->getExpression()->getNumElements())
+ return false;
+
+ Type *Ty = V->getType();
+ uint64_t ValueOperandSize = getAllocSizeInBits(M, Ty);
+ Optional<uint64_t> DbgVarSize = DVI->getFragmentSizeInBits();
+ if (!ValueOperandSize || !DbgVarSize)
+ return false;
+
+ bool HasBadSize = false;
+ if (Ty->isIntegerTy()) {
+ auto Signedness = DVI->getVariable()->getSignedness();
+ if (Signedness && *Signedness == DIBasicType::Signedness::Signed)
+ HasBadSize = ValueOperandSize < *DbgVarSize;
+ } else {
+ HasBadSize = ValueOperandSize != *DbgVarSize;
+ }
+
+ if (HasBadSize) {
+ dbg() << "ERROR: dbg.value operand has size " << ValueOperandSize
+ << ", but its variable has size " << *DbgVarSize << ": ";
+ DVI->print(dbg());
+ dbg() << "\n";
+ }
+ return HasBadSize;
+}
+
+bool checkDebugifyMetadata(Module &M,
+ iterator_range<Module::iterator> Functions,
+ StringRef NameOfWrappedPass, StringRef Banner,
+ bool Strip, DebugifyStatsMap *StatsMap) {
// Skip modules without debugify metadata.
NamedMDNode *NMD = M.getNamedMetadata("llvm.debugify");
- if (!NMD)
- return;
+ if (!NMD) {
+ dbg() << Banner << "Skipping module without debugify metadata\n";
+ return false;
+ }
auto getDebugifyOperand = [&](unsigned Idx) -> unsigned {
return mdconst::extract<ConstantInt>(NMD->getOperand(Idx)->getOperand(0))
->getZExtValue();
};
+ assert(NMD->getNumOperands() == 2 &&
+ "llvm.debugify should have exactly 2 operands!");
unsigned OriginalNumLines = getDebugifyOperand(0);
unsigned OriginalNumVars = getDebugifyOperand(1);
bool HasErrors = false;
- // Find missing lines.
+ // Track debug info loss statistics if able.
+ DebugifyStatistics *Stats = nullptr;
+ if (StatsMap && !NameOfWrappedPass.empty())
+ Stats = &StatsMap->operator[](NameOfWrappedPass);
+
BitVector MissingLines{OriginalNumLines, true};
- for (Function &F : M) {
+ BitVector MissingVars{OriginalNumVars, true};
+ for (Function &F : Functions) {
+ if (isFunctionSkipped(F))
+ continue;
+
+ // Find missing lines.
for (Instruction &I : instructions(F)) {
if (isa<DbgValueInst>(&I))
continue;
auto DL = I.getDebugLoc();
- if (DL) {
+ if (DL && DL.getLine() != 0) {
MissingLines.reset(DL.getLine() - 1);
continue;
}
- outs() << "ERROR: Instruction with empty DebugLoc -- ";
- I.print(outs());
- outs() << "\n";
- HasErrors = true;
+ if (!DL) {
+ dbg() << "ERROR: Instruction with empty DebugLoc in function ";
+ dbg() << F.getName() << " --";
+ I.print(dbg());
+ dbg() << "\n";
+ HasErrors = true;
+ }
}
- }
- for (unsigned Idx : MissingLines.set_bits())
- outs() << "WARNING: Missing line " << Idx + 1 << "\n";
- // Find missing variables.
- BitVector MissingVars{OriginalNumVars, true};
- for (Function &F : M) {
+ // Find missing variables and mis-sized debug values.
for (Instruction &I : instructions(F)) {
auto *DVI = dyn_cast<DbgValueInst>(&I);
if (!DVI)
@@ -162,21 +267,70 @@ void checkDebugifyMetadata(Module &M) {
unsigned Var = ~0U;
(void)to_integer(DVI->getVariable()->getName(), Var, 10);
assert(Var <= OriginalNumVars && "Unexpected name for DILocalVariable");
- MissingVars.reset(Var - 1);
+ bool HasBadSize = diagnoseMisSizedDbgValue(M, DVI);
+ if (!HasBadSize)
+ MissingVars.reset(Var - 1);
+ HasErrors |= HasBadSize;
}
}
+
+ // Print the results.
+ for (unsigned Idx : MissingLines.set_bits())
+ dbg() << "WARNING: Missing line " << Idx + 1 << "\n";
+
for (unsigned Idx : MissingVars.set_bits())
- outs() << "ERROR: Missing variable " << Idx + 1 << "\n";
- HasErrors |= MissingVars.count() > 0;
+ dbg() << "WARNING: Missing variable " << Idx + 1 << "\n";
+
+ // Update DI loss statistics.
+ if (Stats) {
+ Stats->NumDbgLocsExpected += OriginalNumLines;
+ Stats->NumDbgLocsMissing += MissingLines.count();
+ Stats->NumDbgValuesExpected += OriginalNumVars;
+ Stats->NumDbgValuesMissing += MissingVars.count();
+ }
+
+ dbg() << Banner;
+ if (!NameOfWrappedPass.empty())
+ dbg() << " [" << NameOfWrappedPass << "]";
+ dbg() << ": " << (HasErrors ? "FAIL" : "PASS") << '\n';
- outs() << "CheckDebugify: " << (HasErrors ? "FAIL" : "PASS") << "\n";
+ // Strip the Debugify Metadata if required.
+ if (Strip) {
+ StripDebugInfo(M);
+ M.eraseNamedMetadata(NMD);
+ return true;
+ }
+
+ return false;
}
-/// Attach synthetic debug info to everything.
-struct DebugifyPass : public ModulePass {
- bool runOnModule(Module &M) override { return applyDebugifyMetadata(M); }
+/// ModulePass for attaching synthetic debug info to everything, used with the
+/// legacy module pass manager.
+struct DebugifyModulePass : public ModulePass {
+ bool runOnModule(Module &M) override {
+ return applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: ");
+ }
+
+ DebugifyModulePass() : ModulePass(ID) {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ static char ID; // Pass identification.
+};
+
+/// FunctionPass for attaching synthetic debug info to instructions within a
+/// single function, used with the legacy module pass manager.
+struct DebugifyFunctionPass : public FunctionPass {
+ bool runOnFunction(Function &F) override {
+ Module &M = *F.getParent();
+ auto FuncIt = F.getIterator();
+ return applyDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
+ "FunctionDebugify: ");
+ }
- DebugifyPass() : ModulePass(ID) {}
+ DebugifyFunctionPass() : FunctionPass(ID) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
@@ -185,28 +339,125 @@ struct DebugifyPass : public ModulePass {
static char ID; // Pass identification.
};
-/// Check debug info inserted by -debugify for completeness.
-struct CheckDebugifyPass : public ModulePass {
+/// ModulePass for checking debug info inserted by -debugify, used with the
+/// legacy module pass manager.
+struct CheckDebugifyModulePass : public ModulePass {
bool runOnModule(Module &M) override {
- checkDebugifyMetadata(M);
- return false;
+ return checkDebugifyMetadata(M, M.functions(), NameOfWrappedPass,
+ "CheckModuleDebugify", Strip, StatsMap);
+ }
+
+ CheckDebugifyModulePass(bool Strip = false, StringRef NameOfWrappedPass = "",
+ DebugifyStatsMap *StatsMap = nullptr)
+ : ModulePass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass),
+ StatsMap(StatsMap) {}
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override {
+ AU.setPreservesAll();
+ }
+
+ static char ID; // Pass identification.
+
+private:
+ bool Strip;
+ StringRef NameOfWrappedPass;
+ DebugifyStatsMap *StatsMap;
+};
+
+/// FunctionPass for checking debug info inserted by -debugify-function, used
+/// with the legacy module pass manager.
+struct CheckDebugifyFunctionPass : public FunctionPass {
+ bool runOnFunction(Function &F) override {
+ Module &M = *F.getParent();
+ auto FuncIt = F.getIterator();
+ return checkDebugifyMetadata(M, make_range(FuncIt, std::next(FuncIt)),
+ NameOfWrappedPass, "CheckFunctionDebugify",
+ Strip, StatsMap);
}
- CheckDebugifyPass() : ModulePass(ID) {}
+ CheckDebugifyFunctionPass(bool Strip = false,
+ StringRef NameOfWrappedPass = "",
+ DebugifyStatsMap *StatsMap = nullptr)
+ : FunctionPass(ID), Strip(Strip), NameOfWrappedPass(NameOfWrappedPass),
+ StatsMap(StatsMap) {}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
}
static char ID; // Pass identification.
+
+private:
+ bool Strip;
+ StringRef NameOfWrappedPass;
+ DebugifyStatsMap *StatsMap;
};
} // end anonymous namespace
-char DebugifyPass::ID = 0;
-static RegisterPass<DebugifyPass> X("debugify",
- "Attach debug info to everything");
+void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map) {
+ std::error_code EC;
+ raw_fd_ostream OS{Path, EC};
+ if (EC) {
+ errs() << "Could not open file: " << EC.message() << ", " << Path << '\n';
+ return;
+ }
+
+ OS << "Pass Name" << ',' << "# of missing debug values" << ','
+ << "# of missing locations" << ',' << "Missing/Expected value ratio" << ','
+ << "Missing/Expected location ratio" << '\n';
+ for (const auto &Entry : Map) {
+ StringRef Pass = Entry.first;
+ DebugifyStatistics Stats = Entry.second;
+
+ OS << Pass << ',' << Stats.NumDbgValuesMissing << ','
+ << Stats.NumDbgLocsMissing << ',' << Stats.getMissingValueRatio() << ','
+ << Stats.getEmptyLocationRatio() << '\n';
+ }
+}
+
+ModulePass *createDebugifyModulePass() { return new DebugifyModulePass(); }
+
+FunctionPass *createDebugifyFunctionPass() {
+ return new DebugifyFunctionPass();
+}
+
+PreservedAnalyses NewPMDebugifyPass::run(Module &M, ModuleAnalysisManager &) {
+ applyDebugifyMetadata(M, M.functions(), "ModuleDebugify: ");
+ return PreservedAnalyses::all();
+}
+
+ModulePass *createCheckDebugifyModulePass(bool Strip,
+ StringRef NameOfWrappedPass,
+ DebugifyStatsMap *StatsMap) {
+ return new CheckDebugifyModulePass(Strip, NameOfWrappedPass, StatsMap);
+}
+
+FunctionPass *createCheckDebugifyFunctionPass(bool Strip,
+ StringRef NameOfWrappedPass,
+ DebugifyStatsMap *StatsMap) {
+ return new CheckDebugifyFunctionPass(Strip, NameOfWrappedPass, StatsMap);
+}
+
+PreservedAnalyses NewPMCheckDebugifyPass::run(Module &M,
+ ModuleAnalysisManager &) {
+ checkDebugifyMetadata(M, M.functions(), "", "CheckModuleDebugify", false,
+ nullptr);
+ return PreservedAnalyses::all();
+}
+
+char DebugifyModulePass::ID = 0;
+static RegisterPass<DebugifyModulePass> DM("debugify",
+ "Attach debug info to everything");
+
+char CheckDebugifyModulePass::ID = 0;
+static RegisterPass<CheckDebugifyModulePass>
+ CDM("check-debugify", "Check debug info from -debugify");
+
+char DebugifyFunctionPass::ID = 0;
+static RegisterPass<DebugifyFunctionPass> DF("debugify-function",
+ "Attach debug info to a function");
-char CheckDebugifyPass::ID = 0;
-static RegisterPass<CheckDebugifyPass> Y("check-debugify",
- "Check debug info from -debugify");
+char CheckDebugifyFunctionPass::ID = 0;
+static RegisterPass<CheckDebugifyFunctionPass>
+ CDF("check-debugify-function", "Check debug info from -debugify-function");
diff --git a/contrib/llvm/tools/opt/Debugify.h b/contrib/llvm/tools/opt/Debugify.h
new file mode 100644
index 000000000000..d1a60c73e723
--- /dev/null
+++ b/contrib/llvm/tools/opt/Debugify.h
@@ -0,0 +1,75 @@
+//===- Debugify.h - Attach synthetic debug info to everything -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file Interface to the `debugify` synthetic debug info testing utility.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_TOOLS_OPT_DEBUGIFY_H
+#define LLVM_TOOLS_OPT_DEBUGIFY_H
+
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/raw_ostream.h"
+
+llvm::ModulePass *createDebugifyModulePass();
+llvm::FunctionPass *createDebugifyFunctionPass();
+
+struct NewPMDebugifyPass : public llvm::PassInfoMixin<NewPMDebugifyPass> {
+ llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
+};
+
+/// Track how much `debugify` information has been lost.
+struct DebugifyStatistics {
+ /// Number of missing dbg.values.
+ unsigned NumDbgValuesMissing = 0;
+
+ /// Number of dbg.values expected.
+ unsigned NumDbgValuesExpected = 0;
+
+ /// Number of instructions with empty debug locations.
+ unsigned NumDbgLocsMissing = 0;
+
+ /// Number of instructions expected to have debug locations.
+ unsigned NumDbgLocsExpected = 0;
+
+ /// Get the ratio of missing/expected dbg.values.
+ float getMissingValueRatio() const {
+ return float(NumDbgValuesMissing) / float(NumDbgLocsExpected);
+ }
+
+ /// Get the ratio of missing/expected instructions with locations.
+ float getEmptyLocationRatio() const {
+ return float(NumDbgLocsMissing) / float(NumDbgLocsExpected);
+ }
+};
+
+/// Map pass names to a per-pass DebugifyStatistics instance.
+using DebugifyStatsMap = llvm::MapVector<llvm::StringRef, DebugifyStatistics>;
+
+/// Export per-pass debugify statistics to the file specified by \p Path.
+void exportDebugifyStats(llvm::StringRef Path, const DebugifyStatsMap &Map);
+
+llvm::ModulePass *
+createCheckDebugifyModulePass(bool Strip = false,
+ llvm::StringRef NameOfWrappedPass = "",
+ DebugifyStatsMap *StatsMap = nullptr);
+
+llvm::FunctionPass *
+createCheckDebugifyFunctionPass(bool Strip = false,
+ llvm::StringRef NameOfWrappedPass = "",
+ DebugifyStatsMap *StatsMap = nullptr);
+
+struct NewPMCheckDebugifyPass
+ : public llvm::PassInfoMixin<NewPMCheckDebugifyPass> {
+ llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &AM);
+};
+
+#endif // LLVM_TOOLS_OPT_DEBUGIFY_H
diff --git a/contrib/llvm/tools/opt/NewPMDriver.cpp b/contrib/llvm/tools/opt/NewPMDriver.cpp
index a3f16f2538c4..a91d4cb5f9cd 100644
--- a/contrib/llvm/tools/opt/NewPMDriver.cpp
+++ b/contrib/llvm/tools/opt/NewPMDriver.cpp
@@ -13,12 +13,14 @@
///
//===----------------------------------------------------------------------===//
+#include "Debugify.h"
#include "NewPMDriver.h"
+#include "PassPrinters.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Analysis/AliasAnalysis.h"
#include "llvm/Analysis/CGSCCPassManager.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
-#include "llvm/Config/config.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/IR/Dominators.h"
#include "llvm/IR/IRPrintingPasses.h"
#include "llvm/IR/LLVMContext.h"
@@ -26,6 +28,7 @@
#include "llvm/IR/PassManager.h"
#include "llvm/IR/Verifier.h"
#include "llvm/Passes/PassBuilder.h"
+#include "llvm/Passes/PassPlugin.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ToolOutputFile.h"
@@ -40,6 +43,10 @@ static cl::opt<bool>
DebugPM("debug-pass-manager", cl::Hidden,
cl::desc("Print pass management debugging information"));
+static cl::list<std::string>
+ PassPlugins("load-pass-plugin",
+ cl::desc("Load passes from plugin library"));
+
// This flag specifies a textual description of the alias analysis pipeline to
// use when querying for aliasing information. It only works in concert with
// the "passes" flag above.
@@ -82,6 +89,11 @@ static cl::opt<std::string> VectorizerStartEPPipeline(
cl::desc("A textual description of the function pass pipeline inserted at "
"the VectorizerStart extension point into default pipelines"),
cl::Hidden);
+static cl::opt<std::string> PipelineStartEPPipeline(
+ "passes-ep-pipeline-start",
+ cl::desc("A textual description of the function pass pipeline inserted at "
+ "the PipelineStart extension point into default pipelines"),
+ cl::Hidden);
enum PGOKind { NoPGO, InstrGen, InstrUse, SampleUse };
static cl::opt<PGOKind> PGOKindFlag(
"pgo-kind", cl::init(NoPGO), cl::Hidden,
@@ -159,6 +171,12 @@ static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass,
PB.parsePassPipeline(PM, VectorizerStartEPPipeline, VerifyEachPass,
DebugLogging);
});
+ if (tryParsePipelineText<ModulePassManager>(PB, PipelineStartEPPipeline))
+ PB.registerPipelineStartEPCallback(
+ [&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM) {
+ PB.parsePassPipeline(PM, PipelineStartEPPipeline, VerifyEachPass,
+ DebugLogging);
+ });
}
#ifdef LINK_POLLY_INTO_TOOLS
@@ -174,7 +192,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
VerifierKind VK,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
- bool EmitSummaryIndex, bool EmitModuleHash) {
+ bool EmitSummaryIndex, bool EmitModuleHash,
+ bool EnableDebugify) {
bool VerifyEachPass = VK == VK_VerifyEachPass;
Optional<PGOOptions> P;
@@ -197,6 +216,32 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
PassBuilder PB(TM, P);
registerEPCallbacks(PB, VerifyEachPass, DebugPM);
+ // Load requested pass plugins and let them register pass builder callbacks
+ for (auto &PluginFN : PassPlugins) {
+ auto PassPlugin = PassPlugin::Load(PluginFN);
+ if (!PassPlugin) {
+ errs() << "Failed to load passes from '" << PluginFN
+ << "'. Request ignored.\n";
+ continue;
+ }
+
+ PassPlugin->registerPassBuilderCallbacks(PB);
+ }
+
+ // Register a callback that creates the debugify passes as needed.
+ PB.registerPipelineParsingCallback(
+ [](StringRef Name, ModulePassManager &MPM,
+ ArrayRef<PassBuilder::PipelineElement>) {
+ if (Name == "debugify") {
+ MPM.addPass(NewPMDebugifyPass());
+ return true;
+ } else if (Name == "check-debugify") {
+ MPM.addPass(NewPMCheckDebugifyPass());
+ return true;
+ }
+ return false;
+ });
+
#ifdef LINK_POLLY_INTO_TOOLS
polly::RegisterPollyPasses(PB);
#endif
@@ -227,6 +272,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
ModulePassManager MPM(DebugPM);
if (VK > VK_NoVerifier)
MPM.addPass(VerifierPass());
+ if (EnableDebugify)
+ MPM.addPass(NewPMDebugifyPass());
if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) {
errs() << Arg0 << ": unable to parse pass pipeline description.\n";
@@ -235,6 +282,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
if (VK > VK_NoVerifier)
MPM.addPass(VerifierPass());
+ if (EnableDebugify)
+ MPM.addPass(NewPMCheckDebugifyPass());
// Add any relevant output pass at the end of the pipeline.
switch (OK) {
diff --git a/contrib/llvm/tools/opt/NewPMDriver.h b/contrib/llvm/tools/opt/NewPMDriver.h
index e5490deaeaf5..7d74a5777d11 100644
--- a/contrib/llvm/tools/opt/NewPMDriver.h
+++ b/contrib/llvm/tools/opt/NewPMDriver.h
@@ -42,7 +42,7 @@ enum VerifierKind {
};
}
-/// \brief Driver function to run the new pass manager over a module.
+/// Driver function to run the new pass manager over a module.
///
/// This function only exists factored away from opt.cpp in order to prevent
/// inclusion of the new pass manager headers and the old headers into the same
@@ -57,7 +57,8 @@ bool runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM,
opt_tool::OutputKind OK, opt_tool::VerifierKind VK,
bool ShouldPreserveAssemblyUseListOrder,
bool ShouldPreserveBitcodeUseListOrder,
- bool EmitSummaryIndex, bool EmitModuleHash);
-}
+ bool EmitSummaryIndex, bool EmitModuleHash,
+ bool EnableDebugify);
+} // namespace llvm
#endif
diff --git a/contrib/llvm/tools/opt/PassPrinters.cpp b/contrib/llvm/tools/opt/PassPrinters.cpp
index f52b52080949..310d491c06a5 100644
--- a/contrib/llvm/tools/opt/PassPrinters.cpp
+++ b/contrib/llvm/tools/opt/PassPrinters.cpp
@@ -8,7 +8,7 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief Utilities to print analysis info for various kinds of passes.
+/// Utilities to print analysis info for various kinds of passes.
///
//===----------------------------------------------------------------------===//
diff --git a/contrib/llvm/tools/opt/PassPrinters.h b/contrib/llvm/tools/opt/PassPrinters.h
index 14b6e43d18e0..e66f3f457b7a 100644
--- a/contrib/llvm/tools/opt/PassPrinters.h
+++ b/contrib/llvm/tools/opt/PassPrinters.h
@@ -8,13 +8,15 @@
//===----------------------------------------------------------------------===//
///
/// \file
-/// \brief Utilities to print analysis info for various kinds of passes.
+/// Utilities to print analysis info for various kinds of passes.
///
//===----------------------------------------------------------------------===//
#ifndef LLVM_TOOLS_OPT_PASSPRINTERS_H
#define LLVM_TOOLS_OPT_PASSPRINTERS_H
+#include "llvm/IR/PassManager.h"
+
namespace llvm {
class BasicBlockPass;
@@ -25,6 +27,7 @@ class LoopPass;
class PassInfo;
class raw_ostream;
class RegionPass;
+class Module;
FunctionPass *createFunctionPassPrinter(const PassInfo *PI, raw_ostream &out,
bool Quiet);
diff --git a/contrib/llvm/tools/opt/opt.cpp b/contrib/llvm/tools/opt/opt.cpp
index c471e0f2e3ec..6e287b6c0ab6 100644
--- a/contrib/llvm/tools/opt/opt.cpp
+++ b/contrib/llvm/tools/opt/opt.cpp
@@ -13,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "BreakpointPrinter.h"
+#include "Debugify.h"
#include "NewPMDriver.h"
#include "PassPrinters.h"
#include "llvm/ADT/Triple.h"
@@ -23,8 +24,9 @@
#include "llvm/Analysis/TargetLibraryInfo.h"
#include "llvm/Analysis/TargetTransformInfo.h"
#include "llvm/Bitcode/BitcodeWriterPass.h"
-#include "llvm/CodeGen/CommandFlags.def"
+#include "llvm/CodeGen/CommandFlags.inc"
#include "llvm/CodeGen/TargetPassConfig.h"
+#include "llvm/Config/llvm-config.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/DebugInfo.h"
#include "llvm/IR/IRPrintingPasses.h"
@@ -41,10 +43,8 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Host.h"
-#include "llvm/Support/ManagedStatic.h"
+#include "llvm/Support/InitLLVM.h"
#include "llvm/Support/PluginLoader.h"
-#include "llvm/Support/PrettyStackTrace.h"
-#include "llvm/Support/Signals.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/SystemUtils.h"
#include "llvm/Support/TargetRegistry.h"
@@ -123,7 +123,11 @@ StripDebug("strip-debug",
cl::desc("Strip debugger symbol info from translation unit"));
static cl::opt<bool>
-DisableInline("disable-inlining", cl::desc("Do not run the inliner pass"));
+ StripNamedMetadata("strip-named-metadata",
+ cl::desc("Strip module-level named metadata"));
+
+static cl::opt<bool> DisableInline("disable-inlining",
+ cl::desc("Do not run the inliner pass"));
static cl::opt<bool>
DisableOptimizations("disable-opt",
@@ -203,6 +207,21 @@ QuietA("quiet", cl::desc("Alias for -q"), cl::aliasopt(Quiet));
static cl::opt<bool>
AnalyzeOnly("analyze", cl::desc("Only perform analysis, no optimization"));
+static cl::opt<bool> EnableDebugify(
+ "enable-debugify",
+ cl::desc(
+ "Start the pipeline with debugify and end it with check-debugify"));
+
+static cl::opt<bool> DebugifyEach(
+ "debugify-each",
+ cl::desc(
+ "Start each pass with debugify and end it with check-debugify"));
+
+static cl::opt<std::string>
+ DebugifyExport("debugify-export",
+ cl::desc("Export per-pass debugify statistics to this file"),
+ cl::value_desc("filename"), cl::init(""));
+
static cl::opt<bool>
PrintBreakpoints("print-breakpoints-for-testing",
cl::desc("Print select breakpoints location for testing"));
@@ -252,6 +271,48 @@ static cl::opt<std::string>
cl::desc("YAML output filename for pass remarks"),
cl::value_desc("filename"));
+class OptCustomPassManager : public legacy::PassManager {
+ DebugifyStatsMap DIStatsMap;
+
+public:
+ using super = legacy::PassManager;
+
+ void add(Pass *P) override {
+ // Wrap each pass with (-check)-debugify passes if requested, making
+ // exceptions for passes which shouldn't see -debugify instrumentation.
+ bool WrapWithDebugify = DebugifyEach && !P->getAsImmutablePass() &&
+ !isIRPrintingPass(P) && !isBitcodeWriterPass(P);
+ if (!WrapWithDebugify) {
+ super::add(P);
+ return;
+ }
+
+ // Apply -debugify/-check-debugify before/after each pass and collect
+ // debug info loss statistics.
+ PassKind Kind = P->getPassKind();
+ StringRef Name = P->getPassName();
+
+ // TODO: Implement Debugify for BasicBlockPass, LoopPass.
+ switch (Kind) {
+ case PT_Function:
+ super::add(createDebugifyFunctionPass());
+ super::add(P);
+ super::add(createCheckDebugifyFunctionPass(true, Name, &DIStatsMap));
+ break;
+ case PT_Module:
+ super::add(createDebugifyModulePass());
+ super::add(P);
+ super::add(createCheckDebugifyModulePass(true, Name, &DIStatsMap));
+ break;
+ default:
+ super::add(P);
+ break;
+ }
+ }
+
+ const DebugifyStatsMap &getDebugifyStatsMap() const { return DIStatsMap; }
+};
+
static inline void addPass(legacy::PassManagerBase &PM, Pass *P) {
// Add the pass to the pass manager...
PM.add(P);
@@ -362,13 +423,11 @@ void initializePollyPasses(llvm::PassRegistry &Registry);
// main for opt
//
int main(int argc, char **argv) {
- sys::PrintStackTraceOnErrorSignal(argv[0]);
- llvm::PrettyStackTraceProgram X(argc, argv);
+ InitLLVM X(argc, argv);
// Enable debug stream buffering.
EnableDebugBuffering = true;
- llvm_shutdown_obj Y; // Call llvm_shutdown() on exit.
LLVMContext Context;
InitializeAllTargets();
@@ -387,6 +446,7 @@ int main(int argc, char **argv) {
initializeAnalysis(Registry);
initializeTransformUtils(Registry);
initializeInstCombine(Registry);
+ initializeAggressiveInstCombine(Registry);
initializeInstrumentation(Registry);
initializeTarget(Registry);
// For codegen passes, only passes that do IR to IR transformation are
@@ -408,6 +468,7 @@ int main(int argc, char **argv) {
initializePostInlineEntryExitInstrumenterPass(Registry);
initializeUnreachableBlockElimLegacyPassPass(Registry);
initializeExpandReductionsPass(Registry);
+ initializeWasmEHPreparePass(Registry);
initializeWriteBitcodePassPass(Registry);
#ifdef LINK_POLLY_INTO_TOOLS
@@ -449,7 +510,7 @@ int main(int argc, char **argv) {
// Load the input module...
std::unique_ptr<Module> M =
- parseIRFile(InputFilename, Err, Context, !NoVerify);
+ parseIRFile(InputFilename, Err, Context, !NoVerify, ClDataLayout);
if (!M) {
Err.print(argv[0], errs());
@@ -460,6 +521,18 @@ int main(int argc, char **argv) {
if (StripDebug)
StripDebugInfo(*M);
+ // Erase module-level named metadata, if requested.
+ if (StripNamedMetadata) {
+ while (!M->named_metadata_empty()) {
+ NamedMDNode *NMD = &*M->named_metadata_begin();
+ M->eraseNamedMetadata(NMD);
+ }
+ }
+
+ // If we are supposed to override the target triple or data layout, do so now.
+ if (!TargetTriple.empty())
+ M->setTargetTriple(Triple::normalize(TargetTriple));
+
// Immediately run the verifier to catch any problems before starting up the
// pass pipelines. Otherwise we can crash on broken code during
// doInitialization().
@@ -469,12 +542,6 @@ int main(int argc, char **argv) {
return 1;
}
- // If we are supposed to override the target triple or data layout, do so now.
- if (!TargetTriple.empty())
- M->setTargetTriple(Triple::normalize(TargetTriple));
- if (!ClDataLayout.empty())
- M->setDataLayout(ClDataLayout);
-
// Figure out what stream we are supposed to write to...
std::unique_ptr<ToolOutputFile> Out;
std::unique_ptr<ToolOutputFile> ThinLinkOut;
@@ -548,15 +615,15 @@ int main(int argc, char **argv) {
OptRemarkFile.get(), PassPipeline, OK, VK,
PreserveAssemblyUseListOrder,
PreserveBitcodeUseListOrder, EmitSummaryIndex,
- EmitModuleHash)
+ EmitModuleHash, EnableDebugify)
? 0
: 1;
}
// Create a PassManager to hold and optimize the collection of passes we are
// about to build.
- //
- legacy::PassManager Passes;
+ OptCustomPassManager Passes;
+ bool AddOneTimeDebugifyPasses = EnableDebugify && !DebugifyEach;
// Add an appropriate TargetLibraryInfo pass for the module's triple.
TargetLibraryInfoImpl TLII(ModuleTriple);
@@ -570,6 +637,9 @@ int main(int argc, char **argv) {
Passes.add(createTargetTransformInfoWrapperPass(TM ? TM->getTargetIRAnalysis()
: TargetIRAnalysis()));
+ if (AddOneTimeDebugifyPasses)
+ Passes.add(createDebugifyModulePass());
+
std::unique_ptr<legacy::FunctionPassManager> FPasses;
if (OptLevelO0 || OptLevelO1 || OptLevelO2 || OptLevelOs || OptLevelOz ||
OptLevelO3) {
@@ -715,12 +785,15 @@ int main(int argc, char **argv) {
if (!NoVerify && !VerifyEach)
Passes.add(createVerifierPass());
+ if (AddOneTimeDebugifyPasses)
+ Passes.add(createCheckDebugifyModulePass(false));
+
// In run twice mode, we want to make sure the output is bit-by-bit
// equivalent if we run the pass manager again, so setup two buffers and
// a stream to write to them. Note that llc does something similar and it
// may be worth to abstract this out in the future.
SmallVector<char, 0> Buffer;
- SmallVector<char, 0> CompileTwiceBuffer;
+ SmallVector<char, 0> FirstRunBuffer;
std::unique_ptr<raw_svector_ostream> BOS;
raw_ostream *OS = nullptr;
@@ -749,28 +822,30 @@ int main(int argc, char **argv) {
// Before executing passes, print the final values of the LLVM options.
cl::PrintOptionValues();
- // If requested, run all passes again with the same pass manager to catch
- // bugs caused by persistent state in the passes
- if (RunTwice) {
- std::unique_ptr<Module> M2(CloneModule(M.get()));
- Passes.run(*M2);
- CompileTwiceBuffer = Buffer;
- Buffer.clear();
- }
-
- // Now that we have all of the passes ready, run them.
- Passes.run(*M);
-
- // Compare the two outputs and make sure they're the same
- if (RunTwice) {
+ if (!RunTwice) {
+ // Now that we have all of the passes ready, run them.
+ Passes.run(*M);
+ } else {
+ // If requested, run all passes twice with the same pass manager to catch
+ // bugs caused by persistent state in the passes.
+ std::unique_ptr<Module> M2(CloneModule(*M));
+ // Run all passes on the original module first, so the second run processes
+ // the clone to catch CloneModule bugs.
+ Passes.run(*M);
+ FirstRunBuffer = Buffer;
+ Buffer.clear();
+
+ Passes.run(*M2);
+
+ // Compare the two outputs and make sure they're the same
assert(Out);
- if (Buffer.size() != CompileTwiceBuffer.size() ||
- (memcmp(Buffer.data(), CompileTwiceBuffer.data(), Buffer.size()) !=
- 0)) {
- errs() << "Running the pass manager twice changed the output.\n"
- "Writing the result of the second run to the specified output.\n"
- "To generate the one-run comparison binary, just run without\n"
- "the compile-twice option\n";
+ if (Buffer.size() != FirstRunBuffer.size() ||
+ (memcmp(Buffer.data(), FirstRunBuffer.data(), Buffer.size()) != 0)) {
+ errs()
+ << "Running the pass manager twice changed the output.\n"
+ "Writing the result of the second run to the specified output.\n"
+ "To generate the one-run comparison binary, just run without\n"
+ "the compile-twice option\n";
Out->os() << BOS->str();
Out->keep();
if (OptRemarkFile)
@@ -780,6 +855,9 @@ int main(int argc, char **argv) {
Out->os() << BOS->str();
}
+ if (DebugifyEach && !DebugifyExport.empty())
+ exportDebugifyStats(DebugifyExport, Passes.getDebugifyStatsMap());
+
// Declare success.
if (!NoOutput || PrintBreakpoints)
Out->keep();