diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2018-07-28 10:51:19 +0000 |
commit | eb11fae6d08f479c0799db45860a98af528fa6e7 (patch) | |
tree | 44d492a50c8c1a7eb8e2d17ea3360ec4d066f042 /tools | |
parent | b8a2042aa938069e862750553db0e4d82d25822c (diff) |
Notes
Diffstat (limited to 'tools')
355 files changed, 27731 insertions, 8833 deletions
diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 63caea64cf34..1732ea0cb8a0 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -32,12 +32,13 @@ subdirectories = llvm-dis llvm-dwarfdump llvm-dwp + llvm-exegesis llvm-extract llvm-jitlistener llvm-link llvm-lto llvm-mc - llvm-mcmarkup + llvm-mca llvm-modextract llvm-mt llvm-nm @@ -49,6 +50,7 @@ subdirectories = llvm-rtdyld llvm-size llvm-split + llvm-undname opt verify-uselistorder diff --git a/tools/bugpoint/BugDriver.cpp b/tools/bugpoint/BugDriver.cpp index 37bdb7bc96b6..3832e075a693 100644 --- a/tools/bugpoint/BugDriver.cpp +++ b/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/tools/bugpoint/BugDriver.h b/tools/bugpoint/BugDriver.h index 0e6a9b4f2f38..bc60ae753548 100644 --- a/tools/bugpoint/BugDriver.h +++ b/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/tools/bugpoint/CMakeLists.txt b/tools/bugpoint/CMakeLists.txt index 72c597379c8b..654ecc496a91 100644 --- a/tools/bugpoint/CMakeLists.txt +++ b/tools/bugpoint/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS Core IPO IRReader + AggressiveInstCombine InstCombine Instrumentation Linker diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index 9097917d5fef..a5b31e1ab321 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/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/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp index 7562aa603bbb..773bad69fae0 100644 --- a/tools/bugpoint/ExecutionDriver.cpp +++ b/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/tools/bugpoint/ExtractFunction.cpp b/tools/bugpoint/ExtractFunction.cpp index 431dcedfe203..48f1575c25eb 100644 --- a/tools/bugpoint/ExtractFunction.cpp +++ b/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/tools/bugpoint/FindBugs.cpp b/tools/bugpoint/FindBugs.cpp index 40502cbf9495..a695e875b787 100644 --- a/tools/bugpoint/FindBugs.cpp +++ b/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/tools/bugpoint/Miscompilation.cpp b/tools/bugpoint/Miscompilation.cpp index 80f4cea23481..375bee7a0d50 100644 --- a/tools/bugpoint/Miscompilation.cpp +++ b/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/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index ee3f2f0174d2..cbb048db8fe7 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/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/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index 8094dfdd78fa..812e8e3bbae5 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/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/tools/bugpoint/bugpoint.cpp b/tools/bugpoint/bugpoint.cpp index ec1ca2e54968..f6b7d08455d4 100644 --- a/tools/bugpoint/bugpoint.cpp +++ b/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/tools/dsymutil/BinaryHolder.cpp b/tools/dsymutil/BinaryHolder.cpp index eabbe8d83065..e78871c48779 100644 --- a/tools/dsymutil/BinaryHolder.cpp +++ b/tools/dsymutil/BinaryHolder.cpp @@ -14,11 +14,21 @@ #include "BinaryHolder.h" #include "llvm/Object/MachO.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" namespace llvm { namespace dsymutil { +static std::pair<StringRef, StringRef> +getArchiveAndObjectName(StringRef Filename) { + StringRef Archive = Filename.substr(0, Filename.find('(')); + StringRef Object = Filename.substr(Archive.size() + 1).drop_back(); + return {Archive, Object}; +} + +static bool isArchive(StringRef Filename) { return Filename.endswith(")"); } + static std::vector<MemoryBufferRef> getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem, object::MachOUniversalBinary &Fat) { @@ -32,175 +42,221 @@ getMachOFatMemoryBuffers(StringRef Filename, MemoryBuffer &Mem, return Buffers; } -void BinaryHolder::changeBackingMemoryBuffer( - std::unique_ptr<MemoryBuffer> &&Buf) { - CurrentArchives.clear(); - CurrentObjectFiles.clear(); - CurrentFatBinary.reset(); +Error BinaryHolder::ArchiveEntry::load(StringRef Filename, + TimestampTy Timestamp, bool Verbose) { + StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; - CurrentMemoryBuffer = std::move(Buf); -} + // Try to load archive and force it to be memory mapped. + auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename, -1, false); + if (auto Err = ErrOrBuff.getError()) + return errorCodeToError(Err); + + MemBuffer = std::move(*ErrOrBuff); -ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetMemoryBuffersForFile( - StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { if (Verbose) - outs() << "trying to open '" << Filename << "'\n"; - - // Try that first as it doesn't involve any filesystem access. - if (auto ErrOrArchiveMembers = GetArchiveMemberBuffers(Filename, Timestamp)) - return *ErrOrArchiveMembers; - - // If the name ends with a closing paren, there is a huge chance - // it is an archive member specification. - if (Filename.endswith(")")) - if (auto ErrOrArchiveMembers = - MapArchiveAndGetMemberBuffers(Filename, Timestamp)) - return *ErrOrArchiveMembers; - - // Otherwise, just try opening a standard file. If this is an - // archive member specifiaction and any of the above didn't handle it - // (either because the archive is not there anymore, or because the - // archive doesn't contain the requested member), this will still - // provide a sensible error message. - auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(Filename); - if (auto Err = ErrOrFile.getError()) - return Err; - - changeBackingMemoryBuffer(std::move(*ErrOrFile)); + WithColor::note() << "loaded archive '" << ArchiveFilename << "'\n"; + + // Load one or more archive buffers, depending on whether we're dealing with + // a fat binary. + std::vector<MemoryBufferRef> ArchiveBuffers; + + auto ErrOrFat = + object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); + if (!ErrOrFat) { + consumeError(ErrOrFat.takeError()); + ArchiveBuffers.push_back(MemBuffer->getMemBufferRef()); + } else { + FatBinary = std::move(*ErrOrFat); + FatBinaryName = ArchiveFilename; + ArchiveBuffers = + getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); + } + + // Finally, try to load the archives. + Archives.reserve(ArchiveBuffers.size()); + for (auto MemRef : ArchiveBuffers) { + auto ErrOrArchive = object::Archive::create(MemRef); + if (!ErrOrArchive) + return ErrOrArchive.takeError(); + Archives.push_back(std::move(*ErrOrArchive)); + } + + return Error::success(); +} + +Error BinaryHolder::ObjectEntry::load(StringRef Filename, bool Verbose) { + // Try to load regular binary and force it to be memory mapped. + auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(Filename, -1, false); + if (auto Err = ErrOrBuff.getError()) + return errorCodeToError(Err); + + MemBuffer = std::move(*ErrOrBuff); + if (Verbose) - outs() << "\tloaded file.\n"; + WithColor::note() << "loaded object.\n"; + + // Load one or more object buffers, depending on whether we're dealing with a + // fat binary. + std::vector<MemoryBufferRef> ObjectBuffers; - auto ErrOrFat = object::MachOUniversalBinary::create( - CurrentMemoryBuffer->getMemBufferRef()); + auto ErrOrFat = + object::MachOUniversalBinary::create(MemBuffer->getMemBufferRef()); if (!ErrOrFat) { consumeError(ErrOrFat.takeError()); - // Not a fat binary must be a standard one. Return a one element vector. - return std::vector<MemoryBufferRef>{CurrentMemoryBuffer->getMemBufferRef()}; + ObjectBuffers.push_back(MemBuffer->getMemBufferRef()); + } else { + FatBinary = std::move(*ErrOrFat); + FatBinaryName = Filename; + ObjectBuffers = + getMachOFatMemoryBuffers(FatBinaryName, *MemBuffer, *FatBinary); } - CurrentFatBinary = std::move(*ErrOrFat); - CurrentFatBinaryName = Filename; - return getMachOFatMemoryBuffers(CurrentFatBinaryName, *CurrentMemoryBuffer, - *CurrentFatBinary); + Objects.reserve(ObjectBuffers.size()); + for (auto MemRef : ObjectBuffers) { + auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemRef); + if (!ErrOrObjectFile) + return ErrOrObjectFile.takeError(); + Objects.push_back(std::move(*ErrOrObjectFile)); + } + + return Error::success(); } -ErrorOr<std::vector<MemoryBufferRef>> BinaryHolder::GetArchiveMemberBuffers( - StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { - if (CurrentArchives.empty()) - return make_error_code(errc::no_such_file_or_directory); +std::vector<const object::ObjectFile *> +BinaryHolder::ObjectEntry::getObjects() const { + std::vector<const object::ObjectFile *> Result; + Result.reserve(Objects.size()); + for (auto &Object : Objects) { + Result.push_back(Object.get()); + } + return Result; +} +Expected<const object::ObjectFile &> +BinaryHolder::ObjectEntry::getObject(const Triple &T) const { + for (const auto &Obj : Objects) { + if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { + if (MachO->getArchTriple().str() == T.str()) + return *MachO; + } else if (Obj->getArch() == T.getArch()) + return *Obj; + } + return errorCodeToError(object::object_error::arch_not_found); +} - StringRef CurArchiveName = CurrentArchives.front()->getFileName(); - if (!Filename.startswith(Twine(CurArchiveName, "(").str())) - return make_error_code(errc::no_such_file_or_directory); +Expected<const BinaryHolder::ObjectEntry &> +BinaryHolder::ArchiveEntry::getObjectEntry(StringRef Filename, + TimestampTy Timestamp, + bool Verbose) { + StringRef ArchiveFilename; + StringRef ObjectFilename; + std::tie(ArchiveFilename, ObjectFilename) = getArchiveAndObjectName(Filename); - // Remove the archive name and the parens around the archive member name. - Filename = Filename.substr(CurArchiveName.size() + 1).drop_back(); + // Try the cache first. + KeyTy Key = {ObjectFilename, Timestamp}; - std::vector<MemoryBufferRef> Buffers; - Buffers.reserve(CurrentArchives.size()); + { + std::lock_guard<std::mutex> Lock(MemberCacheMutex); + if (MemberCache.count(Key)) + return MemberCache[Key]; + } + + // Create a new ObjectEntry, but don't add it to the cache yet. Loading of + // the archive members might fail and we don't want to lock the whole archive + // during this operation. + ObjectEntry OE; - for (const auto &CurrentArchive : CurrentArchives) { + for (const auto &Archive : Archives) { Error Err = Error::success(); - for (auto Child : CurrentArchive->children(Err)) { + for (auto Child : Archive->children(Err)) { if (auto NameOrErr = Child.getName()) { - if (*NameOrErr == Filename) { + if (*NameOrErr == ObjectFilename) { auto ModTimeOrErr = Child.getLastModified(); if (!ModTimeOrErr) - return errorToErrorCode(ModTimeOrErr.takeError()); + return ModTimeOrErr.takeError(); + if (Timestamp != sys::TimePoint<>() && Timestamp != ModTimeOrErr.get()) { if (Verbose) - outs() << "\tmember had timestamp mismatch.\n"; + WithColor::warning() << "member has timestamp mismatch.\n"; continue; } + if (Verbose) - outs() << "\tfound member in current archive.\n"; + WithColor::note() << "found member in archive.\n"; + auto ErrOrMem = Child.getMemoryBufferRef(); if (!ErrOrMem) - return errorToErrorCode(ErrOrMem.takeError()); - Buffers.push_back(*ErrOrMem); + return ErrOrMem.takeError(); + + auto ErrOrObjectFile = + object::ObjectFile::createObjectFile(*ErrOrMem); + if (!ErrOrObjectFile) + return ErrOrObjectFile.takeError(); + + OE.Objects.push_back(std::move(*ErrOrObjectFile)); } } } if (Err) - return errorToErrorCode(std::move(Err)); + return std::move(Err); } - if (Buffers.empty()) - return make_error_code(errc::no_such_file_or_directory); - return Buffers; -} - -ErrorOr<std::vector<MemoryBufferRef>> -BinaryHolder::MapArchiveAndGetMemberBuffers( - StringRef Filename, sys::TimePoint<std::chrono::seconds> Timestamp) { - StringRef ArchiveFilename = Filename.substr(0, Filename.find('(')); + if (OE.Objects.empty()) + return errorCodeToError(errc::no_such_file_or_directory); - auto ErrOrBuff = MemoryBuffer::getFileOrSTDIN(ArchiveFilename); - if (auto Err = ErrOrBuff.getError()) - return Err; + std::lock_guard<std::mutex> Lock(MemberCacheMutex); + MemberCache.try_emplace(Key, std::move(OE)); + return MemberCache[Key]; +} +Expected<const BinaryHolder::ObjectEntry &> +BinaryHolder::getObjectEntry(StringRef Filename, TimestampTy Timestamp) { if (Verbose) - outs() << "\topened new archive '" << ArchiveFilename << "'\n"; - - changeBackingMemoryBuffer(std::move(*ErrOrBuff)); - std::vector<MemoryBufferRef> ArchiveBuffers; - auto ErrOrFat = object::MachOUniversalBinary::create( - CurrentMemoryBuffer->getMemBufferRef()); - if (!ErrOrFat) { - consumeError(ErrOrFat.takeError()); - // Not a fat binary must be a standard one. - ArchiveBuffers.push_back(CurrentMemoryBuffer->getMemBufferRef()); - } else { - CurrentFatBinary = std::move(*ErrOrFat); - CurrentFatBinaryName = ArchiveFilename; - ArchiveBuffers = getMachOFatMemoryBuffers( - CurrentFatBinaryName, *CurrentMemoryBuffer, *CurrentFatBinary); - } + WithColor::note() << "trying to open '" << Filename << "'\n"; - for (auto MemRef : ArchiveBuffers) { - auto ErrOrArchive = object::Archive::create(MemRef); - if (!ErrOrArchive) - return errorToErrorCode(ErrOrArchive.takeError()); - CurrentArchives.push_back(std::move(*ErrOrArchive)); + // If this is an archive, we might have either the object or the archive + // cached. In this case we can load it without accessing the file system. + if (isArchive(Filename)) { + StringRef ArchiveFilename = getArchiveAndObjectName(Filename).first; + std::lock_guard<std::mutex> Lock(ArchiveCacheMutex); + if (ArchiveCache.count(ArchiveFilename)) { + return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, + Verbose); + } else { + ArchiveEntry &AE = ArchiveCache[ArchiveFilename]; + auto Err = AE.load(Filename, Timestamp, Verbose); + if (Err) { + ArchiveCache.erase(ArchiveFilename); + // Don't return the error here: maybe the file wasn't an archive. + llvm::consumeError(std::move(Err)); + } else { + return ArchiveCache[ArchiveFilename].getObjectEntry(Filename, Timestamp, + Verbose); + } + } } - return GetArchiveMemberBuffers(Filename, Timestamp); -} -ErrorOr<const object::ObjectFile &> -BinaryHolder::getObjfileForArch(const Triple &T) { - for (const auto &Obj : CurrentObjectFiles) { - if (const auto *MachO = dyn_cast<object::MachOObjectFile>(Obj.get())) { - if (MachO->getArchTriple().str() == T.str()) - return *MachO; - } else if (Obj->getArch() == T.getArch()) - return *Obj; + // If this is an object, we might have it cached. If not we'll have to load + // it from the file system and cache it now. + std::lock_guard<std::mutex> Lock(ObjectCacheMutex); + if (!ObjectCache.count(Filename)) { + ObjectEntry &OE = ObjectCache[Filename]; + auto Err = OE.load(Filename, Verbose); + if (Err) { + ObjectCache.erase(Filename); + return std::move(Err); + } } - return make_error_code(object::object_error::arch_not_found); + return ObjectCache[Filename]; } -ErrorOr<std::vector<const object::ObjectFile *>> -BinaryHolder::GetObjectFiles(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp) { - auto ErrOrMemBufferRefs = GetMemoryBuffersForFile(Filename, Timestamp); - if (auto Err = ErrOrMemBufferRefs.getError()) - return Err; - - std::vector<const object::ObjectFile *> Objects; - Objects.reserve(ErrOrMemBufferRefs->size()); - - CurrentObjectFiles.clear(); - for (auto MemBuf : *ErrOrMemBufferRefs) { - auto ErrOrObjectFile = object::ObjectFile::createObjectFile(MemBuf); - if (!ErrOrObjectFile) - return errorToErrorCode(ErrOrObjectFile.takeError()); - - Objects.push_back(ErrOrObjectFile->get()); - CurrentObjectFiles.push_back(std::move(*ErrOrObjectFile)); - } - - return std::move(Objects); -} -} +void BinaryHolder::clear() { + std::lock_guard<std::mutex> ArchiveLock(ArchiveCacheMutex); + std::lock_guard<std::mutex> ObjectLock(ObjectCacheMutex); + ArchiveCache.clear(); + ObjectCache.clear(); } + +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/BinaryHolder.h b/tools/dsymutil/BinaryHolder.h index 5b2061d23aac..c78bff2f8f31 100644 --- a/tools/dsymutil/BinaryHolder.h +++ b/tools/dsymutil/BinaryHolder.h @@ -14,6 +14,7 @@ #ifndef LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H #define LLVM_TOOLS_DSYMUTIL_BINARYHOLDER_H +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Triple.h" #include "llvm/Object/Archive.h" #include "llvm/Object/Error.h" @@ -23,118 +24,142 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/ErrorOr.h" +#include <mutex> + namespace llvm { namespace dsymutil { -/// \brief The BinaryHolder class is responsible for creating and -/// owning ObjectFile objects and their underlying MemoryBuffer. This -/// is different from a simple OwningBinary in that it handles -/// accessing to archive members. -/// -/// As an optimization, this class will reuse an already mapped and -/// parsed Archive object if 2 successive requests target the same -/// archive file (Which is always the case in debug maps). -/// Currently it only owns one memory buffer at any given time, -/// meaning that a mapping request will invalidate the previous memory -/// mapping. +/// The BinaryHolder class is responsible for creating and owning +/// ObjectFiles and their underlying MemoryBuffers. It differs from a simple +/// OwningBinary in that it handles accessing and caching of archives and its +/// members. class BinaryHolder { - std::vector<std::unique_ptr<object::Archive>> CurrentArchives; - std::unique_ptr<MemoryBuffer> CurrentMemoryBuffer; - std::vector<std::unique_ptr<object::ObjectFile>> CurrentObjectFiles; - std::unique_ptr<object::MachOUniversalBinary> CurrentFatBinary; - std::string CurrentFatBinaryName; - bool Verbose; +public: + using TimestampTy = sys::TimePoint<std::chrono::seconds>; - /// Get the MemoryBufferRefs for the file specification in \p - /// Filename from the current archive. Multiple buffers are returned - /// when there are multiple architectures available for the - /// requested file. - /// - /// This function performs no system calls, it just looks up a - /// potential match for the given \p Filename in the currently - /// mapped archive if there is one. - ErrorOr<std::vector<MemoryBufferRef>> - GetArchiveMemberBuffers(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp); - - /// Interpret Filename as an archive member specification map the - /// corresponding archive to memory and return the MemoryBufferRefs - /// corresponding to the described member. Multiple buffers are - /// returned when there are multiple architectures available for the - /// requested file. - ErrorOr<std::vector<MemoryBufferRef>> - MapArchiveAndGetMemberBuffers(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp); - - /// Return the MemoryBufferRef that holds the memory mapping for the - /// given \p Filename. This function will try to parse archive - /// member specifications of the form /path/to/archive.a(member.o). - /// - /// The returned MemoryBufferRefs points to a buffer owned by this - /// object. The buffer is valid until the next call to - /// GetMemoryBufferForFile() on this object. - /// Multiple buffers are returned when there are multiple - /// architectures available for the requested file. - ErrorOr<std::vector<MemoryBufferRef>> - GetMemoryBuffersForFile(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp); - - void changeBackingMemoryBuffer(std::unique_ptr<MemoryBuffer> &&MemBuf); - ErrorOr<const object::ObjectFile &> getObjfileForArch(const Triple &T); + BinaryHolder(bool Verbose = false) : Verbose(Verbose) {} -public: - BinaryHolder(bool Verbose) : Verbose(Verbose) {} - - /// Get the ObjectFiles designated by the \p Filename. This - /// might be an archive member specification of the form - /// /path/to/archive.a(member.o). - /// - /// Calling this function invalidates the previous mapping owned by - /// the BinaryHolder. Multiple buffers are returned when there are - /// multiple architectures available for the requested file. - ErrorOr<std::vector<const object::ObjectFile *>> - GetObjectFiles(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp = - sys::TimePoint<std::chrono::seconds>()); - - /// Wraps GetObjectFiles() to return a derived ObjectFile type. - template <typename ObjectFileType> - ErrorOr<std::vector<const ObjectFileType *>> - GetFilesAs(StringRef Filename, - sys::TimePoint<std::chrono::seconds> Timestamp = - sys::TimePoint<std::chrono::seconds>()) { - auto ErrOrObjFile = GetObjectFiles(Filename, Timestamp); - if (auto Err = ErrOrObjFile.getError()) - return Err; - - std::vector<const ObjectFileType *> Objects; - Objects.reserve((*ErrOrObjFile).size()); - for (const auto &Obj : *ErrOrObjFile) { - const auto *Derived = dyn_cast<ObjectFileType>(Obj); - if (!Derived) - return make_error_code(object::object_error::invalid_file_type); - Objects.push_back(Derived); + // Forward declarations for friend declaration. + class ObjectEntry; + class ArchiveEntry; + + /// Base class shared by cached entries, representing objects and archives. + class EntryBase { + protected: + std::unique_ptr<MemoryBuffer> MemBuffer; + std::unique_ptr<object::MachOUniversalBinary> FatBinary; + std::string FatBinaryName; + }; + + /// Cached entry holding one or more (in case of a fat binary) object files. + class ObjectEntry : public EntryBase { + public: + /// Load the given object binary in memory. + Error load(StringRef Filename, bool Verbose = false); + + /// Access all owned ObjectFiles. + std::vector<const object::ObjectFile *> getObjects() const; + + /// Access to a derived version of all the currently owned ObjectFiles. The + /// conversion might be invalid, in which case an Error is returned. + template <typename ObjectFileType> + Expected<std::vector<const ObjectFileType *>> getObjectsAs() const { + std::vector<const ObjectFileType *> Result; + Result.reserve(Objects.size()); + for (auto &Object : Objects) { + const auto *Derived = dyn_cast<ObjectFileType>(Object.get()); + if (!Derived) + return errorCodeToError(object::object_error::invalid_file_type); + Result.push_back(Derived); + } + return Result; } - return std::move(Objects); + + /// Access the owned ObjectFile with architecture \p T. + Expected<const object::ObjectFile &> getObject(const Triple &T) const; + + /// Access to a derived version of the currently owned ObjectFile with + /// architecture \p T. The conversion must be known to be valid. + template <typename ObjectFileType> + Expected<const ObjectFileType &> getObjectAs(const Triple &T) const { + auto Object = getObject(T); + if (!Object) + return Object.takeError(); + return cast<ObjectFileType>(*Object); + } + + private: + std::vector<std::unique_ptr<object::ObjectFile>> Objects; + friend ArchiveEntry; + }; + + /// Cached entry holding one or more (in the of a fat binary) archive files. + class ArchiveEntry : public EntryBase { + public: + struct KeyTy { + std::string Filename; + TimestampTy Timestamp; + + KeyTy() : Filename(), Timestamp() {} + KeyTy(StringRef Filename, TimestampTy Timestamp) + : Filename(Filename.str()), Timestamp(Timestamp) {} + }; + + /// Load the given object binary in memory. + Error load(StringRef Filename, TimestampTy Timestamp, bool Verbose = false); + + Expected<const ObjectEntry &> getObjectEntry(StringRef Filename, + TimestampTy Timestamp, + bool Verbose = false); + + private: + std::vector<std::unique_ptr<object::Archive>> Archives; + DenseMap<KeyTy, ObjectEntry> MemberCache; + std::mutex MemberCacheMutex; + }; + + Expected<const ObjectEntry &> + getObjectEntry(StringRef Filename, TimestampTy Timestamp = TimestampTy()); + + void clear(); + +private: + /// Cache of static archives. Objects that are part of a static archive are + /// stored under this object, rather than in the map below. + StringMap<ArchiveEntry> ArchiveCache; + std::mutex ArchiveCacheMutex; + + /// Object entries for objects that are not in a static archive. + StringMap<ObjectEntry> ObjectCache; + std::mutex ObjectCacheMutex; + + bool Verbose; +}; + +} // namespace dsymutil + +template <> struct DenseMapInfo<dsymutil::BinaryHolder::ArchiveEntry::KeyTy> { + + static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getEmptyKey() { + return dsymutil::BinaryHolder::ArchiveEntry::KeyTy(); } - /// Access the currently owned ObjectFile with architecture \p T. As - /// successfull call to GetObjectFiles() or GetFilesAs() must have - /// been performed before calling this. - ErrorOr<const object::ObjectFile &> Get(const Triple &T) { - return getObjfileForArch(T); + static inline dsymutil::BinaryHolder::ArchiveEntry::KeyTy getTombstoneKey() { + return dsymutil::BinaryHolder::ArchiveEntry::KeyTy("/", {}); } - /// Access to a derived version of the currently owned - /// ObjectFile. The conversion must be known to be valid. - template <typename ObjectFileType> - ErrorOr<const ObjectFileType &> GetAs(const Triple &T) { - auto ErrOrObj = Get(T); - if (auto Err = ErrOrObj.getError()) - return Err; - return cast<ObjectFileType>(*ErrOrObj); + static unsigned + getHashValue(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &K) { + return hash_combine(DenseMapInfo<StringRef>::getHashValue(K.Filename), + DenseMapInfo<unsigned>::getHashValue( + K.Timestamp.time_since_epoch().count())); + } + + static bool isEqual(const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &LHS, + const dsymutil::BinaryHolder::ArchiveEntry::KeyTy &RHS) { + return LHS.Filename == RHS.Filename && LHS.Timestamp == RHS.Timestamp; } }; -} -} + +} // namespace llvm #endif diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt index 1dcb2116f34b..f41a6fd28504 100644 --- a/tools/dsymutil/CMakeLists.txt +++ b/tools/dsymutil/CMakeLists.txt @@ -8,19 +8,23 @@ set(LLVM_LINK_COMPONENTS Target ) -add_llvm_tool(llvm-dsymutil +add_llvm_tool(dsymutil dsymutil.cpp BinaryHolder.cpp CFBundle.cpp + CompileUnit.cpp DebugMap.cpp + DeclContext.cpp DwarfLinker.cpp + DwarfStreamer.cpp MachODebugMapParser.cpp MachOUtils.cpp + NonRelocatableStringpool.cpp DEPENDS intrinsics_gen ) -IF(APPLE) - target_link_libraries(llvm-dsymutil PRIVATE "-framework CoreFoundation") -ENDIF(APPLE) +if(APPLE) + target_link_libraries(dsymutil PRIVATE "-framework CoreFoundation") +endif(APPLE) diff --git a/tools/dsymutil/CompileUnit.cpp b/tools/dsymutil/CompileUnit.cpp new file mode 100644 index 000000000000..67e1739ae108 --- /dev/null +++ b/tools/dsymutil/CompileUnit.cpp @@ -0,0 +1,136 @@ +//===- tools/dsymutil/CompileUnit.h - Dwarf compile unit ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompileUnit.h" +#include "DeclContext.h" + +namespace llvm { +namespace dsymutil { + +/// Check if the DIE at \p Idx is in the scope of a function. +static bool inFunctionScope(CompileUnit &U, unsigned Idx) { + while (Idx) { + if (U.getOrigUnit().getDIEAtIndex(Idx).getTag() == dwarf::DW_TAG_subprogram) + return true; + Idx = U.getInfo(Idx).ParentIdx; + } + return false; +} + +void CompileUnit::markEverythingAsKept() { + unsigned Idx = 0; + + setHasInterestingContent(); + + for (auto &I : Info) { + // Mark everything that wasn't explicit marked for pruning. + I.Keep = !I.Prune; + auto DIE = OrigUnit.getDIEAtIndex(Idx++); + + // Try to guess which DIEs must go to the accelerator tables. We do that + // just for variables, because functions will be handled depending on + // whether they carry a DW_AT_low_pc attribute or not. + if (DIE.getTag() != dwarf::DW_TAG_variable && + DIE.getTag() != dwarf::DW_TAG_constant) + continue; + + Optional<DWARFFormValue> Value; + if (!(Value = DIE.find(dwarf::DW_AT_location))) { + if ((Value = DIE.find(dwarf::DW_AT_const_value)) && + !inFunctionScope(*this, I.ParentIdx)) + I.InDebugMap = true; + continue; + } + if (auto Block = Value->getAsBlock()) { + if (Block->size() > OrigUnit.getAddressByteSize() && + (*Block)[0] == dwarf::DW_OP_addr) + I.InDebugMap = true; + } + } +} + +uint64_t CompileUnit::computeNextUnitOffset() { + NextUnitOffset = StartOffset + 11 /* Header size */; + // The root DIE might be null, meaning that the Unit had nothing to + // contribute to the linked output. In that case, we will emit the + // unit header without any actual DIE. + if (NewUnit) + NextUnitOffset += NewUnit->getUnitDie().getSize(); + return NextUnitOffset; +} + +/// Keep track of a forward cross-cu reference from this unit +/// to \p Die that lives in \p RefUnit. +void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, + DeclContext *Ctxt, PatchLocation Attr) { + ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); +} + +void CompileUnit::fixupForwardReferences() { + for (const auto &Ref : ForwardDIEReferences) { + DIE *RefDie; + const CompileUnit *RefUnit; + PatchLocation Attr; + DeclContext *Ctxt; + std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; + if (Ctxt && Ctxt->getCanonicalDIEOffset()) + Attr.set(Ctxt->getCanonicalDIEOffset()); + else + Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); + } +} + +void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { + Labels.insert({LabelLowPc, PcOffset}); +} + +void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, + int64_t PcOffset) { + Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); + this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); + this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); +} + +void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { + if (Die.getTag() != dwarf::DW_TAG_compile_unit) + RangeAttributes.push_back(Attr); + else + UnitRangeAttribute = Attr; +} + +void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) { + LocationAttributes.emplace_back(Attr, PcOffset); +} + +void CompileUnit::addNamespaceAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name) { + Namespaces.emplace_back(Name, Die); +} + +void CompileUnit::addObjCAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name, + bool SkipPubSection) { + ObjC.emplace_back(Name, Die, SkipPubSection); +} + +void CompileUnit::addNameAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name, + bool SkipPubSection) { + Pubnames.emplace_back(Name, Die, SkipPubSection); +} + +void CompileUnit::addTypeAccelerator(const DIE *Die, + DwarfStringPoolEntryRef Name, + bool ObjcClassImplementation, + uint32_t QualifiedNameHash) { + Pubtypes.emplace_back(Name, Die, QualifiedNameHash, ObjcClassImplementation); +} + +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/CompileUnit.h b/tools/dsymutil/CompileUnit.h new file mode 100644 index 000000000000..0f5efdd68051 --- /dev/null +++ b/tools/dsymutil/CompileUnit.h @@ -0,0 +1,327 @@ +//===- tools/dsymutil/CompileUnit.h - Dwarf debug info linker ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/IntervalMap.h" +#include "llvm/CodeGen/DIE.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +#ifndef LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H +#define LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H + +namespace llvm { +namespace dsymutil { + +class DeclContext; + +template <typename KeyT, typename ValT> +using HalfOpenIntervalMap = + IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize, + IntervalMapHalfOpenInfo<KeyT>>; + +using FunctionIntervals = HalfOpenIntervalMap<uint64_t, int64_t>; + +// FIXME: Delete this structure. +struct PatchLocation { + DIE::value_iterator I; + + PatchLocation() = default; + PatchLocation(DIE::value_iterator I) : I(I) {} + + void set(uint64_t New) const { + assert(I); + const auto &Old = *I; + assert(Old.getType() == DIEValue::isInteger); + *I = DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(New)); + } + + uint64_t get() const { + assert(I); + return I->getDIEInteger().getValue(); + } +}; + +/// Stores all information relating to a compile unit, be it in its original +/// instance in the object file to its brand new cloned and linked DIE tree. +class CompileUnit { +public: + /// Information gathered about a DIE in the object file. + struct DIEInfo { + /// Address offset to apply to the described entity. + int64_t AddrAdjust; + + /// ODR Declaration context. + DeclContext *Ctxt; + + /// Cloned version of that DIE. + DIE *Clone; + + /// The index of this DIE's parent. + uint32_t ParentIdx; + + /// Is the DIE part of the linked output? + bool Keep : 1; + + /// Was this DIE's entity found in the map? + bool InDebugMap : 1; + + /// Is this a pure forward declaration we can strip? + bool Prune : 1; + + /// Does DIE transitively refer an incomplete decl? + bool Incomplete : 1; + }; + + CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, + StringRef ClangModuleName) + : OrigUnit(OrigUnit), ID(ID), Ranges(RangeAlloc), + ClangModuleName(ClangModuleName) { + Info.resize(OrigUnit.getNumDIEs()); + + auto CUDie = OrigUnit.getUnitDIE(false); + if (!CUDie) { + HasODR = false; + return; + } + if (auto Lang = dwarf::toUnsigned(CUDie.find(dwarf::DW_AT_language))) + HasODR = CanUseODR && (*Lang == dwarf::DW_LANG_C_plus_plus || + *Lang == dwarf::DW_LANG_C_plus_plus_03 || + *Lang == dwarf::DW_LANG_C_plus_plus_11 || + *Lang == dwarf::DW_LANG_C_plus_plus_14 || + *Lang == dwarf::DW_LANG_ObjC_plus_plus); + else + HasODR = false; + } + + DWARFUnit &getOrigUnit() const { return OrigUnit; } + + unsigned getUniqueID() const { return ID; } + + void createOutputDIE() { + NewUnit.emplace(OrigUnit.getVersion(), OrigUnit.getAddressByteSize(), + OrigUnit.getUnitDIE().getTag()); + } + + DIE *getOutputUnitDIE() const { + if (NewUnit) + return &const_cast<BasicDIEUnit &>(*NewUnit).getUnitDie(); + return nullptr; + } + + bool hasODR() const { return HasODR; } + bool isClangModule() const { return !ClangModuleName.empty(); } + const std::string &getClangModuleName() const { return ClangModuleName; } + + DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } + const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } + + uint64_t getStartOffset() const { return StartOffset; } + uint64_t getNextUnitOffset() const { return NextUnitOffset; } + void setStartOffset(uint64_t DebugInfoSize) { StartOffset = DebugInfoSize; } + + uint64_t getLowPc() const { return LowPc; } + uint64_t getHighPc() const { return HighPc; } + bool hasLabelAt(uint64_t Addr) const { return Labels.count(Addr); } + + Optional<PatchLocation> getUnitRangesAttribute() const { + return UnitRangeAttribute; + } + + const FunctionIntervals &getFunctionRanges() const { return Ranges; } + + const std::vector<PatchLocation> &getRangesAttributes() const { + return RangeAttributes; + } + + const std::vector<std::pair<PatchLocation, int64_t>> & + getLocationAttributes() const { + return LocationAttributes; + } + + void setHasInterestingContent() { HasInterestingContent = true; } + bool hasInterestingContent() { return HasInterestingContent; } + + /// Mark every DIE in this unit as kept. This function also + /// marks variables as InDebugMap so that they appear in the + /// reconstructed accelerator tables. + void markEverythingAsKept(); + + /// Compute the end offset for this unit. Must be called after the CU's DIEs + /// have been cloned. \returns the next unit offset (which is also the + /// current debug_info section size). + uint64_t computeNextUnitOffset(); + + /// Keep track of a forward reference to DIE \p Die in \p RefUnit by \p + /// Attr. The attribute should be fixed up later to point to the absolute + /// offset of \p Die in the debug_info section or to the canonical offset of + /// \p Ctxt if it is non-null. + void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, + DeclContext *Ctxt, PatchLocation Attr); + + /// Apply all fixups recorded by noteForwardReference(). + void fixupForwardReferences(); + + /// Add the low_pc of a label that is relocated by applying + /// offset \p PCOffset. + void addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset); + + /// Add a function range [\p LowPC, \p HighPC) that is relocated by applying + /// offset \p PCOffset. + void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); + + /// Keep track of a DW_AT_range attribute that we will need to patch up later. + void noteRangeAttribute(const DIE &Die, PatchLocation Attr); + + /// Keep track of a location attribute pointing to a location list in the + /// debug_loc section. + void noteLocationAttribute(PatchLocation Attr, int64_t PcOffset); + + /// Add a name accelerator entry for \a Die with \a Name. + void addNamespaceAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name); + + /// Add a name accelerator entry for \a Die with \a Name. + void addNameAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name, + bool SkipPubnamesSection = false); + + /// Add various accelerator entries for \p Die with \p Name which is stored + /// in the string table at \p Offset. \p Name must be an Objective-C + /// selector. + void addObjCAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name, + bool SkipPubnamesSection = false); + + /// Add a type accelerator entry for \p Die with \p Name which is stored in + /// the string table at \p Offset. + void addTypeAccelerator(const DIE *Die, DwarfStringPoolEntryRef Name, + bool ObjcClassImplementation, + uint32_t QualifiedNameHash); + + struct AccelInfo { + /// Name of the entry. + DwarfStringPoolEntryRef Name; + + /// DIE this entry describes. + const DIE *Die; + + /// Hash of the fully qualified name. + uint32_t QualifiedNameHash; + + /// Emit this entry only in the apple_* sections. + bool SkipPubSection; + + /// Is this an ObjC class implementation? + bool ObjcClassImplementation; + + AccelInfo(DwarfStringPoolEntryRef Name, const DIE *Die, + bool SkipPubSection = false) + : Name(Name), Die(Die), SkipPubSection(SkipPubSection) {} + + AccelInfo(DwarfStringPoolEntryRef Name, const DIE *Die, + uint32_t QualifiedNameHash, bool ObjCClassIsImplementation) + : Name(Name), Die(Die), QualifiedNameHash(QualifiedNameHash), + SkipPubSection(false), + ObjcClassImplementation(ObjCClassIsImplementation) {} + }; + + const std::vector<AccelInfo> &getPubnames() const { return Pubnames; } + const std::vector<AccelInfo> &getPubtypes() const { return Pubtypes; } + const std::vector<AccelInfo> &getNamespaces() const { return Namespaces; } + const std::vector<AccelInfo> &getObjC() const { return ObjC; } + + /// Get the full path for file \a FileNum in the line table + StringRef getResolvedPath(unsigned FileNum) { + if (FileNum >= ResolvedPaths.size()) + return StringRef(); + return ResolvedPaths[FileNum]; + } + + /// Set the fully resolved path for the line-table's file \a FileNum + /// to \a Path. + void setResolvedPath(unsigned FileNum, StringRef Path) { + if (ResolvedPaths.size() <= FileNum) + ResolvedPaths.resize(FileNum + 1); + ResolvedPaths[FileNum] = Path; + } + + MCSymbol *getLabelBegin() { return LabelBegin; } + void setLabelBegin(MCSymbol *S) { LabelBegin = S; } + +private: + DWARFUnit &OrigUnit; + unsigned ID; + std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. + Optional<BasicDIEUnit> NewUnit; + MCSymbol *LabelBegin = nullptr; + + uint64_t StartOffset; + uint64_t NextUnitOffset; + + uint64_t LowPc = std::numeric_limits<uint64_t>::max(); + uint64_t HighPc = 0; + + /// A list of attributes to fixup with the absolute offset of + /// a DIE in the debug_info section. + /// + /// The offsets for the attributes in this array couldn't be set while + /// cloning because for cross-cu forward references the target DIE's offset + /// isn't known you emit the reference attribute. + std::vector< + std::tuple<DIE *, const CompileUnit *, DeclContext *, PatchLocation>> + ForwardDIEReferences; + + FunctionIntervals::Allocator RangeAlloc; + + /// The ranges in that interval map are the PC ranges for + /// functions in this unit, associated with the PC offset to apply + /// to the addresses to get the linked address. + FunctionIntervals Ranges; + + /// The DW_AT_low_pc of each DW_TAG_label. + SmallDenseMap<uint64_t, uint64_t, 1> Labels; + + /// DW_AT_ranges attributes to patch after we have gathered + /// all the unit's function addresses. + /// @{ + std::vector<PatchLocation> RangeAttributes; + Optional<PatchLocation> UnitRangeAttribute; + /// @} + + /// Location attributes that need to be transferred from the + /// original debug_loc section to the liked one. They are stored + /// along with the PC offset that is to be applied to their + /// function's address. + std::vector<std::pair<PatchLocation, int64_t>> LocationAttributes; + + /// Accelerator entries for the unit, both for the pub* + /// sections and the apple* ones. + /// @{ + std::vector<AccelInfo> Pubnames; + std::vector<AccelInfo> Pubtypes; + std::vector<AccelInfo> Namespaces; + std::vector<AccelInfo> ObjC; + /// @} + + /// Cached resolved paths from the line table. + /// Note, the StringRefs here point in to the intern (uniquing) string pool. + /// This means that a StringRef returned here doesn't need to then be uniqued + /// for the purposes of getting a unique address for each string. + std::vector<StringRef> ResolvedPaths; + + /// Is this unit subject to the ODR rule? + bool HasODR; + + /// Did a DIE actually contain a valid reloc? + bool HasInterestingContent; + + /// If this is a Clang module, this holds the module's name. + std::string ClangModuleName; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp index b543c212fe80..26137773a4e7 100644 --- a/tools/dsymutil/DebugMap.cpp +++ b/tools/dsymutil/DebugMap.cpp @@ -1,6 +1,6 @@ //===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -22,6 +22,7 @@ #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> @@ -62,7 +63,7 @@ void DebugMapObject::print(raw_ostream &OS) const { Entries.reserve(Symbols.getNumItems()); for (const auto &Sym : make_range(Symbols.begin(), Symbols.end())) Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue())); - std::sort( + llvm::sort( Entries.begin(), Entries.end(), [](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; }); for (const auto &Sym : Entries) { @@ -239,26 +240,31 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { StringMap<uint64_t> SymbolAddresses; sys::path::append(Path, Filename); - auto ErrOrObjectFiles = BinHolder.GetObjectFiles(Path); - if (auto EC = ErrOrObjectFiles.getError()) { - errs() << "warning: Unable to open " << Path << " " << EC.message() << '\n'; - } else if (auto ErrOrObjectFile = BinHolder.Get(Ctxt.BinaryTriple)) { - // Rewrite the object file symbol addresses in the debug map. The - // YAML input is mainly used to test llvm-dsymutil without - // requiring binaries checked-in. If we generate the object files - // during the test, we can't hardcode the symbols addresses, so - // look them up here and rewrite them. - for (const auto &Sym : ErrOrObjectFile->symbols()) { - uint64_t Address = Sym.getValue(); - Expected<StringRef> Name = Sym.getName(); - if (!Name || - (Sym.getFlags() & (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { - // TODO: Actually report errors helpfully. - if (!Name) - consumeError(Name.takeError()); - continue; + + auto ObjectEntry = BinHolder.getObjectEntry(Path); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + WithColor::warning() << "Unable to open " << Path << " " + << toString(std::move(Err)) << '\n'; + } else { + auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple); + if (!Object) { + auto Err = Object.takeError(); + WithColor::warning() << "Unable to open " << Path << " " + << toString(std::move(Err)) << '\n'; + } else { + for (const auto &Sym : Object->symbols()) { + uint64_t Address = Sym.getValue(); + Expected<StringRef> Name = Sym.getName(); + if (!Name || (Sym.getFlags() & + (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { + // TODO: Actually report errors helpfully. + if (!Name) + consumeError(Name.takeError()); + continue; + } + SymbolAddresses[*Name] = Address; } - SymbolAddresses[*Name] = Address; } } @@ -277,5 +283,4 @@ MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { } } // end namespace yaml - } // end namespace llvm diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h index 3b5b437ccff9..c9883773d3dc 100644 --- a/tools/dsymutil/DebugMap.h +++ b/tools/dsymutil/DebugMap.h @@ -1,6 +1,6 @@ //=- tools/dsymutil/DebugMap.h - Generic debug map representation -*- C++ -*-=// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -28,6 +28,7 @@ #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Object/MachO.h" #include "llvm/Support/Chrono.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/YAMLTraits.h" @@ -47,10 +48,9 @@ namespace dsymutil { class DebugMapObject; -/// \brief The DebugMap object stores the list of object files to -/// query for debug information along with the mapping between the -/// symbols' addresses in the object file to their linked address in -/// the linked binary. +/// The DebugMap object stores the list of object files to query for debug +/// information along with the mapping between the symbols' addresses in the +/// object file to their linked address in the linked binary. /// /// A DebugMap producer could look like this: /// DebugMap *DM = new DebugMap(); @@ -102,12 +102,14 @@ public: const_iterator end() const { return Objects.end(); } + unsigned getNumberOfObjects() const { return Objects.size(); } + /// This function adds an DebugMapObject to the list owned by this /// debug map. DebugMapObject & addDebugMapObject(StringRef ObjectFilePath, sys::TimePoint<std::chrono::seconds> Timestamp, - uint8_t Type); + uint8_t Type = llvm::MachO::N_OSO); const Triple &getTriple() const { return BinaryTriple; } @@ -124,10 +126,9 @@ public: parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, bool Verbose); }; -/// \brief The DebugMapObject represents one object file described by -/// the DebugMap. It contains a list of mappings between addresses in -/// the object file and in the linked binary for all the linked atoms -/// in this object file. +/// The DebugMapObject represents one object file described by the DebugMap. It +/// contains a list of mappings between addresses in the object file and in the +/// linked binary for all the linked atoms in this object file. class DebugMapObject { public: struct SymbolMapping { @@ -149,17 +150,17 @@ public: using YAMLSymbolMapping = std::pair<std::string, SymbolMapping>; using DebugMapEntry = StringMapEntry<SymbolMapping>; - /// \brief Adds a symbol mapping to this DebugMapObject. + /// Adds a symbol mapping to this DebugMapObject. /// \returns false if the symbol was already registered. The request /// is discarded in this case. bool addSymbol(StringRef SymName, Optional<uint64_t> ObjectAddress, uint64_t LinkedAddress, uint32_t Size); - /// \brief Lookup a symbol mapping. + /// Lookup a symbol mapping. /// \returns null if the symbol isn't found. const DebugMapEntry *lookupSymbol(StringRef SymbolName) const; - /// \brief Lookup an objectfile address. + /// Lookup an object file address. /// \returns null if the address isn't found. const DebugMapEntry *lookupObjectAddress(uint64_t Address) const; @@ -175,6 +176,11 @@ public: return make_range(Symbols.begin(), Symbols.end()); } + bool empty() const { return Symbols.empty(); } + + void addWarning(StringRef Warning) { Warnings.push_back(Warning); } + const std::vector<std::string> &getWarnings() const { return Warnings; } + void print(raw_ostream &OS) const; #ifndef NDEBUG void dump() const; @@ -193,6 +199,8 @@ private: DenseMap<uint64_t, DebugMapEntry *> AddressToMapping; uint8_t Type; + std::vector<std::string> Warnings; + /// For YAMLIO support. ///@{ friend yaml::MappingTraits<dsymutil::DebugMapObject>; @@ -207,7 +215,6 @@ public: }; } // end namespace dsymutil - } // end namespace llvm LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::dsymutil::DebugMapObject::YAMLSymbolMapping) diff --git a/tools/dsymutil/DeclContext.cpp b/tools/dsymutil/DeclContext.cpp new file mode 100644 index 000000000000..3e6dcaf51a2c --- /dev/null +++ b/tools/dsymutil/DeclContext.cpp @@ -0,0 +1,211 @@ +//===- tools/dsymutil/DeclContext.cpp - Declaration context ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DeclContext.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/DebugInfo/DWARF/DWARFUnit.h" + +namespace llvm { +namespace dsymutil { + +/// Set the last DIE/CU a context was seen in and, possibly invalidate the +/// context if it is ambiguous. +/// +/// In the current implementation, we don't handle overloaded functions well, +/// because the argument types are not taken into account when computing the +/// DeclContext tree. +/// +/// Some of this is mitigated byt using mangled names that do contain the +/// arguments types, but sometimes (e.g. with function templates) we don't have +/// that. In that case, just do not unique anything that refers to the contexts +/// we are not able to distinguish. +/// +/// If a context that is not a namespace appears twice in the same CU, we know +/// it is ambiguous. Make it invalid. +bool DeclContext::setLastSeenDIE(CompileUnit &U, const DWARFDie &Die) { + if (LastSeenCompileUnitID == U.getUniqueID()) { + DWARFUnit &OrigUnit = U.getOrigUnit(); + uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE); + U.getInfo(FirstIdx).Ctxt = nullptr; + return false; + } + + LastSeenCompileUnitID = U.getUniqueID(); + LastSeenDIE = Die; + return true; +} + +PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( + DeclContext &Context, const DWARFDie &DIE, CompileUnit &U, + UniquingStringPool &StringPool, bool InClangModule) { + unsigned Tag = DIE.getTag(); + + // FIXME: dsymutil-classic compat: We should bail out here if we + // have a specification or an abstract_origin. We will get the + // parent context wrong here. + + switch (Tag) { + default: + // By default stop gathering child contexts. + return PointerIntPair<DeclContext *, 1>(nullptr); + case dwarf::DW_TAG_module: + break; + case dwarf::DW_TAG_compile_unit: + return PointerIntPair<DeclContext *, 1>(&Context); + case dwarf::DW_TAG_subprogram: + // Do not unique anything inside CU local functions. + if ((Context.getTag() == dwarf::DW_TAG_namespace || + Context.getTag() == dwarf::DW_TAG_compile_unit) && + !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0)) + return PointerIntPair<DeclContext *, 1>(nullptr); + LLVM_FALLTHROUGH; + case dwarf::DW_TAG_member: + case dwarf::DW_TAG_namespace: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_typedef: + // Artificial things might be ambiguous, because they might be created on + // demand. For example implicitly defined constructors are ambiguous + // because of the way we identify contexts, and they won't be generated + // every time everywhere. + if (dwarf::toUnsigned(DIE.find(dwarf::DW_AT_artificial), 0)) + return PointerIntPair<DeclContext *, 1>(nullptr); + break; + } + + const char *Name = DIE.getName(DINameKind::LinkageName); + const char *ShortName = DIE.getName(DINameKind::ShortName); + StringRef NameRef; + StringRef ShortNameRef; + StringRef FileRef; + + if (Name) + NameRef = StringPool.internString(Name); + else if (Tag == dwarf::DW_TAG_namespace) + // FIXME: For dsymutil-classic compatibility. I think uniquing within + // anonymous namespaces is wrong. There is no ODR guarantee there. + NameRef = StringPool.internString("(anonymous namespace)"); + + if (ShortName && ShortName != Name) + ShortNameRef = StringPool.internString(ShortName); + else + ShortNameRef = NameRef; + + if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type && + Tag != dwarf::DW_TAG_union_type && + Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty()) + return PointerIntPair<DeclContext *, 1>(nullptr); + + unsigned Line = 0; + unsigned ByteSize = std::numeric_limits<uint32_t>::max(); + + if (!InClangModule) { + // Gather some discriminating data about the DeclContext we will be + // creating: File, line number and byte size. This shouldn't be necessary, + // because the ODR is just about names, but given that we do some + // approximations with overloaded functions and anonymous namespaces, use + // these additional data points to make the process safer. + // + // This is disabled for clang modules, because forward declarations of + // module-defined types do not have a file and line. + ByteSize = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_byte_size), + std::numeric_limits<uint64_t>::max()); + if (Tag != dwarf::DW_TAG_namespace || !Name) { + if (unsigned FileNum = + dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) { + if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( + &U.getOrigUnit())) { + // FIXME: dsymutil-classic compatibility. I'd rather not + // unique anything in anonymous namespaces, but if we do, then + // verify that the file and line correspond. + if (!Name && Tag == dwarf::DW_TAG_namespace) + FileNum = 1; + + if (LT->hasFileAtIndex(FileNum)) { + Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0); + // Cache the resolved paths based on the index in the line table, + // because calling realpath is expansive. + StringRef ResolvedPath = U.getResolvedPath(FileNum); + if (!ResolvedPath.empty()) { + FileRef = ResolvedPath; + } else { + std::string File; + bool FoundFileName = LT->getFileNameByIndex( + FileNum, U.getOrigUnit().getCompilationDir(), + DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, + File); + (void)FoundFileName; + assert(FoundFileName && "Must get file name from line table"); + // Second level of caching, this time based on the file's parent + // path. + FileRef = PathResolver.resolve(File, StringPool); + U.setResolvedPath(FileNum, FileRef); + } + } + } + } + } + } + + if (!Line && NameRef.empty()) + return PointerIntPair<DeclContext *, 1>(nullptr); + + // We hash NameRef, which is the mangled name, in order to get most + // overloaded functions resolve correctly. + // + // Strictly speaking, hashing the Tag is only necessary for a + // DW_TAG_module, to prevent uniquing of a module and a namespace + // with the same name. + // + // FIXME: dsymutil-classic won't unique the same type presented + // once as a struct and once as a class. Using the Tag in the fully + // qualified name hash to get the same effect. + unsigned Hash = hash_combine(Context.getQualifiedNameHash(), Tag, NameRef); + + // FIXME: dsymutil-classic compatibility: when we don't have a name, + // use the filename. + if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)") + Hash = hash_combine(Hash, FileRef); + + // Now look if this context already exists. + DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); + auto ContextIter = Contexts.find(&Key); + + if (ContextIter == Contexts.end()) { + // The context wasn't found. + bool Inserted; + DeclContext *NewContext = + new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, + Context, DIE, U.getUniqueID()); + std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); + assert(Inserted && "Failed to insert DeclContext"); + (void)Inserted; + } else if (Tag != dwarf::DW_TAG_namespace && + !(*ContextIter)->setLastSeenDIE(U, DIE)) { + // The context was found, but it is ambiguous with another context + // in the same file. Mark it invalid. + return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1); + } + + assert(ContextIter != Contexts.end()); + // FIXME: dsymutil-classic compatibility. Union types aren't + // uniques, but their children might be. + if ((Tag == dwarf::DW_TAG_subprogram && + Context.getTag() != dwarf::DW_TAG_structure_type && + Context.getTag() != dwarf::DW_TAG_class_type) || + (Tag == dwarf::DW_TAG_union_type)) + return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1); + + return PointerIntPair<DeclContext *, 1>(*ContextIter); +} +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/DeclContext.h b/tools/dsymutil/DeclContext.h new file mode 100644 index 000000000000..1fa6815fa3b9 --- /dev/null +++ b/tools/dsymutil/DeclContext.h @@ -0,0 +1,172 @@ +//===- tools/dsymutil/DeclContext.h - Dwarf debug info linker ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompileUnit.h" +#include "NonRelocatableStringpool.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseMapInfo.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/DebugInfo/DWARF/DWARFDie.h" +#include "llvm/Support/Path.h" + +#ifndef LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H +#define LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H + +namespace llvm { +namespace dsymutil { + +struct DeclMapInfo; + +/// Small helper that resolves and caches file paths. This helps reduce the +/// number of calls to realpath which is expensive. We assume the input are +/// files, and cache the realpath of their parent. This way we can quickly +/// resolve different files under the same path. +class CachedPathResolver { +public: + /// Resolve a path by calling realpath and cache its result. The returned + /// StringRef is interned in the given \p StringPool. + StringRef resolve(std::string Path, NonRelocatableStringpool &StringPool) { + StringRef FileName = sys::path::filename(Path); + SmallString<256> ParentPath = sys::path::parent_path(Path); + + // If the ParentPath has not yet been resolved, resolve and cache it for + // future look-ups. + if (!ResolvedPaths.count(ParentPath)) { + SmallString<256> RealPath; + sys::fs::real_path(ParentPath, RealPath); + ResolvedPaths.insert({ParentPath, StringRef(RealPath).str()}); + } + + // Join the file name again with the resolved path. + SmallString<256> ResolvedPath(ResolvedPaths[ParentPath]); + sys::path::append(ResolvedPath, FileName); + return StringPool.internString(ResolvedPath); + } + +private: + StringMap<std::string> ResolvedPaths; +}; + +/// A DeclContext is a named program scope that is used for ODR uniquing of +/// types. +/// +/// The set of DeclContext for the ODR-subject parts of a Dwarf link is +/// expanded (and uniqued) with each new object file processed. We need to +/// determine the context of each DIE in an linked object file to see if the +/// corresponding type has already been emitted. +/// +/// The contexts are conceptually organized as a tree (eg. a function scope is +/// contained in a namespace scope that contains other scopes), but +/// storing/accessing them in an actual tree is too inefficient: we need to be +/// able to very quickly query a context for a given child context by name. +/// Storing a StringMap in each DeclContext would be too space inefficient. +/// +/// The solution here is to give each DeclContext a link to its parent (this +/// allows to walk up the tree), but to query the existence of a specific +/// DeclContext using a separate DenseMap keyed on the hash of the fully +/// qualified name of the context. +class DeclContext { +public: + using Map = DenseSet<DeclContext *, DeclMapInfo>; + + DeclContext() : DefinedInClangModule(0), Parent(*this) {} + + DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, + StringRef Name, StringRef File, const DeclContext &Parent, + DWARFDie LastSeenDIE = DWARFDie(), unsigned CUId = 0) + : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), + DefinedInClangModule(0), Name(Name), File(File), Parent(Parent), + LastSeenDIE(LastSeenDIE), LastSeenCompileUnitID(CUId) {} + + uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } + + bool setLastSeenDIE(CompileUnit &U, const DWARFDie &Die); + + uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } + void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } + + bool isDefinedInClangModule() const { return DefinedInClangModule; } + void setDefinedInClangModule(bool Val) { DefinedInClangModule = Val; } + + uint16_t getTag() const { return Tag; } + StringRef getName() const { return Name; } + +private: + friend DeclMapInfo; + + unsigned QualifiedNameHash = 0; + uint32_t Line = 0; + uint32_t ByteSize = 0; + uint16_t Tag = dwarf::DW_TAG_compile_unit; + unsigned DefinedInClangModule : 1; + StringRef Name; + StringRef File; + const DeclContext &Parent; + DWARFDie LastSeenDIE; + uint32_t LastSeenCompileUnitID = 0; + uint32_t CanonicalDIEOffset = 0; +}; + +/// This class gives a tree-like API to the DenseMap that stores the +/// DeclContext objects. It holds the BumpPtrAllocator where these objects will +/// be allocated. +class DeclContextTree { +public: + /// Get the child of \a Context described by \a DIE in \a Unit. The + /// required strings will be interned in \a StringPool. + /// \returns The child DeclContext along with one bit that is set if + /// this context is invalid. + /// + /// An invalid context means it shouldn't be considered for uniquing, but its + /// not returning null, because some children of that context might be + /// uniquing candidates. + /// + /// FIXME: The invalid bit along the return value is to emulate some + /// dsymutil-classic functionality. + PointerIntPair<DeclContext *, 1> + getChildDeclContext(DeclContext &Context, const DWARFDie &DIE, + CompileUnit &Unit, UniquingStringPool &StringPool, + bool InClangModule); + + DeclContext &getRoot() { return Root; } + +private: + BumpPtrAllocator Allocator; + DeclContext Root; + DeclContext::Map Contexts; + + /// Cache resolved paths from the line table. + CachedPathResolver PathResolver; +}; + +/// Info type for the DenseMap storing the DeclContext pointers. +struct DeclMapInfo : private DenseMapInfo<DeclContext *> { + using DenseMapInfo<DeclContext *>::getEmptyKey; + using DenseMapInfo<DeclContext *>::getTombstoneKey; + + static unsigned getHashValue(const DeclContext *Ctxt) { + return Ctxt->QualifiedNameHash; + } + + static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) { + if (RHS == getEmptyKey() || RHS == getTombstoneKey()) + return RHS == LHS; + return LHS->QualifiedNameHash == RHS->QualifiedNameHash && + LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && + LHS->Name.data() == RHS->Name.data() && + LHS->File.data() == RHS->File.data() && + LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; + } +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index 50ffc69dfaa0..e685a59277ba 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -1,18 +1,22 @@ //===- tools/dsymutil/DwarfLinker.cpp - Dwarf debug info linker -----------===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// +#include "DwarfLinker.h" #include "BinaryHolder.h" #include "DebugMap.h" +#include "DeclContext.h" +#include "DwarfStreamer.h" #include "MachOUtils.h" #include "NonRelocatableStringpool.h" #include "dsymutil.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMapInfo.h" #include "llvm/ADT/DenseSet.h" @@ -30,6 +34,7 @@ #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/BinaryFormat/MachO.h" +#include "llvm/CodeGen/AccelTable.h" #include "llvm/CodeGen/AsmPrinter.h" #include "llvm/CodeGen/DIE.h" #include "llvm/Config/config.h" @@ -50,18 +55,19 @@ #include "llvm/MC/MCDwarf.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSection.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" #include "llvm/MC/MCTargetOptions.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.def" #include "llvm/Object/MachO.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Compiler.h" +#include "llvm/Support/DJB.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" @@ -73,7 +79,9 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/ThreadPool.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" @@ -96,1505 +104,26 @@ namespace llvm { namespace dsymutil { -namespace { - -template <typename KeyT, typename ValT> -using HalfOpenIntervalMap = - IntervalMap<KeyT, ValT, IntervalMapImpl::NodeSizer<KeyT, ValT>::LeafSize, - IntervalMapHalfOpenInfo<KeyT>>; - -using FunctionIntervals = HalfOpenIntervalMap<uint64_t, int64_t>; - -// FIXME: Delete this structure. -struct PatchLocation { - DIE::value_iterator I; - - PatchLocation() = default; - PatchLocation(DIE::value_iterator I) : I(I) {} - - void set(uint64_t New) const { - assert(I); - const auto &Old = *I; - assert(Old.getType() == DIEValue::isInteger); - *I = DIEValue(Old.getAttribute(), Old.getForm(), DIEInteger(New)); - } - - uint64_t get() const { - assert(I); - return I->getDIEInteger().getValue(); - } -}; - -class CompileUnit; -struct DeclMapInfo; - -/// A DeclContext is a named program scope that is used for ODR -/// uniquing of types. -/// The set of DeclContext for the ODR-subject parts of a Dwarf link -/// is expanded (and uniqued) with each new object file processed. We -/// need to determine the context of each DIE in an linked object file -/// to see if the corresponding type has already been emitted. -/// -/// The contexts are conceptually organised as a tree (eg. a function -/// scope is contained in a namespace scope that contains other -/// scopes), but storing/accessing them in an actual tree is too -/// inefficient: we need to be able to very quickly query a context -/// for a given child context by name. Storing a StringMap in each -/// DeclContext would be too space inefficient. -/// The solution here is to give each DeclContext a link to its parent -/// (this allows to walk up the tree), but to query the existance of a -/// specific DeclContext using a separate DenseMap keyed on the hash -/// of the fully qualified name of the context. -class DeclContext { - friend DeclMapInfo; - - unsigned QualifiedNameHash = 0; - uint32_t Line = 0; - uint32_t ByteSize = 0; - uint16_t Tag = dwarf::DW_TAG_compile_unit; - unsigned DefinedInClangModule : 1; - StringRef Name; - StringRef File; - const DeclContext &Parent; - DWARFDie LastSeenDIE; - uint32_t LastSeenCompileUnitID = 0; - uint32_t CanonicalDIEOffset = 0; - -public: - using Map = DenseSet<DeclContext *, DeclMapInfo>; - - DeclContext() : DefinedInClangModule(0), Parent(*this) {} - - DeclContext(unsigned Hash, uint32_t Line, uint32_t ByteSize, uint16_t Tag, - StringRef Name, StringRef File, const DeclContext &Parent, - DWARFDie LastSeenDIE = DWARFDie(), unsigned CUId = 0) - : QualifiedNameHash(Hash), Line(Line), ByteSize(ByteSize), Tag(Tag), - DefinedInClangModule(0), Name(Name), File(File), Parent(Parent), - LastSeenDIE(LastSeenDIE), LastSeenCompileUnitID(CUId) {} - - uint32_t getQualifiedNameHash() const { return QualifiedNameHash; } - - bool setLastSeenDIE(CompileUnit &U, const DWARFDie &Die); - - uint32_t getCanonicalDIEOffset() const { return CanonicalDIEOffset; } - void setCanonicalDIEOffset(uint32_t Offset) { CanonicalDIEOffset = Offset; } - - bool isDefinedInClangModule() const { return DefinedInClangModule; } - void setDefinedInClangModule(bool Val) { DefinedInClangModule = Val; } - - uint16_t getTag() const { return Tag; } - StringRef getName() const { return Name; } -}; - -/// Info type for the DenseMap storing the DeclContext pointers. -struct DeclMapInfo : private DenseMapInfo<DeclContext *> { - using DenseMapInfo<DeclContext *>::getEmptyKey; - using DenseMapInfo<DeclContext *>::getTombstoneKey; - - static unsigned getHashValue(const DeclContext *Ctxt) { - return Ctxt->QualifiedNameHash; - } - - static bool isEqual(const DeclContext *LHS, const DeclContext *RHS) { - if (RHS == getEmptyKey() || RHS == getTombstoneKey()) - return RHS == LHS; - return LHS->QualifiedNameHash == RHS->QualifiedNameHash && - LHS->Line == RHS->Line && LHS->ByteSize == RHS->ByteSize && - LHS->Name.data() == RHS->Name.data() && - LHS->File.data() == RHS->File.data() && - LHS->Parent.QualifiedNameHash == RHS->Parent.QualifiedNameHash; - } -}; - -/// This class gives a tree-like API to the DenseMap that stores the -/// DeclContext objects. It also holds the BumpPtrAllocator where -/// these objects will be allocated. -class DeclContextTree { - BumpPtrAllocator Allocator; - DeclContext Root; - DeclContext::Map Contexts; - -public: - /// Get the child of \a Context described by \a DIE in \a Unit. The - /// required strings will be interned in \a StringPool. - /// \returns The child DeclContext along with one bit that is set if - /// this context is invalid. - /// An invalid context means it shouldn't be considered for uniquing, but its - /// not returning null, because some children of that context might be - /// uniquing candidates. FIXME: The invalid bit along the return value is to - /// emulate some dsymutil-classic functionality. - PointerIntPair<DeclContext *, 1> - getChildDeclContext(DeclContext &Context, - const DWARFDie &DIE, CompileUnit &Unit, - NonRelocatableStringpool &StringPool, bool InClangModule); - - DeclContext &getRoot() { return Root; } -}; - -/// Stores all information relating to a compile unit, be it in its original -/// instance in the object file to its brand new cloned and linked DIE tree. -class CompileUnit { -public: - /// Information gathered about a DIE in the object file. - struct DIEInfo { - /// Address offset to apply to the described entity. - int64_t AddrAdjust; - - /// ODR Declaration context. - DeclContext *Ctxt; - - /// Cloned version of that DIE. - DIE *Clone; - - /// The index of this DIE's parent. - uint32_t ParentIdx; - - /// Is the DIE part of the linked output? - bool Keep : 1; - - /// Was this DIE's entity found in the map? - bool InDebugMap : 1; - - /// Is this a pure forward declaration we can strip? - bool Prune : 1; - - /// Does DIE transitively refer an incomplete decl? - bool Incomplete : 1; - }; - - CompileUnit(DWARFUnit &OrigUnit, unsigned ID, bool CanUseODR, - StringRef ClangModuleName) - : OrigUnit(OrigUnit), ID(ID), Ranges(RangeAlloc), - ClangModuleName(ClangModuleName) { - Info.resize(OrigUnit.getNumDIEs()); - - auto CUDie = OrigUnit.getUnitDIE(false); - if (auto Lang = dwarf::toUnsigned(CUDie.find(dwarf::DW_AT_language))) - HasODR = CanUseODR && (*Lang == dwarf::DW_LANG_C_plus_plus || - *Lang == dwarf::DW_LANG_C_plus_plus_03 || - *Lang == dwarf::DW_LANG_C_plus_plus_11 || - *Lang == dwarf::DW_LANG_C_plus_plus_14 || - *Lang == dwarf::DW_LANG_ObjC_plus_plus); - else - HasODR = false; - } - - DWARFUnit &getOrigUnit() const { return OrigUnit; } - - unsigned getUniqueID() const { return ID; } - - void createOutputDIE() { - NewUnit.emplace(OrigUnit.getVersion(), OrigUnit.getAddressByteSize(), - OrigUnit.getUnitDIE().getTag()); - } - - DIE *getOutputUnitDIE() const { - if (NewUnit) - return &const_cast<BasicDIEUnit &>(*NewUnit).getUnitDie(); - return nullptr; - } - - bool hasODR() const { return HasODR; } - bool isClangModule() const { return !ClangModuleName.empty(); } - const std::string &getClangModuleName() const { return ClangModuleName; } - - DIEInfo &getInfo(unsigned Idx) { return Info[Idx]; } - const DIEInfo &getInfo(unsigned Idx) const { return Info[Idx]; } - - uint64_t getStartOffset() const { return StartOffset; } - uint64_t getNextUnitOffset() const { return NextUnitOffset; } - void setStartOffset(uint64_t DebugInfoSize) { StartOffset = DebugInfoSize; } - - uint64_t getLowPc() const { return LowPc; } - uint64_t getHighPc() const { return HighPc; } - - Optional<PatchLocation> getUnitRangesAttribute() const { - return UnitRangeAttribute; - } - - const FunctionIntervals &getFunctionRanges() const { return Ranges; } - - const std::vector<PatchLocation> &getRangesAttributes() const { - return RangeAttributes; - } - - const std::vector<std::pair<PatchLocation, int64_t>> & - getLocationAttributes() const { - return LocationAttributes; - } - - void setHasInterestingContent() { HasInterestingContent = true; } - bool hasInterestingContent() { return HasInterestingContent; } - - /// Mark every DIE in this unit as kept. This function also - /// marks variables as InDebugMap so that they appear in the - /// reconstructed accelerator tables. - void markEverythingAsKept(); - - /// Compute the end offset for this unit. Must be called after the CU's DIEs - /// have been cloned. \returns the next unit offset (which is also the - /// current debug_info section size). - uint64_t computeNextUnitOffset(); - - /// Keep track of a forward reference to DIE \p Die in \p RefUnit by \p - /// Attr. The attribute should be fixed up later to point to the absolute - /// offset of \p Die in the debug_info section or to the canonical offset of - /// \p Ctxt if it is non-null. - void noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - DeclContext *Ctxt, PatchLocation Attr); - - /// Apply all fixups recored by noteForwardReference(). - void fixupForwardReferences(); - - /// Add a function range [\p LowPC, \p HighPC) that is relocatad by applying - /// offset \p PCOffset. - void addFunctionRange(uint64_t LowPC, uint64_t HighPC, int64_t PCOffset); - - /// Keep track of a DW_AT_range attribute that we will need to patch up later. - void noteRangeAttribute(const DIE &Die, PatchLocation Attr); - - /// Keep track of a location attribute pointing to a location list in the - /// debug_loc section. - void noteLocationAttribute(PatchLocation Attr, int64_t PcOffset); - - /// Add a name accelerator entry for \p Die with \p Name which is stored in - /// the string table at \p Offset. - void addNameAccelerator(const DIE *Die, const char *Name, uint32_t Offset, - bool SkipPubnamesSection = false); - - /// Add a type accelerator entry for \p Die with \p Name which is stored in - /// the string table at \p Offset. - void addTypeAccelerator(const DIE *Die, const char *Name, uint32_t Offset); - - struct AccelInfo { - StringRef Name; ///< Name of the entry. - const DIE *Die; ///< DIE this entry describes. - uint32_t NameOffset; ///< Offset of Name in the string pool. - bool SkipPubSection; ///< Emit this entry only in the apple_* sections. - - AccelInfo(StringRef Name, const DIE *Die, uint32_t NameOffset, - bool SkipPubSection = false) - : Name(Name), Die(Die), NameOffset(NameOffset), - SkipPubSection(SkipPubSection) {} - }; - - const std::vector<AccelInfo> &getPubnames() const { return Pubnames; } - const std::vector<AccelInfo> &getPubtypes() const { return Pubtypes; } - - /// Get the full path for file \a FileNum in the line table - StringRef getResolvedPath(unsigned FileNum) { - if (FileNum >= ResolvedPaths.size()) - return StringRef(); - return ResolvedPaths[FileNum]; - } - - /// Set the fully resolved path for the line-table's file \a FileNum - /// to \a Path. - void setResolvedPath(unsigned FileNum, StringRef Path) { - if (ResolvedPaths.size() <= FileNum) - ResolvedPaths.resize(FileNum + 1); - ResolvedPaths[FileNum] = Path; - } - -private: - DWARFUnit &OrigUnit; - unsigned ID; - std::vector<DIEInfo> Info; ///< DIE info indexed by DIE index. - Optional<BasicDIEUnit> NewUnit; - - uint64_t StartOffset; - uint64_t NextUnitOffset; - - uint64_t LowPc = std::numeric_limits<uint64_t>::max(); - uint64_t HighPc = 0; - - /// A list of attributes to fixup with the absolute offset of - /// a DIE in the debug_info section. - /// - /// The offsets for the attributes in this array couldn't be set while - /// cloning because for cross-cu forward refences the target DIE's - /// offset isn't known you emit the reference attribute. - std::vector<std::tuple<DIE *, const CompileUnit *, DeclContext *, - PatchLocation>> ForwardDIEReferences; - - FunctionIntervals::Allocator RangeAlloc; - - /// The ranges in that interval map are the PC ranges for - /// functions in this unit, associated with the PC offset to apply - /// to the addresses to get the linked address. - FunctionIntervals Ranges; - - /// DW_AT_ranges attributes to patch after we have gathered - /// all the unit's function addresses. - /// @{ - std::vector<PatchLocation> RangeAttributes; - Optional<PatchLocation> UnitRangeAttribute; - /// @} - - /// Location attributes that need to be transferred from the - /// original debug_loc section to the liked one. They are stored - /// along with the PC offset that is to be applied to their - /// function's address. - std::vector<std::pair<PatchLocation, int64_t>> LocationAttributes; - - /// Accelerator entries for the unit, both for the pub* - /// sections and the apple* ones. - /// @{ - std::vector<AccelInfo> Pubnames; - std::vector<AccelInfo> Pubtypes; - /// @} - - /// Cached resolved paths from the line table. - /// Note, the StringRefs here point in to the intern (uniquing) string pool. - /// This means that a StringRef returned here doesn't need to then be uniqued - /// for the purposes of getting a unique address for each string. - std::vector<StringRef> ResolvedPaths; - - /// Is this unit subject to the ODR rule? - bool HasODR; - - /// Did a DIE actually contain a valid reloc? - bool HasInterestingContent; - - /// If this is a Clang module, this holds the module's name. - std::string ClangModuleName; -}; - -} // end anonymous namespace - -void CompileUnit::markEverythingAsKept() { - for (auto &I : Info) - // Mark everything that wasn't explicity marked for pruning. - I.Keep = !I.Prune; -} - -uint64_t CompileUnit::computeNextUnitOffset() { - NextUnitOffset = StartOffset + 11 /* Header size */; - // The root DIE might be null, meaning that the Unit had nothing to - // contribute to the linked output. In that case, we will emit the - // unit header without any actual DIE. - if (NewUnit) - NextUnitOffset += NewUnit->getUnitDie().getSize(); - return NextUnitOffset; -} - -/// Keep track of a forward cross-cu reference from this unit -/// to \p Die that lives in \p RefUnit. -void CompileUnit::noteForwardReference(DIE *Die, const CompileUnit *RefUnit, - DeclContext *Ctxt, PatchLocation Attr) { - ForwardDIEReferences.emplace_back(Die, RefUnit, Ctxt, Attr); -} - -/// Apply all fixups recorded by noteForwardReference(). -void CompileUnit::fixupForwardReferences() { - for (const auto &Ref : ForwardDIEReferences) { - DIE *RefDie; - const CompileUnit *RefUnit; - PatchLocation Attr; - DeclContext *Ctxt; - std::tie(RefDie, RefUnit, Ctxt, Attr) = Ref; - if (Ctxt && Ctxt->getCanonicalDIEOffset()) - Attr.set(Ctxt->getCanonicalDIEOffset()); - else - Attr.set(RefDie->getOffset() + RefUnit->getStartOffset()); - } -} - -void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, - int64_t PcOffset) { - Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); - this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); - this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); -} - -void CompileUnit::noteRangeAttribute(const DIE &Die, PatchLocation Attr) { - if (Die.getTag() != dwarf::DW_TAG_compile_unit) - RangeAttributes.push_back(Attr); - else - UnitRangeAttribute = Attr; -} - -void CompileUnit::noteLocationAttribute(PatchLocation Attr, int64_t PcOffset) { - LocationAttributes.emplace_back(Attr, PcOffset); -} - -/// Add a name accelerator entry for \p Die with \p Name -/// which is stored in the string table at \p Offset. -void CompileUnit::addNameAccelerator(const DIE *Die, const char *Name, - uint32_t Offset, bool SkipPubSection) { - Pubnames.emplace_back(Name, Die, Offset, SkipPubSection); -} - -/// Add a type accelerator entry for \p Die with \p Name -/// which is stored in the string table at \p Offset. -void CompileUnit::addTypeAccelerator(const DIE *Die, const char *Name, - uint32_t Offset) { - Pubtypes.emplace_back(Name, Die, Offset, false); -} - -namespace { - -/// The Dwarf streaming logic -/// -/// All interactions with the MC layer that is used to build the debug -/// information binary representation are handled in this class. -class DwarfStreamer { - /// \defgroup MCObjects MC layer objects constructed by the streamer - /// @{ - std::unique_ptr<MCRegisterInfo> MRI; - std::unique_ptr<MCAsmInfo> MAI; - std::unique_ptr<MCObjectFileInfo> MOFI; - std::unique_ptr<MCContext> MC; - MCAsmBackend *MAB; // Owned by MCStreamer - std::unique_ptr<MCInstrInfo> MII; - std::unique_ptr<MCSubtargetInfo> MSTI; - MCCodeEmitter *MCE; // Owned by MCStreamer - MCStreamer *MS; // Owned by AsmPrinter - std::unique_ptr<TargetMachine> TM; - std::unique_ptr<AsmPrinter> Asm; - /// @} - - /// The file we stream the linked Dwarf to. - raw_fd_ostream &OutFile; - - uint32_t RangesSectionSize; - uint32_t LocSectionSize; - uint32_t LineSectionSize; - uint32_t FrameSectionSize; - - /// Emit the pubnames or pubtypes section contribution for \p - /// Unit into \p Sec. The data is provided in \p Names. - void emitPubSectionForUnit(MCSection *Sec, StringRef Name, - const CompileUnit &Unit, - const std::vector<CompileUnit::AccelInfo> &Names); - -public: - DwarfStreamer(raw_fd_ostream &OutFile) : OutFile(OutFile) {} - bool init(Triple TheTriple); - - /// Dump the file to the disk. - bool finish(const DebugMap &); - - AsmPrinter &getAsmPrinter() const { return *Asm; } - - /// Set the current output section to debug_info and change - /// the MC Dwarf version to \p DwarfVersion. - void switchToDebugInfoSection(unsigned DwarfVersion); - - /// Emit the compilation unit header for \p Unit in the - /// debug_info section. - /// - /// As a side effect, this also switches the current Dwarf version - /// of the MC layer to the one of U.getOrigUnit(). - void emitCompileUnitHeader(CompileUnit &Unit); - - /// Recursively emit the DIE tree rooted at \p Die. - void emitDIE(DIE &Die); - - /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. - void emitAbbrevs(const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs, - unsigned DwarfVersion); - - /// Emit the string table described by \p Pool. - void emitStrings(const NonRelocatableStringpool &Pool); - - /// Emit the swift_ast section stored in \p Buffer. - void emitSwiftAST(StringRef Buffer); - - /// Emit debug_ranges for \p FuncRange by translating the - /// original \p Entries. - void emitRangesEntries( - int64_t UnitPcOffset, uint64_t OrigLowPc, - const FunctionIntervals::const_iterator &FuncRange, - const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries, - unsigned AddressSize); - - /// Emit debug_aranges entries for \p Unit and if \p DoRangesSection is true, - /// also emit the debug_ranges entries for the DW_TAG_compile_unit's - /// DW_AT_ranges attribute. - void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection); - - uint32_t getRangesSectionSize() const { return RangesSectionSize; } - - /// Emit the debug_loc contribution for \p Unit by copying the entries from \p - /// Dwarf and offseting them. Update the location attributes to point to the - /// new entries. - void emitLocationsForUnit(const CompileUnit &Unit, DWARFContext &Dwarf); - - /// Emit the line table described in \p Rows into the debug_line section. - void emitLineTableForUnit(MCDwarfLineTableParams Params, - StringRef PrologueBytes, unsigned MinInstLength, - std::vector<DWARFDebugLine::Row> &Rows, - unsigned AdddressSize); - - uint32_t getLineSectionSize() const { return LineSectionSize; } - - /// Emit the .debug_pubnames contribution for \p Unit. - void emitPubNamesForUnit(const CompileUnit &Unit); - - /// Emit the .debug_pubtypes contribution for \p Unit. - void emitPubTypesForUnit(const CompileUnit &Unit); - - /// Emit a CIE. - void emitCIE(StringRef CIEBytes); - - /// Emit an FDE with data \p Bytes. - void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, - StringRef Bytes); - - uint32_t getFrameSectionSize() const { return FrameSectionSize; } -}; - -} // end anonymous namespace - -bool DwarfStreamer::init(Triple TheTriple) { - std::string ErrorStr; - std::string TripleName; - StringRef Context = "dwarf streamer init"; - - // Get the target. - const Target *TheTarget = - TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); - if (!TheTarget) - return error(ErrorStr, Context); - TripleName = TheTriple.getTriple(); - - // Create all the MC Objects. - MRI.reset(TheTarget->createMCRegInfo(TripleName)); - if (!MRI) - return error(Twine("no register info for target ") + TripleName, Context); - - MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); - if (!MAI) - return error("no asm info for target " + TripleName, Context); - - MOFI.reset(new MCObjectFileInfo); - MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); - MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC); - - MCTargetOptions Options; - MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", Options); - if (!MAB) - return error("no asm backend for target " + TripleName, Context); - - MII.reset(TheTarget->createMCInstrInfo()); - if (!MII) - return error("no instr info info for target " + TripleName, Context); - - MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); - if (!MSTI) - return error("no subtarget info for target " + TripleName, Context); - - MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); - if (!MCE) - return error("no code emitter for target " + TripleName, Context); - - MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); - MS = TheTarget->createMCObjectStreamer( - TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB), OutFile, - std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, MCOptions.MCRelaxAll, - MCOptions.MCIncrementalLinkerCompatible, - /*DWARFMustBeAtTheEnd*/ false); - if (!MS) - return error("no object streamer for target " + TripleName, Context); - - // Finally create the AsmPrinter we'll use to emit the DIEs. - TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(), - None)); - if (!TM) - return error("no target machine for target " + TripleName, Context); - - Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS))); - if (!Asm) - return error("no asm printer for target " + TripleName, Context); - - RangesSectionSize = 0; - LocSectionSize = 0; - LineSectionSize = 0; - FrameSectionSize = 0; - - return true; -} - -bool DwarfStreamer::finish(const DebugMap &DM) { - bool Result = true; - if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty()) - Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile); - else - MS->Finish(); - return Result; -} - -/// Set the current output section to debug_info and change -/// the MC Dwarf version to \p DwarfVersion. -void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) { - MS->SwitchSection(MOFI->getDwarfInfoSection()); - MC->setDwarfVersion(DwarfVersion); -} - -/// Emit the compilation unit header for \p Unit in the debug_info section. -/// -/// A Dwarf scetion header is encoded as: -/// uint32_t Unit length (omiting this field) -/// uint16_t Version -/// uint32_t Abbreviation table offset -/// uint8_t Address size -/// -/// Leading to a total of 11 bytes. -void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { - unsigned Version = Unit.getOrigUnit().getVersion(); - switchToDebugInfoSection(Version); - - // Emit size of content not including length itself. The size has - // already been computed in CompileUnit::computeOffsets(). Substract - // 4 to that size to account for the length field. - Asm->EmitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4); - Asm->EmitInt16(Version); - // We share one abbreviations table across all units so it's always at the - // start of the section. - Asm->EmitInt32(0); - Asm->EmitInt8(Unit.getOrigUnit().getAddressByteSize()); -} - -/// Emit the \p Abbrevs array as the shared abbreviation table -/// for the linked Dwarf file. -void DwarfStreamer::emitAbbrevs( - const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs, - unsigned DwarfVersion) { - MS->SwitchSection(MOFI->getDwarfAbbrevSection()); - MC->setDwarfVersion(DwarfVersion); - Asm->emitDwarfAbbrevs(Abbrevs); -} - -/// Recursively emit the DIE tree rooted at \p Die. -void DwarfStreamer::emitDIE(DIE &Die) { - MS->SwitchSection(MOFI->getDwarfInfoSection()); - Asm->emitDwarfDIE(Die); -} - -/// Emit the debug_str section stored in \p Pool. -void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { - Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection()); - for (auto *Entry = Pool.getFirstEntry(); Entry; - Entry = Pool.getNextEntry(Entry)) - Asm->OutStreamer->EmitBytes( - StringRef(Entry->getKey().data(), Entry->getKey().size() + 1)); -} - -/// Emit the swift_ast section stored in \p Buffers. -void DwarfStreamer::emitSwiftAST(StringRef Buffer) { - MCSection *SwiftASTSection = MOFI->getDwarfSwiftASTSection(); - SwiftASTSection->setAlignment(1 << 5); - MS->SwitchSection(SwiftASTSection); - MS->EmitBytes(Buffer); -} - -/// Emit the debug_range section contents for \p FuncRange by -/// translating the original \p Entries. The debug_range section -/// format is totally trivial, consisting just of pairs of address -/// sized addresses describing the ranges. -void DwarfStreamer::emitRangesEntries( - int64_t UnitPcOffset, uint64_t OrigLowPc, - const FunctionIntervals::const_iterator &FuncRange, - const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries, - unsigned AddressSize) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); - - // Offset each range by the right amount. - int64_t PcOffset = Entries.empty() ? 0 : FuncRange.value() + UnitPcOffset; - for (const auto &Range : Entries) { - if (Range.isBaseAddressSelectionEntry(AddressSize)) { - warn("unsupported base address selection operation", - "emitting debug_ranges"); - break; - } - // Do not emit empty ranges. - if (Range.StartAddress == Range.EndAddress) - continue; - - // All range entries should lie in the function range. - if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && - Range.EndAddress + OrigLowPc <= FuncRange.stop())) - warn("inconsistent range data.", "emitting debug_ranges"); - MS->EmitIntValue(Range.StartAddress + PcOffset, AddressSize); - MS->EmitIntValue(Range.EndAddress + PcOffset, AddressSize); - RangesSectionSize += 2 * AddressSize; - } - - // Add the terminator entry. - MS->EmitIntValue(0, AddressSize); - MS->EmitIntValue(0, AddressSize); - RangesSectionSize += 2 * AddressSize; -} - -/// Emit the debug_aranges contribution of a unit and -/// if \p DoDebugRanges is true the debug_range contents for a -/// compile_unit level DW_AT_ranges attribute (Which are basically the -/// same thing with a different base address). -/// Just aggregate all the ranges gathered inside that unit. -void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit, - bool DoDebugRanges) { - unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); - // Gather the ranges in a vector, so that we can simplify them. The - // IntervalMap will have coalesced the non-linked ranges, but here - // we want to coalesce the linked addresses. - std::vector<std::pair<uint64_t, uint64_t>> Ranges; - const auto &FunctionRanges = Unit.getFunctionRanges(); - for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); - Range != End; ++Range) - Ranges.push_back(std::make_pair(Range.start() + Range.value(), - Range.stop() + Range.value())); - - // The object addresses where sorted, but again, the linked - // addresses might end up in a different order. - std::sort(Ranges.begin(), Ranges.end()); - - if (!Ranges.empty()) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); - - MCSymbol *BeginLabel = Asm->createTempSymbol("Barange"); - MCSymbol *EndLabel = Asm->createTempSymbol("Earange"); - - unsigned HeaderSize = - sizeof(int32_t) + // Size of contents (w/o this field - sizeof(int16_t) + // DWARF ARange version number - sizeof(int32_t) + // Offset of CU in the .debug_info section - sizeof(int8_t) + // Pointer Size (in bytes) - sizeof(int8_t); // Segment Size (in bytes) - - unsigned TupleSize = AddressSize * 2; - unsigned Padding = OffsetToAlignment(HeaderSize, TupleSize); - - Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Arange length - Asm->OutStreamer->EmitLabel(BeginLabel); - Asm->EmitInt16(dwarf::DW_ARANGES_VERSION); // Version number - Asm->EmitInt32(Unit.getStartOffset()); // Corresponding unit's offset - Asm->EmitInt8(AddressSize); // Address size - Asm->EmitInt8(0); // Segment size - - Asm->OutStreamer->emitFill(Padding, 0x0); - - for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; - ++Range) { - uint64_t RangeStart = Range->first; - MS->EmitIntValue(RangeStart, AddressSize); - while ((Range + 1) != End && Range->second == (Range + 1)->first) - ++Range; - MS->EmitIntValue(Range->second - RangeStart, AddressSize); - } - - // Emit terminator - Asm->OutStreamer->EmitIntValue(0, AddressSize); - Asm->OutStreamer->EmitIntValue(0, AddressSize); - Asm->OutStreamer->EmitLabel(EndLabel); - } - - if (!DoDebugRanges) - return; - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); - // Offset each range by the right amount. - int64_t PcOffset = -Unit.getLowPc(); - // Emit coalesced ranges. - for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { - MS->EmitIntValue(Range->first + PcOffset, AddressSize); - while (Range + 1 != End && Range->second == (Range + 1)->first) - ++Range; - MS->EmitIntValue(Range->second + PcOffset, AddressSize); - RangesSectionSize += 2 * AddressSize; - } - - // Add the terminator entry. - MS->EmitIntValue(0, AddressSize); - MS->EmitIntValue(0, AddressSize); - RangesSectionSize += 2 * AddressSize; -} - -/// Emit location lists for \p Unit and update attribtues to -/// point to the new entries. -void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, - DWARFContext &Dwarf) { - const auto &Attributes = Unit.getLocationAttributes(); - - if (Attributes.empty()) - return; - - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); - - unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); - const DWARFSection &InputSec = Dwarf.getDWARFObj().getLocSection(); - DataExtractor Data(InputSec.Data, Dwarf.isLittleEndian(), AddressSize); - DWARFUnit &OrigUnit = Unit.getOrigUnit(); - auto OrigUnitDie = OrigUnit.getUnitDIE(false); - int64_t UnitPcOffset = 0; - if (auto OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc))) - UnitPcOffset = int64_t(*OrigLowPc) - Unit.getLowPc(); - - for (const auto &Attr : Attributes) { - uint32_t Offset = Attr.first.get(); - Attr.first.set(LocSectionSize); - // This is the quantity to add to the old location address to get - // the correct address for the new one. - int64_t LocPcOffset = Attr.second + UnitPcOffset; - while (Data.isValidOffset(Offset)) { - uint64_t Low = Data.getUnsigned(&Offset, AddressSize); - uint64_t High = Data.getUnsigned(&Offset, AddressSize); - LocSectionSize += 2 * AddressSize; - if (Low == 0 && High == 0) { - Asm->OutStreamer->EmitIntValue(0, AddressSize); - Asm->OutStreamer->EmitIntValue(0, AddressSize); - break; - } - Asm->OutStreamer->EmitIntValue(Low + LocPcOffset, AddressSize); - Asm->OutStreamer->EmitIntValue(High + LocPcOffset, AddressSize); - uint64_t Length = Data.getU16(&Offset); - Asm->OutStreamer->EmitIntValue(Length, 2); - // Just copy the bytes over. - Asm->OutStreamer->EmitBytes( - StringRef(InputSec.Data.substr(Offset, Length))); - Offset += Length; - LocSectionSize += Length + 2; - } - } -} - -void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, - StringRef PrologueBytes, - unsigned MinInstLength, - std::vector<DWARFDebugLine::Row> &Rows, - unsigned PointerSize) { - // Switch to the section where the table will be emitted into. - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); - MCSymbol *LineStartSym = MC->createTempSymbol(); - MCSymbol *LineEndSym = MC->createTempSymbol(); - - // The first 4 bytes is the total length of the information for this - // compilation unit (not including these 4 bytes for the length). - Asm->EmitLabelDifference(LineEndSym, LineStartSym, 4); - Asm->OutStreamer->EmitLabel(LineStartSym); - // Copy Prologue. - MS->EmitBytes(PrologueBytes); - LineSectionSize += PrologueBytes.size() + 4; - - SmallString<128> EncodingBuffer; - raw_svector_ostream EncodingOS(EncodingBuffer); - - if (Rows.empty()) { - // We only have the dummy entry, dsymutil emits an entry with a 0 - // address in that case. - MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits<int64_t>::max(), 0, - EncodingOS); - MS->EmitBytes(EncodingOS.str()); - LineSectionSize += EncodingBuffer.size(); - MS->EmitLabel(LineEndSym); - return; - } - - // Line table state machine fields - unsigned FileNum = 1; - unsigned LastLine = 1; - unsigned Column = 0; - unsigned IsStatement = 1; - unsigned Isa = 0; - uint64_t Address = -1ULL; - - unsigned RowsSinceLastSequence = 0; - - for (unsigned Idx = 0; Idx < Rows.size(); ++Idx) { - auto &Row = Rows[Idx]; - - int64_t AddressDelta; - if (Address == -1ULL) { - MS->EmitIntValue(dwarf::DW_LNS_extended_op, 1); - MS->EmitULEB128IntValue(PointerSize + 1); - MS->EmitIntValue(dwarf::DW_LNE_set_address, 1); - MS->EmitIntValue(Row.Address, PointerSize); - LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); - AddressDelta = 0; - } else { - AddressDelta = (Row.Address - Address) / MinInstLength; - } - - // FIXME: code copied and transfromed from - // MCDwarf.cpp::EmitDwarfLineTable. We should find a way to share - // this code, but the current compatibility requirement with - // classic dsymutil makes it hard. Revisit that once this - // requirement is dropped. - - if (FileNum != Row.File) { - FileNum = Row.File; - MS->EmitIntValue(dwarf::DW_LNS_set_file, 1); - MS->EmitULEB128IntValue(FileNum); - LineSectionSize += 1 + getULEB128Size(FileNum); - } - if (Column != Row.Column) { - Column = Row.Column; - MS->EmitIntValue(dwarf::DW_LNS_set_column, 1); - MS->EmitULEB128IntValue(Column); - LineSectionSize += 1 + getULEB128Size(Column); - } - - // FIXME: We should handle the discriminator here, but dsymutil - // doesn' consider it, thus ignore it for now. - - if (Isa != Row.Isa) { - Isa = Row.Isa; - MS->EmitIntValue(dwarf::DW_LNS_set_isa, 1); - MS->EmitULEB128IntValue(Isa); - LineSectionSize += 1 + getULEB128Size(Isa); - } - if (IsStatement != Row.IsStmt) { - IsStatement = Row.IsStmt; - MS->EmitIntValue(dwarf::DW_LNS_negate_stmt, 1); - LineSectionSize += 1; - } - if (Row.BasicBlock) { - MS->EmitIntValue(dwarf::DW_LNS_set_basic_block, 1); - LineSectionSize += 1; - } - - if (Row.PrologueEnd) { - MS->EmitIntValue(dwarf::DW_LNS_set_prologue_end, 1); - LineSectionSize += 1; - } - - if (Row.EpilogueBegin) { - MS->EmitIntValue(dwarf::DW_LNS_set_epilogue_begin, 1); - LineSectionSize += 1; - } - - int64_t LineDelta = int64_t(Row.Line) - LastLine; - if (!Row.EndSequence) { - MCDwarfLineAddr::Encode(*MC, Params, LineDelta, AddressDelta, EncodingOS); - MS->EmitBytes(EncodingOS.str()); - LineSectionSize += EncodingBuffer.size(); - EncodingBuffer.resize(0); - Address = Row.Address; - LastLine = Row.Line; - RowsSinceLastSequence++; - } else { - if (LineDelta) { - MS->EmitIntValue(dwarf::DW_LNS_advance_line, 1); - MS->EmitSLEB128IntValue(LineDelta); - LineSectionSize += 1 + getSLEB128Size(LineDelta); - } - if (AddressDelta) { - MS->EmitIntValue(dwarf::DW_LNS_advance_pc, 1); - MS->EmitULEB128IntValue(AddressDelta); - LineSectionSize += 1 + getULEB128Size(AddressDelta); - } - MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits<int64_t>::max(), - 0, EncodingOS); - MS->EmitBytes(EncodingOS.str()); - LineSectionSize += EncodingBuffer.size(); - EncodingBuffer.resize(0); - Address = -1ULL; - LastLine = FileNum = IsStatement = 1; - RowsSinceLastSequence = Column = Isa = 0; - } - } - - if (RowsSinceLastSequence) { - MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits<int64_t>::max(), 0, - EncodingOS); - MS->EmitBytes(EncodingOS.str()); - LineSectionSize += EncodingBuffer.size(); - EncodingBuffer.resize(0); - } - - MS->EmitLabel(LineEndSym); -} - -/// Emit the pubnames or pubtypes section contribution for \p -/// Unit into \p Sec. The data is provided in \p Names. -void DwarfStreamer::emitPubSectionForUnit( - MCSection *Sec, StringRef SecName, const CompileUnit &Unit, - const std::vector<CompileUnit::AccelInfo> &Names) { - if (Names.empty()) - return; - - // Start the dwarf pubnames section. - Asm->OutStreamer->SwitchSection(Sec); - MCSymbol *BeginLabel = Asm->createTempSymbol("pub" + SecName + "_begin"); - MCSymbol *EndLabel = Asm->createTempSymbol("pub" + SecName + "_end"); - - bool HeaderEmitted = false; - // Emit the pubnames for this compilation unit. - for (const auto &Name : Names) { - if (Name.SkipPubSection) - continue; - - if (!HeaderEmitted) { - // Emit the header. - Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Length - Asm->OutStreamer->EmitLabel(BeginLabel); - Asm->EmitInt16(dwarf::DW_PUBNAMES_VERSION); // Version - Asm->EmitInt32(Unit.getStartOffset()); // Unit offset - Asm->EmitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset()); // Size - HeaderEmitted = true; - } - Asm->EmitInt32(Name.Die->getOffset()); - Asm->OutStreamer->EmitBytes( - StringRef(Name.Name.data(), Name.Name.size() + 1)); - } - - if (!HeaderEmitted) - return; - Asm->EmitInt32(0); // End marker. - Asm->OutStreamer->EmitLabel(EndLabel); -} - -/// Emit .debug_pubnames for \p Unit. -void DwarfStreamer::emitPubNamesForUnit(const CompileUnit &Unit) { - emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubNamesSection(), - "names", Unit, Unit.getPubnames()); -} - -/// Emit .debug_pubtypes for \p Unit. -void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { - emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubTypesSection(), - "types", Unit, Unit.getPubtypes()); -} - -/// Emit a CIE into the debug_frame section. -void DwarfStreamer::emitCIE(StringRef CIEBytes) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); - - MS->EmitBytes(CIEBytes); - FrameSectionSize += CIEBytes.size(); -} - -/// Emit a FDE into the debug_frame section. \p FDEBytes -/// contains the FDE data without the length, CIE offset and address -/// which will be replaced with the parameter values. -void DwarfStreamer::emitFDE(uint32_t CIEOffset, uint32_t AddrSize, - uint32_t Address, StringRef FDEBytes) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); - - MS->EmitIntValue(FDEBytes.size() + 4 + AddrSize, 4); - MS->EmitIntValue(CIEOffset, 4); - MS->EmitIntValue(Address, AddrSize); - MS->EmitBytes(FDEBytes); - FrameSectionSize += FDEBytes.size() + 8 + AddrSize; -} - -namespace { - -/// The core of the Dwarf linking logic. -/// -/// The link of the dwarf information from the object files will be -/// driven by the selection of 'root DIEs', which are DIEs that -/// describe variables or functions that are present in the linked -/// binary (and thus have entries in the debug map). All the debug -/// information that will be linked (the DIEs, but also the line -/// tables, ranges, ...) is derived from that set of root DIEs. -/// -/// The root DIEs are identified because they contain relocations that -/// correspond to a debug map entry at specific places (the low_pc for -/// a function, the location for a variable). These relocations are -/// called ValidRelocs in the DwarfLinker and are gathered as a very -/// first step when we start processing a DebugMapObject. -class DwarfLinker { -public: - DwarfLinker(raw_fd_ostream &OutFile, const LinkOptions &Options) - : OutFile(OutFile), Options(Options), BinHolder(Options.Verbose) {} - - /// Link the contents of the DebugMap. - bool link(const DebugMap &); - - void reportWarning(const Twine &Warning, - const DWARFDie *DIE = nullptr) const; - -private: - /// Called at the start of a debug object link. - void startDebugObject(DWARFContext &, DebugMapObject &); - - /// Called at the end of a debug object link. - void endDebugObject(); - - /// Remembers the newest DWARF version we've seen in a unit. - void maybeUpdateMaxDwarfVersion(unsigned Version) { - if (MaxDwarfVersion < Version) - MaxDwarfVersion = Version; - } - - /// Keeps track of relocations. - class RelocationManager { - struct ValidReloc { - uint32_t Offset; - uint32_t Size; - uint64_t Addend; - const DebugMapObject::DebugMapEntry *Mapping; - - ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, - const DebugMapObject::DebugMapEntry *Mapping) - : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} - - bool operator<(const ValidReloc &RHS) const { - return Offset < RHS.Offset; - } - }; - - DwarfLinker &Linker; - - /// The valid relocations for the current DebugMapObject. - /// This vector is sorted by relocation offset. - std::vector<ValidReloc> ValidRelocs; - - /// Index into ValidRelocs of the next relocation to - /// consider. As we walk the DIEs in acsending file offset and as - /// ValidRelocs is sorted by file offset, keeping this index - /// uptodate is all we have to do to have a cheap lookup during the - /// root DIE selection and during DIE cloning. - unsigned NextValidReloc = 0; - - public: - RelocationManager(DwarfLinker &Linker) : Linker(Linker) {} - - bool hasValidRelocs() const { return !ValidRelocs.empty(); } - - /// Reset the NextValidReloc counter. - void resetValidRelocs() { NextValidReloc = 0; } - - /// \defgroup FindValidRelocations Translate debug map into a list - /// of relevant relocations - /// - /// @{ - bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, - const DebugMapObject &DMO); - - bool findValidRelocs(const object::SectionRef &Section, - const object::ObjectFile &Obj, - const DebugMapObject &DMO); - - void findValidRelocsMachO(const object::SectionRef &Section, - const object::MachOObjectFile &Obj, - const DebugMapObject &DMO); - /// @} - - bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, - CompileUnit::DIEInfo &Info); - - bool applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset, - bool isLittleEndian); - }; - - /// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries. - /// - /// @{ - /// Recursively walk the \p DIE tree and look for DIEs to - /// keep. Store that information in \p CU's DIEInfo. - /// - /// The return value indicates whether the DIE is incomplete. - bool lookForDIEsToKeep(RelocationManager &RelocMgr, const DWARFDie &DIE, - const DebugMapObject &DMO, CompileUnit &CU, - unsigned Flags); - - /// If this compile unit is really a skeleton CU that points to a - /// clang module, register it in ClangModules and return true. - /// - /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name - /// pointing to the module, and a DW_AT_gnu_dwo_id with the module - /// hash. - bool registerModuleReference(const DWARFDie &CUDie, - const DWARFUnit &Unit, DebugMap &ModuleMap, - unsigned Indent = 0); - - /// Recursively add the debug info in this clang module .pcm - /// file (and all the modules imported by it in a bottom-up fashion) - /// to Units. - Error loadClangModule(StringRef Filename, StringRef ModulePath, - StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, unsigned Indent = 0); - - /// Flags passed to DwarfLinker::lookForDIEsToKeep - enum TravesalFlags { - TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. - TF_InFunctionScope = 1 << 1, ///< Current scope is a fucntion scope. - TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. - TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. - TF_ODR = 1 << 4, ///< Use the ODR whhile keeping dependants. - TF_SkipPC = 1 << 5, ///< Skip all location attributes. - }; - - /// Mark the passed DIE as well as all the ones it depends on as kept. - void keepDIEAndDependencies(RelocationManager &RelocMgr, - const DWARFDie &DIE, - CompileUnit::DIEInfo &MyInfo, - const DebugMapObject &DMO, CompileUnit &CU, - bool UseODR); - - unsigned shouldKeepDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, - CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, - unsigned Flags); - - unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, - CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, unsigned Flags); - - unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, - CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, - unsigned Flags); - - bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, - CompileUnit::DIEInfo &Info); - /// @} - - /// \defgroup Linking Methods used to link the debug information - /// - /// @{ - - class DIECloner { - DwarfLinker &Linker; - RelocationManager &RelocMgr; - - /// Allocator used for all the DIEValue objects. - BumpPtrAllocator &DIEAlloc; - - std::vector<std::unique_ptr<CompileUnit>> &CompileUnits; - LinkOptions Options; - - public: - DIECloner(DwarfLinker &Linker, RelocationManager &RelocMgr, - BumpPtrAllocator &DIEAlloc, - std::vector<std::unique_ptr<CompileUnit>> &CompileUnits, - LinkOptions &Options) - : Linker(Linker), RelocMgr(RelocMgr), DIEAlloc(DIEAlloc), - CompileUnits(CompileUnits), Options(Options) {} - - /// Recursively clone \p InputDIE into an tree of DIE objects - /// where useless (as decided by lookForDIEsToKeep()) bits have been - /// stripped out and addresses have been rewritten according to the - /// debug map. - /// - /// \param OutOffset is the offset the cloned DIE in the output - /// compile unit. - /// \param PCOffset (while cloning a function scope) is the offset - /// applied to the entry point of the function to get the linked address. - /// \param Die the output DIE to use, pass NULL to create one. - /// \returns the root of the cloned tree or null if nothing was selected. - DIE *cloneDIE(const DWARFDie &InputDIE, CompileUnit &U, - int64_t PCOffset, uint32_t OutOffset, unsigned Flags, - DIE *Die = nullptr); - - /// Construct the output DIE tree by cloning the DIEs we - /// chose to keep above. If there are no valid relocs, then there's - /// nothing to clone/emit. - void cloneAllCompileUnits(DWARFContext &DwarfContext); - - private: - using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; - - /// Information gathered and exchanged between the various - /// clone*Attributes helpers about the attributes of a particular DIE. - struct AttributesInfo { - /// Names. - const char *Name = nullptr; - const char *MangledName = nullptr; - - /// Offsets in the string pool. - uint32_t NameOffset = 0; - uint32_t MangledNameOffset = 0; - - /// Value of AT_low_pc in the input DIE - uint64_t OrigLowPc = std::numeric_limits<uint64_t>::max(); - - /// Value of AT_high_pc in the input DIE - uint64_t OrigHighPc = 0; - - /// Offset to apply to PC addresses inside a function. - int64_t PCOffset = 0; - - /// Does the DIE have a low_pc attribute? - bool HasLowPc = false; - - /// Is this DIE only a declaration? - bool IsDeclaration = false; - - AttributesInfo() = default; - }; - - /// Helper for cloneDIE. - unsigned cloneAttribute(DIE &Die, - const DWARFDie &InputDIE, - CompileUnit &U, const DWARFFormValue &Val, - const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &AttrInfo); - - /// Clone a string attribute described by \p AttrSpec and add - /// it to \p Die. - /// \returns the size of the new attribute. - unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const DWARFUnit &U); - - /// Clone an attribute referencing another DIE and add - /// it to \p Die. - /// \returns the size of the new attribute. - unsigned - cloneDieReferenceAttribute(DIE &Die, - const DWARFDie &InputDIE, - AttributeSpec AttrSpec, unsigned AttrSize, - const DWARFFormValue &Val, CompileUnit &Unit); - - /// Clone an attribute referencing another DIE and add - /// it to \p Die. - /// \returns the size of the new attribute. - unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize); - - /// Clone an attribute referencing another DIE and add - /// it to \p Die. - /// \returns the size of the new attribute. - unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const CompileUnit &Unit, - AttributesInfo &Info); - - /// Clone a scalar attribute and add it to \p Die. - /// \returns the size of the new attribute. - unsigned cloneScalarAttribute(DIE &Die, - const DWARFDie &InputDIE, - CompileUnit &U, AttributeSpec AttrSpec, - const DWARFFormValue &Val, unsigned AttrSize, - AttributesInfo &Info); - - /// Get the potential name and mangled name for the entity - /// described by \p Die and store them in \Info if they are not - /// already there. - /// \returns is a name was found. - bool getDIENames(const DWARFDie &Die, AttributesInfo &Info); - - /// Create a copy of abbreviation Abbrev. - void copyAbbrev(const DWARFAbbreviationDeclaration &Abbrev, bool hasODR); - }; - - /// Assign an abbreviation number to \p Abbrev - void AssignAbbrev(DIEAbbrev &Abbrev); - - /// Compute and emit debug_ranges section for \p Unit, and - /// patch the attributes referencing it. - void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf) const; - - /// Generate and emit the DW_AT_ranges attribute for a - /// compile_unit if it had one. - void generateUnitRanges(CompileUnit &Unit) const; - - /// Extract the line tables fromt he original dwarf, extract - /// the relevant parts according to the linked function ranges and - /// emit the result in the debug_line section. - void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf); - - /// Emit the accelerator entries for \p Unit. - void emitAcceleratorEntriesForUnit(CompileUnit &Unit); - - /// Patch the frame info for an object file and emit it. - void patchFrameInfoForObject(const DebugMapObject &, DWARFContext &, - unsigned AddressSize); - - /// FoldingSet that uniques the abbreviations. - FoldingSet<DIEAbbrev> AbbreviationsSet; - - /// Storage for the unique Abbreviations. - /// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot - /// be changed to a vecot of unique_ptrs. - std::vector<std::unique_ptr<DIEAbbrev>> Abbreviations; - - /// DIELoc objects that need to be destructed (but not freed!). - std::vector<DIELoc *> DIELocs; - - /// DIEBlock objects that need to be destructed (but not freed!). - std::vector<DIEBlock *> DIEBlocks; - - /// Allocator used for all the DIEValue objects. - BumpPtrAllocator DIEAlloc; - /// @} - - /// ODR Contexts for that link. - DeclContextTree ODRContexts; - - /// \defgroup Helpers Various helper methods. - /// - /// @{ - bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); - - /// Attempt to load a debug object from disk. - ErrorOr<const object::ObjectFile &> loadObject(BinaryHolder &BinaryHolder, - DebugMapObject &Obj, - const DebugMap &Map); - /// @} - - raw_fd_ostream &OutFile; - LinkOptions Options; - BinaryHolder BinHolder; - std::unique_ptr<DwarfStreamer> Streamer; - uint64_t OutputDebugInfoSize; - - /// A unique ID that identifies each compile unit. - unsigned UnitID; - - unsigned MaxDwarfVersion = 0; - - /// The units of the current debug map object. - std::vector<std::unique_ptr<CompileUnit>> Units; - - /// The debug map object currently under consideration. - DebugMapObject *CurrentDebugObject; - - /// The Dwarf string pool. - NonRelocatableStringpool StringPool; - - /// This map is keyed by the entry PC of functions in that - /// debug object and the associated value is a pair storing the - /// corresponding end PC and the offset to apply to get the linked - /// address. - /// - /// See startDebugObject() for a more complete description of its use. - std::map<uint64_t, std::pair<uint64_t, int64_t>> Ranges; - - /// The CIEs that have been emitted in the output - /// section. The actual CIE data serves a the key to this StringMap, - /// this takes care of comparing the semantics of CIEs defined in - /// different object files. - StringMap<uint32_t> EmittedCIEs; - - /// Offset of the last CIE that has been emitted in the output - /// debug_frame section. - uint32_t LastCIEOffset = 0; - - /// Mapping the PCM filename to the DwoId. - StringMap<uint64_t> ClangModules; - - bool ModuleCacheHintDisplayed = false; - bool ArchiveHintDisplayed = false; -}; - -} // end anonymous namespace - /// Similar to DWARFUnitSection::getUnitForOffset(), but returning our /// CompileUnit object instead. -static CompileUnit *getUnitForOffset( - std::vector<std::unique_ptr<CompileUnit>> &Units, unsigned Offset) { - auto CU = - std::upper_bound(Units.begin(), Units.end(), Offset, - [](uint32_t LHS, const std::unique_ptr<CompileUnit> &RHS) { - return LHS < RHS->getOrigUnit().getNextUnitOffset(); - }); +static CompileUnit *getUnitForOffset(const UnitListTy &Units, unsigned Offset) { + auto CU = std::upper_bound( + Units.begin(), Units.end(), Offset, + [](uint32_t LHS, const std::unique_ptr<CompileUnit> &RHS) { + return LHS < RHS->getOrigUnit().getNextUnitOffset(); + }); return CU != Units.end() ? CU->get() : nullptr; } -/// Resolve the DIE attribute reference that has been -/// extracted in \p RefValue. The resulting DIE migh be in another -/// CompileUnit which is stored into \p ReferencedCU. -/// \returns null if resolving fails for any reason. -static DWARFDie resolveDIEReference( - const DwarfLinker &Linker, std::vector<std::unique_ptr<CompileUnit>> &Units, - const DWARFFormValue &RefValue, const DWARFUnit &Unit, - const DWARFDie &DIE, CompileUnit *&RefCU) { +/// Resolve the DIE attribute reference that has been extracted in \p RefValue. +/// The resulting DIE might be in another CompileUnit which is stored into \p +/// ReferencedCU. \returns null if resolving fails for any reason. +static DWARFDie resolveDIEReference(const DwarfLinker &Linker, + const DebugMapObject &DMO, + const UnitListTy &Units, + const DWARFFormValue &RefValue, + const DWARFUnit &Unit, const DWARFDie &DIE, + CompileUnit *&RefCU) { assert(RefValue.isFormClass(DWARFFormValue::FC_Reference)); uint64_t RefOffset = *RefValue.getAsReference(); @@ -1602,16 +131,16 @@ static DWARFDie resolveDIEReference( if (const auto RefDie = RefCU->getOrigUnit().getDIEForOffset(RefOffset)) { // In a file with broken references, an attribute might point to a NULL // DIE. - if(!RefDie.isNULL()) + if (!RefDie.isNULL()) return RefDie; } - Linker.reportWarning("could not find referenced DIE", &DIE); + Linker.reportWarning("could not find referenced DIE", DMO, &DIE); return DWARFDie(); } -/// \returns whether the passed \a Attr type might contain a DIE -/// reference suitable for ODR uniquing. +/// \returns whether the passed \a Attr type might contain a DIE reference +/// suitable for ODR uniquing. static bool isODRAttribute(uint16_t Attr) { switch (Attr) { default: @@ -1626,227 +155,41 @@ static bool isODRAttribute(uint16_t Attr) { llvm_unreachable("Improper attribute."); } -/// Set the last DIE/CU a context was seen in and, possibly invalidate -/// the context if it is ambiguous. -/// -/// In the current implementation, we don't handle overloaded -/// functions well, because the argument types are not taken into -/// account when computing the DeclContext tree. -/// -/// Some of this is mitigated byt using mangled names that do contain -/// the arguments types, but sometimes (eg. with function templates) -/// we don't have that. In that case, just do not unique anything that -/// refers to the contexts we are not able to distinguish. -/// -/// If a context that is not a namespace appears twice in the same CU, -/// we know it is ambiguous. Make it invalid. -bool DeclContext::setLastSeenDIE(CompileUnit &U, - const DWARFDie &Die) { - if (LastSeenCompileUnitID == U.getUniqueID()) { - DWARFUnit &OrigUnit = U.getOrigUnit(); - uint32_t FirstIdx = OrigUnit.getDIEIndex(LastSeenDIE); - U.getInfo(FirstIdx).Ctxt = nullptr; +bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die, + AttributesInfo &Info, + OffsetsStringPool &StringPool, + bool StripTemplate) { + // This function will be called on DIEs having low_pcs and + // ranges. As getting the name might be more expansive, filter out + // blocks directly. + if (Die.getTag() == dwarf::DW_TAG_lexical_block) return false; - } - LastSeenCompileUnitID = U.getUniqueID(); - LastSeenDIE = Die; - return true; -} - -PointerIntPair<DeclContext *, 1> DeclContextTree::getChildDeclContext( - DeclContext &Context, const DWARFDie &DIE, CompileUnit &U, - NonRelocatableStringpool &StringPool, bool InClangModule) { - unsigned Tag = DIE.getTag(); - - // FIXME: dsymutil-classic compat: We should bail out here if we - // have a specification or an abstract_origin. We will get the - // parent context wrong here. - - switch (Tag) { - default: - // By default stop gathering child contexts. - return PointerIntPair<DeclContext *, 1>(nullptr); - case dwarf::DW_TAG_module: - break; - case dwarf::DW_TAG_compile_unit: - return PointerIntPair<DeclContext *, 1>(&Context); - case dwarf::DW_TAG_subprogram: - // Do not unique anything inside CU local functions. - if ((Context.getTag() == dwarf::DW_TAG_namespace || - Context.getTag() == dwarf::DW_TAG_compile_unit) && - !dwarf::toUnsigned(DIE.find(dwarf::DW_AT_external), 0)) - return PointerIntPair<DeclContext *, 1>(nullptr); - LLVM_FALLTHROUGH; - case dwarf::DW_TAG_member: - case dwarf::DW_TAG_namespace: - case dwarf::DW_TAG_structure_type: - case dwarf::DW_TAG_class_type: - case dwarf::DW_TAG_union_type: - case dwarf::DW_TAG_enumeration_type: - case dwarf::DW_TAG_typedef: - // Artificial things might be ambiguous, because they might be - // created on demand. For example implicitely defined constructors - // are ambiguous because of the way we identify contexts, and they - // won't be generated everytime everywhere. - if (dwarf::toUnsigned(DIE.find(dwarf::DW_AT_artificial), 0)) - return PointerIntPair<DeclContext *, 1>(nullptr); - break; - } - - const char *Name = DIE.getName(DINameKind::LinkageName); - const char *ShortName = DIE.getName(DINameKind::ShortName); - StringRef NameRef; - StringRef ShortNameRef; - StringRef FileRef; - - if (Name) - NameRef = StringPool.internString(Name); - else if (Tag == dwarf::DW_TAG_namespace) - // FIXME: For dsymutil-classic compatibility. I think uniquing - // within anonymous namespaces is wrong. There is no ODR guarantee - // there. - NameRef = StringPool.internString("(anonymous namespace)"); - - if (ShortName && ShortName != Name) - ShortNameRef = StringPool.internString(ShortName); - else - ShortNameRef = NameRef; - - if (Tag != dwarf::DW_TAG_class_type && Tag != dwarf::DW_TAG_structure_type && - Tag != dwarf::DW_TAG_union_type && - Tag != dwarf::DW_TAG_enumeration_type && NameRef.empty()) - return PointerIntPair<DeclContext *, 1>(nullptr); - - unsigned Line = 0; - unsigned ByteSize = std::numeric_limits<uint32_t>::max(); - - if (!InClangModule) { - // Gather some discriminating data about the DeclContext we will be - // creating: File, line number and byte size. This shouldn't be - // necessary, because the ODR is just about names, but given that we - // do some approximations with overloaded functions and anonymous - // namespaces, use these additional data points to make the process - // safer. This is disabled for clang modules, because forward - // declarations of module-defined types do not have a file and line. - ByteSize = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_byte_size), - std::numeric_limits<uint64_t>::max()); - if (Tag != dwarf::DW_TAG_namespace || !Name) { - if (unsigned FileNum = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_file), 0)) { - if (const auto *LT = U.getOrigUnit().getContext().getLineTableForUnit( - &U.getOrigUnit())) { - // FIXME: dsymutil-classic compatibility. I'd rather not - // unique anything in anonymous namespaces, but if we do, then - // verify that the file and line correspond. - if (!Name && Tag == dwarf::DW_TAG_namespace) - FileNum = 1; - - // FIXME: Passing U.getOrigUnit().getCompilationDir() - // instead of "" would allow more uniquing, but for now, do - // it this way to match dsymutil-classic. - if (LT->hasFileAtIndex(FileNum)) { - Line = dwarf::toUnsigned(DIE.find(dwarf::DW_AT_decl_line), 0); - // Cache the resolved paths, because calling realpath is expansive. - StringRef ResolvedPath = U.getResolvedPath(FileNum); - if (!ResolvedPath.empty()) { - FileRef = ResolvedPath; - } else { - std::string File; - bool gotFileName = - LT->getFileNameByIndex(FileNum, "", - DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath, - File); - (void)gotFileName; - assert(gotFileName && "Must get file name from line table"); -#ifdef HAVE_REALPATH - char RealPath[PATH_MAX + 1]; - RealPath[PATH_MAX] = 0; - if (::realpath(File.c_str(), RealPath)) - File = RealPath; -#endif - FileRef = StringPool.internString(File); - U.setResolvedPath(FileNum, FileRef); - } - } - } - } - } - } - - if (!Line && NameRef.empty()) - return PointerIntPair<DeclContext *, 1>(nullptr); - - // We hash NameRef, which is the mangled name, in order to get most - // overloaded functions resolve correctly. - // - // Strictly speaking, hashing the Tag is only necessary for a - // DW_TAG_module, to prevent uniquing of a module and a namespace - // with the same name. - // - // FIXME: dsymutil-classic won't unique the same type presented - // once as a struct and once as a class. Using the Tag in the fully - // qualified name hash to get the same effect. - unsigned Hash = hash_combine(Context.getQualifiedNameHash(), Tag, NameRef); - - // FIXME: dsymutil-classic compatibility: when we don't have a name, - // use the filename. - if (Tag == dwarf::DW_TAG_namespace && NameRef == "(anonymous namespace)") - Hash = hash_combine(Hash, FileRef); - - // Now look if this context already exists. - DeclContext Key(Hash, Line, ByteSize, Tag, NameRef, FileRef, Context); - auto ContextIter = Contexts.find(&Key); - - if (ContextIter == Contexts.end()) { - // The context wasn't found. - bool Inserted; - DeclContext *NewContext = - new (Allocator) DeclContext(Hash, Line, ByteSize, Tag, NameRef, FileRef, - Context, DIE, U.getUniqueID()); - std::tie(ContextIter, Inserted) = Contexts.insert(NewContext); - assert(Inserted && "Failed to insert DeclContext"); - (void)Inserted; - } else if (Tag != dwarf::DW_TAG_namespace && - !(*ContextIter)->setLastSeenDIE(U, DIE)) { - // The context was found, but it is ambiguous with another context - // in the same file. Mark it invalid. - return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1); - } - - assert(ContextIter != Contexts.end()); - // FIXME: dsymutil-classic compatibility. Union types aren't - // uniques, but their children might be. - if ((Tag == dwarf::DW_TAG_subprogram && - Context.getTag() != dwarf::DW_TAG_structure_type && - Context.getTag() != dwarf::DW_TAG_class_type) || - (Tag == dwarf::DW_TAG_union_type)) - return PointerIntPair<DeclContext *, 1>(*ContextIter, /* Invalid= */ 1); - - return PointerIntPair<DeclContext *, 1>(*ContextIter); -} - -bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die, - AttributesInfo &Info) { // FIXME: a bit wasteful as the first getName might return the // short name. - if (!Info.MangledName && - (Info.MangledName = Die.getName(DINameKind::LinkageName))) - Info.MangledNameOffset = - Linker.StringPool.getStringOffset(Info.MangledName); + if (!Info.MangledName) + if (const char *MangledName = Die.getName(DINameKind::LinkageName)) + Info.MangledName = StringPool.getEntry(MangledName); - if (!Info.Name && (Info.Name = Die.getName(DINameKind::ShortName))) - Info.NameOffset = Linker.StringPool.getStringOffset(Info.Name); + if (!Info.Name) + if (const char *Name = Die.getName(DINameKind::ShortName)) + Info.Name = StringPool.getEntry(Name); + + if (StripTemplate && Info.Name && Info.MangledName != Info.Name) { + // FIXME: dsymutil compatibility. This is wrong for operator< + auto Split = Info.Name.getString().split('<'); + if (!Split.second.empty()) + Info.NameWithoutTemplate = StringPool.getEntry(Split.first); + } return Info.Name || Info.MangledName; } -/// Report a warning to the user, optionaly including -/// information about a specific \p DIE related to the warning. -void DwarfLinker::reportWarning(const Twine &Warning, +/// Report a warning to the user, optionally including information about a +/// specific \p DIE related to the warning. +void DwarfLinker::reportWarning(const Twine &Warning, const DebugMapObject &DMO, const DWARFDie *DIE) const { - StringRef Context = "<debug map>"; - if (CurrentDebugObject) - Context = CurrentDebugObject->getObjectFilename(); + StringRef Context = DMO.getObjectFilename(); warn(Warning, Context); if (!Options.Verbose || !DIE) @@ -1856,7 +199,7 @@ void DwarfLinker::reportWarning(const Twine &Warning, DumpOpts.RecurseDepth = 0; DumpOpts.Verbose = Options.Verbose; - errs() << " in DIE:\n"; + WithColor::note() << " in DIE:\n"; DIE->dump(errs(), 6 /* Indent */, DumpOpts); } @@ -1865,7 +208,7 @@ bool DwarfLinker::createStreamer(const Triple &TheTriple, if (Options.NoOutput) return true; - Streamer = llvm::make_unique<DwarfStreamer>(OutFile); + Streamer = llvm::make_unique<DwarfStreamer>(OutFile, Options); return Streamer->init(TheTriple); } @@ -1875,10 +218,9 @@ bool DwarfLinker::createStreamer(const Triple &TheTriple, /// \return true when this DIE and all of its children are only /// forward declarations to types defined in external clang modules /// (i.e., forward declarations that are children of a DW_TAG_module). -static bool analyzeContextInfo(const DWARFDie &DIE, - unsigned ParentIdx, CompileUnit &CU, - DeclContext *CurrentDeclContext, - NonRelocatableStringpool &StringPool, +static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx, + CompileUnit &CU, DeclContext *CurrentDeclContext, + UniquingStringPool &StringPool, DeclContextTree &Contexts, bool InImportedModule = false) { unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); @@ -1919,16 +261,15 @@ static bool analyzeContextInfo(const DWARFDie &DIE, Info.Prune = InImportedModule; if (DIE.hasChildren()) - for (auto Child: DIE.children()) + for (auto Child : DIE.children()) Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, StringPool, Contexts, InImportedModule); // Prune this DIE if it is either a forward declaration inside a // DW_TAG_module or a DW_TAG_module that contains nothing but // forward declarations. - Info.Prune &= - (DIE.getTag() == dwarf::DW_TAG_module) || - dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0); + Info.Prune &= (DIE.getTag() == dwarf::DW_TAG_module) || + dwarf::toUnsigned(DIE.find(dwarf::DW_AT_declaration), 0); // Don't prune it if there is no definition for the DIE. Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); @@ -1951,33 +292,35 @@ static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { llvm_unreachable("Invalid Tag"); } -void DwarfLinker::startDebugObject(DWARFContext &Dwarf, DebugMapObject &Obj) { +void DwarfLinker::startDebugObject(LinkContext &Context) { // Iterate over the debug map entries and put all the ones that are - // functions (because they have a size) into the Ranges map. This - // map is very similar to the FunctionRanges that are stored in each - // unit, with 2 notable differences: - // - obviously this one is global, while the other ones are per-unit. - // - this one contains not only the functions described in the DIE - // tree, but also the ones that are only in the debug map. - // The latter information is required to reproduce dsymutil's logic - // while linking line tables. The cases where this information - // matters look like bugs that need to be investigated, but for now - // we need to reproduce dsymutil's behavior. + // functions (because they have a size) into the Ranges map. This map is + // very similar to the FunctionRanges that are stored in each unit, with 2 + // notable differences: + // + // 1. Obviously this one is global, while the other ones are per-unit. + // + // 2. This one contains not only the functions described in the DIE + // tree, but also the ones that are only in the debug map. + // + // The latter information is required to reproduce dsymutil's logic while + // linking line tables. The cases where this information matters look like + // bugs that need to be investigated, but for now we need to reproduce + // dsymutil's behavior. // FIXME: Once we understood exactly if that information is needed, // maybe totally remove this (or try to use it to do a real // -gline-tables-only on Darwin. - for (const auto &Entry : Obj.symbols()) { + for (const auto &Entry : Context.DMO.symbols()) { const auto &Mapping = Entry.getValue(); if (Mapping.Size && Mapping.ObjectAddress) - Ranges[*Mapping.ObjectAddress] = std::make_pair( + Context.Ranges[*Mapping.ObjectAddress] = DebugMapObjectRange( *Mapping.ObjectAddress + Mapping.Size, int64_t(Mapping.BinaryAddress) - *Mapping.ObjectAddress); } } -void DwarfLinker::endDebugObject() { - Units.clear(); - Ranges.clear(); +void DwarfLinker::endDebugObject(LinkContext &Context) { + Context.Clear(); for (auto I = DIEBlocks.begin(), E = DIEBlocks.end(); I != E; ++I) (*I)->~DIEBlock(); @@ -2012,10 +355,9 @@ static bool isMachOPairedReloc(uint64_t RelocType, uint64_t Arch) { /// Iterate over the relocations of the given \p Section and /// store the ones that correspond to debug map entries into the /// ValidRelocs array. -void DwarfLinker::RelocationManager:: -findValidRelocsMachO(const object::SectionRef &Section, - const object::MachOObjectFile &Obj, - const DebugMapObject &DMO) { +void DwarfLinker::RelocationManager::findValidRelocsMachO( + const object::SectionRef &Section, const object::MachOObjectFile &Obj, + const DebugMapObject &DMO) { StringRef Contents; Section.getContents(Contents); DataExtractor Data(Contents, Obj.isLittleEndian(), 0); @@ -2033,14 +375,16 @@ findValidRelocsMachO(const object::SectionRef &Section, if (isMachOPairedReloc(Obj.getAnyRelocationType(MachOReloc), Obj.getArch())) { SkipNext = true; - Linker.reportWarning(" unsupported relocation in debug_info section."); + Linker.reportWarning("unsupported relocation in debug_info section.", + DMO); continue; } unsigned RelocSize = 1 << Obj.getAnyRelocationLength(MachOReloc); uint64_t Offset64 = Reloc.getOffset(); if ((RelocSize != 4 && RelocSize != 8)) { - Linker.reportWarning(" unsupported relocation in debug_info section."); + Linker.reportWarning("unsupported relocation in debug_info section.", + DMO); continue; } uint32_t Offset = Offset64; @@ -2065,15 +409,15 @@ findValidRelocsMachO(const object::SectionRef &Section, Expected<StringRef> SymbolName = Sym->getName(); if (!SymbolName) { consumeError(SymbolName.takeError()); - Linker.reportWarning("error getting relocation symbol name."); + Linker.reportWarning("error getting relocation symbol name.", DMO); continue; } if (const auto *Mapping = DMO.lookupSymbol(*SymbolName)) ValidRelocs.emplace_back(Offset64, RelocSize, Addend, Mapping); } else if (const auto *Mapping = DMO.lookupObjectAddress(SymAddress)) { - // Do not store the addend. The addend was the address of the - // symbol in the object file, the address in the binary that is - // stored in the debug map doesn't need to be offseted. + // Do not store the addend. The addend was the address of the symbol in + // the object file, the address in the binary that is stored in the debug + // map doesn't need to be offset. ValidRelocs.emplace_back(Offset64, RelocSize, SymOffset, Mapping); } } @@ -2088,8 +432,8 @@ bool DwarfLinker::RelocationManager::findValidRelocs( if (auto *MachOObj = dyn_cast<object::MachOObjectFile>(&Obj)) findValidRelocsMachO(Section, *MachOObj, DMO); else - Linker.reportWarning(Twine("unsupported object file type: ") + - Obj.getFileName()); + Linker.reportWarning( + Twine("unsupported object file type: ") + Obj.getFileName(), DMO); if (ValidRelocs.empty()) return false; @@ -2098,7 +442,7 @@ bool DwarfLinker::RelocationManager::findValidRelocs( // the file, this allows us to just keep an index in the relocation // array that we advance during our walk, rather than resorting to // some associative container. See DwarfLinker::NextValidReloc. - std::sort(ValidRelocs.begin(), ValidRelocs.end()); + llvm::sort(ValidRelocs.begin(), ValidRelocs.end()); return true; } @@ -2106,10 +450,9 @@ bool DwarfLinker::RelocationManager::findValidRelocs( /// entries in the debug map. These relocations will drive the Dwarf /// link by indicating which DIEs refer to symbols present in the /// linked binary. -/// \returns wether there are any valid relocations in the debug info. -bool DwarfLinker::RelocationManager:: -findValidRelocsInDebugInfo(const object::ObjectFile &Obj, - const DebugMapObject &DMO) { +/// \returns whether there are any valid relocations in the debug info. +bool DwarfLinker::RelocationManager::findValidRelocsInDebugInfo( + const object::ObjectFile &Obj, const DebugMapObject &DMO) { // Find the debug_info section. for (const object::SectionRef &Section : Obj.sections()) { StringRef SectionName; @@ -2128,9 +471,8 @@ findValidRelocsInDebugInfo(const object::ObjectFile &Obj, /// This function must be called with offsets in strictly ascending /// order because it never looks back at relocations it already 'went past'. /// \returns true and sets Info.InDebugMap if it is the case. -bool DwarfLinker::RelocationManager:: -hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, - CompileUnit::DIEInfo &Info) { +bool DwarfLinker::RelocationManager::hasValidRelocation( + uint32_t StartOffset, uint32_t EndOffset, CompileUnit::DIEInfo &Info) { assert(NextValidReloc == 0 || StartOffset > ValidRelocs[NextValidReloc - 1].Offset); if (NextValidReloc >= ValidRelocs.size()) @@ -2155,8 +497,9 @@ hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, : std::numeric_limits<uint64_t>::max(); if (Linker.Options.Verbose) outs() << "Found valid debug map entry: " << ValidReloc.Mapping->getKey() - << " " << format("\t%016" PRIx64 " => %016" PRIx64, ObjectAddress, - uint64_t(Mapping.BinaryAddress)); + << " " + << format("\t%016" PRIx64 " => %016" PRIx64, ObjectAddress, + uint64_t(Mapping.BinaryAddress)); Info.AddrAdjust = int64_t(Mapping.BinaryAddress) + ValidReloc.Addend; if (Mapping.ObjectAddress) @@ -2235,9 +578,9 @@ unsigned DwarfLinker::shouldKeepVariableDIE(RelocationManager &RelocMgr, /// Check if a function describing DIE should be kept. /// \returns updated TraversalFlags. unsigned DwarfLinker::shouldKeepSubprogramDIE( - RelocationManager &RelocMgr, - const DWARFDie &DIE, CompileUnit &Unit, - CompileUnit::DIEInfo &MyInfo, unsigned Flags) { + RelocationManager &RelocMgr, RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags) { const auto *Abbrev = DIE.getAbbreviationDeclarationPtr(); Flags |= TF_InFunctionScope; @@ -2247,7 +590,7 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE( return Flags; uint32_t Offset = DIE.getOffset() + getULEB128Size(Abbrev->getCode()); - const DWARFUnit &OrigUnit = Unit.getOrigUnit(); + DWARFUnit &OrigUnit = Unit.getOrigUnit(); uint32_t LowPcOffset, LowPcEndOffset; std::tie(LowPcOffset, LowPcEndOffset) = getAttributeOffsets(Abbrev, *LowPcIdx, Offset, OrigUnit); @@ -2265,17 +608,31 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE( DIE.dump(outs(), 8 /* Indent */, DumpOpts); } + if (DIE.getTag() == dwarf::DW_TAG_label) { + if (Unit.hasLabelAt(*LowPc)) + return Flags; + // FIXME: dsymutil-classic compat. dsymutil-classic doesn't consider labels + // that don't fall into the CU's aranges. This is wrong IMO. Debug info + // generation bugs aside, this is really wrong in the case of labels, where + // a label marking the end of a function will have a PC == CU's high_pc. + if (dwarf::toAddress(OrigUnit.getUnitDIE().find(dwarf::DW_AT_high_pc)) + .getValueOr(UINT64_MAX) <= LowPc) + return Flags; + Unit.addLabelLowPc(*LowPc, MyInfo.AddrAdjust); + return Flags | TF_Keep; + } + Flags |= TF_Keep; Optional<uint64_t> HighPc = DIE.getHighPC(*LowPc); if (!HighPc) { - reportWarning("Function without high_pc. Range will be discarded.\n", + reportWarning("Function without high_pc. Range will be discarded.\n", DMO, &DIE); return Flags; } // Replace the debug map range with a more accurate one. - Ranges[*LowPc] = std::make_pair(*HighPc, MyInfo.AddrAdjust); + Ranges[*LowPc] = DebugMapObjectRange(*HighPc, MyInfo.AddrAdjust); Unit.addFunctionRange(*LowPc, *HighPc, MyInfo.AddrAdjust); return Flags; } @@ -2283,7 +640,8 @@ unsigned DwarfLinker::shouldKeepSubprogramDIE( /// Check if a DIE should be kept. /// \returns updated TraversalFlags. unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr, - const DWARFDie &DIE, + RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, unsigned Flags) { @@ -2292,7 +650,9 @@ unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr, case dwarf::DW_TAG_variable: return shouldKeepVariableDIE(RelocMgr, DIE, Unit, MyInfo, Flags); case dwarf::DW_TAG_subprogram: - return shouldKeepSubprogramDIE(RelocMgr, DIE, Unit, MyInfo, Flags); + case dwarf::DW_TAG_label: + return shouldKeepSubprogramDIE(RelocMgr, Ranges, DIE, DMO, Unit, MyInfo, + Flags); case dwarf::DW_TAG_imported_module: case dwarf::DW_TAG_imported_declaration: case dwarf::DW_TAG_imported_unit: @@ -2313,11 +673,10 @@ unsigned DwarfLinker::shouldKeepDIE(RelocationManager &RelocMgr, /// back to lookForDIEsToKeep while adding TF_DependencyWalk to the /// TraversalFlags to inform it that it's not doing the primary DIE /// tree walk. -void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, - const DWARFDie &Die, - CompileUnit::DIEInfo &MyInfo, - const DebugMapObject &DMO, - CompileUnit &CU, bool UseODR) { +void DwarfLinker::keepDIEAndDependencies( + RelocationManager &RelocMgr, RangesTy &Ranges, const UnitListTy &Units, + const DWARFDie &Die, CompileUnit::DIEInfo &MyInfo, + const DebugMapObject &DMO, CompileUnit &CU, bool UseODR) { DWARFUnit &Unit = CU.getOrigUnit(); MyInfo.Keep = true; @@ -2330,7 +689,8 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, unsigned AncestorIdx = MyInfo.ParentIdx; while (!CU.getInfo(AncestorIdx).Keep) { unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, Unit.getDIEAtIndex(AncestorIdx), DMO, CU, + lookForDIEsToKeep(RelocMgr, Ranges, Units, Unit.getDIEAtIndex(AncestorIdx), + DMO, CU, TF_ParentWalk | TF_Keep | TF_DependencyWalk | ODRFlag); AncestorIdx = CU.getInfo(AncestorIdx).ParentIdx; } @@ -2345,7 +705,8 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, for (const auto &AttrSpec : Abbrev->attributes()) { DWARFFormValue Val(AttrSpec.Form); - if (!Val.isFormClass(DWARFFormValue::FC_Reference)) { + if (!Val.isFormClass(DWARFFormValue::FC_Reference) || + AttrSpec.Attr == dwarf::DW_AT_sibling) { DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, Unit.getFormParams()); continue; @@ -2353,8 +714,8 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, Val.extractValue(Data, &Offset, Unit.getFormParams(), &Unit); CompileUnit *ReferencedCU; - if (auto RefDie = - resolveDIEReference(*this, Units, Val, Unit, Die, ReferencedCU)) { + if (auto RefDie = resolveDIEReference(*this, DMO, Units, Val, Unit, Die, + ReferencedCU)) { uint32_t RefIdx = ReferencedCU->getOrigUnit().getDIEIndex(RefDie); CompileUnit::DIEInfo &Info = ReferencedCU->getInfo(RefIdx); bool IsModuleRef = Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() && @@ -2379,7 +740,7 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, Info.Prune = false; unsigned ODRFlag = UseODR ? TF_ODR : 0; - lookForDIEsToKeep(RelocMgr, RefDie, DMO, *ReferencedCU, + lookForDIEsToKeep(RelocMgr, Ranges, Units, RefDie, DMO, *ReferencedCU, TF_Keep | TF_DependencyWalk | ODRFlag); // The incomplete property is propagated if the current DIE is complete @@ -2410,6 +771,7 @@ void DwarfLinker::keepDIEAndDependencies(RelocationManager &RelocMgr, /// /// The return value indicates whether the DIE is incomplete. bool DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, + RangesTy &Ranges, const UnitListTy &Units, const DWARFDie &Die, const DebugMapObject &DMO, CompileUnit &CU, unsigned Flags) { @@ -2428,12 +790,13 @@ bool DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, // We must not call shouldKeepDIE while called from keepDIEAndDependencies, // because it would screw up the relocation finding logic. if (!(Flags & TF_DependencyWalk)) - Flags = shouldKeepDIE(RelocMgr, Die, CU, MyInfo, Flags); + Flags = shouldKeepDIE(RelocMgr, Ranges, Die, DMO, CU, MyInfo, Flags); // If it is a newly kept DIE mark it as well as all its dependencies as kept. if (!AlreadyKept && (Flags & TF_Keep)) { bool UseOdr = (Flags & TF_DependencyWalk) ? (Flags & TF_ODR) : CU.hasODR(); - keepDIEAndDependencies(RelocMgr, Die, MyInfo, DMO, CU, UseOdr); + keepDIEAndDependencies(RelocMgr, Ranges, Units, Die, MyInfo, DMO, CU, + UseOdr); } // The TF_ParentWalk flag tells us that we are currently walking up // the parent chain of a required DIE, and we don't want to mark all @@ -2449,7 +812,8 @@ bool DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, bool Incomplete = false; for (auto Child : Die.children()) { - Incomplete |= lookForDIEsToKeep(RelocMgr, Child, DMO, CU, Flags); + Incomplete |= + lookForDIEsToKeep(RelocMgr, Ranges, Units, Child, DMO, CU, Flags); // If any of the members are incomplete we propagate the incompleteness. if (!MyInfo.Incomplete && Incomplete && @@ -2460,7 +824,7 @@ bool DwarfLinker::lookForDIEsToKeep(RelocationManager &RelocMgr, return MyInfo.Incomplete; } -/// Assign an abbreviation numer to \p Abbrev. +/// Assign an abbreviation number to \p Abbrev. /// /// Our DIEs get freed after every DebugMapObject has been processed, /// thus the FoldingSet we use to unique DIEAbbrevs cannot refer to @@ -2490,21 +854,29 @@ void DwarfLinker::AssignAbbrev(DIEAbbrev &Abbrev) { } } -unsigned DwarfLinker::DIECloner::cloneStringAttribute(DIE &Die, - AttributeSpec AttrSpec, - const DWARFFormValue &Val, - const DWARFUnit &U) { +unsigned DwarfLinker::DIECloner::cloneStringAttribute( + DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, + const DWARFUnit &U, OffsetsStringPool &StringPool, AttributesInfo &Info) { // Switch everything to out of line strings. const char *String = *Val.getAsCString(); - unsigned Offset = Linker.StringPool.getStringOffset(String); + auto StringEntry = StringPool.getEntry(String); + + // Update attributes info. + if (AttrSpec.Attr == dwarf::DW_AT_name) + Info.Name = StringEntry; + else if (AttrSpec.Attr == dwarf::DW_AT_MIPS_linkage_name || + AttrSpec.Attr == dwarf::DW_AT_linkage_name) + Info.MangledName = StringEntry; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::DW_FORM_strp, - DIEInteger(Offset)); + DIEInteger(StringEntry.getOffset())); + return 4; } unsigned DwarfLinker::DIECloner::cloneDieReferenceAttribute( - DIE &Die, const DWARFDie &InputDIE, - AttributeSpec AttrSpec, unsigned AttrSize, const DWARFFormValue &Val, + DIE &Die, const DWARFDie &InputDIE, AttributeSpec AttrSpec, + unsigned AttrSize, const DWARFFormValue &Val, const DebugMapObject &DMO, CompileUnit &Unit) { const DWARFUnit &U = Unit.getOrigUnit(); uint32_t Ref = *Val.getAsReference(); @@ -2512,11 +884,11 @@ unsigned DwarfLinker::DIECloner::cloneDieReferenceAttribute( CompileUnit *RefUnit = nullptr; DeclContext *Ctxt = nullptr; - DWARFDie RefDie = resolveDIEReference(Linker, CompileUnits, Val, U, InputDIE, - RefUnit); + DWARFDie RefDie = + resolveDIEReference(Linker, DMO, CompileUnits, Val, U, InputDIE, RefUnit); // If the referenced DIE is not found, drop the attribute. - if (!RefDie) + if (!RefDie || AttrSpec.Attr == dwarf::DW_AT_sibling) return 0; unsigned Idx = RefUnit->getOrigUnit().getDIEIndex(RefDie); @@ -2620,6 +992,15 @@ unsigned DwarfLinker::DIECloner::cloneAddressAttribute( DIE &Die, AttributeSpec AttrSpec, const DWARFFormValue &Val, const CompileUnit &Unit, AttributesInfo &Info) { uint64_t Addr = *Val.getAsAddress(); + + if (LLVM_UNLIKELY(Linker.Options.Update)) { + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) + Info.HasLowPc = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Addr)); + return Unit.getOrigUnit().getAddressByteSize(); + } + if (AttrSpec.Attr == dwarf::DW_AT_low_pc) { if (Die.getTag() == dwarf::DW_TAG_inlined_subroutine || Die.getTag() == dwarf::DW_TAG_lexical_block) @@ -2656,10 +1037,31 @@ unsigned DwarfLinker::DIECloner::cloneAddressAttribute( } unsigned DwarfLinker::DIECloner::cloneScalarAttribute( - DIE &Die, const DWARFDie &InputDIE, CompileUnit &Unit, - AttributeSpec AttrSpec, const DWARFFormValue &Val, unsigned AttrSize, - AttributesInfo &Info) { + DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, + CompileUnit &Unit, AttributeSpec AttrSpec, const DWARFFormValue &Val, + unsigned AttrSize, AttributesInfo &Info) { uint64_t Value; + + if (LLVM_UNLIKELY(Linker.Options.Update)) { + if (auto OptionalValue = Val.getAsUnsignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSignedConstant()) + Value = *OptionalValue; + else if (auto OptionalValue = Val.getAsSectionOffset()) + Value = *OptionalValue; + else { + Linker.reportWarning( + "Unsupported scalar attribute form. Dropping attribute.", DMO, + &InputDIE); + return 0; + } + if (AttrSpec.Attr == dwarf::DW_AT_declaration && Value) + Info.IsDeclaration = true; + Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), + dwarf::Form(AttrSpec.Form), DIEInteger(Value)); + return AttrSize; + } + if (AttrSpec.Attr == dwarf::DW_AT_high_pc && Die.getTag() == dwarf::DW_TAG_compile_unit) { if (Unit.getLowPc() == -1ULL) @@ -2674,15 +1076,17 @@ unsigned DwarfLinker::DIECloner::cloneScalarAttribute( Value = *OptionalValue; else { Linker.reportWarning( - "Unsupported scalar attribute form. Dropping attribute.", + "Unsupported scalar attribute form. Dropping attribute.", DMO, &InputDIE); return 0; } PatchLocation Patch = Die.addValue(DIEAlloc, dwarf::Attribute(AttrSpec.Attr), dwarf::Form(AttrSpec.Form), DIEInteger(Value)); - if (AttrSpec.Attr == dwarf::DW_AT_ranges) + if (AttrSpec.Attr == dwarf::DW_AT_ranges) { Unit.noteRangeAttribute(Die, Patch); + Info.HasRanges = true; + } // A more generic way to check for location attributes would be // nice, but it's very unlikely that any other attribute needs a @@ -2700,22 +1104,22 @@ unsigned DwarfLinker::DIECloner::cloneScalarAttribute( /// value \p Val, and add it to \p Die. /// \returns the size of the cloned attribute. unsigned DwarfLinker::DIECloner::cloneAttribute( - DIE &Die, const DWARFDie &InputDIE, CompileUnit &Unit, - const DWARFFormValue &Val, const AttributeSpec AttrSpec, unsigned AttrSize, - AttributesInfo &Info) { + DIE &Die, const DWARFDie &InputDIE, const DebugMapObject &DMO, + CompileUnit &Unit, OffsetsStringPool &StringPool, const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, AttributesInfo &Info) { const DWARFUnit &U = Unit.getOrigUnit(); switch (AttrSpec.Form) { case dwarf::DW_FORM_strp: case dwarf::DW_FORM_string: - return cloneStringAttribute(Die, AttrSpec, Val, U); + return cloneStringAttribute(Die, AttrSpec, Val, U, StringPool, Info); case dwarf::DW_FORM_ref_addr: case dwarf::DW_FORM_ref1: case dwarf::DW_FORM_ref2: case dwarf::DW_FORM_ref4: case dwarf::DW_FORM_ref8: return cloneDieReferenceAttribute(Die, InputDIE, AttrSpec, AttrSize, Val, - Unit); + DMO, Unit); case dwarf::DW_FORM_block: case dwarf::DW_FORM_block1: case dwarf::DW_FORM_block2: @@ -2733,11 +1137,12 @@ unsigned DwarfLinker::DIECloner::cloneAttribute( case dwarf::DW_FORM_sec_offset: case dwarf::DW_FORM_flag: case dwarf::DW_FORM_flag_present: - return cloneScalarAttribute(Die, InputDIE, Unit, AttrSpec, Val, AttrSize, - Info); + return cloneScalarAttribute(Die, InputDIE, DMO, Unit, AttrSpec, Val, + AttrSize, Info); default: Linker.reportWarning( - "Unsupported attribute form in cloneAttribute. Dropping.", &InputDIE); + "Unsupported attribute form in cloneAttribute. Dropping.", DMO, + &InputDIE); } return 0; @@ -2750,10 +1155,9 @@ unsigned DwarfLinker::DIECloner::cloneAttribute( /// Like for findValidRelocs(), this function must be called with /// monotonic \p BaseOffset values. /// -/// \returns wether any reloc has been applied. -bool DwarfLinker::RelocationManager:: -applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset, - bool isLittleEndian) { +/// \returns whether any reloc has been applied. +bool DwarfLinker::RelocationManager::applyValidRelocs( + MutableArrayRef<char> Data, uint32_t BaseOffset, bool isLittleEndian) { assert((NextValidReloc == 0 || BaseOffset > ValidRelocs[NextValidReloc - 1].Offset) && "BaseOffset should only be increasing."); @@ -2822,6 +1226,53 @@ static bool isTypeTag(uint16_t Tag) { return false; } +static bool isObjCSelector(StringRef Name) { + return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') && + (Name[1] == '['); +} + +void DwarfLinker::DIECloner::addObjCAccelerator(CompileUnit &Unit, + const DIE *Die, + DwarfStringPoolEntryRef Name, + OffsetsStringPool &StringPool, + bool SkipPubSection) { + assert(isObjCSelector(Name.getString()) && "not an objc selector"); + // Objective C method or class function. + // "- [Class(Category) selector :withArg ...]" + StringRef ClassNameStart(Name.getString().drop_front(2)); + size_t FirstSpace = ClassNameStart.find(' '); + if (FirstSpace == StringRef::npos) + return; + + StringRef SelectorStart(ClassNameStart.data() + FirstSpace + 1); + if (!SelectorStart.size()) + return; + + StringRef Selector(SelectorStart.data(), SelectorStart.size() - 1); + Unit.addNameAccelerator(Die, StringPool.getEntry(Selector), SkipPubSection); + + // Add an entry for the class name that points to this + // method/class function. + StringRef ClassName(ClassNameStart.data(), FirstSpace); + Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassName), SkipPubSection); + + if (ClassName[ClassName.size() - 1] == ')') { + size_t OpenParens = ClassName.find('('); + if (OpenParens != StringRef::npos) { + StringRef ClassNameNoCategory(ClassName.data(), OpenParens); + Unit.addObjCAccelerator(Die, StringPool.getEntry(ClassNameNoCategory), + SkipPubSection); + + std::string MethodNameNoCategory(Name.getString().data(), OpenParens + 2); + // FIXME: The missing space here may be a bug, but + // dsymutil-classic also does it this way. + MethodNameNoCategory.append(SelectorStart); + Unit.addNameAccelerator(Die, StringPool.getEntry(MethodNameNoCategory), + SkipPubSection); + } + } +} + static bool shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, uint16_t Tag, bool InDebugMap, bool SkipPC, @@ -2835,20 +1286,23 @@ shouldSkipAttribute(DWARFAbbreviationDeclaration::AttributeSpec AttrSpec, return SkipPC; case dwarf::DW_AT_location: case dwarf::DW_AT_frame_base: - // FIXME: for some reason dsymutil-classic keeps the location - // attributes when they are of block type (ie. not location - // lists). This is totally wrong for globals where we will keep a - // wrong address. It is mostly harmless for locals, but there is - // no point in keeping these anyway when the function wasn't linked. + // FIXME: for some reason dsymutil-classic keeps the location attributes + // when they are of block type (i.e. not location lists). This is totally + // wrong for globals where we will keep a wrong address. It is mostly + // harmless for locals, but there is no point in keeping these anyway when + // the function wasn't linked. return (SkipPC || (!InFunctionScope && Tag == dwarf::DW_TAG_variable && !InDebugMap)) && !DWARFFormValue(AttrSpec.Form).isFormClass(DWARFFormValue::FC_Block); } } -DIE *DwarfLinker::DIECloner::cloneDIE( - const DWARFDie &InputDIE, CompileUnit &Unit, - int64_t PCOffset, uint32_t OutOffset, unsigned Flags, DIE *Die) { +DIE *DwarfLinker::DIECloner::cloneDIE(const DWARFDie &InputDIE, + const DebugMapObject &DMO, + CompileUnit &Unit, + OffsetsStringPool &StringPool, + int64_t PCOffset, uint32_t OutOffset, + unsigned Flags, DIE *Die) { DWARFUnit &U = Unit.getOrigUnit(); unsigned Idx = U.getDIEIndex(InputDIE); CompileUnit::DIEInfo &Info = Unit.getInfo(Idx); @@ -2884,15 +1338,14 @@ DIE *DwarfLinker::DIECloner::cloneDIE( // Point to the next DIE (generally there is always at least a NULL // entry after the current one). If this is a lone // DW_TAG_compile_unit without any children, point to the next unit. - uint32_t NextOffset = - (Idx + 1 < U.getNumDIEs()) - ? U.getDIEAtIndex(Idx + 1).getOffset() - : U.getNextUnitOffset(); + uint32_t NextOffset = (Idx + 1 < U.getNumDIEs()) + ? U.getDIEAtIndex(Idx + 1).getOffset() + : U.getNextUnitOffset(); AttributesInfo AttrInfo; - // We could copy the data only if we need to aply a relocation to - // it. After testing, it seems there is no performance downside to - // doing the copy unconditionally, and it makes the code simpler. + // We could copy the data only if we need to apply a relocation to it. After + // testing, it seems there is no performance downside to doing the copy + // unconditionally, and it makes the code simpler. SmallString<40> DIECopy(Data.getData().substr(Offset, NextOffset - Offset)); Data = DWARFDataExtractor(DIECopy, Data.isLittleEndian(), Data.getAddressSize()); @@ -2903,7 +1356,7 @@ DIE *DwarfLinker::DIECloner::cloneDIE( // (Dwarf version == 2), then it might have been relocated to a // totally unrelated value (because the end address in the object // file might be start address of another function which got moved - // independantly by the linker). The computation of the actual + // independently by the linker). The computation of the actual // high_pc value is done in cloneAddressAttribute(). AttrInfo.OrigHighPc = dwarf::toAddress(InputDIE.find(dwarf::DW_AT_high_pc), 0); @@ -2928,13 +1381,14 @@ DIE *DwarfLinker::DIECloner::cloneDIE( if (Abbrev->getTag() == dwarf::DW_TAG_subprogram) { Flags |= TF_InFunctionScope; - if (!Info.InDebugMap) + if (!Info.InDebugMap && LLVM_LIKELY(!Options.Update)) Flags |= TF_SkipPC; } bool Copied = false; for (const auto &AttrSpec : Abbrev->attributes()) { - if (shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, + if (LLVM_LIKELY(!Options.Update) && + shouldSkipAttribute(AttrSpec, Die->getTag(), Info.InDebugMap, Flags & TF_SkipPC, Flags & TF_InFunctionScope)) { DWARFFormValue::skipValue(AttrSpec.Form, Data, &Offset, U.getFormParams()); @@ -2953,8 +1407,8 @@ DIE *DwarfLinker::DIECloner::cloneDIE( Val.extractValue(Data, &Offset, U.getFormParams(), &U); AttrSize = Offset - AttrSize; - OutOffset += - cloneAttribute(*Die, InputDIE, Unit, Val, AttrSpec, AttrSize, AttrInfo); + OutOffset += cloneAttribute(*Die, InputDIE, DMO, Unit, StringPool, Val, + AttrSpec, AttrSize, AttrInfo); } // Look for accelerator entries. @@ -2962,25 +1416,47 @@ DIE *DwarfLinker::DIECloner::cloneDIE( // FIXME: This is slightly wrong. An inline_subroutine without a // low_pc, but with AT_ranges might be interesting to get into the // accelerator tables too. For now stick with dsymutil's behavior. - if ((Info.InDebugMap || AttrInfo.HasLowPc) && + if ((Info.InDebugMap || AttrInfo.HasLowPc || AttrInfo.HasRanges) && Tag != dwarf::DW_TAG_compile_unit && - getDIENames(InputDIE, AttrInfo)) { + getDIENames(InputDIE, AttrInfo, StringPool, + Tag != dwarf::DW_TAG_inlined_subroutine)) { if (AttrInfo.MangledName && AttrInfo.MangledName != AttrInfo.Name) Unit.addNameAccelerator(Die, AttrInfo.MangledName, - AttrInfo.MangledNameOffset, Tag == dwarf::DW_TAG_inlined_subroutine); - if (AttrInfo.Name) - Unit.addNameAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset, + if (AttrInfo.Name) { + if (AttrInfo.NameWithoutTemplate) + Unit.addNameAccelerator(Die, AttrInfo.NameWithoutTemplate, + /* SkipPubSection */ true); + Unit.addNameAccelerator(Die, AttrInfo.Name, Tag == dwarf::DW_TAG_inlined_subroutine); + } + if (AttrInfo.Name && isObjCSelector(AttrInfo.Name.getString())) + addObjCAccelerator(Unit, Die, AttrInfo.Name, StringPool, + /* SkipPubSection =*/true); + + } else if (Tag == dwarf::DW_TAG_namespace) { + if (!AttrInfo.Name) + AttrInfo.Name = StringPool.getEntry("(anonymous namespace)"); + Unit.addNamespaceAccelerator(Die, AttrInfo.Name); } else if (isTypeTag(Tag) && !AttrInfo.IsDeclaration && - getDIENames(InputDIE, AttrInfo)) { - if (AttrInfo.Name) - Unit.addTypeAccelerator(Die, AttrInfo.Name, AttrInfo.NameOffset); + getDIENames(InputDIE, AttrInfo, StringPool) && AttrInfo.Name && + AttrInfo.Name.getString()[0]) { + uint32_t Hash = hashFullyQualifiedName(InputDIE, Unit, DMO); + uint64_t RuntimeLang = + dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_runtime_class)) + .getValueOr(0); + bool ObjCClassIsImplementation = + (RuntimeLang == dwarf::DW_LANG_ObjC || + RuntimeLang == dwarf::DW_LANG_ObjC_plus_plus) && + dwarf::toUnsigned(InputDIE.find(dwarf::DW_AT_APPLE_objc_complete_type)) + .getValueOr(0); + Unit.addTypeAccelerator(Die, AttrInfo.Name, ObjCClassIsImplementation, + Hash); } // Determine whether there are any children that we want to keep. bool HasChildren = false; - for (auto Child: InputDIE.children()) { + for (auto Child : InputDIE.children()) { unsigned Idx = U.getDIEIndex(Child); if (Unit.getInfo(Idx).Keep) { HasChildren = true; @@ -3005,8 +1481,9 @@ DIE *DwarfLinker::DIECloner::cloneDIE( } // Recursively clone children. - for (auto Child: InputDIE.children()) { - if (DIE *Clone = cloneDIE(Child, Unit, PCOffset, OutOffset, Flags)) { + for (auto Child : InputDIE.children()) { + if (DIE *Clone = cloneDIE(Child, DMO, Unit, StringPool, PCOffset, OutOffset, + Flags)) { Die->addChild(Clone); OutOffset = Clone->getOffset() + Clone->getSize(); } @@ -3023,7 +1500,8 @@ DIE *DwarfLinker::DIECloner::cloneDIE( /// and emit them in the output file. Update the relevant attributes /// to point at the new entries. void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, - DWARFContext &OrigDwarf) const { + DWARFContext &OrigDwarf, + const DebugMapObject &DMO) const { DWARFDebugRangeList RangeList; const auto &FunctionRanges = Unit.getFunctionRanges(); unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); @@ -3044,7 +1522,11 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, for (const auto &RangeAttribute : Unit.getRangesAttributes()) { uint32_t Offset = RangeAttribute.get(); RangeAttribute.set(Streamer->getRangesSectionSize()); - RangeList.extract(RangeExtractor, &Offset); + if (Error E = RangeList.extract(RangeExtractor, &Offset)) { + llvm::consumeError(std::move(E)); + reportWarning("invalid range list ignored.", DMO); + RangeList.clear(); + } const auto &Entries = RangeList.getEntries(); if (!Entries.empty()) { const DWARFDebugRangeList::RangeListEntry &First = Entries.front(); @@ -3055,7 +1537,7 @@ void DwarfLinker::patchRangesForUnit(const CompileUnit &Unit, CurrRange = FunctionRanges.find(First.StartAddress + OrigLowPc); if (CurrRange == InvalidRange || CurrRange.start() > First.StartAddress + OrigLowPc) { - reportWarning("no mapping for range."); + reportWarning("no mapping for range.", DMO); continue; } } @@ -3099,7 +1581,7 @@ static void insertLineSequence(std::vector<DWARFDebugLine::Row> &Seq, }); // FIXME: this only removes the unneeded end_sequence if the - // sequences have been inserted in order. using a global sort like + // sequences have been inserted in order. Using a global sort like // described in patchLineTableForUnit() and delaying the end_sequene // elimination to emitLineTableForUnit() we can get rid of all of them. if (InsertPoint != Rows.end() && @@ -3127,7 +1609,9 @@ static void patchStmtList(DIE &Die, DIEInteger Offset) { /// recreate a relocated version of these for the address ranges that /// are present in the binary. void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, - DWARFContext &OrigDwarf) { + DWARFContext &OrigDwarf, + RangesTy &Ranges, + const DebugMapObject &DMO) { DWARFDie CUDie = Unit.getOrigUnit().getUnitDIE(); auto StmtList = dwarf::toSectionOffset(CUDie.find(dwarf::DW_AT_stmt_list)); if (!StmtList) @@ -3143,7 +1627,10 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, DWARFDataExtractor LineExtractor( OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(), OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize()); - LineTable.parse(LineExtractor, &StmtOffset, &Unit.getOrigUnit()); + + Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, + &Unit.getOrigUnit()); + DWARFDebugLine::warn(std::move(Err)); // This vector is the output line table. std::vector<DWARFDebugLine::Row> NewRows; @@ -3156,7 +1643,7 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, auto InvalidRange = FunctionRanges.end(), CurrRange = InvalidRange; // FIXME: This logic is meant to generate exactly the same output as - // Darwin's classic dsynutil. There is a nicer way to implement this + // Darwin's classic dsymutil. There is a nicer way to implement this // by simply putting all the relocated line info in NewRows and simply // sorting NewRows before passing it to emitLineTableForUnit. This // should be correct as sequences for a function should stay @@ -3167,7 +1654,7 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, // Iterate over the object file line info and extract the sequences // that correspond to linked functions. for (auto &Row : LineTable.Rows) { - // Check wether we stepped out of the range. The range is + // Check whether we stepped out of the range. The range is // half-open, but consider accept the end address of the range if // it is marked as end_sequence in the input (because in that // case, the relocation offset is accurate and that entry won't @@ -3197,8 +1684,8 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, --Range; if (Range != Ranges.end() && Range->first <= Row.Address && - Range->second.first >= Row.Address) { - StopAddress = Row.Address + Range->second.second; + Range->second.HighPC >= Row.Address) { + StopAddress = Row.Address + Range->second.Offset; } } } @@ -3232,7 +1719,7 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, } // Finished extracting, now emit the line tables. - // FIXME: LLVM hardcodes its prologue values. We just copy the + // FIXME: LLVM hard-codes its prologue values. We just copy the // prologue over and that works because we act as both producer and // consumer. It would be nicer to have a real configurable line // table emitter. @@ -3240,10 +1727,10 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, LineTable.Prologue.getVersion() > 5 || LineTable.Prologue.DefaultIsStmt != DWARF2_LINE_DEFAULT_IS_STMT || LineTable.Prologue.OpcodeBase > 13) - reportWarning("line table parameters mismatch. Cannot emit."); + reportWarning("line table parameters mismatch. Cannot emit.", DMO); else { uint32_t PrologueEnd = *StmtList + 10 + LineTable.Prologue.PrologueLength; - // DWARFv5 has an extra 2 bytes of information before the header_length + // DWARF v5 has an extra 2 bytes of information before the header_length // field. if (LineTable.Prologue.getVersion() == 5) PrologueEnd += 2; @@ -3260,8 +1747,58 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, } void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { - Streamer->emitPubNamesForUnit(Unit); - Streamer->emitPubTypesForUnit(Unit); + switch (Options.TheAccelTableKind) { + case AccelTableKind::Apple: + emitAppleAcceleratorEntriesForUnit(Unit); + break; + case AccelTableKind::Dwarf: + emitDwarfAcceleratorEntriesForUnit(Unit); + break; + case AccelTableKind::Default: + llvm_unreachable("The default must be updated to a concrete value."); + break; + } +} + +void DwarfLinker::emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit) { + // Add namespaces. + for (const auto &Namespace : Unit.getNamespaces()) + AppleNamespaces.addName(Namespace.Name, + Namespace.Die->getOffset() + Unit.getStartOffset()); + + /// Add names. + if (!Options.Minimize) + Streamer->emitPubNamesForUnit(Unit); + for (const auto &Pubname : Unit.getPubnames()) + AppleNames.addName(Pubname.Name, + Pubname.Die->getOffset() + Unit.getStartOffset()); + + /// Add types. + if (!Options.Minimize) + Streamer->emitPubTypesForUnit(Unit); + for (const auto &Pubtype : Unit.getPubtypes()) + AppleTypes.addName( + Pubtype.Name, Pubtype.Die->getOffset() + Unit.getStartOffset(), + Pubtype.Die->getTag(), + Pubtype.ObjcClassImplementation ? dwarf::DW_FLAG_type_implementation + : 0, + Pubtype.QualifiedNameHash); + + /// Add ObjC names. + for (const auto &ObjC : Unit.getObjC()) + AppleObjc.addName(ObjC.Name, ObjC.Die->getOffset() + Unit.getStartOffset()); +} + +void DwarfLinker::emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit) { + for (const auto &Namespace : Unit.getNamespaces()) + DebugNames.addName(Namespace.Name, Namespace.Die->getOffset(), + Namespace.Die->getTag(), Unit.getUniqueID()); + for (const auto &Pubname : Unit.getPubnames()) + DebugNames.addName(Pubname.Name, Pubname.Die->getOffset(), + Pubname.Die->getTag(), Unit.getUniqueID()); + for (const auto &Pubtype : Unit.getPubtypes()) + DebugNames.addName(Pubtype.Name, Pubtype.Die->getOffset(), + Pubtype.Die->getTag(), Unit.getUniqueID()); } /// Read the frame info stored in the object, and emit the @@ -3271,6 +1808,7 @@ void DwarfLinker::emitAcceleratorEntriesForUnit(CompileUnit &Unit) { /// be considered as black boxes and moved as is. The only thing to do /// is to patch the addresses in the headers. void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, + RangesTy &Ranges, DWARFContext &OrigDwarf, unsigned AddrSize) { StringRef FrameData = OrigDwarf.getDWARFObj().getDebugFrameSection(); @@ -3288,7 +1826,7 @@ void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, uint32_t EntryOffset = InputOffset; uint32_t InitialLength = Data.getU32(&InputOffset); if (InitialLength == 0xFFFFFFFF) - return reportWarning("Dwarf64 bits no supported"); + return reportWarning("Dwarf64 bits no supported", DMO); uint32_t CIEId = Data.getU32(&InputOffset); if (CIEId == 0xFFFFFFFF) { @@ -3310,7 +1848,7 @@ void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, if (Range != Ranges.begin()) --Range; if (Range == Ranges.end() || Range->first > Loc || - Range->second.first <= Loc) { + Range->second.HighPC <= Loc) { // The +4 is to account for the size of the InitialLength field itself. InputOffset = EntryOffset + InitialLength + 4; continue; @@ -3320,7 +1858,7 @@ void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, // Have we already emitted a corresponding CIE? StringRef CIEData = LocalCIES[CIEId]; if (CIEData.empty()) - return reportWarning("Inconsistent debug_frame content. Dropping."); + return reportWarning("Inconsistent debug_frame content. Dropping.", DMO); // Look if we already emitted a CIE that corresponds to the // referenced one (the CIE data is the key of that lookup). @@ -3343,7 +1881,7 @@ void DwarfLinker::patchFrameInfoForObject(const DebugMapObject &DMO, // fields that will get reconstructed by emitFDE(). unsigned FDERemainingBytes = InitialLength - (4 + AddrSize); Streamer->emitFDE(IteratorInserted.first->getValue(), AddrSize, - Loc + Range->second.second, + Loc + Range->second.Offset, FrameData.substr(InputOffset, FDERemainingBytes)); InputOffset += FDERemainingBytes; } @@ -3364,21 +1902,65 @@ void DwarfLinker::DIECloner::copyAbbrev( Linker.AssignAbbrev(Copy); } -static uint64_t getDwoId(const DWARFDie &CUDie, - const DWARFUnit &Unit) { - auto DwoId = dwarf::toUnsigned(CUDie.find({dwarf::DW_AT_dwo_id, - dwarf::DW_AT_GNU_dwo_id})); +uint32_t DwarfLinker::DIECloner::hashFullyQualifiedName( + DWARFDie DIE, CompileUnit &U, const DebugMapObject &DMO, int RecurseDepth) { + const char *Name = nullptr; + DWARFUnit *OrigUnit = &U.getOrigUnit(); + CompileUnit *CU = &U; + Optional<DWARFFormValue> Ref; + + while (1) { + if (const char *CurrentName = DIE.getName(DINameKind::ShortName)) + Name = CurrentName; + + if (!(Ref = DIE.find(dwarf::DW_AT_specification)) && + !(Ref = DIE.find(dwarf::DW_AT_abstract_origin))) + break; + + if (!Ref->isFormClass(DWARFFormValue::FC_Reference)) + break; + + CompileUnit *RefCU; + if (auto RefDIE = resolveDIEReference(Linker, DMO, CompileUnits, *Ref, + U.getOrigUnit(), DIE, RefCU)) { + CU = RefCU; + OrigUnit = &RefCU->getOrigUnit(); + DIE = RefDIE; + } + } + + unsigned Idx = OrigUnit->getDIEIndex(DIE); + if (!Name && DIE.getTag() == dwarf::DW_TAG_namespace) + Name = "(anonymous namespace)"; + + if (CU->getInfo(Idx).ParentIdx == 0 || + // FIXME: dsymutil-classic compatibility. Ignore modules. + CU->getOrigUnit().getDIEAtIndex(CU->getInfo(Idx).ParentIdx).getTag() == + dwarf::DW_TAG_module) + return djbHash(Name ? Name : "", djbHash(RecurseDepth ? "" : "::")); + + DWARFDie Die = OrigUnit->getDIEAtIndex(CU->getInfo(Idx).ParentIdx); + return djbHash( + (Name ? Name : ""), + djbHash((Name ? "::" : ""), + hashFullyQualifiedName(Die, *CU, DMO, ++RecurseDepth))); +} + +static uint64_t getDwoId(const DWARFDie &CUDie, const DWARFUnit &Unit) { + auto DwoId = dwarf::toUnsigned( + CUDie.find({dwarf::DW_AT_dwo_id, dwarf::DW_AT_GNU_dwo_id})); if (DwoId) return *DwoId; return 0; } bool DwarfLinker::registerModuleReference( - const DWARFDie &CUDie, const DWARFUnit &Unit, - DebugMap &ModuleMap, unsigned Indent) { - std::string PCMfile = - dwarf::toString(CUDie.find({dwarf::DW_AT_dwo_name, - dwarf::DW_AT_GNU_dwo_name}), ""); + const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, + const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, + UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, + unsigned &UnitID, unsigned Indent) { + std::string PCMfile = dwarf::toString( + CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); if (PCMfile.empty()) return false; @@ -3388,7 +1970,7 @@ bool DwarfLinker::registerModuleReference( std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); if (Name.empty()) { - reportWarning("Anonymous module skeleton CU for " + PCMfile); + reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO); return true; } @@ -3404,7 +1986,9 @@ bool DwarfLinker::registerModuleReference( // ASTFileSignatures will change randomly when a module is rebuilt. if (Options.Verbose && (Cached->second != DwoId)) reportWarning(Twine("hash mismatch: this object file was built against a " - "different version of the module ") + PCMfile); + "different version of the module ") + + PCMfile, + DMO); if (Options.Verbose) outs() << " [cached].\n"; return true; @@ -3415,8 +1999,9 @@ bool DwarfLinker::registerModuleReference( // Cyclic dependencies are disallowed by Clang, but we still // shouldn't run into an infinite loop, so mark it as processed now. ClangModules.insert({PCMfile, DwoId}); - if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, - Indent + 2)) { + if (Error E = loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, + Ranges, StringPool, UniquingStringPool, + ODRContexts, UnitID, Indent + 2)) { consumeError(std::move(E)); return false; } @@ -3424,35 +2009,48 @@ bool DwarfLinker::registerModuleReference( } ErrorOr<const object::ObjectFile &> -DwarfLinker::loadObject(BinaryHolder &BinaryHolder, DebugMapObject &Obj, - const DebugMap &Map) { - auto ErrOrObjs = - BinaryHolder.GetObjectFiles(Obj.getObjectFilename(), Obj.getTimestamp()); - if (std::error_code EC = ErrOrObjs.getError()) { - reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message()); - return EC; - } - auto ErrOrObj = BinaryHolder.Get(Map.getTriple()); - if (std::error_code EC = ErrOrObj.getError()) - reportWarning(Twine(Obj.getObjectFilename()) + ": " + EC.message()); - return ErrOrObj; +DwarfLinker::loadObject(const DebugMapObject &Obj, const DebugMap &Map) { + auto ObjectEntry = + BinHolder.getObjectEntry(Obj.getObjectFilename(), Obj.getTimestamp()); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + reportWarning( + Twine(Obj.getObjectFilename()) + ": " + toString(std::move(Err)), Obj); + return errorToErrorCode(std::move(Err)); + } + + auto Object = ObjectEntry->getObject(Map.getTriple()); + if (!Object) { + auto Err = Object.takeError(); + reportWarning( + Twine(Obj.getObjectFilename()) + ": " + toString(std::move(Err)), Obj); + return errorToErrorCode(std::move(Err)); + } + + return *Object; } Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, unsigned Indent) { + DebugMap &ModuleMap, + const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool, + UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, + unsigned &UnitID, unsigned Indent) { SmallString<80> Path(Options.PrependPath); if (sys::path::is_relative(Filename)) sys::path::append(Path, ModulePath, Filename); else sys::path::append(Path, Filename); - BinaryHolder ObjHolder(Options.Verbose); + // Don't use the cached binary holder because we have no thread-safety + // guarantee and the lifetime is limited. auto &Obj = ModuleMap.addDebugMapObject( Path, sys::TimePoint<std::chrono::seconds>(), MachO::N_OSO); - auto ErrOrObj = loadObject(ObjHolder, Obj, ModuleMap); + auto ErrOrObj = loadObject(Obj, ModuleMap); if (!ErrOrObj) { // Try and emit more helpful warnings by applying some heuristics. - StringRef ObjFile = CurrentDebugObject->getObjectFilename(); + StringRef ObjFile = DMO.getObjectFilename(); bool isClangModule = sys::path::extension(Filename).equals(".pcm"); bool isArchive = ObjFile.endswith(")"); if (isClangModule) { @@ -3462,9 +2060,9 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, // cache has expired and was pruned by clang. A more adventurous // dsymutil would invoke clang to rebuild the module now. if (!ModuleCacheHintDisplayed) { - errs() << "note: The clang module cache may have expired since this " - "object file was built. Rebuilding the object file will " - "rebuild the module cache.\n"; + WithColor::note() << "The clang module cache may have expired since " + "this object file was built. Rebuilding the " + "object file will rebuild the module cache.\n"; ModuleCacheHintDisplayed = true; } } else if (isArchive) { @@ -3473,11 +2071,13 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, // was built on a different machine. We don't want to discourage module // debugging for convenience libraries within a project though. if (!ArchiveHintDisplayed) { - errs() << "note: Linking a static library that was built with " - "-gmodules, but the module cache was not found. " - "Redistributable static libraries should never be built " - "with module debugging enabled. The debug experience will " - "be degraded due to incomplete debug information.\n"; + WithColor::note() + << "Linking a static library that was built with " + "-gmodules, but the module cache was not found. " + "Redistributable static libraries should never be " + "built with module debugging enabled. The debug " + "experience will be degraded due to incomplete " + "debug information.\n"; ArchiveHintDisplayed = true; } } @@ -3490,18 +2090,22 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, // Setup access to the debug info. auto DwarfContext = DWARFContext::create(*ErrOrObj); RelocationManager RelocMgr(*this); - for (const auto &CU : DwarfContext->compile_units()) { - maybeUpdateMaxDwarfVersion(CU->getVersion()); + for (const auto &CU : DwarfContext->compile_units()) { + updateDwarfVersion(CU->getVersion()); // Recursively get all modules imported by this one. auto CUDie = CU->getUnitDIE(false); - if (!registerModuleReference(CUDie, *CU, ModuleMap, Indent)) { + if (!CUDie) + continue; + if (!registerModuleReference(CUDie, *CU, ModuleMap, DMO, Ranges, StringPool, + UniquingStringPool, ODRContexts, UnitID, + Indent)) { if (Unit) { std::string Err = (Filename + ": Clang modules are expected to have exactly 1 compile unit.\n") .str(); - errs() << Err; + error(Err); return make_error<StringError>(Err, inconvertibleErrorCode()); } // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is @@ -3512,7 +2116,9 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, if (Options.Verbose) reportWarning( Twine("hash mismatch: this object file was built against a " - "different version of the module ") + Filename); + "different version of the module ") + + Filename, + DMO); // Update the cache entry with the DwoId of the module loaded from disk. ClangModules[Filename] = PCMDwoId; } @@ -3521,8 +2127,8 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, Unit = llvm::make_unique<CompileUnit>(*CU, UnitID++, !Options.NoODR, ModuleName); Unit->setHasInterestingContent(); - analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), StringPool, - ODRContexts); + analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), + UniquingStringPool, ODRContexts); // Keep everything. Unit->markEverythingAsKept(); } @@ -3534,37 +2140,48 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, outs() << "cloning .debug_info from " << Filename << "\n"; } - std::vector<std::unique_ptr<CompileUnit>> CompileUnits; + UnitListTy CompileUnits; CompileUnits.push_back(std::move(Unit)); DIECloner(*this, RelocMgr, DIEAlloc, CompileUnits, Options) - .cloneAllCompileUnits(*DwarfContext); + .cloneAllCompileUnits(*DwarfContext, DMO, Ranges, StringPool); return Error::success(); } -void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) { +void DwarfLinker::DIECloner::cloneAllCompileUnits( + DWARFContext &DwarfContext, const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool) { if (!Linker.Streamer) return; for (auto &CurrentUnit : CompileUnits) { auto InputDIE = CurrentUnit->getOrigUnit().getUnitDIE(); CurrentUnit->setStartOffset(Linker.OutputDebugInfoSize); + if (!InputDIE) { + Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); + continue; + } if (CurrentUnit->getInfo(0).Keep) { // Clone the InputDIE into your Unit DIE in our compile unit since it // already has a DIE inside of it. CurrentUnit->createOutputDIE(); - cloneDIE(InputDIE, *CurrentUnit, 0 /* PC offset */, + cloneDIE(InputDIE, DMO, *CurrentUnit, StringPool, 0 /* PC offset */, 11 /* Unit Header size */, 0, CurrentUnit->getOutputUnitDIE()); } Linker.OutputDebugInfoSize = CurrentUnit->computeNextUnitOffset(); if (Linker.Options.NoOutput) continue; - // FIXME: for compatibility with the classic dsymutil, we emit - // an empty line table for the unit, even if the unit doesn't - // actually exist in the DIE tree. - Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext); - Linker.patchRangesForUnit(*CurrentUnit, DwarfContext); - Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + + if (LLVM_LIKELY(!Linker.Options.Update)) { + // FIXME: for compatibility with the classic dsymutil, we emit an empty + // line table for the unit, even if the unit doesn't actually exist in + // the DIE tree. + Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext, Ranges, DMO); + Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); + Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); + } else { + Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + } } if (Linker.Options.NoOutput) @@ -3572,7 +2189,8 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) { // Emit all the compile unit's debug information. for (auto &CurrentUnit : CompileUnits) { - Linker.generateUnitRanges(*CurrentUnit); + if (LLVM_LIKELY(!Linker.Options.Update)) + Linker.generateUnitRanges(*CurrentUnit); CurrentUnit->fixupForwardReferences(); Linker.Streamer->emitCompileUnitHeader(*CurrentUnit); if (!CurrentUnit->getOutputUnitDIE()) @@ -3581,6 +2199,84 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits(DWARFContext &DwarfContext) { } } +void DwarfLinker::updateAccelKind(DWARFContext &Dwarf) { + if (Options.TheAccelTableKind != AccelTableKind::Default) + return; + + auto &DwarfObj = Dwarf.getDWARFObj(); + + if (!AtLeastOneDwarfAccelTable && + (!DwarfObj.getAppleNamesSection().Data.empty() || + !DwarfObj.getAppleTypesSection().Data.empty() || + !DwarfObj.getAppleNamespacesSection().Data.empty() || + !DwarfObj.getAppleObjCSection().Data.empty())) { + AtLeastOneAppleAccelTable = true; + } + + if (!AtLeastOneDwarfAccelTable && + !DwarfObj.getDebugNamesSection().Data.empty()) { + AtLeastOneDwarfAccelTable = true; + } +} + +bool DwarfLinker::emitPaperTrailWarnings(const DebugMapObject &DMO, + const DebugMap &Map, + OffsetsStringPool &StringPool) { + if (DMO.getWarnings().empty() || !DMO.empty()) + return false; + + Streamer->switchToDebugInfoSection(/* Version */ 2); + DIE *CUDie = DIE::get(DIEAlloc, dwarf::DW_TAG_compile_unit); + CUDie->setOffset(11); + StringRef Producer = StringPool.internString("dsymutil"); + StringRef File = StringPool.internString(DMO.getObjectFilename()); + CUDie->addValue(DIEAlloc, dwarf::DW_AT_producer, dwarf::DW_FORM_strp, + DIEInteger(StringPool.getStringOffset(Producer))); + DIEBlock *String = new (DIEAlloc) DIEBlock(); + DIEBlocks.push_back(String); + for (auto &C : File) + String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, + DIEInteger(C)); + String->addValue(DIEAlloc, dwarf::Attribute(0), dwarf::DW_FORM_data1, + DIEInteger(0)); + + CUDie->addValue(DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_string, String); + for (const auto &Warning : DMO.getWarnings()) { + DIE &ConstDie = CUDie->addChild(DIE::get(DIEAlloc, dwarf::DW_TAG_constant)); + ConstDie.addValue( + DIEAlloc, dwarf::DW_AT_name, dwarf::DW_FORM_strp, + DIEInteger(StringPool.getStringOffset("dsymutil_warning"))); + ConstDie.addValue(DIEAlloc, dwarf::DW_AT_artificial, dwarf::DW_FORM_flag, + DIEInteger(1)); + ConstDie.addValue(DIEAlloc, dwarf::DW_AT_const_value, dwarf::DW_FORM_strp, + DIEInteger(StringPool.getStringOffset(Warning))); + } + unsigned Size = 4 /* FORM_strp */ + File.size() + 1 + + DMO.getWarnings().size() * (4 + 1 + 4) + + 1 /* End of children */; + DIEAbbrev Abbrev = CUDie->generateAbbrev(); + AssignAbbrev(Abbrev); + CUDie->setAbbrevNumber(Abbrev.getNumber()); + Size += getULEB128Size(Abbrev.getNumber()); + // Abbreviation ordering needed for classic compatibility. + for (auto &Child : CUDie->children()) { + Abbrev = Child.generateAbbrev(); + AssignAbbrev(Abbrev); + Child.setAbbrevNumber(Abbrev.getNumber()); + Size += getULEB128Size(Abbrev.getNumber()); + } + CUDie->setSize(Size); + auto &Asm = Streamer->getAsmPrinter(); + Asm.emitInt32(11 + CUDie->getSize() - 4); + Asm.emitInt16(2); + Asm.emitInt32(0); + Asm.emitInt8(Map.getTriple().isArch64Bit() ? 8 : 4); + Streamer->emitDIE(*CUDie); + OutputDebugInfoSize += 11 /* Header */ + Size; + + return true; +} + bool DwarfLinker::link(const DebugMap &Map) { if (!createStreamer(Map.getTriple(), OutFile)) return false; @@ -3588,34 +2284,73 @@ bool DwarfLinker::link(const DebugMap &Map) { // Size of the DIEs (and headers) generated for the linked output. OutputDebugInfoSize = 0; // A unique ID that identifies each compile unit. - UnitID = 0; + unsigned UnitID = 0; DebugMap ModuleMap(Map.getTriple(), Map.getBinaryPath()); + // First populate the data structure we need for each iteration of the + // parallel loop. + unsigned NumObjects = Map.getNumberOfObjects(); + std::vector<LinkContext> ObjectContexts; + ObjectContexts.reserve(NumObjects); for (const auto &Obj : Map.objects()) { - CurrentDebugObject = Obj.get(); + ObjectContexts.emplace_back(Map, *this, *Obj.get()); + LinkContext &LC = ObjectContexts.back(); + if (LC.ObjectFile) + updateAccelKind(*LC.DwarfContext); + } + + // This Dwarf string pool which is only used for uniquing. This one should + // never be used for offsets as its not thread-safe or predictable. + UniquingStringPool UniquingStringPool; + // This Dwarf string pool which is used for emission. It must be used + // serially as the order of calling getStringOffset matters for + // reproducibility. + OffsetsStringPool OffsetsStringPool; + + // ODR Contexts for the link. + DeclContextTree ODRContexts; + + // If we haven't decided on an accelerator table kind yet, we base ourselves + // on the DWARF we have seen so far. At this point we haven't pulled in debug + // information from modules yet, so it is technically possible that they + // would affect the decision. However, as they're built with the same + // compiler and flags, it is safe to assume that they will follow the + // decision made here. + if (Options.TheAccelTableKind == AccelTableKind::Default) { + if (AtLeastOneDwarfAccelTable && !AtLeastOneAppleAccelTable) + Options.TheAccelTableKind = AccelTableKind::Dwarf; + else + Options.TheAccelTableKind = AccelTableKind::Apple; + } + + for (LinkContext &LinkContext : ObjectContexts) { if (Options.Verbose) - outs() << "DEBUG MAP OBJECT: " << Obj->getObjectFilename() << "\n"; + outs() << "DEBUG MAP OBJECT: " << LinkContext.DMO.getObjectFilename() + << "\n"; // N_AST objects (swiftmodule files) should get dumped directly into the // appropriate DWARF section. - if (Obj->getType() == MachO::N_AST) { - StringRef File = Obj->getObjectFilename(); + if (LinkContext.DMO.getType() == MachO::N_AST) { + StringRef File = LinkContext.DMO.getObjectFilename(); auto ErrorOrMem = MemoryBuffer::getFile(File); if (!ErrorOrMem) { - errs() << "Warning: Could not open " << File << "\n"; + warn("Could not open '" + File + "'\n"); continue; } sys::fs::file_status Stat; - if (auto errc = sys::fs::status(File, Stat)) { - errs() << "Warning: " << errc.message() << "\n"; + if (auto Err = sys::fs::status(File, Stat)) { + warn(Err.message()); continue; } - if (!Options.NoTimestamp && Stat.getLastModificationTime() != - sys::TimePoint<>(Obj->getTimestamp())) { - errs() << "Warning: Timestamp mismatch for " << File << ": " - << Stat.getLastModificationTime() << " and " - << sys::TimePoint<>(Obj->getTimestamp()) << "\n"; + if (!Options.NoTimestamp && + Stat.getLastModificationTime() != + sys::TimePoint<>(LinkContext.DMO.getTimestamp())) { + // Not using the helper here as we can easily stream TimePoint<>. + WithColor::warning() + << "Timestamp mismatch for " << File << ": " + << Stat.getLastModificationTime() << " and " + << sys::TimePoint<>(LinkContext.DMO.getTimestamp()) << "\n"; continue; } @@ -3625,24 +2360,38 @@ bool DwarfLinker::link(const DebugMap &Map) { continue; } - auto ErrOrObj = loadObject(BinHolder, *Obj, Map); - if (!ErrOrObj) + if (emitPaperTrailWarnings(LinkContext.DMO, Map, OffsetsStringPool)) + continue; + + if (!LinkContext.ObjectFile) continue; // Look for relocations that correspond to debug map entries. - RelocationManager RelocMgr(*this); - if (!RelocMgr.findValidRelocsInDebugInfo(*ErrOrObj, *Obj)) { + + if (LLVM_LIKELY(!Options.Update) && + !LinkContext.RelocMgr.findValidRelocsInDebugInfo( + *LinkContext.ObjectFile, LinkContext.DMO)) { if (Options.Verbose) outs() << "No valid relocations found. Skipping.\n"; + + // Clear this ObjFile entry as a signal to other loops that we should not + // process this iteration. + LinkContext.ObjectFile = nullptr; continue; } // Setup access to the debug info. - auto DwarfContext = DWARFContext::create(*ErrOrObj); - startDebugObject(*DwarfContext, *Obj); + if (!LinkContext.DwarfContext) + continue; + + startDebugObject(LinkContext); // In a first phase, just read in the debug info and load all clang modules. - for (const auto &CU : DwarfContext->compile_units()) { + LinkContext.CompileUnits.reserve( + LinkContext.DwarfContext->getNumCompileUnits()); + + for (const auto &CU : LinkContext.DwarfContext->compile_units()) { + updateDwarfVersion(CU->getVersion()); auto CUDie = CU->getUnitDIE(false); if (Options.Verbose) { outs() << "Input compilation unit:"; @@ -3652,101 +2401,151 @@ bool DwarfLinker::link(const DebugMap &Map) { CUDie.dump(outs(), 0, DumpOpts); } - if (!registerModuleReference(CUDie, *CU, ModuleMap)) { - Units.push_back(llvm::make_unique<CompileUnit>(*CU, UnitID++, - !Options.NoODR, "")); - maybeUpdateMaxDwarfVersion(CU->getVersion()); + if (!CUDie || LLVM_UNLIKELY(Options.Update) || + !registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, + LinkContext.Ranges, OffsetsStringPool, + UniquingStringPool, ODRContexts, UnitID)) { + LinkContext.CompileUnits.push_back(llvm::make_unique<CompileUnit>( + *CU, UnitID++, !Options.NoODR && !Options.Update, "")); } } - - // Now build the DIE parent links that we will use during the next phase. - for (auto &CurrentUnit : Units) - analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, *CurrentUnit, - &ODRContexts.getRoot(), StringPool, ODRContexts); - - // Then mark all the DIEs that need to be present in the linked - // output and collect some information about them. Note that this - // loop can not be merged with the previous one becaue cross-cu - // references require the ParentIdx to be setup for every CU in - // the object file before calling this. - for (auto &CurrentUnit : Units) - lookForDIEsToKeep(RelocMgr, CurrentUnit->getOrigUnit().getUnitDIE(), *Obj, - *CurrentUnit, 0); - - // The calls to applyValidRelocs inside cloneDIE will walk the - // reloc array again (in the same way findValidRelocsInDebugInfo() - // did). We need to reset the NextValidReloc index to the beginning. - RelocMgr.resetValidRelocs(); - if (RelocMgr.hasValidRelocs()) - DIECloner(*this, RelocMgr, DIEAlloc, Units, Options) - .cloneAllCompileUnits(*DwarfContext); - if (!Options.NoOutput && !Units.empty()) - patchFrameInfoForObject(*Obj, *DwarfContext, - Units[0]->getOrigUnit().getAddressByteSize()); - - // Clean-up before starting working on the next object. - endDebugObject(); - } - - // Emit everything that's global. - if (!Options.NoOutput) { - Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion); - Streamer->emitStrings(StringPool); } - return Options.NoOutput ? true : Streamer->finish(Map); -} + // If we haven't seen any CUs, pick an arbitrary valid Dwarf version anyway. + if (MaxDwarfVersion == 0) + MaxDwarfVersion = 3; -/// Get the offset of string \p S in the string table. This -/// can insert a new element or return the offset of a preexisitng -/// one. -uint32_t NonRelocatableStringpool::getStringOffset(StringRef S) { - if (S.empty() && !Strings.empty()) - return 0; + // These variables manage the list of processed object files. + // The mutex and condition variable are to ensure that this is thread safe. + std::mutex ProcessedFilesMutex; + std::condition_variable ProcessedFilesConditionVariable; + BitVector ProcessedFiles(NumObjects, false); - std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr); - MapTy::iterator It; - bool Inserted; - - // A non-empty string can't be at offset 0, so if we have an entry - // with a 0 offset, it must be a previously interned string. - std::tie(It, Inserted) = Strings.insert(std::make_pair(S, Entry)); - if (Inserted || It->getValue().first == 0) { - // Set offset and chain at the end of the entries list. - It->getValue().first = CurrentEndOffset; - CurrentEndOffset += S.size() + 1; // +1 for the '\0'. - Last->getValue().second = &*It; - Last = &*It; - } - return It->getValue().first; -} + // Now do analyzeContextInfo in parallel as it is particularly expensive. + auto AnalyzeLambda = [&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + auto &LinkContext = ObjectContexts[i]; -/// Put \p S into the StringMap so that it gets permanent -/// storage, but do not actually link it in the chain of elements -/// that go into the output section. A latter call to -/// getStringOffset() with the same string will chain it though. -StringRef NonRelocatableStringpool::internString(StringRef S) { - std::pair<uint32_t, StringMapEntryBase *> Entry(0, nullptr); - auto InsertResult = Strings.insert(std::make_pair(S, Entry)); - return InsertResult.first->getKey(); -} + if (!LinkContext.ObjectFile) { + std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex); + ProcessedFiles.set(i); + ProcessedFilesConditionVariable.notify_one(); + continue; + } -void warn(const Twine &Warning, const Twine &Context) { - errs() << Twine("while processing ") + Context + ":\n"; - errs() << Twine("warning: ") + Warning + "\n"; -} + // Now build the DIE parent links that we will use during the next phase. + for (auto &CurrentUnit : LinkContext.CompileUnits) { + auto CUDie = CurrentUnit->getOrigUnit().getUnitDIE(); + if (!CUDie) + continue; + analyzeContextInfo(CurrentUnit->getOrigUnit().getUnitDIE(), 0, + *CurrentUnit, &ODRContexts.getRoot(), + UniquingStringPool, ODRContexts); + } -bool error(const Twine &Error, const Twine &Context) { - errs() << Twine("while processing ") + Context + ":\n"; - errs() << Twine("error: ") + Error + "\n"; - return false; -} + std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex); + ProcessedFiles.set(i); + ProcessedFilesConditionVariable.notify_one(); + } + }; + + // And then the remaining work in serial again. + // Note, although this loop runs in serial, it can run in parallel with + // the analyzeContextInfo loop so long as we process files with indices >= + // than those processed by analyzeContextInfo. + auto CloneLambda = [&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + { + std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex); + if (!ProcessedFiles[i]) { + ProcessedFilesConditionVariable.wait( + LockGuard, [&]() { return ProcessedFiles[i]; }); + } + } + + auto &LinkContext = ObjectContexts[i]; + if (!LinkContext.ObjectFile) + continue; + + // Then mark all the DIEs that need to be present in the linked output + // and collect some information about them. + // Note that this loop can not be merged with the previous one because + // cross-cu references require the ParentIdx to be setup for every CU in + // the object file before calling this. + if (LLVM_UNLIKELY(Options.Update)) { + for (auto &CurrentUnit : LinkContext.CompileUnits) + CurrentUnit->markEverythingAsKept(); + Streamer->copyInvariantDebugSection(*LinkContext.ObjectFile); + } else { + for (auto &CurrentUnit : LinkContext.CompileUnits) + lookForDIEsToKeep(LinkContext.RelocMgr, LinkContext.Ranges, + LinkContext.CompileUnits, + CurrentUnit->getOrigUnit().getUnitDIE(), + LinkContext.DMO, *CurrentUnit, 0); + } + + // The calls to applyValidRelocs inside cloneDIE will walk the reloc + // array again (in the same way findValidRelocsInDebugInfo() did). We + // need to reset the NextValidReloc index to the beginning. + LinkContext.RelocMgr.resetValidRelocs(); + if (LinkContext.RelocMgr.hasValidRelocs() || + LLVM_UNLIKELY(Options.Update)) + DIECloner(*this, LinkContext.RelocMgr, DIEAlloc, + LinkContext.CompileUnits, Options) + .cloneAllCompileUnits(*LinkContext.DwarfContext, LinkContext.DMO, + LinkContext.Ranges, OffsetsStringPool); + if (!Options.NoOutput && !LinkContext.CompileUnits.empty() && + LLVM_LIKELY(!Options.Update)) + patchFrameInfoForObject( + LinkContext.DMO, LinkContext.Ranges, *LinkContext.DwarfContext, + LinkContext.CompileUnits[0]->getOrigUnit().getAddressByteSize()); + + // Clean-up before starting working on the next object. + endDebugObject(LinkContext); + } + + // Emit everything that's global. + if (!Options.NoOutput) { + Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion); + Streamer->emitStrings(OffsetsStringPool); + switch (Options.TheAccelTableKind) { + case AccelTableKind::Apple: + Streamer->emitAppleNames(AppleNames); + Streamer->emitAppleNamespaces(AppleNamespaces); + Streamer->emitAppleTypes(AppleTypes); + Streamer->emitAppleObjc(AppleObjc); + break; + case AccelTableKind::Dwarf: + Streamer->emitDebugNames(DebugNames); + break; + case AccelTableKind::Default: + llvm_unreachable("Default should have already been resolved."); + break; + } + } + }; + + // FIXME: The DwarfLinker can have some very deep recursion that can max + // out the (significantly smaller) stack when using threads. We don't + // want this limitation when we only have a single thread. + if (Options.Threads == 1) { + AnalyzeLambda(); + CloneLambda(); + } else { + ThreadPool pool(2); + pool.async(AnalyzeLambda); + pool.async(CloneLambda); + pool.wait(); + } + + return Options.NoOutput ? true : Streamer->finish(Map); +} // namespace dsymutil -bool linkDwarf(raw_fd_ostream &OutFile, const DebugMap &DM, - const LinkOptions &Options) { - DwarfLinker Linker(OutFile, Options); +bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, + const DebugMap &DM, const LinkOptions &Options) { + DwarfLinker Linker(OutFile, BinHolder, Options); return Linker.link(DM); } -} // end namespace dsymutil -} // end namespace llvm +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/DwarfLinker.h b/tools/dsymutil/DwarfLinker.h new file mode 100644 index 000000000000..4097acc568ad --- /dev/null +++ b/tools/dsymutil/DwarfLinker.h @@ -0,0 +1,492 @@ +//===- tools/dsymutil/DwarfLinker.h - Dwarf debug info linker ---*- 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_DSYMUTIL_DWARFLINKER_H +#define LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H + +#include "BinaryHolder.h" +#include "CompileUnit.h" +#include "DebugMap.h" +#include "DeclContext.h" +#include "DwarfStreamer.h" +#include "LinkUtils.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" + +namespace llvm { +namespace dsymutil { + +/// Partial address range for debug map objects. Besides an offset, only the +/// HighPC is stored. The structure is stored in a map where the LowPC is the +/// key. +struct DebugMapObjectRange { + /// Function HighPC. + uint64_t HighPC; + /// Offset to apply to the linked address. + int64_t Offset; + + DebugMapObjectRange(uint64_t EndPC, int64_t Offset) + : HighPC(EndPC), Offset(Offset) {} + + DebugMapObjectRange() : HighPC(0), Offset(0) {} +}; + +/// Map LowPC to DebugMapObjectRange. +using RangesTy = std::map<uint64_t, DebugMapObjectRange>; +using UnitListTy = std::vector<std::unique_ptr<CompileUnit>>; + +/// The core of the Dwarf linking logic. +/// +/// The link of the dwarf information from the object files will be +/// driven by the selection of 'root DIEs', which are DIEs that +/// describe variables or functions that are present in the linked +/// binary (and thus have entries in the debug map). All the debug +/// information that will be linked (the DIEs, but also the line +/// tables, ranges, ...) is derived from that set of root DIEs. +/// +/// The root DIEs are identified because they contain relocations that +/// correspond to a debug map entry at specific places (the low_pc for +/// a function, the location for a variable). These relocations are +/// called ValidRelocs in the DwarfLinker and are gathered as a very +/// first step when we start processing a DebugMapObject. +class DwarfLinker { +public: + DwarfLinker(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, + const LinkOptions &Options) + : OutFile(OutFile), BinHolder(BinHolder), Options(Options) {} + + /// Link the contents of the DebugMap. + bool link(const DebugMap &); + + void reportWarning(const Twine &Warning, const DebugMapObject &DMO, + const DWARFDie *DIE = nullptr) const; + +private: + /// Remembers the oldest and newest DWARF version we've seen in a unit. + void updateDwarfVersion(unsigned Version) { + MaxDwarfVersion = std::max(MaxDwarfVersion, Version); + MinDwarfVersion = std::min(MinDwarfVersion, Version); + } + + /// Remembers the kinds of accelerator tables we've seen in a unit. + void updateAccelKind(DWARFContext &Dwarf); + + /// Emit warnings as Dwarf compile units to leave a trail after linking. + bool emitPaperTrailWarnings(const DebugMapObject &DMO, const DebugMap &Map, + OffsetsStringPool &StringPool); + + /// Keeps track of relocations. + class RelocationManager { + struct ValidReloc { + uint32_t Offset; + uint32_t Size; + uint64_t Addend; + const DebugMapObject::DebugMapEntry *Mapping; + + ValidReloc(uint32_t Offset, uint32_t Size, uint64_t Addend, + const DebugMapObject::DebugMapEntry *Mapping) + : Offset(Offset), Size(Size), Addend(Addend), Mapping(Mapping) {} + + bool operator<(const ValidReloc &RHS) const { + return Offset < RHS.Offset; + } + }; + + const DwarfLinker &Linker; + + /// The valid relocations for the current DebugMapObject. + /// This vector is sorted by relocation offset. + std::vector<ValidReloc> ValidRelocs; + + /// Index into ValidRelocs of the next relocation to consider. As we walk + /// the DIEs in acsending file offset and as ValidRelocs is sorted by file + /// offset, keeping this index up to date is all we have to do to have a + /// cheap lookup during the root DIE selection and during DIE cloning. + unsigned NextValidReloc = 0; + + public: + RelocationManager(DwarfLinker &Linker) : Linker(Linker) {} + + bool hasValidRelocs() const { return !ValidRelocs.empty(); } + + /// Reset the NextValidReloc counter. + void resetValidRelocs() { NextValidReloc = 0; } + + /// \defgroup FindValidRelocations Translate debug map into a list + /// of relevant relocations + /// + /// @{ + bool findValidRelocsInDebugInfo(const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + bool findValidRelocs(const object::SectionRef &Section, + const object::ObjectFile &Obj, + const DebugMapObject &DMO); + + void findValidRelocsMachO(const object::SectionRef &Section, + const object::MachOObjectFile &Obj, + const DebugMapObject &DMO); + /// @} + + bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info); + + bool applyValidRelocs(MutableArrayRef<char> Data, uint32_t BaseOffset, + bool isLittleEndian); + }; + + /// Keeps track of data associated with one object during linking. + struct LinkContext { + DebugMapObject &DMO; + const object::ObjectFile *ObjectFile; + RelocationManager RelocMgr; + std::unique_ptr<DWARFContext> DwarfContext; + RangesTy Ranges; + UnitListTy CompileUnits; + + LinkContext(const DebugMap &Map, DwarfLinker &Linker, DebugMapObject &DMO) + : DMO(DMO), RelocMgr(Linker) { + // Swift ASTs are not object files. + if (DMO.getType() == MachO::N_AST) { + ObjectFile = nullptr; + return; + } + auto ErrOrObj = Linker.loadObject(DMO, Map); + ObjectFile = ErrOrObj ? &*ErrOrObj : nullptr; + DwarfContext = ObjectFile ? DWARFContext::create(*ObjectFile) : nullptr; + } + + /// Clear part of the context that's no longer needed when we're done with + /// the debug object. + void Clear() { + DwarfContext.reset(nullptr); + CompileUnits.clear(); + Ranges.clear(); + } + }; + + /// Called at the start of a debug object link. + void startDebugObject(LinkContext &Context); + + /// Called at the end of a debug object link. + void endDebugObject(LinkContext &Context); + + /// \defgroup FindRootDIEs Find DIEs corresponding to debug map entries. + /// + /// @{ + /// Recursively walk the \p DIE tree and look for DIEs to + /// keep. Store that information in \p CU's DIEInfo. + /// + /// The return value indicates whether the DIE is incomplete. + bool lookForDIEsToKeep(RelocationManager &RelocMgr, RangesTy &Ranges, + const UnitListTy &Units, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &CU, + unsigned Flags); + + /// If this compile unit is really a skeleton CU that points to a + /// clang module, register it in ClangModules and return true. + /// + /// A skeleton CU is a CU without children, a DW_AT_gnu_dwo_name + /// pointing to the module, and a DW_AT_gnu_dwo_id with the module + /// hash. + bool registerModuleReference(const DWARFDie &CUDie, const DWARFUnit &Unit, + DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, + OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPoolStringPool, + DeclContextTree &ODRContexts, unsigned &UnitID, + unsigned Indent = 0); + + /// Recursively add the debug info in this clang module .pcm + /// file (and all the modules imported by it in a bottom-up fashion) + /// to Units. + Error loadClangModule(StringRef Filename, StringRef ModulePath, + StringRef ModuleName, uint64_t DwoId, + DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, + UniquingStringPool &UniquingStringPool, + DeclContextTree &ODRContexts, unsigned &UnitID, + unsigned Indent = 0); + + /// Flags passed to DwarfLinker::lookForDIEsToKeep + enum TraversalFlags { + TF_Keep = 1 << 0, ///< Mark the traversed DIEs as kept. + TF_InFunctionScope = 1 << 1, ///< Current scope is a function scope. + TF_DependencyWalk = 1 << 2, ///< Walking the dependencies of a kept DIE. + TF_ParentWalk = 1 << 3, ///< Walking up the parents of a kept DIE. + TF_ODR = 1 << 4, ///< Use the ODR while keeping dependents. + TF_SkipPC = 1 << 5, ///< Skip all location attributes. + }; + + /// Mark the passed DIE as well as all the ones it depends on as kept. + void keepDIEAndDependencies(RelocationManager &RelocMgr, RangesTy &Ranges, + const UnitListTy &Units, const DWARFDie &DIE, + CompileUnit::DIEInfo &MyInfo, + const DebugMapObject &DMO, CompileUnit &CU, + bool UseODR); + + unsigned shouldKeepDIE(RelocationManager &RelocMgr, RangesTy &Ranges, + const DWARFDie &DIE, const DebugMapObject &DMO, + CompileUnit &Unit, CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + unsigned shouldKeepVariableDIE(RelocationManager &RelocMgr, + const DWARFDie &DIE, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, unsigned Flags); + + unsigned shouldKeepSubprogramDIE(RelocationManager &RelocMgr, + RangesTy &Ranges, const DWARFDie &DIE, + const DebugMapObject &DMO, CompileUnit &Unit, + CompileUnit::DIEInfo &MyInfo, + unsigned Flags); + + bool hasValidRelocation(uint32_t StartOffset, uint32_t EndOffset, + CompileUnit::DIEInfo &Info); + /// @} + + /// \defgroup Linking Methods used to link the debug information + /// + /// @{ + + class DIECloner { + DwarfLinker &Linker; + RelocationManager &RelocMgr; + + /// Allocator used for all the DIEValue objects. + BumpPtrAllocator &DIEAlloc; + + std::vector<std::unique_ptr<CompileUnit>> &CompileUnits; + LinkOptions Options; + + public: + DIECloner(DwarfLinker &Linker, RelocationManager &RelocMgr, + BumpPtrAllocator &DIEAlloc, + std::vector<std::unique_ptr<CompileUnit>> &CompileUnits, + LinkOptions &Options) + : Linker(Linker), RelocMgr(RelocMgr), DIEAlloc(DIEAlloc), + CompileUnits(CompileUnits), Options(Options) {} + + /// Recursively clone \p InputDIE into an tree of DIE objects + /// where useless (as decided by lookForDIEsToKeep()) bits have been + /// stripped out and addresses have been rewritten according to the + /// debug map. + /// + /// \param OutOffset is the offset the cloned DIE in the output + /// compile unit. + /// \param PCOffset (while cloning a function scope) is the offset + /// applied to the entry point of the function to get the linked address. + /// \param Die the output DIE to use, pass NULL to create one. + /// \returns the root of the cloned tree or null if nothing was selected. + DIE *cloneDIE(const DWARFDie &InputDIE, const DebugMapObject &DMO, + CompileUnit &U, OffsetsStringPool &StringPool, + int64_t PCOffset, uint32_t OutOffset, unsigned Flags, + DIE *Die = nullptr); + + /// Construct the output DIE tree by cloning the DIEs we + /// chose to keep above. If there are no valid relocs, then there's + /// nothing to clone/emit. + void cloneAllCompileUnits(DWARFContext &DwarfContext, + const DebugMapObject &DMO, RangesTy &Ranges, + OffsetsStringPool &StringPool); + + private: + using AttributeSpec = DWARFAbbreviationDeclaration::AttributeSpec; + + /// Information gathered and exchanged between the various + /// clone*Attributes helpers about the attributes of a particular DIE. + struct AttributesInfo { + /// Names. + DwarfStringPoolEntryRef Name, MangledName, NameWithoutTemplate; + + /// Offsets in the string pool. + uint32_t NameOffset = 0; + uint32_t MangledNameOffset = 0; + + /// Value of AT_low_pc in the input DIE + uint64_t OrigLowPc = std::numeric_limits<uint64_t>::max(); + + /// Value of AT_high_pc in the input DIE + uint64_t OrigHighPc = 0; + + /// Offset to apply to PC addresses inside a function. + int64_t PCOffset = 0; + + /// Does the DIE have a low_pc attribute? + bool HasLowPc = false; + + /// Does the DIE have a ranges attribute? + bool HasRanges = false; + + /// Is this DIE only a declaration? + bool IsDeclaration = false; + + AttributesInfo() = default; + }; + + /// Helper for cloneDIE. + unsigned cloneAttribute(DIE &Die, const DWARFDie &InputDIE, + const DebugMapObject &DMO, CompileUnit &U, + OffsetsStringPool &StringPool, + const DWARFFormValue &Val, + const AttributeSpec AttrSpec, unsigned AttrSize, + AttributesInfo &AttrInfo); + + /// Clone a string attribute described by \p AttrSpec and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneStringAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, const DWARFUnit &U, + OffsetsStringPool &StringPool, + AttributesInfo &Info); + + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneDieReferenceAttribute(DIE &Die, const DWARFDie &InputDIE, + AttributeSpec AttrSpec, + unsigned AttrSize, + const DWARFFormValue &Val, + const DebugMapObject &DMO, + CompileUnit &Unit); + + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneBlockAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize); + + /// Clone an attribute referencing another DIE and add + /// it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneAddressAttribute(DIE &Die, AttributeSpec AttrSpec, + const DWARFFormValue &Val, + const CompileUnit &Unit, + AttributesInfo &Info); + + /// Clone a scalar attribute and add it to \p Die. + /// \returns the size of the new attribute. + unsigned cloneScalarAttribute(DIE &Die, const DWARFDie &InputDIE, + const DebugMapObject &DMO, CompileUnit &U, + AttributeSpec AttrSpec, + const DWARFFormValue &Val, unsigned AttrSize, + AttributesInfo &Info); + + /// Get the potential name and mangled name for the entity + /// described by \p Die and store them in \Info if they are not + /// already there. + /// \returns is a name was found. + bool getDIENames(const DWARFDie &Die, AttributesInfo &Info, + OffsetsStringPool &StringPool, bool StripTemplate = false); + + /// Create a copy of abbreviation Abbrev. + void copyAbbrev(const DWARFAbbreviationDeclaration &Abbrev, bool hasODR); + + uint32_t hashFullyQualifiedName(DWARFDie DIE, CompileUnit &U, + const DebugMapObject &DMO, + int RecurseDepth = 0); + + /// Helper for cloneDIE. + void addObjCAccelerator(CompileUnit &Unit, const DIE *Die, + DwarfStringPoolEntryRef Name, + OffsetsStringPool &StringPool, bool SkipPubSection); + }; + + /// Assign an abbreviation number to \p Abbrev + void AssignAbbrev(DIEAbbrev &Abbrev); + + /// Compute and emit debug_ranges section for \p Unit, and + /// patch the attributes referencing it. + void patchRangesForUnit(const CompileUnit &Unit, DWARFContext &Dwarf, + const DebugMapObject &DMO) const; + + /// Generate and emit the DW_AT_ranges attribute for a compile_unit if it had + /// one. + void generateUnitRanges(CompileUnit &Unit) const; + + /// Extract the line tables from the original dwarf, extract the relevant + /// parts according to the linked function ranges and emit the result in the + /// debug_line section. + void patchLineTableForUnit(CompileUnit &Unit, DWARFContext &OrigDwarf, + RangesTy &Ranges, const DebugMapObject &DMO); + + /// Emit the accelerator entries for \p Unit. + void emitAcceleratorEntriesForUnit(CompileUnit &Unit); + void emitDwarfAcceleratorEntriesForUnit(CompileUnit &Unit); + void emitAppleAcceleratorEntriesForUnit(CompileUnit &Unit); + + /// Patch the frame info for an object file and emit it. + void patchFrameInfoForObject(const DebugMapObject &, RangesTy &Ranges, + DWARFContext &, unsigned AddressSize); + + /// FoldingSet that uniques the abbreviations. + FoldingSet<DIEAbbrev> AbbreviationsSet; + + /// Storage for the unique Abbreviations. + /// This is passed to AsmPrinter::emitDwarfAbbrevs(), thus it cannot be + /// changed to a vector of unique_ptrs. + std::vector<std::unique_ptr<DIEAbbrev>> Abbreviations; + + /// DIELoc objects that need to be destructed (but not freed!). + std::vector<DIELoc *> DIELocs; + + /// DIEBlock objects that need to be destructed (but not freed!). + std::vector<DIEBlock *> DIEBlocks; + + /// Allocator used for all the DIEValue objects. + BumpPtrAllocator DIEAlloc; + /// @} + + /// \defgroup Helpers Various helper methods. + /// + /// @{ + bool createStreamer(const Triple &TheTriple, raw_fd_ostream &OutFile); + + /// Attempt to load a debug object from disk. + ErrorOr<const object::ObjectFile &> loadObject(const DebugMapObject &Obj, + const DebugMap &Map); + /// @} + + raw_fd_ostream &OutFile; + BinaryHolder &BinHolder; + LinkOptions Options; + std::unique_ptr<DwarfStreamer> Streamer; + uint64_t OutputDebugInfoSize; + + unsigned MaxDwarfVersion = 0; + unsigned MinDwarfVersion = std::numeric_limits<unsigned>::max(); + + bool AtLeastOneAppleAccelTable = false; + bool AtLeastOneDwarfAccelTable = false; + + /// The CIEs that have been emitted in the output section. The actual CIE + /// data serves a the key to this StringMap, this takes care of comparing the + /// semantics of CIEs defined in different object files. + StringMap<uint32_t> EmittedCIEs; + + /// Offset of the last CIE that has been emitted in the output + /// debug_frame section. + uint32_t LastCIEOffset = 0; + + /// Apple accelerator tables. + AccelTable<DWARF5AccelTableStaticData> DebugNames; + AccelTable<AppleAccelTableStaticOffsetData> AppleNames; + AccelTable<AppleAccelTableStaticOffsetData> AppleNamespaces; + AccelTable<AppleAccelTableStaticOffsetData> AppleObjc; + AccelTable<AppleAccelTableStaticTypeData> AppleTypes; + + /// Mapping the PCM filename to the DwoId. + StringMap<uint64_t> ClangModules; + + bool ModuleCacheHintDisplayed = false; + bool ArchiveHintDisplayed = false; +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DWARFLINKER_H diff --git a/tools/dsymutil/DwarfStreamer.cpp b/tools/dsymutil/DwarfStreamer.cpp new file mode 100644 index 000000000000..7350d19e17bf --- /dev/null +++ b/tools/dsymutil/DwarfStreamer.cpp @@ -0,0 +1,684 @@ +//===- tools/dsymutil/DwarfStreamer.cpp - Dwarf Streamer ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "DwarfStreamer.h" +#include "CompileUnit.h" +#include "LinkUtils.h" +#include "MachOUtils.h" +#include "llvm/ADT/Triple.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/MC/MCTargetOptionsCommandFlags.inc" +#include "llvm/Support/LEB128.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +namespace llvm { +namespace dsymutil { + +/// Retrieve the section named \a SecName in \a Obj. +/// +/// To accommodate for platform discrepancies, the name passed should be +/// (for example) 'debug_info' to match either '__debug_info' or '.debug_info'. +/// This function will strip the initial platform-specific characters. +static Optional<object::SectionRef> +getSectionByName(const object::ObjectFile &Obj, StringRef SecName) { + for (const object::SectionRef &Section : Obj.sections()) { + StringRef SectionName; + Section.getName(SectionName); + SectionName = SectionName.substr(SectionName.find_first_not_of("._")); + if (SectionName != SecName) + continue; + return Section; + } + return None; +} + +bool DwarfStreamer::init(Triple TheTriple) { + std::string ErrorStr; + std::string TripleName; + StringRef Context = "dwarf streamer init"; + + // Get the target. + const Target *TheTarget = + TargetRegistry::lookupTarget(TripleName, TheTriple, ErrorStr); + if (!TheTarget) + return error(ErrorStr, Context); + TripleName = TheTriple.getTriple(); + + // Create all the MC Objects. + MRI.reset(TheTarget->createMCRegInfo(TripleName)); + if (!MRI) + return error(Twine("no register info for target ") + TripleName, Context); + + MAI.reset(TheTarget->createMCAsmInfo(*MRI, TripleName)); + if (!MAI) + return error("no asm info for target " + TripleName, Context); + + MOFI.reset(new MCObjectFileInfo); + MC.reset(new MCContext(MAI.get(), MRI.get(), MOFI.get())); + MOFI->InitMCObjectFileInfo(TheTriple, /*PIC*/ false, *MC); + + MSTI.reset(TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return error("no subtarget info for target " + TripleName, Context); + + MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); + MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, MCOptions); + if (!MAB) + return error("no asm backend for target " + TripleName, Context); + + MII.reset(TheTarget->createMCInstrInfo()); + if (!MII) + return error("no instr info info for target " + TripleName, Context); + + MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, *MC); + if (!MCE) + return error("no code emitter for target " + TripleName, Context); + + switch (Options.FileType) { + case OutputFileType::Assembly: { + MIP = TheTarget->createMCInstPrinter(TheTriple, MAI->getAssemblerDialect(), + *MAI, *MII, *MRI); + MS = TheTarget->createAsmStreamer( + *MC, llvm::make_unique<formatted_raw_ostream>(OutFile), true, true, MIP, + std::unique_ptr<MCCodeEmitter>(MCE), std::unique_ptr<MCAsmBackend>(MAB), + true); + break; + } + case OutputFileType::Object: { + MS = TheTarget->createMCObjectStreamer( + TheTriple, *MC, std::unique_ptr<MCAsmBackend>(MAB), + MAB->createObjectWriter(OutFile), std::unique_ptr<MCCodeEmitter>(MCE), + *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, + /*DWARFMustBeAtTheEnd*/ false); + break; + } + } + + if (!MS) + return error("no object streamer for target " + TripleName, Context); + + // Finally create the AsmPrinter we'll use to emit the DIEs. + TM.reset(TheTarget->createTargetMachine(TripleName, "", "", TargetOptions(), + None)); + if (!TM) + return error("no target machine for target " + TripleName, Context); + + Asm.reset(TheTarget->createAsmPrinter(*TM, std::unique_ptr<MCStreamer>(MS))); + if (!Asm) + return error("no asm printer for target " + TripleName, Context); + + RangesSectionSize = 0; + LocSectionSize = 0; + LineSectionSize = 0; + FrameSectionSize = 0; + + return true; +} + +bool DwarfStreamer::finish(const DebugMap &DM) { + bool Result = true; + if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty() && + Options.FileType == OutputFileType::Object) + Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile); + else + MS->Finish(); + return Result; +} + +void DwarfStreamer::switchToDebugInfoSection(unsigned DwarfVersion) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + MC->setDwarfVersion(DwarfVersion); +} + +/// Emit the compilation unit header for \p Unit in the debug_info section. +/// +/// A Dwarf section header is encoded as: +/// uint32_t Unit length (omitting this field) +/// uint16_t Version +/// uint32_t Abbreviation table offset +/// uint8_t Address size +/// +/// Leading to a total of 11 bytes. +void DwarfStreamer::emitCompileUnitHeader(CompileUnit &Unit) { + unsigned Version = Unit.getOrigUnit().getVersion(); + switchToDebugInfoSection(Version); + + /// The start of the unit within its section. + Unit.setLabelBegin(Asm->createTempSymbol("cu_begin")); + Asm->OutStreamer->EmitLabel(Unit.getLabelBegin()); + + // Emit size of content not including length itself. The size has already + // been computed in CompileUnit::computeOffsets(). Subtract 4 to that size to + // account for the length field. + Asm->emitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset() - 4); + Asm->emitInt16(Version); + + // We share one abbreviations table across all units so it's always at the + // start of the section. + Asm->emitInt32(0); + Asm->emitInt8(Unit.getOrigUnit().getAddressByteSize()); + + // Remember this CU. + EmittedUnits.push_back({Unit.getUniqueID(), Unit.getLabelBegin()}); +} + +/// Emit the \p Abbrevs array as the shared abbreviation table +/// for the linked Dwarf file. +void DwarfStreamer::emitAbbrevs( + const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs, + unsigned DwarfVersion) { + MS->SwitchSection(MOFI->getDwarfAbbrevSection()); + MC->setDwarfVersion(DwarfVersion); + Asm->emitDwarfAbbrevs(Abbrevs); +} + +/// Recursively emit the DIE tree rooted at \p Die. +void DwarfStreamer::emitDIE(DIE &Die) { + MS->SwitchSection(MOFI->getDwarfInfoSection()); + Asm->emitDwarfDIE(Die); +} + +/// Emit the debug_str section stored in \p Pool. +void DwarfStreamer::emitStrings(const NonRelocatableStringpool &Pool) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfStrSection()); + std::vector<DwarfStringPoolEntryRef> Entries = Pool.getEntries(); + for (auto Entry : Entries) { + if (Entry.getIndex() == -1U) + break; + // Emit the string itself. + Asm->OutStreamer->EmitBytes(Entry.getString()); + // Emit a null terminator. + Asm->emitInt8(0); + } +} + +void DwarfStreamer::emitDebugNames( + AccelTable<DWARF5AccelTableStaticData> &Table) { + if (EmittedUnits.empty()) + return; + + // Build up data structures needed to emit this section. + std::vector<MCSymbol *> CompUnits; + DenseMap<unsigned, size_t> UniqueIdToCuMap; + unsigned Id = 0; + for (auto &CU : EmittedUnits) { + CompUnits.push_back(CU.LabelBegin); + // We might be omitting CUs, so we need to remap them. + UniqueIdToCuMap[CU.ID] = Id++; + } + + Asm->OutStreamer->SwitchSection(MOFI->getDwarfDebugNamesSection()); + emitDWARF5AccelTable( + Asm.get(), Table, CompUnits, + [&UniqueIdToCuMap](const DWARF5AccelTableStaticData &Entry) { + return UniqueIdToCuMap[Entry.getCUIndex()]; + }); +} + +void DwarfStreamer::emitAppleNamespaces( + AccelTable<AppleAccelTableStaticOffsetData> &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelNamespaceSection()); + auto *SectionBegin = Asm->createTempSymbol("namespac_begin"); + Asm->OutStreamer->EmitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "namespac", SectionBegin); +} + +void DwarfStreamer::emitAppleNames( + AccelTable<AppleAccelTableStaticOffsetData> &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelNamesSection()); + auto *SectionBegin = Asm->createTempSymbol("names_begin"); + Asm->OutStreamer->EmitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "names", SectionBegin); +} + +void DwarfStreamer::emitAppleObjc( + AccelTable<AppleAccelTableStaticOffsetData> &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelObjCSection()); + auto *SectionBegin = Asm->createTempSymbol("objc_begin"); + Asm->OutStreamer->EmitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "objc", SectionBegin); +} + +void DwarfStreamer::emitAppleTypes( + AccelTable<AppleAccelTableStaticTypeData> &Table) { + Asm->OutStreamer->SwitchSection(MOFI->getDwarfAccelTypesSection()); + auto *SectionBegin = Asm->createTempSymbol("types_begin"); + Asm->OutStreamer->EmitLabel(SectionBegin); + emitAppleAccelTable(Asm.get(), Table, "types", SectionBegin); +} + +/// Emit the swift_ast section stored in \p Buffers. +void DwarfStreamer::emitSwiftAST(StringRef Buffer) { + MCSection *SwiftASTSection = MOFI->getDwarfSwiftASTSection(); + SwiftASTSection->setAlignment(1 << 5); + MS->SwitchSection(SwiftASTSection); + MS->EmitBytes(Buffer); +} + +/// Emit the debug_range section contents for \p FuncRange by +/// translating the original \p Entries. The debug_range section +/// format is totally trivial, consisting just of pairs of address +/// sized addresses describing the ranges. +void DwarfStreamer::emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries, + unsigned AddressSize) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + + // Offset each range by the right amount. + int64_t PcOffset = Entries.empty() ? 0 : FuncRange.value() + UnitPcOffset; + for (const auto &Range : Entries) { + if (Range.isBaseAddressSelectionEntry(AddressSize)) { + warn("unsupported base address selection operation", + "emitting debug_ranges"); + break; + } + // Do not emit empty ranges. + if (Range.StartAddress == Range.EndAddress) + continue; + + // All range entries should lie in the function range. + if (!(Range.StartAddress + OrigLowPc >= FuncRange.start() && + Range.EndAddress + OrigLowPc <= FuncRange.stop())) + warn("inconsistent range data.", "emitting debug_ranges"); + MS->EmitIntValue(Range.StartAddress + PcOffset, AddressSize); + MS->EmitIntValue(Range.EndAddress + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->EmitIntValue(0, AddressSize); + MS->EmitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// Emit the debug_aranges contribution of a unit and +/// if \p DoDebugRanges is true the debug_range contents for a +/// compile_unit level DW_AT_ranges attribute (Which are basically the +/// same thing with a different base address). +/// Just aggregate all the ranges gathered inside that unit. +void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit, + bool DoDebugRanges) { + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + // Gather the ranges in a vector, so that we can simplify them. The + // IntervalMap will have coalesced the non-linked ranges, but here + // we want to coalesce the linked addresses. + std::vector<std::pair<uint64_t, uint64_t>> Ranges; + const auto &FunctionRanges = Unit.getFunctionRanges(); + for (auto Range = FunctionRanges.begin(), End = FunctionRanges.end(); + Range != End; ++Range) + Ranges.push_back(std::make_pair(Range.start() + Range.value(), + Range.stop() + Range.value())); + + // The object addresses where sorted, but again, the linked + // addresses might end up in a different order. + llvm::sort(Ranges.begin(), Ranges.end()); + + if (!Ranges.empty()) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); + + MCSymbol *BeginLabel = Asm->createTempSymbol("Barange"); + MCSymbol *EndLabel = Asm->createTempSymbol("Earange"); + + unsigned HeaderSize = + sizeof(int32_t) + // Size of contents (w/o this field + sizeof(int16_t) + // DWARF ARange version number + sizeof(int32_t) + // Offset of CU in the .debug_info section + sizeof(int8_t) + // Pointer Size (in bytes) + sizeof(int8_t); // Segment Size (in bytes) + + unsigned TupleSize = AddressSize * 2; + unsigned Padding = OffsetToAlignment(HeaderSize, TupleSize); + + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Arange length + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->emitInt16(dwarf::DW_ARANGES_VERSION); // Version number + Asm->emitInt32(Unit.getStartOffset()); // Corresponding unit's offset + Asm->emitInt8(AddressSize); // Address size + Asm->emitInt8(0); // Segment size + + Asm->OutStreamer->emitFill(Padding, 0x0); + + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; + ++Range) { + uint64_t RangeStart = Range->first; + MS->EmitIntValue(RangeStart, AddressSize); + while ((Range + 1) != End && Range->second == (Range + 1)->first) + ++Range; + MS->EmitIntValue(Range->second - RangeStart, AddressSize); + } + + // Emit terminator + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitLabel(EndLabel); + } + + if (!DoDebugRanges) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + // Offset each range by the right amount. + int64_t PcOffset = -Unit.getLowPc(); + // Emit coalesced ranges. + for (auto Range = Ranges.begin(), End = Ranges.end(); Range != End; ++Range) { + MS->EmitIntValue(Range->first + PcOffset, AddressSize); + while (Range + 1 != End && Range->second == (Range + 1)->first) + ++Range; + MS->EmitIntValue(Range->second + PcOffset, AddressSize); + RangesSectionSize += 2 * AddressSize; + } + + // Add the terminator entry. + MS->EmitIntValue(0, AddressSize); + MS->EmitIntValue(0, AddressSize); + RangesSectionSize += 2 * AddressSize; +} + +/// Emit location lists for \p Unit and update attributes to point to the new +/// entries. +void DwarfStreamer::emitLocationsForUnit(const CompileUnit &Unit, + DWARFContext &Dwarf) { + const auto &Attributes = Unit.getLocationAttributes(); + + if (Attributes.empty()) + return; + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + + unsigned AddressSize = Unit.getOrigUnit().getAddressByteSize(); + const DWARFSection &InputSec = Dwarf.getDWARFObj().getLocSection(); + DataExtractor Data(InputSec.Data, Dwarf.isLittleEndian(), AddressSize); + DWARFUnit &OrigUnit = Unit.getOrigUnit(); + auto OrigUnitDie = OrigUnit.getUnitDIE(false); + int64_t UnitPcOffset = 0; + if (auto OrigLowPc = dwarf::toAddress(OrigUnitDie.find(dwarf::DW_AT_low_pc))) + UnitPcOffset = int64_t(*OrigLowPc) - Unit.getLowPc(); + + for (const auto &Attr : Attributes) { + uint32_t Offset = Attr.first.get(); + Attr.first.set(LocSectionSize); + // This is the quantity to add to the old location address to get + // the correct address for the new one. + int64_t LocPcOffset = Attr.second + UnitPcOffset; + while (Data.isValidOffset(Offset)) { + uint64_t Low = Data.getUnsigned(&Offset, AddressSize); + uint64_t High = Data.getUnsigned(&Offset, AddressSize); + LocSectionSize += 2 * AddressSize; + if (Low == 0 && High == 0) { + Asm->OutStreamer->EmitIntValue(0, AddressSize); + Asm->OutStreamer->EmitIntValue(0, AddressSize); + break; + } + Asm->OutStreamer->EmitIntValue(Low + LocPcOffset, AddressSize); + Asm->OutStreamer->EmitIntValue(High + LocPcOffset, AddressSize); + uint64_t Length = Data.getU16(&Offset); + Asm->OutStreamer->EmitIntValue(Length, 2); + // Just copy the bytes over. + Asm->OutStreamer->EmitBytes( + StringRef(InputSec.Data.substr(Offset, Length))); + Offset += Length; + LocSectionSize += Length + 2; + } + } +} + +void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, + unsigned MinInstLength, + std::vector<DWARFDebugLine::Row> &Rows, + unsigned PointerSize) { + // Switch to the section where the table will be emitted into. + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + MCSymbol *LineStartSym = MC->createTempSymbol(); + MCSymbol *LineEndSym = MC->createTempSymbol(); + + // The first 4 bytes is the total length of the information for this + // compilation unit (not including these 4 bytes for the length). + Asm->EmitLabelDifference(LineEndSym, LineStartSym, 4); + Asm->OutStreamer->EmitLabel(LineStartSym); + // Copy Prologue. + MS->EmitBytes(PrologueBytes); + LineSectionSize += PrologueBytes.size() + 4; + + SmallString<128> EncodingBuffer; + raw_svector_ostream EncodingOS(EncodingBuffer); + + if (Rows.empty()) { + // We only have the dummy entry, dsymutil emits an entry with a 0 + // address in that case. + MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits<int64_t>::max(), 0, + EncodingOS); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + MS->EmitLabel(LineEndSym); + return; + } + + // Line table state machine fields + unsigned FileNum = 1; + unsigned LastLine = 1; + unsigned Column = 0; + unsigned IsStatement = 1; + unsigned Isa = 0; + uint64_t Address = -1ULL; + + unsigned RowsSinceLastSequence = 0; + + for (unsigned Idx = 0; Idx < Rows.size(); ++Idx) { + auto &Row = Rows[Idx]; + + int64_t AddressDelta; + if (Address == -1ULL) { + MS->EmitIntValue(dwarf::DW_LNS_extended_op, 1); + MS->EmitULEB128IntValue(PointerSize + 1); + MS->EmitIntValue(dwarf::DW_LNE_set_address, 1); + MS->EmitIntValue(Row.Address, PointerSize); + LineSectionSize += 2 + PointerSize + getULEB128Size(PointerSize + 1); + AddressDelta = 0; + } else { + AddressDelta = (Row.Address - Address) / MinInstLength; + } + + // FIXME: code copied and transformed from MCDwarf.cpp::EmitDwarfLineTable. + // We should find a way to share this code, but the current compatibility + // requirement with classic dsymutil makes it hard. Revisit that once this + // requirement is dropped. + + if (FileNum != Row.File) { + FileNum = Row.File; + MS->EmitIntValue(dwarf::DW_LNS_set_file, 1); + MS->EmitULEB128IntValue(FileNum); + LineSectionSize += 1 + getULEB128Size(FileNum); + } + if (Column != Row.Column) { + Column = Row.Column; + MS->EmitIntValue(dwarf::DW_LNS_set_column, 1); + MS->EmitULEB128IntValue(Column); + LineSectionSize += 1 + getULEB128Size(Column); + } + + // FIXME: We should handle the discriminator here, but dsymutil doesn't + // consider it, thus ignore it for now. + + if (Isa != Row.Isa) { + Isa = Row.Isa; + MS->EmitIntValue(dwarf::DW_LNS_set_isa, 1); + MS->EmitULEB128IntValue(Isa); + LineSectionSize += 1 + getULEB128Size(Isa); + } + if (IsStatement != Row.IsStmt) { + IsStatement = Row.IsStmt; + MS->EmitIntValue(dwarf::DW_LNS_negate_stmt, 1); + LineSectionSize += 1; + } + if (Row.BasicBlock) { + MS->EmitIntValue(dwarf::DW_LNS_set_basic_block, 1); + LineSectionSize += 1; + } + + if (Row.PrologueEnd) { + MS->EmitIntValue(dwarf::DW_LNS_set_prologue_end, 1); + LineSectionSize += 1; + } + + if (Row.EpilogueBegin) { + MS->EmitIntValue(dwarf::DW_LNS_set_epilogue_begin, 1); + LineSectionSize += 1; + } + + int64_t LineDelta = int64_t(Row.Line) - LastLine; + if (!Row.EndSequence) { + MCDwarfLineAddr::Encode(*MC, Params, LineDelta, AddressDelta, EncodingOS); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + Address = Row.Address; + LastLine = Row.Line; + RowsSinceLastSequence++; + } else { + if (LineDelta) { + MS->EmitIntValue(dwarf::DW_LNS_advance_line, 1); + MS->EmitSLEB128IntValue(LineDelta); + LineSectionSize += 1 + getSLEB128Size(LineDelta); + } + if (AddressDelta) { + MS->EmitIntValue(dwarf::DW_LNS_advance_pc, 1); + MS->EmitULEB128IntValue(AddressDelta); + LineSectionSize += 1 + getULEB128Size(AddressDelta); + } + MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits<int64_t>::max(), + 0, EncodingOS); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + Address = -1ULL; + LastLine = FileNum = IsStatement = 1; + RowsSinceLastSequence = Column = Isa = 0; + } + } + + if (RowsSinceLastSequence) { + MCDwarfLineAddr::Encode(*MC, Params, std::numeric_limits<int64_t>::max(), 0, + EncodingOS); + MS->EmitBytes(EncodingOS.str()); + LineSectionSize += EncodingBuffer.size(); + EncodingBuffer.resize(0); + } + + MS->EmitLabel(LineEndSym); +} + +static void emitSectionContents(const object::ObjectFile &Obj, + StringRef SecName, MCStreamer *MS) { + StringRef Contents; + if (auto Sec = getSectionByName(Obj, SecName)) + if (!Sec->getContents(Contents)) + MS->EmitBytes(Contents); +} + +void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + emitSectionContents(Obj, "debug_line", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); + emitSectionContents(Obj, "debug_loc", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfRangesSection()); + emitSectionContents(Obj, "debug_ranges", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + emitSectionContents(Obj, "debug_frame", MS); + + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); + emitSectionContents(Obj, "debug_aranges", MS); +} + +/// Emit the pubnames or pubtypes section contribution for \p +/// Unit into \p Sec. The data is provided in \p Names. +void DwarfStreamer::emitPubSectionForUnit( + MCSection *Sec, StringRef SecName, const CompileUnit &Unit, + const std::vector<CompileUnit::AccelInfo> &Names) { + if (Names.empty()) + return; + + // Start the dwarf pubnames section. + Asm->OutStreamer->SwitchSection(Sec); + MCSymbol *BeginLabel = Asm->createTempSymbol("pub" + SecName + "_begin"); + MCSymbol *EndLabel = Asm->createTempSymbol("pub" + SecName + "_end"); + + bool HeaderEmitted = false; + // Emit the pubnames for this compilation unit. + for (const auto &Name : Names) { + if (Name.SkipPubSection) + continue; + + if (!HeaderEmitted) { + // Emit the header. + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Length + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->emitInt16(dwarf::DW_PUBNAMES_VERSION); // Version + Asm->emitInt32(Unit.getStartOffset()); // Unit offset + Asm->emitInt32(Unit.getNextUnitOffset() - Unit.getStartOffset()); // Size + HeaderEmitted = true; + } + Asm->emitInt32(Name.Die->getOffset()); + + // Emit the string itself. + Asm->OutStreamer->EmitBytes(Name.Name.getString()); + // Emit a null terminator. + Asm->emitInt8(0); + } + + if (!HeaderEmitted) + return; + Asm->emitInt32(0); // End marker. + Asm->OutStreamer->EmitLabel(EndLabel); +} + +/// Emit .debug_pubnames for \p Unit. +void DwarfStreamer::emitPubNamesForUnit(const CompileUnit &Unit) { + emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubNamesSection(), + "names", Unit, Unit.getPubnames()); +} + +/// Emit .debug_pubtypes for \p Unit. +void DwarfStreamer::emitPubTypesForUnit(const CompileUnit &Unit) { + emitPubSectionForUnit(MC->getObjectFileInfo()->getDwarfPubTypesSection(), + "types", Unit, Unit.getPubtypes()); +} + +/// Emit a CIE into the debug_frame section. +void DwarfStreamer::emitCIE(StringRef CIEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->EmitBytes(CIEBytes); + FrameSectionSize += CIEBytes.size(); +} + +/// Emit a FDE into the debug_frame section. \p FDEBytes +/// contains the FDE data without the length, CIE offset and address +/// which will be replaced with the parameter values. +void DwarfStreamer::emitFDE(uint32_t CIEOffset, uint32_t AddrSize, + uint32_t Address, StringRef FDEBytes) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfFrameSection()); + + MS->EmitIntValue(FDEBytes.size() + 4 + AddrSize, 4); + MS->EmitIntValue(CIEOffset, 4); + MS->EmitIntValue(Address, AddrSize); + MS->EmitBytes(FDEBytes); + FrameSectionSize += FDEBytes.size() + 8 + AddrSize; +} + +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/DwarfStreamer.h b/tools/dsymutil/DwarfStreamer.h new file mode 100644 index 000000000000..74f0a09001d0 --- /dev/null +++ b/tools/dsymutil/DwarfStreamer.h @@ -0,0 +1,186 @@ +//===- tools/dsymutil/DwarfStreamer.h - Dwarf Streamer ----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CompileUnit.h" +#include "DebugMap.h" +#include "LinkUtils.h" +#include "NonRelocatableStringpool.h" +#include "llvm/CodeGen/AccelTable.h" +#include "llvm/CodeGen/AsmPrinter.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugLine.h" +#include "llvm/DebugInfo/DWARF/DWARFDebugRangeList.h" +#include "llvm/MC/MCAsmBackend.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSection.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/MC/MCSymbol.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +#ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H +#define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H + +namespace llvm { +namespace dsymutil { + +/// The Dwarf streaming logic. +/// +/// All interactions with the MC layer that is used to build the debug +/// information binary representation are handled in this class. +class DwarfStreamer { +public: + DwarfStreamer(raw_fd_ostream &OutFile, LinkOptions Options) + : OutFile(OutFile), Options(std::move(Options)) {} + + bool init(Triple TheTriple); + + /// Dump the file to the disk. + bool finish(const DebugMap &); + + AsmPrinter &getAsmPrinter() const { return *Asm; } + + /// Set the current output section to debug_info and change + /// the MC Dwarf version to \p DwarfVersion. + void switchToDebugInfoSection(unsigned DwarfVersion); + + /// Emit the compilation unit header for \p Unit in the + /// debug_info section. + /// + /// As a side effect, this also switches the current Dwarf version + /// of the MC layer to the one of U.getOrigUnit(). + void emitCompileUnitHeader(CompileUnit &Unit); + + /// Recursively emit the DIE tree rooted at \p Die. + void emitDIE(DIE &Die); + + /// Emit the abbreviation table \p Abbrevs to the debug_abbrev section. + void emitAbbrevs(const std::vector<std::unique_ptr<DIEAbbrev>> &Abbrevs, + unsigned DwarfVersion); + + /// Emit the string table described by \p Pool. + void emitStrings(const NonRelocatableStringpool &Pool); + + /// Emit the swift_ast section stored in \p Buffer. + void emitSwiftAST(StringRef Buffer); + + /// Emit debug_ranges for \p FuncRange by translating the + /// original \p Entries. + void emitRangesEntries( + int64_t UnitPcOffset, uint64_t OrigLowPc, + const FunctionIntervals::const_iterator &FuncRange, + const std::vector<DWARFDebugRangeList::RangeListEntry> &Entries, + unsigned AddressSize); + + /// Emit debug_aranges entries for \p Unit and if \p DoRangesSection is true, + /// also emit the debug_ranges entries for the DW_TAG_compile_unit's + /// DW_AT_ranges attribute. + void emitUnitRangesEntries(CompileUnit &Unit, bool DoRangesSection); + + uint32_t getRangesSectionSize() const { return RangesSectionSize; } + + /// Emit the debug_loc contribution for \p Unit by copying the entries from + /// \p Dwarf and offsetting them. Update the location attributes to point to + /// the new entries. + void emitLocationsForUnit(const CompileUnit &Unit, DWARFContext &Dwarf); + + /// Emit the line table described in \p Rows into the debug_line section. + void emitLineTableForUnit(MCDwarfLineTableParams Params, + StringRef PrologueBytes, unsigned MinInstLength, + std::vector<DWARFDebugLine::Row> &Rows, + unsigned AdddressSize); + + /// Copy over the debug sections that are not modified when updating. + void copyInvariantDebugSection(const object::ObjectFile &Obj); + + uint32_t getLineSectionSize() const { return LineSectionSize; } + + /// Emit the .debug_pubnames contribution for \p Unit. + void emitPubNamesForUnit(const CompileUnit &Unit); + + /// Emit the .debug_pubtypes contribution for \p Unit. + void emitPubTypesForUnit(const CompileUnit &Unit); + + /// Emit a CIE. + void emitCIE(StringRef CIEBytes); + + /// Emit an FDE with data \p Bytes. + void emitFDE(uint32_t CIEOffset, uint32_t AddreSize, uint32_t Address, + StringRef Bytes); + + /// Emit DWARF debug names. + void emitDebugNames(AccelTable<DWARF5AccelTableStaticData> &Table); + + /// Emit Apple namespaces accelerator table. + void emitAppleNamespaces(AccelTable<AppleAccelTableStaticOffsetData> &Table); + + /// Emit Apple names accelerator table. + void emitAppleNames(AccelTable<AppleAccelTableStaticOffsetData> &Table); + + /// Emit Apple Objective-C accelerator table. + void emitAppleObjc(AccelTable<AppleAccelTableStaticOffsetData> &Table); + + /// Emit Apple type accelerator table. + void emitAppleTypes(AccelTable<AppleAccelTableStaticTypeData> &Table); + + uint32_t getFrameSectionSize() const { return FrameSectionSize; } + +private: + /// \defgroup MCObjects MC layer objects constructed by the streamer + /// @{ + std::unique_ptr<MCRegisterInfo> MRI; + std::unique_ptr<MCAsmInfo> MAI; + std::unique_ptr<MCObjectFileInfo> MOFI; + std::unique_ptr<MCContext> MC; + MCAsmBackend *MAB; // Owned by MCStreamer + std::unique_ptr<MCInstrInfo> MII; + std::unique_ptr<MCSubtargetInfo> MSTI; + MCInstPrinter *MIP; // Owned by AsmPrinter + MCCodeEmitter *MCE; // Owned by MCStreamer + MCStreamer *MS; // Owned by AsmPrinter + std::unique_ptr<TargetMachine> TM; + std::unique_ptr<AsmPrinter> Asm; + /// @} + + /// The file we stream the linked Dwarf to. + raw_fd_ostream &OutFile; + + LinkOptions Options; + + uint32_t RangesSectionSize; + uint32_t LocSectionSize; + uint32_t LineSectionSize; + uint32_t FrameSectionSize; + + /// Keep track of emitted CUs and their Unique ID. + struct EmittedUnit { + unsigned ID; + MCSymbol *LabelBegin; + }; + std::vector<EmittedUnit> EmittedUnits; + + /// Emit the pubnames or pubtypes section contribution for \p + /// Unit into \p Sec. The data is provided in \p Names. + void emitPubSectionForUnit(MCSection *Sec, StringRef Name, + const CompileUnit &Unit, + const std::vector<CompileUnit::AccelInfo> &Names); +}; + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H diff --git a/tools/dsymutil/LLVMBuild.txt b/tools/dsymutil/LLVMBuild.txt index 99b0b441c28e..3769fbe33f21 100644 --- a/tools/dsymutil/LLVMBuild.txt +++ b/tools/dsymutil/LLVMBuild.txt @@ -17,6 +17,6 @@ [component_0] type = Tool -name = llvm-dsymutil +name = dsymutil parent = Tools required_libraries = AsmPrinter DebugInfoDWARF MC Object Support all-targets diff --git a/tools/dsymutil/LinkUtils.h b/tools/dsymutil/LinkUtils.h new file mode 100644 index 000000000000..f0abd888b529 --- /dev/null +++ b/tools/dsymutil/LinkUtils.h @@ -0,0 +1,82 @@ +//===- tools/dsymutil/LinkUtils.h - Dwarf linker utilities ------*- 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_DSYMUTIL_LINKOPTIONS_H +#define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/WithColor.h" +#include <string> + +namespace llvm { +namespace dsymutil { + +enum class OutputFileType { + Object, + Assembly, +}; + +/// The kind of accelerator tables we should emit. +enum class AccelTableKind { + Apple, ///< .apple_names, .apple_namespaces, .apple_types, .apple_objc. + Dwarf, ///< DWARF v5 .debug_names. + Default, ///< Dwarf for DWARF5 or later, Apple otherwise. +}; + +struct LinkOptions { + /// Verbosity + bool Verbose = false; + + /// Skip emitting output + bool NoOutput = false; + + /// Do not unique types according to ODR + bool NoODR = false; + + /// Update + bool Update = false; + + /// Minimize + bool Minimize = false; + + /// Do not check swiftmodule timestamp + bool NoTimestamp = false; + + /// Number of threads. + unsigned Threads = 1; + + // Output file type. + OutputFileType FileType = OutputFileType::Object; + + /// The accelerator table kind + AccelTableKind TheAccelTableKind; + + /// -oso-prepend-path + std::string PrependPath; + + LinkOptions() = default; +}; + +inline void warn(Twine Warning, Twine Context = {}) { + WithColor::warning() << Warning + "\n"; + if (!Context.isTriviallyEmpty()) + WithColor::note() << Twine("while processing ") + Context + "\n"; +} + +inline bool error(Twine Error, Twine Context = {}) { + WithColor::error() << Error + "\n"; + if (!Context.isTriviallyEmpty()) + WithColor::note() << Twine("while processing ") + Context + "\n"; + return false; +} + +} // end namespace dsymutil +} // end namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index 2142baa72ec1..48155b402042 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -1,6 +1,6 @@ //===- tools/dsymutil/MachODebugMapParser.cpp - Parse STABS debug maps ----===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -9,9 +9,11 @@ #include "BinaryHolder.h" #include "DebugMap.h" +#include "MachOUtils.h" #include "llvm/ADT/Optional.h" #include "llvm/Object/MachO.h" #include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" namespace { @@ -22,14 +24,14 @@ using namespace llvm::object; class MachODebugMapParser { public: MachODebugMapParser(StringRef BinaryPath, ArrayRef<std::string> Archs, - StringRef PathPrefix = "", bool Verbose = false) + StringRef PathPrefix = "", + bool PaperTrailWarnings = false, bool Verbose = false) : BinaryPath(BinaryPath), Archs(Archs.begin(), Archs.end()), - PathPrefix(PathPrefix), MainBinaryHolder(Verbose), - CurrentObjectHolder(Verbose), CurrentDebugMapObject(nullptr) {} + PathPrefix(PathPrefix), PaperTrailWarnings(PaperTrailWarnings), + BinHolder(Verbose), CurrentDebugMapObject(nullptr) {} - /// \brief Parses and returns the DebugMaps of the input binary. - /// The binary contains multiple maps in case it is a universal - /// binary. + /// Parses and returns the DebugMaps of the input binary. The binary contains + /// multiple maps in case it is a universal binary. /// \returns an error in case the provided BinaryPath doesn't exist /// or isn't of a supported type. ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parse(); @@ -41,20 +43,19 @@ private: std::string BinaryPath; SmallVector<StringRef, 1> Archs; std::string PathPrefix; + bool PaperTrailWarnings; /// Owns the MemoryBuffer for the main binary. - BinaryHolder MainBinaryHolder; + BinaryHolder BinHolder; /// Map of the binary symbol addresses. StringMap<uint64_t> MainBinarySymbolAddresses; StringRef MainBinaryStrings; /// The constructed DebugMap. std::unique_ptr<DebugMap> Result; - /// Owns the MemoryBuffer for the currently handled object file. - BinaryHolder CurrentObjectHolder; /// Map of the currently processed object file symbol addresses. StringMap<Optional<uint64_t>> CurrentObjectAddresses; - /// Element of the debug map corresponfing to the current object file. + /// Element of the debug map corresponding to the current object file. DebugMapObject *CurrentDebugMapObject; /// Holds function info while function scope processing. @@ -96,12 +97,25 @@ private: } void dumpOneBinaryStab(const MachOObjectFile &MainBinary, StringRef BinaryPath); + + void Warning(const Twine &Msg, StringRef File = StringRef()) { + WithColor::warning() << "(" + << MachOUtils::getArchName( + Result->getTriple().getArchName()) + << ") " << File << " " << Msg << "\n"; + + if (PaperTrailWarnings) { + if (!File.empty()) + Result->addDebugMapObject(File, sys::TimePoint<std::chrono::seconds>()); + if (Result->end() != Result->begin()) + (*--Result->end())->addWarning(Msg.str()); + } + } }; -static void Warning(const Twine &Msg) { errs() << "warning: " + Msg + "\n"; } } // anonymous namespace -/// Reset the parser state coresponding to the current object +/// Reset the parser state corresponding to the current object /// file. This is to be called after an object file is finished /// processing. void MachODebugMapParser::resetParserState() { @@ -119,24 +133,25 @@ void MachODebugMapParser::switchToNewDebugMapObject( SmallString<80> Path(PathPrefix); sys::path::append(Path, Filename); - auto MachOOrError = - CurrentObjectHolder.GetFilesAs<MachOObjectFile>(Path, Timestamp); - if (auto Error = MachOOrError.getError()) { - Warning(Twine("cannot open debug object \"") + Path.str() + "\": " + - Error.message() + "\n"); + auto ObjectEntry = BinHolder.getObjectEntry(Path, Timestamp); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + Path.str()); return; } - auto ErrOrAchObj = - CurrentObjectHolder.GetAs<MachOObjectFile>(Result->getTriple()); - if (auto Err = ErrOrAchObj.getError()) { - return Warning(Twine("cannot open debug object \"") + Path.str() + "\": " + - Err.message() + "\n"); + auto Object = ObjectEntry->getObjectAs<MachOObjectFile>(Result->getTriple()); + if (!Object) { + auto Err = Object.takeError(); + Warning("unable to open object file: " + toString(std::move(Err)), + Path.str()); + return; } CurrentDebugMapObject = &Result->addDebugMapObject(Path, Timestamp, MachO::N_OSO); - loadCurrentObjectFileSymbols(*ErrOrAchObj); + loadCurrentObjectFileSymbols(*Object); } static std::string getArchName(const object::MachOObjectFile &Obj) { @@ -212,9 +227,11 @@ void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, uint8_t SectionIndex, uint16_t Flags, uint64_t Value) { // Index - OS << '[' << format_decimal(Index, 6) << "] " + OS << '[' << format_decimal(Index, 6) + << "] " // n_strx - << format_hex_no_prefix(StringIndex, 8) << ' ' + << format_hex_no_prefix(StringIndex, 8) + << ' ' // n_type... << format_hex_no_prefix(Type, 2) << " ("; @@ -253,9 +270,11 @@ void MachODebugMapParser::dumpSymTabEntry(raw_ostream &OS, uint64_t Index, OS << ") " // n_sect - << format_hex_no_prefix(SectionIndex, 2) << " " + << format_hex_no_prefix(SectionIndex, 2) + << " " // n_desc - << format_hex_no_prefix(Flags, 4) << " " + << format_hex_no_prefix(Flags, 4) + << " " // n_value << format_hex_no_prefix(Value, 16); @@ -302,17 +321,26 @@ static bool shouldLinkArch(SmallVectorImpl<StringRef> &Archs, StringRef Arch) { } bool MachODebugMapParser::dumpStab() { - auto MainBinOrError = - MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath); - if (auto Error = MainBinOrError.getError()) { - llvm::errs() << "Cannot get '" << BinaryPath - << "' as MachO file: " << Error.message() << "\n"; + auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); + WithColor::error() << "cannot load '" << BinaryPath + << "': " << toString(std::move(Err)) << '\n'; return false; } - for (const auto *Binary : *MainBinOrError) - if (shouldLinkArch(Archs, Binary->getArchTriple().getArchName())) - dumpOneBinaryStab(*Binary, BinaryPath); + auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); + if (!Objects) { + auto Err = Objects.takeError(); + WithColor::error() << "cannot get '" << BinaryPath + << "' as MachO file: " << toString(std::move(Err)) + << "\n"; + return false; + } + + for (const auto *Object : *Objects) + if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) + dumpOneBinaryStab(*Object, BinaryPath); return true; } @@ -321,15 +349,20 @@ bool MachODebugMapParser::dumpStab() { /// successful iterates over the STAB entries. The real parsing is /// done in handleStabSymbolTableEntry. ErrorOr<std::vector<std::unique_ptr<DebugMap>>> MachODebugMapParser::parse() { - auto MainBinOrError = - MainBinaryHolder.GetFilesAs<MachOObjectFile>(BinaryPath); - if (auto Error = MainBinOrError.getError()) - return Error; + auto ObjectEntry = BinHolder.getObjectEntry(BinaryPath); + if (!ObjectEntry) { + return errorToErrorCode(ObjectEntry.takeError()); + } + + auto Objects = ObjectEntry->getObjectsAs<MachOObjectFile>(); + if (!Objects) { + return errorToErrorCode(ObjectEntry.takeError()); + } std::vector<std::unique_ptr<DebugMap>> Results; - for (const auto *Binary : *MainBinOrError) - if (shouldLinkArch(Archs, Binary->getArchTriple().getArchName())) - Results.push_back(parseOneBinary(*Binary, BinaryPath)); + for (const auto *Object : *Objects) + if (shouldLinkArch(Archs, Object->getArchTriple().getArchName())) + Results.push_back(parseOneBinary(*Object, BinaryPath)); return std::move(Results); } @@ -356,9 +389,8 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, return; } - // If the last N_OSO object file wasn't found, - // CurrentDebugMapObject will be null. Do not update anything - // until we find the next valid N_OSO entry. + // If the last N_OSO object file wasn't found, CurrentDebugMapObject will be + // null. Do not update anything until we find the next valid N_OSO entry. if (!CurrentDebugMapObject) return; @@ -401,14 +433,16 @@ void MachODebugMapParser::handleStabSymbolTableEntry(uint32_t StringIndex, } } - if (ObjectSymIt == CurrentObjectAddresses.end()) - return Warning("could not find object file symbol for symbol " + - Twine(Name)); + if (ObjectSymIt == CurrentObjectAddresses.end()) { + Warning("could not find object file symbol for symbol " + Twine(Name)); + return; + } if (!CurrentDebugMapObject->addSymbol(Name, ObjectSymIt->getValue(), Value, - Size)) - return Warning(Twine("failed to insert symbol '") + Name + - "' in the debug map."); + Size)) { + Warning(Twine("failed to insert symbol '") + Name + "' in the debug map."); + return; + } } /// Load the current object file symbols into CurrentObjectAddresses. @@ -512,13 +546,14 @@ namespace llvm { namespace dsymutil { llvm::ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs, - StringRef PrependPath, bool Verbose, bool InputIsYAML) { - if (!InputIsYAML) { - MachODebugMapParser Parser(InputFile, Archs, PrependPath, Verbose); - return Parser.parse(); - } else { + StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, + bool InputIsYAML) { + if (InputIsYAML) return DebugMap::parseYAMLDebugMap(InputFile, PrependPath, Verbose); - } + + MachODebugMapParser Parser(InputFile, Archs, PrependPath, PaperTrailWarnings, + Verbose); + return Parser.parse(); } bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs, diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp index 06d0f72a61cc..eda530b810c0 100644 --- a/tools/dsymutil/MachOUtils.cpp +++ b/tools/dsymutil/MachOUtils.cpp @@ -1,4 +1,4 @@ -//===-- MachOUtils.h - Mach-o specific helpers for dsymutil --------------===// +//===-- MachOUtils.cpp - Mach-o specific helpers for dsymutil ------------===// // // The LLVM Compiler Infrastructure // @@ -10,17 +10,17 @@ #include "MachOUtils.h" #include "BinaryHolder.h" #include "DebugMap.h" -#include "dsymutil.h" +#include "LinkUtils.h" #include "NonRelocatableStringpool.h" -#include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCAsmLayout.h" -#include "llvm/MC/MCSectionMachO.h" -#include "llvm/MC/MCObjectStreamer.h" #include "llvm/MC/MCMachObjectWriter.h" +#include "llvm/MC/MCObjectStreamer.h" +#include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCStreamer.h" #include "llvm/Object/MachO.h" #include "llvm/Support/FileUtilities.h" #include "llvm/Support/Program.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" namespace llvm { @@ -33,21 +33,20 @@ std::string getArchName(StringRef Arch) { return Arch; } -static bool runLipo(StringRef SDKPath, SmallVectorImpl<const char *> &Args) { +static bool runLipo(StringRef SDKPath, SmallVectorImpl<StringRef> &Args) { auto Path = sys::findProgramByName("lipo", makeArrayRef(SDKPath)); if (!Path) Path = sys::findProgramByName("lipo"); if (!Path) { - errs() << "error: lipo: " << Path.getError().message() << "\n"; + WithColor::error() << "lipo: " << Path.getError().message() << "\n"; return false; } std::string ErrMsg; - int result = - sys::ExecuteAndWait(*Path, Args.data(), nullptr, {}, 0, 0, &ErrMsg); + int result = sys::ExecuteAndWait(*Path, Args, None, {}, 0, 0, &ErrMsg); if (result) { - errs() << "error: lipo: " << ErrMsg << "\n"; + WithColor::error() << "lipo: " << ErrMsg << "\n"; return false; } @@ -64,8 +63,8 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles, StringRef From(ArchFiles.front().Path); if (sys::fs::rename(From, OutputFileName)) { if (std::error_code EC = sys::fs::copy_file(From, OutputFileName)) { - errs() << "error: while copying " << From << " to " << OutputFileName - << ": " << EC.message() << "\n"; + WithColor::error() << "while copying " << From << " to " + << OutputFileName << ": " << EC.message() << "\n"; return false; } sys::fs::remove(From); @@ -73,37 +72,37 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFilename> &ArchFiles, return true; } - SmallVector<const char *, 8> Args; + SmallVector<StringRef, 8> Args; Args.push_back("lipo"); Args.push_back("-create"); for (auto &Thin : ArchFiles) - Args.push_back(Thin.Path.c_str()); + Args.push_back(Thin.Path); // Align segments to match dsymutil-classic alignment for (auto &Thin : ArchFiles) { Thin.Arch = getArchName(Thin.Arch); Args.push_back("-segalign"); - Args.push_back(Thin.Arch.c_str()); + Args.push_back(Thin.Arch); Args.push_back("20"); } Args.push_back("-output"); Args.push_back(OutputFileName.data()); - Args.push_back(nullptr); if (Options.Verbose) { outs() << "Running lipo\n"; for (auto Arg : Args) - outs() << ' ' << ((Arg == nullptr) ? "\n" : Arg); + outs() << ' ' << Arg; + outs() << "\n"; } return Options.NoOutput ? true : runLipo(SDKPath, Args); } -// Return a MachO::segment_command_64 that holds the same values as -// the passed MachO::segment_command. We do that to avoid having to -// duplicat the logic for 32bits and 64bits segments. +// Return a MachO::segment_command_64 that holds the same values as the passed +// MachO::segment_command. We do that to avoid having to duplicate the logic +// for 32bits and 64bits segments. struct MachO::segment_command_64 adaptFrom32bits(MachO::segment_command Seg) { MachO::segment_command_64 Seg64; Seg64.cmd = Seg.cmd; @@ -137,9 +136,9 @@ static void iterateOnSegments(const object::MachOObjectFile &Obj, } } -// Transfer the symbols described by \a NList to \a NewSymtab which is -// just the raw contents of the symbol table for the dSYM companion file. -// \returns whether the symbol was tranfered or not. +// Transfer the symbols described by \a NList to \a NewSymtab which is just the +// raw contents of the symbol table for the dSYM companion file. \returns +// whether the symbol was transferred or not. template <typename NListTy> static bool transferSymbol(NListTy NList, bool IsLittleEndian, StringRef Strings, SmallVectorImpl<char> &NewSymtab, @@ -225,7 +224,7 @@ getSection(const object::MachOObjectFile &Obj, template <typename SegmentTy> static void transferSegmentAndSections( const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment, - const object::MachOObjectFile &Obj, MCObjectWriter &Writer, + const object::MachOObjectFile &Obj, MachObjectWriter &Writer, uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t DwarfSegmentSize, uint64_t &GapForDwarf, uint64_t &EndAddress) { if (StringRef("__DWARF") == Segment.segname) @@ -255,14 +254,13 @@ static void transferSegmentAndSections( unsigned nsects = Segment.nsects; if (Obj.isLittleEndian() != sys::IsLittleEndianHost) MachO::swapStruct(Segment); - Writer.writeBytes( - StringRef(reinterpret_cast<char *>(&Segment), sizeof(Segment))); + Writer.W.OS.write(reinterpret_cast<char *>(&Segment), sizeof(Segment)); for (unsigned i = 0; i < nsects; ++i) { auto Sect = getSection(Obj, Segment, LCI, i); Sect.offset = Sect.reloff = Sect.nreloc = 0; if (Obj.isLittleEndian() != sys::IsLittleEndianHost) MachO::swapStruct(Sect); - Writer.writeBytes(StringRef(reinterpret_cast<char *>(&Sect), sizeof(Sect))); + Writer.W.OS.write(reinterpret_cast<char *>(&Sect), sizeof(Sect)); } } @@ -324,24 +322,32 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS); MCAssembler &MCAsm = ObjectStreamer.getAssembler(); auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter()); - MCAsmLayout Layout(MCAsm); + // Layout but don't emit. + ObjectStreamer.flushPendingLabels(); + MCAsmLayout Layout(MCAsm); MCAsm.layout(Layout); BinaryHolder InputBinaryHolder(false); - auto ErrOrObjs = InputBinaryHolder.GetObjectFiles(DM.getBinaryPath()); - if (auto Error = ErrOrObjs.getError()) + + auto ObjectEntry = InputBinaryHolder.getObjectEntry(DM.getBinaryPath()); + if (!ObjectEntry) { + auto Err = ObjectEntry.takeError(); return error(Twine("opening ") + DM.getBinaryPath() + ": " + - Error.message(), + toString(std::move(Err)), "output file streaming"); + } - auto ErrOrInputBinary = - InputBinaryHolder.GetAs<object::MachOObjectFile>(DM.getTriple()); - if (auto Error = ErrOrInputBinary.getError()) + auto Object = + ObjectEntry->getObjectAs<object::MachOObjectFile>(DM.getTriple()); + if (!Object) { + auto Err = Object.takeError(); return error(Twine("opening ") + DM.getBinaryPath() + ": " + - Error.message(), + toString(std::move(Err)), "output file streaming"); - auto &InputBinary = *ErrOrInputBinary; + } + + auto &InputBinary = *Object; bool Is64Bit = Writer.is64Bit(); MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand(); @@ -429,10 +435,9 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, // Write the load commands. assert(OutFile.tell() == HeaderSize); if (UUIDCmd.cmd != 0) { - Writer.write32(UUIDCmd.cmd); - Writer.write32(UUIDCmd.cmdsize); - Writer.writeBytes( - StringRef(reinterpret_cast<const char *>(UUIDCmd.uuid), 16)); + Writer.W.write<uint32_t>(UUIDCmd.cmd); + Writer.W.write<uint32_t>(UUIDCmd.cmdsize); + OutFile.write(reinterpret_cast<const char *>(UUIDCmd.uuid), 16); assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd)); } @@ -467,7 +472,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, if (DwarfVMAddr + DwarfSegmentSize > DwarfVMMax || DwarfVMAddr + DwarfSegmentSize < DwarfVMAddr /* Overflow */) { // There is no room for the __DWARF segment at the end of the - // address space. Look trhough segments to find a gap. + // address space. Look through segments to find a gap. DwarfVMAddr = GapForDwarf; if (DwarfVMAddr == UINT64_MAX) warn("not enough VM space for the __DWARF segment.", @@ -479,12 +484,12 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, NumDwarfSections, Layout, Writer); assert(OutFile.tell() == LoadCommandSize + HeaderSize); - Writer.WriteZeros(SymtabStart - (LoadCommandSize + HeaderSize)); + OutFile.write_zeros(SymtabStart - (LoadCommandSize + HeaderSize)); assert(OutFile.tell() == SymtabStart); // Transfer symbols. if (ShouldEmitSymtab) { - Writer.writeBytes(NewSymtab.str()); + OutFile << NewSymtab.str(); assert(OutFile.tell() == StringStart); // Transfer string table. @@ -492,18 +497,20 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, // dsymutil-classic starts the reconstructed string table with 2 of these. // Reproduce that behavior for now (there is corresponding code in // transferSymbol). - Writer.WriteZeros(1); - typedef NonRelocatableStringpool::MapTy MapTy; - for (auto *Entry = NewStrings.getFirstEntry(); Entry; - Entry = static_cast<MapTy::MapEntryTy *>(Entry->getValue().second)) - Writer.writeBytes( - StringRef(Entry->getKey().data(), Entry->getKey().size() + 1)); + OutFile << '\0'; + std::vector<DwarfStringPoolEntryRef> Strings = NewStrings.getEntries(); + for (auto EntryRef : Strings) { + if (EntryRef.getIndex() == -1U) + break; + OutFile.write(EntryRef.getString().data(), + EntryRef.getString().size() + 1); + } } assert(OutFile.tell() == StringStart + NewStringsSize); // Pad till the Dwarf segment start. - Writer.WriteZeros(DwarfSegmentStart - (StringStart + NewStringsSize)); + OutFile.write_zeros(DwarfSegmentStart - (StringStart + NewStringsSize)); assert(OutFile.tell() == DwarfSegmentStart); // Emit the Dwarf sections contents. @@ -512,12 +519,12 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, continue; uint64_t Pos = OutFile.tell(); - Writer.WriteZeros(alignTo(Pos, Sec.getAlignment()) - Pos); - MCAsm.writeSectionData(&Sec, Layout); + OutFile.write_zeros(alignTo(Pos, Sec.getAlignment()) - Pos); + MCAsm.writeSectionData(OutFile, &Sec, Layout); } return true; } -} -} -} +} // namespace MachOUtils +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h index 61dfadc70270..0db8ed1a1e31 100644 --- a/tools/dsymutil/MachOUtils.h +++ b/tools/dsymutil/MachOUtils.h @@ -9,8 +9,8 @@ #ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H #define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H -#include <string> #include "llvm/ADT/StringRef.h" +#include <string> namespace llvm { class MCStreamer; @@ -33,7 +33,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, raw_fd_ostream &OutFile); std::string getArchName(StringRef Arch); -} -} -} +} // namespace MachOUtils +} // namespace dsymutil +} // namespace llvm #endif // LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H diff --git a/tools/dsymutil/NonRelocatableStringpool.cpp b/tools/dsymutil/NonRelocatableStringpool.cpp new file mode 100644 index 000000000000..f1e5f596b849 --- /dev/null +++ b/tools/dsymutil/NonRelocatableStringpool.cpp @@ -0,0 +1,51 @@ +//===- NonRelocatableStringpool.cpp - A simple stringpool ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "NonRelocatableStringpool.h" + +namespace llvm { +namespace dsymutil { + +DwarfStringPoolEntryRef NonRelocatableStringpool::getEntry(StringRef S) { + if (S.empty() && !Strings.empty()) + return EmptyString; + + auto I = Strings.insert({S, DwarfStringPoolEntry()}); + auto &Entry = I.first->second; + if (I.second || Entry.Index == -1U) { + Entry.Index = NumEntries++; + Entry.Offset = CurrentEndOffset; + Entry.Symbol = nullptr; + CurrentEndOffset += S.size() + 1; + } + return DwarfStringPoolEntryRef(*I.first); +} + +StringRef NonRelocatableStringpool::internString(StringRef S) { + DwarfStringPoolEntry Entry{nullptr, 0, -1U}; + auto InsertResult = Strings.insert({S, Entry}); + return InsertResult.first->getKey(); +} + +std::vector<DwarfStringPoolEntryRef> +NonRelocatableStringpool::getEntries() const { + std::vector<DwarfStringPoolEntryRef> Result; + Result.reserve(Strings.size()); + for (const auto &E : Strings) + Result.emplace_back(E); + llvm::sort( + Result.begin(), Result.end(), + [](const DwarfStringPoolEntryRef A, const DwarfStringPoolEntryRef B) { + return A.getIndex() < B.getIndex(); + }); + return Result; +} + +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/NonRelocatableStringpool.h b/tools/dsymutil/NonRelocatableStringpool.h index aa21d77ad438..733ceebea614 100644 --- a/tools/dsymutil/NonRelocatableStringpool.h +++ b/tools/dsymutil/NonRelocatableStringpool.h @@ -12,63 +12,71 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" +#include "llvm/CodeGen/DwarfStringPoolEntry.h" #include "llvm/Support/Allocator.h" #include <cstdint> -#include <utility> +#include <vector> namespace llvm { namespace dsymutil { -/// \brief A string table that doesn't need relocations. +/// A string table that doesn't need relocations. /// -/// We are doing a final link, no need for a string table that -/// has relocation entries for every reference to it. This class -/// provides this ablitity by just associating offsets with -/// strings. +/// We are doing a final link, no need for a string table that has relocation +/// entries for every reference to it. This class provides this ability by just +/// associating offsets with strings. class NonRelocatableStringpool { public: - /// \brief Entries are stored into the StringMap and simply linked - /// together through the second element of this pair in order to - /// keep track of insertion order. - using MapTy = - StringMap<std::pair<uint32_t, StringMapEntryBase *>, BumpPtrAllocator>; + /// Entries are stored into the StringMap and simply linked together through + /// the second element of this pair in order to keep track of insertion + /// order. + using MapTy = StringMap<DwarfStringPoolEntry, BumpPtrAllocator>; - NonRelocatableStringpool() : Sentinel(0), Last(&Sentinel) { - // Legacy dsymutil puts an empty string at the start of the line - // table. - getStringOffset(""); + NonRelocatableStringpool() { + // Legacy dsymutil puts an empty string at the start of the line table. + EmptyString = getEntry(""); } - /// \brief Get the offset of string \p S in the string table. This - /// can insert a new element or return the offset of a preexisitng - /// one. - uint32_t getStringOffset(StringRef S); + DwarfStringPoolEntryRef getEntry(StringRef S); - /// \brief Get permanent storage for \p S (but do not necessarily - /// emit \p S in the output section). + /// Get the offset of string \p S in the string table. This can insert a new + /// element or return the offset of a pre-existing one. + uint32_t getStringOffset(StringRef S) { return getEntry(S).getOffset(); } + + /// Get permanent storage for \p S (but do not necessarily emit \p S in the + /// output section). A latter call to getStringOffset() with the same string + /// will chain it though. + /// /// \returns The StringRef that points to permanent storage to use /// in place of \p S. StringRef internString(StringRef S); - // \brief Return the first entry of the string table. - const MapTy::MapEntryTy *getFirstEntry() const { - return getNextEntry(&Sentinel); - } - - // \brief Get the entry following \p E in the string table or null - // if \p E was the last entry. - const MapTy::MapEntryTy *getNextEntry(const MapTy::MapEntryTy *E) const { - return static_cast<const MapTy::MapEntryTy *>(E->getValue().second); - } - uint64_t getSize() { return CurrentEndOffset; } + std::vector<DwarfStringPoolEntryRef> getEntries() const; + private: MapTy Strings; uint32_t CurrentEndOffset = 0; - MapTy::MapEntryTy Sentinel, *Last; + unsigned NumEntries = 0; + DwarfStringPoolEntryRef EmptyString; }; +/// Helper for making strong types. +template <typename T, typename S> class StrongType : public T { +public: + template <typename... Args> + explicit StrongType(Args... A) : T(std::forward<Args>(A)...) {} +}; + +/// It's very easy to introduce bugs by passing the wrong string pool in the +/// dwarf linker. By using strong types the interface enforces that the right +/// kind of pool is used. +struct UniqueTag {}; +struct OffsetsTag {}; +using UniquingStringPool = StrongType<NonRelocatableStringpool, UniqueTag>; +using OffsetsStringPool = StrongType<NonRelocatableStringpool, OffsetsTag>; + } // end namespace dsymutil } // end namespace llvm diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index 1f882abd1811..fc447b30be98 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -13,11 +13,14 @@ //===----------------------------------------------------------------------===// #include "dsymutil.h" +#include "BinaryHolder.h" #include "CFBundle.h" #include "DebugMap.h" +#include "LinkUtils.h" #include "MachOUtils.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/DebugInfo/DIContext.h" @@ -27,12 +30,12 @@ #include "llvm/Object/MachO.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Path.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ThreadPool.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/thread.h" #include <algorithm> @@ -63,6 +66,11 @@ static opt<std::string> OsoPrependPath( desc("Specify a directory to prepend to the paths of object files."), value_desc("path"), cat(DsymCategory)); +static opt<bool> Assembly( + "S", + desc("Output textual assembly instead of a binary dSYM companion file."), + init(false), cat(DsymCategory), cl::Hidden); + static opt<bool> DumpStab( "symtab", desc("Dumps the symbol table found in executable or object file(s) and\n" @@ -75,6 +83,32 @@ static opt<bool> FlatOut("flat", init(false), cat(DsymCategory)); static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut)); +static opt<bool> Minimize( + "minimize", + desc("When used when creating a dSYM file with Apple accelerator tables,\n" + "this option will suppress the emission of the .debug_inlines, \n" + ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n" + "has better equivalents: .apple_names and .apple_types. When used in\n" + "conjunction with --update option, this option will cause redundant\n" + "accelerator tables to be removed."), + init(false), cat(DsymCategory)); +static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize)); + +static opt<bool> Update( + "update", + desc("Updates existing dSYM files to contain the latest accelerator\n" + "tables and other DWARF optimizations."), + init(false), cat(DsymCategory)); +static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update)); + +static cl::opt<AccelTableKind> AcceleratorTable( + "accelerator", cl::desc("Output accelerator tables."), + cl::values(clEnumValN(AccelTableKind::Default, "Default", + "Default for input."), + clEnumValN(AccelTableKind::Apple, "Apple", "Apple"), + clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")), + cl::init(AccelTableKind::Default), cat(DsymCategory)); + static opt<unsigned> NumThreads( "num-threads", desc("Specifies the maximum number (n) of simultaneous threads to use\n" @@ -122,6 +156,15 @@ static opt<bool> InputIsYAMLDebugMap( static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."), cat(DsymCategory)); +static opt<std::string> + Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."), + cat(DsymCategory)); + +static opt<bool> + PaperTrailWarnings("papertrail", + desc("Embed warnings in the linked DWARF debug info."), + cat(DsymCategory)); + static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { if (NoOutput) return true; @@ -132,8 +175,8 @@ static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { std::error_code EC; llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text); if (EC) { - llvm::errs() << "error: cannot create plist file " << InfoPlist << ": " - << EC.message() << '\n'; + WithColor::error() << "cannot create plist file " << InfoPlist << ": " + << EC.message() << '\n'; return false; } @@ -164,13 +207,26 @@ static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { << "\t\t<key>CFBundleSignature</key>\n" << "\t\t<string>\?\?\?\?</string>\n"; - if (!BI.OmitShortVersion()) - PL << "\t\t<key>CFBundleShortVersionString</key>\n" - << "\t\t<string>" << BI.ShortVersionStr << "</string>\n"; + if (!BI.OmitShortVersion()) { + PL << "\t\t<key>CFBundleShortVersionString</key>\n"; + PL << "\t\t<string>"; + printHTMLEscaped(BI.ShortVersionStr, PL); + PL << "</string>\n"; + } + + PL << "\t\t<key>CFBundleVersion</key>\n"; + PL << "\t\t<string>"; + printHTMLEscaped(BI.VersionStr, PL); + PL << "</string>\n"; + + if (!Toolchain.empty()) { + PL << "\t\t<key>Toolchain</key>\n"; + PL << "\t\t<string>"; + printHTMLEscaped(Toolchain, PL); + PL << "</string>\n"; + } - PL << "\t\t<key>CFBundleVersion</key>\n" - << "\t\t<string>" << BI.VersionStr << "</string>\n" - << "\t</dict>\n" + PL << "\t</dict>\n" << "</plist>\n"; PL.close(); @@ -185,8 +241,8 @@ static bool createBundleDir(llvm::StringRef BundleBase) { llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF"); if (std::error_code EC = create_directories(Bundle.str(), true, llvm::sys::fs::perms::all_all)) { - llvm::errs() << "error: cannot create directory " << Bundle << ": " - << EC.message() << "\n"; + WithColor::error() << "cannot create directory " << Bundle << ": " + << EC.message() << "\n"; return false; } return true; @@ -194,8 +250,8 @@ static bool createBundleDir(llvm::StringRef BundleBase) { static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { if (OutputFile == "-") { - llvm::errs() << "warning: verification skipped for " << Arch - << "because writing to stdout.\n"; + WithColor::warning() << "verification skipped for " << Arch + << "because writing to stdout.\n"; return true; } @@ -213,7 +269,7 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { DIDumpOptions DumpOpts; bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion()); if (!success) - errs() << "error: verification failed for " << Arch << '\n'; + WithColor::error() << "verification failed for " << Arch << '\n'; return success; } @@ -221,8 +277,12 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { } static std::string getOutputFileName(llvm::StringRef InputFile) { + // When updating, do in place replacement. + if (OutputFileOpt.empty() && Update) + return InputFile; + + // If a flat dSYM has been requested, things are pretty simple. if (FlatOut) { - // If a flat dSYM has been requested, things are pretty simple. if (OutputFileOpt.empty()) { if (InputFile == "-") return "a.out.dwarf"; @@ -260,6 +320,86 @@ static Expected<sys::fs::TempFile> createTempFile() { return sys::fs::TempFile::create(TmpModel); } +/// Parses the command line options into the LinkOptions struct and performs +/// some sanity checking. Returns an error in case the latter fails. +static Expected<LinkOptions> getOptions() { + LinkOptions Options; + + Options.Verbose = Verbose; + Options.NoOutput = NoOutput; + Options.NoODR = NoODR; + Options.Minimize = Minimize; + Options.Update = Update; + Options.NoTimestamp = NoTimestamp; + Options.PrependPath = OsoPrependPath; + Options.TheAccelTableKind = AcceleratorTable; + + if (Assembly) + Options.FileType = OutputFileType::Assembly; + + if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") != + InputFiles.end()) { + // FIXME: We cannot use stdin for an update because stdin will be + // consumed by the BinaryHolder during the debugmap parsing, and + // then we will want to consume it again in DwarfLinker. If we + // used a unique BinaryHolder object that could cache multiple + // binaries this restriction would go away. + return make_error<StringError>( + "standard input cannot be used as input for a dSYM update.", + inconvertibleErrorCode()); + } + + if (NumThreads == 0) + Options.Threads = llvm::thread::hardware_concurrency(); + if (DumpDebugMap || Verbose) + Options.Threads = 1; + + return Options; +} + +/// Return a list of input files. This function has logic for dealing with the +/// special case where we might have dSYM bundles as input. The function +/// returns an error when the directory structure doesn't match that of a dSYM +/// bundle. +static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) { + if (!DsymAsInput) + return InputFiles; + + // If we are updating, we might get dSYM bundles as input. + std::vector<std::string> Inputs; + for (const auto &Input : InputFiles) { + if (!llvm::sys::fs::is_directory(Input)) { + Inputs.push_back(Input); + continue; + } + + // Make sure that we're dealing with a dSYM bundle. + SmallString<256> BundlePath(Input); + sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); + if (!llvm::sys::fs::is_directory(BundlePath)) + return make_error<StringError>( + Input + " is a directory, but doesn't look like a dSYM bundle.", + inconvertibleErrorCode()); + + // Create a directory iterator to iterate over all the entries in the + // bundle. + std::error_code EC; + llvm::sys::fs::directory_iterator DirIt(BundlePath, EC); + llvm::sys::fs::directory_iterator DirEnd; + if (EC) + return errorCodeToError(EC); + + // Add each entry to the list of inputs. + while (DirIt != DirEnd) { + Inputs.push_back(DirIt->path()); + DirIt.increment(EC); + if (EC) + return errorCodeToError(EC); + } + } + return Inputs; +} + namespace { struct TempFileVector { std::vector<sys::fs::TempFile> Files; @@ -273,15 +413,13 @@ struct TempFileVector { } // namespace int main(int argc, char **argv) { - llvm::sys::PrintStackTraceOnErrorSignal(argv[0]); - llvm::PrettyStackTraceProgram StackPrinter(argc, argv); - llvm::llvm_shutdown_obj Shutdown; - LinkOptions Options; + InitLLVM X(argc, argv); + void *P = (void *)(intptr_t)getOutputFileName; std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P); SDKPath = llvm::sys::path::parent_path(SDKPath); - HideUnrelatedOptions(DsymCategory); + HideUnrelatedOptions({&DsymCategory, &ColorCategory}); llvm::cl::ParseCommandLineOptions( argc, argv, "manipulate archived DWARF debug symbol files.\n\n" @@ -299,35 +437,48 @@ int main(int argc, char **argv) { return 0; } - Options.Verbose = Verbose; - Options.NoOutput = NoOutput; - Options.NoODR = NoODR; - Options.NoTimestamp = NoTimestamp; - Options.PrependPath = OsoPrependPath; + auto OptionsOrErr = getOptions(); + if (!OptionsOrErr) { + WithColor::error() << toString(OptionsOrErr.takeError()); + return 1; + } llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); + auto InputsOrErr = getInputs(OptionsOrErr->Update); + if (!InputsOrErr) { + WithColor::error() << toString(InputsOrErr.takeError()) << '\n'; + return 1; + } + if (!FlatOut && OutputFileOpt == "-") { - llvm::errs() << "error: cannot emit to standard output without --flat\n"; + WithColor::error() << "cannot emit to standard output without --flat\n"; return 1; } - if (InputFiles.size() > 1 && FlatOut && !OutputFileOpt.empty()) { - llvm::errs() << "error: cannot use -o with multiple inputs in flat mode\n"; + if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) { + WithColor::error() << "cannot use -o with multiple inputs in flat mode\n"; return 1; } + if (getenv("RC_DEBUG_OPTIONS")) + PaperTrailWarnings = true; + + if (PaperTrailWarnings && InputIsYAMLDebugMap) + WithColor::warning() + << "Paper trail warnings are not supported for YAML input"; + for (const auto &Arch : ArchFlags) if (Arch != "*" && Arch != "all" && !llvm::object::MachOObjectFile::isValidArch(Arch)) { - llvm::errs() << "error: Unsupported cpu architecture: '" << Arch << "'\n"; + WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; return 1; } - for (auto &InputFile : InputFiles) { + for (auto &InputFile : *InputsOrErr) { // Dump the symbol table for each input file and requested arch if (DumpStab) { if (!dumpStab(InputFile, ArchFlags, OsoPrependPath)) @@ -335,31 +486,43 @@ int main(int argc, char **argv) { continue; } - auto DebugMapPtrsOrErr = parseDebugMap(InputFile, ArchFlags, OsoPrependPath, - Verbose, InputIsYAMLDebugMap); + auto DebugMapPtrsOrErr = + parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings, + Verbose, InputIsYAMLDebugMap); if (auto EC = DebugMapPtrsOrErr.getError()) { - llvm::errs() << "error: cannot parse the debug map for \"" << InputFile - << "\": " << EC.message() << '\n'; + WithColor::error() << "cannot parse the debug map for '" << InputFile + << "': " << EC.message() << '\n'; return 1; } + if (OptionsOrErr->Update) { + // The debug map should be empty. Add one object file corresponding to + // the input file. + for (auto &Map : *DebugMapPtrsOrErr) + Map->addDebugMapObject(InputFile, + llvm::sys::TimePoint<std::chrono::seconds>()); + } + + // Ensure that the debug map is not empty (anymore). if (DebugMapPtrsOrErr->empty()) { - llvm::errs() << "error: no architecture to link\n"; + WithColor::error() << "no architecture to link\n"; return 1; } - if (NumThreads == 0) - NumThreads = llvm::thread::hardware_concurrency(); - if (DumpDebugMap || Verbose) - NumThreads = 1; - NumThreads = std::min<unsigned>(NumThreads, DebugMapPtrsOrErr->size()); + // Shared a single binary holder for all the link steps. + BinaryHolder BinHolder; + NumThreads = + std::min<unsigned>(OptionsOrErr->Threads, DebugMapPtrsOrErr->size()); llvm::ThreadPool Threads(NumThreads); // If there is more than one link to execute, we need to generate // temporary files. - bool NeedsTempFiles = !DumpDebugMap && (*DebugMapPtrsOrErr).size() != 1; + bool NeedsTempFiles = + !DumpDebugMap && (OutputFileOpt != "-") && + (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update); + llvm::SmallVector<MachOUtils::ArchAndFilename, 4> TempFiles; TempFileVector TempFileStore; std::atomic_char AllOK(1); @@ -371,9 +534,9 @@ int main(int argc, char **argv) { continue; if (Map->begin() == Map->end()) - llvm::errs() << "warning: no debug symbols in executable (-arch " - << MachOUtils::getArchName(Map->getTriple().getArchName()) - << ")\n"; + WithColor::warning() + << "no debug symbols in executable (-arch " + << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n"; // Using a std::shared_ptr rather than std::unique_ptr because move-only // types don't work with std::bind in the ThreadPool implementation. @@ -402,7 +565,7 @@ int main(int argc, char **argv) { auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream) { - AllOK.fetch_and(linkDwarf(*Stream, *Map, Options)); + AllOK.fetch_and(linkDwarf(*Stream, BinHolder, *Map, *OptionsOrErr)); Stream->flush(); if (Verify && !NoOutput) AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName())); @@ -424,7 +587,7 @@ int main(int argc, char **argv) { if (NeedsTempFiles && !MachOUtils::generateUniversalBinary( - TempFiles, getOutputFileName(InputFile), Options, SDKPath)) + TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath)) return 1; } diff --git a/tools/dsymutil/dsymutil.h b/tools/dsymutil/dsymutil.h index 09053d1b1cc8..556b2d65e1a7 100644 --- a/tools/dsymutil/dsymutil.h +++ b/tools/dsymutil/dsymutil.h @@ -1,6 +1,6 @@ //===- tools/dsymutil/dsymutil.h - dsymutil high-level functionality ------===// // -// The LLVM Linker +// The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -18,6 +18,7 @@ #define LLVM_TOOLS_DSYMUTIL_DSYMUTIL_H #include "DebugMap.h" +#include "LinkUtils.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Compiler.h" @@ -29,44 +30,24 @@ namespace llvm { namespace dsymutil { -struct LinkOptions { - /// Verbosity - bool Verbose = false; +class BinaryHolder; - /// Skip emitting output - bool NoOutput = false; - - /// Do not unique types according to ODR - bool NoODR; - - /// Do not check swiftmodule timestamp - bool NoTimestamp = false; - - /// -oso-prepend-path - std::string PrependPath; - - LinkOptions() = default; -}; - -/// \brief Extract the DebugMaps from the given file. +/// Extract the DebugMaps from the given file. /// The file has to be a MachO object file. Multiple debug maps can be /// returned when the file is universal (aka fat) binary. ErrorOr<std::vector<std::unique_ptr<DebugMap>>> parseDebugMap(StringRef InputFile, ArrayRef<std::string> Archs, - StringRef PrependPath, bool Verbose, bool InputIsYAML); + StringRef PrependPath, bool PaperTrailWarnings, bool Verbose, + bool InputIsYAML); -/// \brief Dump the symbol table +/// Dump the symbol table bool dumpStab(StringRef InputFile, ArrayRef<std::string> Archs, StringRef PrependPath = ""); -/// \brief Link the Dwarf debuginfo as directed by the passed DebugMap -/// \p DM into a DwarfFile named \p OutputFilename. -/// \returns false if the link failed. -bool linkDwarf(raw_fd_ostream &OutFile, const DebugMap &DM, - const LinkOptions &Options); - -void warn(const Twine &Warning, const Twine &Context); -bool error(const Twine &Error, const Twine &Context); +/// Link the Dwarf debug info as directed by the passed DebugMap \p DM into a +/// DwarfFile named \p OutputFilename. \returns false if the link failed. +bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, + const DebugMap &DM, const LinkOptions &Options); } // end namespace dsymutil } // end namespace llvm diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index 856d8172fc95..6c55ebcddc43 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -15,7 +15,7 @@ #include "llvm/ADT/Statistic.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" -#include "llvm/CodeGen/CommandFlags.def" +#include "llvm/CodeGen/CommandFlags.inc" #include "llvm/Config/config.h" // plugin-api.h requires HAVE_STDINT_H #include "llvm/IR/Constants.h" #include "llvm/IR/DiagnosticPrinter.h" @@ -44,9 +44,18 @@ #define LDPT_GET_SYMBOLS_V3 28 +// FIXME: Remove when binutils 2.31 (containing gold 1.16) is the minimum +// required version. +#define LDPT_GET_WRAP_SYMBOLS 32 + using namespace llvm; using namespace lto; +// FIXME: Remove when binutils 2.31 (containing gold 1.16) is the minimum +// required version. +typedef enum ld_plugin_status (*ld_plugin_get_wrap_symbols)( + uint64_t *num_symbols, const char ***wrap_symbol_list); + static ld_plugin_status discard_message(int level, const char *format, ...) { // Die loudly. Recent versions of Gold pass ld_plugin_message as the first // callback in the transfer vector. This should never be called. @@ -56,6 +65,7 @@ static ld_plugin_status discard_message(int level, const char *format, ...) { static ld_plugin_release_input_file release_input_file = nullptr; static ld_plugin_get_input_file get_input_file = nullptr; static ld_plugin_message message = discard_message; +static ld_plugin_get_wrap_symbols get_wrap_symbols = nullptr; namespace { struct claimed_file { @@ -93,6 +103,8 @@ struct PluginInputFile { struct ResolutionInfo { bool CanOmitFromDynSym = true; bool DefaultVisibility = true; + bool CanInline = true; + bool IsUsedInRegularObj = false; }; } @@ -103,6 +115,7 @@ static ld_plugin_add_input_file add_input_file = nullptr; static ld_plugin_set_extra_library_path set_extra_library_path = nullptr; static ld_plugin_get_view get_view = nullptr; static bool IsExecutable = false; +static bool SplitSections = true; static Optional<Reloc::Model> RelocationModel = None; static std::string output_name = ""; static std::list<claimed_file> Modules; @@ -185,6 +198,16 @@ namespace options { static std::string sample_profile; // New pass manager static bool new_pass_manager = false; + // Debug new pass manager + static bool debug_pass_manager = false; + // Directory to store the .dwo files. + static std::string dwo_dir; + /// Statistics output filename. + static std::string stats_file; + + // Optimization remarks filename and hotness options + static std::string OptRemarksFilename; + static bool OptRemarksWithHotness = false; static void process_plugin_option(const char *opt_) { @@ -246,6 +269,16 @@ namespace options { sample_profile= opt.substr(strlen("sample-profile=")); } else if (opt == "new-pass-manager") { new_pass_manager = true; + } else if (opt == "debug-pass-manager") { + debug_pass_manager = true; + } else if (opt.startswith("dwo_dir=")) { + dwo_dir = opt.substr(strlen("dwo_dir=")); + } else if (opt.startswith("opt-remarks-filename=")) { + OptRemarksFilename = opt.substr(strlen("opt-remarks-filename=")); + } else if (opt == "opt-remarks-with-hotness") { + OptRemarksWithHotness = true; + } else if (opt.startswith("stats-file=")) { + stats_file = opt.substr(strlen("stats-file=")); } else { // Save this option to pass to the code generator. // ParseCommandLineOptions() expects argv[0] to be program name. Lazily @@ -292,6 +325,7 @@ ld_plugin_status onload(ld_plugin_tv *tv) { switch (tv->tv_u.tv_val) { case LDPO_REL: // .o IsExecutable = false; + SplitSections = false; break; case LDPO_DYN: // .so IsExecutable = false; @@ -367,6 +401,13 @@ ld_plugin_status onload(ld_plugin_tv *tv) { case LDPT_MESSAGE: message = tv->tv_u.tv_message; break; + case LDPT_GET_WRAP_SYMBOLS: + // FIXME: When binutils 2.31 (containing gold 1.16) is the minimum + // required version, this should be changed to: + // get_wrap_symbols = tv->tv_u.tv_get_wrap_symbols; + get_wrap_symbols = + (ld_plugin_get_wrap_symbols)tv->tv_u.tv_message; + break; default: break; } @@ -563,6 +604,29 @@ static ld_plugin_status claim_file_hook(const ld_plugin_input_file *file, } } + // Handle any --wrap options passed to gold, which are than passed + // along to the plugin. + if (get_wrap_symbols) { + const char **wrap_symbols; + uint64_t count = 0; + if (get_wrap_symbols(&count, &wrap_symbols) != LDPS_OK) { + message(LDPL_ERROR, "Unable to get wrap symbols!"); + return LDPS_ERR; + } + for (uint64_t i = 0; i < count; i++) { + StringRef Name = wrap_symbols[i]; + ResolutionInfo &Res = ResInfo[Name]; + ResolutionInfo &WrapRes = ResInfo["__wrap_" + Name.str()]; + ResolutionInfo &RealRes = ResInfo["__real_" + Name.str()]; + // Tell LTO not to inline symbols that will be overwritten. + Res.CanInline = false; + RealRes.CanInline = false; + // Tell LTO not to eliminate symbols that will be used after renaming. + Res.IsUsedInRegularObj = true; + WrapRes.IsUsedInRegularObj = true; + } + } + return LDPS_OK; } @@ -686,6 +750,12 @@ static void addModule(LTO &Lto, claimed_file &F, const void *View, (IsExecutable || !Res.DefaultVisibility)) R.FinalDefinitionInLinkageUnit = true; + if (!Res.CanInline) + R.LinkerRedefined = true; + + if (Res.IsUsedInRegularObj) + R.VisibleToRegularObj = true; + freeSymName(Sym); } @@ -719,7 +789,7 @@ static int getOutputFileName(StringRef InFilename, bool TempOutFile, if (TaskID > 0) NewFilename += utostr(TaskID); std::error_code EC = - sys::fs::openFileForWrite(NewFilename, FD, sys::fs::F_None); + sys::fs::openFileForWrite(NewFilename, FD, sys::fs::CD_CreateAlways); if (EC) message(LDPL_FATAL, "Could not open file %s: %s", NewFilename.c_str(), EC.message().c_str()); @@ -750,7 +820,12 @@ static void getThinLTOOldAndNewPrefix(std::string &OldPrefix, std::tie(OldPrefix, NewPrefix) = PrefixReplace.split(';'); } -static std::unique_ptr<LTO> createLTO() { +/// Creates instance of LTO. +/// OnIndexWrite is callback to let caller know when LTO writes index files. +/// LinkedObjectsFile is an output stream to write the list of object files for +/// the final ThinLTO linking. Can be nullptr. +static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite, + raw_fd_ostream *LinkedObjectsFile) { Config Conf; ThinBackend Backend; @@ -761,12 +836,13 @@ static std::unique_ptr<LTO> createLTO() { // FIXME: Check the gold version or add a new option to enable them. Conf.Options.RelaxELFRelocations = false; - // Enable function/data sections by default. - Conf.Options.FunctionSections = true; - Conf.Options.DataSections = true; + // Toggle function/data sections. + Conf.Options.FunctionSections = SplitSections; + Conf.Options.DataSections = SplitSections; Conf.MAttrs = MAttrs; Conf.RelocModel = RelocationModel; + Conf.CodeModel = getCodeModel(); Conf.CGOptLevel = getCGOptLevel(); Conf.DisableVerify = options::DisableVerify; Conf.OptLevel = options::OptLevel; @@ -775,9 +851,9 @@ static std::unique_ptr<LTO> createLTO() { if (options::thinlto_index_only) { std::string OldPrefix, NewPrefix; getThinLTOOldAndNewPrefix(OldPrefix, NewPrefix); - Backend = createWriteIndexesThinBackend( - OldPrefix, NewPrefix, options::thinlto_emit_imports_files, - options::thinlto_linked_objects_file); + Backend = createWriteIndexesThinBackend(OldPrefix, NewPrefix, + options::thinlto_emit_imports_files, + LinkedObjectsFile, OnIndexWrite); } Conf.OverrideTriple = options::triple; @@ -799,7 +875,7 @@ static std::unique_ptr<LTO> createLTO() { raw_fd_ostream OS(output_name, EC, sys::fs::OpenFlags::F_None); if (EC) message(LDPL_FATAL, "Failed to write the output file."); - WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ false); + WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ false); return false; }; break; @@ -813,9 +889,18 @@ static std::unique_ptr<LTO> createLTO() { if (!options::sample_profile.empty()) Conf.SampleProfile = options::sample_profile; + Conf.DwoDir = options::dwo_dir; + + // Set up optimization remarks handling. + Conf.RemarksFilename = options::OptRemarksFilename; + Conf.RemarksWithHotness = options::OptRemarksWithHotness; + // Use new pass manager if set in driver Conf.UseNewPM = options::new_pass_manager; + // Debug new pass manager if requested + Conf.DebugPassManager = options::debug_pass_manager; + Conf.StatsFile = options::stats_file; return llvm::make_unique<LTO>(std::move(Conf), Backend, options::ParallelCodeGenParallelismLevel); } @@ -826,9 +911,14 @@ static std::unique_ptr<LTO> createLTO() { // final link. Frequently the distributed build system will want to // confirm that all expected outputs are created based on all of the // modules provided to the linker. -static void writeEmptyDistributedBuildOutputs(std::string &ModulePath, - std::string &OldPrefix, - std::string &NewPrefix) { +// If SkipModule is true then .thinlto.bc should contain just +// SkipModuleByDistributedBackend flag which requests distributed backend +// to skip the compilation of the corresponding module and produce an empty +// object file. +static void writeEmptyDistributedBuildOutputs(const std::string &ModulePath, + const std::string &OldPrefix, + const std::string &NewPrefix, + bool SkipModule) { std::string NewModulePath = getThinLTOOutputFile(ModulePath, OldPrefix, NewPrefix); std::error_code EC; @@ -838,6 +928,12 @@ static void writeEmptyDistributedBuildOutputs(std::string &ModulePath, if (EC) message(LDPL_FATAL, "Failed to write '%s': %s", (NewModulePath + ".thinlto.bc").c_str(), EC.message().c_str()); + + if (SkipModule) { + ModuleSummaryIndex Index(/*HaveGVs*/ false); + Index.setSkipModuleByDistributedBackend(); + WriteIndexToFile(Index, OS, nullptr); + } } if (options::thinlto_emit_imports_files) { raw_fd_ostream OS(NewModulePath + ".imports", EC, @@ -848,16 +944,23 @@ static void writeEmptyDistributedBuildOutputs(std::string &ModulePath, } } -/// gold informs us that all symbols have been read. At this point, we use -/// get_symbols to see if any of our definitions have been overridden by a -/// native object file. Then, perform optimization and codegen. -static ld_plugin_status allSymbolsReadHook() { - if (Modules.empty()) - return LDPS_OK; - - if (unsigned NumOpts = options::extra.size()) - cl::ParseCommandLineOptions(NumOpts, &options::extra[0]); +// Creates and returns output stream with a list of object files for final +// linking of distributed ThinLTO. +static std::unique_ptr<raw_fd_ostream> CreateLinkedObjectsFile() { + if (options::thinlto_linked_objects_file.empty()) + return nullptr; + assert(options::thinlto_index_only); + std::error_code EC; + auto LinkedObjectsFile = llvm::make_unique<raw_fd_ostream>( + options::thinlto_linked_objects_file, EC, sys::fs::OpenFlags::F_None); + if (EC) + message(LDPL_FATAL, "Failed to create '%s': %s", + options::thinlto_linked_objects_file.c_str(), EC.message().c_str()); + return LinkedObjectsFile; +} +/// Runs LTO and return a list of pairs <FileName, IsTemporary>. +static std::vector<std::pair<SmallString<128>, bool>> runLTO() { // Map to own RAII objects that manage the file opening and releasing // interfaces with gold. This is needed only for ThinLTO mode, since // unlike regular LTO, where addModule will result in the opened file @@ -865,7 +968,15 @@ static ld_plugin_status allSymbolsReadHook() { // through Lto->run(). DenseMap<void *, std::unique_ptr<PluginInputFile>> HandleToInputFile; - std::unique_ptr<LTO> Lto = createLTO(); + // Owns string objects and tells if index file was already created. + StringMap<bool> ObjectToIndexFileState; + + std::unique_ptr<raw_fd_ostream> LinkedObjects = CreateLinkedObjectsFile(); + std::unique_ptr<LTO> Lto = createLTO( + [&ObjectToIndexFileState](const std::string &Identifier) { + ObjectToIndexFileState[Identifier] = true; + }, + LinkedObjects.get()); std::string OldPrefix, NewPrefix; if (options::thinlto_index_only) @@ -873,29 +984,25 @@ static ld_plugin_status allSymbolsReadHook() { std::string OldSuffix, NewSuffix; getThinLTOOldAndNewSuffix(OldSuffix, NewSuffix); - // Set for owning string objects used as buffer identifiers. - StringSet<> ObjectFilenames; for (claimed_file &F : Modules) { if (options::thinlto && !HandleToInputFile.count(F.leader_handle)) HandleToInputFile.insert(std::make_pair( F.leader_handle, llvm::make_unique<PluginInputFile>(F.handle))); - const void *View = getSymbolsAndView(F); // In case we are thin linking with a minimized bitcode file, ensure // the module paths encoded in the index reflect where the backends // will locate the full bitcode files for compiling/importing. std::string Identifier = getThinLTOObjectFileName(F.name, OldSuffix, NewSuffix); - auto ObjFilename = ObjectFilenames.insert(Identifier); + auto ObjFilename = ObjectToIndexFileState.insert({Identifier, false}); assert(ObjFilename.second); - if (!View) { - if (options::thinlto_index_only) - // Write empty output files that may be expected by the distributed - // build system. - writeEmptyDistributedBuildOutputs(Identifier, OldPrefix, NewPrefix); - continue; + if (const void *View = getSymbolsAndView(F)) + addModule(*Lto, F, View, ObjFilename.first->first()); + else if (options::thinlto_index_only) { + ObjFilename.first->second = true; + writeEmptyDistributedBuildOutputs(Identifier, OldPrefix, NewPrefix, + /* SkipModule */ true); } - addModule(*Lto, F, View, ObjFilename.first->first()); } SmallString<128> Filename; @@ -907,23 +1014,19 @@ static ld_plugin_status allSymbolsReadHook() { bool SaveTemps = !Filename.empty(); size_t MaxTasks = Lto->getMaxTasks(); - std::vector<uintptr_t> IsTemporary(MaxTasks); - std::vector<SmallString<128>> Filenames(MaxTasks); + std::vector<std::pair<SmallString<128>, bool>> Files(MaxTasks); auto AddStream = [&](size_t Task) -> std::unique_ptr<lto::NativeObjectStream> { - IsTemporary[Task] = !SaveTemps; - int FD = getOutputFileName(Filename, /*TempOutFile=*/!SaveTemps, - Filenames[Task], Task); + Files[Task].second = !SaveTemps; + int FD = getOutputFileName(Filename, /* TempOutFile */ !SaveTemps, + Files[Task].first, Task); return llvm::make_unique<lto::NativeObjectStream>( llvm::make_unique<llvm::raw_fd_ostream>(FD, true)); }; - auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB, - StringRef Path) { - // Note that this requires that the memory buffers provided to AddBuffer are - // backed by a file. - Filenames[Task] = Path; + auto AddBuffer = [&](size_t Task, std::unique_ptr<MemoryBuffer> MB) { + *AddStream(Task)->OS << MB->getBuffer(); }; NativeObjectCache Cache; @@ -932,20 +1035,42 @@ static ld_plugin_status allSymbolsReadHook() { check(Lto->run(AddStream, Cache)); + // Write empty output files that may be expected by the distributed build + // system. + if (options::thinlto_index_only) + for (auto &Identifier : ObjectToIndexFileState) + if (!Identifier.getValue()) + writeEmptyDistributedBuildOutputs(Identifier.getKey(), OldPrefix, + NewPrefix, /* SkipModule */ false); + + return Files; +} + +/// gold informs us that all symbols have been read. At this point, we use +/// get_symbols to see if any of our definitions have been overridden by a +/// native object file. Then, perform optimization and codegen. +static ld_plugin_status allSymbolsReadHook() { + if (Modules.empty()) + return LDPS_OK; + + if (unsigned NumOpts = options::extra.size()) + cl::ParseCommandLineOptions(NumOpts, &options::extra[0]); + + std::vector<std::pair<SmallString<128>, bool>> Files = runLTO(); + if (options::TheOutputType == options::OT_DISABLE || options::TheOutputType == options::OT_BC_ONLY) return LDPS_OK; if (options::thinlto_index_only) { - if (llvm::AreStatisticsEnabled()) - llvm::PrintStatistics(); + llvm_shutdown(); cleanup_hook(); exit(0); } - for (unsigned I = 0; I != MaxTasks; ++I) - if (!Filenames[I].empty()) - recordFile(Filenames[I].str(), IsTemporary[I]); + for (const auto &F : Files) + if (!F.first.empty()) + recordFile(F.first.str(), F.second); if (!options::extra_library_path.empty() && set_extra_library_path(options::extra_library_path.c_str()) != LDPS_OK) @@ -983,7 +1108,7 @@ static ld_plugin_status cleanup_hook(void) { } // Prune cache - if (!options::cache_policy.empty()) { + if (!options::cache_dir.empty()) { CachePruningPolicy policy = check(parseCachePruningPolicy(options::cache_policy)); pruneCache(options::cache_dir, policy); } diff --git a/tools/llc/llc.cpp b/tools/llc/llc.cpp index a4810890f9b4..2329fb3e87c9 100644 --- a/tools/llc/llc.cpp +++ b/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/tools/lli/CMakeLists.txt b/tools/lli/CMakeLists.txt index f02e19313b79..42f6c2b2ede4 100644 --- a/tools/lli/CMakeLists.txt +++ b/tools/lli/CMakeLists.txt @@ -36,9 +36,17 @@ if( LLVM_USE_INTEL_JITEVENTS ) ) endif( LLVM_USE_INTEL_JITEVENTS ) +if( LLVM_USE_PERF ) + set(LLVM_LINK_COMPONENTS + ${LLVM_LINK_COMPONENTS} + DebugInfoDWARF + PerfJITEvents + Object + ) +endif( LLVM_USE_PERF ) + add_llvm_tool(lli lli.cpp - OrcLazyJIT.cpp DEPENDS intrinsics_gen diff --git a/tools/lli/OrcLazyJIT.cpp b/tools/lli/OrcLazyJIT.cpp deleted file mode 100644 index f1a752e0790d..000000000000 --- a/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/tools/lli/OrcLazyJIT.h b/tools/lli/OrcLazyJIT.h deleted file mode 100644 index a5cc804bb045..000000000000 --- a/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/tools/lli/RemoteJITUtils.h b/tools/lli/RemoteJITUtils.h index 4e948413865c..944881070c70 100644 --- a/tools/lli/RemoteJITUtils.h +++ b/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/tools/lli/lli.cpp b/tools/lli/lli.cpp index a33c51d77877..1940dbd848cc 100644 --- a/tools/lli/lli.cpp +++ b/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/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index ae7d1a7f1b7a..9023bdd1a0d6 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/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/tools/llvm-as-fuzzer/CMakeLists.txt b/tools/llvm-as-fuzzer/CMakeLists.txt index 4d75ad4825a8..cda76a73e601 100644 --- a/tools/llvm-as-fuzzer/CMakeLists.txt +++ b/tools/llvm-as-fuzzer/CMakeLists.txt @@ -4,4 +4,6 @@ set(LLVM_LINK_COMPONENTS Core Support ) -add_llvm_fuzzer(llvm-as-fuzzer llvm-as-fuzzer.cpp) +add_llvm_fuzzer(llvm-as-fuzzer + llvm-as-fuzzer.cpp + ) diff --git a/tools/llvm-as/llvm-as.cpp b/tools/llvm-as/llvm-as.cpp index 9f0f162b74f8..bb4233aa9ba0 100644 --- a/tools/llvm-as/llvm-as.cpp +++ b/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/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 7f20e136eefd..1939dc6440fe 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/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/tools/llvm-c-test/attributes.c b/tools/llvm-c-test/attributes.c index c6beab1d31b0..c7bc0d3c87b2 100644 --- a/tools/llvm-c-test/attributes.c +++ b/tools/llvm-c-test/attributes.c @@ -14,6 +14,7 @@ #include "llvm-c-test.h" +#include <assert.h> #include <stdlib.h> int llvm_test_function_attributes(void) { @@ -30,6 +31,7 @@ int llvm_test_function_attributes(void) { int AttrCount = LLVMGetAttributeCountAtIndex(F, Idx); LLVMAttributeRef *Attrs = (LLVMAttributeRef *)malloc(AttrCount * sizeof(LLVMAttributeRef)); + assert(Attrs); LLVMGetAttributesAtIndex(F, Idx, Attrs); free(Attrs); } @@ -61,6 +63,7 @@ int llvm_test_callsite_attributes(void) { int AttrCount = LLVMGetCallSiteAttributeCount(I, Idx); LLVMAttributeRef *Attrs = (LLVMAttributeRef *)malloc( AttrCount * sizeof(LLVMAttributeRef)); + assert(Attrs); LLVMGetCallSiteAttributes(I, Idx, Attrs); free(Attrs); } diff --git a/tools/llvm-c-test/debuginfo.c b/tools/llvm-c-test/debuginfo.c index c88c74167b63..74d215ea8182 100644 --- a/tools/llvm-c-test/debuginfo.c +++ b/tools/llvm-c-test/debuginfo.c @@ -11,19 +11,168 @@ |* *| \*===----------------------------------------------------------------------===*/ +#include "llvm-c-test.h" +#include "llvm-c/Core.h" #include "llvm-c/DebugInfo.h" #include <stdio.h> +#include <string.h> + +static LLVMMetadataRef +declare_objc_class(LLVMDIBuilderRef DIB, LLVMMetadataRef File) { + LLVMMetadataRef Decl = LLVMDIBuilderCreateStructType(DIB, File, "TestClass", 9, File, 42, 64, 0, LLVMDIFlagObjcClassComplete, NULL, NULL, 0, 0, NULL, NULL, 0); + LLVMMetadataRef SuperDecl = LLVMDIBuilderCreateStructType(DIB, File, "TestSuperClass", 14, File, 42, 64, 0, LLVMDIFlagObjcClassComplete, NULL, NULL, 0, 0, NULL, NULL, 0); + LLVMDIBuilderCreateInheritance(DIB, Decl, SuperDecl, 0, 0, 0); + LLVMMetadataRef TestProperty = + LLVMDIBuilderCreateObjCProperty(DIB, "test", 4, File, 42, "getTest", 7, "setTest", 7, 0x20 /*copy*/ | 0x40 /*nonatomic*/, SuperDecl); + LLVMDIBuilderCreateObjCIVar(DIB, "_test", 5, File, 42, 64, 0, 64, LLVMDIFlagPublic, SuperDecl, TestProperty); + return Decl; +} int llvm_test_dibuilder(void) { - LLVMModuleRef M = LLVMModuleCreateWithName("debuginfo.c"); + const char *Filename = "debuginfo.c"; + LLVMModuleRef M = LLVMModuleCreateWithName(Filename); LLVMDIBuilderRef DIB = LLVMCreateDIBuilder(M); - LLVMMetadataRef File = LLVMDIBuilderCreateFile(DIB, "debuginfo.c", 12, - ".", 1); + LLVMMetadataRef File = LLVMDIBuilderCreateFile(DIB, Filename, + strlen(Filename), ".", 1); + + LLVMMetadataRef CompileUnit = LLVMDIBuilderCreateCompileUnit(DIB, + LLVMDWARFSourceLanguageC, File, "llvm-c-test", 11, 0, NULL, 0, 0, + NULL, 0, LLVMDWARFEmissionFull, 0, 0, 0); + + LLVMMetadataRef Module = + LLVMDIBuilderCreateModule(DIB, CompileUnit, + "llvm-c-test", 11, + "", 0, + "/test/include/llvm-c-test.h", 27, + "", 0); + + LLVMMetadataRef OtherModule = + LLVMDIBuilderCreateModule(DIB, CompileUnit, + "llvm-c-test-import", 18, + "", 0, + "/test/include/llvm-c-test-import.h", 34, + "", 0); + LLVMMetadataRef ImportedModule = + LLVMDIBuilderCreateImportedModuleFromModule(DIB, Module, OtherModule, + File, 42); + LLVMDIBuilderCreateImportedModuleFromAlias(DIB, Module, ImportedModule, + File, 42); + + LLVMMetadataRef ClassTy = declare_objc_class(DIB, File); + LLVMMetadataRef GlobalClassValueExpr = + LLVMDIBuilderCreateConstantValueExpression(DIB, 0); + LLVMDIBuilderCreateGlobalVariableExpression(DIB, Module, "globalClass", 11, + "", 0, File, 1, ClassTy, + true, GlobalClassValueExpr, + NULL, 0); + + LLVMMetadataRef Int64Ty = + LLVMDIBuilderCreateBasicType(DIB, "Int64", 5, 64, 0); + LLVMMetadataRef Int64TypeDef = + LLVMDIBuilderCreateTypedef(DIB, Int64Ty, "int64_t", 7, File, 42, File); + + LLVMMetadataRef GlobalVarValueExpr = + LLVMDIBuilderCreateConstantValueExpression(DIB, 0); + LLVMDIBuilderCreateGlobalVariableExpression(DIB, Module, "global", 6, + "", 0, File, 1, Int64TypeDef, + true, GlobalVarValueExpr, + NULL, 0); + + LLVMMetadataRef NameSpace = + LLVMDIBuilderCreateNameSpace(DIB, Module, "NameSpace", 9, false); + + LLVMMetadataRef StructDbgElts[] = {Int64Ty, Int64Ty, Int64Ty}; + LLVMMetadataRef StructDbgTy = + LLVMDIBuilderCreateStructType(DIB, NameSpace, "MyStruct", + 8, File, 0, 192, 0, 0, NULL, StructDbgElts, 3, + LLVMDWARFSourceLanguageC, NULL, "MyStruct", 8); + + LLVMMetadataRef StructDbgPtrTy = + LLVMDIBuilderCreatePointerType(DIB, StructDbgTy, 192, 0, 0, "", 0); + + LLVMAddNamedMetadataOperand(M, "FooType", + LLVMMetadataAsValue(LLVMGetModuleContext(M), StructDbgPtrTy)); + + + LLVMTypeRef FooParamTys[] = { + LLVMInt64Type(), + LLVMInt64Type(), + LLVMVectorType(LLVMInt64Type(), 10), + }; + LLVMTypeRef FooFuncTy = LLVMFunctionType(LLVMInt64Type(), FooParamTys, 3, 0); + LLVMValueRef FooFunction = LLVMAddFunction(M, "foo", FooFuncTy); + LLVMBasicBlockRef FooEntryBlock = LLVMAppendBasicBlock(FooFunction, "entry"); + + LLVMMetadataRef Subscripts[] = { + LLVMDIBuilderGetOrCreateSubrange(DIB, 0, 10), + }; + LLVMMetadataRef VectorTy = + LLVMDIBuilderCreateVectorType(DIB, 64 * 10, 0, + Int64Ty, Subscripts, 1); + + + LLVMMetadataRef ParamTypes[] = {Int64Ty, Int64Ty, VectorTy}; + LLVMMetadataRef FunctionTy = + LLVMDIBuilderCreateSubroutineType(DIB, File, ParamTypes, 3, 0); + + LLVMMetadataRef ReplaceableFunctionMetadata = + LLVMDIBuilderCreateReplaceableCompositeType(DIB, 0x15, "foo", 3, + File, File, 42, + 0, 0, 0, + LLVMDIFlagFwdDecl, + "", 0); + + LLVMMetadataRef FooParamLocation = + LLVMDIBuilderCreateDebugLocation(LLVMGetGlobalContext(), 42, 0, + ReplaceableFunctionMetadata, NULL); + LLVMMetadataRef FunctionMetadata = + LLVMDIBuilderCreateFunction(DIB, File, "foo", 3, "foo", 3, + File, 42, FunctionTy, true, true, + 42, 0, false); + LLVMMetadataReplaceAllUsesWith(ReplaceableFunctionMetadata, FunctionMetadata); + + LLVMMetadataRef FooParamExpression = + LLVMDIBuilderCreateExpression(DIB, NULL, 0); + LLVMMetadataRef FooParamVar1 = + LLVMDIBuilderCreateParameterVariable(DIB, FunctionMetadata, "a", 1, 1, File, + 42, Int64Ty, true, 0); + LLVMDIBuilderInsertDeclareAtEnd(DIB, LLVMConstInt(LLVMInt64Type(), 0, false), + FooParamVar1, FooParamExpression, + FooParamLocation, FooEntryBlock); + LLVMMetadataRef FooParamVar2 = + LLVMDIBuilderCreateParameterVariable(DIB, FunctionMetadata, "b", 1, 2, File, + 42, Int64Ty, true, 0); + LLVMDIBuilderInsertDeclareAtEnd(DIB, LLVMConstInt(LLVMInt64Type(), 0, false), + FooParamVar2, FooParamExpression, + FooParamLocation, FooEntryBlock); + LLVMMetadataRef FooParamVar3 = + LLVMDIBuilderCreateParameterVariable(DIB, FunctionMetadata, "c", 1, 3, File, + 42, VectorTy, true, 0); + LLVMDIBuilderInsertDeclareAtEnd(DIB, LLVMConstInt(LLVMInt64Type(), 0, false), + FooParamVar3, FooParamExpression, + FooParamLocation, FooEntryBlock); + + LLVMSetSubprogram(FooFunction, FunctionMetadata); + + LLVMMetadataRef FooLexicalBlock = + LLVMDIBuilderCreateLexicalBlock(DIB, FunctionMetadata, File, 42, 0); + + LLVMBasicBlockRef FooVarBlock = LLVMAppendBasicBlock(FooFunction, "vars"); + LLVMMetadataRef FooVarsLocation = + LLVMDIBuilderCreateDebugLocation(LLVMGetGlobalContext(), 43, 0, + FunctionMetadata, NULL); + LLVMMetadataRef FooVar1 = + LLVMDIBuilderCreateAutoVariable(DIB, FooLexicalBlock, "d", 1, File, + 43, Int64Ty, true, 0, 0); + LLVMValueRef FooVal1 = LLVMConstInt(LLVMInt64Type(), 0, false); + LLVMMetadataRef FooVarValueExpr = + LLVMDIBuilderCreateConstantValueExpression(DIB, 0); + + LLVMDIBuilderInsertDbgValueAtEnd(DIB, FooVal1, FooVar1, FooVarValueExpr, + FooVarsLocation, FooVarBlock); - LLVMDIBuilderCreateCompileUnit(DIB, - LLVMDWARFSourceLanguageC, File,"llvm-c-test", 11, 0, NULL, 0, 0, - NULL, 0, LLVMDWARFEmissionFull, 0, 0, 0); + LLVMDIBuilderFinalize(DIB); char *MStr = LLVMPrintModuleToString(M); puts(MStr); diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp index 885f9c0d899d..a9cd4cf5cbb7 100644 --- a/tools/llvm-c-test/echo.cpp +++ b/tools/llvm-c-test/echo.cpp @@ -90,7 +90,8 @@ struct TypeCloner { unsigned ParamCount = LLVMCountParamTypes(Src); LLVMTypeRef* Params = nullptr; if (ParamCount > 0) { - Params = (LLVMTypeRef*) malloc(ParamCount * sizeof(LLVMTypeRef)); + Params = static_cast<LLVMTypeRef*>( + safe_malloc(ParamCount * sizeof(LLVMTypeRef))); LLVMGetParamTypes(Src, Params); for (unsigned i = 0; i < ParamCount; i++) Params[i] = Clone(Params[i]); @@ -145,8 +146,8 @@ struct TypeCloner { return LLVMMetadataTypeInContext(Ctx); case LLVMX86_MMXTypeKind: return LLVMX86MMXTypeInContext(Ctx); - default: - break; + case LLVMTokenTypeKind: + return LLVMTokenTypeInContext(Ctx); } fprintf(stderr, "%d is not a supported typekind\n", Kind); @@ -173,8 +174,9 @@ static ValueMap clone_params(LLVMValueRef Src, LLVMValueRef Dst) { LLVMValueRef SrcNext = nullptr; LLVMValueRef DstNext = nullptr; while (true) { - const char *Name = LLVMGetValueName(SrcCur); - LLVMSetValueName(DstCur, Name); + size_t NameLen; + const char *Name = LLVMGetValueName2(SrcCur, &NameLen); + LLVMSetValueName2(DstCur, Name, NameLen); VMap[SrcCur] = DstCur; @@ -231,7 +233,8 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { // Maybe it is a symbol if (LLVMIsAGlobalValue(Cst)) { - const char *Name = LLVMGetValueName(Cst); + size_t NameLen; + const char *Name = LLVMGetValueName2(Cst, &NameLen); // Try function if (LLVMIsAFunction(Cst)) { @@ -245,10 +248,19 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { // Try global variable if (LLVMIsAGlobalVariable(Cst)) { check_value_kind(Cst, LLVMGlobalVariableValueKind); - LLVMValueRef Dst = LLVMGetNamedGlobal(M, Name); + LLVMValueRef Dst = LLVMGetNamedGlobal(M, Name); if (Dst) return Dst; - report_fatal_error("Could not find function"); + report_fatal_error("Could not find variable"); + } + + // Try global alias + if (LLVMIsAGlobalAlias(Cst)) { + check_value_kind(Cst, LLVMGlobalAliasValueKind); + LLVMValueRef Dst = LLVMGetNamedGlobalAlias(M, Name, NameLen); + if (Dst) + return Dst; + report_fatal_error("Could not find alias"); } fprintf(stderr, "Could not find @%s\n", Name); @@ -310,6 +322,13 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { return LLVMGetUndef(TypeCloner(M).Clone(Cst)); } + // Try null + if (LLVMIsNull(Cst)) { + check_value_kind(Cst, LLVMConstantTokenNoneValueKind); + LLVMTypeRef Ty = TypeCloner(M).Clone(Cst); + return LLVMConstNull(Ty); + } + // Try float literal if (LLVMIsAConstantFP(Cst)) { check_value_kind(Cst, LLVMConstantFPValueKind); @@ -394,7 +413,8 @@ struct FunCloner { if (!LLVMIsAInstruction(Src)) report_fatal_error("Expected an instruction"); - const char *Name = LLVMGetValueName(Src); + size_t NameLen; + const char *Name = LLVMGetValueName2(Src, &NameLen); // Check if this is something we already computed. { @@ -437,7 +457,7 @@ struct FunCloner { LLVMBasicBlockRef ElseBB = DeclareBB(LLVMValueAsBasicBlock(Else)); LLVMValueRef Then = LLVMGetOperand(Src, 2); LLVMBasicBlockRef ThenBB = DeclareBB(LLVMValueAsBasicBlock(Then)); - Dst = LLVMBuildCondBr(Builder, Cond, ThenBB, ElseBB); + Dst = LLVMBuildCondBr(Builder, CloneValue(Cond), ThenBB, ElseBB); break; } case LLVMSwitch: @@ -630,6 +650,58 @@ struct FunCloner { LLVMSetCleanup(Dst, LLVMIsCleanup(Src)); break; } + case LLVMCleanupRet: { + LLVMValueRef CatchPad = CloneValue(LLVMGetOperand(Src, 0)); + LLVMBasicBlockRef Unwind = nullptr; + if (LLVMBasicBlockRef UDest = LLVMGetUnwindDest(Src)) + Unwind = DeclareBB(UDest); + Dst = LLVMBuildCleanupRet(Builder, CatchPad, Unwind); + break; + } + case LLVMCatchRet: { + LLVMValueRef CatchPad = CloneValue(LLVMGetOperand(Src, 0)); + LLVMBasicBlockRef SuccBB = DeclareBB(LLVMGetSuccessor(Src, 0)); + Dst = LLVMBuildCatchRet(Builder, CatchPad, SuccBB); + break; + } + case LLVMCatchPad: { + LLVMValueRef ParentPad = CloneValue(LLVMGetParentCatchSwitch(Src)); + SmallVector<LLVMValueRef, 8> Args; + int ArgCount = LLVMGetNumArgOperands(Src); + for (int i = 0; i < ArgCount; i++) + Args.push_back(CloneValue(LLVMGetOperand(Src, i))); + Dst = LLVMBuildCatchPad(Builder, ParentPad, + Args.data(), ArgCount, Name); + break; + } + case LLVMCleanupPad: { + LLVMValueRef ParentPad = CloneValue(LLVMGetOperand(Src, 0)); + SmallVector<LLVMValueRef, 8> Args; + int ArgCount = LLVMGetNumArgOperands(Src); + for (int i = 0; i < ArgCount; i++) + Args.push_back(CloneValue(LLVMGetArgOperand(Src, i))); + Dst = LLVMBuildCleanupPad(Builder, ParentPad, + Args.data(), ArgCount, Name); + break; + } + case LLVMCatchSwitch: { + LLVMValueRef ParentPad = CloneValue(LLVMGetOperand(Src, 0)); + LLVMBasicBlockRef UnwindBB = nullptr; + if (LLVMBasicBlockRef UDest = LLVMGetUnwindDest(Src)) { + UnwindBB = DeclareBB(UDest); + } + unsigned NumHandlers = LLVMGetNumHandlers(Src); + Dst = LLVMBuildCatchSwitch(Builder, ParentPad, UnwindBB, NumHandlers, Name); + if (NumHandlers > 0) { + LLVMBasicBlockRef *Handlers = static_cast<LLVMBasicBlockRef*>( + safe_malloc(NumHandlers * sizeof(LLVMBasicBlockRef))); + LLVMGetHandlers(Src, Handlers); + for (unsigned i = 0; i < NumHandlers; i++) + LLVMAddHandler(Dst, DeclareBB(Handlers[i])); + free(Handlers); + } + break; + } case LLVMExtractValue: { LLVMValueRef Agg = CloneValue(LLVMGetOperand(Src, 0)); if (LLVMGetNumIndices(Src) != 1) @@ -674,7 +746,8 @@ struct FunCloner { report_fatal_error("Basic block is not a basic block"); const char *Name = LLVMGetBasicBlockName(Src); - const char *VName = LLVMGetValueName(V); + size_t NameLen; + const char *VName = LLVMGetValueName2(V, &NameLen); if (Name != VName) report_fatal_error("Basic block name mismatch"); @@ -758,6 +831,8 @@ struct FunCloner { }; static void declare_symbols(LLVMModuleRef Src, LLVMModuleRef M) { + auto Ctx = LLVMGetModuleContext(M); + LLVMValueRef Begin = LLVMGetFirstGlobal(Src); LLVMValueRef End = LLVMGetLastGlobal(Src); @@ -770,7 +845,8 @@ static void declare_symbols(LLVMModuleRef Src, LLVMModuleRef M) { } while (true) { - const char *Name = LLVMGetValueName(Cur); + size_t NameLen; + const char *Name = LLVMGetValueName2(Cur, &NameLen); if (LLVMGetNamedGlobal(M, Name)) report_fatal_error("GlobalVariable already cloned"); LLVMAddGlobal(M, LLVMGetElementType(TypeCloner(M).Clone(Cur)), Name); @@ -795,15 +871,14 @@ FunDecl: if (!Begin) { if (End != nullptr) report_fatal_error("Range has an end but no beginning"); - return; + goto AliasDecl; } - auto Ctx = LLVMGetModuleContext(M); - Cur = Begin; Next = nullptr; while (true) { - const char *Name = LLVMGetValueName(Cur); + size_t NameLen; + const char *Name = LLVMGetValueName2(Cur, &NameLen); if (LLVMGetNamedFunction(M, Name)) report_fatal_error("Function already cloned"); auto Ty = LLVMGetElementType(TypeCloner(M).Clone(Cur)); @@ -834,6 +909,40 @@ FunDecl: Cur = Next; } + +AliasDecl: + Begin = LLVMGetFirstGlobalAlias(Src); + End = LLVMGetLastGlobalAlias(Src); + if (!Begin) { + if (End != nullptr) + report_fatal_error("Range has an end but no beginning"); + return; + } + + Cur = Begin; + Next = nullptr; + while (true) { + size_t NameLen; + const char *Name = LLVMGetValueName2(Cur, &NameLen); + if (LLVMGetNamedGlobalAlias(M, Name, NameLen)) + report_fatal_error("Global alias already cloned"); + LLVMTypeRef CurType = TypeCloner(M).Clone(Cur); + // FIXME: Allow NULL aliasee. + LLVMAddAlias(M, CurType, LLVMGetUndef(CurType), Name); + + Next = LLVMGetNextGlobalAlias(Cur); + if (Next == nullptr) { + if (Cur != End) + report_fatal_error(""); + break; + } + + LLVMValueRef Prev = LLVMGetPreviousGlobalAlias(Next); + if (Prev != Cur) + report_fatal_error("Next.Previous global is not Current"); + + Cur = Next; + } } static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { @@ -849,7 +958,8 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { } while (true) { - const char *Name = LLVMGetValueName(Cur); + size_t NameLen; + const char *Name = LLVMGetValueName2(Cur, &NameLen); LLVMValueRef G = LLVMGetNamedGlobal(M, Name); if (!G) report_fatal_error("GlobalVariable must have been declared already"); @@ -863,7 +973,7 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { LLVMSetLinkage(G, LLVMGetLinkage(Cur)); LLVMSetSection(G, LLVMGetSection(Cur)); LLVMSetVisibility(G, LLVMGetVisibility(Cur)); - LLVMSetUnnamedAddr(G, LLVMHasUnnamedAddr(Cur)); + LLVMSetUnnamedAddress(G, LLVMGetUnnamedAddress(Cur)); LLVMSetAlignment(G, LLVMGetAlignment(Cur)); Next = LLVMGetNextGlobal(Cur); @@ -886,19 +996,22 @@ FunClone: if (!Begin) { if (End != nullptr) report_fatal_error("Range has an end but no beginning"); - return; + goto AliasClone; } Cur = Begin; Next = nullptr; while (true) { - const char *Name = LLVMGetValueName(Cur); + size_t NameLen; + const char *Name = LLVMGetValueName2(Cur, &NameLen); LLVMValueRef Fun = LLVMGetNamedFunction(M, Name); if (!Fun) report_fatal_error("Function must have been declared already"); if (LLVMHasPersonalityFn(Cur)) { - const char *FName = LLVMGetValueName(LLVMGetPersonalityFn(Cur)); + size_t FNameLen; + const char *FName = LLVMGetValueName2(LLVMGetPersonalityFn(Cur), + &FNameLen); LLVMValueRef P = LLVMGetNamedFunction(M, FName); if (!P) report_fatal_error("Could not find personality function"); @@ -921,36 +1034,90 @@ FunClone: Cur = Next; } + +AliasClone: + Begin = LLVMGetFirstGlobalAlias(Src); + End = LLVMGetLastGlobalAlias(Src); + if (!Begin) { + if (End != nullptr) + report_fatal_error("Range has an end but no beginning"); + return; + } + + Cur = Begin; + Next = nullptr; + while (true) { + size_t NameLen; + const char *Name = LLVMGetValueName2(Cur, &NameLen); + LLVMValueRef Alias = LLVMGetNamedGlobalAlias(M, Name, NameLen); + if (!Alias) + report_fatal_error("Global alias must have been declared already"); + + if (LLVMValueRef Aliasee = LLVMAliasGetAliasee(Cur)) { + LLVMAliasSetAliasee(Alias, clone_constant(Aliasee, M)); + } + + LLVMSetLinkage(Alias, LLVMGetLinkage(Cur)); + LLVMSetUnnamedAddress(Alias, LLVMGetUnnamedAddress(Cur)); + + Next = LLVMGetNextGlobalAlias(Cur); + if (Next == nullptr) { + if (Cur != End) + report_fatal_error("Last global alias does not match End"); + break; + } + + LLVMValueRef Prev = LLVMGetPreviousGlobalAlias(Next); + if (Prev != Cur) + report_fatal_error("Next.Previous global alias is not Current"); + + Cur = Next; + } } int llvm_echo(void) { LLVMEnablePrettyStackTrace(); LLVMModuleRef Src = llvm_load_module(false, true); - size_t Len; - const char *ModuleName = LLVMGetModuleIdentifier(Src, &Len); + size_t SourceFileLen; + const char *SourceFileName = LLVMGetSourceFileName(Src, &SourceFileLen); + size_t ModuleIdentLen; + const char *ModuleName = LLVMGetModuleIdentifier(Src, &ModuleIdentLen); LLVMContextRef Ctx = LLVMContextCreate(); LLVMModuleRef M = LLVMModuleCreateWithNameInContext(ModuleName, Ctx); - // This whole switcharound is done because the C API has no way to - // set the source_filename - LLVMSetModuleIdentifier(M, "", 0); - LLVMGetModuleIdentifier(M, &Len); - if (Len != 0) - report_fatal_error("LLVM{Set,Get}ModuleIdentifier failed"); - LLVMSetModuleIdentifier(M, ModuleName, strlen(ModuleName)); + LLVMSetSourceFileName(M, SourceFileName, SourceFileLen); + LLVMSetModuleIdentifier(M, ModuleName, ModuleIdentLen); + + size_t SourceFlagsLen; + LLVMModuleFlagEntry *ModuleFlags = + LLVMCopyModuleFlagsMetadata(Src, &SourceFlagsLen); + for (unsigned i = 0; i < SourceFlagsLen; ++i) { + size_t EntryNameLen; + const char *EntryName = + LLVMModuleFlagEntriesGetKey(ModuleFlags, i, &EntryNameLen); + LLVMAddModuleFlag(M, LLVMModuleFlagEntriesGetFlagBehavior(ModuleFlags, i), + EntryName, EntryNameLen, + LLVMModuleFlagEntriesGetMetadata(ModuleFlags, i)); + } LLVMSetTarget(M, LLVMGetTarget(Src)); LLVMSetModuleDataLayout(M, LLVMGetModuleDataLayout(Src)); if (strcmp(LLVMGetDataLayoutStr(M), LLVMGetDataLayoutStr(Src))) report_fatal_error("Inconsistent DataLayout string representation"); + size_t ModuleInlineAsmLen; + const char *ModuleAsm = LLVMGetModuleInlineAsm(Src, &ModuleInlineAsmLen); + LLVMSetModuleInlineAsm2(M, ModuleAsm, ModuleInlineAsmLen); + declare_symbols(Src, M); clone_symbols(Src, M); char *Str = LLVMPrintModuleToString(M); fputs(Str, stdout); + LLVMDisposeModuleFlagsMetadata(ModuleFlags); LLVMDisposeMessage(Str); + LLVMDisposeModule(Src); LLVMDisposeModule(M); LLVMContextDispose(Ctx); diff --git a/tools/llvm-cat/llvm-cat.cpp b/tools/llvm-cat/llvm-cat.cpp index 26e0a0d6c61e..99973a22eb96 100644 --- a/tools/llvm-cat/llvm-cat.cpp +++ b/tools/llvm-cat/llvm-cat.cpp @@ -73,7 +73,7 @@ int main(int argc, char **argv) { Err.print(argv[0], errs()); return 1; } - Writer.writeModule(M.get()); + Writer.writeModule(*M); OwnedMods.push_back(std::move(M)); } Writer.writeStrtab(); diff --git a/tools/llvm-cfi-verify/CMakeLists.txt b/tools/llvm-cfi-verify/CMakeLists.txt index 7a008a66770c..ae12bec5e807 100644 --- a/tools/llvm-cfi-verify/CMakeLists.txt +++ b/tools/llvm-cfi-verify/CMakeLists.txt @@ -12,7 +12,8 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_tool(llvm-cfi-verify - llvm-cfi-verify.cpp) + llvm-cfi-verify.cpp + ) add_subdirectory(lib) target_link_libraries(llvm-cfi-verify PRIVATE LLVMCFIVerify) diff --git a/tools/llvm-cfi-verify/lib/CMakeLists.txt b/tools/llvm-cfi-verify/lib/CMakeLists.txt index cd728e004b26..82ca42e624a4 100644 --- a/tools/llvm-cfi-verify/lib/CMakeLists.txt +++ b/tools/llvm-cfi-verify/lib/CMakeLists.txt @@ -3,7 +3,8 @@ add_library(LLVMCFIVerify FileAnalysis.cpp FileAnalysis.h GraphBuilder.cpp - GraphBuilder.h) + GraphBuilder.h + ) llvm_update_compile_flags(LLVMCFIVerify) llvm_map_components_to_libnames(libs @@ -12,6 +13,7 @@ llvm_map_components_to_libnames(libs MCParser Object Support - Symbolize) + Symbolize + ) target_link_libraries(LLVMCFIVerify ${libs}) -set_target_properties(LLVMCFIVerify PROPERTIES FOLDER "Libraries")
\ No newline at end of file +set_target_properties(LLVMCFIVerify PROPERTIES FOLDER "Libraries") diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp index 754825447183..29819d8d28ed 100644 --- a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -85,6 +85,16 @@ Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) { if (!Analysis.Object) return make_error<UnsupportedDisassembly>("Failed to cast object"); + switch (Analysis.Object->getArch()) { + case Triple::x86: + case Triple::x86_64: + case Triple::aarch64: + case Triple::aarch64_be: + break; + default: + return make_error<UnsupportedDisassembly>("Unsupported architecture."); + } + Analysis.ObjectTriple = Analysis.Object->makeTriple(); Analysis.Features = Analysis.Object->getFeatures(); @@ -154,7 +164,8 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const { } bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const { - return MII->getName(InstrMeta.Instruction.getOpcode()) == "TRAP"; + const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); + return InstrDesc.isTrap(); } bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const { @@ -296,20 +307,38 @@ uint64_t FileAnalysis::indirectCFOperandClobber(const GraphResult &Graph) const else Node = Branch.Fallthrough; - while (Node != Graph.BaseAddress) { + // Some architectures (e.g., AArch64) cannot load in an indirect branch, so + // we allow them one load. + bool canLoad = !MII->get(IndirectCF.Instruction.getOpcode()).mayLoad(); + + // We walk backwards from the indirect CF. It is the last node returned by + // Graph.flattenAddress, so we skip it since we already handled it. + DenseSet<unsigned> CurRegisterNumbers = RegisterNumbers; + std::vector<uint64_t> Nodes = Graph.flattenAddress(Node); + for (auto I = Nodes.rbegin() + 1, E = Nodes.rend(); I != E; ++I) { + Node = *I; const Instr &NodeInstr = getInstructionOrDie(Node); const auto &InstrDesc = MII->get(NodeInstr.Instruction.getOpcode()); - for (unsigned RegNum : RegisterNumbers) { + for (auto RI = CurRegisterNumbers.begin(), RE = CurRegisterNumbers.end(); + RI != RE; ++RI) { + unsigned RegNum = *RI; if (InstrDesc.hasDefOfPhysReg(NodeInstr.Instruction, RegNum, - *RegisterInfo)) - return Node; + *RegisterInfo)) { + if (!canLoad || !InstrDesc.mayLoad()) + return Node; + canLoad = false; + CurRegisterNumbers.erase(RI); + // Add the registers this load reads to those we check for clobbers. + for (unsigned i = InstrDesc.getNumDefs(), + e = InstrDesc.getNumOperands(); i != e; i++) { + const auto Operand = NodeInstr.Instruction.getOperand(i); + if (Operand.isReg()) + CurRegisterNumbers.insert(Operand.getReg()); + } + break; + } } - - const auto &KV = Graph.IntermediateNodes.find(Node); - assert((KV != Graph.IntermediateNodes.end()) && - "Could not get next node."); - Node = KV->second; } } @@ -456,6 +485,9 @@ void FileAnalysis::parseSectionContents(ArrayRef<uint8_t> SectionBytes, if (!usesRegisterOperand(InstrMeta)) continue; + if (InstrDesc.isReturn()) + continue; + // Check if this instruction exists in the range of the DWARF metadata. if (!IgnoreDWARFFlag) { auto LineInfo = diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.h b/tools/llvm-cfi-verify/lib/FileAnalysis.h index ce81f8bfbe3b..3f0a70487882 100644 --- a/tools/llvm-cfi-verify/lib/FileAnalysis.h +++ b/tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -149,10 +149,13 @@ public: CFIProtectionStatus validateCFIProtection(const GraphResult &Graph) const; // Returns the first place the operand register is clobbered between the CFI- - // check and the indirect CF instruction execution. If the register is not - // modified, returns the address of the indirect CF instruction. The result is - // undefined if the provided graph does not fall under either the - // FAIL_REGISTER_CLOBBERED or PROTECTED status (see CFIProtectionStatus). + // check and the indirect CF instruction execution. We do this by walking + // backwards from the indirect CF and ensuring there is at most one load + // involving the operand register (which is the indirect CF itself on x86). + // If the register is not modified, returns the address of the indirect CF + // instruction. The result is undefined if the provided graph does not fall + // under either the FAIL_REGISTER_CLOBBERED or PROTECTED status (see + // CFIProtectionStatus). uint64_t indirectCFOperandClobber(const GraphResult& Graph) const; // Prints an instruction to the provided stream using this object's pretty- diff --git a/tools/llvm-cfi-verify/llvm-cfi-verify.cpp b/tools/llvm-cfi-verify/llvm-cfi-verify.cpp index 245ce05a254e..2fde74bd24c9 100644 --- a/tools/llvm-cfi-verify/llvm-cfi-verify.cpp +++ b/tools/llvm-cfi-verify/llvm-cfi-verify.cpp @@ -41,9 +41,87 @@ cl::opt<bool> PrintGraphs( "print-graphs", cl::desc("Print graphs around indirect CF instructions in DOT format."), cl::init(false)); +cl::opt<unsigned> PrintBlameContext( + "blame-context", + cl::desc("Print the blame context (if possible) for BAD instructions. This " + "specifies the number of lines of context to include, where zero " + "disables this feature."), + cl::init(0)); +cl::opt<unsigned> PrintBlameContextAll( + "blame-context-all", + cl::desc("Prints the blame context (if possible) for ALL instructions. " + "This specifies the number of lines of context for non-BAD " + "instructions (see --blame-context). If --blame-context is " + "unspecified, it prints this number of contextual lines for BAD " + "instructions as well."), + cl::init(0)); +cl::opt<bool> Summarize("summarize", cl::desc("Print the summary only."), + cl::init(false)); ExitOnError ExitOnErr; +void printBlameContext(const DILineInfo &LineInfo, unsigned Context) { + auto FileOrErr = MemoryBuffer::getFile(LineInfo.FileName); + if (!FileOrErr) { + errs() << "Could not open file: " << LineInfo.FileName << "\n"; + return; + } + + std::unique_ptr<MemoryBuffer> File = std::move(FileOrErr.get()); + SmallVector<StringRef, 100> Lines; + File->getBuffer().split(Lines, '\n'); + + for (unsigned i = std::max<size_t>(1, LineInfo.Line - Context); + i < + std::min<size_t>(Lines.size() + 1, LineInfo.Line + Context + 1); + ++i) { + if (i == LineInfo.Line) + outs() << ">"; + else + outs() << " "; + + outs() << i << ": " << Lines[i - 1] << "\n"; + } +} + +void printInstructionInformation(const FileAnalysis &Analysis, + const Instr &InstrMeta, + const GraphResult &Graph, + CFIProtectionStatus ProtectionStatus) { + outs() << "Instruction: " << format_hex(InstrMeta.VMAddress, 2) << " (" + << stringCFIProtectionStatus(ProtectionStatus) << "): "; + Analysis.printInstruction(InstrMeta, outs()); + outs() << " \n"; + + if (PrintGraphs) + Graph.printToDOT(Analysis, outs()); +} + +void printInstructionStatus(unsigned BlameLine, bool CFIProtected, + const DILineInfo &LineInfo) { + if (BlameLine) { + outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine + << "\n"; + if (CFIProtected) + outs() << "====> Unexpected Protected\n"; + else + outs() << "====> Expected Unprotected\n"; + + if (PrintBlameContextAll) + printBlameContext(LineInfo, PrintBlameContextAll); + } else { + if (CFIProtected) { + outs() << "====> Expected Protected\n"; + if (PrintBlameContextAll) + printBlameContext(LineInfo, PrintBlameContextAll); + } else { + outs() << "====> Unexpected Unprotected (BAD)\n"; + if (PrintBlameContext) + printBlameContext(LineInfo, PrintBlameContext); + } + } +} + void printIndirectCFInstructions(FileAnalysis &Analysis, const SpecialCaseList *SpecialCaseList) { uint64_t ExpectedProtected = 0; @@ -61,17 +139,10 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, Analysis.validateCFIProtection(Graph); bool CFIProtected = (ProtectionStatus == CFIProtectionStatus::PROTECTED); - if (CFIProtected) - outs() << "P "; - else - outs() << "U "; - - outs() << format_hex(Address, 2) << " | "; - Analysis.printInstruction(InstrMeta, outs()); - outs() << " \n"; - - if (PrintGraphs) - Graph.printToDOT(Analysis, outs()); + if (!Summarize) { + outs() << "-----------------------------------------------------\n"; + printInstructionInformation(Analysis, InstrMeta, Graph, ProtectionStatus); + } if (IgnoreDWARFFlag) { if (CFIProtected) @@ -88,22 +159,28 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, exit(EXIT_FAILURE); } - const auto &LineInfo = - InliningInfo->getFrame(InliningInfo->getNumberOfFrames() - 1); + const auto &LineInfo = InliningInfo->getFrame(0); // Print the inlining symbolisation of this instruction. - for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) { - const auto &Line = InliningInfo->getFrame(i); - outs() << " " << format_hex(Address, 2) << " = " << Line.FileName << ":" - << Line.Line << ":" << Line.Column << " (" << Line.FunctionName - << ")\n"; + if (!Summarize) { + for (uint32_t i = 0; i < InliningInfo->getNumberOfFrames(); ++i) { + const auto &Line = InliningInfo->getFrame(i); + outs() << " " << format_hex(Address, 2) << " = " << Line.FileName + << ":" << Line.Line << ":" << Line.Column << " (" + << Line.FunctionName << ")\n"; + } } if (!SpecialCaseList) { - if (CFIProtected) + if (CFIProtected) { + if (PrintBlameContextAll && !Summarize) + printBlameContext(LineInfo, PrintBlameContextAll); ExpectedProtected++; - else + } else { + if (PrintBlameContext && !Summarize) + printBlameContext(LineInfo, PrintBlameContext); UnexpectedUnprotected++; + } continue; } @@ -118,25 +195,20 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, } if (BlameLine) { - outs() << "Blacklist Match: " << BlacklistFilename << ":" << BlameLine - << "\n"; BlameCounter[BlameLine]++; - if (CFIProtected) { + if (CFIProtected) UnexpectedProtected++; - outs() << "====> Unexpected Protected\n"; - } else { + else ExpectedUnprotected++; - outs() << "====> Expected Unprotected\n"; - } } else { - if (CFIProtected) { + if (CFIProtected) ExpectedProtected++; - outs() << "====> Expected Protected\n"; - } else { + else UnexpectedUnprotected++; - outs() << "====> Unexpected Unprotected\n"; - } } + + if (!Summarize) + printInstructionStatus(BlameLine, CFIProtected, LineInfo); } uint64_t IndirectCFInstructions = ExpectedProtected + UnexpectedProtected + @@ -147,11 +219,12 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, return; } - outs() << formatv("Expected Protected: {0} ({1:P})\n" - "Unexpected Protected: {2} ({3:P})\n" - "Expected Unprotected: {4} ({5:P})\n" - "Unexpected Unprotected (BAD): {6} ({7:P})\n", - ExpectedProtected, + outs() << formatv("\nTotal Indirect CF Instructions: {0}\n" + "Expected Protected: {1} ({2:P})\n" + "Unexpected Protected: {3} ({4:P})\n" + "Expected Unprotected: {5} ({6:P})\n" + "Unexpected Unprotected (BAD): {7} ({8:P})\n", + IndirectCFInstructions, ExpectedProtected, ((double)ExpectedProtected) / IndirectCFInstructions, UnexpectedProtected, ((double)UnexpectedProtected) / IndirectCFInstructions, @@ -163,7 +236,7 @@ void printIndirectCFInstructions(FileAnalysis &Analysis, if (!SpecialCaseList) return; - outs() << "Blacklist Results:\n"; + outs() << "\nBlacklist Results:\n"; for (const auto &KV : BlameCounter) { outs() << " " << BlacklistFilename << ":" << KV.first << " affects " << KV.second << " indirect CF instructions.\n"; @@ -183,6 +256,9 @@ int main(int argc, char **argv) { InitializeAllAsmParsers(); InitializeAllDisassemblers(); + if (PrintBlameContextAll && !PrintBlameContext) + PrintBlameContext.setValue(PrintBlameContextAll); + std::unique_ptr<SpecialCaseList> SpecialCaseList; if (BlacklistFilename != "-") { std::string Error; diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt index 25f99cec9788..a0bd36c37315 100644 --- a/tools/llvm-config/CMakeLists.txt +++ b/tools/llvm-config/CMakeLists.txt @@ -9,8 +9,9 @@ add_llvm_tool(llvm-config ) # Compute the substitution values for various items. -get_property(LLVM_SYSTEM_LIBS_LIST TARGET LLVMSupport PROPERTY LLVM_SYSTEM_LIBS) -foreach(l ${LLVM_SYSTEM_LIBS_LIST}) +get_property(SUPPORT_SYSTEM_LIBS TARGET LLVMSupport PROPERTY LLVM_SYSTEM_LIBS) +get_property(WINDOWSMANIFEST_SYSTEM_LIBS TARGET LLVMWindowsManifest PROPERTY LLVM_SYSTEM_LIBS) +foreach(l ${SUPPORT_SYSTEM_LIBS} ${WINDOWSMANIFEST_SYSTEM_LIBS}) if(MSVC) set(SYSTEM_LIBS ${SYSTEM_LIBS} "${l}.lib") else() @@ -36,7 +37,7 @@ set(LLVM_CFLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} set(LLVM_CXXFLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${COMPILE_FLAGS} ${LLVM_DEFINITIONS}") set(LLVM_BUILD_SYSTEM cmake) set(LLVM_HAS_RTTI ${LLVM_CONFIG_HAS_RTTI}) -set(LLVM_DYLIB_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}${LLVM_VERSION_SUFFIX}") +set(LLVM_DYLIB_VERSION "${LLVM_VERSION_MAJOR}${LLVM_VERSION_SUFFIX}") set(LLVM_HAS_GLOBAL_ISEL "ON") # Use the C++ link flags, since they should be a superset of C link flags. @@ -64,18 +65,17 @@ endif() # Add the dependency on the generation step. add_file_dependencies(${CMAKE_CURRENT_SOURCE_DIR}/llvm-config.cpp ${BUILDVARIABLES_OBJPATH}) -if(CMAKE_CROSSCOMPILING) - set(${project}_LLVM_CONFIG_EXE "${LLVM_NATIVE_BUILD}/bin/llvm-config") - set(${project}_LLVM_CONFIG_EXE ${${project}_LLVM_CONFIG_EXE} PARENT_SCOPE) +if(CMAKE_CROSSCOMPILING AND NOT LLVM_CONFIG_PATH) + set(LLVM_CONFIG_PATH "${LLVM_NATIVE_BUILD}/bin/llvm-config" CACHE STRING "") - add_custom_command(OUTPUT "${${project}_LLVM_CONFIG_EXE}" + add_custom_command(OUTPUT "${LLVM_CONFIG_PATH}" COMMAND ${CMAKE_COMMAND} --build . --target llvm-config --config $<CONFIGURATION> DEPENDS ${LLVM_NATIVE_BUILD}/CMakeCache.txt WORKING_DIRECTORY ${LLVM_NATIVE_BUILD} COMMENT "Building native llvm-config..." USES_TERMINAL) - add_custom_target(${project}NativeLLVMConfig DEPENDS ${${project}_LLVM_CONFIG_EXE}) - add_dependencies(${project}NativeLLVMConfig CONFIGURE_LLVM_NATIVE) + add_custom_target(NativeLLVMConfig DEPENDS ${LLVM_CONFIG_PATH}) + add_dependencies(NativeLLVMConfig CONFIGURE_LLVM_NATIVE) - add_dependencies(llvm-config ${project}NativeLLVMConfig) -endif(CMAKE_CROSSCOMPILING) + add_dependencies(llvm-config NativeLLVMConfig) +endif() diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp index 08b096afb052..892adc3b9dd8 100644 --- a/tools/llvm-config/llvm-config.cpp +++ b/tools/llvm-config/llvm-config.cpp @@ -17,15 +17,16 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Config/llvm-config.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/Config/config.h" -#include "llvm/Config/llvm-config.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cstdlib> #include <set> @@ -62,7 +63,7 @@ enum LinkMode { LinkModeStatic = 2, }; -/// \brief Traverse a single component adding to the topological ordering in +/// Traverse a single component adding to the topological ordering in /// \arg RequiredLibs. /// /// \param Name - The component to traverse. @@ -129,7 +130,7 @@ static void VisitComponent(const std::string &Name, } } -/// \brief Compute the list of required libraries for a given list of +/// Compute the list of required libraries for a given list of /// components, in an order suitable for passing to a linker (that is, libraries /// appear prior to their dependencies). /// @@ -223,7 +224,7 @@ Typical components:\n\ exit(1); } -/// \brief Compute the path to the main executable. +/// Compute the path to the main executable. std::string GetExecutablePath(const char *Argv0) { // This just needs to be some symbol in the binary; C++ doesn't // allow taking the address of ::main however. @@ -231,7 +232,7 @@ std::string GetExecutablePath(const char *Argv0) { return llvm::sys::fs::getMainExecutable(Argv0, P); } -/// \brief Expand the semi-colon delimited LLVM_DYLIB_COMPONENTS into +/// Expand the semi-colon delimited LLVM_DYLIB_COMPONENTS into /// the full list of components. std::vector<std::string> GetAllDyLibComponents(const bool IsInDevelopmentTree, const bool GetComponentNames, @@ -522,7 +523,7 @@ int main(int argc, char **argv) { if (DyLibExists && !sys::fs::exists(path)) { Components = GetAllDyLibComponents(IsInDevelopmentTree, true, DirSep); - std::sort(Components.begin(), Components.end()); + llvm::sort(Components.begin(), Components.end()); break; } } @@ -579,7 +580,7 @@ int main(int argc, char **argv) { usage(); if (LinkMode == LinkModeShared && !DyLibExists && !BuiltSharedLibs) { - errs() << "llvm-config: error: " << DyLibName << " is missing\n"; + WithColor::error(errs(), "llvm-config") << DyLibName << " is missing\n"; return 1; } @@ -612,19 +613,19 @@ int main(int argc, char **argv) { break; // Using component shared libraries. for (auto &Lib : MissingLibs) - errs() << "llvm-config: error: missing: " << Lib << "\n"; + WithColor::error(errs(), "llvm-config") << "missing: " << Lib << "\n"; return 1; case LinkModeAuto: if (DyLibExists) { LinkMode = LinkModeShared; break; } - errs() - << "llvm-config: error: component libraries and shared library\n\n"; + WithColor::error(errs(), "llvm-config") + << "component libraries and shared library\n\n"; LLVM_FALLTHROUGH; case LinkModeStatic: for (auto &Lib : MissingLibs) - errs() << "llvm-config: error: missing: " << Lib << "\n"; + WithColor::error(errs(), "llvm-config") << "missing: " << Lib << "\n"; return 1; } } else if (LinkMode == LinkModeAuto) { @@ -707,7 +708,8 @@ int main(int argc, char **argv) { OS << (LinkMode == LinkModeStatic ? LLVM_SYSTEM_LIBS : "") << '\n'; } } else if (!Components.empty()) { - errs() << "llvm-config: error: components given, but unused\n\n"; + WithColor::error(errs(), "llvm-config") + << "components given, but unused\n\n"; usage(); } diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index c5ea50bff273..e93b63d388e0 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/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/tools/llvm-cov/CoverageExporter.h b/tools/llvm-cov/CoverageExporter.h new file mode 100644 index 000000000000..884fba96d618 --- /dev/null +++ b/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/tools/llvm-cov/CoverageExporterJson.cpp b/tools/llvm-cov/CoverageExporterJson.cpp index 7b700908968d..56c3a0003b02 100644 --- a/tools/llvm-cov/CoverageExporterJson.cpp +++ b/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/tools/llvm-cov/CoverageExporterJson.h b/tools/llvm-cov/CoverageExporterJson.h new file mode 100644 index 000000000000..f88dffa0ebea --- /dev/null +++ b/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/tools/llvm-cov/CoverageFilters.cpp b/tools/llvm-cov/CoverageFilters.cpp index 441179601dcc..4dd0f552c7e0 100644 --- a/tools/llvm-cov/CoverageFilters.cpp +++ b/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/tools/llvm-cov/CoverageFilters.h b/tools/llvm-cov/CoverageFilters.h index aeaf61de1730..6424ca5a8081 100644 --- a/tools/llvm-cov/CoverageFilters.h +++ b/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/tools/llvm-cov/CoverageReport.cpp b/tools/llvm-cov/CoverageReport.cpp index 9c553a7f64c7..607a3ceb30cb 100644 --- a/tools/llvm-cov/CoverageReport.cpp +++ b/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/tools/llvm-cov/CoverageReport.h b/tools/llvm-cov/CoverageReport.h index 1c9e68e832f3..4a6527e9fe5d 100644 --- a/tools/llvm-cov/CoverageReport.h +++ b/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/tools/llvm-cov/CoverageSummaryInfo.h b/tools/llvm-cov/CoverageSummaryInfo.h index 8eae0b7fec97..0845e2ce2e77 100644 --- a/tools/llvm-cov/CoverageSummaryInfo.h +++ b/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/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h index 17614c4e9ba2..20085a957bb5 100644 --- a/tools/llvm-cov/CoverageViewOptions.h +++ b/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/tools/llvm-cov/RenderingSupport.h b/tools/llvm-cov/RenderingSupport.h index aa70fbc23e3c..2cfe24919142 100644 --- a/tools/llvm-cov/RenderingSupport.h +++ b/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/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index 8c39dab580de..a5a8fa9a4814 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/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/tools/llvm-cov/SourceCoverageView.h b/tools/llvm-cov/SourceCoverageView.h index 7f58ea5d7be8..e3a2f9e5c0b4 100644 --- a/tools/llvm-cov/SourceCoverageView.h +++ b/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/tools/llvm-cov/SourceCoverageViewHTML.cpp b/tools/llvm-cov/SourceCoverageViewHTML.cpp index e45c6f4cb473..acb67aa5cfc7 100644 --- a/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/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/tools/llvm-cov/SourceCoverageViewHTML.h b/tools/llvm-cov/SourceCoverageViewHTML.h index 91b4ad4e220c..cb41fcaf37b9 100644 --- a/tools/llvm-cov/SourceCoverageViewHTML.h +++ b/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/tools/llvm-cov/SourceCoverageViewText.cpp b/tools/llvm-cov/SourceCoverageViewText.cpp index 2480ee9f416a..aac70baed613 100644 --- a/tools/llvm-cov/SourceCoverageViewText.cpp +++ b/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/tools/llvm-cov/SourceCoverageViewText.h b/tools/llvm-cov/SourceCoverageViewText.h index cabf91975df3..a46f35cc6495 100644 --- a/tools/llvm-cov/SourceCoverageViewText.h +++ b/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/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp index 4713d75f17dd..e07abdbd17f1 100644 --- a/tools/llvm-cov/TestingSupport.cpp +++ b/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/tools/llvm-cov/llvm-cov.cpp b/tools/llvm-cov/llvm-cov.cpp index 158415870250..4c3b574451c3 100644 --- a/tools/llvm-cov/llvm-cov.cpp +++ b/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/tools/llvm-cvtres/Opts.td b/tools/llvm-cvtres/Opts.td index 64041bceb034..cb3bd402004b 100644 --- a/tools/llvm-cvtres/Opts.td +++ b/tools/llvm-cvtres/Opts.td @@ -1,11 +1,13 @@ include "llvm/Option/OptParser.td" -def DEFINE : Joined<["/"], "DEFINE:">, HelpText<"">, MetaVarName<"symbol">; -def FOLDDUPS : Flag<["/"], "FOLDDUPS:">, HelpText<"">; -def MACHINE : Joined<["/"], "MACHINE:">, HelpText<"">, MetaVarName<"{ARM|EBC|IA64|X64|X86}">; -def NOLOGO : Flag<["/"], "NOLOGO">, HelpText<"">; -def OUT : Joined<["/"], "OUT:">, HelpText<"">, MetaVarName<"filename">; -def READONLY : Flag<["/"], "READONLY">, HelpText<"">; -def VERBOSE : Flag<["/"], "VERBOSE">, HelpText<"">; -def HELP : Flag<["/"], "HELP">; -def H : Flag<["/"], "H">, Alias<HELP>; +// All the switches can be preceded by either '/' or '-'. + +def DEFINE : Joined<["/", "-"], "DEFINE:">, HelpText<"">, MetaVarName<"symbol">; +def FOLDDUPS : Flag<["/", "-"], "FOLDDUPS:">, HelpText<"">; +def MACHINE : Joined<["/", "-"], "MACHINE:">, HelpText<"">, MetaVarName<"{ARM|ARM64|EBC|IA64|X64|X86}">; +def NOLOGO : Flag<["/", "-"], "NOLOGO">, HelpText<"">; +def OUT : Joined<["/", "-"], "OUT:">, HelpText<"">, MetaVarName<"filename">; +def READONLY : Flag<["/", "-"], "READONLY">, HelpText<"">; +def VERBOSE : Flag<["/", "-"], "VERBOSE">, HelpText<"">; +def HELP : Flag<["/", "-"], "HELP">; +def H : Flag<["/", "-"], "H">, Alias<HELP>; diff --git a/tools/llvm-cvtres/llvm-cvtres.cpp b/tools/llvm-cvtres/llvm-cvtres.cpp index 2f33afdb0be9..5d8e6ab3929b 100644 --- a/tools/llvm-cvtres/llvm-cvtres.cpp +++ b/tools/llvm-cvtres/llvm-cvtres.cpp @@ -20,6 +20,7 @@ #include "llvm/Option/Option.h" #include "llvm/Support/BinaryStreamError.h" #include "llvm/Support/Error.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" @@ -63,8 +64,6 @@ class CvtResOptTable : public opt::OptTable { public: CvtResOptTable() : OptTable(InfoTable, true) {} }; - -static ExitOnError ExitOnErr; } LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { @@ -95,22 +94,12 @@ template <typename T> T error(Expected<T> EC) { return std::move(EC.get()); } -int main(int argc_, const char *argv_[]) { - sys::PrintStackTraceOnErrorSignal(argv_[0]); - PrettyStackTraceProgram X(argc_, argv_); - - ExitOnErr.setBanner("llvm-cvtres: "); - - 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. +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); CvtResOptTable T; unsigned MAI, MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(argv_ + 1, argc_); + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); if (InputArgs.hasArg(OPT_HELP)) { diff --git a/tools/llvm-cxxdump/Error.cpp b/tools/llvm-cxxdump/Error.cpp index d59547e3a2ce..54207fad32af 100644 --- a/tools/llvm-cxxdump/Error.cpp +++ b/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/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp index 9b687e4fbe22..09e40d9b0db7 100644 --- a/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/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/tools/llvm-cxxfilt/llvm-cxxfilt.cpp b/tools/llvm-cxxfilt/llvm-cxxfilt.cpp index 9c6a1612fa08..afc1e4a8d128 100644 --- a/tools/llvm-cxxfilt/llvm-cxxfilt.cpp +++ b/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/tools/llvm-demangle-fuzzer/CMakeLists.txt b/tools/llvm-demangle-fuzzer/CMakeLists.txt index 34a04b43fafd..0fe711cdb16c 100644 --- a/tools/llvm-demangle-fuzzer/CMakeLists.txt +++ b/tools/llvm-demangle-fuzzer/CMakeLists.txt @@ -6,4 +6,5 @@ set(LLVM_LINK_COMPONENTS add_llvm_fuzzer(llvm-demangle-fuzzer llvm-demangle-fuzzer.cpp - DUMMY_MAIN DummyDemanglerFuzzer.cpp) + DUMMY_MAIN DummyDemanglerFuzzer.cpp + ) diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp index 95a63d7f9c83..af0a055ea21f 100644 --- a/tools/llvm-diff/DifferenceEngine.cpp +++ b/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/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index c91aa1c71a15..8143a2a5a934 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/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/tools/llvm-dwarfdump/Statistics.cpp b/tools/llvm-dwarfdump/Statistics.cpp index 9a7454a52624..5af853d4ef28 100644 --- a/tools/llvm-dwarfdump/Statistics.cpp +++ b/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/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp b/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp index 53c74df40280..2caccaa0fb68 100644 --- a/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp +++ b/tools/llvm-dwarfdump/fuzzer/llvm-dwarfdump-fuzzer.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file implements a function that runs llvm-dwarfdump +/// This file implements a function that runs llvm-dwarfdump /// on a single input. This function is then linked into the Fuzzer library. /// //===----------------------------------------------------------------------===// diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index 12c005de6005..d75f33906098 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/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/tools/llvm-dwp/llvm-dwp.cpp b/tools/llvm-dwp/llvm-dwp.cpp index dbbe61bf3b06..d3380b5b57a1 100644 --- a/tools/llvm-dwp/llvm-dwp.cpp +++ b/tools/llvm-dwp/llvm-dwp.cpp @@ -24,14 +24,16 @@ #include "llvm/MC/MCContext.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCStreamer.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.def" +#include "llvm/MC/MCTargetOptionsCommandFlags.inc" #include "llvm/Object/Decompressor.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/DataExtractor.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -183,7 +185,7 @@ static Expected<CompileUnitIdentifiers> getCUIdentifiers(StringRef Abbrev, break; default: DWARFFormValue::skipValue(Form, InfoData, &Offset, - DWARFFormParams({Version, AddrSize, Format})); + dwarf::FormParams({Version, AddrSize, Format})); } } return ID; @@ -640,6 +642,7 @@ static int error(const Twine &Error, const Twine &Context) { } int main(int argc, char **argv) { + InitLLVM X(argc, argv); ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files"); @@ -673,8 +676,13 @@ int main(int argc, char **argv) { MCContext MC(MAI.get(), MRI.get(), &MOFI); MOFI.InitMCObjectFileInfo(TheTriple, /*PIC*/ false, MC); + std::unique_ptr<MCSubtargetInfo> MSTI( + TheTarget->createMCSubtargetInfo(TripleName, "", "")); + if (!MSTI) + return error("no subtarget info for target " + TripleName, Context); + MCTargetOptions Options; - auto MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, "", Options); + auto MAB = TheTarget->createMCAsmBackend(*MSTI, *MRI, Options); if (!MAB) return error("no asm backend for target " + TripleName, Context); @@ -682,11 +690,6 @@ int main(int argc, char **argv) { if (!MII) return error("no instr info info for target " + TripleName, Context); - std::unique_ptr<MCSubtargetInfo> MSTI( - TheTarget->createMCSubtargetInfo(TripleName, "", "")); - if (!MSTI) - return error("no subtarget info for target " + TripleName, Context); - MCCodeEmitter *MCE = TheTarget->createMCCodeEmitter(*MII, *MRI, MC); if (!MCE) return error("no code emitter for target " + TripleName, Context); @@ -699,9 +702,9 @@ int main(int argc, char **argv) { MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer( - TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB), OutFile, - std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, MCOptions.MCRelaxAll, - MCOptions.MCIncrementalLinkerCompatible, + TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB), + MAB->createObjectWriter(OutFile), std::unique_ptr<MCCodeEmitter>(MCE), + *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ false)); if (!MS) return error("no object streamer for target " + TripleName, Context); diff --git a/tools/llvm-exegesis/CMakeLists.txt b/tools/llvm-exegesis/CMakeLists.txt new file mode 100644 index 000000000000..65b1ada85299 --- /dev/null +++ b/tools/llvm-exegesis/CMakeLists.txt @@ -0,0 +1,26 @@ + +set(LLVM_LINK_COMPONENTS + Support + native + ) + +add_llvm_tool(llvm-exegesis + llvm-exegesis.cpp + ) + +add_subdirectory(lib) + +# Link the native exegesis target if compiled and on the right host. +if ((LLVM_TARGETS_TO_BUILD MATCHES "${LLVM_NATIVE_ARCH}") AND (LLVM_EXEGESIS_TARGETS MATCHES "${LLVM_NATIVE_ARCH}")) + set(LLVM_EXEGESIS_NATIVE_ARCH "${LLVM_NATIVE_ARCH}") +endif() + +if (LLVM_EXEGESIS_NATIVE_ARCH) + set(LLVM_EXEGESIS_NATIVE_TARGET "LLVMExegesis${LLVM_EXEGESIS_NATIVE_ARCH}") + set_source_files_properties(llvm-exegesis.cpp PROPERTIES COMPILE_FLAGS "-DLLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET=Initialize${LLVM_EXEGESIS_NATIVE_ARCH}ExegesisTarget") +endif() + +target_link_libraries(llvm-exegesis PRIVATE + LLVMExegesis + ${LLVM_EXEGESIS_NATIVE_TARGET} + ) diff --git a/tools/llvm-exegesis/LLVMBuild.txt b/tools/llvm-exegesis/LLVMBuild.txt new file mode 100644 index 000000000000..2955dc5c8801 --- /dev/null +++ b/tools/llvm-exegesis/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-exegesis/LLVMBuild.txt ----------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-exegesis +parent = Tools +required_libraries = CodeGen ExecutionEngine MC MCJIT Native NativeCodeGen Object Support diff --git a/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt b/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt new file mode 100644 index 000000000000..a251b8ff683e --- /dev/null +++ b/tools/llvm-exegesis/lib/AArch64/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/AArch64 + ${LLVM_BINARY_DIR}/lib/Target/AArch64 + ) + +add_library(LLVMExegesisAArch64 + STATIC + Target.cpp + ) + +llvm_update_compile_flags(LLVMExegesisAArch64) +llvm_map_components_to_libnames(libs + AArch64 + Exegesis + ) + +target_link_libraries(LLVMExegesisAArch64 ${libs}) +set_target_properties(LLVMExegesisAArch64 PROPERTIES FOLDER "Libraries") diff --git a/tools/llvm-exegesis/lib/AArch64/LLVMBuild.txt b/tools/llvm-exegesis/lib/AArch64/LLVMBuild.txt new file mode 100644 index 000000000000..886fcb22749e --- /dev/null +++ b/tools/llvm-exegesis/lib/AArch64/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-exegesis/lib/AArch64/LLVMBuild.txt ----------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = ExegesisAArch64 +parent = Libraries +required_libraries = AArch64 diff --git a/tools/llvm-exegesis/lib/AArch64/Target.cpp b/tools/llvm-exegesis/lib/AArch64/Target.cpp new file mode 100644 index 000000000000..88df44bd94c2 --- /dev/null +++ b/tools/llvm-exegesis/lib/AArch64/Target.cpp @@ -0,0 +1,54 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "../Target.h" +#include "../Latency.h" +#include "AArch64.h" + +namespace exegesis { + +namespace { + +class AArch64LatencyBenchmarkRunner : public LatencyBenchmarkRunner { +public: + AArch64LatencyBenchmarkRunner(const LLVMState &State) + : LatencyBenchmarkRunner(State) {} + +private: + const char *getCounterName() const override { + // All AArch64 subtargets have CPU_CYCLES as the cycle counter name + return "CPU_CYCLES"; + } +}; + +class ExegesisAArch64Target : public ExegesisTarget { + bool matchesArch(llvm::Triple::ArchType Arch) const override { + return Arch == llvm::Triple::aarch64 || Arch == llvm::Triple::aarch64_be; + } + void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override { + // Function return is a pseudo-instruction that needs to be expanded + PM.add(llvm::createAArch64ExpandPseudoPass()); + } + std::unique_ptr<BenchmarkRunner> + createLatencyBenchmarkRunner(const LLVMState &State) const override { + return llvm::make_unique<AArch64LatencyBenchmarkRunner>(State); + } +}; + +} // namespace + +static ExegesisTarget *getTheExegesisAArch64Target() { + static ExegesisAArch64Target Target; + return &Target; +} + +void InitializeAArch64ExegesisTarget() { + ExegesisTarget::registerTarget(getTheExegesisAArch64Target()); +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Analysis.cpp b/tools/llvm-exegesis/lib/Analysis.cpp new file mode 100644 index 000000000000..bb5118080967 --- /dev/null +++ b/tools/llvm-exegesis/lib/Analysis.cpp @@ -0,0 +1,753 @@ +//===-- Analysis.cpp --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Analysis.h" +#include "BenchmarkResult.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/Support/FormatVariadic.h" +#include <unordered_set> +#include <vector> + +namespace exegesis { + +static const char kCsvSep = ','; + +namespace { + +enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString }; + +template <EscapeTag Tag> +void writeEscaped(llvm::raw_ostream &OS, const llvm::StringRef S); + +template <> +void writeEscaped<kEscapeCsv>(llvm::raw_ostream &OS, const llvm::StringRef S) { + if (std::find(S.begin(), S.end(), kCsvSep) == S.end()) { + OS << S; + } else { + // Needs escaping. + OS << '"'; + for (const char C : S) { + if (C == '"') + OS << "\"\""; + else + OS << C; + } + OS << '"'; + } +} + +template <> +void writeEscaped<kEscapeHtml>(llvm::raw_ostream &OS, const llvm::StringRef S) { + for (const char C : S) { + if (C == '<') + OS << "<"; + else if (C == '>') + OS << ">"; + else if (C == '&') + OS << "&"; + else + OS << C; + } +} + +template <> +void writeEscaped<kEscapeHtmlString>(llvm::raw_ostream &OS, + const llvm::StringRef S) { + for (const char C : S) { + if (C == '"') + OS << "\\\""; + else + OS << C; + } +} + +} // namespace + +template <EscapeTag Tag> +static void +writeClusterId(llvm::raw_ostream &OS, + const InstructionBenchmarkClustering::ClusterId &CID) { + if (CID.isNoise()) + writeEscaped<Tag>(OS, "[noise]"); + else if (CID.isError()) + writeEscaped<Tag>(OS, "[error]"); + else + OS << CID.getId(); +} + +template <EscapeTag Tag> +static void writeMeasurementValue(llvm::raw_ostream &OS, const double Value) { + writeEscaped<Tag>(OS, llvm::formatv("{0:F}", Value).str()); +} + +template <typename EscapeTag, EscapeTag Tag> +void Analysis::writeSnippet(llvm::raw_ostream &OS, + llvm::ArrayRef<uint8_t> Bytes, + const char *Separator) const { + llvm::SmallVector<std::string, 3> Lines; + // Parse the asm snippet and print it. + while (!Bytes.empty()) { + llvm::MCInst MI; + uint64_t MISize = 0; + if (!Disasm_->getInstruction(MI, MISize, Bytes, 0, llvm::nulls(), + llvm::nulls())) { + writeEscaped<Tag>(OS, llvm::join(Lines, Separator)); + writeEscaped<Tag>(OS, Separator); + writeEscaped<Tag>(OS, "[error decoding asm snippet]"); + return; + } + Lines.emplace_back(); + std::string &Line = Lines.back(); + llvm::raw_string_ostream OSS(Line); + InstPrinter_->printInst(&MI, OSS, "", *SubtargetInfo_); + Bytes = Bytes.drop_front(MISize); + OSS.flush(); + Line = llvm::StringRef(Line).trim().str(); + } + writeEscaped<Tag>(OS, llvm::join(Lines, Separator)); +} + +// Prints a row representing an instruction, along with scheduling info and +// point coordinates (measurements). +void Analysis::printInstructionRowCsv(const size_t PointId, + llvm::raw_ostream &OS) const { + const InstructionBenchmark &Point = Clustering_.getPoints()[PointId]; + writeClusterId<kEscapeCsv>(OS, Clustering_.getClusterIdForPoint(PointId)); + OS << kCsvSep; + writeSnippet<EscapeTag, kEscapeCsv>(OS, Point.AssembledSnippet, "; "); + OS << kCsvSep; + writeEscaped<kEscapeCsv>(OS, Point.Key.Config); + OS << kCsvSep; + assert(!Point.Key.Instructions.empty()); + // FIXME: Resolve variant classes. + const unsigned SchedClassId = + InstrInfo_->get(Point.Key.Instructions[0].getOpcode()).getSchedClass(); +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + const auto &SchedModel = SubtargetInfo_->getSchedModel(); + const llvm::MCSchedClassDesc *const SCDesc = + SchedModel.getSchedClassDesc(SchedClassId); + writeEscaped<kEscapeCsv>(OS, SCDesc->Name); +#else + OS << SchedClassId; +#endif + for (const auto &Measurement : Point.Measurements) { + OS << kCsvSep; + writeMeasurementValue<kEscapeCsv>(OS, Measurement.Value); + } + OS << "\n"; +} + +Analysis::Analysis(const llvm::Target &Target, + const InstructionBenchmarkClustering &Clustering) + : Clustering_(Clustering) { + if (Clustering.getPoints().empty()) + return; + + const InstructionBenchmark &FirstPoint = Clustering.getPoints().front(); + InstrInfo_.reset(Target.createMCInstrInfo()); + RegInfo_.reset(Target.createMCRegInfo(FirstPoint.LLVMTriple)); + AsmInfo_.reset(Target.createMCAsmInfo(*RegInfo_, FirstPoint.LLVMTriple)); + SubtargetInfo_.reset(Target.createMCSubtargetInfo(FirstPoint.LLVMTriple, + FirstPoint.CpuName, "")); + InstPrinter_.reset(Target.createMCInstPrinter( + llvm::Triple(FirstPoint.LLVMTriple), 0 /*default variant*/, *AsmInfo_, + *InstrInfo_, *RegInfo_)); + + Context_ = llvm::make_unique<llvm::MCContext>(AsmInfo_.get(), RegInfo_.get(), + &ObjectFileInfo_); + Disasm_.reset(Target.createMCDisassembler(*SubtargetInfo_, *Context_)); + assert(Disasm_ && "cannot create MCDisassembler. missing call to " + "InitializeXXXTargetDisassembler ?"); +} + +template <> +llvm::Error +Analysis::run<Analysis::PrintClusters>(llvm::raw_ostream &OS) const { + if (Clustering_.getPoints().empty()) + return llvm::Error::success(); + + // Write the header. + OS << "cluster_id" << kCsvSep << "opcode_name" << kCsvSep << "config" + << kCsvSep << "sched_class"; + for (const auto &Measurement : Clustering_.getPoints().front().Measurements) { + OS << kCsvSep; + writeEscaped<kEscapeCsv>(OS, Measurement.Key); + } + OS << "\n"; + + // Write the points. + const auto &Clusters = Clustering_.getValidClusters(); + for (size_t I = 0, E = Clusters.size(); I < E; ++I) { + for (const size_t PointId : Clusters[I].PointIndices) { + printInstructionRowCsv(PointId, OS); + } + OS << "\n\n"; + } + return llvm::Error::success(); +} + +std::unordered_map<unsigned, std::vector<size_t>> +Analysis::makePointsPerSchedClass() const { + std::unordered_map<unsigned, std::vector<size_t>> PointsPerSchedClass; + const auto &Points = Clustering_.getPoints(); + for (size_t PointId = 0, E = Points.size(); PointId < E; ++PointId) { + const InstructionBenchmark &Point = Points[PointId]; + if (!Point.Error.empty()) + continue; + assert(!Point.Key.Instructions.empty()); + const auto Opcode = Point.Key.Instructions[0].getOpcode(); + // FIXME: Resolve variant classes. + PointsPerSchedClass[InstrInfo_->get(Opcode).getSchedClass()].push_back( + PointId); + } + return PointsPerSchedClass; +} + +// Uops repeat the same opcode over again. Just show this opcode and show the +// whole snippet only on hover. +static void writeUopsSnippetHtml(llvm::raw_ostream &OS, + const std::vector<llvm::MCInst> &Instructions, + const llvm::MCInstrInfo &InstrInfo) { + if (Instructions.empty()) + return; + writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instructions[0].getOpcode())); + if (Instructions.size() > 1) + OS << " (x" << Instructions.size() << ")"; +} + +// Latency tries to find a serial path. Just show the opcode path and show the +// whole snippet only on hover. +static void +writeLatencySnippetHtml(llvm::raw_ostream &OS, + const std::vector<llvm::MCInst> &Instructions, + const llvm::MCInstrInfo &InstrInfo) { + bool First = true; + for (const llvm::MCInst &Instr : Instructions) { + if (First) + First = false; + else + OS << " → "; + writeEscaped<kEscapeHtml>(OS, InstrInfo.getName(Instr.getOpcode())); + } +} + +void Analysis::printSchedClassClustersHtml( + const std::vector<SchedClassCluster> &Clusters, const SchedClass &SC, + llvm::raw_ostream &OS) const { + const auto &Points = Clustering_.getPoints(); + OS << "<table class=\"sched-class-clusters\">"; + OS << "<tr><th>ClusterId</th><th>Opcode/Config</th>"; + assert(!Clusters.empty()); + for (const auto &Measurement : + Points[Clusters[0].getPointIds()[0]].Measurements) { + OS << "<th>"; + if (Measurement.DebugString.empty()) + writeEscaped<kEscapeHtml>(OS, Measurement.Key); + else + writeEscaped<kEscapeHtml>(OS, Measurement.DebugString); + OS << "</th>"; + } + OS << "</tr>"; + for (const SchedClassCluster &Cluster : Clusters) { + OS << "<tr class=\"" + << (Cluster.measurementsMatch(*SubtargetInfo_, SC, Clustering_) + ? "good-cluster" + : "bad-cluster") + << "\"><td>"; + writeClusterId<kEscapeHtml>(OS, Cluster.id()); + OS << "</td><td><ul>"; + for (const size_t PointId : Cluster.getPointIds()) { + const auto &Point = Points[PointId]; + OS << "<li><span class=\"mono\" title=\""; + writeSnippet<EscapeTag, kEscapeHtmlString>(OS, Point.AssembledSnippet, + "\n"); + OS << "\">"; + switch (Point.Mode) { + case InstructionBenchmark::Latency: + writeLatencySnippetHtml(OS, Point.Key.Instructions, *InstrInfo_); + break; + case InstructionBenchmark::Uops: + writeUopsSnippetHtml(OS, Point.Key.Instructions, *InstrInfo_); + break; + default: + llvm_unreachable("invalid mode"); + } + OS << "</span> <span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, Point.Key.Config); + OS << "</span></li>"; + } + OS << "</ul></td>"; + for (const auto &Stats : Cluster.getRepresentative()) { + OS << "<td class=\"measurement\">"; + writeMeasurementValue<kEscapeHtml>(OS, Stats.avg()); + OS << "<br><span class=\"minmax\">["; + writeMeasurementValue<kEscapeHtml>(OS, Stats.min()); + OS << ";"; + writeMeasurementValue<kEscapeHtml>(OS, Stats.max()); + OS << "]</span></td>"; + } + OS << "</tr>"; + } + OS << "</table>"; +} + +// Return the non-redundant list of WriteProcRes used by the given sched class. +// The scheduling model for LLVM is such that each instruction has a certain +// number of uops which consume resources which are described by WriteProcRes +// entries. Each entry describe how many cycles are spent on a specific ProcRes +// kind. +// For example, an instruction might have 3 uOps, one dispatching on P0 +// (ProcResIdx=1) and two on P06 (ProcResIdx = 7). +// Note that LLVM additionally denormalizes resource consumption to include +// usage of super resources by subresources. So in practice if there exists a +// P016 (ProcResIdx=10), then the cycles consumed by P0 are also consumed by +// P06 (ProcResIdx = 7) and P016 (ProcResIdx = 10), and the resources consumed +// by P06 are also consumed by P016. In the figure below, parenthesized cycles +// denote implied usage of superresources by subresources: +// P0 P06 P016 +// uOp1 1 (1) (1) +// uOp2 1 (1) +// uOp3 1 (1) +// ============================= +// 1 3 3 +// Eventually we end up with three entries for the WriteProcRes of the +// instruction: +// {ProcResIdx=1, Cycles=1} // P0 +// {ProcResIdx=7, Cycles=3} // P06 +// {ProcResIdx=10, Cycles=3} // P016 +// +// Note that in this case, P016 does not contribute any cycles, so it would +// be removed by this function. +// FIXME: Move this to MCSubtargetInfo and use it in llvm-mca. +static llvm::SmallVector<llvm::MCWriteProcResEntry, 8> +getNonRedundantWriteProcRes(const llvm::MCSchedClassDesc &SCDesc, + const llvm::MCSubtargetInfo &STI) { + llvm::SmallVector<llvm::MCWriteProcResEntry, 8> Result; + const auto &SM = STI.getSchedModel(); + const unsigned NumProcRes = SM.getNumProcResourceKinds(); + + // This assumes that the ProcResDescs are sorted in topological order, which + // is guaranteed by the tablegen backend. + llvm::SmallVector<float, 32> ProcResUnitUsage(NumProcRes); + for (const auto *WPR = STI.getWriteProcResBegin(&SCDesc), + *const WPREnd = STI.getWriteProcResEnd(&SCDesc); + WPR != WPREnd; ++WPR) { + const llvm::MCProcResourceDesc *const ProcResDesc = + SM.getProcResource(WPR->ProcResourceIdx); + if (ProcResDesc->SubUnitsIdxBegin == nullptr) { + // This is a ProcResUnit. + Result.push_back({WPR->ProcResourceIdx, WPR->Cycles}); + ProcResUnitUsage[WPR->ProcResourceIdx] += WPR->Cycles; + } else { + // This is a ProcResGroup. First see if it contributes any cycles or if + // it has cycles just from subunits. + float RemainingCycles = WPR->Cycles; + for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin; + SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits; + ++SubResIdx) { + RemainingCycles -= ProcResUnitUsage[*SubResIdx]; + } + if (RemainingCycles < 0.01f) { + // The ProcResGroup contributes no cycles of its own. + continue; + } + // The ProcResGroup contributes `RemainingCycles` cycles of its own. + Result.push_back({WPR->ProcResourceIdx, + static_cast<uint16_t>(std::round(RemainingCycles))}); + // Spread the remaining cycles over all subunits. + for (const auto *SubResIdx = ProcResDesc->SubUnitsIdxBegin; + SubResIdx != ProcResDesc->SubUnitsIdxBegin + ProcResDesc->NumUnits; + ++SubResIdx) { + ProcResUnitUsage[*SubResIdx] += RemainingCycles / ProcResDesc->NumUnits; + } + } + } + return Result; +} + +Analysis::SchedClass::SchedClass(const llvm::MCSchedClassDesc &SD, + const llvm::MCSubtargetInfo &STI) + : SCDesc(&SD), + NonRedundantWriteProcRes(getNonRedundantWriteProcRes(SD, STI)), + IdealizedProcResPressure(computeIdealizedProcResPressure( + STI.getSchedModel(), NonRedundantWriteProcRes)) {} + +void Analysis::SchedClassCluster::addPoint( + size_t PointId, const InstructionBenchmarkClustering &Clustering) { + PointIds.push_back(PointId); + const auto &Point = Clustering.getPoints()[PointId]; + if (ClusterId.isUndef()) { + ClusterId = Clustering.getClusterIdForPoint(PointId); + Representative.resize(Point.Measurements.size()); + } + for (size_t I = 0, E = Point.Measurements.size(); I < E; ++I) { + Representative[I].push(Point.Measurements[I]); + } + assert(ClusterId == Clustering.getClusterIdForPoint(PointId)); +} + +bool Analysis::SchedClassCluster::measurementsMatch( + const llvm::MCSubtargetInfo &STI, const SchedClass &SC, + const InstructionBenchmarkClustering &Clustering) const { + const size_t NumMeasurements = Representative.size(); + std::vector<BenchmarkMeasure> ClusterCenterPoint(NumMeasurements); + std::vector<BenchmarkMeasure> SchedClassPoint(NumMeasurements); + // Latency case. + assert(!Clustering.getPoints().empty()); + const InstructionBenchmark::ModeE Mode = Clustering.getPoints()[0].Mode; + if (Mode == InstructionBenchmark::Latency) { + if (NumMeasurements != 1) { + llvm::errs() + << "invalid number of measurements in latency mode: expected 1, got " + << NumMeasurements << "\n"; + return false; + } + // Find the latency. + SchedClassPoint[0].Value = 0.0; + for (unsigned I = 0; I < SC.SCDesc->NumWriteLatencyEntries; ++I) { + const llvm::MCWriteLatencyEntry *const WLE = + STI.getWriteLatencyEntry(SC.SCDesc, I); + SchedClassPoint[0].Value = + std::max<double>(SchedClassPoint[0].Value, WLE->Cycles); + } + ClusterCenterPoint[0].Value = Representative[0].avg(); + } else if (Mode == InstructionBenchmark::Uops) { + for (int I = 0, E = Representative.size(); I < E; ++I) { + // Find the pressure on ProcResIdx `Key`. + uint16_t ProcResIdx = 0; + if (!llvm::to_integer(Representative[I].key(), ProcResIdx, 10)) { + llvm::errs() << "expected ProcResIdx key, got " + << Representative[I].key() << "\n"; + return false; + } + const auto ProcResPressureIt = + std::find_if(SC.IdealizedProcResPressure.begin(), + SC.IdealizedProcResPressure.end(), + [ProcResIdx](const std::pair<uint16_t, float> &WPR) { + return WPR.first == ProcResIdx; + }); + SchedClassPoint[I].Value = + ProcResPressureIt == SC.IdealizedProcResPressure.end() + ? 0.0 + : ProcResPressureIt->second; + ClusterCenterPoint[I].Value = Representative[I].avg(); + } + } else { + llvm::errs() << "unimplemented measurement matching for mode " << Mode + << "\n"; + return false; + } + return Clustering.isNeighbour(ClusterCenterPoint, SchedClassPoint); +} + +void Analysis::printSchedClassDescHtml(const SchedClass &SC, + llvm::raw_ostream &OS) const { + OS << "<table class=\"sched-class-desc\">"; + OS << "<tr><th>Valid</th><th>Variant</th><th>uOps</th><th>Latency</" + "th><th>WriteProcRes</th><th title=\"This is the idealized unit " + "resource (port) pressure assuming ideal distribution\">Idealized " + "Resource Pressure</th></tr>"; + if (SC.SCDesc->isValid()) { + const auto &SM = SubtargetInfo_->getSchedModel(); + OS << "<tr><td>✔</td>"; + OS << "<td>" << (SC.SCDesc->isVariant() ? "✔" : "✕") + << "</td>"; + OS << "<td>" << SC.SCDesc->NumMicroOps << "</td>"; + // Latencies. + OS << "<td><ul>"; + for (int I = 0, E = SC.SCDesc->NumWriteLatencyEntries; I < E; ++I) { + const auto *const Entry = + SubtargetInfo_->getWriteLatencyEntry(SC.SCDesc, I); + OS << "<li>" << Entry->Cycles; + if (SC.SCDesc->NumWriteLatencyEntries > 1) { + // Dismabiguate if more than 1 latency. + OS << " (WriteResourceID " << Entry->WriteResourceID << ")"; + } + OS << "</li>"; + } + OS << "</ul></td>"; + // WriteProcRes. + OS << "<td><ul>"; + for (const auto &WPR : SC.NonRedundantWriteProcRes) { + OS << "<li><span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, + SM.getProcResource(WPR.ProcResourceIdx)->Name); + OS << "</span>: " << WPR.Cycles << "</li>"; + } + OS << "</ul></td>"; + // Idealized port pressure. + OS << "<td><ul>"; + for (const auto &Pressure : SC.IdealizedProcResPressure) { + OS << "<li><span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, SubtargetInfo_->getSchedModel() + .getProcResource(Pressure.first) + ->Name); + OS << "</span>: "; + writeMeasurementValue<kEscapeHtml>(OS, Pressure.second); + OS << "</li>"; + } + OS << "</ul></td>"; + OS << "</tr>"; + } else { + OS << "<tr><td>✕</td><td></td><td></td></tr>"; + } + OS << "</table>"; +} + +static constexpr const char kHtmlHead[] = R"( +<head> +<title>llvm-exegesis Analysis Results</title> +<style> +body { + font-family: sans-serif +} +span.sched-class-name { + font-weight: bold; + font-family: monospace; +} +span.opcode { + font-family: monospace; +} +span.config { + font-family: monospace; +} +div.inconsistency { + margin-top: 50px; +} +table { + margin-left: 50px; + border-collapse: collapse; +} +table, table tr,td,th { + border: 1px solid #444; +} +table ul { + padding-left: 0px; + margin: 0px; + list-style-type: none; +} +table.sched-class-clusters td { + padding-left: 10px; + padding-right: 10px; + padding-top: 10px; + padding-bottom: 10px; +} +table.sched-class-desc td { + padding-left: 10px; + padding-right: 10px; + padding-top: 2px; + padding-bottom: 2px; +} +span.mono { + font-family: monospace; +} +td.measurement { + text-align: center; +} +tr.good-cluster td.measurement { + color: #292 +} +tr.bad-cluster td.measurement { + color: #922 +} +tr.good-cluster td.measurement span.minmax { + color: #888; +} +tr.bad-cluster td.measurement span.minmax { + color: #888; +} +</style> +</head> +)"; + +template <> +llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>( + llvm::raw_ostream &OS) const { + const auto &FirstPoint = Clustering_.getPoints()[0]; + // Print the header. + OS << "<!DOCTYPE html><html>" << kHtmlHead << "<body>"; + OS << "<h1><span class=\"mono\">llvm-exegesis</span> Analysis Results</h1>"; + OS << "<h3>Triple: <span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, FirstPoint.LLVMTriple); + OS << "</span></h3><h3>Cpu: <span class=\"mono\">"; + writeEscaped<kEscapeHtml>(OS, FirstPoint.CpuName); + OS << "</span></h3>"; + + for (const auto &SchedClassAndPoints : makePointsPerSchedClass()) { + const auto SchedClassId = SchedClassAndPoints.first; + const std::vector<size_t> &SchedClassPoints = SchedClassAndPoints.second; + const auto &SchedModel = SubtargetInfo_->getSchedModel(); + const llvm::MCSchedClassDesc *const SCDesc = + SchedModel.getSchedClassDesc(SchedClassId); + if (!SCDesc) + continue; + const SchedClass SC(*SCDesc, *SubtargetInfo_); + + // Bucket sched class points into sched class clusters. + std::vector<SchedClassCluster> SchedClassClusters; + for (const size_t PointId : SchedClassPoints) { + const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId); + if (!ClusterId.isValid()) + continue; // Ignore noise and errors. FIXME: take noise into account ? + auto SchedClassClusterIt = + std::find_if(SchedClassClusters.begin(), SchedClassClusters.end(), + [ClusterId](const SchedClassCluster &C) { + return C.id() == ClusterId; + }); + if (SchedClassClusterIt == SchedClassClusters.end()) { + SchedClassClusters.emplace_back(); + SchedClassClusterIt = std::prev(SchedClassClusters.end()); + } + SchedClassClusterIt->addPoint(PointId, Clustering_); + } + + // Print any scheduling class that has at least one cluster that does not + // match the checked-in data. + if (std::all_of(SchedClassClusters.begin(), SchedClassClusters.end(), + [this, &SC](const SchedClassCluster &C) { + return C.measurementsMatch(*SubtargetInfo_, SC, + Clustering_); + })) + continue; // Nothing weird. + + OS << "<div class=\"inconsistency\"><p>Sched Class <span " + "class=\"sched-class-name\">"; +#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) + writeEscaped<kEscapeHtml>(OS, SCDesc->Name); +#else + OS << SchedClassId; +#endif + OS << "</span> contains instructions whose performance characteristics do" + " not match that of LLVM:</p>"; + printSchedClassClustersHtml(SchedClassClusters, SC, OS); + OS << "<p>llvm SchedModel data:</p>"; + printSchedClassDescHtml(SC, OS); + OS << "</div>"; + } + + OS << "</body></html>"; + return llvm::Error::success(); +} + +// Distributes a pressure budget as evenly as possible on the provided subunits +// given the already existing port pressure distribution. +// +// The algorithm is as follows: while there is remaining pressure to +// distribute, find the subunits with minimal pressure, and distribute +// remaining pressure equally up to the pressure of the unit with +// second-to-minimal pressure. +// For example, let's assume we want to distribute 2*P1256 +// (Subunits = [P1,P2,P5,P6]), and the starting DensePressure is: +// DensePressure = P0 P1 P2 P3 P4 P5 P6 P7 +// 0.1 0.3 0.2 0.0 0.0 0.5 0.5 0.5 +// RemainingPressure = 2.0 +// We sort the subunits by pressure: +// Subunits = [(P2,p=0.2), (P1,p=0.3), (P5,p=0.5), (P6, p=0.5)] +// We'll first start by the subunits with minimal pressure, which are at +// the beginning of the sorted array. In this example there is one (P2). +// The subunit with second-to-minimal pressure is the next one in the +// array (P1). So we distribute 0.1 pressure to P2, and remove 0.1 cycles +// from the budget. +// Subunits = [(P2,p=0.3), (P1,p=0.3), (P5,p=0.5), (P5,p=0.5)] +// RemainingPressure = 1.9 +// We repeat this process: distribute 0.2 pressure on each of the minimal +// P2 and P1, decrease budget by 2*0.2: +// Subunits = [(P2,p=0.5), (P1,p=0.5), (P5,p=0.5), (P5,p=0.5)] +// RemainingPressure = 1.5 +// There are no second-to-minimal subunits so we just share the remaining +// budget (1.5 cycles) equally: +// Subunits = [(P2,p=0.875), (P1,p=0.875), (P5,p=0.875), (P5,p=0.875)] +// RemainingPressure = 0.0 +// We stop as there is no remaining budget to distribute. +void distributePressure(float RemainingPressure, + llvm::SmallVector<uint16_t, 32> Subunits, + llvm::SmallVector<float, 32> &DensePressure) { + // Find the number of subunits with minimal pressure (they are at the + // front). + llvm::sort(Subunits.begin(), Subunits.end(), + [&DensePressure](const uint16_t A, const uint16_t B) { + return DensePressure[A] < DensePressure[B]; + }); + const auto getPressureForSubunit = [&DensePressure, + &Subunits](size_t I) -> float & { + return DensePressure[Subunits[I]]; + }; + size_t NumMinimalSU = 1; + while (NumMinimalSU < Subunits.size() && + getPressureForSubunit(NumMinimalSU) == getPressureForSubunit(0)) { + ++NumMinimalSU; + } + while (RemainingPressure > 0.0f) { + if (NumMinimalSU == Subunits.size()) { + // All units are minimal, just distribute evenly and be done. + for (size_t I = 0; I < NumMinimalSU; ++I) { + getPressureForSubunit(I) += RemainingPressure / NumMinimalSU; + } + return; + } + // Distribute the remaining pressure equally. + const float MinimalPressure = getPressureForSubunit(NumMinimalSU - 1); + const float SecondToMinimalPressure = getPressureForSubunit(NumMinimalSU); + assert(MinimalPressure < SecondToMinimalPressure); + const float Increment = SecondToMinimalPressure - MinimalPressure; + if (RemainingPressure <= NumMinimalSU * Increment) { + // There is not enough remaining pressure. + for (size_t I = 0; I < NumMinimalSU; ++I) { + getPressureForSubunit(I) += RemainingPressure / NumMinimalSU; + } + return; + } + // Bump all minimal pressure subunits to `SecondToMinimalPressure`. + for (size_t I = 0; I < NumMinimalSU; ++I) { + getPressureForSubunit(I) = SecondToMinimalPressure; + RemainingPressure -= SecondToMinimalPressure; + } + while (NumMinimalSU < Subunits.size() && + getPressureForSubunit(NumMinimalSU) == SecondToMinimalPressure) { + ++NumMinimalSU; + } + } +} + +std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure( + const llvm::MCSchedModel &SM, + llvm::SmallVector<llvm::MCWriteProcResEntry, 8> WPRS) { + // DensePressure[I] is the port pressure for Proc Resource I. + llvm::SmallVector<float, 32> DensePressure(SM.getNumProcResourceKinds()); + llvm::sort(WPRS.begin(), WPRS.end(), + [](const llvm::MCWriteProcResEntry &A, + const llvm::MCWriteProcResEntry &B) { + return A.ProcResourceIdx < B.ProcResourceIdx; + }); + for (const llvm::MCWriteProcResEntry &WPR : WPRS) { + // Get units for the entry. + const llvm::MCProcResourceDesc *const ProcResDesc = + SM.getProcResource(WPR.ProcResourceIdx); + if (ProcResDesc->SubUnitsIdxBegin == nullptr) { + // This is a ProcResUnit. + DensePressure[WPR.ProcResourceIdx] += WPR.Cycles; + } else { + // This is a ProcResGroup. + llvm::SmallVector<uint16_t, 32> Subunits(ProcResDesc->SubUnitsIdxBegin, + ProcResDesc->SubUnitsIdxBegin + + ProcResDesc->NumUnits); + distributePressure(WPR.Cycles, Subunits, DensePressure); + } + } + // Turn dense pressure into sparse pressure by removing zero entries. + std::vector<std::pair<uint16_t, float>> Pressure; + for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { + if (DensePressure[I] > 0.0f) + Pressure.emplace_back(I, DensePressure[I]); + } + return Pressure; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Analysis.h b/tools/llvm-exegesis/lib/Analysis.h new file mode 100644 index 000000000000..3c33cafc5fb9 --- /dev/null +++ b/tools/llvm-exegesis/lib/Analysis.h @@ -0,0 +1,129 @@ +//===-- Analysis.h ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Analysis output for benchmark results. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ANALYSIS_H +#define LLVM_TOOLS_LLVM_EXEGESIS_ANALYSIS_H + +#include "Clustering.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCDisassembler/MCDisassembler.h" +#include "llvm/MC/MCInstPrinter.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <set> +#include <string> +#include <unordered_map> + +namespace exegesis { + +// A helper class to analyze benchmark results for a target. +class Analysis { +public: + Analysis(const llvm::Target &Target, + const InstructionBenchmarkClustering &Clustering); + + // Prints a csv of instructions for each cluster. + struct PrintClusters {}; + // Find potential errors in the scheduling information given measurements. + struct PrintSchedClassInconsistencies {}; + + template <typename Pass> llvm::Error run(llvm::raw_ostream &OS) const; + +private: + using ClusterId = InstructionBenchmarkClustering::ClusterId; + + // An llvm::MCSchedClassDesc augmented with some additional data. + struct SchedClass { + SchedClass(const llvm::MCSchedClassDesc &SD, + const llvm::MCSubtargetInfo &STI); + + const llvm::MCSchedClassDesc *const SCDesc; + const llvm::SmallVector<llvm::MCWriteProcResEntry, 8> + NonRedundantWriteProcRes; + const std::vector<std::pair<uint16_t, float>> IdealizedProcResPressure; + }; + + // Represents the intersection of a sched class and a cluster. + class SchedClassCluster { + public: + const InstructionBenchmarkClustering::ClusterId &id() const { + return ClusterId; + } + + const std::vector<size_t> &getPointIds() const { return PointIds; } + + // Return the cluster centroid. + const std::vector<BenchmarkMeasureStats> &getRepresentative() const { + return Representative; + } + + // Returns true if the cluster representative measurements match that of SC. + bool + measurementsMatch(const llvm::MCSubtargetInfo &STI, const SchedClass &SC, + const InstructionBenchmarkClustering &Clustering) const; + + void addPoint(size_t PointId, + const InstructionBenchmarkClustering &Clustering); + + private: + InstructionBenchmarkClustering::ClusterId ClusterId; + std::vector<size_t> PointIds; + // Measurement stats for the points in the SchedClassCluster. + std::vector<BenchmarkMeasureStats> Representative; + }; + + void printInstructionRowCsv(size_t PointId, llvm::raw_ostream &OS) const; + + void + printSchedClassClustersHtml(const std::vector<SchedClassCluster> &Clusters, + const SchedClass &SC, + llvm::raw_ostream &OS) const; + void printSchedClassDescHtml(const SchedClass &SC, + llvm::raw_ostream &OS) const; + + // Builds a map of Sched Class -> indices of points that belong to the sched + // class. + std::unordered_map<unsigned, std::vector<size_t>> + makePointsPerSchedClass() const; + + template <typename EscapeTag, EscapeTag Tag> + void writeSnippet(llvm::raw_ostream &OS, llvm::ArrayRef<uint8_t> Bytes, + const char *Separator) const; + + const InstructionBenchmarkClustering &Clustering_; + llvm::MCObjectFileInfo ObjectFileInfo_; + std::unique_ptr<llvm::MCContext> Context_; + std::unique_ptr<llvm::MCSubtargetInfo> SubtargetInfo_; + std::unique_ptr<llvm::MCInstrInfo> InstrInfo_; + std::unique_ptr<llvm::MCRegisterInfo> RegInfo_; + std::unique_ptr<llvm::MCAsmInfo> AsmInfo_; + std::unique_ptr<llvm::MCInstPrinter> InstPrinter_; + std::unique_ptr<llvm::MCDisassembler> Disasm_; +}; + +// Computes the idealized ProcRes Unit pressure. This is the expected +// distribution if the CPU scheduler can distribute the load as evenly as +// possible. +std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure( + const llvm::MCSchedModel &SM, + llvm::SmallVector<llvm::MCWriteProcResEntry, 8> WPRS); + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H diff --git a/tools/llvm-exegesis/lib/Assembler.cpp b/tools/llvm-exegesis/lib/Assembler.cpp new file mode 100644 index 000000000000..d2be7f4829a7 --- /dev/null +++ b/tools/llvm-exegesis/lib/Assembler.cpp @@ -0,0 +1,283 @@ +//===-- Assembler.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Assembler.h" + +#include "Target.h" +#include "llvm/CodeGen/GlobalISel/CallLowering.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/ExecutionEngine/SectionMemoryManager.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/Support/MemoryBuffer.h" + +namespace exegesis { + +static constexpr const char ModuleID[] = "ExegesisInfoTest"; +static constexpr const char FunctionID[] = "foo"; + +static std::vector<llvm::MCInst> +generateSnippetSetupCode(const llvm::ArrayRef<unsigned> RegsToDef, + const ExegesisTarget &ET, + const llvm::LLVMTargetMachine &TM, bool &IsComplete) { + IsComplete = true; + std::vector<llvm::MCInst> Result; + for (const unsigned Reg : RegsToDef) { + // Load a constant in the register. + const auto Code = ET.setRegToConstant(*TM.getMCSubtargetInfo(), Reg); + if (Code.empty()) + IsComplete = false; + Result.insert(Result.end(), Code.begin(), Code.end()); + } + return Result; +} + +// Small utility function to add named passes. +static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName, + llvm::TargetPassConfig &TPC) { + const llvm::PassRegistry *PR = llvm::PassRegistry::getPassRegistry(); + const llvm::PassInfo *PI = PR->getPassInfo(PassName); + if (!PI) { + llvm::errs() << " run-pass " << PassName << " is not registered.\n"; + return true; + } + + if (!PI->getNormalCtor()) { + llvm::errs() << " cannot create pass: " << PI->getPassName() << "\n"; + return true; + } + llvm::Pass *P = PI->getNormalCtor()(); + std::string Banner = std::string("After ") + std::string(P->getPassName()); + PM.add(P); + TPC.printAndVerify(Banner); + + return false; +} + +// Creates a void MachineFunction with no argument. +static llvm::MachineFunction & +createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module, + llvm::MachineModuleInfo *MMI) { + llvm::Type *const ReturnType = llvm::Type::getInt32Ty(Module->getContext()); + llvm::FunctionType *FunctionType = llvm::FunctionType::get(ReturnType, false); + llvm::Function *const F = llvm::Function::Create( + FunctionType, llvm::GlobalValue::InternalLinkage, FunctionID, Module); + // Making sure we can create a MachineFunction out of this Function even if it + // contains no IR. + F->setIsMaterializable(true); + return MMI->getOrCreateMachineFunction(*F); +} + +static void fillMachineFunction(llvm::MachineFunction &MF, + llvm::ArrayRef<llvm::MCInst> Instructions) { + llvm::MachineBasicBlock *MBB = MF.CreateMachineBasicBlock(); + MF.push_back(MBB); + const llvm::MCInstrInfo *MCII = MF.getTarget().getMCInstrInfo(); + llvm::DebugLoc DL; + for (const llvm::MCInst &Inst : Instructions) { + const unsigned Opcode = Inst.getOpcode(); + const llvm::MCInstrDesc &MCID = MCII->get(Opcode); + llvm::MachineInstrBuilder Builder = llvm::BuildMI(MBB, DL, MCID); + for (unsigned OpIndex = 0, E = Inst.getNumOperands(); OpIndex < E; + ++OpIndex) { + const llvm::MCOperand &Op = Inst.getOperand(OpIndex); + if (Op.isReg()) { + const bool IsDef = OpIndex < MCID.getNumDefs(); + unsigned Flags = 0; + const llvm::MCOperandInfo &OpInfo = MCID.operands().begin()[OpIndex]; + if (IsDef && !OpInfo.isOptionalDef()) + Flags |= llvm::RegState::Define; + Builder.addReg(Op.getReg(), Flags); + } else if (Op.isImm()) { + Builder.addImm(Op.getImm()); + } else { + llvm_unreachable("Not yet implemented"); + } + } + } + // Insert the return code. + const llvm::TargetInstrInfo *TII = MF.getSubtarget().getInstrInfo(); + if (TII->getReturnOpcode() < TII->getNumOpcodes()) { + llvm::BuildMI(MBB, DL, TII->get(TII->getReturnOpcode())); + } else { + llvm::MachineIRBuilder MIB(MF); + MIB.setMBB(*MBB); + MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, 0); + } +} + +static std::unique_ptr<llvm::Module> +createModule(const std::unique_ptr<llvm::LLVMContext> &Context, + const llvm::DataLayout DL) { + auto Module = llvm::make_unique<llvm::Module>(ModuleID, *Context); + Module->setDataLayout(DL); + return Module; +} + +llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) { + std::unique_ptr<llvm::LLVMContext> Context = + llvm::make_unique<llvm::LLVMContext>(); + std::unique_ptr<llvm::Module> Module = + createModule(Context, TM.createDataLayout()); + std::unique_ptr<llvm::MachineModuleInfo> MMI = + llvm::make_unique<llvm::MachineModuleInfo>(&TM); + llvm::MachineFunction &MF = + createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get()); + // Saving reserved registers for client. + return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF); +} + +void assembleToStream(const ExegesisTarget &ET, + std::unique_ptr<llvm::LLVMTargetMachine> TM, + llvm::ArrayRef<unsigned> RegsToDef, + llvm::ArrayRef<llvm::MCInst> Instructions, + llvm::raw_pwrite_stream &AsmStream) { + std::unique_ptr<llvm::LLVMContext> Context = + llvm::make_unique<llvm::LLVMContext>(); + std::unique_ptr<llvm::Module> Module = + createModule(Context, TM->createDataLayout()); + std::unique_ptr<llvm::MachineModuleInfo> MMI = + llvm::make_unique<llvm::MachineModuleInfo>(TM.get()); + llvm::MachineFunction &MF = + createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get()); + + // We need to instruct the passes that we're done with SSA and virtual + // registers. + auto &Properties = MF.getProperties(); + Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs); + Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA); + bool IsSnippetSetupComplete = false; + std::vector<llvm::MCInst> SnippetWithSetup = + generateSnippetSetupCode(RegsToDef, ET, *TM, IsSnippetSetupComplete); + if (!SnippetWithSetup.empty()) { + SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(), + Instructions.end()); + Instructions = SnippetWithSetup; + } + // If the snippet setup is not complete, we disable liveliness tracking. This + // means that we won't know what values are in the registers. + if (!IsSnippetSetupComplete) + Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); + + // prologue/epilogue pass needs the reserved registers to be frozen, this + // is usually done by the SelectionDAGISel pass. + MF.getRegInfo().freezeReservedRegs(MF); + + // Fill the MachineFunction from the instructions. + fillMachineFunction(MF, Instructions); + + // We create the pass manager, run the passes to populate AsmBuffer. + llvm::MCContext &MCContext = MMI->getContext(); + llvm::legacy::PassManager PM; + + llvm::TargetLibraryInfoImpl TLII(llvm::Triple(Module->getTargetTriple())); + PM.add(new llvm::TargetLibraryInfoWrapperPass(TLII)); + + llvm::TargetPassConfig *TPC = TM->createPassConfig(PM); + PM.add(TPC); + PM.add(MMI.release()); + TPC->printAndVerify("MachineFunctionGenerator::assemble"); + // Add target-specific passes. + ET.addTargetSpecificPasses(PM); + TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses"); + // Adding the following passes: + // - machineverifier: checks that the MachineFunction is well formed. + // - prologepilog: saves and restore callee saved registers. + for (const char *PassName : {"machineverifier", "prologepilog"}) + if (addPass(PM, PassName, *TPC)) + llvm::report_fatal_error("Unable to add a mandatory pass"); + TPC->setInitialized(); + + // AsmPrinter is responsible for generating the assembly into AsmBuffer. + if (TM->addAsmPrinter(PM, AsmStream, nullptr, + llvm::TargetMachine::CGFT_ObjectFile, MCContext)) + llvm::report_fatal_error("Cannot add AsmPrinter passes"); + + PM.run(*Module); // Run all the passes +} + +llvm::object::OwningBinary<llvm::object::ObjectFile> +getObjectFromBuffer(llvm::StringRef InputData) { + // Storing the generated assembly into a MemoryBuffer that owns the memory. + std::unique_ptr<llvm::MemoryBuffer> Buffer = + llvm::MemoryBuffer::getMemBufferCopy(InputData); + // Create the ObjectFile from the MemoryBuffer. + std::unique_ptr<llvm::object::ObjectFile> Obj = llvm::cantFail( + llvm::object::ObjectFile::createObjectFile(Buffer->getMemBufferRef())); + // Returning both the MemoryBuffer and the ObjectFile. + return llvm::object::OwningBinary<llvm::object::ObjectFile>( + std::move(Obj), std::move(Buffer)); +} + +llvm::object::OwningBinary<llvm::object::ObjectFile> +getObjectFromFile(llvm::StringRef Filename) { + return llvm::cantFail(llvm::object::ObjectFile::createObjectFile(Filename)); +} + +namespace { + +// Implementation of this class relies on the fact that a single object with a +// single function will be loaded into memory. +class TrackingSectionMemoryManager : public llvm::SectionMemoryManager { +public: + explicit TrackingSectionMemoryManager(uintptr_t *CodeSize) + : CodeSize(CodeSize) {} + + uint8_t *allocateCodeSection(uintptr_t Size, unsigned Alignment, + unsigned SectionID, + llvm::StringRef SectionName) override { + *CodeSize = Size; + return llvm::SectionMemoryManager::allocateCodeSection( + Size, Alignment, SectionID, SectionName); + } + +private: + uintptr_t *const CodeSize = nullptr; +}; + +} // namespace + +ExecutableFunction::ExecutableFunction( + std::unique_ptr<llvm::LLVMTargetMachine> TM, + llvm::object::OwningBinary<llvm::object::ObjectFile> &&ObjectFileHolder) + : Context(llvm::make_unique<llvm::LLVMContext>()) { + assert(ObjectFileHolder.getBinary() && "cannot create object file"); + // Initializing the execution engine. + // We need to use the JIT EngineKind to be able to add an object file. + LLVMLinkInMCJIT(); + uintptr_t CodeSize = 0; + std::string Error; + ExecEngine.reset( + llvm::EngineBuilder(createModule(Context, TM->createDataLayout())) + .setErrorStr(&Error) + .setMCPU(TM->getTargetCPU()) + .setEngineKind(llvm::EngineKind::JIT) + .setMCJITMemoryManager( + llvm::make_unique<TrackingSectionMemoryManager>(&CodeSize)) + .create(TM.release())); + if (!ExecEngine) + llvm::report_fatal_error(Error); + // Adding the generated object file containing the assembled function. + // The ExecutionEngine makes sure the object file is copied into an + // executable page. + ExecEngine->addObjectFile(std::move(ObjectFileHolder)); + // Fetching function bytes. + FunctionBytes = + llvm::StringRef(reinterpret_cast<const char *>( + ExecEngine->getFunctionAddress(FunctionID)), + CodeSize); +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Assembler.h b/tools/llvm-exegesis/lib/Assembler.h new file mode 100644 index 000000000000..e9a65cbbe369 --- /dev/null +++ b/tools/llvm-exegesis/lib/Assembler.h @@ -0,0 +1,82 @@ +//===-- Assembler.h ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines classes to assemble functions composed of a single basic block of +/// MCInsts. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H +#define LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H + +#include <memory> + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineModuleInfo.h" +#include "llvm/ExecutionEngine/ExecutionEngine.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ObjectFile.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" + +namespace exegesis { + +class ExegesisTarget; + +// Gather the set of reserved registers (depends on function's calling +// convention and target machine). +llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); + +// Creates a temporary `void foo()` function containing the provided +// Instructions. Runs a set of llvm Passes to provide correct prologue and +// epilogue. Once the MachineFunction is ready, it is assembled for TM to +// AsmStream, the temporary function is eventually discarded. +void assembleToStream(const ExegesisTarget &ET, + std::unique_ptr<llvm::LLVMTargetMachine> TM, + llvm::ArrayRef<unsigned> RegsToDef, + llvm::ArrayRef<llvm::MCInst> Instructions, + llvm::raw_pwrite_stream &AsmStream); + +// Creates an ObjectFile in the format understood by the host. +// Note: the resulting object keeps a copy of Buffer so it can be discarded once +// this function returns. +llvm::object::OwningBinary<llvm::object::ObjectFile> +getObjectFromBuffer(llvm::StringRef Buffer); + +// Loads the content of Filename as on ObjectFile and returns it. +llvm::object::OwningBinary<llvm::object::ObjectFile> +getObjectFromFile(llvm::StringRef Filename); + +// Consumes an ObjectFile containing a `void foo()` function and make it +// executable. +struct ExecutableFunction { + explicit ExecutableFunction( + std::unique_ptr<llvm::LLVMTargetMachine> TM, + llvm::object::OwningBinary<llvm::object::ObjectFile> &&ObjectFileHolder); + + // Retrieves the function as an array of bytes. + llvm::StringRef getFunctionBytes() const { return FunctionBytes; } + + // Executes the function. + void operator()() const { ((void (*)())(intptr_t)FunctionBytes.data())(); } + + std::unique_ptr<llvm::LLVMContext> Context; + std::unique_ptr<llvm::ExecutionEngine> ExecEngine; + llvm::StringRef FunctionBytes; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H diff --git a/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/tools/llvm-exegesis/lib/BenchmarkResult.cpp new file mode 100644 index 000000000000..33ad65075d92 --- /dev/null +++ b/tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -0,0 +1,302 @@ +//===-- BenchmarkResult.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "BenchmarkResult.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ObjectYAML/YAML.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/raw_ostream.h" + +static constexpr const char kIntegerFormat[] = "i_0x%" PRId64 "x"; +static constexpr const char kDoubleFormat[] = "f_%la"; + +static void serialize(const exegesis::BenchmarkResultContext &Context, + const llvm::MCOperand &MCOperand, llvm::raw_ostream &OS) { + if (MCOperand.isReg()) { + OS << Context.getRegName(MCOperand.getReg()); + } else if (MCOperand.isImm()) { + OS << llvm::format(kIntegerFormat, MCOperand.getImm()); + } else if (MCOperand.isFPImm()) { + OS << llvm::format(kDoubleFormat, MCOperand.getFPImm()); + } else { + OS << "INVALID"; + } +} + +static void serialize(const exegesis::BenchmarkResultContext &Context, + const llvm::MCInst &MCInst, llvm::raw_ostream &OS) { + OS << Context.getInstrName(MCInst.getOpcode()); + for (const auto &Op : MCInst) { + OS << ' '; + serialize(Context, Op, OS); + } +} + +static llvm::MCOperand +deserialize(const exegesis::BenchmarkResultContext &Context, + llvm::StringRef String) { + assert(!String.empty()); + int64_t IntValue = 0; + double DoubleValue = 0; + if (sscanf(String.data(), kIntegerFormat, &IntValue) == 1) + return llvm::MCOperand::createImm(IntValue); + if (sscanf(String.data(), kDoubleFormat, &DoubleValue) == 1) + return llvm::MCOperand::createFPImm(DoubleValue); + if (unsigned RegNo = Context.getRegNo(String)) // Returns 0 if invalid. + return llvm::MCOperand::createReg(RegNo); + return {}; +} + +static llvm::StringRef +deserialize(const exegesis::BenchmarkResultContext &Context, + llvm::StringRef String, llvm::MCInst &Value) { + llvm::SmallVector<llvm::StringRef, 8> Pieces; + String.split(Pieces, " "); + if (Pieces.empty()) + return "Invalid Instruction"; + bool ProcessOpcode = true; + for (llvm::StringRef Piece : Pieces) { + if (ProcessOpcode) { + ProcessOpcode = false; + Value.setOpcode(Context.getInstrOpcode(Piece)); + if (Value.getOpcode() == 0) + return "Unknown Opcode Name"; + } else { + Value.addOperand(deserialize(Context, Piece)); + } + } + return {}; +} + +// YAML IO requires a mutable pointer to Context but we guarantee to not +// modify it. +static void *getUntypedContext(const exegesis::BenchmarkResultContext &Ctx) { + return const_cast<exegesis::BenchmarkResultContext *>(&Ctx); +} + +static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) { + assert(Ctx); + return *static_cast<const exegesis::BenchmarkResultContext *>(Ctx); +} + +// Defining YAML traits for IO. +namespace llvm { +namespace yaml { + +// std::vector<llvm::MCInst> will be rendered as a list. +template <> struct SequenceElementTraits<llvm::MCInst> { + static const bool flow = false; +}; + +template <> struct ScalarTraits<llvm::MCInst> { + + static void output(const llvm::MCInst &Value, void *Ctx, + llvm::raw_ostream &Out) { + serialize(getTypedContext(Ctx), Value, Out); + } + + static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) { + return deserialize(getTypedContext(Ctx), Scalar, Value); + } + + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } + + static const bool flow = true; +}; + +// std::vector<exegesis::Measure> will be rendered as a list. +template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> { + static const bool flow = false; +}; + +// exegesis::Measure is rendererd as a flow instead of a list. +// e.g. { "key": "the key", "value": 0123 } +template <> struct MappingTraits<exegesis::BenchmarkMeasure> { + static void mapping(IO &Io, exegesis::BenchmarkMeasure &Obj) { + Io.mapRequired("key", Obj.Key); + Io.mapRequired("value", Obj.Value); + Io.mapOptional("debug_string", Obj.DebugString); + } + static const bool flow = true; +}; + +template <> +struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> { + static void enumeration(IO &Io, + exegesis::InstructionBenchmark::ModeE &Value) { + Io.enumCase(Value, "", exegesis::InstructionBenchmark::Unknown); + Io.enumCase(Value, "latency", exegesis::InstructionBenchmark::Latency); + Io.enumCase(Value, "uops", exegesis::InstructionBenchmark::Uops); + } +}; + +template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> { + static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) { + Io.mapRequired("instructions", Obj.Instructions); + Io.mapOptional("config", Obj.Config); + } +}; + +template <> struct MappingTraits<exegesis::InstructionBenchmark> { + class NormalizedBinary { + public: + NormalizedBinary(IO &io) {} + NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {} + std::vector<uint8_t> denormalize(IO &) { + std::vector<uint8_t> Data; + std::string Str; + raw_string_ostream OSS(Str); + Binary.writeAsBinary(OSS); + OSS.flush(); + Data.assign(Str.begin(), Str.end()); + return Data; + } + + BinaryRef Binary; + }; + + static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) { + Io.mapRequired("mode", Obj.Mode); + Io.mapRequired("key", Obj.Key); + Io.mapRequired("cpu_name", Obj.CpuName); + Io.mapRequired("llvm_triple", Obj.LLVMTriple); + Io.mapRequired("num_repetitions", Obj.NumRepetitions); + Io.mapRequired("measurements", Obj.Measurements); + Io.mapRequired("error", Obj.Error); + Io.mapOptional("info", Obj.Info); + // AssembledSnippet + MappingNormalization<NormalizedBinary, std::vector<uint8_t>> BinaryString( + Io, Obj.AssembledSnippet); + Io.mapOptional("assembled_snippet", BinaryString->Binary); + } +}; + +} // namespace yaml +} // namespace llvm + +LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(exegesis::InstructionBenchmark) + +namespace exegesis { + +void BenchmarkResultContext::addRegEntry(unsigned RegNo, llvm::StringRef Name) { + assert(RegNoToName.find(RegNo) == RegNoToName.end()); + assert(RegNameToNo.find(Name) == RegNameToNo.end()); + RegNoToName[RegNo] = Name; + RegNameToNo[Name] = RegNo; +} + +llvm::StringRef BenchmarkResultContext::getRegName(unsigned RegNo) const { + const auto Itr = RegNoToName.find(RegNo); + if (Itr != RegNoToName.end()) + return Itr->second; + return {}; +} + +unsigned BenchmarkResultContext::getRegNo(llvm::StringRef Name) const { + const auto Itr = RegNameToNo.find(Name); + if (Itr != RegNameToNo.end()) + return Itr->second; + return 0; +} + +void BenchmarkResultContext::addInstrEntry(unsigned Opcode, + llvm::StringRef Name) { + assert(InstrOpcodeToName.find(Opcode) == InstrOpcodeToName.end()); + assert(InstrNameToOpcode.find(Name) == InstrNameToOpcode.end()); + InstrOpcodeToName[Opcode] = Name; + InstrNameToOpcode[Name] = Opcode; +} + +llvm::StringRef BenchmarkResultContext::getInstrName(unsigned Opcode) const { + const auto Itr = InstrOpcodeToName.find(Opcode); + if (Itr != InstrOpcodeToName.end()) + return Itr->second; + return {}; +} + +unsigned BenchmarkResultContext::getInstrOpcode(llvm::StringRef Name) const { + const auto Itr = InstrNameToOpcode.find(Name); + if (Itr != InstrNameToOpcode.end()) + return Itr->second; + return 0; +} + +template <typename ObjectOrList> +static llvm::Expected<ObjectOrList> +readYamlCommon(const BenchmarkResultContext &Context, + llvm::StringRef Filename) { + if (auto ExpectedMemoryBuffer = + llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) { + std::unique_ptr<llvm::MemoryBuffer> MemoryBuffer = + std::move(ExpectedMemoryBuffer.get()); + llvm::yaml::Input Yin(*MemoryBuffer, getUntypedContext(Context)); + ObjectOrList Benchmark; + Yin >> Benchmark; + return Benchmark; + } else { + return ExpectedMemoryBuffer.takeError(); + } +} + +llvm::Expected<InstructionBenchmark> +InstructionBenchmark::readYaml(const BenchmarkResultContext &Context, + llvm::StringRef Filename) { + return readYamlCommon<InstructionBenchmark>(Context, Filename); +} + +llvm::Expected<std::vector<InstructionBenchmark>> +InstructionBenchmark::readYamls(const BenchmarkResultContext &Context, + llvm::StringRef Filename) { + return readYamlCommon<std::vector<InstructionBenchmark>>(Context, Filename); +} + +void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context, + llvm::raw_ostream &OS) { + llvm::yaml::Output Yout(OS, getUntypedContext(Context)); + Yout << *this; +} + +void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context, + llvm::StringRef InputContent) { + llvm::yaml::Input Yin(InputContent, getUntypedContext(Context)); + Yin >> *this; +} + +llvm::Error +InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context, + const llvm::StringRef Filename) { + if (Filename == "-") { + writeYamlTo(Context, llvm::outs()); + } else { + int ResultFD = 0; + if (auto E = llvm::errorCodeToError( + openFileForWrite(Filename, ResultFD, llvm::sys::fs::CD_CreateAlways, + llvm::sys::fs::F_Text))) { + return E; + } + llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/); + writeYamlTo(Context, Ostr); + } + return llvm::Error::success(); +} + +void BenchmarkMeasureStats::push(const BenchmarkMeasure &BM) { + if (Key.empty()) + Key = BM.Key; + assert(Key == BM.Key); + ++NumValues; + SumValues += BM.Value; + MaxValue = std::max(MaxValue, BM.Value); + MinValue = std::min(MinValue, BM.Value); +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/BenchmarkResult.h b/tools/llvm-exegesis/lib/BenchmarkResult.h new file mode 100644 index 000000000000..c9e77ca49223 --- /dev/null +++ b/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -0,0 +1,139 @@ +//===-- BenchmarkResult.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines classes to represent measurements and serialize/deserialize them to +// Yaml. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H +#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H + +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/Support/YAMLTraits.h" +#include <limits> +#include <string> +#include <unordered_map> +#include <vector> + +namespace exegesis { + +struct BenchmarkResultContext; // Forward declaration. + +struct InstructionBenchmarkKey { + // The LLVM opcode name. + std::vector<llvm::MCInst> Instructions; + // An opaque configuration, that can be used to separate several benchmarks of + // the same instruction under different configurations. + std::string Config; +}; + +struct BenchmarkMeasure { + std::string Key; + double Value; + std::string DebugString; +}; + +// The result of an instruction benchmark. +struct InstructionBenchmark { + InstructionBenchmarkKey Key; + enum ModeE { Unknown, Latency, Uops }; + ModeE Mode; + std::string CpuName; + std::string LLVMTriple; + // The number of instructions inside the repeated snippet. For example, if a + // snippet of 3 instructions is repeated 4 times, this is 12. + int NumRepetitions = 0; + // Note that measurements are per instruction. + std::vector<BenchmarkMeasure> Measurements; + std::string Error; + std::string Info; + std::vector<uint8_t> AssembledSnippet; + + // Read functions. + static llvm::Expected<InstructionBenchmark> + readYaml(const BenchmarkResultContext &Context, llvm::StringRef Filename); + + static llvm::Expected<std::vector<InstructionBenchmark>> + readYamls(const BenchmarkResultContext &Context, llvm::StringRef Filename); + + void readYamlFrom(const BenchmarkResultContext &Context, + llvm::StringRef InputContent); + + // Write functions, non-const because of YAML traits. + void writeYamlTo(const BenchmarkResultContext &Context, llvm::raw_ostream &S); + + llvm::Error writeYaml(const BenchmarkResultContext &Context, + const llvm::StringRef Filename); +}; + +//------------------------------------------------------------------------------ +// Utilities to work with Benchmark measures. + +// A class that measures stats over benchmark measures. +class BenchmarkMeasureStats { +public: + void push(const BenchmarkMeasure &BM); + + double avg() const { + assert(NumValues); + return SumValues / NumValues; + } + double min() const { return MinValue; } + double max() const { return MaxValue; } + + const std::string &key() const { return Key; } + +private: + std::string Key; + double SumValues = 0.0; + int NumValues = 0; + double MaxValue = std::numeric_limits<double>::min(); + double MinValue = std::numeric_limits<double>::max(); +}; + +// This context is used when de/serializing InstructionBenchmark to guarantee +// that Registers and Instructions are human readable and preserved accross +// different versions of LLVM. +struct BenchmarkResultContext { + BenchmarkResultContext() = default; + BenchmarkResultContext(BenchmarkResultContext &&) = default; + BenchmarkResultContext &operator=(BenchmarkResultContext &&) = default; + BenchmarkResultContext(const BenchmarkResultContext &) = delete; + BenchmarkResultContext &operator=(const BenchmarkResultContext &) = delete; + + // Populate Registers and Instruction mapping. + void addRegEntry(unsigned RegNo, llvm::StringRef Name); + void addInstrEntry(unsigned Opcode, llvm::StringRef Name); + + // Register accessors. + llvm::StringRef getRegName(unsigned RegNo) const; + unsigned getRegNo(llvm::StringRef Name) const; // 0 is not found. + + // Instruction accessors. + llvm::StringRef getInstrName(unsigned Opcode) const; + unsigned getInstrOpcode(llvm::StringRef Name) const; // 0 is not found. + +private: + // Ideally we would like to use MCRegisterInfo and MCInstrInfo but doing so + // would make testing harder, instead we create a mapping that we can easily + // populate. + std::unordered_map<unsigned, llvm::StringRef> InstrOpcodeToName; + std::unordered_map<unsigned, llvm::StringRef> RegNoToName; + llvm::StringMap<unsigned> InstrNameToOpcode; + llvm::StringMap<unsigned> RegNameToNo; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp new file mode 100644 index 000000000000..55012bc1e83f --- /dev/null +++ b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -0,0 +1,229 @@ +//===-- BenchmarkRunner.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include <array> +#include <string> + +#include "Assembler.h" +#include "BenchmarkRunner.h" +#include "MCInstrDescView.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Program.h" + +namespace exegesis { + +BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S) + : llvm::StringError(S, llvm::inconvertibleErrorCode()) {} + +BenchmarkRunner::BenchmarkRunner(const LLVMState &State, + InstructionBenchmark::ModeE Mode) + : State(State), RATC(State.getRegInfo(), + getFunctionReservedRegs(State.getTargetMachine())), + Mode(Mode) {} + +BenchmarkRunner::~BenchmarkRunner() = default; + +llvm::Expected<std::vector<InstructionBenchmark>> +BenchmarkRunner::run(unsigned Opcode, unsigned NumRepetitions) { + const llvm::MCInstrDesc &InstrDesc = State.getInstrInfo().get(Opcode); + // Ignore instructions that we cannot run. + if (InstrDesc.isPseudo()) + return llvm::make_error<BenchmarkFailure>("Unsupported opcode: isPseudo"); + if (InstrDesc.isBranch() || InstrDesc.isIndirectBranch()) + return llvm::make_error<BenchmarkFailure>( + "Unsupported opcode: isBranch/isIndirectBranch"); + if (InstrDesc.isCall() || InstrDesc.isReturn()) + return llvm::make_error<BenchmarkFailure>( + "Unsupported opcode: isCall/isReturn"); + + llvm::Expected<std::vector<BenchmarkConfiguration>> ConfigurationOrError = + generateConfigurations(Opcode); + + if (llvm::Error E = ConfigurationOrError.takeError()) + return std::move(E); + + std::vector<InstructionBenchmark> InstrBenchmarks; + for (const BenchmarkConfiguration &Conf : ConfigurationOrError.get()) + InstrBenchmarks.push_back(runOne(Conf, Opcode, NumRepetitions)); + return InstrBenchmarks; +} + +InstructionBenchmark +BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, + unsigned Opcode, unsigned NumRepetitions) const { + InstructionBenchmark InstrBenchmark; + InstrBenchmark.Mode = Mode; + InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU(); + InstrBenchmark.LLVMTriple = + State.getTargetMachine().getTargetTriple().normalize(); + InstrBenchmark.NumRepetitions = NumRepetitions; + InstrBenchmark.Info = Configuration.Info; + + const std::vector<llvm::MCInst> &Snippet = Configuration.Snippet; + if (Snippet.empty()) { + InstrBenchmark.Error = "Empty snippet"; + return InstrBenchmark; + } + + InstrBenchmark.Key.Instructions = Snippet; + + // Repeat the snippet until there are at least NumInstructions in the + // resulting code. The snippet is always repeated at least once. + const auto GenerateInstructions = [&Configuration]( + const int MinInstructions) { + std::vector<llvm::MCInst> Code = Configuration.Snippet; + for (int I = 0; I < MinInstructions; ++I) + Code.push_back(Configuration.Snippet[I % Configuration.Snippet.size()]); + return Code; + }; + + // Assemble at least kMinInstructionsForSnippet instructions by repeating the + // snippet for debug/analysis. This is so that the user clearly understands + // that the inside instructions are repeated. + constexpr const int kMinInstructionsForSnippet = 16; + { + auto ObjectFilePath = + writeObjectFile(Configuration.SnippetSetup, + GenerateInstructions(kMinInstructionsForSnippet)); + if (llvm::Error E = ObjectFilePath.takeError()) { + InstrBenchmark.Error = llvm::toString(std::move(E)); + return InstrBenchmark; + } + const ExecutableFunction EF(State.createTargetMachine(), + getObjectFromFile(*ObjectFilePath)); + const auto FnBytes = EF.getFunctionBytes(); + InstrBenchmark.AssembledSnippet.assign(FnBytes.begin(), FnBytes.end()); + } + + // Assemble NumRepetitions instructions repetitions of the snippet for + // measurements. + auto ObjectFilePath = + writeObjectFile(Configuration.SnippetSetup, + GenerateInstructions(InstrBenchmark.NumRepetitions)); + if (llvm::Error E = ObjectFilePath.takeError()) { + InstrBenchmark.Error = llvm::toString(std::move(E)); + return InstrBenchmark; + } + llvm::outs() << "Check generated assembly with: /usr/bin/objdump -d " + << *ObjectFilePath << "\n"; + const ExecutableFunction EF(State.createTargetMachine(), + getObjectFromFile(*ObjectFilePath)); + InstrBenchmark.Measurements = runMeasurements(EF, NumRepetitions); + + return InstrBenchmark; +} + +llvm::Expected<std::vector<BenchmarkConfiguration>> +BenchmarkRunner::generateConfigurations(unsigned Opcode) const { + if (auto E = generatePrototype(Opcode)) { + SnippetPrototype &Prototype = E.get(); + // TODO: Generate as many configurations as needed here. + BenchmarkConfiguration Configuration; + Configuration.Info = Prototype.Explanation; + for (InstructionInstance &II : Prototype.Snippet) { + II.randomizeUnsetVariables(); + Configuration.Snippet.push_back(II.build()); + } + Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet); + return std::vector<BenchmarkConfiguration>{Configuration}; + } else + return E.takeError(); +} + +std::vector<unsigned> BenchmarkRunner::computeRegsToDef( + const std::vector<InstructionInstance> &Snippet) const { + // Collect all register uses and create an assignment for each of them. + // Loop invariant: DefinedRegs[i] is true iif it has been set at least once + // before the current instruction. + llvm::BitVector DefinedRegs = RATC.emptyRegisters(); + std::vector<unsigned> RegsToDef; + for (const InstructionInstance &II : Snippet) { + // Returns the register that this Operand sets or uses, or 0 if this is not + // a register. + const auto GetOpReg = [&II](const Operand &Op) -> unsigned { + if (Op.ImplicitReg) { + return *Op.ImplicitReg; + } else if (Op.IsExplicit && II.getValueFor(Op).isReg()) { + return II.getValueFor(Op).getReg(); + } + return 0; + }; + // Collect used registers that have never been def'ed. + for (const Operand &Op : II.Instr.Operands) { + if (!Op.IsDef) { + const unsigned Reg = GetOpReg(Op); + if (Reg > 0 && !DefinedRegs.test(Reg)) { + RegsToDef.push_back(Reg); + DefinedRegs.set(Reg); + } + } + } + // Mark defs as having been def'ed. + for (const Operand &Op : II.Instr.Operands) { + if (Op.IsDef) { + const unsigned Reg = GetOpReg(Op); + if (Reg > 0) { + DefinedRegs.set(Reg); + } + } + } + } + return RegsToDef; +} + +llvm::Expected<std::string> +BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + llvm::ArrayRef<llvm::MCInst> Code) const { + int ResultFD = 0; + llvm::SmallString<256> ResultPath; + if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile( + "snippet", "o", ResultFD, ResultPath))) + return std::move(E); + llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); + assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), + Setup.RegsToDef, Code, OFS); + return ResultPath.str(); +} + +llvm::Expected<SnippetPrototype> +BenchmarkRunner::generateSelfAliasingPrototype(const Instruction &Instr) const { + const AliasingConfigurations SelfAliasing(Instr, Instr); + if (SelfAliasing.empty()) { + return llvm::make_error<BenchmarkFailure>("empty self aliasing"); + } + SnippetPrototype Prototype; + InstructionInstance II(Instr); + if (SelfAliasing.hasImplicitAliasing()) { + Prototype.Explanation = "implicit Self cycles, picking random values."; + } else { + Prototype.Explanation = + "explicit self cycles, selecting one aliasing Conf."; + // This is a self aliasing instruction so defs and uses are from the same + // instance, hence twice II in the following call. + setRandomAliasing(SelfAliasing, II, II); + } + Prototype.Snippet.push_back(std::move(II)); + return std::move(Prototype); +} + +llvm::Expected<SnippetPrototype> +BenchmarkRunner::generateUnconstrainedPrototype(const Instruction &Instr, + llvm::StringRef Msg) const { + SnippetPrototype Prototype; + Prototype.Explanation = + llvm::formatv("{0}, repeating an unconstrained assignment", Msg); + Prototype.Snippet.emplace_back(Instr); + return std::move(Prototype); +} +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.h b/tools/llvm-exegesis/lib/BenchmarkRunner.h new file mode 100644 index 000000000000..91d2c8e2d8c0 --- /dev/null +++ b/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -0,0 +1,108 @@ +//===-- BenchmarkRunner.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines the abstract BenchmarkRunner class for measuring a certain execution +/// property of instructions (e.g. latency). +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H +#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H + +#include "Assembler.h" +#include "BenchmarkResult.h" +#include "LlvmState.h" +#include "MCInstrDescView.h" +#include "RegisterAliasing.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/Error.h" +#include <vector> + +namespace exegesis { + +// A class representing failures that happened during Benchmark, they are used +// to report informations to the user. +class BenchmarkFailure : public llvm::StringError { +public: + BenchmarkFailure(const llvm::Twine &S); +}; + +// A collection of instructions that are to be assembled, executed and measured. +struct BenchmarkConfiguration { + // This code is run before the Snippet is iterated. Since it is part of the + // measurement it should be as short as possible. It is usually used to setup + // the content of the Registers. + struct Setup { + std::vector<unsigned> RegsToDef; + }; + Setup SnippetSetup; + + // The sequence of instructions that are to be repeated. + std::vector<llvm::MCInst> Snippet; + + // Informations about how this configuration was built. + std::string Info; +}; + +// Common code for all benchmark modes. +class BenchmarkRunner { +public: + explicit BenchmarkRunner(const LLVMState &State, + InstructionBenchmark::ModeE Mode); + + virtual ~BenchmarkRunner(); + + llvm::Expected<std::vector<InstructionBenchmark>> + run(unsigned Opcode, unsigned NumRepetitions); + + // Given a snippet, computes which registers the setup code needs to define. + std::vector<unsigned> + computeRegsToDef(const std::vector<InstructionInstance> &Snippet) const; + +protected: + const LLVMState &State; + const RegisterAliasingTrackerCache RATC; + + // Generates a single instruction prototype that has a self-dependency. + llvm::Expected<SnippetPrototype> + generateSelfAliasingPrototype(const Instruction &Instr) const; + // Generates a single instruction prototype without assignment constraints. + llvm::Expected<SnippetPrototype> + generateUnconstrainedPrototype(const Instruction &Instr, + llvm::StringRef Msg) const; + +private: + // API to be implemented by subclasses. + virtual llvm::Expected<SnippetPrototype> + generatePrototype(unsigned Opcode) const = 0; + + virtual std::vector<BenchmarkMeasure> + runMeasurements(const ExecutableFunction &EF, + const unsigned NumRepetitions) const = 0; + + // Internal helpers. + InstructionBenchmark runOne(const BenchmarkConfiguration &Configuration, + unsigned Opcode, unsigned NumRepetitions) const; + + // Calls generatePrototype and expands the SnippetPrototype into one or more + // BenchmarkConfiguration. + llvm::Expected<std::vector<BenchmarkConfiguration>> + generateConfigurations(unsigned Opcode) const; + + llvm::Expected<std::string> + writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + llvm::ArrayRef<llvm::MCInst> Code) const; + + const InstructionBenchmark::ModeE Mode; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H diff --git a/tools/llvm-exegesis/lib/CMakeLists.txt b/tools/llvm-exegesis/lib/CMakeLists.txt new file mode 100644 index 000000000000..175c2adf9de8 --- /dev/null +++ b/tools/llvm-exegesis/lib/CMakeLists.txt @@ -0,0 +1,46 @@ +if (LLVM_TARGETS_TO_BUILD MATCHES "X86") + add_subdirectory(X86) + set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} X86" PARENT_SCOPE) +endif() +if (LLVM_TARGETS_TO_BUILD MATCHES "AArch64") + add_subdirectory(AArch64) + set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} AArch64" PARENT_SCOPE) +endif() + +add_library(LLVMExegesis + STATIC + Analysis.cpp + Assembler.cpp + BenchmarkResult.cpp + BenchmarkRunner.cpp + Clustering.cpp + Latency.cpp + LlvmState.cpp + MCInstrDescView.cpp + PerfHelper.cpp + RegisterAliasing.cpp + Target.cpp + Uops.cpp + ) + +llvm_update_compile_flags(LLVMExegesis) +llvm_map_components_to_libnames(libs + Analysis + CodeGen + Core + ExecutionEngine + GlobalISel + MC + MCDisassembler + MCJIT + Object + ObjectYAML + Support + ) + +if(LLVM_ENABLE_LIBPFM AND HAVE_LIBPFM) + list(APPEND libs pfm) +endif() + +target_link_libraries(LLVMExegesis ${libs}) +set_target_properties(LLVMExegesis PROPERTIES FOLDER "Libraries") diff --git a/tools/llvm-exegesis/lib/Clustering.cpp b/tools/llvm-exegesis/lib/Clustering.cpp new file mode 100644 index 000000000000..a966c722a78f --- /dev/null +++ b/tools/llvm-exegesis/lib/Clustering.cpp @@ -0,0 +1,172 @@ +//===-- Clustering.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Clustering.h" +#include <string> +#include <unordered_set> + +namespace exegesis { + +// The clustering problem has the following characteristics: +// (A) - Low dimension (dimensions are typically proc resource units, +// typically < 10). +// (B) - Number of points : ~thousands (points are measurements of an MCInst) +// (C) - Number of clusters: ~tens. +// (D) - The number of clusters is not known /a priory/. +// (E) - The amount of noise is relatively small. +// The problem is rather small. In terms of algorithms, (D) disqualifies +// k-means and makes algorithms such as DBSCAN[1] or OPTICS[2] more applicable. +// +// We've used DBSCAN here because it's simple to implement. This is a pretty +// straightforward and inefficient implementation of the pseudocode in [2]. +// +// [1] https://en.wikipedia.org/wiki/DBSCAN +// [2] https://en.wikipedia.org/wiki/OPTICS_algorithm + +// Finds the points at distance less than sqrt(EpsilonSquared) of Q (not +// including Q). +std::vector<size_t> +InstructionBenchmarkClustering::rangeQuery(const size_t Q) const { + std::vector<size_t> Neighbors; + const auto &QMeasurements = Points_[Q].Measurements; + for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) { + if (P == Q) + continue; + const auto &PMeasurements = Points_[P].Measurements; + if (PMeasurements.empty()) // Error point. + continue; + if (isNeighbour(PMeasurements, QMeasurements)) { + Neighbors.push_back(P); + } + } + return Neighbors; +} + +bool InstructionBenchmarkClustering::isNeighbour( + const std::vector<BenchmarkMeasure> &P, + const std::vector<BenchmarkMeasure> &Q) const { + double DistanceSquared = 0.0; + for (size_t I = 0, E = P.size(); I < E; ++I) { + const auto Diff = P[I].Value - Q[I].Value; + DistanceSquared += Diff * Diff; + } + return DistanceSquared <= EpsilonSquared_; +} + +InstructionBenchmarkClustering::InstructionBenchmarkClustering( + const std::vector<InstructionBenchmark> &Points, + const double EpsilonSquared) + : Points_(Points), EpsilonSquared_(EpsilonSquared), + NoiseCluster_(ClusterId::noise()), ErrorCluster_(ClusterId::error()) {} + +llvm::Error InstructionBenchmarkClustering::validateAndSetup() { + ClusterIdForPoint_.resize(Points_.size()); + // Mark erroneous measurements out. + // All points must have the same number of dimensions, in the same order. + const std::vector<BenchmarkMeasure> *LastMeasurement = nullptr; + for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) { + const auto &Point = Points_[P]; + if (!Point.Error.empty()) { + ClusterIdForPoint_[P] = ClusterId::error(); + ErrorCluster_.PointIndices.push_back(P); + continue; + } + const auto *CurMeasurement = &Point.Measurements; + if (LastMeasurement) { + if (LastMeasurement->size() != CurMeasurement->size()) { + return llvm::make_error<llvm::StringError>( + "inconsistent measurement dimensions", + llvm::inconvertibleErrorCode()); + } + for (size_t I = 0, E = LastMeasurement->size(); I < E; ++I) { + if (LastMeasurement->at(I).Key != CurMeasurement->at(I).Key) { + return llvm::make_error<llvm::StringError>( + "inconsistent measurement dimensions keys", + llvm::inconvertibleErrorCode()); + } + } + } + LastMeasurement = CurMeasurement; + } + if (LastMeasurement) { + NumDimensions_ = LastMeasurement->size(); + } + return llvm::Error::success(); +} + +void InstructionBenchmarkClustering::dbScan(const size_t MinPts) { + for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) { + if (!ClusterIdForPoint_[P].isUndef()) + continue; // Previously processed in inner loop. + const auto Neighbors = rangeQuery(P); + if (Neighbors.size() + 1 < MinPts) { // Density check. + // The region around P is not dense enough to create a new cluster, mark + // as noise for now. + ClusterIdForPoint_[P] = ClusterId::noise(); + continue; + } + + // Create a new cluster, add P. + Clusters_.emplace_back(ClusterId::makeValid(Clusters_.size())); + Cluster &CurrentCluster = Clusters_.back(); + ClusterIdForPoint_[P] = CurrentCluster.Id; /* Label initial point */ + CurrentCluster.PointIndices.push_back(P); + + // Process P's neighbors. + std::unordered_set<size_t> ToProcess(Neighbors.begin(), Neighbors.end()); + while (!ToProcess.empty()) { + // Retrieve a point from the set. + const size_t Q = *ToProcess.begin(); + ToProcess.erase(Q); + + if (ClusterIdForPoint_[Q].isNoise()) { + // Change noise point to border point. + ClusterIdForPoint_[Q] = CurrentCluster.Id; + CurrentCluster.PointIndices.push_back(Q); + continue; + } + if (!ClusterIdForPoint_[Q].isUndef()) { + continue; // Previously processed. + } + // Add Q to the current custer. + ClusterIdForPoint_[Q] = CurrentCluster.Id; + CurrentCluster.PointIndices.push_back(Q); + // And extend to the neighbors of Q if the region is dense enough. + const auto Neighbors = rangeQuery(Q); + if (Neighbors.size() + 1 >= MinPts) { + ToProcess.insert(Neighbors.begin(), Neighbors.end()); + } + } + } + + // Add noisy points to noise cluster. + for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) { + if (ClusterIdForPoint_[P].isNoise()) { + NoiseCluster_.PointIndices.push_back(P); + } + } +} + +llvm::Expected<InstructionBenchmarkClustering> +InstructionBenchmarkClustering::create( + const std::vector<InstructionBenchmark> &Points, const size_t MinPts, + const double Epsilon) { + InstructionBenchmarkClustering Clustering(Points, Epsilon * Epsilon); + if (auto Error = Clustering.validateAndSetup()) { + return std::move(Error); + } + if (Clustering.ErrorCluster_.PointIndices.size() == Points.size()) { + return Clustering; // Nothing to cluster. + } + + Clustering.dbScan(MinPts); + return Clustering; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Clustering.h b/tools/llvm-exegesis/lib/Clustering.h new file mode 100644 index 000000000000..c811020e0fe8 --- /dev/null +++ b/tools/llvm-exegesis/lib/Clustering.h @@ -0,0 +1,113 @@ +//===-- Clustering.h --------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Utilities to compute benchmark result clusters. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H +#define LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H + +#include "BenchmarkResult.h" +#include "llvm/Support/Error.h" +#include <vector> + +namespace exegesis { + +class InstructionBenchmarkClustering { +public: + // Clusters `Points` using DBSCAN with the given parameters. See the cc file + // for more explanations on the algorithm. + static llvm::Expected<InstructionBenchmarkClustering> + create(const std::vector<InstructionBenchmark> &Points, size_t MinPts, + double Epsilon); + + class ClusterId { + public: + static ClusterId noise() { return ClusterId(kNoise); } + static ClusterId error() { return ClusterId(kError); } + static ClusterId makeValid(size_t Id) { return ClusterId(Id); } + ClusterId() : Id_(kUndef) {} + bool operator==(const ClusterId &O) const { return Id_ == O.Id_; } + bool operator<(const ClusterId &O) const { return Id_ < O.Id_; } + + bool isValid() const { return Id_ <= kMaxValid; } + bool isUndef() const { return Id_ == kUndef; } + bool isNoise() const { return Id_ == kNoise; } + bool isError() const { return Id_ == kError; } + + // Precondition: isValid(). + size_t getId() const { + assert(isValid()); + return Id_; + } + + private: + explicit ClusterId(size_t Id) : Id_(Id) {} + static constexpr const size_t kMaxValid = + std::numeric_limits<size_t>::max() - 4; + static constexpr const size_t kNoise = kMaxValid + 1; + static constexpr const size_t kError = kMaxValid + 2; + static constexpr const size_t kUndef = kMaxValid + 3; + size_t Id_; + }; + + struct Cluster { + Cluster() = delete; + explicit Cluster(const ClusterId &Id) : Id(Id) {} + + const ClusterId Id; + // Indices of benchmarks within the cluster. + std::vector<int> PointIndices; + }; + + ClusterId getClusterIdForPoint(size_t P) const { + return ClusterIdForPoint_[P]; + } + + const std::vector<InstructionBenchmark> &getPoints() const { return Points_; } + + const Cluster &getCluster(ClusterId Id) const { + assert(!Id.isUndef() && "unlabeled cluster"); + if (Id.isNoise()) { + return NoiseCluster_; + } + if (Id.isError()) { + return ErrorCluster_; + } + return Clusters_[Id.getId()]; + } + + const std::vector<Cluster> &getValidClusters() const { return Clusters_; } + + // Returns true if the given point is within a distance Epsilon of each other. + bool isNeighbour(const std::vector<BenchmarkMeasure> &P, + const std::vector<BenchmarkMeasure> &Q) const; + +private: + InstructionBenchmarkClustering( + const std::vector<InstructionBenchmark> &Points, double EpsilonSquared); + llvm::Error validateAndSetup(); + void dbScan(size_t MinPts); + std::vector<size_t> rangeQuery(size_t Q) const; + + const std::vector<InstructionBenchmark> &Points_; + const double EpsilonSquared_; + int NumDimensions_ = 0; + // ClusterForPoint_[P] is the cluster id for Points[P]. + std::vector<ClusterId> ClusterIdForPoint_; + std::vector<Cluster> Clusters_; + Cluster NoiseCluster_; + Cluster ErrorCluster_; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H diff --git a/tools/llvm-exegesis/lib/LLVMBuild.txt b/tools/llvm-exegesis/lib/LLVMBuild.txt new file mode 100644 index 000000000000..e02ea7151dfe --- /dev/null +++ b/tools/llvm-exegesis/lib/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-exegesis/lib/LLVMBuild.txt ------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = Exegesis +parent = Libraries +required_libraries = CodeGen ExecutionEngine MC MCDisassembler MCJIT Object ObjectYAML Support diff --git a/tools/llvm-exegesis/lib/Latency.cpp b/tools/llvm-exegesis/lib/Latency.cpp new file mode 100644 index 000000000000..e2aae970e226 --- /dev/null +++ b/tools/llvm-exegesis/lib/Latency.cpp @@ -0,0 +1,134 @@ +//===-- Latency.cpp ---------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Latency.h" + +#include "Assembler.h" +#include "BenchmarkRunner.h" +#include "MCInstrDescView.h" +#include "PerfHelper.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/Support/FormatVariadic.h" + +namespace exegesis { + +static bool hasUnknownOperand(const llvm::MCOperandInfo &OpInfo) { + return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN; +} + +// FIXME: Handle memory, see PR36905. +static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) { + return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY; +} + +LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default; + +llvm::Error LatencyBenchmarkRunner::isInfeasible( + const llvm::MCInstrDesc &MCInstrDesc) const { + if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand)) + return llvm::make_error<BenchmarkFailure>( + "Infeasible : has unknown operands"); + if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand)) + return llvm::make_error<BenchmarkFailure>( + "Infeasible : has memory operands"); + return llvm::Error::success(); +} + +llvm::Expected<SnippetPrototype> +LatencyBenchmarkRunner::generateTwoInstructionPrototype( + const Instruction &Instr) const { + std::vector<unsigned> Opcodes; + Opcodes.resize(State.getInstrInfo().getNumOpcodes()); + std::iota(Opcodes.begin(), Opcodes.end(), 0U); + std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator()); + for (const unsigned OtherOpcode : Opcodes) { + if (OtherOpcode == Instr.Description->Opcode) + continue; + const auto &OtherInstrDesc = State.getInstrInfo().get(OtherOpcode); + if (auto E = isInfeasible(OtherInstrDesc)) { + llvm::consumeError(std::move(E)); + continue; + } + const Instruction OtherInstr(OtherInstrDesc, RATC); + const AliasingConfigurations Forward(Instr, OtherInstr); + const AliasingConfigurations Back(OtherInstr, Instr); + if (Forward.empty() || Back.empty()) + continue; + InstructionInstance ThisII(Instr); + InstructionInstance OtherII(OtherInstr); + if (!Forward.hasImplicitAliasing()) + setRandomAliasing(Forward, ThisII, OtherII); + if (!Back.hasImplicitAliasing()) + setRandomAliasing(Back, OtherII, ThisII); + SnippetPrototype Prototype; + Prototype.Explanation = + llvm::formatv("creating cycle through {0}.", + State.getInstrInfo().getName(OtherOpcode)); + Prototype.Snippet.push_back(std::move(ThisII)); + Prototype.Snippet.push_back(std::move(OtherII)); + return std::move(Prototype); + } + return llvm::make_error<BenchmarkFailure>( + "Infeasible : Didn't find any scheme to make the instruction serial"); +} + +llvm::Expected<SnippetPrototype> +LatencyBenchmarkRunner::generatePrototype(unsigned Opcode) const { + const auto &InstrDesc = State.getInstrInfo().get(Opcode); + if (auto E = isInfeasible(InstrDesc)) + return std::move(E); + const Instruction Instr(InstrDesc, RATC); + if (auto SelfAliasingPrototype = generateSelfAliasingPrototype(Instr)) + return SelfAliasingPrototype; + else + llvm::consumeError(SelfAliasingPrototype.takeError()); + // No self aliasing, trying to create a dependency through another opcode. + return generateTwoInstructionPrototype(Instr); +} + +const char *LatencyBenchmarkRunner::getCounterName() const { + if (!State.getSubtargetInfo().getSchedModel().hasExtraProcessorInfo()) + llvm::report_fatal_error("sched model is missing extra processor info!"); + const char *CounterName = State.getSubtargetInfo() + .getSchedModel() + .getExtraProcessorInfo() + .PfmCounters.CycleCounter; + if (!CounterName) + llvm::report_fatal_error("sched model does not define a cycle counter"); + return CounterName; +} + +std::vector<BenchmarkMeasure> +LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, + const unsigned NumRepetitions) const { + // Cycle measurements include some overhead from the kernel. Repeat the + // measure several times and take the minimum value. + constexpr const int NumMeasurements = 30; + int64_t MinLatency = std::numeric_limits<int64_t>::max(); + const char *CounterName = getCounterName(); + if (!CounterName) + llvm::report_fatal_error("could not determine cycle counter name"); + const pfm::PerfEvent CyclesPerfEvent(CounterName); + if (!CyclesPerfEvent.valid()) + llvm::report_fatal_error("invalid perf event"); + for (size_t I = 0; I < NumMeasurements; ++I) { + pfm::Counter Counter(CyclesPerfEvent); + Counter.start(); + Function(); + Counter.stop(); + const int64_t Value = Counter.read(); + if (Value < MinLatency) + MinLatency = Value; + } + return {{"latency", static_cast<double>(MinLatency) / NumRepetitions, ""}}; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Latency.h b/tools/llvm-exegesis/lib/Latency.h new file mode 100644 index 000000000000..9d6cfc7b387d --- /dev/null +++ b/tools/llvm-exegesis/lib/Latency.h @@ -0,0 +1,47 @@ +//===-- Latency.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 BenchmarkRunner implementation to measure instruction latencies. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H +#define LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H + +#include "BenchmarkRunner.h" +#include "MCInstrDescView.h" + +namespace exegesis { + +class LatencyBenchmarkRunner : public BenchmarkRunner { +public: + LatencyBenchmarkRunner(const LLVMState &State) + : BenchmarkRunner(State, InstructionBenchmark::Latency) {} + ~LatencyBenchmarkRunner() override; + + llvm::Expected<SnippetPrototype> + generatePrototype(unsigned Opcode) const override; + +private: + llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const; + + llvm::Expected<SnippetPrototype> generateTwoInstructionPrototype( + const Instruction &Instr) const; + + std::vector<BenchmarkMeasure> + runMeasurements(const ExecutableFunction &EF, + const unsigned NumRepetitions) const override; + + virtual const char *getCounterName() const; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H diff --git a/tools/llvm-exegesis/lib/LlvmState.cpp b/tools/llvm-exegesis/lib/LlvmState.cpp new file mode 100644 index 000000000000..9ff42ca71fd2 --- /dev/null +++ b/tools/llvm-exegesis/lib/LlvmState.cpp @@ -0,0 +1,71 @@ +//===-- LlvmState.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "LlvmState.h" +#include "Target.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/MC/MCCodeEmitter.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCFixup.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Target/TargetMachine.h" +#include "llvm/Target/TargetOptions.h" + +namespace exegesis { + +LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName) { + std::string Error; + const llvm::Target *const TheTarget = + llvm::TargetRegistry::lookupTarget(Triple, Error); + assert(TheTarget && "unknown target for host"); + const llvm::TargetOptions Options; + TargetMachine.reset(static_cast<llvm::LLVMTargetMachine *>( + TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options, + llvm::Reloc::Model::Static))); + TheExegesisTarget = ExegesisTarget::lookup(TargetMachine->getTargetTriple()); + if (!TheExegesisTarget) { + llvm::errs() << "no exegesis target for " << Triple << ", using default\n"; + TheExegesisTarget = &ExegesisTarget::getDefault(); + } +} + +LLVMState::LLVMState() + : LLVMState(llvm::sys::getProcessTriple(), + llvm::sys::getHostCPUName().str()) {} + +std::unique_ptr<llvm::LLVMTargetMachine> +LLVMState::createTargetMachine() const { + return std::unique_ptr<llvm::LLVMTargetMachine>( + static_cast<llvm::LLVMTargetMachine *>( + TargetMachine->getTarget().createTargetMachine( + TargetMachine->getTargetTriple().normalize(), + TargetMachine->getTargetCPU(), + TargetMachine->getTargetFeatureString(), TargetMachine->Options, + llvm::Reloc::Model::Static))); +} + +bool LLVMState::canAssemble(const llvm::MCInst &Inst) const { + llvm::MCObjectFileInfo ObjectFileInfo; + llvm::MCContext Context(TargetMachine->getMCAsmInfo(), + TargetMachine->getMCRegisterInfo(), &ObjectFileInfo); + std::unique_ptr<const llvm::MCCodeEmitter> CodeEmitter( + TargetMachine->getTarget().createMCCodeEmitter( + *TargetMachine->getMCInstrInfo(), *TargetMachine->getMCRegisterInfo(), + Context)); + llvm::SmallVector<char, 16> Tmp; + llvm::raw_svector_ostream OS(Tmp); + llvm::SmallVector<llvm::MCFixup, 4> Fixups; + CodeEmitter->encodeInstruction(Inst, OS, Fixups, + *TargetMachine->getMCSubtargetInfo()); + return Tmp.size() > 0; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/LlvmState.h b/tools/llvm-exegesis/lib/LlvmState.h new file mode 100644 index 000000000000..c84db300841e --- /dev/null +++ b/tools/llvm-exegesis/lib/LlvmState.h @@ -0,0 +1,65 @@ +//===-- LlvmState.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 class to set up and access common LLVM objects. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H +#define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H + +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrInfo.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Target/TargetMachine.h" +#include <memory> +#include <string> + +namespace exegesis { + +class ExegesisTarget; + +// An object to initialize LLVM and prepare objects needed to run the +// measurements. +class LLVMState { +public: + LLVMState(); + + LLVMState(const std::string &Triple, + const std::string &CpuName); // For tests. + + const llvm::TargetMachine &getTargetMachine() const { return *TargetMachine; } + std::unique_ptr<llvm::LLVMTargetMachine> createTargetMachine() const; + + const ExegesisTarget &getExegesisTarget() const { return *TheExegesisTarget; } + + bool canAssemble(const llvm::MCInst &mc_inst) const; + + // For convenience: + const llvm::MCInstrInfo &getInstrInfo() const { + return *TargetMachine->getMCInstrInfo(); + } + const llvm::MCRegisterInfo &getRegInfo() const { + return *TargetMachine->getMCRegisterInfo(); + } + const llvm::MCSubtargetInfo &getSubtargetInfo() const { + return *TargetMachine->getMCSubtargetInfo(); + } + +private: + const ExegesisTarget *TheExegesisTarget; + std::unique_ptr<const llvm::TargetMachine> TargetMachine; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/tools/llvm-exegesis/lib/MCInstrDescView.cpp new file mode 100644 index 000000000000..fc0d5b327b80 --- /dev/null +++ b/tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -0,0 +1,307 @@ +//===-- MCInstrDescView.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MCInstrDescView.h" + +#include <iterator> +#include <map> +#include <tuple> + +#include "llvm/ADT/STLExtras.h" + +namespace exegesis { + +Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc, + const RegisterAliasingTrackerCache &RATC) + : Description(&MCInstrDesc) { + unsigned OpIndex = 0; + for (; OpIndex < MCInstrDesc.getNumOperands(); ++OpIndex) { + const auto &OpInfo = MCInstrDesc.opInfo_begin()[OpIndex]; + Operand Operand; + Operand.Index = OpIndex; + Operand.IsDef = (OpIndex < MCInstrDesc.getNumDefs()); + Operand.IsExplicit = true; + // TODO(gchatelet): Handle isLookupPtrRegClass. + if (OpInfo.RegClass >= 0) + Operand.Tracker = &RATC.getRegisterClass(OpInfo.RegClass); + Operand.TiedToIndex = + MCInstrDesc.getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO); + Operand.Info = &OpInfo; + Operands.push_back(Operand); + } + for (const llvm::MCPhysReg *MCPhysReg = MCInstrDesc.getImplicitDefs(); + MCPhysReg && *MCPhysReg; ++MCPhysReg, ++OpIndex) { + Operand Operand; + Operand.Index = OpIndex; + Operand.IsDef = true; + Operand.IsExplicit = false; + Operand.Tracker = &RATC.getRegister(*MCPhysReg); + Operand.ImplicitReg = MCPhysReg; + Operands.push_back(Operand); + } + for (const llvm::MCPhysReg *MCPhysReg = MCInstrDesc.getImplicitUses(); + MCPhysReg && *MCPhysReg; ++MCPhysReg, ++OpIndex) { + Operand Operand; + Operand.Index = OpIndex; + Operand.IsDef = false; + Operand.IsExplicit = false; + Operand.Tracker = &RATC.getRegister(*MCPhysReg); + Operand.ImplicitReg = MCPhysReg; + Operands.push_back(Operand); + } + // Assigning Variables to non tied explicit operands. + Variables.reserve(Operands.size()); // Variables.size() <= Operands.size() + for (auto &Op : Operands) + if (Op.IsExplicit && Op.TiedToIndex < 0) { + const size_t VariableIndex = Variables.size(); + Op.VariableIndex = VariableIndex; + Variables.emplace_back(); + Variables.back().Index = VariableIndex; + } + // Assigning Variables to tied operands. + for (auto &Op : Operands) + if (Op.TiedToIndex >= 0) + Op.VariableIndex = Operands[Op.TiedToIndex].VariableIndex; + // Assigning Operands to Variables. + for (auto &Op : Operands) + if (Op.VariableIndex >= 0) + Variables[Op.VariableIndex].TiedOperands.push_back(Op.Index); + // Processing Aliasing. + DefRegisters = RATC.emptyRegisters(); + UseRegisters = RATC.emptyRegisters(); + for (const auto &Op : Operands) { + if (Op.Tracker) { + auto &Registers = Op.IsDef ? DefRegisters : UseRegisters; + Registers |= Op.Tracker->aliasedBits(); + } + } +} + +InstructionInstance::InstructionInstance(const Instruction &Instr) + : Instr(Instr), VariableValues(Instr.Variables.size()) {} + +InstructionInstance::InstructionInstance(InstructionInstance &&) = default; + +InstructionInstance &InstructionInstance:: +operator=(InstructionInstance &&) = default; + +unsigned InstructionInstance::getOpcode() const { + return Instr.Description->getOpcode(); +} + +llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) { + return VariableValues[Var.Index]; +} + +const llvm::MCOperand & +InstructionInstance::getValueFor(const Variable &Var) const { + return VariableValues[Var.Index]; +} + +llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) { + assert(Op.VariableIndex >= 0); + return getValueFor(Instr.Variables[Op.VariableIndex]); +} + +const llvm::MCOperand & +InstructionInstance::getValueFor(const Operand &Op) const { + assert(Op.VariableIndex >= 0); + return getValueFor(Instr.Variables[Op.VariableIndex]); +} + +// forward declaration. +static void randomize(const Instruction &Instr, const Variable &Var, + llvm::MCOperand &AssignedValue); + +bool InstructionInstance::hasImmediateVariables() const { + return llvm::any_of(Instr.Variables, [this](const Variable &Var) { + assert(!Var.TiedOperands.empty()); + const unsigned OpIndex = Var.TiedOperands[0]; + const Operand &Op = Instr.Operands[OpIndex]; + assert(Op.Info); + return Op.Info->OperandType == llvm::MCOI::OPERAND_IMMEDIATE; + }); +} + +void InstructionInstance::randomizeUnsetVariables() { + for (const Variable &Var : Instr.Variables) { + llvm::MCOperand &AssignedValue = getValueFor(Var); + if (!AssignedValue.isValid()) + randomize(Instr, Var, AssignedValue); + } +} + +llvm::MCInst InstructionInstance::build() const { + llvm::MCInst Result; + Result.setOpcode(Instr.Description->Opcode); + for (const auto &Op : Instr.Operands) + if (Op.IsExplicit) + Result.addOperand(getValueFor(Op)); + return Result; +} + +SnippetPrototype::SnippetPrototype(SnippetPrototype &&) = default; + +SnippetPrototype &SnippetPrototype::operator=(SnippetPrototype &&) = default; + +bool RegisterOperandAssignment:: +operator==(const RegisterOperandAssignment &Other) const { + return std::tie(Op, Reg) == std::tie(Other.Op, Other.Reg); +} + +bool AliasingRegisterOperands:: +operator==(const AliasingRegisterOperands &Other) const { + return std::tie(Defs, Uses) == std::tie(Other.Defs, Other.Uses); +} + +static void addOperandIfAlias( + const llvm::MCPhysReg Reg, bool SelectDef, llvm::ArrayRef<Operand> Operands, + llvm::SmallVectorImpl<RegisterOperandAssignment> &OperandValues) { + for (const auto &Op : Operands) { + if (Op.Tracker && Op.IsDef == SelectDef) { + const int SourceReg = Op.Tracker->getOrigin(Reg); + if (SourceReg >= 0) + OperandValues.emplace_back(&Op, SourceReg); + } + } +} + +bool AliasingRegisterOperands::hasImplicitAliasing() const { + const auto HasImplicit = [](const RegisterOperandAssignment &ROV) { + return !ROV.Op->IsExplicit; + }; + return llvm::any_of(Defs, HasImplicit) && llvm::any_of(Uses, HasImplicit); +} + +bool AliasingConfigurations::empty() const { return Configurations.empty(); } + +bool AliasingConfigurations::hasImplicitAliasing() const { + return llvm::any_of(Configurations, [](const AliasingRegisterOperands &ARO) { + return ARO.hasImplicitAliasing(); + }); +} + +AliasingConfigurations::AliasingConfigurations( + const Instruction &DefInstruction, const Instruction &UseInstruction) + : DefInstruction(DefInstruction), UseInstruction(UseInstruction) { + if (UseInstruction.UseRegisters.anyCommon(DefInstruction.DefRegisters)) { + auto CommonRegisters = UseInstruction.UseRegisters; + CommonRegisters &= DefInstruction.DefRegisters; + for (const llvm::MCPhysReg Reg : CommonRegisters.set_bits()) { + AliasingRegisterOperands ARO; + addOperandIfAlias(Reg, true, DefInstruction.Operands, ARO.Defs); + addOperandIfAlias(Reg, false, UseInstruction.Operands, ARO.Uses); + if (!ARO.Defs.empty() && !ARO.Uses.empty() && + !llvm::is_contained(Configurations, ARO)) + Configurations.push_back(std::move(ARO)); + } + } +} + +std::mt19937 &randomGenerator() { + static std::random_device RandomDevice; + static std::mt19937 RandomGenerator(RandomDevice()); + return RandomGenerator; +} + +static size_t randomIndex(size_t Size) { + assert(Size > 0); + std::uniform_int_distribution<> Distribution(0, Size - 1); + return Distribution(randomGenerator()); +} + +template <typename C> +static auto randomElement(const C &Container) -> decltype(Container[0]) { + return Container[randomIndex(Container.size())]; +} + +static void randomize(const Instruction &Instr, const Variable &Var, + llvm::MCOperand &AssignedValue) { + assert(!Var.TiedOperands.empty()); + const Operand &Op = Instr.Operands[Var.TiedOperands.front()]; + assert(Op.Info != nullptr); + const auto &OpInfo = *Op.Info; + switch (OpInfo.OperandType) { + case llvm::MCOI::OperandType::OPERAND_IMMEDIATE: + // FIXME: explore immediate values too. + AssignedValue = llvm::MCOperand::createImm(1); + break; + case llvm::MCOI::OperandType::OPERAND_REGISTER: { + assert(Op.Tracker); + const auto &Registers = Op.Tracker->sourceBits(); + AssignedValue = llvm::MCOperand::createReg(randomBit(Registers)); + break; + } + default: + break; + } +} + +static void setRegisterOperandValue(const RegisterOperandAssignment &ROV, + InstructionInstance &II) { + assert(ROV.Op); + if (ROV.Op->IsExplicit) { + auto &AssignedValue = II.getValueFor(*ROV.Op); + if (AssignedValue.isValid()) { + assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg); + return; + } + AssignedValue = llvm::MCOperand::createReg(ROV.Reg); + } else { + assert(ROV.Op->ImplicitReg != nullptr); + assert(ROV.Reg == *ROV.Op->ImplicitReg); + } +} + +size_t randomBit(const llvm::BitVector &Vector) { + assert(Vector.any()); + auto Itr = Vector.set_bits_begin(); + for (size_t I = randomIndex(Vector.count()); I != 0; --I) + ++Itr; + return *Itr; +} + +void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations, + InstructionInstance &DefII, InstructionInstance &UseII) { + assert(!AliasingConfigurations.empty()); + assert(!AliasingConfigurations.hasImplicitAliasing()); + const auto &RandomConf = randomElement(AliasingConfigurations.Configurations); + setRegisterOperandValue(randomElement(RandomConf.Defs), DefII); + setRegisterOperandValue(randomElement(RandomConf.Uses), UseII); +} + +void DumpMCOperand(const llvm::MCRegisterInfo &MCRegisterInfo, + const llvm::MCOperand &Op, llvm::raw_ostream &OS) { + if (!Op.isValid()) + OS << "Invalid"; + else if (Op.isReg()) + OS << MCRegisterInfo.getName(Op.getReg()); + else if (Op.isImm()) + OS << Op.getImm(); + else if (Op.isFPImm()) + OS << Op.getFPImm(); + else if (Op.isExpr()) + OS << "Expr"; + else if (Op.isInst()) + OS << "SubInst"; +} + +void DumpMCInst(const llvm::MCRegisterInfo &MCRegisterInfo, + const llvm::MCInstrInfo &MCInstrInfo, + const llvm::MCInst &MCInst, llvm::raw_ostream &OS) { + OS << MCInstrInfo.getName(MCInst.getOpcode()); + for (unsigned I = 0, E = MCInst.getNumOperands(); I < E; ++I) { + if (I > 0) + OS << ','; + OS << ' '; + DumpMCOperand(MCRegisterInfo, MCInst.getOperand(I), OS); + } +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.h b/tools/llvm-exegesis/lib/MCInstrDescView.h new file mode 100644 index 000000000000..c36582ac72d8 --- /dev/null +++ b/tools/llvm-exegesis/lib/MCInstrDescView.h @@ -0,0 +1,196 @@ +//===-- MCInstrDescView.h ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Provide views around LLVM structures to represents an instruction instance, +/// as well as its implicit and explicit arguments in a uniform way. +/// Arguments that are explicit and independant (non tied) also have a Variable +/// associated to them so the instruction can be fully defined by reading its +/// Variables. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H +#define LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H + +#include <random> + +#include "RegisterAliasing.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCInstrDesc.h" +#include "llvm/MC/MCInstrInfo.h" + +namespace exegesis { + +struct Operand; // forward declaration. + +// A variable represents the value associated to an Operand or a set of Operands +// if they are tied together. +struct Variable { + // The indices of the operands tied to this Variable. + llvm::SmallVector<unsigned, 2> TiedOperands; + llvm::MCOperand AssignedValue; + // The index of this Variable in Instruction.Variables and its associated + // Value in InstructionInstance.VariableValues. + unsigned Index = -1; +}; + +// MCOperandInfo can only represents Explicit operands. This object gives a +// uniform view of Implicit and Explicit Operands. +// +// - Index: can be used to refer to MCInstrDesc::operands for Explicit operands. +// - Tracker: is set for Register Operands and is used to keep track of possible +// registers and the registers reachable from them (aliasing registers). +// - Info: a shortcut for MCInstrDesc::operands()[Index]. +// - TiedToIndex: the index of the Operand holding the value or -1. +// - ImplicitReg: a pointer to the register value when Operand is Implicit, +// nullptr otherwise. +// - VariableIndex: the index of the Variable holding the value for this Operand +// or -1 if this operand is implicit. +struct Operand { + unsigned Index = 0; + bool IsDef = false; + bool IsExplicit = false; + const RegisterAliasingTracker *Tracker = nullptr; // Set for Register Op. + const llvm::MCOperandInfo *Info = nullptr; // Set for Explicit Op. + int TiedToIndex = -1; // Set for Reg&Explicit Op. + const llvm::MCPhysReg *ImplicitReg = nullptr; // Set for Implicit Op. + int VariableIndex = -1; // Set for Explicit Op. +}; + +// A view over an MCInstrDesc offering a convenient interface to compute +// Register aliasing. +struct Instruction { + Instruction(const llvm::MCInstrDesc &MCInstrDesc, + const RegisterAliasingTrackerCache &ATC); + + const llvm::MCInstrDesc *Description; // Never nullptr. + llvm::SmallVector<Operand, 8> Operands; + llvm::SmallVector<Variable, 4> Variables; + llvm::BitVector DefRegisters; // The union of the aliased def registers. + llvm::BitVector UseRegisters; // The union of the aliased use registers. +}; + +// An instance of an Instruction holding values for each of its Variables. +struct InstructionInstance { + InstructionInstance(const Instruction &Instr); + + // No copy. + InstructionInstance(const InstructionInstance &) = delete; + InstructionInstance &operator=(const InstructionInstance &) = delete; + + // Moving is OK. + InstructionInstance(InstructionInstance &&); + InstructionInstance &operator=(InstructionInstance &&); + + unsigned getOpcode() const; + llvm::MCOperand &getValueFor(const Variable &Var); + const llvm::MCOperand &getValueFor(const Variable &Var) const; + llvm::MCOperand &getValueFor(const Operand &Op); + const llvm::MCOperand &getValueFor(const Operand &Op) const; + bool hasImmediateVariables() const; + + // Assigns a Random Value to all Variables that are still Invalid. + void randomizeUnsetVariables(); + + // Returns the instance as an llvm::MCInst. The InstructionInstance must be + // fully allocated (no invalid variables). + llvm::MCInst build() const; + + Instruction Instr; + llvm::SmallVector<llvm::MCOperand, 4> VariableValues; +}; + +// A prototype is a set of InstructionInstances with an explanation of how +// it's been built. The prototype can then be randomized to exercice several +// immediate values. It is also used to gather the used registers and define +// their initial values. +struct SnippetPrototype { + SnippetPrototype() = default; + + // No copy. + SnippetPrototype(const SnippetPrototype &) = delete; + SnippetPrototype &operator=(const SnippetPrototype &) = delete; + + // Moving is OK. + SnippetPrototype(SnippetPrototype &&); + SnippetPrototype &operator=(SnippetPrototype &&); + + std::string Explanation; + std::vector<InstructionInstance> Snippet; +}; + +// Represents the assignment of a Register to an Operand. +struct RegisterOperandAssignment { + RegisterOperandAssignment(const Operand *Operand, llvm::MCPhysReg Reg) + : Op(Operand), Reg(Reg) {} + + const Operand *Op; // Pointer to an Explicit Register Operand. + llvm::MCPhysReg Reg; + + bool operator==(const RegisterOperandAssignment &other) const; +}; + +// Represents a set of Operands that would alias through the use of some +// Registers. +// There are two reasons why operands would alias: +// - The registers assigned to each of the operands are the same or alias each +// other (e.g. AX/AL) +// - The operands are tied. +struct AliasingRegisterOperands { + llvm::SmallVector<RegisterOperandAssignment, 1> Defs; // Unlikely size() > 1. + llvm::SmallVector<RegisterOperandAssignment, 2> Uses; + + // True is Defs and Use contain an Implicit Operand. + bool hasImplicitAliasing() const; + + bool operator==(const AliasingRegisterOperands &other) const; +}; + +// Returns all possible configurations leading Def registers of DefInstruction +// to alias with Use registers of UseInstruction. +struct AliasingConfigurations { + AliasingConfigurations(const Instruction &DefInstruction, + const Instruction &UseInstruction); + + bool empty() const; // True if no aliasing configuration is found. + bool hasImplicitAliasing() const; + void setExplicitAliasing() const; + + const Instruction &DefInstruction; + const Instruction &UseInstruction; + llvm::SmallVector<AliasingRegisterOperands, 32> Configurations; +}; + +// A global Random Number Generator to randomize configurations. +// FIXME: Move random number generation into an object and make it seedable for +// unit tests. +std::mt19937 &randomGenerator(); + +// Picks a random bit among the bits set in Vector and returns its index. +// Precondition: Vector must have at least one bit set. +size_t randomBit(const llvm::BitVector &Vector); + +// Picks a random configuration, then selects a random def and a random use from +// it and finally set the selected values in the provided InstructionInstances. +void setRandomAliasing(const AliasingConfigurations &AliasingConfigurations, + InstructionInstance &DefII, InstructionInstance &UseII); + +// Writes MCInst to OS. +// This is not assembly but the internal LLVM's name for instructions and +// registers. +void DumpMCInst(const llvm::MCRegisterInfo &MCRegisterInfo, + const llvm::MCInstrInfo &MCInstrInfo, + const llvm::MCInst &MCInst, llvm::raw_ostream &OS); + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H diff --git a/tools/llvm-exegesis/lib/PerfHelper.cpp b/tools/llvm-exegesis/lib/PerfHelper.cpp new file mode 100644 index 000000000000..c145ea8404b4 --- /dev/null +++ b/tools/llvm-exegesis/lib/PerfHelper.cpp @@ -0,0 +1,138 @@ +//===-- PerfHelper.cpp ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "PerfHelper.h" +#include "llvm/Config/config.h" +#include "llvm/Support/raw_ostream.h" +#ifdef HAVE_LIBPFM +#include "perfmon/perf_event.h" +#include "perfmon/pfmlib.h" +#include "perfmon/pfmlib_perf_event.h" +#endif +#include <cassert> + +namespace exegesis { +namespace pfm { + +#ifdef HAVE_LIBPFM +static bool isPfmError(int Code) { return Code != PFM_SUCCESS; } +#endif + +bool pfmInitialize() { +#ifdef HAVE_LIBPFM + return isPfmError(pfm_initialize()); +#else + return true; +#endif +} + +void pfmTerminate() { +#ifdef HAVE_LIBPFM + pfm_terminate(); +#endif +} + +PerfEvent::~PerfEvent() { +#ifdef HAVE_LIBPFM + delete Attr; + ; +#endif +} + +PerfEvent::PerfEvent(PerfEvent &&Other) + : EventString(std::move(Other.EventString)), + FullQualifiedEventString(std::move(Other.FullQualifiedEventString)), + Attr(Other.Attr) { + Other.Attr = nullptr; +} + +PerfEvent::PerfEvent(llvm::StringRef PfmEventString) + : EventString(PfmEventString.str()), Attr(nullptr) { +#ifdef HAVE_LIBPFM + char *Fstr = nullptr; + pfm_perf_encode_arg_t Arg = {}; + Attr = new perf_event_attr(); + Arg.attr = Attr; + Arg.fstr = &Fstr; + Arg.size = sizeof(pfm_perf_encode_arg_t); + const int Result = pfm_get_os_event_encoding(EventString.c_str(), PFM_PLM3, + PFM_OS_PERF_EVENT, &Arg); + if (isPfmError(Result)) { + // We don't know beforehand which counters are available (e.g. 6 uops ports + // on Sandybridge but 8 on Haswell) so we report the missing counter without + // crashing. + llvm::errs() << pfm_strerror(Result) << " - cannot create event " + << EventString << "\n"; + } + if (Fstr) { + FullQualifiedEventString = Fstr; + free(Fstr); + } +#endif +} + +llvm::StringRef PerfEvent::name() const { return EventString; } + +bool PerfEvent::valid() const { return !FullQualifiedEventString.empty(); } + +const perf_event_attr *PerfEvent::attribute() const { return Attr; } + +llvm::StringRef PerfEvent::getPfmEventString() const { + return FullQualifiedEventString; +} + +#ifdef HAVE_LIBPFM +Counter::Counter(const PerfEvent &Event) { + assert(Event.valid()); + const pid_t Pid = 0; // measure current process/thread. + const int Cpu = -1; // measure any processor. + const int GroupFd = -1; // no grouping of counters. + const uint32_t Flags = 0; + perf_event_attr AttrCopy = *Event.attribute(); + FileDescriptor = perf_event_open(&AttrCopy, Pid, Cpu, GroupFd, Flags); + if (FileDescriptor == -1) { + llvm::errs() << "Unable to open event, make sure your kernel allows user " + "space perf monitoring.\nYou may want to try:\n$ sudo sh " + "-c 'echo -1 > /proc/sys/kernel/perf_event_paranoid'\n"; + } + assert(FileDescriptor != -1 && "Unable to open event"); +} + +Counter::~Counter() { close(FileDescriptor); } + +void Counter::start() { ioctl(FileDescriptor, PERF_EVENT_IOC_RESET, 0); } + +void Counter::stop() { ioctl(FileDescriptor, PERF_EVENT_IOC_DISABLE, 0); } + +int64_t Counter::read() const { + int64_t Count = 0; + ssize_t ReadSize = ::read(FileDescriptor, &Count, sizeof(Count)); + if (ReadSize != sizeof(Count)) { + Count = -1; + llvm::errs() << "Failed to read event counter\n"; + } + return Count; +} + +#else + +Counter::Counter(const PerfEvent &Event) {} + +Counter::~Counter() = default; + +void Counter::start() {} + +void Counter::stop() {} + +int64_t Counter::read() const { return 42; } + +#endif + +} // namespace pfm +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/PerfHelper.h b/tools/llvm-exegesis/lib/PerfHelper.h new file mode 100644 index 000000000000..8c3f13e6c5cd --- /dev/null +++ b/tools/llvm-exegesis/lib/PerfHelper.h @@ -0,0 +1,106 @@ +//===-- PerfHelper.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Helpers for measuring perf events. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H +#define LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Config/config.h" +#include <functional> +#include <memory> + +struct perf_event_attr; + +namespace exegesis { +namespace pfm { + +// Returns true on error. +bool pfmInitialize(); +void pfmTerminate(); + +// Retrieves the encoding for the event described by pfm_event_string. +// NOTE: pfm_initialize() must be called before creating PerfEvent objects. +class PerfEvent { +public: + // http://perfmon2.sourceforge.net/manv4/libpfm.html + // Events are expressed as strings. e.g. "INSTRUCTION_RETIRED" + explicit PerfEvent(llvm::StringRef pfm_event_string); + + PerfEvent(const PerfEvent &) = delete; + PerfEvent(PerfEvent &&other); + ~PerfEvent(); + + // The pfm_event_string passed at construction time. + llvm::StringRef name() const; + + // Whether the event was successfully created. + bool valid() const; + + // The encoded event to be passed to the Kernel. + const perf_event_attr *attribute() const; + + // The fully qualified name for the event. + // e.g. "snb_ep::INSTRUCTION_RETIRED:e=0:i=0:c=0:t=0:u=1:k=0:mg=0:mh=1" + llvm::StringRef getPfmEventString() const; + +private: + const std::string EventString; + std::string FullQualifiedEventString; + perf_event_attr *Attr; +}; + +// Uses a valid PerfEvent to configure the Kernel so we can measure the +// underlying event. +struct Counter { + // event: the PerfEvent to measure. + explicit Counter(const PerfEvent &event); + + Counter(const Counter &) = delete; + Counter(Counter &&other) = default; + + ~Counter(); + + void start(); // Starts the measurement of the event. + void stop(); // Stops the measurement of the event. + int64_t read() const; // Return the current value of the counter. + +private: +#ifdef HAVE_LIBPFM + int FileDescriptor = -1; +#endif +}; + +// Helper to measure a list of PerfEvent for a particular function. +// callback is called for each successful measure (PerfEvent needs to be valid). +template <typename Function> +void Measure( + llvm::ArrayRef<PerfEvent> Events, + const std::function<void(const PerfEvent &Event, int64_t Value)> &Callback, + Function Fn) { + for (const auto &Event : Events) { + if (!Event.valid()) + continue; + Counter Cnt(Event); + Cnt.start(); + Fn(); + Cnt.stop(); + Callback(Event, Cnt.read()); + } +} + +} // namespace pfm +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H diff --git a/tools/llvm-exegesis/lib/RegisterAliasing.cpp b/tools/llvm-exegesis/lib/RegisterAliasing.cpp new file mode 100644 index 000000000000..039f78db985f --- /dev/null +++ b/tools/llvm-exegesis/lib/RegisterAliasing.cpp @@ -0,0 +1,83 @@ +//===-- RegisterAliasingTracker.cpp -----------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterAliasing.h" + +namespace exegesis { + +llvm::BitVector getAliasedBits(const llvm::MCRegisterInfo &RegInfo, + const llvm::BitVector &SourceBits) { + llvm::BitVector AliasedBits(RegInfo.getNumRegs()); + for (const size_t PhysReg : SourceBits.set_bits()) { + using RegAliasItr = llvm::MCRegAliasIterator; + for (auto Itr = RegAliasItr(PhysReg, &RegInfo, true); Itr.isValid(); + ++Itr) { + AliasedBits.set(*Itr); + } + } + return AliasedBits; +} + +RegisterAliasingTracker::RegisterAliasingTracker( + const llvm::MCRegisterInfo &RegInfo) + : SourceBits(RegInfo.getNumRegs()), AliasedBits(RegInfo.getNumRegs()), + Origins(RegInfo.getNumRegs()) {} + +RegisterAliasingTracker::RegisterAliasingTracker( + const llvm::MCRegisterInfo &RegInfo, const llvm::BitVector &ReservedReg, + const llvm::MCRegisterClass &RegClass) + : RegisterAliasingTracker(RegInfo) { + for (llvm::MCPhysReg PhysReg : RegClass) + if (!ReservedReg[PhysReg]) // Removing reserved registers. + SourceBits.set(PhysReg); + FillOriginAndAliasedBits(RegInfo, SourceBits); +} + +RegisterAliasingTracker::RegisterAliasingTracker( + const llvm::MCRegisterInfo &RegInfo, const llvm::MCPhysReg PhysReg) + : RegisterAliasingTracker(RegInfo) { + SourceBits.set(PhysReg); + FillOriginAndAliasedBits(RegInfo, SourceBits); +} + +void RegisterAliasingTracker::FillOriginAndAliasedBits( + const llvm::MCRegisterInfo &RegInfo, const llvm::BitVector &SourceBits) { + using RegAliasItr = llvm::MCRegAliasIterator; + for (const size_t PhysReg : SourceBits.set_bits()) { + for (auto Itr = RegAliasItr(PhysReg, &RegInfo, true); Itr.isValid(); + ++Itr) { + AliasedBits.set(*Itr); + Origins[*Itr] = PhysReg; + } + } +} + +RegisterAliasingTrackerCache::RegisterAliasingTrackerCache( + const llvm::MCRegisterInfo &RegInfo, const llvm::BitVector &ReservedReg) + : RegInfo(RegInfo), ReservedReg(ReservedReg), + EmptyRegisters(RegInfo.getNumRegs()) {} + +const RegisterAliasingTracker & +RegisterAliasingTrackerCache::getRegister(llvm::MCPhysReg PhysReg) const { + auto &Found = Registers[PhysReg]; + if (!Found) + Found.reset(new RegisterAliasingTracker(RegInfo, PhysReg)); + return *Found; +} + +const RegisterAliasingTracker & +RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const { + auto &Found = RegisterClasses[RegClassIndex]; + const auto &RegClass = RegInfo.getRegClass(RegClassIndex); + if (!Found) + Found.reset(new RegisterAliasingTracker(RegInfo, ReservedReg, RegClass)); + return *Found; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/RegisterAliasing.h b/tools/llvm-exegesis/lib/RegisterAliasing.h new file mode 100644 index 000000000000..0b0360ef9ff4 --- /dev/null +++ b/tools/llvm-exegesis/lib/RegisterAliasing.h @@ -0,0 +1,107 @@ +//===-- RegisterAliasingTracker.h -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Defines classes to keep track of register aliasing. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H +#define LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H + +#include <memory> +#include <unordered_map> + +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/PackedVector.h" +#include "llvm/MC/MCRegisterInfo.h" + +namespace exegesis { + +// Returns the registers that are aliased by the ones set in SourceBits. +llvm::BitVector getAliasedBits(const llvm::MCRegisterInfo &RegInfo, + const llvm::BitVector &SourceBits); + +// Keeps track of a mapping from one register (or a register class) to its +// aliased registers. +// +// e.g. +// RegisterAliasingTracker Tracker(RegInfo, llvm::X86::EAX); +// Tracker.sourceBits() == { llvm::X86::EAX } +// Tracker.aliasedBits() == { llvm::X86::AL, llvm::X86::AH, llvm::X86::AX, +// llvm::X86::EAX,llvm::X86::HAX, llvm::X86::RAX } +// Tracker.getOrigin(llvm::X86::AL) == llvm::X86::EAX; +// Tracker.getOrigin(llvm::X86::BX) == -1; +struct RegisterAliasingTracker { + // Construct a tracker from an MCRegisterClass. + RegisterAliasingTracker(const llvm::MCRegisterInfo &RegInfo, + const llvm::BitVector &ReservedReg, + const llvm::MCRegisterClass &RegClass); + + // Construct a tracker from an MCPhysReg. + RegisterAliasingTracker(const llvm::MCRegisterInfo &RegInfo, + const llvm::MCPhysReg Register); + + const llvm::BitVector &sourceBits() const { return SourceBits; } + + // Retrieves all the touched registers as a BitVector. + const llvm::BitVector &aliasedBits() const { return AliasedBits; } + + // Returns the origin of this register or -1. + int getOrigin(llvm::MCPhysReg Aliased) const { + if (!AliasedBits[Aliased]) + return -1; + return Origins[Aliased]; + } + +private: + RegisterAliasingTracker(const llvm::MCRegisterInfo &RegInfo); + + void FillOriginAndAliasedBits(const llvm::MCRegisterInfo &RegInfo, + const llvm::BitVector &OriginalBits); + + llvm::BitVector SourceBits; + llvm::BitVector AliasedBits; + llvm::PackedVector<size_t, 10> Origins; // Max 1024 physical registers. +}; + +// A cache of existing trackers. +struct RegisterAliasingTrackerCache { + // RegInfo must outlive the cache. + RegisterAliasingTrackerCache(const llvm::MCRegisterInfo &RegInfo, + const llvm::BitVector &ReservedReg); + + // Convenient function to retrieve a BitVector of the right size. + const llvm::BitVector &emptyRegisters() const { return EmptyRegisters; } + + // Convenient function to retrieve the registers the function body can't use. + const llvm::BitVector &reservedRegisters() const { return ReservedReg; } + + // Convenient function to retrieve the underlying MCRegInfo. + const llvm::MCRegisterInfo ®Info() const { return RegInfo; } + + // Retrieves the RegisterAliasingTracker for this particular register. + const RegisterAliasingTracker &getRegister(llvm::MCPhysReg Reg) const; + + // Retrieves the RegisterAliasingTracker for this particular register class. + const RegisterAliasingTracker &getRegisterClass(unsigned RegClassIndex) const; + +private: + const llvm::MCRegisterInfo &RegInfo; + const llvm::BitVector ReservedReg; + const llvm::BitVector EmptyRegisters; + mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>> + Registers; + mutable std::unordered_map<unsigned, std::unique_ptr<RegisterAliasingTracker>> + RegisterClasses; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H diff --git a/tools/llvm-exegesis/lib/Target.cpp b/tools/llvm-exegesis/lib/Target.cpp new file mode 100644 index 000000000000..44156c815dbe --- /dev/null +++ b/tools/llvm-exegesis/lib/Target.cpp @@ -0,0 +1,82 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "Target.h" + +#include "Latency.h" +#include "Uops.h" + +namespace exegesis { + +ExegesisTarget::~ExegesisTarget() {} // anchor. + +static ExegesisTarget *FirstTarget = nullptr; + +const ExegesisTarget *ExegesisTarget::lookup(llvm::Triple TT) { + for (const ExegesisTarget *T = FirstTarget; T != nullptr; T = T->Next) { + if (T->matchesArch(TT.getArch())) + return T; + } + return nullptr; +} + +void ExegesisTarget::registerTarget(ExegesisTarget *Target) { + if (FirstTarget == nullptr) { + FirstTarget = Target; + return; + } + assert(Target->Next == nullptr && "target has already been registered"); + if (Target->Next != nullptr) + return; + Target->Next = FirstTarget; + FirstTarget = Target; +} + +std::unique_ptr<BenchmarkRunner> +ExegesisTarget::createBenchmarkRunner(InstructionBenchmark::ModeE Mode, + const LLVMState &State) const { + switch (Mode) { + case InstructionBenchmark::Unknown: + return nullptr; + case InstructionBenchmark::Latency: + return createLatencyBenchmarkRunner(State); + case InstructionBenchmark::Uops: + return createUopsBenchmarkRunner(State); + } + return nullptr; +} + +std::unique_ptr<BenchmarkRunner> +ExegesisTarget::createLatencyBenchmarkRunner(const LLVMState &State) const { + return llvm::make_unique<LatencyBenchmarkRunner>(State); +} + +std::unique_ptr<BenchmarkRunner> +ExegesisTarget::createUopsBenchmarkRunner(const LLVMState &State) const { + return llvm::make_unique<UopsBenchmarkRunner>(State); +} + +namespace { + +// Default implementation. +class ExegesisDefaultTarget : public ExegesisTarget { +private: + bool matchesArch(llvm::Triple::ArchType Arch) const override { + llvm_unreachable("never called"); + return false; + } +}; + +} // namespace + +const ExegesisTarget &ExegesisTarget::getDefault() { + static ExegesisDefaultTarget Target; + return Target; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Target.h b/tools/llvm-exegesis/lib/Target.h new file mode 100644 index 000000000000..4f1f2869b749 --- /dev/null +++ b/tools/llvm-exegesis/lib/Target.h @@ -0,0 +1,72 @@ +//===-- Target.h ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Classes that handle the creation of target-specific objects. This is +/// similar to llvm::Target/TargetRegistry. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H +#define LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H + +#include "BenchmarkResult.h" +#include "BenchmarkRunner.h" +#include "LlvmState.h" +#include "llvm/ADT/Triple.h" +#include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/LegacyPassManager.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" + +namespace exegesis { + +class ExegesisTarget { +public: + // Targets can use this to add target-specific passes in assembleToStream(); + virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {} + + // Generates code to move a constant into a the given register. + virtual std::vector<llvm::MCInst> + setRegToConstant(const llvm::MCSubtargetInfo &STI, unsigned Reg) const { + return {}; + } + + // Creates a benchmark runner for the given mode. + std::unique_ptr<BenchmarkRunner> + createBenchmarkRunner(InstructionBenchmark::ModeE Mode, + const LLVMState &State) const; + + // Returns the ExegesisTarget for the given triple or nullptr if the target + // does not exist. + static const ExegesisTarget *lookup(llvm::Triple TT); + // Returns the default (unspecialized) ExegesisTarget. + static const ExegesisTarget &getDefault(); + // Registers a target. Not thread safe. + static void registerTarget(ExegesisTarget *T); + + virtual ~ExegesisTarget(); + +private: + virtual bool matchesArch(llvm::Triple::ArchType Arch) const = 0; + + // Targets can implement their own Latency/Uops benchmarks runners by + // implementing these. + std::unique_ptr<BenchmarkRunner> virtual createLatencyBenchmarkRunner( + const LLVMState &State) const; + std::unique_ptr<BenchmarkRunner> virtual createUopsBenchmarkRunner( + const LLVMState &State) const; + + const ExegesisTarget *Next = nullptr; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H diff --git a/tools/llvm-exegesis/lib/Uops.cpp b/tools/llvm-exegesis/lib/Uops.cpp new file mode 100644 index 000000000000..897f10808889 --- /dev/null +++ b/tools/llvm-exegesis/lib/Uops.cpp @@ -0,0 +1,235 @@ +//===-- Uops.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Uops.h" + +#include "Assembler.h" +#include "BenchmarkRunner.h" +#include "MCInstrDescView.h" +#include "PerfHelper.h" + +// FIXME: Load constants into registers (e.g. with fld1) to not break +// instructions like x87. + +// Ideally we would like the only limitation on executing uops to be the issue +// ports. Maximizing port pressure increases the likelihood that the load is +// distributed evenly across possible ports. + +// To achieve that, one approach is to generate instructions that do not have +// data dependencies between them. +// +// For some instructions, this is trivial: +// mov rax, qword ptr [rsi] +// mov rax, qword ptr [rsi] +// mov rax, qword ptr [rsi] +// mov rax, qword ptr [rsi] +// For the above snippet, haswell just renames rax four times and executes the +// four instructions two at a time on P23 and P0126. +// +// For some instructions, we just need to make sure that the source is +// different from the destination. For example, IDIV8r reads from GPR and +// writes to AX. We just need to ensure that the Var is assigned a +// register which is different from AX: +// idiv bx +// idiv bx +// idiv bx +// idiv bx +// The above snippet will be able to fully saturate the ports, while the same +// with ax would issue one uop every `latency(IDIV8r)` cycles. +// +// Some instructions make this harder because they both read and write from +// the same register: +// inc rax +// inc rax +// inc rax +// inc rax +// This has a data dependency from each instruction to the next, limit the +// number of instructions that can be issued in parallel. +// It turns out that this is not a big issue on recent Intel CPUs because they +// have heuristics to balance port pressure. In the snippet above, subsequent +// instructions will end up evenly distributed on {P0,P1,P5,P6}, but some CPUs +// might end up executing them all on P0 (just because they can), or try +// avoiding P5 because it's usually under high pressure from vector +// instructions. +// This issue is even more important for high-latency instructions because +// they increase the idle time of the CPU, e.g. : +// imul rax, rbx +// imul rax, rbx +// imul rax, rbx +// imul rax, rbx +// +// To avoid that, we do the renaming statically by generating as many +// independent exclusive assignments as possible (until all possible registers +// are exhausted) e.g.: +// imul rax, rbx +// imul rcx, rbx +// imul rdx, rbx +// imul r8, rbx +// +// Some instruction even make the above static renaming impossible because +// they implicitly read and write from the same operand, e.g. ADC16rr reads +// and writes from EFLAGS. +// In that case we just use a greedy register assignment and hope for the +// best. + +namespace exegesis { + +static bool hasUnknownOperand(const llvm::MCOperandInfo &OpInfo) { + return OpInfo.OperandType == llvm::MCOI::OPERAND_UNKNOWN; +} + +// FIXME: Handle memory, see PR36905. +static bool hasMemoryOperand(const llvm::MCOperandInfo &OpInfo) { + return OpInfo.OperandType == llvm::MCOI::OPERAND_MEMORY; +} + +llvm::Error +UopsBenchmarkRunner::isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const { + if (llvm::any_of(MCInstrDesc.operands(), hasUnknownOperand)) + return llvm::make_error<BenchmarkFailure>( + "Infeasible : has unknown operands"); + if (llvm::any_of(MCInstrDesc.operands(), hasMemoryOperand)) + return llvm::make_error<BenchmarkFailure>( + "Infeasible : has memory operands"); + return llvm::Error::success(); +} + +// Returns whether this Variable ties Use and Def operands together. +static bool hasTiedOperands(const Instruction &Instr, const Variable &Var) { + bool HasUse = false; + bool HasDef = false; + for (const unsigned OpIndex : Var.TiedOperands) { + const Operand &Op = Instr.Operands[OpIndex]; + if (Op.IsDef) + HasDef = true; + else + HasUse = true; + } + return HasUse && HasDef; +} + +static llvm::SmallVector<const Variable *, 8> +getTiedVariables(const Instruction &Instr) { + llvm::SmallVector<const Variable *, 8> Result; + for (const auto &Var : Instr.Variables) + if (hasTiedOperands(Instr, Var)) + Result.push_back(&Var); + return Result; +} + +static void remove(llvm::BitVector &a, const llvm::BitVector &b) { + assert(a.size() == b.size()); + for (auto I : b.set_bits()) + a.reset(I); +} + +UopsBenchmarkRunner::~UopsBenchmarkRunner() = default; + +llvm::Expected<SnippetPrototype> +UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const { + const auto &InstrDesc = State.getInstrInfo().get(Opcode); + if (auto E = isInfeasible(InstrDesc)) + return std::move(E); + const Instruction Instr(InstrDesc, RATC); + const AliasingConfigurations SelfAliasing(Instr, Instr); + if (SelfAliasing.empty()) { + return generateUnconstrainedPrototype(Instr, "instruction is parallel"); + } + if (SelfAliasing.hasImplicitAliasing()) { + return generateUnconstrainedPrototype(Instr, "instruction is serial"); + } + const auto TiedVariables = getTiedVariables(Instr); + if (!TiedVariables.empty()) { + if (TiedVariables.size() > 1) + return llvm::make_error<llvm::StringError>( + "Infeasible : don't know how to handle several tied variables", + llvm::inconvertibleErrorCode()); + const Variable *Var = TiedVariables.front(); + assert(Var); + assert(!Var->TiedOperands.empty()); + const Operand &Op = Instr.Operands[Var->TiedOperands.front()]; + assert(Op.Tracker); + SnippetPrototype Prototype; + Prototype.Explanation = + "instruction has tied variables using static renaming."; + for (const llvm::MCPhysReg Reg : Op.Tracker->sourceBits().set_bits()) { + Prototype.Snippet.emplace_back(Instr); + Prototype.Snippet.back().getValueFor(*Var) = + llvm::MCOperand::createReg(Reg); + } + return std::move(Prototype); + } + InstructionInstance II(Instr); + // No tied variables, we pick random values for defs. + llvm::BitVector Defs(State.getRegInfo().getNumRegs()); + for (const auto &Op : Instr.Operands) { + if (Op.Tracker && Op.IsExplicit && Op.IsDef) { + auto PossibleRegisters = Op.Tracker->sourceBits(); + remove(PossibleRegisters, RATC.reservedRegisters()); + assert(PossibleRegisters.any() && "No register left to choose from"); + const auto RandomReg = randomBit(PossibleRegisters); + Defs.set(RandomReg); + II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); + } + } + // And pick random use values that are not reserved and don't alias with defs. + const auto DefAliases = getAliasedBits(State.getRegInfo(), Defs); + for (const auto &Op : Instr.Operands) { + if (Op.Tracker && Op.IsExplicit && !Op.IsDef) { + auto PossibleRegisters = Op.Tracker->sourceBits(); + remove(PossibleRegisters, RATC.reservedRegisters()); + remove(PossibleRegisters, DefAliases); + assert(PossibleRegisters.any() && "No register left to choose from"); + const auto RandomReg = randomBit(PossibleRegisters); + II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); + } + } + SnippetPrototype Prototype; + Prototype.Explanation = + "instruction has no tied variables picking Uses different from defs"; + Prototype.Snippet.push_back(std::move(II)); + return std::move(Prototype); +} + +std::vector<BenchmarkMeasure> +UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, + const unsigned NumRepetitions) const { + const auto &SchedModel = State.getSubtargetInfo().getSchedModel(); + + std::vector<BenchmarkMeasure> Result; + for (unsigned ProcResIdx = 1; + ProcResIdx < SchedModel.getNumProcResourceKinds(); ++ProcResIdx) { + const char *const PfmCounters = SchedModel.getExtraProcessorInfo() + .PfmCounters.IssueCounters[ProcResIdx]; + if (!PfmCounters) + continue; + // We sum counts when there are several counters for a single ProcRes + // (e.g. P23 on SandyBridge). + int64_t CounterValue = 0; + llvm::SmallVector<llvm::StringRef, 2> CounterNames; + llvm::StringRef(PfmCounters).split(CounterNames, ','); + for (const auto &CounterName : CounterNames) { + pfm::PerfEvent UopPerfEvent(CounterName); + if (!UopPerfEvent.valid()) + llvm::report_fatal_error( + llvm::Twine("invalid perf event ").concat(PfmCounters)); + pfm::Counter Counter(UopPerfEvent); + Counter.start(); + Function(); + Counter.stop(); + CounterValue += Counter.read(); + } + Result.push_back({llvm::itostr(ProcResIdx), + static_cast<double>(CounterValue) / NumRepetitions, + SchedModel.getProcResource(ProcResIdx)->Name}); + } + return Result; +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/lib/Uops.h b/tools/llvm-exegesis/lib/Uops.h new file mode 100644 index 000000000000..c7b5709f7d9e --- /dev/null +++ b/tools/llvm-exegesis/lib/Uops.h @@ -0,0 +1,41 @@ +//===-- Uops.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 BenchmarkRunner implementation to measure uop decomposition. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H +#define LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H + +#include "BenchmarkRunner.h" + +namespace exegesis { + +class UopsBenchmarkRunner : public BenchmarkRunner { +public: + UopsBenchmarkRunner(const LLVMState &State) + : BenchmarkRunner(State, InstructionBenchmark::Uops) {} + ~UopsBenchmarkRunner() override; + + llvm::Expected<SnippetPrototype> + generatePrototype(unsigned Opcode) const override; + +private: + llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const; + + std::vector<BenchmarkMeasure> + runMeasurements(const ExecutableFunction &EF, + const unsigned NumRepetitions) const override; +}; + +} // namespace exegesis + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H diff --git a/tools/llvm-exegesis/lib/X86/CMakeLists.txt b/tools/llvm-exegesis/lib/X86/CMakeLists.txt new file mode 100644 index 000000000000..912877dd6ed1 --- /dev/null +++ b/tools/llvm-exegesis/lib/X86/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/X86 + ${LLVM_BINARY_DIR}/lib/Target/X86 + ) + +add_library(LLVMExegesisX86 + STATIC + Target.cpp + ) + +llvm_update_compile_flags(LLVMExegesisX86) +llvm_map_components_to_libnames(libs + X86 + Exegesis + ) + +target_link_libraries(LLVMExegesisX86 ${libs}) +set_target_properties(LLVMExegesisX86 PROPERTIES FOLDER "Libraries") diff --git a/tools/llvm-exegesis/lib/X86/LLVMBuild.txt b/tools/llvm-exegesis/lib/X86/LLVMBuild.txt new file mode 100644 index 000000000000..2a7ddca81e7c --- /dev/null +++ b/tools/llvm-exegesis/lib/X86/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-exegesis/lib/X86LLVMBuild.txt ---------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Library +name = ExegesisX86 +parent = Libraries +required_libraries = X86 diff --git a/tools/llvm-exegesis/lib/X86/Target.cpp b/tools/llvm-exegesis/lib/X86/Target.cpp new file mode 100644 index 000000000000..5c417e325f5d --- /dev/null +++ b/tools/llvm-exegesis/lib/X86/Target.cpp @@ -0,0 +1,251 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include "../Target.h" + +#include "../Latency.h" +#include "../Uops.h" +#include "MCTargetDesc/X86BaseInfo.h" +#include "MCTargetDesc/X86MCTargetDesc.h" +#include "X86.h" +#include "X86RegisterInfo.h" +#include "X86Subtarget.h" +#include "llvm/MC/MCInstBuilder.h" + +namespace exegesis { + +namespace { + +// Common code for X86 Uops and Latency runners. +template <typename Impl> class X86BenchmarkRunner : public Impl { + using Impl::Impl; + + llvm::Expected<SnippetPrototype> + generatePrototype(unsigned Opcode) const override { + // Test whether we can generate a snippet for this instruction. + const auto &InstrInfo = this->State.getInstrInfo(); + const auto OpcodeName = InstrInfo.getName(Opcode); + if (OpcodeName.startswith("POPF") || OpcodeName.startswith("PUSHF") || + OpcodeName.startswith("ADJCALLSTACK")) { + return llvm::make_error<BenchmarkFailure>( + "Unsupported opcode: Push/Pop/AdjCallStack"); + } + + // Handle X87. + const auto &InstrDesc = InstrInfo.get(Opcode); + const unsigned FPInstClass = InstrDesc.TSFlags & llvm::X86II::FPTypeMask; + const Instruction Instr(InstrDesc, this->RATC); + switch (FPInstClass) { + case llvm::X86II::NotFP: + break; + case llvm::X86II::ZeroArgFP: + return llvm::make_error<BenchmarkFailure>("Unsupported x87 ZeroArgFP"); + case llvm::X86II::OneArgFP: + return llvm::make_error<BenchmarkFailure>("Unsupported x87 OneArgFP"); + case llvm::X86II::OneArgFPRW: + case llvm::X86II::TwoArgFP: { + // These are instructions like + // - `ST(0) = fsqrt(ST(0))` (OneArgFPRW) + // - `ST(0) = ST(0) + ST(i)` (TwoArgFP) + // They are intrinsically serial and do not modify the state of the stack. + // We generate the same code for latency and uops. + return this->generateSelfAliasingPrototype(Instr); + } + case llvm::X86II::CompareFP: + return Impl::handleCompareFP(Instr); + case llvm::X86II::CondMovFP: + return Impl::handleCondMovFP(Instr); + case llvm::X86II::SpecialFP: + return llvm::make_error<BenchmarkFailure>("Unsupported x87 SpecialFP"); + default: + llvm_unreachable("Unknown FP Type!"); + } + + // Fallback to generic implementation. + return Impl::Base::generatePrototype(Opcode); + } +}; + +class X86LatencyImpl : public LatencyBenchmarkRunner { +protected: + using Base = LatencyBenchmarkRunner; + using Base::Base; + llvm::Expected<SnippetPrototype> + handleCompareFP(const Instruction &Instr) const { + return llvm::make_error<BenchmarkFailure>("Unsupported x87 CompareFP"); + } + llvm::Expected<SnippetPrototype> + handleCondMovFP(const Instruction &Instr) const { + return llvm::make_error<BenchmarkFailure>("Unsupported x87 CondMovFP"); + } +}; + +class X86UopsImpl : public UopsBenchmarkRunner { +protected: + using Base = UopsBenchmarkRunner; + using Base::Base; + // We can compute uops for any FP instruction that does not grow or shrink the + // stack (either do not touch the stack or push as much as they pop). + llvm::Expected<SnippetPrototype> + handleCompareFP(const Instruction &Instr) const { + return generateUnconstrainedPrototype( + Instr, "instruction does not grow/shrink the FP stack"); + } + llvm::Expected<SnippetPrototype> + handleCondMovFP(const Instruction &Instr) const { + return generateUnconstrainedPrototype( + Instr, "instruction does not grow/shrink the FP stack"); + } +}; + +class ExegesisX86Target : public ExegesisTarget { + void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override { + // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F. + PM.add(llvm::createX86FloatingPointStackifierPass()); + } + + std::vector<llvm::MCInst> setRegToConstant(const llvm::MCSubtargetInfo &STI, + unsigned Reg) const override { + // GPR. + if (llvm::X86::GR8RegClass.contains(Reg)) + return {llvm::MCInstBuilder(llvm::X86::MOV8ri).addReg(Reg).addImm(1)}; + if (llvm::X86::GR16RegClass.contains(Reg)) + return {llvm::MCInstBuilder(llvm::X86::MOV16ri).addReg(Reg).addImm(1)}; + if (llvm::X86::GR32RegClass.contains(Reg)) + return {llvm::MCInstBuilder(llvm::X86::MOV32ri).addReg(Reg).addImm(1)}; + if (llvm::X86::GR64RegClass.contains(Reg)) + return {llvm::MCInstBuilder(llvm::X86::MOV64ri32).addReg(Reg).addImm(1)}; + // MMX. + if (llvm::X86::VR64RegClass.contains(Reg)) + return setVectorRegToConstant(Reg, 8, llvm::X86::MMX_MOVQ64rm); + // {X,Y,Z}MM. + if (llvm::X86::VR128XRegClass.contains(Reg)) { + if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) + return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQU32Z128rm); + if (STI.getFeatureBits()[llvm::X86::FeatureAVX]) + return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQUrm); + return setVectorRegToConstant(Reg, 16, llvm::X86::MOVDQUrm); + } + if (llvm::X86::VR256XRegClass.contains(Reg)) { + if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) + return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQU32Z256rm); + return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQUYrm); + } + if (llvm::X86::VR512RegClass.contains(Reg)) + return setVectorRegToConstant(Reg, 64, llvm::X86::VMOVDQU32Zrm); + // X87. + if (llvm::X86::RFP32RegClass.contains(Reg) || + llvm::X86::RFP64RegClass.contains(Reg) || + llvm::X86::RFP80RegClass.contains(Reg)) + return setVectorRegToConstant(Reg, 8, llvm::X86::LD_Fp64m); + if (Reg == llvm::X86::EFLAGS) { + // Set all flags to 0 but the bits that are "reserved and set to 1". + constexpr const uint32_t kImmValue = 0x00007002u; + std::vector<llvm::MCInst> Result; + Result.push_back(allocateStackSpace(8)); + Result.push_back(fillStackSpace(llvm::X86::MOV64mi32, 0, kImmValue)); + Result.push_back(llvm::MCInstBuilder(llvm::X86::POPF64)); // Also pops. + return Result; + } + return {}; + } + + std::unique_ptr<BenchmarkRunner> + createLatencyBenchmarkRunner(const LLVMState &State) const override { + return llvm::make_unique<X86BenchmarkRunner<X86LatencyImpl>>(State); + } + + std::unique_ptr<BenchmarkRunner> + createUopsBenchmarkRunner(const LLVMState &State) const override { + return llvm::make_unique<X86BenchmarkRunner<X86UopsImpl>>(State); + } + + bool matchesArch(llvm::Triple::ArchType Arch) const override { + return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86; + } + +private: + // setRegToConstant() specialized for a vector register of size + // `RegSizeBytes`. `RMOpcode` is the opcode used to do a memory -> vector + // register load. + static std::vector<llvm::MCInst> + setVectorRegToConstant(const unsigned Reg, const unsigned RegSizeBytes, + const unsigned RMOpcode) { + // There is no instruction to directly set XMM, go through memory. + // Since vector values can be interpreted as integers of various sizes (8 + // to 64 bits) as well as floats and double, so we chose an immediate + // value that has set bits for all byte values and is a normal float/ + // double. 0x40404040 is ~32.5 when interpreted as a double and ~3.0f when + // interpreted as a float. + constexpr const uint32_t kImmValue = 0x40404040u; + std::vector<llvm::MCInst> Result; + Result.push_back(allocateStackSpace(RegSizeBytes)); + constexpr const unsigned kMov32NumBytes = 4; + for (unsigned Disp = 0; Disp < RegSizeBytes; Disp += kMov32NumBytes) { + Result.push_back(fillStackSpace(llvm::X86::MOV32mi, Disp, kImmValue)); + } + Result.push_back(loadToReg(Reg, RMOpcode)); + Result.push_back(releaseStackSpace(RegSizeBytes)); + return Result; + } + + // Allocates scratch memory on the stack. + static llvm::MCInst allocateStackSpace(unsigned Bytes) { + return llvm::MCInstBuilder(llvm::X86::SUB64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(Bytes); + } + + // Fills scratch memory at offset `OffsetBytes` with value `Imm`. + static llvm::MCInst fillStackSpace(unsigned MovOpcode, unsigned OffsetBytes, + uint64_t Imm) { + return llvm::MCInstBuilder(MovOpcode) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(OffsetBytes) // Disp + .addReg(0) // Segment + // Immediate. + .addImm(Imm); + } + + // Loads scratch memory into register `Reg` using opcode `RMOpcode`. + static llvm::MCInst loadToReg(unsigned Reg, unsigned RMOpcode) { + return llvm::MCInstBuilder(RMOpcode) + .addReg(Reg) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0); // Segment + } + + // Releases scratch memory. + static llvm::MCInst releaseStackSpace(unsigned Bytes) { + return llvm::MCInstBuilder(llvm::X86::ADD64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(Bytes); + } +}; + +} // namespace + +static ExegesisTarget *getTheExegesisX86Target() { + static ExegesisX86Target Target; + return &Target; +} + +void InitializeX86ExegesisTarget() { + ExegesisTarget::registerTarget(getTheExegesisX86Target()); +} + +} // namespace exegesis diff --git a/tools/llvm-exegesis/llvm-exegesis.cpp b/tools/llvm-exegesis/llvm-exegesis.cpp new file mode 100644 index 000000000000..6b626b0eaa34 --- /dev/null +++ b/tools/llvm-exegesis/llvm-exegesis.cpp @@ -0,0 +1,242 @@ +//===-- llvm-exegesis.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Measures execution properties (latencies/uops) of an instruction. +/// +//===----------------------------------------------------------------------===// + +#include "lib/Analysis.h" +#include "lib/BenchmarkResult.h" +#include "lib/BenchmarkRunner.h" +#include "lib/Clustering.h" +#include "lib/LlvmState.h" +#include "lib/PerfHelper.h" +#include "lib/Target.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Twine.h" +#include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/TargetRegistry.h" +#include "llvm/Support/TargetSelect.h" +#include <algorithm> +#include <random> +#include <string> +#include <unordered_map> + +static llvm::cl::opt<unsigned> + OpcodeIndex("opcode-index", llvm::cl::desc("opcode to measure, by index"), + llvm::cl::init(0)); + +static llvm::cl::opt<std::string> + OpcodeName("opcode-name", llvm::cl::desc("opcode to measure, by name"), + llvm::cl::init("")); + +static llvm::cl::opt<std::string> + BenchmarkFile("benchmarks-file", llvm::cl::desc(""), llvm::cl::init("")); + +static llvm::cl::opt<exegesis::InstructionBenchmark::ModeE> BenchmarkMode( + "mode", llvm::cl::desc("the mode to run"), + llvm::cl::values(clEnumValN(exegesis::InstructionBenchmark::Latency, + "latency", "Instruction Latency"), + clEnumValN(exegesis::InstructionBenchmark::Uops, "uops", + "Uop Decomposition"), + // When not asking for a specific benchmark mode, we'll + // analyse the results. + clEnumValN(exegesis::InstructionBenchmark::Unknown, + "analysis", "Analysis"))); + +static llvm::cl::opt<unsigned> + NumRepetitions("num-repetitions", + llvm::cl::desc("number of time to repeat the asm snippet"), + llvm::cl::init(10000)); + +static llvm::cl::opt<bool> IgnoreInvalidSchedClass( + "ignore-invalid-sched-class", + llvm::cl::desc("ignore instructions that do not define a sched class"), + llvm::cl::init(false)); + +static llvm::cl::opt<unsigned> AnalysisNumPoints( + "analysis-numpoints", + llvm::cl::desc("minimum number of points in an analysis cluster"), + llvm::cl::init(3)); + +static llvm::cl::opt<float> + AnalysisEpsilon("analysis-epsilon", + llvm::cl::desc("dbscan epsilon for analysis clustering"), + llvm::cl::init(0.1)); + +static llvm::cl::opt<std::string> + AnalysisClustersOutputFile("analysis-clusters-output-file", + llvm::cl::desc(""), llvm::cl::init("-")); +static llvm::cl::opt<std::string> + AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file", + llvm::cl::desc(""), llvm::cl::init("-")); + +namespace exegesis { + +static llvm::ExitOnError ExitOnErr; + +#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET +void LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET(); +#endif + +static unsigned GetOpcodeOrDie(const llvm::MCInstrInfo &MCInstrInfo) { + if (OpcodeName.empty() && (OpcodeIndex == 0)) + llvm::report_fatal_error( + "please provide one and only one of 'opcode-index' or 'opcode-name'"); + if (OpcodeIndex > 0) + return OpcodeIndex; + // Resolve opcode name -> opcode. + for (unsigned I = 0, E = MCInstrInfo.getNumOpcodes(); I < E; ++I) + if (MCInstrInfo.getName(I) == OpcodeName) + return I; + llvm::report_fatal_error(llvm::Twine("unknown opcode ").concat(OpcodeName)); +} + +static BenchmarkResultContext +getBenchmarkResultContext(const LLVMState &State) { + BenchmarkResultContext Ctx; + + const llvm::MCInstrInfo &InstrInfo = State.getInstrInfo(); + for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I) + Ctx.addInstrEntry(I, InstrInfo.getName(I).data()); + + const llvm::MCRegisterInfo &RegInfo = State.getRegInfo(); + for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I) + Ctx.addRegEntry(I, RegInfo.getName(I)); + + return Ctx; +} + +void benchmarkMain() { + if (exegesis::pfm::pfmInitialize()) + llvm::report_fatal_error("cannot initialize libpfm"); + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); +#ifdef LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET + LLVM_EXEGESIS_INITIALIZE_NATIVE_TARGET(); +#endif + + const LLVMState State; + const auto Opcode = GetOpcodeOrDie(State.getInstrInfo()); + + // Ignore instructions without a sched class if -ignore-invalid-sched-class is + // passed. + if (IgnoreInvalidSchedClass && + State.getInstrInfo().get(Opcode).getSchedClass() == 0) { + llvm::errs() << "ignoring instruction without sched class\n"; + return; + } + + const std::unique_ptr<BenchmarkRunner> Runner = + State.getExegesisTarget().createBenchmarkRunner(BenchmarkMode, State); + if (!Runner) { + llvm::report_fatal_error("cannot create benchmark runner"); + } + + if (NumRepetitions == 0) + llvm::report_fatal_error("--num-repetitions must be greater than zero"); + + // Write to standard output if file is not set. + if (BenchmarkFile.empty()) + BenchmarkFile = "-"; + + const BenchmarkResultContext Context = getBenchmarkResultContext(State); + std::vector<InstructionBenchmark> Results = + ExitOnErr(Runner->run(Opcode, NumRepetitions)); + for (InstructionBenchmark &Result : Results) + ExitOnErr(Result.writeYaml(Context, BenchmarkFile)); + + exegesis::pfm::pfmTerminate(); +} + +// Prints the results of running analysis pass `Pass` to file `OutputFilename` +// if OutputFilename is non-empty. +template <typename Pass> +static void maybeRunAnalysis(const Analysis &Analyzer, const std::string &Name, + const std::string &OutputFilename) { + if (OutputFilename.empty()) + return; + if (OutputFilename != "-") { + llvm::errs() << "Printing " << Name << " results to file '" + << OutputFilename << "'\n"; + } + std::error_code ErrorCode; + llvm::raw_fd_ostream ClustersOS(OutputFilename, ErrorCode, + llvm::sys::fs::FA_Read | + llvm::sys::fs::FA_Write); + if (ErrorCode) + llvm::report_fatal_error("cannot open out file: " + OutputFilename); + if (auto Err = Analyzer.run<Pass>(ClustersOS)) + llvm::report_fatal_error(std::move(Err)); +} + +static void analysisMain() { + if (BenchmarkFile.empty()) + llvm::report_fatal_error("--benchmarks-file must be set."); + + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetDisassembler(); + // Read benchmarks. + const LLVMState State; + const std::vector<InstructionBenchmark> Points = + ExitOnErr(InstructionBenchmark::readYamls( + getBenchmarkResultContext(State), BenchmarkFile)); + llvm::outs() << "Parsed " << Points.size() << " benchmark points\n"; + if (Points.empty()) { + llvm::errs() << "no benchmarks to analyze\n"; + return; + } + // FIXME: Check that all points have the same triple/cpu. + // FIXME: Merge points from several runs (latency and uops). + + std::string Error; + const auto *TheTarget = + llvm::TargetRegistry::lookupTarget(Points[0].LLVMTriple, Error); + if (!TheTarget) { + llvm::errs() << "unknown target '" << Points[0].LLVMTriple << "'\n"; + return; + } + const auto Clustering = ExitOnErr(InstructionBenchmarkClustering::create( + Points, AnalysisNumPoints, AnalysisEpsilon)); + + const Analysis Analyzer(*TheTarget, Clustering); + + maybeRunAnalysis<Analysis::PrintClusters>(Analyzer, "analysis clusters", + AnalysisClustersOutputFile); + maybeRunAnalysis<Analysis::PrintSchedClassInconsistencies>( + Analyzer, "sched class consistency analysis", + AnalysisInconsistenciesOutputFile); +} + +} // namespace exegesis + +int main(int Argc, char **Argv) { + llvm::cl::ParseCommandLineOptions(Argc, Argv, ""); + + exegesis::ExitOnErr.setExitCodeMapper([](const llvm::Error &Err) { + if (Err.isA<llvm::StringError>()) + return EXIT_SUCCESS; + return EXIT_FAILURE; + }); + + if (BenchmarkMode == exegesis::InstructionBenchmark::Unknown) { + exegesis::analysisMain(); + } else { + exegesis::benchmarkMain(); + } + return EXIT_SUCCESS; +} diff --git a/tools/llvm-extract/llvm-extract.cpp b/tools/llvm-extract/llvm-extract.cpp index c39ffa58fbf7..94aaa2f52eb5 100644 --- a/tools/llvm-extract/llvm-extract.cpp +++ b/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/tools/llvm-isel-fuzzer/CMakeLists.txt b/tools/llvm-isel-fuzzer/CMakeLists.txt index 21f3d14f1749..dab124a64aca 100644 --- a/tools/llvm-isel-fuzzer/CMakeLists.txt +++ b/tools/llvm-isel-fuzzer/CMakeLists.txt @@ -14,5 +14,7 @@ set(LLVM_LINK_COMPONENTS Support Target ) -add_llvm_fuzzer(llvm-isel-fuzzer llvm-isel-fuzzer.cpp - DUMMY_MAIN DummyISelFuzzer.cpp) +add_llvm_fuzzer(llvm-isel-fuzzer + llvm-isel-fuzzer.cpp + DUMMY_MAIN DummyISelFuzzer.cpp + ) diff --git a/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp b/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp index 764134d18b7e..0cab6799f22c 100644 --- a/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp +++ b/tools/llvm-isel-fuzzer/llvm-isel-fuzzer.cpp @@ -15,7 +15,7 @@ #include "llvm/Analysis/TargetLibraryInfo.h" #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" -#include "llvm/CodeGen/CommandFlags.def" +#include "llvm/CodeGen/CommandFlags.inc" #include "llvm/FuzzMutate/FuzzerCLI.h" #include "llvm/FuzzMutate/IRMutator.h" #include "llvm/FuzzMutate/Operations.h" @@ -84,8 +84,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { return 0; LLVMContext Context; - auto M = parseModule(Data, Size, Context); - if (!M || verifyModule(*M, &errs())) { + auto M = parseAndVerify(Data, Size, Context); + if (!M) { errs() << "error: input module is broken!\n"; return 0; } @@ -99,7 +99,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { TargetLibraryInfoImpl TLII(TM->getTargetTriple()); PM.add(new TargetLibraryInfoWrapperPass(TLII)); raw_null_ostream OS; - TM->addPassesToEmitFile(PM, OS, TargetMachine::CGFT_Null); + TM->addPassesToEmitFile(PM, OS, nullptr, TargetMachine::CGFT_Null); PM.run(*M); return 0; diff --git a/tools/llvm-jitlistener/llvm-jitlistener.cpp b/tools/llvm-jitlistener/llvm-jitlistener.cpp index 6b72c17b1fed..693c69d8fa7b 100644 --- a/tools/llvm-jitlistener/llvm-jitlistener.cpp +++ b/tools/llvm-jitlistener/llvm-jitlistener.cpp @@ -24,10 +24,8 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.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/TargetSelect.h" #include "llvm/Support/raw_ostream.h" @@ -178,16 +176,10 @@ InputFilename(cl::Positional, cl::desc("<input IR file>"), cl::Required); 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 jit event listener test utility\n"); JitEventListenerTest Test; - Test.ProcessInput(InputFilename); - return 0; } diff --git a/tools/llvm-link/llvm-link.cpp b/tools/llvm-link/llvm-link.cpp index 50f506aeaae9..b7a888375b3d 100644 --- a/tools/llvm-link/llvm-link.cpp +++ b/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/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 7d71a3e8dfe3..75668a9dd8b6 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/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/tools/llvm-lto2/llvm-lto2.cpp b/tools/llvm-lto2/llvm-lto2.cpp index 70aae0f41507..442973f90209 100644 --- a/tools/llvm-lto2/llvm-lto2.cpp +++ b/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/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt b/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt index 3545d53503b7..fb6befd3c54a 100644 --- a/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt +++ b/tools/llvm-mc-assemble-fuzzer/CMakeLists.txt @@ -7,4 +7,6 @@ set(LLVM_LINK_COMPONENTS MCParser Support ) -add_llvm_fuzzer(llvm-mc-assemble-fuzzer llvm-mc-assemble-fuzzer.cpp) +add_llvm_fuzzer(llvm-mc-assemble-fuzzer + llvm-mc-assemble-fuzzer.cpp + ) diff --git a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp index efbf3bc29232..8aad6ab67423 100644 --- a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp +++ b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp @@ -24,7 +24,7 @@ #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" -#include "llvm/MC/MCTargetOptionsCommandFlags.def" +#include "llvm/MC/MCTargetOptionsCommandFlags.inc" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileUtilities.h" @@ -190,8 +190,8 @@ int AssembleOneInput(const uint8_t *Data, size_t Size) { const char *ProgName = "llvm-mc-fuzzer"; std::unique_ptr<MCSubtargetInfo> STI( TheTarget->createMCSubtargetInfo(TripleName, MCPU, FeaturesStr)); - MCCodeEmitter *CE = nullptr; - MCAsmBackend *MAB = nullptr; + std::unique_ptr<MCCodeEmitter> CE = nullptr; + std::unique_ptr<MCAsmBackend> MAB = nullptr; MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); @@ -202,9 +202,9 @@ int AssembleOneInput(const uint8_t *Data, size_t Size) { std::unique_ptr<MCStreamer> Str; if (FileType == OFT_AssemblyFile) { - Str.reset(TheTarget->createAsmStreamer( - Ctx, std::move(FOut), AsmVerbose, - UseDwarfDirectory, IP, CE, MAB, ShowInst)); + Str.reset(TheTarget->createAsmStreamer(Ctx, std::move(FOut), AsmVerbose, + UseDwarfDirectory, IP, std::move(CE), + std::move(MAB), ShowInst)); } else { assert(FileType == OFT_ObjectFile && "Invalid file type!"); @@ -228,8 +228,7 @@ int AssembleOneInput(const uint8_t *Data, size_t Size) { } MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); - MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU, - MCOptions); + MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); Str.reset(TheTarget->createMCObjectStreamer( TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), *OS, std::unique_ptr<MCCodeEmitter>(CE), *STI, MCOptions.MCRelaxAll, diff --git a/tools/llvm-mc-disassemble-fuzzer/CMakeLists.txt b/tools/llvm-mc-disassemble-fuzzer/CMakeLists.txt index 60b08062d09f..e055e0c6d917 100644 --- a/tools/llvm-mc-disassemble-fuzzer/CMakeLists.txt +++ b/tools/llvm-mc-disassemble-fuzzer/CMakeLists.txt @@ -8,4 +8,6 @@ set(LLVM_LINK_COMPONENTS MCParser Support ) -add_llvm_fuzzer(llvm-mc-disassemble-fuzzer llvm-mc-disassemble-fuzzer.cpp) +add_llvm_fuzzer(llvm-mc-disassemble-fuzzer + llvm-mc-disassemble-fuzzer.cpp + ) diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index e925346eb2d1..f494d02f3bca 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/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(*MRI, TripleName, MCPU, 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)); @@ -588,10 +486,11 @@ int main(int argc, char **argv) { } MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); - MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*MRI, TripleName, MCPU, - MCOptions); + 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)); @@ -599,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) { @@ -623,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/tools/llvm-mca/CMakeLists.txt b/tools/llvm-mca/CMakeLists.txt new file mode 100644 index 000000000000..bb657b8d19fe --- /dev/null +++ b/tools/llvm-mca/CMakeLists.txt @@ -0,0 +1,42 @@ +set(LLVM_LINK_COMPONENTS + AllTargetsAsmPrinters + AllTargetsAsmParsers + AllTargetsDescs + AllTargetsDisassemblers + AllTargetsInfos + MC + MCParser + Support + ) + +add_llvm_tool(llvm-mca + CodeRegion.cpp + Context.cpp + DispatchStage.cpp + DispatchStatistics.cpp + ExecuteStage.cpp + FetchStage.cpp + HWEventListener.cpp + HardwareUnit.cpp + InstrBuilder.cpp + Instruction.cpp + InstructionInfoView.cpp + InstructionTables.cpp + LSUnit.cpp + llvm-mca.cpp + Pipeline.cpp + PipelinePrinter.cpp + RegisterFile.cpp + RegisterFileStatistics.cpp + ResourcePressureView.cpp + RetireControlUnit.cpp + RetireControlUnitStatistics.cpp + RetireStage.cpp + Scheduler.cpp + SchedulerStatistics.cpp + Stage.cpp + Support.cpp + SummaryView.cpp + TimelineView.cpp + View.cpp + ) diff --git a/tools/llvm-mca/CodeRegion.cpp b/tools/llvm-mca/CodeRegion.cpp new file mode 100644 index 000000000000..896865996504 --- /dev/null +++ b/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/tools/llvm-mca/CodeRegion.h b/tools/llvm-mca/CodeRegion.h new file mode 100644 index 000000000000..7f0025e4884c --- /dev/null +++ b/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/tools/llvm-mca/Context.cpp b/tools/llvm-mca/Context.cpp new file mode 100644 index 000000000000..685714e64b92 --- /dev/null +++ b/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/tools/llvm-mca/Context.h b/tools/llvm-mca/Context.h new file mode 100644 index 000000000000..cf483fa7b37d --- /dev/null +++ b/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/tools/llvm-mca/DispatchStage.cpp b/tools/llvm-mca/DispatchStage.cpp new file mode 100644 index 000000000000..be6f1f89be5c --- /dev/null +++ b/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/tools/llvm-mca/DispatchStage.h b/tools/llvm-mca/DispatchStage.h new file mode 100644 index 000000000000..f21789a29c50 --- /dev/null +++ b/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/tools/llvm-mca/DispatchStatistics.cpp b/tools/llvm-mca/DispatchStatistics.cpp new file mode 100644 index 000000000000..4bddbef9a0c8 --- /dev/null +++ b/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/tools/llvm-mca/DispatchStatistics.h b/tools/llvm-mca/DispatchStatistics.h new file mode 100644 index 000000000000..1e389d54766b --- /dev/null +++ b/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/tools/llvm-mca/ExecuteStage.cpp b/tools/llvm-mca/ExecuteStage.cpp new file mode 100644 index 000000000000..437f864b072c --- /dev/null +++ b/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/tools/llvm-mca/ExecuteStage.h b/tools/llvm-mca/ExecuteStage.h new file mode 100644 index 000000000000..4914a9373e7c --- /dev/null +++ b/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/tools/llvm-mca/FetchStage.cpp b/tools/llvm-mca/FetchStage.cpp new file mode 100644 index 000000000000..3da117c0abc1 --- /dev/null +++ b/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/tools/llvm-mca/FetchStage.h b/tools/llvm-mca/FetchStage.h new file mode 100644 index 000000000000..620075d24fea --- /dev/null +++ b/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/tools/llvm-mca/HWEventListener.cpp b/tools/llvm-mca/HWEventListener.cpp new file mode 100644 index 000000000000..f27a04a9a980 --- /dev/null +++ b/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/tools/llvm-mca/HWEventListener.h b/tools/llvm-mca/HWEventListener.h new file mode 100644 index 000000000000..aa3e6dcf19a0 --- /dev/null +++ b/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/tools/llvm-mca/HardwareUnit.cpp b/tools/llvm-mca/HardwareUnit.cpp new file mode 100644 index 000000000000..103cde9afcc8 --- /dev/null +++ b/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/tools/llvm-mca/HardwareUnit.h b/tools/llvm-mca/HardwareUnit.h new file mode 100644 index 000000000000..e8c496ab967a --- /dev/null +++ b/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/tools/llvm-mca/InstrBuilder.cpp b/tools/llvm-mca/InstrBuilder.cpp new file mode 100644 index 000000000000..dbd457196f9d --- /dev/null +++ b/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/tools/llvm-mca/InstrBuilder.h b/tools/llvm-mca/InstrBuilder.h new file mode 100644 index 000000000000..69a53b6fec21 --- /dev/null +++ b/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/tools/llvm-mca/Instruction.cpp b/tools/llvm-mca/Instruction.cpp new file mode 100644 index 000000000000..0c8476705572 --- /dev/null +++ b/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/tools/llvm-mca/Instruction.h b/tools/llvm-mca/Instruction.h new file mode 100644 index 000000000000..ddf5c3a5e33f --- /dev/null +++ b/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/tools/llvm-mca/InstructionInfoView.cpp b/tools/llvm-mca/InstructionInfoView.cpp new file mode 100644 index 000000000000..0e50a96d19c1 --- /dev/null +++ b/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/tools/llvm-mca/InstructionInfoView.h b/tools/llvm-mca/InstructionInfoView.h new file mode 100644 index 000000000000..0770ae3d2b57 --- /dev/null +++ b/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/tools/llvm-mca/InstructionTables.cpp b/tools/llvm-mca/InstructionTables.cpp new file mode 100644 index 000000000000..9b9dbc37fbdb --- /dev/null +++ b/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/tools/llvm-mca/InstructionTables.h b/tools/llvm-mca/InstructionTables.h new file mode 100644 index 000000000000..18e019988430 --- /dev/null +++ b/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/tools/llvm-mca/LLVMBuild.txt b/tools/llvm-mca/LLVMBuild.txt new file mode 100644 index 000000000000..0afcd3129ecd --- /dev/null +++ b/tools/llvm-mca/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-mc/LLVMBuild.txt ----------------------------*- Conf -*--===; +; +; The LLVM Compiler Infrastructure +; +; This file is distributed under the University of Illinois Open Source +; License. See LICENSE.TXT for details. +; +;===------------------------------------------------------------------------===; +; +; This is an LLVMBuild description file for the components in this subdirectory. +; +; For more information on the LLVMBuild system, please see: +; +; http://llvm.org/docs/LLVMBuild.html +; +;===------------------------------------------------------------------------===; + +[component_0] +type = Tool +name = llvm-mca +parent = Tools +required_libraries = MC MCParser Support all-targets diff --git a/tools/llvm-mca/LSUnit.cpp b/tools/llvm-mca/LSUnit.cpp new file mode 100644 index 000000000000..9ee3b6171893 --- /dev/null +++ b/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/tools/llvm-mca/LSUnit.h b/tools/llvm-mca/LSUnit.h new file mode 100644 index 000000000000..817522190589 --- /dev/null +++ b/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/tools/llvm-mca/Pipeline.cpp b/tools/llvm-mca/Pipeline.cpp new file mode 100644 index 000000000000..7c937e7b48b5 --- /dev/null +++ b/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/tools/llvm-mca/Pipeline.h b/tools/llvm-mca/Pipeline.h new file mode 100644 index 000000000000..6916e422be39 --- /dev/null +++ b/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/tools/llvm-mca/PipelinePrinter.cpp b/tools/llvm-mca/PipelinePrinter.cpp new file mode 100644 index 000000000000..c5b1a12b792f --- /dev/null +++ b/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/tools/llvm-mca/PipelinePrinter.h b/tools/llvm-mca/PipelinePrinter.h new file mode 100644 index 000000000000..fe871414418f --- /dev/null +++ b/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/tools/llvm-mca/README.txt b/tools/llvm-mca/README.txt new file mode 100644 index 000000000000..8b1670db0fca --- /dev/null +++ b/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/tools/llvm-mca/RegisterFile.cpp b/tools/llvm-mca/RegisterFile.cpp new file mode 100644 index 000000000000..44de105b8996 --- /dev/null +++ b/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/tools/llvm-mca/RegisterFile.h b/tools/llvm-mca/RegisterFile.h new file mode 100644 index 000000000000..349e9789b6ee --- /dev/null +++ b/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/tools/llvm-mca/RegisterFileStatistics.cpp b/tools/llvm-mca/RegisterFileStatistics.cpp new file mode 100644 index 000000000000..1b07bf9a3b33 --- /dev/null +++ b/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/tools/llvm-mca/RegisterFileStatistics.h b/tools/llvm-mca/RegisterFileStatistics.h new file mode 100644 index 000000000000..cbe816cd3332 --- /dev/null +++ b/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/tools/llvm-mca/ResourcePressureView.cpp b/tools/llvm-mca/ResourcePressureView.cpp new file mode 100644 index 000000000000..fe9d5b7fabc8 --- /dev/null +++ b/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/tools/llvm-mca/ResourcePressureView.h b/tools/llvm-mca/ResourcePressureView.h new file mode 100644 index 000000000000..fe1c6af5e6f6 --- /dev/null +++ b/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/tools/llvm-mca/RetireControlUnit.cpp b/tools/llvm-mca/RetireControlUnit.cpp new file mode 100644 index 000000000000..123058541f28 --- /dev/null +++ b/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/tools/llvm-mca/RetireControlUnit.h b/tools/llvm-mca/RetireControlUnit.h new file mode 100644 index 000000000000..3530ff21ba0d --- /dev/null +++ b/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/tools/llvm-mca/RetireControlUnitStatistics.cpp b/tools/llvm-mca/RetireControlUnitStatistics.cpp new file mode 100644 index 000000000000..edb855e11e84 --- /dev/null +++ b/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/tools/llvm-mca/RetireControlUnitStatistics.h b/tools/llvm-mca/RetireControlUnitStatistics.h new file mode 100644 index 000000000000..1f03e7efe889 --- /dev/null +++ b/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/tools/llvm-mca/RetireStage.cpp b/tools/llvm-mca/RetireStage.cpp new file mode 100644 index 000000000000..386ec54d7ba3 --- /dev/null +++ b/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/tools/llvm-mca/RetireStage.h b/tools/llvm-mca/RetireStage.h new file mode 100644 index 000000000000..8cf672d92c6e --- /dev/null +++ b/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/tools/llvm-mca/Scheduler.cpp b/tools/llvm-mca/Scheduler.cpp new file mode 100644 index 000000000000..975a50e4b638 --- /dev/null +++ b/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/tools/llvm-mca/Scheduler.h b/tools/llvm-mca/Scheduler.h new file mode 100644 index 000000000000..428fbc01707d --- /dev/null +++ b/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/tools/llvm-mca/SchedulerStatistics.cpp b/tools/llvm-mca/SchedulerStatistics.cpp new file mode 100644 index 000000000000..5c6d22a71812 --- /dev/null +++ b/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/tools/llvm-mca/SchedulerStatistics.h b/tools/llvm-mca/SchedulerStatistics.h new file mode 100644 index 000000000000..7383c54a1615 --- /dev/null +++ b/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/tools/llvm-mca/SourceMgr.h b/tools/llvm-mca/SourceMgr.h new file mode 100644 index 000000000000..15a85a69569f --- /dev/null +++ b/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/tools/llvm-mca/Stage.cpp b/tools/llvm-mca/Stage.cpp new file mode 100644 index 000000000000..7ead940e63c1 --- /dev/null +++ b/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/tools/llvm-mca/Stage.h b/tools/llvm-mca/Stage.h new file mode 100644 index 000000000000..9dbdcd89a33b --- /dev/null +++ b/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/tools/llvm-mca/SummaryView.cpp b/tools/llvm-mca/SummaryView.cpp new file mode 100644 index 000000000000..01399055c4fd --- /dev/null +++ b/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/tools/llvm-mca/SummaryView.h b/tools/llvm-mca/SummaryView.h new file mode 100644 index 000000000000..b799ce3aa747 --- /dev/null +++ b/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/tools/llvm-mca/Support.cpp b/tools/llvm-mca/Support.cpp new file mode 100644 index 000000000000..8f6b8a91f38f --- /dev/null +++ b/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/tools/llvm-mca/Support.h b/tools/llvm-mca/Support.h new file mode 100644 index 000000000000..fd8d8b5a23b3 --- /dev/null +++ b/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/tools/llvm-mca/TimelineView.cpp b/tools/llvm-mca/TimelineView.cpp new file mode 100644 index 000000000000..6e75cac0d432 --- /dev/null +++ b/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/tools/llvm-mca/TimelineView.h b/tools/llvm-mca/TimelineView.h new file mode 100644 index 000000000000..e53c23ec1cc2 --- /dev/null +++ b/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/tools/llvm-mca/View.cpp b/tools/llvm-mca/View.cpp new file mode 100644 index 000000000000..390a7aeb3b9d --- /dev/null +++ b/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/tools/llvm-mca/View.h b/tools/llvm-mca/View.h new file mode 100644 index 000000000000..9ba94a5da977 --- /dev/null +++ b/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/tools/llvm-mca/llvm-mca.cpp b/tools/llvm-mca/llvm-mca.cpp new file mode 100644 index 000000000000..2d292f375e6e --- /dev/null +++ b/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/tools/llvm-mcmarkup/CMakeLists.txt b/tools/llvm-mcmarkup/CMakeLists.txt deleted file mode 100644 index 0a51e99f1953..000000000000 --- a/tools/llvm-mcmarkup/CMakeLists.txt +++ /dev/null @@ -1,5 +0,0 @@ -set(LLVM_LINK_COMPONENTS support) - -add_llvm_tool(llvm-mcmarkup - llvm-mcmarkup.cpp - ) diff --git a/tools/llvm-mcmarkup/llvm-mcmarkup.cpp b/tools/llvm-mcmarkup/llvm-mcmarkup.cpp deleted file mode 100644 index 711493ad8307..000000000000 --- a/tools/llvm-mcmarkup/llvm-mcmarkup.cpp +++ /dev/null @@ -1,223 +0,0 @@ -//===-- llvm-mcmarkup.cpp - Parse the MC assembly markup tags -------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// Example simple parser implementation for the MC assembly markup language. -// -//===----------------------------------------------------------------------===// - -#include "llvm/Support/CommandLine.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/MemoryBuffer.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/raw_ostream.h" -using namespace llvm; - -static cl::list<std::string> - InputFilenames(cl::Positional, cl::desc("<input files>"), - cl::ZeroOrMore); -static cl::opt<bool> -DumpTags("dump-tags", cl::desc("List all tags encountered in input")); - -static StringRef ToolName; - -/// Trivial lexer for the markup parser. Input is always handled a character -/// at a time. The lexer just encapsulates EOF and lookahead handling. -class MarkupLexer { - StringRef::const_iterator Start; - StringRef::const_iterator CurPtr; - StringRef::const_iterator End; -public: - MarkupLexer(StringRef Source) - : Start(Source.begin()), CurPtr(Source.begin()), End(Source.end()) {} - // When processing non-markup, input is consumed a character at a time. - bool isEOF() { return CurPtr == End; } - int getNextChar() { - if (CurPtr == End) return EOF; - return *CurPtr++; - } - int peekNextChar() { - if (CurPtr == End) return EOF; - return *CurPtr; - } - StringRef::const_iterator getPosition() const { return CurPtr; } -}; - -/// A markup tag is a name and a (usually empty) list of modifiers. -class MarkupTag { - StringRef Name; - StringRef Modifiers; - SMLoc StartLoc; -public: - MarkupTag(StringRef n, StringRef m, SMLoc Loc) - : Name(n), Modifiers(m), StartLoc(Loc) {} - StringRef getName() const { return Name; } - StringRef getModifiers() const { return Modifiers; } - SMLoc getLoc() const { return StartLoc; } -}; - -/// A simple parser implementation for creating MarkupTags from input text. -class MarkupParser { - MarkupLexer &Lex; - SourceMgr &SM; -public: - MarkupParser(MarkupLexer &lex, SourceMgr &SrcMgr) : Lex(lex), SM(SrcMgr) {} - /// Create a MarkupTag from the current position in the MarkupLexer. - /// The parseTag() method should be called when the lexer has processed - /// the opening '<' character. Input will be consumed up to and including - /// the ':' which terminates the tag open. - MarkupTag parseTag(); - /// Issue a diagnostic and terminate program execution. - void FatalError(SMLoc Loc, StringRef Msg); -}; - -void MarkupParser::FatalError(SMLoc Loc, StringRef Msg) { - SM.PrintMessage(Loc, SourceMgr::DK_Error, Msg); - exit(1); -} - -// Example handler for when a tag is recognized. -static void processStartTag(MarkupTag &Tag) { - // If we're just printing the tags, do that, otherwise do some simple - // colorization. - if (DumpTags) { - outs() << Tag.getName(); - if (Tag.getModifiers().size()) - outs() << " " << Tag.getModifiers(); - outs() << "\n"; - return; - } - - if (!outs().has_colors()) - return; - // Color registers as red and immediates as cyan. Those don't have nested - // tags, so don't bother keeping a stack of colors to reset to. - if (Tag.getName() == "reg") - outs().changeColor(raw_ostream::RED); - else if (Tag.getName() == "imm") - outs().changeColor(raw_ostream::CYAN); -} - -// Example handler for when the end of a tag is recognized. -static void processEndTag(MarkupTag &Tag) { - // If we're printing the tags, there's nothing more to do here. Otherwise, - // set the color back the normal. - if (DumpTags) - return; - if (!outs().has_colors()) - return; - // Just reset to basic white. - outs().changeColor(raw_ostream::WHITE, false); -} - -MarkupTag MarkupParser::parseTag() { - // First off, extract the tag into it's own StringRef so we can look at it - // outside of the context of consuming input. - StringRef::const_iterator Start = Lex.getPosition(); - SMLoc Loc = SMLoc::getFromPointer(Start - 1); - while(Lex.getNextChar() != ':') { - // EOF is an error. - if (Lex.isEOF()) - FatalError(SMLoc::getFromPointer(Start), "unterminated markup tag"); - } - StringRef RawTag(Start, Lex.getPosition() - Start - 1); - std::pair<StringRef, StringRef> SplitTag = RawTag.split(' '); - return MarkupTag(SplitTag.first, SplitTag.second, Loc); -} - -static void parseMCMarkup(StringRef Filename) { - ErrorOr<std::unique_ptr<MemoryBuffer>> BufferPtr = - MemoryBuffer::getFileOrSTDIN(Filename); - if (std::error_code EC = BufferPtr.getError()) { - errs() << ToolName << ": " << EC.message() << '\n'; - return; - } - std::unique_ptr<MemoryBuffer> &Buffer = BufferPtr.get(); - - SourceMgr SrcMgr; - - StringRef InputSource = Buffer->getBuffer(); - - // Tell SrcMgr about this buffer, which is what the parser will pick up. - SrcMgr.AddNewSourceBuffer(std::move(Buffer), SMLoc()); - - MarkupLexer Lex(InputSource); - MarkupParser Parser(Lex, SrcMgr); - - SmallVector<MarkupTag, 4> TagStack; - - for (int CurChar = Lex.getNextChar(); - CurChar != EOF; - CurChar = Lex.getNextChar()) { - switch (CurChar) { - case '<': { - // A "<<" is output as a literal '<' and does not start a markup tag. - if (Lex.peekNextChar() == '<') { - (void)Lex.getNextChar(); - break; - } - // Parse the markup entry. - TagStack.push_back(Parser.parseTag()); - - // Do any special handling for the start of a tag. - processStartTag(TagStack.back()); - continue; - } - case '>': { - SMLoc Loc = SMLoc::getFromPointer(Lex.getPosition() - 1); - // A ">>" is output as a literal '>' and does not end a markup tag. - if (Lex.peekNextChar() == '>') { - (void)Lex.getNextChar(); - break; - } - // Close out the innermost tag. - if (TagStack.empty()) - Parser.FatalError(Loc, "'>' without matching '<'"); - - // Do any special handling for the end of a tag. - processEndTag(TagStack.back()); - - TagStack.pop_back(); - continue; - } - default: - break; - } - // For anything else, just echo the character back out. - if (!DumpTags && CurChar != EOF) - outs() << (char)CurChar; - } - - // If there are any unterminated markup tags, issue diagnostics for them. - while (!TagStack.empty()) { - MarkupTag &Tag = TagStack.back(); - SrcMgr.PrintMessage(Tag.getLoc(), SourceMgr::DK_Error, - "unterminated markup tag"); - TagStack.pop_back(); - } -} - -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 MC markup parser\n"); - - ToolName = argv[0]; - - // If no input files specified, read from stdin. - if (InputFilenames.size() == 0) - InputFilenames.push_back("-"); - - llvm::for_each(InputFilenames, parseMCMarkup); - return 0; -} diff --git a/tools/llvm-modextract/llvm-modextract.cpp b/tools/llvm-modextract/llvm-modextract.cpp index b2d21c23a094..9fd8340505aa 100644 --- a/tools/llvm-modextract/llvm-modextract.cpp +++ b/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/tools/llvm-mt/llvm-mt.cpp b/tools/llvm-mt/llvm-mt.cpp index 944af22cf9c8..1339fd1572ec 100644 --- a/tools/llvm-mt/llvm-mt.cpp +++ b/tools/llvm-mt/llvm-mt.cpp @@ -17,12 +17,14 @@ #include "llvm/Option/Option.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/WindowsManifest/WindowsManifestMerger.h" @@ -61,12 +63,10 @@ class CvtResOptTable : public opt::OptTable { public: CvtResOptTable() : OptTable(InfoTable, true) {} }; - -static ExitOnError ExitOnErr; } // namespace LLVM_ATTRIBUTE_NORETURN void reportError(Twine Msg) { - errs() << "llvm-mt error: " << Msg << "\n"; + WithColor::error(errs(), "llvm-mt") << Msg << '\n'; exit(1); } @@ -86,25 +86,26 @@ void error(Error EC) { }); } -int main(int argc, const char **argv) { - sys::PrintStackTraceOnErrorSignal(argv[0]); - PrettyStackTraceProgram X(argc, argv); - - ExitOnErr.setBanner("llvm-mt: "); - - SmallVector<const char *, 256> argv_buf; - SpecificBumpPtrAllocator<char> ArgAllocator; - ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector( - argv_buf, makeArrayRef(argv, argc), ArgAllocator))); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); CvtResOptTable T; unsigned MAI, MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc); + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); - for (auto *Arg : InputArgs.filtered(OPT_INPUT)) - reportError(Twine("invalid option ") + Arg->getSpelling()); + for (auto *Arg : InputArgs.filtered(OPT_INPUT)) { + auto ArgString = Arg->getAsString(InputArgs); + std::string Diag; + raw_string_ostream OS(Diag); + OS << "invalid option '" << ArgString << "'"; + + std::string Nearest; + if (T.findNearest(ArgString, Nearest) < 2) + OS << ", did you mean '" << Nearest << "'?"; + + reportError(OS.str()); + } for (auto &Arg : InputArgs) { if (Arg->getOption().matches(OPT_unsupported)) { diff --git a/tools/llvm-nm/CMakeLists.txt b/tools/llvm-nm/CMakeLists.txt index f093cc4328ae..07ecce4e4451 100644 --- a/tools/llvm-nm/CMakeLists.txt +++ b/tools/llvm-nm/CMakeLists.txt @@ -2,6 +2,7 @@ set(LLVM_LINK_COMPONENTS AllTargetsAsmParsers AllTargetsDescs AllTargetsInfos + BinaryFormat Core Demangle Object diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index b6ac9c20a946..37c1bf85809e 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/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/tools/llvm-objcopy/CMakeLists.txt b/tools/llvm-objcopy/CMakeLists.txt index 05aa727ab9d8..b0cd66be5b3a 100644 --- a/tools/llvm-objcopy/CMakeLists.txt +++ b/tools/llvm-objcopy/CMakeLists.txt @@ -1,13 +1,29 @@ set(LLVM_LINK_COMPONENTS Object + Option Support MC ) + +set(LLVM_TARGET_DEFINITIONS ObjcopyOpts.td) +tablegen(LLVM ObjcopyOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(ObjcopyOptsTableGen) + +set(LLVM_TARGET_DEFINITIONS StripOpts.td) +tablegen(LLVM StripOpts.inc -gen-opt-parser-defs) +add_public_tablegen_target(StripOptsTableGen) + add_llvm_tool(llvm-objcopy llvm-objcopy.cpp Object.cpp + DEPENDS + ObjcopyOptsTableGen + StripOptsTableGen ) +add_llvm_tool_symlink(llvm-strip llvm-objcopy) + if(LLVM_INSTALL_BINUTILS_SYMLINKS) add_llvm_tool_symlink(objcopy llvm-objcopy) + add_llvm_tool_symlink(strip llvm-objcopy) endif() diff --git a/tools/llvm-objcopy/LLVMBuild.txt b/tools/llvm-objcopy/LLVMBuild.txt index 0a3473a222b0..a26e13932b21 100644 --- a/tools/llvm-objcopy/LLVMBuild.txt +++ b/tools/llvm-objcopy/LLVMBuild.txt @@ -18,4 +18,4 @@ type = Tool name = llvm-objcopy parent = Tools -required_libraries = Object Support MC +required_libraries = Object Option Support MC diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td new file mode 100644 index 000000000000..2af2108d98d3 --- /dev/null +++ b/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/tools/llvm-objcopy/Object.cpp b/tools/llvm-objcopy/Object.cpp index d5dfcac40e4e..7e88f5263a39 100644 --- a/tools/llvm-objcopy/Object.cpp +++ b/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,19 +220,29 @@ 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, - uint16_t Shndx, uint64_t Sz) { + uint8_t Visibility, uint16_t Shndx, + uint64_t Sz) { Symbol Sym; Sym.Name = Name; 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); @@ -154,6 +250,7 @@ void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type, Sym.ShndxType = SYMBOL_SIMPLE_INDEX; } Sym.Value = Value; + Sym.Visibility = Visibility; Sym.Size = Sz; Sym.Index = Symbols.size(); Symbols.emplace_back(llvm::make_unique<Symbol>(Sym)); @@ -161,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) { @@ -198,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) @@ -211,16 +335,22 @@ 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; + Sym->st_other = Symbol->Visibility; Sym->setBinding(Symbol->Binding); Sym->setType(Symbol->Type); Sym->st_shndx = Symbol->getShndx(); @@ -228,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); } } @@ -249,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); } @@ -264,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); @@ -283,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, @@ -342,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) @@ -352,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; @@ -370,62 +621,128 @@ 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, - Sym.getValue(), Sym.st_shndx, Sym.st_size); + Sym.getValue(), Sym.st_other, Sym.st_shndx, Sym.st_size); } } @@ -437,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; @@ -450,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; @@ -465,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; @@ -613,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) { @@ -662,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. @@ -681,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) { @@ -713,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 @@ -722,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 @@ -752,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 @@ -762,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. @@ -870,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. @@ -884,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. @@ -892,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 @@ -926,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/tools/llvm-objcopy/Object.h b/tools/llvm-objcopy/Object.h index b04b0c1a6415..76748d5fc641 100644 --- a/tools/llvm-objcopy/Object.h +++ b/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 { @@ -193,41 +391,80 @@ struct Symbol { uint64_t Size; 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, uint16_t Shndx, - uint64_t Sz); - void addSymbolNames(); + SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, + uint16_t Shndx, uint64_t Sz); + 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; @@ -259,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) @@ -294,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; @@ -328,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)) @@ -343,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/tools/llvm-objcopy/StripOpts.td b/tools/llvm-objcopy/StripOpts.td new file mode 100644 index 000000000000..333b0d288efa --- /dev/null +++ b/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/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp index 20ce93bb40e8..4ccc67cc75db 100644 --- a/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/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/tools/llvm-objcopy/llvm-objcopy.h b/tools/llvm-objcopy/llvm-objcopy.h index 6732e410d8e0..e222b65dc78f 100644 --- a/tools/llvm-objcopy/llvm-objcopy.h +++ b/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/tools/llvm-objdump/CMakeLists.txt b/tools/llvm-objdump/CMakeLists.txt index 177c98166ef1..001fcb399fa6 100644 --- a/tools/llvm-objdump/CMakeLists.txt +++ b/tools/llvm-objdump/CMakeLists.txt @@ -3,6 +3,7 @@ set(LLVM_LINK_COMPONENTS AllTargetsDescs AllTargetsDisassemblers AllTargetsInfos + BinaryFormat CodeGen DebugInfoDWARF DebugInfoPDB diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp index 780d1e9e6111..7ca5d04593ff 100644 --- a/tools/llvm-objdump/COFFDump.cpp +++ b/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/tools/llvm-objdump/ELFDump.cpp b/tools/llvm-objdump/ELFDump.cpp index 7f5fe5a9d3b8..f4d36656a6c4 100644 --- a/tools/llvm-objdump/ELFDump.cpp +++ b/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/tools/llvm-objdump/MachODump.cpp b/tools/llvm-objdump/MachODump.cpp index 9908c2f2d016..bdf80c73b999 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/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/tools/llvm-objdump/WasmDump.cpp b/tools/llvm-objdump/WasmDump.cpp index 0d8ffba6ba45..045002cd4b34 100644 --- a/tools/llvm-objdump/WasmDump.cpp +++ b/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/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 3a9112423cff..8041e6f59940 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/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/tools/llvm-objdump/llvm-objdump.h b/tools/llvm-objdump/llvm-objdump.h index 2fcd506884b1..b2eb6e9d7771 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/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/tools/llvm-opt-fuzzer/CMakeLists.txt b/tools/llvm-opt-fuzzer/CMakeLists.txt index d2fb07f96fc8..97f72c86fa54 100644 --- a/tools/llvm-opt-fuzzer/CMakeLists.txt +++ b/tools/llvm-opt-fuzzer/CMakeLists.txt @@ -8,6 +8,7 @@ set(LLVM_LINK_COMPONENTS Coroutines IPO IRReader + AggressiveInstCombine InstCombine Instrumentation FuzzMutate @@ -21,5 +22,7 @@ set(LLVM_LINK_COMPONENTS Passes ) -add_llvm_fuzzer(llvm-opt-fuzzer llvm-opt-fuzzer.cpp - DUMMY_MAIN DummyOptFuzzer.cpp) +add_llvm_fuzzer(llvm-opt-fuzzer + llvm-opt-fuzzer.cpp + DUMMY_MAIN DummyOptFuzzer.cpp + ) diff --git a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp index 8187bbcea668..98d5428ddd1a 100644 --- a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp +++ b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp @@ -13,7 +13,7 @@ #include "llvm/Bitcode/BitcodeReader.h" #include "llvm/Bitcode/BitcodeWriter.h" -#include "llvm/CodeGen/CommandFlags.def" +#include "llvm/CodeGen/CommandFlags.inc" #include "llvm/FuzzMutate/FuzzerCLI.h" #include "llvm/FuzzMutate/IRMutator.h" #include "llvm/IR/Verifier.h" @@ -57,23 +57,49 @@ extern "C" LLVM_ATTRIBUTE_USED size_t LLVMFuzzerCustomMutator( "IR mutator should have been created during fuzzer initialization"); LLVMContext Context; - auto M = parseModule(Data, Size, Context); - if (!M || verifyModule(*M, &errs())) { + auto M = parseAndVerify(Data, Size, Context); + if (!M) { errs() << "error: mutator input module is broken!\n"; return 0; } Mutator->mutateModule(*M, Seed, Size, MaxSize); -#ifndef NDEBUG if (verifyModule(*M, &errs())) { errs() << "mutation result doesn't pass verification\n"; +#ifndef NDEBUG M->dump(); - abort(); +#endif + // Avoid adding incorrect test cases to the corpus. + return 0; + } + + std::string Buf; + { + raw_string_ostream OS(Buf); + WriteBitcodeToFile(*M, OS); } + if (Buf.size() > MaxSize) + return 0; + + // There are some invariants which are not checked by the verifier in favor + // of having them checked by the parser. They may be considered as bugs in the + // verifier and should be fixed there. However until all of those are covered + // we want to check for them explicitly. Otherwise we will add incorrect input + // to the corpus and this is going to confuse the fuzzer which will start + // exploration of the bitcode reader error handling code. + auto NewM = parseAndVerify( + reinterpret_cast<const uint8_t*>(Buf.data()), Buf.size(), Context); + if (!NewM) { + errs() << "mutator failed to re-read the module\n"; +#ifndef NDEBUG + M->dump(); #endif + return 0; + } - return writeModule(*M, Data, MaxSize); + memcpy(Data, Buf.data(), Buf.size()); + return Buf.size(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { @@ -87,8 +113,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { // LLVMContext Context; - auto M = parseModule(Data, Size, Context); - if (!M || verifyModule(*M, &errs())) { + auto M = parseAndVerify(Data, Size, Context); + if (!M) { errs() << "error: input module is broken!\n"; return 0; } @@ -166,6 +192,7 @@ extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize( initializeAnalysis(Registry); initializeTransformUtils(Registry); initializeInstCombine(Registry); + initializeAggressiveInstCombine(Registry); initializeInstrumentation(Registry); initializeTarget(Registry); diff --git a/tools/llvm-opt-report/OptReport.cpp b/tools/llvm-opt-report/OptReport.cpp index 3c6115db6ac0..aa7966132c28 100644 --- a/tools/llvm-opt-report/OptReport.cpp +++ b/tools/llvm-opt-report/OptReport.cpp @@ -8,26 +8,26 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief This file implements a tool that can parse the YAML optimization +/// This file implements a tool that can parse the YAML optimization /// records and generate an optimization summary annotated source listing /// report. /// //===----------------------------------------------------------------------===// -#include "llvm/Support/CommandLine.h" #include "llvm/Demangle/Demangle.h" +#include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" -#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" #include "llvm/Support/Program.h" -#include "llvm/Support/raw_ostream.h" -#include "llvm/Support/Signals.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" #include <cstdlib> #include <map> #include <set> @@ -274,8 +274,8 @@ static bool readLocationInfo(LocationInfoTy &LocationInfo) { ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFileOrSTDIN(InputFileName); if (std::error_code EC = Buf.getError()) { - errs() << "error: Can't open file " << InputFileName << ": " << - EC.message() << "\n"; + WithColor::error() << "Can't open file " << InputFileName << ": " + << EC.message() << "\n"; return false; } @@ -283,7 +283,7 @@ static bool readLocationInfo(LocationInfoTy &LocationInfo) { yaml::Stream Stream(Buf.get()->getBuffer(), SM); collectLocationInfo(Stream, LocationInfo); - return true; + return true; } static bool writeReport(LocationInfoTy &LocationInfo) { @@ -291,8 +291,8 @@ static bool writeReport(LocationInfoTy &LocationInfo) { llvm::raw_fd_ostream OS(OutputFileName, EC, llvm::sys::fs::F_Text); if (EC) { - errs() << "error: Can't open file " << OutputFileName << ": " << - EC.message() << "\n"; + WithColor::error() << "Can't open file " << OutputFileName << ": " + << EC.message() << "\n"; return false; } @@ -301,8 +301,8 @@ static bool writeReport(LocationInfoTy &LocationInfo) { SmallString<128> FileName(FI.first); if (!InputRelDir.empty()) { if (std::error_code EC = sys::fs::make_absolute(InputRelDir, FileName)) { - errs() << "error: Can't resolve file path to " << FileName << ": " << - EC.message() << "\n"; + WithColor::error() << "Can't resolve file path to " << FileName << ": " + << EC.message() << "\n"; return false; } } @@ -312,8 +312,8 @@ static bool writeReport(LocationInfoTy &LocationInfo) { ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = MemoryBuffer::getFile(FileName); if (std::error_code EC = Buf.getError()) { - errs() << "error: Can't open file " << FileName << ": " << - EC.message() << "\n"; + WithColor::error() << "Can't open file " << FileName << ": " + << EC.message() << "\n"; return false; } @@ -397,7 +397,7 @@ static bool writeReport(LocationInfoTy &LocationInfo) { if (!Printed) OS << FuncName; - } + } OS << ":\n"; } @@ -506,7 +506,7 @@ static bool writeReport(LocationInfoTy &LocationInfo) { } int main(int argc, const char **argv) { - sys::PrintStackTraceOnErrorSignal(argv[0]); + InitLLVM X(argc, argv); cl::HideUnrelatedOptions(OptReportCategory); cl::ParseCommandLineOptions( @@ -523,8 +523,7 @@ int main(int argc, const char **argv) { if (!readLocationInfo(LocationInfo)) return 1; if (!writeReport(LocationInfo)) - return 1; + return 1; return 0; } - diff --git a/tools/llvm-pdbutil/Analyze.cpp b/tools/llvm-pdbutil/Analyze.cpp index 6c603dd8542b..974ab49d9440 100644 --- a/tools/llvm-pdbutil/Analyze.cpp +++ b/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/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt index ab4ce1ac30e0..1ccbfdfc4df4 100644 --- a/tools/llvm-pdbutil/CMakeLists.txt +++ b/tools/llvm-pdbutil/CMakeLists.txt @@ -11,9 +11,8 @@ set(LLVM_LINK_COMPONENTS add_llvm_tool(llvm-pdbutil Analyze.cpp BytesOutputStyle.cpp - Diff.cpp - DiffPrinter.cpp DumpOutputStyle.cpp + ExplainOutputStyle.cpp InputFile.cpp llvm-pdbutil.cpp FormatUtil.cpp diff --git a/tools/llvm-pdbutil/Diff.cpp b/tools/llvm-pdbutil/Diff.cpp deleted file mode 100644 index 286dc51c29b6..000000000000 --- a/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/tools/llvm-pdbutil/Diff.h b/tools/llvm-pdbutil/Diff.h deleted file mode 100644 index 6037576e21bb..000000000000 --- a/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/tools/llvm-pdbutil/DiffPrinter.cpp b/tools/llvm-pdbutil/DiffPrinter.cpp deleted file mode 100644 index dd61cc182593..000000000000 --- a/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/tools/llvm-pdbutil/DiffPrinter.h b/tools/llvm-pdbutil/DiffPrinter.h deleted file mode 100644 index 475747d8dc11..000000000000 --- a/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/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp index dd8436728baf..9e59adc71967 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/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/tools/llvm-pdbutil/DumpOutputStyle.h b/tools/llvm-pdbutil/DumpOutputStyle.h index 3ce2884b2712..e7e9252f2fa9 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/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/tools/llvm-pdbutil/ExplainOutputStyle.cpp b/tools/llvm-pdbutil/ExplainOutputStyle.cpp new file mode 100644 index 000000000000..d16bfa480e1d --- /dev/null +++ b/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/tools/llvm-pdbutil/ExplainOutputStyle.h b/tools/llvm-pdbutil/ExplainOutputStyle.h new file mode 100644 index 000000000000..9a497accb812 --- /dev/null +++ b/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/tools/llvm-pdbutil/InputFile.cpp b/tools/llvm-pdbutil/InputFile.cpp index 8b05381174df..7b5af7e96920 100644 --- a/tools/llvm-pdbutil/InputFile.cpp +++ b/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/tools/llvm-pdbutil/InputFile.h b/tools/llvm-pdbutil/InputFile.h index 8063439133c2..552f3a3b2127 100644 --- a/tools/llvm-pdbutil/InputFile.h +++ b/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/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index 40a0e46efd48..b454ab345456 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/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/tools/llvm-pdbutil/MinimalSymbolDumper.h b/tools/llvm-pdbutil/MinimalSymbolDumper.h index d9e9861d5b30..1c26a85a4eaf 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.h +++ b/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/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp index fae89920e0b8..569bca7490fa 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp b/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp index 10b3d9ee7304..bcdecca81aec 100644 --- a/tools/llvm-pdbutil/PrettyBuiltinDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp b/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp index 66c29fc5d4ee..a572522c8cd7 100644 --- a/tools/llvm-pdbutil/PrettyClassLayoutGraphicalDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/tools/llvm-pdbutil/PrettyCompilandDumper.cpp index 65e8badbc99a..0d99c9b1245c 100644 --- a/tools/llvm-pdbutil/PrettyCompilandDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp b/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp index fc40d90cee96..1270223b1c78 100644 --- a/tools/llvm-pdbutil/PrettyExternalSymbolDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/tools/llvm-pdbutil/PrettyFunctionDumper.cpp index 0bffc73f6c74..177d8a009a2b 100644 --- a/tools/llvm-pdbutil/PrettyFunctionDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/tools/llvm-pdbutil/PrettyTypeDumper.cpp index 0f6086395ad1..663a608fe429 100644 --- a/tools/llvm-pdbutil/PrettyTypeDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/tools/llvm-pdbutil/PrettyTypedefDumper.cpp index ba3b4c8035c5..65443d6bca90 100644 --- a/tools/llvm-pdbutil/PrettyTypedefDumper.cpp +++ b/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/tools/llvm-pdbutil/PrettyVariableDumper.cpp b/tools/llvm-pdbutil/PrettyVariableDumper.cpp index 4884fc8ee5a4..ddac8cf0da4a 100644 --- a/tools/llvm-pdbutil/PrettyVariableDumper.cpp +++ b/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/tools/llvm-pdbutil/StreamUtil.cpp b/tools/llvm-pdbutil/StreamUtil.cpp index 991c99aa8686..367d947d25ee 100644 --- a/tools/llvm-pdbutil/StreamUtil.cpp +++ b/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/tools/llvm-pdbutil/StreamUtil.h b/tools/llvm-pdbutil/StreamUtil.h index 443267ca3290..0e2e80707361 100644 --- a/tools/llvm-pdbutil/StreamUtil.h +++ b/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/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index 089f7256536f..5b0d21f83db7 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/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/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h index 3ce03d5880af..7496adaeb62f 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/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/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 9afd0ae92eae..1a0b9e127bbc 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/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/tools/llvm-rc/CMakeLists.txt b/tools/llvm-rc/CMakeLists.txt index e5c0eb25d7bc..4cadc176691c 100644 --- a/tools/llvm-rc/CMakeLists.txt +++ b/tools/llvm-rc/CMakeLists.txt @@ -11,6 +11,7 @@ add_public_tablegen_target(RcTableGen) add_llvm_tool(llvm-rc llvm-rc.cpp ResourceFileWriter.cpp + ResourceScriptCppFilter.cpp ResourceScriptParser.cpp ResourceScriptStmt.cpp ResourceScriptToken.cpp diff --git a/tools/llvm-rc/Opts.td b/tools/llvm-rc/Opts.td index 9792aa582cbb..11f40f571037 100644 --- a/tools/llvm-rc/Opts.td +++ b/tools/llvm-rc/Opts.td @@ -35,6 +35,9 @@ def H : Flag<[ "/", "-" ], "H">, def DRY_RUN : Flag<[ "/", "-" ], "dry-run">, HelpText<"Don't compile the input; only try to parse it.">; +def CODEPAGE : JoinedOrSeparate<[ "/", "-" ], "C">, + HelpText<"Set the codepage used for input strings.">; + // Unused switches (at least for now). These will stay unimplemented // in an early stage of development and can be ignored. However, we need to // parse them in order to preserve the compatibility with the original tool. @@ -44,7 +47,6 @@ def R : Flag<[ "/", "-" ], "R">; def SL : Flag<[ "/", "-" ], "SL">; // (Codepages support.) -def C : Flag<[ "/", "-" ], "C">; def W : Flag<[ "/", "-" ], "W">; // (Support of MUI and similar.) diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp index f141dc7e3564..4b561940d2ec 100644 --- a/tools/llvm-rc/ResourceFileWriter.cpp +++ b/tools/llvm-rc/ResourceFileWriter.cpp @@ -110,6 +110,18 @@ static bool stripQuotes(StringRef &Str, bool &IsLongString) { return true; } +static UTF16 cp1252ToUnicode(unsigned char C) { + static const UTF16 Map80[] = { + 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, + 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, + 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, + 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, + }; + if (C >= 0x80 && C <= 0x9F) + return Map80[C - 0x80]; + return C; +} + // Describes a way to handle '\0' characters when processing the string. // rc.exe tool sometimes behaves in a weird way in postprocessing. // If the string to be output is equivalent to a C-string (e.g. in MENU @@ -132,10 +144,26 @@ enum class NullHandlingMethod { // * Replace the escape sequences with their processed version. // For identifiers, this is no-op. static Error processString(StringRef Str, NullHandlingMethod NullHandler, - bool &IsLongString, SmallVectorImpl<UTF16> &Result) { + bool &IsLongString, SmallVectorImpl<UTF16> &Result, + int CodePage) { bool IsString = stripQuotes(Str, IsLongString); SmallVector<UTF16, 128> Chars; - convertUTF8ToUTF16String(Str, Chars); + + // Convert the input bytes according to the chosen codepage. + if (CodePage == CpUtf8) { + convertUTF8ToUTF16String(Str, Chars); + } else if (CodePage == CpWin1252) { + for (char C : Str) + Chars.push_back(cp1252ToUnicode((unsigned char)C)); + } else { + // For other, unknown codepages, only allow plain ASCII input. + for (char C : Str) { + if ((unsigned char)C > 0x7F) + return createError("Non-ASCII 8-bit codepoint (" + Twine(C) + + ") can't be interpreted in the current codepage"); + Chars.push_back((unsigned char)C); + } + } if (!IsString) { // It's an identifier if it's not a string. Make all characters uppercase. @@ -157,21 +185,35 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler, if (Char > 0xFF) return createError("Non-8-bit codepoint (" + Twine(Char) + ") can't occur in a user-defined narrow string"); + } + } + Result.push_back(Char); + return Error::success(); + }; + auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error { + if (!IsLongString) { + // Escaped chars in narrow strings have to be interpreted according to + // the chosen code page. + if (Char > 0xFF) + return createError("Non-8-bit escaped char (" + Twine(Char) + + ") can't occur in narrow string"); + if (CodePage == CpUtf8) { + if (Char >= 0x80) + return createError("Unable to interpret single byte (" + Twine(Char) + + ") as UTF-8"); + } else if (CodePage == CpWin1252) { + Char = cp1252ToUnicode(Char); } else { - // In case of narrow non-user strings, Windows RC converts - // [0x80, 0xFF] chars according to the current codepage. - // There is no 'codepage' concept settled in every supported platform, - // so we should reject such inputs. - if (Char > 0x7F && Char <= 0xFF) + // Unknown/unsupported codepage, only allow ASCII input. + if (Char > 0x7F) return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) + ") can't " "occur in a non-Unicode string"); } } - Result.push_back(Char); - return Error::success(); + return AddRes(Char); }; while (Pos < Chars.size()) { @@ -223,7 +265,7 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler, --RemainingChars; } - RETURN_IF_ERROR(AddRes(ReadInt)); + RETURN_IF_ERROR(AddEscapedChar(ReadInt)); continue; } @@ -240,7 +282,7 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler, ++Pos; } - RETURN_IF_ERROR(AddRes(ReadInt)); + RETURN_IF_ERROR(AddEscapedChar(ReadInt)); continue; } @@ -328,7 +370,8 @@ Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) { SmallVector<UTF16, 128> ProcessedString; bool IsLongString; RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull, - IsLongString, ProcessedString)); + IsLongString, ProcessedString, + Params.CodePage)); for (auto Ch : ProcessedString) writeInt<uint16_t>(Ch); if (WriteTerminator) @@ -394,6 +437,10 @@ Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); } +Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeBitmapBody); +} + Error ResourceFileWriter::visitCursorResource(const RCResource *Res) { return handleError(visitIconOrCursorResource(Res), Res); } @@ -411,6 +458,11 @@ Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) { return Error::success(); } +Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) { + ObjectData.Class = Stmt->Value; + return Error::success(); +} + Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { return writeResource(Res, &ResourceFileWriter::writeHTMLBody); } @@ -435,8 +487,8 @@ Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) { if (Iter == BundleData.end()) { // Need to create a bundle. StringTableData.BundleList.push_back(Key); - auto EmplaceResult = - BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData)); + auto EmplaceResult = BundleData.emplace( + Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags)); assert(EmplaceResult.second && "Could not create a bundle"); Iter = EmplaceResult.first; } @@ -509,7 +561,7 @@ Error ResourceFileWriter::writeResource( padStream(sizeof(uint32_t)); object::WinResHeaderSuffix HeaderSuffix{ ulittle32_t(0), // DataVersion; seems to always be 0 - ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo), + ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo), ulittle32_t(ObjectData.VersionInfo), ulittle32_t(ObjectData.Characteristics)}; writeObject(HeaderSuffix); @@ -641,6 +693,29 @@ Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { return Error::success(); } +// --- BitmapResource helpers. --- // + +Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) { + StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc; + bool IsLong; + stripQuotes(Filename, IsLong); + + auto File = loadFile(Filename); + if (!File) + return File.takeError(); + + StringRef Buffer = (*File)->getBuffer(); + + // Skip the 14 byte BITMAPFILEHEADER. + constexpr size_t BITMAPFILEHEADER_size = 14; + if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' || + Buffer[1] != 'M') + return createError("Incorrect bitmap file."); + + *FS << Buffer.substr(BITMAPFILEHEADER_size); + return Error::success(); +} + // --- CursorResource and IconResource helpers. --- // // ICONRESDIR structure. Describes a single icon in resouce group. @@ -715,16 +790,14 @@ public: SingleIconCursorResource(IconCursorGroupType ResourceType, const ResourceDirEntryStart &HeaderEntry, - ArrayRef<uint8_t> ImageData) - : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {} + ArrayRef<uint8_t> ImageData, uint16_t Flags) + : RCResource(Flags), Type(ResourceType), Header(HeaderEntry), + Image(ImageData) {} Twine getResourceTypeName() const override { return "Icon/cursor image"; } IntOrString getResourceType() const override { return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor; } - uint16_t getMemoryFlags() const override { - return MfDiscardable | MfMoveable; - } ResourceKind getKind() const override { return RkSingleCursorOrIconRes; } static bool classof(const RCResource *Res) { return Res->getKind() == RkSingleCursorOrIconRes; @@ -845,46 +918,57 @@ Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) { Reader.setOffset(ItemOffsets[ID]); ArrayRef<uint8_t> Image; RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size)); - SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image); + SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image, + Base->MemoryFlags); SingleRes.setName(IconCursorID + ID); RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes)); } // Now, write all the headers concatenated into a separate resource. for (size_t ID = 0; ID < NumItems; ++ID) { - if (Type == IconCursorGroupType::Icon) { - // rc.exe seems to always set NumPlanes to 1. No idea why it happens. - ItemEntries[ID].Planes = 1; - continue; - } - - // We need to rewrite the cursor headers. + // We need to rewrite the cursor headers, and fetch actual values + // for Planes/BitCount. const auto &OldHeader = ItemEntries[ID]; - ResourceDirEntryStart NewHeader; - NewHeader.Cursor.Width = OldHeader.Icon.Width; - // Each cursor in fact stores two bitmaps, one under another. - // Height provided in cursor definition describes the height of the - // cursor, whereas the value existing in resource definition describes - // the height of the bitmap. Therefore, we need to double this height. - NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; + ResourceDirEntryStart NewHeader = OldHeader; + + if (Type == IconCursorGroupType::Cursor) { + NewHeader.Cursor.Width = OldHeader.Icon.Width; + // Each cursor in fact stores two bitmaps, one under another. + // Height provided in cursor definition describes the height of the + // cursor, whereas the value existing in resource definition describes + // the height of the bitmap. Therefore, we need to double this height. + NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; + + // Two WORDs were written at the beginning of the resource (hotspot + // location). This is reflected in Size field. + NewHeader.Size += 2 * sizeof(uint16_t); + } // Now, we actually need to read the bitmap header to find // the number of planes and the number of bits per pixel. Reader.setOffset(ItemOffsets[ID]); const BitmapInfoHeader *BMPHeader; RETURN_IF_ERROR(Reader.readObject(BMPHeader)); - NewHeader.Planes = BMPHeader->Planes; - NewHeader.BitCount = BMPHeader->BitCount; - - // Two WORDs were written at the beginning of the resource (hotspot - // location). This is reflected in Size field. - NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t); + if (BMPHeader->Size == sizeof(BitmapInfoHeader)) { + NewHeader.Planes = BMPHeader->Planes; + NewHeader.BitCount = BMPHeader->BitCount; + } else { + // A PNG .ico file. + // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473 + // "The image must be in 32bpp" + NewHeader.Planes = 1; + NewHeader.BitCount = 32; + } ItemEntries[ID] = NewHeader; } IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries)); HeaderRes.setName(ResName); + if (Base->MemoryFlags & MfPreload) { + HeaderRes.MemoryFlags |= MfPreload; + HeaderRes.MemoryFlags &= ~MfPure; + } RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes)); return Error::success(); @@ -938,15 +1022,18 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX. if (!IsExtended) { - RETURN_IF_ERROR(checkNumberFits<uint16_t>( - Ctl.ID, "Control ID in simple DIALOG resource")); + // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't + // want to refer to later. + if (Ctl.ID != static_cast<uint32_t>(-1)) + RETURN_IF_ERROR(checkNumberFits<uint16_t>( + Ctl.ID, "Control ID in simple DIALOG resource")); writeInt<uint16_t>(Ctl.ID); } else { writeInt<uint32_t>(Ctl.ID); } // Window class - either 0xFFFF + 16-bit integer or a string. - RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass))); + RETURN_IF_ERROR(writeIntOrString(Ctl.Class)); // Element caption/reference ID. ID is preceded by 0xFFFF. RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID")); @@ -1038,8 +1125,8 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { // think there is no menu attached to the dialog. writeInt<uint16_t>(0); - // Window CLASS field. Not kept here. - writeInt<uint16_t>(0); + // Window CLASS field. + RETURN_IF_ERROR(writeIntOrString(ObjectData.Class)); // Window title or a single word equal to 0. RETURN_IF_ERROR(writeCString(ObjectData.Caption)); @@ -1135,13 +1222,15 @@ public: using BundleType = ResourceFileWriter::StringTableInfo::Bundle; BundleType Bundle; - BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {} + BundleResource(const BundleType &StrBundle) + : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {} IntOrString getResourceType() const override { return 6; } ResourceKind getKind() const override { return RkStringTableBundle; } static bool classof(const RCResource *Res) { return Res->getKind() == RkStringTableBundle; } + Twine getResourceTypeName() const override { return "STRINGTABLE"; } }; Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) { @@ -1168,7 +1257,7 @@ Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) { SmallVector<UTF16, 128> Data; RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()), NullHandlingMethod::CutAtDoubleNull, - IsLongString, Data)); + IsLongString, Data, Params.CodePage)); if (AppendNull && Res->Bundle.Data[ID]) Data.push_back('\0'); RETURN_IF_ERROR( @@ -1215,9 +1304,9 @@ Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) { SmallVector<UTF16, 128> ProcessedString; bool IsLongString; - RETURN_IF_ERROR(processString(Elem.getString(), - NullHandlingMethod::UserResource, - IsLongString, ProcessedString)); + RETURN_IF_ERROR( + processString(Elem.getString(), NullHandlingMethod::UserResource, + IsLongString, ProcessedString, Params.CodePage)); for (auto Ch : ProcessedString) { if (IsLongString) { @@ -1241,6 +1330,7 @@ Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { bool OutputHeader = Blk.Name != ""; uint64_t LengthLoc; + padStream(sizeof(uint32_t)); if (OutputHeader) { LengthLoc = writeInt<uint16_t>(0); writeInt<uint16_t>(0); @@ -1266,7 +1356,6 @@ Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); } - padStream(sizeof(uint32_t)); return Error::success(); } @@ -1296,6 +1385,7 @@ Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) { return createError(Twine("VALUE ") + Val.Key + " cannot contain both strings and integers"); + padStream(sizeof(uint32_t)); auto LengthLoc = writeInt<uint16_t>(0); auto ValLengthLoc = writeInt<uint16_t>(0); writeInt<uint16_t>(HasStrings); @@ -1325,7 +1415,6 @@ Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) { } writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc); - padStream(sizeof(uint32_t)); return Error::success(); } diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h index 20bd4bd73679..4c31d018bde4 100644 --- a/tools/llvm-rc/ResourceFileWriter.h +++ b/tools/llvm-rc/ResourceFileWriter.h @@ -25,15 +25,25 @@ class MemoryBuffer; namespace rc { -struct SearchParams { +enum CodePage { + CpAcp = 0, // The current used codepage. Since there's no such + // notion in LLVM what codepage it actually means, + // this only allows ASCII. + CpWin1252 = 1252, // A codepage where most 8 bit values correspond to + // unicode code points with the same value. + CpUtf8 = 65001, // UTF-8. +}; + +struct WriterParams { std::vector<std::string> Include; // Additional folders to search for files. std::vector<std::string> NoInclude; // Folders to exclude from file search. StringRef InputFilePath; // The full path of the input file. + int CodePage = CpAcp; // The codepage for interpreting characters. }; class ResourceFileWriter : public Visitor { public: - ResourceFileWriter(const SearchParams &Params, + ResourceFileWriter(const WriterParams &Params, std::unique_ptr<raw_fd_ostream> Stream) : Params(Params), FS(std::move(Stream)), IconCursorID(1) { assert(FS && "Output stream needs to be provided to the serializator"); @@ -52,6 +62,7 @@ public: Error visitCaptionStmt(const CaptionStmt *) override; Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; + Error visitClassStmt(const ClassStmt *) override; Error visitFontStmt(const FontStmt *) override; Error visitLanguageStmt(const LanguageResource *) override; Error visitStyleStmt(const StyleStmt *) override; @@ -78,8 +89,11 @@ public: uint32_t Charset; }; Optional<FontInfo> Font; + IntOrString Class; - ObjectInfo() : LanguageInfo(0), Characteristics(0), VersionInfo(0) {} + ObjectInfo() + : LanguageInfo(0), Characteristics(0), VersionInfo(0), + Class(StringRef()) {} } ObjectData; struct StringTableInfo { @@ -90,10 +104,12 @@ public: struct Bundle { std::array<Optional<StringRef>, 16> Data; ObjectInfo DeclTimeInfo; - Bundle(const ObjectInfo &Info) : DeclTimeInfo(Info) {} + uint16_t MemoryFlags; + Bundle(const ObjectInfo &Info, uint16_t Flags) + : DeclTimeInfo(Info), MemoryFlags(Flags) {} }; std::map<BundleKey, Bundle> BundleData; - // Bundles are listed in the order of their first occurence. + // Bundles are listed in the order of their first occurrence. std::vector<BundleKey> BundleList; } StringTableData; @@ -112,6 +128,10 @@ private: bool IsLastItem); Error writeAcceleratorsBody(const RCResource *); + // BitmapResource + Error visitBitmapResource(const RCResource *) override; + Error writeBitmapBody(const RCResource *); + // CursorResource and IconResource Error visitIconOrCursorResource(const RCResource *); Error visitIconOrCursorGroup(const RCResource *); @@ -146,7 +166,7 @@ private: Error writeVersionInfoBlock(const VersionInfoBlock &); Error writeVersionInfoValue(const VersionInfoValue &); - const SearchParams &Params; + const WriterParams &Params; // Output stream handling. std::unique_ptr<raw_fd_ostream> FS; diff --git a/tools/llvm-rc/ResourceScriptCppFilter.cpp b/tools/llvm-rc/ResourceScriptCppFilter.cpp new file mode 100644 index 000000000000..07740f9e8bf0 --- /dev/null +++ b/tools/llvm-rc/ResourceScriptCppFilter.cpp @@ -0,0 +1,112 @@ +//===-- ResourceScriptCppFilter.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 implements an interface defined in ResourceScriptCppFilter.h. +// +//===---------------------------------------------------------------------===// + +#include "ResourceScriptCppFilter.h" +#include "llvm/ADT/StringExtras.h" + +#include <vector> + +using namespace llvm; + +namespace { + +class Filter { +public: + explicit Filter(StringRef Input) : Data(Input), DataLength(Input.size()) {} + + std::string run(); + +private: + // Parse the line, returning whether the line should be included in + // the output. + bool parseLine(StringRef Line); + + bool streamEof() const; + + StringRef Data; + size_t DataLength; + + size_t Pos = 0; + bool Outputting = true; +}; + +std::string Filter::run() { + std::vector<StringRef> Output; + + while (!streamEof() && Pos != StringRef::npos) { + size_t LineStart = Pos; + Pos = Data.find_first_of("\r\n", Pos); + Pos = Data.find_first_not_of("\r\n", Pos); + StringRef Line = Data.take_front(Pos).drop_front(LineStart); + + if (parseLine(Line)) + Output.push_back(Line); + } + + return llvm::join(Output, ""); +} + +bool Filter::parseLine(StringRef Line) { + Line = Line.ltrim(); + + if (!Line.consume_front("#")) { + // A normal content line, filtered according to the current mode. + return Outputting; + } + + // Found a preprocessing directive line. From here on, we always return + // false since the preprocessing directives should be filtered out. + + Line.consume_front("line"); + if (!Line.startswith(" ")) + return false; // Not a line directive (pragma etc). + + // #line 123 "path/file.h" + // # 123 "path/file.h" 1 + + Line = + Line.ltrim(); // There could be multiple spaces after the #line directive + + size_t N; + if (Line.consumeInteger(10, N)) // Returns true to signify an error + return false; + + Line = Line.ltrim(); + + if (!Line.consume_front("\"")) + return false; // Malformed line, no quote found. + + // Split the string at the last quote (in case the path name had + // escaped quotes as well). + Line = Line.rsplit('"').first; + + StringRef Ext = Line.rsplit('.').second; + + if (Ext.equals_lower("h") || Ext.equals_lower("c")) { + Outputting = false; + } else { + Outputting = true; + } + + return false; +} + +bool Filter::streamEof() const { return Pos == DataLength; } + +} // anonymous namespace + +namespace llvm { + +std::string filterCppOutput(StringRef Input) { return Filter(Input).run(); } + +} // namespace llvm diff --git a/tools/llvm-rc/ResourceScriptCppFilter.h b/tools/llvm-rc/ResourceScriptCppFilter.h new file mode 100644 index 000000000000..2e61a075fb8d --- /dev/null +++ b/tools/llvm-rc/ResourceScriptCppFilter.h @@ -0,0 +1,35 @@ +//===-- ResourceScriptCppFilter.h ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===---------------------------------------------------------------------===// +// +// This filters the input to llvm-rc for preprocessor markers, removing +// preprocessing directives that a preprocessor can output or leave behind. +// +// It also filters out any contribution from files named *.h or *.c, based +// on preprocessor line markers. When preprocessing RC files, the included +// headers can leave behind C declarations, that RC doesn't understand. +// Rc.exe simply discards anything that comes from files named *.h or *.h. +// +// https://msdn.microsoft.com/en-us/library/windows/desktop/aa381033(v=vs.85).aspx +// +//===---------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H +#define LLVM_TOOLS_LLVMRC_RESOURCESCRIPTCPPFILTER_H + +#include "llvm/ADT/StringRef.h" + +#include <string> + +namespace llvm { + +std::string filterCppOutput(StringRef Input); + +} // namespace llvm + +#endif diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp index cf1579ee2a11..8cc0b50933c2 100644 --- a/tools/llvm-rc/ResourceScriptParser.cpp +++ b/tools/llvm-rc/ResourceScriptParser.cpp @@ -66,18 +66,22 @@ RCParser::ParseType RCParser::parseSingleResource() { if (TypeToken->equalsLower("ACCELERATORS")) Result = parseAcceleratorsResource(); + else if (TypeToken->equalsLower("BITMAP")) + Result = parseBitmapResource(); else if (TypeToken->equalsLower("CURSOR")) Result = parseCursorResource(); else if (TypeToken->equalsLower("DIALOG")) Result = parseDialogResource(false); else if (TypeToken->equalsLower("DIALOGEX")) Result = parseDialogResource(true); - else if (TypeToken->equalsLower("ICON")) - Result = parseIconResource(); else if (TypeToken->equalsLower("HTML")) Result = parseHTMLResource(); + else if (TypeToken->equalsLower("ICON")) + Result = parseIconResource(); else if (TypeToken->equalsLower("MENU")) Result = parseMenuResource(); + else if (TypeToken->equalsLower("RCDATA")) + Result = parseUserDefinedResource(RkRcData); else if (TypeToken->equalsLower("VERSIONINFO")) Result = parseVersionInfoResource(); else @@ -212,6 +216,12 @@ Expected<StringRef> RCParser::readString() { return read().value(); } +Expected<StringRef> RCParser::readFilename() { + if (!isNextTokenKind(Kind::String) && !isNextTokenKind(Kind::Identifier)) + return getExpectedError("string"); + return read().value(); +} + Expected<StringRef> RCParser::readIdentifier() { if (!isNextTokenKind(Kind::Identifier)) return getExpectedError("identifier"); @@ -319,6 +329,37 @@ Expected<uint32_t> RCParser::parseFlags(ArrayRef<StringRef> FlagDesc, return Result; } +uint16_t RCParser::parseMemoryFlags(uint16_t Flags) { + while (!isEof()) { + const RCToken &Token = look(); + if (Token.kind() != Kind::Identifier) + return Flags; + const StringRef Ident = Token.value(); + if (Ident.equals_lower("PRELOAD")) + Flags |= MfPreload; + else if (Ident.equals_lower("LOADONCALL")) + Flags &= ~MfPreload; + else if (Ident.equals_lower("FIXED")) + Flags &= ~(MfMoveable | MfDiscardable); + else if (Ident.equals_lower("MOVEABLE")) + Flags |= MfMoveable; + else if (Ident.equals_lower("DISCARDABLE")) + Flags |= MfDiscardable | MfMoveable | MfPure; + else if (Ident.equals_lower("PURE")) + Flags |= MfPure; + else if (Ident.equals_lower("IMPURE")) + Flags &= ~(MfPure | MfDiscardable); + else if (Ident.equals_lower("SHARED")) + Flags |= MfPure; + else if (Ident.equals_lower("NONSHARED")) + Flags &= ~(MfPure | MfDiscardable); + else + return Flags; + consume(); + } + return Flags; +} + Expected<OptionalStmtList> RCParser::parseOptionalStatements(OptStmtType StmtsType) { OptionalStmtList Result; @@ -345,6 +386,8 @@ RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { if (StmtsType != OptStmtType::BasicStmt) { if (TypeToken->equals_lower("CAPTION")) return parseCaptionStmt(); + if (TypeToken->equals_lower("CLASS")) + return parseClassStmt(); if (TypeToken->equals_lower("FONT")) return parseFontStmt(StmtsType); if (TypeToken->equals_lower("STYLE")) @@ -362,11 +405,13 @@ RCParser::ParseType RCParser::parseLanguageResource() { } RCParser::ParseType RCParser::parseAcceleratorsResource() { + uint16_t MemoryFlags = + parseMemoryFlags(AcceleratorsResource::getDefaultMemoryFlags()); ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - auto Accels = - llvm::make_unique<AcceleratorsResource>(std::move(*OptStatements)); + auto Accels = llvm::make_unique<AcceleratorsResource>( + std::move(*OptStatements), MemoryFlags); while (!consumeOptionalType(Kind::BlockEnd)) { ASSIGN_OR_RETURN(EventResult, readIntOrString()); @@ -383,11 +428,15 @@ RCParser::ParseType RCParser::parseAcceleratorsResource() { } RCParser::ParseType RCParser::parseCursorResource() { - ASSIGN_OR_RETURN(Arg, readString()); - return llvm::make_unique<CursorResource>(*Arg); + uint16_t MemoryFlags = + parseMemoryFlags(CursorResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return llvm::make_unique<CursorResource>(*Arg, MemoryFlags); } RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { + uint16_t MemoryFlags = + parseMemoryFlags(DialogResource::getDefaultMemoryFlags()); // Dialog resources have the following format of the arguments: // DIALOG: x, y, width, height [opt stmts...] {controls...} // DIALOGEX: x, y, width, height [, helpID] [opt stmts...] {controls...} @@ -410,7 +459,7 @@ RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { auto Dialog = llvm::make_unique<DialogResource>( (*LocResult)[0], (*LocResult)[1], (*LocResult)[2], (*LocResult)[3], - HelpID, std::move(*OptStatements), IsExtended); + HelpID, std::move(*OptStatements), IsExtended, MemoryFlags); while (!consumeOptionalType(Kind::BlockEnd)) { ASSIGN_OR_RETURN(ControlDefResult, parseControl()); @@ -421,12 +470,20 @@ RCParser::ParseType RCParser::parseDialogResource(bool IsExtended) { } RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { + uint16_t MemoryFlags = + parseMemoryFlags(UserDefinedResource::getDefaultMemoryFlags()); if (isEof()) return getExpectedError("filename, '{' or BEGIN"); // Check if this is a file resource. - if (look().kind() == Kind::String) - return llvm::make_unique<UserDefinedResource>(Type, read().value()); + switch (look().kind()) { + case Kind::String: + case Kind::Identifier: + return llvm::make_unique<UserDefinedResource>(Type, read().value(), + MemoryFlags); + default: + break; + } RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); std::vector<IntOrString> Data; @@ -442,14 +499,17 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) { Data.push_back(*Item); } - return llvm::make_unique<UserDefinedResource>(Type, std::move(Data)); + return llvm::make_unique<UserDefinedResource>(Type, std::move(Data), + MemoryFlags); } RCParser::ParseType RCParser::parseVersionInfoResource() { + uint16_t MemoryFlags = + parseMemoryFlags(VersionInfoResource::getDefaultMemoryFlags()); ASSIGN_OR_RETURN(FixedResult, parseVersionInfoFixed()); ASSIGN_OR_RETURN(BlockResult, parseVersionInfoBlockContents(StringRef())); - return llvm::make_unique<VersionInfoResource>(std::move(**BlockResult), - std::move(*FixedResult)); + return llvm::make_unique<VersionInfoResource>( + std::move(**BlockResult), std::move(*FixedResult), MemoryFlags); } Expected<Control> RCParser::parseControl() { @@ -473,32 +533,76 @@ Expected<Control> RCParser::parseControl() { Caption = *CaptionResult; } - ASSIGN_OR_RETURN(Args, readIntsWithCommas(5, 8)); + ASSIGN_OR_RETURN(ID, readInt()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + + IntOrString Class; + Optional<uint32_t> Style; + if (ClassUpper == "CONTROL") { + // CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID] + ASSIGN_OR_RETURN(ClassStr, readString()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Class = *ClassStr; + ASSIGN_OR_RETURN(StyleVal, readInt()); + RETURN_IF_ERROR(consumeType(Kind::Comma)); + Style = *StyleVal; + } else { + Class = CtlInfo->getValue().CtlClass; + } + + // x, y, width, height + ASSIGN_OR_RETURN(Args, readIntsWithCommas(4, 4)); - auto TakeOptArg = [&Args](size_t Id) -> Optional<uint32_t> { - return Args->size() > Id ? (uint32_t)(*Args)[Id] : Optional<uint32_t>(); - }; + if (ClassUpper != "CONTROL") { + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Val, readInt()); + Style = *Val; + } + } - return Control(*ClassResult, Caption, (*Args)[0], (*Args)[1], (*Args)[2], - (*Args)[3], (*Args)[4], TakeOptArg(5), TakeOptArg(6), - TakeOptArg(7)); + Optional<uint32_t> ExStyle; + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Val, readInt()); + ExStyle = *Val; + } + Optional<uint32_t> HelpID; + if (consumeOptionalType(Kind::Comma)) { + ASSIGN_OR_RETURN(Val, readInt()); + HelpID = *Val; + } + + return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], + (*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class); +} + +RCParser::ParseType RCParser::parseBitmapResource() { + uint16_t MemoryFlags = + parseMemoryFlags(BitmapResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return llvm::make_unique<BitmapResource>(*Arg, MemoryFlags); } RCParser::ParseType RCParser::parseIconResource() { - ASSIGN_OR_RETURN(Arg, readString()); - return llvm::make_unique<IconResource>(*Arg); + uint16_t MemoryFlags = + parseMemoryFlags(IconResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return llvm::make_unique<IconResource>(*Arg, MemoryFlags); } RCParser::ParseType RCParser::parseHTMLResource() { - ASSIGN_OR_RETURN(Arg, readString()); - return llvm::make_unique<HTMLResource>(*Arg); + uint16_t MemoryFlags = + parseMemoryFlags(HTMLResource::getDefaultMemoryFlags()); + ASSIGN_OR_RETURN(Arg, readFilename()); + return llvm::make_unique<HTMLResource>(*Arg, MemoryFlags); } RCParser::ParseType RCParser::parseMenuResource() { + uint16_t MemoryFlags = + parseMemoryFlags(MenuResource::getDefaultMemoryFlags()); ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); ASSIGN_OR_RETURN(Items, parseMenuItemsList()); return llvm::make_unique<MenuResource>(std::move(*OptStatements), - std::move(*Items)); + std::move(*Items), MemoryFlags); } Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { @@ -561,11 +665,13 @@ Expected<MenuDefinitionList> RCParser::parseMenuItemsList() { } RCParser::ParseType RCParser::parseStringTableResource() { + uint16_t MemoryFlags = + parseMemoryFlags(StringTableResource::getDefaultMemoryFlags()); ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements()); RETURN_IF_ERROR(consumeType(Kind::BlockBegin)); - auto Table = - llvm::make_unique<StringTableResource>(std::move(*OptStatements)); + auto Table = llvm::make_unique<StringTableResource>(std::move(*OptStatements), + MemoryFlags); // Read strings until we reach the end of the block. while (!consumeOptionalType(Kind::BlockEnd)) { @@ -573,6 +679,7 @@ RCParser::ParseType RCParser::parseStringTableResource() { // Some examples in documentation suggest that there might be a comma in // between, however we strictly adhere to the single statement definition. ASSIGN_OR_RETURN(IDResult, readInt()); + consumeOptionalType(Kind::Comma); ASSIGN_OR_RETURN(StrResult, readString()); Table->addString(*IDResult, *StrResult); } @@ -674,6 +781,11 @@ RCParser::ParseOptionType RCParser::parseCaptionStmt() { return llvm::make_unique<CaptionStmt>(*Arg); } +RCParser::ParseOptionType RCParser::parseClassStmt() { + ASSIGN_OR_RETURN(Arg, readIntOrString()); + return llvm::make_unique<ClassStmt>(*Arg); +} + RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) { assert(DialogType != OptStmtType::BasicStmt); diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h index 84fdfd5a5860..3dd110f7e384 100644 --- a/tools/llvm-rc/ResourceScriptParser.h +++ b/tools/llvm-rc/ResourceScriptParser.h @@ -84,6 +84,7 @@ private: Expected<RCInt> readInt(); // Parse an integer. Expected<StringRef> readString(); // Parse a string. Expected<StringRef> readIdentifier(); // Parse an identifier. + Expected<StringRef> readFilename(); // Parse a filename. Expected<IntOrString> readIntOrString(); // Parse an integer or a string. Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier. @@ -128,6 +129,8 @@ private: // msdn.microsoft.com/en-us/library/windows/desktop/aa381002(v=vs.85).aspx enum class OptStmtType { BasicStmt, DialogStmt, DialogExStmt }; + uint16_t parseMemoryFlags(uint16_t DefaultFlags); + Expected<OptionalStmtList> parseOptionalStatements(OptStmtType StmtsType = OptStmtType::BasicStmt); @@ -138,6 +141,7 @@ private: // Top-level resource parsers. ParseType parseLanguageResource(); ParseType parseAcceleratorsResource(); + ParseType parseBitmapResource(); ParseType parseCursorResource(); ParseType parseDialogResource(bool IsExtended); ParseType parseIconResource(); @@ -167,6 +171,7 @@ private: ParseOptionType parseCharacteristicsStmt(); ParseOptionType parseVersionStmt(); ParseOptionType parseCaptionStmt(); + ParseOptionType parseClassStmt(); ParseOptionType parseFontStmt(OptStmtType DialogType); ParseOptionType parseStyleStmt(); diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp index 42505cc76d0e..728c24b36693 100644 --- a/tools/llvm-rc/ResourceScriptStmt.cpp +++ b/tools/llvm-rc/ResourceScriptStmt.cpp @@ -57,6 +57,10 @@ raw_ostream &AcceleratorsResource::log(raw_ostream &OS) const { return OS; } +raw_ostream &BitmapResource::log(raw_ostream &OS) const { + return OS << "Bitmap (" << ResName << "): " << BitmapLoc << "\n"; +} + raw_ostream &CursorResource::log(raw_ostream &OS) const { return OS << "Cursor (" << ResName << "): " << CursorLoc << "\n"; } @@ -124,9 +128,22 @@ const StringMap<Control::CtlInfo> Control::SupportedCtls = { {"LTEXT", CtlInfo{0x50020000, ClsStatic, true}}, {"CTEXT", CtlInfo{0x50020001, ClsStatic, true}}, {"RTEXT", CtlInfo{0x50020002, ClsStatic, true}}, + {"ICON", CtlInfo{0x50000003, ClsStatic, true}}, {"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}}, {"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}}, + {"AUTO3STATE", CtlInfo{0x50010006, ClsButton, true}}, + {"AUTOCHECKBOX", CtlInfo{0x50010003, ClsButton, true}}, + {"AUTORADIOBUTTON", CtlInfo{0x50000009, ClsButton, true}}, + {"CHECKBOX", CtlInfo{0x50010002, ClsButton, true}}, + {"GROUPBOX", CtlInfo{0x50000007, ClsButton, true}}, + {"RADIOBUTTON", CtlInfo{0x50000004, ClsButton, true}}, + {"STATE3", CtlInfo{0x50010005, ClsButton, true}}, + {"PUSHBOX", CtlInfo{0x5001000A, ClsButton, true}}, {"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}}, + {"COMBOBOX", CtlInfo{0x50000000, ClsComboBox, false}}, + {"LISTBOX", CtlInfo{0x50800001, ClsListBox, false}}, + {"SCROLLBAR", CtlInfo{0x50000000, ClsScrollBar, false}}, + {"CONTROL", CtlInfo{0x50000000, 0, true}}, }; raw_ostream &Control::log(raw_ostream &OS) const { @@ -250,6 +267,10 @@ raw_ostream &CaptionStmt::log(raw_ostream &OS) const { return OS << "Caption: " << Value << "\n"; } +raw_ostream &ClassStmt::log(raw_ostream &OS) const { + return OS << "Class: " << Value << "\n"; +} + raw_ostream &FontStmt::log(raw_ostream &OS) const { OS << "Font: size = " << Size << ", face = " << Name << ", weight = " << Weight; diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index e44120b770f3..2071ac6a9a3a 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -121,11 +121,13 @@ enum ResourceKind { // kind is equal to this type ID. RkNull = 0, RkSingleCursor = 1, + RkBitmap = 2, RkSingleIcon = 3, RkMenu = 4, RkDialog = 5, RkStringTableBundle = 6, RkAccelerators = 9, + RkRcData = 10, RkCursorGroup = 12, RkIconGroup = 14, RkVersionInfo = 16, @@ -158,10 +160,13 @@ enum MemoryFlags { class RCResource { public: IntOrString ResName; + uint16_t MemoryFlags = getDefaultMemoryFlags(); void setName(const IntOrString &Name) { ResName = Name; } virtual raw_ostream &log(raw_ostream &OS) const { return OS << "Base statement\n"; }; + RCResource() {} + RCResource(uint16_t Flags) : MemoryFlags(Flags) {} virtual ~RCResource() {} virtual Error visit(Visitor *) const { @@ -173,9 +178,10 @@ public: virtual Error applyStmts(Visitor *) const { return Error::success(); } // By default, memory flags are DISCARDABLE | PURE | MOVEABLE. - virtual uint16_t getMemoryFlags() const { + static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfPure | MfMoveable; } + virtual ResourceKind getKind() const { return RkBase; } static bool classof(const RCResource *Res) { return true; } @@ -191,13 +197,13 @@ public: // characteristics are equal to 0. class NullResource : public RCResource { public: + NullResource() : RCResource(0) {} raw_ostream &log(raw_ostream &OS) const override { return OS << "Null resource\n"; } Error visit(Visitor *V) const override { return V->visitNullResource(this); } IntOrString getResourceType() const override { return 0; } Twine getResourceTypeName() const override { return "(NULL)"; } - uint16_t getMemoryFlags() const override { return 0; } }; // Optional statement base. All such statements should derive from this base. @@ -226,8 +232,10 @@ class OptStatementsRCResource : public RCResource { public: std::unique_ptr<OptionalStmtList> OptStatements; - OptStatementsRCResource(OptionalStmtList &&Stmts) - : OptStatements(llvm::make_unique<OptionalStmtList>(std::move(Stmts))) {} + OptStatementsRCResource(OptionalStmtList &&Stmts, + uint16_t Flags = RCResource::getDefaultMemoryFlags()) + : RCResource(Flags), + OptStatements(llvm::make_unique<OptionalStmtList>(std::move(Stmts))) {} virtual Error applyStmts(Visitor *V) const { return OptStatements->visit(V); } }; @@ -282,18 +290,18 @@ public: static uint32_t OptionsFlags[NumFlags]; }; + AcceleratorsResource(OptionalStmtList &&List, uint16_t Flags) + : OptStatementsRCResource(std::move(List), Flags) {} + std::vector<Accelerator> Accelerators; - using OptStatementsRCResource::OptStatementsRCResource; void addAccelerator(IntOrString Event, uint32_t Id, uint16_t Flags) { Accelerators.push_back(Accelerator{Event, Id, Flags}); } raw_ostream &log(raw_ostream &) const override; IntOrString getResourceType() const override { return RkAccelerators; } - uint16_t getMemoryFlags() const override { - return MfPure | MfMoveable; - } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } Twine getResourceTypeName() const override { return "ACCELERATORS"; } Error visit(Visitor *V) const override { @@ -305,6 +313,30 @@ public: } }; +// BITMAP resource. Represents a bitmap (".bmp") file. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380680(v=vs.85).aspx +class BitmapResource : public RCResource { +public: + StringRef BitmapLoc; + + BitmapResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), BitmapLoc(Location) {} + raw_ostream &log(raw_ostream &) const override; + + IntOrString getResourceType() const override { return RkBitmap; } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } + + Twine getResourceTypeName() const override { return "BITMAP"; } + Error visit(Visitor *V) const override { + return V->visitBitmapResource(this); + } + ResourceKind getKind() const override { return RkBitmap; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkBitmap; + } +}; + // CURSOR resource. Represents a single cursor (".cur") file. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx @@ -312,10 +344,12 @@ class CursorResource : public RCResource { public: StringRef CursorLoc; - CursorResource(StringRef Location) : CursorLoc(Location) {} + CursorResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), CursorLoc(Location) {} raw_ostream &log(raw_ostream &) const override; Twine getResourceTypeName() const override { return "CURSOR"; } + static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; } Error visit(Visitor *V) const override { return V->visitCursorResource(this); } @@ -332,10 +366,12 @@ class IconResource : public RCResource { public: StringRef IconLoc; - IconResource(StringRef Location) : IconLoc(Location) {} + IconResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), IconLoc(Location) {} raw_ostream &log(raw_ostream &) const override; Twine getResourceTypeName() const override { return "ICON"; } + static uint16_t getDefaultMemoryFlags() { return MfDiscardable | MfMoveable; } Error visit(Visitor *V) const override { return V->visitIconResource(this); } ResourceKind getKind() const override { return RkIcon; } static bool classof(const RCResource *Res) { @@ -352,13 +388,14 @@ class HTMLResource : public RCResource { public: StringRef HTMLLoc; - HTMLResource(StringRef Location) : HTMLLoc(Location) {} + HTMLResource(StringRef Location, uint16_t Flags) + : RCResource(Flags), HTMLLoc(Location) {} raw_ostream &log(raw_ostream &) const override; Error visit(Visitor *V) const override { return V->visitHTMLResource(this); } // Curiously, file resources don't have DISCARDABLE flag set. - uint16_t getMemoryFlags() const override { return MfPure | MfMoveable; } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } IntOrString getResourceType() const override { return RkHTML; } Twine getResourceTypeName() const override { return "HTML"; } ResourceKind getKind() const override { return RkHTML; } @@ -472,8 +509,9 @@ class MenuResource : public OptStatementsRCResource { public: MenuDefinitionList Elements; - MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items) - : OptStatementsRCResource(std::move(OptStmts)), + MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items, + uint16_t Flags) + : OptStatementsRCResource(std::move(OptStmts), Flags), Elements(std::move(Items)) {} raw_ostream &log(raw_ostream &) const override; @@ -493,7 +531,8 @@ class StringTableResource : public OptStatementsRCResource { public: std::vector<std::pair<uint32_t, StringRef>> Table; - using OptStatementsRCResource::OptStatementsRCResource; + StringTableResource(OptionalStmtList &&List, uint16_t Flags) + : OptStatementsRCResource(std::move(List), Flags) {} void addString(uint32_t ID, StringRef String) { Table.emplace_back(ID, String); } @@ -518,6 +557,7 @@ public: IntOrString Title; uint32_t ID, X, Y, Width, Height; Optional<uint32_t> Style, ExtStyle, HelpID; + IntOrString Class; // Control classes as described in DLGITEMTEMPLATEEX documentation. // @@ -541,10 +581,10 @@ public: Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID, uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight, Optional<uint32_t> ItemStyle, Optional<uint32_t> ExtItemStyle, - Optional<uint32_t> CtlHelpID) + Optional<uint32_t> CtlHelpID, IntOrString CtlClass) : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY), Width(ItemWidth), Height(ItemHeight), Style(ItemStyle), - ExtStyle(ExtItemStyle), HelpID(CtlHelpID) {} + ExtStyle(ExtItemStyle), HelpID(CtlHelpID), Class(CtlClass) {} static const StringMap<CtlInfo> SupportedCtls; @@ -562,8 +602,8 @@ public: DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth, uint32_t DlgHeight, uint32_t DlgHelpID, - OptionalStmtList &&OptStmts, bool IsDialogEx) - : OptStatementsRCResource(std::move(OptStmts)), X(PosX), Y(PosY), + OptionalStmtList &&OptStmts, bool IsDialogEx, uint16_t Flags) + : OptStatementsRCResource(std::move(OptStmts), Flags), X(PosX), Y(PosY), Width(DlgWidth), Height(DlgHeight), HelpID(DlgHelpID), IsExtended(IsDialogEx) {} @@ -597,15 +637,19 @@ public: std::vector<IntOrString> Contents; bool IsFileResource; - UserDefinedResource(IntOrString ResourceType, StringRef FileLocation) - : Type(ResourceType), FileLoc(FileLocation), IsFileResource(true) {} - UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data) - : Type(ResourceType), Contents(std::move(Data)), IsFileResource(false) {} + UserDefinedResource(IntOrString ResourceType, StringRef FileLocation, + uint16_t Flags) + : RCResource(Flags), Type(ResourceType), FileLoc(FileLocation), + IsFileResource(true) {} + UserDefinedResource(IntOrString ResourceType, std::vector<IntOrString> &&Data, + uint16_t Flags) + : RCResource(Flags), Type(ResourceType), Contents(std::move(Data)), + IsFileResource(false) {} raw_ostream &log(raw_ostream &) const override; IntOrString getResourceType() const override { return Type; } Twine getResourceTypeName() const override { return Type; } - uint16_t getMemoryFlags() const override { return MfPure | MfMoveable; } + static uint16_t getDefaultMemoryFlags() { return MfPure | MfMoveable; } Error visit(Visitor *V) const override { return V->visitUserDefinedResource(this); @@ -728,12 +772,13 @@ public: VersionInfoFixed FixedData; VersionInfoResource(VersionInfoBlock &&TopLevelBlock, - VersionInfoFixed &&FixedInfo) - : MainBlock(std::move(TopLevelBlock)), FixedData(std::move(FixedInfo)) {} + VersionInfoFixed &&FixedInfo, uint16_t Flags) + : RCResource(Flags), MainBlock(std::move(TopLevelBlock)), + FixedData(std::move(FixedInfo)) {} raw_ostream &log(raw_ostream &) const override; IntOrString getResourceType() const override { return RkVersionInfo; } - uint16_t getMemoryFlags() const override { return MfMoveable | MfPure; } + static uint16_t getDefaultMemoryFlags() { return MfMoveable | MfPure; } Twine getResourceTypeName() const override { return "VERSIONINFO"; } Error visit(Visitor *V) const override { return V->visitVersionInfoResource(this); @@ -821,6 +866,19 @@ public: Error visit(Visitor *V) const override { return V->visitStyleStmt(this); } }; +// CLASS optional statement. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx +class ClassStmt : public OptionalStmt { +public: + IntOrString Value; + + ClassStmt(IntOrString Class) : Value(Class) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "CLASS"; } + Error visit(Visitor *V) const override { return V->visitClassStmt(this); } +}; + } // namespace rc } // namespace llvm diff --git a/tools/llvm-rc/ResourceScriptToken.cpp b/tools/llvm-rc/ResourceScriptToken.cpp index 7bbf0d16f044..fc0a0e918450 100644 --- a/tools/llvm-rc/ResourceScriptToken.cpp +++ b/tools/llvm-rc/ResourceScriptToken.cpp @@ -281,13 +281,14 @@ bool Tokenizer::canStartIdentifier() const { assert(!streamEof()); const char CurChar = Data[Pos]; - return std::isalpha(CurChar) || CurChar == '_'; + return std::isalpha(CurChar) || CurChar == '_' || CurChar == '.'; } bool Tokenizer::canContinueIdentifier() const { assert(!streamEof()); const char CurChar = Data[Pos]; - return std::isalnum(CurChar) || CurChar == '_'; + return std::isalnum(CurChar) || CurChar == '_' || CurChar == '.' || + CurChar == '/' || CurChar == '\\'; } bool Tokenizer::canStartInt() const { diff --git a/tools/llvm-rc/ResourceScriptToken.h b/tools/llvm-rc/ResourceScriptToken.h index 0f108b50ed10..d1a2ae7a41bb 100644 --- a/tools/llvm-rc/ResourceScriptToken.h +++ b/tools/llvm-rc/ResourceScriptToken.h @@ -11,9 +11,8 @@ // the input data. The list of available tokens is located at // ResourceScriptTokenList.def. // -// Note that the tokenizer does not support comments or preprocessor -// directives. The preprocessor should do its work on the .rc file before -// running llvm-rc. +// Note that the tokenizer does not support preprocessor directives. The +// preprocessor should do its work on the .rc file before running llvm-rc. // // As for now, it is possible to parse ASCII files only (the behavior on // UTF files might be undefined). However, it already consumes UTF-8 BOM, if @@ -68,7 +67,7 @@ private: }; // Tokenize Input. -// In case no error occured, the return value contains +// In case no error occurred, the return value contains // tokens in order they were in the input file. // In case of any error, the return value contains // a textual representation of error. diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h index 530b4a8add2c..53ef3c5cf7df 100644 --- a/tools/llvm-rc/ResourceVisitor.h +++ b/tools/llvm-rc/ResourceVisitor.h @@ -22,6 +22,7 @@ namespace rc { class RCResource; class CaptionStmt; +class ClassStmt; class CharacteristicsStmt; class FontStmt; class LanguageResource; @@ -32,6 +33,7 @@ class Visitor { public: virtual Error visitNullResource(const RCResource *) = 0; virtual Error visitAcceleratorsResource(const RCResource *) = 0; + virtual Error visitBitmapResource(const RCResource *) = 0; virtual Error visitCursorResource(const RCResource *) = 0; virtual Error visitDialogResource(const RCResource *) = 0; virtual Error visitHTMLResource(const RCResource *) = 0; @@ -42,6 +44,7 @@ public: virtual Error visitVersionInfoResource(const RCResource *) = 0; virtual Error visitCaptionStmt(const CaptionStmt *) = 0; + virtual Error visitClassStmt(const ClassStmt *) = 0; virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0; virtual Error visitFontStmt(const FontStmt *) = 0; virtual Error visitLanguageStmt(const LanguageResource *) = 0; diff --git a/tools/llvm-rc/llvm-rc.cpp b/tools/llvm-rc/llvm-rc.cpp index 4a1f52e66dee..0448f4519b4c 100644 --- a/tools/llvm-rc/llvm-rc.cpp +++ b/tools/llvm-rc/llvm-rc.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "ResourceFileWriter.h" +#include "ResourceScriptCppFilter.h" #include "ResourceScriptParser.h" #include "ResourceScriptStmt.h" #include "ResourceScriptToken.h" @@ -21,8 +22,10 @@ #include "llvm/Option/ArgList.h" #include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/ManagedStatic.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Support/PrettyStackTrace.h" #include "llvm/Support/Process.h" #include "llvm/Support/Signals.h" @@ -76,22 +79,13 @@ LLVM_ATTRIBUTE_NORETURN static void fatalError(const Twine &Message) { } // anonymous namespace -int main(int argc_, const char *argv_[]) { - sys::PrintStackTraceOnErrorSignal(argv_[0]); - PrettyStackTraceProgram X(argc_, argv_); - +int main(int Argc, const char **Argv) { + InitLLVM X(Argc, Argv); ExitOnErr.setBanner("llvm-rc: "); - SmallVector<const char *, 256> argv; - SpecificBumpPtrAllocator<char> ArgAllocator; - ExitOnErr(errorCodeToError(sys::Process::GetArgumentVector( - argv, makeArrayRef(argv_, argc_), ArgAllocator))); - - llvm_shutdown_obj Y; - RcOptTable T; unsigned MAI, MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(argv_ + 1, argc_); + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); // The tool prints nothing when invoked with no command-line arguments. @@ -118,7 +112,8 @@ int main(int argc_, const char *argv_[]) { std::unique_ptr<MemoryBuffer> FileContents = std::move(*File); StringRef Contents = FileContents->getBuffer(); - std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(Contents)); + std::string FilteredContents = filterCppOutput(Contents); + std::vector<RCToken> Tokens = ExitOnErr(tokenizeRC(FilteredContents)); if (BeVerbose) { const Twine TokenNames[] = { @@ -137,25 +132,47 @@ int main(int argc_, const char *argv_[]) { } } - SearchParams Params; + WriterParams Params; SmallString<128> InputFile(InArgsInfo[0]); llvm::sys::fs::make_absolute(InputFile); Params.InputFilePath = InputFile; Params.Include = InputArgs.getAllArgValues(OPT_INCLUDE); Params.NoInclude = InputArgs.getAllArgValues(OPT_NOINCLUDE); + if (InputArgs.hasArg(OPT_CODEPAGE)) { + if (InputArgs.getLastArgValue(OPT_CODEPAGE) + .getAsInteger(10, Params.CodePage)) + fatalError("Invalid code page: " + + InputArgs.getLastArgValue(OPT_CODEPAGE)); + switch (Params.CodePage) { + case CpAcp: + case CpWin1252: + case CpUtf8: + break; + default: + fatalError( + "Unsupported code page, only 0, 1252 and 65001 are supported!"); + } + } + std::unique_ptr<ResourceFileWriter> Visitor; bool IsDryRun = InputArgs.hasArg(OPT_DRY_RUN); if (!IsDryRun) { auto OutArgsInfo = InputArgs.getAllArgValues(OPT_FILEOUT); + if (OutArgsInfo.empty()) { + SmallString<128> OutputFile = InputFile; + llvm::sys::path::replace_extension(OutputFile, "res"); + OutArgsInfo.push_back(OutputFile.str()); + } + if (OutArgsInfo.size() != 1) fatalError( - "Exactly one output file should be provided (using /FO flag)."); + "No more than one output file should be provided (using /FO flag)."); std::error_code EC; - auto FOut = - llvm::make_unique<raw_fd_ostream>(OutArgsInfo[0], EC, sys::fs::F_RW); + auto FOut = llvm::make_unique<raw_fd_ostream>( + OutArgsInfo[0], EC, sys::fs::FA_Read | sys::fs::FA_Write); if (EC) fatalError("Error opening output file '" + OutArgsInfo[0] + "': " + EC.message()); diff --git a/tools/llvm-readobj/ARMEHABIPrinter.h b/tools/llvm-readobj/ARMEHABIPrinter.h index 4417aa60fe90..51128f113c4c 100644 --- a/tools/llvm-readobj/ARMEHABIPrinter.h +++ b/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/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp index 1a033b1eb42e..a90840b22c8d 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/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/tools/llvm-readobj/CMakeLists.txt b/tools/llvm-readobj/CMakeLists.txt index dafc9e10cfa1..b0550f340123 100644 --- a/tools/llvm-readobj/CMakeLists.txt +++ b/tools/llvm-readobj/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS DebugInfoCodeView + DebugInfoDWARF Object BinaryFormat Support diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 0e76e75c085d..0ed4ccd09f6f 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/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/tools/llvm-readobj/COFFImportDumper.cpp b/tools/llvm-readobj/COFFImportDumper.cpp index 3b546b3ef508..18010c34f0f3 100644 --- a/tools/llvm-readobj/COFFImportDumper.cpp +++ b/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/tools/llvm-readobj/DwarfCFIEHPrinter.h b/tools/llvm-readobj/DwarfCFIEHPrinter.h new file mode 100644 index 000000000000..5a1eef1d007d --- /dev/null +++ b/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/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 5605eaea7555..645ec2d7e04b 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/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/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index 39e909279937..69ef1556f78d 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -39,6 +39,8 @@ public: void printUnwindInfo() override; void printStackMap() const override; + void printNeededLibraries() override; + // MachO-specific. void printMachODataInCode() override; void printMachOVersionMin() override; @@ -667,12 +669,39 @@ 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() { + ListScope D(W, "NeededLibraries"); + + using LibsTy = std::vector<StringRef>; + LibsTy Libs; + + for (const auto &Command : Obj->load_commands()) { + if (Command.C.cmd == MachO::LC_LOAD_DYLIB || + Command.C.cmd == MachO::LC_ID_DYLIB || + Command.C.cmd == MachO::LC_LOAD_WEAK_DYLIB || + Command.C.cmd == MachO::LC_REEXPORT_DYLIB || + Command.C.cmd == MachO::LC_LAZY_LOAD_DYLIB || + Command.C.cmd == MachO::LC_LOAD_UPWARD_DYLIB) { + MachO::dylib_command Dl = Obj->getDylibIDLoadCommand(Command); + if (Dl.dylib.name < Dl.cmdsize) { + auto *P = static_cast<const char*>(Command.Ptr) + Dl.dylib.name; + Libs.push_back(P); + } + } + } + + std::stable_sort(Libs.begin(), Libs.end()); + + for (const auto &L : Libs) { + outs() << " " << L << "\n"; + } } void MachODumper::printMachODataInCode() { diff --git a/tools/llvm-readobj/ObjDumper.cpp b/tools/llvm-readobj/ObjDumper.cpp index 2a0a90e5cfd5..a725140c9d33 100644 --- a/tools/llvm-readobj/ObjDumper.cpp +++ b/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/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index c5b331d944a2..8c3a7bec73be 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/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/tools/llvm-readobj/StackMapPrinter.h b/tools/llvm-readobj/StackMapPrinter.h index f4ed68e92d78..77a054b178a5 100644 --- a/tools/llvm-readobj/StackMapPrinter.h +++ b/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/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp index 223c1c752469..ce224836225e 100644 --- a/tools/llvm-readobj/WasmDumper.cpp +++ b/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/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index c076582794fe..a7236c02b8ae 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/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/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 840ddbabdc59..374ffd03e13a 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/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/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index b09594622ca9..54db1ec113fc 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/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/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index b2109c8758df..836024eb8101 100644 --- a/tools/llvm-shlib/CMakeLists.txt +++ b/tools/llvm-shlib/CMakeLists.txt @@ -39,6 +39,8 @@ add_llvm_library(LLVM SHARED DISABLE_LLVM_LINK_LLVM_DYLIB SONAME ${SOURCES}) list(REMOVE_DUPLICATES LIB_NAMES) if(("${CMAKE_SYSTEM_NAME}" STREQUAL "Linux") OR (MINGW) OR (HAIKU) OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "FreeBSD") + OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "OpenBSD") + OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "Fuchsia") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "DragonFly") OR ("${CMAKE_SYSTEM_NAME}" STREQUAL "SunOS")) # FIXME: It should be "GNU ld for elf" configure_file( diff --git a/tools/llvm-shlib/simple_version_script.map.in b/tools/llvm-shlib/simple_version_script.map.in index e9515fe78625..d58a8b3abcd3 100644 --- a/tools/llvm-shlib/simple_version_script.map.in +++ b/tools/llvm-shlib/simple_version_script.map.in @@ -1 +1 @@ -LLVM_@LLVM_VERSION_MAJOR@.@LLVM_VERSION_MINOR@ { global: *; }; +LLVM_@LLVM_VERSION_MAJOR@ { global: *; }; diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp index cf35a5795e71..67c81ec9e7cf 100644 --- a/tools/llvm-size/llvm-size.cpp +++ b/tools/llvm-size/llvm-size.cpp @@ -23,10 +23,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/Signals.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <string> @@ -70,7 +68,7 @@ cl::opt<bool> static cl::list<std::string> ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), cl::ZeroOrMore); -bool ArchAll = false; +static bool ArchAll = false; enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; static cl::opt<unsigned int> @@ -95,7 +93,7 @@ static cl::alias TotalSizesShort("t", cl::desc("Short for --totals"), static cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input files>"), cl::ZeroOrMore); -bool HadError = false; +static bool HadError = false; static std::string ToolName; @@ -854,11 +852,7 @@ static void printBerkelyTotals() { } 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 object size dumper\n"); ToolName = argv[0]; diff --git a/tools/llvm-special-case-list-fuzzer/CMakeLists.txt b/tools/llvm-special-case-list-fuzzer/CMakeLists.txt index f4ebf7a8ce7b..381f07b1ae91 100644 --- a/tools/llvm-special-case-list-fuzzer/CMakeLists.txt +++ b/tools/llvm-special-case-list-fuzzer/CMakeLists.txt @@ -5,4 +5,5 @@ set(LLVM_LINK_COMPONENTS add_llvm_fuzzer(llvm-special-case-list-fuzzer special-case-list-fuzzer.cpp - DUMMY_MAIN DummySpecialCaseListFuzzer.cpp) + DUMMY_MAIN DummySpecialCaseListFuzzer.cpp + ) diff --git a/tools/llvm-split/llvm-split.cpp b/tools/llvm-split/llvm-split.cpp index 03625fb4a854..a6204dc156d1 100644 --- a/tools/llvm-split/llvm-split.cpp +++ b/tools/llvm-split/llvm-split.cpp @@ -63,7 +63,7 @@ int main(int argc, char **argv) { } verifyModule(*MPart); - WriteBitcodeToFile(MPart.get(), Out->os()); + WriteBitcodeToFile(*MPart, Out->os()); // Declare success. Out->keep(); diff --git a/tools/llvm-strings/llvm-strings.cpp b/tools/llvm-strings/llvm-strings.cpp index 638d58fce2b7..8e2d213bcc73 100644 --- a/tools/llvm-strings/llvm-strings.cpp +++ b/tools/llvm-strings/llvm-strings.cpp @@ -16,10 +16,9 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.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 <cctype> #include <string> @@ -94,8 +93,7 @@ static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { } int main(int argc, char **argv) { - sys::PrintStackTraceOnErrorSignal(argv[0]); - PrettyStackTraceProgram X(argc, argv); + InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv, "llvm string dumper\n"); if (MinLength == 0) { diff --git a/tools/llvm-symbolizer/CMakeLists.txt b/tools/llvm-symbolizer/CMakeLists.txt index d9b05208afd8..8185c296c501 100644 --- a/tools/llvm-symbolizer/CMakeLists.txt +++ b/tools/llvm-symbolizer/CMakeLists.txt @@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS DebugInfoDWARF DebugInfoPDB + Demangle Object Support Symbolize diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp index b51ec513f23b..6d40a5403504 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/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/tools/llvm-undname/CMakeLists.txt b/tools/llvm-undname/CMakeLists.txt new file mode 100644 index 000000000000..062f0052597e --- /dev/null +++ b/tools/llvm-undname/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Demangle + Support + ) + +add_llvm_tool(llvm-undname + llvm-undname.cpp + ) diff --git a/tools/llvm-mcmarkup/LLVMBuild.txt b/tools/llvm-undname/LLVMBuild.txt index 6423493a543d..6e598f888ff1 100644 --- a/tools/llvm-mcmarkup/LLVMBuild.txt +++ b/tools/llvm-undname/LLVMBuild.txt @@ -1,4 +1,4 @@ -;===- ./tools/llvm-mcmarkup/LLVMBuild.txt ----------------------*- Conf -*--===; +;===- ./tools/llvm-undname/LLVMBuild.txt -----------------------*- Conf -*--===; ; ; The LLVM Compiler Infrastructure ; @@ -17,6 +17,7 @@ [component_0] type = Tool -name = llvm-mcmarkup +name = llvm-undname parent = Tools -required_libraries = Support +required_libraries = Demangle Support + diff --git a/tools/llvm-undname/llvm-undname.cpp b/tools/llvm-undname/llvm-undname.cpp new file mode 100644 index 000000000000..2124ec169455 --- /dev/null +++ b/tools/llvm-undname/llvm-undname.cpp @@ -0,0 +1,82 @@ +//===-- llvm-undname.cpp - Microsoft ABI name undecorator +//------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This utility works like the windows undname utility. It converts mangled +// Microsoft symbol names into pretty C/C++ human-readable names. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" +#include <cstdio> +#include <cstring> +#include <iostream> +#include <string> + +using namespace llvm; + +cl::list<std::string> Symbols(cl::Positional, cl::desc("<input symbols>"), + cl::ZeroOrMore); + +static void demangle(const std::string &S) { + int Status; + char *ResultBuf = microsoftDemangle(S.c_str(), nullptr, nullptr, &Status); + if (Status == llvm::demangle_success) { + outs() << ResultBuf << "\n"; + outs().flush(); + } else { + errs() << "Error: Invalid mangled name\n"; + } + std::free(ResultBuf); +} + +int main(int argc, char **argv) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "llvm-undname\n"); + + if (Symbols.empty()) { + while (true) { + std::string LineStr; + std::getline(std::cin, LineStr); + if (std::cin.eof()) + break; + + StringRef Line(LineStr); + Line = Line.trim(); + if (Line.empty() || Line.startswith("#") || Line.startswith(";")) + continue; + + // If the user is manually typing in these decorated names, don't echo + // them to the terminal a second time. If they're coming from redirected + // input, however, then we should display the input line so that the + // mangled and demangled name can be easily correlated in the output. + if (!sys::Process::StandardInIsUserInput()) { + outs() << Line << "\n"; + outs().flush(); + } + demangle(Line); + outs() << "\n"; + } + } else { + for (StringRef S : Symbols) { + outs() << S << "\n"; + outs().flush(); + demangle(S); + outs() << "\n"; + } + } + + return 0; +} diff --git a/tools/llvm-xray/CMakeLists.txt b/tools/llvm-xray/CMakeLists.txt index b1df67f9b770..66b7f21c43fb 100644 --- a/tools/llvm-xray/CMakeLists.txt +++ b/tools/llvm-xray/CMakeLists.txt @@ -4,18 +4,18 @@ set(LLVM_LINK_COMPONENTS Object Support Symbolize - XRay) + XRay + ) -set(LLVM_XRAY_TOOLS - func-id-helper.cc - xray-account.cc - xray-color-helper.cc - xray-converter.cc - xray-extract.cc - xray-extract.cc - xray-graph.cc - xray-graph-diff.cc - xray-stacks.cc - xray-registry.cc) - -add_llvm_tool(llvm-xray llvm-xray.cc ${LLVM_XRAY_TOOLS}) +add_llvm_tool(llvm-xray + func-id-helper.cpp + llvm-xray.cpp + xray-account.cpp + xray-color-helper.cpp + xray-converter.cpp + xray-extract.cpp + xray-graph-diff.cpp + xray-graph.cpp + xray-registry.cpp + xray-stacks.cpp + ) diff --git a/tools/llvm-xray/func-id-helper.cc b/tools/llvm-xray/func-id-helper.cpp index 3234010695b2..c2bef6ddfb39 100644 --- a/tools/llvm-xray/func-id-helper.cc +++ b/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/tools/llvm-xray/func-id-helper.h b/tools/llvm-xray/func-id-helper.h index 7348a7100b05..3e0780d54f90 100644 --- a/tools/llvm-xray/func-id-helper.h +++ b/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/tools/llvm-xray/llvm-xray.cc b/tools/llvm-xray/llvm-xray.cpp index 17cc9f90dd71..e74628f5025f 100644 --- a/tools/llvm-xray/llvm-xray.cc +++ b/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/tools/llvm-xray/xray-account.cc b/tools/llvm-xray/xray-account.cpp index 7b684aad693d..2776a8888481 100644 --- a/tools/llvm-xray/xray-account.cc +++ b/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/tools/llvm-xray/xray-account.h b/tools/llvm-xray/xray-account.h index cc9ba897e537..5c457f178166 100644 --- a/tools/llvm-xray/xray-account.h +++ b/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/tools/llvm-xray/xray-color-helper.cc b/tools/llvm-xray/xray-color-helper.cpp index 61314d3c766a..78a264b73d8f 100644 --- a/tools/llvm-xray/xray-color-helper.cc +++ b/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/tools/llvm-xray/xray-converter.cc b/tools/llvm-xray/xray-converter.cpp index aa0da55207b3..90e14d0d8896 100644 --- a/tools/llvm-xray/xray-converter.cc +++ b/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/tools/llvm-xray/xray-extract.cc b/tools/llvm-xray/xray-extract.cpp index cd87798d0e60..10fe7d8d6209 100644 --- a/tools/llvm-xray/xray-extract.cc +++ b/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/tools/llvm-xray/xray-graph-diff.cc b/tools/llvm-xray/xray-graph-diff.cpp index 3c69b3fb0751..a22f2a99811d 100644 --- a/tools/llvm-xray/xray-graph-diff.cc +++ b/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/tools/llvm-xray/xray-graph.cc b/tools/llvm-xray/xray-graph.cpp index feb676331f89..c619bf86299b 100644 --- a/tools/llvm-xray/xray-graph.cc +++ b/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/tools/llvm-xray/xray-graph.h b/tools/llvm-xray/xray-graph.h index a43df265d0e1..fc7f8bb470f2 100644 --- a/tools/llvm-xray/xray-graph.h +++ b/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/tools/llvm-xray/xray-registry.cc b/tools/llvm-xray/xray-registry.cpp index 36d3a2e58f97..fe58e4deaa1e 100644 --- a/tools/llvm-xray/xray-registry.cc +++ b/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/tools/llvm-xray/xray-stacks.cc b/tools/llvm-xray/xray-stacks.cpp index 9474de047990..1a6069780a31 100644 --- a/tools/llvm-xray/xray-stacks.cc +++ b/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/tools/lto/lto.cpp b/tools/lto/lto.cpp index e6843986dbe2..f9b518ca039b 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -15,7 +15,7 @@ #include "llvm-c/lto.h" #include "llvm/ADT/STLExtras.h" #include "llvm/Bitcode/BitcodeReader.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" @@ -96,7 +96,7 @@ struct LTOToolDiagnosticHandler : public DiagnosticHandler { // Initialize the configured targets if they have not been initialized. static void lto_initialize() { if (!initialized) { -#ifdef LLVM_ON_WIN32 +#ifdef _WIN32 // Dialog box on crash disabling doesn't work across DLL boundaries, so do // it here. llvm::sys::DisableSystemDialogsOnCrash(); @@ -586,6 +586,16 @@ void thinlto_codegen_set_final_cache_size_relative_to_available_space( return unwrap(cg)->setMaxCacheSizeRelativeToAvailableSpace(Percentage); } +void thinlto_codegen_set_cache_size_bytes( + thinlto_code_gen_t cg, unsigned MaxSizeBytes) { + return unwrap(cg)->setCacheMaxSizeBytes(MaxSizeBytes); +} + +void thinlto_codegen_set_cache_size_files( + thinlto_code_gen_t cg, unsigned MaxSizeFiles) { + return unwrap(cg)->setCacheMaxSizeFiles(MaxSizeFiles); +} + void thinlto_codegen_set_savetemps_dir(thinlto_code_gen_t cg, const char *save_temps_dir) { return unwrap(cg)->setSaveTempsDir(save_temps_dir); diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports index 2e09026ae500..abde3894cb20 100644 --- a/tools/lto/lto.exports +++ b/tools/lto/lto.exports @@ -56,15 +56,17 @@ thinlto_codegen_set_pic_model thinlto_codegen_set_cache_dir thinlto_codegen_set_cache_pruning_interval thinlto_codegen_set_cache_entry_expiration +thinlto_codegen_set_final_cache_size_relative_to_available_space +thinlto_codegen_set_cache_size_bytes +thinlto_codegen_set_cache_size_files thinlto_codegen_set_savetemps_dir thinlto_codegen_set_cpu thinlto_debug_options lto_module_is_thinlto thinlto_codegen_add_must_preserve_symbol thinlto_codegen_add_cross_referenced_symbol -thinlto_codegen_set_final_cache_size_relative_to_available_space thinlto_codegen_set_codegen_only thinlto_codegen_disable_codegen thinlto_module_get_num_object_files thinlto_module_get_object_file -thinlto_set_generated_objects_dir
\ No newline at end of file +thinlto_set_generated_objects_dir diff --git a/tools/msbuild/.gitignore b/tools/msbuild/.gitignore new file mode 100644 index 000000000000..01c1f5a9d0b9 --- /dev/null +++ b/tools/msbuild/.gitignore @@ -0,0 +1,2 @@ +bin
+obj
diff --git a/tools/msbuild/CMakeLists.txt b/tools/msbuild/CMakeLists.txt deleted file mode 100644 index 9d132ea58d5d..000000000000 --- a/tools/msbuild/CMakeLists.txt +++ /dev/null @@ -1,69 +0,0 @@ -if (MSVC) - # CPack will install a registry key in this format that we wish to reference. - set(REG_KEY "${CPACK_PACKAGE_INSTALL_REGISTRY_KEY}") - set(LIB_PATH_VERSION "${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}") - - foreach (platform "Win32" "x64") - set(prop_file_in "Microsoft.Cpp.Win32.llvm.props.in") - set(prop_file_v100 "Microsoft.Cpp.${platform}.LLVM-vs2010.props") - set(prop_file_v110 "Microsoft.Cpp.${platform}.LLVM-vs2012.props") - set(prop_file_v110_xp "Microsoft.Cpp.${platform}.LLVM-vs2012_xp.props") - set(prop_file_v120 "toolset-vs2013.props") - set(prop_file_v120_xp "toolset-vs2013_xp.props") - set(prop_file_v140 "toolset-vs2014.props") - set(prop_file_v140_xp "toolset-vs2014_xp.props") - - if (platform STREQUAL "Win32") - set(mflag "m32") - else() - set(mflag "m64") - endif() - set(VS_VERSION "v100") - set(MSC_VERSION "1600") - configure_file(${prop_file_in} ${platform}/${prop_file_v100}) - set(VS_VERSION "v110") - set(MSC_VERSION "1700") - configure_file(${prop_file_in} ${platform}/${prop_file_v110}) - set(VS_VERSION "v110_xp") - configure_file(${prop_file_in} ${platform}/${prop_file_v110_xp}) - set(VS_VERSION "v120") - set(MSC_VERSION "1800") - configure_file(${prop_file_in} ${platform}/${prop_file_v120}) - set(VS_VERSION "v120_xp") - configure_file(${prop_file_in} ${platform}/${prop_file_v120_xp}) - set(VS_VERSION "v140") - set(MSC_VERSION "1900") - configure_file(${prop_file_in} ${platform}/${prop_file_v140}) - set(VS_VERSION "v140_xp") - configure_file(${prop_file_in} ${platform}/${prop_file_v140_xp}) - set(VS_VERSION) - set(MSC_VERSION) - set(mflag) - - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v100}" DESTINATION tools/msbuild/${platform}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v110}" DESTINATION tools/msbuild/${platform}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v110_xp}" DESTINATION tools/msbuild/${platform}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v120}" DESTINATION tools/msbuild/${platform}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v120_xp}" DESTINATION tools/msbuild/${platform}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v140}" DESTINATION tools/msbuild/${platform}) - install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${platform}/${prop_file_v140_xp}" DESTINATION tools/msbuild/${platform}) - - install(FILES "Microsoft.Cpp.Win32.LLVM-vs2010.targets" DESTINATION "tools/msbuild/${platform}" RENAME "Microsoft.Cpp.${platform}.LLVM-vs2010.targets") - install(FILES "Microsoft.Cpp.Win32.LLVM-vs2012.targets" DESTINATION "tools/msbuild/${platform}" RENAME "Microsoft.Cpp.${platform}.LLVM-vs2012.targets") - install(FILES "Microsoft.Cpp.Win32.LLVM-vs2012_xp.targets" DESTINATION "tools/msbuild/${platform}" RENAME "Microsoft.Cpp.${platform}.LLVM-vs2012_xp.targets") - install(FILES "toolset-vs2013.targets" DESTINATION "tools/msbuild/${platform}") - install(FILES "toolset-vs2013_xp.targets" DESTINATION "tools/msbuild/${platform}") - install(FILES "toolset-vs2014.targets" DESTINATION "tools/msbuild/${platform}") - install(FILES "toolset-vs2014_xp.targets" DESTINATION "tools/msbuild/${platform}") - endforeach() - - set(LIB_PATH_VERSION) - set(REG_KEY) - - install(DIRECTORY . - DESTINATION tools/msbuild - FILES_MATCHING - PATTERN "*.bat" - PATTERN ".svn" EXCLUDE - ) -endif() diff --git a/tools/msbuild/LLVM.Cpp.Common.props b/tools/msbuild/LLVM.Cpp.Common.props new file mode 100644 index 000000000000..ffc1270a85c9 --- /dev/null +++ b/tools/msbuild/LLVM.Cpp.Common.props @@ -0,0 +1,75 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <!-- The general order of executing an MSBuild file is roughly: + 1) vcxproj file + 2) ├─ Import Microsoft.Cpp.props + 3) │ └─ Import Toolset specific props (e.g. $(VCTargets)Platforms\Win32\PlatformToolsets\llvm\Toolset.props) + 4) │ └─ Import This File (LLVM.Cpp.Common.props) + 5) │─ Core logic of vcxproj (define files, override properties, etc) + 6) └─ Import Microsoft.Cpp.targets + 7) │─ Import Toolset specific targets file (e.g. $(VCTargets)Platforms\Win32\PlatformToolsets\llvm\Toolset.targets) + 8) └─ Run the compiler. + The important thing is that we have hooks at 3, 4, and 7. 3 and 4 give + us the ability to provide initial values for toolchain settings (where + is the compiler, what values are considered "default" for a given + setting, etc), 7 gives us the ability to act on anything that the user + has overridden (such as warning or erroring on incompatible settings, + mapping settings to other settings, etc). + --> + + <PropertyGroup> + <!-- This initializes the values in Properties > General > Output Directory. + Builds will fail without this. --> + <OutDirWasSpecified Condition=" '$(OutDir)'!='' AND '$(OutDirWasSpecified)'=='' ">true</OutDirWasSpecified> + <OutDirWasSpecified Condition=" '$(OutDir)'=='' AND '$(OutDirWasSpecified)'=='' ">false</OutDirWasSpecified> + + <IntDir Condition="'$(IntDir)'=='' AND '$(IntermediateOutputPath)'!=''">$(IntermediateOutputPath)</IntDir> + <IntDir Condition="'$(IntDir)'=='' AND '$(IntermediateOutputPath)'==''">$(Configuration)\</IntDir> + <OutDir Condition="'$(OutDir)'=='' AND '$(SolutionDir)' != ''">$(SolutionDir)$(Configuration)\</OutDir> + <OutDir Condition="'$(OutDir)'=='' AND '$(SolutionDir)' == ''">$(IntDir)</OutDir> + <DebuggerFlavor Condition="'$(DebuggerFlavor)'==''">WindowsLocalDebugger</DebuggerFlavor> + </PropertyGroup> + + <PropertyGroup> + <!-- Short names for platform toolsets (added to project name in Solution Explorer) --> + <_PlatformToolsetShortNameFor_llvm>LLVM</_PlatformToolsetShortNameFor_llvm> + <_PlatformToolsetFriendlyNameFor_llvm>LLVM</_PlatformToolsetFriendlyNameFor_llvm> + </PropertyGroup> + + <!-- Find an installed LLVM and set up our paths. --> + <PropertyGroup> + <LLVMInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\LLVM@LLVM)</LLVMInstallDir> + <LLVMInstallDir Condition="'$(LLVMInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\LLVM@LLVM)</LLVMInstallDir> + <ClangClExecutable>$(LLVMInstallDir)bin\clang-cl.exe</ClangClExecutable> + </PropertyGroup> + + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props" Condition="Exists('$(VCTargetsPath)\Microsoft.Cpp.WindowsSDK.props')"/> + <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Common.props" /> + + <PropertyGroup> + <!-- Set some paths (such as include paths) that are common to all platforms. This is the same as what + the default paths for cl will use. + --> + <IncludePath Condition="'$(IncludePath)' == ''">$(IncludePath);$(VC_IncludePath);$(WindowsSDK_IncludePath);</IncludePath> + <LibraryWPath Condition="'$(LibraryWPath)' == ''">$(WindowsSDK_MetadataPath);</LibraryWPath> + <SourcePath Condition="'$(SourcePath)' == ''">$(VC_SourcePath);</SourcePath> + </PropertyGroup> + + + <!-- Set values which are reflected in the property UI by default. The user can override these + by editing the vcxproj file (or making changes via the UI, which has the same effect). + --> + <ItemDefinitionGroup> + <ClCompile> + <!-- Set this to "Default" (which means not passing any /RTC option) so that any other value will + be treated as having been overridden by the user. This Serves as a hint to the user that + Default is the value we support, and other values will generate a warning. It also means + that if the user simply creates a new project in MSVC (which uses /RTCu by default), then + switches the toolset to Clang, we will still treat the value as default (which for us is to + not pass the option). Only if the user explicitly overrode this setting in a project to use + /RTCu would we see the warning. --> + <BasicRuntimeChecks>Default</BasicRuntimeChecks> + + <AdditionalOptions>-m$(PlatformArchitecture) %(AdditionalOptions)</AdditionalOptions> + </ClCompile> + </ItemDefinitionGroup> +</Project> diff --git a/tools/msbuild/LLVM.Cpp.Common.targets b/tools/msbuild/LLVM.Cpp.Common.targets new file mode 100644 index 000000000000..1edc08d2a325 --- /dev/null +++ b/tools/msbuild/LLVM.Cpp.Common.targets @@ -0,0 +1,184 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" /> + + <PropertyGroup> + <!-- Set the path to clang-cl executable based on the value of the project- + level setting. This has to be done in the .targets file since values + selected via the settings UI appear in the vcxproj (which is imported + before the targets file but after the props file) and we need the path + that the user may have overridden in the UI. --> + <CLToolExe>$(ClangClExecutable)</CLToolExe> + </PropertyGroup> + + <ItemGroup> + <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\llvm-general.xml"> + <Context>Project</Context> + </PropertyPageSchema> + </ItemGroup> + + <!-- We hook up a target to run every time ClCompile is about to run, the + purpose of which is to sanitize the command line before it gets passed to + the compiler. Some options we silently discard, other options we warn on + and then discard, and other options we generate a hard error. + + We try to keep hard errors to a minimum and reserve it for cases where + the option implies fundamentally different assumptions about the way code + should be compiled. This code would probably generate an error anyway, + but at least this way we give the user a more useful message about what + the actual problem is, rather than relying on some obscure compilation + error. + + For any options that clang-cl discards, we would prefer to not even pass + them in on the command line. So if a user starts with a cl projects and + changes the toolset to clang, they could have set options such as /Gm + (minimal rebuild), /sdl (Security Checks), etc. The ClCompile task would + then notice this and pass these through to clang-cl.exe. Clang would of + course ignore them, but in some cases (such as /Gm), they would generate + -Wunused-command-line-argument warnings, so it's better if we can just + strip them from the command line entirely. This also has the side + benefit of making command lines shorter which is always nice when trying + to look at the tool output. + --> + <Target Name="BeforeClCompile" BeforeTargets="ClCompile"> + <!-- Warn on /Zi and /ZI, then map them both to /Z7. --> + <Warning Condition="'%(ClCompile.DebugInformationFormat)' == 'ProgramDatabase'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support /Zi (Program Database). The file will be compiled as if /Z7 (C7 Compatible Debug Info) had been passed. Update the Debug Information Format in project settings to silence this warning."/> + <Warning Condition="'%(ClCompile.DebugInformationFormat)' == 'EditAndContinue'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support /ZI (Program Database for Edit and Continue). The file will be compiled as if /Z7 (C7 Compatible Debug Info) had been passed. Update the Debug Information Format in project settings to silence this warning."/> + + <!-- Warn if Fiber Safe Optimizations are enabled, and then ignore them. --> + <Warning Condition="'%(ClCompile.EnableFiberSafeOptimizations)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support fiber safe optimizations (/GT). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if Whole Program Optimization is enabled, and then ignore it. --> + <Warning Condition="'%(ClCompile.WholeProgramOptimization)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support MSVC Link Time Optimization. Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if Ignore Standard Include Paths is non-empty, then ignore it. --> + <Warning Condition="'%(ClCompile.IgnoreStandardIncludePath)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support Ignore Standard Include Path (/X). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if Smaller Type Check is enabled, then ignore it.--> + <Warning Condition="'%(ClCompile.SmallerTypeCheck)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support Smaller Type Check (/RTCc). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if Runtime Checks are enabled, then ignore them.--> + <Warning Condition="'%(ClCompile.BasicRuntimeChecks)' != 'Default'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support Basic Runtime Checks (/RTCu, /RTC1, /RTCs). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if parallel code generation on #pragma loop is enabled, then ignore. --> + <Warning Condition="'(ClCompile.EnableParallelCodeGeneration)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support parallel code generation with #pragma loop(hint) (/Qpar). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if hotpatchable images are turned on --> + <Warning Condition="'%(ClCompile.CreateHotpatchableImage)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support creating hotpatchable images (/hotpatch). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if /Zc:forScope- is specified, and then ignore it. --> + <Warning Condition="'%(ClCompile.ForceConformanceInForLoopScope)' == 'false'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support disabling for loop scope conformance (/Zc:forScope-). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if /Zc:wchar_t- is specified, and then ignore it. --> + <Warning Condition="'%(ClCompile.TreatWChar_tAsBuiltInType)' == 'false'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support treating wchar_t as a non builtin type (/Zc:wchar_t-). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if XML Documentation is generated, and then ignore it. --> + <Warning Condition="'%(ClCompile.GenerateXMLDocumentationFiles)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support generating xml documentation comment files (/doc). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if Browse Information is generated, and then ignore it. --> + <Warning Condition="'%(ClCompile.BrowseInformation)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support generating browse information (/FR). Disable this option in compatibility settings to silence this warning."/> + + <!-- Warn if /analyze is passed, then ignore it. --> + <Warning Condition="'%(ClCompile.EnablePREfast)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support MSVC code analysis functionality (/analyze). Disable this option in compatibility settings to silence this warning."/> + + <!-- Error if they're trying to compile this file as managed code. --> + <Error Condition="('%(ClCompile.CompileAsManaged)' != 'false') AND ('%(ClCompile.CompileAsManaged)' != '')" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support compiling managed code (/clr). This file cannot be compiled."/> + + <!-- Error if WinRT is being used. --> + <Error Condition="('%(ClCompile.CompileAsWinRT)' == 'true') OR ('%(ClCompile.WinRTNoStdLib)' == 'true')" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support Windows Runtime Language Extensions (/ZW, /ZW:nostdlib). This file cannot be compiled."/> + + <!-- Error if OpenMP language extensions are enabled. --> + <Error Condition="'%(ClCompile.OpenMPSupport)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support OpenMP (/openmp). This file cannot be compiled."/> + + <!-- Error if C++ Modules are enabled. Clang has its own notion of modules that are not compatible. --> + <Error Condition="'%(ClCompile.EnableModules)' == 'true'" + File="@(ClCompile)(0,0)" + Text="clang-cl does not support MSVC Modules (/experimental:module). This file cannot be compiled."/> + + <ItemGroup> + <ClCompile> + <!-- Map /ZI and /Zi to /Z7. Clang internally does this, so if we were + to just pass the option through, clang would work. The problem is + that MSBuild would not. MSBuild detects /ZI and /Zi and then + assumes (rightly) that there will be a compiler-generated PDB (e.g. + vc141.pdb). Since clang-cl will not emit this, MSBuild will always + think that the compiler-generated PDB needs to be re-generated from + scratch and trigger a full build. The way to avoid this is to + always give MSBuild accurate information about how we plan to + generate debug info (which is to always using /Z7 semantics). + --> + <DebugInformationFormat Condition="'%(ClCompile.DebugInformationFormat)' == 'ProgramDatabase'">OldStyle</DebugInformationFormat> + <DebugInformationFormat Condition="'%(ClCompile.DebugInformationFormat)' == 'EditAndContinue'">OldStyle</DebugInformationFormat> + + <!-- Unset any options that we either silently ignore or warn about due to compatibility. + Generally when an option is set to no value, that means "Don't pass an option to the + compiler at all." + --> + <WholeProgramOptimization/> + <EnableFiberSafeOptimizations/> + <IgnoreStandardIncludePath/> + <EnableParallelCodeGeneration/> + <ForceConformanceInForLoopScope/> + <TreatWChar_tAsBuiltInType/> + <SDLCheck/> + <GenerateXMLDocumentationFiles/> + <BrowseInformation/> + <EnablePREfast/> + <MinimalRebuild/> + <StringPooling/> + <ExpandAttributedSource/> + <EnforceTypeConversionRules/> + <ErrorReporting/> + <DisableLanguageExtensions/> + <ProgramDataBaseFileName/> + <DisableSpecificWarnings/> + <TreatSpecificWarningsAsErrors/> + <ForcedUsingFiles/> + <PREfastLog/> + <PREfastAdditionalOptions/> + <PREfastAdditionalPlugins/> + <MultiProcessorCompilation/> + <UseFullPaths/> + <RemoveUnreferencedCodeData/> + + <!-- We can't just unset BasicRuntimeChecks, as that will pass /RTCu to the compiler. + We have to explicitly set it to 'Default' to avoid passing anything. --> + <BasicRuntimeChecks>Default</BasicRuntimeChecks> + </ClCompile> + </ItemGroup> + </Target> + +</Project> diff --git a/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2010.targets b/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2010.targets deleted file mode 100644 index df41a844aa70..000000000000 --- a/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2010.targets +++ /dev/null @@ -1,2 +0,0 @@ -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-</Project>
diff --git a/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2012.targets b/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2012.targets deleted file mode 100644 index f7432f2fe5dc..000000000000 --- a/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2012.targets +++ /dev/null @@ -1,3 +0,0 @@ -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
-</Project>
diff --git a/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2012_xp.targets b/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2012_xp.targets deleted file mode 100644 index e8250d8e8248..000000000000 --- a/tools/msbuild/Microsoft.Cpp.Win32.LLVM-vs2012_xp.targets +++ /dev/null @@ -1,21 +0,0 @@ -<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <!-- Force TargetFrameworkVersion to v4.0 to support XP--> - <PropertyGroup> - <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> - <BeforeClCompileTargets>NoSupportCodeAnalysisXP;$(BeforeClCompileTargets)</BeforeClCompileTargets> - </PropertyGroup> - - <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" /> - - <Target Name="NoSupportCodeAnalysisXP" Condition="'$(ErrorNoSupportCodeAnalysisXP)' != 'false'"> - <VCMessage Condition="'$(DesignTimeBuild)' != 'true' and '@(ClCompile->AnyHaveMetadataValue('EnablePREfast', 'true'))'=='true'" Code="MSB8026" Type="Error"/> - </Target> - - <PropertyGroup> - <PrepareForBuildDependsOn>CheckWindowsSDK71A;$(PrepareForBuildDependsOn)</PrepareForBuildDependsOn> - </PropertyGroup> - - <Target Name="CheckWindowsSDK71A"> - <VCMessage Code="MSB8003" Type="Warning" Arguments="WindowsSdkDir_71A" Condition="'$(WindowsSdkDir_71A)'=='' and '$(UseEnv)' != 'true'" /> - </Target> -</Project> diff --git a/tools/msbuild/Microsoft.Cpp.Win32.llvm.props.in b/tools/msbuild/Microsoft.Cpp.Win32.llvm.props.in deleted file mode 100644 index a775c31c7670..000000000000 --- a/tools/msbuild/Microsoft.Cpp.Win32.llvm.props.in +++ /dev/null @@ -1,18 +0,0 @@ -<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(VCTargetsPath)\Platforms\$(Platform)\PlatformToolsets\@VS_VERSION@\Microsoft.Cpp.$(Platform).@VS_VERSION@.props" Condition="Exists('$(VCTargetsPath)\Platforms\$(Platform)\PlatformToolsets\@VS_VERSION@\Microsoft.Cpp.$(Platform).@VS_VERSION@.props')"/>
- <Import Project="$(VCTargetsPath)\Platforms\$(Platform)\PlatformToolsets\@VS_VERSION@\Toolset.props" Condition="Exists('$(VCTargetsPath)\Platforms\$(Platform)\PlatformToolsets\@VS_VERSION@\Toolset.props')"/>
-
- <PropertyGroup>
- <LLVMInstallDir>$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\LLVM\@REG_KEY@)</LLVMInstallDir>
- <LLVMInstallDir Condition="'$(LLVMInstallDir)' == ''">$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\LLVM\@REG_KEY@)</LLVMInstallDir>
- <ExecutablePath>$(LLVMInstallDir)\msbuild-bin;$(ExecutablePath)</ExecutablePath>
- <LibraryPath>$(LLVMInstallDir)\lib\clang\@LIB_PATH_VERSION@\lib\windows;$(LibraryPath)</LibraryPath>
- </PropertyGroup>
-
- <ItemDefinitionGroup>
- <ClCompile>
- <!-- Set the value of _MSC_VER to claim for compatibility -->
- <AdditionalOptions>-@mflag@ -fmsc-version=@MSC_VERSION@ %(AdditionalOptions)</AdditionalOptions>
- </ClCompile>
- </ItemDefinitionGroup>
-</Project>
diff --git a/tools/msbuild/Platformx64/Toolset.props b/tools/msbuild/Platformx64/Toolset.props new file mode 100644 index 000000000000..4762f4eaa1c8 --- /dev/null +++ b/tools/msbuild/Platformx64/Toolset.props @@ -0,0 +1,11 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(VCTargetsPath)\LLVM.Cpp.Common.props"/>
+
+ <PropertyGroup>
+ <ExecutablePath Condition="'$(ExecutablePath)' == ''">$(VC_ExecutablePath_x64);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(FxCopDir);$(PATH);</ExecutablePath>
+ <ReferencePath Condition="'$(ReferencePath)' == ''">$(VC_ReferencesPath_x64);</ReferencePath>
+ <LibraryPath Condition="'$(LibraryPath)' == ''">$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64);$(NETFXKitsDir)Lib\um\x64</LibraryPath>
+ <ExcludePath Condition="'$(ExcludePath)' == ''">$(VC_IncludePath);$(WindowsSDK_IncludePath);$(MSBuild_ExecutablePath);$(VC_LibraryPath_x64);</ExcludePath>
+ <DebugCppRuntimeFilesPath Condition="'$(DebugCppRuntimeFilesPath)' == ''">$(VCToolsInstallDir)redist\Debug_NonRedist\x64</DebugCppRuntimeFilesPath>
+ </PropertyGroup>
+</Project>
diff --git a/tools/msbuild/Platformx64/Toolset.targets b/tools/msbuild/Platformx64/Toolset.targets new file mode 100644 index 000000000000..938e03b51e27 --- /dev/null +++ b/tools/msbuild/Platformx64/Toolset.targets @@ -0,0 +1,3 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(VCTargetsPath)\LLVM.Cpp.Common.targets"/>
+</Project>
diff --git a/tools/msbuild/Platformx86/Toolset.props b/tools/msbuild/Platformx86/Toolset.props new file mode 100644 index 000000000000..5a5cd6c736fd --- /dev/null +++ b/tools/msbuild/Platformx86/Toolset.props @@ -0,0 +1,11 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(VCTargetsPath)\LLVM.Cpp.Common.props"/>
+
+ <PropertyGroup>
+ <ExecutablePath Condition="'$(ExecutablePath)' == ''">$(VC_ExecutablePath_x86);$(WindowsSDK_ExecutablePath);$(VS_ExecutablePath);$(MSBuild_ExecutablePath);$(SystemRoot)\SysWow64;$(FxCopDir);$(PATH);</ExecutablePath>
+ <ReferencePath Condition="'$(ReferencePath)' == ''">$(VC_ReferencesPath_x86);</ReferencePath>
+ <LibraryPath Condition="'$(LibraryPath)' == ''">$(VC_LibraryPath_x86);$(WindowsSDK_LibraryPath_x86);$(NETFXKitsDir)Lib\um\x86</LibraryPath>
+ <ExcludePath Condition="'$(ExcludePath)' == ''">$(VC_IncludePath);$(WindowsSDK_IncludePath);$(MSBuild_ExecutablePath);$(VC_LibraryPath_x86);</ExcludePath>
+ <DebugCppRuntimeFilesPath Condition="'$(DebugCppRuntimeFilesPath)' == ''">$(VCToolsInstallDir)redist\Debug_NonRedist\x86</DebugCppRuntimeFilesPath>
+ </PropertyGroup>
+</Project>
diff --git a/tools/msbuild/Platformx86/Toolset.targets b/tools/msbuild/Platformx86/Toolset.targets new file mode 100644 index 000000000000..938e03b51e27 --- /dev/null +++ b/tools/msbuild/Platformx86/Toolset.targets @@ -0,0 +1,3 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(VCTargetsPath)\LLVM.Cpp.Common.targets"/>
+</Project>
diff --git a/tools/msbuild/install.bat b/tools/msbuild/install.bat index 6e321e3f5801..bbc8a7461513 100644 --- a/tools/msbuild/install.bat +++ b/tools/msbuild/install.bat @@ -1,123 +1,57 @@ -@echo off
-
-echo Installing MSVC integration...
-set SUCCESS=0
-
-REM Change to the directory of this batch file.
-cd /d %~dp0
-
-REM Loop over the two platforms in awkward batch file fashion.
-set PLATFORM=None
-:PLATFORMLOOPHEAD
-IF %PLATFORM% == x64 GOTO PLATFORMLOOPEND
-IF %PLATFORM% == Win32 SET PLATFORM=x64
-IF %PLATFORM% == None SET PLATFORM=Win32
-
-REM Search for the MSBuild toolsets directory.
-
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V100
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V100
-
-:TRY_V110
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V110
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V110
-
-:TRY_V120
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V120\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V120
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V120\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V120
-
-:TRY_V140
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V140
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D% GOTO FOUND_V140
-
-:TRY_V150
-
-GOTO PLATFORMLOOPHEAD
-
-:PLATFORMLOOPEND
-IF %SUCCESS% == 1 goto DONE
-echo Failed to find MSBuild toolsets directory.
-goto FAILED
-
-
-:FOUND_V100
-REM Routine for installing v100 toolchain.
-IF NOT EXIST %D%\LLVM-vs2010 mkdir %D%\LLVM-vs2010
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.props %D%\LLVM-vs2010
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.targets %D%\LLVM-vs2010
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-set SUCCESS=1
-GOTO TRY_V110
-
-:FOUND_V110
-REM Routine for installing v110 toolchain.
-IF NOT EXIST %D%\LLVM-vs2012 mkdir %D%\LLVM-vs2012
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.props %D%\LLVM-vs2012
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.targets %D%\LLVM-vs2012
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-IF NOT EXIST %D%\LLVM-vs2012_xp mkdir %D%\LLVM-vs2012_xp
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012_xp.props %D%\LLVM-vs2012_xp
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012_xp.targets %D%\LLVM-vs2012_xp
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-set SUCCESS=1
-GOTO TRY_V120
-
-:FOUND_V120
-REM Routine for installing v120 toolchain.
-IF NOT EXIST %D%\LLVM-vs2013 mkdir %D%\LLVM-vs2013
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2013.props %D%\LLVM-vs2013\toolset.props
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2013.targets %D%\LLVM-vs2013\toolset.targets
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-IF NOT EXIST %D%\LLVM-vs2013_xp mkdir %D%\LLVM-vs2013_xp
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2013_xp.props %D%\LLVM-vs2013_xp\toolset.props
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2013_xp.targets %D%\LLVM-vs2013_xp\toolset.targets
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-set SUCCESS=1
-GOTO TRY_V140
-
-:FOUND_V140
-REM Routine for installing v140 toolchain.
-IF NOT EXIST %D%\LLVM-vs2014 mkdir %D%\LLVM-vs2014
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2014.props %D%\LLVM-vs2014\toolset.props
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2014.targets %D%\LLVM-vs2014\toolset.targets
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-IF NOT EXIST %D%\LLVM-vs2014_xp mkdir %D%\LLVM-vs2014_xp
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2014_xp.props %D%\LLVM-vs2014_xp\toolset.props
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-copy %PLATFORM%\toolset-vs2014_xp.targets %D%\LLVM-vs2014_xp\toolset.targets
-IF NOT %ERRORLEVEL% == 0 GOTO FAILED
-set SUCCESS=1
-GOTO TRY_V150
-
-
-:DONE
-echo Done!
-goto END
-
-:FAILED
-echo MSVC integration install failed.
-pause
-goto END
-
-:END
+@echo off + +echo Installing MSVC integration... +set SUCCESS=0 + +REM In general this script should not be used except for development and testing +REM purposes. The proper way to install is via the VSIX, and the proper way to +REM uninstall is through the Visual Studio extension manager. + +REM Change to the directory of this batch file. +cd /d %~dp0 + +REM Older versions of VS would look for these files in the Program Files\MSBuild directory +REM but with VS2017 it seems to look for these directly in the Visual Studio instance. +REM This means we'll need to do a little extra work to properly detect all the various +REM instances, but in reality we can probably sidestep all of this by just wrapping this +REM in a vsix and calling it a day, as that should handle everything for us. +SET VCTargets=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets + +ECHO Installing Common Files +copy LLVM.Cpp.Common.props "%VCTargets%" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED +copy LLVM.Cpp.Common.targets "%VCTargets%" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED + +ECHO Installing x64 Platform Toolset +SET PlatformToolsets=%VCTargets%\Platforms\x64\PlatformToolsets +IF NOT EXIST "%PlatformToolsets%\llvm" mkdir "%PlatformToolsets%\llvm" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED +copy PlatformX64\Toolset.props "%PlatformToolsets%\llvm" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED +copy PlatformX64\Toolset.targets "%PlatformToolsets%\llvm" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED + +ECHO Installing Win32 Platform Toolset +SET PlatformToolsets=%VCTargets%\Platforms\Win32\PlatformToolsets +IF NOT EXIST "%PlatformToolsets%\llvm" mkdir "%PlatformToolsets%\llvm" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED +copy PlatformX86\Toolset.props "%PlatformToolsets%\llvm" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED +copy PlatformX86\Toolset.targets "%PlatformToolsets%\llvm" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED + +ECHO Installing C++ Settings UI +copy llvm-general.xml "%VCTargets%\1033" +IF NOT %ERRORLEVEL% == 0 GOTO FAILED + +:DONE +echo Done! +goto END + +:FAILED +echo MSVC integration install failed. +pause +goto END + +:END diff --git a/tools/msbuild/license.txt b/tools/msbuild/license.txt new file mode 100644 index 000000000000..76aa2afdf81e --- /dev/null +++ b/tools/msbuild/license.txt @@ -0,0 +1,39 @@ +==================== +LLVM Release License +==================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2007-2018 University of Illinois at Urbana-Champaign. +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal with the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimers in the documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at Urbana-Champaign, nor the names of its contributors may be used to endorse or promote products derived from this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. + +==================== +The LLVM software contains code written by third parties. Such software will have its own individual LICENSE.TXT file in the directory in which it appears. This file will describe the copyrights, license, and restrictions which apply +to that code. + +The disclaimer of warranty in the University of Illinois Open Source License applies to all code in the LLVM Distribution, and nothing in any of the other licenses gives permission to use the names of the LLVM Team or the University of Illinois to endorse or promote products derived from this Software. + +The following pieces of software have additional or alternate copyrights, licenses, and/or restrictions: + +Program Directory +------- --------- +<none yet> + diff --git a/tools/msbuild/llvm-general.xml b/tools/msbuild/llvm-general.xml new file mode 100644 index 000000000000..3b72c1250da2 --- /dev/null +++ b/tools/msbuild/llvm-general.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<Rule + Name="LLVMGeneral" + DisplayName="LLVM" + PageTemplate="generic" + Description="LLVM" + xmlns="http://schemas.microsoft.com/build/2009/properties"> + <Rule.Categories> + <Category Name="General" DisplayName="General" Description="General" /> + </Rule.Categories> + <Rule.DataSource> + <DataSource Persistence="ProjectFile" Label="Configuration" /> + </Rule.DataSource> + + <StringProperty Name="ClangClExecutable" + DisplayName="Clang-CL Executable" + Description="Specifies the path to clang-cl.exe." + Category="General" + Default="$(LLVMInstallDir)bin\clang-cl.exe" + Subtype="file"> + <StringProperty.DataSource> + <DataSource Persistence="ProjectFile" Label="" /> + </StringProperty.DataSource> + </StringProperty> +</Rule> diff --git a/tools/msbuild/llvm.csproj b/tools/msbuild/llvm.csproj new file mode 100644 index 000000000000..a614bb2f22ba --- /dev/null +++ b/tools/msbuild/llvm.csproj @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <MinimumVisualStudioVersion>15.0</MinimumVisualStudioVersion>
+ <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
+ </PropertyGroup>
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
+ <ProjectGuid>{62530D9E-1B24-4C31-8DC9-AE47E9E5DC53}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>llvm</RootNamespace>
+ <AssemblyName>llvm</AssemblyName>
+ <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+ <GeneratePkgDefFile>false</GeneratePkgDefFile>
+ <IncludeAssemblyInVSIXContainer>false</IncludeAssemblyInVSIXContainer>
+ <IncludeDebugSymbolsInVSIXContainer>false</IncludeDebugSymbolsInVSIXContainer>
+ <IncludeDebugSymbolsInLocalVSIXDeployment>false</IncludeDebugSymbolsInLocalVSIXDeployment>
+ <CopyBuildOutputToOutputDirectory>false</CopyBuildOutputToOutputDirectory>
+ <CopyOutputSymbolsToOutputDirectory>false</CopyOutputSymbolsToOutputDirectory>
+ <StartAction>Program</StartAction>
+ <StartProgram Condition="'$(DevEnvDir)' != ''">$(DevEnvDir)devenv.exe</StartProgram>
+ <StartArguments>/rootsuffix Exp</StartArguments>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Content Include="llvm-general.xml">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ <VSIXSubPath>1033</VSIXSubPath>
+ </Content>
+ <Content Include="LLVM.Cpp.Common.props">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ </Content>
+ <Content Include="license.txt">
+ <CopyToOutputDirectory>Always</CopyToOutputDirectory>
+ <IncludeInVSIX>true</IncludeInVSIX>
+ </Content>
+ <Content Include="LLVM.Cpp.Common.targets">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ </Content>
+ <None Include="source.extension.vsixmanifest">
+ <SubType>Designer</SubType>
+ </None>
+ <Content Include="Platformx64\Toolset.props">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ <VSIXSubPath>Platforms\x64\PlatformToolsets\llvm</VSIXSubPath>
+ </Content>
+ <Content Include="Platformx64\Toolset.targets">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ <VSIXSubPath>Platforms\x64\PlatformToolsets\llvm</VSIXSubPath>
+ </Content>
+ <Content Include="Platformx86\Toolset.props">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ <VSIXSubPath>Platforms\Win32\PlatformToolsets\llvm</VSIXSubPath>
+ </Content>
+ <Content Include="Platformx86\Toolset.targets">
+ <IncludeInVSIX>true</IncludeInVSIX>
+ <InstallRoot>VCTargets</InstallRoot>
+ <VSIXSubPath>Platforms\Win32\PlatformToolsets\llvm</VSIXSubPath>
+ </Content>
+ </ItemGroup>
+ <ItemGroup>
+ <Folder Include="Properties\" />
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file diff --git a/tools/msbuild/llvm.sln b/tools/msbuild/llvm.sln new file mode 100644 index 000000000000..9fc37441b8b9 --- /dev/null +++ b/tools/msbuild/llvm.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27004.2006 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "llvm", "llvm.csproj", "{62530D9E-1B24-4C31-8DC9-AE47E9E5DC53}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {62530D9E-1B24-4C31-8DC9-AE47E9E5DC53}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {62530D9E-1B24-4C31-8DC9-AE47E9E5DC53}.Debug|Any CPU.Build.0 = Debug|Any CPU + {62530D9E-1B24-4C31-8DC9-AE47E9E5DC53}.Release|Any CPU.ActiveCfg = Release|Any CPU + {62530D9E-1B24-4C31-8DC9-AE47E9E5DC53}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD0FC803-C28B-4327-A129-CFB35C873897} + EndGlobalSection +EndGlobal diff --git a/tools/msbuild/source.extension.vsixmanifest b/tools/msbuild/source.extension.vsixmanifest new file mode 100644 index 000000000000..28be6a4242c2 --- /dev/null +++ b/tools/msbuild/source.extension.vsixmanifest @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?>
+<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
+ <Metadata>
+ <Identity Id="llvm.d29c51f0-961f-4e20-aad6-356af569907f" Version="1.0" Language="en-US" Publisher="The LLVM Foundation" />
+ <DisplayName>LLVM Compiler Toolchain</DisplayName>
+ <Description xml:space="preserve">Allows the LLVM Compiler Toolchain (installed separately) to be used from within Visual Studio to build C/C++ Projects.</Description>
+ <License>license.txt</License>
+ </Metadata>
+ <Installation AllUsers="true">
+ <InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[14.0,16.0)" />
+ <InstallationTarget Version="[14.0,16.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[14.0,16.0)" Id="Microsoft.VisualStudio.Enterprise" />
+ </Installation>
+ <Dependencies>
+ <Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
+ </Dependencies>
+ <Prerequisites>
+ <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0,16.0)" DisplayName="Visual Studio core editor" />
+ <Prerequisite Id="Microsoft.VisualStudio.ComponentGroup.NativeDesktop.Core" Version="[15.0.26621.2,16.0)" DisplayName="Visual C++ core desktop features" />
+ <Prerequisite Id="Microsoft.VisualStudio.Component.VC.CoreIde" Version="[15.0.26606.0,16.0)" DisplayName="Visual Studio C++ core features" />
+ </Prerequisites>
+</PackageManifest>
diff --git a/tools/msbuild/toolset-vs2013.targets b/tools/msbuild/toolset-vs2013.targets deleted file mode 100644 index a6efac48bae2..000000000000 --- a/tools/msbuild/toolset-vs2013.targets +++ /dev/null @@ -1,3 +0,0 @@ -<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
-</Project>
diff --git a/tools/msbuild/toolset-vs2013_xp.targets b/tools/msbuild/toolset-vs2013_xp.targets deleted file mode 100644 index e71968185423..000000000000 --- a/tools/msbuild/toolset-vs2013_xp.targets +++ /dev/null @@ -1,21 +0,0 @@ -<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> - <!-- Force TargetFrameworkVersion to v4.0 to support XP--> - <PropertyGroup> - <TargetFrameworkVersion>v4.0</TargetFrameworkVersion> - <BeforeClCompileTargets>NoSupportCodeAnalysisXP;$(BeforeClCompileTargets)</BeforeClCompileTargets> - </PropertyGroup> - - <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" /> - - <Target Name="NoSupportCodeAnalysisXP" Condition="'$(ErrorNoSupportCodeAnalysisXP)' != 'false'"> - <VCMessage Condition="'$(DesignTimeBuild)' != 'true' and '@(ClCompile->AnyHaveMetadataValue('EnablePREfast', 'true'))'=='true'" Code="MSB8026" Type="Error"/> - </Target> - - <PropertyGroup> - <PrepareForBuildDependsOn>CheckWindowsSDK71A;$(PrepareForBuildDependsOn)</PrepareForBuildDependsOn> - </PropertyGroup> - - <Target Name="CheckWindowsSDK71A"> - <VCMessage Code="MSB8003" Type="Warning" Arguments="WindowsSdkDir_71A" Condition="'$(WindowsSdkDir_71A)'=='' and '$(UseEnv)' != 'true'" /> - </Target> -</Project> diff --git a/tools/msbuild/toolset-vs2014.targets b/tools/msbuild/toolset-vs2014.targets deleted file mode 100644 index 05b59a2d84f0..000000000000 --- a/tools/msbuild/toolset-vs2014.targets +++ /dev/null @@ -1,3 +0,0 @@ -<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
-</Project>
diff --git a/tools/msbuild/toolset-vs2014_xp.targets b/tools/msbuild/toolset-vs2014_xp.targets deleted file mode 100644 index eec4f18daa4f..000000000000 --- a/tools/msbuild/toolset-vs2014_xp.targets +++ /dev/null @@ -1,21 +0,0 @@ -<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <!-- Force TargetFrameworkVersion to v4.0 to support XP-->
- <PropertyGroup>
- <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
- <BeforeClCompileTargets>NoSupportCodeAnalysisXP;$(BeforeClCompileTargets)</BeforeClCompileTargets>
- </PropertyGroup>
-
- <Import Project="$(VCTargetsPath)\Microsoft.CppCommon.targets" />
-
- <Target Name="NoSupportCodeAnalysisXP" Condition="'$(ErrorNoSupportCodeAnalysisXP)' != 'false'">
- <VCMessage Condition="'$(DesignTimeBuild)' != 'true' and '@(ClCompile->AnyHaveMetadataValue('EnablePREfast', 'true'))'=='true'" Code="MSB8026" Type="Error"/>
- </Target>
-
- <PropertyGroup>
- <PrepareForBuildDependsOn>CheckWindowsSDK71A;$(PrepareForBuildDependsOn)</PrepareForBuildDependsOn>
- </PropertyGroup>
-
- <Target Name="CheckWindowsSDK71A">
- <VCMessage Code="MSB8003" Type="Warning" Arguments="WindowsSdkDir_71A" Condition="'$(WindowsSdkDir_71A)'=='' and '$(UseEnv)' != 'true'" />
- </Target>
-</Project>
diff --git a/tools/msbuild/uninstall.bat b/tools/msbuild/uninstall.bat index c1afae2c3c96..0c8852ec46db 100644 --- a/tools/msbuild/uninstall.bat +++ b/tools/msbuild/uninstall.bat @@ -1,73 +1,33 @@ -@echo off
-
-echo Uninstalling MSVC integration...
-
-REM CD to the directory of this batch file.
-cd /d %~dp0
-
-set PLATFORM=None
-:LOOPHEAD
-IF %PLATFORM% == x64 GOTO LOOPEND
-IF %PLATFORM% == Win32 SET PLATFORM=x64
-IF %PLATFORM% == None SET PLATFORM=Win32
-
-
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2010 del %D%\LLVM-vs2010\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.props
-IF EXIST %D%\LLVM-vs2010 del %D%\LLVM-vs2010\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.targets
-IF EXIST %D%\LLVM-vs2010 rmdir %D%\LLVM-vs2010
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2010 del %D%\LLVM-vs2010\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.props
-IF EXIST %D%\LLVM-vs2010 del %D%\LLVM-vs2010\Microsoft.Cpp.%PLATFORM%.LLVM-vs2010.targets
-IF EXIST %D%\LLVM-vs2010 rmdir %D%\LLVM-vs2010
-
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2012 del %D%\LLVM-vs2012\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.props
-IF EXIST %D%\LLVM-vs2012 del %D%\LLVM-vs2012\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.targets
-IF EXIST %D%\LLVM-vs2012 rmdir %D%\LLVM-vs2012
-IF EXIST %D%\LLVM-vs2012_xp del %D%\LLVM-vs2012_xp\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012_xp.props
-IF EXIST %D%\LLVM-vs2012_xp del %D%\LLVM-vs2012_xp\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012_xp.targets
-IF EXIST %D%\LLVM-vs2012_xp rmdir %D%\LLVM-vs2012_xp
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V110\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2012 del %D%\LLVM-vs2012\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.props
-IF EXIST %D%\LLVM-vs2012 del %D%\LLVM-vs2012\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012.targets
-IF EXIST %D%\LLVM-vs2012 rmdir %D%\LLVM-vs2012
-IF EXIST %D%\LLVM-vs2012_xp del %D%\LLVM-vs2012_xp\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012_xp.props
-IF EXIST %D%\LLVM-vs2012_xp del %D%\LLVM-vs2012_xp\Microsoft.Cpp.%PLATFORM%.LLVM-vs2012_xp.targets
-IF EXIST %D%\LLVM-vs2012_xp rmdir %D%\LLVM-vs2012_xp
-
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V120\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2013 del %D%\LLVM-vs2013\toolset.props
-IF EXIST %D%\LLVM-vs2013 del %D%\LLVM-vs2013\toolset.targets
-IF EXIST %D%\LLVM-vs2013 rmdir %D%\LLVM-vs2013
-IF EXIST %D%\LLVM-vs2013_xp del %D%\LLVM-vs2013_xp\toolset.props
-IF EXIST %D%\LLVM-vs2013_xp del %D%\LLVM-vs2013_xp\toolset.targets
-IF EXIST %D%\LLVM-vs2013_xp rmdir %D%\LLVM-vs2013_xp
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V120\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2013 del %D%\LLVM-vs2013\toolset.props
-IF EXIST %D%\LLVM-vs2013 del %D%\LLVM-vs2013\toolset.targets
-IF EXIST %D%\LLVM-vs2013 rmdir %D%\LLVM-vs2013
-IF EXIST %D%\LLVM-vs2013_xp del %D%\LLVM-vs2013_xp\toolset.props
-IF EXIST %D%\LLVM-vs2013_xp del %D%\LLVM-vs2013_xp\toolset.targets
-IF EXIST %D%\LLVM-vs2013_xp rmdir %D%\LLVM-vs2013_xp
-
-SET D="%ProgramFiles%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.props
-IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.targets
-IF EXIST %D%\LLVM-vs2014 rmdir %D%\LLVM-vs2014
-IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.props
-IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.targets
-IF EXIST %D%\LLVM-vs2014_xp rmdir %D%\LLVM-vs2014_xp
-SET D="%ProgramFiles(x86)%\MSBuild\Microsoft.Cpp\v4.0\V140\Platforms\%PLATFORM%\PlatformToolsets"
-IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.props
-IF EXIST %D%\LLVM-vs2014 del %D%\LLVM-vs2014\toolset.targets
-IF EXIST %D%\LLVM-vs2014 rmdir %D%\LLVM-vs2014
-IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.props
-IF EXIST %D%\LLVM-vs2014_xp del %D%\LLVM-vs2014_xp\toolset.targets
-IF EXIST %D%\LLVM-vs2014_xp rmdir %D%\LLVM-vs2014_xp
-
-
-GOTO LOOPHEAD
-
-:LOOPEND
-echo Done!
+@echo off + +echo Uninstalling MSVC integration... + +REM In general this script should not be used except for development and testing +REM purposes. The proper way to install is via the VSIX, and the proper way to +REM uninstall is through the Visual Studio extension manager. + +REM CD to the directory of this batch file. +cd /d %~dp0 + +SET VCTargets=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Professional\Common7\IDE\VC\VCTargets + +ECHO Uninstalling Common Files +IF EXIST "%VCTargets%\LLVM.Cpp.Common.props" del "%VCTargets%\LLVM.Cpp.Common.props" +IF EXIST "%VCTargets%\LLVM.Cpp.Common.targets" del "%VCTargets%\LLVM.Cpp.Common.targets" + +ECHO Uninstalling x64 Platform Toolset +SET PlatformToolsets=%VCTargets%\Platforms\x64\PlatformToolsets +IF EXIST "%PlatformToolsets%\llvm\Toolset.props" del "%PlatformToolsets%\llvm\Toolset.props" +IF EXIST "%PlatformToolsets%\llvm\Toolset.targets" del "%PlatformToolsets%\llvm\Toolset.targets" +IF EXIST "%PlatformToolsets%\llvm" rd "%PlatformToolsets%\llvm" + +ECHO Uninstalling Win32 Platform Toolset +SET PlatformToolsets=%VCTargets%\Platforms\Win32\PlatformToolsets +IF EXIST "%PlatformToolsets%\llvm\Toolset.props" del "%PlatformToolsets%\llvm\Toolset.props" +IF EXIST "%PlatformToolsets%\llvm\Toolset.targets" del "%PlatformToolsets%\llvm\Toolset.targets" +IF EXIST "%PlatformToolsets%\llvm" rd "%PlatformToolsets%\llvm" + +ECHO Uninstalling C++ Settings UI +IF EXIST "%VCTargets%\1033\llvm-general.xml" del "%VCTargets%\1033\llvm-general.xml" + +echo Done! diff --git a/tools/obj2yaml/CMakeLists.txt b/tools/obj2yaml/CMakeLists.txt index 36dcfd92dfee..c59fe650952b 100644 --- a/tools/obj2yaml/CMakeLists.txt +++ b/tools/obj2yaml/CMakeLists.txt @@ -1,4 +1,5 @@ set(LLVM_LINK_COMPONENTS + BinaryFormat DebugInfoCodeView DebugInfoDWARF Object diff --git a/tools/obj2yaml/coff2yaml.cpp b/tools/obj2yaml/coff2yaml.cpp index 6c4f8437caef..e7e9e19fd3dc 100644 --- a/tools/obj2yaml/coff2yaml.cpp +++ b/tools/obj2yaml/coff2yaml.cpp @@ -159,7 +159,8 @@ void COFFDumper::dumpSections(unsigned NumSections) { NewYAMLSection.Header.PointerToRelocations = COFFSection->PointerToRelocations; NewYAMLSection.Header.SizeOfRawData = COFFSection->SizeOfRawData; - NewYAMLSection.Alignment = ObjSection.getAlignment(); + uint32_t Shift = (COFFSection->Characteristics >> 20) & 0xF; + NewYAMLSection.Alignment = (1U << Shift) >> 1; assert(NewYAMLSection.Alignment <= 8192); ArrayRef<uint8_t> sectionData; @@ -170,7 +171,11 @@ void COFFDumper::dumpSections(unsigned NumSections) { if (NewYAMLSection.Name == ".debug$S") NewYAMLSection.DebugS = CodeViewYAML::fromDebugS(sectionData, SC); else if (NewYAMLSection.Name == ".debug$T") - NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData); + NewYAMLSection.DebugT = CodeViewYAML::fromDebugT(sectionData, + NewYAMLSection.Name); + else if (NewYAMLSection.Name == ".debug$P") + NewYAMLSection.DebugP = CodeViewYAML::fromDebugT(sectionData, + NewYAMLSection.Name); else if (NewYAMLSection.Name == ".debug$H") NewYAMLSection.DebugH = CodeViewYAML::fromDebugH(sectionData); diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index f68bcf4ee402..dea4d1b31827 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -22,10 +22,10 @@ namespace { template <class ELFT> class ELFDumper { typedef object::Elf_Sym_Impl<ELFT> Elf_Sym; - typedef typename object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr; - typedef typename object::ELFFile<ELFT>::Elf_Word Elf_Word; - typedef typename object::ELFFile<ELFT>::Elf_Rel Elf_Rel; - typedef typename object::ELFFile<ELFT>::Elf_Rela Elf_Rela; + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Word Elf_Word; + typedef typename ELFT::Rel Elf_Rel; + typedef typename ELFT::Rela Elf_Rela; ArrayRef<Elf_Shdr> Sections; diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp index 8bf09c2164bf..76786158d989 100644 --- a/tools/obj2yaml/obj2yaml.cpp +++ b/tools/obj2yaml/obj2yaml.cpp @@ -7,14 +7,12 @@ // //===----------------------------------------------------------------------===// -#include "Error.h" #include "obj2yaml.h" +#include "Error.h" #include "llvm/Object/Archive.h" #include "llvm/Object/COFF.h" #include "llvm/Support/CommandLine.h" -#include "llvm/Support/ManagedStatic.h" -#include "llvm/Support/PrettyStackTrace.h" -#include "llvm/Support/Signals.h" +#include "llvm/Support/InitLLVM.h" using namespace llvm; using namespace llvm::object; @@ -62,10 +60,8 @@ cl::opt<std::string> InputFilename(cl::Positional, cl::desc("<input file>"), cl::init("-")); int main(int argc, char *argv[]) { + InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv); - sys::PrintStackTraceOnErrorSignal(argv[0]); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. if (Error Err = dumpInput(InputFilename)) { reportError(InputFilename, std::move(Err)); diff --git a/tools/obj2yaml/wasm2yaml.cpp b/tools/obj2yaml/wasm2yaml.cpp index 1bf8149493c4..dbaf1a2848f5 100644 --- a/tools/obj2yaml/wasm2yaml.cpp +++ b/tools/obj2yaml/wasm2yaml.cpp @@ -53,42 +53,71 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was std::unique_ptr<WasmYAML::CustomSection> CustomSec; if (WasmSec.Name == "name") { std::unique_ptr<WasmYAML::NameSection> NameSec = make_unique<WasmYAML::NameSection>(); - for (const object::SymbolRef& Sym: Obj.symbols()) { - const object::WasmSymbol Symbol = Obj.getWasmSymbol(Sym); - if (Symbol.Type != object::WasmSymbol::SymbolType::DEBUG_FUNCTION_NAME) - continue; + for (const llvm::wasm::WasmFunctionName &Func: Obj.debugNames()) { WasmYAML::NameEntry NameEntry; - NameEntry.Name = Symbol.Name; - NameEntry.Index = Sym.getValue(); + NameEntry.Name = Func.Name; + NameEntry.Index = Func.Index; NameSec->FunctionNames.push_back(NameEntry); } CustomSec = std::move(NameSec); } else if (WasmSec.Name == "linking") { std::unique_ptr<WasmYAML::LinkingSection> LinkingSec = make_unique<WasmYAML::LinkingSection>(); - size_t Index = 0; + LinkingSec->Version = Obj.linkingData().Version; + + ArrayRef<StringRef> Comdats = Obj.linkingData().Comdats; + for (StringRef ComdatName : Comdats) + LinkingSec->Comdats.emplace_back(WasmYAML::Comdat{ComdatName, {}}); + for (auto &Func : Obj.functions()) { + if (Func.Comdat != UINT32_MAX) { + LinkingSec->Comdats[Func.Comdat].Entries.emplace_back( + WasmYAML::ComdatEntry{wasm::WASM_COMDAT_FUNCTION, Func.Index}); + } + } + + uint32_t SegmentIndex = 0; for (const object::WasmSegment &Segment : Obj.dataSegments()) { if (!Segment.Data.Name.empty()) { WasmYAML::SegmentInfo SegmentInfo; SegmentInfo.Name = Segment.Data.Name; - SegmentInfo.Index = Index; + SegmentInfo.Index = SegmentIndex; SegmentInfo.Alignment = Segment.Data.Alignment; SegmentInfo.Flags = Segment.Data.Flags; LinkingSec->SegmentInfos.push_back(SegmentInfo); } - Index++; + if (Segment.Data.Comdat != UINT32_MAX) { + LinkingSec->Comdats[Segment.Data.Comdat].Entries.emplace_back( + WasmYAML::ComdatEntry{wasm::WASM_COMDAT_DATA, SegmentIndex}); + } + SegmentIndex++; } - for (const object::SymbolRef& Sym: Obj.symbols()) { - const object::WasmSymbol Symbol = Obj.getWasmSymbol(Sym); - if (Symbol.Flags != 0) { - WasmYAML::SymbolInfo Info{Symbol.Name, Symbol.Flags}; - LinkingSec->SymbolInfos.emplace_back(Info); + + uint32_t SymbolIndex = 0; + for (const wasm::WasmSymbolInfo &Symbol : Obj.linkingData().SymbolTable) { + WasmYAML::SymbolInfo Info; + Info.Index = SymbolIndex++; + Info.Kind = static_cast<uint32_t>(Symbol.Kind); + Info.Name = Symbol.Name; + Info.Flags = Symbol.Flags; + switch (Symbol.Kind) { + case wasm::WASM_SYMBOL_TYPE_DATA: + Info.DataRef = Symbol.DataRef; + break; + case wasm::WASM_SYMBOL_TYPE_FUNCTION: + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + Info.ElementIndex = Symbol.ElementIndex; + break; + case wasm::WASM_SYMBOL_TYPE_SECTION: + Info.ElementIndex = Symbol.ElementIndex; + break; } + LinkingSec->SymbolTable.emplace_back(Info); } - LinkingSec->DataSize = Obj.linkingData().DataSize; + for (const wasm::WasmInitFunc &Func : Obj.linkingData().InitFunctions) { - WasmYAML::InitFunction F{Func.Priority, Func.FunctionIndex}; + WasmYAML::InitFunction F{Func.Priority, Func.Symbol}; LinkingSec->InitFunctions.emplace_back(F); } + CustomSec = std::move(LinkingSec); } else { CustomSec = make_unique<WasmYAML::CustomSection>(WasmSec.Name); @@ -186,8 +215,9 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { auto GlobalSec = make_unique<WasmYAML::GlobalSection>(); for (auto &Global : Obj.globals()) { WasmYAML::Global G; - G.Type = Global.Type; - G.Mutable = Global.Mutable; + G.Index = Global.Index; + G.Type = Global.Type.Type; + G.Mutable = Global.Type.Mutable; G.InitExpr = Global.InitExpr; GlobalSec->Globals.push_back(G); } @@ -230,6 +260,7 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { auto CodeSec = make_unique<WasmYAML::CodeSection>(); for (auto &Func : Obj.functions()) { WasmYAML::Function Function; + Function.Index = Func.Index; for (auto &Local : Func.Locals) { WasmYAML::LocalDecl LocalDecl; LocalDecl.Type = Local.Type; diff --git a/tools/opt-viewer/opt-diff.py b/tools/opt-viewer/opt-diff.py index 6b20d82c7eec..f3bfd1860b91 100755 --- a/tools/opt-viewer/opt-diff.py +++ b/tools/opt-viewer/opt-diff.py @@ -19,7 +19,6 @@ except ImportError: import optrecord import argparse from collections import defaultdict -from multiprocessing import cpu_count, Pool if __name__ == '__main__': parser = argparse.ArgumentParser(description=desc) @@ -34,16 +33,22 @@ if __name__ == '__main__': parser.add_argument( '--jobs', '-j', - default=cpu_count(), + default=None, type=int, help='Max job count (defaults to %(default)s, the current CPU count)') parser.add_argument( + '--max-size', + '-m', + default=100000, + type=int, + help='Maximum number of remarks stored in an output file') + parser.add_argument( '--no-progress-indicator', '-n', action='store_true', default=False, help='Do not display any indicator of how many YAML files were read.') - parser.add_argument('--output', '-o', default='diff.opt.yaml') + parser.add_argument('--output', '-o', default='diff{}.opt.yaml') args = parser.parse_args() files1 = optrecord.find_opt_files(args.yaml_dir_or_file_1) @@ -61,9 +66,10 @@ if __name__ == '__main__': for r in removed: r.Added = False - result = added | removed + result = list(added | removed) for r in result: r.recover_yaml_structure() - with open(args.output, 'w') as stream: - yaml.dump_all(result, stream) + for i in range(0, len(result), args.max_size): + with open(args.output.format(i / args.max_size), 'w') as stream: + yaml.dump_all(result[i:i + args.max_size], stream) diff --git a/tools/opt-viewer/opt-stats.py b/tools/opt-viewer/opt-stats.py index 5c415df1bb6d..03de23bdb275 100755 --- a/tools/opt-viewer/opt-stats.py +++ b/tools/opt-viewer/opt-stats.py @@ -30,7 +30,7 @@ if __name__ == '__main__': parser.add_argument( '--jobs', '-j', - default=cpu_count(), + default=None, type=int, help='Max job count (defaults to %(default)s, the current CPU count)') parser.add_argument( diff --git a/tools/opt-viewer/opt-viewer.py b/tools/opt-viewer/opt-viewer.py index 27b36064ced9..4887043e0f96 100755 --- a/tools/opt-viewer/opt-viewer.py +++ b/tools/opt-viewer/opt-viewer.py @@ -43,7 +43,8 @@ def suppress(remark): return False class SourceFileRenderer: - def __init__(self, source_dir, output_dir, filename): + def __init__(self, source_dir, output_dir, filename, no_highlight): + self.filename = filename existing_filename = None if os.path.exists(filename): existing_filename = filename @@ -52,6 +53,7 @@ class SourceFileRenderer: if os.path.exists(fn): existing_filename = fn + self.no_highlight = no_highlight self.stream = codecs.open(os.path.join(output_dir, optrecord.html_file_name(filename)), 'w', encoding='utf-8') if existing_filename: self.source_stream = open(existing_filename) @@ -69,7 +71,7 @@ class SourceFileRenderer: def render_source_lines(self, stream, line_remarks): file_text = stream.read() - if args.no_highlight: + if self.no_highlight: html_highlighted = file_text.decode('utf-8') else: html_highlighted = highlight( @@ -130,6 +132,7 @@ class SourceFileRenderer: print(''' <html> +<title>{}</title> <meta charset="utf-8" /> <head> <link rel='stylesheet' type='text/css' href='style.css'> @@ -146,7 +149,7 @@ class SourceFileRenderer: <th style="width: 15%">Inline Context</td> </tr> </thead> -<tbody>''', file=self.stream) +<tbody>'''.format(os.path.basename(self.filename)), file=self.stream) self.render_source_lines(self.source_stream, line_remarks) print(''' @@ -157,9 +160,10 @@ class SourceFileRenderer: class IndexRenderer: - def __init__(self, output_dir, should_display_hotness): + def __init__(self, output_dir, should_display_hotness, max_hottest_remarks_on_index): self.stream = codecs.open(os.path.join(output_dir, 'index.html'), 'w', encoding='utf-8') self.should_display_hotness = should_display_hotness + self.max_hottest_remarks_on_index = max_hottest_remarks_on_index def render_entry(self, r, odd): escaped_name = cgi.escape(r.DemangledFunctionName) @@ -189,8 +193,8 @@ class IndexRenderer: </tr>''', file=self.stream) max_entries = None - if should_display_hotness: - max_entries = args.max_hottest_remarks_on_index + if self.should_display_hotness: + max_entries = self.max_hottest_remarks_on_index for i, remark in enumerate(all_remarks[:max_entries]): if not suppress(remark): @@ -201,11 +205,11 @@ class IndexRenderer: </html>''', file=self.stream) -def _render_file(source_dir, output_dir, ctx, entry): +def _render_file(source_dir, output_dir, ctx, no_highlight, entry): global context context = ctx filename, remarks = entry - SourceFileRenderer(source_dir, output_dir, filename).render(remarks) + SourceFileRenderer(source_dir, output_dir, filename, no_highlight).render(remarks) def map_remarks(all_remarks): @@ -227,7 +231,9 @@ def generate_report(all_remarks, file_remarks, source_dir, output_dir, + no_highlight, should_display_hotness, + max_hottest_remarks_on_index, num_jobs, should_print_progress): try: @@ -238,25 +244,27 @@ def generate_report(all_remarks, else: raise - _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context) if should_print_progress: - print('Rendering HTML files...') - optpmap.pmap(_render_file_bound, - file_remarks.items(), - num_jobs, - should_print_progress) - + print('Rendering index page...') if should_display_hotness: sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.Hotness, r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function), reverse=True) else: sorted_remarks = sorted(optrecord.itervalues(all_remarks), key=lambda r: (r.File, r.Line, r.Column, r.PassWithDiffPrefix, r.yaml_tag, r.Function)) - IndexRenderer(args.output_dir, should_display_hotness).render(sorted_remarks) + IndexRenderer(output_dir, should_display_hotness, max_hottest_remarks_on_index).render(sorted_remarks) shutil.copy(os.path.join(os.path.dirname(os.path.realpath(__file__)), "style.css"), output_dir) + _render_file_bound = functools.partial(_render_file, source_dir, output_dir, context, no_highlight) + if should_print_progress: + print('Rendering HTML files...') + optpmap.pmap(_render_file_bound, + file_remarks.items(), + num_jobs, + should_print_progress) -if __name__ == '__main__': + +def main(): parser = argparse.ArgumentParser(description=desc) parser.add_argument( 'yaml_dirs_or_files', @@ -273,7 +281,7 @@ if __name__ == '__main__': parser.add_argument( '--jobs', '-j', - default=cpu_count(), + default=None, type=int, help='Max job count (defaults to %(default)s, the current CPU count)') parser.add_argument( @@ -301,6 +309,10 @@ if __name__ == '__main__': parser.add_argument( '--demangler', help='Set the demangler to be used (defaults to %s)' % optrecord.Remark.default_demangler) + + # Do not make this a global variable. Values needed to be propagated through + # to individual classes and functions to be portable with multiprocessing across + # Windows and non-Windows. args = parser.parse_args() print_progress = not args.no_progress_indicator @@ -321,6 +333,11 @@ if __name__ == '__main__': file_remarks, args.source_dir, args.output_dir, + args.no_highlight, should_display_hotness, + args.max_hottest_remarks_on_index, args.jobs, print_progress) + +if __name__ == '__main__': + main() diff --git a/tools/opt-viewer/optpmap.py b/tools/opt-viewer/optpmap.py index 16cb22e21491..db6b079b3a6d 100644 --- a/tools/opt-viewer/optpmap.py +++ b/tools/opt-viewer/optpmap.py @@ -41,13 +41,15 @@ def pmap(func, iterable, processes, should_print_progress, *args, **kwargs): _total = multiprocessing.Value('i', len(iterable)) func_and_args = [(func, arg, should_print_progress,) for arg in iterable] - if processes <= 1: + if processes == 1: result = map(_wrapped_func, func_and_args, *args, **kwargs) else: pool = multiprocessing.Pool(initializer=_init, initargs=(_current, _total,), processes=processes) result = pool.map(_wrapped_func, func_and_args, *args, **kwargs) + pool.close() + pool.join() if should_print_progress: sys.stdout.write('\r') diff --git a/tools/opt-viewer/optrecord.py b/tools/opt-viewer/optrecord.py index f79e05124b29..8cf22ee3f05c 100644 --- a/tools/opt-viewer/optrecord.py +++ b/tools/opt-viewer/optrecord.py @@ -328,6 +328,6 @@ def find_opt_files(*dirs_or_files): subdirs[:] = [d for d in subdirs if not os.path.ismount(os.path.join(dir, d))] for file in files: - if fnmatch.fnmatch(file, "*.opt.yaml"): + if fnmatch.fnmatch(file, "*.opt.yaml*"): all.append(os.path.join(dir, file)) return all diff --git a/tools/opt/BreakpointPrinter.cpp b/tools/opt/BreakpointPrinter.cpp index e5614ed061e3..d3f54c034f55 100644 --- a/tools/opt/BreakpointPrinter.cpp +++ b/tools/opt/BreakpointPrinter.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief Breakpoint location printer. +/// Breakpoint location printer. /// //===----------------------------------------------------------------------===// #include "BreakpointPrinter.h" diff --git a/tools/opt/BreakpointPrinter.h b/tools/opt/BreakpointPrinter.h index 81c88e19199e..57670e5ee8d8 100644 --- a/tools/opt/BreakpointPrinter.h +++ b/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/tools/opt/CMakeLists.txt b/tools/opt/CMakeLists.txt index dedc25143cf4..f03d11516657 100644 --- a/tools/opt/CMakeLists.txt +++ b/tools/opt/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + AggressiveInstCombine Analysis BitWriter CodeGen diff --git a/tools/opt/Debugify.cpp b/tools/opt/Debugify.cpp index 40ee545c098d..6c3cdc75e334 100644 --- a/tools/opt/Debugify.cpp +++ b/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/tools/opt/Debugify.h b/tools/opt/Debugify.h new file mode 100644 index 000000000000..d1a60c73e723 --- /dev/null +++ b/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/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp index a3f16f2538c4..a91d4cb5f9cd 100644 --- a/tools/opt/NewPMDriver.cpp +++ b/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/tools/opt/NewPMDriver.h b/tools/opt/NewPMDriver.h index e5490deaeaf5..7d74a5777d11 100644 --- a/tools/opt/NewPMDriver.h +++ b/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/tools/opt/PassPrinters.cpp b/tools/opt/PassPrinters.cpp index f52b52080949..310d491c06a5 100644 --- a/tools/opt/PassPrinters.cpp +++ b/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/tools/opt/PassPrinters.h b/tools/opt/PassPrinters.h index 14b6e43d18e0..e66f3f457b7a 100644 --- a/tools/opt/PassPrinters.h +++ b/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/tools/opt/opt.cpp b/tools/opt/opt.cpp index 5bc00ea35ae5..6e287b6c0ab6 100644 --- a/tools/opt/opt.cpp +++ b/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 @@ -402,11 +462,13 @@ int main(int argc, char **argv) { initializeSjLjEHPreparePass(Registry); initializePreISelIntrinsicLoweringLegacyPassPass(Registry); initializeGlobalMergePass(Registry); + initializeIndirectBrExpandPassPass(Registry); initializeInterleavedAccessPass(Registry); initializeEntryExitInstrumenterPass(Registry); initializePostInlineEntryExitInstrumenterPass(Registry); initializeUnreachableBlockElimLegacyPassPass(Registry); initializeExpandReductionsPass(Registry); + initializeWasmEHPreparePass(Registry); initializeWriteBitcodePassPass(Registry); #ifdef LINK_POLLY_INTO_TOOLS @@ -448,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()); @@ -459,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(). @@ -468,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; @@ -547,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); @@ -569,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) { @@ -714,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; @@ -748,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) @@ -779,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(); diff --git a/tools/sancov/CMakeLists.txt b/tools/sancov/CMakeLists.txt index e92b1fcbb862..2a7707289335 100644 --- a/tools/sancov/CMakeLists.txt +++ b/tools/sancov/CMakeLists.txt @@ -13,5 +13,5 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_tool(sancov - sancov.cc + sancov.cpp ) diff --git a/tools/sancov/sancov.cc b/tools/sancov/sancov.cpp index f3bc635dde8d..0bddd35a96f2 100644 --- a/tools/sancov/sancov.cc +++ b/tools/sancov/sancov.cpp @@ -1,4 +1,4 @@ -//===-- sancov.cc --------------------------------------------===// +//===-- sancov.cpp --------------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -6,7 +6,6 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -// // This file is a command-line tool for reading and analyzing sanitizer // coverage. //===----------------------------------------------------------------------===// diff --git a/tools/verify-uselistorder/verify-uselistorder.cpp b/tools/verify-uselistorder/verify-uselistorder.cpp index e391f78d3e77..99c7007e4e99 100644 --- a/tools/verify-uselistorder/verify-uselistorder.cpp +++ b/tools/verify-uselistorder/verify-uselistorder.cpp @@ -42,10 +42,8 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FileUtilities.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/SystemUtils.h" #include "llvm/Support/raw_ostream.h" @@ -85,7 +83,7 @@ struct ValueMapping { DenseMap<const Value *, unsigned> IDs; std::vector<const Value *> Values; - /// \brief Construct a value mapping for module. + /// Construct a value mapping for module. /// /// Creates mapping from every value in \c M to an ID. This mapping includes /// un-referencable values. @@ -98,7 +96,7 @@ struct ValueMapping { /// mapping, but others -- which wouldn't be serialized -- are not. ValueMapping(const Module &M); - /// \brief Map a value. + /// Map a value. /// /// Maps a value. If it's a constant, maps all of its operands first. void map(const Value *V); @@ -109,7 +107,7 @@ struct ValueMapping { bool TempFile::init(const std::string &Ext) { SmallVector<char, 64> Vector; - DEBUG(dbgs() << " - create-temp-file\n"); + LLVM_DEBUG(dbgs() << " - create-temp-file\n"); if (auto EC = sys::fs::createTemporaryFile("uselistorder", Ext, Vector)) { errs() << "verify-uselistorder: error: " << EC.message() << "\n"; return true; @@ -124,7 +122,7 @@ bool TempFile::init(const std::string &Ext) { } bool TempFile::writeBitcode(const Module &M) const { - DEBUG(dbgs() << " - write bitcode\n"); + LLVM_DEBUG(dbgs() << " - write bitcode\n"); std::error_code EC; raw_fd_ostream OS(Filename, EC, sys::fs::F_None); if (EC) { @@ -132,12 +130,12 @@ bool TempFile::writeBitcode(const Module &M) const { return true; } - WriteBitcodeToFile(&M, OS, /* ShouldPreserveUseListOrder */ true); + WriteBitcodeToFile(M, OS, /* ShouldPreserveUseListOrder */ true); return false; } bool TempFile::writeAssembly(const Module &M) const { - DEBUG(dbgs() << " - write assembly\n"); + LLVM_DEBUG(dbgs() << " - write assembly\n"); std::error_code EC; raw_fd_ostream OS(Filename, EC, sys::fs::F_Text); if (EC) { @@ -150,7 +148,7 @@ bool TempFile::writeAssembly(const Module &M) const { } std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const { - DEBUG(dbgs() << " - read bitcode\n"); + LLVM_DEBUG(dbgs() << " - read bitcode\n"); ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOr = MemoryBuffer::getFile(Filename); if (!BufferOr) { @@ -171,7 +169,7 @@ std::unique_ptr<Module> TempFile::readBitcode(LLVMContext &Context) const { } std::unique_ptr<Module> TempFile::readAssembly(LLVMContext &Context) const { - DEBUG(dbgs() << " - read assembly\n"); + LLVM_DEBUG(dbgs() << " - read assembly\n"); SMDiagnostic Err; std::unique_ptr<Module> M = parseAssemblyFile(Filename, Err, Context); if (!M.get()) @@ -290,9 +288,9 @@ static void debugSizeMismatch(const ValueMapping &L, const ValueMapping &R) { #endif static bool matches(const ValueMapping &LM, const ValueMapping &RM) { - DEBUG(dbgs() << "compare value maps\n"); + LLVM_DEBUG(dbgs() << "compare value maps\n"); if (LM.Values.size() != RM.Values.size()) { - DEBUG(debugSizeMismatch(LM, RM)); + LLVM_DEBUG(debugSizeMismatch(LM, RM)); return false; } @@ -319,22 +317,22 @@ static bool matches(const ValueMapping &LM, const ValueMapping &RM) { while (LU != LE) { if (RU == RE) { - DEBUG(debugUserMismatch(LM, RM, I)); + LLVM_DEBUG(debugUserMismatch(LM, RM, I)); return false; } if (LM.lookup(LU->getUser()) != RM.lookup(RU->getUser())) { - DEBUG(debugUserMismatch(LM, RM, I)); + LLVM_DEBUG(debugUserMismatch(LM, RM, I)); return false; } if (LU->getOperandNo() != RU->getOperandNo()) { - DEBUG(debugUserMismatch(LM, RM, I)); + LLVM_DEBUG(debugUserMismatch(LM, RM, I)); return false; } skipUnmappedUsers(++LU, LE, LM); skipUnmappedUsers(++RU, RE, RM); } if (RU != RE) { - DEBUG(debugUserMismatch(LM, RM, I)); + LLVM_DEBUG(debugUserMismatch(LM, RM, I)); return false; } } @@ -399,7 +397,7 @@ static void shuffleValueUseLists(Value *V, std::minstd_rand0 &Gen, // Generate random numbers between 10 and 99, which will line up nicely in // debug output. We're not worried about collisons here. - DEBUG(dbgs() << "V = "; V->dump()); + LLVM_DEBUG(dbgs() << "V = "; V->dump()); std::uniform_int_distribution<short> Dist(10, 99); SmallDenseMap<const Use *, short, 16> Order; auto compareUses = @@ -408,16 +406,16 @@ static void shuffleValueUseLists(Value *V, std::minstd_rand0 &Gen, for (const Use &U : V->uses()) { auto I = Dist(Gen); Order[&U] = I; - DEBUG(dbgs() << " - order: " << I << ", op = " << U.getOperandNo() - << ", U = "; - U.getUser()->dump()); + LLVM_DEBUG(dbgs() << " - order: " << I << ", op = " << U.getOperandNo() + << ", U = "; + U.getUser()->dump()); } } while (std::is_sorted(V->use_begin(), V->use_end(), compareUses)); - DEBUG(dbgs() << " => shuffle\n"); + LLVM_DEBUG(dbgs() << " => shuffle\n"); V->sortUseList(compareUses); - DEBUG({ + LLVM_DEBUG({ for (const Use &U : V->uses()) { dbgs() << " - order: " << Order.lookup(&U) << ", op = " << U.getOperandNo() << ", U = "; @@ -439,7 +437,7 @@ static void reverseValueUseLists(Value *V, DenseSet<Value *> &Seen) { // Nothing to shuffle for 0 or 1 users. return; - DEBUG({ + LLVM_DEBUG({ dbgs() << "V = "; V->dump(); for (const Use &U : V->uses()) { @@ -451,7 +449,7 @@ static void reverseValueUseLists(Value *V, DenseSet<Value *> &Seen) { V->reverseUseList(); - DEBUG({ + LLVM_DEBUG({ for (const Use &U : V->uses()) { dbgs() << " - order: op = " << U.getOperandNo() << ", U = "; U.getUser()->dump(); @@ -517,23 +515,21 @@ static void shuffleUseLists(Module &M, unsigned SeedOffset) { std::minstd_rand0 Gen(std::minstd_rand0::default_seed + SeedOffset); DenseSet<Value *> Seen; changeUseLists(M, [&](Value *V) { shuffleValueUseLists(V, Gen, Seen); }); - DEBUG(dbgs() << "\n"); + LLVM_DEBUG(dbgs() << "\n"); } static void reverseUseLists(Module &M) { DenseSet<Value *> Seen; changeUseLists(M, [&](Value *V) { reverseValueUseLists(V, Seen); }); - DEBUG(dbgs() << "\n"); + LLVM_DEBUG(dbgs() << "\n"); } 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; cl::ParseCommandLineOptions(argc, argv, diff --git a/tools/xcode-toolchain/CMakeLists.txt b/tools/xcode-toolchain/CMakeLists.txt index d433c52febf1..0ae5e374fe9f 100644 --- a/tools/xcode-toolchain/CMakeLists.txt +++ b/tools/xcode-toolchain/CMakeLists.txt @@ -49,7 +49,7 @@ if(NOT XCODE_VERSION) OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_FILE /dev/null ) - string(REGEX MATCH "Xcode ([0-9]([.][0-9])+)" version_match ${xcodebuild_version}) + string(REGEX MATCH "Xcode ([0-9][0-9]?([.][0-9])+)" version_match ${xcodebuild_version}) if(version_match) message(STATUS "Identified Xcode Version: ${CMAKE_MATCH_1}") set(XCODE_VERSION ${CMAKE_MATCH_1}) diff --git a/tools/yaml2obj/yaml2coff.cpp b/tools/yaml2obj/yaml2coff.cpp index 648317e97bb3..befa01b369c0 100644 --- a/tools/yaml2obj/yaml2coff.cpp +++ b/tools/yaml2obj/yaml2coff.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief The COFF component of yaml2obj. +/// The COFF component of yaml2obj. /// //===----------------------------------------------------------------------===// @@ -233,7 +233,10 @@ static bool layoutCOFF(COFFParser &CP) { } } else if (S.Name == ".debug$T") { if (S.SectionData.binary_size() == 0) - S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator); + S.SectionData = CodeViewYAML::toDebugT(S.DebugT, CP.Allocator, S.Name); + } else if (S.Name == ".debug$P") { + if (S.SectionData.binary_size() == 0) + S.SectionData = CodeViewYAML::toDebugT(S.DebugP, CP.Allocator, S.Name); } else if (S.Name == ".debug$H") { if (S.DebugH.hasValue() && S.SectionData.binary_size() == 0) S.SectionData = CodeViewYAML::toDebugH(*S.DebugH, CP.Allocator); diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index 21648469654a..7ca5ac206c9e 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief The ELF component of yaml2obj. +/// The ELF component of yaml2obj. /// //===----------------------------------------------------------------------===// @@ -19,6 +19,7 @@ #include "llvm/Object/ELFObjectFile.h" #include "llvm/ObjectYAML/ELFYAML.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" @@ -102,27 +103,29 @@ static void zero(T &Obj) { } namespace { -/// \brief "Single point of truth" for the ELF file construction. +/// "Single point of truth" for the ELF file construction. /// TODO: This class still has a ways to go before it is truly a "single /// point of truth". template <class ELFT> class ELFState { - typedef typename object::ELFFile<ELFT>::Elf_Ehdr Elf_Ehdr; - typedef typename object::ELFFile<ELFT>::Elf_Phdr Elf_Phdr; - typedef typename object::ELFFile<ELFT>::Elf_Shdr Elf_Shdr; - typedef typename object::ELFFile<ELFT>::Elf_Sym Elf_Sym; - typedef typename object::ELFFile<ELFT>::Elf_Rel Elf_Rel; - typedef typename object::ELFFile<ELFT>::Elf_Rela Elf_Rela; + typedef typename ELFT::Ehdr Elf_Ehdr; + typedef typename ELFT::Phdr Elf_Phdr; + typedef typename ELFT::Shdr Elf_Shdr; + typedef typename ELFT::Sym Elf_Sym; + typedef typename ELFT::Rel Elf_Rel; + typedef typename ELFT::Rela Elf_Rela; + typedef typename ELFT::Relr Elf_Relr; + typedef typename ELFT::Dyn Elf_Dyn; enum class SymtabType { Static, Dynamic }; - /// \brief The future ".strtab" section. + /// The future ".strtab" section. StringTableBuilder DotStrtab{StringTableBuilder::ELF}; - /// \brief The future ".shstrtab" section. + /// The future ".shstrtab" section. StringTableBuilder DotShStrtab{StringTableBuilder::ELF}; - /// \brief The future ".dynstr" section. + /// The future ".dynstr" section. StringTableBuilder DotDynstr{StringTableBuilder::ELF}; NameToIdxMap SN2I; @@ -243,8 +246,8 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, if (!Sec->Link.empty()) { unsigned Index; if (SN2I.lookup(Sec->Link, Index)) { - errs() << "error: Unknown section referenced: '" << Sec->Link - << "' at YAML section '" << Sec->Name << "'.\n"; + WithColor::error() << "Unknown section referenced: '" << Sec->Link + << "' at YAML section '" << Sec->Name << "'.\n"; return false; } SHeader.sh_link = Index; @@ -260,8 +263,8 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, unsigned Index; if (SN2I.lookup(S->Info, Index)) { if (S->Info.getAsInteger(0, Index)) { - errs() << "error: Unknown section referenced: '" << S->Info - << "' at YAML section '" << S->Name << "'.\n"; + WithColor::error() << "Unknown section referenced: '" << S->Info + << "' at YAML section '" << S->Name << "'.\n"; return false; } } @@ -272,8 +275,8 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, } else if (auto S = dyn_cast<ELFYAML::Group>(Sec.get())) { unsigned SymIdx; if (SymN2I.lookup(S->Info, SymIdx)) { - errs() << "error: Unknown symbol referenced: '" << S->Info - << "' at YAML section '" << S->Name << "'.\n"; + WithColor::error() << "Unknown symbol referenced: '" << S->Info + << "' at YAML section '" << S->Name << "'.\n"; return false; } SHeader.sh_info = SymIdx; @@ -430,8 +433,8 @@ void ELFState<ELFT>::addSymbols(const std::vector<ELFYAML::Symbol> &Symbols, if (!Sym.Section.empty()) { unsigned Index; if (SN2I.lookup(Sym.Section, Index)) { - errs() << "error: Unknown section referenced: '" << Sym.Section - << "' by YAML symbol " << Sym.Name << ".\n"; + WithColor::error() << "Unknown section referenced: '" << Sym.Section + << "' by YAML symbol " << Sym.Name << ".\n"; exit(1); } Symbol.st_shndx = Index; @@ -458,7 +461,12 @@ ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, Section.Content.writeAsBinary(OS); for (auto i = Section.Content.binary_size(); i < Section.Size; ++i) OS.write(0); - SHeader.sh_entsize = 0; + if (Section.Type == llvm::ELF::SHT_RELR) + SHeader.sh_entsize = sizeof(Elf_Relr); + else if (Section.Type == llvm::ELF::SHT_DYNAMIC) + SHeader.sh_entsize = sizeof(Elf_Dyn); + else + SHeader.sh_entsize = 0; SHeader.sh_size = Section.Size; } @@ -513,7 +521,7 @@ template <class ELFT> bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, const ELFYAML::Group &Section, ContiguousBlobAccumulator &CBA) { - typedef typename object::ELFFile<ELFT>::Elf_Word Elf_Word; + typedef typename ELFT::Word Elf_Word; assert(Section.Type == llvm::ELF::SHT_GROUP && "Section type is not SHT_GROUP"); @@ -528,9 +536,9 @@ bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, if (member.sectionNameOrType == "GRP_COMDAT") sectionIndex = llvm::ELF::GRP_COMDAT; else if (SN2I.lookup(member.sectionNameOrType, sectionIndex)) { - errs() << "error: Unknown section referenced: '" - << member.sectionNameOrType << "' at YAML section' " - << Section.Name << "\n"; + WithColor::error() << "Unknown section referenced: '" + << member.sectionNameOrType << "' at YAML section' " + << Section.Name << "\n"; return false; } SIdx = sectionIndex; @@ -574,8 +582,8 @@ template <class ELFT> bool ELFState<ELFT>::buildSectionIndex() { DotShStrtab.add(Name); // "+ 1" to take into account the SHT_NULL entry. if (SN2I.addName(Name, i + 1)) { - errs() << "error: Repeated section name: '" << Name - << "' at YAML section number " << i << ".\n"; + WithColor::error() << "Repeated section name: '" << Name + << "' at YAML section number " << i << ".\n"; return false; } } @@ -602,7 +610,7 @@ ELFState<ELFT>::buildSymbolIndex(std::size_t &StartIndex, if (Sym.Name.empty()) continue; if (SymN2I.addName(Sym.Name, StartIndex)) { - errs() << "error: Repeated symbol name: '" << Sym.Name << "'.\n"; + WithColor::error() << "Repeated symbol name: '" << Sym.Name << "'.\n"; return false; } } @@ -692,20 +700,15 @@ static bool isLittleEndian(const ELFYAML::Object &Doc) { } int yaml2elf(llvm::ELFYAML::Object &Doc, raw_ostream &Out) { - using object::ELFType; - typedef ELFType<support::little, true> LE64; - typedef ELFType<support::big, true> BE64; - typedef ELFType<support::little, false> LE32; - typedef ELFType<support::big, false> BE32; if (is64Bit(Doc)) { if (isLittleEndian(Doc)) - return ELFState<LE64>::writeELF(Out, Doc); + return ELFState<object::ELF64LE>::writeELF(Out, Doc); else - return ELFState<BE64>::writeELF(Out, Doc); + return ELFState<object::ELF64BE>::writeELF(Out, Doc); } else { if (isLittleEndian(Doc)) - return ELFState<LE32>::writeELF(Out, Doc); + return ELFState<object::ELF32LE>::writeELF(Out, Doc); else - return ELFState<BE32>::writeELF(Out, Doc); + return ELFState<object::ELF32BE>::writeELF(Out, Doc); } } diff --git a/tools/yaml2obj/yaml2macho.cpp b/tools/yaml2obj/yaml2macho.cpp index 34b6ac2029fc..23fe946f1da3 100644 --- a/tools/yaml2obj/yaml2macho.cpp +++ b/tools/yaml2obj/yaml2macho.cpp @@ -8,7 +8,7 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief The Mach component of yaml2obj. +/// The Mach component of yaml2obj. /// //===----------------------------------------------------------------------===// @@ -417,10 +417,10 @@ Error MachOWriter::writeLinkEditData(raw_ostream &OS) { } } - std::sort(WriteQueue.begin(), WriteQueue.end(), - [](const writeOperation &a, const writeOperation &b) { - return a.first < b.first; - }); + llvm::sort(WriteQueue.begin(), WriteQueue.end(), + [](const writeOperation &a, const writeOperation &b) { + return a.first < b.first; + }); for (auto writeOp : WriteQueue) { ZeroToOffset(OS, writeOp.first); diff --git a/tools/yaml2obj/yaml2obj.cpp b/tools/yaml2obj/yaml2obj.cpp index 0f21d7a54708..949daffa9e8c 100644 --- a/tools/yaml2obj/yaml2obj.cpp +++ b/tools/yaml2obj/yaml2obj.cpp @@ -19,10 +19,8 @@ #include "llvm/ObjectYAML/ObjectYAML.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.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/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" @@ -70,10 +68,8 @@ static int convertYAML(yaml::Input &YIn, raw_ostream &Out) { } int main(int argc, char **argv) { + InitLLVM X(argc, argv); cl::ParseCommandLineOptions(argc, argv); - sys::PrintStackTraceOnErrorSignal(argv[0]); - PrettyStackTraceProgram X(argc, argv); - llvm_shutdown_obj Y; // Call llvm_shutdown() on exit. if (OutputFilename.empty()) OutputFilename = "-"; diff --git a/tools/yaml2obj/yaml2obj.h b/tools/yaml2obj/yaml2obj.h index cb8f11904916..fc784067a9f0 100644 --- a/tools/yaml2obj/yaml2obj.h +++ b/tools/yaml2obj/yaml2obj.h @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// /// \file -/// \brief Common declarations for yaml2obj +/// Common declarations for yaml2obj //===----------------------------------------------------------------------===// #ifndef LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H #define LLVM_TOOLS_YAML2OBJ_YAML2OBJ_H diff --git a/tools/yaml2obj/yaml2wasm.cpp b/tools/yaml2obj/yaml2wasm.cpp index 792f7c108bc1..a7ec25e31c73 100644 --- a/tools/yaml2obj/yaml2wasm.cpp +++ b/tools/yaml2obj/yaml2wasm.cpp @@ -8,10 +8,11 @@ //===----------------------------------------------------------------------===// /// /// \file -/// \brief The Wasm component of yaml2obj. +/// The Wasm component of yaml2obj. /// //===----------------------------------------------------------------------===// // + #include "llvm/ObjectYAML/ObjectYAML.h" #include "llvm/Support/Endian.h" #include "llvm/Support/LEB128.h" @@ -26,7 +27,8 @@ public: int writeWasm(raw_ostream &OS); private: - int writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec); + int writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, + uint32_t SectionIndex); int writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::TypeSection &Section); @@ -45,6 +47,8 @@ private: int writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section); WasmYAML::Object &Obj; + uint32_t NumImportedFunctions = 0; + uint32_t NumImportedGlobals = 0; }; static int writeUint64(raw_ostream &OS, uint64_t Value) { @@ -75,7 +79,7 @@ static int writeStringRef(const StringRef &Str, raw_ostream &OS) { } static int writeLimits(const WasmYAML::Limits &Lim, raw_ostream &OS) { - encodeULEB128(Lim.Flags, OS); + writeUint8(OS, Lim.Flags); encodeULEB128(Lim.Initial, OS); if (Lim.Flags & wasm::WASM_LIMITS_FLAG_HAS_MAX) encodeULEB128(Lim.Maximum, OS); @@ -101,7 +105,7 @@ static int writeInitExpr(const wasm::WasmInitExpr &InitExpr, raw_ostream &OS) { encodeULEB128(InitExpr.Value.Global, OS); break; default: - errs() << "Unknown opcode in init_expr: " << InitExpr.Opcode; + errs() << "Unknown opcode in init_expr: " << InitExpr.Opcode << "\n"; return 1; } writeUint8(OS, wasm::WASM_OPCODE_END); @@ -130,22 +134,43 @@ public: int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section) { writeStringRef(Section.Name, OS); + encodeULEB128(Section.Version, OS); SubSectionWriter SubSection(OS); - // DATA_SIZE subsection - encodeULEB128(wasm::WASM_DATA_SIZE, OS); - encodeULEB128(Section.DataSize, SubSection.GetStream()); - SubSection.Done(); - - // SYMBOL_INFO subsection - if (Section.SymbolInfos.size()) { - encodeULEB128(wasm::WASM_SYMBOL_INFO, OS); - - encodeULEB128(Section.SymbolInfos.size(), SubSection.GetStream()); - for (const WasmYAML::SymbolInfo &Info : Section.SymbolInfos) { - writeStringRef(Info.Name, SubSection.GetStream()); + // SYMBOL_TABLE subsection + if (Section.SymbolTable.size()) { + writeUint8(OS, wasm::WASM_SYMBOL_TABLE); + + encodeULEB128(Section.SymbolTable.size(), SubSection.GetStream()); +#ifndef NDEBUG + uint32_t SymbolIndex = 0; +#endif + for (const WasmYAML::SymbolInfo &Info : Section.SymbolTable) { + assert(Info.Index == SymbolIndex++); + writeUint8(SubSection.GetStream(), Info.Kind); encodeULEB128(Info.Flags, SubSection.GetStream()); + switch (Info.Kind) { + case wasm::WASM_SYMBOL_TYPE_FUNCTION: + case wasm::WASM_SYMBOL_TYPE_GLOBAL: + encodeULEB128(Info.ElementIndex, SubSection.GetStream()); + if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) + writeStringRef(Info.Name, SubSection.GetStream()); + break; + case wasm::WASM_SYMBOL_TYPE_DATA: + writeStringRef(Info.Name, SubSection.GetStream()); + if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) { + encodeULEB128(Info.DataRef.Segment, SubSection.GetStream()); + encodeULEB128(Info.DataRef.Offset, SubSection.GetStream()); + encodeULEB128(Info.DataRef.Size, SubSection.GetStream()); + } + break; + case wasm::WASM_SYMBOL_TYPE_SECTION: + encodeULEB128(Info.ElementIndex, SubSection.GetStream()); + break; + default: + llvm_unreachable("unexpected kind"); + } } SubSection.Done(); @@ -153,7 +178,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &S // SEGMENT_NAMES subsection if (Section.SegmentInfos.size()) { - encodeULEB128(wasm::WASM_SEGMENT_INFO, OS); + writeUint8(OS, wasm::WASM_SEGMENT_INFO); encodeULEB128(Section.SegmentInfos.size(), SubSection.GetStream()); for (const WasmYAML::SegmentInfo &SegmentInfo : Section.SegmentInfos) { writeStringRef(SegmentInfo.Name, SubSection.GetStream()); @@ -165,21 +190,38 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &S // INIT_FUNCS subsection if (Section.InitFunctions.size()) { - encodeULEB128(wasm::WASM_INIT_FUNCS, OS); + writeUint8(OS, wasm::WASM_INIT_FUNCS); encodeULEB128(Section.InitFunctions.size(), SubSection.GetStream()); for (const WasmYAML::InitFunction &Func : Section.InitFunctions) { encodeULEB128(Func.Priority, SubSection.GetStream()); - encodeULEB128(Func.FunctionIndex, SubSection.GetStream()); + encodeULEB128(Func.Symbol, SubSection.GetStream()); + } + SubSection.Done(); + } + + // COMDAT_INFO subsection + if (Section.Comdats.size()) { + writeUint8(OS, wasm::WASM_COMDAT_INFO); + encodeULEB128(Section.Comdats.size(), SubSection.GetStream()); + for (const auto &C : Section.Comdats) { + writeStringRef(C.Name, SubSection.GetStream()); + encodeULEB128(0, SubSection.GetStream()); // flags for future use + encodeULEB128(C.Entries.size(), SubSection.GetStream()); + for (const WasmYAML::ComdatEntry &Entry : C.Entries) { + writeUint8(SubSection.GetStream(), Entry.Kind); + encodeULEB128(Entry.Index, SubSection.GetStream()); + } } SubSection.Done(); } + return 0; } int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section) { writeStringRef(Section.Name, OS); if (Section.FunctionNames.size()) { - encodeULEB128(wasm::WASM_NAMES_FUNCTION, OS); + writeUint8(OS, wasm::WASM_NAMES_FUNCTION); SubSectionWriter SubSection(OS); @@ -203,6 +245,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, if (auto Err = writeSectionContent(OS, *S)) return Err; } else { + writeStringRef(Section.Name, OS); Section.Payload.writeAsBinary(OS); } return 0; @@ -211,16 +254,22 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::TypeSection &Section) { encodeULEB128(Section.Signatures.size(), OS); + uint32_t ExpectedIndex = 0; for (const WasmYAML::Signature &Sig : Section.Signatures) { - encodeSLEB128(Sig.Form, OS); + if (Sig.Index != ExpectedIndex) { + errs() << "Unexpected type index: " << Sig.Index << "\n"; + return 1; + } + ++ExpectedIndex; + writeUint8(OS, Sig.Form); encodeULEB128(Sig.ParamTypes.size(), OS); for (auto ParamType : Sig.ParamTypes) - encodeSLEB128(ParamType, OS); + writeUint8(OS, ParamType); if (Sig.ReturnType == wasm::WASM_TYPE_NORESULT) { - encodeSLEB128(0, OS); + encodeULEB128(0, OS); } else { encodeULEB128(1, OS); - encodeSLEB128(Sig.ReturnType, OS); + writeUint8(OS, Sig.ReturnType); } } return 0; @@ -232,24 +281,26 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, for (const WasmYAML::Import &Import : Section.Imports) { writeStringRef(Import.Module, OS); writeStringRef(Import.Field, OS); - encodeULEB128(Import.Kind, OS); + writeUint8(OS, Import.Kind); switch (Import.Kind) { case wasm::WASM_EXTERNAL_FUNCTION: encodeULEB128(Import.SigIndex, OS); + NumImportedFunctions++; break; case wasm::WASM_EXTERNAL_GLOBAL: - encodeSLEB128(Import.GlobalImport.Type, OS); + writeUint8(OS, Import.GlobalImport.Type); writeUint8(OS, Import.GlobalImport.Mutable); + NumImportedGlobals++; break; case wasm::WASM_EXTERNAL_MEMORY: writeLimits(Import.Memory, OS); break; case wasm::WASM_EXTERNAL_TABLE: - encodeSLEB128(Import.TableImport.ElemType, OS); + writeUint8(OS,Import.TableImport.ElemType); writeLimits(Import.TableImport.TableLimits, OS); break; default: - errs() << "Unknown import type: " << Import.Kind; + errs() << "Unknown import type: " << Import.Kind << "\n"; return 1; } } @@ -270,7 +321,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, encodeULEB128(Section.Exports.size(), OS); for (const WasmYAML::Export &Export : Section.Exports) { writeStringRef(Export.Name, OS); - encodeULEB128(Export.Kind, OS); + writeUint8(OS, Export.Kind); encodeULEB128(Export.Index, OS); } return 0; @@ -286,7 +337,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section) { encodeULEB128(Section.Tables.size(), OS); for (auto &Table : Section.Tables) { - encodeSLEB128(Table.ElemType, OS); + writeUint8(OS, Table.ElemType); writeLimits(Table.TableLimits, OS); } return 0; @@ -304,8 +355,14 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section) { encodeULEB128(Section.Globals.size(), OS); + uint32_t ExpectedIndex = NumImportedGlobals; for (auto &Global : Section.Globals) { - encodeSLEB128(Global.Type, OS); + if (Global.Index != ExpectedIndex) { + errs() << "Unexpected global index: " << Global.Index << "\n"; + return 1; + } + ++ExpectedIndex; + writeUint8(OS, Global.Type); writeUint8(OS, Global.Mutable); writeInitExpr(Global.InitExpr, OS); } @@ -330,14 +387,20 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::CodeSection &Section) { encodeULEB128(Section.Functions.size(), OS); + uint32_t ExpectedIndex = NumImportedFunctions; for (auto &Func : Section.Functions) { std::string OutString; raw_string_ostream StringStream(OutString); + if (Func.Index != ExpectedIndex) { + errs() << "Unexpected function index: " << Func.Index << "\n"; + return 1; + } + ++ExpectedIndex; encodeULEB128(Func.Locals.size(), StringStream); for (auto &LocalDecl : Func.Locals) { encodeULEB128(LocalDecl.Count, StringStream); - encodeSLEB128(LocalDecl.Type, StringStream); + writeUint8(StringStream, LocalDecl.Type); } Func.Body.writeAsBinary(StringStream); @@ -362,33 +425,43 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, return 0; } -int WasmWriter::writeRelocSection(raw_ostream &OS, - WasmYAML::Section &Sec) { - StringRef Name; +int WasmWriter::writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, + uint32_t SectionIndex) { switch (Sec.Type) { case wasm::WASM_SEC_CODE: - Name = "reloc.CODE"; + writeStringRef("reloc.CODE", OS); break; case wasm::WASM_SEC_DATA: - Name = "reloc.DATA"; + writeStringRef("reloc.DATA", OS); + break; + case wasm::WASM_SEC_CUSTOM: { + auto CustomSection = dyn_cast<WasmYAML::CustomSection>(&Sec); + if (!CustomSection->Name.startswith(".debug_")) { + llvm_unreachable("not yet implemented (only for debug sections)"); + return 1; + } + + writeStringRef(("reloc." + CustomSection->Name).str(), OS); break; + } default: llvm_unreachable("not yet implemented"); return 1; } - writeStringRef(Name, OS); - encodeULEB128(Sec.Type, OS); + encodeULEB128(SectionIndex, OS); encodeULEB128(Sec.Relocations.size(), OS); for (auto Reloc: Sec.Relocations) { - encodeULEB128(Reloc.Type, OS); + writeUint8(OS, Reloc.Type); encodeULEB128(Reloc.Offset, OS); encodeULEB128(Reloc.Index, OS); switch (Reloc.Type) { 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: encodeULEB128(Reloc.Addend, OS); } } @@ -402,9 +475,18 @@ int WasmWriter::writeWasm(raw_ostream &OS) { writeUint32(OS, Obj.Header.Version); // Write each section + uint32_t LastType = 0; for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { - encodeULEB128(Sec->Type, OS); + uint32_t Type = Sec->Type; + if (Type != wasm::WASM_SEC_CUSTOM) { + if (Type < LastType) { + errs() << "Out of order section type: " << Type << "\n"; + return 1; + } + LastType = Type; + } + encodeULEB128(Sec->Type, OS); std::string OutString; raw_string_ostream StringStream(OutString); if (auto S = dyn_cast<WasmYAML::CustomSection>(Sec.get())) { @@ -455,14 +537,17 @@ int WasmWriter::writeWasm(raw_ostream &OS) { } // write reloc sections for any section that have relocations + uint32_t SectionIndex = 0; for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { - if (Sec->Relocations.empty()) + if (Sec->Relocations.empty()) { + SectionIndex++; continue; + } - encodeULEB128(wasm::WASM_SEC_CUSTOM, OS); + writeUint8(OS, wasm::WASM_SEC_CUSTOM); std::string OutString; raw_string_ostream StringStream(OutString); - writeRelocSection(StringStream, *Sec); + writeRelocSection(StringStream, *Sec, SectionIndex++); StringStream.flush(); encodeULEB128(OutString.size(), OS); |