diff options
Diffstat (limited to 'contrib/llvm/tools')
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 += "&"; - else if (C == '<') - Result += "<"; - else if (C == '>') - Result += ">"; - else if (C == '\"') - Result += """; - 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 += " "; + 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(); |