diff options
| author | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
|---|---|---|
| committer | Dimitry Andric <dim@FreeBSD.org> | 2019-01-19 10:01:25 +0000 |
| commit | d8e91e46262bc44006913e6796843909f1ac7bcd (patch) | |
| tree | 7d0c143d9b38190e0fa0180805389da22cd834c5 /tools | |
| parent | b7eb8e35e481a74962664b63dfb09483b200209a (diff) | |
Notes
Diffstat (limited to 'tools')
312 files changed, 14854 insertions, 11190 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index b654b8c5cb8e..ddafc98d63bb 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -17,7 +17,7 @@ else() set(LLVM_TOOL_POLLY_BUILD Off) endif() -if(NOT LLVM_BUILD_LLVM_DYLIB ) +if(NOT LLVM_BUILD_LLVM_DYLIB AND NOT LLVM_BUILD_LLVM_C_DYLIB) set(LLVM_TOOL_LLVM_SHLIB_BUILD Off) endif() diff --git a/tools/LLVMBuild.txt b/tools/LLVMBuild.txt index 1732ea0cb8a0..61e053cac48a 100644 --- a/tools/LLVMBuild.txt +++ b/tools/LLVMBuild.txt @@ -32,6 +32,7 @@ subdirectories = llvm-dis llvm-dwarfdump llvm-dwp + llvm-elfabi llvm-exegesis llvm-extract llvm-jitlistener diff --git a/tools/bugpoint-passes/CMakeLists.txt b/tools/bugpoint-passes/CMakeLists.txt index e32b0a3aa34f..eea3e239b808 100644 --- a/tools/bugpoint-passes/CMakeLists.txt +++ b/tools/bugpoint-passes/CMakeLists.txt @@ -14,7 +14,7 @@ if(WIN32 OR CYGWIN) set(LLVM_LINK_COMPONENTS Core) endif() -add_llvm_loadable_module( BugpointPasses +add_llvm_library( BugpointPasses MODULE BUILDTREE_ONLY TestPasses.cpp DEPENDS diff --git a/tools/bugpoint-passes/TestPasses.cpp b/tools/bugpoint-passes/TestPasses.cpp index 22ded6261a1a..6b1463685b2c 100644 --- a/tools/bugpoint-passes/TestPasses.cpp +++ b/tools/bugpoint-passes/TestPasses.cpp @@ -123,3 +123,28 @@ char CrashOnTooManyCUs::ID = 0; static RegisterPass<CrashOnTooManyCUs> A("bugpoint-crash-too-many-cus", "BugPoint Test Pass - Intentionally crash on too many CUs"); + +namespace { +class CrashOnFunctionAttribute : public FunctionPass { +public: + static char ID; // Pass ID, replacement for typeid + CrashOnFunctionAttribute() : FunctionPass(ID) {} + +private: + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + } + + bool runOnFunction(Function &F) override { + AttributeSet A = F.getAttributes().getFnAttributes(); + if (A.hasAttribute("bugpoint-crash")) + abort(); + return false; + } +}; +} // namespace + +char CrashOnFunctionAttribute::ID = 0; +static RegisterPass<CrashOnFunctionAttribute> + B("bugpoint-crashfuncattr", "BugPoint Test Pass - Intentionally crash on " + "function attribute 'bugpoint-crash'"); diff --git a/tools/bugpoint/CrashDebugger.cpp b/tools/bugpoint/CrashDebugger.cpp index a5b31e1ab321..ef6a214fde20 100644 --- a/tools/bugpoint/CrashDebugger.cpp +++ b/tools/bugpoint/CrashDebugger.cpp @@ -315,6 +315,66 @@ bool ReduceCrashingFunctions::TestFuncs(std::vector<Function *> &Funcs) { } namespace { +/// ReduceCrashingFunctionAttributes reducer - This works by removing +/// attributes on a particular function and seeing if the program still crashes. +/// If it does, then keep the newer, smaller program. +/// +class ReduceCrashingFunctionAttributes : public ListReducer<Attribute> { + BugDriver &BD; + std::string FnName; + BugTester TestFn; + +public: + ReduceCrashingFunctionAttributes(BugDriver &bd, const std::string &FnName, + BugTester testFn) + : BD(bd), FnName(FnName), TestFn(testFn) {} + + Expected<TestResult> doTest(std::vector<Attribute> &Prefix, + std::vector<Attribute> &Kept) override { + if (!Kept.empty() && TestFuncAttrs(Kept)) + return KeepSuffix; + if (!Prefix.empty() && TestFuncAttrs(Prefix)) + return KeepPrefix; + return NoFailure; + } + + bool TestFuncAttrs(std::vector<Attribute> &Attrs); +}; +} + +bool ReduceCrashingFunctionAttributes::TestFuncAttrs( + std::vector<Attribute> &Attrs) { + // Clone the program to try hacking it apart... + std::unique_ptr<Module> M = CloneModule(BD.getProgram()); + Function *F = M->getFunction(FnName); + + // Build up an AttributeList from the attributes we've been given by the + // reducer. + AttrBuilder AB; + for (auto A : Attrs) + AB.addAttribute(A); + AttributeList NewAttrs; + NewAttrs = + NewAttrs.addAttributes(BD.getContext(), AttributeList::FunctionIndex, AB); + + // Set this new list of attributes on the function. + F->setAttributes(NewAttrs); + + // Try running on the hacked up program... + if (TestFn(BD, M.get())) { + BD.setNewProgram(std::move(M)); // It crashed, keep the trimmed version... + + // Pass along the set of attributes that caused the crash. + Attrs.clear(); + for (Attribute A : NewAttrs.getFnAttributes()) { + Attrs.push_back(A); + } + return true; + } + return false; +} + +namespace { /// Simplify the CFG without completely destroying it. /// This is not well defined, but basically comes down to "try to eliminate /// unreachable blocks and constant fold terminators without deciding that @@ -409,7 +469,7 @@ bool ReduceCrashingBlocks::TestBlocks(std::vector<const BasicBlock *> &BBs) { for (BasicBlock *Succ : successors(&BB)) Succ->removePredecessor(&BB); - TerminatorInst *BBTerm = BB.getTerminator(); + Instruction *BBTerm = BB.getTerminator(); if (BBTerm->isEHPad() || BBTerm->getType()->isTokenTy()) continue; if (!BBTerm->getType()->isVoidTy()) @@ -703,7 +763,7 @@ bool ReduceCrashingInstructions::TestInsts( // Convert list to set for fast lookup... SmallPtrSet<Instruction *, 32> Instructions; for (unsigned i = 0, e = Insts.size(); i != e; ++i) { - assert(!isa<TerminatorInst>(Insts[i])); + assert(!Insts[i]->isTerminator()); Instructions.insert(cast<Instruction>(VMap[Insts[i]])); } @@ -717,7 +777,7 @@ bool ReduceCrashingInstructions::TestInsts( for (Function::iterator FI = MI->begin(), FE = MI->end(); FI != FE; ++FI) for (BasicBlock::iterator I = FI->begin(), E = FI->end(); I != E;) { Instruction *Inst = &*I++; - if (!Instructions.count(Inst) && !isa<TerminatorInst>(Inst) && + if (!Instructions.count(Inst) && !Inst->isTerminator() && !Inst->isEHPad() && !Inst->getType()->isTokenTy() && !Inst->isSwiftError()) { if (!Inst->getType()->isVoidTy()) @@ -950,7 +1010,7 @@ static Error ReduceInsts(BugDriver &BD, BugTester TestFn) { for (const Function &F : BD.getProgram()) for (const BasicBlock &BB : F) for (const Instruction &I : BB) - if (!isa<TerminatorInst>(&I)) + if (!I.isTerminator()) Insts.push_back(&I); Expected<bool> Result = @@ -1056,6 +1116,38 @@ static Error DebugACrash(BugDriver &BD, BugTester TestFn) { BD.EmitProgressBitcode(BD.getProgram(), "reduced-function"); } + // For each remaining function, try to reduce that function's attributes. + std::vector<std::string> FunctionNames; + for (Function &F : BD.getProgram()) + FunctionNames.push_back(F.getName()); + + if (!FunctionNames.empty() && !BugpointIsInterrupted) { + outs() << "\n*** Attempting to reduce the number of function attributes in " + "the testcase\n"; + + unsigned OldSize = 0; + unsigned NewSize = 0; + for (std::string &Name : FunctionNames) { + Function *Fn = BD.getProgram().getFunction(Name); + assert(Fn && "Could not find funcion?"); + + std::vector<Attribute> Attrs; + for (Attribute A : Fn->getAttributes().getFnAttributes()) + Attrs.push_back(A); + + OldSize += Attrs.size(); + Expected<bool> Result = + ReduceCrashingFunctionAttributes(BD, Name, TestFn).reduceList(Attrs); + if (Error E = Result.takeError()) + return E; + + NewSize += Attrs.size(); + } + + if (OldSize < NewSize) + BD.EmitProgressBitcode(BD.getProgram(), "reduced-function-attributes"); + } + // Attempt to change conditional branches into unconditional branches to // eliminate blocks. if (!DisableSimplifyCFG && !BugpointIsInterrupted) { diff --git a/tools/bugpoint/ExecutionDriver.cpp b/tools/bugpoint/ExecutionDriver.cpp index 773bad69fae0..1b86b103d835 100644 --- a/tools/bugpoint/ExecutionDriver.cpp +++ b/tools/bugpoint/ExecutionDriver.cpp @@ -148,8 +148,9 @@ Error BugDriver::initializeExecutionEnvironment() { std::string Message; if (CCBinary.empty()) { - if (sys::findProgramByName("clang")) - CCBinary = "clang"; + if (ErrorOr<std::string> ClangPath = + FindProgramByName("clang", getToolName(), &AbsTolerance)) + CCBinary = *ClangPath; else CCBinary = "gcc"; } @@ -193,11 +194,11 @@ Error BugDriver::initializeExecutionEnvironment() { break; case CompileCustom: Interpreter = AbstractInterpreter::createCustomCompiler( - Message, CustomCompileCommand); + getToolName(), Message, CustomCompileCommand); break; case Custom: - Interpreter = - AbstractInterpreter::createCustomExecutor(Message, CustomExecCommand); + Interpreter = AbstractInterpreter::createCustomExecutor( + getToolName(), Message, CustomExecCommand); break; } if (!Interpreter) @@ -239,8 +240,8 @@ Error BugDriver::initializeExecutionEnvironment() { SafeInterpreterSel == RunLLCIA); break; case Custom: - SafeInterpreter = - AbstractInterpreter::createCustomExecutor(Message, CustomExecCommand); + SafeInterpreter = AbstractInterpreter::createCustomExecutor( + getToolName(), Message, CustomExecCommand); break; default: Message = "Sorry, this back-end is not supported by bugpoint as the " @@ -252,7 +253,7 @@ Error BugDriver::initializeExecutionEnvironment() { exit(1); } - cc = CC::create(Message, CCBinary, &CCToolArgv); + cc = CC::create(getToolName(), Message, CCBinary, &CCToolArgv); if (!cc) { outs() << Message << "\nExiting.\n"; exit(1); @@ -299,26 +300,32 @@ Expected<std::string> BugDriver::executeProgram(const Module &Program, if (!AI) AI = Interpreter; assert(AI && "Interpreter should have been created already!"); + bool CreatedBitcode = false; if (BitcodeFile.empty()) { // Emit the program to a bitcode file... - auto File = - sys::fs::TempFile::create(OutputPrefix + "-test-program-%%%%%%%.bc"); - if (!File) { - errs() << ToolName - << ": Error making unique filename: " << toString(File.takeError()) + SmallString<128> UniqueFilename; + int UniqueFD; + std::error_code EC = sys::fs::createUniqueFile( + OutputPrefix + "-test-program-%%%%%%%.bc", UniqueFD, UniqueFilename); + if (EC) { + errs() << ToolName << ": Error making unique filename: " << EC.message() << "!\n"; exit(1); } - DiscardTemp Discard{*File}; - BitcodeFile = File->TmpName; + BitcodeFile = UniqueFilename.str(); - if (writeProgramToFile(File->FD, Program)) { + if (writeProgramToFile(BitcodeFile, UniqueFD, Program)) { errs() << ToolName << ": Error emitting bitcode to file '" << BitcodeFile << "'!\n"; exit(1); } + CreatedBitcode = true; } + // Remove the temporary bitcode file when we are done. + std::string BitcodePath(BitcodeFile); + FileRemover BitcodeFileRemover(BitcodePath, CreatedBitcode && !SaveTemps); + if (OutputFile.empty()) OutputFile = OutputPrefix + "-execution-output-%%%%%%%"; diff --git a/tools/bugpoint/OptimizerDriver.cpp b/tools/bugpoint/OptimizerDriver.cpp index cbb048db8fe7..64fe675de20c 100644 --- a/tools/bugpoint/OptimizerDriver.cpp +++ b/tools/bugpoint/OptimizerDriver.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "BugDriver.h" +#include "ToolRunner.h" #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Module.h" @@ -166,7 +167,8 @@ bool BugDriver::runPasses(Module &Program, std::string tool = OptCmd; if (OptCmd.empty()) { - if (ErrorOr<std::string> Path = sys::findProgramByName("opt")) + if (ErrorOr<std::string> Path = + FindProgramByName("opt", getToolName(), &OutputPrefix)) tool = *Path; else errs() << Path.getError().message() << "\n"; diff --git a/tools/bugpoint/ToolRunner.cpp b/tools/bugpoint/ToolRunner.cpp index 812e8e3bbae5..7ba8ea1f16c5 100644 --- a/tools/bugpoint/ToolRunner.cpp +++ b/tools/bugpoint/ToolRunner.cpp @@ -202,19 +202,7 @@ Expected<int> LLI::ExecuteProgram(const std::string &Bitcode, void AbstractInterpreter::anchor() {} -#if defined(LLVM_ON_UNIX) -const char EXESuffix[] = ""; -#elif defined(_WIN32) -const char EXESuffix[] = "exe"; -#endif - -/// Prepend the path to the program being executed -/// to \p ExeName, given the value of argv[0] and the address of main() -/// 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. -/// Find a named executable. -static std::string PrependMainExecutablePath(const std::string &ExeName, +ErrorOr<std::string> llvm::FindProgramByName(const std::string &ExeName, const char *Argv0, void *MainAddr) { // Check the directory that the calling program is in. We can do @@ -222,30 +210,25 @@ static std::string PrependMainExecutablePath(const std::string &ExeName, // is a relative path to the executable itself. std::string Main = sys::fs::getMainExecutable(Argv0, MainAddr); StringRef Result = sys::path::parent_path(Main); + if (ErrorOr<std::string> Path = sys::findProgramByName(ExeName, Result)) + return *Path; - if (!Result.empty()) { - SmallString<128> Storage = Result; - sys::path::append(Storage, ExeName); - sys::path::replace_extension(Storage, EXESuffix); - return Storage.str(); - } - - return Result.str(); + // Check the user PATH. + return sys::findProgramByName(ExeName); } // LLI create method - Try to find the LLI executable AbstractInterpreter * AbstractInterpreter::createLLI(const char *Argv0, std::string &Message, const std::vector<std::string> *ToolArgs) { - std::string LLIPath = - PrependMainExecutablePath("lli", Argv0, (void *)(intptr_t)&createLLI); - if (!LLIPath.empty()) { - Message = "Found lli: " + LLIPath + "\n"; - return new LLI(LLIPath, ToolArgs); + if (ErrorOr<std::string> LLIPath = + FindProgramByName("lli", Argv0, (void *)(intptr_t)&createLLI)) { + Message = "Found lli: " + *LLIPath + "\n"; + return new LLI(*LLIPath, ToolArgs); + } else { + Message = LLIPath.getError().message() + "\n"; + return nullptr; } - - Message = "Cannot find `lli' in executable directory!\n"; - return nullptr; } //===---------------------------------------------------------------------===// @@ -368,8 +351,9 @@ Expected<int> CustomExecutor::ExecuteProgram( // '\ ' -> ' ' // 'exa\mple' -> 'example' // -static void lexCommand(std::string &Message, const std::string &CommandLine, - std::string &CmdPath, std::vector<std::string> &Args) { +static void lexCommand(const char *Argv0, std::string &Message, + const std::string &CommandLine, std::string &CmdPath, + std::vector<std::string> &Args) { std::string Token; std::string Command; @@ -402,7 +386,7 @@ static void lexCommand(std::string &Message, const std::string &CommandLine, Token.push_back(CommandLine[Pos]); } - auto Path = sys::findProgramByName(Command); + auto Path = FindProgramByName(Command, Argv0, (void *)(intptr_t)&lexCommand); if (!Path) { Message = std::string("Cannot find '") + Command + "' in PATH: " + Path.getError().message() + "\n"; @@ -416,11 +400,12 @@ static void lexCommand(std::string &Message, const std::string &CommandLine, // Custom execution environment create method, takes the execution command // as arguments AbstractInterpreter *AbstractInterpreter::createCustomCompiler( - std::string &Message, const std::string &CompileCommandLine) { + const char *Argv0, std::string &Message, + const std::string &CompileCommandLine) { std::string CmdPath; std::vector<std::string> Args; - lexCommand(Message, CompileCommandLine, CmdPath, Args); + lexCommand(Argv0, Message, CompileCommandLine, CmdPath, Args); if (CmdPath.empty()) return nullptr; @@ -430,12 +415,13 @@ AbstractInterpreter *AbstractInterpreter::createCustomCompiler( // Custom execution environment create method, takes the execution command // as arguments AbstractInterpreter * -AbstractInterpreter::createCustomExecutor(std::string &Message, +AbstractInterpreter::createCustomExecutor(const char *Argv0, + std::string &Message, const std::string &ExecCommandLine) { std::string CmdPath; std::vector<std::string> Args; - lexCommand(Message, ExecCommandLine, CmdPath, Args); + lexCommand(Argv0, Message, ExecCommandLine, CmdPath, Args); if (CmdPath.empty()) return nullptr; @@ -524,20 +510,20 @@ LLC *AbstractInterpreter::createLLC(const char *Argv0, std::string &Message, const std::vector<std::string> *Args, const std::vector<std::string> *CCArgs, bool UseIntegratedAssembler) { - std::string LLCPath = - PrependMainExecutablePath("llc", Argv0, (void *)(intptr_t)&createLLC); - if (LLCPath.empty()) { - Message = "Cannot find `llc' in executable directory!\n"; + ErrorOr<std::string> LLCPath = + FindProgramByName("llc", Argv0, (void *)(intptr_t)&createLLC); + if (!LLCPath) { + Message = LLCPath.getError().message() + "\n"; return nullptr; } - CC *cc = CC::create(Message, CCBinary, CCArgs); + CC *cc = CC::create(Argv0, Message, CCBinary, CCArgs); if (!cc) { errs() << Message << "\n"; exit(1); } - Message = "Found llc: " + LLCPath + "\n"; - return new LLC(LLCPath, cc, Args, UseIntegratedAssembler); + Message = "Found llc: " + *LLCPath + "\n"; + return new LLC(*LLCPath, cc, Args, UseIntegratedAssembler); } //===---------------------------------------------------------------------===// @@ -606,15 +592,14 @@ Expected<int> JIT::ExecuteProgram(const std::string &Bitcode, AbstractInterpreter * AbstractInterpreter::createJIT(const char *Argv0, std::string &Message, const std::vector<std::string> *Args) { - std::string LLIPath = - PrependMainExecutablePath("lli", Argv0, (void *)(intptr_t)&createJIT); - if (!LLIPath.empty()) { - Message = "Found lli: " + LLIPath + "\n"; - return new JIT(LLIPath, Args); + if (ErrorOr<std::string> LLIPath = + FindProgramByName("lli", Argv0, (void *)(intptr_t)&createJIT)) { + Message = "Found lli: " + *LLIPath + "\n"; + return new JIT(*LLIPath, Args); + } else { + Message = LLIPath.getError().message() + "\n"; + return nullptr; } - - Message = "Cannot find `lli' in executable directory!\n"; - return nullptr; } //===---------------------------------------------------------------------===// @@ -855,9 +840,10 @@ Error CC::MakeSharedObject(const std::string &InputFile, FileType fileType, /// create - Try to find the CC executable /// -CC *CC::create(std::string &Message, const std::string &CCBinary, +CC *CC::create(const char *Argv0, std::string &Message, + const std::string &CCBinary, const std::vector<std::string> *Args) { - auto CCPath = sys::findProgramByName(CCBinary); + auto CCPath = FindProgramByName(CCBinary, Argv0, (void *)(intptr_t)&create); if (!CCPath) { Message = "Cannot find `" + CCBinary + "' in PATH: " + CCPath.getError().message() + "\n"; diff --git a/tools/bugpoint/ToolRunner.h b/tools/bugpoint/ToolRunner.h index f218ad534ee9..ef8551cc669b 100644 --- a/tools/bugpoint/ToolRunner.h +++ b/tools/bugpoint/ToolRunner.h @@ -49,7 +49,8 @@ class CC { public: enum FileType { AsmFile, ObjectFile, CFile }; - static CC *create(std::string &Message, const std::string &CCBinary, + static CC *create(const char *Argv0, std::string &Message, + const std::string &CCBinary, const std::vector<std::string> *Args); /// ExecuteProgram - Execute the program specified by "ProgramFile" (which is @@ -98,11 +99,11 @@ public: const std::vector<std::string> *Args = nullptr); static AbstractInterpreter * - createCustomCompiler(std::string &Message, + createCustomCompiler(const char *Argv0, std::string &Message, const std::string &CompileCommandLine); static AbstractInterpreter * - createCustomExecutor(std::string &Message, + createCustomExecutor(const char *Argv0, std::string &Message, const std::string &ExecCommandLine); virtual ~AbstractInterpreter() {} @@ -178,6 +179,13 @@ public: unsigned MemoryLimit = 0) override; }; +/// Find the first executable file \ExeName, either in the user's PATH or, +/// failing that, in the same directory as argv[0]. This allows us to find +/// another LLVM tool if it is built in the same directory. If no executable is +/// found, an error is returned. +ErrorOr<std::string> FindProgramByName(const std::string &ExeName, + const char *Argv0, void *MainAddr); + } // End llvm namespace #endif diff --git a/tools/dsymutil/CFBundle.h b/tools/dsymutil/CFBundle.h index bdbecb4785c0..5f241849910d 100644 --- a/tools/dsymutil/CFBundle.h +++ b/tools/dsymutil/CFBundle.h @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H +#define LLVM_TOOLS_DSYMUTIL_CFBUNDLE_H + #include "llvm/ADT/StringRef.h" #include <string> @@ -24,3 +27,5 @@ CFBundleInfo getBundleInfo(llvm::StringRef ExePath); } // end namespace dsymutil } // end namespace llvm + +#endif diff --git a/tools/dsymutil/CMakeLists.txt b/tools/dsymutil/CMakeLists.txt index f41a6fd28504..480f78fb1888 100644 --- a/tools/dsymutil/CMakeLists.txt +++ b/tools/dsymutil/CMakeLists.txt @@ -20,6 +20,7 @@ add_llvm_tool(dsymutil MachODebugMapParser.cpp MachOUtils.cpp NonRelocatableStringpool.cpp + SymbolMap.cpp DEPENDS intrinsics_gen diff --git a/tools/dsymutil/CompileUnit.cpp b/tools/dsymutil/CompileUnit.cpp index 67e1739ae108..4654e41b2176 100644 --- a/tools/dsymutil/CompileUnit.cpp +++ b/tools/dsymutil/CompileUnit.cpp @@ -92,7 +92,11 @@ void CompileUnit::addLabelLowPc(uint64_t LabelLowPc, int64_t PcOffset) { void CompileUnit::addFunctionRange(uint64_t FuncLowPc, uint64_t FuncHighPc, int64_t PcOffset) { - Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); + // Don't add empty ranges to the interval map. They are a problem because + // the interval map expects half open intervals. This is safe because they + // are empty anyway. + if (FuncHighPc != FuncLowPc) + Ranges.insert(FuncLowPc, FuncHighPc, PcOffset); this->LowPc = std::min(LowPc, FuncLowPc + PcOffset); this->HighPc = std::max(HighPc, FuncHighPc + PcOffset); } diff --git a/tools/dsymutil/CompileUnit.h b/tools/dsymutil/CompileUnit.h index 0f5efdd68051..79b88fd4d6bb 100644 --- a/tools/dsymutil/CompileUnit.h +++ b/tools/dsymutil/CompileUnit.h @@ -7,13 +7,13 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H +#define LLVM_TOOLS_DSYMUTIL_COMPILEUNIT_H + #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 { diff --git a/tools/dsymutil/DebugMap.cpp b/tools/dsymutil/DebugMap.cpp index 26137773a4e7..509afaf56f4e 100644 --- a/tools/dsymutil/DebugMap.cpp +++ b/tools/dsymutil/DebugMap.cpp @@ -63,9 +63,9 @@ 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())); - llvm::sort( - Entries.begin(), Entries.end(), - [](const Entry &LHS, const Entry &RHS) { return LHS.first < RHS.first; }); + llvm::sort(Entries, [](const Entry &LHS, const Entry &RHS) { + return LHS.first < RHS.first; + }); for (const auto &Sym : Entries) { if (Sym.second.ObjectAddress) OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress)); diff --git a/tools/dsymutil/DebugMap.h b/tools/dsymutil/DebugMap.h index c9883773d3dc..d8de37e33d55 100644 --- a/tools/dsymutil/DebugMap.h +++ b/tools/dsymutil/DebugMap.h @@ -75,7 +75,7 @@ class DebugMapObject; class DebugMap { Triple BinaryTriple; std::string BinaryPath; - + std::vector<uint8_t> BinaryUUID; using ObjectContainer = std::vector<std::unique_ptr<DebugMapObject>>; ObjectContainer Objects; @@ -89,8 +89,10 @@ class DebugMap { ///@} public: - DebugMap(const Triple &BinaryTriple, StringRef BinaryPath) - : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath) {} + DebugMap(const Triple &BinaryTriple, StringRef BinaryPath, + ArrayRef<uint8_t> BinaryUUID = ArrayRef<uint8_t>()) + : BinaryTriple(BinaryTriple), BinaryPath(BinaryPath), + BinaryUUID(BinaryUUID.begin(), BinaryUUID.end()) {} using const_iterator = ObjectContainer::const_iterator; @@ -113,6 +115,10 @@ public: const Triple &getTriple() const { return BinaryTriple; } + const ArrayRef<uint8_t> getUUID() const { + return ArrayRef<uint8_t>(BinaryUUID); + } + StringRef getBinaryPath() const { return BinaryPath; } void print(raw_ostream &OS) const; diff --git a/tools/dsymutil/DeclContext.h b/tools/dsymutil/DeclContext.h index 1fa6815fa3b9..425c73671e12 100644 --- a/tools/dsymutil/DeclContext.h +++ b/tools/dsymutil/DeclContext.h @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H +#define LLVM_TOOLS_DSYMUTIL_DECLCONTEXT_H + #include "CompileUnit.h" #include "NonRelocatableStringpool.h" #include "llvm/ADT/DenseMap.h" @@ -16,9 +19,6 @@ #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 { diff --git a/tools/dsymutil/DwarfLinker.cpp b/tools/dsymutil/DwarfLinker.cpp index 430e8e063e3c..0743cfc3ed40 100644 --- a/tools/dsymutil/DwarfLinker.cpp +++ b/tools/dsymutil/DwarfLinker.cpp @@ -155,6 +155,40 @@ static bool isODRAttribute(uint16_t Attr) { llvm_unreachable("Improper attribute."); } +static bool isTypeTag(uint16_t Tag) { + switch (Tag) { + case dwarf::DW_TAG_array_type: + case dwarf::DW_TAG_class_type: + case dwarf::DW_TAG_enumeration_type: + case dwarf::DW_TAG_pointer_type: + case dwarf::DW_TAG_reference_type: + case dwarf::DW_TAG_string_type: + case dwarf::DW_TAG_structure_type: + case dwarf::DW_TAG_subroutine_type: + case dwarf::DW_TAG_typedef: + case dwarf::DW_TAG_union_type: + case dwarf::DW_TAG_ptr_to_member_type: + case dwarf::DW_TAG_set_type: + case dwarf::DW_TAG_subrange_type: + case dwarf::DW_TAG_base_type: + case dwarf::DW_TAG_const_type: + case dwarf::DW_TAG_constant: + case dwarf::DW_TAG_file_type: + case dwarf::DW_TAG_namelist: + case dwarf::DW_TAG_packed_type: + case dwarf::DW_TAG_volatile_type: + case dwarf::DW_TAG_restrict_type: + case dwarf::DW_TAG_atomic_type: + case dwarf::DW_TAG_interface_type: + case dwarf::DW_TAG_unspecified_type: + case dwarf::DW_TAG_shared_type: + return true; + default: + break; + } + return false; +} + bool DwarfLinker::DIECloner::getDIENames(const DWARFDie &Die, AttributesInfo &Info, OffsetsStringPool &StringPool, @@ -222,6 +256,7 @@ static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx, CompileUnit &CU, DeclContext *CurrentDeclContext, UniquingStringPool &StringPool, DeclContextTree &Contexts, + uint64_t ModulesEndOffset, bool InImportedModule = false) { unsigned MyIdx = CU.getOrigUnit().getDIEIndex(DIE); CompileUnit::DIEInfo &Info = CU.getInfo(MyIdx); @@ -262,20 +297,27 @@ static bool analyzeContextInfo(const DWARFDie &DIE, unsigned ParentIdx, Info.Prune = InImportedModule; if (DIE.hasChildren()) for (auto Child : DIE.children()) - Info.Prune &= analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, - StringPool, Contexts, InImportedModule); + Info.Prune &= + analyzeContextInfo(Child, MyIdx, CU, CurrentDeclContext, StringPool, + Contexts, ModulesEndOffset, 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); + (isTypeTag(DIE.getTag()) && + 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(); + // Only prune forward declarations inside a DW_TAG_module for which a + // definition exists elsewhere. + if (ModulesEndOffset == 0) + Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset(); + else + Info.Prune &= Info.Ctxt && Info.Ctxt->getCanonicalDIEOffset() > 0 && + Info.Ctxt->getCanonicalDIEOffset() <= ModulesEndOffset; return Info.Prune; -} +} // namespace dsymutil static bool dieNeedsChildrenToBeMeaningful(uint32_t Tag) { switch (Tag) { @@ -442,7 +484,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. - llvm::sort(ValidRelocs.begin(), ValidRelocs.end()); + llvm::sort(ValidRelocs); return true; } @@ -1258,40 +1300,6 @@ bool DwarfLinker::RelocationManager::applyValidRelocs( return Applied; } -static bool isTypeTag(uint16_t Tag) { - switch (Tag) { - case dwarf::DW_TAG_array_type: - case dwarf::DW_TAG_class_type: - case dwarf::DW_TAG_enumeration_type: - case dwarf::DW_TAG_pointer_type: - case dwarf::DW_TAG_reference_type: - case dwarf::DW_TAG_string_type: - case dwarf::DW_TAG_structure_type: - case dwarf::DW_TAG_subroutine_type: - case dwarf::DW_TAG_typedef: - case dwarf::DW_TAG_union_type: - case dwarf::DW_TAG_ptr_to_member_type: - case dwarf::DW_TAG_set_type: - case dwarf::DW_TAG_subrange_type: - case dwarf::DW_TAG_base_type: - case dwarf::DW_TAG_const_type: - case dwarf::DW_TAG_constant: - case dwarf::DW_TAG_file_type: - case dwarf::DW_TAG_namelist: - case dwarf::DW_TAG_packed_type: - case dwarf::DW_TAG_volatile_type: - case dwarf::DW_TAG_restrict_type: - case dwarf::DW_TAG_atomic_type: - case dwarf::DW_TAG_interface_type: - case dwarf::DW_TAG_unspecified_type: - case dwarf::DW_TAG_shared_type: - return true; - default: - break; - } - return false; -} - static bool isObjCSelector(StringRef Name) { return Name.size() > 2 && (Name[0] == '-' || Name[0] == '+') && (Name[1] == '['); @@ -1693,10 +1701,12 @@ void DwarfLinker::patchLineTableForUnit(CompileUnit &Unit, DWARFDataExtractor LineExtractor( OrigDwarf.getDWARFObj(), OrigDwarf.getDWARFObj().getLineSection(), OrigDwarf.isLittleEndian(), Unit.getOrigUnit().getAddressByteSize()); + if (Options.Translator) + return Streamer->translateLineTable(LineExtractor, StmtOffset, Options); Error Err = LineTable.parse(LineExtractor, &StmtOffset, OrigDwarf, - &Unit.getOrigUnit()); - DWARFDebugLine::warn(std::move(Err)); + &Unit.getOrigUnit(), DWARFContext::dumpWarning); + DWARFContext::dumpWarning(std::move(Err)); // This vector is the output line table. std::vector<DWARFDebugLine::Row> NewRows; @@ -2024,7 +2034,7 @@ bool DwarfLinker::registerModuleReference( const DWARFDie &CUDie, const DWARFUnit &Unit, DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &StringPool, UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, - unsigned &UnitID, unsigned Indent) { + uint64_t ModulesEndOffset, unsigned &UnitID, unsigned Indent, bool Quiet) { std::string PCMfile = dwarf::toString( CUDie.find({dwarf::DW_AT_dwo_name, dwarf::DW_AT_GNU_dwo_name}), ""); if (PCMfile.empty()) @@ -2036,11 +2046,12 @@ bool DwarfLinker::registerModuleReference( std::string Name = dwarf::toString(CUDie.find(dwarf::DW_AT_name), ""); if (Name.empty()) { - reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO); + if (!Quiet) + reportWarning("Anonymous module skeleton CU for " + PCMfile, DMO); return true; } - if (Options.Verbose) { + if (!Quiet && Options.Verbose) { outs().indent(Indent); outs() << "Found clang module reference " << PCMfile; } @@ -2050,24 +2061,25 @@ bool DwarfLinker::registerModuleReference( // FIXME: Until PR27449 (https://llvm.org/bugs/show_bug.cgi?id=27449) is // fixed in clang, only warn about DWO_id mismatches in verbose mode. // ASTFileSignatures will change randomly when a module is rebuilt. - if (Options.Verbose && (Cached->second != DwoId)) + if (!Quiet && Options.Verbose && (Cached->second != DwoId)) reportWarning(Twine("hash mismatch: this object file was built against a " "different version of the module ") + PCMfile, DMO); - if (Options.Verbose) + if (!Quiet && Options.Verbose) outs() << " [cached].\n"; return true; } - if (Options.Verbose) + if (!Quiet && Options.Verbose) outs() << " ...\n"; // 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, DMO, - Ranges, StringPool, UniquingStringPool, - ODRContexts, UnitID, Indent + 2)) { + if (Error E = + loadClangModule(PCMfile, PCMpath, Name, DwoId, ModuleMap, DMO, Ranges, + StringPool, UniquingStringPool, ODRContexts, + ModulesEndOffset, UnitID, Indent + 2, Quiet)) { consumeError(std::move(E)); return false; } @@ -2096,14 +2108,12 @@ DwarfLinker::loadObject(const DebugMapObject &Obj, const DebugMap &Map) { return *Object; } -Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, - StringRef ModuleName, uint64_t DwoId, - DebugMap &ModuleMap, - const DebugMapObject &DMO, RangesTy &Ranges, - OffsetsStringPool &StringPool, - UniquingStringPool &UniquingStringPool, - DeclContextTree &ODRContexts, - unsigned &UnitID, unsigned Indent) { +Error DwarfLinker::loadClangModule( + StringRef Filename, StringRef ModulePath, StringRef ModuleName, + uint64_t DwoId, DebugMap &ModuleMap, const DebugMapObject &DMO, + RangesTy &Ranges, OffsetsStringPool &StringPool, + UniquingStringPool &UniquingStringPool, DeclContextTree &ODRContexts, + uint64_t ModulesEndOffset, unsigned &UnitID, unsigned Indent, bool Quiet) { SmallString<80> Path(Options.PrependPath); if (sys::path::is_relative(Filename)) sys::path::append(Path, ModulePath, Filename); @@ -2164,8 +2174,8 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, if (!CUDie) continue; if (!registerModuleReference(CUDie, *CU, ModuleMap, DMO, Ranges, StringPool, - UniquingStringPool, ODRContexts, UnitID, - Indent)) { + UniquingStringPool, ODRContexts, + ModulesEndOffset, UnitID, Indent, Quiet)) { if (Unit) { std::string Err = (Filename + @@ -2179,7 +2189,7 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, // ASTFileSignatures will change randomly when a module is rebuilt. uint64_t PCMDwoId = getDwoId(CUDie, *CU); if (PCMDwoId != DwoId) { - if (Options.Verbose) + if (!Quiet && Options.Verbose) reportWarning( Twine("hash mismatch: this object file was built against a " "different version of the module ") + @@ -2194,14 +2204,14 @@ Error DwarfLinker::loadClangModule(StringRef Filename, StringRef ModulePath, ModuleName); Unit->setHasInterestingContent(); analyzeContextInfo(CUDie, 0, *Unit, &ODRContexts.getRoot(), - UniquingStringPool, ODRContexts); + UniquingStringPool, ODRContexts, ModulesEndOffset); // Keep everything. Unit->markEverythingAsKept(); } } if (!Unit->getOrigUnit().getUnitDIE().hasChildren()) return Error::success(); - if (Options.Verbose) { + if (!Quiet && Options.Verbose) { outs().indent(Indent); outs() << "cloning .debug_info from " << Filename << "\n"; } @@ -2237,17 +2247,16 @@ void DwarfLinker::DIECloner::cloneAllCompileUnits( if (Linker.Options.NoOutput) continue; - 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. + // 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. + if (LLVM_LIKELY(!Linker.Options.Update) || Linker.Options.Translator) Linker.patchLineTableForUnit(*CurrentUnit, DwarfContext, Ranges, DMO); - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); - Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); - } else { - Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); - } + Linker.emitAcceleratorEntriesForUnit(*CurrentUnit); + if (Linker.Options.Update) + continue; + Linker.patchRangesForUnit(*CurrentUnit, DwarfContext, DMO); + Linker.Streamer->emitLocationsForUnit(*CurrentUnit, DwarfContext); } if (Linker.Options.NoOutput) @@ -2372,7 +2381,7 @@ bool DwarfLinker::link(const DebugMap &Map) { // 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; + OffsetsStringPool OffsetsStringPool(Options.Translator); // ODR Contexts for the link. DeclContextTree ODRContexts; @@ -2409,15 +2418,20 @@ bool DwarfLinker::link(const DebugMap &Map) { warn(Err.message()); continue; } - 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; + if (!Options.NoTimestamp) { + // The modification can have sub-second precision so we need to cast + // away the extra precision that's not present in the debug map. + auto ModificationTime = + std::chrono::time_point_cast<std::chrono::seconds>( + Stat.getLastModificationTime()); + if (ModificationTime != 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; + } } // Copy the module into the .swift_ast section. @@ -2466,14 +2480,10 @@ bool DwarfLinker::link(const DebugMap &Map) { DumpOpts.Verbose = Options.Verbose; CUDie.dump(outs(), 0, DumpOpts); } - - 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, "")); - } + if (CUDie && !LLVM_UNLIKELY(Options.Update)) + registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, + LinkContext.Ranges, OffsetsStringPool, + UniquingStringPool, ODRContexts, 0, UnitID); } } @@ -2481,37 +2491,54 @@ bool DwarfLinker::link(const DebugMap &Map) { if (MaxDwarfVersion == 0) MaxDwarfVersion = 3; + // At this point we know how much data we have emitted. We use this value to + // compare canonical DIE offsets in analyzeContextInfo to see if a definition + // is already emitted, without being affected by canonical die offsets set + // later. This prevents undeterminism when analyze and clone execute + // concurrently, as clone set the canonical DIE offset and analyze reads it. + const uint64_t ModulesEndOffset = OutputDebugInfoSize; + // 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); - // 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]; + // Analyzing the context info is particularly expensive so it is executed in + // parallel with emitting the previous compile unit. + auto AnalyzeLambda = [&](size_t i) { + auto &LinkContext = ObjectContexts[i]; - if (!LinkContext.ObjectFile) { - std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex); - ProcessedFiles.set(i); - ProcessedFilesConditionVariable.notify_one(); - continue; - } + if (!LinkContext.ObjectFile || !LinkContext.DwarfContext) + return; - // 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); + for (const auto &CU : LinkContext.DwarfContext->compile_units()) { + updateDwarfVersion(CU->getVersion()); + // The !registerModuleReference() condition effectively skips + // over fully resolved skeleton units. This second pass of + // registerModuleReferences doesn't do any new work, but it + // will collect top-level errors, which are suppressed. Module + // warnings were already displayed in the first iteration. + bool Quiet = true; + auto CUDie = CU->getUnitDIE(false); + if (!CUDie || LLVM_UNLIKELY(Options.Update) || + !registerModuleReference(CUDie, *CU, ModuleMap, LinkContext.DMO, + LinkContext.Ranges, OffsetsStringPool, + UniquingStringPool, ODRContexts, + ModulesEndOffset, UnitID, Quiet)) { + LinkContext.CompileUnits.push_back(llvm::make_unique<CompileUnit>( + *CU, UnitID++, !Options.NoODR && !Options.Update, "")); } + } - std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex); - ProcessedFiles.set(i); - ProcessedFilesConditionVariable.notify_one(); + // 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, ModulesEndOffset); } }; @@ -2519,57 +2546,48 @@ bool DwarfLinker::link(const DebugMap &Map) { // 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); - } + auto CloneLambda = [&](size_t i) { + auto &LinkContext = ObjectContexts[i]; + if (!LinkContext.ObjectFile) + return; - // 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); + // 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); + }; + + auto EmitLambda = [&]() { // Emit everything that's global. if (!Options.NoOutput) { Streamer->emitAbbrevs(Abbreviations, MaxDwarfVersion); @@ -2591,20 +2609,48 @@ bool DwarfLinker::link(const DebugMap &Map) { } }; - // 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. + auto AnalyzeAll = [&]() { + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + AnalyzeLambda(i); + + std::unique_lock<std::mutex> LockGuard(ProcessedFilesMutex); + ProcessedFiles.set(i); + ProcessedFilesConditionVariable.notify_one(); + } + }; + + auto CloneAll = [&]() { + 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]; }); + } + } + + CloneLambda(i); + } + EmitLambda(); + }; + + // To limit memory usage in the single threaded case, analyze and clone are + // run sequentially so the LinkContext is freed after processing each object + // in endDebugObject. if (Options.Threads == 1) { - AnalyzeLambda(); - CloneLambda(); + for (unsigned i = 0, e = NumObjects; i != e; ++i) { + AnalyzeLambda(i); + CloneLambda(i); + } + EmitLambda(); } else { ThreadPool pool(2); - pool.async(AnalyzeLambda); - pool.async(CloneLambda); + pool.async(AnalyzeAll); + pool.async(CloneAll); pool.wait(); } - return Options.NoOutput ? true : Streamer->finish(Map); + return Options.NoOutput ? true : Streamer->finish(Map, Options.Translator); } // namespace dsymutil bool linkDwarf(raw_fd_ostream &OutFile, BinaryHolder &BinHolder, diff --git a/tools/dsymutil/DwarfLinker.h b/tools/dsymutil/DwarfLinker.h index b1a950ff0fa2..64fa8d2107d5 100644 --- a/tools/dsymutil/DwarfLinker.h +++ b/tools/dsymutil/DwarfLinker.h @@ -199,8 +199,9 @@ private: RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, UniquingStringPool &UniquingStringPoolStringPool, - DeclContextTree &ODRContexts, unsigned &UnitID, - unsigned Indent = 0); + DeclContextTree &ODRContexts, + uint64_t ModulesEndOffset, unsigned &UnitID, + unsigned Indent = 0, bool Quiet = false); /// Recursively add the debug info in this clang module .pcm /// file (and all the modules imported by it in a bottom-up fashion) @@ -210,8 +211,9 @@ private: DebugMap &ModuleMap, const DebugMapObject &DMO, RangesTy &Ranges, OffsetsStringPool &OffsetsStringPool, UniquingStringPool &UniquingStringPool, - DeclContextTree &ODRContexts, unsigned &UnitID, - unsigned Indent = 0); + DeclContextTree &ODRContexts, uint64_t ModulesEndOffset, + unsigned &UnitID, unsigned Indent = 0, + bool Quiet = false); /// Flags passed to DwarfLinker::lookForDIEsToKeep enum TraversalFlags { diff --git a/tools/dsymutil/DwarfStreamer.cpp b/tools/dsymutil/DwarfStreamer.cpp index 7350d19e17bf..28088ff3369c 100644 --- a/tools/dsymutil/DwarfStreamer.cpp +++ b/tools/dsymutil/DwarfStreamer.cpp @@ -124,11 +124,11 @@ bool DwarfStreamer::init(Triple TheTriple) { return true; } -bool DwarfStreamer::finish(const DebugMap &DM) { +bool DwarfStreamer::finish(const DebugMap &DM, SymbolMapTranslator &T) { bool Result = true; if (DM.getTriple().isOSDarwin() && !DM.getBinaryPath().empty() && Options.FileType == OutputFileType::Object) - Result = MachOUtils::generateDsymCompanion(DM, *MS, OutFile); + Result = MachOUtils::generateDsymCompanion(DM, T, *MS, OutFile); else MS->Finish(); return Result; @@ -190,10 +190,8 @@ void DwarfStreamer::emitDIE(DIE &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(); + std::vector<DwarfStringPoolEntryRef> Entries = Pool.getEntriesForEmission(); for (auto Entry : Entries) { - if (Entry.getIndex() == -1U) - break; // Emit the string itself. Asm->OutStreamer->EmitBytes(Entry.getString()); // Emit a null terminator. @@ -322,7 +320,7 @@ void DwarfStreamer::emitUnitRangesEntries(CompileUnit &Unit, // The object addresses where sorted, but again, the linked // addresses might end up in a different order. - llvm::sort(Ranges.begin(), Ranges.end()); + llvm::sort(Ranges); if (!Ranges.empty()) { MS->SwitchSection(MC->getObjectFileInfo()->getDwarfARangesSection()); @@ -579,6 +577,89 @@ void DwarfStreamer::emitLineTableForUnit(MCDwarfLineTableParams Params, MS->EmitLabel(LineEndSym); } +/// Copy the debug_line over to the updated binary while unobfuscating the file +/// names and directories. +void DwarfStreamer::translateLineTable(DataExtractor Data, uint32_t Offset, + LinkOptions &Options) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + StringRef Contents = Data.getData(); + + // We have to deconstruct the line table header, because it contains to + // length fields that will need to be updated when we change the length of + // the files and directories in there. + unsigned UnitLength = Data.getU32(&Offset); + unsigned UnitEnd = Offset + UnitLength; + MCSymbol *BeginLabel = MC->createTempSymbol(); + MCSymbol *EndLabel = MC->createTempSymbol(); + unsigned Version = Data.getU16(&Offset); + + if (Version > 5) { + warn("Unsupported line table version: dropping contents and not " + "unobfsucating line table."); + return; + } + + Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); + Asm->OutStreamer->EmitLabel(BeginLabel); + Asm->emitInt16(Version); + LineSectionSize += 6; + + MCSymbol *HeaderBeginLabel = MC->createTempSymbol(); + MCSymbol *HeaderEndLabel = MC->createTempSymbol(); + Asm->EmitLabelDifference(HeaderEndLabel, HeaderBeginLabel, 4); + Asm->OutStreamer->EmitLabel(HeaderBeginLabel); + Offset += 4; + LineSectionSize += 4; + + uint32_t AfterHeaderLengthOffset = Offset; + // Skip to the directories. + Offset += (Version >= 4) ? 5 : 4; + unsigned OpcodeBase = Data.getU8(&Offset); + Offset += OpcodeBase - 1; + Asm->OutStreamer->EmitBytes(Contents.slice(AfterHeaderLengthOffset, Offset)); + LineSectionSize += Offset - AfterHeaderLengthOffset; + + // Offset points to the first directory. + while (const char *Dir = Data.getCStr(&Offset)) { + if (Dir[0] == 0) + break; + + StringRef Translated = Options.Translator(Dir); + Asm->OutStreamer->EmitBytes(Translated); + Asm->emitInt8(0); + LineSectionSize += Translated.size() + 1; + } + Asm->emitInt8(0); + LineSectionSize += 1; + + while (const char *File = Data.getCStr(&Offset)) { + if (File[0] == 0) + break; + + StringRef Translated = Options.Translator(File); + Asm->OutStreamer->EmitBytes(Translated); + Asm->emitInt8(0); + LineSectionSize += Translated.size() + 1; + + uint32_t OffsetBeforeLEBs = Offset; + Asm->EmitULEB128(Data.getULEB128(&Offset)); + Asm->EmitULEB128(Data.getULEB128(&Offset)); + Asm->EmitULEB128(Data.getULEB128(&Offset)); + LineSectionSize += Offset - OffsetBeforeLEBs; + } + Asm->emitInt8(0); + LineSectionSize += 1; + + Asm->OutStreamer->EmitLabel(HeaderEndLabel); + + // Copy the actual line table program over. + Asm->OutStreamer->EmitBytes(Contents.slice(Offset, UnitEnd)); + LineSectionSize += UnitEnd - Offset; + + Asm->OutStreamer->EmitLabel(EndLabel); + Offset = UnitEnd; +} + static void emitSectionContents(const object::ObjectFile &Obj, StringRef SecName, MCStreamer *MS) { StringRef Contents; @@ -588,8 +669,10 @@ static void emitSectionContents(const object::ObjectFile &Obj, } void DwarfStreamer::copyInvariantDebugSection(const object::ObjectFile &Obj) { - MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); - emitSectionContents(Obj, "debug_line", MS); + if (!Options.Translator) { + MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLineSection()); + emitSectionContents(Obj, "debug_line", MS); + } MS->SwitchSection(MC->getObjectFileInfo()->getDwarfLocSection()); emitSectionContents(Obj, "debug_loc", MS); diff --git a/tools/dsymutil/DwarfStreamer.h b/tools/dsymutil/DwarfStreamer.h index 74f0a09001d0..abc86547ef6f 100644 --- a/tools/dsymutil/DwarfStreamer.h +++ b/tools/dsymutil/DwarfStreamer.h @@ -7,6 +7,9 @@ // //===----------------------------------------------------------------------===// +#ifndef LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H +#define LLVM_TOOLS_DSYMUTIL_DWARFSTREAMER_H + #include "CompileUnit.h" #include "DebugMap.h" #include "LinkUtils.h" @@ -32,9 +35,6 @@ #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 { @@ -50,7 +50,7 @@ public: bool init(Triple TheTriple); /// Dump the file to the disk. - bool finish(const DebugMap &); + bool finish(const DebugMap &, SymbolMapTranslator &T); AsmPrinter &getAsmPrinter() const { return *Asm; } @@ -104,6 +104,11 @@ public: std::vector<DWARFDebugLine::Row> &Rows, unsigned AdddressSize); + /// Copy the debug_line over to the updated binary while unobfuscating the + /// file names and directories. + void translateLineTable(DataExtractor LineData, uint32_t Offset, + LinkOptions &Options); + /// Copy over the debug sections that are not modified when updating. void copyInvariantDebugSection(const object::ObjectFile &Obj); diff --git a/tools/dsymutil/LinkUtils.h b/tools/dsymutil/LinkUtils.h index f0abd888b529..07697418535c 100644 --- a/tools/dsymutil/LinkUtils.h +++ b/tools/dsymutil/LinkUtils.h @@ -10,8 +10,11 @@ #ifndef LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H #define LLVM_TOOLS_DSYMUTIL_LINKOPTIONS_H +#include "SymbolMap.h" + #include "llvm/ADT/Twine.h" #include "llvm/Support/WithColor.h" + #include <string> namespace llvm { @@ -60,6 +63,9 @@ struct LinkOptions { /// -oso-prepend-path std::string PrependPath; + /// Symbol map translator. + SymbolMapTranslator Translator; + LinkOptions() = default; }; diff --git a/tools/dsymutil/MachODebugMapParser.cpp b/tools/dsymutil/MachODebugMapParser.cpp index 48155b402042..8ff7e22da228 100644 --- a/tools/dsymutil/MachODebugMapParser.cpp +++ b/tools/dsymutil/MachODebugMapParser.cpp @@ -163,7 +163,8 @@ std::unique_ptr<DebugMap> MachODebugMapParser::parseOneBinary(const MachOObjectFile &MainBinary, StringRef BinaryPath) { loadMainBinarySymbols(MainBinary); - Result = make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath); + ArrayRef<uint8_t> UUID = MainBinary.getUuid(); + Result = make_unique<DebugMap>(MainBinary.getArchTriple(), BinaryPath, UUID); MainBinaryStrings = MainBinary.getStringTableData(); for (const SymbolRef &Symbol : MainBinary.symbols()) { const DataRefImpl &DRI = Symbol.getRawDataRefImpl(); @@ -511,14 +512,16 @@ void MachODebugMapParser::loadMainBinarySymbols( // Skip undefined and STAB entries. if ((Type == SymbolRef::ST_Debug) || (Type == SymbolRef::ST_Unknown)) continue; - // The only symbols of interest are the global variables. These - // are the only ones that need to be queried because the address - // of common data won't be described in the debug map. All other - // addresses should be fetched for the debug map. + // In theory, the only symbols of interest are the global variables. These + // are the only ones that need to be queried because the address of common + // data won't be described in the debug map. All other addresses should be + // fetched for the debug map. In reality, by playing with 'ld -r' and + // export lists, you can get symbols described as N_GSYM in the debug map, + // but associated with a local symbol. Gather all the symbols, but prefer + // the global ones. uint8_t SymType = MainBinary.getSymbolTableEntry(Sym.getRawDataRefImpl()).n_type; - if (!(SymType & (MachO::N_EXT | MachO::N_PEXT))) - continue; + bool Extern = SymType & (MachO::N_EXT | MachO::N_PEXT); Expected<section_iterator> SectionOrErr = Sym.getSection(); if (!SectionOrErr) { // TODO: Actually report errors helpfully. @@ -538,7 +541,11 @@ void MachODebugMapParser::loadMainBinarySymbols( StringRef Name = *NameOrErr; if (Name.size() == 0 || Name[0] == '\0') continue; - MainBinarySymbolAddresses[Name] = Addr; + // Override only if the new key is global. + if (Extern) + MainBinarySymbolAddresses[Name] = Addr; + else + MainBinarySymbolAddresses.try_emplace(Name, Addr); } } diff --git a/tools/dsymutil/MachOUtils.cpp b/tools/dsymutil/MachOUtils.cpp index fc498cc49c19..8c54563eab98 100644 --- a/tools/dsymutil/MachOUtils.cpp +++ b/tools/dsymutil/MachOUtils.cpp @@ -333,8 +333,8 @@ static unsigned segmentLoadCommandSize(bool Is64Bit, unsigned NumSections) { // Stream a dSYM companion binary file corresponding to the binary referenced // by \a DM to \a OutFile. The passed \a MS MCStreamer is setup to write to // \a OutFile and it must be using a MachObjectWriter object to do so. -bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, - raw_fd_ostream &OutFile) { +bool generateDsymCompanion(const DebugMap &DM, SymbolMapTranslator &Translator, + MCStreamer &MS, raw_fd_ostream &OutFile) { auto &ObjectStreamer = static_cast<MCObjectStreamer &>(MS); MCAssembler &MCAsm = ObjectStreamer.getAssembler(); auto &Writer = static_cast<MachObjectWriter &>(MCAsm.getWriter()); @@ -368,25 +368,37 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, bool Is64Bit = Writer.is64Bit(); MachO::symtab_command SymtabCmd = InputBinary.getSymtabLoadCommand(); - // Get UUID. + // Compute the number of load commands we will need. + unsigned LoadCommandSize = 0; + unsigned NumLoadCommands = 0; + + // Get LC_UUID and LC_BUILD_VERSION. MachO::uuid_command UUIDCmd; + SmallVector<MachO::build_version_command, 2> BuildVersionCmd; memset(&UUIDCmd, 0, sizeof(UUIDCmd)); - UUIDCmd.cmd = MachO::LC_UUID; - UUIDCmd.cmdsize = sizeof(MachO::uuid_command); for (auto &LCI : InputBinary.load_commands()) { - if (LCI.C.cmd == MachO::LC_UUID) { + switch (LCI.C.cmd) { + case MachO::LC_UUID: + if (UUIDCmd.cmd) + return error("Binary contains more than one UUID"); UUIDCmd = InputBinary.getUuidCommand(LCI); + ++NumLoadCommands; + LoadCommandSize += sizeof(UUIDCmd); + break; + case MachO::LC_BUILD_VERSION: { + MachO::build_version_command Cmd; + memset(&Cmd, 0, sizeof(Cmd)); + Cmd = InputBinary.getBuildVersionLoadCommand(LCI); + ++NumLoadCommands; + LoadCommandSize += sizeof(Cmd); + // LLDB doesn't care about the build tools for now. + Cmd.ntools = 0; + BuildVersionCmd.push_back(Cmd); + break; + } + default: break; } - } - - // Compute the number of load commands we will need. - unsigned LoadCommandSize = 0; - unsigned NumLoadCommands = 0; - // We will copy the UUID if there is one. - if (UUIDCmd.cmd != 0) { - ++NumLoadCommands; - LoadCommandSize += sizeof(MachO::uuid_command); } // If we have a valid symtab to copy, do it. @@ -431,7 +443,7 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, } SmallString<0> NewSymtab; - NonRelocatableStringpool NewStrings; + NonRelocatableStringpool NewStrings(Translator); unsigned NListSize = Is64Bit ? sizeof(MachO::nlist_64) : sizeof(MachO::nlist); unsigned NumSyms = 0; uint64_t NewStringsSize = 0; @@ -452,10 +464,18 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, assert(OutFile.tell() == HeaderSize); if (UUIDCmd.cmd != 0) { Writer.W.write<uint32_t>(UUIDCmd.cmd); - Writer.W.write<uint32_t>(UUIDCmd.cmdsize); + Writer.W.write<uint32_t>(sizeof(UUIDCmd)); OutFile.write(reinterpret_cast<const char *>(UUIDCmd.uuid), 16); assert(OutFile.tell() == HeaderSize + sizeof(UUIDCmd)); } + for (auto Cmd : BuildVersionCmd) { + Writer.W.write<uint32_t>(Cmd.cmd); + Writer.W.write<uint32_t>(sizeof(Cmd)); + Writer.W.write<uint32_t>(Cmd.platform); + Writer.W.write<uint32_t>(Cmd.minos); + Writer.W.write<uint32_t>(Cmd.sdk); + Writer.W.write<uint32_t>(Cmd.ntools); + } assert(SymtabCmd.cmd && "No symbol table."); uint64_t StringStart = SymtabStart + NumSyms * NListSize; @@ -514,10 +534,9 @@ bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, // Reproduce that behavior for now (there is corresponding code in // transferSymbol). OutFile << '\0'; - std::vector<DwarfStringPoolEntryRef> Strings = NewStrings.getEntries(); + std::vector<DwarfStringPoolEntryRef> Strings = + NewStrings.getEntriesForEmission(); for (auto EntryRef : Strings) { - if (EntryRef.getIndex() == -1U) - break; OutFile.write(EntryRef.getString().data(), EntryRef.getString().size() + 1); } diff --git a/tools/dsymutil/MachOUtils.h b/tools/dsymutil/MachOUtils.h index a8be89e906b5..c24f963e1d98 100644 --- a/tools/dsymutil/MachOUtils.h +++ b/tools/dsymutil/MachOUtils.h @@ -9,8 +9,11 @@ #ifndef LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H #define LLVM_TOOLS_DSYMUTIL_MACHOUTILS_H +#include "SymbolMap.h" + #include "llvm/ADT/StringRef.h" #include "llvm/Support/FileSystem.h" + #include <string> namespace llvm { @@ -38,8 +41,8 @@ bool generateUniversalBinary(SmallVectorImpl<ArchAndFile> &ArchFiles, StringRef OutputFileName, const LinkOptions &, StringRef SDKPath); -bool generateDsymCompanion(const DebugMap &DM, MCStreamer &MS, - raw_fd_ostream &OutFile); +bool generateDsymCompanion(const DebugMap &DM, SymbolMapTranslator &Translator, + MCStreamer &MS, raw_fd_ostream &OutFile); std::string getArchName(StringRef Arch); } // namespace MachOUtils diff --git a/tools/dsymutil/NonRelocatableStringpool.cpp b/tools/dsymutil/NonRelocatableStringpool.cpp index f1e5f596b849..b8392a112526 100644 --- a/tools/dsymutil/NonRelocatableStringpool.cpp +++ b/tools/dsymutil/NonRelocatableStringpool.cpp @@ -16,34 +16,40 @@ DwarfStringPoolEntryRef NonRelocatableStringpool::getEntry(StringRef S) { if (S.empty() && !Strings.empty()) return EmptyString; + if (Translator) + S = Translator(S); auto I = Strings.insert({S, DwarfStringPoolEntry()}); auto &Entry = I.first->second; - if (I.second || Entry.Index == -1U) { + if (I.second || !Entry.isIndexed()) { Entry.Index = NumEntries++; Entry.Offset = CurrentEndOffset; Entry.Symbol = nullptr; CurrentEndOffset += S.size() + 1; } - return DwarfStringPoolEntryRef(*I.first); + return DwarfStringPoolEntryRef(*I.first, true); } StringRef NonRelocatableStringpool::internString(StringRef S) { - DwarfStringPoolEntry Entry{nullptr, 0, -1U}; + DwarfStringPoolEntry Entry{nullptr, 0, DwarfStringPoolEntry::NotIndexed}; + + if (Translator) + S = Translator(S); + auto InsertResult = Strings.insert({S, Entry}); return InsertResult.first->getKey(); } std::vector<DwarfStringPoolEntryRef> -NonRelocatableStringpool::getEntries() const { +NonRelocatableStringpool::getEntriesForEmission() 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(); - }); + if (E.getValue().isIndexed()) + Result.emplace_back(E, true); + llvm::sort(Result, [](const DwarfStringPoolEntryRef A, + const DwarfStringPoolEntryRef B) { + return A.getIndex() < B.getIndex(); + }); return Result; } diff --git a/tools/dsymutil/NonRelocatableStringpool.h b/tools/dsymutil/NonRelocatableStringpool.h index 733ceebea614..c398ff0cec69 100644 --- a/tools/dsymutil/NonRelocatableStringpool.h +++ b/tools/dsymutil/NonRelocatableStringpool.h @@ -10,6 +10,8 @@ #ifndef LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H #define LLVM_TOOLS_DSYMUTIL_NONRELOCATABLESTRINGPOOL_H +#include "SymbolMap.h" + #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/CodeGen/DwarfStringPoolEntry.h" @@ -32,7 +34,9 @@ public: /// order. using MapTy = StringMap<DwarfStringPoolEntry, BumpPtrAllocator>; - NonRelocatableStringpool() { + NonRelocatableStringpool( + SymbolMapTranslator Translator = SymbolMapTranslator()) + : Translator(Translator) { // Legacy dsymutil puts an empty string at the start of the line table. EmptyString = getEntry(""); } @@ -53,13 +57,16 @@ public: uint64_t getSize() { return CurrentEndOffset; } - std::vector<DwarfStringPoolEntryRef> getEntries() const; + /// Return the list of strings to be emitted. This does not contain the + /// strings which were added via internString only. + std::vector<DwarfStringPoolEntryRef> getEntriesForEmission() const; private: MapTy Strings; uint32_t CurrentEndOffset = 0; unsigned NumEntries = 0; DwarfStringPoolEntryRef EmptyString; + SymbolMapTranslator Translator; }; /// Helper for making strong types. diff --git a/tools/dsymutil/SymbolMap.cpp b/tools/dsymutil/SymbolMap.cpp new file mode 100644 index 000000000000..cab9374a7d97 --- /dev/null +++ b/tools/dsymutil/SymbolMap.cpp @@ -0,0 +1,162 @@ +//===- tools/dsymutil/SymbolMap.cpp ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "SymbolMap.h" +#include "DebugMap.h" +#include "MachOUtils.h" + +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/WithColor.h" + +#ifdef __APPLE__ +#include <CoreFoundation/CoreFoundation.h> +#include <uuid/uuid.h> +#endif + +namespace llvm { +namespace dsymutil { + +StringRef SymbolMapTranslator::operator()(StringRef Input) { + if (!Input.startswith("__hidden#") && !Input.startswith("___hidden#")) + return Input; + + bool MightNeedUnderscore = false; + StringRef Line = Input.drop_front(sizeof("__hidden#") - 1); + if (Line[0] == '#') { + Line = Line.drop_front(); + MightNeedUnderscore = true; + } + + std::size_t LineNumber = std::numeric_limits<std::size_t>::max(); + Line.split('_').first.getAsInteger(10, LineNumber); + if (LineNumber >= UnobfuscatedStrings.size()) { + WithColor::warning() << "reference to a unexisting unobfuscated string " + << Input << ": symbol map mismatch?\n" + << Line << '\n'; + return Input; + } + + const std::string &Translation = UnobfuscatedStrings[LineNumber]; + if (!MightNeedUnderscore || !MangleNames) + return Translation; + + // Objective-C symbols for the MachO symbol table start with a \1. Please see + // `CGObjCCommonMac::GetNameForMethod` in clang. + if (Translation[0] == 1) + return StringRef(Translation).drop_front(); + + // We need permanent storage for the string we are about to create. Just + // append it to the vector containing translations. This should only happen + // during MachO symbol table translation, thus there should be no risk on + // exponential growth. + UnobfuscatedStrings.emplace_back("_" + Translation); + return UnobfuscatedStrings.back(); +} + +SymbolMapTranslator SymbolMapLoader::Load(StringRef InputFile, + const DebugMap &Map) const { + if (SymbolMap.empty()) + return {}; + + std::string SymbolMapPath = SymbolMap; + +#if __APPLE__ + // Look through the UUID Map. + if (sys::fs::is_directory(SymbolMapPath) && !Map.getUUID().empty()) { + uuid_string_t UUIDString; + uuid_unparse_upper((const uint8_t *)Map.getUUID().data(), UUIDString); + + SmallString<256> PlistPath( + sys::path::parent_path(sys::path::parent_path(InputFile))); + sys::path::append(PlistPath, StringRef(UUIDString).str() + ".plist"); + + CFStringRef plistFile = CFStringCreateWithCString( + kCFAllocatorDefault, PlistPath.c_str(), kCFStringEncodingUTF8); + CFURLRef fileURL = CFURLCreateWithFileSystemPath( + kCFAllocatorDefault, plistFile, kCFURLPOSIXPathStyle, false); + CFReadStreamRef resourceData = + CFReadStreamCreateWithFile(kCFAllocatorDefault, fileURL); + if (resourceData) { + CFReadStreamOpen(resourceData); + CFDictionaryRef plist = (CFDictionaryRef)CFPropertyListCreateWithStream( + kCFAllocatorDefault, resourceData, 0, kCFPropertyListImmutable, + nullptr, nullptr); + + if (plist) { + if (CFDictionaryContainsKey(plist, CFSTR("DBGOriginalUUID"))) { + CFStringRef OldUUID = (CFStringRef)CFDictionaryGetValue( + plist, CFSTR("DBGOriginalUUID")); + + StringRef UUID(CFStringGetCStringPtr(OldUUID, kCFStringEncodingUTF8)); + SmallString<256> BCSymbolMapPath(SymbolMapPath); + sys::path::append(BCSymbolMapPath, UUID.str() + ".bcsymbolmap"); + SymbolMapPath = BCSymbolMapPath.str(); + } + CFRelease(plist); + } + CFReadStreamClose(resourceData); + CFRelease(resourceData); + } + CFRelease(fileURL); + CFRelease(plistFile); + } +#endif + + if (sys::fs::is_directory(SymbolMapPath)) { + SymbolMapPath += (Twine("/") + sys::path::filename(InputFile) + "-" + + MachOUtils::getArchName(Map.getTriple().getArchName()) + + ".bcsymbolmap") + .str(); + } + + auto ErrOrMemBuffer = MemoryBuffer::getFile(SymbolMapPath); + if (auto EC = ErrOrMemBuffer.getError()) { + WithColor::warning() << SymbolMapPath << ": " << EC.message() + << ": not unobfuscating.\n"; + return {}; + } + + std::vector<std::string> UnobfuscatedStrings; + auto &MemBuf = **ErrOrMemBuffer; + StringRef Data(MemBuf.getBufferStart(), + MemBuf.getBufferEnd() - MemBuf.getBufferStart()); + StringRef LHS; + std::tie(LHS, Data) = Data.split('\n'); + bool MangleNames = false; + + // Check version string first. + if (!LHS.startswith("BCSymbolMap Version:")) { + // Version string not present, warns but try to parse it. + WithColor::warning() << SymbolMapPath + << " is missing version string: assuming 1.0.\n"; + UnobfuscatedStrings.emplace_back(LHS); + } else if (LHS.equals("BCSymbolMap Version: 1.0")) { + MangleNames = true; + } else if (LHS.equals("BCSymbolMap Version: 2.0")) { + MangleNames = false; + } else { + StringRef VersionNum; + std::tie(LHS, VersionNum) = LHS.split(':'); + WithColor::warning() << SymbolMapPath + << " has unsupported symbol map version" << VersionNum + << ": not unobfuscating.\n"; + return {}; + } + + while (!Data.empty()) { + std::tie(LHS, Data) = Data.split('\n'); + UnobfuscatedStrings.emplace_back(LHS); + } + + return SymbolMapTranslator(std::move(UnobfuscatedStrings), MangleNames); +} + +} // namespace dsymutil +} // namespace llvm diff --git a/tools/dsymutil/SymbolMap.h b/tools/dsymutil/SymbolMap.h new file mode 100644 index 000000000000..e3fbdbb01d82 --- /dev/null +++ b/tools/dsymutil/SymbolMap.h @@ -0,0 +1,54 @@ +//=- tools/dsymutil/SymbolMap.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_DSYMUTIL_SYMBOLMAP_H +#define LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H + +#include "llvm/ADT/StringRef.h" + +#include <string> +#include <vector> + +namespace llvm { +namespace dsymutil { +class DebugMap; + +/// Callable class to unobfuscate strings based on a BCSymbolMap. +class SymbolMapTranslator { +public: + SymbolMapTranslator() : MangleNames(false) {} + + SymbolMapTranslator(std::vector<std::string> UnobfuscatedStrings, + bool MangleNames) + : UnobfuscatedStrings(std::move(UnobfuscatedStrings)), + MangleNames(MangleNames) {} + + StringRef operator()(StringRef Input); + + operator bool() const { return !UnobfuscatedStrings.empty(); } + +private: + std::vector<std::string> UnobfuscatedStrings; + bool MangleNames; +}; + +/// Class to initialize SymbolMapTranslators from a BCSymbolMap. +class SymbolMapLoader { +public: + SymbolMapLoader(std::string SymbolMap) : SymbolMap(std::move(SymbolMap)) {} + + SymbolMapTranslator Load(StringRef InputFile, const DebugMap &Map) const; + +private: + const std::string SymbolMap; +}; +} // namespace dsymutil +} // namespace llvm + +#endif // LLVM_TOOLS_DSYMUTIL_SYMBOLMAP_H diff --git a/tools/dsymutil/dsymutil.cpp b/tools/dsymutil/dsymutil.cpp index c0e6d505941f..ec8d0507b08a 100644 --- a/tools/dsymutil/dsymutil.cpp +++ b/tools/dsymutil/dsymutil.cpp @@ -7,9 +7,8 @@ // //===----------------------------------------------------------------------===// // -// This program is a utility that aims to be a dropin replacement for -// Darwin's dsymutil. -// +// This program is a utility that aims to be a dropin replacement for Darwin's +// dsymutil. //===----------------------------------------------------------------------===// #include "dsymutil.h" @@ -60,6 +59,8 @@ static opt<std::string> OutputFileOpt("o", desc("Specify the output file. default: <input file>.dwarf"), value_desc("filename"), cat(DsymCategory)); +static alias OutputFileOptA("out", desc("Alias for -o"), + aliasopt(OutputFileOpt)); static opt<std::string> OsoPrependPath( "oso-prepend-path", @@ -101,6 +102,11 @@ static opt<bool> Update( init(false), cat(DsymCategory)); static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update)); +static opt<std::string> SymbolMap( + "symbol-map", + desc("Updates the existing dSYMs inplace using symbol map specified."), + value_desc("bcsymbolmap"), cat(DsymCategory)); + static cl::opt<AccelTableKind> AcceleratorTable( "accelerator", cl::desc("Output accelerator tables."), cl::values(clEnumValN(AccelTableKind::Default, "Default", @@ -165,20 +171,18 @@ static opt<bool> desc("Embed warnings in the linked DWARF debug info."), cat(DsymCategory)); -static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { +static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { if (NoOutput) - return true; + return Error::success(); // Create plist file to write to. llvm::SmallString<128> InfoPlist(BundleRoot); llvm::sys::path::append(InfoPlist, "Contents/Info.plist"); std::error_code EC; llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text); - if (EC) { - WithColor::error() << "cannot create plist file " << InfoPlist << ": " - << EC.message() << '\n'; - return false; - } + if (EC) + return make_error<StringError>( + "cannot create Plist: " + toString(errorCodeToError(EC)), EC); CFBundleInfo BI = getBundleInfo(Bin); @@ -230,22 +234,21 @@ static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { << "</plist>\n"; PL.close(); - return true; + return Error::success(); } -static bool createBundleDir(llvm::StringRef BundleBase) { +static Error createBundleDir(llvm::StringRef BundleBase) { if (NoOutput) - return true; + return Error::success(); llvm::SmallString<128> Bundle(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)) { - WithColor::error() << "cannot create directory " << Bundle << ": " - << EC.message() << "\n"; - return false; - } - return true; + if (std::error_code EC = + create_directories(Bundle.str(), true, llvm::sys::fs::perms::all_all)) + return make_error<StringError>( + "cannot create bundle: " + toString(errorCodeToError(EC)), EC); + + return Error::success(); } static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { @@ -257,7 +260,7 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile); if (!BinOrErr) { - errs() << OutputFile << ": " << toString(BinOrErr.takeError()); + WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError()); return false; } @@ -276,9 +279,12 @@ static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { return false; } -static std::string getOutputFileName(llvm::StringRef InputFile) { +static Expected<std::string> getOutputFileName(llvm::StringRef InputFile) { + if (OutputFileOpt == "-") + return OutputFileOpt; + // When updating, do in place replacement. - if (OutputFileOpt.empty() && Update) + if (OutputFileOpt.empty() && (Update || !SymbolMap.empty())) return InputFile; // If a flat dSYM has been requested, things are pretty simple. @@ -305,8 +311,10 @@ static std::string getOutputFileName(llvm::StringRef InputFile) { llvm::SmallString<128> BundleDir(OutputFileOpt); if (BundleDir.empty()) BundleDir = DwarfFile + ".dSYM"; - if (!createBundleDir(BundleDir) || !createPlistFile(DwarfFile, BundleDir)) - return ""; + if (auto E = createBundleDir(BundleDir)) + return std::move(E); + if (auto E = createPlistFile(DwarfFile, BundleDir)) + return std::move(E); llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF", llvm::sys::path::filename(DwarfFile)); @@ -327,6 +335,9 @@ static Expected<LinkOptions> getOptions() { Options.PrependPath = OsoPrependPath; Options.TheAccelTableKind = AcceleratorTable; + if (!SymbolMap.empty()) + Options.Update = true; + if (Assembly) Options.FileType = OutputFileType::Assembly; @@ -445,6 +456,13 @@ int main(int argc, char **argv) { return 1; } + if (InputFiles.size() > 1 && !SymbolMap.empty() && + !llvm::sys::fs::is_directory(SymbolMap)) { + WithColor::error() << "when unobfuscating multiple files, --symbol-map " + << "needs to point to a directory.\n"; + return 1; + } + if (getenv("RC_DEBUG_OPTIONS")) PaperTrailWarnings = true; @@ -459,6 +477,8 @@ int main(int argc, char **argv) { return 1; } + SymbolMapLoader SymMapLoader(SymbolMap); + for (auto &InputFile : *InputsOrErr) { // Dump the symbol table for each input file and requested arch if (DumpStab) { @@ -513,6 +533,9 @@ int main(int argc, char **argv) { if (DumpDebugMap) continue; + if (!SymbolMap.empty()) + OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map); + if (Map->begin() == Map->end()) WithColor::warning() << "no debug symbols in executable (-arch " @@ -521,13 +544,20 @@ int main(int argc, char **argv) { // Using a std::shared_ptr rather than std::unique_ptr because move-only // types don't work with std::bind in the ThreadPool implementation. std::shared_ptr<raw_fd_ostream> OS; - std::string OutputFile = getOutputFileName(InputFile); + + Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile); + if (!OutputFileOrErr) { + WithColor::error() << toString(OutputFileOrErr.takeError()); + return 1; + } + + std::string OutputFile = *OutputFileOrErr; if (NeedsTempFiles) { TempFiles.emplace_back(Map->getTriple().getArchName().str()); auto E = TempFiles.back().createTempFile(); if (E) { - errs() << toString(std::move(E)); + WithColor::error() << toString(std::move(E)); return 1; } @@ -540,7 +570,7 @@ int main(int argc, char **argv) { OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC, sys::fs::F_None); if (EC) { - errs() << OutputFile << ": " << EC.message(); + WithColor::error() << OutputFile << ": " << EC.message(); return 1; } } @@ -567,10 +597,16 @@ int main(int argc, char **argv) { if (!AllOK) return 1; - if (NeedsTempFiles && - !MachOUtils::generateUniversalBinary( - TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath)) - return 1; + if (NeedsTempFiles) { + Expected<std::string> OutputFileOrErr = getOutputFileName(InputFile); + if (!OutputFileOrErr) { + WithColor::error() << toString(OutputFileOrErr.takeError()); + return 1; + } + if (!MachOUtils::generateUniversalBinary(TempFiles, *OutputFileOrErr, + *OptionsOrErr, SDKPath)) + return 1; + } } return 0; diff --git a/tools/gold/CMakeLists.txt b/tools/gold/CMakeLists.txt index d2580329acab..72f76558c088 100644 --- a/tools/gold/CMakeLists.txt +++ b/tools/gold/CMakeLists.txt @@ -11,7 +11,7 @@ if( LLVM_ENABLE_PIC AND LLVM_BINUTILS_INCDIR ) IPO ) - add_llvm_loadable_module(LLVMgold + add_llvm_library(LLVMgold MODULE gold-plugin.cpp ) diff --git a/tools/gold/gold-plugin.cpp b/tools/gold/gold-plugin.cpp index 6c55ebcddc43..738cafa6cacf 100644 --- a/tools/gold/gold-plugin.cpp +++ b/tools/gold/gold-plugin.cpp @@ -128,6 +128,7 @@ namespace options { OT_NORMAL, OT_DISABLE, OT_BC_ONLY, + OT_ASM_ONLY, OT_SAVE_TEMPS }; static OutputType TheOutputType = OT_NORMAL; @@ -229,6 +230,8 @@ namespace options { TheOutputType = OT_SAVE_TEMPS; } else if (opt == "disable-output") { TheOutputType = OT_DISABLE; + } else if (opt == "emit-asm") { + TheOutputType = OT_ASM_ONLY; } else if (opt == "thinlto") { thinlto = true; } else if (opt == "thinlto-index-only") { @@ -447,8 +450,8 @@ static void diagnosticHandler(const DiagnosticInfo &DI) { ld_plugin_level Level; switch (DI.getSeverity()) { case DS_Error: - message(LDPL_FATAL, "LLVM gold plugin has failed to create LTO module: %s", - ErrStorage.c_str()); + Level = LDPL_FATAL; + break; case DS_Warning: Level = LDPL_WARNING; break; @@ -667,13 +670,9 @@ static void getThinLTOOldAndNewSuffix(std::string &OldSuffix, /// suffix matching \p OldSuffix with \p NewSuffix. static std::string getThinLTOObjectFileName(StringRef Path, StringRef OldSuffix, StringRef NewSuffix) { - if (OldSuffix.empty() && NewSuffix.empty()) - return Path; - StringRef NewPath = Path; - NewPath.consume_back(OldSuffix); - std::string NewNewPath = NewPath; - NewNewPath += NewSuffix; - return NewNewPath; + if (Path.consume_back(OldSuffix)) + return (Path + NewSuffix).str(); + return Path; } // Returns true if S is valid as a C language identifier. @@ -837,8 +836,10 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite, Conf.Options.RelaxELFRelocations = false; // Toggle function/data sections. - Conf.Options.FunctionSections = SplitSections; - Conf.Options.DataSections = SplitSections; + if (FunctionSections.getNumOccurrences() == 0) + Conf.Options.FunctionSections = SplitSections; + if (DataSections.getNumOccurrences() == 0) + Conf.Options.DataSections = SplitSections; Conf.MAttrs = MAttrs; Conf.RelocModel = RelocationModel; @@ -884,6 +885,9 @@ static std::unique_ptr<LTO> createLTO(IndexWriteCallback OnIndexWrite, check(Conf.addSaveTemps(output_name + ".", /* UseInputModulePath */ true)); break; + case options::OT_ASM_ONLY: + Conf.CGFileType = TargetMachine::CGFT_AssemblyFile; + break; } if (!options::sample_profile.empty()) @@ -1011,6 +1015,8 @@ static std::vector<std::pair<SmallString<128>, bool>> runLTO() { Filename = options::obj_path; else if (options::TheOutputType == options::OT_SAVE_TEMPS) Filename = output_name + ".o"; + else if (options::TheOutputType == options::OT_ASM_ONLY) + Filename = output_name; bool SaveTemps = !Filename.empty(); size_t MaxTasks = Lto->getMaxTasks(); @@ -1059,7 +1065,8 @@ static ld_plugin_status allSymbolsReadHook() { std::vector<std::pair<SmallString<128>, bool>> Files = runLTO(); if (options::TheOutputType == options::OT_DISABLE || - options::TheOutputType == options::OT_BC_ONLY) + options::TheOutputType == options::OT_BC_ONLY || + options::TheOutputType == options::OT_ASM_ONLY) return LDPS_OK; if (options::thinlto_index_only) { @@ -1084,6 +1091,7 @@ static ld_plugin_status all_symbols_read_hook(void) { llvm_shutdown(); if (options::TheOutputType == options::OT_BC_ONLY || + options::TheOutputType == options::OT_ASM_ONLY || options::TheOutputType == options::OT_DISABLE) { if (options::TheOutputType == options::OT_DISABLE) { // Remove the output file here since ld.bfd creates the output file diff --git a/tools/lli/lli.cpp b/tools/lli/lli.cpp index 1940dbd848cc..7e93d31361aa 100644 --- a/tools/lli/lli.cpp +++ b/tools/lli/lli.cpp @@ -26,6 +26,7 @@ #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/ExecutionEngine/ObjectCache.h" #include "llvm/ExecutionEngine/Orc/ExecutionUtils.h" +#include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h" #include "llvm/ExecutionEngine/Orc/LLJIT.h" #include "llvm/ExecutionEngine/Orc/OrcRemoteTargetClient.h" #include "llvm/ExecutionEngine/OrcMCJITReplacement.h" @@ -34,7 +35,6 @@ #include "llvm/IR/LLVMContext.h" #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" @@ -97,6 +97,28 @@ namespace { "orc-lazy", "Orc-based lazy JIT."))); + cl::opt<unsigned> + LazyJITCompileThreads("compile-threads", + cl::desc("Choose the number of compile threads " + "(jit-kind=orc-lazy only)"), + cl::init(0)); + + cl::list<std::string> + ThreadEntryPoints("thread-entry", + cl::desc("calls the given entry-point on a new thread " + "(jit-kind=orc-lazy only)")); + + cl::opt<bool> PerModuleLazy( + "per-module-lazy", + cl::desc("Performs lazy compilation on whole module boundaries " + "rather than individual functions"), + cl::init(false)); + + cl::list<std::string> + JITDylibs("jd", + cl::desc("Specifies the JITDylib to be used for any subsequent " + "-extra-module arguments.")); + // The MCJIT supports building for a target address space separate from // the JIT compilation process. Use a forked process and a copying // memory manager with IPC to execute using this functionality. @@ -294,23 +316,18 @@ static void addCygMingExtraModule(ExecutionEngine &EE, LLVMContext &Context, M->setTargetTriple(TargetTripleStr); // Create an empty function named "__main". - Function *Result; - if (TargetTriple.isArch64Bit()) { - Result = Function::Create( - TypeBuilder<int64_t(void), false>::get(Context), - GlobalValue::ExternalLinkage, "__main", M.get()); - } else { - Result = Function::Create( - TypeBuilder<int32_t(void), false>::get(Context), - GlobalValue::ExternalLinkage, "__main", M.get()); - } - BasicBlock *BB = BasicBlock::Create(Context, "__main", Result); - Builder.SetInsertPoint(BB); - Value *ReturnVal; + Type *ReturnTy; if (TargetTriple.isArch64Bit()) - ReturnVal = ConstantInt::get(Context, APInt(64, 0)); + ReturnTy = Type::getInt64Ty(Context); else - ReturnVal = ConstantInt::get(Context, APInt(32, 0)); + ReturnTy = Type::getInt32Ty(Context); + Function *Result = + Function::Create(FunctionType::get(ReturnTy, {}, false), + GlobalValue::ExternalLinkage, "__main", M.get()); + + BasicBlock *BB = BasicBlock::Create(Context, "__main", Result); + Builder.SetInsertPoint(BB); + Value *ReturnVal = ConstantInt::get(ReturnTy, 0); Builder.CreateRet(ReturnVal); // Add this new module to the ExecutionEngine. @@ -337,8 +354,8 @@ 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); +int runOrcLazyJIT(const char *ProgName); +void disallowOrcOptions(); //===----------------------------------------------------------------------===// // main Driver function @@ -362,6 +379,11 @@ int main(int argc, char **argv, char * const *envp) { if (DisableCoreFiles) sys::Process::PreventCoreFiles(); + if (UseJITKind == JITKind::OrcLazy) + return runOrcLazyJIT(argv[0]); + else + disallowOrcOptions(); + LLVMContext Context; // Load the bitcode... @@ -371,21 +393,6 @@ int main(int argc, char **argv, char * const *envp) { if (!Mod) reportError(Err, argv[0]); - if (UseJITKind == JITKind::OrcLazy) { - std::vector<std::unique_ptr<Module>> Ms; - Ms.push_back(std::move(Owner)); - for (auto &ExtraMod : ExtraModules) { - Ms.push_back(parseIRFile(ExtraMod, Err, Context)); - if (!Ms.back()) - reportError(Err, argv[0]); - } - std::vector<std::string> Args; - Args.push_back(InputFile); - for (auto &Arg : InputArgv) - Args.push_back(Arg); - return runOrcLazyJIT(Context, std::move(Ms), Args); - } - if (EnableCacheManager) { std::string CacheName("file:"); CacheName.append(InputFile); @@ -498,7 +505,7 @@ int main(int argc, char **argv, char * const *envp) { if (!ArOrErr) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(ArOrErr.takeError(), OS, ""); + logAllUnhandledErrors(ArOrErr.takeError(), OS); OS.flush(); errs() << Buf; exit(1); @@ -688,16 +695,18 @@ int main(int argc, char **argv, char * const *envp) { return Result; } -static orc::IRTransformLayer2::TransformFunction createDebugDumper() { +static orc::IRTransformLayer::TransformFunction createDebugDumper() { switch (OrcDumpKind) { case DumpKind::NoDump: - return [](std::unique_ptr<Module> M) { return M; }; + return [](orc::ThreadSafeModule TSM, + const orc::MaterializationResponsibility &R) { return TSM; }; case DumpKind::DumpFuncsToStdOut: - return [](std::unique_ptr<Module> M) { + return [](orc::ThreadSafeModule TSM, + const orc::MaterializationResponsibility &R) { printf("[ "); - for (const auto &F : *M) { + for (const auto &F : *TSM.getModule()) { if (F.isDeclaration()) continue; @@ -709,55 +718,58 @@ static orc::IRTransformLayer2::TransformFunction createDebugDumper() { } printf("]\n"); - return M; + return TSM; }; case DumpKind::DumpModsToStdOut: - return [](std::unique_ptr<Module> M) { + return [](orc::ThreadSafeModule TSM, + const orc::MaterializationResponsibility &R) { outs() << "----- Module Start -----\n" - << *M << "----- Module End -----\n"; + << *TSM.getModule() << "----- Module End -----\n"; - return M; + return TSM; }; case DumpKind::DumpModsToDisk: - return [](std::unique_ptr<Module> M) { + return [](orc::ThreadSafeModule TSM, + const orc::MaterializationResponsibility &R) { std::error_code EC; - raw_fd_ostream Out(M->getModuleIdentifier() + ".ll", EC, sys::fs::F_Text); + raw_fd_ostream Out(TSM.getModule()->getModuleIdentifier() + ".ll", EC, + sys::fs::F_Text); if (EC) { - errs() << "Couldn't open " << M->getModuleIdentifier() + errs() << "Couldn't open " << TSM.getModule()->getModuleIdentifier() << " for dumping.\nError:" << EC.message() << "\n"; exit(1); } - Out << *M; - return M; + Out << *TSM.getModule(); + return TSM; }; } 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; - } +static void exitOnLazyCallThroughFailure() { exit(1); } + +int runOrcLazyJIT(const char *ProgName) { + // Start setting up the JIT environment. - const auto &TT = Ms.front()->getTargetTriple(); - orc::JITTargetMachineBuilder TMD = + // Parse the main module. + orc::ThreadSafeContext TSCtx(llvm::make_unique<LLVMContext>()); + SMDiagnostic Err; + auto MainModule = orc::ThreadSafeModule( + parseIRFile(InputFile, Err, *TSCtx.getContext()), TSCtx); + if (!MainModule) + reportError(Err, ProgName); + + const auto &TT = MainModule.getModule()->getTargetTriple(); + orc::JITTargetMachineBuilder JTMB = TT.empty() ? ExitOnErr(orc::JITTargetMachineBuilder::detectHost()) : orc::JITTargetMachineBuilder(Triple(TT)); - TMD.setArch(MArch) - .setCPU(getCPUStr()) + if (!MArch.empty()) + JTMB.getTargetTriple().setArchName(MArch); + + JTMB.setCPU(getCPUStr()) .addFeatures(getFeatureList()) .setRelocationModel(RelocModel.getNumOccurrences() ? Optional<Reloc::Model>(RelocModel) @@ -765,53 +777,135 @@ int runOrcLazyJIT(LLVMContext &Ctx, std::vector<std::unique_ptr<Module>> Ms, .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)); + + DataLayout DL = ExitOnErr(JTMB.getDefaultDataLayoutForTarget()); + + auto J = ExitOnErr(orc::LLLazyJIT::Create( + std::move(JTMB), DL, + pointerToJITTargetAddress(exitOnLazyCallThroughFailure), + LazyJITCompileThreads)); + + if (PerModuleLazy) + J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule); 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; })); + J->setLazyCompileTransform([&](orc::ThreadSafeModule TSM, + const orc::MaterializationResponsibility &R) { + if (verifyModule(*TSM.getModule(), &dbgs())) { + dbgs() << "Bad module: " << *TSM.getModule() << "\n"; + exit(1); + } + return Dump(std::move(TSM), R); + }); + J->getMainJITDylib().setGenerator( + ExitOnErr(orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(DL))); orc::MangleAndInterner Mangle(J->getExecutionSession(), DL); - orc::LocalCXXRuntimeOverrides2 CXXRuntimeOverrides; - ExitOnErr(CXXRuntimeOverrides.enable(J->getMainVSO(), Mangle)); + orc::LocalCXXRuntimeOverrides CXXRuntimeOverrides; + ExitOnErr(CXXRuntimeOverrides.enable(J->getMainJITDylib(), Mangle)); + + // Add the main module. + ExitOnErr(J->addLazyIRModule(std::move(MainModule))); + + // Create JITDylibs and add any extra modules. + { + // Create JITDylibs, keep a map from argument index to dylib. We will use + // -extra-module argument indexes to determine what dylib to use for each + // -extra-module. + std::map<unsigned, orc::JITDylib *> IdxToDylib; + IdxToDylib[0] = &J->getMainJITDylib(); + for (auto JDItr = JITDylibs.begin(), JDEnd = JITDylibs.end(); + JDItr != JDEnd; ++JDItr) { + IdxToDylib[JITDylibs.getPosition(JDItr - JITDylibs.begin())] = + &J->createJITDylib(*JDItr); + } - for (auto &M : Ms) { - orc::makeAllSymbolsExternallyAccessible(*M); - ExitOnErr(J->addLazyIRModule(std::move(M))); + for (auto EMItr = ExtraModules.begin(), EMEnd = ExtraModules.end(); + EMItr != EMEnd; ++EMItr) { + auto M = parseIRFile(*EMItr, Err, *TSCtx.getContext()); + if (!M) + reportError(Err, ProgName); + + auto EMIdx = ExtraModules.getPosition(EMItr - ExtraModules.begin()); + assert(EMIdx != 0 && "ExtraModule should have index > 0"); + auto JDItr = std::prev(IdxToDylib.lower_bound(EMIdx)); + auto &JD = *JDItr->second; + ExitOnErr( + J->addLazyIRModule(JD, orc::ThreadSafeModule(std::move(M), TSCtx))); + } } + // Add the objects. + for (auto &ObjPath : ExtraObjects) { + auto Obj = ExitOnErr(errorOrToExpected(MemoryBuffer::getFile(ObjPath))); + ExitOnErr(J->addObjectFile(std::move(Obj))); + } + + // Generate a argument string. + std::vector<std::string> Args; + Args.push_back(InputFile); + for (auto &Arg : InputArgv) + Args.push_back(Arg); + + // Run any static constructors. ExitOnErr(J->runConstructors()); + // Run any -thread-entry points. + std::vector<std::thread> AltEntryThreads; + for (auto &ThreadEntryPoint : ThreadEntryPoints) { + auto EntryPointSym = ExitOnErr(J->lookup(ThreadEntryPoint)); + typedef void (*EntryPointPtr)(); + auto EntryPoint = + reinterpret_cast<EntryPointPtr>(static_cast<uintptr_t>(EntryPointSym.getAddress())); + AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); })); + } + + J->getExecutionSession().dump(llvm::dbgs()); + + // Run main. 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()); + ArgV.push_back(nullptr); + + int ArgC = ArgV.size() - 1; auto Main = reinterpret_cast<MainFnPtr>(static_cast<uintptr_t>(MainSym.getAddress())); - auto Result = Main(ArgV.size(), (const char **)ArgV.data()); + auto Result = Main(ArgC, (const char **)ArgV.data()); - ExitOnErr(J->runDestructors()); + // Wait for -entry-point threads. + for (auto &AltEntryThread : AltEntryThreads) + AltEntryThread.join(); + // Run destructors. + ExitOnErr(J->runDestructors()); CXXRuntimeOverrides.runDestructors(); return Result; } +void disallowOrcOptions() { + // Make sure nobody used an orc-lazy specific option accidentally. + + if (LazyJITCompileThreads != 0) { + errs() << "-compile-threads requires -jit-kind=orc-lazy\n"; + exit(1); + } + + if (!ThreadEntryPoints.empty()) { + errs() << "-thread-entry requires -jit-kind=orc-lazy\n"; + exit(1); + } + + if (PerModuleLazy) { + errs() << "-per-module-lazy requires -jit-kind=orc-lazy\n"; + exit(1); + } +} + std::unique_ptr<FDRawChannel> launchRemote() { #ifndef LLVM_ON_UNIX llvm_unreachable("launchRemote not supported on non-Unix platforms"); diff --git a/tools/llvm-ar/CMakeLists.txt b/tools/llvm-ar/CMakeLists.txt index 2970a59beee2..191c684d5245 100644 --- a/tools/llvm-ar/CMakeLists.txt +++ b/tools/llvm-ar/CMakeLists.txt @@ -1,5 +1,6 @@ set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} + BinaryFormat Core DlltoolDriver LibDriver diff --git a/tools/llvm-ar/llvm-ar.cpp b/tools/llvm-ar/llvm-ar.cpp index 9023bdd1a0d6..1c453ee0b569 100644 --- a/tools/llvm-ar/llvm-ar.cpp +++ b/tools/llvm-ar/llvm-ar.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/Errc.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/LineIterator.h" #include "llvm/Support/MemoryBuffer.h" @@ -32,6 +33,7 @@ #include "llvm/Support/StringSaver.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h" #include "llvm/ToolDrivers/llvm-lib/LibDriver.h" @@ -63,46 +65,45 @@ OPTIONS: )"; const char ArHelp[] = R"( -OVERVIEW: LLVM Archiver (llvm-ar) +OVERVIEW: LLVM Archiver - This program archives bitcode files into single libraries - -USAGE: llvm-ar [options] [relpos] [count] <archive-file> [members]... +USAGE: llvm-ar [options] [-]<operation>[modifiers] [relpos] <archive> [files] + llvm-ar -M [<mri-script] 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 + --format - Archive format to create + =default - default + =gnu - gnu + =darwin - darwin + =bsd - bsd + --plugin=<string> - 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 - delete [files] from the archive + m - move [files] in the archive + p - print [files] found in the archive + q - quick append [files] to the archive + r - replace or insert [files] into the archive + s - act as ranlib + t - display contents of archive + x - extract [files] from the archive + +MODIFIERS: + [a] - put [files] after [relpos] + [b] - put [files] before [relpos] (same as [i]) + [c] - do not warn if archive had to be created [D] - use zero for timestamps and uids/gids (default) - [i] - put file(s) before [relpos] (same as [b]) + [i] - put [files] before [relpos] (same as [b]) + [l] - ignored for compatibility + [L] - add archive's contents [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] - 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 )"; @@ -115,7 +116,7 @@ void printHelpMessage() { // Show the error message and exit. LLVM_ATTRIBUTE_NORETURN static void fail(Twine Error) { - errs() << ToolName << ": " << Error << ".\n"; + WithColor::error(errs(), ToolName) << Error << ".\n"; printHelpMessage(); exit(1); } @@ -125,7 +126,7 @@ static void failIfError(std::error_code EC, Twine Context = "") { return; std::string ContextStr = Context.str(); - if (ContextStr == "") + if (ContextStr.empty()) fail(EC.message()); fail(Context + ": " + EC.message()); } @@ -136,7 +137,7 @@ static void failIfError(Error E, Twine Context = "") { handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) { std::string ContextStr = Context.str(); - if (ContextStr == "") + if (ContextStr.empty()) fail(EIB.message()); fail(Context + ": " + EIB.message()); }); @@ -157,14 +158,14 @@ static std::string Options; // This enumeration delineates the kinds of operations on an archive // that are permitted. enum ArchiveOperation { - Print, ///< Print the contents of the archive - Delete, ///< Delete the specified members - Move, ///< Move members to end or as given by {a,b,i} modifiers - QuickAppend, ///< Quickly append to end of archive - ReplaceOrInsert, ///< Replace or Insert members - DisplayTable, ///< Display the table of contents - Extract, ///< Extract files back to file system - CreateSymTab ///< Create a symbol table in an existing archive + Print, ///< Print the contents of the archive + Delete, ///< Delete the specified members + Move, ///< Move members to end or as given by {a,b,i} modifiers + QuickAppend, ///< Quickly append to end of archive + ReplaceOrInsert, ///< Replace or Insert members + DisplayTable, ///< Display the table of contents + Extract, ///< Extract files back to file system + CreateSymTab ///< Create a symbol table in an existing archive }; // Modifiers to follow operation to vary behavior @@ -177,6 +178,7 @@ static bool Verbose = false; ///< 'v' modifier static bool Symtab = true; ///< 's' modifier static bool Deterministic = true; ///< 'D' and 'U' modifiers static bool Thin = false; ///< 'T' modifier +static bool AddLibrary = false; ///< 'L' modifier // Relative Positional Argument (for insert/move). This variable holds // the name of the archive member to which the 'a', 'b' or 'i' modifier @@ -195,7 +197,7 @@ 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 (PositionalArgs.size() == 0) + if (PositionalArgs.empty()) fail("Expected [relpos] for a, b, or i modifier"); RelPos = PositionalArgs[0]; PositionalArgs.erase(PositionalArgs.begin()); @@ -203,7 +205,7 @@ static void getRelPos() { // Get the archive file name from the command line static void getArchive() { - if (PositionalArgs.size() == 0) + if (PositionalArgs.empty()) fail("An archive name must be specified"); ArchiveName = PositionalArgs[0]; PositionalArgs.erase(PositionalArgs.begin()); @@ -215,6 +217,21 @@ static void getMembers() { Members.push_back(Arg); } +std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; +std::vector<std::unique_ptr<object::Archive>> Archives; + +static object::Archive &readLibrary(const Twine &Library) { + auto BufOrErr = MemoryBuffer::getFile(Library, -1, false); + failIfError(BufOrErr.getError(), "Could not open library " + Library); + ArchiveBuffers.push_back(std::move(*BufOrErr)); + auto LibOrErr = + object::Archive::create(ArchiveBuffers.back()->getMemBufferRef()); + failIfError(errorToErrorCode(LibOrErr.takeError()), + "Could not parse library"); + Archives.push_back(std::move(*LibOrErr)); + return *Archives.back(); +} + static void runMRIScript(); // Parse the command line options as presented and return the operation @@ -240,18 +257,44 @@ static ArchiveOperation parseCommandLine() { bool MaybeJustCreateSymTab = false; - for(unsigned i=0; i<Options.size(); ++i) { - switch(Options[i]) { - case 'd': ++NumOperations; Operation = Delete; break; - case 'm': ++NumOperations; Operation = Move ; break; - case 'p': ++NumOperations; Operation = Print; break; - case 'q': ++NumOperations; Operation = QuickAppend; break; - case 'r': ++NumOperations; Operation = ReplaceOrInsert; break; - case 't': ++NumOperations; Operation = DisplayTable; break; - case 'x': ++NumOperations; Operation = Extract; break; - case 'c': Create = true; break; - case 'l': /* accepted but unused */ break; - case 'o': OriginalDates = true; break; + for (unsigned i = 0; i < Options.size(); ++i) { + switch (Options[i]) { + case 'd': + ++NumOperations; + Operation = Delete; + break; + case 'm': + ++NumOperations; + Operation = Move; + break; + case 'p': + ++NumOperations; + Operation = Print; + break; + case 'q': + ++NumOperations; + Operation = QuickAppend; + break; + case 'r': + ++NumOperations; + Operation = ReplaceOrInsert; + break; + case 't': + ++NumOperations; + Operation = DisplayTable; + break; + case 'x': + ++NumOperations; + Operation = Extract; + break; + case 'c': + Create = true; + break; + case 'l': /* accepted but unused */ + break; + case 'o': + OriginalDates = true; + break; case 's': Symtab = true; MaybeJustCreateSymTab = true; @@ -259,8 +302,12 @@ static ArchiveOperation parseCommandLine() { case 'S': Symtab = false; break; - case 'u': OnlyUpdate = true; break; - case 'v': Verbose = true; break; + case 'u': + OnlyUpdate = true; + break; + case 'v': + Verbose = true; + break; case 'a': getRelPos(); AddAfter = true; @@ -285,6 +332,9 @@ static ArchiveOperation parseCommandLine() { case 'T': Thin = true; break; + case 'L': + AddLibrary = true; + break; default: fail(std::string("unknown option ") + Options[i]); } @@ -297,7 +347,7 @@ static ArchiveOperation parseCommandLine() { // Everything on the command line at this point is a member. getMembers(); - if (NumOperations == 0 && MaybeJustCreateSymTab) { + if (NumOperations == 0 && MaybeJustCreateSymTab) { NumOperations = 1; Operation = CreateSymTab; if (!Members.empty()) @@ -321,6 +371,8 @@ static ArchiveOperation parseCommandLine() { fail("The 'o' modifier is only applicable to the 'x' operation"); if (OnlyUpdate && Operation != ReplaceOrInsert) fail("The 'u' modifier is only applicable to the 'r' operation"); + if (AddLibrary && Operation != QuickAppend) + fail("The 'L' modifier is only applicable to the 'q' operation"); // Return the parsed operation to the caller return Operation; @@ -369,7 +421,11 @@ static void doDisplayTable(StringRef Name, const object::Archive::Child &C) { outs() << ' ' << format("%6llu", Size.get()); auto ModTimeOrErr = C.getLastModified(); failIfError(ModTimeOrErr.takeError()); - outs() << ' ' << ModTimeOrErr.get(); + // Note: formatv() only handles the default TimePoint<>, which is in + // nanoseconds. + // TODO: fix format_provider<TimePoint<>> to allow other units. + sys::TimePoint<> ModTimeInNs = ModTimeOrErr.get(); + outs() << ' ' << formatv("{0:%b %e %H:%M %Y}", ModTimeInNs); outs() << ' '; } @@ -412,7 +468,7 @@ static void doExtract(StringRef Name, const object::Archive::Child &C) { auto ModTimeOrErr = C.getLastModified(); failIfError(ModTimeOrErr.takeError()); failIfError( - sys::fs::setLastModificationAndAccessTime(FD, ModTimeOrErr.get())); + sys::fs::setLastAccessAndModificationTime(FD, ModTimeOrErr.get())); } if (close(FD)) @@ -477,36 +533,57 @@ static void performReadOperation(ArchiveOperation Operation, if (Members.empty()) return; for (StringRef Name : Members) - errs() << Name << " was not found\n"; + WithColor::error(errs(), ToolName) << "'" << Name << "' was not found\n"; exit(1); } +static void addChildMember(std::vector<NewArchiveMember> &Members, + const object::Archive::Child &M, + bool FlattenArchive = false) { + if (Thin && !M.getParent()->isThin()) + fail("Cannot convert a regular archive to a thin one"); + Expected<NewArchiveMember> NMOrErr = + NewArchiveMember::getOldMember(M, Deterministic); + failIfError(NMOrErr.takeError()); + if (FlattenArchive && + identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + Expected<std::string> FileNameOrErr = M.getFullName(); + failIfError(FileNameOrErr.takeError()); + object::Archive &Lib = readLibrary(*FileNameOrErr); + // When creating thin archives, only flatten if the member is also thin. + if (!Thin || Lib.isThin()) { + Error Err = Error::success(); + // Only Thin archives are recursively flattened. + for (auto &Child : Lib.children(Err)) + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + failIfError(std::move(Err)); + return; + } + } + Members.push_back(std::move(*NMOrErr)); +} + static void addMember(std::vector<NewArchiveMember> &Members, - StringRef FileName, int Pos = -1) { + StringRef FileName, bool FlattenArchive = false) { Expected<NewArchiveMember> NMOrErr = NewArchiveMember::getFile(FileName, Deterministic); failIfError(NMOrErr.takeError(), FileName); - + if (FlattenArchive && + identify_magic(NMOrErr->Buf->getBuffer()) == file_magic::archive) { + object::Archive &Lib = readLibrary(FileName); + // When creating thin archives, only flatten if the member is also thin. + if (!Thin || Lib.isThin()) { + Error Err = Error::success(); + // Only Thin archives are recursively flattened. + for (auto &Child : Lib.children(Err)) + addChildMember(Members, Child, /*FlattenArchive=*/Thin); + failIfError(std::move(Err)); + return; + } + } // Use the basename of the object path for the member name. NMOrErr->MemberName = sys::path::filename(NMOrErr->MemberName); - - if (Pos == -1) - Members.push_back(std::move(*NMOrErr)); - else - Members[Pos] = std::move(*NMOrErr); -} - -static void addMember(std::vector<NewArchiveMember> &Members, - const object::Archive::Child &M, int Pos = -1) { - if (Thin && !M.getParent()->isThin()) - fail("Cannot convert a regular archive to a thin one"); - Expected<NewArchiveMember> NMOrErr = - NewArchiveMember::getOldMember(M, Deterministic); - failIfError(NMOrErr.takeError()); - if (Pos == -1) - Members.push_back(std::move(*NMOrErr)); - else - Members[Pos] = std::move(*NMOrErr); + Members.push_back(std::move(*NMOrErr)); } enum InsertAction { @@ -595,7 +672,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, computeInsertAction(Operation, Child, Name, MemberI); switch (Action) { case IA_AddOldMember: - addMember(Ret, Child); + addChildMember(Ret, Child); break; case IA_AddNewMember: addMember(Ret, *MemberI); @@ -603,7 +680,7 @@ computeNewArchiveMembers(ArchiveOperation Operation, case IA_Delete: break; case IA_MoveOldMember: - addMember(Moved, Child); + addChildMember(Moved, Child); break; case IA_MoveNewMember: addMember(Moved, *MemberI); @@ -631,14 +708,20 @@ computeNewArchiveMembers(ArchiveOperation Operation, ++Pos; } - for (unsigned I = 0; I != Members.size(); ++I) - Ret.insert(Ret.begin() + InsertPos, NewArchiveMember()); - Pos = InsertPos; - for (auto &Member : Members) { - addMember(Ret, Member, Pos); - ++Pos; + if (AddLibrary) { + assert(Operation == QuickAppend); + for (auto &Member : Members) + addMember(Ret, Member, /*FlattenArchive=*/true); + return Ret; } + std::vector<NewArchiveMember> NewMembers; + for (auto &Member : Members) + addMember(NewMembers, Member, /*FlattenArchive=*/Thin); + Ret.reserve(Ret.size() + NewMembers.size()); + std::move(NewMembers.begin(), NewMembers.end(), + std::inserter(Ret, std::next(Ret.begin(), InsertPos))); + return Ret; } @@ -662,11 +745,10 @@ static object::Archive::Kind getKindFromMember(const NewArchiveMember &Member) { return getDefaultForHost(); } -static void -performWriteOperation(ArchiveOperation Operation, - object::Archive *OldArchive, - std::unique_ptr<MemoryBuffer> OldArchiveBuf, - std::vector<NewArchiveMember> *NewMembersP) { +static void performWriteOperation(ArchiveOperation Operation, + object::Archive *OldArchive, + std::unique_ptr<MemoryBuffer> OldArchiveBuf, + std::vector<NewArchiveMember> *NewMembersP) { std::vector<NewArchiveMember> NewMembers; if (!NewMembersP) NewMembers = computeNewArchiveMembers(Operation, OldArchive); @@ -679,11 +761,11 @@ performWriteOperation(ArchiveOperation Operation, else if (OldArchive) Kind = OldArchive->kind(); else if (NewMembersP) - Kind = NewMembersP->size() ? getKindFromMember(NewMembersP->front()) - : getDefaultForHost(); + Kind = !NewMembersP->empty() ? getKindFromMember(NewMembersP->front()) + : getDefaultForHost(); else - Kind = NewMembers.size() ? getKindFromMember(NewMembers.front()) - : getDefaultForHost(); + Kind = !NewMembers.empty() ? getKindFromMember(NewMembers.front()) + : getDefaultForHost(); break; case GNU: Kind = object::Archive::K_GNU; @@ -772,7 +854,8 @@ static int performOperation(ArchiveOperation Operation, } else { if (!Create) { // Produce a warning if we should and we're creating the archive - errs() << ToolName << ": creating " << ArchiveName << "\n"; + WithColor::warning(errs(), ToolName) + << "creating " << ArchiveName << "\n"; } } @@ -788,11 +871,14 @@ static void runMRIScript() { const MemoryBuffer &Ref = *Buf.get(); bool Saved = false; std::vector<NewArchiveMember> NewMembers; - std::vector<std::unique_ptr<MemoryBuffer>> ArchiveBuffers; - std::vector<std::unique_ptr<object::Archive>> Archives; - for (line_iterator I(Ref, /*SkipBlanks*/ true, ';'), E; I != E; ++I) { + for (line_iterator I(Ref, /*SkipBlanks*/ false), E; I != E; ++I) { StringRef Line = *I; + Line = Line.split(';').first; + Line = Line.split('*').first; + Line = Line.trim(); + if (Line.empty()) + continue; StringRef CommandStr, Rest; std::tie(CommandStr, Rest) = Line.split(' '); Rest = Rest.trim(); @@ -809,19 +895,11 @@ static void runMRIScript() { switch (Command) { case MRICommand::AddLib: { - auto BufOrErr = MemoryBuffer::getFile(Rest, -1, false); - failIfError(BufOrErr.getError(), "Could not open library"); - ArchiveBuffers.push_back(std::move(*BufOrErr)); - auto LibOrErr = - object::Archive::create(ArchiveBuffers.back()->getMemBufferRef()); - failIfError(errorToErrorCode(LibOrErr.takeError()), - "Could not parse library"); - Archives.push_back(std::move(*LibOrErr)); - object::Archive &Lib = *Archives.back(); + object::Archive &Lib = readLibrary(Rest); { Error Err = Error::success(); for (auto &Member : Lib.children(Err)) - addMember(NewMembers, Member); + addChildMember(NewMembers, Member); failIfError(std::move(Err)); } break; @@ -876,7 +954,7 @@ static int ar_main(int argc, char **argv) { BumpPtrAllocator Alloc; StringSaver Saver(Alloc); cl::ExpandResponseFiles(Saver, cl::TokenizeGNUCommandLine, Argv); - for(size_t i = 1; i < Argv.size(); ++i) { + for (size_t i = 1; i < Argv.size(); ++i) { StringRef Arg = Argv[i]; const char *match; auto MatchFlagWithArg = [&](const char *expected) { @@ -887,8 +965,7 @@ static int ar_main(int argc, char **argv) { match = Argv[i]; return true; } - if (Arg.startswith(expected) && Arg.size() > len && - Arg[len] == '=') { + if (Arg.startswith(expected) && Arg.size() > len && Arg[len] == '=') { match = Arg.data() + len + 1; return true; } @@ -897,7 +974,7 @@ static int ar_main(int argc, char **argv) { if (handleGenericOption(Argv[i])) return 0; if (Arg == "--") { - for(; i < Argv.size(); ++i) + for (; i < Argv.size(); ++i) PositionalArgs.push_back(Argv[i]); break; } @@ -910,11 +987,11 @@ static int ar_main(int argc, char **argv) { MRI = true; } else if (MatchFlagWithArg("format")) { FormatType = StringSwitch<Format>(match) - .Case("default", Default) - .Case("gnu", GNU) - .Case("darwin", DARWIN) - .Case("bsd", BSD) - .Default(Unknown); + .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")) { @@ -934,7 +1011,7 @@ static int ar_main(int argc, char **argv) { static int ranlib_main(int argc, char **argv) { bool ArchiveSpecified = false; - for(int i = 1; i < argc; ++i) { + for (int i = 1; i < argc; ++i) { if (handleGenericOption(argv[i])) { return 0; } else { diff --git a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp index 1939dc6440fe..789a666cb41a 100644 --- a/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ b/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -247,6 +247,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(CST_CODE, CE_CMP) STRINGIFY_CODE(CST_CODE, INLINEASM) STRINGIFY_CODE(CST_CODE, CE_SHUFVEC_EX) + STRINGIFY_CODE(CST_CODE, CE_UNOP) case bitc::CST_CODE_BLOCKADDRESS: return "CST_CODE_BLOCKADDRESS"; STRINGIFY_CODE(CST_CODE, DATA) } @@ -267,6 +268,7 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(FUNC_CODE, INST_BR) STRINGIFY_CODE(FUNC_CODE, INST_SWITCH) STRINGIFY_CODE(FUNC_CODE, INST_INVOKE) + STRINGIFY_CODE(FUNC_CODE, INST_UNOP) STRINGIFY_CODE(FUNC_CODE, INST_UNREACHABLE) STRINGIFY_CODE(FUNC_CODE, INST_CLEANUPRET) STRINGIFY_CODE(FUNC_CODE, INST_CATCHRET) @@ -285,6 +287,11 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID, STRINGIFY_CODE(FUNC_CODE, DEBUG_LOC) STRINGIFY_CODE(FUNC_CODE, INST_GEP) STRINGIFY_CODE(FUNC_CODE, OPERAND_BUNDLE) + STRINGIFY_CODE(FUNC_CODE, INST_FENCE) + STRINGIFY_CODE(FUNC_CODE, INST_ATOMICRMW) + STRINGIFY_CODE(FUNC_CODE, INST_LOADATOMIC) + STRINGIFY_CODE(FUNC_CODE, INST_STOREATOMIC) + STRINGIFY_CODE(FUNC_CODE, INST_CMPXCHG) } case bitc::VALUE_SYMTAB_BLOCK_ID: switch (CodeID) { diff --git a/tools/llvm-c-test/debuginfo.c b/tools/llvm-c-test/debuginfo.c index 74d215ea8182..fbd41e97d7a1 100644 --- a/tools/llvm-c-test/debuginfo.c +++ b/tools/llvm-c-test/debuginfo.c @@ -61,26 +61,24 @@ int llvm_test_dibuilder(void) { 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); + LLVMDIBuilderCreateConstantValueExpression(DIB, 0); + LLVMDIBuilderCreateGlobalVariableExpression( + DIB, Module, "globalClass", 11, "", 0, File, 1, ClassTy, true, + GlobalClassValueExpr, NULL, 0); LLVMMetadataRef Int64Ty = - LLVMDIBuilderCreateBasicType(DIB, "Int64", 5, 64, 0); + LLVMDIBuilderCreateBasicType(DIB, "Int64", 5, 64, 0, LLVMDIFlagZero); 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); + LLVMDIBuilderCreateConstantValueExpression(DIB, 0); + LLVMDIBuilderCreateGlobalVariableExpression( + DIB, Module, "global", 6, "", 0, File, 1, Int64TypeDef, true, + GlobalVarValueExpr, NULL, 0); LLVMMetadataRef NameSpace = - LLVMDIBuilderCreateNameSpace(DIB, Module, "NameSpace", 9, false); + LLVMDIBuilderCreateNameSpace(DIB, Module, "NameSpace", 9, false); LLVMMetadataRef StructDbgElts[] = {Int64Ty, Int64Ty, Int64Ty}; LLVMMetadataRef StructDbgTy = diff --git a/tools/llvm-c-test/echo.cpp b/tools/llvm-c-test/echo.cpp index a9cd4cf5cbb7..db926e8aceaa 100644 --- a/tools/llvm-c-test/echo.cpp +++ b/tools/llvm-c-test/echo.cpp @@ -16,6 +16,7 @@ //===----------------------------------------------------------------------===// #include "llvm-c-test.h" +#include "llvm-c/DebugInfo.h" #include "llvm-c/Target.h" #include "llvm/ADT/DenseMap.h" #include "llvm/Support/ErrorHandling.h" @@ -239,7 +240,17 @@ static LLVMValueRef clone_constant_impl(LLVMValueRef Cst, LLVMModuleRef M) { // Try function if (LLVMIsAFunction(Cst)) { check_value_kind(Cst, LLVMFunctionValueKind); - LLVMValueRef Dst = LLVMGetNamedFunction(M, Name); + + LLVMValueRef Dst = nullptr; + // Try an intrinsic + unsigned ID = LLVMGetIntrinsicID(Cst); + if (ID > 0 && !LLVMIntrinsicIsOverloaded(ID)) { + Dst = LLVMGetIntrinsicDeclaration(M, ID, nullptr, 0); + } else { + // Try a normal function + Dst = LLVMGetNamedFunction(M, Name); + } + if (Dst) return Dst; report_fatal_error("Could not find function"); @@ -728,6 +739,19 @@ struct FunCloner { exit(-1); } + auto Ctx = LLVMGetModuleContext(M); + size_t NumMetadataEntries; + auto *AllMetadata = + LLVMInstructionGetAllMetadataOtherThanDebugLoc(Src, + &NumMetadataEntries); + for (unsigned i = 0; i < NumMetadataEntries; ++i) { + unsigned Kind = LLVMValueMetadataEntriesGetKind(AllMetadata, i); + LLVMMetadataRef MD = LLVMValueMetadataEntriesGetMetadata(AllMetadata, i); + LLVMSetMetadata(Dst, Kind, LLVMMetadataAsValue(Ctx, MD)); + } + LLVMDisposeValueMetadataEntries(AllMetadata); + LLVMSetInstDebugLocation(Builder, Dst); + check_value_kind(Dst, LLVMInstructionValueKind); return VMap[Src] = Dst; } @@ -916,7 +940,7 @@ AliasDecl: if (!Begin) { if (End != nullptr) report_fatal_error("Range has an end but no beginning"); - return; + goto NamedMDDecl; } Cur = Begin; @@ -943,6 +967,38 @@ AliasDecl: Cur = Next; } + +NamedMDDecl: + LLVMNamedMDNodeRef BeginMD = LLVMGetFirstNamedMetadata(Src); + LLVMNamedMDNodeRef EndMD = LLVMGetLastNamedMetadata(Src); + if (!BeginMD) { + if (EndMD != nullptr) + report_fatal_error("Range has an end but no beginning"); + return; + } + + LLVMNamedMDNodeRef CurMD = BeginMD; + LLVMNamedMDNodeRef NextMD = nullptr; + while (true) { + size_t NameLen; + const char *Name = LLVMGetNamedMetadataName(CurMD, &NameLen); + if (LLVMGetNamedMetadata(M, Name, NameLen)) + report_fatal_error("Named Metadata Node already cloned"); + LLVMGetOrInsertNamedMetadata(M, Name, NameLen); + + NextMD = LLVMGetNextNamedMetadata(CurMD); + if (NextMD == nullptr) { + if (CurMD != EndMD) + report_fatal_error(""); + break; + } + + LLVMNamedMDNodeRef PrevMD = LLVMGetPreviousNamedMetadata(NextMD); + if (PrevMD != CurMD) + report_fatal_error("Next.Previous global is not Current"); + + CurMD = NextMD; + } } static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { @@ -967,6 +1023,15 @@ static void clone_symbols(LLVMModuleRef Src, LLVMModuleRef M) { if (auto I = LLVMGetInitializer(Cur)) LLVMSetInitializer(G, clone_constant(I, M)); + size_t NumMetadataEntries; + auto *AllMetadata = LLVMGlobalCopyAllMetadata(Cur, &NumMetadataEntries); + for (unsigned i = 0; i < NumMetadataEntries; ++i) { + unsigned Kind = LLVMValueMetadataEntriesGetKind(AllMetadata, i); + LLVMMetadataRef MD = LLVMValueMetadataEntriesGetMetadata(AllMetadata, i); + LLVMGlobalSetMetadata(G, Kind, MD); + } + LLVMDisposeValueMetadataEntries(AllMetadata); + LLVMSetGlobalConstant(G, LLVMIsGlobalConstant(Cur)); LLVMSetThreadLocal(G, LLVMIsThreadLocal(Cur)); LLVMSetExternallyInitialized(G, LLVMIsExternallyInitialized(Cur)); @@ -1018,6 +1083,15 @@ FunClone: LLVMSetPersonalityFn(Fun, P); } + size_t NumMetadataEntries; + auto *AllMetadata = LLVMGlobalCopyAllMetadata(Cur, &NumMetadataEntries); + for (unsigned i = 0; i < NumMetadataEntries; ++i) { + unsigned Kind = LLVMValueMetadataEntriesGetKind(AllMetadata, i); + LLVMMetadataRef MD = LLVMValueMetadataEntriesGetMetadata(AllMetadata, i); + LLVMGlobalSetMetadata(Fun, Kind, MD); + } + LLVMDisposeValueMetadataEntries(AllMetadata); + FunCloner FC(Cur, Fun); FC.CloneBBs(Cur); @@ -1041,7 +1115,7 @@ AliasClone: if (!Begin) { if (End != nullptr) report_fatal_error("Range has an end but no beginning"); - return; + goto NamedMDClone; } Cur = Begin; @@ -1073,6 +1147,47 @@ AliasClone: Cur = Next; } + +NamedMDClone: + LLVMNamedMDNodeRef BeginMD = LLVMGetFirstNamedMetadata(Src); + LLVMNamedMDNodeRef EndMD = LLVMGetLastNamedMetadata(Src); + if (!BeginMD) { + if (EndMD != nullptr) + report_fatal_error("Range has an end but no beginning"); + return; + } + + LLVMNamedMDNodeRef CurMD = BeginMD; + LLVMNamedMDNodeRef NextMD = nullptr; + while (true) { + size_t NameLen; + const char *Name = LLVMGetNamedMetadataName(CurMD, &NameLen); + LLVMNamedMDNodeRef NamedMD = LLVMGetNamedMetadata(M, Name, NameLen); + if (!NamedMD) + report_fatal_error("Named MD Node must have been declared already"); + + unsigned OperandCount = LLVMGetNamedMetadataNumOperands(Src, Name); + LLVMValueRef *OperandBuf = static_cast<LLVMValueRef *>( + safe_malloc(OperandCount * sizeof(LLVMValueRef))); + LLVMGetNamedMetadataOperands(Src, Name, OperandBuf); + for (unsigned i = 0, e = OperandCount; i != e; ++i) { + LLVMAddNamedMetadataOperand(M, Name, OperandBuf[i]); + } + free(OperandBuf); + + NextMD = LLVMGetNextNamedMetadata(CurMD); + if (NextMD == nullptr) { + if (CurMD != EndMD) + report_fatal_error("Last Named MD Node does not match End"); + break; + } + + LLVMNamedMDNodeRef PrevMD = LLVMGetPreviousNamedMetadata(NextMD); + if (PrevMD != CurMD) + report_fatal_error("Next.Previous Named MD Node is not Current"); + + CurMD = NextMD; + } } int llvm_echo(void) { @@ -1089,18 +1204,6 @@ int llvm_echo(void) { 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))) @@ -1115,7 +1218,6 @@ int llvm_echo(void) { char *Str = LLVMPrintModuleToString(M); fputs(Str, stdout); - LLVMDisposeModuleFlagsMetadata(ModuleFlags); LLVMDisposeMessage(Str); LLVMDisposeModule(Src); LLVMDisposeModule(M); diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp index 29819d8d28ed..8dc0802c4652 100644 --- a/tools/llvm-cfi-verify/lib/FileAnalysis.cpp +++ b/tools/llvm-cfi-verify/lib/FileAnalysis.cpp @@ -105,6 +105,9 @@ Expected<FileAnalysis> FileAnalysis::Create(StringRef Filename) { if (auto SectionParseResponse = Analysis.parseCodeSections()) return std::move(SectionParseResponse); + if (auto SymbolTableParseResponse = Analysis.parseSymbolTable()) + return std::move(SymbolTableParseResponse); + return std::move(Analysis); } @@ -165,7 +168,18 @@ const Instr &FileAnalysis::getInstructionOrDie(uint64_t Address) const { bool FileAnalysis::isCFITrap(const Instr &InstrMeta) const { const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); - return InstrDesc.isTrap(); + return InstrDesc.isTrap() || willTrapOnCFIViolation(InstrMeta); +} + +bool FileAnalysis::willTrapOnCFIViolation(const Instr &InstrMeta) const { + const auto &InstrDesc = MII->get(InstrMeta.Instruction.getOpcode()); + if (!InstrDesc.isCall()) + return false; + uint64_t Target; + if (!MIA->evaluateBranch(InstrMeta.Instruction, InstrMeta.VMAddress, + InstrMeta.InstructionSize, Target)) + return false; + return TrapOnFailFunctionAddresses.count(Target) > 0; } bool FileAnalysis::canFallThrough(const Instr &InstrMeta) const { @@ -431,6 +445,12 @@ Error FileAnalysis::parseCodeSections() { if (!(object::ELFSectionRef(Section).getFlags() & ELF::SHF_EXECINSTR)) continue; + // Avoid checking the PLT since it produces spurious failures on AArch64 + // when ignoring DWARF data. + StringRef SectionName; + if (!Section.getName(SectionName) && SectionName == ".plt") + continue; + StringRef SectionContents; if (Section.getContents(SectionContents)) return make_error<StringError>("Failed to retrieve section contents", @@ -518,6 +538,40 @@ void FileAnalysis::addInstruction(const Instr &Instruction) { } } +Error FileAnalysis::parseSymbolTable() { + // Functions that will trap on CFI violations. + SmallSet<StringRef, 4> TrapOnFailFunctions; + TrapOnFailFunctions.insert("__cfi_slowpath"); + TrapOnFailFunctions.insert("__cfi_slowpath_diag"); + TrapOnFailFunctions.insert("abort"); + + // Look through the list of symbols for functions that will trap on CFI + // violations. + for (auto &Sym : Object->symbols()) { + auto SymNameOrErr = Sym.getName(); + if (!SymNameOrErr) + consumeError(SymNameOrErr.takeError()); + else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0) { + auto AddrOrErr = Sym.getAddress(); + if (!AddrOrErr) + consumeError(AddrOrErr.takeError()); + else + TrapOnFailFunctionAddresses.insert(*AddrOrErr); + } + } + if (auto *ElfObject = dyn_cast<object::ELFObjectFileBase>(Object)) { + for (const auto &Addr : ElfObject->getPltAddresses()) { + object::SymbolRef Sym(Addr.first, Object); + auto SymNameOrErr = Sym.getName(); + if (!SymNameOrErr) + consumeError(SymNameOrErr.takeError()); + else if (TrapOnFailFunctions.count(*SymNameOrErr) > 0) + TrapOnFailFunctionAddresses.insert(Addr.second); + } + } + return Error::success(); +} + UnsupportedDisassembly::UnsupportedDisassembly(StringRef Text) : Text(Text) {} char UnsupportedDisassembly::ID; diff --git a/tools/llvm-cfi-verify/lib/FileAnalysis.h b/tools/llvm-cfi-verify/lib/FileAnalysis.h index 3f0a70487882..67421e80da37 100644 --- a/tools/llvm-cfi-verify/lib/FileAnalysis.h +++ b/tools/llvm-cfi-verify/lib/FileAnalysis.h @@ -11,6 +11,7 @@ #define LLVM_CFI_VERIFY_FILE_ANALYSIS_H #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/MC/MCAsmInfo.h" @@ -110,6 +111,10 @@ public: // Returns whether this instruction is used by CFI to trap the program. bool isCFITrap(const Instr &InstrMeta) const; + // Returns whether this instruction is a call to a function that will trap on + // CFI violations (i.e., it serves as a trap in this instance). + bool willTrapOnCFIViolation(const Instr &InstrMeta) const; + // Returns whether this function can fall through to the next instruction. // Undefined (and bad) instructions cannot fall through, and instruction that // modify the control flow can only fall through if they are conditional @@ -183,6 +188,10 @@ protected: // internal members. Should only be called once by Create(). Error parseCodeSections(); + // Parses the symbol table to look for the addresses of functions that will + // trap on CFI violations. + Error parseSymbolTable(); + private: // Members that describe the input file. object::OwningBinary<object::Binary> Binary; @@ -218,6 +227,9 @@ private: // A list of addresses of indirect control flow instructions. std::set<uint64_t> IndirectInstructions; + + // The addresses of functions that will trap on CFI violations. + SmallSet<uint64_t, 4> TrapOnFailFunctionAddresses; }; class UnsupportedDisassembly : public ErrorInfo<UnsupportedDisassembly> { diff --git a/tools/llvm-cfi-verify/lib/GraphBuilder.cpp b/tools/llvm-cfi-verify/lib/GraphBuilder.cpp index 4153b5f6844a..5b2bc6f0c3bb 100644 --- a/tools/llvm-cfi-verify/lib/GraphBuilder.cpp +++ b/tools/llvm-cfi-verify/lib/GraphBuilder.cpp @@ -311,6 +311,24 @@ void GraphBuilder::buildFlowGraphImpl(const FileAnalysis &Analysis, Result.ConditionalBranchNodes.push_back(BranchNode); } + // When using cross-DSO, some indirect calls are not guarded by a branch to a + // trap but instead follow a call to __cfi_slowpath. For example: + // if (!InlinedFastCheck(f)) + // call *f + // else { + // __cfi_slowpath(CallSiteTypeId, f); + // call *f + // } + // To mark the second call as protected, we recognize indirect calls that + // directly follow calls to functions that will trap on CFI violations. + if (CFCrossRefs.empty()) { + const Instr *PrevInstr = Analysis.getPrevInstructionSequential(ChildMeta); + if (PrevInstr && Analysis.willTrapOnCFIViolation(*PrevInstr)) { + Result.IntermediateNodes[PrevInstr->VMAddress] = Address; + HasValidCrossRef = true; + } + } + if (!HasValidCrossRef) Result.OrphanedNodes.push_back(Address); diff --git a/tools/llvm-config/CMakeLists.txt b/tools/llvm-config/CMakeLists.txt index a0bd36c37315..a7db17386fb3 100644 --- a/tools/llvm-config/CMakeLists.txt +++ b/tools/llvm-config/CMakeLists.txt @@ -29,12 +29,20 @@ string(REPLACE ";" " " SYSTEM_LIBS "${SYSTEM_LIBS}") # Fetch target specific compile options, e.g. RTTI option get_property(COMPILE_FLAGS TARGET llvm-config PROPERTY COMPILE_FLAGS) +# The language standard potentially affects the ABI/API of LLVM, so we want +# to make sure it is reported by llvm-config. +# NOTE: We don't want to start extracting any random C/CXX flags that the +# user may add that could affect the ABI. We only want to extract flags +# that have been added by the LLVM build system. +string(REGEX MATCH "-std=[^ ]\+" LLVM_CXX_STD_FLAG ${CMAKE_CXX_FLAGS}) +string(REGEX MATCH "-std=[^ ]\+" LLVM_C_STD_FLAG ${CMAKE_C_FLAGS}) + # Use configure_file to create BuildVariables.inc. set(LLVM_SRC_ROOT ${LLVM_MAIN_SRC_DIR}) set(LLVM_OBJ_ROOT ${LLVM_BINARY_DIR}) -set(LLVM_CPPFLAGS "${CMAKE_CPP_FLAGS} ${CMAKE_CPP_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") -set(LLVM_CFLAGS "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${LLVM_DEFINITIONS}") -set(LLVM_CXXFLAGS "${CMAKE_CXX_FLAGS} ${CMAKE_CXX_FLAGS_${uppercase_CMAKE_BUILD_TYPE}} ${COMPILE_FLAGS} ${LLVM_DEFINITIONS}") +set(LLVM_CPPFLAGS "${LLVM_DEFINITIONS}") +set(LLVM_CFLAGS "${LLVM_C_STD_FLAG} ${LLVM_DEFINITIONS}") +set(LLVM_CXXFLAGS "${LLVM_CXX_STD_FLAG} ${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_SUFFIX}") diff --git a/tools/llvm-config/llvm-config.cpp b/tools/llvm-config/llvm-config.cpp index 892adc3b9dd8..bec89fef98ca 100644 --- a/tools/llvm-config/llvm-config.cpp +++ b/tools/llvm-config/llvm-config.cpp @@ -523,7 +523,7 @@ int main(int argc, char **argv) { if (DyLibExists && !sys::fs::exists(path)) { Components = GetAllDyLibComponents(IsInDevelopmentTree, true, DirSep); - llvm::sort(Components.begin(), Components.end()); + llvm::sort(Components); break; } } diff --git a/tools/llvm-cov/CMakeLists.txt b/tools/llvm-cov/CMakeLists.txt index d0416b06f9cd..c3afec86cba2 100644 --- a/tools/llvm-cov/CMakeLists.txt +++ b/tools/llvm-cov/CMakeLists.txt @@ -5,6 +5,7 @@ add_llvm_tool(llvm-cov gcov.cpp CodeCoverage.cpp CoverageExporterJson.cpp + CoverageExporterLcov.cpp CoverageFilters.cpp CoverageReport.cpp CoverageSummaryInfo.cpp diff --git a/tools/llvm-cov/CodeCoverage.cpp b/tools/llvm-cov/CodeCoverage.cpp index e93b63d388e0..728e00e7c3c2 100644 --- a/tools/llvm-cov/CodeCoverage.cpp +++ b/tools/llvm-cov/CodeCoverage.cpp @@ -14,6 +14,7 @@ //===----------------------------------------------------------------------===// #include "CoverageExporterJson.h" +#include "CoverageExporterLcov.h" #include "CoverageFilters.h" #include "CoverageReport.h" #include "CoverageSummaryInfo.h" @@ -215,12 +216,13 @@ void CodeCoverageTool::collectPaths(const std::string &Path) { for (llvm::sys::fs::recursive_directory_iterator F(Path, EC), E; F != E; F.increment(EC)) { - if (EC) { - warning(EC.message(), F->path()); + auto Status = F->status(); + if (!Status) { + warning(Status.getError().message(), F->path()); continue; } - if (llvm::sys::fs::is_regular_file(F->path())) + if (Status->type() == llvm::sys::fs::file_type::regular_file) addCollectedPath(F->path()); } } @@ -368,12 +370,6 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() { << "No profile record found for '" << HashMismatch.first << "'" << " with hash = 0x" << Twine::utohexstr(HashMismatch.second) << '\n'; - - for (const auto &CounterMismatch : Coverage->getCounterMismatches()) - errs() << "counter-mismatch: " - << "Coverage mapping for " << CounterMismatch.first - << " only has " << CounterMismatch.second - << " valid counter expressions\n"; } } @@ -571,7 +567,9 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text", "Text output"), clEnumValN(CoverageViewOptions::OutputFormat::HTML, "html", - "HTML output")), + "HTML output"), + clEnumValN(CoverageViewOptions::OutputFormat::Lcov, "lcov", + "lcov tracefile output")), cl::init(CoverageViewOptions::OutputFormat::Text)); cl::opt<std::string> PathRemap( @@ -679,6 +677,11 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { errs() << "Color output cannot be disabled when generating html.\n"; ViewOpts.Colors = true; break; + case CoverageViewOptions::OutputFormat::Lcov: + if (UseColor == cl::BOU_TRUE) + errs() << "Color output cannot be enabled when generating lcov.\n"; + ViewOpts.Colors = false; + break; } // If path-equivalence was given and is a comma seperated pair then set @@ -688,7 +691,7 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) { PathRemapping = EquivPair; // If a demangler is supplied, check if it exists and register it. - if (DemanglerOpts.size()) { + if (!DemanglerOpts.empty()) { auto DemanglerPathOrErr = sys::findProgramByName(DemanglerOpts[0]); if (!DemanglerPathOrErr) { error("Could not find the demangler!", @@ -838,6 +841,11 @@ int CodeCoverageTool::doShow(int argc, const char **argv, if (Err) return Err; + if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { + error("Lcov format should be used with 'llvm-cov export'."); + return 1; + } + ViewOpts.ShowLineNumbers = true; ViewOpts.ShowLineStats = ShowLineExecutionCounts.getNumOccurrences() != 0 || !ShowRegions || ShowBestLineRegionsCounts; @@ -969,6 +977,9 @@ int CodeCoverageTool::doReport(int argc, const char **argv, if (ViewOpts.Format == CoverageViewOptions::OutputFormat::HTML) { error("HTML output for summary reports is not yet supported."); return 1; + } else if (ViewOpts.Format == CoverageViewOptions::OutputFormat::Lcov) { + error("Lcov format should be used with 'llvm-cov export'."); + return 1; } auto Coverage = load(); @@ -1000,8 +1011,10 @@ int CodeCoverageTool::doExport(int argc, const char **argv, if (Err) return Err; - if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text) { - error("Coverage data can only be exported as textual JSON."); + if (ViewOpts.Format != CoverageViewOptions::OutputFormat::Text && + ViewOpts.Format != CoverageViewOptions::OutputFormat::Lcov) { + error("Coverage data can only be exported as textual JSON or an " + "lcov tracefile."); return 1; } @@ -1011,12 +1024,27 @@ int CodeCoverageTool::doExport(int argc, const char **argv, return 1; } - auto Exporter = CoverageExporterJson(*Coverage.get(), ViewOpts, outs()); + std::unique_ptr<CoverageExporter> Exporter; + + switch (ViewOpts.Format) { + case CoverageViewOptions::OutputFormat::Text: + Exporter = llvm::make_unique<CoverageExporterJson>(*Coverage.get(), + ViewOpts, outs()); + break; + case CoverageViewOptions::OutputFormat::HTML: + // Unreachable because we should have gracefully terminated with an error + // above. + llvm_unreachable("Export in HTML is not supported!"); + case CoverageViewOptions::OutputFormat::Lcov: + Exporter = llvm::make_unique<CoverageExporterLcov>(*Coverage.get(), + ViewOpts, outs()); + break; + } if (SourceFiles.empty()) - Exporter.renderRoot(IgnoreFilenameFilters); + Exporter->renderRoot(IgnoreFilenameFilters); else - Exporter.renderRoot(SourceFiles); + Exporter->renderRoot(SourceFiles); return 0; } diff --git a/tools/llvm-cov/CoverageExporter.h b/tools/llvm-cov/CoverageExporter.h index 884fba96d618..b226d68813d9 100644 --- a/tools/llvm-cov/CoverageExporter.h +++ b/tools/llvm-cov/CoverageExporter.h @@ -30,7 +30,7 @@ protected: /// The options passed to the tool. const CoverageViewOptions &Options; - /// Output stream to print JSON to. + /// Output stream to print to. raw_ostream &OS; CoverageExporter(const coverage::CoverageMapping &CoverageMapping, @@ -41,10 +41,10 @@ public: virtual ~CoverageExporter(){}; /// Render the CoverageMapping object. - virtual void renderRoot(const CoverageFilters &IgnoreFilenameFilters) = 0; + virtual void renderRoot(const CoverageFilters &IgnoreFilters) = 0; /// Render the CoverageMapping object for specified source files. - virtual void renderRoot(const std::vector<std::string> &SourceFiles) = 0; + virtual void renderRoot(ArrayRef<std::string> SourceFiles) = 0; }; } // end namespace llvm diff --git a/tools/llvm-cov/CoverageExporterJson.cpp b/tools/llvm-cov/CoverageExporterJson.cpp index 56c3a0003b02..22243f8e2c3e 100644 --- a/tools/llvm-cov/CoverageExporterJson.cpp +++ b/tools/llvm-cov/CoverageExporterJson.cpp @@ -16,33 +16,34 @@ // The json code coverage export follows the following format // Root: dict => Root Element containing metadata // -- Data: array => Homogeneous array of one or more export objects -// ---- Export: dict => Json representation of one CoverageMapping -// ------ Files: array => List of objects describing coverage for files -// -------- File: dict => Coverage for a single file -// ---------- Segments: array => List of Segments contained in the file -// ------------ Segment: dict => Describes a segment of the file with a counter -// ---------- Expansions: array => List of expansion records -// ------------ Expansion: dict => Object that descibes a single expansion -// -------------- CountedRegion: dict => The region to be expanded -// -------------- TargetRegions: array => List of Regions in the expansion -// ---------------- CountedRegion: dict => Single Region in the expansion -// ---------- Summary: dict => Object summarizing the coverage for this file -// ------------ LineCoverage: dict => Object summarizing line coverage -// ------------ FunctionCoverage: dict => Object summarizing function coverage -// ------------ RegionCoverage: dict => Object summarizing region coverage -// ------ Functions: array => List of objects describing coverage for functions -// -------- Function: dict => Coverage info for a single function -// ---------- Filenames: array => List of filenames that the function relates to -// ---- Summary: dict => Object summarizing the coverage for the entire binary -// ------ LineCoverage: dict => Object summarizing line coverage -// ------ FunctionCoverage: dict => Object summarizing function coverage -// ------ InstantiationCoverage: dict => Object summarizing inst. coverage -// ------ RegionCoverage: dict => Object summarizing region coverage +// -- Export: dict => Json representation of one CoverageMapping +// -- Files: array => List of objects describing coverage for files +// -- File: dict => Coverage for a single file +// -- Segments: array => List of Segments contained in the file +// -- Segment: dict => Describes a segment of the file with a counter +// -- Expansions: array => List of expansion records +// -- Expansion: dict => Object that descibes a single expansion +// -- CountedRegion: dict => The region to be expanded +// -- TargetRegions: array => List of Regions in the expansion +// -- CountedRegion: dict => Single Region in the expansion +// -- Summary: dict => Object summarizing the coverage for this file +// -- LineCoverage: dict => Object summarizing line coverage +// -- FunctionCoverage: dict => Object summarizing function coverage +// -- RegionCoverage: dict => Object summarizing region coverage +// -- Functions: array => List of objects describing coverage for functions +// -- Function: dict => Coverage info for a single function +// -- Filenames: array => List of filenames that the function relates to +// -- Summary: dict => Object summarizing the coverage for the entire binary +// -- LineCoverage: dict => Object summarizing line coverage +// -- FunctionCoverage: dict => Object summarizing function coverage +// -- InstantiationCoverage: dict => Object summarizing inst. coverage +// -- RegionCoverage: dict => Object summarizing region coverage // //===----------------------------------------------------------------------===// #include "CoverageExporterJson.h" #include "CoverageReport.h" +#include "llvm/Support/JSON.h" /// The semantic version combined as a string. #define LLVM_COVERAGE_EXPORT_JSON_STR "2.0.0" @@ -52,335 +53,142 @@ using namespace llvm; -CoverageExporterJson::CoverageExporterJson( - const coverage::CoverageMapping &CoverageMapping, - const CoverageViewOptions &Options, raw_ostream &OS) - : CoverageExporter(CoverageMapping, Options, OS) { - State.push(JsonState::None); -} - -void CoverageExporterJson::emitSerialized(const int64_t Value) { OS << Value; } +namespace { -void CoverageExporterJson::emitSerialized(const std::string &Value) { - OS << "\""; - for (char C : Value) { - if (C != '\\') - OS << C; - else - OS << "\\\\"; - } - OS << "\""; +json::Array renderSegment(const coverage::CoverageSegment &Segment) { + return json::Array({Segment.Line, Segment.Col, int64_t(Segment.Count), + Segment.HasCount, Segment.IsRegionEntry}); } -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"); - State.push(JsonState::NonEmptyElement); - } +json::Array renderRegion(const coverage::CountedRegion &Region) { + return json::Array({Region.LineStart, Region.ColumnStart, Region.LineEnd, + Region.ColumnEnd, int64_t(Region.ExecutionCount), + Region.FileID, Region.ExpandedFileID, + int64_t(Region.Kind)}); } -void CoverageExporterJson::emitDictStart() { - emitComma(); - State.push(JsonState::EmptyElement); - OS << "{"; +json::Array renderRegions(ArrayRef<coverage::CountedRegion> Regions) { + json::Array RegionArray; + for (const auto &Region : Regions) + RegionArray.push_back(renderRegion(Region)); + return RegionArray; +} + +json::Object renderExpansion(const coverage::ExpansionRecord &Expansion) { + return json::Object( + {{"filenames", json::Array(Expansion.Function.Filenames)}, + // Mark the beginning and end of this expansion in the source file. + {"source_region", renderRegion(Expansion.Region)}, + // Enumerate the coverage information for the expansion. + {"target_regions", renderRegions(Expansion.Function.CountedRegions)}}); +} + +json::Object renderSummary(const FileCoverageSummary &Summary) { + return json::Object( + {{"lines", + json::Object({{"count", int64_t(Summary.LineCoverage.getNumLines())}, + {"covered", int64_t(Summary.LineCoverage.getCovered())}, + {"percent", Summary.LineCoverage.getPercentCovered()}})}, + {"functions", + json::Object( + {{"count", int64_t(Summary.FunctionCoverage.getNumFunctions())}, + {"covered", int64_t(Summary.FunctionCoverage.getExecuted())}, + {"percent", Summary.FunctionCoverage.getPercentCovered()}})}, + {"instantiations", + json::Object( + {{"count", + int64_t(Summary.InstantiationCoverage.getNumFunctions())}, + {"covered", int64_t(Summary.InstantiationCoverage.getExecuted())}, + {"percent", Summary.InstantiationCoverage.getPercentCovered()}})}, + {"regions", + json::Object( + {{"count", int64_t(Summary.RegionCoverage.getNumRegions())}, + {"covered", int64_t(Summary.RegionCoverage.getCovered())}, + {"notcovered", int64_t(Summary.RegionCoverage.getNumRegions() - + Summary.RegionCoverage.getCovered())}, + {"percent", Summary.RegionCoverage.getPercentCovered()}})}}); +} + +json::Array renderFileExpansions(const coverage::CoverageData &FileCoverage, + const FileCoverageSummary &FileReport) { + json::Array ExpansionArray; + for (const auto &Expansion : FileCoverage.getExpansions()) + ExpansionArray.push_back(renderExpansion(Expansion)); + return ExpansionArray; } -void CoverageExporterJson::emitDictKey(const std::string &Key) { - emitComma(); - emitSerialized(Key); - OS << ":"; - State.pop(); - assert((State.size() >= 1) && "Closed too many JSON elements"); - - // We do not want to emit a comma after this key. - State.push(JsonState::EmptyElement); +json::Array renderFileSegments(const coverage::CoverageData &FileCoverage, + const FileCoverageSummary &FileReport) { + json::Array SegmentArray; + for (const auto &Segment : FileCoverage) + SegmentArray.push_back(renderSegment(Segment)); + return SegmentArray; } -void CoverageExporterJson::emitDictEnd() { - State.pop(); - assert((State.size() >= 1) && "Closed too many JSON elements"); - OS << "}"; +json::Object renderFile(const coverage::CoverageMapping &Coverage, + const std::string &Filename, + const FileCoverageSummary &FileReport, + bool ExportSummaryOnly) { + json::Object File({{"filename", Filename}}); + if (!ExportSummaryOnly) { + // Calculate and render detailed coverage information for given file. + auto FileCoverage = Coverage.getCoverageForFile(Filename); + File["segments"] = renderFileSegments(FileCoverage, FileReport); + File["expansions"] = renderFileExpansions(FileCoverage, FileReport); + } + File["summary"] = renderSummary(FileReport); + return File; } -void CoverageExporterJson::emitArrayStart() { - emitComma(); - State.push(JsonState::EmptyElement); - OS << "["; +json::Array renderFiles(const coverage::CoverageMapping &Coverage, + ArrayRef<std::string> SourceFiles, + ArrayRef<FileCoverageSummary> FileReports, + bool ExportSummaryOnly) { + json::Array FileArray; + for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) + FileArray.push_back(renderFile(Coverage, SourceFiles[I], FileReports[I], + ExportSummaryOnly)); + return FileArray; } -void CoverageExporterJson::emitArrayEnd() { - State.pop(); - assert((State.size() >= 1) && "Closed too many JSON elements"); - OS << "]"; +json::Array renderFunctions( + const iterator_range<coverage::FunctionRecordIterator> &Functions) { + json::Array FunctionArray; + for (const auto &F : Functions) + FunctionArray.push_back( + json::Object({{"name", F.Name}, + {"count", int64_t(F.ExecutionCount)}, + {"regions", renderRegions(F.CountedRegions)}, + {"filenames", json::Array(F.Filenames)}})); + return FunctionArray; } -void CoverageExporterJson::renderRoot( - const CoverageFilters &IgnoreFilenameFilters) { +} // end anonymous namespace + +void CoverageExporterJson::renderRoot(const CoverageFilters &IgnoreFilters) { std::vector<std::string> SourceFiles; for (StringRef SF : Coverage.getUniqueSourceFiles()) { - if (!IgnoreFilenameFilters.matchesFilename(SF)) + if (!IgnoreFilters.matchesFilename(SF)) SourceFiles.emplace_back(SF); } renderRoot(SourceFiles); } -void CoverageExporterJson::renderRoot( - const std::vector<std::string> &SourceFiles) { - // Start Root of JSON object. - emitDictStart(); - - emitDictElement("version", LLVM_COVERAGE_EXPORT_JSON_STR); - emitDictElement("type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR); - emitDictKey("data"); - - // Start List of Exports. - emitArrayStart(); - - // Start Export. - emitDictStart(); - - emitDictKey("files"); - +void CoverageExporterJson::renderRoot(ArrayRef<std::string> SourceFiles) { FileCoverageSummary Totals = FileCoverageSummary("Totals"); auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, SourceFiles, Options); - renderFiles(SourceFiles, FileReports); - + auto Export = + json::Object({{"files", renderFiles(Coverage, SourceFiles, FileReports, + Options.ExportSummaryOnly)}, + {"totals", renderSummary(Totals)}}); // 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"); -} - -void CoverageExporterJson::renderFunctions( - const iterator_range<coverage::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); - - emitDictKey("filenames"); - - // Start Filenames for Function. - emitArrayStart(); - - for (const auto &FileName : Function.Filenames) - emitArrayElement(FileName); - - // End Filenames for Function. - emitArrayEnd(); - - // End Function. - emitDictEnd(); - } - - // End List of Functions. - emitArrayEnd(); -} - -void CoverageExporterJson::renderFiles( - ArrayRef<std::string> SourceFiles, - ArrayRef<FileCoverageSummary> FileReports) { - // Start List of Files. - emitArrayStart(); - - for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) { - renderFile(SourceFiles[I], FileReports[I]); - } - - // End List of Files. - emitArrayEnd(); -} - -void CoverageExporterJson::renderFile(const std::string &Filename, - const FileCoverageSummary &FileReport) { - // Start File. - emitDictStart(); - - emitDictElement("filename", Filename); - - if (!Options.ExportSummaryOnly) { - // Calculate and render detailed coverage information for given file. - auto FileCoverage = Coverage.getCoverageForFile(Filename); - renderFileCoverage(FileCoverage, FileReport); - } - - emitDictKey("summary"); - renderSummary(FileReport); - - // End File. - emitDictEnd(); -} - - -void CoverageExporterJson::renderFileCoverage( - const coverage::CoverageData &FileCoverage, - const FileCoverageSummary &FileReport) { - emitDictKey("segments"); - - // Start List of Segments. - emitArrayStart(); - - for (const auto &Segment : FileCoverage) - renderSegment(Segment); - - // End List of Segments. - emitArrayEnd(); - - emitDictKey("expansions"); - - // Start List of Expansions. - emitArrayStart(); - - for (const auto &Expansion : FileCoverage.getExpansions()) - renderExpansion(Expansion); - - // End List of Expansions. - emitArrayEnd(); -} - -void CoverageExporterJson::renderSegment( - const coverage::CoverageSegment &Segment) { - // Start Segment. - emitArrayStart(); - - emitArrayElement(Segment.Line); - emitArrayElement(Segment.Col); - emitArrayElement(Segment.Count); - emitArrayElement(Segment.HasCount); - emitArrayElement(Segment.IsRegionEntry); - - // End Segment. - emitArrayEnd(); -} - -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(); -} - -void CoverageExporterJson::renderRegions( - ArrayRef<coverage::CountedRegion> Regions) { - // Start List of Regions. - emitArrayStart(); - - for (const auto &Region : Regions) - renderRegion(Region); - - // End List of Regions. - emitArrayEnd(); -} - -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(); -} - -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"); + if (!Options.ExportSummaryOnly) + Export["functions"] = renderFunctions(Coverage.getCoveredFunctions()); - // 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(); + auto ExportArray = json::Array({std::move(Export)}); - // End Summary for the file. - emitDictEnd(); + OS << json::Object({{"version", LLVM_COVERAGE_EXPORT_JSON_STR}, + {"type", LLVM_COVERAGE_EXPORT_JSON_TYPE_STR}, + {"data", std::move(ExportArray)}}); } diff --git a/tools/llvm-cov/CoverageExporterJson.h b/tools/llvm-cov/CoverageExporterJson.h index f88dffa0ebea..c37c86b42be9 100644 --- a/tools/llvm-cov/CoverageExporterJson.h +++ b/tools/llvm-cov/CoverageExporterJson.h @@ -15,96 +15,20 @@ #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); + const CoverageViewOptions &Options, raw_ostream &OS) + : CoverageExporter(CoverageMapping, Options, OS) {} /// Render the CoverageMapping object. - void renderRoot(const CoverageFilters &IgnoreFilenameFilters) override; + void renderRoot(const CoverageFilters &IgnoreFilters) override; /// Render the CoverageMapping object for specified source files. - void renderRoot(const std::vector<std::string> &SourceFiles) override; + void renderRoot(ArrayRef<std::string> SourceFiles) override; }; } // end namespace llvm diff --git a/tools/llvm-cov/CoverageExporterLcov.cpp b/tools/llvm-cov/CoverageExporterLcov.cpp new file mode 100644 index 000000000000..d149ba1a4c87 --- /dev/null +++ b/tools/llvm-cov/CoverageExporterLcov.cpp @@ -0,0 +1,125 @@ +//===- CoverageExporterLcov.cpp - Code coverage export --------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements export of code coverage data to lcov trace file format. +// +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// +// The trace file code coverage export follows the following format (see also +// https://linux.die.net/man/1/geninfo). Each quoted string appears on its own +// line; the indentation shown here is only for documentation purposes. +// +// - for each source file: +// - "SF:<absolute path to source file>" +// - for each function: +// - "FN:<line number of function start>,<function name>" +// - for each function: +// - "FNDA:<execution count>,<function name>" +// - "FNF:<number of functions found>" +// - "FNH:<number of functions hit>" +// - for each instrumented line: +// - "DA:<line number>,<execution count>[,<checksum>] +// - "LH:<number of lines with non-zero execution count>" +// - "LF:<nubmer of instrumented lines>" +// - "end_of_record" +// +// If the user is exporting summary information only, then the FN, FNDA, and DA +// lines will not be present. +// +//===----------------------------------------------------------------------===// + +#include "CoverageExporterLcov.h" +#include "CoverageReport.h" + +using namespace llvm; + +namespace { + +void renderFunctionSummary(raw_ostream &OS, + const FileCoverageSummary &Summary) { + OS << "FNF:" << Summary.FunctionCoverage.getNumFunctions() << '\n' + << "FNH:" << Summary.FunctionCoverage.getExecuted() << '\n'; +} + +void renderFunctions( + raw_ostream &OS, + const iterator_range<coverage::FunctionRecordIterator> &Functions) { + for (const auto &F : Functions) { + auto StartLine = F.CountedRegions.front().LineStart; + OS << "FN:" << StartLine << ',' << F.Name << '\n'; + } + for (const auto &F : Functions) + OS << "FNDA:" << F.ExecutionCount << ',' << F.Name << '\n'; +} + +void renderLineExecutionCounts(raw_ostream &OS, + const coverage::CoverageData &FileCoverage) { + coverage::LineCoverageIterator LCI{FileCoverage, 1}; + coverage::LineCoverageIterator LCIEnd = LCI.getEnd(); + for (; LCI != LCIEnd; ++LCI) { + const coverage::LineCoverageStats &LCS = *LCI; + if (LCS.isMapped()) { + OS << "DA:" << LCS.getLine() << ',' << LCS.getExecutionCount() << '\n'; + } + } +} + +void renderLineSummary(raw_ostream &OS, const FileCoverageSummary &Summary) { + OS << "LF:" << Summary.LineCoverage.getNumLines() << '\n' + << "LH:" << Summary.LineCoverage.getCovered() << '\n'; +} + +void renderFile(raw_ostream &OS, const coverage::CoverageMapping &Coverage, + const std::string &Filename, + const FileCoverageSummary &FileReport, bool ExportSummaryOnly) { + OS << "SF:" << Filename << '\n'; + + if (!ExportSummaryOnly) { + renderFunctions(OS, Coverage.getCoveredFunctions()); + } + renderFunctionSummary(OS, FileReport); + + if (!ExportSummaryOnly) { + // Calculate and render detailed coverage information for given file. + auto FileCoverage = Coverage.getCoverageForFile(Filename); + renderLineExecutionCounts(OS, FileCoverage); + } + renderLineSummary(OS, FileReport); + + OS << "end_of_record\n"; +} + +void renderFiles(raw_ostream &OS, const coverage::CoverageMapping &Coverage, + ArrayRef<std::string> SourceFiles, + ArrayRef<FileCoverageSummary> FileReports, + bool ExportSummaryOnly) { + for (unsigned I = 0, E = SourceFiles.size(); I < E; ++I) + renderFile(OS, Coverage, SourceFiles[I], FileReports[I], ExportSummaryOnly); +} + +} // end anonymous namespace + +void CoverageExporterLcov::renderRoot(const CoverageFilters &IgnoreFilters) { + std::vector<std::string> SourceFiles; + for (StringRef SF : Coverage.getUniqueSourceFiles()) { + if (!IgnoreFilters.matchesFilename(SF)) + SourceFiles.emplace_back(SF); + } + renderRoot(SourceFiles); +} + +void CoverageExporterLcov::renderRoot(ArrayRef<std::string> SourceFiles) { + FileCoverageSummary Totals = FileCoverageSummary("Totals"); + auto FileReports = CoverageReport::prepareFileReports(Coverage, Totals, + SourceFiles, Options); + renderFiles(OS, Coverage, SourceFiles, FileReports, + Options.ExportSummaryOnly); +} diff --git a/tools/llvm-cov/CoverageExporterLcov.h b/tools/llvm-cov/CoverageExporterLcov.h new file mode 100644 index 000000000000..539b2dacd384 --- /dev/null +++ b/tools/llvm-cov/CoverageExporterLcov.h @@ -0,0 +1,36 @@ +//===- CoverageExporterLcov.h - Code coverage lcov 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 lcov trace file format. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_COV_COVERAGEEXPORTERLCOV_H +#define LLVM_COV_COVERAGEEXPORTERLCOV_H + +#include "CoverageExporter.h" + +namespace llvm { + +class CoverageExporterLcov : public CoverageExporter { +public: + CoverageExporterLcov(const coverage::CoverageMapping &CoverageMapping, + const CoverageViewOptions &Options, raw_ostream &OS) + : CoverageExporter(CoverageMapping, Options, OS) {} + + /// Render the CoverageMapping object. + void renderRoot(const CoverageFilters &IgnoreFilters) override; + + /// Render the CoverageMapping object for specified source files. + void renderRoot(ArrayRef<std::string> SourceFiles) override; +}; + +} // end namespace llvm + +#endif // LLVM_COV_COVERAGEEXPORTERLCOV_H diff --git a/tools/llvm-cov/CoverageViewOptions.h b/tools/llvm-cov/CoverageViewOptions.h index 20085a957bb5..c8a472860027 100644 --- a/tools/llvm-cov/CoverageViewOptions.h +++ b/tools/llvm-cov/CoverageViewOptions.h @@ -20,7 +20,8 @@ namespace llvm { struct CoverageViewOptions { enum class OutputFormat { Text, - HTML + HTML, + Lcov }; bool Debug; diff --git a/tools/llvm-cov/SourceCoverageView.cpp b/tools/llvm-cov/SourceCoverageView.cpp index a5a8fa9a4814..cebaf63adb12 100644 --- a/tools/llvm-cov/SourceCoverageView.cpp +++ b/tools/llvm-cov/SourceCoverageView.cpp @@ -31,7 +31,7 @@ void CoveragePrinter::StreamDestructor::operator()(raw_ostream *OS) const { std::string CoveragePrinter::getOutputPath(StringRef Path, StringRef Extension, bool InToplevel, bool Relative) const { - assert(Extension.size() && "The file extension may not be empty"); + assert(!Extension.empty() && "The file extension may not be empty"); SmallString<256> FullPath; @@ -80,6 +80,10 @@ CoveragePrinter::create(const CoverageViewOptions &Opts) { return llvm::make_unique<CoveragePrinterText>(Opts); case CoverageViewOptions::OutputFormat::HTML: return llvm::make_unique<CoveragePrinterHTML>(Opts); + case CoverageViewOptions::OutputFormat::Lcov: + // Unreachable because CodeCoverage.cpp should terminate with an error + // before we get here. + llvm_unreachable("Lcov format is not supported!"); } llvm_unreachable("Unknown coverage output format!"); } @@ -143,6 +147,10 @@ SourceCoverageView::create(StringRef SourceName, const MemoryBuffer &File, case CoverageViewOptions::OutputFormat::HTML: return llvm::make_unique<SourceCoverageViewHTML>( SourceName, File, Options, std::move(CoverageInfo)); + case CoverageViewOptions::OutputFormat::Lcov: + // Unreachable because CodeCoverage.cpp should terminate with an error + // before we get here. + llvm_unreachable("Lcov format is not supported!"); } llvm_unreachable("Unknown coverage output format!"); } diff --git a/tools/llvm-cov/SourceCoverageViewHTML.cpp b/tools/llvm-cov/SourceCoverageViewHTML.cpp index acb67aa5cfc7..3f730bb7bc82 100644 --- a/tools/llvm-cov/SourceCoverageViewHTML.cpp +++ b/tools/llvm-cov/SourceCoverageViewHTML.cpp @@ -54,7 +54,7 @@ std::string escape(StringRef Str, const CoverageViewOptions &Opts) { std::string tag(const std::string &Name, const std::string &Str, const std::string &ClassName = "") { std::string Tag = "<" + Name; - if (ClassName != "") + if (!ClassName.empty()) Tag += " class='" + ClassName + "'"; return Tag + ">" + Str + "</" + Name + ">"; } diff --git a/tools/llvm-cov/TestingSupport.cpp b/tools/llvm-cov/TestingSupport.cpp index e07abdbd17f1..16a1c2665299 100644 --- a/tools/llvm-cov/TestingSupport.cpp +++ b/tools/llvm-cov/TestingSupport.cpp @@ -33,7 +33,7 @@ int convertForTestingMain(int argc, const char *argv[]) { if (!ObjErr) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(ObjErr.takeError(), OS, ""); + logAllUnhandledErrors(ObjErr.takeError(), OS); OS.flush(); errs() << "error: " << Buf; return 1; diff --git a/tools/llvm-cvtres/llvm-cvtres.cpp b/tools/llvm-cvtres/llvm-cvtres.cpp index 5d8e6ab3929b..53989c8191fa 100644 --- a/tools/llvm-cvtres/llvm-cvtres.cpp +++ b/tools/llvm-cvtres/llvm-cvtres.cpp @@ -103,7 +103,7 @@ int main(int Argc, const char **Argv) { opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); if (InputArgs.hasArg(OPT_HELP)) { - T.PrintHelp(outs(), "cvtres", "Resource Converter", false); + T.PrintHelp(outs(), "llvm-cvtres [options] file...", "Resource Converter", false); return 0; } diff --git a/tools/llvm-cxxdump/llvm-cxxdump.cpp b/tools/llvm-cxxdump/llvm-cxxdump.cpp index 09e40d9b0db7..7594066a395d 100644 --- a/tools/llvm-cxxdump/llvm-cxxdump.cpp +++ b/tools/llvm-cxxdump/llvm-cxxdump.cpp @@ -23,6 +23,7 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <map> #include <string> @@ -43,17 +44,18 @@ namespace llvm { static void error(std::error_code EC) { if (!EC) return; - outs() << "\nError reading file: " << EC.message() << ".\n"; + WithColor::error(outs(), "") << "reading file: " << EC.message() << ".\n"; outs().flush(); exit(1); } static void error(Error Err) { - if (Err) { - logAllUnhandledErrors(std::move(Err), outs(), "Error reading file: "); - outs().flush(); - exit(1); - } + if (!Err) + return; + logAllUnhandledErrors(std::move(Err), WithColor::error(outs()), + "reading file: "); + outs().flush(); + exit(1); } } // namespace llvm @@ -61,7 +63,7 @@ static void error(Error Err) { static void reportError(StringRef Input, StringRef Message) { if (Input == "-") Input = "<stdin>"; - errs() << Input << ": " << Message << "\n"; + WithColor::error(errs(), Input) << Message << "\n"; errs().flush(); exit(1); } @@ -496,7 +498,7 @@ static void dumpArchive(const Archive *Arc) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); reportError(Arc->getFileName(), Buf); } diff --git a/tools/llvm-cxxmap/CMakeLists.txt b/tools/llvm-cxxmap/CMakeLists.txt new file mode 100644 index 000000000000..e180d3cd82ff --- /dev/null +++ b/tools/llvm-cxxmap/CMakeLists.txt @@ -0,0 +1,8 @@ +set(LLVM_LINK_COMPONENTS + Core + Support + ) + +add_llvm_tool(llvm-cxxmap + llvm-cxxmap.cpp + ) diff --git a/tools/llvm-cxxmap/LLVMBuild.txt b/tools/llvm-cxxmap/LLVMBuild.txt new file mode 100644 index 000000000000..2876c4a2d1f2 --- /dev/null +++ b/tools/llvm-cxxmap/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-cxxmap/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-cxxmap +parent = Tools +required_libraries = Support diff --git a/tools/llvm-cxxmap/llvm-cxxmap.cpp b/tools/llvm-cxxmap/llvm-cxxmap.cpp new file mode 100644 index 000000000000..39028cc86723 --- /dev/null +++ b/tools/llvm-cxxmap/llvm-cxxmap.cpp @@ -0,0 +1,155 @@ +//===- llvm-cxxmap.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// llvm-cxxmap computes a correspondence between old symbol names and new +// symbol names based on a symbol equivalence file. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/InitLLVM.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/SymbolRemappingReader.h" +#include "llvm/Support/WithColor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; + +cl::opt<std::string> OldSymbolFile(cl::Positional, cl::Required, + cl::desc("<symbol-file>")); +cl::opt<std::string> NewSymbolFile(cl::Positional, cl::Required, + cl::desc("<symbol-file>")); +cl::opt<std::string> RemappingFile("remapping-file", cl::Required, + cl::desc("Remapping file")); +cl::alias RemappingFileA("r", cl::aliasopt(RemappingFile)); +cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), + cl::init("-"), cl::desc("Output file")); +cl::alias OutputFilenameA("o", cl::aliasopt(OutputFilename)); + +cl::opt<bool> WarnAmbiguous( + "Wambiguous", + cl::desc("Warn on equivalent symbols in the output symbol list")); +cl::opt<bool> WarnIncomplete( + "Wincomplete", + cl::desc("Warn on input symbols missing from output symbol list")); + +static void warn(Twine Message, Twine Whence = "", + std::string Hint = "") { + WithColor::warning(); + std::string WhenceStr = Whence.str(); + if (!WhenceStr.empty()) + errs() << WhenceStr << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; +} + +static void exitWithError(Twine Message, Twine Whence = "", + std::string Hint = "") { + WithColor::error(); + std::string WhenceStr = Whence.str(); + if (!WhenceStr.empty()) + errs() << WhenceStr << ": "; + errs() << Message << "\n"; + if (!Hint.empty()) + WithColor::note() << Hint << "\n"; + ::exit(1); +} + +static void exitWithError(Error E, StringRef Whence = "") { + exitWithError(toString(std::move(E)), Whence); +} + +static void exitWithErrorCode(std::error_code EC, StringRef Whence = "") { + exitWithError(EC.message(), Whence); +} + +static void remapSymbols(MemoryBuffer &OldSymbolFile, + MemoryBuffer &NewSymbolFile, + MemoryBuffer &RemappingFile, + raw_ostream &Out) { + // Load the remapping file and prepare to canonicalize symbols. + SymbolRemappingReader Reader; + if (Error E = Reader.read(RemappingFile)) + exitWithError(std::move(E)); + + // Canonicalize the new symbols. + DenseMap<SymbolRemappingReader::Key, StringRef> MappedNames; + DenseSet<StringRef> UnparseableSymbols; + for (line_iterator LineIt(NewSymbolFile, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + StringRef Symbol = *LineIt; + + auto K = Reader.insert(Symbol); + if (!K) { + UnparseableSymbols.insert(Symbol); + continue; + } + + auto ItAndIsNew = MappedNames.insert({K, Symbol}); + if (WarnAmbiguous && !ItAndIsNew.second && + ItAndIsNew.first->second != Symbol) { + warn("symbol " + Symbol + " is equivalent to earlier symbol " + + ItAndIsNew.first->second, + NewSymbolFile.getBufferIdentifier() + ":" + + Twine(LineIt.line_number()), + "later symbol will not be the target of any remappings"); + } + } + + // Figure out which new symbol each old symbol is equivalent to. + for (line_iterator LineIt(OldSymbolFile, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + StringRef Symbol = *LineIt; + + auto K = Reader.lookup(Symbol); + StringRef NewSymbol = MappedNames.lookup(K); + + if (NewSymbol.empty()) { + if (WarnIncomplete && !UnparseableSymbols.count(Symbol)) { + warn("no new symbol matches old symbol " + Symbol, + OldSymbolFile.getBufferIdentifier() + ":" + + Twine(LineIt.line_number())); + } + continue; + } + + Out << Symbol << " " << NewSymbol << "\n"; + } +} + +int main(int argc, const char *argv[]) { + InitLLVM X(argc, argv); + + cl::ParseCommandLineOptions(argc, argv, "LLVM C++ mangled name remapper\n"); + + auto OldSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(OldSymbolFile); + if (!OldSymbolBufOrError) + exitWithErrorCode(OldSymbolBufOrError.getError(), OldSymbolFile); + + auto NewSymbolBufOrError = MemoryBuffer::getFileOrSTDIN(NewSymbolFile); + if (!NewSymbolBufOrError) + exitWithErrorCode(NewSymbolBufOrError.getError(), NewSymbolFile); + + auto RemappingBufOrError = MemoryBuffer::getFileOrSTDIN(RemappingFile); + if (!RemappingBufOrError) + exitWithErrorCode(RemappingBufOrError.getError(), RemappingFile); + + std::error_code EC; + raw_fd_ostream OS(OutputFilename.data(), EC, sys::fs::F_Text); + if (EC) + exitWithErrorCode(EC, OutputFilename); + + remapSymbols(*OldSymbolBufOrError.get(), *NewSymbolBufOrError.get(), + *RemappingBufOrError.get(), OS); +} diff --git a/tools/llvm-diff/DifferenceEngine.cpp b/tools/llvm-diff/DifferenceEngine.cpp index af0a055ea21f..acff8bb3e89b 100644 --- a/tools/llvm-diff/DifferenceEngine.cpp +++ b/tools/llvm-diff/DifferenceEngine.cpp @@ -629,8 +629,8 @@ void FunctionDifferenceEngine::runBlockDiff(BasicBlock::iterator LStart, // If the terminators have different kinds, but one is an invoke and the // other is an unconditional branch immediately following a call, unify // the results and the destinations. - TerminatorInst *LTerm = LStart->getParent()->getTerminator(); - TerminatorInst *RTerm = RStart->getParent()->getTerminator(); + Instruction *LTerm = LStart->getParent()->getTerminator(); + Instruction *RTerm = RStart->getParent()->getTerminator(); if (isa<BranchInst>(LTerm) && isa<InvokeInst>(RTerm)) { if (cast<BranchInst>(LTerm)->isConditional()) return; BasicBlock::iterator I = LTerm->getIterator(); @@ -686,9 +686,18 @@ void DifferenceEngine::diff(Module *L, Module *R) { StringSet<> LNames; SmallVector<std::pair<Function*,Function*>, 20> Queue; + unsigned LeftAnonCount = 0; + unsigned RightAnonCount = 0; + for (Module::iterator I = L->begin(), E = L->end(); I != E; ++I) { Function *LFn = &*I; - LNames.insert(LFn->getName()); + StringRef Name = LFn->getName(); + if (Name.empty()) { + ++LeftAnonCount; + continue; + } + + LNames.insert(Name); if (Function *RFn = R->getFunction(LFn->getName())) Queue.push_back(std::make_pair(LFn, RFn)); @@ -698,10 +707,25 @@ void DifferenceEngine::diff(Module *L, Module *R) { for (Module::iterator I = R->begin(), E = R->end(); I != E; ++I) { Function *RFn = &*I; - if (!LNames.count(RFn->getName())) + StringRef Name = RFn->getName(); + if (Name.empty()) { + ++RightAnonCount; + continue; + } + + if (!LNames.count(Name)) logf("function %r exists only in right module") << RFn; } + + if (LeftAnonCount != 0 || RightAnonCount != 0) { + SmallString<32> Tmp; + logf(("not comparing " + Twine(LeftAnonCount) + + " anonymous functions in the left module and " + + Twine(RightAnonCount) + " in the right module") + .toStringRef(Tmp)); + } + for (SmallVectorImpl<std::pair<Function*,Function*> >::iterator I = Queue.begin(), E = Queue.end(); I != E; ++I) diff(I->first, I->second); diff --git a/tools/llvm-dwarfdump/Statistics.cpp b/tools/llvm-dwarfdump/Statistics.cpp index 5af853d4ef28..5fe7e8b4615b 100644 --- a/tools/llvm-dwarfdump/Statistics.cpp +++ b/tools/llvm-dwarfdump/Statistics.cpp @@ -1,4 +1,6 @@ #include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSet.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFDebugLoc.h" @@ -18,18 +20,27 @@ struct PerFunctionStats { /// Number of constants with location across all inlined instances. unsigned ConstantMembers = 0; /// List of all Variables in this function. - SmallDenseSet<uint32_t, 4> VarsInFunction; + StringSet<> VarsInFunction; /// Compile units also cover a PC range, but have this flag set to false. bool IsFunction = false; }; -/// Holds accumulated global statistics about local variables. +/// Holds accumulated global statistics about DIEs. struct GlobalStats { /// Total number of PC range bytes covered by DW_AT_locations. unsigned ScopeBytesCovered = 0; /// Total number of PC range bytes in each variable's enclosing scope, /// starting from the first definition of the variable. unsigned ScopeBytesFromFirstDefinition = 0; + /// Total number of call site entries (DW_TAG_call_site). + unsigned CallSiteEntries = 0; + /// Total byte size of concrete functions. This byte size includes + /// inline functions contained in the concrete functions. + uint64_t FunctionSize = 0; + /// Total byte size of inlined functions. This is the total number of bytes + /// for the top inline functions within concrete functions. This can help + /// tune the inline settings when compiling to match user expectations. + uint64_t InlineFunctionSize = 0; }; /// Extract the low pc from a Die. @@ -46,19 +57,37 @@ static uint64_t getLowPC(DWARFDie Die) { } /// Collect debug info quality metrics for one DIE. -static void collectStatsForDie(DWARFDie Die, std::string Prefix, - uint64_t ScopeLowPC, uint64_t BytesInScope, +static void collectStatsForDie(DWARFDie Die, std::string FnPrefix, + std::string VarPrefix, uint64_t ScopeLowPC, + uint64_t BytesInScope, + uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats) { bool HasLoc = false; uint64_t BytesCovered = 0; uint64_t OffsetToFirstDefinition = 0; + + if (Die.getTag() == dwarf::DW_TAG_call_site) { + GlobalStats.CallSiteEntries++; + return; + } + + if (Die.getTag() != dwarf::DW_TAG_formal_parameter && + Die.getTag() != dwarf::DW_TAG_variable && + Die.getTag() != dwarf::DW_TAG_member) { + // Not a variable or constant member. + return; + } + if (Die.find(dwarf::DW_AT_const_value)) { // This catches constant members *and* variables. HasLoc = true; BytesCovered = BytesInScope; - } else if (Die.getTag() == dwarf::DW_TAG_variable || - Die.getTag() == dwarf::DW_TAG_formal_parameter) { + } else { + if (Die.getTag() == dwarf::DW_TAG_member) { + // Non-const member. + return; + } // Handle variables and function arguments. auto FormValue = Die.find(dwarf::DW_AT_location); HasLoc = FormValue.hasValue(); @@ -86,19 +115,17 @@ static void collectStatsForDie(DWARFDie Die, std::string Prefix, BytesCovered = BytesInScope; } } - } else { - // Not a variable or constant member. - return; } // Collect PC range coverage data. - auto &FnStats = FnStatMap[Prefix]; + auto &FnStats = FnStatMap[FnPrefix]; if (DWARFDie D = Die.getAttributeValueAsReferencedDie(dwarf::DW_AT_abstract_origin)) Die = D; - // This is a unique ID for the variable inside the current object file. - unsigned CanonicalDieOffset = Die.getOffset(); - FnStats.VarsInFunction.insert(CanonicalDieOffset); + // By using the variable name + the path through the lexical block tree, the + // keys are consistent across duplicate abstract origins in different CUs. + std::string VarName = StringRef(Die.getName(DINameKind::ShortName)); + FnStats.VarsInFunction.insert(VarPrefix+VarName); if (BytesInScope) { FnStats.TotalVarWithLoc += (unsigned)HasLoc; // Adjust for the fact the variables often start their lifetime in the @@ -115,24 +142,34 @@ static void collectStatsForDie(DWARFDie Die, std::string Prefix, } /// Recursively collect debug info quality metrics. -static void collectStatsRecursive(DWARFDie Die, std::string Prefix, - uint64_t ScopeLowPC, uint64_t BytesInScope, +static void collectStatsRecursive(DWARFDie Die, std::string FnPrefix, + std::string VarPrefix, uint64_t ScopeLowPC, + uint64_t BytesInScope, + uint32_t InlineDepth, StringMap<PerFunctionStats> &FnStatMap, GlobalStats &GlobalStats) { // Handle any kind of lexical scope. - if (Die.getTag() == dwarf::DW_TAG_subprogram || - Die.getTag() == dwarf::DW_TAG_inlined_subroutine || - Die.getTag() == dwarf::DW_TAG_lexical_block) { + const dwarf::Tag Tag = Die.getTag(); + const bool IsFunction = Tag == dwarf::DW_TAG_subprogram; + const bool IsBlock = Tag == dwarf::DW_TAG_lexical_block; + const bool IsInlinedFunction = Tag == dwarf::DW_TAG_inlined_subroutine; + if (IsFunction || IsInlinedFunction || IsBlock) { + + // Reset VarPrefix when entering a new function. + if (Die.getTag() == dwarf::DW_TAG_subprogram || + Die.getTag() == dwarf::DW_TAG_inlined_subroutine) + VarPrefix = "v"; + // Ignore forward declarations. if (Die.find(dwarf::DW_AT_declaration)) return; // Count the function. - if (Die.getTag() != dwarf::DW_TAG_lexical_block) { + if (!IsBlock) { StringRef Name = Die.getName(DINameKind::LinkageName); if (Name.empty()) Name = Die.getName(DINameKind::ShortName); - Prefix = Name; + FnPrefix = Name; // Skip over abstract origins. if (Die.find(dwarf::DW_AT_inline)) return; @@ -148,26 +185,42 @@ static void collectStatsRecursive(DWARFDie Die, std::string Prefix, llvm::consumeError(RangesOrError.takeError()); return; } - + auto Ranges = RangesOrError.get(); uint64_t BytesInThisScope = 0; for (auto Range : Ranges) BytesInThisScope += Range.HighPC - Range.LowPC; ScopeLowPC = getLowPC(Die); - if (BytesInThisScope) + if (BytesInThisScope) { BytesInScope = BytesInThisScope; + if (IsFunction) + GlobalStats.FunctionSize += BytesInThisScope; + else if (IsInlinedFunction && InlineDepth == 0) + GlobalStats.InlineFunctionSize += BytesInThisScope; + } } else { // Not a scope, visit the Die itself. It could be a variable. - collectStatsForDie(Die, Prefix, ScopeLowPC, BytesInScope, FnStatMap, - GlobalStats); + collectStatsForDie(Die, FnPrefix, VarPrefix, ScopeLowPC, BytesInScope, + InlineDepth, FnStatMap, GlobalStats); } + // Set InlineDepth correctly for child recursion + if (IsFunction) + InlineDepth = 0; + else if (IsInlinedFunction) + ++InlineDepth; + // Traverse children. + unsigned LexicalBlockIndex = 0; DWARFDie Child = Die.getFirstChild(); while (Child) { - collectStatsRecursive(Child, Prefix, ScopeLowPC, BytesInScope, FnStatMap, - GlobalStats); + std::string ChildVarPrefix = VarPrefix; + if (Child.getTag() == dwarf::DW_TAG_lexical_block) + ChildVarPrefix += toHex(LexicalBlockIndex++) + '.'; + + collectStatsRecursive(Child, FnPrefix, ChildVarPrefix, ScopeLowPC, + BytesInScope, InlineDepth, FnStatMap, GlobalStats); Child = Child.getSibling(); } } @@ -200,7 +253,7 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, StringMap<PerFunctionStats> Statistics; for (const auto &CU : static_cast<DWARFContext *>(&DICtx)->compile_units()) if (DWARFDie CUDie = CU->getUnitDIE(false)) - collectStatsRecursive(CUDie, "/", 0, 0, Statistics, GlobalStats); + collectStatsRecursive(CUDie, "/", "g", 0, 0, 0, Statistics, GlobalStats); /// The version number should be increased every time the algorithm is changed /// (including bug fixes). New metrics may be added without increasing the @@ -218,16 +271,15 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, VarWithLoc += Stats.TotalVarWithLoc + Constants; VarTotal += TotalVars + Constants; VarUnique += Stats.VarsInFunction.size(); - LLVM_DEBUG(for (auto V - : Stats.VarsInFunction) llvm::dbgs() - << Entry.getKey() << ": " << V << "\n"); + LLVM_DEBUG(for (auto &V : Stats.VarsInFunction) llvm::dbgs() + << Entry.getKey() << ": " << V.getKey() << "\n"); NumFunctions += Stats.IsFunction; NumInlinedFunctions += Stats.IsFunction * Stats.NumFnInlined; } // Print summary. OS.SetBufferSize(1024); - OS << "{\"version\":\"" << Version << '"'; + OS << "{\"version\":" << Version; LLVM_DEBUG(llvm::dbgs() << "Variable location quality metrics\n"; llvm::dbgs() << "---------------------------------\n"); printDatum(OS, "file", Filename.str()); @@ -237,9 +289,12 @@ bool collectStatsForObjectFile(ObjectFile &Obj, DWARFContext &DICtx, printDatum(OS, "unique source variables", VarUnique); printDatum(OS, "source variables", VarTotal); printDatum(OS, "variables with location", VarWithLoc); + printDatum(OS, "call site entries", GlobalStats.CallSiteEntries); printDatum(OS, "scope bytes total", GlobalStats.ScopeBytesFromFirstDefinition); printDatum(OS, "scope bytes covered", GlobalStats.ScopeBytesCovered); + printDatum(OS, "total function size", GlobalStats.FunctionSize); + printDatum(OS, "total inlined function size", GlobalStats.InlineFunctionSize); OS << "}\n"; LLVM_DEBUG( llvm::dbgs() << "Total Availability: " diff --git a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp index d75f33906098..d9e8e36efe5c 100644 --- a/tools/llvm-dwarfdump/llvm-dwarfdump.cpp +++ b/tools/llvm-dwarfdump/llvm-dwarfdump.cpp @@ -156,7 +156,7 @@ static list<std::string> Name( value_desc("pattern"), cat(DwarfDumpCategory)); static alias NameAlias("n", desc("Alias for -name"), aliasopt(Name)); static opt<unsigned long long> Lookup("lookup", - desc("Lookup <address> in the debug information and print out any" + desc("Lookup <address> in the debug information and print out any " "available file, function, block and line table details."), value_desc("address"), cat(DwarfDumpCategory)); static opt<std::string> @@ -226,7 +226,7 @@ static alias VerboseAlias("v", desc("Alias for -verbose."), aliasopt(Verbose), static void error(StringRef Prefix, std::error_code EC) { if (!EC) return; - errs() << Prefix << ": " << EC.message() << "\n"; + WithColor::error() << Prefix << ": " << EC.message() << "\n"; exit(1); } @@ -281,32 +281,45 @@ using HandlerFn = std::function<bool(ObjectFile &, DWARFContext &DICtx, Twine, raw_ostream &)>; /// Print only DIEs that have a certain name. +static bool filterByName(const StringSet<> &Names, DWARFDie Die, + StringRef NameRef, raw_ostream &OS) { + std::string Name = + (IgnoreCase && !UseRegex) ? NameRef.lower() : NameRef.str(); + if (UseRegex) { + // Match regular expression. + for (auto Pattern : Names.keys()) { + Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags); + std::string Error; + if (!RE.isValid(Error)) { + errs() << "error in regular expression: " << Error << "\n"; + exit(1); + } + if (RE.match(Name)) { + Die.dump(OS, 0, getDumpOpts()); + return true; + } + } + } else if (Names.count(Name)) { + // Match full text. + Die.dump(OS, 0, getDumpOpts()); + return true; + } + return false; +} + +/// Print only DIEs that have a certain name. static void filterByName(const StringSet<> &Names, - DWARFContext::cu_iterator_range CUs, raw_ostream &OS) { + DWARFContext::unit_iterator_range CUs, + raw_ostream &OS) { for (const auto &CU : CUs) for (const auto &Entry : CU->dies()) { DWARFDie Die = {CU.get(), &Entry}; - if (const char *NamePtr = Die.getName(DINameKind::ShortName)) { - std::string Name = - (IgnoreCase && !UseRegex) ? StringRef(NamePtr).lower() : NamePtr; - // Match regular expression. - if (UseRegex) - for (auto Pattern : Names.keys()) { - Regex RE(Pattern, IgnoreCase ? Regex::IgnoreCase : Regex::NoFlags); - std::string Error; - if (!RE.isValid(Error)) { - errs() << "error in regular expression: " << Error << "\n"; - exit(1); - } - if (RE.match(Name)) - Die.dump(OS, 0, getDumpOpts()); - } - // Match full text. - else if (Names.count(Name)) - Die.dump(OS, 0, getDumpOpts()); - } + if (const char *Name = Die.getName(DINameKind::ShortName)) + if (filterByName(Names, Die, Name, OS)) + continue; + if (const char *Name = Die.getName(DINameKind::LinkageName)) + filterByName(Names, Die, Name, OS); } - } static void getDies(DWARFContext &DICtx, const AppleAcceleratorTable &Accel, @@ -358,7 +371,7 @@ static void filterByAccelName(ArrayRef<std::string> Names, DWARFContext &DICtx, getDies(DICtx, DICtx.getAppleNamespaces(), Name, Dies); getDies(DICtx, DICtx.getDebugNames(), Name, Dies); } - llvm::sort(Dies.begin(), Dies.end()); + llvm::sort(Dies); Dies.erase(std::unique(Dies.begin(), Dies.end()), Dies.end()); for (DWARFDie Die : Dies) @@ -409,8 +422,8 @@ static bool dumpObjectFile(ObjectFile &Obj, DWARFContext &DICtx, Twine Filename, for (auto name : Name) Names.insert((IgnoreCase && !UseRegex) ? StringRef(name).lower() : name); - filterByName(Names, DICtx.compile_units(), OS); - filterByName(Names, DICtx.dwo_compile_units(), OS); + filterByName(Names, DICtx.normal_units(), OS); + filterByName(Names, DICtx.dwo_units(), OS); return true; } @@ -558,6 +571,14 @@ int main(int argc, char **argv) { return 0; } + // FIXME: Audit interactions between these two options and make them + // compatible. + if (Diff && Verbose) { + WithColor::error() << "incompatible arguments: specifying both -diff and " + "-verbose is currently not supported"; + return 0; + } + std::unique_ptr<ToolOutputFile> OutputFile; if (!OutputFilename.empty()) { std::error_code EC; @@ -611,7 +632,7 @@ int main(int argc, char **argv) { if (Verify) { // If we encountered errors during verify, exit with a non-zero exit status. - if (!std::all_of(Objects.begin(), Objects.end(), [&](std::string Object) { + if (!all_of(Objects, [&](std::string Object) { return handleFile(Object, verifyObjectFile, OS); })) exit(1); diff --git a/tools/llvm-dwp/llvm-dwp.cpp b/tools/llvm-dwp/llvm-dwp.cpp index d3380b5b57a1..8a0744cf1e01 100644 --- a/tools/llvm-dwp/llvm-dwp.cpp +++ b/tools/llvm-dwp/llvm-dwp.cpp @@ -43,21 +43,21 @@ using namespace llvm; using namespace llvm::object; -using namespace cl; -OptionCategory DwpCategory("Specific Options"); -static list<std::string> InputFiles(Positional, ZeroOrMore, - desc("<input files>"), cat(DwpCategory)); +cl::OptionCategory DwpCategory("Specific Options"); +static cl::list<std::string> InputFiles(cl::Positional, cl::ZeroOrMore, + cl::desc("<input files>"), + cl::cat(DwpCategory)); -static list<std::string> ExecFilenames( - "e", ZeroOrMore, - desc("Specify the executable/library files to get the list of *.dwo from"), - value_desc("filename"), cat(DwpCategory)); +static cl::list<std::string> ExecFilenames( + "e", cl::ZeroOrMore, + cl::desc("Specify the executable/library files to get the list of *.dwo from"), + cl::value_desc("filename"), cl::cat(DwpCategory)); -static opt<std::string> OutputFilename(Required, "o", - desc("Specify the output file."), - value_desc("filename"), - cat(DwpCategory)); +static cl::opt<std::string> OutputFilename(cl::Required, "o", + cl::desc("Specify the output file."), + cl::value_desc("filename"), + cl::cat(DwpCategory)); static void writeStringsAndOffsets(MCStreamer &Out, DWPStringPool &Strings, MCSection *StrOffsetSection, @@ -644,7 +644,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"); + cl::ParseCommandLineOptions(argc, argv, "merge split dwarf (.dwo) files\n"); llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); @@ -697,13 +697,21 @@ int main(int argc, char **argv) { // Create the output file. std::error_code EC; raw_fd_ostream OutFile(OutputFilename, EC, sys::fs::F_None); + Optional<buffer_ostream> BOS; + raw_pwrite_stream *OS; if (EC) return error(Twine(OutputFilename) + ": " + EC.message(), Context); + if (OutFile.supportsSeeking()) { + OS = &OutFile; + } else { + BOS.emplace(OutFile); + OS = BOS.getPointer(); + } MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); std::unique_ptr<MCStreamer> MS(TheTarget->createMCObjectStreamer( TheTriple, MC, std::unique_ptr<MCAsmBackend>(MAB), - MAB->createObjectWriter(OutFile), std::unique_ptr<MCCodeEmitter>(MCE), + MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(MCE), *MSTI, MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ false)); if (!MS) diff --git a/tools/llvm-elfabi/CMakeLists.txt b/tools/llvm-elfabi/CMakeLists.txt new file mode 100644 index 000000000000..bd3ec851887a --- /dev/null +++ b/tools/llvm-elfabi/CMakeLists.txt @@ -0,0 +1,11 @@ +set(LLVM_LINK_COMPONENTS + Object + Support + TextAPI + ) + +add_llvm_tool(llvm-elfabi + ELFObjHandler.cpp + ErrorCollector.cpp + llvm-elfabi.cpp + ) diff --git a/tools/llvm-elfabi/ELFObjHandler.cpp b/tools/llvm-elfabi/ELFObjHandler.cpp new file mode 100644 index 000000000000..412c299c1f7d --- /dev/null +++ b/tools/llvm-elfabi/ELFObjHandler.cpp @@ -0,0 +1,68 @@ +//===- ELFObjHandler.cpp --------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "ELFObjHandler.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/TextAPI/ELF/ELFStub.h" + +using llvm::MemoryBufferRef; +using llvm::object::ELFObjectFile; + +using namespace llvm; +using namespace llvm::object; +using namespace llvm::elfabi; +using namespace llvm::ELF; + +namespace llvm { +namespace elfabi { + +/// Returns a new ELFStub with all members populated from an ELFObjectFile. +/// @param ElfObj Source ELFObjectFile. +template <class ELFT> +Expected<std::unique_ptr<ELFStub>> +buildStub(const ELFObjectFile<ELFT> &ElfObj) { + std::unique_ptr<ELFStub> DestStub = make_unique<ELFStub>(); + const ELFFile<ELFT> *ElfFile = ElfObj.getELFFile(); + + DestStub->Arch = ElfFile->getHeader()->e_machine; + + // TODO: Populate SoName from .dynamic entries and linked string table. + // TODO: Populate NeededLibs from .dynamic entries and linked string table. + // TODO: Populate Symbols from .dynsym table and linked string table. + + return std::move(DestStub); +} + +Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf) { + Expected<std::unique_ptr<Binary>> BinOrErr = createBinary(Buf); + if (!BinOrErr) { + return BinOrErr.takeError(); + } + + Binary *Bin = BinOrErr->get(); + if (auto Obj = dyn_cast<ELFObjectFile<ELF32LE>>(Bin)) { + return buildStub(*Obj); + } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64LE>>(Bin)) { + return buildStub(*Obj); + } else if (auto Obj = dyn_cast<ELFObjectFile<ELF32BE>>(Bin)) { + return buildStub(*Obj); + } else if (auto Obj = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { + return buildStub(*Obj); + } + + return createStringError(errc::not_supported, "Unsupported binary format"); +} + +} // end namespace elfabi +} // end namespace llvm diff --git a/tools/llvm-elfabi/ELFObjHandler.h b/tools/llvm-elfabi/ELFObjHandler.h new file mode 100644 index 000000000000..496bad0f9cad --- /dev/null +++ b/tools/llvm-elfabi/ELFObjHandler.h @@ -0,0 +1,33 @@ +//===- ELFObjHandler.h ------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ +/// +/// This supports reading and writing of elf dynamic shared objects. +/// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H +#define LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H + +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/TextAPI/ELF/ELFStub.h" + +namespace llvm { + +class MemoryBuffer; + +namespace elfabi { + +/// Attempt to read a binary ELF file from a MemoryBuffer. +Expected<std::unique_ptr<ELFStub>> readELFFile(MemoryBufferRef Buf); + +} // end namespace elfabi +} // end namespace llvm + +#endif // LLVM_TOOLS_ELFABI_ELFOBJHANDLER_H diff --git a/tools/llvm-elfabi/ErrorCollector.cpp b/tools/llvm-elfabi/ErrorCollector.cpp new file mode 100644 index 000000000000..0d74979c9a30 --- /dev/null +++ b/tools/llvm-elfabi/ErrorCollector.cpp @@ -0,0 +1,70 @@ +//===- ErrorCollector.cpp -------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "ErrorCollector.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/WithColor.h" +#include <vector> + +using namespace llvm; +using namespace llvm::elfabi; + +void ErrorCollector::escalateToFatal() { + ErrorsAreFatal = true; +} + +void ErrorCollector::addError(Error &&Err, StringRef Tag) { + if (Err) { + Errors.push_back(std::move(Err)); + Tags.push_back(Tag.str()); + } +} + +Error ErrorCollector::makeError() { + // TODO: Make this return something (an AggregateError?) that gives more + // individual control over each error and which might be of interest. + Error JoinedErrors = Error::success(); + for (Error &E : Errors) { + JoinedErrors = joinErrors(std::move(JoinedErrors), std::move(E)); + } + Errors.clear(); + Tags.clear(); + return JoinedErrors; +} + +void ErrorCollector::log(raw_ostream &OS) { + OS << "Encountered multiple errors:\n"; + for (size_t i = 0; i < Errors.size(); ++i) { + WithColor::error(OS) << "(" << Tags[i] << ") " << Errors[i]; + if (i != Errors.size() - 1) + OS << "\n"; + } +} + +bool ErrorCollector::allErrorsHandled() const { + return Errors.empty(); +} + +ErrorCollector::~ErrorCollector() { + if (ErrorsAreFatal && !allErrorsHandled()) + fatalUnhandledError(); + + for (Error &E : Errors) { + consumeError(std::move(E)); + } +} + +LLVM_ATTRIBUTE_NORETURN void ErrorCollector::fatalUnhandledError() { + errs() << "Program aborted due to unhandled Error(s):\n"; + log(errs()); + errs() << "\n"; + abort(); +} diff --git a/tools/llvm-elfabi/ErrorCollector.h b/tools/llvm-elfabi/ErrorCollector.h new file mode 100644 index 000000000000..d54b3fbf4a3c --- /dev/null +++ b/tools/llvm-elfabi/ErrorCollector.h @@ -0,0 +1,75 @@ +//===- ErrorCollector.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ +/// +/// This class collects errors that should be reported or ignored in aggregate. +/// +/// Like llvm::Error, an ErrorCollector cannot be copied. Unlike llvm::Error, +/// an ErrorCollector may be destroyed if it was originally constructed to treat +/// errors as non-fatal. In this case, all Errors are consumed upon destruction. +/// An ErrorCollector may be initially constructed (or escalated) such that +/// errors are treated as fatal. This causes a crash if an attempt is made to +/// delete the ErrorCollector when some Errors have not been retrieved via +/// makeError(). +/// +//===-----------------------------------------------------------------------===/ + +#ifndef LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H +#define LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H + +#include "llvm/Support/Error.h" +#include <vector> + +namespace llvm { +namespace elfabi { + +class ErrorCollector { +public: + /// Upon destruction, an ErrorCollector will crash if UseFatalErrors=true and + /// there are remaining errors that haven't been fetched by makeError(). + ErrorCollector(bool UseFatalErrors = true) : ErrorsAreFatal(UseFatalErrors) {} + // Don't allow copying. + ErrorCollector(const ErrorCollector &Stub) = delete; + ErrorCollector &operator=(const ErrorCollector &Other) = delete; + ~ErrorCollector(); + + // TODO: Add move constructor and operator= when a testable situation arises. + + /// Returns a single error that contains messages for all stored Errors. + Error makeError(); + + /// Adds an error with a descriptive tag that helps with identification. + /// If the error is an Error::success(), it is checked and discarded. + void addError(Error &&E, StringRef Tag); + + /// This ensures an ErrorCollector will treat unhandled errors as fatal. + /// This function should be called if errors that usually can be ignored + /// are suddenly of concern (i.e. attempt multiple things that return Error, + /// but only care about the Errors if no attempt succeeds). + void escalateToFatal(); + +private: + /// Logs all errors to a raw_ostream. + void log(raw_ostream &OS); + + /// Returns true if all errors have been retrieved through makeError(), or + /// false if errors have been added since the last makeError() call. + bool allErrorsHandled() const; + + /// Dump output and crash. + LLVM_ATTRIBUTE_NORETURN void fatalUnhandledError(); + + bool ErrorsAreFatal; + std::vector<Error> Errors; + std::vector<std::string> Tags; +}; + +} // end namespace elfabi +} // end namespace llvm + +#endif // LLVM_TOOLS_ELFABI_ERRORCOLLECTOR_H diff --git a/tools/llvm-elfabi/LLVMBuild.txt b/tools/llvm-elfabi/LLVMBuild.txt new file mode 100644 index 000000000000..e4fdc9ae5a07 --- /dev/null +++ b/tools/llvm-elfabi/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-elfabi/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-elfabi +parent = Tools +required_libraries = Object Support TextAPI diff --git a/tools/llvm-elfabi/llvm-elfabi.cpp b/tools/llvm-elfabi/llvm-elfabi.cpp new file mode 100644 index 000000000000..4c15bc2eaf34 --- /dev/null +++ b/tools/llvm-elfabi/llvm-elfabi.cpp @@ -0,0 +1,143 @@ +//===- llvm-elfabi.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===-----------------------------------------------------------------------===/ + +#include "ELFObjHandler.h" +#include "ErrorCollector.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/WithColor.h" +#include "llvm/TextAPI/ELF/TBEHandler.h" +#include <string> + +namespace llvm { +namespace elfabi { + +enum class FileFormat { + TBE, + ELF +}; + +} // end namespace elfabi +} // end namespace llvm + +using namespace llvm; +using namespace llvm::elfabi; + +// Command line flags: +cl::opt<FileFormat> InputFileFormat( + cl::desc("Force input file format:"), + cl::values(clEnumValN(FileFormat::TBE, + "tbe", "Read `input` as text-based ELF stub"), + clEnumValN(FileFormat::ELF, + "elf", "Read `input` as ELF binary"))); +cl::opt<std::string> InputFilePath(cl::Positional, cl::desc("input"), + cl::Required); +cl::opt<std::string> + EmitTBE("emit-tbe", + cl::desc("Emit a text-based ELF stub (.tbe) from the input file"), + cl::value_desc("path")); +cl::opt<std::string> SOName( + "soname", + cl::desc("Manually set the DT_SONAME entry of any emitted files"), + cl::value_desc("name")); + +/// writeTBE() writes a Text-Based ELF stub to a file using the latest version +/// of the YAML parser. +static Error writeTBE(StringRef FilePath, ELFStub &Stub) { + std::error_code SysErr; + + // Open file for writing. + raw_fd_ostream Out(FilePath, SysErr); + if (SysErr) + return createStringError(SysErr, "Couldn't open `%s` for writing", + FilePath.data()); + // Write file. + Error YAMLErr = writeTBEToOutputStream(Out, Stub); + if (YAMLErr) + return YAMLErr; + + return Error::success(); +} + +/// readInputFile populates an ELFStub by attempting to read the +/// input file using both the TBE and binary ELF parsers. +static Expected<std::unique_ptr<ELFStub>> readInputFile(StringRef FilePath) { + // Read in file. + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrError = + MemoryBuffer::getFile(FilePath); + if (!BufOrError) { + return createStringError(BufOrError.getError(), "Could not open `%s`", + FilePath.data()); + } + + std::unique_ptr<MemoryBuffer> FileReadBuffer = std::move(*BufOrError); + ErrorCollector EC(/*UseFatalErrors=*/false); + + // First try to read as a binary (fails fast if not binary). + if (InputFileFormat.getNumOccurrences() == 0 || + InputFileFormat == FileFormat::ELF) { + Expected<std::unique_ptr<ELFStub>> StubFromELF = + readELFFile(FileReadBuffer->getMemBufferRef()); + if (StubFromELF) { + return std::move(*StubFromELF); + } + EC.addError(StubFromELF.takeError(), "BinaryRead"); + } + + // Fall back to reading as a tbe. + if (InputFileFormat.getNumOccurrences() == 0 || + InputFileFormat == FileFormat::TBE) { + Expected<std::unique_ptr<ELFStub>> StubFromTBE = + readTBEFromBuffer(FileReadBuffer->getBuffer()); + if (StubFromTBE) { + return std::move(*StubFromTBE); + } + EC.addError(StubFromTBE.takeError(), "YamlParse"); + } + + // If both readers fail, build a new error that includes all information. + EC.addError(createStringError(errc::not_supported, + "No file readers succeeded reading `%s` " + "(unsupported/malformed file?)", + FilePath.data()), + "ReadInputFile"); + EC.escalateToFatal(); + return EC.makeError(); +} + +int main(int argc, char *argv[]) { + // Parse arguments. + cl::ParseCommandLineOptions(argc, argv); + + Expected<std::unique_ptr<ELFStub>> StubOrErr = readInputFile(InputFilePath); + if (!StubOrErr) { + Error ReadError = StubOrErr.takeError(); + WithColor::error() << ReadError << "\n"; + exit(1); + } + + std::unique_ptr<ELFStub> TargetStub = std::move(StubOrErr.get()); + + // Write out .tbe file. + if (EmitTBE.getNumOccurrences() == 1) { + TargetStub->TbeVersion = TBEVersionCurrent; + if (SOName.getNumOccurrences() == 1) { + TargetStub->SoName = SOName; + } + Error TBEWriteError = writeTBE(EmitTBE, *TargetStub); + if (TBEWriteError) { + WithColor::error() << TBEWriteError << "\n"; + exit(1); + } + } +} diff --git a/tools/llvm-exegesis/CMakeLists.txt b/tools/llvm-exegesis/CMakeLists.txt index 65b1ada85299..a59e1b74024e 100644 --- a/tools/llvm-exegesis/CMakeLists.txt +++ b/tools/llvm-exegesis/CMakeLists.txt @@ -1,5 +1,5 @@ - set(LLVM_LINK_COMPONENTS + MCParser Support native ) diff --git a/tools/llvm-exegesis/lib/AArch64/Target.cpp b/tools/llvm-exegesis/lib/AArch64/Target.cpp index 88df44bd94c2..d6b6cff54727 100644 --- a/tools/llvm-exegesis/lib/AArch64/Target.cpp +++ b/tools/llvm-exegesis/lib/AArch64/Target.cpp @@ -9,35 +9,59 @@ #include "../Target.h" #include "../Latency.h" #include "AArch64.h" +#include "AArch64RegisterInfo.h" +namespace llvm { namespace exegesis { +static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) { + switch (RegBitWidth) { + case 32: + return llvm::AArch64::MOVi32imm; + case 64: + return llvm::AArch64::MOVi64imm; + } + llvm_unreachable("Invalid Value Width"); +} + +// Generates instruction to load an immediate value into a register. +static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth, + const llvm::APInt &Value) { + if (Value.getBitWidth() > RegBitWidth) + llvm_unreachable("Value must fit in the Register"); + return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth)) + .addReg(Reg) + .addImm(Value.getZExtValue()); +} + +#include "AArch64GenExegesis.inc" + namespace { -class AArch64LatencyBenchmarkRunner : public LatencyBenchmarkRunner { +class ExegesisAArch64Target : public ExegesisTarget { public: - AArch64LatencyBenchmarkRunner(const LLVMState &State) - : LatencyBenchmarkRunner(State) {} + ExegesisAArch64Target() : ExegesisTarget(AArch64CpuPfmCounters) {} private: - const char *getCounterName() const override { - // All AArch64 subtargets have CPU_CYCLES as the cycle counter name - return "CPU_CYCLES"; + std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override { + if (llvm::AArch64::GPR32RegClass.contains(Reg)) + return {loadImmediate(Reg, 32, Value)}; + if (llvm::AArch64::GPR64RegClass.contains(Reg)) + return {loadImmediate(Reg, 64, Value)}; + llvm::errs() << "setRegTo is not implemented, results will be unreliable\n"; + return {}; } -}; -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 @@ -52,3 +76,4 @@ void InitializeAArch64ExegesisTarget() { } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Analysis.cpp b/tools/llvm-exegesis/lib/Analysis.cpp index bb5118080967..0a91679fe1d1 100644 --- a/tools/llvm-exegesis/lib/Analysis.cpp +++ b/tools/llvm-exegesis/lib/Analysis.cpp @@ -12,13 +12,25 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/Support/FormatVariadic.h" +#include <limits> #include <unordered_set> #include <vector> +namespace llvm { namespace exegesis { static const char kCsvSep = ','; +static unsigned resolveSchedClassId(const llvm::MCSubtargetInfo &STI, + unsigned SchedClassId, + const llvm::MCInst &MCI) { + const auto &SM = STI.getSchedModel(); + while (SchedClassId && SM.getSchedClassDesc(SchedClassId)->isVariant()) + SchedClassId = + STI.resolveVariantSchedClass(SchedClassId, &MCI, SM.getProcessorID()); + return SchedClassId; +} + namespace { enum EscapeTag { kEscapeCsv, kEscapeHtml, kEscapeHtmlString }; @@ -84,7 +96,21 @@ writeClusterId(llvm::raw_ostream &OS, template <EscapeTag Tag> static void writeMeasurementValue(llvm::raw_ostream &OS, const double Value) { - writeEscaped<Tag>(OS, llvm::formatv("{0:F}", Value).str()); + // Given Value, if we wanted to serialize it to a string, + // how many base-10 digits will we need to store, max? + static constexpr auto MaxDigitCount = + std::numeric_limits<decltype(Value)>::max_digits10; + // Also, we will need a decimal separator. + static constexpr auto DecimalSeparatorLen = 1; // '.' e.g. + // So how long of a string will the serialization produce, max? + static constexpr auto SerializationLen = MaxDigitCount + DecimalSeparatorLen; + + // WARNING: when changing the format, also adjust the small-size estimate ^. + static constexpr StringLiteral SimpleFloatFormat = StringLiteral("{0:F}"); + + writeEscaped<Tag>( + OS, + llvm::formatv(SimpleFloatFormat.data(), Value).sstr<SerializationLen>()); } template <typename EscapeTag, EscapeTag Tag> @@ -103,13 +129,11 @@ void Analysis::writeSnippet(llvm::raw_ostream &OS, writeEscaped<Tag>(OS, "[error decoding asm snippet]"); return; } - Lines.emplace_back(); - std::string &Line = Lines.back(); - llvm::raw_string_ostream OSS(Line); + llvm::SmallString<128> InstPrinterStr; // FIXME: magic number. + llvm::raw_svector_ostream OSS(InstPrinterStr); InstPrinter_->printInst(&MI, OSS, "", *SubtargetInfo_); Bytes = Bytes.drop_front(MISize); - OSS.flush(); - Line = llvm::StringRef(Line).trim().str(); + Lines.emplace_back(llvm::StringRef(InstPrinterStr).trim()); } writeEscaped<Tag>(OS, llvm::join(Lines, Separator)); } @@ -126,20 +150,20 @@ void Analysis::printInstructionRowCsv(const size_t PointId, 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(); + const llvm::MCInst &MCI = Point.Key.Instructions[0]; + const unsigned SchedClassId = resolveSchedClassId( + *SubtargetInfo_, InstrInfo_->get(MCI.getOpcode()).getSchedClass(), MCI); + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) - const auto &SchedModel = SubtargetInfo_->getSchedModel(); const llvm::MCSchedClassDesc *const SCDesc = - SchedModel.getSchedClassDesc(SchedClassId); + SubtargetInfo_->getSchedModel().getSchedClassDesc(SchedClassId); writeEscaped<kEscapeCsv>(OS, SCDesc->Name); #else OS << SchedClassId; #endif for (const auto &Measurement : Point.Measurements) { OS << kCsvSep; - writeMeasurementValue<kEscapeCsv>(OS, Measurement.Value); + writeMeasurementValue<kEscapeCsv>(OS, Measurement.PerInstructionValue); } OS << "\n"; } @@ -193,21 +217,43 @@ Analysis::run<Analysis::PrintClusters>(llvm::raw_ostream &OS) const { return llvm::Error::success(); } -std::unordered_map<unsigned, std::vector<size_t>> +Analysis::ResolvedSchedClassAndPoints::ResolvedSchedClassAndPoints( + ResolvedSchedClass &&RSC) + : RSC(std::move(RSC)) {} + +std::vector<Analysis::ResolvedSchedClassAndPoints> Analysis::makePointsPerSchedClass() const { - std::unordered_map<unsigned, std::vector<size_t>> PointsPerSchedClass; + std::vector<ResolvedSchedClassAndPoints> Entries; + // Maps SchedClassIds to index in result. + std::unordered_map<unsigned, size_t> SchedClassIdToIndex; 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); + // FIXME: we should be using the tuple of classes for instructions in the + // snippet as key. + const llvm::MCInst &MCI = Point.Key.Instructions[0]; + unsigned SchedClassId = InstrInfo_->get(MCI.getOpcode()).getSchedClass(); + const bool WasVariant = SchedClassId && SubtargetInfo_->getSchedModel() + .getSchedClassDesc(SchedClassId) + ->isVariant(); + SchedClassId = resolveSchedClassId(*SubtargetInfo_, SchedClassId, MCI); + const auto IndexIt = SchedClassIdToIndex.find(SchedClassId); + if (IndexIt == SchedClassIdToIndex.end()) { + // Create a new entry. + SchedClassIdToIndex.emplace(SchedClassId, Entries.size()); + ResolvedSchedClassAndPoints Entry( + ResolvedSchedClass(*SubtargetInfo_, SchedClassId, WasVariant)); + Entry.PointIds.push_back(PointId); + Entries.push_back(std::move(Entry)); + } else { + // Append to the existing entry. + Entries[IndexIt->second].PointIds.push_back(PointId); + } } - return PointsPerSchedClass; + return Entries; } // Uops repeat the same opcode over again. Just show this opcode and show the @@ -239,8 +285,8 @@ writeLatencySnippetHtml(llvm::raw_ostream &OS, } void Analysis::printSchedClassClustersHtml( - const std::vector<SchedClassCluster> &Clusters, const SchedClass &SC, - llvm::raw_ostream &OS) const { + const std::vector<SchedClassCluster> &Clusters, + const ResolvedSchedClass &RSC, 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>"; @@ -248,16 +294,13 @@ void Analysis::printSchedClassClustersHtml( 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); + writeEscaped<kEscapeHtml>(OS, Measurement.Key); OS << "</th>"; } OS << "</tr>"; for (const SchedClassCluster &Cluster : Clusters) { OS << "<tr class=\"" - << (Cluster.measurementsMatch(*SubtargetInfo_, SC, Clustering_) + << (Cluster.measurementsMatch(*SubtargetInfo_, RSC, Clustering_) ? "good-cluster" : "bad-cluster") << "\"><td>"; @@ -372,12 +415,17 @@ getNonRedundantWriteProcRes(const llvm::MCSchedClassDesc &SCDesc, return Result; } -Analysis::SchedClass::SchedClass(const llvm::MCSchedClassDesc &SD, - const llvm::MCSubtargetInfo &STI) - : SCDesc(&SD), - NonRedundantWriteProcRes(getNonRedundantWriteProcRes(SD, STI)), +Analysis::ResolvedSchedClass::ResolvedSchedClass( + const llvm::MCSubtargetInfo &STI, unsigned ResolvedSchedClassId, + bool WasVariant) + : SchedClassId(ResolvedSchedClassId), SCDesc(STI.getSchedModel().getSchedClassDesc(ResolvedSchedClassId)), + WasVariant(WasVariant), + NonRedundantWriteProcRes(getNonRedundantWriteProcRes(*SCDesc, STI)), IdealizedProcResPressure(computeIdealizedProcResPressure( - STI.getSchedModel(), NonRedundantWriteProcRes)) {} + STI.getSchedModel(), NonRedundantWriteProcRes)) { + assert((SCDesc == nullptr || !SCDesc->isVariant()) && + "ResolvedSchedClass should never be variant"); +} void Analysis::SchedClassCluster::addPoint( size_t PointId, const InstructionBenchmarkClustering &Clustering) { @@ -393,8 +441,24 @@ void Analysis::SchedClassCluster::addPoint( assert(ClusterId == Clustering.getClusterIdForPoint(PointId)); } +// Returns a ProxResIdx by id or name. +static unsigned findProcResIdx(const llvm::MCSubtargetInfo &STI, + const llvm::StringRef NameOrId) { + // Interpret the key as an ProcResIdx. + unsigned ProcResIdx = 0; + if (llvm::to_integer(NameOrId, ProcResIdx, 10)) + return ProcResIdx; + // Interpret the key as a ProcRes name. + const auto &SchedModel = STI.getSchedModel(); + for (int I = 0, E = SchedModel.getNumProcResourceKinds(); I < E; ++I) { + if (NameOrId == SchedModel.getProcResource(I)->Name) + return I; + } + return 0; +} + bool Analysis::SchedClassCluster::measurementsMatch( - const llvm::MCSubtargetInfo &STI, const SchedClass &SC, + const llvm::MCSubtargetInfo &STI, const ResolvedSchedClass &RSC, const InstructionBenchmarkClustering &Clustering) const { const size_t NumMeasurements = Representative.size(); std::vector<BenchmarkMeasure> ClusterCenterPoint(NumMeasurements); @@ -410,34 +474,39 @@ bool Analysis::SchedClassCluster::measurementsMatch( return false; } // Find the latency. - SchedClassPoint[0].Value = 0.0; - for (unsigned I = 0; I < SC.SCDesc->NumWriteLatencyEntries; ++I) { + SchedClassPoint[0].PerInstructionValue = 0.0; + for (unsigned I = 0; I < RSC.SCDesc->NumWriteLatencyEntries; ++I) { const llvm::MCWriteLatencyEntry *const WLE = - STI.getWriteLatencyEntry(SC.SCDesc, I); - SchedClassPoint[0].Value = - std::max<double>(SchedClassPoint[0].Value, WLE->Cycles); + STI.getWriteLatencyEntry(RSC.SCDesc, I); + SchedClassPoint[0].PerInstructionValue = + std::max<double>(SchedClassPoint[0].PerInstructionValue, WLE->Cycles); } - ClusterCenterPoint[0].Value = Representative[0].avg(); + ClusterCenterPoint[0].PerInstructionValue = 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"; + const auto Key = Representative[I].key(); + uint16_t ProcResIdx = findProcResIdx(STI, Key); + if (ProcResIdx > 0) { + // Find the pressure on ProcResIdx `Key`. + const auto ProcResPressureIt = + std::find_if(RSC.IdealizedProcResPressure.begin(), + RSC.IdealizedProcResPressure.end(), + [ProcResIdx](const std::pair<uint16_t, float> &WPR) { + return WPR.first == ProcResIdx; + }); + SchedClassPoint[I].PerInstructionValue = + ProcResPressureIt == RSC.IdealizedProcResPressure.end() + ? 0.0 + : ProcResPressureIt->second; + } else if (Key == "NumMicroOps") { + SchedClassPoint[I].PerInstructionValue = RSC.SCDesc->NumMicroOps; + } else { + llvm::errs() << "expected `key` to be either a ProcResIdx or a ProcRes " + "name, got " + << 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(); + ClusterCenterPoint[I].PerInstructionValue = Representative[I].avg(); } } else { llvm::errs() << "unimplemented measurement matching for mode " << Mode @@ -447,26 +516,25 @@ bool Analysis::SchedClassCluster::measurementsMatch( return Clustering.isNeighbour(ClusterCenterPoint, SchedClassPoint); } -void Analysis::printSchedClassDescHtml(const SchedClass &SC, +void Analysis::printSchedClassDescHtml(const ResolvedSchedClass &RSC, llvm::raw_ostream &OS) const { OS << "<table class=\"sched-class-desc\">"; - OS << "<tr><th>Valid</th><th>Variant</th><th>uOps</th><th>Latency</" + OS << "<tr><th>Valid</th><th>Variant</th><th>NumMicroOps</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()) { + if (RSC.SCDesc->isValid()) { const auto &SM = SubtargetInfo_->getSchedModel(); OS << "<tr><td>✔</td>"; - OS << "<td>" << (SC.SCDesc->isVariant() ? "✔" : "✕") - << "</td>"; - OS << "<td>" << SC.SCDesc->NumMicroOps << "</td>"; + OS << "<td>" << (RSC.WasVariant ? "✔" : "✕") << "</td>"; + OS << "<td>" << RSC.SCDesc->NumMicroOps << "</td>"; // Latencies. OS << "<td><ul>"; - for (int I = 0, E = SC.SCDesc->NumWriteLatencyEntries; I < E; ++I) { + for (int I = 0, E = RSC.SCDesc->NumWriteLatencyEntries; I < E; ++I) { const auto *const Entry = - SubtargetInfo_->getWriteLatencyEntry(SC.SCDesc, I); + SubtargetInfo_->getWriteLatencyEntry(RSC.SCDesc, I); OS << "<li>" << Entry->Cycles; - if (SC.SCDesc->NumWriteLatencyEntries > 1) { + if (RSC.SCDesc->NumWriteLatencyEntries > 1) { // Dismabiguate if more than 1 latency. OS << " (WriteResourceID " << Entry->WriteResourceID << ")"; } @@ -475,7 +543,7 @@ void Analysis::printSchedClassDescHtml(const SchedClass &SC, OS << "</ul></td>"; // WriteProcRes. OS << "<td><ul>"; - for (const auto &WPR : SC.NonRedundantWriteProcRes) { + for (const auto &WPR : RSC.NonRedundantWriteProcRes) { OS << "<li><span class=\"mono\">"; writeEscaped<kEscapeHtml>(OS, SM.getProcResource(WPR.ProcResourceIdx)->Name); @@ -484,7 +552,7 @@ void Analysis::printSchedClassDescHtml(const SchedClass &SC, OS << "</ul></td>"; // Idealized port pressure. OS << "<td><ul>"; - for (const auto &Pressure : SC.IdealizedProcResPressure) { + for (const auto &Pressure : RSC.IdealizedProcResPressure) { OS << "<li><span class=\"mono\">"; writeEscaped<kEscapeHtml>(OS, SubtargetInfo_->getSchedModel() .getProcResource(Pressure.first) @@ -580,19 +648,12 @@ llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>( 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) + for (const auto &RSCAndPoints : makePointsPerSchedClass()) { + if (!RSCAndPoints.RSC.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) { + for (const size_t PointId : RSCAndPoints.PointIds) { const auto &ClusterId = Clustering_.getClusterIdForPoint(PointId); if (!ClusterId.isValid()) continue; // Ignore noise and errors. FIXME: take noise into account ? @@ -610,25 +671,25 @@ llvm::Error Analysis::run<Analysis::PrintSchedClassInconsistencies>( // 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_); - })) + if (llvm::all_of(SchedClassClusters, + [this, &RSCAndPoints](const SchedClassCluster &C) { + return C.measurementsMatch( + *SubtargetInfo_, RSCAndPoints.RSC, 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); + writeEscaped<kEscapeHtml>(OS, RSCAndPoints.RSC.SCDesc->Name); #else - OS << SchedClassId; + OS << RSCAndPoints.RSC.SchedClassId; #endif OS << "</span> contains instructions whose performance characteristics do" " not match that of LLVM:</p>"; - printSchedClassClustersHtml(SchedClassClusters, SC, OS); + printSchedClassClustersHtml(SchedClassClusters, RSCAndPoints.RSC, OS); OS << "<p>llvm SchedModel data:</p>"; - printSchedClassDescHtml(SC, OS); + printSchedClassDescHtml(RSCAndPoints.RSC, OS); OS << "</div>"; } @@ -671,10 +732,9 @@ void distributePressure(float RemainingPressure, 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]; - }); + llvm::sort(Subunits, [&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]]; @@ -721,11 +781,10 @@ std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure( 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; - }); + llvm::sort(WPRS, [](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 = @@ -751,3 +810,4 @@ std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure( } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Analysis.h b/tools/llvm-exegesis/lib/Analysis.h index 3c33cafc5fb9..9ee1493f4e05 100644 --- a/tools/llvm-exegesis/lib/Analysis.h +++ b/tools/llvm-exegesis/lib/Analysis.h @@ -30,6 +30,7 @@ #include <string> #include <unordered_map> +namespace llvm { namespace exegesis { // A helper class to analyze benchmark results for a target. @@ -49,11 +50,13 @@ private: using ClusterId = InstructionBenchmarkClustering::ClusterId; // An llvm::MCSchedClassDesc augmented with some additional data. - struct SchedClass { - SchedClass(const llvm::MCSchedClassDesc &SD, - const llvm::MCSubtargetInfo &STI); + struct ResolvedSchedClass { + ResolvedSchedClass(const llvm::MCSubtargetInfo &STI, + unsigned ResolvedSchedClassId, bool WasVariant); + const unsigned SchedClassId; const llvm::MCSchedClassDesc *const SCDesc; + const bool WasVariant; // Whether the original class was variant. const llvm::SmallVector<llvm::MCWriteProcResEntry, 8> NonRedundantWriteProcRes; const std::vector<std::pair<uint16_t, float>> IdealizedProcResPressure; @@ -69,13 +72,14 @@ private: const std::vector<size_t> &getPointIds() const { return PointIds; } // Return the cluster centroid. - const std::vector<BenchmarkMeasureStats> &getRepresentative() const { + const std::vector<PerInstructionStats> &getRepresentative() const { return Representative; } // Returns true if the cluster representative measurements match that of SC. bool - measurementsMatch(const llvm::MCSubtargetInfo &STI, const SchedClass &SC, + measurementsMatch(const llvm::MCSubtargetInfo &STI, + const ResolvedSchedClass &SC, const InstructionBenchmarkClustering &Clustering) const; void addPoint(size_t PointId, @@ -85,22 +89,29 @@ private: InstructionBenchmarkClustering::ClusterId ClusterId; std::vector<size_t> PointIds; // Measurement stats for the points in the SchedClassCluster. - std::vector<BenchmarkMeasureStats> Representative; + std::vector<PerInstructionStats> Representative; }; void printInstructionRowCsv(size_t PointId, llvm::raw_ostream &OS) const; void printSchedClassClustersHtml(const std::vector<SchedClassCluster> &Clusters, - const SchedClass &SC, + const ResolvedSchedClass &SC, llvm::raw_ostream &OS) const; - void printSchedClassDescHtml(const SchedClass &SC, + void printSchedClassDescHtml(const ResolvedSchedClass &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; + // A pair of (Sched Class, indices of points that belong to the sched + // class). + struct ResolvedSchedClassAndPoints { + explicit ResolvedSchedClassAndPoints(ResolvedSchedClass &&RSC); + + ResolvedSchedClass RSC; + std::vector<size_t> PointIds; + }; + + // Builds a list of ResolvedSchedClassAndPoints. + std::vector<ResolvedSchedClassAndPoints> makePointsPerSchedClass() const; template <typename EscapeTag, EscapeTag Tag> void writeSnippet(llvm::raw_ostream &OS, llvm::ArrayRef<uint8_t> Bytes, @@ -125,5 +136,6 @@ std::vector<std::pair<uint16_t, float>> computeIdealizedProcResPressure( llvm::SmallVector<llvm::MCWriteProcResEntry, 8> WPRS); } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H diff --git a/tools/llvm-exegesis/lib/Assembler.cpp b/tools/llvm-exegesis/lib/Assembler.cpp index d2be7f4829a7..2e3712ce7dc7 100644 --- a/tools/llvm-exegesis/lib/Assembler.cpp +++ b/tools/llvm-exegesis/lib/Assembler.cpp @@ -23,23 +23,25 @@ #include "llvm/MC/MCInstrInfo.h" #include "llvm/Support/MemoryBuffer.h" +namespace llvm { 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; +generateSnippetSetupCode(const ExegesisTarget &ET, + const llvm::MCSubtargetInfo *const MSI, + llvm::ArrayRef<RegisterValue> RegisterInitialValues, + bool &IsSnippetSetupComplete) { + IsSnippetSetupComplete = true; std::vector<llvm::MCInst> Result; - for (const unsigned Reg : RegsToDef) { + for (const RegisterValue &RV : RegisterInitialValues) { // 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()); + const auto SetRegisterCode = ET.setRegTo(*MSI, RV.Register, RV.Value); + if (SetRegisterCode.empty()) + IsSnippetSetupComplete = false; + Result.insert(Result.end(), SetRegisterCode.begin(), SetRegisterCode.end()); } return Result; } @@ -66,12 +68,16 @@ static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName, return false; } -// Creates a void MachineFunction with no argument. +// Creates a void(int8*) MachineFunction. static llvm::MachineFunction & -createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module, - llvm::MachineModuleInfo *MMI) { +createVoidVoidPtrMachineFunction(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::Type *const MemParamType = llvm::PointerType::get( + llvm::Type::getInt8Ty(Module->getContext()), 0 /*default address space*/); + llvm::FunctionType *FunctionType = + llvm::FunctionType::get(ReturnType, {MemParamType}, 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 @@ -81,9 +87,12 @@ createVoidVoidMachineFunction(llvm::StringRef FunctionID, llvm::Module *Module, } static void fillMachineFunction(llvm::MachineFunction &MF, + llvm::ArrayRef<unsigned> LiveIns, llvm::ArrayRef<llvm::MCInst> Instructions) { llvm::MachineBasicBlock *MBB = MF.CreateMachineBasicBlock(); MF.push_back(MBB); + for (const unsigned Reg : LiveIns) + MBB->addLiveIn(Reg); const llvm::MCInstrInfo *MCII = MF.getTarget().getMCInstrInfo(); llvm::DebugLoc DL; for (const llvm::MCInst &Inst : Instructions) { @@ -102,6 +111,8 @@ static void fillMachineFunction(llvm::MachineFunction &MF, Builder.addReg(Op.getReg(), Flags); } else if (Op.isImm()) { Builder.addImm(Op.getImm()); + } else if (!Op.isValid()) { + llvm_unreachable("Operand is not set"); } else { llvm_unreachable("Not yet implemented"); } @@ -114,7 +125,7 @@ static void fillMachineFunction(llvm::MachineFunction &MF, } else { llvm::MachineIRBuilder MIB(MF); MIB.setMBB(*MBB); - MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, 0); + MF.getSubtarget().getCallLowering()->lowerReturn(MIB, nullptr, {}); } } @@ -131,17 +142,20 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) { llvm::make_unique<llvm::LLVMContext>(); std::unique_ptr<llvm::Module> Module = createModule(Context, TM.createDataLayout()); + // TODO: This only works for targets implementing LLVMTargetMachine. + const LLVMTargetMachine &LLVMTM = static_cast<const LLVMTargetMachine&>(TM); std::unique_ptr<llvm::MachineModuleInfo> MMI = - llvm::make_unique<llvm::MachineModuleInfo>(&TM); + llvm::make_unique<llvm::MachineModuleInfo>(&LLVMTM); llvm::MachineFunction &MF = - createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get()); + createVoidVoidPtrMachineFunction(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<unsigned> LiveIns, + llvm::ArrayRef<RegisterValue> RegisterInitialValues, llvm::ArrayRef<llvm::MCInst> Instructions, llvm::raw_pwrite_stream &AsmStream) { std::unique_ptr<llvm::LLVMContext> Context = @@ -151,21 +165,24 @@ void assembleToStream(const ExegesisTarget &ET, std::unique_ptr<llvm::MachineModuleInfo> MMI = llvm::make_unique<llvm::MachineModuleInfo>(TM.get()); llvm::MachineFunction &MF = - createVoidVoidMachineFunction(FunctionID, Module.get(), MMI.get()); + createVoidVoidPtrMachineFunction(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; - } + + for (const unsigned Reg : LiveIns) + MF.getRegInfo().addLiveIn(Reg); + + bool IsSnippetSetupComplete; + std::vector<llvm::MCInst> Code = + generateSnippetSetupCode(ET, TM->getMCSubtargetInfo(), + RegisterInitialValues, IsSnippetSetupComplete); + + Code.insert(Code.end(), Instructions.begin(), Instructions.end()); + // 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) @@ -176,7 +193,7 @@ void assembleToStream(const ExegesisTarget &ET, MF.getRegInfo().freezeReservedRegs(MF); // Fill the MachineFunction from the instructions. - fillMachineFunction(MF, Instructions); + fillMachineFunction(MF, LiveIns, Code); // We create the pass manager, run the passes to populate AsmBuffer. llvm::MCContext &MCContext = MMI->getContext(); @@ -281,3 +298,4 @@ ExecutableFunction::ExecutableFunction( } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Assembler.h b/tools/llvm-exegesis/lib/Assembler.h index e9a65cbbe369..ee6bc86f3788 100644 --- a/tools/llvm-exegesis/lib/Assembler.h +++ b/tools/llvm-exegesis/lib/Assembler.h @@ -18,6 +18,7 @@ #include <memory> +#include "BenchmarkCode.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/BitVector.h" #include "llvm/CodeGen/MachineFunction.h" @@ -31,6 +32,7 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetMachine.h" +namespace llvm { namespace exegesis { class ExegesisTarget; @@ -39,13 +41,14 @@ class ExegesisTarget; // convention and target machine). llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); -// Creates a temporary `void foo()` function containing the provided +// Creates a temporary `void foo(char*)` 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<unsigned> LiveIns, + llvm::ArrayRef<RegisterValue> RegisterInitialValues, llvm::ArrayRef<llvm::MCInst> Instructions, llvm::raw_pwrite_stream &AsmStream); @@ -59,7 +62,7 @@ getObjectFromBuffer(llvm::StringRef Buffer); llvm::object::OwningBinary<llvm::object::ObjectFile> getObjectFromFile(llvm::StringRef Filename); -// Consumes an ObjectFile containing a `void foo()` function and make it +// Consumes an ObjectFile containing a `void foo(char*)` function and make it // executable. struct ExecutableFunction { explicit ExecutableFunction( @@ -70,7 +73,9 @@ struct ExecutableFunction { llvm::StringRef getFunctionBytes() const { return FunctionBytes; } // Executes the function. - void operator()() const { ((void (*)())(intptr_t)FunctionBytes.data())(); } + void operator()(char *Memory) const { + ((void (*)(char *))(intptr_t)FunctionBytes.data())(Memory); + } std::unique_ptr<llvm::LLVMContext> Context; std::unique_ptr<llvm::ExecutionEngine> ExecEngine; @@ -78,5 +83,6 @@ struct ExecutableFunction { }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_ASSEMBLER_H diff --git a/tools/llvm-exegesis/lib/BenchmarkCode.h b/tools/llvm-exegesis/lib/BenchmarkCode.h new file mode 100644 index 000000000000..38bea2519a64 --- /dev/null +++ b/tools/llvm-exegesis/lib/BenchmarkCode.h @@ -0,0 +1,41 @@ +//===-- BenchmarkCode.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_LLVM_EXEGESIS_BENCHMARKCODE_H +#define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H + +#include "RegisterValue.h" +#include "llvm/MC/MCInst.h" +#include <string> +#include <vector> + +namespace llvm { +namespace exegesis { + +// A collection of instructions that are to be assembled, executed and measured. +struct BenchmarkCode { + // The sequence of instructions that are to be repeated. + std::vector<llvm::MCInst> Instructions; + + // Before the code is executed some instructions are added to setup the + // registers initial values. + std::vector<RegisterValue> RegisterInitialValues; + + // We also need to provide the registers that are live on entry for the + // assembler to generate proper prologue/epilogue. + std::vector<unsigned> LiveIns; + + // Informations about how this configuration was built. + std::string Info; +}; + +} // namespace exegesis +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKCODE_H diff --git a/tools/llvm-exegesis/lib/BenchmarkResult.cpp b/tools/llvm-exegesis/lib/BenchmarkResult.cpp index 33ad65075d92..0507ae8959d0 100644 --- a/tools/llvm-exegesis/lib/BenchmarkResult.cpp +++ b/tools/llvm-exegesis/lib/BenchmarkResult.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// #include "BenchmarkResult.h" +#include "BenchmarkRunner.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/bit.h" #include "llvm/ADT/StringRef.h" #include "llvm/ObjectYAML/YAML.h" #include "llvm/Support/FileOutputBuffer.h" @@ -16,82 +18,146 @@ #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 constexpr const char kIntegerPrefix[] = "i_0x"; +static constexpr const char kDoublePrefix[] = "f_"; +static constexpr const char kInvalidOperand[] = "INVALID"; + +namespace llvm { + +namespace { + +// A mutable struct holding an LLVMState that can be passed through the +// serialization process to encode/decode registers and instructions. +struct YamlContext { + YamlContext(const exegesis::LLVMState &State) + : State(&State), ErrorStream(LastError) {} + + void serializeMCInst(const llvm::MCInst &MCInst, llvm::raw_ostream &OS) { + OS << getInstrName(MCInst.getOpcode()); + for (const auto &Op : MCInst) { + OS << ' '; + serializeMCOperand(Op, OS); + } } -} -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); + void deserializeMCInst(llvm::StringRef String, llvm::MCInst &Value) { + llvm::SmallVector<llvm::StringRef, 8> Pieces; + String.split(Pieces, " ", /* MaxSplit */ -1, /* KeepEmpty */ false); + if (Pieces.empty()) { + ErrorStream << "Unknown Instruction: '" << String << "'"; + return; + } + bool ProcessOpcode = true; + for (llvm::StringRef Piece : Pieces) { + if (ProcessOpcode) + Value.setOpcode(getInstrOpcode(Piece)); + else + Value.addOperand(deserializeMCOperand(Piece)); + ProcessOpcode = false; + } + } + + std::string &getLastError() { return ErrorStream.str(); } + + llvm::raw_string_ostream &getErrorStream() { return ErrorStream; } + + llvm::StringRef getRegName(unsigned RegNo) { + const llvm::StringRef RegName = State->getRegInfo().getName(RegNo); + if (RegName.empty()) + ErrorStream << "No register with enum value" << RegNo; + return RegName; } -} -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 {}; -} + unsigned getRegNo(llvm::StringRef RegName) { + const llvm::MCRegisterInfo &RegInfo = State->getRegInfo(); + for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I) + if (RegInfo.getName(I) == RegName) + return I; + ErrorStream << "No register with name " << RegName; + return 0; + } -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"; +private: + void serializeIntegerOperand(llvm::raw_ostream &OS, int64_t Value) { + OS << kIntegerPrefix; + OS.write_hex(llvm::bit_cast<uint64_t>(Value)); + } + + bool tryDeserializeIntegerOperand(llvm::StringRef String, int64_t &Value) { + if (!String.consume_front(kIntegerPrefix)) + return false; + return !String.consumeInteger(16, Value); + } + + void serializeFPOperand(llvm::raw_ostream &OS, double Value) { + OS << kDoublePrefix << llvm::format("%la", Value); + } + + bool tryDeserializeFPOperand(llvm::StringRef String, double &Value) { + if (!String.consume_front(kDoublePrefix)) + return false; + char *EndPointer = nullptr; + Value = strtod(String.begin(), &EndPointer); + return EndPointer == String.end(); + } + + void serializeMCOperand(const llvm::MCOperand &MCOperand, + llvm::raw_ostream &OS) { + if (MCOperand.isReg()) { + OS << getRegName(MCOperand.getReg()); + } else if (MCOperand.isImm()) { + serializeIntegerOperand(OS, MCOperand.getImm()); + } else if (MCOperand.isFPImm()) { + serializeFPOperand(OS, MCOperand.getFPImm()); } else { - Value.addOperand(deserialize(Context, Piece)); + OS << kInvalidOperand; } } - 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); -} + llvm::MCOperand deserializeMCOperand(llvm::StringRef String) { + assert(!String.empty()); + int64_t IntValue = 0; + double DoubleValue = 0; + if (tryDeserializeIntegerOperand(String, IntValue)) + return llvm::MCOperand::createImm(IntValue); + if (tryDeserializeFPOperand(String, DoubleValue)) + return llvm::MCOperand::createFPImm(DoubleValue); + if (unsigned RegNo = getRegNo(String)) + return llvm::MCOperand::createReg(RegNo); + if (String != kInvalidOperand) + ErrorStream << "Unknown Operand: '" << String << "'"; + return {}; + } -static const exegesis::BenchmarkResultContext &getTypedContext(void *Ctx) { - assert(Ctx); - return *static_cast<const exegesis::BenchmarkResultContext *>(Ctx); -} + llvm::StringRef getInstrName(unsigned InstrNo) { + const llvm::StringRef InstrName = State->getInstrInfo().getName(InstrNo); + if (InstrName.empty()) + ErrorStream << "No opcode with enum value" << InstrNo; + return InstrName; + } + + unsigned getInstrOpcode(llvm::StringRef InstrName) { + const llvm::MCInstrInfo &InstrInfo = State->getInstrInfo(); + for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I) + if (InstrInfo.getName(I) == InstrName) + return I; + ErrorStream << "No opcode with name " << InstrName; + return 0; + } + + const llvm::exegesis::LLVMState *State; + std::string LastError; + llvm::raw_string_ostream ErrorStream; +}; +} // namespace // Defining YAML traits for IO. -namespace llvm { namespace yaml { +static YamlContext &getTypedContext(void *Ctx) { + return *reinterpret_cast<YamlContext *>(Ctx); +} + // std::vector<llvm::MCInst> will be rendered as a list. template <> struct SequenceElementTraits<llvm::MCInst> { static const bool flow = false; @@ -101,13 +167,17 @@ template <> struct ScalarTraits<llvm::MCInst> { static void output(const llvm::MCInst &Value, void *Ctx, llvm::raw_ostream &Out) { - serialize(getTypedContext(Ctx), Value, Out); + getTypedContext(Ctx).serializeMCInst(Value, Out); } static StringRef input(StringRef Scalar, void *Ctx, llvm::MCInst &Value) { - return deserialize(getTypedContext(Ctx), Scalar, Value); + YamlContext &Context = getTypedContext(Ctx); + Context.deserializeMCInst(Scalar, Value); + return Context.getLastError(); } + // By default strings are quoted only when necessary. + // We force the use of single quotes for uniformity. static QuotingType mustQuote(StringRef) { return QuotingType::Single; } static const bool flow = true; @@ -123,8 +193,12 @@ template <> struct SequenceElementTraits<exegesis::BenchmarkMeasure> { 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); + if (!Io.outputting()) { + // For backward compatibility, interpret debug_string as a key. + Io.mapOptional("debug_string", Obj.Key); + } + Io.mapRequired("value", Obj.PerInstructionValue); + Io.mapOptional("per_snippet_value", Obj.PerSnippetValue); } static const bool flow = true; }; @@ -139,16 +213,58 @@ struct ScalarEnumerationTraits<exegesis::InstructionBenchmark::ModeE> { } }; -template <> struct MappingTraits<exegesis::InstructionBenchmarkKey> { - static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj) { +// std::vector<exegesis::RegisterValue> will be rendered as a list. +template <> struct SequenceElementTraits<exegesis::RegisterValue> { + static const bool flow = false; +}; + +template <> struct ScalarTraits<exegesis::RegisterValue> { + static constexpr const unsigned kRadix = 16; + static constexpr const bool kSigned = false; + + static void output(const exegesis::RegisterValue &RV, void *Ctx, + llvm::raw_ostream &Out) { + YamlContext &Context = getTypedContext(Ctx); + Out << Context.getRegName(RV.Register) << "=0x" + << RV.Value.toString(kRadix, kSigned); + } + + static StringRef input(StringRef String, void *Ctx, + exegesis::RegisterValue &RV) { + llvm::SmallVector<llvm::StringRef, 2> Pieces; + String.split(Pieces, "=0x", /* MaxSplit */ -1, + /* KeepEmpty */ false); + YamlContext &Context = getTypedContext(Ctx); + if (Pieces.size() == 2) { + RV.Register = Context.getRegNo(Pieces[0]); + const unsigned BitsNeeded = llvm::APInt::getBitsNeeded(Pieces[1], kRadix); + RV.Value = llvm::APInt(BitsNeeded, Pieces[1], kRadix); + } else { + Context.getErrorStream() + << "Unknown initial register value: '" << String << "'"; + } + return Context.getLastError(); + } + + static QuotingType mustQuote(StringRef) { return QuotingType::Single; } + + static const bool flow = true; +}; + +template <> +struct MappingContextTraits<exegesis::InstructionBenchmarkKey, YamlContext> { + static void mapping(IO &Io, exegesis::InstructionBenchmarkKey &Obj, + YamlContext &Context) { + Io.setContext(&Context); Io.mapRequired("instructions", Obj.Instructions); Io.mapOptional("config", Obj.Config); + Io.mapRequired("register_initial_values", Obj.RegisterInitialValues); } }; -template <> struct MappingTraits<exegesis::InstructionBenchmark> { - class NormalizedBinary { - public: +template <> +struct MappingContextTraits<exegesis::InstructionBenchmark, YamlContext> { + struct NormalizedBinary { NormalizedBinary(IO &io) {} NormalizedBinary(IO &, std::vector<uint8_t> &Data) : Binary(Data) {} std::vector<uint8_t> denormalize(IO &) { @@ -164,9 +280,10 @@ template <> struct MappingTraits<exegesis::InstructionBenchmark> { BinaryRef Binary; }; - static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj) { + static void mapping(IO &Io, exegesis::InstructionBenchmark &Obj, + YamlContext &Context) { Io.mapRequired("mode", Obj.Mode); - Io.mapRequired("key", Obj.Key); + Io.mapRequired("key", Obj.Key, Context); Io.mapRequired("cpu_name", Obj.CpuName); Io.mapRequired("llvm_triple", Obj.LLVMTriple); Io.mapRequired("num_repetitions", Obj.NumRepetitions); @@ -181,101 +298,71 @@ template <> struct MappingTraits<exegesis::InstructionBenchmark> { }; } // 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) { +llvm::Expected<InstructionBenchmark> +InstructionBenchmark::readYaml(const LLVMState &State, + 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; + llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get()); + YamlContext Context(State); + InstructionBenchmark Benchmark; + if (Yin.setCurrentDocument()) + llvm::yaml::yamlize(Yin, Benchmark, /*unused*/ true, Context); + if (!Context.getLastError().empty()) + return llvm::make_error<BenchmarkFailure>(Context.getLastError()); 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, +InstructionBenchmark::readYamls(const LLVMState &State, llvm::StringRef Filename) { - return readYamlCommon<std::vector<InstructionBenchmark>>(Context, Filename); + if (auto ExpectedMemoryBuffer = + llvm::errorOrToExpected(llvm::MemoryBuffer::getFile(Filename))) { + llvm::yaml::Input Yin(*ExpectedMemoryBuffer.get()); + YamlContext Context(State); + std::vector<InstructionBenchmark> Benchmarks; + while (Yin.setCurrentDocument()) { + Benchmarks.emplace_back(); + yamlize(Yin, Benchmarks.back(), /*unused*/ true, Context); + if (Yin.error()) + return llvm::errorCodeToError(Yin.error()); + if (!Context.getLastError().empty()) + return llvm::make_error<BenchmarkFailure>(Context.getLastError()); + Yin.nextDocument(); + } + return Benchmarks; + } else { + return ExpectedMemoryBuffer.takeError(); + } } -void InstructionBenchmark::writeYamlTo(const BenchmarkResultContext &Context, +void InstructionBenchmark::writeYamlTo(const LLVMState &State, llvm::raw_ostream &OS) { - llvm::yaml::Output Yout(OS, getUntypedContext(Context)); - Yout << *this; + llvm::yaml::Output Yout(OS, nullptr /*Ctx*/, 200 /*WrapColumn*/); + YamlContext Context(State); + Yout.beginDocuments(); + llvm::yaml::yamlize(Yout, *this, /*unused*/ true, Context); + Yout.endDocuments(); } -void InstructionBenchmark::readYamlFrom(const BenchmarkResultContext &Context, +void InstructionBenchmark::readYamlFrom(const LLVMState &State, llvm::StringRef InputContent) { - llvm::yaml::Input Yin(InputContent, getUntypedContext(Context)); - Yin >> *this; + llvm::yaml::Input Yin(InputContent); + YamlContext Context(State); + if (Yin.setCurrentDocument()) + llvm::yaml::yamlize(Yin, *this, /*unused*/ true, Context); } -llvm::Error -InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context, - const llvm::StringRef Filename) { +llvm::Error InstructionBenchmark::writeYaml(const LLVMState &State, + const llvm::StringRef Filename) { if (Filename == "-") { - writeYamlTo(Context, llvm::outs()); + writeYamlTo(State, llvm::outs()); } else { int ResultFD = 0; if (auto E = llvm::errorCodeToError( @@ -284,19 +371,20 @@ InstructionBenchmark::writeYaml(const BenchmarkResultContext &Context, return E; } llvm::raw_fd_ostream Ostr(ResultFD, true /*shouldClose*/); - writeYamlTo(Context, Ostr); + writeYamlTo(State, Ostr); } return llvm::Error::success(); } -void BenchmarkMeasureStats::push(const BenchmarkMeasure &BM) { +void PerInstructionStats::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); + SumValues += BM.PerInstructionValue; + MaxValue = std::max(MaxValue, BM.PerInstructionValue); + MinValue = std::min(MinValue, BM.PerInstructionValue); } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/BenchmarkResult.h b/tools/llvm-exegesis/lib/BenchmarkResult.h index c9e77ca49223..773a2e50abc4 100644 --- a/tools/llvm-exegesis/lib/BenchmarkResult.h +++ b/tools/llvm-exegesis/lib/BenchmarkResult.h @@ -16,6 +16,8 @@ #ifndef LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H #define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H +#include "BenchmarkCode.h" +#include "LlvmState.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" @@ -26,22 +28,31 @@ #include <unordered_map> #include <vector> +namespace llvm { namespace exegesis { -struct BenchmarkResultContext; // Forward declaration. - struct InstructionBenchmarkKey { // The LLVM opcode name. std::vector<llvm::MCInst> Instructions; + // The initial values of the registers. + std::vector<RegisterValue> RegisterInitialValues; // An opaque configuration, that can be used to separate several benchmarks of // the same instruction under different configurations. std::string Config; }; struct BenchmarkMeasure { + // A helper to create an unscaled BenchmarkMeasure. + static BenchmarkMeasure Create(std::string Key, double Value) { + return {Key, Value, Value}; + } std::string Key; - double Value; - std::string DebugString; + // This is the per-instruction value, i.e. measured quantity scaled per + // instruction. + double PerInstructionValue; + // This is the per-snippet value, i.e. measured quantity for one repetition of + // the whole snippet. + double PerSnippetValue; }; // The result of an instruction benchmark. @@ -62,26 +73,24 @@ struct InstructionBenchmark { // Read functions. static llvm::Expected<InstructionBenchmark> - readYaml(const BenchmarkResultContext &Context, llvm::StringRef Filename); + readYaml(const LLVMState &State, llvm::StringRef Filename); static llvm::Expected<std::vector<InstructionBenchmark>> - readYamls(const BenchmarkResultContext &Context, llvm::StringRef Filename); + readYamls(const LLVMState &State, llvm::StringRef Filename); - void readYamlFrom(const BenchmarkResultContext &Context, - llvm::StringRef InputContent); + void readYamlFrom(const LLVMState &State, llvm::StringRef InputContent); // Write functions, non-const because of YAML traits. - void writeYamlTo(const BenchmarkResultContext &Context, llvm::raw_ostream &S); + void writeYamlTo(const LLVMState &State, llvm::raw_ostream &S); - llvm::Error writeYaml(const BenchmarkResultContext &Context, - const llvm::StringRef Filename); + llvm::Error writeYaml(const LLVMState &State, const llvm::StringRef Filename); }; //------------------------------------------------------------------------------ // Utilities to work with Benchmark measures. // A class that measures stats over benchmark measures. -class BenchmarkMeasureStats { +class PerInstructionStats { public: void push(const BenchmarkMeasure &BM); @@ -102,38 +111,7 @@ private: 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 +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRESULT_H diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp index 55012bc1e83f..437503f84865 100644 --- a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -13,14 +13,16 @@ #include "Assembler.h" #include "BenchmarkRunner.h" #include "MCInstrDescView.h" +#include "PerfHelper.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileSystem.h" -#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" +namespace llvm { namespace exegesis { BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S) @@ -28,74 +30,94 @@ BenchmarkFailure::BenchmarkFailure(const llvm::Twine &S) BenchmarkRunner::BenchmarkRunner(const LLVMState &State, InstructionBenchmark::ModeE Mode) - : State(State), RATC(State.getRegInfo(), - getFunctionReservedRegs(State.getTargetMachine())), - Mode(Mode) {} + : State(State), Mode(Mode), Scratch(llvm::make_unique<ScratchSpace>()) {} 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; +// Repeat the snippet until there are at least MinInstructions in the resulting +// code. +static std::vector<llvm::MCInst> +GenerateInstructions(const BenchmarkCode &BC, const size_t MinInstructions) { + if (BC.Instructions.empty()) + return {}; + std::vector<llvm::MCInst> Code = BC.Instructions; + for (int I = 0; Code.size() < MinInstructions; ++I) + Code.push_back(BC.Instructions[I % BC.Instructions.size()]); + return Code; } +namespace { +class FunctionExecutorImpl : public BenchmarkRunner::FunctionExecutor { +public: + FunctionExecutorImpl(const LLVMState &State, + llvm::object::OwningBinary<llvm::object::ObjectFile> Obj, + BenchmarkRunner::ScratchSpace *Scratch) + : Function(State.createTargetMachine(), std::move(Obj)), + Scratch(Scratch) {} + +private: + llvm::Expected<int64_t> runAndMeasure(const char *Counters) const override { + // 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(Counters).split(CounterNames, '+'); + char *const ScratchPtr = Scratch->ptr(); + for (auto &CounterName : CounterNames) { + CounterName = CounterName.trim(); + pfm::PerfEvent PerfEvent(CounterName); + if (!PerfEvent.valid()) + llvm::report_fatal_error( + llvm::Twine("invalid perf event '").concat(CounterName).concat("'")); + pfm::Counter Counter(PerfEvent); + Scratch->clear(); + { + llvm::CrashRecoveryContext CRC; + llvm::CrashRecoveryContext::Enable(); + const bool Crashed = !CRC.RunSafely([this, &Counter, ScratchPtr]() { + Counter.start(); + this->Function(ScratchPtr); + Counter.stop(); + }); + llvm::CrashRecoveryContext::Disable(); + // FIXME: Better diagnosis. + if (Crashed) + return llvm::make_error<BenchmarkFailure>( + "snippet crashed while running"); + } + CounterValue += Counter.read(); + } + return CounterValue; + } + + const ExecutableFunction Function; + BenchmarkRunner::ScratchSpace *const Scratch; +}; +} // namespace + InstructionBenchmark -BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, - unsigned Opcode, unsigned NumRepetitions) const { +BenchmarkRunner::runConfiguration(const BenchmarkCode &BC, + 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; + InstrBenchmark.Info = BC.Info; - const std::vector<llvm::MCInst> &Snippet = Configuration.Snippet; - if (Snippet.empty()) { - InstrBenchmark.Error = "Empty snippet"; - return InstrBenchmark; - } - - InstrBenchmark.Key.Instructions = Snippet; + const std::vector<llvm::MCInst> &Instructions = BC.Instructions; - // 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; - }; + InstrBenchmark.Key.Instructions = Instructions; + InstrBenchmark.Key.RegisterInitialValues = BC.RegisterInitialValues; // 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)); + auto ObjectFilePath = writeObjectFile( + BC, GenerateInstructions(BC, kMinInstructionsForSnippet)); if (llvm::Error E = ObjectFilePath.takeError()) { InstrBenchmark.Error = llvm::toString(std::move(E)); return InstrBenchmark; @@ -108,82 +130,36 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, // Assemble NumRepetitions instructions repetitions of the snippet for // measurements. - auto ObjectFilePath = - writeObjectFile(Configuration.SnippetSetup, - GenerateInstructions(InstrBenchmark.NumRepetitions)); + auto ObjectFilePath = writeObjectFile( + BC, GenerateInstructions(BC, 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); + const FunctionExecutorImpl Executor(State, getObjectFromFile(*ObjectFilePath), + Scratch.get()); + auto Measurements = runMeasurements(Executor); + if (llvm::Error E = Measurements.takeError()) { + InstrBenchmark.Error = llvm::toString(std::move(E)); + return InstrBenchmark; + } + InstrBenchmark.Measurements = std::move(*Measurements); + assert(InstrBenchmark.NumRepetitions > 0 && "invalid NumRepetitions"); + for (BenchmarkMeasure &BM : InstrBenchmark.Measurements) { + // Scale the measurements by instruction. + BM.PerInstructionValue /= InstrBenchmark.NumRepetitions; + // Scale the measurements by snippet. + BM.PerSnippetValue *= static_cast<double>(BC.Instructions.size()) / + InstrBenchmark.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, +BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC, llvm::ArrayRef<llvm::MCInst> Code) const { int ResultFD = 0; llvm::SmallString<256> ResultPath; @@ -192,38 +168,11 @@ BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup, return std::move(E); llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), - Setup.RegsToDef, Code, OFS); + BC.LiveIns, BC.RegisterInitialValues, 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); -} +BenchmarkRunner::FunctionExecutor::~FunctionExecutor() {} -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 +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.h b/tools/llvm-exegesis/lib/BenchmarkRunner.h index 91d2c8e2d8c0..4f77f492ab4b 100644 --- a/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -17,14 +17,17 @@ #define LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H #include "Assembler.h" +#include "BenchmarkCode.h" #include "BenchmarkResult.h" #include "LlvmState.h" #include "MCInstrDescView.h" -#include "RegisterAliasing.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/Error.h" +#include <cstdlib> +#include <memory> #include <vector> +namespace llvm { namespace exegesis { // A class representing failures that happened during Benchmark, they are used @@ -34,23 +37,6 @@ 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: @@ -59,50 +45,52 @@ public: virtual ~BenchmarkRunner(); - llvm::Expected<std::vector<InstructionBenchmark>> - run(unsigned Opcode, unsigned NumRepetitions); + InstructionBenchmark runConfiguration(const BenchmarkCode &Configuration, + unsigned NumRepetitions) const; + + // Scratch space to run instructions that touch memory. + struct ScratchSpace { + static constexpr const size_t kAlignment = 1024; + static constexpr const size_t kSize = 1 << 20; // 1MB. + ScratchSpace() + : UnalignedPtr(llvm::make_unique<char[]>(kSize + kAlignment)), + AlignedPtr( + UnalignedPtr.get() + kAlignment - + (reinterpret_cast<intptr_t>(UnalignedPtr.get()) % kAlignment)) {} + char *ptr() const { return AlignedPtr; } + void clear() { std::memset(ptr(), 0, kSize); } + + private: + const std::unique_ptr<char[]> UnalignedPtr; + char *const AlignedPtr; + }; - // Given a snippet, computes which registers the setup code needs to define. - std::vector<unsigned> - computeRegsToDef(const std::vector<InstructionInstance> &Snippet) const; + // A helper to measure counters while executing a function in a sandboxed + // context. + class FunctionExecutor { + public: + virtual ~FunctionExecutor(); + virtual llvm::Expected<int64_t> + runAndMeasure(const char *Counters) const = 0; + }; 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; + virtual llvm::Expected<std::vector<BenchmarkMeasure>> + runMeasurements(const FunctionExecutor &Executor) const = 0; llvm::Expected<std::string> - writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + writeObjectFile(const BenchmarkCode &Configuration, llvm::ArrayRef<llvm::MCInst> Code) const; const InstructionBenchmark::ModeE Mode; + + const std::unique_ptr<ScratchSpace> Scratch; }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_BENCHMARKRUNNER_H diff --git a/tools/llvm-exegesis/lib/CMakeLists.txt b/tools/llvm-exegesis/lib/CMakeLists.txt index 175c2adf9de8..ef85056db0a4 100644 --- a/tools/llvm-exegesis/lib/CMakeLists.txt +++ b/tools/llvm-exegesis/lib/CMakeLists.txt @@ -1,12 +1,20 @@ +set(TARGETS_TO_APPEND "") + if (LLVM_TARGETS_TO_BUILD MATCHES "X86") add_subdirectory(X86) - set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} X86" PARENT_SCOPE) + set(TARGETS_TO_APPEND "${TARGETS_TO_APPEND} X86") endif() if (LLVM_TARGETS_TO_BUILD MATCHES "AArch64") add_subdirectory(AArch64) - set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} AArch64" PARENT_SCOPE) + set(TARGETS_TO_APPEND "${TARGETS_TO_APPEND} AArch64") +endif() +if (LLVM_TARGETS_TO_BUILD MATCHES "PowerPC") + add_subdirectory(PowerPC) + set(TARGETS_TO_APPEND "${TARGETS_TO_APPEND} PowerPC") endif() +set(LLVM_EXEGESIS_TARGETS "${LLVM_EXEGESIS_TARGETS} ${TARGETS_TO_APPEND}" PARENT_SCOPE) + add_library(LLVMExegesis STATIC Analysis.cpp @@ -14,11 +22,14 @@ add_library(LLVMExegesis BenchmarkResult.cpp BenchmarkRunner.cpp Clustering.cpp + CodeTemplate.cpp Latency.cpp LlvmState.cpp MCInstrDescView.cpp PerfHelper.cpp RegisterAliasing.cpp + SnippetGenerator.cpp + RegisterValue.cpp Target.cpp Uops.cpp ) diff --git a/tools/llvm-exegesis/lib/Clustering.cpp b/tools/llvm-exegesis/lib/Clustering.cpp index a966c722a78f..b2cd97c12eb0 100644 --- a/tools/llvm-exegesis/lib/Clustering.cpp +++ b/tools/llvm-exegesis/lib/Clustering.cpp @@ -8,9 +8,11 @@ //===----------------------------------------------------------------------===// #include "Clustering.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallVector.h" #include <string> -#include <unordered_set> +namespace llvm { namespace exegesis { // The clustering problem has the following characteristics: @@ -31,9 +33,10 @@ namespace exegesis { // 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; +void InstructionBenchmarkClustering::rangeQuery( + const size_t Q, std::vector<size_t> &Neighbors) const { + Neighbors.clear(); + Neighbors.reserve(Points_.size() - 1); // The Q itself isn't a neighbor. const auto &QMeasurements = Points_[Q].Measurements; for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) { if (P == Q) @@ -45,18 +48,6 @@ InstructionBenchmarkClustering::rangeQuery(const size_t Q) const { 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( @@ -101,10 +92,11 @@ llvm::Error InstructionBenchmarkClustering::validateAndSetup() { } void InstructionBenchmarkClustering::dbScan(const size_t MinPts) { + std::vector<size_t> Neighbors; // Persistent buffer to avoid allocs. 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); + rangeQuery(P, Neighbors); 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. @@ -119,11 +111,12 @@ void InstructionBenchmarkClustering::dbScan(const size_t MinPts) { CurrentCluster.PointIndices.push_back(P); // Process P's neighbors. - std::unordered_set<size_t> ToProcess(Neighbors.begin(), Neighbors.end()); + llvm::SetVector<size_t, std::deque<size_t>> ToProcess; + ToProcess.insert(Neighbors.begin(), Neighbors.end()); while (!ToProcess.empty()) { // Retrieve a point from the set. const size_t Q = *ToProcess.begin(); - ToProcess.erase(Q); + ToProcess.erase(ToProcess.begin()); if (ClusterIdForPoint_[Q].isNoise()) { // Change noise point to border point. @@ -138,12 +131,14 @@ void InstructionBenchmarkClustering::dbScan(const size_t MinPts) { 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); + rangeQuery(Q, Neighbors); if (Neighbors.size() + 1 >= MinPts) { ToProcess.insert(Neighbors.begin(), Neighbors.end()); } } } + // assert(Neighbors.capacity() == (Points_.size() - 1)); + // ^ True, but it is not quaranteed to be true in all the cases. // Add noisy points to noise cluster. for (size_t P = 0, NumPoints = Points_.size(); P < NumPoints; ++P) { @@ -170,3 +165,4 @@ InstructionBenchmarkClustering::create( } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Clustering.h b/tools/llvm-exegesis/lib/Clustering.h index c811020e0fe8..932cbba2038e 100644 --- a/tools/llvm-exegesis/lib/Clustering.h +++ b/tools/llvm-exegesis/lib/Clustering.h @@ -19,6 +19,7 @@ #include "llvm/Support/Error.h" #include <vector> +namespace llvm { namespace exegesis { class InstructionBenchmarkClustering { @@ -89,14 +90,21 @@ public: // 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; + 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].PerInstructionValue - Q[I].PerInstructionValue; + DistanceSquared += Diff * Diff; + } + return DistanceSquared <= EpsilonSquared_; + } 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; + void rangeQuery(size_t Q, std::vector<size_t> &Scratchpad) const; const std::vector<InstructionBenchmark> &Points_; const double EpsilonSquared_; @@ -109,5 +117,6 @@ private: }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_CLUSTERING_H diff --git a/tools/llvm-exegesis/lib/CodeTemplate.cpp b/tools/llvm-exegesis/lib/CodeTemplate.cpp new file mode 100644 index 000000000000..e159b000755a --- /dev/null +++ b/tools/llvm-exegesis/lib/CodeTemplate.cpp @@ -0,0 +1,119 @@ +//===-- CodeTemplate.cpp ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CodeTemplate.h" + +namespace llvm { +namespace exegesis { + +CodeTemplate::CodeTemplate(CodeTemplate &&) = default; + +CodeTemplate &CodeTemplate::operator=(CodeTemplate &&) = default; + +InstructionTemplate::InstructionTemplate(const Instruction &Instr) + : Instr(Instr), VariableValues(Instr.Variables.size()) {} + +InstructionTemplate::InstructionTemplate(InstructionTemplate &&) = default; + +InstructionTemplate &InstructionTemplate:: +operator=(InstructionTemplate &&) = default; + +InstructionTemplate::InstructionTemplate(const InstructionTemplate &) = default; + +InstructionTemplate &InstructionTemplate:: +operator=(const InstructionTemplate &) = default; + +unsigned InstructionTemplate::getOpcode() const { + return Instr.Description->getOpcode(); +} + +llvm::MCOperand &InstructionTemplate::getValueFor(const Variable &Var) { + return VariableValues[Var.getIndex()]; +} + +const llvm::MCOperand & +InstructionTemplate::getValueFor(const Variable &Var) const { + return VariableValues[Var.getIndex()]; +} + +llvm::MCOperand &InstructionTemplate::getValueFor(const Operand &Op) { + return getValueFor(Instr.Variables[Op.getVariableIndex()]); +} + +const llvm::MCOperand & +InstructionTemplate::getValueFor(const Operand &Op) const { + return getValueFor(Instr.Variables[Op.getVariableIndex()]); +} + +bool InstructionTemplate::hasImmediateVariables() const { + return llvm::any_of(Instr.Variables, [this](const Variable &Var) { + return Instr.getPrimaryOperand(Var).isImmediate(); + }); +} + +llvm::MCInst InstructionTemplate::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; +} + +bool isEnumValue(ExecutionMode Execution) { + return llvm::isPowerOf2_32(static_cast<uint32_t>(Execution)); +} + +llvm::StringRef getName(ExecutionMode Bit) { + assert(isEnumValue(Bit) && "Bit must be a power of two"); + switch (Bit) { + case ExecutionMode::UNKNOWN: + return "UNKNOWN"; + case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS: + return "ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS"; + case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: + return "ALWAYS_SERIAL_TIED_REGS_ALIAS"; + case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: + return "SERIAL_VIA_MEMORY_INSTR"; + case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: + return "SERIAL_VIA_EXPLICIT_REGS"; + case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: + return "SERIAL_VIA_NON_MEMORY_INSTR"; + case ExecutionMode::ALWAYS_PARALLEL_MISSING_USE_OR_DEF: + return "ALWAYS_PARALLEL_MISSING_USE_OR_DEF"; + case ExecutionMode::PARALLEL_VIA_EXPLICIT_REGS: + return "PARALLEL_VIA_EXPLICIT_REGS"; + } + llvm_unreachable("Missing enum case"); +} + +llvm::ArrayRef<ExecutionMode> getAllExecutionBits() { + static const ExecutionMode kAllExecutionModeBits[] = { + ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS, + ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS, + ExecutionMode::SERIAL_VIA_MEMORY_INSTR, + ExecutionMode::SERIAL_VIA_EXPLICIT_REGS, + ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR, + ExecutionMode::ALWAYS_PARALLEL_MISSING_USE_OR_DEF, + ExecutionMode::PARALLEL_VIA_EXPLICIT_REGS, + }; + return llvm::makeArrayRef(kAllExecutionModeBits); +} + +llvm::SmallVector<ExecutionMode, 4> +getExecutionModeBits(ExecutionMode Execution) { + llvm::SmallVector<ExecutionMode, 4> Result; + for (const auto Bit : getAllExecutionBits()) + if ((Execution & Bit) == Bit) + Result.push_back(Bit); + return Result; +} + +} // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/CodeTemplate.h b/tools/llvm-exegesis/lib/CodeTemplate.h new file mode 100644 index 000000000000..4c55487f3d12 --- /dev/null +++ b/tools/llvm-exegesis/lib/CodeTemplate.h @@ -0,0 +1,131 @@ +//===-- CodeTemplate.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 set of structures and functions to craft instructions for the +/// SnippetGenerator. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H +#define LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H + +#include "MCInstrDescView.h" +#include "llvm/ADT/BitmaskEnum.h" + +namespace llvm { +namespace exegesis { + +// A template for an Instruction holding values for each of its Variables. +struct InstructionTemplate { + InstructionTemplate(const Instruction &Instr); + + InstructionTemplate(const InstructionTemplate &); // default + InstructionTemplate &operator=(const InstructionTemplate &); // default + InstructionTemplate(InstructionTemplate &&); // default + InstructionTemplate &operator=(InstructionTemplate &&); // default + + 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; + + // Builds an llvm::MCInst from this InstructionTemplate setting its operands + // to the corresponding variable values. Precondition: All VariableValues must + // be set. + llvm::MCInst build() const; + + Instruction Instr; + llvm::SmallVector<llvm::MCOperand, 4> VariableValues; +}; + +enum class ExecutionMode : uint8_t { + UNKNOWN = 0U, + // The instruction is always serial because implicit Use and Def alias. + // e.g. AAA (alias via EFLAGS) + ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS = 1u << 0, + + // The instruction is always serial because one Def is tied to a Use. + // e.g. AND32ri (alias via tied GR32) + ALWAYS_SERIAL_TIED_REGS_ALIAS = 1u << 1, + + // The execution can be made serial by inserting a second instruction that + // clobbers/reads memory. + // e.g. MOV8rm + SERIAL_VIA_MEMORY_INSTR = 1u << 2, + + // The execution can be made serial by picking one Def that aliases with one + // Use. + // e.g. VXORPSrr XMM1, XMM1, XMM2 + SERIAL_VIA_EXPLICIT_REGS = 1u << 3, + + // The execution can be made serial by inserting a second instruction that + // uses one of the Defs and defs one of the Uses. + // e.g. + // 1st instruction: MMX_PMOVMSKBrr ECX, MM7 + // 2nd instruction: MMX_MOVD64rr MM7, ECX + // or instruction: MMX_MOVD64to64rr MM7, ECX + // or instruction: MMX_PINSRWrr MM7, MM7, ECX, 1 + SERIAL_VIA_NON_MEMORY_INSTR = 1u << 4, + + // The execution is always parallel because the instruction is missing Use or + // Def operands. + ALWAYS_PARALLEL_MISSING_USE_OR_DEF = 1u << 5, + + // The execution can be made parallel by repeating the same instruction but + // making sure that Defs of one instruction do not alias with Uses of the + // second one. + PARALLEL_VIA_EXPLICIT_REGS = 1u << 6, + + LLVM_MARK_AS_BITMASK_ENUM(/*Largest*/ PARALLEL_VIA_EXPLICIT_REGS) +}; + +// Returns whether Execution is one of the values defined in the enum above. +bool isEnumValue(ExecutionMode Execution); + +// Returns a human readable string for the enum. +llvm::StringRef getName(ExecutionMode Execution); + +// Returns a sequence of increasing powers of two corresponding to all the +// Execution flags. +llvm::ArrayRef<ExecutionMode> getAllExecutionBits(); + +// Decomposes Execution into individual set bits. +llvm::SmallVector<ExecutionMode, 4> getExecutionModeBits(ExecutionMode); + +LLVM_ENABLE_BITMASK_ENUMS_IN_NAMESPACE(); + +// A CodeTemplate is a set of InstructionTemplates that may not be fully +// specified (i.e. some variables are not yet set). This allows the +// SnippetGenerator to instantiate it many times with specific values to study +// their impact on instruction's performance. +struct CodeTemplate { + CodeTemplate() = default; + + CodeTemplate(CodeTemplate &&); // default + CodeTemplate &operator=(CodeTemplate &&); // default + CodeTemplate(const CodeTemplate &) = delete; + CodeTemplate &operator=(const CodeTemplate &) = delete; + + ExecutionMode Execution = ExecutionMode::UNKNOWN; + // Some information about how this template has been created. + std::string Info; + // The list of the instructions for this template. + std::vector<InstructionTemplate> Instructions; + // If the template uses the provided scratch memory, the register in which + // the pointer to this memory is passed in to the function. + unsigned ScratchSpacePointerInReg = 0; +}; + +} // namespace exegesis +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H diff --git a/tools/llvm-exegesis/lib/Latency.cpp b/tools/llvm-exegesis/lib/Latency.cpp index e2aae970e226..9a56b275088b 100644 --- a/tools/llvm-exegesis/lib/Latency.cpp +++ b/tools/llvm-exegesis/lib/Latency.cpp @@ -13,122 +13,182 @@ #include "BenchmarkRunner.h" #include "MCInstrDescView.h" #include "PerfHelper.h" +#include "Target.h" #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" #include "llvm/Support/FormatVariadic.h" +namespace llvm { 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; -} +struct ExecutionClass { + ExecutionMode Mask; + const char *Description; +} static const kExecutionClasses[] = { + {ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS | + ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS, + "Repeating a single implicitly serial instruction"}, + {ExecutionMode::SERIAL_VIA_EXPLICIT_REGS, + "Repeating a single explicitly serial instruction"}, + {ExecutionMode::SERIAL_VIA_MEMORY_INSTR | + ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR, + "Repeating two instructions"}, +}; -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(); -} +static constexpr size_t kMaxAliasingInstructions = 10; -llvm::Expected<SnippetPrototype> -LatencyBenchmarkRunner::generateTwoInstructionPrototype( - const Instruction &Instr) const { +static std::vector<Instruction> +computeAliasingInstructions(const LLVMState &State, const Instruction &Instr, + size_t MaxAliasingInstructions) { + // Randomly iterate the set of instructions. std::vector<unsigned> Opcodes; Opcodes.resize(State.getInstrInfo().getNumOpcodes()); std::iota(Opcodes.begin(), Opcodes.end(), 0U); std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator()); + + std::vector<Instruction> AliasingInstructions; for (const unsigned OtherOpcode : Opcodes) { - if (OtherOpcode == Instr.Description->Opcode) + if (OtherOpcode == Instr.Description->getOpcode()) 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()) + const Instruction &OtherInstr = State.getIC().getInstr(OtherOpcode); + if (OtherInstr.hasMemoryOperands()) 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); + if (Instr.hasAliasingRegistersThrough(OtherInstr)) + AliasingInstructions.push_back(std::move(OtherInstr)); + if (AliasingInstructions.size() >= MaxAliasingInstructions) + break; } - return llvm::make_error<BenchmarkFailure>( - "Infeasible : Didn't find any scheme to make the instruction serial"); + return AliasingInstructions; } -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); +static ExecutionMode getExecutionModes(const Instruction &Instr) { + ExecutionMode EM = ExecutionMode::UNKNOWN; + if (Instr.hasAliasingImplicitRegisters()) + EM |= ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS; + if (Instr.hasTiedRegisters()) + EM |= ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS; + if (Instr.hasMemoryOperands()) + EM |= ExecutionMode::SERIAL_VIA_MEMORY_INSTR; + else { + if (Instr.hasAliasingRegisters()) + EM |= ExecutionMode::SERIAL_VIA_EXPLICIT_REGS; + if (Instr.hasOneUseOrOneDef()) + EM |= ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR; + } + return EM; } -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; +static void appendCodeTemplates(const LLVMState &State, + const Instruction &Instr, + ExecutionMode ExecutionModeBit, + llvm::StringRef ExecutionClassDescription, + std::vector<CodeTemplate> &CodeTemplates) { + assert(isEnumValue(ExecutionModeBit) && "Bit must be a power of two"); + switch (ExecutionModeBit) { + case ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS: + // Nothing to do, the instruction is always serial. + LLVM_FALLTHROUGH; + case ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS: { + // Picking whatever value for the tied variable will make the instruction + // serial. + CodeTemplate CT; + CT.Execution = ExecutionModeBit; + CT.Info = ExecutionClassDescription; + CT.Instructions.push_back(Instr); + CodeTemplates.push_back(std::move(CT)); + return; + } + case ExecutionMode::SERIAL_VIA_MEMORY_INSTR: { + // Select back-to-back memory instruction. + // TODO: Implement me. + return; + } + case ExecutionMode::SERIAL_VIA_EXPLICIT_REGS: { + // Making the execution of this instruction serial by selecting one def + // register to alias with one use register. + const AliasingConfigurations SelfAliasing(Instr, Instr); + assert(!SelfAliasing.empty() && !SelfAliasing.hasImplicitAliasing() && + "Instr must alias itself explicitly"); + InstructionTemplate IT(Instr); + // This is a self aliasing instruction so defs and uses are from the same + // instance, hence twice IT in the following call. + setRandomAliasing(SelfAliasing, IT, IT); + CodeTemplate CT; + CT.Execution = ExecutionModeBit; + CT.Info = ExecutionClassDescription; + CT.Instructions.push_back(std::move(IT)); + CodeTemplates.push_back(std::move(CT)); + return; + } + case ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR: { + // Select back-to-back non-memory instruction. + for (const auto OtherInstr : + computeAliasingInstructions(State, Instr, kMaxAliasingInstructions)) { + const AliasingConfigurations Forward(Instr, OtherInstr); + const AliasingConfigurations Back(OtherInstr, Instr); + InstructionTemplate ThisIT(Instr); + InstructionTemplate OtherIT(OtherInstr); + if (!Forward.hasImplicitAliasing()) + setRandomAliasing(Forward, ThisIT, OtherIT); + if (!Back.hasImplicitAliasing()) + setRandomAliasing(Back, OtherIT, ThisIT); + CodeTemplate CT; + CT.Execution = ExecutionModeBit; + CT.Info = ExecutionClassDescription; + CT.Instructions.push_back(std::move(ThisIT)); + CT.Instructions.push_back(std::move(OtherIT)); + CodeTemplates.push_back(std::move(CT)); + } + return; + } + default: + llvm_unreachable("Unhandled enum value"); + } +} + +LatencySnippetGenerator::~LatencySnippetGenerator() = default; + +llvm::Expected<std::vector<CodeTemplate>> +LatencySnippetGenerator::generateCodeTemplates(const Instruction &Instr) const { + std::vector<CodeTemplate> Results; + const ExecutionMode EM = getExecutionModes(Instr); + for (const auto EC : kExecutionClasses) { + for (const auto ExecutionModeBit : getExecutionModeBits(EM & EC.Mask)) + appendCodeTemplates(State, Instr, ExecutionModeBit, EC.Description, + Results); + if (!Results.empty()) + break; + } + if (Results.empty()) + return llvm::make_error<BenchmarkFailure>( + "No strategy found to make the execution serial"); + return std::move(Results); } -std::vector<BenchmarkMeasure> -LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, - const unsigned NumRepetitions) const { +LatencyBenchmarkRunner::~LatencyBenchmarkRunner() = default; + +llvm::Expected<std::vector<BenchmarkMeasure>> +LatencyBenchmarkRunner::runMeasurements( + const FunctionExecutor &Executor) 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(); + int64_t MinValue = std::numeric_limits<int64_t>::max(); + const char *CounterName = State.getPfmCounters().CycleCounter; 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"); + llvm::report_fatal_error("sched model does not define a cycle counter"); 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; + auto ExpectedCounterValue = Executor.runAndMeasure(CounterName); + if (!ExpectedCounterValue) + return ExpectedCounterValue.takeError(); + if (*ExpectedCounterValue < MinValue) + MinValue = *ExpectedCounterValue; } - return {{"latency", static_cast<double>(MinLatency) / NumRepetitions, ""}}; + std::vector<BenchmarkMeasure> Result = { + BenchmarkMeasure::Create("latency", MinValue)}; + return std::move(Result); } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Latency.h b/tools/llvm-exegesis/lib/Latency.h index 9d6cfc7b387d..513c5f3ce780 100644 --- a/tools/llvm-exegesis/lib/Latency.h +++ b/tools/llvm-exegesis/lib/Latency.h @@ -17,31 +17,31 @@ #include "BenchmarkRunner.h" #include "MCInstrDescView.h" +#include "SnippetGenerator.h" +namespace llvm { namespace exegesis { +class LatencySnippetGenerator : public SnippetGenerator { +public: + LatencySnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} + ~LatencySnippetGenerator() override; + + llvm::Expected<std::vector<CodeTemplate>> + generateCodeTemplates(const Instruction &Instr) const override; +}; + 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; + llvm::Expected<std::vector<BenchmarkMeasure>> + runMeasurements(const FunctionExecutor &Executor) const override; }; - } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_LATENCY_H diff --git a/tools/llvm-exegesis/lib/LlvmState.cpp b/tools/llvm-exegesis/lib/LlvmState.cpp index 9ff42ca71fd2..cc01f1fa4ca5 100644 --- a/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/tools/llvm-exegesis/lib/LlvmState.cpp @@ -19,27 +19,35 @@ #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" +namespace llvm { namespace exegesis { -LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName) { +LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName, + const std::string &Features) { 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))); + 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(); } + PfmCounters = &TheExegesisTarget->getPfmCounters(CpuName); + + RATC.reset(new RegisterAliasingTrackerCache( + getRegInfo(), getFunctionReservedRegs(getTargetMachine()))); + IC.reset(new InstructionsCache(getInstrInfo(), getRATC())); } -LLVMState::LLVMState() +LLVMState::LLVMState(const std::string &CpuName) : LLVMState(llvm::sys::getProcessTriple(), - llvm::sys::getHostCPUName().str()) {} + CpuName.empty() ? llvm::sys::getHostCPUName().str() : CpuName, + "") {} std::unique_ptr<llvm::LLVMTargetMachine> LLVMState::createTargetMachine() const { @@ -69,3 +77,4 @@ bool LLVMState::canAssemble(const llvm::MCInst &Inst) const { } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/LlvmState.h b/tools/llvm-exegesis/lib/LlvmState.h index c84db300841e..e4f12a3f858e 100644 --- a/tools/llvm-exegesis/lib/LlvmState.h +++ b/tools/llvm-exegesis/lib/LlvmState.h @@ -15,6 +15,8 @@ #ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H #define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H +#include "MCInstrDescView.h" +#include "RegisterAliasing.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" @@ -24,18 +26,22 @@ #include <memory> #include <string> +namespace llvm { namespace exegesis { class ExegesisTarget; +struct PfmCountersInfo; // An object to initialize LLVM and prepare objects needed to run the // measurements. class LLVMState { public: - LLVMState(); + // Uses the host triple. If CpuName is empty, uses the host CPU. + LLVMState(const std::string &CpuName); LLVMState(const std::string &Triple, - const std::string &CpuName); // For tests. + const std::string &CpuName, + const std::string &Features = ""); // For tests. const llvm::TargetMachine &getTargetMachine() const { return *TargetMachine; } std::unique_ptr<llvm::LLVMTargetMachine> createTargetMachine() const; @@ -55,11 +61,20 @@ public: return *TargetMachine->getMCSubtargetInfo(); } + const RegisterAliasingTrackerCache &getRATC() const { return *RATC; } + const InstructionsCache &getIC() const { return *IC; } + + const PfmCountersInfo &getPfmCounters() const { return *PfmCounters; } + private: const ExegesisTarget *TheExegesisTarget; std::unique_ptr<const llvm::TargetMachine> TargetMachine; + std::unique_ptr<const RegisterAliasingTrackerCache> RATC; + std::unique_ptr<const InstructionsCache> IC; + const PfmCountersInfo *PfmCounters; }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/tools/llvm-exegesis/lib/MCInstrDescView.cpp index fc0d5b327b80..e0521af4d19e 100644 --- a/tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ b/tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -15,42 +15,118 @@ #include "llvm/ADT/STLExtras.h" +namespace llvm { namespace exegesis { -Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc, - const RegisterAliasingTrackerCache &RATC) - : Description(&MCInstrDesc) { +unsigned Variable::getIndex() const { + assert(Index >= 0 && "Index must be set"); + return Index; +} + +unsigned Variable::getPrimaryOperandIndex() const { + assert(!TiedOperands.empty()); + return TiedOperands[0]; +} + +bool Variable::hasTiedOperands() const { + assert(TiedOperands.size() <= 2 && + "No more than two operands can be tied together"); + // By definition only Use and Def operands can be tied together. + // TiedOperands[0] is the Def operand (LLVM stores defs first). + // TiedOperands[1] is the Use operand. + return TiedOperands.size() > 1; +} + +unsigned Operand::getIndex() const { + assert(Index >= 0 && "Index must be set"); + return Index; +} + +bool Operand::isExplicit() const { return Info; } + +bool Operand::isImplicit() const { return !Info; } + +bool Operand::isImplicitReg() const { return ImplicitReg; } + +bool Operand::isDef() const { return IsDef; } + +bool Operand::isUse() const { return !IsDef; } + +bool Operand::isReg() const { return Tracker; } + +bool Operand::isTied() const { return TiedToIndex >= 0; } + +bool Operand::isVariable() const { return VariableIndex >= 0; } + +bool Operand::isMemory() const { + return isExplicit() && + getExplicitOperandInfo().OperandType == llvm::MCOI::OPERAND_MEMORY; +} + +bool Operand::isImmediate() const { + return isExplicit() && + getExplicitOperandInfo().OperandType == llvm::MCOI::OPERAND_IMMEDIATE; +} + +unsigned Operand::getTiedToIndex() const { + assert(isTied() && "Operand must be tied to get the tied index"); + assert(TiedToIndex >= 0 && "TiedToIndex must be set"); + return TiedToIndex; +} + +unsigned Operand::getVariableIndex() const { + assert(isVariable() && "Operand must be variable to get the Variable index"); + assert(VariableIndex >= 0 && "VariableIndex must be set"); + return VariableIndex; +} + +unsigned Operand::getImplicitReg() const { + assert(ImplicitReg); + return *ImplicitReg; +} + +const RegisterAliasingTracker &Operand::getRegisterAliasing() const { + assert(Tracker); + return *Tracker; +} + +const llvm::MCOperandInfo &Operand::getExplicitOperandInfo() const { + assert(Info); + return *Info; +} + +Instruction::Instruction(const llvm::MCInstrInfo &InstrInfo, + const RegisterAliasingTrackerCache &RATC, + unsigned Opcode) + : Description(&InstrInfo.get(Opcode)), Name(InstrInfo.getName(Opcode)) { unsigned OpIndex = 0; - for (; OpIndex < MCInstrDesc.getNumOperands(); ++OpIndex) { - const auto &OpInfo = MCInstrDesc.opInfo_begin()[OpIndex]; + for (; OpIndex < Description->getNumOperands(); ++OpIndex) { + const auto &OpInfo = Description->opInfo_begin()[OpIndex]; Operand Operand; Operand.Index = OpIndex; - Operand.IsDef = (OpIndex < MCInstrDesc.getNumDefs()); - Operand.IsExplicit = true; + Operand.IsDef = (OpIndex < Description->getNumDefs()); // TODO(gchatelet): Handle isLookupPtrRegClass. if (OpInfo.RegClass >= 0) Operand.Tracker = &RATC.getRegisterClass(OpInfo.RegClass); Operand.TiedToIndex = - MCInstrDesc.getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO); + Description->getOperandConstraint(OpIndex, llvm::MCOI::TIED_TO); Operand.Info = &OpInfo; Operands.push_back(Operand); } - for (const llvm::MCPhysReg *MCPhysReg = MCInstrDesc.getImplicitDefs(); + for (const llvm::MCPhysReg *MCPhysReg = Description->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(); + for (const llvm::MCPhysReg *MCPhysReg = Description->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); @@ -58,7 +134,7 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc, // 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) { + if (Op.isExplicit() && !Op.isTied()) { const size_t VariableIndex = Variables.size(); Op.VariableIndex = VariableIndex; Variables.emplace_back(); @@ -66,89 +142,136 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc, } // Assigning Variables to tied operands. for (auto &Op : Operands) - if (Op.TiedToIndex >= 0) - Op.VariableIndex = Operands[Op.TiedToIndex].VariableIndex; + if (Op.isTied()) + Op.VariableIndex = Operands[Op.getTiedToIndex()].getVariableIndex(); // Assigning Operands to Variables. for (auto &Op : Operands) - if (Op.VariableIndex >= 0) - Variables[Op.VariableIndex].TiedOperands.push_back(Op.Index); + if (Op.isVariable()) + Variables[Op.getVariableIndex()].TiedOperands.push_back(Op.getIndex()); // Processing Aliasing. - DefRegisters = RATC.emptyRegisters(); - UseRegisters = RATC.emptyRegisters(); + ImplDefRegs = RATC.emptyRegisters(); + ImplUseRegs = RATC.emptyRegisters(); + AllDefRegs = RATC.emptyRegisters(); + AllUseRegs = RATC.emptyRegisters(); for (const auto &Op : Operands) { - if (Op.Tracker) { - auto &Registers = Op.IsDef ? DefRegisters : UseRegisters; - Registers |= Op.Tracker->aliasedBits(); + if (Op.isReg()) { + const auto &AliasingBits = Op.getRegisterAliasing().aliasedBits(); + if (Op.isDef()) + AllDefRegs |= AliasingBits; + if (Op.isUse()) + AllUseRegs |= AliasingBits; + if (Op.isDef() && Op.isImplicit()) + ImplDefRegs |= AliasingBits; + if (Op.isUse() && Op.isImplicit()) + ImplUseRegs |= AliasingBits; } } } -InstructionInstance::InstructionInstance(const Instruction &Instr) - : Instr(Instr), VariableValues(Instr.Variables.size()) {} - -InstructionInstance::InstructionInstance(InstructionInstance &&) = default; - -InstructionInstance &InstructionInstance:: -operator=(InstructionInstance &&) = default; +const Operand &Instruction::getPrimaryOperand(const Variable &Var) const { + const auto PrimaryOperandIndex = Var.getPrimaryOperandIndex(); + assert(PrimaryOperandIndex < Operands.size()); + return Operands[PrimaryOperandIndex]; +} -unsigned InstructionInstance::getOpcode() const { - return Instr.Description->getOpcode(); +bool Instruction::hasMemoryOperands() const { + return any_of(Operands, [](const Operand &Op) { + return Op.isReg() && Op.isExplicit() && Op.isMemory(); + }); } -llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) { - return VariableValues[Var.Index]; +bool Instruction::hasAliasingImplicitRegisters() const { + return ImplDefRegs.anyCommon(ImplUseRegs); } -const llvm::MCOperand & -InstructionInstance::getValueFor(const Variable &Var) const { - return VariableValues[Var.Index]; +bool Instruction::hasAliasingImplicitRegistersThrough( + const Instruction &OtherInstr) const { + return ImplDefRegs.anyCommon(OtherInstr.ImplUseRegs) && + OtherInstr.ImplDefRegs.anyCommon(ImplUseRegs); } -llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) { - assert(Op.VariableIndex >= 0); - return getValueFor(Instr.Variables[Op.VariableIndex]); +bool Instruction::hasAliasingRegistersThrough( + const Instruction &OtherInstr) const { + return AllDefRegs.anyCommon(OtherInstr.AllUseRegs) && + OtherInstr.AllDefRegs.anyCommon(AllUseRegs); } -const llvm::MCOperand & -InstructionInstance::getValueFor(const Operand &Op) const { - assert(Op.VariableIndex >= 0); - return getValueFor(Instr.Variables[Op.VariableIndex]); +bool Instruction::hasTiedRegisters() const { + return llvm::any_of( + Variables, [](const Variable &Var) { return Var.hasTiedOperands(); }); } -// 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; - }); +bool Instruction::hasAliasingRegisters() const { + return AllDefRegs.anyCommon(AllUseRegs); } -void InstructionInstance::randomizeUnsetVariables() { - for (const Variable &Var : Instr.Variables) { - llvm::MCOperand &AssignedValue = getValueFor(Var); - if (!AssignedValue.isValid()) - randomize(Instr, Var, AssignedValue); - } +bool Instruction::hasOneUseOrOneDef() const { + return AllDefRegs.count() || AllUseRegs.count(); } -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; +void Instruction::dump(const llvm::MCRegisterInfo &RegInfo, + llvm::raw_ostream &Stream) const { + Stream << "- " << Name << "\n"; + for (const auto &Op : Operands) { + Stream << "- Op" << Op.getIndex(); + if (Op.isExplicit()) + Stream << " Explicit"; + if (Op.isImplicit()) + Stream << " Implicit"; + if (Op.isUse()) + Stream << " Use"; + if (Op.isDef()) + Stream << " Def"; + if (Op.isImmediate()) + Stream << " Immediate"; + if (Op.isMemory()) + Stream << " Memory"; + if (Op.isReg()) { + if (Op.isImplicitReg()) + Stream << " Reg(" << RegInfo.getName(Op.getImplicitReg()) << ")"; + else + Stream << " RegClass(" + << RegInfo.getRegClassName( + &RegInfo.getRegClass(Op.Info->RegClass)) + << ")"; + } + if (Op.isTied()) + Stream << " TiedToOp" << Op.getTiedToIndex(); + Stream << "\n"; + } + for (const auto &Var : Variables) { + Stream << "- Var" << Var.getIndex(); + Stream << " ["; + bool IsFirst = true; + for (auto OperandIndex : Var.TiedOperands) { + if (!IsFirst) + Stream << ","; + Stream << "Op" << OperandIndex; + IsFirst = false; + } + Stream << "]"; + Stream << "\n"; + } + if (hasMemoryOperands()) + Stream << "- hasMemoryOperands\n"; + if (hasAliasingImplicitRegisters()) + Stream << "- hasAliasingImplicitRegisters (execution is always serial)\n"; + if (hasTiedRegisters()) + Stream << "- hasTiedRegisters (execution is always serial)\n"; + if (hasAliasingRegisters()) + Stream << "- hasAliasingRegisters\n"; } -SnippetPrototype::SnippetPrototype(SnippetPrototype &&) = default; +InstructionsCache::InstructionsCache(const llvm::MCInstrInfo &InstrInfo, + const RegisterAliasingTrackerCache &RATC) + : InstrInfo(InstrInfo), RATC(RATC) {} -SnippetPrototype &SnippetPrototype::operator=(SnippetPrototype &&) = default; +const Instruction &InstructionsCache::getInstr(unsigned Opcode) const { + auto &Found = Instructions[Opcode]; + if (!Found) + Found.reset(new Instruction(InstrInfo, RATC, Opcode)); + return *Found; +} bool RegisterOperandAssignment:: operator==(const RegisterOperandAssignment &Other) const { @@ -164,8 +287,8 @@ 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 (Op.isReg() && Op.isDef() == SelectDef) { + const int SourceReg = Op.getRegisterAliasing().getOrigin(Reg); if (SourceReg >= 0) OperandValues.emplace_back(&Op, SourceReg); } @@ -174,7 +297,7 @@ static void addOperandIfAlias( bool AliasingRegisterOperands::hasImplicitAliasing() const { const auto HasImplicit = [](const RegisterOperandAssignment &ROV) { - return !ROV.Op->IsExplicit; + return ROV.Op->isImplicit(); }; return llvm::any_of(Defs, HasImplicit) && llvm::any_of(Uses, HasImplicit); } @@ -188,11 +311,10 @@ bool AliasingConfigurations::hasImplicitAliasing() const { } 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; + const Instruction &DefInstruction, const Instruction &UseInstruction) { + if (UseInstruction.AllUseRegs.anyCommon(DefInstruction.AllDefRegs)) { + auto CommonRegisters = UseInstruction.AllUseRegs; + CommonRegisters &= DefInstruction.AllDefRegs; for (const llvm::MCPhysReg Reg : CommonRegisters.set_bits()) { AliasingRegisterOperands ARO; addOperandIfAlias(Reg, true, DefInstruction.Operands, ARO.Defs); @@ -204,78 +326,6 @@ AliasingConfigurations::AliasingConfigurations( } } -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()) @@ -305,3 +355,4 @@ void DumpMCInst(const llvm::MCRegisterInfo &MCRegisterInfo, } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.h b/tools/llvm-exegesis/lib/MCInstrDescView.h index c36582ac72d8..58efd2a4e41c 100644 --- a/tools/llvm-exegesis/lib/MCInstrDescView.h +++ b/tools/llvm-exegesis/lib/MCInstrDescView.h @@ -20,6 +20,7 @@ #define LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H #include <random> +#include <unordered_map> #include "RegisterAliasing.h" #include "llvm/ADT/ArrayRef.h" @@ -28,24 +29,31 @@ #include "llvm/MC/MCInstrDesc.h" #include "llvm/MC/MCInstrInfo.h" +namespace llvm { 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 { + // Returns the index of this Variable inside Instruction's Variable. + unsigned getIndex() const; + + // Returns the index of the Operand linked to this Variable. + unsigned getPrimaryOperandIndex() const; + + // Returns whether this Variable has more than one Operand linked to it. + bool hasTiedOperands() const; + // 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; + // Value in InstructionBuilder.VariableValues. + int 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). @@ -56,9 +64,26 @@ struct Variable { // - 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 isExplicit() const; + bool isImplicit() const; + bool isImplicitReg() const; + bool isDef() const; + bool isUse() const; + bool isReg() const; + bool isTied() const; + bool isVariable() const; + bool isMemory() const; + bool isImmediate() const; + unsigned getIndex() const; + unsigned getTiedToIndex() const; + unsigned getVariableIndex() const; + unsigned getImplicitReg() const; + const RegisterAliasingTracker &getRegisterAliasing() const; + const llvm::MCOperandInfo &getExplicitOperandInfo() const; + + // Please use the accessors above and not the following fields. + int Index = -1; 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. @@ -69,63 +94,72 @@ struct Operand { // A view over an MCInstrDesc offering a convenient interface to compute // Register aliasing. struct Instruction { - Instruction(const llvm::MCInstrDesc &MCInstrDesc, - const RegisterAliasingTrackerCache &ATC); + Instruction(const llvm::MCInstrInfo &InstrInfo, + const RegisterAliasingTrackerCache &RATC, unsigned Opcode); - 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. -}; + // Returns the Operand linked to this Variable. + // In case the Variable is tied, the primary (i.e. Def) Operand is returned. + const Operand &getPrimaryOperand(const Variable &Var) const; -// An instance of an Instruction holding values for each of its Variables. -struct InstructionInstance { - InstructionInstance(const Instruction &Instr); + // Whether this instruction is self aliasing through its tied registers. + // Repeating this instruction is guaranteed to executes sequentially. + bool hasTiedRegisters() const; - // No copy. - InstructionInstance(const InstructionInstance &) = delete; - InstructionInstance &operator=(const InstructionInstance &) = delete; + // Whether this instruction is self aliasing through its implicit registers. + // Repeating this instruction is guaranteed to executes sequentially. + bool hasAliasingImplicitRegisters() const; - // Moving is OK. - InstructionInstance(InstructionInstance &&); - InstructionInstance &operator=(InstructionInstance &&); + // Whether this instruction is self aliasing through some registers. + // Repeating this instruction may execute sequentially by picking aliasing + // Use and Def registers. It may also execute in parallel by picking non + // aliasing Use and Def registers. + bool hasAliasingRegisters() const; - 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; + // Whether this instruction's implicit registers alias with OtherInstr's + // implicit registers. + bool hasAliasingImplicitRegistersThrough(const Instruction &OtherInstr) const; - // Assigns a Random Value to all Variables that are still Invalid. - void randomizeUnsetVariables(); + // Whether this instruction's registers alias with OtherInstr's registers. + bool hasAliasingRegistersThrough(const Instruction &OtherInstr) const; - // Returns the instance as an llvm::MCInst. The InstructionInstance must be - // fully allocated (no invalid variables). - llvm::MCInst build() const; + // Returns whether this instruction has Memory Operands. + // Repeating this instruction executes sequentially with an instruction that + // reads or write the same memory region. + bool hasMemoryOperands() const; - Instruction Instr; - llvm::SmallVector<llvm::MCOperand, 4> VariableValues; -}; + // Returns whether this instruction as at least one use or one def. + // Repeating this instruction may execute sequentially by adding an + // instruction that aliases one of these. + bool hasOneUseOrOneDef() const; -// 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; + // Convenient function to help with debugging. + void dump(const llvm::MCRegisterInfo &RegInfo, + llvm::raw_ostream &Stream) const; - // No copy. - SnippetPrototype(const SnippetPrototype &) = delete; - SnippetPrototype &operator=(const SnippetPrototype &) = delete; + const llvm::MCInstrDesc *Description; // Never nullptr. + llvm::StringRef Name; // The name of this instruction. + llvm::SmallVector<Operand, 8> Operands; + llvm::SmallVector<Variable, 4> Variables; + llvm::BitVector ImplDefRegs; // The set of aliased implicit def registers. + llvm::BitVector ImplUseRegs; // The set of aliased implicit use registers. + llvm::BitVector AllDefRegs; // The set of all aliased def registers. + llvm::BitVector AllUseRegs; // The set of all aliased use registers. +}; - // Moving is OK. - SnippetPrototype(SnippetPrototype &&); - SnippetPrototype &operator=(SnippetPrototype &&); +// Instructions are expensive to instantiate. This class provides a cache of +// Instructions with lazy construction. +struct InstructionsCache { + InstructionsCache(const llvm::MCInstrInfo &InstrInfo, + const RegisterAliasingTrackerCache &RATC); - std::string Explanation; - std::vector<InstructionInstance> Snippet; + // Returns the Instruction object corresponding to this Opcode. + const Instruction &getInstr(unsigned Opcode) const; + +private: + const llvm::MCInstrInfo &InstrInfo; + const RegisterAliasingTrackerCache &RATC; + mutable std::unordered_map<unsigned, std::unique_ptr<Instruction>> + Instructions; }; // Represents the assignment of a Register to an Operand. @@ -163,27 +197,10 @@ struct AliasingConfigurations { 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. @@ -192,5 +209,6 @@ void DumpMCInst(const llvm::MCRegisterInfo &MCRegisterInfo, const llvm::MCInst &MCInst, llvm::raw_ostream &OS); } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_MCINSTRDESCVIEW_H diff --git a/tools/llvm-exegesis/lib/PerfHelper.cpp b/tools/llvm-exegesis/lib/PerfHelper.cpp index c145ea8404b4..c1c242ca88fa 100644 --- a/tools/llvm-exegesis/lib/PerfHelper.cpp +++ b/tools/llvm-exegesis/lib/PerfHelper.cpp @@ -17,6 +17,7 @@ #endif #include <cassert> +namespace llvm { namespace exegesis { namespace pfm { @@ -136,3 +137,4 @@ int64_t Counter::read() const { return 42; } } // namespace pfm } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/PerfHelper.h b/tools/llvm-exegesis/lib/PerfHelper.h index 8c3f13e6c5cd..2d0810846606 100644 --- a/tools/llvm-exegesis/lib/PerfHelper.h +++ b/tools/llvm-exegesis/lib/PerfHelper.h @@ -23,6 +23,7 @@ struct perf_event_attr; +namespace llvm { namespace exegesis { namespace pfm { @@ -102,5 +103,6 @@ void Measure( } // namespace pfm } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_PERFHELPER_H diff --git a/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt b/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt new file mode 100644 index 000000000000..89e33437952e --- /dev/null +++ b/tools/llvm-exegesis/lib/PowerPC/CMakeLists.txt @@ -0,0 +1,18 @@ +include_directories( + ${LLVM_MAIN_SRC_DIR}/lib/Target/PowerPC + ${LLVM_BINARY_DIR}/lib/Target/PowerPC + ) + +add_library(LLVMExegesisPowerPC + STATIC + Target.cpp + ) + +llvm_update_compile_flags(LLVMExegesisPowerPC) +llvm_map_components_to_libnames(libs + PowerPC + Exegesis + ) + +target_link_libraries(LLVMExegesisPowerPC ${libs}) +set_target_properties(LLVMExegesisPowerPC PROPERTIES FOLDER "Libraries") diff --git a/tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt b/tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt new file mode 100644 index 000000000000..c535562065eb --- /dev/null +++ b/tools/llvm-exegesis/lib/PowerPC/LLVMBuild.txt @@ -0,0 +1,22 @@ +;===- ./tools/llvm-exegesis/lib/PowerPC/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 = ExegesisPowerPC +parent = Libraries +required_libraries = PowerPC diff --git a/tools/llvm-exegesis/lib/PowerPC/Target.cpp b/tools/llvm-exegesis/lib/PowerPC/Target.cpp new file mode 100644 index 000000000000..b4bdb5dc4adc --- /dev/null +++ b/tools/llvm-exegesis/lib/PowerPC/Target.cpp @@ -0,0 +1,76 @@ +//===-- Target.cpp ----------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// The PowerPC ExegesisTarget. +//===----------------------------------------------------------------------===// +#include "../Target.h" +#include "../Latency.h" +#include "PPC.h" +#include "PPCRegisterInfo.h" + +namespace llvm { +namespace exegesis { + +#include "PPCGenExegesis.inc" + +namespace { +class ExegesisPowerPCTarget : public ExegesisTarget { +public: + ExegesisPowerPCTarget() : ExegesisTarget(PPCCpuPfmCounters) {} + +private: + std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override; + bool matchesArch(llvm::Triple::ArchType Arch) const override { + return Arch == llvm::Triple::ppc64le; + } +}; +} // end anonymous namespace + +static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) { + switch (RegBitWidth) { + case 32: + return llvm::PPC::LI; + case 64: + return llvm::PPC::LI8; + } + llvm_unreachable("Invalid Value Width"); +} + +// Generates instruction to load an immediate value into a register. +static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth, + const llvm::APInt &Value) { + if (Value.getBitWidth() > RegBitWidth) + llvm_unreachable("Value must fit in the Register"); + return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth)) + .addReg(Reg) + .addImm(Value.getZExtValue()); +} + +std::vector<llvm::MCInst> +ExegesisPowerPCTarget::setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg, + const llvm::APInt &Value) const { + if (llvm::PPC::GPRCRegClass.contains(Reg)) + return {loadImmediate(Reg, 32, Value)}; + if (llvm::PPC::G8RCRegClass.contains(Reg)) + return {loadImmediate(Reg, 64, Value)}; + llvm::errs() << "setRegTo is not implemented, results will be unreliable\n"; + return {}; +} + +static ExegesisTarget *getTheExegesisPowerPCTarget() { + static ExegesisPowerPCTarget Target; + return &Target; +} + +void InitializePowerPCExegesisTarget() { + ExegesisTarget::registerTarget(getTheExegesisPowerPCTarget()); +} + +} // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/RegisterAliasing.cpp b/tools/llvm-exegesis/lib/RegisterAliasing.cpp index 039f78db985f..54041ca30aa0 100644 --- a/tools/llvm-exegesis/lib/RegisterAliasing.cpp +++ b/tools/llvm-exegesis/lib/RegisterAliasing.cpp @@ -9,6 +9,7 @@ #include "RegisterAliasing.h" +namespace llvm { namespace exegesis { llvm::BitVector getAliasedBits(const llvm::MCRegisterInfo &RegInfo, @@ -81,3 +82,4 @@ RegisterAliasingTrackerCache::getRegisterClass(unsigned RegClassIndex) const { } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/RegisterAliasing.h b/tools/llvm-exegesis/lib/RegisterAliasing.h index 0b0360ef9ff4..94a2eb07f495 100644 --- a/tools/llvm-exegesis/lib/RegisterAliasing.h +++ b/tools/llvm-exegesis/lib/RegisterAliasing.h @@ -22,6 +22,7 @@ #include "llvm/ADT/PackedVector.h" #include "llvm/MC/MCRegisterInfo.h" +namespace llvm { namespace exegesis { // Returns the registers that are aliased by the ones set in SourceBits. @@ -62,6 +63,7 @@ struct RegisterAliasingTracker { private: RegisterAliasingTracker(const llvm::MCRegisterInfo &RegInfo); + RegisterAliasingTracker(const RegisterAliasingTracker &) = delete; void FillOriginAndAliasedBits(const llvm::MCRegisterInfo &RegInfo, const llvm::BitVector &OriginalBits); @@ -103,5 +105,6 @@ private: }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_ALIASINGTRACKER_H diff --git a/tools/llvm-exegesis/lib/RegisterValue.cpp b/tools/llvm-exegesis/lib/RegisterValue.cpp new file mode 100644 index 000000000000..2bf996cead48 --- /dev/null +++ b/tools/llvm-exegesis/lib/RegisterValue.cpp @@ -0,0 +1,51 @@ +//===-- RegisterValue.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "RegisterValue.h" +#include "llvm/ADT/APFloat.h" + +namespace llvm { +namespace exegesis { + +static llvm::APFloat getFloatValue(const llvm::fltSemantics &FltSemantics, + PredefinedValues Value) { + switch (Value) { + case PredefinedValues::POS_ZERO: + return llvm::APFloat::getZero(FltSemantics); + case PredefinedValues::NEG_ZERO: + return llvm::APFloat::getZero(FltSemantics, true); + case PredefinedValues::ONE: + return llvm::APFloat(FltSemantics, "1"); + case PredefinedValues::TWO: + return llvm::APFloat(FltSemantics, "2"); + case PredefinedValues::INF: + return llvm::APFloat::getInf(FltSemantics); + case PredefinedValues::QNAN: + return llvm::APFloat::getQNaN(FltSemantics); + case PredefinedValues::SMALLEST_NORM: + return llvm::APFloat::getSmallestNormalized(FltSemantics); + case PredefinedValues::LARGEST: + return llvm::APFloat::getLargest(FltSemantics); + case PredefinedValues::ULP: + return llvm::APFloat::getSmallest(FltSemantics); + case PredefinedValues::ONE_PLUS_ULP: + auto Output = getFloatValue(FltSemantics, PredefinedValues::ONE); + Output.next(false); + return Output; + } + llvm_unreachable("Unhandled exegesis::PredefinedValues"); +} + +llvm::APInt bitcastFloatValue(const llvm::fltSemantics &FltSemantics, + PredefinedValues Value) { + return getFloatValue(FltSemantics, Value).bitcastToAPInt(); +} + +} // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/RegisterValue.h b/tools/llvm-exegesis/lib/RegisterValue.h new file mode 100644 index 000000000000..689e354e2416 --- /dev/null +++ b/tools/llvm-exegesis/lib/RegisterValue.h @@ -0,0 +1,53 @@ +//===-- RegisterValue.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 a Target independent value for a Register. This is useful to explore +/// the influence of the instruction input values on its execution time. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H +#define LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H + +#include <llvm/ADT/APFloat.h> +#include <llvm/ADT/APInt.h> + +namespace llvm { +namespace exegesis { + +// A simple object storing the value for a particular register. +struct RegisterValue { + static RegisterValue zero(unsigned Reg) { return {Reg, llvm::APInt()}; } + unsigned Register; + llvm::APInt Value; +}; + +enum class PredefinedValues { + POS_ZERO, // Positive zero + NEG_ZERO, // Negative zero + ONE, // 1.0 + TWO, // 2.0 + INF, // Infinity + QNAN, // Quiet NaN + ULP, // One Unit in the last place + SMALLEST = ULP, // The minimum subnormal number + SMALLEST_NORM, // The minimum normal number + LARGEST, // The maximum normal number + ONE_PLUS_ULP, // The value just after 1.0 +}; + +llvm::APInt bitcastFloatValue(const llvm::fltSemantics &FltSemantics, + PredefinedValues Value); + +} // namespace exegesis +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_REGISTERVALUE_H diff --git a/tools/llvm-exegesis/lib/SnippetGenerator.cpp b/tools/llvm-exegesis/lib/SnippetGenerator.cpp new file mode 100644 index 000000000000..88ba315548d3 --- /dev/null +++ b/tools/llvm-exegesis/lib/SnippetGenerator.cpp @@ -0,0 +1,226 @@ +//===-- SnippetGenerator.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 "MCInstrDescView.h" +#include "SnippetGenerator.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/Program.h" + +namespace llvm { +namespace exegesis { + +std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT) { + std::vector<CodeTemplate> Result; + Result.push_back(std::move(CT)); + return Result; +} + +SnippetGeneratorFailure::SnippetGeneratorFailure(const llvm::Twine &S) + : llvm::StringError(S, llvm::inconvertibleErrorCode()) {} + +SnippetGenerator::SnippetGenerator(const LLVMState &State) : State(State) {} + +SnippetGenerator::~SnippetGenerator() = default; + +llvm::Expected<std::vector<BenchmarkCode>> +SnippetGenerator::generateConfigurations(const Instruction &Instr) const { + if (auto E = generateCodeTemplates(Instr)) { + const auto &RATC = State.getRATC(); + std::vector<BenchmarkCode> Output; + for (CodeTemplate &CT : E.get()) { + const llvm::BitVector &ForbiddenRegs = + CT.ScratchSpacePointerInReg + ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits() + : RATC.emptyRegisters(); + // TODO: Generate as many BenchmarkCode as needed. + { + BenchmarkCode BC; + BC.Info = CT.Info; + for (InstructionTemplate &IT : CT.Instructions) { + randomizeUnsetVariables(ForbiddenRegs, IT); + BC.Instructions.push_back(IT.build()); + } + if (CT.ScratchSpacePointerInReg) + BC.LiveIns.push_back(CT.ScratchSpacePointerInReg); + BC.RegisterInitialValues = + computeRegisterInitialValues(CT.Instructions); + Output.push_back(std::move(BC)); + } + } + return Output; + } else + return E.takeError(); +} + +std::vector<RegisterValue> SnippetGenerator::computeRegisterInitialValues( + const std::vector<InstructionTemplate> &Instructions) const { + // Collect all register uses and create an assignment for each of them. + // Ignore memory operands which are handled separately. + // Loop invariant: DefinedRegs[i] is true iif it has been set at least once + // before the current instruction. + llvm::BitVector DefinedRegs = State.getRATC().emptyRegisters(); + std::vector<RegisterValue> RIV; + for (const InstructionTemplate &IT : Instructions) { + // Returns the register that this Operand sets or uses, or 0 if this is not + // a register. + const auto GetOpReg = [&IT](const Operand &Op) -> unsigned { + if (Op.isMemory()) + return 0; + if (Op.isImplicitReg()) + return Op.getImplicitReg(); + if (Op.isExplicit() && IT.getValueFor(Op).isReg()) + return IT.getValueFor(Op).getReg(); + return 0; + }; + // Collect used registers that have never been def'ed. + for (const Operand &Op : IT.Instr.Operands) { + if (Op.isUse()) { + const unsigned Reg = GetOpReg(Op); + if (Reg > 0 && !DefinedRegs.test(Reg)) { + RIV.push_back(RegisterValue::zero(Reg)); + DefinedRegs.set(Reg); + } + } + } + // Mark defs as having been def'ed. + for (const Operand &Op : IT.Instr.Operands) { + if (Op.isDef()) { + const unsigned Reg = GetOpReg(Op); + if (Reg > 0) + DefinedRegs.set(Reg); + } + } + } + return RIV; +} + +llvm::Expected<std::vector<CodeTemplate>> +generateSelfAliasingCodeTemplates(const Instruction &Instr) { + const AliasingConfigurations SelfAliasing(Instr, Instr); + if (SelfAliasing.empty()) + return llvm::make_error<SnippetGeneratorFailure>("empty self aliasing"); + std::vector<CodeTemplate> Result; + Result.emplace_back(); + CodeTemplate &CT = Result.back(); + InstructionTemplate IT(Instr); + if (SelfAliasing.hasImplicitAliasing()) { + CT.Info = "implicit Self cycles, picking random values."; + } else { + CT.Info = "explicit self cycles, selecting one aliasing Conf."; + // This is a self aliasing instruction so defs and uses are from the same + // instance, hence twice IT in the following call. + setRandomAliasing(SelfAliasing, IT, IT); + } + CT.Instructions.push_back(std::move(IT)); + return std::move(Result); +} + +llvm::Expected<std::vector<CodeTemplate>> +generateUnconstrainedCodeTemplates(const Instruction &Instr, + llvm::StringRef Msg) { + std::vector<CodeTemplate> Result; + Result.emplace_back(); + CodeTemplate &CT = Result.back(); + CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg); + CT.Instructions.emplace_back(Instr); + return std::move(Result); +} + +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, + const llvm::BitVector &ForbiddenRegs) { + const Operand &Op = Instr.getPrimaryOperand(Var); + switch (Op.getExplicitOperandInfo().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.isReg()); + auto AllowedRegs = Op.getRegisterAliasing().sourceBits(); + assert(AllowedRegs.size() == ForbiddenRegs.size()); + for (auto I : ForbiddenRegs.set_bits()) + AllowedRegs.reset(I); + AssignedValue = llvm::MCOperand::createReg(randomBit(AllowedRegs)); + break; + } + default: + break; + } +} + +static void setRegisterOperandValue(const RegisterOperandAssignment &ROV, + InstructionTemplate &IB) { + assert(ROV.Op); + if (ROV.Op->isExplicit()) { + auto &AssignedValue = IB.getValueFor(*ROV.Op); + if (AssignedValue.isValid()) { + assert(AssignedValue.isReg() && AssignedValue.getReg() == ROV.Reg); + return; + } + AssignedValue = llvm::MCOperand::createReg(ROV.Reg); + } else { + assert(ROV.Op->isImplicitReg()); + assert(ROV.Reg == ROV.Op->getImplicitReg()); + } +} + +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, + InstructionTemplate &DefIB, InstructionTemplate &UseIB) { + assert(!AliasingConfigurations.empty()); + assert(!AliasingConfigurations.hasImplicitAliasing()); + const auto &RandomConf = randomElement(AliasingConfigurations.Configurations); + setRegisterOperandValue(randomElement(RandomConf.Defs), DefIB); + setRegisterOperandValue(randomElement(RandomConf.Uses), UseIB); +} + +void randomizeUnsetVariables(const llvm::BitVector &ForbiddenRegs, + InstructionTemplate &IT) { + for (const Variable &Var : IT.Instr.Variables) { + llvm::MCOperand &AssignedValue = IT.getValueFor(Var); + if (!AssignedValue.isValid()) + randomize(IT.Instr, Var, AssignedValue, ForbiddenRegs); + } +} + +} // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/SnippetGenerator.h b/tools/llvm-exegesis/lib/SnippetGenerator.h new file mode 100644 index 000000000000..967b273182b7 --- /dev/null +++ b/tools/llvm-exegesis/lib/SnippetGenerator.h @@ -0,0 +1,98 @@ +//===-- SnippetGenerator.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 SnippetGenerator class for generating code that allows +/// measuring a certain property of instructions (e.g. latency). +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H +#define LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H + +#include "Assembler.h" +#include "BenchmarkCode.h" +#include "CodeTemplate.h" +#include "LlvmState.h" +#include "MCInstrDescView.h" +#include "RegisterAliasing.h" +#include "llvm/MC/MCInst.h" +#include "llvm/Support/Error.h" +#include <cstdlib> +#include <memory> +#include <vector> + +namespace llvm { +namespace exegesis { + +std::vector<CodeTemplate> getSingleton(CodeTemplate &&CT); + +// Generates code templates that has a self-dependency. +llvm::Expected<std::vector<CodeTemplate>> +generateSelfAliasingCodeTemplates(const Instruction &Instr); + +// Generates code templates without assignment constraints. +llvm::Expected<std::vector<CodeTemplate>> +generateUnconstrainedCodeTemplates(const Instruction &Instr, + llvm::StringRef Msg); + +// A class representing failures that happened during Benchmark, they are used +// to report informations to the user. +class SnippetGeneratorFailure : public llvm::StringError { +public: + SnippetGeneratorFailure(const llvm::Twine &S); +}; + +// Common code for all benchmark modes. +class SnippetGenerator { +public: + explicit SnippetGenerator(const LLVMState &State); + + virtual ~SnippetGenerator(); + + // Calls generateCodeTemplate and expands it into one or more BenchmarkCode. + llvm::Expected<std::vector<BenchmarkCode>> + generateConfigurations(const Instruction &Instr) const; + + // Given a snippet, computes which registers the setup code needs to define. + std::vector<RegisterValue> computeRegisterInitialValues( + const std::vector<InstructionTemplate> &Snippet) const; + +protected: + const LLVMState &State; + +private: + // API to be implemented by subclasses. + virtual llvm::Expected<std::vector<CodeTemplate>> + generateCodeTemplates(const Instruction &Instr) const = 0; +}; + +// 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, + InstructionTemplate &DefIB, InstructionTemplate &UseIB); + +// Assigns a Random Value to all Variables in IT that are still Invalid. +// Do not use any of the registers in `ForbiddenRegs`. +void randomizeUnsetVariables(const llvm::BitVector &ForbiddenRegs, + InstructionTemplate &IT); + +} // namespace exegesis +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_EXEGESIS_SNIPPETGENERATOR_H diff --git a/tools/llvm-exegesis/lib/Target.cpp b/tools/llvm-exegesis/lib/Target.cpp index 44156c815dbe..2c89d27b4ab6 100644 --- a/tools/llvm-exegesis/lib/Target.cpp +++ b/tools/llvm-exegesis/lib/Target.cpp @@ -11,6 +11,7 @@ #include "Latency.h" #include "Uops.h" +namespace llvm { namespace exegesis { ExegesisTarget::~ExegesisTarget() {} // anchor. @@ -30,13 +31,26 @@ void ExegesisTarget::registerTarget(ExegesisTarget *Target) { FirstTarget = Target; return; } - assert(Target->Next == nullptr && "target has already been registered"); if (Target->Next != nullptr) - return; + return; // Already registered. Target->Next = FirstTarget; FirstTarget = Target; } +std::unique_ptr<SnippetGenerator> +ExegesisTarget::createSnippetGenerator(InstructionBenchmark::ModeE Mode, + const LLVMState &State) const { + switch (Mode) { + case InstructionBenchmark::Unknown: + return nullptr; + case InstructionBenchmark::Latency: + return createLatencySnippetGenerator(State); + case InstructionBenchmark::Uops: + return createUopsSnippetGenerator(State); + } + return nullptr; +} + std::unique_ptr<BenchmarkRunner> ExegesisTarget::createBenchmarkRunner(InstructionBenchmark::ModeE Mode, const LLVMState &State) const { @@ -51,6 +65,16 @@ ExegesisTarget::createBenchmarkRunner(InstructionBenchmark::ModeE Mode, return nullptr; } +std::unique_ptr<SnippetGenerator> +ExegesisTarget::createLatencySnippetGenerator(const LLVMState &State) const { + return llvm::make_unique<LatencySnippetGenerator>(State); +} + +std::unique_ptr<SnippetGenerator> +ExegesisTarget::createUopsSnippetGenerator(const LLVMState &State) const { + return llvm::make_unique<UopsSnippetGenerator>(State); +} + std::unique_ptr<BenchmarkRunner> ExegesisTarget::createLatencyBenchmarkRunner(const LLVMState &State) const { return llvm::make_unique<LatencyBenchmarkRunner>(State); @@ -61,11 +85,51 @@ ExegesisTarget::createUopsBenchmarkRunner(const LLVMState &State) const { return llvm::make_unique<UopsBenchmarkRunner>(State); } +static_assert(std::is_pod<PfmCountersInfo>::value, + "We shouldn't have dynamic initialization here"); +const PfmCountersInfo PfmCountersInfo::Default = {nullptr, nullptr, nullptr, + 0u}; + +const PfmCountersInfo & +ExegesisTarget::getPfmCounters(llvm::StringRef CpuName) const { + assert(std::is_sorted( + CpuPfmCounters.begin(), CpuPfmCounters.end(), + [](const CpuAndPfmCounters &LHS, const CpuAndPfmCounters &RHS) { + return strcmp(LHS.CpuName, RHS.CpuName) < 0; + }) && + "CpuPfmCounters table is not sorted"); + + // Find entry + auto Found = + std::lower_bound(CpuPfmCounters.begin(), CpuPfmCounters.end(), CpuName); + if (Found == CpuPfmCounters.end() || + llvm::StringRef(Found->CpuName) != CpuName) { + // Use the default. + if (CpuPfmCounters.begin() != CpuPfmCounters.end() && + CpuPfmCounters.begin()->CpuName[0] == '\0') { + Found = CpuPfmCounters.begin(); // The target specifies a default. + } else { + return PfmCountersInfo::Default; // No default for the target. + } + } + assert(Found->PCI && "Missing counters"); + return *Found->PCI; +} + namespace { // Default implementation. class ExegesisDefaultTarget : public ExegesisTarget { +public: + ExegesisDefaultTarget() : ExegesisTarget({}) {} + private: + std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override { + llvm_unreachable("Not yet implemented"); + } + bool matchesArch(llvm::Triple::ArchType Arch) const override { llvm_unreachable("never called"); return false; @@ -80,3 +144,4 @@ const ExegesisTarget &ExegesisTarget::getDefault() { } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Target.h b/tools/llvm-exegesis/lib/Target.h index 4f1f2869b749..b0f0e9961736 100644 --- a/tools/llvm-exegesis/lib/Target.h +++ b/tools/llvm-exegesis/lib/Target.h @@ -20,25 +20,88 @@ #include "BenchmarkResult.h" #include "BenchmarkRunner.h" #include "LlvmState.h" +#include "SnippetGenerator.h" #include "llvm/ADT/Triple.h" #include "llvm/CodeGen/TargetPassConfig.h" +#include "llvm/IR/CallingConv.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCRegisterInfo.h" +namespace llvm { namespace exegesis { +struct PfmCountersInfo { + // An optional name of a performance counter that can be used to measure + // cycles. + const char *CycleCounter; + + // An optional name of a performance counter that can be used to measure + // uops. + const char *UopsCounter; + + // An IssueCounter specifies how to measure uops issued to specific proc + // resources. + struct IssueCounter { + const char *Counter; + // The name of the ProcResource that this counter measures. + const char *ProcResName; + }; + // An optional list of IssueCounters. + const IssueCounter *IssueCounters; + unsigned NumIssueCounters; + + static const PfmCountersInfo Default; +}; + +struct CpuAndPfmCounters { + const char *CpuName; + const PfmCountersInfo *PCI; + bool operator<(llvm::StringRef S) const { + return llvm::StringRef(CpuName) < S; + } +}; + class ExegesisTarget { public: + explicit ExegesisTarget(llvm::ArrayRef<CpuAndPfmCounters> CpuPfmCounters) + : CpuPfmCounters(CpuPfmCounters) {} + // 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. + // Precondition: Value must fit into Reg. virtual std::vector<llvm::MCInst> - setRegToConstant(const llvm::MCSubtargetInfo &STI, unsigned Reg) const { - return {}; + setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg, + const llvm::APInt &Value) const = 0; + + // Returns the register pointing to scratch memory, or 0 if this target + // does not support memory operands. The benchmark function uses the + // default calling convention. + virtual unsigned getScratchMemoryRegister(const llvm::Triple &) const { + return 0; + } + + // Fills memory operands with references to the address at [Reg] + Offset. + virtual void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg, + unsigned Offset) const { + + llvm_unreachable( + "fillMemoryOperands() requires getScratchMemoryRegister() > 0"); } + // Returns the maximum number of bytes a load/store instruction can access at + // once. This is typically the size of the largest register available on the + // processor. Note that this only used as a hint to generate independant + // load/stores to/from memory, so the exact returned value does not really + // matter as long as it's large enough. + virtual unsigned getMaxMemoryAccessSize() const { return 0; } + + // Creates a snippet generator for the given mode. + std::unique_ptr<SnippetGenerator> + createSnippetGenerator(InstructionBenchmark::ModeE Mode, + const LLVMState &State) const; // Creates a benchmark runner for the given mode. std::unique_ptr<BenchmarkRunner> createBenchmarkRunner(InstructionBenchmark::ModeE Mode, @@ -54,19 +117,29 @@ public: virtual ~ExegesisTarget(); + // Returns the Pfm counters for the given CPU (or the default if no pfm + // counters are defined for this CPU). + const PfmCountersInfo &getPfmCounters(llvm::StringRef CpuName) const; + private: virtual bool matchesArch(llvm::Triple::ArchType Arch) const = 0; - // Targets can implement their own Latency/Uops benchmarks runners by + // Targets can implement their own snippet generators/benchmarks runners by // implementing these. + std::unique_ptr<SnippetGenerator> virtual createLatencySnippetGenerator( + const LLVMState &State) const; + std::unique_ptr<SnippetGenerator> virtual createUopsSnippetGenerator( + const LLVMState &State) const; std::unique_ptr<BenchmarkRunner> virtual createLatencyBenchmarkRunner( const LLVMState &State) const; std::unique_ptr<BenchmarkRunner> virtual createUopsBenchmarkRunner( const LLVMState &State) const; const ExegesisTarget *Next = nullptr; + const llvm::ArrayRef<CpuAndPfmCounters> CpuPfmCounters; }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_TARGET_H diff --git a/tools/llvm-exegesis/lib/Uops.cpp b/tools/llvm-exegesis/lib/Uops.cpp index 897f10808889..9768f4533f78 100644 --- a/tools/llvm-exegesis/lib/Uops.cpp +++ b/tools/llvm-exegesis/lib/Uops.cpp @@ -12,7 +12,7 @@ #include "Assembler.h" #include "BenchmarkRunner.h" #include "MCInstrDescView.h" -#include "PerfHelper.h" +#include "Target.h" // FIXME: Load constants into registers (e.g. with fld1) to not break // instructions like x87. @@ -78,47 +78,14 @@ // In that case we just use a greedy register assignment and hope for the // best. +namespace llvm { 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) { +getVariablesWithTiedOperands(const Instruction &Instr) { llvm::SmallVector<const Variable *, 8> Result; for (const auto &Var : Instr.Variables) - if (hasTiedOperands(Instr, Var)) + if (Var.hasTiedOperands()) Result.push_back(&Var); return Result; } @@ -131,20 +98,70 @@ static void remove(llvm::BitVector &a, const llvm::BitVector &b) { 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); +UopsSnippetGenerator::~UopsSnippetGenerator() = default; + +void UopsSnippetGenerator::instantiateMemoryOperands( + const unsigned ScratchSpacePointerInReg, + std::vector<InstructionTemplate> &Instructions) const { + if (ScratchSpacePointerInReg == 0) + return; // no memory operands. + const auto &ET = State.getExegesisTarget(); + const unsigned MemStep = ET.getMaxMemoryAccessSize(); + const size_t OriginalInstructionsSize = Instructions.size(); + size_t I = 0; + for (InstructionTemplate &IT : Instructions) { + ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep); + ++I; + } + + while (Instructions.size() < kMinNumDifferentAddresses) { + InstructionTemplate IT = Instructions[I % OriginalInstructionsSize]; + ET.fillMemoryOperands(IT, ScratchSpacePointerInReg, I * MemStep); + ++I; + Instructions.push_back(std::move(IT)); + } + assert(I * MemStep < BenchmarkRunner::ScratchSpace::kSize && + "not enough scratch space"); +} + +llvm::Expected<std::vector<CodeTemplate>> +UopsSnippetGenerator::generateCodeTemplates(const Instruction &Instr) const { + CodeTemplate CT; + const llvm::BitVector *ScratchSpaceAliasedRegs = nullptr; + if (Instr.hasMemoryOperands()) { + const auto &ET = State.getExegesisTarget(); + CT.ScratchSpacePointerInReg = + ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple()); + if (CT.ScratchSpacePointerInReg == 0) + return llvm::make_error<BenchmarkFailure>( + "Infeasible : target does not support memory instructions"); + ScratchSpaceAliasedRegs = + &State.getRATC().getRegister(CT.ScratchSpacePointerInReg).aliasedBits(); + // If the instruction implicitly writes to ScratchSpacePointerInReg , abort. + // FIXME: We could make a copy of the scratch register. + for (const auto &Op : Instr.Operands) { + if (Op.isDef() && Op.isImplicitReg() && + ScratchSpaceAliasedRegs->test(Op.getImplicitReg())) + return llvm::make_error<BenchmarkFailure>( + "Infeasible : memory instruction uses scratch memory register"); + } + } + const AliasingConfigurations SelfAliasing(Instr, Instr); + InstructionTemplate IT(Instr); if (SelfAliasing.empty()) { - return generateUnconstrainedPrototype(Instr, "instruction is parallel"); + CT.Info = "instruction is parallel, repeating a random one."; + CT.Instructions.push_back(std::move(IT)); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return getSingleton(std::move(CT)); } if (SelfAliasing.hasImplicitAliasing()) { - return generateUnconstrainedPrototype(Instr, "instruction is serial"); + CT.Info = "instruction is serial, repeating a random one."; + CT.Instructions.push_back(std::move(IT)); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return getSingleton(std::move(CT)); } - const auto TiedVariables = getTiedVariables(Instr); + const auto TiedVariables = getVariablesWithTiedOperands(Instr); if (!TiedVariables.empty()) { if (TiedVariables.size() > 1) return llvm::make_error<llvm::StringError>( @@ -152,84 +169,86 @@ UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const { 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); + const Operand &Op = Instr.getPrimaryOperand(*Var); + assert(Op.isReg()); + CT.Info = "instruction has tied variables using static renaming."; + for (const llvm::MCPhysReg Reg : + Op.getRegisterAliasing().sourceBits().set_bits()) { + if (ScratchSpaceAliasedRegs && ScratchSpaceAliasedRegs->test(Reg)) + continue; // Do not use the scratch memory address register. + InstructionTemplate TmpIT = IT; + TmpIT.getValueFor(*Var) = llvm::MCOperand::createReg(Reg); + CT.Instructions.push_back(std::move(TmpIT)); } - return std::move(Prototype); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return getSingleton(std::move(CT)); } - InstructionInstance II(Instr); + const auto &ReservedRegisters = State.getRATC().reservedRegisters(); // 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()); + if (Op.isReg() && Op.isExplicit() && Op.isDef() && !Op.isMemory()) { + auto PossibleRegisters = Op.getRegisterAliasing().sourceBits(); + remove(PossibleRegisters, ReservedRegisters); + // Do not use the scratch memory address register. + if (ScratchSpaceAliasedRegs) + remove(PossibleRegisters, *ScratchSpaceAliasedRegs); assert(PossibleRegisters.any() && "No register left to choose from"); const auto RandomReg = randomBit(PossibleRegisters); Defs.set(RandomReg); - II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); + IT.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()); + if (Op.isReg() && Op.isExplicit() && Op.isUse() && !Op.isMemory()) { + auto PossibleRegisters = Op.getRegisterAliasing().sourceBits(); + remove(PossibleRegisters, ReservedRegisters); + // Do not use the scratch memory address register. + if (ScratchSpaceAliasedRegs) + remove(PossibleRegisters, *ScratchSpaceAliasedRegs); remove(PossibleRegisters, DefAliases); assert(PossibleRegisters.any() && "No register left to choose from"); const auto RandomReg = randomBit(PossibleRegisters); - II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); + IT.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); } } - SnippetPrototype Prototype; - Prototype.Explanation = + CT.Info = "instruction has no tied variables picking Uses different from defs"; - Prototype.Snippet.push_back(std::move(II)); - return std::move(Prototype); + CT.Instructions.push_back(std::move(IT)); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return getSingleton(std::move(CT)); } -std::vector<BenchmarkMeasure> -UopsBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, - const unsigned NumRepetitions) const { - const auto &SchedModel = State.getSubtargetInfo().getSchedModel(); - +llvm::Expected<std::vector<BenchmarkMeasure>> +UopsBenchmarkRunner::runMeasurements(const FunctionExecutor &Executor) const { std::vector<BenchmarkMeasure> Result; - for (unsigned ProcResIdx = 1; - ProcResIdx < SchedModel.getNumProcResourceKinds(); ++ProcResIdx) { - const char *const PfmCounters = SchedModel.getExtraProcessorInfo() - .PfmCounters.IssueCounters[ProcResIdx]; - if (!PfmCounters) + const PfmCountersInfo &PCI = State.getPfmCounters(); + // Uops per port. + for (const auto *IssueCounter = PCI.IssueCounters, + *IssueCounterEnd = PCI.IssueCounters + PCI.NumIssueCounters; + IssueCounter != IssueCounterEnd; ++IssueCounter) { + if (!IssueCounter->Counter) 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}); + auto ExpectedCounterValue = Executor.runAndMeasure(IssueCounter->Counter); + if (!ExpectedCounterValue) + return ExpectedCounterValue.takeError(); + Result.push_back(BenchmarkMeasure::Create(IssueCounter->ProcResName, + *ExpectedCounterValue)); } - return Result; + // NumMicroOps. + if (const char *const UopsCounter = PCI.UopsCounter) { + auto ExpectedCounterValue = Executor.runAndMeasure(UopsCounter); + if (!ExpectedCounterValue) + return ExpectedCounterValue.takeError(); + Result.push_back( + BenchmarkMeasure::Create("NumMicroOps", *ExpectedCounterValue)); + } + return std::move(Result); } +constexpr const size_t UopsSnippetGenerator::kMinNumDifferentAddresses; + } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/lib/Uops.h b/tools/llvm-exegesis/lib/Uops.h index c7b5709f7d9e..b2a5ea177f44 100644 --- a/tools/llvm-exegesis/lib/Uops.h +++ b/tools/llvm-exegesis/lib/Uops.h @@ -16,26 +16,64 @@ #define LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H #include "BenchmarkRunner.h" +#include "SnippetGenerator.h" +namespace llvm { namespace exegesis { +class UopsSnippetGenerator : public SnippetGenerator { +public: + UopsSnippetGenerator(const LLVMState &State) : SnippetGenerator(State) {} + ~UopsSnippetGenerator() override; + + llvm::Expected<std::vector<CodeTemplate>> + generateCodeTemplates(const Instruction &Instr) const override; + + static constexpr const size_t kMinNumDifferentAddresses = 6; + +private: + // Instantiates memory operands within a snippet. + // To make computations as parallel as possible, we generate independant + // memory locations for instructions that load and store. If there are less + // than kMinNumDifferentAddresses in the original snippet, we duplicate + // instructions until there are this number of instructions. + // For example, assuming kMinNumDifferentAddresses=5 and + // getMaxMemoryAccessSize()=64, if the original snippet is: + // mov eax, [memory] + // we might generate: + // mov eax, [rdi] + // mov eax, [rdi + 64] + // mov eax, [rdi + 128] + // mov eax, [rdi + 192] + // mov eax, [rdi + 256] + // If the original snippet is: + // mov eax, [memory] + // add eax, [memory] + // we might generate: + // mov eax, [rdi] + // add eax, [rdi + 64] + // mov eax, [rdi + 128] + // add eax, [rdi + 192] + // mov eax, [rdi + 256] + void instantiateMemoryOperands( + unsigned ScratchSpaceReg, + std::vector<InstructionTemplate> &SnippetTemplate) const; +}; + class UopsBenchmarkRunner : public BenchmarkRunner { public: UopsBenchmarkRunner(const LLVMState &State) : BenchmarkRunner(State, InstructionBenchmark::Uops) {} ~UopsBenchmarkRunner() override; - llvm::Expected<SnippetPrototype> - generatePrototype(unsigned Opcode) const override; + static constexpr const size_t kMinNumDifferentAddresses = 6; private: - llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const; - - std::vector<BenchmarkMeasure> - runMeasurements(const ExecutableFunction &EF, - const unsigned NumRepetitions) const override; + llvm::Expected<std::vector<BenchmarkMeasure>> + runMeasurements(const FunctionExecutor &Executor) const override; }; } // namespace exegesis +} // namespace llvm #endif // LLVM_TOOLS_LLVM_EXEGESIS_UOPS_H diff --git a/tools/llvm-exegesis/lib/X86/Target.cpp b/tools/llvm-exegesis/lib/X86/Target.cpp index 5c417e325f5d..43bf9b750580 100644 --- a/tools/llvm-exegesis/lib/X86/Target.cpp +++ b/tools/llvm-exegesis/lib/X86/Target.cpp @@ -17,227 +17,526 @@ #include "X86Subtarget.h" #include "llvm/MC/MCInstBuilder.h" +namespace llvm { namespace exegesis { -namespace { +// Returns an error if we cannot handle the memory references in this +// instruction. +static Error isInvalidMemoryInstr(const Instruction &Instr) { + switch (Instr.Description->TSFlags & X86II::FormMask) { + default: + llvm_unreachable("Unknown FormMask value"); + // These have no memory access. + case X86II::Pseudo: + case X86II::RawFrm: + case X86II::MRMDestReg: + case X86II::MRMSrcReg: + case X86II::MRMSrcReg4VOp3: + case X86II::MRMSrcRegOp4: + case X86II::MRMXr: + case X86II::MRM0r: + case X86II::MRM1r: + case X86II::MRM2r: + case X86II::MRM3r: + case X86II::MRM4r: + case X86II::MRM5r: + case X86II::MRM6r: + case X86II::MRM7r: + case X86II::MRM_C0: + case X86II::MRM_C1: + case X86II::MRM_C2: + case X86II::MRM_C3: + case X86II::MRM_C4: + case X86II::MRM_C5: + case X86II::MRM_C6: + case X86II::MRM_C7: + case X86II::MRM_C8: + case X86II::MRM_C9: + case X86II::MRM_CA: + case X86II::MRM_CB: + case X86II::MRM_CC: + case X86II::MRM_CD: + case X86II::MRM_CE: + case X86II::MRM_CF: + case X86II::MRM_D0: + case X86II::MRM_D1: + case X86II::MRM_D2: + case X86II::MRM_D3: + case X86II::MRM_D4: + case X86II::MRM_D5: + case X86II::MRM_D6: + case X86II::MRM_D7: + case X86II::MRM_D8: + case X86II::MRM_D9: + case X86II::MRM_DA: + case X86II::MRM_DB: + case X86II::MRM_DC: + case X86II::MRM_DD: + case X86II::MRM_DE: + case X86II::MRM_DF: + case X86II::MRM_E0: + case X86II::MRM_E1: + case X86II::MRM_E2: + case X86II::MRM_E3: + case X86II::MRM_E4: + case X86II::MRM_E5: + case X86II::MRM_E6: + case X86II::MRM_E7: + case X86II::MRM_E8: + case X86II::MRM_E9: + case X86II::MRM_EA: + case X86II::MRM_EB: + case X86II::MRM_EC: + case X86II::MRM_ED: + case X86II::MRM_EE: + case X86II::MRM_EF: + case X86II::MRM_F0: + case X86II::MRM_F1: + case X86II::MRM_F2: + case X86II::MRM_F3: + case X86II::MRM_F4: + case X86II::MRM_F5: + case X86II::MRM_F6: + case X86II::MRM_F7: + case X86II::MRM_F8: + case X86II::MRM_F9: + case X86II::MRM_FA: + case X86II::MRM_FB: + case X86II::MRM_FC: + case X86II::MRM_FD: + case X86II::MRM_FE: + case X86II::MRM_FF: + case X86II::RawFrmImm8: + return Error::success(); + case X86II::AddRegFrm: + return (Instr.Description->Opcode == X86::POP16r || Instr.Description->Opcode == X86::POP32r || + Instr.Description->Opcode == X86::PUSH16r || Instr.Description->Opcode == X86::PUSH32r) + ? make_error<BenchmarkFailure>( + "unsupported opcode: unsupported memory access") + : Error::success(); + // These access memory and are handled. + case X86II::MRMDestMem: + case X86II::MRMSrcMem: + case X86II::MRMSrcMem4VOp3: + case X86II::MRMSrcMemOp4: + case X86II::MRMXm: + case X86II::MRM0m: + case X86II::MRM1m: + case X86II::MRM2m: + case X86II::MRM3m: + case X86II::MRM4m: + case X86II::MRM5m: + case X86II::MRM6m: + case X86II::MRM7m: + return Error::success(); + // These access memory and are not handled yet. + case X86II::RawFrmImm16: + case X86II::RawFrmMemOffs: + case X86II::RawFrmSrc: + case X86II::RawFrmDst: + case X86II::RawFrmDstSrc: + return make_error<BenchmarkFailure>( + "unsupported opcode: non uniform memory access"); + } +} -// 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")) { +static llvm::Error IsInvalidOpcode(const Instruction &Instr) { + const auto OpcodeName = Instr.Name; + if ((Instr.Description->TSFlags & X86II::FormMask) == X86II::Pseudo) + return llvm::make_error<BenchmarkFailure>( + "unsupported opcode: pseudo instruction"); + if (OpcodeName.startswith("POPF") || OpcodeName.startswith("PUSHF") || + OpcodeName.startswith("ADJCALLSTACK")) + return llvm::make_error<BenchmarkFailure>( + "unsupported opcode: Push/Pop/AdjCallStack"); + if (llvm::Error Error = isInvalidMemoryInstr(Instr)) + return Error; + // We do not handle instructions with OPERAND_PCREL. + for (const Operand &Op : Instr.Operands) + if (Op.isExplicit() && + Op.getExplicitOperandInfo().OperandType == llvm::MCOI::OPERAND_PCREL) return llvm::make_error<BenchmarkFailure>( - "Unsupported opcode: Push/Pop/AdjCallStack"); - } + "unsupported opcode: PC relative operand"); + // We do not handle second-form X87 instructions. We only handle first-form + // ones (_Fp), see comment in X86InstrFPStack.td. + for (const Operand &Op : Instr.Operands) + if (Op.isReg() && Op.isExplicit() && + Op.getExplicitOperandInfo().RegClass == llvm::X86::RSTRegClassID) + return llvm::make_error<BenchmarkFailure>( + "unsupported second-form X87 instruction"); + return llvm::Error::success(); +} - // 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!"); - } +static unsigned getX86FPFlags(const Instruction &Instr) { + return Instr.Description->TSFlags & llvm::X86II::FPTypeMask; +} - // Fallback to generic implementation. - return Impl::Base::generatePrototype(Opcode); - } +namespace { +class X86LatencySnippetGenerator : public LatencySnippetGenerator { +public: + using LatencySnippetGenerator::LatencySnippetGenerator; + + llvm::Expected<std::vector<CodeTemplate>> + generateCodeTemplates(const Instruction &Instr) const override; }; +} // namespace -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"); +llvm::Expected<std::vector<CodeTemplate>> +X86LatencySnippetGenerator::generateCodeTemplates( + const Instruction &Instr) const { + if (auto E = IsInvalidOpcode(Instr)) + return std::move(E); + + switch (getX86FPFlags(Instr)) { + case llvm::X86II::NotFP: + return LatencySnippetGenerator::generateCodeTemplates(Instr); + case llvm::X86II::ZeroArgFP: + case llvm::X86II::OneArgFP: + case llvm::X86II::SpecialFP: + case llvm::X86II::CompareFP: + case llvm::X86II::CondMovFP: + return llvm::make_error<BenchmarkFailure>("Unsupported x87 Instruction"); + 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. + return generateSelfAliasingCodeTemplates(Instr); + default: + llvm_unreachable("Unknown FP Type!"); } +} + +namespace { +class X86UopsSnippetGenerator : public UopsSnippetGenerator { +public: + using UopsSnippetGenerator::UopsSnippetGenerator; + + llvm::Expected<std::vector<CodeTemplate>> + generateCodeTemplates(const Instruction &Instr) const override; }; +} // namespace -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( +llvm::Expected<std::vector<CodeTemplate>> +X86UopsSnippetGenerator::generateCodeTemplates( + const Instruction &Instr) const { + if (auto E = IsInvalidOpcode(Instr)) + return std::move(E); + + switch (getX86FPFlags(Instr)) { + case llvm::X86II::NotFP: + return UopsSnippetGenerator::generateCodeTemplates(Instr); + case llvm::X86II::ZeroArgFP: + case llvm::X86II::OneArgFP: + case llvm::X86II::SpecialFP: + return llvm::make_error<BenchmarkFailure>("Unsupported x87 Instruction"); + 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 generateSelfAliasingCodeTemplates(Instr); + case llvm::X86II::CompareFP: + case llvm::X86II::CondMovFP: + // 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). + return generateUnconstrainedCodeTemplates( Instr, "instruction does not grow/shrink the FP stack"); + default: + llvm_unreachable("Unknown FP Type!"); } - llvm::Expected<SnippetPrototype> - handleCondMovFP(const Instruction &Instr) const { - return generateUnconstrainedPrototype( - Instr, "instruction does not grow/shrink the FP stack"); +} + +static unsigned getLoadImmediateOpcode(unsigned RegBitWidth) { + switch (RegBitWidth) { + case 8: + return llvm::X86::MOV8ri; + case 16: + return llvm::X86::MOV16ri; + case 32: + return llvm::X86::MOV32ri; + case 64: + return llvm::X86::MOV64ri; } -}; + llvm_unreachable("Invalid Value Width"); +} -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()); +// Generates instruction to load an immediate value into a register. +static llvm::MCInst loadImmediate(unsigned Reg, unsigned RegBitWidth, + const llvm::APInt &Value) { + if (Value.getBitWidth() > RegBitWidth) + llvm_unreachable("Value must fit in the Register"); + return llvm::MCInstBuilder(getLoadImmediateOpcode(RegBitWidth)) + .addReg(Reg) + .addImm(Value.getZExtValue()); +} + +// 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); +} + +// Reserves some space on the stack, fills it with the content of the provided +// constant and provide methods to load the stack value into a register. +namespace { +struct ConstantInliner { + explicit ConstantInliner(const llvm::APInt &Constant) : Constant_(Constant) {} + + std::vector<llvm::MCInst> loadAndFinalize(unsigned Reg, unsigned RegBitWidth, + unsigned Opcode); + + std::vector<llvm::MCInst> loadX87STAndFinalize(unsigned Reg); + + std::vector<llvm::MCInst> loadX87FPAndFinalize(unsigned Reg); + + std::vector<llvm::MCInst> popFlagAndFinalize(); + +private: + ConstantInliner &add(const llvm::MCInst &Inst) { + Instructions.push_back(Inst); + return *this; } - 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 {}; + void initStack(unsigned Bytes); + + static constexpr const unsigned kF80Bytes = 10; // 80 bits. + + llvm::APInt Constant_; + std::vector<llvm::MCInst> Instructions; +}; +} // namespace + +std::vector<llvm::MCInst> ConstantInliner::loadAndFinalize(unsigned Reg, + unsigned RegBitWidth, + unsigned Opcode) { + assert((RegBitWidth & 7) == 0 && "RegBitWidth must be a multiple of 8 bits"); + initStack(RegBitWidth / 8); + add(loadToReg(Reg, Opcode)); + add(releaseStackSpace(RegBitWidth / 8)); + return std::move(Instructions); +} + +std::vector<llvm::MCInst> ConstantInliner::loadX87STAndFinalize(unsigned Reg) { + initStack(kF80Bytes); + add(llvm::MCInstBuilder(llvm::X86::LD_F80m) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0)); // Segment + if (Reg != llvm::X86::ST0) + add(llvm::MCInstBuilder(llvm::X86::ST_Frr).addReg(Reg)); + add(releaseStackSpace(kF80Bytes)); + return std::move(Instructions); +} + +std::vector<llvm::MCInst> ConstantInliner::loadX87FPAndFinalize(unsigned Reg) { + initStack(kF80Bytes); + add(llvm::MCInstBuilder(llvm::X86::LD_Fp80m) + .addReg(Reg) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0)); // Segment + add(releaseStackSpace(kF80Bytes)); + return std::move(Instructions); +} + +std::vector<llvm::MCInst> ConstantInliner::popFlagAndFinalize() { + initStack(8); + add(llvm::MCInstBuilder(llvm::X86::POPF64)); + return std::move(Instructions); +} + +void ConstantInliner::initStack(unsigned Bytes) { + assert(Constant_.getBitWidth() <= Bytes * 8 && + "Value does not have the correct size"); + const llvm::APInt WideConstant = Constant_.getBitWidth() < Bytes * 8 + ? Constant_.sext(Bytes * 8) + : Constant_; + add(allocateStackSpace(Bytes)); + size_t ByteOffset = 0; + for (; Bytes - ByteOffset >= 4; ByteOffset += 4) + add(fillStackSpace( + llvm::X86::MOV32mi, ByteOffset, + WideConstant.extractBits(32, ByteOffset * 8).getZExtValue())); + if (Bytes - ByteOffset >= 2) { + add(fillStackSpace( + llvm::X86::MOV16mi, ByteOffset, + WideConstant.extractBits(16, ByteOffset * 8).getZExtValue())); + ByteOffset += 2; } + if (Bytes - ByteOffset >= 1) + add(fillStackSpace( + llvm::X86::MOV8mi, ByteOffset, + WideConstant.extractBits(8, ByteOffset * 8).getZExtValue())); +} - std::unique_ptr<BenchmarkRunner> - createLatencyBenchmarkRunner(const LLVMState &State) const override { - return llvm::make_unique<X86BenchmarkRunner<X86LatencyImpl>>(State); +#include "X86GenExegesis.inc" + +namespace { +class ExegesisX86Target : public ExegesisTarget { +public: + ExegesisX86Target() : ExegesisTarget(X86CpuPfmCounters) {} + +private: + void addTargetSpecificPasses(llvm::PassManagerBase &PM) const override; + + unsigned getScratchMemoryRegister(const llvm::Triple &TT) const override; + + unsigned getMaxMemoryAccessSize() const override { return 64; } + + void fillMemoryOperands(InstructionTemplate &IT, unsigned Reg, + unsigned Offset) const override; + + std::vector<llvm::MCInst> setRegTo(const llvm::MCSubtargetInfo &STI, + unsigned Reg, + const llvm::APInt &Value) const override; + + std::unique_ptr<SnippetGenerator> + createLatencySnippetGenerator(const LLVMState &State) const override { + return llvm::make_unique<X86LatencySnippetGenerator>(State); } - std::unique_ptr<BenchmarkRunner> - createUopsBenchmarkRunner(const LLVMState &State) const override { - return llvm::make_unique<X86BenchmarkRunner<X86UopsImpl>>(State); + std::unique_ptr<SnippetGenerator> + createUopsSnippetGenerator(const LLVMState &State) const override { + return llvm::make_unique<X86UopsSnippetGenerator>(State); } bool matchesArch(llvm::Triple::ArchType Arch) const override { return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86; } +}; +} // namespace -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; - } +void ExegesisX86Target::addTargetSpecificPasses( + llvm::PassManagerBase &PM) const { + // Lowers FP pseudo-instructions, e.g. ABS_Fp32 -> ABS_F. + PM.add(llvm::createX86FloatingPointStackifierPass()); +} - // 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); +unsigned +ExegesisX86Target::getScratchMemoryRegister(const llvm::Triple &TT) const { + if (!TT.isArch64Bit()) { + // FIXME: This would require popping from the stack, so we would have to + // add some additional setup code. + return 0; } + return TT.isOSWindows() ? llvm::X86::RCX : llvm::X86::RDI; +} - // 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); +void ExegesisX86Target::fillMemoryOperands(InstructionTemplate &IT, + unsigned Reg, + unsigned Offset) const { + assert(!isInvalidMemoryInstr(IT.Instr) && + "fillMemoryOperands requires a valid memory instruction"); + int MemOpIdx = X86II::getMemoryOperandNo(IT.Instr.Description->TSFlags); + assert(MemOpIdx >= 0 && "invalid memory operand index"); + // getMemoryOperandNo() ignores tied operands, so we have to add them back. + for (unsigned I = 0; I <= static_cast<unsigned>(MemOpIdx); ++I) { + const auto &Op = IT.Instr.Operands[I]; + if (Op.isTied() && Op.getTiedToIndex() < I) { + ++MemOpIdx; + } } + // Now fill in the memory operands. + const auto SetOp = [&IT](int OpIdx, const MCOperand &OpVal) { + const auto Op = IT.Instr.Operands[OpIdx]; + assert(Op.isMemory() && Op.isExplicit() && "invalid memory pattern"); + IT.getValueFor(Op) = OpVal; + }; + SetOp(MemOpIdx + 0, MCOperand::createReg(Reg)); // BaseReg + SetOp(MemOpIdx + 1, MCOperand::createImm(1)); // ScaleAmt + SetOp(MemOpIdx + 2, MCOperand::createReg(0)); // IndexReg + SetOp(MemOpIdx + 3, MCOperand::createImm(Offset)); // Disp + SetOp(MemOpIdx + 4, MCOperand::createReg(0)); // Segment +} - // 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 +std::vector<llvm::MCInst> +ExegesisX86Target::setRegTo(const llvm::MCSubtargetInfo &STI, unsigned Reg, + const llvm::APInt &Value) const { + if (llvm::X86::GR8RegClass.contains(Reg)) + return {loadImmediate(Reg, 8, Value)}; + if (llvm::X86::GR16RegClass.contains(Reg)) + return {loadImmediate(Reg, 16, Value)}; + if (llvm::X86::GR32RegClass.contains(Reg)) + return {loadImmediate(Reg, 32, Value)}; + if (llvm::X86::GR64RegClass.contains(Reg)) + return {loadImmediate(Reg, 64, Value)}; + ConstantInliner CI(Value); + if (llvm::X86::VR64RegClass.contains(Reg)) + return CI.loadAndFinalize(Reg, 64, llvm::X86::MMX_MOVQ64rm); + if (llvm::X86::VR128XRegClass.contains(Reg)) { + if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) + return CI.loadAndFinalize(Reg, 128, llvm::X86::VMOVDQU32Z128rm); + if (STI.getFeatureBits()[llvm::X86::FeatureAVX]) + return CI.loadAndFinalize(Reg, 128, llvm::X86::VMOVDQUrm); + return CI.loadAndFinalize(Reg, 128, llvm::X86::MOVDQUrm); } - - // 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); + if (llvm::X86::VR256XRegClass.contains(Reg)) { + if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) + return CI.loadAndFinalize(Reg, 256, llvm::X86::VMOVDQU32Z256rm); + if (STI.getFeatureBits()[llvm::X86::FeatureAVX]) + return CI.loadAndFinalize(Reg, 256, llvm::X86::VMOVDQUYrm); } -}; - -} // namespace + if (llvm::X86::VR512RegClass.contains(Reg)) + if (STI.getFeatureBits()[llvm::X86::FeatureAVX512]) + return CI.loadAndFinalize(Reg, 512, llvm::X86::VMOVDQU32Zrm); + if (llvm::X86::RSTRegClass.contains(Reg)) { + return CI.loadX87STAndFinalize(Reg); + } + if (llvm::X86::RFP32RegClass.contains(Reg) || + llvm::X86::RFP64RegClass.contains(Reg) || + llvm::X86::RFP80RegClass.contains(Reg)) { + return CI.loadX87FPAndFinalize(Reg); + } + if (Reg == llvm::X86::EFLAGS) + return CI.popFlagAndFinalize(); + return {}; // Not yet implemented. +} static ExegesisTarget *getTheExegesisX86Target() { static ExegesisX86Target Target; @@ -249,3 +548,4 @@ void InitializeX86ExegesisTarget() { } } // namespace exegesis +} // namespace llvm diff --git a/tools/llvm-exegesis/llvm-exegesis.cpp b/tools/llvm-exegesis/llvm-exegesis.cpp index 6b626b0eaa34..a28e68ec006d 100644 --- a/tools/llvm-exegesis/llvm-exegesis.cpp +++ b/tools/llvm-exegesis/llvm-exegesis.cpp @@ -22,101 +22,299 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/MC/MCInstBuilder.h" +#include "llvm/MC/MCObjectFileInfo.h" +#include "llvm/MC/MCParser/MCAsmParser.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" #include "llvm/MC/MCRegisterInfo.h" +#include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Object/ObjectFile.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" +#include "llvm/Support/SourceMgr.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> + +namespace llvm { +namespace exegesis { + +static cl::opt<int> OpcodeIndex("opcode-index", + cl::desc("opcode to measure, by index"), + cl::init(0)); + +static cl::opt<std::string> + OpcodeNames("opcode-name", + cl::desc("comma-separated list of opcodes to measure, by name"), + cl::init("")); + +static cl::opt<std::string> SnippetsFile("snippets-file", + cl::desc("code snippets to measure"), + cl::init("")); + +static cl::opt<std::string> BenchmarkFile("benchmarks-file", cl::desc(""), + cl::init("")); + +static cl::opt<exegesis::InstructionBenchmark::ModeE> + BenchmarkMode("mode", cl::desc("the mode to run"), + 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 cl::opt<unsigned> NumRepetitions("num-repetitions", - llvm::cl::desc("number of time to repeat the asm snippet"), - llvm::cl::init(10000)); + cl::desc("number of time to repeat the asm snippet"), + cl::init(10000)); -static llvm::cl::opt<bool> IgnoreInvalidSchedClass( +static cl::opt<bool> IgnoreInvalidSchedClass( "ignore-invalid-sched-class", - llvm::cl::desc("ignore instructions that do not define a sched class"), - llvm::cl::init(false)); + cl::desc("ignore instructions that do not define a sched class"), + cl::init(false)); -static llvm::cl::opt<unsigned> AnalysisNumPoints( +static cl::opt<unsigned> AnalysisNumPoints( "analysis-numpoints", - llvm::cl::desc("minimum number of points in an analysis cluster"), - llvm::cl::init(3)); + cl::desc("minimum number of points in an analysis cluster"), cl::init(3)); -static llvm::cl::opt<float> +static cl::opt<float> AnalysisEpsilon("analysis-epsilon", - llvm::cl::desc("dbscan epsilon for analysis clustering"), - llvm::cl::init(0.1)); + cl::desc("dbscan epsilon for analysis clustering"), + 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> +static cl::opt<std::string> + AnalysisClustersOutputFile("analysis-clusters-output-file", cl::desc(""), + cl::init("-")); +static cl::opt<std::string> AnalysisInconsistenciesOutputFile("analysis-inconsistencies-output-file", - llvm::cl::desc(""), llvm::cl::init("-")); + cl::desc(""), cl::init("-")); + +static cl::opt<std::string> + CpuName("mcpu", + cl::desc( + "cpu name to use for pfm counters, leave empty to autodetect"), + cl::init("")); -namespace exegesis { -static llvm::ExitOnError ExitOnErr; +static 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)) +// Checks that only one of OpcodeNames, OpcodeIndex or SnippetsFile is provided, +// and returns the opcode indices or {} if snippets should be read from +// `SnippetsFile`. +static std::vector<unsigned> +getOpcodesOrDie(const llvm::MCInstrInfo &MCInstrInfo) { + const size_t NumSetFlags = (OpcodeNames.empty() ? 0 : 1) + + (OpcodeIndex == 0 ? 0 : 1) + + (SnippetsFile.empty() ? 0 : 1); + if (NumSetFlags != 1) llvm::report_fatal_error( - "please provide one and only one of 'opcode-index' or 'opcode-name'"); + "please provide one and only one of 'opcode-index', 'opcode-name' or " + "'snippets-file'"); + if (!SnippetsFile.empty()) + return {}; if (OpcodeIndex > 0) - return OpcodeIndex; + return {static_cast<unsigned>(OpcodeIndex)}; + if (OpcodeIndex < 0) { + std::vector<unsigned> Result; + for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I) + Result.push_back(I); + return Result; + } // 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)); + const auto ResolveName = + [&MCInstrInfo](llvm::StringRef OpcodeName) -> unsigned { + for (unsigned I = 1, E = MCInstrInfo.getNumOpcodes(); I < E; ++I) + if (MCInstrInfo.getName(I) == OpcodeName) + return I; + return 0u; + }; + llvm::SmallVector<llvm::StringRef, 2> Pieces; + llvm::StringRef(OpcodeNames.getValue()) + .split(Pieces, ",", /* MaxSplit */ -1, /* KeepEmpty */ false); + std::vector<unsigned> Result; + for (const llvm::StringRef OpcodeName : Pieces) { + if (unsigned Opcode = ResolveName(OpcodeName)) + Result.push_back(Opcode); + else + llvm::report_fatal_error( + llvm::Twine("unknown opcode ").concat(OpcodeName)); + } + return Result; } -static BenchmarkResultContext -getBenchmarkResultContext(const LLVMState &State) { - BenchmarkResultContext Ctx; +// Generates code snippets for opcode `Opcode`. +static llvm::Expected<std::vector<BenchmarkCode>> +generateSnippets(const LLVMState &State, unsigned Opcode) { + const Instruction &Instr = State.getIC().getInstr(Opcode); + const llvm::MCInstrDesc &InstrDesc = *Instr.Description; + // 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"); + + const std::unique_ptr<SnippetGenerator> Generator = + State.getExegesisTarget().createSnippetGenerator(BenchmarkMode, State); + if (!Generator) + llvm::report_fatal_error("cannot create snippet generator"); + return Generator->generateConfigurations(Instr); +} + +namespace { + +// An MCStreamer that reads a BenchmarkCode definition from a file. +// The BenchmarkCode definition is just an asm file, with additional comments to +// specify which registers should be defined or are live on entry. +class BenchmarkCodeStreamer : public llvm::MCStreamer, + public llvm::AsmCommentConsumer { +public: + explicit BenchmarkCodeStreamer(llvm::MCContext *Context, + const llvm::MCRegisterInfo *TheRegInfo, + BenchmarkCode *Result) + : llvm::MCStreamer(*Context), RegInfo(TheRegInfo), Result(Result) {} + + // Implementation of the llvm::MCStreamer interface. We only care about + // instructions. + void EmitInstruction(const llvm::MCInst &Instruction, + const llvm::MCSubtargetInfo &STI, + bool PrintSchedInfo) override { + Result->Instructions.push_back(Instruction); + } + + // Implementation of the llvm::AsmCommentConsumer. + void HandleComment(llvm::SMLoc Loc, llvm::StringRef CommentText) override { + CommentText = CommentText.trim(); + if (!CommentText.consume_front("LLVM-EXEGESIS-")) + return; + if (CommentText.consume_front("DEFREG")) { + // LLVM-EXEGESIS-DEFREF <reg> <hex_value> + RegisterValue RegVal; + llvm::SmallVector<llvm::StringRef, 2> Parts; + CommentText.split(Parts, ' ', /*unlimited splits*/ -1, + /*do not keep empty strings*/ false); + if (Parts.size() != 2) { + llvm::errs() << "invalid comment 'LLVM-EXEGESIS-DEFREG " << CommentText + << "\n"; + ++InvalidComments; + } + if (!(RegVal.Register = findRegisterByName(Parts[0].trim()))) { + llvm::errs() << "unknown register in 'LLVM-EXEGESIS-DEFREG " + << CommentText << "\n"; + ++InvalidComments; + return; + } + const llvm::StringRef HexValue = Parts[1].trim(); + RegVal.Value = llvm::APInt( + /* each hex digit is 4 bits */ HexValue.size() * 4, HexValue, 16); + Result->RegisterInitialValues.push_back(std::move(RegVal)); + return; + } + if (CommentText.consume_front("LIVEIN")) { + // LLVM-EXEGESIS-LIVEIN <reg> + if (unsigned Reg = findRegisterByName(CommentText.ltrim())) + Result->LiveIns.push_back(Reg); + else { + llvm::errs() << "unknown register in 'LLVM-EXEGESIS-LIVEIN " + << CommentText << "\n"; + ++InvalidComments; + } + return; + } + } - const llvm::MCInstrInfo &InstrInfo = State.getInstrInfo(); - for (unsigned E = InstrInfo.getNumOpcodes(), I = 0; I < E; ++I) - Ctx.addInstrEntry(I, InstrInfo.getName(I).data()); + unsigned numInvalidComments() const { return InvalidComments; } - const llvm::MCRegisterInfo &RegInfo = State.getRegInfo(); - for (unsigned E = RegInfo.getNumRegs(), I = 0; I < E; ++I) - Ctx.addRegEntry(I, RegInfo.getName(I)); +private: + // We only care about instructions, we don't implement this part of the API. + void EmitCommonSymbol(llvm::MCSymbol *Symbol, uint64_t Size, + unsigned ByteAlignment) override {} + bool EmitSymbolAttribute(llvm::MCSymbol *Symbol, + llvm::MCSymbolAttr Attribute) override { + return false; + } + void EmitValueToAlignment(unsigned ByteAlignment, int64_t Value, + unsigned ValueSize, + unsigned MaxBytesToEmit) override {} + void EmitZerofill(llvm::MCSection *Section, llvm::MCSymbol *Symbol, + uint64_t Size, unsigned ByteAlignment, + llvm::SMLoc Loc) override {} + + unsigned findRegisterByName(const llvm::StringRef RegName) const { + // FIXME: Can we do better than this ? + for (unsigned I = 0, E = RegInfo->getNumRegs(); I < E; ++I) { + if (RegName == RegInfo->getName(I)) + return I; + } + llvm::errs() << "'" << RegName + << "' is not a valid register name for the target\n"; + return 0; + } - return Ctx; + const llvm::MCRegisterInfo *const RegInfo; + BenchmarkCode *const Result; + unsigned InvalidComments = 0; +}; + +} // namespace + +// Reads code snippets from file `Filename`. +static llvm::Expected<std::vector<BenchmarkCode>> +readSnippets(const LLVMState &State, llvm::StringRef Filename) { + llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> BufferPtr = + llvm::MemoryBuffer::getFileOrSTDIN(Filename); + if (std::error_code EC = BufferPtr.getError()) { + return llvm::make_error<BenchmarkFailure>( + "cannot read snippet: " + Filename + ": " + EC.message()); + } + llvm::SourceMgr SM; + SM.AddNewSourceBuffer(std::move(BufferPtr.get()), llvm::SMLoc()); + + BenchmarkCode Result; + + llvm::MCObjectFileInfo ObjectFileInfo; + const llvm::TargetMachine &TM = State.getTargetMachine(); + llvm::MCContext Context(TM.getMCAsmInfo(), TM.getMCRegisterInfo(), + &ObjectFileInfo); + ObjectFileInfo.InitMCObjectFileInfo(TM.getTargetTriple(), /*PIC*/ false, + Context); + BenchmarkCodeStreamer Streamer(&Context, TM.getMCRegisterInfo(), &Result); + const std::unique_ptr<llvm::MCAsmParser> AsmParser( + llvm::createMCAsmParser(SM, Context, Streamer, *TM.getMCAsmInfo())); + if (!AsmParser) + return llvm::make_error<BenchmarkFailure>("cannot create asm parser"); + AsmParser->getLexer().setCommentConsumer(&Streamer); + + const std::unique_ptr<llvm::MCTargetAsmParser> TargetAsmParser( + TM.getTarget().createMCAsmParser(*TM.getMCSubtargetInfo(), *AsmParser, + *TM.getMCInstrInfo(), + llvm::MCTargetOptions())); + + if (!TargetAsmParser) + return llvm::make_error<BenchmarkFailure>( + "cannot create target asm parser"); + AsmParser->setTargetParser(*TargetAsmParser); + + if (AsmParser->Run(false)) + return llvm::make_error<BenchmarkFailure>("cannot parse asm file"); + if (Streamer.numInvalidComments()) + return llvm::make_error<BenchmarkFailure>( + llvm::Twine("found ") + .concat(llvm::Twine(Streamer.numInvalidComments())) + .concat(" invalid LLVM-EXEGESIS comments")); + return std::vector<BenchmarkCode>{std::move(Result)}; } void benchmarkMain() { @@ -125,19 +323,37 @@ void benchmarkMain() { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); + llvm::InitializeNativeTargetAsmParser(); #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 LLVMState State(CpuName); + const auto Opcodes = getOpcodesOrDie(State.getInstrInfo()); + + std::vector<BenchmarkCode> Configurations; + if (!Opcodes.empty()) { + for (const unsigned Opcode : Opcodes) { + // Ignore instructions without a sched class if + // -ignore-invalid-sched-class is passed. + if (IgnoreInvalidSchedClass && + State.getInstrInfo().get(Opcode).getSchedClass() == 0) { + llvm::errs() << State.getInstrInfo().getName(Opcode) + << ": ignoring instruction without sched class\n"; + continue; + } + auto ConfigsForInstr = generateSnippets(State, Opcode); + if (!ConfigsForInstr) { + llvm::logAllUnhandledErrors( + ConfigsForInstr.takeError(), llvm::errs(), + llvm::Twine(State.getInstrInfo().getName(Opcode)).concat(": ")); + continue; + } + std::move(ConfigsForInstr->begin(), ConfigsForInstr->end(), + std::back_inserter(Configurations)); + } + } else { + Configurations = ExitOnErr(readSnippets(State, SnippetsFile)); } const std::unique_ptr<BenchmarkRunner> Runner = @@ -153,12 +369,11 @@ void benchmarkMain() { 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)); - + for (const BenchmarkCode &Conf : Configurations) { + InstructionBenchmark Result = + Runner->runConfiguration(Conf, NumRepetitions); + ExitOnErr(Result.writeYaml(State, BenchmarkFile)); + } exegesis::pfm::pfmTerminate(); } @@ -191,10 +406,9 @@ static void analysisMain() { llvm::InitializeNativeTargetAsmPrinter(); llvm::InitializeNativeTargetDisassembler(); // Read benchmarks. - const LLVMState State; + const LLVMState State(""); const std::vector<InstructionBenchmark> Points = - ExitOnErr(InstructionBenchmark::readYamls( - getBenchmarkResultContext(State), BenchmarkFile)); + ExitOnErr(InstructionBenchmark::readYamls(State, BenchmarkFile)); llvm::outs() << "Parsed " << Points.size() << " benchmark points\n"; if (Points.empty()) { llvm::errs() << "no benchmarks to analyze\n"; @@ -223,9 +437,11 @@ static void analysisMain() { } } // namespace exegesis +} // namespace llvm int main(int Argc, char **Argv) { - llvm::cl::ParseCommandLineOptions(Argc, Argv, ""); + using namespace llvm; + cl::ParseCommandLineOptions(Argc, Argv, ""); exegesis::ExitOnErr.setExitCodeMapper([](const llvm::Error &Err) { if (Err.isA<llvm::StringError>()) @@ -233,7 +449,7 @@ int main(int Argc, char **Argv) { return EXIT_FAILURE; }); - if (BenchmarkMode == exegesis::InstructionBenchmark::Unknown) { + if (exegesis::BenchmarkMode == exegesis::InstructionBenchmark::Unknown) { exegesis::analysisMain(); } else { exegesis::benchmarkMain(); diff --git a/tools/llvm-go/llvm-go.go b/tools/llvm-go/llvm-go.go index 4d65de623462..8215483b8332 100644 --- a/tools/llvm-go/llvm-go.go +++ b/tools/llvm-go/llvm-go.go @@ -50,6 +50,7 @@ var components = []string{ "bitwriter", "codegen", "core", + "coroutines", "debuginfodwarf", "executionengine", "instrumentation", diff --git a/tools/llvm-demangle-fuzzer/CMakeLists.txt b/tools/llvm-itanium-demangle-fuzzer/CMakeLists.txt index 0fe711cdb16c..07f02a35b203 100644 --- a/tools/llvm-demangle-fuzzer/CMakeLists.txt +++ b/tools/llvm-itanium-demangle-fuzzer/CMakeLists.txt @@ -4,7 +4,7 @@ set(LLVM_LINK_COMPONENTS Support ) -add_llvm_fuzzer(llvm-demangle-fuzzer - llvm-demangle-fuzzer.cpp +add_llvm_fuzzer(llvm-itanium-demangle-fuzzer + llvm-itanium-demangle-fuzzer.cpp DUMMY_MAIN DummyDemanglerFuzzer.cpp ) diff --git a/tools/llvm-demangle-fuzzer/DummyDemanglerFuzzer.cpp b/tools/llvm-itanium-demangle-fuzzer/DummyDemanglerFuzzer.cpp index a2bf9f1b807e..a2bf9f1b807e 100644 --- a/tools/llvm-demangle-fuzzer/DummyDemanglerFuzzer.cpp +++ b/tools/llvm-itanium-demangle-fuzzer/DummyDemanglerFuzzer.cpp diff --git a/tools/llvm-demangle-fuzzer/llvm-demangle-fuzzer.cpp b/tools/llvm-itanium-demangle-fuzzer/llvm-itanium-demangle-fuzzer.cpp index 07c290a0be5c..07c290a0be5c 100644 --- a/tools/llvm-demangle-fuzzer/llvm-demangle-fuzzer.cpp +++ b/tools/llvm-itanium-demangle-fuzzer/llvm-itanium-demangle-fuzzer.cpp diff --git a/tools/llvm-lto/llvm-lto.cpp b/tools/llvm-lto/llvm-lto.cpp index 75668a9dd8b6..b6facc919b51 100644 --- a/tools/llvm-lto/llvm-lto.cpp +++ b/tools/llvm-lto/llvm-lto.cpp @@ -158,7 +158,7 @@ static cl::opt<int> ThinLTOCachePruningInterval("thinlto-cache-pruning-interval", cl::init(1200), cl::desc("Set ThinLTO cache pruning interval.")); -static cl::opt<int> +static cl::opt<unsigned long long> ThinLTOCacheMaxSizeBytes("thinlto-cache-max-size-bytes", cl::desc("Set ThinLTO cache pruning directory maximum size in bytes.")); @@ -166,6 +166,10 @@ 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<unsigned> + ThinLTOCacheEntryExpiration("thinlto-cache-entry-expiration", cl::init(604800) /* 1w */, + cl::desc("Set ThinLTO cache entry expiration time.")); + static cl::opt<std::string> ThinLTOSaveTempsPrefix( "thinlto-save-temps", cl::desc("Save ThinLTO temp files using filenames created by adding " @@ -481,6 +485,7 @@ public: ThinGenerator.setTargetOptions(Options); ThinGenerator.setCacheDir(ThinLTOCacheDir); ThinGenerator.setCachePruningInterval(ThinLTOCachePruningInterval); + ThinGenerator.setCacheEntryExpiration(ThinLTOCacheEntryExpiration); ThinGenerator.setCacheMaxSizeFiles(ThinLTOCacheMaxSizeFiles); ThinGenerator.setCacheMaxSizeBytes(ThinLTOCacheMaxSizeBytes); ThinGenerator.setFreestanding(EnableFreestanding); @@ -557,11 +562,14 @@ private: auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { + LLVMContext Ctx; + auto TheModule = loadModule(Filename, Ctx); + // Build a map of module to the GUIDs and summary objects that should // be written to its index. std::map<std::string, GVSummaryMapTy> ModuleToSummariesForIndex; - ThinLTOCodeGenerator::gatherImportedSummariesForModule( - Filename, *Index, ModuleToSummariesForIndex); + ThinGenerator.gatherImportedSummariesForModule(*TheModule, *Index, + ModuleToSummariesForIndex); std::string OutputName = OutputFilename; if (OutputName.empty()) { @@ -589,12 +597,14 @@ private: auto Index = loadCombinedIndex(); for (auto &Filename : InputFilenames) { + LLVMContext Ctx; + auto TheModule = loadModule(Filename, Ctx); std::string OutputName = OutputFilename; if (OutputName.empty()) { OutputName = Filename + ".imports"; } OutputName = getThinLTOOutputFile(OutputName, OldPrefix, NewPrefix); - ThinLTOCodeGenerator::emitImports(Filename, OutputName, *Index); + ThinGenerator.emitImports(*TheModule, OutputName, *Index); } } diff --git a/tools/llvm-lto2/llvm-lto2.cpp b/tools/llvm-lto2/llvm-lto2.cpp index 442973f90209..26426367e252 100644 --- a/tools/llvm-lto2/llvm-lto2.cpp +++ b/tools/llvm-lto2/llvm-lto2.cpp @@ -23,6 +23,7 @@ #include "llvm/LTO/LTO.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" +#include "llvm/Support/InitLLVM.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/Threading.h" @@ -388,6 +389,7 @@ static int dumpSymtab(int argc, char **argv) { } int main(int argc, char **argv) { + InitLLVM X(argc, argv); InitializeAllTargets(); InitializeAllTargetMCs(); InitializeAllAsmPrinters(); 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 8aad6ab67423..8731cf60fca8 100644 --- a/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp +++ b/tools/llvm-mc-assemble-fuzzer/llvm-mc-assemble-fuzzer.cpp @@ -18,6 +18,7 @@ #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" @@ -230,9 +231,9 @@ int AssembleOneInput(const uint8_t *Data, size_t Size) { MCCodeEmitter *CE = TheTarget->createMCCodeEmitter(*MCII, *MRI, Ctx); MCAsmBackend *MAB = TheTarget->createMCAsmBackend(*STI, *MRI, MCOptions); Str.reset(TheTarget->createMCObjectStreamer( - TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), *OS, - std::unique_ptr<MCCodeEmitter>(CE), *STI, MCOptions.MCRelaxAll, - MCOptions.MCIncrementalLinkerCompatible, + TheTriple, Ctx, std::unique_ptr<MCAsmBackend>(MAB), + MAB->createObjectWriter(*OS), std::unique_ptr<MCCodeEmitter>(CE), *STI, + MCOptions.MCRelaxAll, MCOptions.MCIncrementalLinkerCompatible, /*DWARFMustBeAtTheEnd*/ false)); } const int Res = AssembleInput(ProgName, TheTarget, SrcMgr, Ctx, *Str, *MAI, *STI, diff --git a/tools/llvm-mc/llvm-mc.cpp b/tools/llvm-mc/llvm-mc.cpp index f494d02f3bca..c0976502f545 100644 --- a/tools/llvm-mc/llvm-mc.cpp +++ b/tools/llvm-mc/llvm-mc.cpp @@ -164,6 +164,10 @@ MainFileName("main-file-name", static cl::opt<bool> SaveTempLabels("save-temp-labels", cl::desc("Don't discard temporary labels")); +static cl::opt<bool> LexMasmIntegers( + "masm-integers", + cl::desc("Enable binary and hex masm integers (0b110 and 0ABCh)")); + static cl::opt<bool> NoExecStack("no-exec-stack", cl::desc("File doesn't need an exec stack")); @@ -293,6 +297,7 @@ static int AssembleInput(const char *ProgName, const Target *TheTarget, return SymbolResult; Parser->setShowParsedOperands(ShowInstOperands); Parser->setTargetParser(*TAP); + Parser->getLexer().setLexMasmIntegers(LexMasmIntegers); int Res = Parser->Run(NoInitialTextSection); @@ -313,7 +318,6 @@ int main(int argc, char **argv) { cl::ParseCommandLineOptions(argc, argv, "llvm machine code playground\n"); MCTargetOptions MCOptions = InitMCTargetOptionsFromFlags(); - TripleName = Triple::normalize(TripleName); setDwarfDebugFlags(argc, argv); setDwarfDebugProducer(); diff --git a/tools/llvm-mca/CMakeLists.txt b/tools/llvm-mca/CMakeLists.txt index bb657b8d19fe..1fceb08c1ca3 100644 --- a/tools/llvm-mca/CMakeLists.txt +++ b/tools/llvm-mca/CMakeLists.txt @@ -1,42 +1,31 @@ +include_directories(include) + set(LLVM_LINK_COMPONENTS AllTargetsAsmPrinters AllTargetsAsmParsers AllTargetsDescs AllTargetsDisassemblers AllTargetsInfos + MCA 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 + CodeRegion.cpp + CodeRegionGenerator.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 + Views/DispatchStatistics.cpp + Views/InstructionInfoView.cpp + Views/RegisterFileStatistics.cpp + Views/ResourcePressureView.cpp + Views/RetireControlUnitStatistics.cpp + Views/SchedulerStatistics.cpp + Views/SummaryView.cpp + Views/TimelineView.cpp + Views/View.cpp ) + +set(LLVM_MCA_SOURCE_DIR ${CURRENT_SOURCE_DIR}) diff --git a/tools/llvm-mca/CodeRegion.cpp b/tools/llvm-mca/CodeRegion.cpp index 896865996504..29a27c50c171 100644 --- a/tools/llvm-mca/CodeRegion.cpp +++ b/tools/llvm-mca/CodeRegion.cpp @@ -14,11 +14,10 @@ #include "CodeRegion.h" -using namespace llvm; - +namespace llvm { namespace mca { -bool CodeRegion::isLocInRange(SMLoc Loc) const { +bool CodeRegion::isLocInRange(llvm::SMLoc Loc) const { if (RangeEnd.isValid() && Loc.getPointer() > RangeEnd.getPointer()) return false; if (RangeStart.isValid() && Loc.getPointer() < RangeStart.getPointer()) @@ -26,11 +25,11 @@ bool CodeRegion::isLocInRange(SMLoc Loc) const { return true; } -void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) { +void CodeRegions::beginRegion(llvm::StringRef Description, llvm::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, + SM.PrintMessage(Loc, llvm::SourceMgr::DK_Warning, "Ignoring invalid region start"); return; } @@ -41,26 +40,28 @@ void CodeRegions::beginRegion(StringRef Description, SMLoc Loc) { addRegion(Description, Loc); } -void CodeRegions::endRegion(SMLoc Loc) { +void CodeRegions::endRegion(llvm::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"); + SM.PrintMessage(Loc, llvm::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(); +void CodeRegions::addInstruction(const llvm::MCInst &Instruction) { + const llvm::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)); + (*It)->addInstruction(Instruction); } } // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/CodeRegion.h b/tools/llvm-mca/CodeRegion.h index 7f0025e4884c..867aa18bb4fe 100644 --- a/tools/llvm-mca/CodeRegion.h +++ b/tools/llvm-mca/CodeRegion.h @@ -34,12 +34,14 @@ #ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_H #define LLVM_TOOLS_LLVM_MCA_CODEREGION_H +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include <vector> +namespace llvm { namespace mca { /// A region of assembly code. @@ -49,7 +51,7 @@ 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; + std::vector<llvm::MCInst> Instructions; // Source location range. llvm::SMLoc RangeStart; llvm::SMLoc RangeEnd; @@ -61,8 +63,8 @@ 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)); + void addInstruction(const llvm::MCInst &Instruction) { + Instructions.emplace_back(Instruction); } llvm::SMLoc startLoc() const { return RangeStart; } @@ -72,10 +74,7 @@ public: 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::ArrayRef<llvm::MCInst> getInstructions() const { return Instructions; } llvm::StringRef getDescription() const { return Description; } }; @@ -106,26 +105,26 @@ public: void beginRegion(llvm::StringRef Description, llvm::SMLoc Loc); void endRegion(llvm::SMLoc Loc); - void addInstruction(std::unique_ptr<const llvm::MCInst> Instruction); + void addInstruction(const llvm::MCInst &Instruction); + llvm::SourceMgr &getSourceMgr() const { return SM; } 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 { + llvm::ArrayRef<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(); - }); + return llvm::all_of(Regions, [](const std::unique_ptr<CodeRegion> &Region) { + return Region->empty(); + }); } }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/CodeRegionGenerator.cpp b/tools/llvm-mca/CodeRegionGenerator.cpp new file mode 100644 index 000000000000..5bd37adeeae9 --- /dev/null +++ b/tools/llvm-mca/CodeRegionGenerator.cpp @@ -0,0 +1,137 @@ +//===----------------------- CodeRegionGenerator.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 classes responsible for generating llvm-mca +/// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions, +/// so the classes here provide the input-to-CodeRegions translation. +// +//===----------------------------------------------------------------------===// + +#include "CodeRegionGenerator.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/MC/MCParser/MCTargetAsmParser.h" +#include "llvm/MC/MCStreamer.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/SMLoc.h" +#include <memory> + +namespace llvm { +namespace mca { + +// This virtual dtor serves as the anchor for the CodeRegionGenerator class. +CodeRegionGenerator::~CodeRegionGenerator() {} + +// A comment consumer that parses strings. The only valid tokens are strings. +class MCACommentConsumer : public AsmCommentConsumer { +public: + CodeRegions &Regions; + + MCACommentConsumer(CodeRegions &R) : Regions(R) {} + void HandleComment(SMLoc Loc, StringRef CommentText) override; +}; + +// This class provides the callbacks that occur when parsing input assembly. +class MCStreamerWrapper final : public MCStreamer { + 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 & /* unused */, + bool /* unused */) override { + Regions.addInstruction(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 {} + + ArrayRef<MCInst> GetInstructionSequence(unsigned Index) const { + return Regions.getInstructionSequence(Index); + } +}; + +void MCACommentConsumer::HandleComment(SMLoc Loc, StringRef CommentText) { + // 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; + } + + // Try to parse the LLVM-MCA-BEGIN comment. + 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); +} + +Expected<const CodeRegions &> AsmCodeRegionGenerator::parseCodeRegions() { + MCTargetOptions Opts; + Opts.PreserveAsmComments = false; + MCStreamerWrapper Str(Ctx, Regions); + + // Create a MCAsmParser and setup the lexer to recognize llvm-mca ASM + // comments. + std::unique_ptr<MCAsmParser> Parser( + createMCAsmParser(Regions.getSourceMgr(), Ctx, Str, MAI)); + MCAsmLexer &Lexer = Parser->getLexer(); + MCACommentConsumer CC(Regions); + Lexer.setCommentConsumer(&CC); + + // Create a target-specific parser and perform the parse. + std::unique_ptr<MCTargetAsmParser> TAP( + TheTarget.createMCAsmParser(STI, *Parser, MCII, Opts)); + if (!TAP) + return make_error<StringError>( + "This target does not support assembly parsing.", + inconvertibleErrorCode()); + Parser->setTargetParser(*TAP); + Parser->Run(false); + + // Get the assembler dialect from the input. llvm-mca will use this as the + // default dialect when printing reports. + AssemblerDialect = Parser->getAssemblerDialect(); + return Regions; +} + +} // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/CodeRegionGenerator.h b/tools/llvm-mca/CodeRegionGenerator.h new file mode 100644 index 000000000000..892cafb92686 --- /dev/null +++ b/tools/llvm-mca/CodeRegionGenerator.h @@ -0,0 +1,70 @@ +//===----------------------- CodeRegionGenerator.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 declares classes responsible for generating llvm-mca +/// CodeRegions from various types of input. llvm-mca only analyzes CodeRegions, +/// so the classes here provide the input-to-CodeRegions translation. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H +#define LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H + +#include "CodeRegion.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCContext.h" +#include "llvm/MC/MCSubtargetInfo.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/TargetRegistry.h" +#include <memory> + +namespace llvm { +namespace mca { + +/// This class is responsible for parsing the input given to the llvm-mca +/// driver, and converting that into a CodeRegions instance. +class CodeRegionGenerator { +protected: + CodeRegions Regions; + CodeRegionGenerator(const CodeRegionGenerator &) = delete; + CodeRegionGenerator &operator=(const CodeRegionGenerator &) = delete; + +public: + CodeRegionGenerator(SourceMgr &SM) : Regions(SM) {} + virtual ~CodeRegionGenerator(); + virtual Expected<const CodeRegions &> parseCodeRegions() = 0; +}; + +/// This class is responsible for parsing input ASM and generating +/// a CodeRegions instance. +class AsmCodeRegionGenerator final : public CodeRegionGenerator { + const Target &TheTarget; + MCContext &Ctx; + const MCAsmInfo &MAI; + const MCSubtargetInfo &STI; + const MCInstrInfo &MCII; + unsigned AssemblerDialect; // This is set during parsing. + +public: + AsmCodeRegionGenerator(const Target &T, SourceMgr &SM, MCContext &C, + const MCAsmInfo &A, const MCSubtargetInfo &S, + const MCInstrInfo &I) + : CodeRegionGenerator(SM), TheTarget(T), Ctx(C), MAI(A), STI(S), MCII(I), + AssemblerDialect(0) {} + + unsigned getAssemblerDialect() const { return AssemblerDialect; } + Expected<const CodeRegions &> parseCodeRegions() override; +}; + +} // namespace mca +} // namespace llvm + +#endif // LLVM_TOOLS_LLVM_MCA_CODEREGION_GENERATOR_H diff --git a/tools/llvm-mca/Context.cpp b/tools/llvm-mca/Context.cpp deleted file mode 100644 index 685714e64b92..000000000000 --- a/tools/llvm-mca/Context.cpp +++ /dev/null @@ -1,63 +0,0 @@ -//===---------------------------- 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 deleted file mode 100644 index cf483fa7b37d..000000000000 --- a/tools/llvm-mca/Context.h +++ /dev/null @@ -1,68 +0,0 @@ -//===---------------------------- 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 deleted file mode 100644 index 1f508886c298..000000000000 --- a/tools/llvm-mca/DispatchStage.cpp +++ /dev/null @@ -1,153 +0,0 @@ -//===--------------------- 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. - bool IsDependencyBreaking = IS.isDependencyBreaking(); - for (std::unique_ptr<ReadState> &RS : IS.getUses()) - if (RS->isImplicitRead() || !IsDependencyBreaking) - 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. - bool ShouldAllocateRegisters = - !(Desc.isZeroLatency() && IsDependencyBreaking); - SmallVector<unsigned, 4> RegisterFiles(PRF.getNumRegisterFiles()); - for (std::unique_ptr<WriteState> &WS : IS.getDefs()) { - PRF.addRegisterWrite(WriteRef(IR.first, WS.get()), RegisterFiles, - ShouldAllocateRegisters); - } - - // 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 deleted file mode 100644 index 4262a241c08c..000000000000 --- a/tools/llvm-mca/DispatchStage.h +++ /dev/null @@ -1,106 +0,0 @@ -//===----------------------- 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 physical registers 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 deleted file mode 100644 index 4bddbef9a0c8..000000000000 --- a/tools/llvm-mca/DispatchStatistics.cpp +++ /dev/null @@ -1,71 +0,0 @@ -//===--------------------- 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/ExecuteStage.cpp b/tools/llvm-mca/ExecuteStage.cpp deleted file mode 100644 index 437f864b072c..000000000000 --- a/tools/llvm-mca/ExecuteStage.cpp +++ /dev/null @@ -1,210 +0,0 @@ -//===---------------------- 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 deleted file mode 100644 index 4914a9373e7c..000000000000 --- a/tools/llvm-mca/ExecuteStage.h +++ /dev/null @@ -1,67 +0,0 @@ -//===---------------------- 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 deleted file mode 100644 index 3da117c0abc1..000000000000 --- a/tools/llvm-mca/FetchStage.cpp +++ /dev/null @@ -1,46 +0,0 @@ -//===---------------------- 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 deleted file mode 100644 index 620075d24fea..000000000000 --- a/tools/llvm-mca/FetchStage.h +++ /dev/null @@ -1,45 +0,0 @@ -//===---------------------- 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.h b/tools/llvm-mca/HWEventListener.h deleted file mode 100644 index aa3e6dcf19a0..000000000000 --- a/tools/llvm-mca/HWEventListener.h +++ /dev/null @@ -1,141 +0,0 @@ -//===----------------------- 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 deleted file mode 100644 index 103cde9afcc8..000000000000 --- a/tools/llvm-mca/HardwareUnit.cpp +++ /dev/null @@ -1,23 +0,0 @@ -//===------------------------- 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 deleted file mode 100644 index e8c496ab967a..000000000000 --- a/tools/llvm-mca/HardwareUnit.h +++ /dev/null @@ -1,31 +0,0 @@ -//===-------------------------- 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 deleted file mode 100644 index 053b7b4e8175..000000000000 --- a/tools/llvm-mca/InstrBuilder.cpp +++ /dev/null @@ -1,469 +0,0 @@ -//===--------------------- 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); - - // Check if this is a dependency breaking instruction. - if (MCIA.isDependencyBreaking(STI, MCI)) - NewIS->setDependencyBreaking(); - - // 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 deleted file mode 100644 index 69a53b6fec21..000000000000 --- a/tools/llvm-mca/InstrBuilder.h +++ /dev/null @@ -1,85 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 0c8476705572..000000000000 --- a/tools/llvm-mca/Instruction.cpp +++ /dev/null @@ -1,177 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 3b2f90528f2e..000000000000 --- a/tools/llvm-mca/Instruction.h +++ /dev/null @@ -1,434 +0,0 @@ -//===--------------------- 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: - 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; } - - bool isReady() const { return IsReady; } - bool isImplicitRead() const { return RD.isImplicitRead(); } - - 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; - - bool IsDepBreaking; - - 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), RCUTokenID(0), - IsDepBreaking(false) {} - 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; } - - bool isDependencyBreaking() const { return IsDepBreaking; } - void setDependencyBreaking() { IsDepBreaking = true; } - - 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/InstructionTables.cpp b/tools/llvm-mca/InstructionTables.cpp deleted file mode 100644 index 9b9dbc37fbdb..000000000000 --- a/tools/llvm-mca/InstructionTables.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 18e019988430..000000000000 --- a/tools/llvm-mca/InstructionTables.h +++ /dev/null @@ -1,43 +0,0 @@ -//===--------------------- 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 index 0afcd3129ecd..a704612934ff 100644 --- a/tools/llvm-mca/LLVMBuild.txt +++ b/tools/llvm-mca/LLVMBuild.txt @@ -19,4 +19,4 @@ type = Tool name = llvm-mca parent = Tools -required_libraries = MC MCParser Support all-targets +required_libraries = MC MCA MCParser Support all-targets diff --git a/tools/llvm-mca/LSUnit.cpp b/tools/llvm-mca/LSUnit.cpp deleted file mode 100644 index 9ee3b6171893..000000000000 --- a/tools/llvm-mca/LSUnit.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//===----------------------- 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 deleted file mode 100644 index 817522190589..000000000000 --- a/tools/llvm-mca/LSUnit.h +++ /dev/null @@ -1,147 +0,0 @@ -//===------------------------- 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 deleted file mode 100644 index 7c937e7b48b5..000000000000 --- a/tools/llvm-mca/Pipeline.cpp +++ /dev/null @@ -1,99 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 6916e422be39..000000000000 --- a/tools/llvm-mca/Pipeline.h +++ /dev/null @@ -1,79 +0,0 @@ -//===--------------------- 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 index c5b1a12b792f..18ef45fc2a65 100644 --- a/tools/llvm-mca/PipelinePrinter.cpp +++ b/tools/llvm-mca/PipelinePrinter.cpp @@ -13,14 +13,14 @@ //===----------------------------------------------------------------------===// #include "PipelinePrinter.h" -#include "View.h" +#include "Views/View.h" +namespace llvm { namespace mca { -using namespace llvm; - void PipelinePrinter::printReport(llvm::raw_ostream &OS) const { for (const auto &V : Views) V->printView(OS); } } // namespace mca. +} // namespace llvm diff --git a/tools/llvm-mca/PipelinePrinter.h b/tools/llvm-mca/PipelinePrinter.h index fe871414418f..456026e12df3 100644 --- a/tools/llvm-mca/PipelinePrinter.h +++ b/tools/llvm-mca/PipelinePrinter.h @@ -17,13 +17,14 @@ #ifndef LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H #define LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H -#include "Pipeline.h" -#include "View.h" +#include "Views/View.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/MCA/Pipeline.h" #include "llvm/Support/raw_ostream.h" #define DEBUG_TYPE "llvm-mca" +namespace llvm { namespace mca { /// A printer class that knows how to collects statistics on the @@ -48,5 +49,6 @@ public: void printReport(llvm::raw_ostream &OS) const; }; } // namespace mca +} // namespace llvm #endif // LLVM_TOOLS_LLVM_MCA_PIPELINEPRINTER_H diff --git a/tools/llvm-mca/RegisterFile.cpp b/tools/llvm-mca/RegisterFile.cpp deleted file mode 100644 index 44de105b8996..000000000000 --- a/tools/llvm-mca/RegisterFile.cpp +++ /dev/null @@ -1,343 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 349e9789b6ee..000000000000 --- a/tools/llvm-mca/RegisterFile.h +++ /dev/null @@ -1,172 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 1b07bf9a3b33..000000000000 --- a/tools/llvm-mca/RegisterFileStatistics.cpp +++ /dev/null @@ -1,107 +0,0 @@ -//===--------------------- 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/RetireControlUnit.cpp b/tools/llvm-mca/RetireControlUnit.cpp deleted file mode 100644 index 123058541f28..000000000000 --- a/tools/llvm-mca/RetireControlUnit.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//===---------------------- 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 deleted file mode 100644 index 8acc8bcc98fe..000000000000 --- a/tools/llvm-mca/RetireControlUnit.h +++ /dev/null @@ -1,98 +0,0 @@ -//===---------------------- 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 physicall registers previously 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 deleted file mode 100644 index edb855e11e84..000000000000 --- a/tools/llvm-mca/RetireControlUnitStatistics.cpp +++ /dev/null @@ -1,49 +0,0 @@ -//===--------------------- 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/RetireStage.cpp b/tools/llvm-mca/RetireStage.cpp deleted file mode 100644 index 55c3b887e478..000000000000 --- a/tools/llvm-mca/RetireStage.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//===---------------------- 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 Instruction &Inst = *IR.getInstruction(); - const InstrDesc &Desc = Inst.getDesc(); - - bool ShouldFreeRegs = !(Desc.isZeroLatency() && Inst.isDependencyBreaking()); - for (const std::unique_ptr<WriteState> &WS : Inst.getDefs()) - PRF.removeRegisterWrite(*WS.get(), FreedRegs, ShouldFreeRegs); - notifyEvent<HWInstructionEvent>(HWInstructionRetiredEvent(IR, FreedRegs)); -} - -} // namespace mca diff --git a/tools/llvm-mca/RetireStage.h b/tools/llvm-mca/RetireStage.h deleted file mode 100644 index 8cf672d92c6e..000000000000 --- a/tools/llvm-mca/RetireStage.h +++ /dev/null @@ -1,48 +0,0 @@ -//===---------------------- 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 deleted file mode 100644 index 975a50e4b638..000000000000 --- a/tools/llvm-mca/Scheduler.cpp +++ /dev/null @@ -1,403 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 428fbc01707d..000000000000 --- a/tools/llvm-mca/Scheduler.h +++ /dev/null @@ -1,515 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 5c6d22a71812..000000000000 --- a/tools/llvm-mca/SchedulerStatistics.cpp +++ /dev/null @@ -1,94 +0,0 @@ -//===--------------------- 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/SourceMgr.h b/tools/llvm-mca/SourceMgr.h deleted file mode 100644 index 15a85a69569f..000000000000 --- a/tools/llvm-mca/SourceMgr.h +++ /dev/null @@ -1,63 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 7ead940e63c1..000000000000 --- a/tools/llvm-mca/Stage.cpp +++ /dev/null @@ -1,27 +0,0 @@ -//===---------------------- 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 deleted file mode 100644 index 9dbdcd89a33b..000000000000 --- a/tools/llvm-mca/Stage.h +++ /dev/null @@ -1,76 +0,0 @@ -//===---------------------- 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/Support.cpp b/tools/llvm-mca/Support.cpp deleted file mode 100644 index 8f6b8a91f38f..000000000000 --- a/tools/llvm-mca/Support.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index fd8d8b5a23b3..000000000000 --- a/tools/llvm-mca/Support.h +++ /dev/null @@ -1,58 +0,0 @@ -//===--------------------- 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 deleted file mode 100644 index 6e75cac0d432..000000000000 --- a/tools/llvm-mca/TimelineView.cpp +++ /dev/null @@ -1,240 +0,0 @@ -//===--------------------- 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/Views/DispatchStatistics.cpp b/tools/llvm-mca/Views/DispatchStatistics.cpp new file mode 100644 index 000000000000..2562c82407bf --- /dev/null +++ b/tools/llvm-mca/Views/DispatchStatistics.cpp @@ -0,0 +1,86 @@ +//===--------------------- 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 "Views/DispatchStatistics.h" +#include "llvm/Support/Format.h" + +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) + return; + + const auto &DE = static_cast<const HWInstructionDispatchedEvent &>(Event); + NumDispatched += DE.MicroOpcodes; +} + +void DispatchStatistics::printDispatchHistogram(raw_ostream &OS) const { + std::string Buffer; + raw_string_ostream TempStream(Buffer); + TempStream << "\n\nDispatch Logic - " + << "number of cycles where we saw N micro opcodes dispatched:\n"; + TempStream << "[# dispatched], [# cycles]\n"; + for (const std::pair<unsigned, unsigned> &Entry : DispatchGroupSizePerCycle) { + double Percentage = ((double)Entry.second / NumCycles) * 100.0; + TempStream << " " << Entry.first << ", " << Entry.second + << " (" << format("%.1f", floor((Percentage * 10) + 0.5) / 10) + << "%)\n"; + } + + TempStream.flush(); + OS << Buffer; +} + +static void printStalls(raw_ostream &OS, unsigned NumStalls, + unsigned NumCycles) { + if (!NumStalls) { + OS << NumStalls; + return; + } + + double Percentage = ((double)NumStalls / NumCycles) * 100.0; + OS << NumStalls << " (" + << format("%.1f", floor((Percentage * 10) + 0.5) / 10) << "%)"; +} + +void DispatchStatistics::printDispatchStalls(raw_ostream &OS) const { + std::string Buffer; + raw_string_ostream SS(Buffer); + SS << "\n\nDynamic Dispatch Stall Cycles:\n"; + SS << "RAT - Register unavailable: "; + printStalls(SS, HWStalls[HWStallEvent::RegisterFileStall], NumCycles); + SS << "\nRCU - Retire tokens unavailable: "; + printStalls(SS, HWStalls[HWStallEvent::RetireControlUnitStall], NumCycles); + SS << "\nSCHEDQ - Scheduler full: "; + printStalls(SS, HWStalls[HWStallEvent::SchedulerQueueFull], NumCycles); + SS << "\nLQ - Load queue full: "; + printStalls(SS, HWStalls[HWStallEvent::LoadQueueFull], NumCycles); + SS << "\nSQ - Store queue full: "; + printStalls(SS, HWStalls[HWStallEvent::StoreQueueFull], NumCycles); + SS << "\nGROUP - Static restrictions on the dispatch group: "; + printStalls(SS, HWStalls[HWStallEvent::DispatchGroupStall], NumCycles); + SS << '\n'; + SS.flush(); + OS << Buffer; +} + +} // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/DispatchStatistics.h b/tools/llvm-mca/Views/DispatchStatistics.h index 1e389d54766b..6679c81efe95 100644 --- a/tools/llvm-mca/DispatchStatistics.h +++ b/tools/llvm-mca/Views/DispatchStatistics.h @@ -24,7 +24,7 @@ /// GROUP - Static restrictions on the dispatch group: 0 /// /// -/// Dispatch Logic - number of cycles where we saw N instructions dispatched: +/// Dispatch Logic - number of cycles where we saw N micro opcodes dispatched: /// [# dispatched], [# cycles] /// 0, 15 (11.5%) /// 2, 4 (3.1%) @@ -34,11 +34,12 @@ #ifndef LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H #define LLVM_TOOLS_LLVM_MCA_DISPATCHVIEW_H -#include "View.h" +#include "Views/View.h" #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCSubtargetInfo.h" #include <map> +namespace llvm { namespace mca { class DispatchStatistics : public View { @@ -80,5 +81,6 @@ public: } }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/InstructionInfoView.cpp b/tools/llvm-mca/Views/InstructionInfoView.cpp index 0e50a96d19c1..5016afb49e44 100644 --- a/tools/llvm-mca/InstructionInfoView.cpp +++ b/tools/llvm-mca/Views/InstructionInfoView.cpp @@ -12,17 +12,15 @@ /// //===----------------------------------------------------------------------===// -#include "InstructionInfoView.h" +#include "Views/InstructionInfoView.h" +namespace llvm { 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); @@ -32,8 +30,7 @@ void InstructionInfoView::printView(raw_ostream &OS) const { << "[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); + for (const MCInst &Inst : Source) { const MCInstrDesc &MCDesc = MCII.get(Inst.getOpcode()); // Obtain the scheduling class information from the instruction. @@ -89,3 +86,4 @@ void InstructionInfoView::printView(raw_ostream &OS) const { OS << Buffer; } } // namespace mca. +} // namespace llvm diff --git a/tools/llvm-mca/InstructionInfoView.h b/tools/llvm-mca/Views/InstructionInfoView.h index 0770ae3d2b57..3ef95d474490 100644 --- a/tools/llvm-mca/InstructionInfoView.h +++ b/tools/llvm-mca/Views/InstructionInfoView.h @@ -35,8 +35,9 @@ #ifndef LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H #define LLVM_TOOLS_LLVM_MCA_INSTRUCTIONINFOVIEW_H -#include "SourceMgr.h" -#include "View.h" +#include "Views/View.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCInstrInfo.h" #include "llvm/MC/MCSubtargetInfo.h" @@ -44,23 +45,25 @@ #define DEBUG_TYPE "llvm-mca" +namespace llvm { 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::ArrayRef<llvm::MCInst> Source; llvm::MCInstPrinter &MCIP; public: InstructionInfoView(const llvm::MCSubtargetInfo &sti, - const llvm::MCInstrInfo &mcii, const SourceMgr &S, - llvm::MCInstPrinter &IP) + const llvm::MCInstrInfo &mcii, + llvm::ArrayRef<llvm::MCInst> S, llvm::MCInstPrinter &IP) : STI(sti), MCII(mcii), Source(S), MCIP(IP) {} void printView(llvm::raw_ostream &OS) const override; }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/Views/RegisterFileStatistics.cpp b/tools/llvm-mca/Views/RegisterFileStatistics.cpp new file mode 100644 index 000000000000..06202bc41421 --- /dev/null +++ b/tools/llvm-mca/Views/RegisterFileStatistics.cpp @@ -0,0 +1,168 @@ +//===--------------------- 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 "Views/RegisterFileStatistics.h" +#include "llvm/Support/Format.h" + +namespace llvm { +namespace mca { + +RegisterFileStatistics::RegisterFileStatistics(const MCSubtargetInfo &sti) + : STI(sti) { + const MCSchedModel &SM = STI.getSchedModel(); + RegisterFileUsage RFUEmpty = {0, 0, 0}; + MoveEliminationInfo MEIEmpty = {0, 0, 0, 0, 0}; + if (!SM.hasExtraProcessorInfo()) { + // Assume a single register file. + PRFUsage.emplace_back(RFUEmpty); + MoveElimInfo.emplace_back(MEIEmpty); + 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); + + PRFUsage.resize(NumRegFiles); + std::fill(PRFUsage.begin(), PRFUsage.end(), RFUEmpty); + + MoveElimInfo.resize(NumRegFiles); + std::fill(MoveElimInfo.begin(), MoveElimInfo.end(), MEIEmpty); +} + +void RegisterFileStatistics::updateRegisterFileUsage( + ArrayRef<unsigned> UsedPhysRegs) { + for (unsigned I = 0, E = PRFUsage.size(); I < E; ++I) { + RegisterFileUsage &RFU = PRFUsage[I]; + unsigned NumUsedPhysRegs = UsedPhysRegs[I]; + RFU.CurrentlyUsedMappings += NumUsedPhysRegs; + RFU.TotalMappings += NumUsedPhysRegs; + RFU.MaxUsedMappings = + std::max(RFU.MaxUsedMappings, RFU.CurrentlyUsedMappings); + } +} + +void RegisterFileStatistics::updateMoveElimInfo(const Instruction &Inst) { + if (!Inst.isOptimizableMove()) + return; + + assert(Inst.getDefs().size() == 1 && "Expected a single definition!"); + assert(Inst.getUses().size() == 1 && "Expected a single register use!"); + const WriteState &WS = Inst.getDefs()[0]; + const ReadState &RS = Inst.getUses()[0]; + + MoveEliminationInfo &Info = + MoveElimInfo[Inst.getDefs()[0].getRegisterFileID()]; + Info.TotalMoveEliminationCandidates++; + if (WS.isEliminated()) + Info.CurrentMovesEliminated++; + if (WS.isWriteZero() && RS.isReadZero()) + Info.TotalMovesThatPropagateZero++; +} + +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 = PRFUsage.size(); I < E; ++I) + PRFUsage[I].CurrentlyUsedMappings -= RE.FreedPhysRegs[I]; + break; + } + case HWInstructionEvent::Dispatched: { + const auto &DE = static_cast<const HWInstructionDispatchedEvent &>(Event); + updateRegisterFileUsage(DE.UsedPhysRegs); + updateMoveElimInfo(*DE.IR.getInstruction()); + } + } +} + +void RegisterFileStatistics::onCycleEnd() { + for (MoveEliminationInfo &MEI : MoveElimInfo) { + unsigned &CurrentMax = MEI.MaxMovesEliminatedPerCycle; + CurrentMax = std::max(CurrentMax, MEI.CurrentMovesEliminated); + MEI.TotalMovesEliminated += MEI.CurrentMovesEliminated; + MEI.CurrentMovesEliminated = 0; + } +} + +void RegisterFileStatistics::printView(raw_ostream &OS) const { + std::string Buffer; + raw_string_ostream TempStream(Buffer); + + TempStream << "\n\nRegister File statistics:"; + const RegisterFileUsage &GlobalUsage = PRFUsage[0]; + TempStream << "\nTotal number of mappings created: " + << GlobalUsage.TotalMappings; + TempStream << "\nMax number of mappings used: " + << GlobalUsage.MaxUsedMappings << '\n'; + + for (unsigned I = 1, E = PRFUsage.size(); I < E; ++I) { + const RegisterFileUsage &RFU = PRFUsage[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'; + const MoveEliminationInfo &MEI = MoveElimInfo[I]; + + if (MEI.TotalMoveEliminationCandidates) { + TempStream << " Number of optimizable moves: " + << MEI.TotalMoveEliminationCandidates; + double EliminatedMovProportion = (double)MEI.TotalMovesEliminated / + MEI.TotalMoveEliminationCandidates * + 100.0; + double ZeroMovProportion = (double)MEI.TotalMovesThatPropagateZero / + MEI.TotalMoveEliminationCandidates * 100.0; + TempStream << "\n Number of moves eliminated: " + << MEI.TotalMovesEliminated << " " + << format("(%.1f%%)", + floor((EliminatedMovProportion * 10) + 0.5) / 10); + TempStream << "\n Number of zero moves: " + << MEI.TotalMovesThatPropagateZero << " " + << format("(%.1f%%)", + floor((ZeroMovProportion * 10) + 0.5) / 10); + TempStream << "\n Max moves eliminated per cycle: " + << MEI.MaxMovesEliminatedPerCycle << '\n'; + } + } + + TempStream.flush(); + OS << Buffer; +} + +} // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/RegisterFileStatistics.h b/tools/llvm-mca/Views/RegisterFileStatistics.h index cbe816cd3332..a2c52a668dae 100644 --- a/tools/llvm-mca/RegisterFileStatistics.h +++ b/tools/llvm-mca/Views/RegisterFileStatistics.h @@ -21,6 +21,10 @@ /// Number of physical registers: 72 /// Total number of mappings created: 0 /// Max number of mappings used: 0 +/// Number of optimizable moves: 200 +/// Number of moves eliminated: 200 (100.0%) +/// Number of zero moves: 200 (100.0%) +/// Max moves eliminated per cycle: 2 /// /// * Register File #2 -- IntegerPRF: /// Number of physical registers: 64 @@ -32,10 +36,11 @@ #ifndef LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H #define LLVM_TOOLS_LLVM_MCA_REGISTERFILESTATISTICS_H -#include "View.h" +#include "Views/View.h" #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCSubtargetInfo.h" +namespace llvm { namespace mca { class RegisterFileStatistics : public View { @@ -48,20 +53,29 @@ class RegisterFileStatistics : public View { unsigned CurrentlyUsedMappings; }; + struct MoveEliminationInfo { + unsigned TotalMoveEliminationCandidates; + unsigned TotalMovesEliminated; + unsigned TotalMovesThatPropagateZero; + unsigned MaxMovesEliminatedPerCycle; + unsigned CurrentMovesEliminated; + }; + // There is one entry for each register file implemented by the processor. - llvm::SmallVector<RegisterFileUsage, 4> RegisterFiles; + llvm::SmallVector<RegisterFileUsage, 4> PRFUsage; + llvm::SmallVector<MoveEliminationInfo, 4> MoveElimInfo; - void initializeRegisterFileInfo(); + void updateRegisterFileUsage(ArrayRef<unsigned> UsedPhysRegs); + void updateMoveElimInfo(const Instruction &Inst); public: - RegisterFileStatistics(const llvm::MCSubtargetInfo &sti) : STI(sti) { - initializeRegisterFileInfo(); - } + RegisterFileStatistics(const llvm::MCSubtargetInfo &sti); + void onCycleEnd() override; void onEvent(const HWInstructionEvent &Event) override; - void printView(llvm::raw_ostream &OS) const override; }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/ResourcePressureView.cpp b/tools/llvm-mca/Views/ResourcePressureView.cpp index fe9d5b7fabc8..6df61840437d 100644 --- a/tools/llvm-mca/ResourcePressureView.cpp +++ b/tools/llvm-mca/Views/ResourcePressureView.cpp @@ -12,15 +12,17 @@ /// //===----------------------------------------------------------------------===// -#include "ResourcePressureView.h" +#include "Views/ResourcePressureView.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" +namespace llvm { namespace mca { -using namespace llvm; - -void ResourcePressureView::initialize() { +ResourcePressureView::ResourcePressureView(const llvm::MCSubtargetInfo &sti, + MCInstPrinter &Printer, + ArrayRef<MCInst> S) + : STI(sti), MCIP(Printer), Source(S), LastInstructionIdx(0) { // Populate the map of resource descriptors. unsigned R2VIndex = 0; const MCSchedModel &SM = STI.getSchedModel(); @@ -41,12 +43,19 @@ void ResourcePressureView::initialize() { } void ResourcePressureView::onEvent(const HWInstructionEvent &Event) { + if (Event.Type == HWInstructionEvent::Dispatched) { + LastInstructionIdx = Event.IR.getSourceIndex(); + return; + } + // 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) { + for (const std::pair<ResourceRef, ResourceCycles> &Use : + IssueEvent.UsedResources) { const ResourceRef &RR = Use.first; assert(Resource2VecIndex.find(RR.first) != Resource2VecIndex.end()); unsigned R2VIndex = Resource2VecIndex[RR.first]; @@ -91,8 +100,7 @@ static void printResourcePressure(formatted_raw_ostream &OS, double Pressure, OS.PadToColumn(Col); } -void ResourcePressureView::printResourcePressurePerIteration( - raw_ostream &OS, unsigned Executions) const { +void ResourcePressureView::printResourcePressurePerIter(raw_ostream &OS) const { std::string Buffer; raw_string_ostream TempStream(Buffer); formatted_raw_ostream FOS(TempStream); @@ -125,6 +133,7 @@ void ResourcePressureView::printResourcePressurePerIteration( FOS << '\n'; FOS.flush(); + const unsigned Executions = LastInstructionIdx / Source.size() + 1; for (unsigned I = 0, E = NumResourceUnits; I < E; ++I) { double Usage = ResourceUsage[I + Source.size() * E]; printResourcePressure(FOS, Usage / Executions, (I + 1) * 7); @@ -134,8 +143,7 @@ void ResourcePressureView::printResourcePressurePerIteration( OS << Buffer; } -void ResourcePressureView::printResourcePressurePerInstruction( - raw_ostream &OS, unsigned Executions) const { +void ResourcePressureView::printResourcePressurePerInst(raw_ostream &OS) const { std::string Buffer; raw_string_ostream TempStream(Buffer); formatted_raw_ostream FOS(TempStream); @@ -147,13 +155,16 @@ void ResourcePressureView::printResourcePressurePerInstruction( std::string Instruction; raw_string_ostream InstrStream(Instruction); - for (unsigned I = 0, E = Source.size(); I < E; ++I) { + unsigned InstrIndex = 0; + const unsigned Executions = LastInstructionIdx / Source.size() + 1; + for (const MCInst &MCI : Source) { + unsigned BaseEltIdx = InstrIndex * NumResourceUnits; for (unsigned J = 0; J < NumResourceUnits; ++J) { - double Usage = ResourceUsage[J + I * NumResourceUnits]; + double Usage = ResourceUsage[J + BaseEltIdx]; printResourcePressure(FOS, Usage / Executions, (J + 1) * 7); } - MCIP.printInst(&Source.getMCInstFromIndex(I), InstrStream, "", STI); + MCIP.printInst(&MCI, InstrStream, "", STI); InstrStream.flush(); StringRef Str(Instruction); @@ -166,6 +177,9 @@ void ResourcePressureView::printResourcePressurePerInstruction( FOS.flush(); OS << Buffer; Buffer = ""; + + ++InstrIndex; } } } // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/ResourcePressureView.h b/tools/llvm-mca/Views/ResourcePressureView.h index fe1c6af5e6f6..572ce6fe6b70 100644 --- a/tools/llvm-mca/ResourcePressureView.h +++ b/tools/llvm-mca/Views/ResourcePressureView.h @@ -58,13 +58,14 @@ #ifndef LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H #define LLVM_TOOLS_LLVM_MCA_RESOURCEPRESSUREVIEW_H -#include "SourceMgr.h" -#include "View.h" +#include "Views/View.h" +#include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" +#include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCSubtargetInfo.h" -#include <map> +namespace llvm { namespace mca { /// This class collects resource pressure statistics and it is able to print @@ -72,38 +73,32 @@ namespace mca { class ResourcePressureView : public View { const llvm::MCSubtargetInfo &STI; llvm::MCInstPrinter &MCIP; - const SourceMgr &Source; + llvm::ArrayRef<llvm::MCInst> Source; + unsigned LastInstructionIdx; // 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; + std::vector<ResourceCycles> 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(); + void printResourcePressurePerIter(llvm::raw_ostream &OS) const; + void printResourcePressurePerInst(llvm::raw_ostream &OS) const; public: ResourcePressureView(const llvm::MCSubtargetInfo &sti, - llvm::MCInstPrinter &Printer, const SourceMgr &SM) - : STI(sti), MCIP(Printer), Source(SM) { - initialize(); - } + llvm::MCInstPrinter &Printer, + llvm::ArrayRef<llvm::MCInst> S); void onEvent(const HWInstructionEvent &Event) override; - void printView(llvm::raw_ostream &OS) const override { - unsigned Executions = Source.getNumIterations(); - printResourcePressurePerIteration(OS, Executions); - printResourcePressurePerInstruction(OS, Executions); + printResourcePressurePerIter(OS); + printResourcePressurePerInst(OS); } }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp b/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp new file mode 100644 index 000000000000..54eb28f1add9 --- /dev/null +++ b/tools/llvm-mca/Views/RetireControlUnitStatistics.cpp @@ -0,0 +1,91 @@ +//===--------------------- 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 "Views/RetireControlUnitStatistics.h" +#include "llvm/Support/Format.h" + +namespace llvm { +namespace mca { + +RetireControlUnitStatistics::RetireControlUnitStatistics(const MCSchedModel &SM) + : NumRetired(0), NumCycles(0), EntriesInUse(0), MaxUsedEntries(0), + SumOfUsedEntries(0) { + TotalROBEntries = SM.MicroOpBufferSize; + if (SM.hasExtraProcessorInfo()) { + const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo(); + if (EPI.ReorderBufferSize) + TotalROBEntries = EPI.ReorderBufferSize; + } +} + +void RetireControlUnitStatistics::onEvent(const HWInstructionEvent &Event) { + if (Event.Type == HWInstructionEvent::Dispatched) { + unsigned NumEntries = + static_cast<const HWInstructionDispatchedEvent &>(Event).MicroOpcodes; + EntriesInUse += NumEntries; + } + + if (Event.Type == HWInstructionEvent::Retired) { + unsigned ReleasedEntries = Event.IR.getInstruction()->getDesc().NumMicroOps; + assert(EntriesInUse >= ReleasedEntries && "Invalid internal state!"); + EntriesInUse -= ReleasedEntries; + ++NumRetired; + } +} + +void RetireControlUnitStatistics::onCycleEnd() { + // Update histogram + RetiredPerCycle[NumRetired]++; + NumRetired = 0; + ++NumCycles; + MaxUsedEntries = std::max(MaxUsedEntries, EntriesInUse); + SumOfUsedEntries += EntriesInUse; +} + +void RetireControlUnitStatistics::printView(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"; + } + + unsigned AvgUsage = (double)SumOfUsedEntries / NumCycles; + double MaxUsagePercentage = ((double)MaxUsedEntries / TotalROBEntries) * 100.0; + double NormalizedMaxPercentage = floor((MaxUsagePercentage * 10) + 0.5) / 10; + double AvgUsagePercentage = ((double)AvgUsage / TotalROBEntries) * 100.0; + double NormalizedAvgPercentage = floor((AvgUsagePercentage * 10) + 0.5) / 10; + + TempStream << "\nTotal ROB Entries: " << TotalROBEntries + << "\nMax Used ROB Entries: " << MaxUsedEntries + << format(" ( %.1f%% )", NormalizedMaxPercentage) + << "\nAverage Used ROB Entries per cy: " << AvgUsage + << format(" ( %.1f%% )\n", NormalizedAvgPercentage); + + TempStream.flush(); + OS << Buffer; +} + +} // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/RetireControlUnitStatistics.h b/tools/llvm-mca/Views/RetireControlUnitStatistics.h index 1f03e7efe889..02aa13bc444a 100644 --- a/tools/llvm-mca/RetireControlUnitStatistics.h +++ b/tools/llvm-mca/Views/RetireControlUnitStatistics.h @@ -16,20 +16,24 @@ /// /// 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%) +/// 0, 109 (17.9%) +/// 1, 102 (16.7%) +/// 2, 399 (65.4%) +/// +/// Total ROB Entries: 64 +/// Max Used ROB Entries: 35 ( 54.7% ) +/// Average Used ROB Entries per cy: 32 ( 50.0% ) /// //===----------------------------------------------------------------------===// #ifndef LLVM_TOOLS_LLVM_MCA_RETIRECONTROLUNITSTATISTICS_H #define LLVM_TOOLS_LLVM_MCA_RETIRECONTROLUNITSTATISTICS_H -#include "View.h" -#include "llvm/MC/MCSubtargetInfo.h" +#include "Views/View.h" +#include "llvm/MC/MCSchedule.h" #include <map> +namespace llvm { namespace mca { class RetireControlUnitStatistics : public View { @@ -38,23 +42,20 @@ class RetireControlUnitStatistics : public View { unsigned NumRetired; unsigned NumCycles; - - void updateHistograms() { - RetiredPerCycle[NumRetired]++; - NumRetired = 0; - } + unsigned TotalROBEntries; + unsigned EntriesInUse; + unsigned MaxUsedEntries; + unsigned SumOfUsedEntries; public: - RetireControlUnitStatistics() : NumRetired(0), NumCycles(0) {} + RetireControlUnitStatistics(const MCSchedModel &SM); void onEvent(const HWInstructionEvent &Event) override; - - void onCycleBegin() override { NumCycles++; } - - void onCycleEnd() override { updateHistograms(); } - + void onCycleEnd() override; void printView(llvm::raw_ostream &OS) const override; }; + } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/Views/SchedulerStatistics.cpp b/tools/llvm-mca/Views/SchedulerStatistics.cpp new file mode 100644 index 000000000000..670f90127f18 --- /dev/null +++ b/tools/llvm-mca/Views/SchedulerStatistics.cpp @@ -0,0 +1,183 @@ +//===--------------------- 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 "Views/SchedulerStatistics.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/FormattedStream.h" + +namespace llvm { +namespace mca { + +SchedulerStatistics::SchedulerStatistics(const llvm::MCSubtargetInfo &STI) + : SM(STI.getSchedModel()), LQResourceID(0), SQResourceID(0), NumIssued(0), + NumCycles(0), MostRecentLoadDispatched(~0U), + MostRecentStoreDispatched(~0U), + IssuedPerCycle(STI.getSchedModel().NumProcResourceKinds, 0), + Usage(STI.getSchedModel().NumProcResourceKinds, {0, 0, 0}) { + if (SM.hasExtraProcessorInfo()) { + const MCExtraProcessorInfo &EPI = SM.getExtraProcessorInfo(); + LQResourceID = EPI.LoadQueueID; + SQResourceID = EPI.StoreQueueID; + } +} + +// FIXME: This implementation works under the assumption that load/store queue +// entries are reserved at 'instruction dispatched' stage, and released at +// 'instruction executed' stage. This currently matches the behavior of LSUnit. +// +// The current design minimizes the number of events generated by the +// Dispatch/Execute stages, at the cost of doing extra bookkeeping in method +// `onEvent`. However, it introduces a subtle dependency between this view and +// how the LSUnit works. +// +// In future we should add a new "memory queue" event type, so that we stop +// making assumptions on how LSUnit internally works (See PR39828). +void SchedulerStatistics::onEvent(const HWInstructionEvent &Event) { + if (Event.Type == HWInstructionEvent::Issued) + ++NumIssued; + else if (Event.Type == HWInstructionEvent::Dispatched) { + const Instruction &Inst = *Event.IR.getInstruction(); + const unsigned Index = Event.IR.getSourceIndex(); + if (LQResourceID && Inst.getDesc().MayLoad && + MostRecentLoadDispatched != Index) { + Usage[LQResourceID].SlotsInUse++; + MostRecentLoadDispatched = Index; + } + if (SQResourceID && Inst.getDesc().MayStore && + MostRecentStoreDispatched != Index) { + Usage[SQResourceID].SlotsInUse++; + MostRecentStoreDispatched = Index; + } + } else if (Event.Type == HWInstructionEvent::Executed) { + const Instruction &Inst = *Event.IR.getInstruction(); + if (LQResourceID && Inst.getDesc().MayLoad) { + assert(Usage[LQResourceID].SlotsInUse); + Usage[LQResourceID].SlotsInUse--; + } + if (SQResourceID && Inst.getDesc().MayStore) { + assert(Usage[SQResourceID].SlotsInUse); + Usage[SQResourceID].SlotsInUse--; + } + } +} + +void SchedulerStatistics::onReservedBuffers(const InstRef & /* unused */, + ArrayRef<unsigned> Buffers) { + for (const unsigned Buffer : Buffers) { + if (Buffer == LQResourceID || Buffer == SQResourceID) + continue; + Usage[Buffer].SlotsInUse++; + } +} + +void SchedulerStatistics::onReleasedBuffers(const InstRef & /* unused */, + ArrayRef<unsigned> Buffers) { + for (const unsigned Buffer : Buffers) { + if (Buffer == LQResourceID || Buffer == SQResourceID) + continue; + Usage[Buffer].SlotsInUse--; + } +} + +void SchedulerStatistics::updateHistograms() { + for (BufferUsage &BU : Usage) { + BU.CumulativeNumUsedSlots += BU.SlotsInUse; + BU.MaxUsedSlots = std::max(BU.MaxUsedSlots, BU.SlotsInUse); + } + + IssuedPerCycle[NumIssued]++; + NumIssued = 0; +} + +void SchedulerStatistics::printSchedulerStats(raw_ostream &OS) const { + OS << "\n\nSchedulers - " + << "number of cycles where we saw N instructions issued:\n"; + OS << "[# issued], [# cycles]\n"; + + const auto It = + std::max_element(IssuedPerCycle.begin(), IssuedPerCycle.end()); + unsigned Index = std::distance(IssuedPerCycle.begin(), It); + + bool HasColors = OS.has_colors(); + for (unsigned I = 0, E = IssuedPerCycle.size(); I < E; ++I) { + unsigned IPC = IssuedPerCycle[I]; + if (!IPC) + continue; + + if (I == Index && HasColors) + OS.changeColor(raw_ostream::SAVEDCOLOR, true, false); + + OS << " " << I << ", " << IPC << " (" + << format("%.1f", ((double)IPC / NumCycles) * 100) << "%)\n"; + if (HasColors) + OS.resetColor(); + } +} + +void SchedulerStatistics::printSchedulerUsage(raw_ostream &OS) const { + assert(NumCycles && "Unexpected number of cycles!"); + + OS << "\nScheduler's queue usage:\n"; + if (all_of(Usage, [](const BufferUsage &BU) { return !BU.MaxUsedSlots; })) { + OS << "No scheduler resources used.\n"; + return; + } + + OS << "[1] Resource name.\n" + << "[2] Average number of used buffer entries.\n" + << "[3] Maximum number of used buffer entries.\n" + << "[4] Total number of buffer entries.\n\n" + << " [1] [2] [3] [4]\n"; + + formatted_raw_ostream FOS(OS); + bool HasColors = FOS.has_colors(); + for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) { + const MCProcResourceDesc &ProcResource = *SM.getProcResource(I); + if (ProcResource.BufferSize <= 0) + continue; + + const BufferUsage &BU = Usage[I]; + double AvgUsage = (double)BU.CumulativeNumUsedSlots / NumCycles; + double AlmostFullThreshold = (double)(ProcResource.BufferSize * 4) / 5; + unsigned NormalizedAvg = floor((AvgUsage * 10) + 0.5) / 10; + unsigned NormalizedThreshold = floor((AlmostFullThreshold * 10) + 0.5) / 10; + + FOS << ProcResource.Name; + FOS.PadToColumn(17); + if (HasColors && NormalizedAvg >= NormalizedThreshold) + FOS.changeColor(raw_ostream::YELLOW, true, false); + FOS << NormalizedAvg; + if (HasColors) + FOS.resetColor(); + FOS.PadToColumn(28); + if (HasColors && + BU.MaxUsedSlots == static_cast<unsigned>(ProcResource.BufferSize)) + FOS.changeColor(raw_ostream::RED, true, false); + FOS << BU.MaxUsedSlots; + if (HasColors) + FOS.resetColor(); + FOS.PadToColumn(39); + FOS << ProcResource.BufferSize << '\n'; + } + + FOS.flush(); +} + +void SchedulerStatistics::printView(raw_ostream &OS) const { + printSchedulerStats(OS); + printSchedulerUsage(OS); +} + +} // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/SchedulerStatistics.h b/tools/llvm-mca/Views/SchedulerStatistics.h index 7383c54a1615..d99a395a726d 100644 --- a/tools/llvm-mca/SchedulerStatistics.h +++ b/tools/llvm-mca/Views/SchedulerStatistics.h @@ -17,75 +17,78 @@ /// /// Schedulers - number of cycles where we saw N instructions issued: /// [# issued], [# cycles] -/// 0, 7 (5.4%) -/// 1, 4 (3.1%) -/// 2, 8 (6.2%) +/// 0, 6 (2.9%) +/// 1, 106 (50.7%) +/// 2, 97 (46.4%) /// /// Scheduler's queue usage: -/// JALU01, 0/20 -/// JFPU01, 18/18 -/// JLSAGU, 0/12 +/// [1] Resource name. +/// [2] Average number of used buffer entries. +/// [3] Maximum number of used buffer entries. +/// [4] Total number of buffer entries. /// +/// [1] [2] [3] [4] +/// JALU01 0 0 20 +/// JFPU01 15 18 18 +/// JLSAGU 0 0 12 +// //===----------------------------------------------------------------------===// #ifndef LLVM_TOOLS_LLVM_MCA_SCHEDULERSTATISTICS_H #define LLVM_TOOLS_LLVM_MCA_SCHEDULERSTATISTICS_H -#include "View.h" +#include "Views/View.h" #include "llvm/ADT/SmallVector.h" #include "llvm/MC/MCSubtargetInfo.h" #include <map> +namespace llvm { namespace mca { -class SchedulerStatistics : public View { +class SchedulerStatistics final : public View { const llvm::MCSchedModel &SM; - - using Histogram = std::map<unsigned, unsigned>; - Histogram IssuedPerCycle; + unsigned LQResourceID; + unsigned SQResourceID; unsigned NumIssued; unsigned NumCycles; + unsigned MostRecentLoadDispatched; + unsigned MostRecentStoreDispatched; + // Tracks the usage of a scheduler's queue. struct BufferUsage { unsigned SlotsInUse; unsigned MaxUsedSlots; + uint64_t CumulativeNumUsedSlots; }; - std::map<unsigned, BufferUsage> BufferedResources; - - void updateHistograms() { - IssuedPerCycle[NumIssued]++; - NumIssued = 0; - } + std::vector<unsigned> IssuedPerCycle; + std::vector<BufferUsage> Usage; - void printSchedulerStatistics(llvm::raw_ostream &OS) const; + void updateHistograms(); + void printSchedulerStats(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) {} - + SchedulerStatistics(const llvm::MCSubtargetInfo &STI); 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; + void onReservedBuffers(const InstRef &IR, + 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 onReleasedBuffers(const InstRef &IR, + llvm::ArrayRef<unsigned> Buffers) override; - void printView(llvm::raw_ostream &OS) const override { - printSchedulerStatistics(OS); - printSchedulerUsage(OS); - } + void printView(llvm::raw_ostream &OS) const override; }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/SummaryView.cpp b/tools/llvm-mca/Views/SummaryView.cpp index 01399055c4fd..d8ac709e784d 100644 --- a/tools/llvm-mca/SummaryView.cpp +++ b/tools/llvm-mca/Views/SummaryView.cpp @@ -13,32 +13,33 @@ /// //===----------------------------------------------------------------------===// -#include "SummaryView.h" -#include "Support.h" +#include "Views/SummaryView.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/MCA/Support.h" #include "llvm/Support/Format.h" +namespace llvm { namespace mca { #define DEBUG_TYPE "llvm-mca" -using namespace llvm; - -SummaryView::SummaryView(const llvm::MCSchedModel &Model, const SourceMgr &S, +SummaryView::SummaryView(const MCSchedModel &Model, ArrayRef<MCInst> S, unsigned Width) - : SM(Model), Source(S), DispatchWidth(Width), TotalCycles(0), - NumMicroOps(0), ProcResourceUsage(Model.getNumProcResourceKinds(), 0), - ProcResourceMasks(Model.getNumProcResourceKinds(), 0) { + : SM(Model), Source(S), DispatchWidth(Width), LastInstructionIdx(0), + TotalCycles(0), NumMicroOps(0), + ProcResourceUsage(Model.getNumProcResourceKinds(), 0), + ProcResourceMasks(Model.getNumProcResourceKinds()) { 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.Type == HWInstructionEvent::Dispatched) + LastInstructionIdx = Event.IR.getSourceIndex(); - if (Event.IR.getSourceIndex() >= Source.size()) + // We are only interested in the "instruction retired" events generated by + // the retire stage for instructions that are part of iteration #0. + if (Event.Type != HWInstructionEvent::Retired || + Event.IR.getSourceIndex() >= Source.size()) return; // Update the cumulative number of resource cycles based on the processor @@ -60,10 +61,12 @@ void SummaryView::onEvent(const HWInstructionEvent &Event) { } void SummaryView::printView(raw_ostream &OS) const { - unsigned Iterations = Source.getNumIterations(); unsigned Instructions = Source.size(); + unsigned Iterations = (LastInstructionIdx / Instructions) + 1; unsigned TotalInstructions = Instructions * Iterations; + unsigned TotalUOps = NumMicroOps * Iterations; double IPC = (double)TotalInstructions / TotalCycles; + double UOpsPerCycle = (double)TotalUOps / TotalCycles; double BlockRThroughput = computeBlockRThroughput( SM, DispatchWidth, NumMicroOps, ProcResourceUsage); @@ -72,10 +75,12 @@ void SummaryView::printView(raw_ostream &OS) const { TempStream << "Iterations: " << Iterations; TempStream << "\nInstructions: " << TotalInstructions; TempStream << "\nTotal Cycles: " << TotalCycles; + TempStream << "\nTotal uOps: " << TotalUOps << '\n'; TempStream << "\nDispatch Width: " << DispatchWidth; - TempStream << "\nIPC: " << format("%.2f", IPC); - - // Round to the block reciprocal throughput to the nearest tenth. + TempStream << "\nuOps Per Cycle: " + << format("%.2f", floor((UOpsPerCycle * 100) + 0.5) / 100); + TempStream << "\nIPC: " + << format("%.2f", floor((IPC * 100) + 0.5) / 100); TempStream << "\nBlock RThroughput: " << format("%.1f", floor((BlockRThroughput * 10) + 0.5) / 10) << '\n'; @@ -83,3 +88,4 @@ void SummaryView::printView(raw_ostream &OS) const { OS << Buffer; } } // namespace mca. +} // namespace llvm diff --git a/tools/llvm-mca/SummaryView.h b/tools/llvm-mca/Views/SummaryView.h index b799ce3aa747..f59fd4233fbe 100644 --- a/tools/llvm-mca/SummaryView.h +++ b/tools/llvm-mca/Views/SummaryView.h @@ -29,19 +29,20 @@ #ifndef LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H #define LLVM_TOOLS_LLVM_MCA_SUMMARYVIEW_H -#include "SourceMgr.h" -#include "View.h" +#include "Views/View.h" #include "llvm/ADT/DenseMap.h" #include "llvm/MC/MCSchedule.h" #include "llvm/Support/raw_ostream.h" +namespace llvm { namespace mca { /// A view that collects and prints a few performance numbers. class SummaryView : public View { const llvm::MCSchedModel &SM; - const SourceMgr &Source; + llvm::ArrayRef<llvm::MCInst> Source; const unsigned DispatchWidth; + unsigned LastInstructionIdx; unsigned TotalCycles; // The total number of micro opcodes contributed by a block of instructions. unsigned NumMicroOps; @@ -62,15 +63,15 @@ class SummaryView : public View { double getBlockRThroughput() const; public: - SummaryView(const llvm::MCSchedModel &Model, const SourceMgr &S, + SummaryView(const llvm::MCSchedModel &Model, llvm::ArrayRef<llvm::MCInst> S, unsigned Width); void onCycleEnd() override { ++TotalCycles; } - void onEvent(const HWInstructionEvent &Event) override; void printView(llvm::raw_ostream &OS) const override; }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/Views/TimelineView.cpp b/tools/llvm-mca/Views/TimelineView.cpp new file mode 100644 index 000000000000..7d55bbc99c73 --- /dev/null +++ b/tools/llvm-mca/Views/TimelineView.cpp @@ -0,0 +1,294 @@ +//===--------------------- 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 "Views/TimelineView.h" + +namespace llvm { +namespace mca { + +TimelineView::TimelineView(const MCSubtargetInfo &sti, MCInstPrinter &Printer, + llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations, + unsigned Cycles) + : STI(sti), MCIP(Printer), Source(S), CurrentCycle(0), + MaxCycle(Cycles == 0 ? 80 : Cycles), LastCycle(0), WaitTime(S.size()), + UsedBuffer(S.size()) { + unsigned NumInstructions = Source.size(); + assert(Iterations && "Invalid number of iterations specified!"); + NumInstructions *= Iterations; + Timeline.resize(NumInstructions); + TimelineViewEntry InvalidTVEntry = {-1, 0, 0, 0, 0}; + std::fill(Timeline.begin(), Timeline.end(), InvalidTVEntry); + + WaitTimeEntry NullWTEntry = {0, 0, 0}; + std::fill(WaitTime.begin(), WaitTime.end(), NullWTEntry); + + std::pair<unsigned, int> NullUsedBufferEntry = {/* Invalid resource ID*/ 0, + /* unknown buffer size */ -1}; + std::fill(UsedBuffer.begin(), UsedBuffer.end(), NullUsedBufferEntry); +} + +void TimelineView::onReservedBuffers(const InstRef &IR, + ArrayRef<unsigned> Buffers) { + if (IR.getSourceIndex() >= Source.size()) + return; + + const MCSchedModel &SM = STI.getSchedModel(); + std::pair<unsigned, int> BufferInfo = {0, -1}; + for (const unsigned Buffer : Buffers) { + const MCProcResourceDesc &MCDesc = *SM.getProcResource(Buffer); + if (!BufferInfo.first || BufferInfo.second > MCDesc.BufferSize) { + BufferInfo.first = Buffer; + BufferInfo.second = MCDesc.BufferSize; + } + } + + UsedBuffer[IR.getSourceIndex()] = BufferInfo; +} + +void TimelineView::onEvent(const HWInstructionEvent &Event) { + const unsigned Index = Event.IR.getSourceIndex(); + if (Index >= Timeline.size()) + return; + + switch (Event.Type) { + case HWInstructionEvent::Retired: { + TimelineViewEntry &TVEntry = Timeline[Index]; + if (CurrentCycle < MaxCycle) + TVEntry.CycleRetired = CurrentCycle; + + // Update the WaitTime entry which corresponds to this Index. + assert(TVEntry.CycleDispatched >= 0 && "Invalid TVEntry found!"); + unsigned CycleDispatched = static_cast<unsigned>(TVEntry.CycleDispatched); + WaitTimeEntry &WTEntry = WaitTime[Index % Source.size()]; + WTEntry.CyclesSpentInSchedulerQueue += + TVEntry.CycleIssued - CycleDispatched; + assert(CycleDispatched <= TVEntry.CycleReady && + "Instruction cannot be ready if it hasn't been dispatched yet!"); + WTEntry.CyclesSpentInSQWhileReady += + TVEntry.CycleIssued - TVEntry.CycleReady; + WTEntry.CyclesSpentAfterWBAndBeforeRetire += + (CurrentCycle - 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: + // There may be multiple dispatch events. Microcoded instructions that are + // expanded into multiple uOps may require multiple dispatch cycles. Here, + // we want to capture the first dispatch cycle. + if (Timeline[Index].CycleDispatched == -1) + Timeline[Index].CycleDispatched = static_cast<int>(CurrentCycle); + break; + default: + return; + } + if (CurrentCycle < MaxCycle) + LastCycle = std::max(LastCycle, CurrentCycle); +} + +static raw_ostream::Colors chooseColor(unsigned CumulativeCycles, + unsigned Executions, int BufferSize) { + if (CumulativeCycles && BufferSize < 0) + return raw_ostream::MAGENTA; + unsigned Size = static_cast<unsigned>(BufferSize); + if (CumulativeCycles >= Size * Executions) + return raw_ostream::RED; + if ((CumulativeCycles * 2) >= Size * Executions) + return raw_ostream::YELLOW; + return raw_ostream::SAVEDCOLOR; +} + +static void tryChangeColor(raw_ostream &OS, unsigned Cycles, + unsigned Executions, int BufferSize) { + if (!OS.has_colors()) + return; + + raw_ostream::Colors Color = chooseColor(Cycles, Executions, BufferSize); + if (Color == raw_ostream::SAVEDCOLOR) { + OS.resetColor(); + return; + } + OS.changeColor(Color, /* bold */ true, /* BG */ false); +} + +void TimelineView::printWaitTimeEntry(formatted_raw_ostream &OS, + const WaitTimeEntry &Entry, + unsigned SourceIndex, + unsigned Executions) const { + OS << SourceIndex << '.'; + OS.PadToColumn(7); + + double AverageTime1, AverageTime2, AverageTime3; + AverageTime1 = (double)Entry.CyclesSpentInSchedulerQueue / Executions; + AverageTime2 = (double)Entry.CyclesSpentInSQWhileReady / Executions; + AverageTime3 = (double)Entry.CyclesSpentAfterWBAndBeforeRetire / Executions; + + OS << Executions; + OS.PadToColumn(13); + int BufferSize = UsedBuffer[SourceIndex].second; + tryChangeColor(OS, Entry.CyclesSpentInSchedulerQueue, Executions, BufferSize); + OS << format("%.1f", floor((AverageTime1 * 10) + 0.5) / 10); + OS.PadToColumn(20); + tryChangeColor(OS, Entry.CyclesSpentInSQWhileReady, Executions, BufferSize); + OS << format("%.1f", floor((AverageTime2 * 10) + 0.5) / 10); + OS.PadToColumn(27); + tryChangeColor(OS, Entry.CyclesSpentAfterWBAndBeforeRetire, Executions, + STI.getSchedModel().MicroOpBufferSize); + OS << format("%.1f", floor((AverageTime3 * 10) + 0.5) / 10); + + if (OS.has_colors()) + OS.resetColor(); + OS.PadToColumn(34); +} + +void TimelineView::printAverageWaitTimes(raw_ostream &OS) const { + std::string Header = + "\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" + " [0] [1] [2] [3]\n"; + OS << Header; + + // Use a different string stream for printing instructions. + std::string Instruction; + raw_string_ostream InstrStream(Instruction); + + formatted_raw_ostream FOS(OS); + unsigned Executions = Timeline.size() / Source.size(); + unsigned IID = 0; + for (const MCInst &Inst : Source) { + printWaitTimeEntry(FOS, WaitTime[IID], IID, Executions); + // Append the instruction info at the end of the line. + 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 = ""; + + ++IID; + } +} + +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); + assert(Entry.CycleDispatched >= 0 && "Invalid TimelineViewEntry!"); + unsigned CycleDispatched = static_cast<unsigned>(Entry.CycleDispatched); + for (unsigned I = 0, E = CycleDispatched; I < E; ++I) + OS << ((I % 5 == 0) ? '.' : ' '); + OS << TimelineView::DisplayChar::Dispatched; + if (CycleDispatched != Entry.CycleExecuted) { + // Zero latency instructions have the same value for CycleDispatched, + // CycleIssued and CycleExecuted. + for (unsigned I = CycleDispatched + 1, E = Entry.CycleIssued; I < E; ++I) + OS << TimelineView::DisplayChar::Waiting; + if (Entry.CycleIssued == Entry.CycleExecuted) + OS << TimelineView::DisplayChar::DisplayChar::Executed; + else { + if (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 { + formatted_raw_ostream FOS(OS); + printTimelineHeader(FOS, LastCycle); + FOS.flush(); + + // Use a different string stream for the instruction. + std::string Instruction; + raw_string_ostream InstrStream(Instruction); + + unsigned IID = 0; + const unsigned Iterations = Timeline.size() / Source.size(); + for (unsigned Iteration = 0; Iteration < Iterations; ++Iteration) { + for (const MCInst &Inst : Source) { + const TimelineViewEntry &Entry = Timeline[IID]; + if (Entry.CycleRetired == 0) + return; + + unsigned SourceIndex = IID % Source.size(); + printTimelineViewEntry(FOS, Entry, Iteration, SourceIndex); + // Append the instruction info at the end of the line. + 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 = ""; + + ++IID; + } + } +} +} // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/TimelineView.h b/tools/llvm-mca/Views/TimelineView.h index e53c23ec1cc2..ee981800161c 100644 --- a/tools/llvm-mca/TimelineView.h +++ b/tools/llvm-mca/Views/TimelineView.h @@ -100,14 +100,15 @@ #ifndef LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H #define LLVM_TOOLS_LLVM_MCA_TIMELINEVIEW_H -#include "SourceMgr.h" -#include "View.h" +#include "Views/View.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/MC/MCInst.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 llvm { namespace mca { /// This class listens to instruction state transition events @@ -120,14 +121,14 @@ namespace mca { class TimelineView : public View { const llvm::MCSubtargetInfo &STI; llvm::MCInstPrinter &MCIP; - const SourceMgr &AsmSequence; + llvm::ArrayRef<llvm::MCInst> Source; unsigned CurrentCycle; unsigned MaxCycle; unsigned LastCycle; struct TimelineViewEntry { - unsigned CycleDispatched; + int CycleDispatched; // A negative value is an "invalid cycle". unsigned CycleReady; unsigned CycleIssued; unsigned CycleExecuted; @@ -136,22 +137,22 @@ class TimelineView : public View { std::vector<TimelineViewEntry> Timeline; struct WaitTimeEntry { - unsigned Executions; unsigned CyclesSpentInSchedulerQueue; unsigned CyclesSpentInSQWhileReady; unsigned CyclesSpentAfterWBAndBeforeRetire; }; std::vector<WaitTimeEntry> WaitTime; + // This field is used to map instructions to buffered resources. + // Elements of this vector are <resourceID, BufferSizer> pairs. + std::vector<std::pair<unsigned, int>> UsedBuffer; + 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); + const WaitTimeEntry &E, unsigned Index, + unsigned Executions) const; // Display characters for the TimelineView report output. struct DisplayChar { @@ -165,16 +166,14 @@ class TimelineView : public View { 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); - } + llvm::ArrayRef<llvm::MCInst> S, unsigned Iterations, + unsigned Cycles); // Event handlers. void onCycleEnd() override { ++CurrentCycle; } void onEvent(const HWInstructionEvent &Event) override; + void onReservedBuffers(const InstRef &IR, + llvm::ArrayRef<unsigned> Buffers) override; // print functionalities. void printTimeline(llvm::raw_ostream &OS) const; @@ -185,5 +184,6 @@ public: } }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/View.cpp b/tools/llvm-mca/Views/View.cpp index 390a7aeb3b9d..6cfb9dd9f394 100644 --- a/tools/llvm-mca/View.cpp +++ b/tools/llvm-mca/Views/View.cpp @@ -12,9 +12,11 @@ /// //===----------------------------------------------------------------------===// -#include "View.h" +#include "Views/View.h" +namespace llvm { namespace mca { void View::anchor() {} } // namespace mca +} // namespace llvm diff --git a/tools/llvm-mca/View.h b/tools/llvm-mca/Views/View.h index 9ba94a5da977..4b82b0da0d27 100644 --- a/tools/llvm-mca/View.h +++ b/tools/llvm-mca/Views/View.h @@ -16,9 +16,10 @@ #ifndef LLVM_TOOLS_LLVM_MCA_VIEW_H #define LLVM_TOOLS_LLVM_MCA_VIEW_H -#include "HWEventListener.h" +#include "llvm/MCA/HWEventListener.h" #include "llvm/Support/raw_ostream.h" +namespace llvm { namespace mca { class View : public HWEventListener { @@ -28,5 +29,6 @@ public: void anchor() override; }; } // namespace mca +} // namespace llvm #endif diff --git a/tools/llvm-mca/llvm-mca.cpp b/tools/llvm-mca/llvm-mca.cpp index 897ff232a36d..68d63db599d7 100644 --- a/tools/llvm-mca/llvm-mca.cpp +++ b/tools/llvm-mca/llvm-mca.cpp @@ -22,26 +22,27 @@ //===----------------------------------------------------------------------===// #include "CodeRegion.h" -#include "Context.h" -#include "DispatchStatistics.h" -#include "FetchStage.h" -#include "InstructionInfoView.h" -#include "InstructionTables.h" -#include "Pipeline.h" +#include "CodeRegionGenerator.h" #include "PipelinePrinter.h" -#include "RegisterFileStatistics.h" -#include "ResourcePressureView.h" -#include "RetireControlUnitStatistics.h" -#include "SchedulerStatistics.h" -#include "SummaryView.h" -#include "TimelineView.h" +#include "Views/DispatchStatistics.h" +#include "Views/InstructionInfoView.h" +#include "Views/RegisterFileStatistics.h" +#include "Views/ResourcePressureView.h" +#include "Views/RetireControlUnitStatistics.h" +#include "Views/SchedulerStatistics.h" +#include "Views/SummaryView.h" +#include "Views/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/MCA/Context.h" +#include "llvm/MCA/Pipeline.h" +#include "llvm/MCA/Stages/EntryStage.h" +#include "llvm/MCA/Stages/InstructionTables.h" +#include "llvm/MCA/Support.h" #include "llvm/Support/CommandLine.h" +#include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/Host.h" @@ -67,13 +68,13 @@ static cl::opt<std::string> OutputFilename("o", cl::desc("Output filename"), cl::value_desc("filename")); static cl::opt<std::string> - ArchName("march", cl::desc("Target arch to assemble for, " - "see -version for available targets"), + ArchName("march", cl::desc("Target architecture. " + "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"), + TripleName("mtriple", + cl::desc("Target triple. See -version for available targets"), cl::cat(ToolOptions)); static cl::opt<std::string> @@ -148,15 +149,13 @@ static cl::opt<bool> 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> LoadQueueSize("lqueue", + cl::desc("Size of the load queue"), + 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<unsigned> StoreQueueSize("squeue", + cl::desc("Size of the store queue"), + cl::cat(ToolOptions), cl::init(0)); static cl::opt<bool> PrintInstructionTables("instruction-tables", @@ -180,7 +179,6 @@ static cl::opt<bool> namespace { const Target *getTarget(const char *ProgName) { - TripleName = Triple::normalize(TripleName); if (TripleName.empty()) TripleName = Triple::normalize(sys::getDefaultTargetTriple()); Triple TheTriple(TripleName); @@ -198,59 +196,6 @@ const Target *getTarget(const char *ProgName) { 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 = "-"; @@ -261,40 +206,6 @@ ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() { 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) { @@ -318,20 +229,30 @@ static void processViewOptions() { EnableAllViews.getPosition() < EnableAllStats.getPosition() ? EnableAllStats : EnableAllViews; - processOptionImpl(PrintSummaryView, Default); processOptionImpl(PrintRegisterFileStats, Default); processOptionImpl(PrintDispatchStats, Default); processOptionImpl(PrintSchedulerStats, Default); processOptionImpl(PrintRetireStats, Default); } +// Returns true on success. +static bool runPipeline(mca::Pipeline &P) { + // Handle pipeline errors here. + Expected<unsigned> Cycles = P.run(); + if (!Cycles) { + WithColor::error() << toString(Cycles.takeError()); + return false; + } + return true; +} + int main(int argc, char **argv) { InitLLVM X(argc, argv); // Initialize targets and assembly parsers. - llvm::InitializeAllTargetInfos(); - llvm::InitializeAllTargetMCs(); - llvm::InitializeAllAsmParsers(); + InitializeAllTargetInfos(); + InitializeAllTargetMCs(); + InitializeAllAsmParsers(); // Enable printing of available targets when flag --version is specified. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); @@ -342,9 +263,6 @@ int main(int argc, char **argv) { 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. @@ -384,9 +302,6 @@ int main(int argc, char **argv) { 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( @@ -419,14 +334,20 @@ int main(int argc, char **argv) { 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)) + // Parse the input and create CodeRegions that llvm-mca can analyze. + mca::AsmCodeRegionGenerator CRG(*TheTarget, SrcMgr, Ctx, *MAI, *STI, *MCII); + Expected<const mca::CodeRegions &> RegionsOrErr = CRG.parseCodeRegions(); + if (!RegionsOrErr) { + if (auto Err = + handleErrors(RegionsOrErr.takeError(), [](const StringError &E) { + WithColor::error() << E.getMessage() << '\n'; + })) { + // Default case. + WithColor::error() << toString(std::move(Err)) << '\n'; + } return 1; - + } + const mca::CodeRegions &Regions = *RegionsOrErr; if (Regions.empty()) { WithColor::error() << "no assembly instructions found.\n"; return 1; @@ -439,7 +360,7 @@ int main(int argc, char **argv) { return 1; } - unsigned AssemblerDialect = P->getAssemblerDialect(); + unsigned AssemblerDialect = CRG.getAssemblerDialect(); if (OutputAsmVariant >= 0) AssemblerDialect = static_cast<unsigned>(OutputAsmVariant); std::unique_ptr<MCInstPrinter> IP(TheTarget->createMCInstPrinter( @@ -452,7 +373,7 @@ int main(int argc, char **argv) { return 1; } - std::unique_ptr<llvm::ToolOutputFile> TOF = std::move(*OF); + std::unique_ptr<ToolOutputFile> TOF = std::move(*OF); const MCSchedModel &SM = STI->getSchedModel(); @@ -461,7 +382,7 @@ int main(int argc, char **argv) { Width = DispatchWidth; // Create an instruction builder. - mca::InstrBuilder IB(*STI, *MCII, *MRI, *MCIA, *IP); + mca::InstrBuilder IB(*STI, *MCII, *MRI, MCIA.get()); // Create a context to control ownership of the pipeline hardware. mca::Context MCA(*MRI, *STI); @@ -471,6 +392,7 @@ int main(int argc, char **argv) { // 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()) @@ -486,24 +408,53 @@ int main(int argc, char **argv) { TOF->os() << "\n\n"; } - mca::SourceMgr S(Region->getInstructions(), - PrintInstructionTables ? 1 : Iterations); + // Lower the MCInst sequence into an mca::Instruction sequence. + ArrayRef<MCInst> Insts = Region->getInstructions(); + std::vector<std::unique_ptr<mca::Instruction>> LoweredSequence; + for (const MCInst &MCI : Insts) { + Expected<std::unique_ptr<mca::Instruction>> Inst = + IB.createInstruction(MCI); + if (!Inst) { + if (auto NewE = handleErrors( + Inst.takeError(), + [&IP, &STI](const mca::InstructionError<MCInst> &IE) { + std::string InstructionStr; + raw_string_ostream SS(InstructionStr); + WithColor::error() << IE.Message << '\n'; + IP->printInst(&IE.Inst, SS, "", *STI); + SS.flush(); + WithColor::note() << "instruction: " << InstructionStr + << '\n'; + })) { + // Default case. + WithColor::error() << toString(std::move(NewE)); + } + return 1; + } + + LoweredSequence.emplace_back(std::move(Inst.get())); + } + + mca::SourceMgr S(LoweredSequence, 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)); + P->appendStage(llvm::make_unique<mca::EntryStage>(S)); + P->appendStage(llvm::make_unique<mca::InstructionTables>(SM)); 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::InstructionInfoView>( + *STI, *MCII, Insts, *IP)); } Printer.addView( - llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S)); - P->run(); + llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts)); + + if (!runPipeline(*P)) + return 1; + Printer.printReport(TOF->os()); continue; } @@ -513,11 +464,11 @@ int main(int argc, char **argv) { mca::PipelinePrinter Printer(*P); if (PrintSummaryView) - Printer.addView(llvm::make_unique<mca::SummaryView>(SM, S, Width)); + Printer.addView(llvm::make_unique<mca::SummaryView>(SM, Insts, Width)); if (PrintInstructionInfoView) Printer.addView( - llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, S, *IP)); + llvm::make_unique<mca::InstructionInfoView>(*STI, *MCII, Insts, *IP)); if (PrintDispatchStats) Printer.addView(llvm::make_unique<mca::DispatchStatistics>()); @@ -526,21 +477,26 @@ int main(int argc, char **argv) { Printer.addView(llvm::make_unique<mca::SchedulerStatistics>(*STI)); if (PrintRetireStats) - Printer.addView(llvm::make_unique<mca::RetireControlUnitStatistics>()); + Printer.addView(llvm::make_unique<mca::RetireControlUnitStatistics>(SM)); if (PrintRegisterFileStats) Printer.addView(llvm::make_unique<mca::RegisterFileStatistics>(*STI)); if (PrintResourcePressureView) Printer.addView( - llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, S)); + llvm::make_unique<mca::ResourcePressureView>(*STI, *IP, Insts)); if (PrintTimelineView) { + unsigned TimelineIterations = + TimelineMaxIterations ? TimelineMaxIterations : 10; Printer.addView(llvm::make_unique<mca::TimelineView>( - *STI, *IP, S, TimelineMaxIterations, TimelineMaxCycles)); + *STI, *IP, Insts, std::min(TimelineIterations, S.getNumIterations()), + TimelineMaxCycles)); } - P->run(); + if (!runPipeline(*P)) + return 1; + Printer.printReport(TOF->os()); // Clear the InstrBuilder internal state in preparation for another round. diff --git a/tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt b/tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..d3db43a686bb --- /dev/null +++ b/tools/llvm-microsoft-demangle-fuzzer/CMakeLists.txt @@ -0,0 +1,10 @@ +set(LLVM_LINK_COMPONENTS + Demangle + FuzzMutate + Support +) + +add_llvm_fuzzer(llvm-microsoft-demangle-fuzzer + llvm-microsoft-demangle-fuzzer.cpp + DUMMY_MAIN DummyDemanglerFuzzer.cpp + ) diff --git a/tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp b/tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp new file mode 100644 index 000000000000..a2bf9f1b807e --- /dev/null +++ b/tools/llvm-microsoft-demangle-fuzzer/DummyDemanglerFuzzer.cpp @@ -0,0 +1,19 @@ +//===--- DummyDemanglerMain.cpp - Entry point to sanity check the fuzzer --===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of main so we can build and test without linking libFuzzer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FuzzMutate/FuzzerCLI.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int main(int argc, char *argv[]) { + return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput); +} diff --git a/tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp b/tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp new file mode 100644 index 000000000000..4c1a4130e0b6 --- /dev/null +++ b/tools/llvm-microsoft-demangle-fuzzer/llvm-microsoft-demangle-fuzzer.cpp @@ -0,0 +1,21 @@ +//===--- llvm-demangle-fuzzer.cpp - Fuzzer for the Itanium Demangler ------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Demangle/Demangle.h" + +#include <cstdint> +#include <cstdlib> +#include <string> + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string NullTerminatedString((const char *)Data, Size); + free(llvm::microsoftDemangle(NullTerminatedString.c_str(), nullptr, nullptr, + nullptr)); + return 0; +} diff --git a/tools/llvm-mt/Opts.td b/tools/llvm-mt/Opts.td index 6dc3eea524e6..da5b2c992ee3 100644 --- a/tools/llvm-mt/Opts.td +++ b/tools/llvm-mt/Opts.td @@ -23,6 +23,7 @@ def validate_file_hashes : Joined<["/", "-"], "validate_file_hashes:">, HelpText def canonicalize : Flag<["/", "-"], "canonicalize:">, HelpText<"Not supported">, Group<unsupported>; def check_for_duplicates : Flag<["/", "-"], "check_for_duplicates:">, HelpText<"Not supported">, Group<unsupported>; def make_cdfs : Flag<["/", "-"], "makecdfs:">, HelpText<"Not supported">, Group<unsupported>; +def notify_update : Flag<["/", "-"], "notify_update">, HelpText<"Not supported">, Group<unsupported>; def verbose : Flag<["/", "-"], "verbose">, HelpText<"Not supported">, Group<unsupported>; def help : Flag<["/", "-"], "?">; def help_long : Flag<["/", "-"], "help">, Alias<help>; diff --git a/tools/llvm-mt/llvm-mt.cpp b/tools/llvm-mt/llvm-mt.cpp index 1339fd1572ec..ea75c012eac8 100644 --- a/tools/llvm-mt/llvm-mt.cpp +++ b/tools/llvm-mt/llvm-mt.cpp @@ -115,7 +115,7 @@ int main(int Argc, const char **Argv) { } if (InputArgs.hasArg(OPT_help)) { - T.PrintHelp(outs(), "mt", "Manifest Tool", false); + T.PrintHelp(outs(), "llvm-mt [options] file...", "Manifest Tool", false); return 0; } diff --git a/tools/llvm-nm/llvm-nm.cpp b/tools/llvm-nm/llvm-nm.cpp index 37c1bf85809e..042e284e8369 100644 --- a/tools/llvm-nm/llvm-nm.cpp +++ b/tools/llvm-nm/llvm-nm.cpp @@ -38,6 +38,7 @@ #include "llvm/Support/Program.h" #include "llvm/Support/Signals.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <vector> @@ -90,6 +91,8 @@ cl::opt<bool> BSDFormat("B", cl::desc("Alias for --format=bsd"), cl::Grouping); cl::opt<bool> POSIXFormat("P", cl::desc("Alias for --format=posix"), cl::Grouping); +cl::alias Portability("portability", cl::desc("Alias for --format=posix"), + cl::aliasopt(POSIXFormat), cl::NotHidden); cl::opt<bool> DarwinFormat("m", cl::desc("Alias for --format=darwin"), cl::Grouping); @@ -183,6 +186,8 @@ cl::opt<bool> DyldInfoOnly("dyldinfo-only", cl::opt<bool> NoLLVMBitcode("no-llvm-bc", cl::desc("Disable LLVM bitcode reader")); +cl::extrahelp HelpResponse("\nPass @FILE as argument to read options from FILE.\n"); + bool PrintAddress = true; bool MultipleFiles = false; @@ -194,7 +199,7 @@ std::string ToolName; static void error(Twine Message, Twine Path = Twine()) { HadError = true; - errs() << ToolName << ": " << Path << ": " << Message << ".\n"; + WithColor::error(errs(), ToolName) << Path << ": " << Message << ".\n"; } static bool error(std::error_code EC, Twine Path = Twine()) { @@ -207,11 +212,11 @@ static bool error(std::error_code EC, Twine Path = Twine()) { // This version of error() prints the archive name and member name, for example: // "libx.a(foo.o)" after the ToolName before the error message. It sets -// HadError but returns allowing the code to move on to other archive members. +// HadError but returns allowing the code to move on to other archive members. static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, StringRef ArchitectureName = StringRef()) { HadError = true; - errs() << ToolName << ": " << FileName; + WithColor::error(errs(), ToolName) << FileName; Expected<StringRef> NameOrErr = C.getName(); // TODO: if we have a error getting the name then it would be nice to print @@ -228,7 +233,7 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << " " << Buf << "\n"; } @@ -236,18 +241,18 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, // This version of error() prints the file name and which architecture slice it // is from, for example: "foo.o (for architecture i386)" after the ToolName // before the error message. It sets HadError but returns allowing the code to -// move on to other architecture slices. +// move on to other architecture slices. static void error(llvm::Error E, StringRef FileName, StringRef ArchitectureName = StringRef()) { HadError = true; - errs() << ToolName << ": " << FileName; + WithColor::error(errs(), ToolName) << FileName; if (!ArchitectureName.empty()) errs() << " (for architecture " << ArchitectureName << ") "; std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << " " << Buf << "\n"; } @@ -674,7 +679,7 @@ static void darwinPrintStab(MachOObjectFile *MachO, SymbolListT::iterator I) { } static Optional<std::string> demangle(StringRef Name, bool StripUnderscore) { - if (StripUnderscore && Name.size() > 0 && Name[0] == '_') + if (StripUnderscore && !Name.empty() && Name[0] == '_') Name = Name.substr(1); if (!Name.startswith("_Z")) @@ -709,7 +714,7 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, if (ReverseSort) Cmp = [=](const NMSymbol &A, const NMSymbol &B) { return Cmp(B, A); }; - llvm::sort(SymbolList.begin(), SymbolList.end(), Cmp); + llvm::sort(SymbolList, Cmp); } if (!PrintFileName) { @@ -757,6 +762,24 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, } } + auto writeFileName = [&](raw_ostream &S) { + if (!ArchitectureName.empty()) + S << "(for architecture " << ArchitectureName << "):"; + if (OutputFormat == posix && !ArchiveName.empty()) + S << ArchiveName << "[" << CurrentFilename << "]: "; + else { + if (!ArchiveName.empty()) + S << ArchiveName << ":"; + S << CurrentFilename << ": "; + } + }; + + if (SymbolList.empty()) { + if (PrintFileName) + writeFileName(errs()); + errs() << "no symbols\n"; + } + for (SymbolListT::iterator I = SymbolList.begin(), E = SymbolList.end(); I != E; ++I) { uint32_t SymFlags; @@ -778,17 +801,8 @@ static void sortAndPrintSymbolList(SymbolicFile &Obj, bool printName, (!Global && ExternalOnly) || (SizeSort && !PrintAddress) || (Weak && NoWeakSymbols)) continue; - if (PrintFileName) { - if (!ArchitectureName.empty()) - outs() << "(for architecture " << ArchitectureName << "):"; - if (OutputFormat == posix && !ArchiveName.empty()) - outs() << ArchiveName << "[" << CurrentFilename << "]: "; - else { - if (!ArchiveName.empty()) - outs() << ArchiveName << ":"; - outs() << CurrentFilename << ": "; - } - } + if (PrintFileName) + writeFileName(outs()); if ((JustSymbolName || (UndefinedOnly && MachO && OutputFormat != darwin)) && OutputFormat != posix) { @@ -1018,8 +1032,7 @@ 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 && + if (Obj.is64Bit() && Obj.getHeader64().filetype == MachO::MH_KEXT_BUNDLE && SegmentName == "__TEXT_EXEC" && SectionName == "__text") return 't'; if (SegmentName == "__TEXT" && SectionName == "__text") @@ -1152,7 +1165,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, // file get the section number for that section in this object file. unsigned int Nsect = 0; MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(&Obj); - if (SegSect.size() != 0 && MachO) { + if (!SegSect.empty() && MachO) { Nsect = getNsectForSegSect(MachO); // If this section is not in the object file no symbols are printed. if (Nsect == 0) @@ -1170,8 +1183,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, // see if this symbol is a symbol from that section and if not skip it. if (Nsect && Nsect != getNsectInMachO(*MachO, Sym)) continue; - NMSymbol S; - memset(&S, '\0', sizeof(S)); + NMSymbol S = {}; S.Size = 0; S.Address = 0; if (PrintSize) { @@ -1265,8 +1277,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } } if (!found) { - NMSymbol S; - memset(&S, '\0', sizeof(NMSymbol)); + NMSymbol S = {}; S.Address = Entry.address() + BaseSegmentAddress; S.Size = 0; S.TypeChar = '\0'; @@ -1356,8 +1367,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, // Now create the undefined symbol using the referened dynamic // library. - NMSymbol U; - memset(&U, '\0', sizeof(NMSymbol)); + NMSymbol U = {}; U.Address = 0; U.Size = 0; U.TypeChar = 'U'; @@ -1423,8 +1433,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } if (!found) { LastSymbolName = Entry.symbolName(); - NMSymbol B; - memset(&B, '\0', sizeof(NMSymbol)); + NMSymbol B = {}; B.Address = 0; B.Size = 0; B.TypeChar = 'U'; @@ -1483,8 +1492,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } if (!found) { LastSymbolName = Entry.symbolName(); - NMSymbol L; - memset(&L, '\0', sizeof(NMSymbol)); + NMSymbol L = {}; L.Name = Entry.symbolName(); L.Address = 0; L.Size = 0; @@ -1600,7 +1608,7 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, 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 + // We found a function starts segment, parse the addresses for // consumption. MachO::linkedit_data_command LLC = MachO->getLinkeditDataLoadCommand(Command); @@ -1622,9 +1630,8 @@ dumpSymbolNamesFromObject(SymbolicFile &Obj, bool printName, } // 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)); + if (!found) { + NMSymbol F = {}; F.Name = "<redacted function X>"; F.Address = FoundFns[f] + BaseSegmentAddress; F.Size = 0; @@ -1744,12 +1751,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { outs() << "Archive map\n"; for (; I != E; ++I) { Expected<Archive::Child> C = I->getMember(); - if (!C) + if (!C) { error(C.takeError(), Filename); + break; + } Expected<StringRef> FileNameOrErr = C->getName(); if (!FileNameOrErr) { error(FileNameOrErr.takeError(), Filename); - return; + break; } StringRef SymName = I->getName(); outs() << SymName << " in " << FileNameOrErr.get() << "\n"; @@ -1769,8 +1778,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { } if (SymbolicFile *O = dyn_cast<SymbolicFile>(&*ChildOrErr.get())) { if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) { - errs() << ToolName << ": warning sizes with -print-size for Mach-O " - "files are always zero.\n"; + WithColor::warning(errs(), ToolName) + << "sizes with -print-size for Mach-O files are always zero.\n"; MachOPrintSizeWarning = true; } if (!checkMachOAndArchFlags(O, Filename)) @@ -1793,7 +1802,7 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { } if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { // If we have a list of architecture flags specified dump only those. - if (!ArchAll && ArchFlags.size() != 0) { + if (!ArchAll && !ArchFlags.empty()) { // Look for a slice in the universal binary that matches each ArchFlag. bool ArchFound; for (unsigned i = 0; i < ArchFlags.size(); ++i) { @@ -1882,14 +1891,14 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { // No architecture flags were specified so if this contains a slice that // matches the host architecture dump only that. if (!ArchAll) { - StringRef HostArchName = MachOObjectFile::getHostArch().getArchName(); + Triple HostTriple = MachOObjectFile::getHostArch(); + StringRef HostArchName = HostTriple.getArchName(); for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), E = UB->end_objects(); I != E; ++I) { if (HostArchName == I->getArchFlagName()) { Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); std::string ArchiveName; - ArchiveName.clear(); if (ObjOrErr) { ObjectFile &Obj = *ObjOrErr.get(); dumpSymbolNamesFromObject(Obj, false); @@ -2011,8 +2020,8 @@ static void dumpSymbolNamesFromFile(std::string &Filename) { } if (SymbolicFile *O = dyn_cast<SymbolicFile>(&Bin)) { if (!MachOPrintSizeWarning && PrintSize && isa<MachOObjectFile>(O)) { - errs() << ToolName << ": warning sizes with -print-size for Mach-O files " - "are always zero.\n"; + WithColor::warning(errs(), ToolName) + << "sizes with -print-size for Mach-O files are always zero.\n"; MachOPrintSizeWarning = true; } if (!checkMachOAndArchFlags(O, Filename)) @@ -2064,7 +2073,7 @@ int main(int argc, char **argv) { } } - if (SegSect.size() != 0 && SegSect.size() != 2) + if (!SegSect.empty() && SegSect.size() != 2) error("bad number of arguments (must be two arguments)", "for the -s option"); diff --git a/tools/llvm-objcopy/Buffer.cpp b/tools/llvm-objcopy/Buffer.cpp new file mode 100644 index 000000000000..8044b023aaad --- /dev/null +++ b/tools/llvm-objcopy/Buffer.cpp @@ -0,0 +1,51 @@ +//===- Buffer.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Buffer.h" +#include "llvm-objcopy.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +Buffer::~Buffer() {} + +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 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); +} + +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/Buffer.h b/tools/llvm-objcopy/Buffer.h new file mode 100644 index 000000000000..e5b9c5b2d22b --- /dev/null +++ b/tools/llvm-objcopy/Buffer.h @@ -0,0 +1,66 @@ +//===- Buffer.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_OBJCOPY_BUFFER_H +#define LLVM_TOOLS_OBJCOPY_BUFFER_H + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/FileOutputBuffer.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> + +namespace llvm { +namespace objcopy { + +// 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(); +}; + +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_BUFFER_H diff --git a/tools/llvm-objcopy/CMakeLists.txt b/tools/llvm-objcopy/CMakeLists.txt index b0cd66be5b3a..1beb7374cf89 100644 --- a/tools/llvm-objcopy/CMakeLists.txt +++ b/tools/llvm-objcopy/CMakeLists.txt @@ -14,8 +14,15 @@ tablegen(LLVM StripOpts.inc -gen-opt-parser-defs) add_public_tablegen_target(StripOptsTableGen) add_llvm_tool(llvm-objcopy + Buffer.cpp + CopyConfig.cpp llvm-objcopy.cpp - Object.cpp + COFF/COFFObjcopy.cpp + COFF/Object.cpp + COFF/Reader.cpp + COFF/Writer.cpp + ELF/ELFObjcopy.cpp + ELF/Object.cpp DEPENDS ObjcopyOptsTableGen StripOptsTableGen diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.cpp b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp new file mode 100644 index 000000000000..6b386d29979c --- /dev/null +++ b/tools/llvm-objcopy/COFF/COFFObjcopy.cpp @@ -0,0 +1,98 @@ +//===- COFFObjcopy.cpp ----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "COFFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "Reader.h" +#include "Writer.h" +#include "llvm-objcopy.h" + +#include "llvm/Object/Binary.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Errc.h" +#include <cassert> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +static Error handleArgs(const CopyConfig &Config, Object &Obj) { + // StripAll removes all symbols and thus also removes all relocations. + if (Config.StripAll || Config.StripAllGNU) + for (Section &Sec : Obj.Sections) + Sec.Relocs.clear(); + + // If we need to do per-symbol removals, initialize the Referenced field. + if (Config.StripUnneeded || Config.DiscardAll || + !Config.SymbolsToRemove.empty()) + if (Error E = Obj.markSymbols()) + return E; + + // Actually do removals of symbols. + Obj.removeSymbols([&](const Symbol &Sym) { + // For StripAll, all relocations have been stripped and we remove all + // symbols. + if (Config.StripAll || Config.StripAllGNU) + return true; + + if (is_contained(Config.SymbolsToRemove, Sym.Name)) { + // Explicitly removing a referenced symbol is an error. + if (Sym.Referenced) + reportError(Config.OutputFilename, + make_error<StringError>( + "not stripping symbol '" + Sym.Name + + "' because it is named in a relocation.", + llvm::errc::invalid_argument)); + return true; + } + + if (!Sym.Referenced) { + // With --strip-unneeded, GNU objcopy removes all unreferenced local + // symbols, and any unreferenced undefined external. + if (Config.StripUnneeded && + (Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC || + Sym.Sym.SectionNumber == 0)) + return true; + + // GNU objcopy keeps referenced local symbols and external symbols + // if --discard-all is set, similar to what --strip-unneeded does, + // but undefined local symbols are kept when --discard-all is set. + if (Config.DiscardAll && Sym.Sym.StorageClass == IMAGE_SYM_CLASS_STATIC && + Sym.Sym.SectionNumber != 0) + return true; + } + + return false; + }); + return Error::success(); +} + +void executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out) { + COFFReader Reader(In); + Expected<std::unique_ptr<Object>> ObjOrErr = Reader.create(); + if (!ObjOrErr) + reportError(Config.InputFilename, ObjOrErr.takeError()); + Object *Obj = ObjOrErr->get(); + assert(Obj && "Unable to deserialize COFF object"); + if (Error E = handleArgs(Config, *Obj)) + reportError(Config.InputFilename, std::move(E)); + COFFWriter Writer(*Obj, Out); + if (Error E = Writer.write()) + reportError(Config.OutputFilename, std::move(E)); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/COFF/COFFObjcopy.h b/tools/llvm-objcopy/COFF/COFFObjcopy.h new file mode 100644 index 000000000000..bf70bd9b4d84 --- /dev/null +++ b/tools/llvm-objcopy/COFF/COFFObjcopy.h @@ -0,0 +1,31 @@ +//===- COFFObjcopy.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_OBJCOPY_COFFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H + +namespace llvm { + +namespace object { +class COFFObjectFile; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace coff { +void executeObjcopyOnBinary(const CopyConfig &Config, + object::COFFObjectFile &In, Buffer &Out); + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFFOBJCOPY_H diff --git a/tools/llvm-objcopy/COFF/Object.cpp b/tools/llvm-objcopy/COFF/Object.cpp new file mode 100644 index 000000000000..315d3a778623 --- /dev/null +++ b/tools/llvm-objcopy/COFF/Object.cpp @@ -0,0 +1,70 @@ +//===- Object.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Object.h" +#include <algorithm> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +void Object::addSymbols(ArrayRef<Symbol> NewSymbols) { + for (Symbol S : NewSymbols) { + S.UniqueId = NextSymbolUniqueId++; + Symbols.emplace_back(S); + } + updateSymbols(); +} + +void Object::updateSymbols() { + SymbolMap = DenseMap<size_t, Symbol *>(Symbols.size()); + size_t RawSymIndex = 0; + for (Symbol &Sym : Symbols) { + SymbolMap[Sym.UniqueId] = &Sym; + Sym.RawIndex = RawSymIndex; + RawSymIndex += 1 + Sym.Sym.NumberOfAuxSymbols; + } +} + +const Symbol *Object::findSymbol(size_t UniqueId) const { + auto It = SymbolMap.find(UniqueId); + if (It == SymbolMap.end()) + return nullptr; + return It->second; +} + +void Object::removeSymbols(function_ref<bool(const Symbol &)> ToRemove) { + Symbols.erase( + std::remove_if(std::begin(Symbols), std::end(Symbols), + [ToRemove](const Symbol &Sym) { return ToRemove(Sym); }), + std::end(Symbols)); + updateSymbols(); +} + +Error Object::markSymbols() { + for (Symbol &Sym : Symbols) + Sym.Referenced = false; + for (const Section &Sec : Sections) { + for (const Relocation &R : Sec.Relocs) { + auto It = SymbolMap.find(R.Target); + if (It == SymbolMap.end()) + return make_error<StringError>("Relocation target " + Twine(R.Target) + + " not found", + object_error::invalid_symbol_index); + It->second->Referenced = true; + } + } + return Error::success(); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/COFF/Object.h b/tools/llvm-objcopy/COFF/Object.h new file mode 100644 index 000000000000..7531fb4cf39e --- /dev/null +++ b/tools/llvm-objcopy/COFF/Object.h @@ -0,0 +1,148 @@ +//===- Object.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_OBJCOPY_COFF_OBJECT_H +#define LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include <cstddef> +#include <cstdint> +#include <vector> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Relocation { + Relocation() {} + Relocation(const object::coff_relocation& R) : Reloc(R) {} + + object::coff_relocation Reloc; + size_t Target; + StringRef TargetName; // Used for diagnostics only +}; + +struct Section { + object::coff_section Header; + ArrayRef<uint8_t> Contents; + std::vector<Relocation> Relocs; + StringRef Name; +}; + +struct Symbol { + object::coff_symbol32 Sym; + StringRef Name; + ArrayRef<uint8_t> AuxData; + size_t UniqueId; + size_t RawIndex; + bool Referenced; +}; + +struct Object { + bool IsPE = false; + + object::dos_header DosHeader; + ArrayRef<uint8_t> DosStub; + + object::coff_file_header CoffFileHeader; + + bool Is64 = false; + object::pe32plus_header PeHeader; + uint32_t BaseOfData = 0; // pe32plus_header lacks this field. + + std::vector<object::data_directory> DataDirectories; + std::vector<Section> Sections; + + ArrayRef<Symbol> getSymbols() const { return Symbols; } + // This allows mutating individual Symbols, but not mutating the list + // of symbols itself. + iterator_range<std::vector<Symbol>::iterator> getMutableSymbols() { + return make_range(Symbols.begin(), Symbols.end()); + } + + const Symbol *findSymbol(size_t UniqueId) const; + + void addSymbols(ArrayRef<Symbol> NewSymbols); + void removeSymbols(function_ref<bool(const Symbol &)> ToRemove); + + // Set the Referenced field on all Symbols, based on relocations in + // all sections. + Error markSymbols(); + +private: + std::vector<Symbol> Symbols; + DenseMap<size_t, Symbol *> SymbolMap; + + size_t NextSymbolUniqueId = 0; + + // Update SymbolMap and RawIndex in each Symbol. + void updateSymbols(); +}; + +// Copy between coff_symbol16 and coff_symbol32. +// The source and destination files can use either coff_symbol16 or +// coff_symbol32, while we always store them as coff_symbol32 in the +// intermediate data structure. +template <class Symbol1Ty, class Symbol2Ty> +void copySymbol(Symbol1Ty &Dest, const Symbol2Ty &Src) { + static_assert(sizeof(Dest.Name.ShortName) == sizeof(Src.Name.ShortName), + "Mismatched name sizes"); + memcpy(Dest.Name.ShortName, Src.Name.ShortName, sizeof(Dest.Name.ShortName)); + Dest.Value = Src.Value; + Dest.SectionNumber = Src.SectionNumber; + Dest.Type = Src.Type; + Dest.StorageClass = Src.StorageClass; + Dest.NumberOfAuxSymbols = Src.NumberOfAuxSymbols; +} + +// Copy between pe32_header and pe32plus_header. +// We store the intermediate state in a pe32plus_header. +template <class PeHeader1Ty, class PeHeader2Ty> +void copyPeHeader(PeHeader1Ty &Dest, const PeHeader2Ty &Src) { + Dest.Magic = Src.Magic; + Dest.MajorLinkerVersion = Src.MajorLinkerVersion; + Dest.MinorLinkerVersion = Src.MinorLinkerVersion; + Dest.SizeOfCode = Src.SizeOfCode; + Dest.SizeOfInitializedData = Src.SizeOfInitializedData; + Dest.SizeOfUninitializedData = Src.SizeOfUninitializedData; + Dest.AddressOfEntryPoint = Src.AddressOfEntryPoint; + Dest.BaseOfCode = Src.BaseOfCode; + Dest.ImageBase = Src.ImageBase; + Dest.SectionAlignment = Src.SectionAlignment; + Dest.FileAlignment = Src.FileAlignment; + Dest.MajorOperatingSystemVersion = Src.MajorOperatingSystemVersion; + Dest.MinorOperatingSystemVersion = Src.MinorOperatingSystemVersion; + Dest.MajorImageVersion = Src.MajorImageVersion; + Dest.MinorImageVersion = Src.MinorImageVersion; + Dest.MajorSubsystemVersion = Src.MajorSubsystemVersion; + Dest.MinorSubsystemVersion = Src.MinorSubsystemVersion; + Dest.Win32VersionValue = Src.Win32VersionValue; + Dest.SizeOfImage = Src.SizeOfImage; + Dest.SizeOfHeaders = Src.SizeOfHeaders; + Dest.CheckSum = Src.CheckSum; + Dest.Subsystem = Src.Subsystem; + Dest.DLLCharacteristics = Src.DLLCharacteristics; + Dest.SizeOfStackReserve = Src.SizeOfStackReserve; + Dest.SizeOfStackCommit = Src.SizeOfStackCommit; + Dest.SizeOfHeapReserve = Src.SizeOfHeapReserve; + Dest.SizeOfHeapCommit = Src.SizeOfHeapCommit; + Dest.LoaderFlags = Src.LoaderFlags; + Dest.NumberOfRvaAndSize = Src.NumberOfRvaAndSize; +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_OBJECT_H diff --git a/tools/llvm-objcopy/COFF/Reader.cpp b/tools/llvm-objcopy/COFF/Reader.cpp new file mode 100644 index 000000000000..a01768392d7d --- /dev/null +++ b/tools/llvm-objcopy/COFF/Reader.cpp @@ -0,0 +1,171 @@ +//===- Reader.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Reader.h" +#include "Object.h" +#include "llvm-objcopy.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; + +Error COFFReader::readExecutableHeaders(Object &Obj) const { + const dos_header *DH = COFFObj.getDOSHeader(); + Obj.Is64 = COFFObj.is64(); + if (!DH) + return Error::success(); + + Obj.IsPE = true; + Obj.DosHeader = *DH; + if (DH->AddressOfNewExeHeader > sizeof(*DH)) + Obj.DosStub = ArrayRef<uint8_t>(reinterpret_cast<const uint8_t *>(&DH[1]), + DH->AddressOfNewExeHeader - sizeof(*DH)); + + if (COFFObj.is64()) { + const pe32plus_header *PE32Plus = nullptr; + if (auto EC = COFFObj.getPE32PlusHeader(PE32Plus)) + return errorCodeToError(EC); + Obj.PeHeader = *PE32Plus; + } else { + const pe32_header *PE32 = nullptr; + if (auto EC = COFFObj.getPE32Header(PE32)) + return errorCodeToError(EC); + copyPeHeader(Obj.PeHeader, *PE32); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + Obj.BaseOfData = PE32->BaseOfData; + } + + for (size_t I = 0; I < Obj.PeHeader.NumberOfRvaAndSize; I++) { + const data_directory *Dir; + if (auto EC = COFFObj.getDataDirectory(I, Dir)) + return errorCodeToError(EC); + Obj.DataDirectories.emplace_back(*Dir); + } + return Error::success(); +} + +Error COFFReader::readSections(Object &Obj) const { + // Section indexing starts from 1. + for (size_t I = 1, E = COFFObj.getNumberOfSections(); I <= E; I++) { + const coff_section *Sec; + if (auto EC = COFFObj.getSection(I, Sec)) + return errorCodeToError(EC); + Obj.Sections.push_back(Section()); + Section &S = Obj.Sections.back(); + S.Header = *Sec; + if (auto EC = COFFObj.getSectionContents(Sec, S.Contents)) + return errorCodeToError(EC); + ArrayRef<coff_relocation> Relocs = COFFObj.getRelocations(Sec); + for (const coff_relocation &R : Relocs) + S.Relocs.push_back(R); + if (auto EC = COFFObj.getSectionName(Sec, S.Name)) + return errorCodeToError(EC); + if (Sec->hasExtendedRelocations()) + return make_error<StringError>("Extended relocations not supported yet", + object_error::parse_failed); + } + return Error::success(); +} + +Error COFFReader::readSymbols(Object &Obj, bool IsBigObj) const { + std::vector<Symbol> Symbols; + Symbols.reserve(COFFObj.getRawNumberOfSymbols()); + for (uint32_t I = 0, E = COFFObj.getRawNumberOfSymbols(); I < E;) { + Expected<COFFSymbolRef> SymOrErr = COFFObj.getSymbol(I); + if (!SymOrErr) + return SymOrErr.takeError(); + COFFSymbolRef SymRef = *SymOrErr; + + Symbols.push_back(Symbol()); + Symbol &Sym = Symbols.back(); + // Copy symbols from the original form into an intermediate coff_symbol32. + if (IsBigObj) + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol32 *>(SymRef.getRawPtr())); + else + copySymbol(Sym.Sym, + *reinterpret_cast<const coff_symbol16 *>(SymRef.getRawPtr())); + if (auto EC = COFFObj.getSymbolName(SymRef, Sym.Name)) + return errorCodeToError(EC); + Sym.AuxData = COFFObj.getSymbolAuxData(SymRef); + assert((Sym.AuxData.size() % + (IsBigObj ? sizeof(coff_symbol32) : sizeof(coff_symbol16))) == 0); + I += 1 + SymRef.getNumberOfAuxSymbols(); + } + Obj.addSymbols(Symbols); + return Error::success(); +} + +Error COFFReader::setRelocTargets(Object &Obj) const { + std::vector<const Symbol *> RawSymbolTable; + for (const Symbol &Sym : Obj.getSymbols()) { + RawSymbolTable.push_back(&Sym); + for (size_t I = 0; I < Sym.Sym.NumberOfAuxSymbols; I++) + RawSymbolTable.push_back(nullptr); + } + for (Section &Sec : Obj.Sections) { + for (Relocation &R : Sec.Relocs) { + if (R.Reloc.SymbolTableIndex >= RawSymbolTable.size()) + return make_error<StringError>("SymbolTableIndex out of range", + object_error::parse_failed); + const Symbol *Sym = RawSymbolTable[R.Reloc.SymbolTableIndex]; + if (Sym == nullptr) + return make_error<StringError>("Invalid SymbolTableIndex", + object_error::parse_failed); + R.Target = Sym->UniqueId; + R.TargetName = Sym->Name; + } + } + return Error::success(); +} + +Expected<std::unique_ptr<Object>> COFFReader::create() const { + auto Obj = llvm::make_unique<Object>(); + + const coff_file_header *CFH = nullptr; + const coff_bigobj_file_header *CBFH = nullptr; + COFFObj.getCOFFHeader(CFH); + COFFObj.getCOFFBigObjHeader(CBFH); + bool IsBigObj = false; + if (CFH) { + Obj->CoffFileHeader = *CFH; + } else { + if (!CBFH) + return make_error<StringError>("No COFF file header returned", + object_error::parse_failed); + // Only copying the few fields from the bigobj header that we need + // and won't recreate in the end. + Obj->CoffFileHeader.Machine = CBFH->Machine; + Obj->CoffFileHeader.TimeDateStamp = CBFH->TimeDateStamp; + IsBigObj = true; + } + + if (Error E = readExecutableHeaders(*Obj)) + return std::move(E); + if (Error E = readSections(*Obj)) + return std::move(E); + if (Error E = readSymbols(*Obj, IsBigObj)) + return std::move(E); + if (Error E = setRelocTargets(*Obj)) + return std::move(E); + + return std::move(Obj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/COFF/Reader.h b/tools/llvm-objcopy/COFF/Reader.h new file mode 100644 index 000000000000..ca7057d08c9f --- /dev/null +++ b/tools/llvm-objcopy/COFF/Reader.h @@ -0,0 +1,43 @@ +//===- Reader.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_OBJCOPY_COFF_READER_H +#define LLVM_TOOLS_OBJCOPY_COFF_READER_H + +#include "Buffer.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/Error.h" + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Object; + +using object::COFFObjectFile; + +class COFFReader { + const COFFObjectFile &COFFObj; + + Error readExecutableHeaders(Object &Obj) const; + Error readSections(Object &Obj) const; + Error readSymbols(Object &Obj, bool IsBigObj) const; + Error setRelocTargets(Object &Obj) const; + +public: + explicit COFFReader(const COFFObjectFile &O) : COFFObj(O) {} + Expected<std::unique_ptr<Object>> create() const; +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_READER_H diff --git a/tools/llvm-objcopy/COFF/Writer.cpp b/tools/llvm-objcopy/COFF/Writer.cpp new file mode 100644 index 000000000000..385d43b1bae5 --- /dev/null +++ b/tools/llvm-objcopy/COFF/Writer.cpp @@ -0,0 +1,337 @@ +//===- Writer.cpp ---------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "Writer.h" +#include "Object.h" +#include "llvm-objcopy.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/BinaryFormat/COFF.h" +#include "llvm/Object/COFF.h" +#include "llvm/Support/ErrorHandling.h" +#include <cstddef> +#include <cstdint> + +namespace llvm { +namespace objcopy { +namespace coff { + +using namespace object; +using namespace COFF; + +Error COFFWriter::finalizeRelocTargets() { + for (Section &Sec : Obj.Sections) { + for (Relocation &R : Sec.Relocs) { + const Symbol *Sym = Obj.findSymbol(R.Target); + if (Sym == nullptr) + return make_error<StringError>("Relocation target " + R.TargetName + + " (" + Twine(R.Target) + + ") not found", + object_error::invalid_symbol_index); + R.Reloc.SymbolTableIndex = Sym->RawIndex; + } + } + return Error::success(); +} + +void COFFWriter::layoutSections() { + for (auto &S : Obj.Sections) { + if (S.Header.SizeOfRawData > 0) + S.Header.PointerToRawData = FileSize; + FileSize += S.Header.SizeOfRawData; // For executables, this is already + // aligned to FileAlignment. + S.Header.NumberOfRelocations = S.Relocs.size(); + S.Header.PointerToRelocations = + S.Header.NumberOfRelocations > 0 ? FileSize : 0; + FileSize += S.Relocs.size() * sizeof(coff_relocation); + FileSize = alignTo(FileSize, FileAlignment); + + if (S.Header.Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA) + SizeOfInitializedData += S.Header.SizeOfRawData; + } +} + +size_t COFFWriter::finalizeStringTable() { + for (auto &S : Obj.Sections) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + for (const auto &S : Obj.getSymbols()) + if (S.Name.size() > COFF::NameSize) + StrTabBuilder.add(S.Name); + + StrTabBuilder.finalize(); + + for (auto &S : Obj.Sections) { + if (S.Name.size() > COFF::NameSize) { + snprintf(S.Header.Name, sizeof(S.Header.Name), "/%d", + (int)StrTabBuilder.getOffset(S.Name)); + } else { + strncpy(S.Header.Name, S.Name.data(), COFF::NameSize); + } + } + for (auto &S : Obj.getMutableSymbols()) { + if (S.Name.size() > COFF::NameSize) { + S.Sym.Name.Offset.Zeroes = 0; + S.Sym.Name.Offset.Offset = StrTabBuilder.getOffset(S.Name); + } else { + strncpy(S.Sym.Name.ShortName, S.Name.data(), COFF::NameSize); + } + } + return StrTabBuilder.getSize(); +} + +template <class SymbolTy> +std::pair<size_t, size_t> COFFWriter::finalizeSymbolTable() { + size_t SymTabSize = Obj.getSymbols().size() * sizeof(SymbolTy); + for (const auto &S : Obj.getSymbols()) + SymTabSize += S.AuxData.size(); + return std::make_pair(SymTabSize, sizeof(SymbolTy)); +} + +Error COFFWriter::finalize(bool IsBigObj) { + if (Error E = finalizeRelocTargets()) + return E; + + size_t SizeOfHeaders = 0; + FileAlignment = 1; + size_t PeHeaderSize = 0; + if (Obj.IsPE) { + Obj.DosHeader.AddressOfNewExeHeader = + sizeof(Obj.DosHeader) + Obj.DosStub.size(); + SizeOfHeaders += Obj.DosHeader.AddressOfNewExeHeader + sizeof(PEMagic); + + FileAlignment = Obj.PeHeader.FileAlignment; + Obj.PeHeader.NumberOfRvaAndSize = Obj.DataDirectories.size(); + + PeHeaderSize = Obj.Is64 ? sizeof(pe32plus_header) : sizeof(pe32_header); + SizeOfHeaders += + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + } + Obj.CoffFileHeader.NumberOfSections = Obj.Sections.size(); + SizeOfHeaders += + IsBigObj ? sizeof(coff_bigobj_file_header) : sizeof(coff_file_header); + SizeOfHeaders += sizeof(coff_section) * Obj.Sections.size(); + SizeOfHeaders = alignTo(SizeOfHeaders, FileAlignment); + + Obj.CoffFileHeader.SizeOfOptionalHeader = + PeHeaderSize + sizeof(data_directory) * Obj.DataDirectories.size(); + + FileSize = SizeOfHeaders; + SizeOfInitializedData = 0; + + layoutSections(); + + if (Obj.IsPE) { + Obj.PeHeader.SizeOfHeaders = SizeOfHeaders; + Obj.PeHeader.SizeOfInitializedData = SizeOfInitializedData; + + if (!Obj.Sections.empty()) { + const Section &S = Obj.Sections.back(); + Obj.PeHeader.SizeOfImage = + alignTo(S.Header.VirtualAddress + S.Header.VirtualSize, + Obj.PeHeader.SectionAlignment); + } + + // If the PE header had a checksum, clear it, since it isn't valid + // any longer. (We don't calculate a new one.) + Obj.PeHeader.CheckSum = 0; + } + + size_t StrTabSize = finalizeStringTable(); + size_t SymTabSize, SymbolSize; + std::tie(SymTabSize, SymbolSize) = IsBigObj + ? finalizeSymbolTable<coff_symbol32>() + : finalizeSymbolTable<coff_symbol16>(); + + size_t PointerToSymbolTable = FileSize; + // StrTabSize <= 4 is the size of an empty string table, only consisting + // of the length field. + if (SymTabSize == 0 && StrTabSize <= 4 && Obj.IsPE) { + // For executables, don't point to the symbol table and skip writing + // the length field, if both the symbol and string tables are empty. + PointerToSymbolTable = 0; + StrTabSize = 0; + } + + size_t NumRawSymbols = SymTabSize / SymbolSize; + Obj.CoffFileHeader.PointerToSymbolTable = PointerToSymbolTable; + Obj.CoffFileHeader.NumberOfSymbols = NumRawSymbols; + FileSize += SymTabSize + StrTabSize; + FileSize = alignTo(FileSize, FileAlignment); + + return Error::success(); +} + +void COFFWriter::writeHeaders(bool IsBigObj) { + uint8_t *Ptr = Buf.getBufferStart(); + if (Obj.IsPE) { + memcpy(Ptr, &Obj.DosHeader, sizeof(Obj.DosHeader)); + Ptr += sizeof(Obj.DosHeader); + memcpy(Ptr, Obj.DosStub.data(), Obj.DosStub.size()); + Ptr += Obj.DosStub.size(); + memcpy(Ptr, PEMagic, sizeof(PEMagic)); + Ptr += sizeof(PEMagic); + } + if (!IsBigObj) { + memcpy(Ptr, &Obj.CoffFileHeader, sizeof(Obj.CoffFileHeader)); + Ptr += sizeof(Obj.CoffFileHeader); + } else { + // Generate a coff_bigobj_file_header, filling it in with the values + // from Obj.CoffFileHeader. All extra fields that don't exist in + // coff_file_header can be set to hardcoded values. + coff_bigobj_file_header BigObjHeader; + BigObjHeader.Sig1 = IMAGE_FILE_MACHINE_UNKNOWN; + BigObjHeader.Sig2 = 0xffff; + BigObjHeader.Version = BigObjHeader::MinBigObjectVersion; + BigObjHeader.Machine = Obj.CoffFileHeader.Machine; + BigObjHeader.TimeDateStamp = Obj.CoffFileHeader.TimeDateStamp; + memcpy(BigObjHeader.UUID, BigObjMagic, sizeof(BigObjMagic)); + BigObjHeader.unused1 = 0; + BigObjHeader.unused2 = 0; + BigObjHeader.unused3 = 0; + BigObjHeader.unused4 = 0; + // The value in Obj.CoffFileHeader.NumberOfSections is truncated, thus + // get the original one instead. + BigObjHeader.NumberOfSections = Obj.Sections.size(); + BigObjHeader.PointerToSymbolTable = Obj.CoffFileHeader.PointerToSymbolTable; + BigObjHeader.NumberOfSymbols = Obj.CoffFileHeader.NumberOfSymbols; + + memcpy(Ptr, &BigObjHeader, sizeof(BigObjHeader)); + Ptr += sizeof(BigObjHeader); + } + if (Obj.IsPE) { + if (Obj.Is64) { + memcpy(Ptr, &Obj.PeHeader, sizeof(Obj.PeHeader)); + Ptr += sizeof(Obj.PeHeader); + } else { + pe32_header PeHeader; + copyPeHeader(PeHeader, Obj.PeHeader); + // The pe32plus_header (stored in Object) lacks the BaseOfData field. + PeHeader.BaseOfData = Obj.BaseOfData; + + memcpy(Ptr, &PeHeader, sizeof(PeHeader)); + Ptr += sizeof(PeHeader); + } + for (const auto &DD : Obj.DataDirectories) { + memcpy(Ptr, &DD, sizeof(DD)); + Ptr += sizeof(DD); + } + } + for (const auto &S : Obj.Sections) { + memcpy(Ptr, &S.Header, sizeof(S.Header)); + Ptr += sizeof(S.Header); + } +} + +void COFFWriter::writeSections() { + for (const auto &S : Obj.Sections) { + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData; + std::copy(S.Contents.begin(), S.Contents.end(), Ptr); + + // For executable sections, pad the remainder of the raw data size with + // 0xcc, which is int3 on x86. + if ((S.Header.Characteristics & IMAGE_SCN_CNT_CODE) && + S.Header.SizeOfRawData > S.Contents.size()) + memset(Ptr + S.Contents.size(), 0xcc, + S.Header.SizeOfRawData - S.Contents.size()); + + Ptr += S.Header.SizeOfRawData; + for (const auto &R : S.Relocs) { + memcpy(Ptr, &R.Reloc, sizeof(R.Reloc)); + Ptr += sizeof(R.Reloc); + } + } +} + +template <class SymbolTy> void COFFWriter::writeSymbolStringTables() { + uint8_t *Ptr = Buf.getBufferStart() + Obj.CoffFileHeader.PointerToSymbolTable; + for (const auto &S : Obj.getSymbols()) { + // Convert symbols back to the right size, from coff_symbol32. + copySymbol<SymbolTy, coff_symbol32>(*reinterpret_cast<SymbolTy *>(Ptr), + S.Sym); + Ptr += sizeof(SymbolTy); + std::copy(S.AuxData.begin(), S.AuxData.end(), Ptr); + Ptr += S.AuxData.size(); + } + if (StrTabBuilder.getSize() > 4 || !Obj.IsPE) { + // Always write a string table in object files, even an empty one. + StrTabBuilder.write(Ptr); + Ptr += StrTabBuilder.getSize(); + } +} + +Error COFFWriter::write(bool IsBigObj) { + if (Error E = finalize(IsBigObj)) + return E; + + Buf.allocate(FileSize); + + writeHeaders(IsBigObj); + writeSections(); + if (IsBigObj) + writeSymbolStringTables<coff_symbol32>(); + else + writeSymbolStringTables<coff_symbol16>(); + + if (Obj.IsPE) + if (Error E = patchDebugDirectory()) + return E; + + return Buf.commit(); +} + +// Locate which sections contain the debug directories, iterate over all +// the debug_directory structs in there, and set the PointerToRawData field +// in all of them, according to their new physical location in the file. +Error COFFWriter::patchDebugDirectory() { + if (Obj.DataDirectories.size() < DEBUG_DIRECTORY) + return Error::success(); + const data_directory *Dir = &Obj.DataDirectories[DEBUG_DIRECTORY]; + if (Dir->Size <= 0) + return Error::success(); + for (const auto &S : Obj.Sections) { + if (Dir->RelativeVirtualAddress >= S.Header.VirtualAddress && + Dir->RelativeVirtualAddress < + S.Header.VirtualAddress + S.Header.SizeOfRawData) { + if (Dir->RelativeVirtualAddress + Dir->Size > + S.Header.VirtualAddress + S.Header.SizeOfRawData) + return make_error<StringError>( + "Debug directory extends past end of section", + object_error::parse_failed); + + size_t Offset = Dir->RelativeVirtualAddress - S.Header.VirtualAddress; + uint8_t *Ptr = Buf.getBufferStart() + S.Header.PointerToRawData + Offset; + uint8_t *End = Ptr + Dir->Size; + while (Ptr < End) { + debug_directory *Debug = reinterpret_cast<debug_directory *>(Ptr); + Debug->PointerToRawData = + S.Header.PointerToRawData + Offset + sizeof(debug_directory); + Ptr += sizeof(debug_directory) + Debug->SizeOfData; + Offset += sizeof(debug_directory) + Debug->SizeOfData; + } + // Debug directory found and patched, all done. + return Error::success(); + } + } + return make_error<StringError>("Debug directory not found", + object_error::parse_failed); +} + +Error COFFWriter::write() { + bool IsBigObj = Obj.Sections.size() > MaxNumberOfSections16; + if (IsBigObj && Obj.IsPE) + return make_error<StringError>("Too many sections for executable", + object_error::parse_failed); + return write(IsBigObj); +} + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/COFF/Writer.h b/tools/llvm-objcopy/COFF/Writer.h new file mode 100644 index 000000000000..ab66e0cc1134 --- /dev/null +++ b/tools/llvm-objcopy/COFF/Writer.h @@ -0,0 +1,61 @@ +//===- Writer.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_OBJCOPY_COFF_WRITER_H +#define LLVM_TOOLS_OBJCOPY_COFF_WRITER_H + +#include "Buffer.h" +#include "llvm/MC/StringTableBuilder.h" +#include "llvm/Support/Error.h" +#include <cstddef> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace coff { + +struct Object; + +class COFFWriter { + Object &Obj; + Buffer &Buf; + + size_t FileSize; + size_t FileAlignment; + size_t SizeOfInitializedData; + StringTableBuilder StrTabBuilder; + + Error finalizeRelocTargets(); + void layoutSections(); + size_t finalizeStringTable(); + template <class SymbolTy> std::pair<size_t, size_t> finalizeSymbolTable(); + + Error finalize(bool IsBigObj); + + void writeHeaders(bool IsBigObj); + void writeSections(); + template <class SymbolTy> void writeSymbolStringTables(); + + Error write(bool IsBigObj); + + Error patchDebugDirectory(); + +public: + virtual ~COFFWriter() {} + Error write(); + + COFFWriter(Object &Obj, Buffer &Buf) + : Obj(Obj), Buf(Buf), StrTabBuilder(StringTableBuilder::WinCOFF) {} +}; + +} // end namespace coff +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_COFF_WRITER_H diff --git a/tools/llvm-objcopy/CopyConfig.cpp b/tools/llvm-objcopy/CopyConfig.cpp new file mode 100644 index 000000000000..3737f571ae61 --- /dev/null +++ b/tools/llvm-objcopy/CopyConfig.cpp @@ -0,0 +1,474 @@ +//===- CopyConfig.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "CopyConfig.h" +#include "llvm-objcopy.h" + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/MemoryBuffer.h" +#include <memory> +#include <string> + +namespace llvm { +namespace objcopy { + +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) {} +}; + +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) {} +}; + +enum SectionFlag { + SecNone = 0, + SecAlloc = 1 << 0, + SecLoad = 1 << 1, + SecNoload = 1 << 2, + SecReadonly = 1 << 3, + SecDebug = 1 << 4, + SecCode = 1 << 5, + SecData = 1 << 6, + SecRom = 1 << 7, + SecMerge = 1 << 8, + SecStrings = 1 << 9, + SecContents = 1 << 10, + SecShare = 1 << 11, + LLVM_MARK_AS_BITMASK_ENUM(/* LargestValue = */ SecShare) +}; + +} // namespace + +static SectionFlag parseSectionRenameFlag(StringRef SectionName) { + return llvm::StringSwitch<SectionFlag>(SectionName) + .Case("alloc", SectionFlag::SecAlloc) + .Case("load", SectionFlag::SecLoad) + .Case("noload", SectionFlag::SecNoload) + .Case("readonly", SectionFlag::SecReadonly) + .Case("debug", SectionFlag::SecDebug) + .Case("code", SectionFlag::SecCode) + .Case("data", SectionFlag::SecData) + .Case("rom", SectionFlag::SecRom) + .Case("merge", SectionFlag::SecMerge) + .Case("strings", SectionFlag::SecStrings) + .Case("contents", SectionFlag::SecContents) + .Case("share", SectionFlag::SecShare) + .Default(SectionFlag::SecNone); +} + +static SectionRename parseRenameSectionValue(StringRef FlagValue) { + if (!FlagValue.contains('=')) + error("Bad format for --rename-section: missing '='"); + + // Initial split: ".foo" = ".bar,f1,f2,..." + auto Old2New = FlagValue.split('='); + SectionRename SR; + SR.OriginalName = Old2New.first; + + // Flags split: ".bar" "f1" "f2" ... + SmallVector<StringRef, 6> NameAndFlags; + Old2New.second.split(NameAndFlags, ','); + SR.NewName = NameAndFlags[0]; + + if (NameAndFlags.size() > 1) { + SectionFlag Flags = SectionFlag::SecNone; + for (size_t I = 1, Size = NameAndFlags.size(); I < Size; ++I) { + SectionFlag Flag = parseSectionRenameFlag(NameAndFlags[I]); + if (Flag == SectionFlag::SecNone) + error("Unrecognized section flag '" + NameAndFlags[I] + + "'. Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, " + "strings."); + Flags |= Flag; + } + + SR.NewFlags = 0; + if (Flags & SectionFlag::SecAlloc) + *SR.NewFlags |= ELF::SHF_ALLOC; + if (!(Flags & SectionFlag::SecReadonly)) + *SR.NewFlags |= ELF::SHF_WRITE; + if (Flags & SectionFlag::SecCode) + *SR.NewFlags |= ELF::SHF_EXECINSTR; + if (Flags & SectionFlag::SecMerge) + *SR.NewFlags |= ELF::SHF_MERGE; + if (Flags & SectionFlag::SecStrings) + *SR.NewFlags |= ELF::SHF_STRINGS; + } + + return SR; +} + +static const StringMap<MachineInfo> ArchMap{ + // Name, {EMachine, 64bit, LittleEndian} + {"aarch64", {ELF::EM_AARCH64, true, true}}, + {"arm", {ELF::EM_ARM, false, true}}, + {"i386", {ELF::EM_386, false, true}}, + {"i386:x86-64", {ELF::EM_X86_64, true, true}}, + {"powerpc:common64", {ELF::EM_PPC64, true, true}}, + {"sparc", {ELF::EM_SPARC, false, true}}, + {"x86-64", {ELF::EM_X86_64, true, true}}, +}; + +static const MachineInfo &getMachineInfo(StringRef Arch) { + auto Iter = ArchMap.find(Arch); + if (Iter == std::end(ArchMap)) + error("Invalid architecture: '" + Arch + "'"); + return Iter->getValue(); +} + +static const StringMap<MachineInfo> OutputFormatMap{ + // Name, {EMachine, 64bit, LittleEndian} + {"elf32-i386", {ELF::EM_386, false, true}}, + {"elf32-powerpcle", {ELF::EM_PPC, false, true}}, + {"elf32-x86-64", {ELF::EM_X86_64, false, true}}, + {"elf64-powerpcle", {ELF::EM_PPC64, true, true}}, + {"elf64-x86-64", {ELF::EM_X86_64, true, true}}, +}; + +static const MachineInfo &getOutputFormatMachineInfo(StringRef Format) { + auto Iter = OutputFormatMap.find(Format); + if (Iter == std::end(OutputFormatMap)) + error("Invalid output format: '" + Format + "'"); + return Iter->getValue(); +} + +static void addGlobalSymbolsFromFile(std::vector<std::string> &Symbols, + StringRef Filename) { + SmallVector<StringRef, 16> Lines; + auto BufOrErr = MemoryBuffer::getFile(Filename); + if (!BufOrErr) + reportError(Filename, BufOrErr.getError()); + + BufOrErr.get()->getBuffer().split(Lines, '\n'); + for (StringRef Line : Lines) { + // Ignore everything after '#', trim whitespace, and only add the symbol if + // it's not empty. + auto TrimmedLine = Line.split('#').first.trim(); + if (!TrimmedLine.empty()) + Symbols.push_back(TrimmedLine.str()); + } +} + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. +DriverConfig 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); + } + + if (InputArgs.hasArg(OBJCOPY_version)) { + outs() << "llvm-objcopy, compatible with GNU objcopy\n"; + cl::PrintVersionMessage(); + 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]; + if (InputArgs.hasArg(OBJCOPY_target) && + (InputArgs.hasArg(OBJCOPY_input_target) || + InputArgs.hasArg(OBJCOPY_output_target))) + error("--target cannot be used with --input-target or --output-target"); + + if (InputArgs.hasArg(OBJCOPY_target)) { + Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_target); + } else { + Config.InputFormat = InputArgs.getLastArgValue(OBJCOPY_input_target); + Config.OutputFormat = InputArgs.getLastArgValue(OBJCOPY_output_target); + } + if (Config.InputFormat == "binary") { + auto BinaryArch = InputArgs.getLastArgValue(OBJCOPY_binary_architecture); + if (BinaryArch.empty()) + error("Specified binary input without specifiying an architecture"); + Config.BinaryArch = getMachineInfo(BinaryArch); + } + if (!Config.OutputFormat.empty() && Config.OutputFormat != "binary") + Config.OutputArch = getOutputFormatMachineInfo(Config.OutputFormat); + + if (auto Arg = InputArgs.getLastArg(OBJCOPY_compress_debug_sections, + OBJCOPY_compress_debug_sections_eq)) { + Config.CompressionType = DebugCompressionType::Z; + + if (Arg->getOption().getID() == OBJCOPY_compress_debug_sections_eq) { + Config.CompressionType = + StringSwitch<DebugCompressionType>( + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)) + .Case("zlib-gnu", DebugCompressionType::GNU) + .Case("zlib", DebugCompressionType::Z) + .Default(DebugCompressionType::None); + if (Config.CompressionType == DebugCompressionType::None) + error("Invalid or unsupported --compress-debug-sections format: " + + InputArgs.getLastArgValue(OBJCOPY_compress_debug_sections_eq)); + if (!zlib::isAvailable()) + error("LLVM was not compiled with LLVM_ENABLE_ZLIB: can not compress."); + } + } + + Config.AddGnuDebugLink = InputArgs.getLastArgValue(OBJCOPY_add_gnu_debuglink); + Config.BuildIdLinkDir = InputArgs.getLastArgValue(OBJCOPY_build_id_link_dir); + if (InputArgs.hasArg(OBJCOPY_build_id_link_input)) + Config.BuildIdLinkInput = + InputArgs.getLastArgValue(OBJCOPY_build_id_link_input); + if (InputArgs.hasArg(OBJCOPY_build_id_link_output)) + Config.BuildIdLinkOutput = + InputArgs.getLastArgValue(OBJCOPY_build_id_link_output); + Config.SplitDWO = InputArgs.getLastArgValue(OBJCOPY_split_dwo); + Config.SymbolsPrefix = InputArgs.getLastArgValue(OBJCOPY_prefix_symbols); + + 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); + } + + for (auto Arg : InputArgs.filtered(OBJCOPY_rename_section)) { + SectionRename SR = parseRenameSectionValue(StringRef(Arg->getValue())); + if (!Config.SectionsToRename.try_emplace(SR.OriginalName, SR).second) + error("Multiple renames of section " + SR.OriginalName); + } + + for (auto Arg : InputArgs.filtered(OBJCOPY_remove_section)) + Config.ToRemove.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_section)) + Config.KeepSection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_only_section)) + Config.OnlySection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_add_section)) + Config.AddSection.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_dump_section)) + Config.DumpSection.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); + Config.DecompressDebugSections = + InputArgs.hasArg(OBJCOPY_decompress_debug_sections); + for (auto Arg : InputArgs.filtered(OBJCOPY_localize_symbol)) + Config.SymbolsToLocalize.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbol)) + Config.SymbolsToKeepGlobal.push_back(Arg->getValue()); + for (auto Arg : InputArgs.filtered(OBJCOPY_keep_global_symbols)) + addGlobalSymbolsFromFile(Config.SymbolsToKeepGlobal, 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()); + + Config.DeterministicArchives = InputArgs.hasFlag( + OBJCOPY_enable_deterministic_archives, + OBJCOPY_disable_deterministic_archives, /*default=*/true); + + Config.PreserveDates = InputArgs.hasArg(OBJCOPY_preserve_dates); + + if (Config.DecompressDebugSections && + Config.CompressionType != DebugCompressionType::None) { + error("Cannot specify --compress-debug-sections at the same time as " + "--decompress-debug-sections at the same time"); + } + + if (Config.DecompressDebugSections && !zlib::isAvailable()) + error("LLVM was not compiled with LLVM_ENABLE_ZLIB: cannot decompress."); + + DriverConfig DC; + DC.CopyConfigs.push_back(std::move(Config)); + return DC; +} + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. +DriverConfig 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 [options] file...", "strip tool"); + exit(1); + } + + if (InputArgs.hasArg(STRIP_help)) { + T.PrintHelp(outs(), "llvm-strip [options] file...", "strip tool"); + exit(0); + } + + if (InputArgs.hasArg(STRIP_version)) { + outs() << "llvm-strip, compatible with GNU strip\n"; + cl::PrintVersionMessage(); + exit(0); + } + + 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() > 1 && InputArgs.hasArg(STRIP_output)) + error("Multiple input files cannot be used in combination with -o"); + + CopyConfig Config; + 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); + Config.StripAllGNU = InputArgs.hasArg(STRIP_strip_all_gnu); + + if (!Config.StripDebug && !Config.StripUnneeded && !Config.DiscardAll && + !Config.StripAllGNU) + Config.StripAll = true; + + for (auto Arg : InputArgs.filtered(STRIP_keep_section)) + Config.KeepSection.push_back(Arg->getValue()); + + 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()); + + Config.DeterministicArchives = + InputArgs.hasFlag(STRIP_enable_deterministic_archives, + STRIP_disable_deterministic_archives, /*default=*/true); + + Config.PreserveDates = InputArgs.hasArg(STRIP_preserve_dates); + + DriverConfig DC; + if (Positional.size() == 1) { + Config.InputFilename = Positional[0]; + Config.OutputFilename = + InputArgs.getLastArgValue(STRIP_output, Positional[0]); + DC.CopyConfigs.push_back(std::move(Config)); + } else { + for (const char *Filename : Positional) { + Config.InputFilename = Filename; + Config.OutputFilename = Filename; + DC.CopyConfigs.push_back(Config); + } + } + + return DC; +} + +} // namespace objcopy +} // namespace llvm diff --git a/tools/llvm-objcopy/CopyConfig.h b/tools/llvm-objcopy/CopyConfig.h new file mode 100644 index 000000000000..71a2423ae1c8 --- /dev/null +++ b/tools/llvm-objcopy/CopyConfig.h @@ -0,0 +1,119 @@ +//===- CopyConfig.h -------------------------------------------------------===// +// +// 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_OBJCOPY_COPY_CONFIG_H +#define LLVM_TOOLS_LLVM_OBJCOPY_COPY_CONFIG_H + +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +// Necessary for llvm::DebugCompressionType::None +#include "llvm/Target/TargetOptions.h" +#include <string> +#include <vector> + +namespace llvm { +namespace objcopy { + +// This type keeps track of the machine info for various architectures. This +// lets us map architecture names to ELF types and the e_machine value of the +// ELF file. +struct MachineInfo { + uint16_t EMachine; + bool Is64Bit; + bool IsLittleEndian; +}; + +struct SectionRename { + StringRef OriginalName; + StringRef NewName; + Optional<uint64_t> NewFlags; +}; + +// Configuration for copying/stripping a single file. +struct CopyConfig { + // Main input/output options + StringRef InputFilename; + StringRef InputFormat; + StringRef OutputFilename; + StringRef OutputFormat; + + // Only applicable for --input-format=binary + MachineInfo BinaryArch; + // Only applicable when --output-format!=binary (e.g. elf64-x86-64). + Optional<MachineInfo> OutputArch; + + // Advanced options + StringRef AddGnuDebugLink; + StringRef BuildIdLinkDir; + Optional<StringRef> BuildIdLinkInput; + Optional<StringRef> BuildIdLinkOutput; + StringRef SplitDWO; + StringRef SymbolsPrefix; + + // Repeated options + std::vector<StringRef> AddSection; + std::vector<StringRef> DumpSection; + std::vector<StringRef> KeepSection; + std::vector<StringRef> OnlySection; + std::vector<StringRef> SymbolsToGlobalize; + std::vector<StringRef> SymbolsToKeep; + std::vector<StringRef> SymbolsToLocalize; + std::vector<StringRef> SymbolsToRemove; + std::vector<StringRef> SymbolsToWeaken; + std::vector<StringRef> ToRemove; + std::vector<std::string> SymbolsToKeepGlobal; + + // Map options + StringMap<SectionRename> SectionsToRename; + StringMap<StringRef> SymbolsToRename; + + // Boolean options + bool DeterministicArchives = true; + bool DiscardAll = false; + bool ExtractDWO = false; + bool KeepFileSymbols = false; + bool LocalizeHidden = false; + bool OnlyKeepDebug = false; + bool PreserveDates = false; + bool StripAll = false; + bool StripAllGNU = false; + bool StripDWO = false; + bool StripDebug = false; + bool StripNonAlloc = false; + bool StripSections = false; + bool StripUnneeded = false; + bool Weaken = false; + bool DecompressDebugSections = false; + DebugCompressionType CompressionType = DebugCompressionType::None; +}; + +// Configuration for the overall invocation of this tool. When invoked as +// objcopy, will always contain exactly one CopyConfig. When invoked as strip, +// will contain one or more CopyConfigs. +struct DriverConfig { + SmallVector<CopyConfig, 1> CopyConfigs; +}; + +// ParseObjcopyOptions returns the config and sets the input arguments. If a +// help flag is set then ParseObjcopyOptions will print the help messege and +// exit. +DriverConfig parseObjcopyOptions(ArrayRef<const char *> ArgsArr); + +// ParseStripOptions returns the config and sets the input arguments. If a +// help flag is set then ParseStripOptions will print the help messege and +// exit. +DriverConfig parseStripOptions(ArrayRef<const char *> ArgsArr); + +} // namespace objcopy +} // namespace llvm + +#endif diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.cpp b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp new file mode 100644 index 000000000000..f5ab8e708267 --- /dev/null +++ b/tools/llvm-objcopy/ELF/ELFObjcopy.cpp @@ -0,0 +1,584 @@ +//===- ELFObjcopy.cpp -----------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ELFObjcopy.h" +#include "Buffer.h" +#include "CopyConfig.h" +#include "Object.h" +#include "llvm-objcopy.h" + +#include "llvm/ADT/BitmaskEnum.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" +#include "llvm/Object/Binary.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/ELFTypes.h" +#include "llvm/Object/Error.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compression.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/ErrorOr.h" +#include "llvm/Support/Memory.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstdlib> +#include <functional> +#include <iterator> +#include <memory> +#include <string> +#include <system_error> +#include <utility> + +namespace llvm { +namespace objcopy { +namespace elf { + +using namespace object; +using namespace ELF; +using SectionPred = std::function<bool(const SectionBase &Sec)>; + +static bool isDebugSection(const SectionBase &Sec) { + return StringRef(Sec.Name).startswith(".debug") || + StringRef(Sec.Name).startswith(".zdebug") || Sec.Name == ".gdb_index"; +} + +static bool isDWOSection(const SectionBase &Sec) { + return StringRef(Sec.Name).endswith(".dwo"); +} + +static bool onlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { + // We can't remove the section header string table. + 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); +} + +static ElfType getOutputElfType(const Binary &Bin) { + // Infer output ELF type from the input ELF object + 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"); +} + +static ElfType getOutputElfType(const MachineInfo &MI) { + // Infer output ELF type from the binary arch specified + if (MI.Is64Bit) + return MI.IsLittleEndian ? ELFT_ELF64LE : ELFT_ELF64BE; + else + return MI.IsLittleEndian ? ELFT_ELF32LE : ELFT_ELF32BE; +} + +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> +static Expected<ArrayRef<uint8_t>> +findBuildID(const object::ELFFile<ELFT> &In) { + for (const auto &Phdr : unwrapOrError(In.program_headers())) { + if (Phdr.p_type != PT_NOTE) + continue; + Error Err = Error::success(); + for (const auto &Note : In.notes(Phdr, Err)) + if (Note.getType() == NT_GNU_BUILD_ID && Note.getName() == ELF_NOTE_GNU) + return Note.getDesc(); + if (Err) + return std::move(Err); + } + return createStringError(llvm::errc::invalid_argument, + "Could not find build ID."); +} + +static Expected<ArrayRef<uint8_t>> +findBuildID(const object::ELFObjectFileBase &In) { + if (auto *O = dyn_cast<ELFObjectFile<ELF32LE>>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF64LE>>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF32BE>>(&In)) + return findBuildID(*O->getELFFile()); + else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(&In)) + return findBuildID(*O->getELFFile()); + + llvm_unreachable("Bad file format"); +} + +static void linkToBuildIdDir(const CopyConfig &Config, StringRef ToLink, + StringRef Suffix, ArrayRef<uint8_t> BuildIdBytes) { + SmallString<128> Path = Config.BuildIdLinkDir; + sys::path::append(Path, llvm::toHex(BuildIdBytes[0], /*LowerCase*/ true)); + if (auto EC = sys::fs::create_directories(Path)) + error("cannot create build ID link directory " + Path + ": " + + EC.message()); + + sys::path::append(Path, + llvm::toHex(BuildIdBytes.slice(1), /*LowerCase*/ true)); + Path += Suffix; + if (auto EC = sys::fs::create_hard_link(ToLink, Path)) { + // Hard linking failed, try to remove the file first if it exists. + if (sys::fs::exists(Path)) + sys::fs::remove(Path); + EC = sys::fs::create_hard_link(ToLink, Path); + if (EC) + error("cannot link " + ToLink + " to " + Path + ": " + EC.message()); + } +} + +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); }); + if (Config.OutputArch) + DWOFile->Machine = Config.OutputArch.getValue().EMachine; + FileBuffer FB(File); + auto Writer = createWriter(Config, *DWOFile, FB, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +static Error dumpSectionToFile(StringRef SecName, StringRef Filename, + Object &Obj) { + for (auto &Sec : Obj.sections()) { + if (Sec.Name == SecName) { + if (Sec.OriginalData.empty()) + return make_error<StringError>("Can't dump section \"" + SecName + + "\": it has no contents", + object_error::parse_failed); + Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr = + FileOutputBuffer::create(Filename, Sec.OriginalData.size()); + if (!BufferOrErr) + return BufferOrErr.takeError(); + std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr); + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), + Buf->getBufferStart()); + if (Error E = Buf->commit()) + return E; + return Error::success(); + } + } + return make_error<StringError>("Section not found", + object_error::parse_failed); +} + +static bool isCompressed(const SectionBase &Section) { + const char *Magic = "ZLIB"; + return StringRef(Section.Name).startswith(".zdebug") || + (Section.OriginalData.size() > strlen(Magic) && + !strncmp(reinterpret_cast<const char *>(Section.OriginalData.data()), + Magic, strlen(Magic))) || + (Section.Flags & ELF::SHF_COMPRESSED); +} + +static bool isCompressable(const SectionBase &Section) { + return !isCompressed(Section) && isDebugSection(Section) && + Section.Name != ".gdb_index"; +} + +static void replaceDebugSections( + const CopyConfig &Config, Object &Obj, SectionPred &RemovePred, + function_ref<bool(const SectionBase &)> shouldReplace, + function_ref<SectionBase *(const SectionBase *)> addSection) { + SmallVector<SectionBase *, 13> ToReplace; + SmallVector<RelocationSection *, 13> RelocationSections; + for (auto &Sec : Obj.sections()) { + if (RelocationSection *R = dyn_cast<RelocationSection>(&Sec)) { + if (shouldReplace(*R->getSection())) + RelocationSections.push_back(R); + continue; + } + + if (shouldReplace(Sec)) + ToReplace.push_back(&Sec); + } + + for (SectionBase *S : ToReplace) { + SectionBase *NewSection = addSection(S); + + for (RelocationSection *RS : RelocationSections) { + if (RS->getSection() == S) + RS->setSection(NewSection); + } + } + + RemovePred = [shouldReplace, RemovePred](const SectionBase &Sec) { + return shouldReplace(Sec) || RemovePred(Sec); + }; +} + +// This function handles the high level operations of GNU objcopy including +// handling command line options. It's important to outline certain properties +// we expect to hold of the command line operations. Any operation that "keeps" +// should keep regardless of a remove. Additionally any removal should respect +// 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. +static void handleArgs(const CopyConfig &Config, Object &Obj, + const Reader &Reader, ElfType OutputElfType) { + + if (!Config.SplitDWO.empty()) { + splitDWOToFile(Config, Reader, Config.SplitDWO, OutputElfType); + } + if (Config.OutputArch) + Obj.Machine = Config.OutputArch.getValue().EMachine; + + // TODO: update or remove symbols only if there is an option that affects + // them. + if (Obj.SymbolTable) { + Obj.SymbolTable->updateSymbols([&](Symbol &Sym) { + if (!Sym.isCommon() && + ((Config.LocalizeHidden && + (Sym.Visibility == STV_HIDDEN || Sym.Visibility == STV_INTERNAL)) || + is_contained(Config.SymbolsToLocalize, Sym.Name))) + Sym.Binding = STB_LOCAL; + + // Note: these two globalize flags have very similar names but different + // meanings: + // + // --globalize-symbol: promote a symbol to global + // --keep-global-symbol: all symbols except for these should be made local + // + // If --globalize-symbol is specified for a given symbol, it will be + // global in the output file even if it is not included via + // --keep-global-symbol. Because of that, make sure to check + // --globalize-symbol second. + if (!Config.SymbolsToKeepGlobal.empty() && + !is_contained(Config.SymbolsToKeepGlobal, Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_LOCAL; + + if (is_contained(Config.SymbolsToGlobalize, Sym.Name) && + Sym.getShndx() != SHN_UNDEF) + Sym.Binding = STB_GLOBAL; + + if (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(); + + if (!Config.SymbolsPrefix.empty() && Sym.Type != STT_SECTION) + Sym.Name = (Config.SymbolsPrefix + Sym.Name).str(); + }); + + // 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 which are not. + if (Config.StripUnneeded) { + for (auto &Section : Obj.sections()) + Section.markSymbols(); + } + + Obj.removeSymbols([&](const Symbol &Sym) { + if (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 (Config.StripAll || Config.StripAllGNU) + return true; + + if (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 (!Config.ToRemove.empty()) { + RemovePred = [&Config](const SectionBase &Sec) { + return is_contained(Config.ToRemove, Sec.Name); + }; + } + + if (Config.StripDWO || !Config.SplitDWO.empty()) + RemovePred = [RemovePred](const SectionBase &Sec) { + return isDWOSection(Sec) || RemovePred(Sec); + }; + + if (Config.ExtractDWO) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + return onlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); + }; + + 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.SectionNames) + return false; + switch (Sec.Type) { + case SHT_SYMTAB: + case SHT_REL: + case SHT_RELA: + case SHT_STRTAB: + return true; + } + return isDebugSection(Sec); + }; + + if (Config.StripSections) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; + }; + } + + if (Config.StripDebug) { + RemovePred = [RemovePred](const SectionBase &Sec) { + return RemovePred(Sec) || isDebugSection(Sec); + }; + } + + if (Config.StripNonAlloc) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + if (Config.StripAll) + RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { + if (RemovePred(Sec)) + return true; + if (&Sec == Obj.SectionNames) + return false; + if (StringRef(Sec.Name).startswith(".gnu.warning")) + return false; + return (Sec.Flags & SHF_ALLOC) == 0; + }; + + // Explicit copies: + if (!Config.OnlySection.empty()) { + RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (is_contained(Config.OnlySection, Sec.Name)) + return false; + + // Allow all implicit removes. + if (RemovePred(Sec)) + return true; + + // Keep special sections. + if (Obj.SectionNames == &Sec) + return false; + if (Obj.SymbolTable == &Sec || + (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) + return false; + + // Remove everything else. + return true; + }; + } + + if (!Config.KeepSection.empty()) { + RemovePred = [&Config, RemovePred](const SectionBase &Sec) { + // Explicitly keep these sections regardless of previous removes. + if (is_contained(Config.KeepSection, Sec.Name)) + return false; + // Otherwise defer to RemovePred. + return RemovePred(Sec); + }; + } + + // 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 (Config.CompressionType != DebugCompressionType::None) + replaceDebugSections(Config, Obj, RemovePred, isCompressable, + [&Config, &Obj](const SectionBase *S) { + return &Obj.addSection<CompressedSection>( + *S, Config.CompressionType); + }); + else if (Config.DecompressDebugSections) + replaceDebugSections( + Config, Obj, RemovePred, + [](const SectionBase &S) { return isa<CompressedSection>(&S); }, + [&Obj](const SectionBase *S) { + auto CS = cast<CompressedSection>(S); + return &Obj.addSection<DecompressedSection>(*CS); + }); + + 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()) { + const SectionRename &SR = Iter->second; + Sec.Name = SR.NewName; + if (SR.NewFlags.hasValue()) { + // Preserve some flags which should not be dropped when setting flags. + // Also, preserve anything OS/processor dependant. + const uint64_t PreserveMask = ELF::SHF_COMPRESSED | ELF::SHF_EXCLUDE | + ELF::SHF_GROUP | ELF::SHF_LINK_ORDER | + ELF::SHF_MASKOS | ELF::SHF_MASKPROC | + ELF::SHF_TLS | ELF::SHF_INFO_LINK; + Sec.Flags = (Sec.Flags & PreserveMask) | + (SR.NewFlags.getValue() & ~PreserveMask); + } + } + } + } + + if (!Config.AddSection.empty()) { + for (const auto &Flag : Config.AddSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = + MemoryBuffer::getFile(File); + if (!BufOrErr) + reportError(File, BufOrErr.getError()); + std::unique_ptr<MemoryBuffer> Buf = std::move(*BufOrErr); + ArrayRef<uint8_t> Data( + reinterpret_cast<const uint8_t *>(Buf->getBufferStart()), + Buf->getBufferSize()); + OwnedDataSection &NewSection = + Obj.addSection<OwnedDataSection>(SecName, Data); + if (SecName.startswith(".note") && SecName != ".note.GNU-stack") + NewSection.Type = SHT_NOTE; + } + } + + if (!Config.DumpSection.empty()) { + for (const auto &Flag : Config.DumpSection) { + std::pair<StringRef, StringRef> SecPair = Flag.split("="); + StringRef SecName = SecPair.first; + StringRef File = SecPair.second; + if (Error E = dumpSectionToFile(SecName, File, Obj)) + reportError(Config.InputFilename, std::move(E)); + } + } + + if (!Config.AddGnuDebugLink.empty()) + Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); +} + +void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out) { + BinaryReader Reader(Config.BinaryArch, &In); + std::unique_ptr<Object> Obj = Reader.create(); + + // Prefer OutputArch (-O<format>) if set, otherwise fallback to BinaryArch + // (-B<arch>). + const ElfType OutputElfType = getOutputElfType( + Config.OutputArch ? Config.OutputArch.getValue() : Config.BinaryArch); + handleArgs(Config, *Obj, Reader, OutputElfType); + std::unique_ptr<Writer> Writer = + createWriter(Config, *Obj, Out, OutputElfType); + Writer->finalize(); + Writer->write(); +} + +void executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out) { + ELFReader Reader(&In); + std::unique_ptr<Object> Obj = Reader.create(); + // Prefer OutputArch (-O<format>) if set, otherwise infer it from the input. + const ElfType OutputElfType = + Config.OutputArch ? getOutputElfType(Config.OutputArch.getValue()) + : getOutputElfType(In); + ArrayRef<uint8_t> BuildIdBytes; + + if (!Config.BuildIdLinkDir.empty()) { + BuildIdBytes = unwrapOrError(findBuildID(In)); + if (BuildIdBytes.size() < 2) + error("build ID in file '" + Config.InputFilename + + "' is smaller than two bytes"); + } + + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkInput) { + linkToBuildIdDir(Config, Config.InputFilename, + Config.BuildIdLinkInput.getValue(), BuildIdBytes); + } + handleArgs(Config, *Obj, Reader, OutputElfType); + std::unique_ptr<Writer> Writer = + createWriter(Config, *Obj, Out, OutputElfType); + Writer->finalize(); + Writer->write(); + if (!Config.BuildIdLinkDir.empty() && Config.BuildIdLinkOutput) { + linkToBuildIdDir(Config, Config.OutputFilename, + Config.BuildIdLinkOutput.getValue(), BuildIdBytes); + } +} + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm diff --git a/tools/llvm-objcopy/ELF/ELFObjcopy.h b/tools/llvm-objcopy/ELF/ELFObjcopy.h new file mode 100644 index 000000000000..43f41c00ce5b --- /dev/null +++ b/tools/llvm-objcopy/ELF/ELFObjcopy.h @@ -0,0 +1,34 @@ +//===- ELFObjcopy.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_OBJCOPY_ELFOBJCOPY_H +#define LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H + +namespace llvm { +class MemoryBuffer; + +namespace object { +class ELFObjectFileBase; +} // end namespace object + +namespace objcopy { +struct CopyConfig; +class Buffer; + +namespace elf { +void executeObjcopyOnRawBinary(const CopyConfig &Config, MemoryBuffer &In, + Buffer &Out); +void executeObjcopyOnBinary(const CopyConfig &Config, + object::ELFObjectFileBase &In, Buffer &Out); + +} // end namespace elf +} // end namespace objcopy +} // end namespace llvm + +#endif // LLVM_TOOLS_OBJCOPY_ELFOBJCOPY_H diff --git a/tools/llvm-objcopy/Object.cpp b/tools/llvm-objcopy/ELF/Object.cpp index 7e88f5263a39..3d3e029c09eb 100644 --- a/tools/llvm-objcopy/Object.cpp +++ b/tools/llvm-objcopy/ELF/Object.cpp @@ -15,7 +15,9 @@ #include "llvm/ADT/Twine.h" #include "llvm/ADT/iterator_range.h" #include "llvm/BinaryFormat/ELF.h" +#include "llvm/MC/MCTargetOptions.h" #include "llvm/Object/ELFObjectFile.h" +#include "llvm/Support/Compression.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/Path.h" @@ -26,45 +28,14 @@ #include <utility> #include <vector> -using namespace llvm; -using namespace llvm::objcopy; +namespace llvm { +namespace objcopy { +namespace elf { + using namespace object; using namespace ELF; -Buffer::~Buffer() {} - -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 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); @@ -87,7 +58,7 @@ 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); + Elf_Shdr &Shdr = *reinterpret_cast<Elf_Shdr *>(B); Shdr.sh_name = Sec.NameIndex; Shdr.sh_type = Sec.Type; Shdr.sh_flags = Sec.Flags; @@ -100,7 +71,46 @@ template <class ELFT> void ELFWriter<ELFT>::writeShdr(const SectionBase &Sec) { Shdr.sh_entsize = Sec.EntrySize; } -SectionVisitor::~SectionVisitor() {} +template <class ELFT> void ELFSectionSizer<ELFT>::visit(Section &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(OwnedDataSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(StringTableSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(DynamicRelocationSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(SymbolTableSection &Sec) { + Sec.EntrySize = sizeof(Elf_Sym); + Sec.Size = Sec.Symbols.size() * Sec.EntrySize; + // Align to the largest field in Elf_Sym. + Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); +} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(RelocationSection &Sec) { + Sec.EntrySize = Sec.Type == SHT_REL ? sizeof(Elf_Rel) : sizeof(Elf_Rela); + Sec.Size = Sec.Relocations.size() * Sec.EntrySize; + // Align to the largest field in Elf_Rel(a). + Sec.Align = ELFT::Is64Bits ? sizeof(Elf_Xword) : sizeof(Elf_Word); +} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(GnuDebugLinkSection &Sec) {} + +template <class ELFT> void ELFSectionSizer<ELFT>::visit(GroupSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(SectionIndexSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(CompressedSection &Sec) {} + +template <class ELFT> +void ELFSectionSizer<ELFT>::visit(DecompressedSection &Sec) {} void BinarySectionWriter::visit(const SectionIndexSection &Sec) { error("Cannot write symbol section index table '" + Sec.Name + "' "); @@ -126,20 +136,169 @@ void SectionWriter::visit(const Section &Sec) { if (Sec.Type == SHT_NOBITS) return; uint8_t *Buf = Out.getBufferStart() + Sec.Offset; - std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), Buf); + llvm::copy(Sec.Contents, Buf); } void Section::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void Section::accept(MutableSectionVisitor &Visitor) { 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); + llvm::copy(Sec.Data, Buf); +} + +static const std::vector<uint8_t> ZlibGnuMagic = {'Z', 'L', 'I', 'B'}; + +static bool isDataGnuCompressed(ArrayRef<uint8_t> Data) { + return Data.size() > ZlibGnuMagic.size() && + std::equal(ZlibGnuMagic.begin(), ZlibGnuMagic.end(), Data.data()); +} + +template <class ELFT> +static std::tuple<uint64_t, uint64_t> +getDecompressedSizeAndAlignment(ArrayRef<uint8_t> Data) { + const bool IsGnuDebug = isDataGnuCompressed(Data); + const uint64_t DecompressedSize = + IsGnuDebug + ? support::endian::read64be(reinterpret_cast<const uint64_t *>( + Data.data() + ZlibGnuMagic.size())) + : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data())->ch_size; + const uint64_t DecompressedAlign = + IsGnuDebug ? 1 + : reinterpret_cast<const Elf_Chdr_Impl<ELFT> *>(Data.data()) + ->ch_addralign; + + return std::make_tuple(DecompressedSize, DecompressedAlign); +} + +template <class ELFT> +void ELFSectionWriter<ELFT>::visit(const DecompressedSection &Sec) { + uint8_t *Buf = Out.getBufferStart() + Sec.Offset; + + if (!zlib::isAvailable()) { + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); + return; + } + + const size_t DataOffset = isDataGnuCompressed(Sec.OriginalData) + ? (ZlibGnuMagic.size() + sizeof(Sec.Size)) + : sizeof(Elf_Chdr_Impl<ELFT>); + + StringRef CompressedContent( + reinterpret_cast<const char *>(Sec.OriginalData.data()) + DataOffset, + Sec.OriginalData.size() - DataOffset); + + SmallVector<char, 128> DecompressedContent; + if (Error E = zlib::uncompress(CompressedContent, DecompressedContent, + static_cast<size_t>(Sec.Size))) + reportError(Sec.Name, std::move(E)); + + std::copy(DecompressedContent.begin(), DecompressedContent.end(), Buf); +} + +void BinarySectionWriter::visit(const DecompressedSection &Sec) { + error("Cannot write compressed section '" + Sec.Name + "' "); +} + +void DecompressedSection::accept(SectionVisitor &Visitor) const { + Visitor.visit(*this); +} + +void DecompressedSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); } void OwnedDataSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void OwnedDataSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + +void BinarySectionWriter::visit(const CompressedSection &Sec) { + error("Cannot write compressed section '" + Sec.Name + "' "); +} + +template <class ELFT> +void ELFSectionWriter<ELFT>::visit(const CompressedSection &Sec) { + uint8_t *Buf = Out.getBufferStart(); + Buf += Sec.Offset; + + if (Sec.CompressionType == DebugCompressionType::None) { + std::copy(Sec.OriginalData.begin(), Sec.OriginalData.end(), Buf); + return; + } + + if (Sec.CompressionType == DebugCompressionType::GNU) { + const char *Magic = "ZLIB"; + memcpy(Buf, Magic, strlen(Magic)); + Buf += strlen(Magic); + const uint64_t DecompressedSize = + support::endian::read64be(&Sec.DecompressedSize); + memcpy(Buf, &DecompressedSize, sizeof(DecompressedSize)); + Buf += sizeof(DecompressedSize); + } else { + Elf_Chdr_Impl<ELFT> Chdr; + Chdr.ch_type = ELF::ELFCOMPRESS_ZLIB; + Chdr.ch_size = Sec.DecompressedSize; + Chdr.ch_addralign = Sec.DecompressedAlign; + memcpy(Buf, &Chdr, sizeof(Chdr)); + Buf += sizeof(Chdr); + } + + std::copy(Sec.CompressedData.begin(), Sec.CompressedData.end(), Buf); +} + +CompressedSection::CompressedSection(const SectionBase &Sec, + DebugCompressionType CompressionType) + : SectionBase(Sec), CompressionType(CompressionType), + DecompressedSize(Sec.OriginalData.size()), DecompressedAlign(Sec.Align) { + + if (!zlib::isAvailable()) { + CompressionType = DebugCompressionType::None; + return; + } + + if (Error E = zlib::compress( + StringRef(reinterpret_cast<const char *>(OriginalData.data()), + OriginalData.size()), + CompressedData)) + reportError(Name, std::move(E)); + + size_t ChdrSize; + if (CompressionType == DebugCompressionType::GNU) { + Name = ".z" + Sec.Name.substr(1); + ChdrSize = sizeof("ZLIB") - 1 + sizeof(uint64_t); + } else { + Flags |= ELF::SHF_COMPRESSED; + ChdrSize = + std::max(std::max(sizeof(object::Elf_Chdr_Impl<object::ELF64LE>), + sizeof(object::Elf_Chdr_Impl<object::ELF64BE>)), + std::max(sizeof(object::Elf_Chdr_Impl<object::ELF32LE>), + sizeof(object::Elf_Chdr_Impl<object::ELF32BE>))); + } + Size = ChdrSize + CompressedData.size(); + Align = 8; +} + +CompressedSection::CompressedSection(ArrayRef<uint8_t> CompressedData, + uint64_t DecompressedSize, + uint64_t DecompressedAlign) + : CompressionType(DebugCompressionType::None), + DecompressedSize(DecompressedSize), DecompressedAlign(DecompressedAlign) { + OriginalData = CompressedData; +} + +void CompressedSection::accept(SectionVisitor &Visitor) const { + Visitor.visit(*this); +} + +void CompressedSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + void StringTableSection::addString(StringRef Name) { StrTabBuilder.add(Name); Size = StrTabBuilder.getSize(); @@ -159,11 +318,15 @@ void StringTableSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void StringTableSection::accept(MutableSectionVisitor &Visitor) { + 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); + auto *IndexesBuffer = reinterpret_cast<Elf_Word *>(Buf); + llvm::copy(Sec.Indexes, IndexesBuffer); } void SectionIndexSection::initialize(SectionTableRef SecTable) { @@ -182,6 +345,10 @@ void SectionIndexSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void SectionIndexSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + static bool isValidReservedSectionIndex(uint16_t Index, uint16_t Machine) { switch (Index) { case SHN_ABS: @@ -226,18 +393,20 @@ uint16_t Symbol::getShndx() const { llvm_unreachable("Symbol with invalid ShndxType encountered"); } +bool Symbol::isCommon() const { return getShndx() == SHN_COMMON; } + 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, +void SymbolTableSection::addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, uint16_t Shndx, - uint64_t Sz) { + uint64_t Size) { Symbol Sym; - Sym.Name = Name; + Sym.Name = Name.str(); Sym.Binding = Bind; Sym.Type = Type; Sym.DefinedIn = DefinedIn; @@ -251,7 +420,7 @@ void SymbolTableSection::addSymbol(StringRef Name, uint8_t Bind, uint8_t Type, } Sym.Value = Value; Sym.Visibility = Visibility; - Sym.Size = Sz; + Sym.Size = Size; Sym.Index = Symbols.size(); Symbols.emplace_back(llvm::make_unique<Symbol>(Sym)); Size += this->EntrySize; @@ -344,7 +513,7 @@ template <class ELFT> void ELFSectionWriter<ELFT>::visit(const SymbolTableSection &Sec) { uint8_t *Buf = Out.getBufferStart(); Buf += Sec.Offset; - typename ELFT::Sym *Sym = reinterpret_cast<typename ELFT::Sym *>(Buf); + Elf_Sym *Sym = reinterpret_cast<Elf_Sym *>(Buf); // Loop though symbols setting each entry of the symbol table. for (auto &Symbol : Sec.Symbols) { Sym->st_name = Symbol->NameIndex; @@ -362,6 +531,10 @@ void SymbolTableSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void SymbolTableSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + template <class SymTabType> void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences( const SectionBase *Sec) { @@ -377,11 +550,13 @@ void RelocSectionWithSymtabBase<SymTabType>::removeSectionReferences( template <class SymTabType> void RelocSectionWithSymtabBase<SymTabType>::initialize( SectionTableRef SecTable) { - setSymTab(SecTable.getSectionOfType<SymTabType>( - Link, - "Link field value " + Twine(Link) + " in section " + Name + " is invalid", - "Link field value " + Twine(Link) + " in section " + Name + - " is not a symbol table")); + if (Link != SHN_UNDEF) + setSymTab(SecTable.getSectionOfType<SymTabType>( + Link, + "Link field value " + Twine(Link) + " in section " + Name + + " is invalid", + "Link field value " + Twine(Link) + " in section " + Name + + " is not a symbol table")); if (Info != SHN_UNDEF) setSection(SecTable.getSection(Info, "Info field value " + Twine(Info) + @@ -393,7 +568,8 @@ void RelocSectionWithSymtabBase<SymTabType>::initialize( template <class SymTabType> void RelocSectionWithSymtabBase<SymTabType>::finalize() { - this->Link = Symbols->Index; + this->Link = Symbols ? Symbols->Index : 0; + if (SecToApplyRel != nullptr) this->Info = SecToApplyRel->Index; } @@ -429,11 +605,15 @@ void RelocationSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void RelocationSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + 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 + + error("not stripping symbol '" + Reloc.RelocSymbol->Name + "' because it is named in a relocation"); } @@ -443,7 +623,7 @@ void RelocationSection::markSymbols() { } void SectionWriter::visit(const DynamicRelocationSection &Sec) { - std::copy(std::begin(Sec.Contents), std::end(Sec.Contents), + llvm::copy(Sec.Contents, Out.getBufferStart() + Sec.Offset); } @@ -451,6 +631,10 @@ void DynamicRelocationSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void DynamicRelocationSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + void Section::removeSectionReferences(const SectionBase *Sec) { if (LinkSection == Sec) { error("Section " + LinkSection->Name + @@ -506,12 +690,12 @@ void GnuDebugLinkSection::init(StringRef File, StringRef Data) { // 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())); + 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(); + CRC32 = ~CRC.getCRC(); } GnuDebugLinkSection::GnuDebugLinkSection(StringRef File) : FileName(File) { @@ -530,13 +714,17 @@ void ELFSectionWriter<ELFT>::visit(const GnuDebugLinkSection &Sec) { 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); + llvm::copy(Sec.FileName, File); } void GnuDebugLinkSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void GnuDebugLinkSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + template <class ELFT> void ELFSectionWriter<ELFT>::visit(const GroupSection &Sec) { ELF::Elf32_Word *Buf = @@ -550,6 +738,10 @@ void GroupSection::accept(SectionVisitor &Visitor) const { Visitor.visit(*this); } +void GroupSection::accept(MutableSectionVisitor &Visitor) { + Visitor.visit(*this); +} + // Returns true IFF a section is wholly inside the range of a segment static bool sectionWithinSegment(const SectionBase &Section, const Segment &Segment) { @@ -589,6 +781,79 @@ static bool compareSegmentsByPAddr(const Segment *A, const Segment *B) { return A->Index < B->Index; } +void BinaryELFBuilder::initFileHeader() { + Obj->Flags = 0x0; + Obj->Type = ET_REL; + Obj->OSABI = ELFOSABI_NONE; + Obj->ABIVersion = 0; + Obj->Entry = 0x0; + Obj->Machine = EMachine; + Obj->Version = 1; +} + +void BinaryELFBuilder::initHeaderSegment() { Obj->ElfHdrSegment.Index = 0; } + +StringTableSection *BinaryELFBuilder::addStrTab() { + auto &StrTab = Obj->addSection<StringTableSection>(); + StrTab.Name = ".strtab"; + + Obj->SectionNames = &StrTab; + return &StrTab; +} + +SymbolTableSection *BinaryELFBuilder::addSymTab(StringTableSection *StrTab) { + auto &SymTab = Obj->addSection<SymbolTableSection>(); + + SymTab.Name = ".symtab"; + SymTab.Link = StrTab->Index; + + // The symbol table always needs a null symbol + SymTab.addSymbol("", 0, 0, nullptr, 0, 0, 0, 0); + + Obj->SymbolTable = &SymTab; + return &SymTab; +} + +void BinaryELFBuilder::addData(SymbolTableSection *SymTab) { + auto Data = ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(MemBuf->getBufferStart()), + MemBuf->getBufferSize()); + auto &DataSection = Obj->addSection<Section>(Data); + DataSection.Name = ".data"; + DataSection.Type = ELF::SHT_PROGBITS; + DataSection.Size = Data.size(); + DataSection.Flags = ELF::SHF_ALLOC | ELF::SHF_WRITE; + + std::string SanitizedFilename = MemBuf->getBufferIdentifier().str(); + std::replace_if(std::begin(SanitizedFilename), std::end(SanitizedFilename), + [](char C) { return !isalnum(C); }, '_'); + Twine Prefix = Twine("_binary_") + SanitizedFilename; + + SymTab->addSymbol(Prefix + "_start", STB_GLOBAL, STT_NOTYPE, &DataSection, + /*Value=*/0, STV_DEFAULT, 0, 0); + SymTab->addSymbol(Prefix + "_end", STB_GLOBAL, STT_NOTYPE, &DataSection, + /*Value=*/DataSection.Size, STV_DEFAULT, 0, 0); + SymTab->addSymbol(Prefix + "_size", STB_GLOBAL, STT_NOTYPE, nullptr, + /*Value=*/DataSection.Size, STV_DEFAULT, SHN_ABS, 0); +} + +void BinaryELFBuilder::initSections() { + for (auto &Section : Obj->sections()) { + Section.initialize(Obj->sections()); + } +} + +std::unique_ptr<Object> BinaryELFBuilder::build() { + initFileHeader(); + initHeaderSegment(); + StringTableSection *StrTab = addStrTab(); + SymbolTableSection *SymTab = addSymTab(StrTab); + initSections(); + addData(SymTab); + + return std::move(Obj); +} + 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 @@ -633,15 +898,6 @@ template <class ELFT> void ELFBuilder<ELFT>::readProgramHeaders() { } 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(); @@ -725,8 +981,7 @@ void ELFBuilder<ELFT>::initSymbolTable(SymbolTableSection *SymTab) { Elf_Word Index = ShndxData[&Sym - Symbols.begin()]; DefSection = Obj.sections().getSection( Index, - "Symbol '" + Name + "' has invalid section index " + - Twine(Index)); + "Symbol '" + Name + "' has invalid section index " + Twine(Index)); } else if (Sym.st_shndx >= SHN_LORESERVE) { if (!isValidReservedSectionIndex(Sym.st_shndx, Obj.Machine)) { error( @@ -828,10 +1083,20 @@ SectionBase &ELFBuilder<ELFT>::makeSection(const Elf_Shdr &Shdr) { } case SHT_NOBITS: return Obj.addSection<Section>(Data); - default: + default: { Data = unwrapOrError(ElfFile.getSectionContents(&Shdr)); + + if (isDataGnuCompressed(Data) || (Shdr.sh_flags & ELF::SHF_COMPRESSED)) { + uint64_t DecompressedSize, DecompressedAlign; + std::tie(DecompressedSize, DecompressedAlign) = + getDecompressedSizeAndAlignment<ELFT>(Data); + return Obj.addSection<CompressedSection>(Data, DecompressedSize, + DecompressedAlign); + } + return Obj.addSection<Section>(Data); } + } } template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { @@ -854,6 +1119,9 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { Sec.Align = Shdr.sh_addralign; Sec.EntrySize = Shdr.sh_entsize; Sec.Index = Index++; + Sec.OriginalData = + ArrayRef<uint8_t>(ElfFile.base() + Shdr.sh_offset, + (Shdr.sh_type == SHT_NOBITS) ? 0 : Shdr.sh_size); } // If a section index table exists we'll need to initialize it before we @@ -894,7 +1162,8 @@ template <class ELFT> void ELFBuilder<ELFT>::readSectionHeaders() { template <class ELFT> void ELFBuilder<ELFT>::build() { const auto &Ehdr = *ElfFile.getHeader(); - std::copy(Ehdr.e_ident, Ehdr.e_ident + 16, Obj.Ident); + Obj.OSABI = Ehdr.e_ident[EI_OSABI]; + Obj.ABIVersion = Ehdr.e_ident[EI_ABIVERSION]; Obj.Type = Ehdr.e_type; Obj.Machine = Ehdr.e_machine; Obj.Version = Ehdr.e_version; @@ -926,34 +1195,26 @@ Writer::~Writer() {} Reader::~Reader() {} -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"); +std::unique_ptr<Object> BinaryReader::create() const { + return BinaryELFBuilder(MInfo.EMachine, MemBuf).build(); } 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); + 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); + } 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); + } 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); + } else if (auto *O = dyn_cast<ELFObjectFile<ELF64BE>>(Bin)) { + ELFBuilder<ELF64BE> Builder(*O, *Obj); Builder.build(); return Obj; } @@ -963,18 +1224,31 @@ std::unique_ptr<Object> ELFReader::create() const { 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); + std::fill(Ehdr.e_ident, Ehdr.e_ident + 16, 0); + Ehdr.e_ident[EI_MAG0] = 0x7f; + Ehdr.e_ident[EI_MAG1] = 'E'; + Ehdr.e_ident[EI_MAG2] = 'L'; + Ehdr.e_ident[EI_MAG3] = 'F'; + Ehdr.e_ident[EI_CLASS] = ELFT::Is64Bits ? ELFCLASS64 : ELFCLASS32; + Ehdr.e_ident[EI_DATA] = + ELFT::TargetEndianness == support::big ? ELFDATA2MSB : ELFDATA2LSB; + Ehdr.e_ident[EI_VERSION] = EV_CURRENT; + Ehdr.e_ident[EI_OSABI] = Obj.OSABI; + Ehdr.e_ident[EI_ABIVERSION] = Obj.ABIVersion; + 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; + // We have to use the fully-qualified name llvm::size + // since some compilers complain on ambiguous resolution. + Ehdr.e_phnum = llvm::size(Obj.segments()); + Ehdr.e_phoff = (Ehdr.e_phnum != 0) ? Obj.ProgramHdrSegment.Offset : 0; + Ehdr.e_phentsize = (Ehdr.e_phnum != 0) ? sizeof(Elf_Phdr) : 0; Ehdr.e_flags = Obj.Flags; Ehdr.e_ehsize = sizeof(Elf_Ehdr); - Ehdr.e_phentsize = sizeof(Elf_Phdr); - Ehdr.e_phnum = size(Obj.segments()); - Ehdr.e_shentsize = sizeof(Elf_Shdr); - if (WriteSectionHeaders) { + if (WriteSectionHeaders && size(Obj.sections()) != 0) { + Ehdr.e_shentsize = sizeof(Elf_Shdr); Ehdr.e_shoff = Obj.SHOffset; // """ // If the number of sections is greater than or equal to @@ -998,6 +1272,7 @@ template <class ELFT> void ELFWriter<ELFT>::writeEhdr() { else Ehdr.e_shstrndx = Obj.SectionNames->Index; } else { + Ehdr.e_shentsize = 0; Ehdr.e_shoff = 0; Ehdr.e_shnum = 0; Ehdr.e_shstrndx = 0; @@ -1106,7 +1381,7 @@ 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) { +static void orderSegments(std::vector<Segment *> &Segments) { std::stable_sort(std::begin(Segments), std::end(Segments), compareSegmentsByOffset); } @@ -1148,7 +1423,7 @@ static uint64_t LayoutSegments(std::vector<Segment *> &Segments, // 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 Range> -static uint64_t LayoutSections(Range Sections, uint64_t Offset) { +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 @@ -1172,6 +1447,17 @@ static uint64_t LayoutSections(Range Sections, uint64_t Offset) { return Offset; } +template <class ELFT> void ELFWriter<ELFT>::initEhdrSegment() { + auto &ElfHdr = Obj.ElfHdrSegment; + 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; +} + 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 @@ -1181,17 +1467,17 @@ template <class ELFT> void ELFWriter<ELFT>::assignOffsets() { OrderedSegments.push_back(&Segment); OrderedSegments.push_back(&Obj.ElfHdrSegment); OrderedSegments.push_back(&Obj.ProgramHdrSegment); - OrderSegments(OrderedSegments); + orderSegments(OrderedSegments); // 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(Obj.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 (WriteSectionHeaders) - Offset = alignTo(Offset, sizeof(typename ELFT::Addr)); + Offset = alignTo(Offset, sizeof(Elf_Addr)); Obj.SHOffset = Offset; } @@ -1263,10 +1549,17 @@ template <class ELFT> void ELFWriter<ELFT>::finalize() { Obj.SectionNames->addString(Section.Name); } + initEhdrSegment(); + // Before we can prepare for layout the indexes need to be finalized. + // Also, the output arch may not be the same as the input arch, so fix up + // size-related fields before doing layout calculations. uint64_t Index = 0; - for (auto &Sec : Obj.sections()) + auto SecSizer = llvm::make_unique<ELFSectionSizer<ELFT>>(); + for (auto &Sec : Obj.sections()) { Sec.Index = Index++; + Sec.accept(*SecSizer); + } // 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 @@ -1324,10 +1617,10 @@ void BinaryWriter::finalize() { // 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; + if (all_of(OrderedSegments, + [](const Segment *Seg) { return Seg->PAddr == 0; })) + for (Segment *Seg : OrderedSegments) + Seg->PAddr = Seg->VAddr; std::stable_sort(std::begin(OrderedSegments), std::end(OrderedSegments), compareSegmentsByPAddr); @@ -1342,8 +1635,8 @@ void BinaryWriter::finalize() { 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. + // our layout algorithm to proceed as expected while not writing out the gap + // at the start. if (!OrderedSegments.empty()) { auto Seg = OrderedSegments[0]; auto Sec = Seg->firstSection(); @@ -1371,7 +1664,7 @@ void BinaryWriter::finalize() { continue; AllocatedSections.push_back(&Section); } - LayoutSections(make_pointee_range(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 @@ -1387,9 +1680,6 @@ void BinaryWriter::finalize() { SecWriter = llvm::make_unique<BinarySectionWriter>(Buf); } -namespace llvm { -namespace objcopy { - template class ELFBuilder<ELF64LE>; template class ELFBuilder<ELF64BE>; template class ELFBuilder<ELF32LE>; @@ -1399,5 +1689,7 @@ template class ELFWriter<ELF64LE>; template class ELFWriter<ELF64BE>; template class ELFWriter<ELF32LE>; template class ELFWriter<ELF32BE>; + +} // end namespace elf } // end namespace objcopy } // end namespace llvm diff --git a/tools/llvm-objcopy/Object.h b/tools/llvm-objcopy/ELF/Object.h index 76748d5fc641..e5730cd543ee 100644 --- a/tools/llvm-objcopy/Object.h +++ b/tools/llvm-objcopy/ELF/Object.h @@ -10,6 +10,8 @@ #ifndef LLVM_TOOLS_OBJCOPY_OBJECT_H #define LLVM_TOOLS_OBJCOPY_OBJECT_H +#include "Buffer.h" +#include "CopyConfig.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" @@ -26,9 +28,10 @@ #include <vector> namespace llvm { +enum class DebugCompressionType; namespace objcopy { +namespace elf { -class Buffer; class SectionBase; class Section; class OwnedDataSection; @@ -39,6 +42,8 @@ class DynamicRelocationSection; class GnuDebugLinkSection; class GroupSection; class SectionIndexSection; +class CompressedSection; +class DecompressedSection; class Segment; class Object; struct Symbol; @@ -66,7 +71,7 @@ enum ElfType { ELFT_ELF32LE, ELFT_ELF64LE, ELFT_ELF32BE, ELFT_ELF64BE }; class SectionVisitor { public: - virtual ~SectionVisitor(); + virtual ~SectionVisitor() = default; virtual void visit(const Section &Sec) = 0; virtual void visit(const OwnedDataSection &Sec) = 0; @@ -77,6 +82,25 @@ public: virtual void visit(const GnuDebugLinkSection &Sec) = 0; virtual void visit(const GroupSection &Sec) = 0; virtual void visit(const SectionIndexSection &Sec) = 0; + virtual void visit(const CompressedSection &Sec) = 0; + virtual void visit(const DecompressedSection &Sec) = 0; +}; + +class MutableSectionVisitor { +public: + virtual ~MutableSectionVisitor() = default; + + virtual void visit(Section &Sec) = 0; + virtual void visit(OwnedDataSection &Sec) = 0; + virtual void visit(StringTableSection &Sec) = 0; + virtual void visit(SymbolTableSection &Sec) = 0; + virtual void visit(RelocationSection &Sec) = 0; + virtual void visit(DynamicRelocationSection &Sec) = 0; + virtual void visit(GnuDebugLinkSection &Sec) = 0; + virtual void visit(GroupSection &Sec) = 0; + virtual void visit(SectionIndexSection &Sec) = 0; + virtual void visit(CompressedSection &Sec) = 0; + virtual void visit(DecompressedSection &Sec) = 0; }; class SectionWriter : public SectionVisitor { @@ -95,6 +119,8 @@ public: virtual void visit(const GnuDebugLinkSection &Sec) override = 0; virtual void visit(const GroupSection &Sec) override = 0; virtual void visit(const SectionIndexSection &Sec) override = 0; + virtual void visit(const CompressedSection &Sec) override = 0; + virtual void visit(const DecompressedSection &Sec) override = 0; explicit SectionWriter(Buffer &Buf) : Out(Buf) {} }; @@ -104,6 +130,7 @@ private: using Elf_Word = typename ELFT::Word; using Elf_Rel = typename ELFT::Rel; using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; public: virtual ~ELFSectionWriter() {} @@ -112,13 +139,38 @@ public: void visit(const GnuDebugLinkSection &Sec) override; void visit(const GroupSection &Sec) override; void visit(const SectionIndexSection &Sec) override; + void visit(const CompressedSection &Sec) override; + void visit(const DecompressedSection &Sec) override; explicit ELFSectionWriter(Buffer &Buf) : SectionWriter(Buf) {} }; +template <class ELFT> class ELFSectionSizer : public MutableSectionVisitor { +private: + using Elf_Rel = typename ELFT::Rel; + using Elf_Rela = typename ELFT::Rela; + using Elf_Sym = typename ELFT::Sym; + using Elf_Word = typename ELFT::Word; + using Elf_Xword = typename ELFT::Xword; + +public: + void visit(Section &Sec) override; + void visit(OwnedDataSection &Sec) override; + void visit(StringTableSection &Sec) override; + void visit(DynamicRelocationSection &Sec) override; + void visit(SymbolTableSection &Sec) override; + void visit(RelocationSection &Sec) override; + void visit(GnuDebugLinkSection &Sec) override; + void visit(GroupSection &Sec) override; + void visit(SectionIndexSection &Sec) override; + void visit(CompressedSection &Sec) override; + void visit(DecompressedSection &Sec) override; +}; + #define MAKE_SEC_WRITER_FRIEND \ friend class SectionWriter; \ - template <class ELFT> friend class ELFSectionWriter; + template <class ELFT> friend class ELFSectionWriter; \ + template <class ELFT> friend class ELFSectionSizer; class BinarySectionWriter : public SectionWriter { public: @@ -129,52 +181,12 @@ public: void visit(const GnuDebugLinkSection &Sec) override; void visit(const GroupSection &Sec) override; void visit(const SectionIndexSection &Sec) override; + void visit(const CompressedSection &Sec) override; + void visit(const DecompressedSection &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; @@ -190,10 +202,13 @@ public: template <class ELFT> class ELFWriter : public Writer { private: + using Elf_Addr = typename ELFT::Addr; using Elf_Shdr = typename ELFT::Shdr; using Elf_Phdr = typename ELFT::Phdr; using Elf_Ehdr = typename ELFT::Ehdr; + void initEhdrSegment(); + void writeEhdr(); void writePhdr(const Segment &Seg); void writeShdr(const SectionBase &Sec); @@ -233,7 +248,7 @@ public: class SectionBase { public: - StringRef Name; + std::string Name; Segment *ParentSegment = nullptr; uint64_t HeaderOffset; uint64_t OriginalOffset = std::numeric_limits<uint64_t>::max(); @@ -250,6 +265,10 @@ public: uint64_t Offset = 0; uint64_t Size = 0; uint64_t Type = ELF::SHT_NULL; + ArrayRef<uint8_t> OriginalData; + + SectionBase() = default; + SectionBase(const SectionBase &) = default; virtual ~SectionBase() = default; @@ -258,6 +277,7 @@ public: virtual void removeSectionReferences(const SectionBase *Sec); virtual void removeSymbols(function_ref<bool(const Symbol &)> ToRemove); virtual void accept(SectionVisitor &Visitor) const = 0; + virtual void accept(MutableSectionVisitor &Visitor) = 0; virtual void markSymbols(); }; @@ -275,21 +295,21 @@ private: }; std::set<const SectionBase *, SectionCompare> Sections; - ArrayRef<uint8_t> Contents; public: - uint64_t Align; - uint64_t FileSize; + uint32_t Type; uint32_t Flags; - uint32_t Index; - uint64_t MemSize; uint64_t Offset; - uint64_t PAddr; - uint64_t Type; uint64_t VAddr; + uint64_t PAddr; + uint64_t FileSize; + uint64_t MemSize; + uint64_t Align; + uint32_t Index; uint64_t OriginalOffset; Segment *ParentSegment = nullptr; + ArrayRef<uint8_t> Contents; explicit Segment(ArrayRef<uint8_t> Data) : Contents(Data) {} Segment() {} @@ -314,6 +334,7 @@ public: explicit Section(ArrayRef<uint8_t> Data) : Contents(Data) {} void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; void removeSectionReferences(const SectionBase *Sec) override; void initialize(SectionTableRef SecTable) override; void finalize() override; @@ -327,13 +348,57 @@ class OwnedDataSection : public SectionBase { public: OwnedDataSection(StringRef SecName, ArrayRef<uint8_t> Data) : Data(std::begin(Data), std::end(Data)) { - Name = SecName; + Name = SecName.str(); Type = ELF::SHT_PROGBITS; Size = Data.size(); OriginalOffset = std::numeric_limits<uint64_t>::max(); } void accept(SectionVisitor &Sec) const override; + void accept(MutableSectionVisitor &Visitor) override; +}; + +class CompressedSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + + DebugCompressionType CompressionType; + uint64_t DecompressedSize; + uint64_t DecompressedAlign; + SmallVector<char, 128> CompressedData; + +public: + CompressedSection(const SectionBase &Sec, + DebugCompressionType CompressionType); + CompressedSection(ArrayRef<uint8_t> CompressedData, uint64_t DecompressedSize, + uint64_t DecompressedAlign); + + uint64_t getDecompressedSize() const { return DecompressedSize; } + uint64_t getDecompressedAlign() const { return DecompressedAlign; } + + void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; + + static bool classof(const SectionBase *S) { + return (S->Flags & ELF::SHF_COMPRESSED) || + (StringRef(S->Name).startswith(".zdebug")); + } +}; + +class DecompressedSection : public SectionBase { + MAKE_SEC_WRITER_FRIEND + +public: + explicit DecompressedSection(const CompressedSection &Sec) + : SectionBase(Sec) { + Size = Sec.getDecompressedSize(); + Align = Sec.getDecompressedAlign(); + Flags = (Flags & ~ELF::SHF_COMPRESSED); + if (StringRef(Name).startswith(".zdebug")) + Name = "." + Name.substr(2); + } + + void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; }; // There are two types of string tables that can exist, dynamic and not dynamic. @@ -358,6 +423,7 @@ public: uint32_t findIndex(StringRef Name) const; void finalize() override; void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; static bool classof(const SectionBase *S) { if (S->Flags & ELF::SHF_ALLOC) @@ -386,7 +452,7 @@ struct Symbol { SectionBase *DefinedIn = nullptr; SymbolShndxType ShndxType; uint32_t Index; - StringRef Name; + std::string Name; uint32_t NameIndex; uint64_t Size; uint8_t Type; @@ -395,6 +461,7 @@ struct Symbol { bool Referenced = false; uint16_t getShndx() const; + bool isCommon() const; }; class SectionIndexSection : public SectionBase { @@ -414,6 +481,7 @@ public: void initialize(SectionTableRef SecTable) override; void finalize() override; void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; SectionIndexSection() { Name = ".symtab_shndx"; @@ -437,9 +505,11 @@ protected: using SymPtr = std::unique_ptr<Symbol>; public: - void addSymbol(StringRef Name, uint8_t Bind, uint8_t Type, - SectionBase *DefinedIn, uint64_t Value, uint8_t Visibility, - uint16_t Shndx, uint64_t Sz); + SymbolTableSection() { Type = ELF::SHT_SYMTAB; } + + void addSymbol(Twine Name, uint8_t Bind, uint8_t Type, SectionBase *DefinedIn, + uint64_t Value, uint8_t Visibility, uint16_t Shndx, + uint64_t Size); void prepareForLayout(); // An 'empty' symbol table still contains a null symbol. bool empty() const { return Symbols.size() == 1; } @@ -456,6 +526,7 @@ public: void initialize(SectionTableRef SecTable) override; void finalize() override; void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; static bool classof(const SectionBase *S) { @@ -517,6 +588,7 @@ class RelocationSection public: void addRelocation(Relocation Rel) { Relocations.push_back(Rel); } void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; void markSymbols() override; @@ -549,8 +621,8 @@ public: 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 accept(MutableSectionVisitor &Visitor) override; void finalize() override; void removeSymbols(function_ref<bool(const Symbol &)> ToRemove) override; void markSymbols() override; @@ -589,6 +661,7 @@ public: explicit DynamicRelocationSection(ArrayRef<uint8_t> Data) : Contents(Data) {} void accept(SectionVisitor &) const override; + void accept(MutableSectionVisitor &Visitor) override; static bool classof(const SectionBase *S) { if (!(S->Flags & ELF::SHF_ALLOC)) @@ -610,6 +683,7 @@ public: // If we add this section from an external source we can use this ctor. explicit GnuDebugLinkSection(StringRef File); void accept(SectionVisitor &Visitor) const override; + void accept(MutableSectionVisitor &Visitor) override; }; class Reader { @@ -623,11 +697,29 @@ using object::ELFFile; using object::ELFObjectFile; using object::OwningBinary; +class BinaryELFBuilder { + uint16_t EMachine; + MemoryBuffer *MemBuf; + std::unique_ptr<Object> Obj; + + void initFileHeader(); + void initHeaderSegment(); + StringTableSection *addStrTab(); + SymbolTableSection *addSymTab(StringTableSection *StrTab); + void addData(SymbolTableSection *SymTab); + void initSections(); + +public: + BinaryELFBuilder(uint16_t EM, MemoryBuffer *MB) + : EMachine(EM), MemBuf(MB), Obj(llvm::make_unique<Object>()) {} + + std::unique_ptr<Object> build(); +}; + 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_Word = typename ELFT::Word; const ELFFile<ELFT> &ElfFile; @@ -647,13 +739,22 @@ public: void build(); }; +class BinaryReader : public Reader { + const MachineInfo &MInfo; + MemoryBuffer *MemBuf; + +public: + BinaryReader(const MachineInfo &MI, MemoryBuffer *MB) + : MInfo(MI), MemBuf(MB) {} + std::unique_ptr<Object> create() const override; +}; + class ELFReader : public Reader { Binary *Bin; public: - ElfType getElfType() const; std::unique_ptr<Object> create() const override; - explicit ELFReader(Binary *B) : Bin(B){}; + explicit ELFReader(Binary *B) : Bin(B) {} }; class Object { @@ -682,7 +783,8 @@ public: Segment ElfHdrSegment; Segment ProgramHdrSegment; - uint8_t Ident[16]; + uint8_t OSABI; + uint8_t ABIVersion; uint64_t Entry; uint64_t SHOffset; uint32_t Type; @@ -708,6 +810,7 @@ public: auto Sec = llvm::make_unique<T>(std::forward<Ts>(Args)...); auto Ptr = Sec.get(); Sections.emplace_back(std::move(Sec)); + Ptr->Index = Sections.size(); return *Ptr; } Segment &addSegment(ArrayRef<uint8_t> Data) { @@ -715,6 +818,8 @@ public: return *Segments.back(); } }; + +} // end namespace elf } // end namespace objcopy } // end namespace llvm diff --git a/tools/llvm-objcopy/ObjcopyOpts.td b/tools/llvm-objcopy/ObjcopyOpts.td index 2af2108d98d3..1f7e64e4091c 100644 --- a/tools/llvm-objcopy/ObjcopyOpts.td +++ b/tools/llvm-objcopy/ObjcopyOpts.td @@ -1,55 +1,98 @@ include "llvm/Option/OptParser.td" -multiclass Eq<string name> { - def NAME: Separate<["--", "-"], name>; - def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>; +multiclass Eq<string name, string help> { + def NAME : Separate<["--", "-"], name>; + def NAME #_eq : Joined<["--", "-"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; } 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">, + +defm binary_architecture + : Eq<"binary-architecture", "Used when transforming an architecture-less " + "format (such as binary) to another format">; +def B : JoinedOrSeparate<["-"], "B">, Alias<binary_architecture>; + +defm target : Eq<"target", "Format of the input and output file">, + Values<"binary">; +def F : JoinedOrSeparate<["-"], "F">, Alias<target>; + +defm input_target : Eq<"input-target", "Format of the input file">, Values<"binary">; -defm output_target : Eq<"output-target">, - HelpText<"Format of the output file">, +def I : JoinedOrSeparate<["-"], "I">, Alias<input_target>; + +defm output_target : Eq<"output-target", "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 O : JoinedOrSeparate<["-"], "O">, Alias<output_target>; + +def compress_debug_sections : Flag<["--", "-"], "compress-debug-sections">; +def compress_debug_sections_eq + : Joined<["--", "-"], "compress-debug-sections=">, + MetaVarName<"[ zlib | zlib-gnu ]">, + HelpText<"Compress DWARF debug sections using specified style. Supported " + "styles: 'zlib-gnu' and 'zlib'">; +def decompress_debug_sections : Flag<["-", "--"], "decompress-debug-sections">, + HelpText<"Decompress DWARF debug sections.">; +defm split_dwo + : Eq<"split-dwo", "Equivalent to extract-dwo on the input file to " + "<dwo-file>, then strip-dwo on the input file">, + MetaVarName<"dwo-file">; + +def enable_deterministic_archives + : Flag<["-", "--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when copying archives (use zero for " + "UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; + +def disable_deterministic_archives + : Flag<["-", "--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when copying archives (use real " + "values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; + +def preserve_dates : Flag<["-", "--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, Alias<preserve_dates>; + +defm add_gnu_debuglink + : Eq<"add-gnu-debuglink", "Add a .gnu_debuglink for <debug-file>">, + MetaVarName<"debug-file">; + +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; + +defm rename_section + : Eq<"rename-section", + "Renames a section from old to new, optionally with specified flags. " + "Flags supported for GNU compatibility: alloc, load, noload, " + "readonly, debug, code, data, rom, share, contents, merge, strings.">, + MetaVarName<"old=new[,flag1,...]">; +defm redefine_symbol + : Eq<"redefine-sym", "Change the name of a symbol old to new">, + MetaVarName<"old=new">; +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; +defm only_section : Eq<"only-section", "Remove all but <section>">, + MetaVarName<"section">; +def j : JoinedOrSeparate<["-"], "j">, Alias<only_section>; +defm add_section + : Eq<"add-section", + "Make a section named <section> with the contents of <file>.">, + MetaVarName<"section=file">; + +def strip_all + : Flag<["-", "--"], "strip-all">, + HelpText< + "Remove non-allocated sections other than .gnu.warning* sections">; +def S : Flag<["-"], "S">, Alias<strip_all>; def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">, - HelpText<"Compaitable with GNU objcopy's --strip-all">; + HelpText<"Compatible with GNU objcopy's --strip-all">; def strip_debug : Flag<["-", "--"], "strip-debug">, HelpText<"Remove all debug information">; def strip_dwo : Flag<["-", "--"], "strip-dwo">, @@ -58,42 +101,80 @@ 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">; + HelpText<"Remove all symbols not needed by relocations">; + +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", "Mark <symbol> as local">, + MetaVarName<"symbol">; +def L : JoinedOrSeparate<["-"], "L">, Alias<localize_symbol>; + +defm globalize_symbol : Eq<"globalize-symbol", "Mark <symbol> as global">, + MetaVarName<"symbol">; +defm keep_global_symbol + : Eq<"keep-global-symbol", + "Convert all symbols except <symbol> to local. May be repeated to " + "convert all except a set of symbols to local.">, + MetaVarName<"symbol">; +def G : JoinedOrSeparate<["-"], "G">, Alias<keep_global_symbol>; + +defm keep_global_symbols + : Eq<"keep-global-symbols", + "Reads a list of symbols from <filename> and runs as if " + "--keep-global-symbol=<symbol> is set for each one. <filename> " + "contains one symbol per line and may contain comments beginning with " + "'#'. Leading and trailing whitespace is stripped from each line. May " + "be repeated to read symbols from many files.">, + MetaVarName<"filename">; + +defm weaken_symbol : Eq<"weaken-symbol", "Mark <symbol> as weak">, + MetaVarName<"symbol">; +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", "Remove symbol <symbol>">, + MetaVarName<"symbol">; +def N : JoinedOrSeparate<["-"], "N">, Alias<strip_symbol>; +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"symbol">; +def K : JoinedOrSeparate<["-"], "K">, Alias<keep_symbol>; +def only_keep_debug + : Flag<["-", "--"], "only-keep-debug">, + HelpText<"Currently ignored. Only for compatibility with GNU objcopy.">; def keep_file_symbols : Flag<["-", "--"], "keep-file-symbols">, - HelpText<"Do not remove file symbols">; + HelpText<"Do not remove file symbols">; +defm dump_section + : Eq<"dump-section", + "Dump contents of section named <section> into file <file>">, + MetaVarName<"section=file">; +defm prefix_symbols + : Eq<"prefix-symbols", "Add <prefix> to the start of every symbol name">, + MetaVarName<"prefix">; + +def version : Flag<["-", "--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, Alias<version>; +defm build_id_link_dir + : Eq<"build-id-link-dir", "Set directory for --build-id-link-input and " + "--build-id-link-output to <dir>">, + MetaVarName<"dir">; +defm build_id_link_input + : Eq<"build-id-link-input", "Hard-link the input to <dir>/xx/xxx<suffix> " + "name derived from hex build ID">, + MetaVarName<"suffix">; +defm build_id_link_output + : Eq<"build-id-link-output", "Hard-link the output to <dir>/xx/xxx<suffix> " + "name derived from hex build ID">, + MetaVarName<"suffix">; diff --git a/tools/llvm-objcopy/StripOpts.td b/tools/llvm-objcopy/StripOpts.td index 333b0d288efa..fa98e27e9321 100644 --- a/tools/llvm-objcopy/StripOpts.td +++ b/tools/llvm-objcopy/StripOpts.td @@ -1,49 +1,67 @@ include "llvm/Option/OptParser.td" -multiclass Eq<string name> { - def NAME: Separate<["--", "-"], name>; - def NAME # _eq: Joined<["--", "-"], name # "=">, Alias<!cast<Separate>(NAME)>; +multiclass Eq<string name, string help> { + def NAME : Separate<["--", "-"], name>; + def NAME #_eq : Joined<["--", "-"], name #"=">, + Alias<!cast<Separate>(NAME)>, + HelpText<help>; } def help : Flag<["-", "--"], "help">; -defm output : Eq<"o">, - MetaVarName<"output">, - HelpText<"Write output to <file>">; +def enable_deterministic_archives + : Flag<["-", "--"], "enable-deterministic-archives">, + HelpText<"Enable deterministic mode when stripping archives (use zero " + "for UIDs, GIDs, and timestamps).">; +def D : Flag<["-"], "D">, + Alias<enable_deterministic_archives>, + HelpText<"Alias for --enable-deterministic-archives">; -def strip_all : Flag<["-", "--"], "strip-all">, - HelpText<"Remove non-allocated sections other than .gnu.warning* sections">; +def disable_deterministic_archives + : Flag<["-", "--"], "disable-deterministic-archives">, + HelpText<"Disable deterministic mode when stripping archives (use real " + "values for UIDs, GIDs, and timestamps).">; +def U : Flag<["-"], "U">, + Alias<disable_deterministic_archives>, + HelpText<"Alias for --disable-deterministic-archives">; -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>; +defm output : Eq<"o", "Write output to <file>">, MetaVarName<"output">; -def S : Flag<["-"], "S">, - Alias<strip_debug>; +def preserve_dates : Flag<["-", "--"], "preserve-dates">, + HelpText<"Preserve access and modification timestamps">; +def p : Flag<["-"], "p">, Alias<preserve_dates>; -defm remove_section : Eq<"remove-section">, - MetaVarName<"section">, - HelpText<"Remove <section>">; +def strip_all + : Flag<["-", "--"], "strip-all">, + HelpText< + "Remove non-allocated sections other than .gnu.warning* sections">; +def s : Flag<["-"], "s">, Alias<strip_all>; -def R : JoinedOrSeparate<["-"], "R">, - Alias<remove_section>; +def strip_all_gnu : Flag<["-", "--"], "strip-all-gnu">, + HelpText<"Compatible with GNU strip's --strip-all">; +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>; +def strip_unneeded : Flag<["-", "--"], "strip-unneeded">, + HelpText<"Remove all symbols not needed by relocations">; -defm keep_symbol : Eq<"keep-symbol">, - MetaVarName<"symbol">, - HelpText<"Do not remove symbol <symbol>">; +defm remove_section : Eq<"remove-section", "Remove <section>">, + MetaVarName<"section">; +def R : JoinedOrSeparate<["-"], "R">, Alias<remove_section>; -def K : JoinedOrSeparate<["-"], "K">, - Alias<keep_symbol>; +defm keep_section : Eq<"keep-section", "Keep <section>">, + MetaVarName<"section">; +defm keep_symbol : Eq<"keep-symbol", "Do not remove symbol <symbol>">, + MetaVarName<"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 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">; +def version : Flag<["-", "--"], "version">, + HelpText<"Print the version and exit.">; +def V : Flag<["-"], "V">, Alias<version>; diff --git a/tools/llvm-objcopy/llvm-objcopy.cpp b/tools/llvm-objcopy/llvm-objcopy.cpp index 21a1622db765..fb1ff18b015b 100644 --- a/tools/llvm-objcopy/llvm-objcopy.cpp +++ b/tools/llvm-objcopy/llvm-objcopy.cpp @@ -8,14 +8,19 @@ //===----------------------------------------------------------------------===// #include "llvm-objcopy.h" -#include "Object.h" +#include "Buffer.h" +#include "COFF/COFFObjcopy.h" +#include "CopyConfig.h" +#include "ELF/ELFObjcopy.h" + #include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" #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/COFF.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/ELFTypes.h" #include "llvm/Object/Error.h" @@ -23,137 +28,23 @@ #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" #include "llvm/Support/Error.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ErrorOr.h" -#include "llvm/Support/FileOutputBuffer.h" #include "llvm/Support/InitLLVM.h" +#include "llvm/Support/Memory.h" #include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cassert> #include <cstdlib> -#include <functional> -#include <iterator> #include <memory> #include <string> #include <system_error> #include <utility> -using namespace llvm; -using namespace llvm::objcopy; -using namespace object; -using namespace ELF; - -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 { @@ -161,14 +52,15 @@ namespace objcopy { StringRef ToolName; LLVM_ATTRIBUTE_NORETURN void error(Twine Message) { - errs() << ToolName << ": " << Message << ".\n"; + WithColor::error(errs(), ToolName) << Message << ".\n"; errs().flush(); exit(1); } LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, std::error_code EC) { assert(EC); - errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n"; + WithColor::error(errs(), ToolName) + << "'" << File << "': " << EC.message() << ".\n"; exit(1); } @@ -176,304 +68,18 @@ LLVM_ATTRIBUTE_NORETURN void reportError(StringRef File, Error E) { assert(E); std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << ToolName << ": '" << File << "': " << Buf; + WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; exit(1); } } // end namespace objcopy } // end namespace llvm -static bool IsDebugSection(const SectionBase &Sec) { - return Sec.Name.startswith(".debug") || Sec.Name.startswith(".zdebug") || - Sec.Name == ".gdb_index"; -} - -static bool IsDWOSection(const SectionBase &Sec) { - return Sec.Name.endswith(".dwo"); -} - -static bool OnlyKeepDWOPred(const Object &Obj, const SectionBase &Sec) { - // We can't remove the section header string table. - 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); -} - -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"); -} - -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 -// handling command line options. It's important to outline certain properties -// we expect to hold of the command line operations. Any operation that "keeps" -// should keep regardless of a remove. Additionally any removal should respect -// 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. -static void HandleArgs(const CopyConfig &Config, Object &Obj, - const Reader &Reader, ElfType OutputElfType) { - - 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 (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 (!Config.ToRemove.empty()) { - RemovePred = [&Config](const SectionBase &Sec) { - return find(Config.ToRemove, Sec.Name) != Config.ToRemove.end(); - }; - } - - if (Config.StripDWO || !Config.SplitDWO.empty()) - RemovePred = [RemovePred](const SectionBase &Sec) { - return IsDWOSection(Sec) || RemovePred(Sec); - }; - - if (Config.ExtractDWO) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - return OnlyKeepDWOPred(Obj, Sec) || RemovePred(Sec); - }; - - 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.SectionNames) - return false; - switch (Sec.Type) { - case SHT_SYMTAB: - case SHT_REL: - case SHT_RELA: - case SHT_STRTAB: - return true; - } - return IsDebugSection(Sec); - }; - - if (Config.StripSections) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || (Sec.Flags & SHF_ALLOC) == 0; - }; - } - - if (Config.StripDebug) { - RemovePred = [RemovePred](const SectionBase &Sec) { - return RemovePred(Sec) || IsDebugSection(Sec); - }; - } - - if (Config.StripNonAlloc) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - if (Config.StripAll) - RemovePred = [RemovePred, &Obj](const SectionBase &Sec) { - if (RemovePred(Sec)) - return true; - if (&Sec == Obj.SectionNames) - return false; - if (Sec.Name.startswith(".gnu.warning")) - return false; - return (Sec.Flags & SHF_ALLOC) == 0; - }; - - // Explicit copies: - if (!Config.OnlyKeep.empty()) { - RemovePred = [&Config, RemovePred, &Obj](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (find(Config.OnlyKeep, Sec.Name) != Config.OnlyKeep.end()) - return false; - - // Allow all implicit removes. - if (RemovePred(Sec)) - return true; - - // Keep special sections. - if (Obj.SectionNames == &Sec) - return false; - if (Obj.SymbolTable == &Sec || - (Obj.SymbolTable && Obj.SymbolTable->getStrTab() == &Sec)) - return false; - - // Remove everything else. - return true; - }; - } - - if (!Config.Keep.empty()) { - RemovePred = [Config, RemovePred](const SectionBase &Sec) { - // Explicitly keep these sections regardless of previous removes. - if (find(Config.Keep, Sec.Name) != Config.Keep.end()) - return false; - // Otherwise defer to RemovePred. - return RemovePred(Sec); - }; - } - - // 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); - }; - } - - 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); - if (!BufOrErr) - reportError(File, BufOrErr.getError()); - auto Buf = std::move(*BufOrErr); - auto BufPtr = reinterpret_cast<const uint8_t *>(Buf->getBufferStart()); - auto BufSize = Buf->getBufferSize(); - Obj.addSection<OwnedDataSection>(SecName, - ArrayRef<uint8_t>(BufPtr, BufSize)); - } - } - - if (!Config.AddGnuDebugLink.empty()) - Obj.addSection<GnuDebugLinkSection>(Config.AddGnuDebugLink); -} - -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(); -} +using namespace llvm; +using namespace llvm::object; +using namespace llvm::objcopy; // For regular archives this function simply calls llvm::writeArchive, // For thin archives it writes the archive file itself as well as its members. @@ -504,22 +110,48 @@ static Error deepWriteArchive(StringRef ArcName, return Error::success(); } -static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive &Ar) { +/// The function executeObjcopyOnRawBinary does the dispatch based on the format +/// of the output specified by the command line options. +static void executeObjcopyOnRawBinary(const CopyConfig &Config, + MemoryBuffer &In, Buffer &Out) { + // TODO: llvm-objcopy should parse CopyConfig.OutputFormat to recognize + // formats other than ELF / "binary" and invoke + // elf::executeObjcopyOnRawBinary, macho::executeObjcopyOnRawBinary or + // coff::executeObjcopyOnRawBinary accordingly. + return elf::executeObjcopyOnRawBinary(Config, In, Out); +} + +/// The function executeObjcopyOnBinary does the dispatch based on the format +/// of the input binary (ELF, MachO or COFF). +static void executeObjcopyOnBinary(const CopyConfig &Config, object::Binary &In, + Buffer &Out) { + if (auto *ELFBinary = dyn_cast<object::ELFObjectFileBase>(&In)) + return elf::executeObjcopyOnBinary(Config, *ELFBinary, Out); + else if (auto *COFFBinary = dyn_cast<object::COFFObjectFile>(&In)) + return coff::executeObjcopyOnBinary(Config, *COFFBinary, Out); + else + error("Unsupported object file format"); +} + +static void executeObjcopyOnArchive(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()); + Binary *Bin = ChildOrErr->get(); + Expected<StringRef> ChildNameOrErr = Child.getName(); if (!ChildNameOrErr) reportError(Ar.getFileName(), ChildNameOrErr.takeError()); MemBuffer MB(ChildNameOrErr.get()); - ExecuteElfObjcopyOnBinary(Config, **ChildOrErr, MB); + executeObjcopyOnBinary(Config, *Bin, MB); Expected<NewArchiveMember> Member = - NewArchiveMember::getOldMember(Child, true); + NewArchiveMember::getOldMember(Child, Config.DeterministicArchives); if (!Member) reportError(Ar.getFileName(), Member.takeError()); Member->Buf = MB.releaseMemoryBuffer(); @@ -529,180 +161,72 @@ static void ExecuteElfObjcopyOnArchive(const CopyConfig &Config, const Archive & if (Err) reportError(Config.InputFilename, std::move(Err)); - if (Error E = - deepWriteArchive(Config.OutputFilename, NewArchiveMembers, - Ar.hasSymbolTable(), Ar.kind(), true, Ar.isThin())) + if (Error E = deepWriteArchive(Config.OutputFilename, NewArchiveMembers, + Ar.hasSymbolTable(), Ar.kind(), + Config.DeterministicArchives, 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(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); - } - - 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); - } - - 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); +static void restoreDateOnFile(StringRef Filename, + const sys::fs::file_status &Stat) { + int FD; + + if (auto EC = + sys::fs::openFileForWrite(Filename, FD, sys::fs::CD_OpenExisting)) + reportError(Filename, EC); + + if (auto EC = sys::fs::setLastAccessAndModificationTime( + FD, Stat.getLastAccessedTime(), Stat.getLastModificationTime())) + reportError(Filename, EC); + + if (auto EC = sys::Process::SafelyCloseFileDescriptor(FD)) + reportError(Filename, EC); +} + +/// The function executeObjcopy does the higher level dispatch based on the type +/// of input (raw binary, archive or single object file) and takes care of the +/// format-agnostic modifications, i.e. preserving dates. +static void executeObjcopy(const CopyConfig &Config) { + sys::fs::file_status Stat; + if (Config.PreserveDates) + if (auto EC = sys::fs::status(Config.InputFilename, Stat)) + reportError(Config.InputFilename, EC); + + if (Config.InputFormat == "binary") { + auto BufOrErr = MemoryBuffer::getFile(Config.InputFilename); + if (!BufOrErr) + reportError(Config.InputFilename, BufOrErr.getError()); + FileBuffer FB(Config.OutputFilename); + executeObjcopyOnRawBinary(Config, *BufOrErr->get(), FB); + } else { + Expected<OwningBinary<llvm::object::Binary>> BinaryOrErr = + createBinary(Config.InputFilename); + if (!BinaryOrErr) + reportError(Config.InputFilename, BinaryOrErr.takeError()); + + if (Archive *Ar = dyn_cast<Archive>(BinaryOrErr.get().getBinary())) { + executeObjcopyOnArchive(Config, *Ar); + } else { + FileBuffer FB(Config.OutputFilename); + executeObjcopyOnBinary(Config, *BinaryOrErr.get().getBinary(), FB); + } } - if (InputArgs.hasArg(STRIP_help)) { - T.PrintHelp(outs(), "llvm-strip <input> [ <output> ]", "strip tool"); - exit(0); + if (Config.PreserveDates) { + restoreDateOnFile(Config.OutputFilename, Stat); + if (!Config.SplitDWO.empty()) + restoreDateOnFile(Config.SplitDWO, Stat); } - - 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)); + DriverConfig DriverConfig; + if (sys::path::stem(ToolName).contains("strip")) + DriverConfig = parseStripOptions(makeArrayRef(argv + 1, argc)); else - Config = ParseObjcopyOptions(makeArrayRef(argv + 1, argc)); - ExecuteElfObjcopy(Config); + DriverConfig = parseObjcopyOptions(makeArrayRef(argv + 1, argc)); + for (const CopyConfig &CopyConfig : DriverConfig.CopyConfigs) + executeObjcopy(CopyConfig); } diff --git a/tools/llvm-objcopy/llvm-objcopy.h b/tools/llvm-objcopy/llvm-objcopy.h index e222b65dc78f..d8edf3e29ee0 100644 --- a/tools/llvm-objcopy/llvm-objcopy.h +++ b/tools/llvm-objcopy/llvm-objcopy.h @@ -31,7 +31,7 @@ template <class T> T unwrapOrError(Expected<T> EO) { return *EO; std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(EO.takeError(), OS, ""); + logAllUnhandledErrors(EO.takeError(), OS); OS.flush(); error(Buf); } diff --git a/tools/llvm-objdump/COFFDump.cpp b/tools/llvm-objdump/COFFDump.cpp index 7ca5d04593ff..55607ec299be 100644 --- a/tools/llvm-objdump/COFFDump.cpp +++ b/tools/llvm-objdump/COFFDump.cpp @@ -16,11 +16,13 @@ //===----------------------------------------------------------------------===// #include "llvm-objdump.h" +#include "llvm/Demangle/Demangle.h" #include "llvm/Object/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Support/Format.h" #include "llvm/Support/Win64EH.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; @@ -453,7 +455,7 @@ static bool getPDataSection(const COFFObjectFile *Obj, Rels.push_back(Reloc); // Sort relocations by address. - llvm::sort(Rels.begin(), Rels.end(), RelocAddressLess); + llvm::sort(Rels, isRelocAddressLess); ArrayRef<uint8_t> Contents; error(Obj->getSectionContents(Pdata, Contents)); @@ -578,8 +580,9 @@ static void printRuntimeFunctionRels(const COFFObjectFile *Obj, void llvm::printCOFFUnwindInfo(const COFFObjectFile *Obj) { if (Obj->getMachine() != COFF::IMAGE_FILE_MACHINE_AMD64) { - errs() << "Unsupported image machine type " - "(currently only AMD64 is supported).\n"; + WithColor::error(errs(), "llvm-objdump") + << "unsupported image machine type " + "(currently only AMD64 is supported).\n"; return; } @@ -646,10 +649,26 @@ void llvm::printCOFFSymbolTable(const COFFObjectFile *coff) { << "(sec " << format("%2d", int(Symbol->getSectionNumber())) << ")" << "(fl 0x00)" // Flag bits, which COFF doesn't have. << "(ty " << format("%3x", unsigned(Symbol->getType())) << ")" - << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) << ") " + << "(scl " << format("%3x", unsigned(Symbol->getStorageClass())) + << ") " << "(nx " << unsigned(Symbol->getNumberOfAuxSymbols()) << ") " << "0x" << format("%08x", unsigned(Symbol->getValue())) << " " - << Name << "\n"; + << Name; + if (Demangle && Name.startswith("?")) { + char *DemangledSymbol = nullptr; + size_t Size = 0; + int Status = -1; + DemangledSymbol = + microsoftDemangle(Name.data(), DemangledSymbol, &Size, &Status); + + if (Status == 0 && DemangledSymbol) { + outs() << " (" << StringRef(DemangledSymbol) << ")"; + std::free(DemangledSymbol); + } else { + outs() << " (invalid mangled name)"; + } + } + outs() << "\n"; for (unsigned AI = 0, AE = Symbol->getNumberOfAuxSymbols(); AI < AE; ++AI, ++SI) { if (Symbol->isSectionDefinition()) { diff --git a/tools/llvm-objdump/ELFDump.cpp b/tools/llvm-objdump/ELFDump.cpp index f4d36656a6c4..b17a15a0d8fc 100644 --- a/tools/llvm-objdump/ELFDump.cpp +++ b/tools/llvm-objdump/ELFDump.cpp @@ -158,37 +158,23 @@ template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { } void llvm::printELFFileHeader(const object::ObjectFile *Obj) { - // Little-endian 32-bit - if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) + if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) printProgramHeaders(ELFObj->getELFFile()); - - // Big-endian 32-bit - if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) + else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) printProgramHeaders(ELFObj->getELFFile()); - - // Little-endian 64-bit - if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) + else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) printProgramHeaders(ELFObj->getELFFile()); - - // Big-endian 64-bit - if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) + else if (const auto *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)) + if (const auto *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) printDynamicSection(ELFObj->getELFFile(), Obj->getFileName()); - - // Big-endian 32-bit - if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) + else if (const auto *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) printDynamicSection(ELFObj->getELFFile(), Obj->getFileName()); - - // Little-endian 64-bit - if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) + else if (const auto *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) printDynamicSection(ELFObj->getELFFile(), Obj->getFileName()); - - // Big-endian 64-bit - if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) + else if (const auto *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 bdf80c73b999..5ef7058ec9da 100644 --- a/tools/llvm-objdump/MachODump.cpp +++ b/tools/llvm-objdump/MachODump.cpp @@ -44,6 +44,7 @@ #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Support/ToolOutputFile.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cstring> @@ -166,7 +167,7 @@ static const Target *GetTarget(const MachOObjectFile *MachOObj, if (*ThumbTarget) return TheTarget; - errs() << "llvm-objdump: error: unable to get target for '"; + WithColor::error(errs(), "llvm-objdump") << "unable to get target for '"; if (!TheTarget) errs() << TripleName; else @@ -483,7 +484,7 @@ static void PrintRType(const uint64_t cputype, const unsigned r_type) { "GOTLDP ", "GOTLDPOF", "PTRTGOT ", "TLVLDP ", "TLVLDPOF", "ADDEND ", " 11 (?) ", " 12 (?) ", " 13 (?) ", " 14 (?) ", " 15 (?) " }; - + if (r_type > 0xf){ outs() << format("%-7u", r_type) << " "; return; @@ -552,7 +553,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O, bool previous_arm_half = false; bool previous_sectdiff = false; uint32_t sectdiff_r_type = 0; - + for (relocation_iterator Reloc = Begin; Reloc != End; ++Reloc) { const DataRefImpl Rel = Reloc->getRawDataRefImpl(); const MachO::any_relocation_info RE = O->getRelocation(Rel); @@ -567,7 +568,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O, O->getScatteredRelocationValue(RE) : 0); const unsigned r_symbolnum = (r_scattered ? 0 : O->getPlainRelocationSymbolNum(RE)); - + if (r_scattered && cputype != MachO::CPU_TYPE_X86_64) { if (verbose) { // scattered: address @@ -578,20 +579,20 @@ static void PrintRelocationEntries(const MachOObjectFile *O, outs() << " "; else outs() << format("%08x ", (unsigned int)r_address); - + // scattered: pcrel if (r_pcrel) outs() << "True "; else outs() << "False "; - + // scattered: length PrintRLength(cputype, r_type, r_length, previous_arm_half); - + // scattered: extern & type outs() << "n/a "; PrintRType(cputype, r_type); - + // scattered: scattered & value outs() << format("True 0x%08x", (unsigned int)r_value); if (previous_sectdiff == false) { @@ -639,22 +640,22 @@ static void PrintRelocationEntries(const MachOObjectFile *O, outs() << " "; else outs() << format("%08x ", (unsigned int)r_address); - + // plain: pcrel if (r_pcrel) outs() << "True "; else outs() << "False "; - + // plain: length PrintRLength(cputype, r_type, r_length, previous_arm_half); - + if (r_extern) { // plain: extern & type & scattered outs() << "True "; PrintRType(cputype, r_type); outs() << "False "; - + // plain: symbolnum/value if (r_symbolnum > Symtab.nsyms) outs() << format("?(%d)\n", r_symbolnum); @@ -675,7 +676,7 @@ static void PrintRelocationEntries(const MachOObjectFile *O, outs() << "False "; PrintRType(cputype, r_type); outs() << "False "; - + // plain: symbolnum/value if (cputype == MachO::CPU_TYPE_ARM && r_type == llvm::MachO::ARM_RELOC_PAIR) @@ -1411,7 +1412,7 @@ static void DumpSectionContents(StringRef Filename, MachOObjectFile *O, std::pair<StringRef, StringRef> DumpSegSectName; DumpSegSectName = DumpSection.split(','); StringRef DumpSegName, DumpSectName; - if (DumpSegSectName.second.size()) { + if (!DumpSegSectName.second.empty()) { DumpSegName = DumpSegSectName.first; DumpSectName = DumpSegSectName.second; } else { @@ -1559,7 +1560,8 @@ static bool checkMachOAndArchFlags(ObjectFile *O, StringRef Filename) { if (none_of(ArchFlags, [&](const std::string &Name) { return Name == ArchFlagName; })) { - errs() << "llvm-objdump: " + Filename + ": No architecture specified.\n"; + WithColor::error(errs(), "llvm-objdump") + << Filename << ": no architecture specified.\n"; return false; } return true; @@ -1580,7 +1582,7 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, if (Disassemble || Relocations || PrivateHeaders || ExportsTrie || Rebase || Bind || SymbolTable || LazyBind || WeakBind || IndirectSymbols || DataInCode || LinkOptHints || DylibsUsed || DylibId || ObjcMetaData || - (FilterSections.size() != 0)) { + (!FilterSections.empty())) { if (!NoLeadingHeaders) { outs() << Name; if (!ArchiveMemberName.empty()) @@ -1605,12 +1607,22 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, // If we need the symbol table to do the operation then check it here to // produce a good error message as to where the Mach-O file comes from in // the error message. - if (Disassemble || IndirectSymbols || FilterSections.size() != 0 || - UnwindInfo) + if (Disassemble || IndirectSymbols || !FilterSections.empty() || UnwindInfo) if (Error Err = MachOOF->checkSymbolTable()) report_error(ArchiveName, FileName, std::move(Err), ArchitectureName); - - if (Disassemble) { + + if (DisassembleAll) { + for (const SectionRef &Section : MachOOF->sections()) { + StringRef SectName; + Section.getName(SectName); + if (SectName.equals("__text")) { + DataRefImpl Ref = Section.getRawDataRefImpl(); + StringRef SegName = MachOOF->getSectionFinalSegmentName(Ref); + DisassembleMachO(FileName, MachOOF, SegName, SectName); + } + } + } + else if (Disassemble) { if (MachOOF->getHeader().filetype == MachO::MH_KEXT_BUNDLE && MachOOF->getHeader().cputype == MachO::CPU_TYPE_ARM64) DisassembleMachO(FileName, MachOOF, "__TEXT_EXEC", "__text"); @@ -1626,10 +1638,10 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, if (Relocations) PrintRelocations(MachOOF, !NonVerbose); if (SectionHeaders) - PrintSectionHeaders(MachOOF); + printSectionHeaders(MachOOF); if (SectionContents) - PrintSectionContents(MachOOF); - if (FilterSections.size() != 0) + printSectionContents(MachOOF); + if (!FilterSections.empty()) DumpSectionContents(FileName, MachOOF, !NonVerbose); if (InfoPlist) DumpInfoPlistSectionContents(FileName, MachOOF); @@ -1638,7 +1650,7 @@ static void ProcessMachO(StringRef Name, MachOObjectFile *MachOOF, if (DylibId) PrintDylibs(MachOOF, true); if (SymbolTable) - PrintSymbolTable(MachOOF, ArchiveName, ArchitectureName); + printSymbolTable(MachOOF, ArchiveName, ArchitectureName); if (UnwindInfo) printMachOUnwindInfo(MachOOF); if (PrivateHeaders) { @@ -1937,23 +1949,30 @@ static void printArchiveHeaders(StringRef Filename, Archive *A, bool verbose, report_error(StringRef(), Filename, std::move(Err), ArchitectureName); } -// ParseInputMachO() parses the named Mach-O file in Filename and handles the -// -arch flags selecting just those slices as specified by them and also parses -// archive files. Then for each individual Mach-O file ProcessMachO() is -// called to process the file based on the command line options. -void llvm::ParseInputMachO(StringRef Filename) { +static bool ValidateArchFlags() { // Check for -arch all and verifiy the -arch flags are valid. for (unsigned i = 0; i < ArchFlags.size(); ++i) { if (ArchFlags[i] == "all") { ArchAll = true; } else { if (!MachOObjectFile::isValidArch(ArchFlags[i])) { - errs() << "llvm-objdump: Unknown architecture named '" + ArchFlags[i] + - "'for the -arch option\n"; - return; + WithColor::error(errs(), "llvm-objdump") + << "unknown architecture named '" + ArchFlags[i] + + "'for the -arch option\n"; + return false; } } } + return true; +} + +// ParseInputMachO() parses the named Mach-O file in Filename and handles the +// -arch flags selecting just those slices as specified by them and also parses +// archive files. Then for each individual Mach-O file ProcessMachO() is +// called to process the file based on the command line options. +void llvm::parseInputMachO(StringRef Filename) { + if (!ValidateArchFlags()) + return; // Attempt to open the binary. Expected<OwningBinary<Binary>> BinaryOrErr = createBinary(Filename); @@ -1989,191 +2008,199 @@ void llvm::ParseInputMachO(StringRef Filename) { report_error(Filename, std::move(Err)); return; } - if (UniversalHeaders) { - if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) - printMachOUniversalHeaders(UB, !NonVerbose); - } if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { - // If we have a list of architecture flags specified dump only those. - if (!ArchAll && ArchFlags.size() != 0) { - // Look for a slice in the universal binary that matches each ArchFlag. - bool ArchFound; - for (unsigned i = 0; i < ArchFlags.size(); ++i) { - ArchFound = false; - for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), - E = UB->end_objects(); - I != E; ++I) { - if (ArchFlags[i] == I->getArchFlagName()) { - ArchFound = true; - Expected<std::unique_ptr<ObjectFile>> ObjOrErr = - I->getAsObjectFile(); - std::string ArchitectureName = ""; - if (ArchFlags.size() > 1) - ArchitectureName = I->getArchFlagName(); - if (ObjOrErr) { - ObjectFile &O = *ObjOrErr.get(); - if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) - ProcessMachO(Filename, MachOOF, "", ArchitectureName); - } else if (auto E = isNotObjectErrorInvalidFileType( - ObjOrErr.takeError())) { - report_error(Filename, StringRef(), std::move(E), - ArchitectureName); - continue; - } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { - std::unique_ptr<Archive> &A = *AOrErr; - outs() << "Archive : " << Filename; - if (!ArchitectureName.empty()) - outs() << " (architecture " << ArchitectureName << ")"; - outs() << "\n"; - if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, - ArchiveMemberOffsets, ArchitectureName); - Error Err = Error::success(); - for (auto &C : A->children(Err)) { - Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); - if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E), ArchitectureName); - continue; - } - if (MachOObjectFile *O = - dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) - ProcessMachO(Filename, O, O->getFileName(), ArchitectureName); - } - if (Err) - report_error(Filename, std::move(Err)); - } else { - consumeError(AOrErr.takeError()); - error("Mach-O universal file: " + Filename + " for " + - "architecture " + StringRef(I->getArchFlagName()) + - " is not a Mach-O file or an archive file"); - } - } - } - if (!ArchFound) { - errs() << "llvm-objdump: file: " + Filename + " does not contain " - << "architecture: " + ArchFlags[i] + "\n"; - return; - } - } + parseInputMachO(UB); + return; + } + if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) { + if (!checkMachOAndArchFlags(O, Filename)) return; - } - // No architecture flags were specified so if this contains a slice that - // matches the host architecture dump only that. - if (!ArchAll) { + if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O)) + ProcessMachO(Filename, MachOOF); + else + WithColor::error(errs(), "llvm-objdump") + << Filename << "': " + << "object is not a Mach-O file type.\n"; + return; + } + llvm_unreachable("Input object can't be invalid at this point"); +} + +void llvm::parseInputMachO(MachOUniversalBinary *UB) { + if (!ValidateArchFlags()) + return; + + auto Filename = UB->getFileName(); + + if (UniversalHeaders) + printMachOUniversalHeaders(UB, !NonVerbose); + + // If we have a list of architecture flags specified dump only those. + if (!ArchAll && !ArchFlags.empty()) { + // Look for a slice in the universal binary that matches each ArchFlag. + bool ArchFound; + for (unsigned i = 0; i < ArchFlags.size(); ++i) { + ArchFound = false; for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), - E = UB->end_objects(); - I != E; ++I) { - if (MachOObjectFile::getHostArch().getArchName() == - I->getArchFlagName()) { - Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); - std::string ArchiveName; - ArchiveName.clear(); + E = UB->end_objects(); + I != E; ++I) { + if (ArchFlags[i] == I->getArchFlagName()) { + ArchFound = true; + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = + I->getAsObjectFile(); + std::string ArchitectureName = ""; + if (ArchFlags.size() > 1) + ArchitectureName = I->getArchFlagName(); if (ObjOrErr) { ObjectFile &O = *ObjOrErr.get(); if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) - ProcessMachO(Filename, MachOOF); + ProcessMachO(Filename, MachOOF, "", ArchitectureName); } else if (auto E = isNotObjectErrorInvalidFileType( - ObjOrErr.takeError())) { - report_error(Filename, std::move(E)); + ObjOrErr.takeError())) { + report_error(Filename, StringRef(), std::move(E), + ArchitectureName); continue; } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { + I->getAsArchive()) { std::unique_ptr<Archive> &A = *AOrErr; - outs() << "Archive : " << Filename << "\n"; + outs() << "Archive : " << Filename; + if (!ArchitectureName.empty()) + outs() << " (architecture " << ArchitectureName << ")"; + outs() << "\n"; if (ArchiveHeaders) printArchiveHeaders(Filename, A.get(), !NonVerbose, - ArchiveMemberOffsets); + ArchiveMemberOffsets, ArchitectureName); Error Err = Error::success(); for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E)); + report_error(Filename, C, std::move(E), ArchitectureName); continue; } if (MachOObjectFile *O = dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) - ProcessMachO(Filename, O, O->getFileName()); + ProcessMachO(Filename, O, O->getFileName(), ArchitectureName); } if (Err) report_error(Filename, std::move(Err)); } else { consumeError(AOrErr.takeError()); - error("Mach-O universal file: " + Filename + " for architecture " + - StringRef(I->getArchFlagName()) + + error("Mach-O universal file: " + Filename + " for " + + "architecture " + StringRef(I->getArchFlagName()) + " is not a Mach-O file or an archive file"); } - return; } } + if (!ArchFound) { + WithColor::error(errs(), "llvm-objdump") + << "file: " + Filename + " does not contain " + << "architecture: " + ArchFlags[i] + "\n"; + return; + } } - // Either all architectures have been specified or none have been specified - // and this does not contain the host architecture so dump all the slices. - bool moreThanOneArch = UB->getNumberOfObjects() > 1; + return; + } + // No architecture flags were specified so if this contains a slice that + // matches the host architecture dump only that. + if (!ArchAll) { for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), - E = UB->end_objects(); - I != E; ++I) { - Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); - std::string ArchitectureName = ""; - if (moreThanOneArch) - ArchitectureName = I->getArchFlagName(); - if (ObjOrErr) { - ObjectFile &Obj = *ObjOrErr.get(); - if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj)) - ProcessMachO(Filename, MachOOF, "", ArchitectureName); - } else if (auto E = isNotObjectErrorInvalidFileType( - ObjOrErr.takeError())) { - report_error(StringRef(), Filename, std::move(E), ArchitectureName); - continue; - } else if (Expected<std::unique_ptr<Archive>> AOrErr = - I->getAsArchive()) { - std::unique_ptr<Archive> &A = *AOrErr; - outs() << "Archive : " << Filename; - if (!ArchitectureName.empty()) - outs() << " (architecture " << ArchitectureName << ")"; - outs() << "\n"; - if (ArchiveHeaders) - printArchiveHeaders(Filename, A.get(), !NonVerbose, - ArchiveMemberOffsets, ArchitectureName); - Error Err = Error::success(); - for (auto &C : A->children(Err)) { - Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); - if (!ChildOrErr) { - if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(Filename, C, std::move(E), ArchitectureName); - continue; - } - if (MachOObjectFile *O = - dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { - if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O)) - ProcessMachO(Filename, MachOOF, MachOOF->getFileName(), - ArchitectureName); + E = UB->end_objects(); + I != E; ++I) { + if (MachOObjectFile::getHostArch().getArchName() == + I->getArchFlagName()) { + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); + std::string ArchiveName; + ArchiveName.clear(); + if (ObjOrErr) { + ObjectFile &O = *ObjOrErr.get(); + if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&O)) + ProcessMachO(Filename, MachOOF); + } else if (auto E = isNotObjectErrorInvalidFileType( + ObjOrErr.takeError())) { + report_error(Filename, std::move(E)); + } else if (Expected<std::unique_ptr<Archive>> AOrErr = + I->getAsArchive()) { + std::unique_ptr<Archive> &A = *AOrErr; + outs() << "Archive : " << Filename << "\n"; + if (ArchiveHeaders) + printArchiveHeaders(Filename, A.get(), !NonVerbose, + ArchiveMemberOffsets); + Error Err = Error::success(); + for (auto &C : A->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + report_error(Filename, C, std::move(E)); + continue; + } + if (MachOObjectFile *O = + dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) + ProcessMachO(Filename, O, O->getFileName()); } + if (Err) + report_error(Filename, std::move(Err)); + } else { + consumeError(AOrErr.takeError()); + error("Mach-O universal file: " + Filename + " for architecture " + + StringRef(I->getArchFlagName()) + + " is not a Mach-O file or an archive file"); } - if (Err) - report_error(Filename, std::move(Err)); - } else { - consumeError(AOrErr.takeError()); - error("Mach-O universal file: " + Filename + " for architecture " + - StringRef(I->getArchFlagName()) + - " is not a Mach-O file or an archive file"); + return; } } - return; } - if (ObjectFile *O = dyn_cast<ObjectFile>(&Bin)) { - if (!checkMachOAndArchFlags(O, Filename)) - return; - if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&*O)) { - ProcessMachO(Filename, MachOOF); - } else - errs() << "llvm-objdump: '" << Filename << "': " - << "Object is not a Mach-O file type.\n"; - return; + // Either all architectures have been specified or none have been specified + // and this does not contain the host architecture so dump all the slices. + bool moreThanOneArch = UB->getNumberOfObjects() > 1; + for (MachOUniversalBinary::object_iterator I = UB->begin_objects(), + E = UB->end_objects(); + I != E; ++I) { + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = I->getAsObjectFile(); + std::string ArchitectureName = ""; + if (moreThanOneArch) + ArchitectureName = I->getArchFlagName(); + if (ObjOrErr) { + ObjectFile &Obj = *ObjOrErr.get(); + if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(&Obj)) + ProcessMachO(Filename, MachOOF, "", ArchitectureName); + } else if (auto E = isNotObjectErrorInvalidFileType( + ObjOrErr.takeError())) { + report_error(StringRef(), Filename, std::move(E), ArchitectureName); + } else if (Expected<std::unique_ptr<Archive>> AOrErr = + I->getAsArchive()) { + std::unique_ptr<Archive> &A = *AOrErr; + outs() << "Archive : " << Filename; + if (!ArchitectureName.empty()) + outs() << " (architecture " << ArchitectureName << ")"; + outs() << "\n"; + if (ArchiveHeaders) + printArchiveHeaders(Filename, A.get(), !NonVerbose, + ArchiveMemberOffsets, ArchitectureName); + Error Err = Error::success(); + for (auto &C : A->children(Err)) { + Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); + if (!ChildOrErr) { + if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) + report_error(Filename, C, std::move(E), ArchitectureName); + continue; + } + if (MachOObjectFile *O = + dyn_cast<MachOObjectFile>(&*ChildOrErr.get())) { + if (MachOObjectFile *MachOOF = dyn_cast<MachOObjectFile>(O)) + ProcessMachO(Filename, MachOOF, MachOOF->getFileName(), + ArchitectureName); + } + } + if (Err) + report_error(Filename, std::move(Err)); + } else { + consumeError(AOrErr.takeError()); + error("Mach-O universal file: " + Filename + " for architecture " + + StringRef(I->getArchFlagName()) + + " is not a Mach-O file or an archive file"); + } } - llvm_unreachable("Input object can't be invalid at this point"); } // The block of info used by the Symbolizer call backs. @@ -5609,7 +5636,9 @@ static void print_image_info64(SectionRef S, struct DisassembleInfo *info) { else if(swift_version == 5) outs() << " Swift 4.0"; else if(swift_version == 6) - outs() << " Swift 4.1"; + outs() << " Swift 4.1/Swift 4.2"; + else if(swift_version == 7) + outs() << " Swift 5 or later"; else outs() << " unknown future Swift version (" << swift_version << ")"; } @@ -5660,7 +5689,9 @@ static void print_image_info32(SectionRef S, struct DisassembleInfo *info) { else if(swift_version == 5) outs() << " Swift 4.0"; else if(swift_version == 6) - outs() << " Swift 4.1"; + outs() << " Swift 4.1/Swift 4.2"; + else if(swift_version == 7) + outs() << " Swift 5 or later"; else outs() << " unknown future Swift version (" << swift_version << ")"; } @@ -6172,8 +6203,9 @@ static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) { ScopedXarIter xi; if (!xi) { - errs() << "Can't obtain an xar iterator for xar archive " - << XarFilename << "\n"; + WithColor::error(errs(), "llvm-objdump") + << "can't obtain an xar iterator for xar archive " << XarFilename + << "\n"; return; } @@ -6181,8 +6213,9 @@ static void PrintXarFilesSummary(const char *XarFilename, xar_t xar) { for (xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)) { ScopedXarIter xp; if(!xp){ - errs() << "Can't obtain an xar iterator for xar archive " - << XarFilename << "\n"; + WithColor::error(errs(), "llvm-objdump") + << "can't obtain an xar iterator for xar archive " << XarFilename + << "\n"; return; } type = nullptr; @@ -6306,7 +6339,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, std::error_code XarEC = sys::fs::createTemporaryFile("llvm-objdump", "xar", FD, XarFilename); if (XarEC) { - errs() << XarEC.message() << "\n"; + WithColor::error(errs(), "llvm-objdump") << XarEC.message() << "\n"; return; } ToolOutputFile XarFile(XarFilename, FD); @@ -6319,7 +6352,8 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, ScopedXarFile xar(XarFilename.c_str(), READ); if (!xar) { - errs() << "Can't create temporary xar archive " << XarFilename << "\n"; + WithColor::error(errs(), "llvm-objdump") + << "can't create temporary xar archive " << XarFilename << "\n"; return; } @@ -6327,7 +6361,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, std::error_code TocEC = sys::fs::createTemporaryFile("llvm-objdump", "toc", TocFilename); if (TocEC) { - errs() << TocEC.message() << "\n"; + WithColor::error(errs(), "llvm-objdump") << TocEC.message() << "\n"; return; } xar_serialize(xar, TocFilename.c_str()); @@ -6344,7 +6378,7 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, ErrorOr<std::unique_ptr<MemoryBuffer>> FileOrErr = MemoryBuffer::getFileOrSTDIN(TocFilename.c_str()); if (std::error_code EC = FileOrErr.getError()) { - errs() << EC.message() << "\n"; + WithColor::error(errs(), "llvm-objdump") << EC.message() << "\n"; return; } std::unique_ptr<MemoryBuffer> &Buffer = FileOrErr.get(); @@ -6359,8 +6393,9 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, // TODO: Go through the xar's files. ScopedXarIter xi; if(!xi){ - errs() << "Can't obtain an xar iterator for xar archive " - << XarFilename.c_str() << "\n"; + WithColor::error(errs(), "llvm-objdump") + << "can't obtain an xar iterator for xar archive " + << XarFilename.c_str() << "\n"; return; } for(xar_file_t xf = xar_file_first(xar, xi); xf; xf = xar_file_next(xi)){ @@ -6370,8 +6405,9 @@ static void DumpBitcodeSection(MachOObjectFile *O, const char *sect, ScopedXarIter xp; if(!xp){ - errs() << "Can't obtain an xar iterator for xar archive " - << XarFilename.c_str() << "\n"; + WithColor::error(errs(), "llvm-objdump") + << "can't obtain an xar iterator for xar archive " + << XarFilename.c_str() << "\n"; return; } member_name = NULL; @@ -6805,7 +6841,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // Package up features to be passed to target/subtarget std::string FeaturesStr; - if (MAttrs.size()) { + if (!MAttrs.empty()) { SubtargetFeatures Features; for (unsigned i = 0; i != MAttrs.size(); ++i) Features.AddFeature(MAttrs[i]); @@ -6848,8 +6884,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, // IP->setCommentStream(CommentStream); if (!AsmInfo || !STI || !DisAsm || !IP) { - errs() << "error: couldn't initialize disassembler for target " - << TripleName << '\n'; + WithColor::error(errs(), "llvm-objdump") + << "couldn't initialize disassembler for target " << TripleName << '\n'; return; } @@ -6890,8 +6926,9 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, } if (ThumbTarget && (!ThumbAsmInfo || !ThumbSTI || !ThumbDisAsm || !ThumbIP)) { - errs() << "error: couldn't initialize disassembler for target " - << ThumbTripleName << '\n'; + WithColor::error(errs(), "llvm-objdump") + << "couldn't initialize disassembler for target " << ThumbTripleName + << '\n'; return; } @@ -6910,7 +6947,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, BaseSegmentAddress); // Sort the symbols by address, just in case they didn't come in that way. - llvm::sort(Symbols.begin(), Symbols.end(), SymbolSorter()); + llvm::sort(Symbols, SymbolSorter()); // Build a data in code table that is sorted on by the address of each entry. uint64_t BaseAddress = 0; @@ -6935,6 +6972,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, std::unique_ptr<DIContext> diContext; ObjectFile *DbgObj = MachOOF; + std::unique_ptr<MemoryBuffer> DSYMBuf; // Try to find debug info and set up the DIContext for it. if (UseDbg) { // A separate DSym file path was specified, parse it as a macho file, @@ -6943,22 +6981,28 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, ErrorOr<std::unique_ptr<MemoryBuffer>> BufOrErr = MemoryBuffer::getFileOrSTDIN(DSYMFile); if (std::error_code EC = BufOrErr.getError()) { - errs() << "llvm-objdump: " << Filename << ": " << EC.message() << '\n'; + report_error(DSYMFile, errorCodeToError(EC)); return; } + Expected<std::unique_ptr<MachOObjectFile>> DbgObjCheck = ObjectFile::createMachOObjectFile(BufOrErr.get()->getMemBufferRef()); - if (DbgObjCheck.takeError()) - report_error(MachOOF->getFileName(), DbgObjCheck.takeError()); + if (Error E = DbgObjCheck.takeError()) { + report_error(DSYMFile, std::move(E)); + return; + } + DbgObj = DbgObjCheck.get().release(); + // We need to keep the file alive, because we're replacing DbgObj with it. + DSYMBuf = std::move(BufOrErr.get()); } // Setup the DIContext diContext = DWARFContext::create(*DbgObj); } - if (FilterSections.size() == 0) + if (FilterSections.empty()) outs() << "(" << DisSegName << "," << DisSectName << ") section\n"; for (unsigned SectIdx = 0; SectIdx != Sections.size(); SectIdx++) { @@ -7021,7 +7065,7 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, unsigned int Arch = MachOOF->getArch(); // Skip all symbols if this is a stubs file. - if (Bytes.size() == 0) + if (Bytes.empty()) return; // If the section has symbols but no symbol at the start of the section @@ -7228,7 +7272,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, outs() << format("\t.short\t0x%04x\n", opcode); Size = 2; } else{ - errs() << "llvm-objdump: warning: invalid instruction encoding\n"; + WithColor::warning(errs(), "llvm-objdump") + << "invalid instruction encoding\n"; if (Size == 0) Size = 1; // skip illegible bytes } @@ -7275,7 +7320,8 @@ static void DisassembleMachO(StringRef Filename, MachOObjectFile *MachOOF, *(Bytes.data() + Index) & 0xff); InstSize = 1; // skip exactly one illegible byte and move on. } else { - errs() << "llvm-objdump: warning: invalid instruction encoding\n"; + WithColor::warning(errs(), "llvm-objdump") + << "invalid instruction encoding\n"; if (InstSize == 0) InstSize = 1; // skip illegible bytes } diff --git a/tools/llvm-objdump/llvm-objdump.cpp b/tools/llvm-objdump/llvm-objdump.cpp index 8041e6f59940..ba8d3c5b8d5c 100644 --- a/tools/llvm-objdump/llvm-objdump.cpp +++ b/tools/llvm-objdump/llvm-objdump.cpp @@ -42,6 +42,7 @@ #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" #include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/Wasm.h" #include "llvm/Support/Casting.h" @@ -55,8 +56,10 @@ #include "llvm/Support/InitLLVM.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Support/StringSaver.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/TargetSelect.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <algorithm> #include <cctype> @@ -91,12 +94,11 @@ 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")); +cl::opt<bool> llvm::Demangle("demangle", cl::desc("Demangle symbols names"), + cl::init(false)); static cl::alias DemangleShort("C", cl::desc("Alias for --demangle"), - cl::aliasopt(Demangle)); + cl::aliasopt(llvm::Demangle)); static cl::list<std::string> DisassembleFunctions("df", @@ -105,7 +107,11 @@ DisassembleFunctions("df", static StringSet<> DisasmFuncsSet; cl::opt<bool> -llvm::Relocations("r", cl::desc("Display the relocation entries in the file")); +llvm::Relocations("reloc", + cl::desc("Display the relocation entries in the file")); +static cl::alias RelocationsShort("r", cl::desc("Alias for --reloc"), + cl::NotHidden, + cl::aliasopt(llvm::Relocations)); cl::opt<bool> llvm::DynamicRelocations("dynamic-reloc", @@ -115,10 +121,16 @@ 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")); + llvm::SectionContents("full-contents", + cl::desc("Display the content of each section")); +static cl::alias SectionContentsShort("s", + cl::desc("Alias for --full-contents"), + cl::aliasopt(SectionContents)); -cl::opt<bool> -llvm::SymbolTable("t", cl::desc("Display the symbol table")); +cl::opt<bool> llvm::SymbolTable("syms", cl::desc("Display the symbol table")); +static cl::alias SymbolTableShort("t", cl::desc("Alias for --syms"), + cl::NotHidden, + cl::aliasopt(llvm::SymbolTable)); cl::opt<bool> llvm::ExportsTrie("exports-trie", cl::desc("Display mach-o exported symbols")); @@ -253,8 +265,17 @@ cl::opt<unsigned long long> StartAddress("start-address", cl::desc("Disassemble beginning at address"), cl::value_desc("address"), cl::init(0)); cl::opt<unsigned long long> - StopAddress("stop-address", cl::desc("Stop disassembly at address"), + StopAddress("stop-address", + cl::desc("Stop disassembly at address"), cl::value_desc("address"), cl::init(UINT64_MAX)); + +cl::opt<bool> DisassembleZeroes( + "disassemble-zeroes", + cl::desc("Do not skip blocks of zeroes when disassembling")); +cl::alias DisassembleZeroesShort("z", + cl::desc("Alias for --disassemble-zeroes"), + cl::aliasopt(DisassembleZeroes)); + static StringRef ToolName; typedef std::vector<std::tuple<uint64_t, StringRef, uint8_t>> SectionSymbolsTy; @@ -326,33 +347,35 @@ SectionFilter ToolSectionFilter(llvm::object::ObjectFile const &O) { void llvm::error(std::error_code EC) { if (!EC) return; - - errs() << ToolName << ": error reading file: " << EC.message() << ".\n"; + WithColor::error(errs(), ToolName) + << "reading file: " << EC.message() << ".\n"; errs().flush(); exit(1); } LLVM_ATTRIBUTE_NORETURN void llvm::error(Twine Message) { - errs() << ToolName << ": " << Message << ".\n"; + WithColor::error(errs(), ToolName) << Message << ".\n"; errs().flush(); exit(1); } void llvm::warn(StringRef Message) { - errs() << ToolName << ": warning: " << Message << ".\n"; + WithColor::warning(errs(), ToolName) << Message << ".\n"; errs().flush(); } LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, Twine Message) { - errs() << ToolName << ": '" << File << "': " << Message << ".\n"; + WithColor::error(errs(), ToolName) + << "'" << File << "': " << Message << ".\n"; exit(1); } LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, std::error_code EC) { assert(EC); - errs() << ToolName << ": '" << File << "': " << EC.message() << ".\n"; + WithColor::error(errs(), ToolName) + << "'" << File << "': " << EC.message() << ".\n"; exit(1); } @@ -361,9 +384,9 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef File, assert(E); std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); - errs() << ToolName << ": '" << File << "': " << Buf; + WithColor::error(errs(), ToolName) << "'" << File << "': " << Buf; exit(1); } @@ -372,7 +395,7 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName, llvm::Error E, StringRef ArchitectureName) { assert(E); - errs() << ToolName << ": "; + WithColor::error(errs(), ToolName); if (ArchiveName != "") errs() << ArchiveName << "(" << FileName << ")"; else @@ -381,7 +404,7 @@ LLVM_ATTRIBUTE_NORETURN void llvm::report_error(StringRef ArchiveName, errs() << " (for architecture " << ArchitectureName << ")"; std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << ": " << Buf; exit(1); @@ -407,18 +430,16 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { // Figure out the target triple. llvm::Triple TheTriple("unknown-unknown-unknown"); if (TripleName.empty()) { - if (Obj) { + if (Obj) TheTriple = Obj->makeTriple(); - } } else { TheTriple.setTriple(Triple::normalize(TripleName)); // Use the triple, but also try to combine with ARM build attributes. if (Obj) { auto Arch = Obj->getArch(); - if (Arch == Triple::arm || Arch == Triple::armeb) { + if (Arch == Triple::arm || Arch == Triple::armeb) Obj->setARMSubArch(TheTriple); - } } } @@ -438,22 +459,35 @@ static const Target *getTarget(const ObjectFile *Obj = nullptr) { return TheTarget; } -bool llvm::RelocAddressLess(RelocationRef a, RelocationRef b) { - return a.getOffset() < b.getOffset(); +bool llvm::isRelocAddressLess(RelocationRef A, RelocationRef B) { + return A.getOffset() < B.getOffset(); +} + +static std::string demangle(StringRef Name) { + char *Demangled = nullptr; + if (Name.startswith("_Z")) + Demangled = itaniumDemangle(Name.data(), Demangled, nullptr, nullptr); + else if (Name.startswith("?")) + Demangled = microsoftDemangle(Name.data(), Demangled, nullptr, nullptr); + + if (!Demangled) + return Name; + + std::string Ret = Demangled; + free(Demangled); + return Ret; } template <class ELFT> static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, const RelocationRef &RelRef, SmallVectorImpl<char> &Result) { - DataRefImpl Rel = RelRef.getRawDataRefImpl(); - typedef typename ELFObjectFile<ELFT>::Elf_Sym Elf_Sym; typedef typename ELFObjectFile<ELFT>::Elf_Shdr Elf_Shdr; typedef typename ELFObjectFile<ELFT>::Elf_Rela Elf_Rela; const ELFFile<ELFT> &EF = *Obj->getELFFile(); - + DataRefImpl Rel = RelRef.getRawDataRefImpl(); auto SecOrErr = EF.getSection(Rel.d.a); if (!SecOrErr) return errorToErrorCode(SecOrErr.takeError()); @@ -471,11 +505,11 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, if (!StrTabOrErr) return errorToErrorCode(StrTabOrErr.takeError()); StringRef StrTab = *StrTabOrErr; - int64_t addend = 0; + 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; + bool Undef = false; switch (Sec->sh_type) { default: return object_error::parse_failed; @@ -485,13 +519,13 @@ 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; + Addend = ERela->r_addend; + Undef = ERela->getSymbol(false) == 0; break; } } - StringRef Target; - if (!undef) { + std::string Target; + if (!Undef) { symbol_iterator SI = RelRef.getSymbol(); const Elf_Sym *symb = Obj->getSymbol(SI->getRawDataRefImpl()); if (symb->getType() == ELF::STT_SECTION) { @@ -507,20 +541,23 @@ static std::error_code getRelocationValueString(const ELFObjectFile<ELFT> *Obj, Expected<StringRef> SymName = symb->getName(StrTab); if (!SymName) return errorToErrorCode(SymName.takeError()); - Target = *SymName; + if (Demangle) + Target = demangle(*SymName); + else + Target = *SymName; } } 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()); + 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(); } @@ -551,18 +588,15 @@ static std::error_code getRelocationValueString(const COFFObjectFile *Obj, static void printRelocationTargetName(const MachOObjectFile *O, const MachO::any_relocation_info &RE, - raw_string_ostream &fmt) { - bool IsScattered = O->isRelocationScattered(RE); - + raw_string_ostream &Fmt) { // Target of a scattered relocation is an address. In the interest of // generating pretty output, scan through the symbol table looking for a // symbol that aligns with that address. If we find one, print it. // Otherwise, we just print the hex address of the target. - if (IsScattered) { + if (O->isRelocationScattered(RE)) { uint32_t Val = O->getPlainRelocationSymbolNum(RE); for (const SymbolRef &Symbol : O->symbols()) { - std::error_code ec; Expected<uint64_t> Addr = Symbol.getAddress(); if (!Addr) report_error(O->getFileName(), Addr.takeError()); @@ -571,7 +605,7 @@ static void printRelocationTargetName(const MachOObjectFile *O, Expected<StringRef> Name = Symbol.getName(); if (!Name) report_error(O->getFileName(), Name.takeError()); - fmt << *Name; + Fmt << *Name; return; } @@ -586,11 +620,11 @@ static void printRelocationTargetName(const MachOObjectFile *O, continue; if ((ec = Section.getName(Name))) report_error(O->getFileName(), ec); - fmt << Name; + Fmt << Name; return; } - fmt << format("0x%x", Val); + Fmt << format("0x%x", Val); return; } @@ -599,9 +633,11 @@ static void printRelocationTargetName(const MachOObjectFile *O, uint64_t Val = O->getPlainRelocationSymbolNum(RE); if (O->getAnyRelocationType(RE) == MachO::ARM64_RELOC_ADDEND) { - fmt << format("0x%0" PRIx64, Val); + Fmt << format("0x%0" PRIx64, Val); return; - } else if (isExtern) { + } + + if (isExtern) { symbol_iterator SI = O->symbol_begin(); advance(SI, Val); Expected<StringRef> SOrErr = SI->getName(); @@ -612,21 +648,21 @@ static void printRelocationTargetName(const MachOObjectFile *O, section_iterator SI = O->section_begin(); // Adjust for the fact that sections are 1-indexed. if (Val == 0) { - fmt << "0 (?,?)"; + Fmt << "0 (?,?)"; return; } - uint32_t i = Val - 1; - while (i != 0 && SI != O->section_end()) { - i--; + uint32_t I = Val - 1; + while (I != 0 && SI != O->section_end()) { + --I; advance(SI, 1); } if (SI == O->section_end()) - fmt << Val << " (?,?)"; + Fmt << Val << " (?,?)"; else SI->getName(S); } - fmt << S; + Fmt << S; } static std::error_code getRelocationValueString(const WasmObjectFile *Obj, @@ -634,12 +670,12 @@ static std::error_code getRelocationValueString(const WasmObjectFile *Obj, SmallVectorImpl<char> &Result) { const wasm::WasmRelocation& Rel = Obj->getWasmRelocation(RelRef); symbol_iterator SI = RelRef.getSymbol(); - std::string fmtbuf; - raw_string_ostream fmt(fmtbuf); + std::string FmtBuf; + raw_string_ostream Fmt(FmtBuf); if (SI == Obj->symbol_end()) { // Not all wasm relocations have symbols associated with them. // In particular R_WEBASSEMBLY_TYPE_INDEX_LEB. - fmt << Rel.Index; + Fmt << Rel.Index; } else { Expected<StringRef> SymNameOrErr = SI->getName(); if (!SymNameOrErr) @@ -647,9 +683,9 @@ static std::error_code getRelocationValueString(const WasmObjectFile *Obj, StringRef SymName = *SymNameOrErr; Result.append(SymName.begin(), SymName.end()); } - fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend; - fmt.flush(); - Result.append(fmtbuf.begin(), fmtbuf.end()); + Fmt << (Rel.Addend < 0 ? "" : "+") << Rel.Addend; + Fmt.flush(); + Result.append(FmtBuf.begin(), FmtBuf.end()); return std::error_code(); } @@ -661,8 +697,8 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, unsigned Arch = Obj->getArch(); - std::string fmtbuf; - raw_string_ostream fmt(fmtbuf); + std::string FmtBuf; + raw_string_ostream Fmt(FmtBuf); unsigned Type = Obj->getAnyRelocationType(RE); bool IsPCRel = Obj->getAnyRelocationPCRel(RE); @@ -671,15 +707,13 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, // X86_64 has entirely custom relocation types. if (Arch == Triple::x86_64) { - bool isPCRel = Obj->getAnyRelocationPCRel(RE); - switch (Type) { case MachO::X86_64_RELOC_GOT_LOAD: case MachO::X86_64_RELOC_GOT: { - printRelocationTargetName(Obj, RE, fmt); - fmt << "@GOT"; - if (isPCRel) - fmt << "PCREL"; + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "@GOT"; + if (IsPCRel) + Fmt << "PCREL"; break; } case MachO::X86_64_RELOC_SUBTRACTOR: { @@ -697,31 +731,31 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, // The X86_64_RELOC_UNSIGNED contains the minuend symbol; // X86_64_RELOC_SUBTRACTOR contains the subtrahend. - printRelocationTargetName(Obj, RENext, fmt); - fmt << "-"; - printRelocationTargetName(Obj, RE, fmt); + printRelocationTargetName(Obj, RENext, Fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RE, Fmt); break; } case MachO::X86_64_RELOC_TLV: - printRelocationTargetName(Obj, RE, fmt); - fmt << "@TLV"; - if (isPCRel) - fmt << "P"; + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "@TLV"; + if (IsPCRel) + Fmt << "P"; break; case MachO::X86_64_RELOC_SIGNED_1: - printRelocationTargetName(Obj, RE, fmt); - fmt << "-1"; + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-1"; break; case MachO::X86_64_RELOC_SIGNED_2: - printRelocationTargetName(Obj, RE, fmt); - fmt << "-2"; + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-2"; break; case MachO::X86_64_RELOC_SIGNED_4: - printRelocationTargetName(Obj, RE, fmt); - fmt << "-4"; + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-4"; break; default: - printRelocationTargetName(Obj, RE, fmt); + printRelocationTargetName(Obj, RE, Fmt); break; } // X86 and ARM share some relocation types in common. @@ -744,9 +778,9 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " "GENERIC_RELOC_SECTDIFF."); - printRelocationTargetName(Obj, RE, fmt); - fmt << "-"; - printRelocationTargetName(Obj, RENext, fmt); + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RENext, Fmt); break; } } @@ -765,20 +799,20 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, report_error(Obj->getFileName(), "Expected GENERIC_RELOC_PAIR after " "GENERIC_RELOC_LOCAL_SECTDIFF."); - printRelocationTargetName(Obj, RE, fmt); - fmt << "-"; - printRelocationTargetName(Obj, RENext, fmt); + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RENext, Fmt); break; } case MachO::GENERIC_RELOC_TLV: { - printRelocationTargetName(Obj, RE, fmt); - fmt << "@TLV"; + printRelocationTargetName(Obj, RE, Fmt); + Fmt << "@TLV"; if (IsPCRel) - fmt << "P"; + Fmt << "P"; break; } default: - printRelocationTargetName(Obj, RE, fmt); + printRelocationTargetName(Obj, RE, Fmt); } } else { // ARM-specific relocations switch (Type) { @@ -789,10 +823,10 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, bool isUpper = (Obj->getAnyRelocationLength(RE) & 0x1) == 1; if (isUpper) - fmt << ":upper16:("; + Fmt << ":upper16:("; else - fmt << ":lower16:("; - printRelocationTargetName(Obj, RE, fmt); + Fmt << ":lower16:("; + printRelocationTargetName(Obj, RE, Fmt); DataRefImpl RelNext = Rel; Obj->moveRelocationNext(RelNext); @@ -813,21 +847,21 @@ static std::error_code getRelocationValueString(const MachOObjectFile *Obj, // ARM_RELOC_HALF_SECTDIFF encodes the second section in the // symbol/section pointer of the follow-on relocation. if (Type == MachO::ARM_RELOC_HALF_SECTDIFF) { - fmt << "-"; - printRelocationTargetName(Obj, RENext, fmt); + Fmt << "-"; + printRelocationTargetName(Obj, RENext, Fmt); } - fmt << ")"; + Fmt << ")"; break; } - default: { printRelocationTargetName(Obj, RE, fmt); } + default: { printRelocationTargetName(Obj, RE, Fmt); } } } } else - printRelocationTargetName(Obj, RE, fmt); + printRelocationTargetName(Obj, RE, Fmt); - fmt.flush(); - Result.append(fmtbuf.begin(), fmtbuf.end()); + Fmt.flush(); + Result.append(FmtBuf.begin(), FmtBuf.end()); return std::error_code(); } @@ -849,8 +883,7 @@ static std::error_code getRelocationValueString(const RelocationRef &Rel, /// 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) { - const ObjectFile *Obj = RelRef.getObject(); - auto *MachO = dyn_cast<MachOObjectFile>(Obj); + auto *MachO = dyn_cast<MachOObjectFile>(RelRef.getObject()); if (!MachO) return false; @@ -860,10 +893,10 @@ static bool getHidden(RelocationRef RelRef) { // On arches that use the generic relocations, GENERIC_RELOC_PAIR // is always hidden. - if (Arch == Triple::x86 || Arch == Triple::arm || Arch == Triple::ppc) { - if (Type == MachO::GENERIC_RELOC_PAIR) - return true; - } else if (Arch == Triple::x86_64) { + if (Arch == Triple::x86 || Arch == Triple::arm || Arch == Triple::ppc) + return Type == MachO::GENERIC_RELOC_PAIR; + + if (Arch == Triple::x86_64) { // On x86_64, X86_64_RELOC_UNSIGNED is hidden only when it follows // an X86_64_RELOC_SUBTRACTOR. if (Type == MachO::X86_64_RELOC_UNSIGNED && Rel.d.a > 0) { @@ -1038,27 +1071,27 @@ public: 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(); + std::vector<RelocationRef>::const_iterator RelCur = Rels->begin(); + std::vector<RelocationRef>::const_iterator RelEnd = 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 + while ((RelCur != RelEnd) && (RelCur->getOffset() <= Address)) { + if (RelCur->getOffset() == Address) { + SmallString<16> Name; + SmallString<32> Val; + RelCur->getTypeName(Name); + error(getRelocationValueString(*RelCur, Val)); + OS << Separator << format(Fmt.data(), Address) << Name << "\t" << Val << "\n"; return; } - rel_cur++; + ++RelCur; } }; - while(!HeadTail.first.empty()) { + while (!HeadTail.first.empty()) { OS << Separator; Separator = "\n"; if (SP && (PrintSource || PrintLines)) @@ -1068,7 +1101,7 @@ public: Preamble = " "; StringRef Inst; auto Duplex = HeadTail.first.split('\v'); - if(!Duplex.second.empty()){ + if (!Duplex.second.empty()) { OS << Duplex.first; OS << "; "; Inst = Duplex.second; @@ -1200,7 +1233,6 @@ addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj, Expected<uint64_t> AddressOrErr = Symbol.getAddress(); if (!AddressOrErr) report_error(Obj->getFileName(), AddressOrErr.takeError()); - uint64_t Address = *AddressOrErr; Expected<StringRef> Name = Symbol.getName(); if (!Name) @@ -1215,7 +1247,7 @@ addDynamicElfSymbols(const ELFObjectFile<ELFT> *Obj, if (SecI == Obj->section_end()) continue; - AllSymbols[*SecI].emplace_back(Address, *Name, SymbolType); + AllSymbols[*SecI].emplace_back(*AddressOrErr, *Name, SymbolType); } } @@ -1235,7 +1267,60 @@ addDynamicElfSymbols(const ObjectFile *Obj, llvm_unreachable("Unsupported binary format"); } -static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { +static void addPltEntries(const ObjectFile *Obj, + std::map<SectionRef, SectionSymbolsTy> &AllSymbols, + StringSaver &Saver) { + Optional<SectionRef> Plt = None; + for (const SectionRef &Section : Obj->sections()) { + StringRef Name; + if (Section.getName(Name)) + continue; + if (Name == ".plt") + Plt = Section; + } + if (!Plt) + return; + if (auto *ElfObj = dyn_cast<ELFObjectFileBase>(Obj)) { + for (auto PltEntry : ElfObj->getPltAddresses()) { + SymbolRef Symbol(PltEntry.first, ElfObj); + uint8_t SymbolType = getElfSymbolType(Obj, Symbol); + + Expected<StringRef> NameOrErr = Symbol.getName(); + if (!NameOrErr) + report_error(Obj->getFileName(), NameOrErr.takeError()); + if (NameOrErr->empty()) + continue; + StringRef Name = Saver.save((*NameOrErr + "@plt").str()); + + AllSymbols[*Plt].emplace_back(PltEntry.second, Name, SymbolType); + } + } +} + +// Normally the disassembly output will skip blocks of zeroes. This function +// returns the number of zero bytes that can be skipped when dumping the +// disassembly of the instructions in Buf. +static size_t countSkippableZeroBytes(ArrayRef<uint8_t> Buf) { + // When -z or --disassemble-zeroes are given we always dissasemble them. + if (DisassembleZeroes) + return 0; + + // Find the number of leading zeroes. + size_t N = 0; + while (N < Buf.size() && !Buf[N]) + ++N; + + // We may want to skip blocks of zero bytes, but unless we see + // at least 8 of them in a row. + if (N < 8) + return 0; + + // We skip zeroes in multiples of 4 because do not want to truncate an + // instruction if it starts with a zero byte. + return N & ~0x3; +} + +static void disassembleObject(const ObjectFile *Obj, bool InlineRelocs) { if (StartAddress > StopAddress) error("Start address should be less than stop address"); @@ -1243,10 +1328,9 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // Package up features to be passed to target/subtarget SubtargetFeatures Features = Obj->getFeatures(); - if (MAttrs.size()) { - for (unsigned i = 0; i != MAttrs.size(); ++i) - Features.AddFeature(MAttrs[i]); - } + if (!MAttrs.empty()) + for (unsigned I = 0; I != MAttrs.size(); ++I) + Features.AddFeature(MAttrs[I]); std::unique_ptr<const MCRegisterInfo> MRI( TheTarget->createMCRegInfo(TripleName)); @@ -1342,6 +1426,10 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { if (AllSymbols.empty() && Obj->isELF()) addDynamicElfSymbols(Obj, AllSymbols); + BumpPtrAllocator A; + StringSaver Saver(A); + addPltEntries(Obj, AllSymbols, Saver); + // Create a mapping from virtual address to section. std::vector<std::pair<uint64_t, SectionRef>> SectionAddresses; for (SectionRef Sec : Obj->sections()) @@ -1411,8 +1499,8 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } } - llvm::sort(DataMappingSymsAddr.begin(), DataMappingSymsAddr.end()); - llvm::sort(TextMappingSymsAddr.begin(), TextMappingSymsAddr.end()); + llvm::sort(DataMappingSymsAddr); + llvm::sort(TextMappingSymsAddr); if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { // AMDGPU disassembler uses symbolizer for printing labels @@ -1437,7 +1525,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } // Sort relocations by address. - llvm::sort(Rels.begin(), Rels.end(), RelocAddressLess); + llvm::sort(Rels, isRelocAddressLess); StringRef SegmentName = ""; if (const MachOObjectFile *MachO = dyn_cast<const MachOObjectFile>(Obj)) { @@ -1467,15 +1555,16 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { uint64_t Index; bool PrintedSection = false; - std::vector<RelocationRef>::const_iterator rel_cur = Rels.begin(); - std::vector<RelocationRef>::const_iterator rel_end = Rels.end(); + std::vector<RelocationRef>::const_iterator RelCur = Rels.begin(); + std::vector<RelocationRef>::const_iterator RelEnd = Rels.end(); // Disassemble symbol by symbol. - for (unsigned si = 0, se = Symbols.size(); si != se; ++si) { - uint64_t Start = std::get<0>(Symbols[si]) - SectionAddr; + for (unsigned SI = 0, SE = Symbols.size(); SI != SE; ++SI) { + uint64_t Start = std::get<0>(Symbols[SI]) - SectionAddr; // The end is either the section end or the beginning of the next // symbol. - uint64_t End = - (si == se - 1) ? SectSize : std::get<0>(Symbols[si + 1]) - SectionAddr; + uint64_t End = (SI == SE - 1) + ? SectSize + : std::get<0>(Symbols[SI + 1]) - SectionAddr; // Don't try to disassemble beyond the end of section contents. if (End > SectSize) End = SectSize; @@ -1492,7 +1581,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { /// Skip if user requested specific symbols and this is not in the list if (!DisasmFuncsSet.empty() && - !DisasmFuncsSet.count(std::get<1>(Symbols[si]))) + !DisasmFuncsSet.count(std::get<1>(Symbols[SI]))) continue; if (!PrintedSection) { @@ -1508,12 +1597,12 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { End = StopAddress - SectionAddr; if (Obj->isELF() && Obj->getArch() == Triple::amdgcn) { - if (std::get<2>(Symbols[si]) == ELF::STT_AMDGPU_HSA_KERNEL) { + 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; } - if (si == se - 1 || - std::get<2>(Symbols[si + 1]) == ELF::STT_AMDGPU_HSA_KERNEL) { + if (SI == SE - 1 || + std::get<2>(Symbols[SI + 1]) == ELF::STT_AMDGPU_HSA_KERNEL) { // cut trailing zeroes at the end of kernel // cut up to 256 bytes const uint64_t EndAlign = 256; @@ -1524,25 +1613,15 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { } } - 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); + outs() << '\n'; + if (!NoLeadingAddr) + outs() << format("%016" PRIx64 " ", SectionAddr + Start); - if (Size != 0) - free(DemangledSymbol); - } else - PrintSymbol(SymbolName); + StringRef SymbolName = std::get<1>(Symbols[SI]); + if (Demangle) + outs() << demangle(SymbolName) << ":\n"; + else + outs() << SymbolName << ":\n"; // Don't print raw contents of a virtual section. A virtual section // doesn't have any contents in the file. @@ -1570,7 +1649,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // same section. We rely on the markers introduced to // understand what we need to dump. If the data marker is within a // function, it is denoted as a word/short etc - if (isArmElf(Obj) && std::get<2>(Symbols[si]) != ELF::STT_OBJECT && + if (isArmElf(Obj) && std::get<2>(Symbols[SI]) != ELF::STT_OBJECT && !DisassembleAll) { uint64_t Stride = 0; @@ -1634,7 +1713,7 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // disassembling text (applicable all architectures), // we are in a situation where we must print the data and not // disassemble it. - if (Obj->isELF() && std::get<2>(Symbols[si]) == ELF::STT_OBJECT && + if (Obj->isELF() && std::get<2>(Symbols[SI]) == ELF::STT_OBJECT && !DisassembleAll && Section.isText()) { // print out data up to 8 bytes at a time in hex and ascii uint8_t AsciiData[9] = {'\0'}; @@ -1675,6 +1754,14 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { if (Index >= End) break; + if (size_t N = + countSkippableZeroBytes(Bytes.slice(Index, End - Index))) { + outs() << "\t\t..." << '\n'; + Index += N; + if (Index >= End) + break; + } + // Disassemble a real instruction or a data when disassemble all is // provided bool Disassembled = DisAsm->getInstruction(Inst, Size, Bytes.slice(Index), @@ -1753,32 +1840,32 @@ static void DisassembleObject(const ObjectFile *Obj, bool InlineRelocs) { // 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; + while (RelCur != RelEnd) { + uint64_t Addr = RelCur->getOffset(); + SmallString<16> Name; + SmallString<32> Val; // If this relocation is hidden, skip it. - if (hidden || ((SectionAddr + addr) < StartAddress)) { - ++rel_cur; + if (getHidden(*RelCur) || ((SectionAddr + Addr) < StartAddress)) { + ++RelCur; 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; + if (Addr >= Index + Size) + break; + RelCur->getTypeName(Name); + error(getRelocationValueString(*RelCur, Val)); + outs() << format(Fmt.data(), SectionAddr + Addr) << Name << "\t" + << Val << "\n"; + ++RelCur; } } } } } -void llvm::PrintRelocations(const ObjectFile *Obj) { +void llvm::printRelocations(const ObjectFile *Obj) { StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; // Regular objdump doesn't print relocations in non-relocatable object @@ -1789,61 +1876,57 @@ void llvm::PrintRelocations(const ObjectFile *Obj) { for (const SectionRef &Section : ToolSectionFilter(*Obj)) { if (Section.relocation_begin() == Section.relocation_end()) continue; - StringRef secname; - error(Section.getName(secname)); - outs() << "RELOCATION RECORDS FOR [" << secname << "]:\n"; + StringRef SecName; + error(Section.getName(SecName)); + outs() << "RELOCATION RECORDS FOR [" << SecName << "]:\n"; for (const RelocationRef &Reloc : Section.relocations()) { - bool hidden = getHidden(Reloc); - uint64_t address = Reloc.getOffset(); - SmallString<32> relocname; - SmallString<32> valuestr; - if (address < StartAddress || address > StopAddress || hidden) + uint64_t Address = Reloc.getOffset(); + SmallString<32> RelocName; + SmallString<32> ValueStr; + if (Address < StartAddress || Address > StopAddress || getHidden(Reloc)) continue; - Reloc.getTypeName(relocname); - error(getRelocationValueString(Reloc, valuestr)); - outs() << format(Fmt.data(), address) << " " << relocname << " " - << valuestr << "\n"; + Reloc.getTypeName(RelocName); + error(getRelocationValueString(Reloc, ValueStr)); + outs() << format(Fmt.data(), Address) << " " << RelocName << " " + << ValueStr << "\n"; } outs() << "\n"; } } -void llvm::PrintDynamicRelocations(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"; + StringRef Fmt = Obj->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; 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"; + 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) { +void llvm::printSectionHeaders(const ObjectFile *Obj) { outs() << "Sections:\n" "Idx Name Size Address Type\n"; for (const SectionRef &Section : ToolSectionFilter(*Obj)) { @@ -1860,9 +1943,10 @@ void llvm::PrintSectionHeaders(const ObjectFile *Obj) { (unsigned)Section.getIndex(), Name.str().c_str(), Size, Address, Type.c_str()); } + outs() << "\n"; } -void llvm::PrintSectionContents(const ObjectFile *Obj) { +void llvm::printSectionContents(const ObjectFile *Obj) { std::error_code EC; for (const SectionRef &Section : ToolSectionFilter(*Obj)) { StringRef Name; @@ -1884,23 +1968,23 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) { error(Section.getContents(Contents)); // Dump out the content as hex and printable ascii characters. - for (std::size_t addr = 0, end = Contents.size(); addr < end; addr += 16) { - outs() << format(" %04" PRIx64 " ", BaseAddr + addr); + for (std::size_t Addr = 0, End = Contents.size(); Addr < End; Addr += 16) { + outs() << format(" %04" PRIx64 " ", BaseAddr + Addr); // Dump line of hex. - for (std::size_t i = 0; i < 16; ++i) { - if (i != 0 && i % 4 == 0) + for (std::size_t I = 0; I < 16; ++I) { + if (I != 0 && I % 4 == 0) outs() << ' '; - if (addr + i < end) - outs() << hexdigit((Contents[addr + i] >> 4) & 0xF, true) - << hexdigit(Contents[addr + i] & 0xF, true); + if (Addr + I < End) + outs() << hexdigit((Contents[Addr + I] >> 4) & 0xF, true) + << hexdigit(Contents[Addr + I] & 0xF, true); else outs() << " "; } // Print ascii. outs() << " "; - for (std::size_t i = 0; i < 16 && addr + i < end; ++i) { - if (isPrint(static_cast<unsigned char>(Contents[addr + i]) & 0xFF)) - outs() << Contents[addr + i]; + for (std::size_t I = 0; I < 16 && Addr + I < End; ++I) { + if (isPrint(static_cast<unsigned char>(Contents[Addr + I]) & 0xFF)) + outs() << Contents[Addr + I]; else outs() << "."; } @@ -1909,40 +1993,47 @@ void llvm::PrintSectionContents(const ObjectFile *Obj) { } } -void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName, +void llvm::printSymbolTable(const ObjectFile *O, StringRef ArchiveName, StringRef ArchitectureName) { outs() << "SYMBOL TABLE:\n"; - if (const COFFObjectFile *coff = dyn_cast<const COFFObjectFile>(o)) { - printCOFFSymbolTable(coff); + if (const COFFObjectFile *Coff = dyn_cast<const COFFObjectFile>(O)) { + printCOFFSymbolTable(Coff); return; } - for (const SymbolRef &Symbol : o->symbols()) { + + for (auto I = O->symbol_begin(), E = O->symbol_end(); I != E; ++I) { + // Skip printing the special zero symbol when dumping an ELF file. + // This makes the output consistent with the GNU objdump. + if (I == O->symbol_begin() && isa<ELFObjectFileBase>(O)) + continue; + + const SymbolRef &Symbol = *I; Expected<uint64_t> AddressOrError = Symbol.getAddress(); if (!AddressOrError) - report_error(ArchiveName, o->getFileName(), AddressOrError.takeError(), + report_error(ArchiveName, O->getFileName(), AddressOrError.takeError(), ArchitectureName); uint64_t Address = *AddressOrError; if ((Address < StartAddress) || (Address > StopAddress)) continue; Expected<SymbolRef::Type> TypeOrError = Symbol.getType(); if (!TypeOrError) - report_error(ArchiveName, o->getFileName(), TypeOrError.takeError(), + report_error(ArchiveName, O->getFileName(), TypeOrError.takeError(), ArchitectureName); SymbolRef::Type Type = *TypeOrError; uint32_t Flags = Symbol.getFlags(); Expected<section_iterator> SectionOrErr = Symbol.getSection(); if (!SectionOrErr) - report_error(ArchiveName, o->getFileName(), SectionOrErr.takeError(), + report_error(ArchiveName, O->getFileName(), SectionOrErr.takeError(), ArchitectureName); section_iterator Section = *SectionOrErr; StringRef Name; - if (Type == SymbolRef::ST_Debug && Section != o->section_end()) { + if (Type == SymbolRef::ST_Debug && Section != O->section_end()) { Section->getName(Name); } else { Expected<StringRef> NameOrErr = Symbol.getName(); if (!NameOrErr) - report_error(ArchiveName, o->getFileName(), NameOrErr.takeError(), + report_error(ArchiveName, O->getFileName(), NameOrErr.takeError(), ArchitectureName); Name = *NameOrErr; } @@ -1963,8 +2054,10 @@ void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName, FileFunc = 'f'; else if (Type == SymbolRef::ST_Function) FileFunc = 'F'; + else if (Type == SymbolRef::ST_Data) + FileFunc = 'O'; - const char *Fmt = o->getBytesInAddress() > 4 ? "%016" PRIx64 : + const char *Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; outs() << format(Fmt, Address) << " " @@ -1980,11 +2073,11 @@ void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName, outs() << "*ABS*"; } else if (Common) { outs() << "*COM*"; - } else if (Section == o->section_end()) { + } else if (Section == O->section_end()) { outs() << "*UND*"; } else { if (const MachOObjectFile *MachO = - dyn_cast<const MachOObjectFile>(o)) { + dyn_cast<const MachOObjectFile>(O)) { DataRefImpl DR = Section->getRawDataRefImpl(); StringRef SegmentName = MachO->getSectionFinalSegmentName(DR); outs() << SegmentName << ","; @@ -1995,98 +2088,95 @@ void llvm::PrintSymbolTable(const ObjectFile *o, StringRef ArchiveName, } outs() << '\t'; - if (Common || isa<ELFObjectFileBase>(o)) { + if (Common || isa<ELFObjectFileBase>(O)) { uint64_t Val = Common ? Symbol.getAlignment() : ELFSymbolRef(Symbol).getSize(); outs() << format("\t %08" PRIx64 " ", Val); } - if (Hidden) { + if (Hidden) outs() << ".hidden "; - } - outs() << Name - << '\n'; + + if (Demangle) + outs() << demangle(Name) << '\n'; + else + outs() << Name << '\n'; } } -static void PrintUnwindInfo(const ObjectFile *o) { +static void printUnwindInfo(const ObjectFile *O) { outs() << "Unwind info:\n\n"; - if (const COFFObjectFile *coff = dyn_cast<COFFObjectFile>(o)) { - printCOFFUnwindInfo(coff); - } else if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) + if (const COFFObjectFile *Coff = dyn_cast<COFFObjectFile>(O)) + printCOFFUnwindInfo(Coff); + else if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(O)) printMachOUnwindInfo(MachO); - else { + else // TODO: Extract DWARF dump tool to objdump. - errs() << "This operation is only currently supported " - "for COFF and MachO object files.\n"; - return; - } + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for COFF and MachO object files.\n"; } void llvm::printExportsTrie(const ObjectFile *o) { outs() << "Exports trie:\n"; if (const MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOExportsTrie(MachO); - else { - errs() << "This operation is only currently supported " - "for Mach-O executable files.\n"; - return; - } + else + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for Mach-O executable files.\n"; } void llvm::printRebaseTable(ObjectFile *o) { outs() << "Rebase table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachORebaseTable(MachO); - else { - errs() << "This operation is only currently supported " - "for Mach-O executable files.\n"; - return; - } + else + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for Mach-O executable files.\n"; } void llvm::printBindTable(ObjectFile *o) { outs() << "Bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOBindTable(MachO); - else { - errs() << "This operation is only currently supported " - "for Mach-O executable files.\n"; - return; - } + else + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for Mach-O executable files.\n"; } void llvm::printLazyBindTable(ObjectFile *o) { outs() << "Lazy bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOLazyBindTable(MachO); - else { - errs() << "This operation is only currently supported " - "for Mach-O executable files.\n"; - return; - } + else + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for Mach-O executable files.\n"; } void llvm::printWeakBindTable(ObjectFile *o) { outs() << "Weak bind table:\n"; if (MachOObjectFile *MachO = dyn_cast<MachOObjectFile>(o)) printMachOWeakBindTable(MachO); - else { - errs() << "This operation is only currently supported " - "for Mach-O executable files.\n"; - return; - } + else + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for Mach-O executable files.\n"; } /// Dump the raw contents of the __clangast section so the output can be piped /// into llvm-bcanalyzer. void llvm::printRawClangAST(const ObjectFile *Obj) { if (outs().is_displayed()) { - errs() << "The -raw-clang-ast option will dump the raw binary contents of " - "the clang ast section.\n" - "Please redirect the output to a file or another program such as " - "llvm-bcanalyzer.\n"; + WithColor::error(errs(), ToolName) + << "The -raw-clang-ast option will dump the raw binary contents of " + "the clang ast section.\n" + "Please redirect the output to a file or another program such as " + "llvm-bcanalyzer.\n"; return; } @@ -2113,15 +2203,16 @@ void llvm::printRawClangAST(const ObjectFile *Obj) { } static void printFaultMaps(const ObjectFile *Obj) { - const char *FaultMapSectionName = nullptr; + StringRef FaultMapSectionName; if (isa<ELFObjectFileBase>(Obj)) { FaultMapSectionName = ".llvm_faultmaps"; } else if (isa<MachOObjectFile>(Obj)) { FaultMapSectionName = "__llvm_faultmaps"; } else { - errs() << "This operation is only currently supported " - "for ELF and Mach-O executable files.\n"; + WithColor::error(errs(), ToolName) + << "This operation is only currently supported " + "for ELF and Mach-O executable files.\n"; return; } @@ -2152,42 +2243,44 @@ static void printFaultMaps(const ObjectFile *Obj) { outs() << FMP; } -static void printPrivateFileHeaders(const ObjectFile *o, bool onlyFirst) { - if (o->isELF()) { - printELFFileHeader(o); - return printELFDynamicSection(o); +static void printPrivateFileHeaders(const ObjectFile *O, bool OnlyFirst) { + if (O->isELF()) { + printELFFileHeader(O); + return printELFDynamicSection(O); } - if (o->isCOFF()) - return printCOFFFileHeader(o); - if (o->isWasm()) - return printWasmFileHeader(o); - if (o->isMachO()) { - printMachOFileHeader(o); - if (!onlyFirst) - printMachOLoadCommands(o); + if (O->isCOFF()) + return printCOFFFileHeader(O); + if (O->isWasm()) + return printWasmFileHeader(O); + if (O->isMachO()) { + printMachOFileHeader(O); + if (!OnlyFirst) + printMachOLoadCommands(O); return; } - report_error(o->getFileName(), "Invalid/Unsupported object file format"); + report_error(O->getFileName(), "Invalid/Unsupported object file format"); } -static void printFileHeaders(const ObjectFile *o) { - if (!o->isELF() && !o->isCOFF()) - report_error(o->getFileName(), "Invalid/Unsupported object file format"); +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(); + Triple::ArchType AT = O->getArch(); outs() << "architecture: " << Triple::getArchTypeName(AT) << "\n"; - Expected<uint64_t> StartAddrOrErr = o->getStartAddress(); + Expected<uint64_t> StartAddrOrErr = O->getStartAddress(); if (!StartAddrOrErr) - report_error(o->getFileName(), StartAddrOrErr.takeError()); + report_error(O->getFileName(), StartAddrOrErr.takeError()); + + StringRef Fmt = O->getBytesInAddress() > 4 ? "%016" PRIx64 : "%08" PRIx64; + uint64_t Address = StartAddrOrErr.get(); outs() << "start address: " - << format("0x%0*x", o->getBytesInAddress(), StartAddrOrErr.get()) - << "\n"; + << "0x" << format(Fmt.data(), Address) << "\n\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"; + WithColor::error(errs(), ToolName) << "ill-formed archive entry.\n"; consumeError(ModeOrErr.takeError()); return; } @@ -2248,55 +2341,55 @@ static void printArchiveChild(StringRef Filename, const Archive::Child &C) { outs() << Name << "\n"; } -static void DumpObject(ObjectFile *o, const Archive *a = nullptr, - const Archive::Child *c = nullptr) { - StringRef ArchiveName = a != nullptr ? a->getFileName() : ""; +static void dumpObject(ObjectFile *O, const Archive *A = nullptr, + const Archive::Child *C = nullptr) { // Avoid other output when using a raw option. if (!RawClangAST) { outs() << '\n'; - if (a) - outs() << a->getFileName() << "(" << o->getFileName() << ")"; + if (A) + outs() << A->getFileName() << "(" << O->getFileName() << ")"; else - outs() << o->getFileName(); - outs() << ":\tfile format " << o->getFileFormatName() << "\n\n"; + outs() << O->getFileName(); + outs() << ":\tfile format " << O->getFileFormatName() << "\n\n"; } - if (ArchiveHeaders && !MachOOpt) - printArchiveChild(a->getFileName(), *c); + StringRef ArchiveName = A ? A->getFileName() : ""; + if (FileHeaders) + printFileHeaders(O); + if (ArchiveHeaders && !MachOOpt && C) + printArchiveChild(ArchiveName, *C); if (Disassemble) - DisassembleObject(o, Relocations); + disassembleObject(O, Relocations); if (Relocations && !Disassemble) - PrintRelocations(o); + printRelocations(O); if (DynamicRelocations) - PrintDynamicRelocations(o); + printDynamicRelocations(O); if (SectionHeaders) - PrintSectionHeaders(o); + printSectionHeaders(O); if (SectionContents) - PrintSectionContents(o); + printSectionContents(O); if (SymbolTable) - PrintSymbolTable(o, ArchiveName); + printSymbolTable(O, ArchiveName); if (UnwindInfo) - PrintUnwindInfo(o); + printUnwindInfo(O); if (PrivateHeaders || FirstPrivateHeader) - printPrivateFileHeaders(o, FirstPrivateHeader); - if (FileHeaders) - printFileHeaders(o); + printPrivateFileHeaders(O, FirstPrivateHeader); if (ExportsTrie) - printExportsTrie(o); + printExportsTrie(O); if (Rebase) - printRebaseTable(o); + printRebaseTable(O); if (Bind) - printBindTable(o); + printBindTable(O); if (LazyBind) - printLazyBindTable(o); + printLazyBindTable(O); if (WeakBind) - printWeakBindTable(o); + printWeakBindTable(O); if (RawClangAST) - printRawClangAST(o); + printRawClangAST(O); if (PrintFaultMaps) - printFaultMaps(o); + printFaultMaps(O); if (DwarfDumpType != DIDT_Null) { - std::unique_ptr<DIContext> DICtx = DWARFContext::create(*o); + std::unique_ptr<DIContext> DICtx = DWARFContext::create(*O); // Dump the complete DWARF structure. DIDumpOptions DumpOpts; DumpOpts.DumpType = DwarfDumpType; @@ -2304,7 +2397,7 @@ 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() : ""; @@ -2315,41 +2408,40 @@ static void DumpObject(const COFFImportFile *I, const Archive *A, << ":\tfile format COFF-import-file" << "\n\n"; - if (ArchiveHeaders && !MachOOpt) - printArchiveChild(A->getFileName(), *C); + if (ArchiveHeaders && !MachOOpt && C) + printArchiveChild(ArchiveName, *C); if (SymbolTable) printCOFFSymbolTable(I); } /// Dump each object file in \a a; -static void DumpArchive(const Archive *a) { +static void dumpArchive(const Archive *A) { Error Err = Error::success(); - for (auto &C : a->children(Err)) { + for (auto &C : A->children(Err)) { Expected<std::unique_ptr<Binary>> ChildOrErr = C.getAsBinary(); if (!ChildOrErr) { if (auto E = isNotObjectErrorInvalidFileType(ChildOrErr.takeError())) - report_error(a->getFileName(), C, std::move(E)); + report_error(A->getFileName(), C, std::move(E)); continue; } - if (ObjectFile *o = dyn_cast<ObjectFile>(&*ChildOrErr.get())) - DumpObject(o, a, &C); + if (ObjectFile *O = dyn_cast<ObjectFile>(&*ChildOrErr.get())) + dumpObject(O, A, &C); else if (COFFImportFile *I = dyn_cast<COFFImportFile>(&*ChildOrErr.get())) - DumpObject(I, a, &C); + dumpObject(I, A, &C); else - report_error(a->getFileName(), object_error::invalid_file_type); + report_error(A->getFileName(), object_error::invalid_file_type); } if (Err) - report_error(a->getFileName(), std::move(Err)); + report_error(A->getFileName(), std::move(Err)); } /// Open file and figure out how to dump it. -static void DumpInput(StringRef file) { - +static void dumpInput(StringRef file) { // If we are using the Mach-O specific object file parser, then let it parse // the file and process the command line options. So the -arch flags can // be used to select specific slices, etc. if (MachOOpt) { - ParseInputMachO(file); + parseInputMachO(file); return; } @@ -2359,10 +2451,12 @@ static void DumpInput(StringRef file) { report_error(file, BinaryOrErr.takeError()); Binary &Binary = *BinaryOrErr.get().getBinary(); - if (Archive *a = dyn_cast<Archive>(&Binary)) - DumpArchive(a); - else if (ObjectFile *o = dyn_cast<ObjectFile>(&Binary)) - DumpObject(o); + if (Archive *A = dyn_cast<Archive>(&Binary)) + dumpArchive(A); + else if (ObjectFile *O = dyn_cast<ObjectFile>(&Binary)) + dumpObject(O); + else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Binary)) + parseInputMachO(UB); else report_error(file, object_error::invalid_file_type); } @@ -2379,24 +2473,20 @@ int main(int argc, char **argv) { cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); cl::ParseCommandLineOptions(argc, argv, "llvm object file dumper\n"); - TripleName = Triple::normalize(TripleName); ToolName = argv[0]; // Defaults to a.out if no filenames specified. - if (InputFilenames.size() == 0) + if (InputFilenames.empty()) InputFilenames.push_back("a.out"); if (AllHeaders) - PrivateHeaders = Relocations = SectionHeaders = SymbolTable = true; + FileHeaders = 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 @@ -2422,7 +2512,7 @@ int main(int argc, char **argv) { && !(DylibsUsed && MachOOpt) && !(DylibId && MachOOpt) && !(ObjcMetaData && MachOOpt) - && !(FilterSections.size() != 0 && MachOOpt) + && !(!FilterSections.empty() && MachOOpt) && !PrintFaultMaps && DwarfDumpType == DIDT_Null) { cl::PrintHelpMessage(); @@ -2432,7 +2522,7 @@ int main(int argc, char **argv) { DisasmFuncsSet.insert(DisassembleFunctions.begin(), DisassembleFunctions.end()); - llvm::for_each(InputFilenames, DumpInput); + 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 b2eb6e9d7771..fe2cb05fe227 100644 --- a/tools/llvm-objdump/llvm-objdump.h +++ b/tools/llvm-objdump/llvm-objdump.h @@ -22,6 +22,7 @@ namespace object { class COFFObjectFile; class COFFImportFile; class MachOObjectFile; + class MachOUniversalBinary; class ObjectFile; class Archive; class RelocationRef; @@ -30,10 +31,10 @@ 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> Demangle; extern cl::opt<bool> Disassemble; extern cl::opt<bool> DisassembleAll; extern cl::opt<bool> NoShowRawInsn; @@ -69,34 +70,35 @@ extern cl::opt<DIDumpType> DwarfDumpType; // Various helper functions. void error(std::error_code ec); -bool RelocAddressLess(object::RelocationRef a, object::RelocationRef b); -void ParseInputMachO(StringRef Filename); -void printCOFFUnwindInfo(const object::COFFObjectFile* o); -void printMachOUnwindInfo(const object::MachOObjectFile* o); -void printMachOExportsTrie(const object::MachOObjectFile* o); -void printMachORebaseTable(object::MachOObjectFile* o); -void printMachOBindTable(object::MachOObjectFile* o); -void printMachOLazyBindTable(object::MachOObjectFile* o); -void printMachOWeakBindTable(object::MachOObjectFile* o); -void printELFFileHeader(const object::ObjectFile *o); +bool isRelocAddressLess(object::RelocationRef A, object::RelocationRef B); +void parseInputMachO(StringRef Filename); +void parseInputMachO(object::MachOUniversalBinary *UB); +void printCOFFUnwindInfo(const object::COFFObjectFile *O); +void printMachOUnwindInfo(const object::MachOObjectFile *O); +void printMachOExportsTrie(const object::MachOObjectFile *O); +void printMachORebaseTable(object::MachOObjectFile *O); +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); -void printMachOFileHeader(const object::ObjectFile *o); -void printMachOLoadCommands(const object::ObjectFile *o); -void printWasmFileHeader(const object::ObjectFile *o); -void printExportsTrie(const object::ObjectFile *o); -void printRebaseTable(object::ObjectFile *o); -void printBindTable(object::ObjectFile *o); -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, +void printCOFFFileHeader(const object::ObjectFile *O); +void printCOFFSymbolTable(const object::COFFImportFile *I); +void printCOFFSymbolTable(const object::COFFObjectFile *O); +void printMachOFileHeader(const object::ObjectFile *O); +void printMachOLoadCommands(const object::ObjectFile *O); +void printWasmFileHeader(const object::ObjectFile *O); +void printExportsTrie(const object::ObjectFile *O); +void printRebaseTable(object::ObjectFile *O); +void printBindTable(object::ObjectFile *O); +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); diff --git a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp index 98d5428ddd1a..57e75b1db9ec 100644 --- a/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp +++ b/tools/llvm-opt-fuzzer/llvm-opt-fuzzer.cpp @@ -144,9 +144,10 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { PB.registerLoopAnalyses(LAM); PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); - bool Ok = PB.parsePassPipeline(MPM, PassPipeline, false, false); - assert(Ok && "Should have been checked during fuzzer initialization"); - (void)Ok; // silence unused variable warning on release builds + auto Err = PB.parsePassPipeline(MPM, PassPipeline, false, false); + assert(!Err && "Should have been checked during fuzzer initialization"); + // Only fail with assert above, otherwise ignore the parsing error. + consumeError(std::move(Err)); // Run passes which we need to test // @@ -235,8 +236,8 @@ extern "C" LLVM_ATTRIBUTE_USED int LLVMFuzzerInitialize( PassBuilder PB(TM.get()); ModulePassManager MPM; - if (!PB.parsePassPipeline(MPM, PassPipeline, false, false)) { - errs() << *argv[0] << ": can't parse pass pipeline\n"; + if (auto Err = PB.parsePassPipeline(MPM, PassPipeline, false, false)) { + errs() << *argv[0] << ": " << toString(std::move(Err)) << "\n"; exit(1); } diff --git a/tools/llvm-opt-report/CMakeLists.txt b/tools/llvm-opt-report/CMakeLists.txt index 777537a54c0f..3aabc03ab3f2 100644 --- a/tools/llvm-opt-report/CMakeLists.txt +++ b/tools/llvm-opt-report/CMakeLists.txt @@ -1,4 +1,4 @@ -set(LLVM_LINK_COMPONENTS Core Demangle Object Support) +set(LLVM_LINK_COMPONENTS Core Demangle Object OptRemarks Support) add_llvm_tool(llvm-opt-report OptReport.cpp diff --git a/tools/llvm-opt-report/OptReport.cpp b/tools/llvm-opt-report/OptReport.cpp index aa7966132c28..0c4bc94d8e44 100644 --- a/tools/llvm-opt-report/OptReport.cpp +++ b/tools/llvm-opt-report/OptReport.cpp @@ -28,6 +28,7 @@ #include "llvm/Support/WithColor.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" +#include "llvm-c/OptRemarks.h" #include <cstdlib> #include <map> #include <set> @@ -142,104 +143,44 @@ typedef std::map<std::string, std::map<int, std::map<std::string, std::map<int, OptReportLocationInfo>>>> LocationInfoTy; } // anonymous namespace -static void collectLocationInfo(yaml::Stream &Stream, - LocationInfoTy &LocationInfo) { - SmallVector<char, 8> Tmp; - - // Note: We're using the YAML parser here directly, instead of using the - // YAMLTraits implementation, because the YAMLTraits implementation does not - // support a way to handle only a subset of the input keys (it will error out - // if there is an input key that you don't map to your class), and - // furthermore, it does not provide a way to handle the Args sequence of - // key/value pairs, where the order must be captured and the 'String' key - // might be repeated. - for (auto &Doc : Stream) { - auto *Root = dyn_cast<yaml::MappingNode>(Doc.getRoot()); - if (!Root) - continue; +static bool readLocationInfo(LocationInfoTy &LocationInfo) { + ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = + MemoryBuffer::getFile(InputFileName.c_str()); + if (std::error_code EC = Buf.getError()) { + WithColor::error() << "Can't open file " << InputFileName << ": " + << EC.message() << "\n"; + return false; + } - bool Transformed = Root->getRawTag() == "!Passed"; - std::string Pass, File, Function; - int Line = 0, Column = 1; + StringRef Buffer = (*Buf)->getBuffer(); + LLVMOptRemarkParserRef Parser = + LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size()); + + LLVMOptRemarkEntry *Remark = nullptr; + while ((Remark = LLVMOptRemarkParserGetNext(Parser))) { + bool Transformed = + StringRef(Remark->RemarkType.Str, Remark->RemarkType.Len) == "!Passed"; + StringRef Pass(Remark->PassName.Str, Remark->PassName.Len); + StringRef File(Remark->DebugLoc.SourceFile.Str, + Remark->DebugLoc.SourceFile.Len); + StringRef Function(Remark->FunctionName.Str, Remark->FunctionName.Len); + uint32_t Line = Remark->DebugLoc.SourceLineNumber; + uint32_t Column = Remark->DebugLoc.SourceColumnNumber; + ArrayRef<LLVMOptRemarkArg> Args(Remark->Args, Remark->NumArgs); int VectorizationFactor = 1; int InterleaveCount = 1; int UnrollCount = 1; - for (auto &RootChild : *Root) { - auto *Key = dyn_cast<yaml::ScalarNode>(RootChild.getKey()); - if (!Key) - continue; - StringRef KeyName = Key->getValue(Tmp); - if (KeyName == "Pass") { - auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue()); - if (!Value) - continue; - Pass = Value->getValue(Tmp); - } else if (KeyName == "Function") { - auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue()); - if (!Value) - continue; - Function = Value->getValue(Tmp); - } else if (KeyName == "DebugLoc") { - auto *DebugLoc = dyn_cast<yaml::MappingNode>(RootChild.getValue()); - if (!DebugLoc) - continue; - - for (auto &DLChild : *DebugLoc) { - auto *DLKey = dyn_cast<yaml::ScalarNode>(DLChild.getKey()); - if (!DLKey) - continue; - StringRef DLKeyName = DLKey->getValue(Tmp); - if (DLKeyName == "File") { - auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue()); - if (!Value) - continue; - File = Value->getValue(Tmp); - } else if (DLKeyName == "Line") { - auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, Line); - } else if (DLKeyName == "Column") { - auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, Column); - } - } - } else if (KeyName == "Args") { - auto *Args = dyn_cast<yaml::SequenceNode>(RootChild.getValue()); - if (!Args) - continue; - for (auto &ArgChild : *Args) { - auto *ArgMap = dyn_cast<yaml::MappingNode>(&ArgChild); - if (!ArgMap) - continue; - for (auto &ArgKV : *ArgMap) { - auto *ArgKey = dyn_cast<yaml::ScalarNode>(ArgKV.getKey()); - if (!ArgKey) - continue; - StringRef ArgKeyName = ArgKey->getValue(Tmp); - if (ArgKeyName == "VectorizationFactor") { - auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, VectorizationFactor); - } else if (ArgKeyName == "InterleaveCount") { - auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, InterleaveCount); - } else if (ArgKeyName == "UnrollCount") { - auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue()); - if (!Value) - continue; - Value->getValue(Tmp).getAsInteger(10, UnrollCount); - } - } - } - } + for (const LLVMOptRemarkArg &Arg : Args) { + StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len); + StringRef ArgValue(Arg.Value.Str, Arg.Value.Len); + if (ArgKeyName == "VectorizationFactor") + ArgValue.getAsInteger(10, VectorizationFactor); + else if (ArgKeyName == "InterleaveCount") + ArgValue.getAsInteger(10, InterleaveCount); + else if (ArgKeyName == "UnrollCount") + ArgValue.getAsInteger(10, UnrollCount); } if (Line < 1 || File.empty()) @@ -268,22 +209,13 @@ static void collectLocationInfo(yaml::Stream &Stream, UpdateLLII(LI.Vectorized); } } -} -static bool readLocationInfo(LocationInfoTy &LocationInfo) { - ErrorOr<std::unique_ptr<MemoryBuffer>> Buf = - MemoryBuffer::getFileOrSTDIN(InputFileName); - if (std::error_code EC = Buf.getError()) { - WithColor::error() << "Can't open file " << InputFileName << ": " - << EC.message() << "\n"; - return false; - } - - SourceMgr SM; - yaml::Stream Stream(Buf.get()->getBuffer(), SM); - collectLocationInfo(Stream, LocationInfo); + bool HasError = LLVMOptRemarkParserHasError(Parser); + if (HasError) + WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n"; - return true; + LLVMOptRemarkParserDispose(Parser); + return !HasError; } static bool writeReport(LocationInfoTy &LocationInfo) { @@ -299,13 +231,8 @@ static bool writeReport(LocationInfoTy &LocationInfo) { bool FirstFile = true; for (auto &FI : LocationInfo) { SmallString<128> FileName(FI.first); - if (!InputRelDir.empty()) { - if (std::error_code EC = sys::fs::make_absolute(InputRelDir, FileName)) { - WithColor::error() << "Can't resolve file path to " << FileName << ": " - << EC.message() << "\n"; - return false; - } - } + if (!InputRelDir.empty()) + sys::fs::make_absolute(InputRelDir, FileName); const auto &FileInfo = FI.second; diff --git a/tools/llvm-pdbutil/Analyze.cpp b/tools/llvm-pdbutil/Analyze.cpp deleted file mode 100644 index 974ab49d9440..000000000000 --- a/tools/llvm-pdbutil/Analyze.cpp +++ /dev/null @@ -1,148 +0,0 @@ -//===- Analyze.cpp - PDB analysis functions ---------------------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#include "Analyze.h" - -#include "llvm/ADT/DenseSet.h" -#include "llvm/ADT/STLExtras.h" -#include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" -#include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" -#include "llvm/DebugInfo/CodeView/TypeRecord.h" -#include "llvm/DebugInfo/CodeView/TypeVisitorCallbacks.h" -#include "llvm/DebugInfo/PDB/Native/PDBFile.h" -#include "llvm/DebugInfo/PDB/Native/RawError.h" -#include "llvm/DebugInfo/PDB/Native/TpiStream.h" - -#include "llvm/Support/FormatVariadic.h" -#include "llvm/Support/raw_ostream.h" - -#include <list> - -using namespace llvm; -using namespace llvm::codeview; -using namespace llvm::pdb; - -static StringRef getLeafTypeName(TypeLeafKind LT) { - switch (LT) { -#define TYPE_RECORD(ename, value, name) \ - case ename: \ - return #name; -#include "llvm/DebugInfo/CodeView/CodeViewTypes.def" - default: - break; - } - return "UnknownLeaf"; -} - -namespace { -struct HashLookupVisitor : public TypeVisitorCallbacks { - struct Entry { - TypeIndex TI; - CVType Record; - }; - - explicit HashLookupVisitor(TpiStream &Tpi) : Tpi(Tpi) {} - - Error visitTypeBegin(CVType &Record) override { - uint32_t H = Tpi.getHashValues()[I]; - Record.Hash = H; - TypeIndex TI(I + TypeIndex::FirstNonSimpleIndex); - Lookup[H].push_back(Entry{TI, Record}); - ++I; - return Error::success(); - } - - uint32_t I = 0; - DenseMap<uint32_t, std::list<Entry>> Lookup; - TpiStream &Tpi; -}; -} - -AnalysisStyle::AnalysisStyle(PDBFile &File) : File(File) {} - -Error AnalysisStyle::dump() { - auto Tpi = File.getPDBTpiStream(); - if (!Tpi) - return Tpi.takeError(); - - HashLookupVisitor Hasher(*Tpi); - - uint32_t RecordCount = Tpi->getNumTypeRecords(); - auto Offsets = Tpi->getTypeIndexOffsets(); - auto Types = llvm::make_unique<LazyRandomTypeCollection>( - Tpi->typeArray(), RecordCount, Offsets); - - if (auto EC = codeview::visitTypeStream(*Types, Hasher)) - return EC; - - auto &Adjusters = Tpi->getHashAdjusters(); - DenseSet<uint32_t> AdjusterSet; - for (const auto &Adj : Adjusters) { - assert(AdjusterSet.find(Adj.second) == AdjusterSet.end()); - AdjusterSet.insert(Adj.second); - } - - uint32_t Count = 0; - outs() << "Searching for hash collisions\n"; - for (const auto &H : Hasher.Lookup) { - if (H.second.size() <= 1) - continue; - ++Count; - outs() << formatv("Hash: {0}, Count: {1} records\n", H.first, - H.second.size()); - for (const auto &R : H.second) { - auto Iter = AdjusterSet.find(R.TI.getIndex()); - StringRef Prefix; - if (Iter != AdjusterSet.end()) { - Prefix = "[HEAD]"; - AdjusterSet.erase(Iter); - } - StringRef LeafName = getLeafTypeName(R.Record.Type); - uint32_t TI = R.TI.getIndex(); - StringRef TypeName = Types->getTypeName(R.TI); - outs() << formatv("{0,-6} {1} ({2:x}) {3}\n", Prefix, LeafName, TI, - TypeName); - } - } - - outs() << "\n"; - outs() << "Dumping hash adjustment chains\n"; - for (const auto &A : Tpi->getHashAdjusters()) { - TypeIndex TI(A.second); - StringRef TypeName = Types->getTypeName(TI); - const CVType &HeadRecord = Types->getType(TI); - assert(HeadRecord.Hash.hasValue()); - - auto CollisionsIter = Hasher.Lookup.find(*HeadRecord.Hash); - if (CollisionsIter == Hasher.Lookup.end()) - continue; - - const auto &Collisions = CollisionsIter->second; - outs() << TypeName << "\n"; - 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) - continue; - const CVType &TailRecord = Types->getType(Chain.TI); - outs() << formatv(" {0:x} {1} {2}\n", Chain.TI.getIndex(), - getLeafTypeName(TailRecord.Type), - Types->getTypeName(Chain.TI)); - } - } - outs() << formatv("There are {0} orphaned hash adjusters\n", - AdjusterSet.size()); - for (const auto &Adj : AdjusterSet) { - outs() << formatv(" {0}\n", Adj); - } - - uint32_t DistinctHashValues = Hasher.Lookup.size(); - outs() << formatv("{0}/{1} hash collisions", Count, DistinctHashValues); - return Error::success(); -} diff --git a/tools/llvm-pdbutil/Analyze.h b/tools/llvm-pdbutil/Analyze.h deleted file mode 100644 index 7230ae45b0c8..000000000000 --- a/tools/llvm-pdbutil/Analyze.h +++ /dev/null @@ -1,30 +0,0 @@ -//===- Analyze.h - PDB analysis functions -----------------------*- 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_ANALYSIS_H -#define LLVM_TOOLS_LLVMPDBDUMP_ANALYSIS_H - -#include "OutputStyle.h" - -namespace llvm { -namespace pdb { -class PDBFile; -class AnalysisStyle : public OutputStyle { -public: - explicit AnalysisStyle(PDBFile &File); - - Error dump() override; - -private: - PDBFile &File; -}; -} -} - -#endif diff --git a/tools/llvm-pdbutil/CMakeLists.txt b/tools/llvm-pdbutil/CMakeLists.txt index 1ccbfdfc4df4..e403d54eef58 100644 --- a/tools/llvm-pdbutil/CMakeLists.txt +++ b/tools/llvm-pdbutil/CMakeLists.txt @@ -9,7 +9,6 @@ set(LLVM_LINK_COMPONENTS ) add_llvm_tool(llvm-pdbutil - Analyze.cpp BytesOutputStyle.cpp DumpOutputStyle.cpp ExplainOutputStyle.cpp diff --git a/tools/llvm-pdbutil/DumpOutputStyle.cpp b/tools/llvm-pdbutil/DumpOutputStyle.cpp index 9e59adc71967..e4f6aa7f6ec5 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.cpp +++ b/tools/llvm-pdbutil/DumpOutputStyle.cpp @@ -22,6 +22,7 @@ #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" #include "llvm/DebugInfo/CodeView/DebugCrossExSubsection.h" #include "llvm/DebugInfo/CodeView/DebugCrossImpSubsection.h" +#include "llvm/DebugInfo/CodeView/DebugFrameDataSubsection.h" #include "llvm/DebugInfo/CodeView/DebugInlineeLinesSubsection.h" #include "llvm/DebugInfo/CodeView/DebugLinesSubsection.h" #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" @@ -65,6 +66,16 @@ DumpOutputStyle::DumpOutputStyle(InputFile &File) PDBFile &DumpOutputStyle::getPdb() { return File.pdb(); } object::COFFObjectFile &DumpOutputStyle::getObj() { return File.obj(); } +void DumpOutputStyle::printStreamNotValidForObj() { + AutoIndent Indent(P, 4); + P.formatLine("Dumping this stream is not valid for object files"); +} + +void DumpOutputStyle::printStreamNotPresent(StringRef StreamName) { + AutoIndent Indent(P, 4); + P.formatLine("{0} stream not present", StreamName); +} + Error DumpOutputStyle::dump() { if (opts::dump::DumpSummary) { if (auto EC = dumpFileSummary()) @@ -132,6 +143,11 @@ Error DumpOutputStyle::dump() { return EC; } + if (opts::dump::DumpFpo) { + if (auto EC = dumpFpo()) + return EC; + } + if (File.isObj()) { if (opts::dump::DumpTypes || !opts::dump::DumpTypeIndex.empty() || opts::dump::DumpTypeExtras) @@ -199,14 +215,14 @@ static void printHeader(LinePrinter &P, const Twine &S) { Error DumpOutputStyle::dumpFileSummary() { printHeader(P, "Summary"); - ExitOnError Err("Invalid PDB Format: "); - - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping File summary is not valid for object files"); + printStreamNotValidForObj(); return Error::success(); } + AutoIndent Indent(P); + ExitOnError Err("Invalid PDB Format: "); + P.formatLine("Block Size: {0}", getPdb().getBlockSize()); P.formatLine("Number of blocks: {0}", getPdb().getBlockCount()); P.formatLine("Number of streams: {0}", getPdb().getNumStreams()); @@ -234,7 +250,7 @@ Error DumpOutputStyle::dumpFileSummary() { static StatCollection getSymbolStats(const SymbolGroup &SG, StatCollection &CumulativeStats) { StatCollection Stats; - if (SG.getFile().isPdb()) { + if (SG.getFile().isPdb() && SG.hasDebugStream()) { // For PDB files, all symbols are packed into one stream. for (const auto &S : SG.getPdbModuleStream().symbols(nullptr)) { Stats.update(S.kind(), S.length()); @@ -326,12 +342,13 @@ static bool shouldDumpSymbolGroup(uint32_t Idx, const SymbolGroup &Group) { Error DumpOutputStyle::dumpStreamSummary() { printHeader(P, "Streams"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping streams is not valid for object files"); + printStreamNotValidForObj(); return Error::success(); } + AutoIndent Indent(P); + if (StreamPurposes.empty()) discoverStreamPurposes(getPdb(), StreamPurposes); @@ -527,18 +544,18 @@ static void dumpSectionContrib(LinePrinter &P, const SectionContrib2 &SC, Error DumpOutputStyle::dumpModules() { printHeader(P, "Modules"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping modules is not supported for object files"); + printStreamNotValidForObj(); return Error::success(); } if (!getPdb().hasPDBDbiStream()) { - P.formatLine("DBI Stream not present"); + printStreamNotPresent("DBI"); return Error::success(); } + AutoIndent Indent(P); ExitOnError Err("Unexpected error processing modules: "); auto &Stream = Err(getPdb().getPDBDbiStream()); @@ -570,7 +587,12 @@ Error DumpOutputStyle::dumpModuleFiles() { printHeader(P, "Files"); if (File.isObj()) { - P.formatLine("Dumping files is not valid for object files"); + printStreamNotValidForObj(); + return Error::success(); + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); return Error::success(); } @@ -591,6 +613,11 @@ Error DumpOutputStyle::dumpModuleFiles() { Error DumpOutputStyle::dumpSymbolStats() { printHeader(P, "Module Stats"); + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + ExitOnError Err("Unexpected error processing modules: "); StatCollection SymStats; @@ -625,9 +652,9 @@ Error DumpOutputStyle::dumpSymbolStats() { } }); - P.printLine(" Summary |"); - AutoIndent Indent(P, 4); if (SymStats.Totals.Count > 0) { + P.printLine(" Summary |"); + AutoIndent Indent(P, 4); printModuleDetailStats<SymbolKind>(P, "Symbols", SymStats); printModuleDetailStats<DebugSubsectionKind>(P, "Chunks", ChunkStats); } @@ -680,6 +707,11 @@ static uint32_t getLongestTypeLeafName(const StatCollection &Stats) { Error DumpOutputStyle::dumpUdtStats() { printHeader(P, "S_UDT Record Stats"); + if (File.isPdb() && !getPdb().hasPDBGlobalsStream()) { + printStreamNotPresent("Globals"); + return Error::success(); + } + StatCollection UdtStats; StatCollection UdtTargetStats; AutoIndent Indent(P, 4); @@ -726,11 +758,6 @@ Error DumpOutputStyle::dumpUdtStats() { P.NewLine(); if (File.isPdb()) { - if (!getPdb().hasPDBGlobalsStream()) { - P.printLine("- Error: globals stream not present"); - return Error::success(); - } - auto &SymbolRecords = cantFail(getPdb().getPDBSymbolStream()); auto ExpGlobals = getPdb().getPDBGlobalsStream(); if (!ExpGlobals) @@ -839,6 +866,11 @@ static void typesetLinesAndColumns(LinePrinter &P, uint32_t Start, Error DumpOutputStyle::dumpLines() { printHeader(P, "Lines"); + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + uint32_t LastModi = UINT32_MAX; uint32_t LastNameIndex = UINT32_MAX; iterateModuleSubsections<DebugLinesSubsectionRef>( @@ -875,6 +907,11 @@ Error DumpOutputStyle::dumpLines() { Error DumpOutputStyle::dumpInlineeLines() { printHeader(P, "Inlinee Lines"); + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + iterateModuleSubsections<DebugInlineeLinesSubsectionRef>( File, PrintScope{P, 2}, [this](uint32_t Modi, const SymbolGroup &Strings, @@ -893,6 +930,12 @@ Error DumpOutputStyle::dumpInlineeLines() { Error DumpOutputStyle::dumpXmi() { printHeader(P, "Cross Module Imports"); + + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + iterateModuleSubsections<DebugCrossModuleImportsSubsectionRef>( File, PrintScope{P, 2}, [this](uint32_t Modi, const SymbolGroup &Strings, @@ -929,6 +972,11 @@ Error DumpOutputStyle::dumpXmi() { Error DumpOutputStyle::dumpXme() { printHeader(P, "Cross Module Exports"); + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + iterateModuleSubsections<DebugCrossModuleExportsSubsectionRef>( File, PrintScope{P, 2}, [this](uint32_t Modi, const SymbolGroup &Strings, @@ -943,6 +991,111 @@ Error DumpOutputStyle::dumpXme() { return Error::success(); } +std::string formatFrameType(object::frame_type FT) { + switch (FT) { + case object::frame_type::Fpo: + return "FPO"; + case object::frame_type::NonFpo: + return "Non-FPO"; + case object::frame_type::Trap: + return "Trap"; + case object::frame_type::Tss: + return "TSS"; + } + return "<unknown>"; +} + +Error DumpOutputStyle::dumpOldFpo(PDBFile &File) { + printHeader(P, "Old FPO Data"); + + ExitOnError Err("Error dumping old fpo data:"); + auto &Dbi = Err(File.getPDBDbiStream()); + + uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::FPO); + if (Index == kInvalidStreamIndex) { + printStreamNotPresent("FPO"); + return Error::success(); + } + + std::unique_ptr<MappedBlockStream> OldFpo = File.createIndexedStream(Index); + BinaryStreamReader Reader(*OldFpo); + FixedStreamArray<object::FpoData> Records; + Err(Reader.readArray(Records, + Reader.bytesRemaining() / sizeof(object::FpoData))); + + P.printLine(" RVA | Code | Locals | Params | Prolog | Saved Regs | Use " + "BP | Has SEH | Frame Type"); + + for (const object::FpoData &FD : Records) { + P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,6} | {5,10} | {6,6} | " + "{7,7} | {8,9}", + uint32_t(FD.Offset), uint32_t(FD.Size), uint32_t(FD.NumLocals), + uint32_t(FD.NumParams), FD.getPrologSize(), + FD.getNumSavedRegs(), FD.useBP(), FD.hasSEH(), + formatFrameType(FD.getFP())); + } + return Error::success(); +} + +Error DumpOutputStyle::dumpNewFpo(PDBFile &File) { + printHeader(P, "New FPO Data"); + + ExitOnError Err("Error dumping new fpo data:"); + auto &Dbi = Err(File.getPDBDbiStream()); + + uint32_t Index = Dbi.getDebugStreamIndex(DbgHeaderType::NewFPO); + if (Index == kInvalidStreamIndex) { + printStreamNotPresent("New FPO"); + return Error::success(); + } + + std::unique_ptr<MappedBlockStream> NewFpo = File.createIndexedStream(Index); + + DebugFrameDataSubsectionRef FDS; + if (auto EC = FDS.initialize(*NewFpo)) + return make_error<RawError>(raw_error_code::corrupt_file, + "Invalid new fpo stream"); + + P.printLine(" RVA | Code | Locals | Params | Stack | Prolog | Saved Regs " + "| Has SEH | Has C++EH | Start | Program"); + for (const FrameData &FD : FDS) { + bool IsFuncStart = FD.Flags & FrameData::IsFunctionStart; + bool HasEH = FD.Flags & FrameData::HasEH; + bool HasSEH = FD.Flags & FrameData::HasSEH; + + auto &StringTable = Err(File.getStringTable()); + + auto Program = Err(StringTable.getStringForID(FD.FrameFunc)); + P.formatLine("{0:X-8} | {1,4} | {2,6} | {3,6} | {4,5} | {5,6} | {6,10} | " + "{7,7} | {8,9} | {9,5} | {10}", + uint32_t(FD.RvaStart), uint32_t(FD.CodeSize), + uint32_t(FD.LocalSize), uint32_t(FD.ParamsSize), + uint32_t(FD.MaxStackSize), uint16_t(FD.PrologSize), + uint16_t(FD.SavedRegsSize), HasSEH, HasEH, IsFuncStart, + Program); + } + return Error::success(); +} + +Error DumpOutputStyle::dumpFpo() { + if (!File.isPdb()) { + printStreamNotValidForObj(); + return Error::success(); + } + + PDBFile &File = getPdb(); + if (!File.hasPDBDbiStream()) { + printStreamNotPresent("DBI"); + return Error::success(); + } + + if (auto EC = dumpOldFpo(File)) + return EC; + if (auto EC = dumpNewFpo(File)) + return EC; + return Error::success(); +} + Error DumpOutputStyle::dumpStringTableFromPdb() { AutoIndent Indent(P); auto IS = getPdb().getStringTable(); @@ -965,7 +1118,7 @@ Error DumpOutputStyle::dumpStringTableFromPdb() { std::vector<uint32_t> SortedIDs(IS->name_ids().begin(), IS->name_ids().end()); - llvm::sort(SortedIDs.begin(), SortedIDs.end()); + llvm::sort(SortedIDs); for (uint32_t I : SortedIDs) { auto ES = IS->getStringForID(I); llvm::SmallString<32> Str; @@ -1037,12 +1190,13 @@ Error DumpOutputStyle::dumpStringTableFromObj() { 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."); + printStreamNotValidForObj(); return Error::success(); } + + AutoIndent Indent(P); ExitOnError Err("Invalid PDB File: "); auto &IS = Err(File.pdb().getPDBInfoStream()); @@ -1087,13 +1241,13 @@ static void dumpFullTypeStream(LinePrinter &Printer, LazyRandomTypeCollection &Types, uint32_t NumTypeRecords, uint32_t NumHashBuckets, FixedStreamArray<support::ulittle32_t> HashValues, - bool Bytes, bool Extras) { + TpiStream *Stream, bool Bytes, bool Extras) { Printer.formatLine("Showing {0:N} records", NumTypeRecords); uint32_t Width = NumDigits(TypeIndex::FirstNonSimpleIndex + NumTypeRecords); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - NumHashBuckets, HashValues); + NumHashBuckets, HashValues, Stream); if (auto EC = codeview::visitTypeStream(Types, V)) { Printer.formatLine("An error occurred dumping type records: {0}", @@ -1109,7 +1263,8 @@ static void dumpPartialTypeStream(LinePrinter &Printer, NumDigits(TypeIndex::FirstNonSimpleIndex + Stream.getNumTypeRecords()); MinimalTypeDumpVisitor V(Printer, Width + 2, Bytes, Extras, Types, - Stream.getNumHashBuckets(), Stream.getHashValues()); + Stream.getNumHashBuckets(), Stream.getHashValues(), + &Stream); if (opts::dump::DumpTypeDependents) { // If we need to dump all dependents, then iterate each index and find @@ -1171,7 +1326,8 @@ Error DumpOutputStyle::dumpTypesFromObjectFile() { Types.reset(Reader, 100); if (opts::dump::DumpTypes) { - dumpFullTypeStream(P, Types, 0, 0, {}, opts::dump::DumpTypeData, false); + dumpFullTypeStream(P, Types, 0, 0, {}, nullptr, opts::dump::DumpTypeData, + false); } else if (opts::dump::DumpTypeExtras) { auto LocalHashes = LocallyHashedType::hashTypeCollection(Types); auto GlobalHashes = GloballyHashedType::hashTypeCollection(Types); @@ -1204,7 +1360,6 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { printHeader(P, "Types (IPI Stream)"); } - AutoIndent Indent(P); assert(!File.isObj()); bool Present = false; @@ -1229,10 +1384,11 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { } if (!Present) { - P.formatLine("Stream not present"); + printStreamNotPresent(StreamIdx == StreamTPI ? "TPI" : "IPI"); return Error::success(); } + AutoIndent Indent(P); ExitOnError Err("Unexpected error processing types: "); auto &Stream = Err((StreamIdx == StreamTPI) ? getPdb().getPDBTpiStream() @@ -1240,11 +1396,14 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { auto &Types = (StreamIdx == StreamTPI) ? File.types() : File.ids(); + // Enable resolving forward decls. + Stream.buildHashMap(); + if (DumpTypes || !Indices.empty()) { if (Indices.empty()) dumpFullTypeStream(P, Types, Stream.getNumTypeRecords(), Stream.getNumHashBuckets(), Stream.getHashValues(), - DumpBytes, DumpExtras); + &Stream, DumpBytes, DumpExtras); else { std::vector<TypeIndex> TiList(Indices.begin(), Indices.end()); dumpPartialTypeStream(P, Types, Stream, TiList, DumpBytes, DumpExtras, @@ -1261,19 +1420,21 @@ Error DumpOutputStyle::dumpTpiStream(uint32_t StreamIdx) { P.formatLine("TI: {0}, Offset: {1}", IO.Type, fmtle(IO.Offset)); } - P.NewLine(); - P.formatLine("Hash Adjusters:"); - auto &Adjusters = Stream.getHashAdjusters(); - auto &Strings = Err(getPdb().getStringTable()); - for (const auto &A : Adjusters) { - AutoIndent Indent2(P); - auto ExpectedStr = Strings.getStringForID(A.first); - TypeIndex TI(A.second); - if (ExpectedStr) - P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); - else { - P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); - consumeError(ExpectedStr.takeError()); + if (getPdb().hasPDBStringTable()) { + P.NewLine(); + P.formatLine("Hash Adjusters:"); + auto &Adjusters = Stream.getHashAdjusters(); + auto &Strings = Err(getPdb().getStringTable()); + for (const auto &A : Adjusters) { + AutoIndent Indent2(P); + auto ExpectedStr = Strings.getStringForID(A.first); + TypeIndex TI(A.second); + if (ExpectedStr) + P.formatLine("`{0}` -> {1}", *ExpectedStr, TI); + else { + P.formatLine("unknown str id ({0}) -> {1}", A.first, TI); + consumeError(ExpectedStr.takeError()); + } } } } @@ -1321,12 +1482,12 @@ Error DumpOutputStyle::dumpModuleSymsForObj() { Error DumpOutputStyle::dumpModuleSymsForPdb() { printHeader(P, "Symbols"); - AutoIndent Indent(P); - if (!getPdb().hasPDBDbiStream()) { - P.formatLine("DBI Stream not present"); + if (File.isPdb() && !getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); return Error::success(); } + AutoIndent Indent(P); ExitOnError Err("Unexpected error processing symbols: "); auto &Ids = File.ids(); @@ -1364,18 +1525,19 @@ Error DumpOutputStyle::dumpModuleSymsForPdb() { Error DumpOutputStyle::dumpGSIRecords() { printHeader(P, "GSI Records"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping Globals is not supported for object files"); + printStreamNotValidForObj(); return Error::success(); } if (!getPdb().hasPDBSymbolStream()) { - P.formatLine("GSI Common Symbol Stream not present"); + printStreamNotPresent("GSI Common Symbol"); return Error::success(); } + AutoIndent Indent(P); + auto &Records = cantFail(getPdb().getPDBSymbolStream()); auto &Types = File.types(); auto &Ids = File.ids(); @@ -1397,38 +1559,72 @@ Error DumpOutputStyle::dumpGSIRecords() { Error DumpOutputStyle::dumpGlobals() { printHeader(P, "Global Symbols"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping Globals is not supported for object files"); + printStreamNotValidForObj(); return Error::success(); } if (!getPdb().hasPDBGlobalsStream()) { - P.formatLine("Globals stream not present"); + printStreamNotPresent("Globals"); return Error::success(); } + + AutoIndent Indent(P); ExitOnError Err("Error dumping globals stream: "); auto &Globals = Err(getPdb().getPDBGlobalsStream()); - const GSIHashTable &Table = Globals.getGlobalsTable(); - Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); + if (opts::dump::DumpGlobalNames.empty()) { + const GSIHashTable &Table = Globals.getGlobalsTable(); + Err(dumpSymbolsFromGSI(Table, opts::dump::DumpGlobalExtras)); + } else { + SymbolStream &SymRecords = cantFail(getPdb().getPDBSymbolStream()); + auto &Types = File.types(); + auto &Ids = File.ids(); + + SymbolVisitorCallbackPipeline Pipeline; + SymbolDeserializer Deserializer(nullptr, CodeViewContainer::Pdb); + MinimalSymbolDumper Dumper(P, opts::dump::DumpSymRecordBytes, Ids, Types); + + Pipeline.addCallbackToPipeline(Deserializer); + Pipeline.addCallbackToPipeline(Dumper); + CVSymbolVisitor Visitor(Pipeline); + + using ResultEntryType = std::pair<uint32_t, CVSymbol>; + for (StringRef Name : opts::dump::DumpGlobalNames) { + AutoIndent Indent(P); + P.formatLine("Global Name `{0}`", Name); + std::vector<ResultEntryType> Results = + Globals.findRecordsByName(Name, SymRecords); + if (Results.empty()) { + AutoIndent Indent(P); + P.printLine("(no matching records found)"); + continue; + } + + for (ResultEntryType Result : Results) { + if (auto E = Visitor.visitSymbolRecord(Result.second, Result.first)) + return E; + } + } + } return Error::success(); } Error DumpOutputStyle::dumpPublics() { printHeader(P, "Public Symbols"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping Globals is not supported for object files"); + printStreamNotValidForObj(); return Error::success(); } if (!getPdb().hasPDBPublicsStream()) { - P.formatLine("Publics stream not present"); + printStreamNotPresent("Publics"); return Error::success(); } + + AutoIndent Indent(P); ExitOnError Err("Error dumping publics stream: "); auto &Publics = Err(getPdb().getPDBPublicsStream()); @@ -1514,8 +1710,6 @@ Error DumpOutputStyle::dumpSymbolsFromGSI(const GSIHashTable &Table, // Return early if we aren't dumping public hash table and address map info. if (HashExtras) { - P.formatBinary("Hash Bitmap", Table.HashBitmap, 0); - P.formatLine("Hash Entries"); { AutoIndent Indent2(P); @@ -1560,12 +1754,17 @@ Error DumpOutputStyle::dumpSectionHeaders() { void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { printHeader(P, Label); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping Section Headers is not supported for object files"); + printStreamNotValidForObj(); + return; + } + + if (!getPdb().hasPDBDbiStream()) { + printStreamNotPresent("DBI"); return; } + AutoIndent Indent(P); ExitOnError Err("Error dumping section headers: "); std::unique_ptr<MappedBlockStream> Stream; ArrayRef<object::coff_section> Headers; @@ -1606,20 +1805,19 @@ void DumpOutputStyle::dumpSectionHeaders(StringRef Label, DbgHeaderType Type) { Error DumpOutputStyle::dumpSectionContribs() { printHeader(P, "Section Contributions"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine( - "Dumping section contributions is not supported for object files"); + printStreamNotValidForObj(); return Error::success(); } - ExitOnError Err("Error dumping section contributions: "); if (!getPdb().hasPDBDbiStream()) { - P.formatLine( - "Section contribs require a DBI Stream, which could not be loaded"); + printStreamNotPresent("DBI"); return Error::success(); } + AutoIndent Indent(P); + ExitOnError Err("Error dumping section contributions: "); + auto &Dbi = Err(getPdb().getPDBDbiStream()); class Visitor : public ISectionContribVisitor { @@ -1651,21 +1849,20 @@ Error DumpOutputStyle::dumpSectionContribs() { Error DumpOutputStyle::dumpSectionMap() { printHeader(P, "Section Map"); - AutoIndent Indent(P); if (File.isObj()) { - P.formatLine("Dumping section map is not supported for object files"); + printStreamNotValidForObj(); return Error::success(); } - ExitOnError Err("Error dumping section map: "); - if (!getPdb().hasPDBDbiStream()) { - P.formatLine("Dumping the section map requires a DBI Stream, which could " - "not be loaded"); + printStreamNotPresent("DBI"); return Error::success(); } + AutoIndent Indent(P); + ExitOnError Err("Error dumping section map: "); + auto &Dbi = Err(getPdb().getPDBDbiStream()); uint32_t I = 0; diff --git a/tools/llvm-pdbutil/DumpOutputStyle.h b/tools/llvm-pdbutil/DumpOutputStyle.h index e7e9252f2fa9..9b3a85587bde 100644 --- a/tools/llvm-pdbutil/DumpOutputStyle.h +++ b/tools/llvm-pdbutil/DumpOutputStyle.h @@ -70,6 +70,9 @@ private: PDBFile &getPdb(); object::COFFObjectFile &getObj(); + void printStreamNotValidForObj(); + void printStreamNotPresent(StringRef StreamName); + Error dumpFileSummary(); Error dumpStreamSummary(); Error dumpSymbolStats(); @@ -82,6 +85,9 @@ private: Error dumpInlineeLines(); Error dumpXmi(); Error dumpXme(); + Error dumpFpo(); + Error dumpOldFpo(PDBFile &File); + Error dumpNewFpo(PDBFile &File); Error dumpTpiStream(uint32_t StreamIdx); Error dumpTypesFromObjectFile(); Error dumpModules(); diff --git a/tools/llvm-pdbutil/InputFile.cpp b/tools/llvm-pdbutil/InputFile.cpp index 7b5af7e96920..8eb116cf0d80 100644 --- a/tools/llvm-pdbutil/InputFile.cpp +++ b/tools/llvm-pdbutil/InputFile.cpp @@ -41,6 +41,10 @@ getModuleDebugStream(PDBFile &File, StringRef &ModuleName, uint32_t Index) { auto &Dbi = Err(File.getPDBDbiStream()); const auto &Modules = Dbi.modules(); + if (Index >= Modules.getModuleCount()) + return make_error<RawError>(raw_error_code::index_out_of_bounds, + "Invalid module index"); + auto Modi = Modules.getModuleDescriptor(Index); ModuleName = Modi.getModuleName(); @@ -112,10 +116,6 @@ static std::string formatChecksumKind(FileChecksumKind Kind) { return formatUnknownEnum(Kind); } -static const DebugStringTableSubsectionRef &extractStringTable(PDBFile &File) { - return cantFail(File.getStringTable()).getStringTable(); -} - template <typename... Args> static void formatInternal(LinePrinter &Printer, bool Append, Args &&... args) { if (Append) @@ -164,8 +164,13 @@ void SymbolGroup::initializeForPdb(uint32_t Modi) { // PDB always uses the same string table, but each module has its own // checksums. So we only set the strings if they're not already set. - if (!SC.hasStrings()) - SC.setStrings(extractStringTable(File->pdb())); + if (!SC.hasStrings()) { + auto StringTable = File->pdb().getStringTable(); + if (StringTable) + SC.setStrings(StringTable->getStringTable()); + else + consumeError(StringTable.takeError()); + } SC.resetChecksums(); auto MDS = getModuleDebugStream(File->pdb(), Name, Modi); diff --git a/tools/llvm-pdbutil/InputFile.h b/tools/llvm-pdbutil/InputFile.h index 552f3a3b2127..ee4e651c1e99 100644 --- a/tools/llvm-pdbutil/InputFile.h +++ b/tools/llvm-pdbutil/InputFile.h @@ -110,6 +110,8 @@ public: const InputFile &getFile() const { return *File; } InputFile &getFile() { return *File; } + bool hasDebugStream() const { return DebugStream != nullptr; } + private: void initializeForPdb(uint32_t Modi); void updatePdbModi(uint32_t Modi); diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp index f4e38a32a511..2c7b213b0a9f 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.cpp +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.cpp @@ -296,6 +296,14 @@ static std::string formatRegisterId(RegisterId Id) { return formatUnknownEnum(Id); } +static std::string formatRegisterId(uint16_t Reg16) { + return formatRegisterId(RegisterId(Reg16)); +} + +static std::string formatRegisterId(ulittle16_t &Reg16) { + return formatRegisterId(uint16_t(Reg16)); +} + static std::string formatRange(LocalVariableAddrRange Range) { return formatv("[{0},+{1})", formatSegmentOffset(Range.ISectStart, Range.OffsetStart), @@ -482,6 +490,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, AutoIndent Indent(P, 7); SourceLanguage Lang = static_cast<SourceLanguage>( Compile2.Flags & CompileSym2Flags::SourceLanguageMask); + CompilationCPU = Compile2.Machine; P.formatLine("machine = {0}, ver = {1}, language = {2}", formatMachineType(Compile2.Machine), Compile2.Version, formatSourceLanguage(Lang)); @@ -502,6 +511,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, AutoIndent Indent(P, 7); SourceLanguage Lang = static_cast<SourceLanguage>( Compile3.Flags & CompileSym3Flags::SourceLanguageMask); + CompilationCPU = Compile3.Machine; P.formatLine("machine = {0}, Ver = {1}, language = {2}", formatMachineType(Compile3.Machine), Compile3.Version, formatSourceLanguage(Lang)); @@ -550,10 +560,11 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeRegisterRelSym &Def) { AutoIndent Indent(P, 7); - P.formatLine("register = {0}, base ptr = {1}, offset in parent = {2}, has " + P.formatLine("register = {0}, offset = {1}, offset in parent = {2}, has " "spilled udt = {3}", - uint16_t(Def.Hdr.Register), int32_t(Def.Hdr.BasePointerOffset), - Def.offsetInParent(), Def.hasSpilledUDTMember()); + formatRegisterId(Def.Hdr.Register), + int32_t(Def.Hdr.BasePointerOffset), Def.offsetInParent(), + Def.hasSpilledUDTMember()); P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), formatGaps(P.getIndentLevel() + 9, Def.Gaps)); return Error::success(); @@ -564,8 +575,8 @@ Error MinimalSymbolDumper::visitKnownRecord( AutoIndent Indent(P, 7); P.formatLine("register = {0}, may have no name = {1}, range start = " "{2}, length = {3}", - uint16_t(DefRangeRegister.Hdr.Register), - uint16_t(DefRangeRegister.Hdr.MayHaveNoName), + formatRegisterId(DefRangeRegister.Hdr.Register), + bool(DefRangeRegister.Hdr.MayHaveNoName), formatSegmentOffset(DefRangeRegister.Range.ISectStart, DefRangeRegister.Range.OffsetStart), DefRangeRegister.Range.Range); @@ -579,7 +590,7 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, AutoIndent Indent(P, 7); bool NoName = !!(Def.Hdr.MayHaveNoName == 0); P.formatLine("register = {0}, may have no name = {1}, offset in parent = {2}", - uint16_t(Def.Hdr.Register), NoName, + formatRegisterId(Def.Hdr.Register), NoName, uint32_t(Def.Hdr.OffsetInParent)); P.formatLine("range = {0}, gaps = {1}", formatRange(Def.Range), formatGaps(P.getIndentLevel() + 9, Def.Gaps)); @@ -606,8 +617,8 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, DefRangeSym &Def) { Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameCookieSym &FC) { AutoIndent Indent(P, 7); P.formatLine("code offset = {0}, Register = {1}, kind = {2}, flags = {3}", - FC.CodeOffset, FC.Register, formatCookieKind(FC.CookieKind), - FC.Flags); + FC.CodeOffset, formatRegisterId(FC.Register), + formatCookieKind(FC.CookieKind), FC.Flags); return Error::success(); } @@ -620,6 +631,9 @@ Error MinimalSymbolDumper::visitKnownRecord(CVSymbol &CVR, FrameProcSym &FP) { FP.BytesOfCalleeSavedRegisters, formatSegmentOffset(FP.SectionIdOfExceptionHandler, FP.OffsetOfExceptionHandler)); + P.formatLine("local fp reg = {0}, param fp reg = {1}", + formatRegisterId(FP.getLocalFramePtrReg(CompilationCPU)), + formatRegisterId(FP.getParamFramePtrReg(CompilationCPU))); P.formatLine("flags = {0}", formatFrameProcedureOptions(P.getIndentLevel() + 9, FP.Flags)); return Error::success(); diff --git a/tools/llvm-pdbutil/MinimalSymbolDumper.h b/tools/llvm-pdbutil/MinimalSymbolDumper.h index 1c26a85a4eaf..033e193cee6c 100644 --- a/tools/llvm-pdbutil/MinimalSymbolDumper.h +++ b/tools/llvm-pdbutil/MinimalSymbolDumper.h @@ -53,6 +53,11 @@ private: std::string idIndex(codeview::TypeIndex TI) const; LinePrinter &P; + + /// Dumping certain records requires knowing what machine this is. The + /// S_COMPILE3 record will tell us, but if we don't see one, default to X64. + codeview::CPUType CompilationCPU = codeview::CPUType::X64; + bool RecordBytes; const SymbolGroup *SymGroup = nullptr; codeview::LazyRandomTypeCollection &Ids; @@ -61,4 +66,4 @@ private: } // namespace pdb } // namespace llvm -#endif
\ No newline at end of file +#endif diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.cpp b/tools/llvm-pdbutil/MinimalTypeDumper.cpp index 569bca7490fa..3f10e8ab8a1e 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.cpp +++ b/tools/llvm-pdbutil/MinimalTypeDumper.cpp @@ -12,6 +12,7 @@ #include "FormatUtil.h" #include "LinePrinter.h" +#include "llvm-pdbutil.h" #include "llvm/DebugInfo/CodeView/CVRecord.h" #include "llvm/DebugInfo/CodeView/CVTypeVisitor.h" #include "llvm/DebugInfo/CodeView/CodeView.h" @@ -19,6 +20,7 @@ #include "llvm/DebugInfo/CodeView/LazyRandomTypeCollection.h" #include "llvm/DebugInfo/CodeView/TypeRecord.h" #include "llvm/DebugInfo/PDB/Native/TpiHashing.h" +#include "llvm/DebugInfo/PDB/Native/TpiStream.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MathExtras.h" @@ -27,15 +29,37 @@ using namespace llvm::codeview; using namespace llvm::pdb; static std::string formatClassOptions(uint32_t IndentLevel, - ClassOptions Options) { + ClassOptions Options, TpiStream *Stream, + TypeIndex CurrentTypeIndex) { std::vector<std::string> Opts; + + if (Stream && Stream->supportsTypeLookup() && + !opts::dump::DontResolveForwardRefs && + ((Options & ClassOptions::ForwardReference) != ClassOptions::None)) { + // If we're able to resolve forward references, do that. + Expected<TypeIndex> ETI = + Stream->findFullDeclForForwardRef(CurrentTypeIndex); + if (!ETI) { + consumeError(ETI.takeError()); + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref (??\?)"); + } else { + const char *Direction = (*ETI == CurrentTypeIndex) + ? "=" + : ((*ETI < CurrentTypeIndex) ? "<-" : "->"); + std::string Formatted = + formatv("forward ref ({0} {1})", Direction, *ETI).str(); + PUSH_FLAG(ClassOptions, ForwardReference, Options, std::move(Formatted)); + } + } else { + PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); + } + PUSH_FLAG(ClassOptions, HasConstructorOrDestructor, Options, "has ctor / dtor"); PUSH_FLAG(ClassOptions, ContainsNestedClass, Options, "contains nested class"); PUSH_FLAG(ClassOptions, HasConversionOperator, Options, "conversion operator"); - PUSH_FLAG(ClassOptions, ForwardReference, Options, "forward ref"); PUSH_FLAG(ClassOptions, HasUniqueName, Options, "has unique name"); PUSH_FLAG(ClassOptions, Intrinsic, Options, "intrin"); PUSH_FLAG(ClassOptions, Nested, Options, "is nested"); @@ -194,6 +218,7 @@ static std::string formatFunctionOptions(FunctionOptions Options) { } Error MinimalTypeDumpVisitor::visitTypeBegin(CVType &Record, TypeIndex Index) { + CurrentTypeIndex = Index; // formatLine puts the newline at the beginning, so we use formatLine here // to start a new line, and then individual visit methods use format to // append to the existing line. @@ -304,7 +329,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, P.formatLine("vtable: {0}, base list: {1}, field list: {2}", Class.VTableShape, Class.DerivationList, Class.FieldList); P.formatLine("options: {0}, sizeof {1}", - formatClassOptions(P.getIndentLevel(), Class.Options), + formatClassOptions(P.getIndentLevel(), Class.Options, Stream, + CurrentTypeIndex), Class.Size); return Error::success(); } @@ -316,7 +342,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, P.formatLine("unique name: `{0}`", Union.UniqueName); P.formatLine("field list: {0}", Union.FieldList); P.formatLine("options: {0}, sizeof {1}", - formatClassOptions(P.getIndentLevel(), Union.Options), + formatClassOptions(P.getIndentLevel(), Union.Options, Stream, + CurrentTypeIndex), Union.Size); return Error::success(); } @@ -328,7 +355,8 @@ Error MinimalTypeDumpVisitor::visitKnownRecord(CVType &CVR, EnumRecord &Enum) { P.formatLine("field list: {0}, underlying type: {1}", Enum.FieldList, Enum.UnderlyingType); P.formatLine("options: {0}", - formatClassOptions(P.getIndentLevel(), Enum.Options)); + formatClassOptions(P.getIndentLevel(), Enum.Options, Stream, + CurrentTypeIndex)); return Error::success(); } diff --git a/tools/llvm-pdbutil/MinimalTypeDumper.h b/tools/llvm-pdbutil/MinimalTypeDumper.h index 4227688f0f71..8f6bdc6110ae 100644 --- a/tools/llvm-pdbutil/MinimalTypeDumper.h +++ b/tools/llvm-pdbutil/MinimalTypeDumper.h @@ -20,15 +20,18 @@ class LazyRandomTypeCollection; namespace pdb { class LinePrinter; +class TpiStream; class MinimalTypeDumpVisitor : public codeview::TypeVisitorCallbacks { public: MinimalTypeDumpVisitor(LinePrinter &P, uint32_t Width, bool RecordBytes, bool Hashes, codeview::LazyRandomTypeCollection &Types, uint32_t NumHashBuckets, - FixedStreamArray<support::ulittle32_t> HashValues) + FixedStreamArray<support::ulittle32_t> HashValues, + pdb::TpiStream *Stream) : P(P), Width(Width), RecordBytes(RecordBytes), Hashes(Hashes), - Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues) {} + Types(Types), NumHashBuckets(NumHashBuckets), HashValues(HashValues), + Stream(Stream) {} Error visitTypeBegin(codeview::CVType &Record, codeview::TypeIndex Index) override; @@ -55,7 +58,9 @@ private: bool Hashes = false; codeview::LazyRandomTypeCollection &Types; uint32_t NumHashBuckets; + codeview::TypeIndex CurrentTypeIndex; FixedStreamArray<support::ulittle32_t> HashValues; + pdb::TpiStream *Stream = nullptr; }; } // namespace pdb } // namespace llvm diff --git a/tools/llvm-pdbutil/PdbYaml.cpp b/tools/llvm-pdbutil/PdbYaml.cpp index eb39708a27e9..3ea333608314 100644 --- a/tools/llvm-pdbutil/PdbYaml.cpp +++ b/tools/llvm-pdbutil/PdbYaml.cpp @@ -110,6 +110,7 @@ void MappingTraits<PdbObject>::mapping(IO &IO, PdbObject &Obj) { IO.mapOptional("DbiStream", Obj.DbiStream); IO.mapOptional("TpiStream", Obj.TpiStream); IO.mapOptional("IpiStream", Obj.IpiStream); + IO.mapOptional("PublicsStream", Obj.PublicsStream); } void MappingTraits<MSFHeaders>::mapping(IO &IO, MSFHeaders &Obj) { @@ -163,6 +164,11 @@ void MappingTraits<PdbTpiStream>::mapping(IO &IO, IO.mapRequired("Records", Obj.Records); } +void MappingTraits<PdbPublicsStream>::mapping( + IO &IO, pdb::yaml::PdbPublicsStream &Obj) { + IO.mapRequired("Records", Obj.PubSyms); +} + void MappingTraits<NamedStreamMapping>::mapping(IO &IO, NamedStreamMapping &Obj) { IO.mapRequired("Name", Obj.StreamName); diff --git a/tools/llvm-pdbutil/PdbYaml.h b/tools/llvm-pdbutil/PdbYaml.h index 91e054490a5f..97ba87266cc6 100644 --- a/tools/llvm-pdbutil/PdbYaml.h +++ b/tools/llvm-pdbutil/PdbYaml.h @@ -92,6 +92,10 @@ struct PdbTpiStream { std::vector<CodeViewYAML::LeafRecord> Records; }; +struct PdbPublicsStream { + std::vector<CodeViewYAML::SymbolRecord> PubSyms; +}; + struct PdbObject { explicit PdbObject(BumpPtrAllocator &Allocator) : Allocator(Allocator) {} @@ -102,6 +106,7 @@ struct PdbObject { Optional<PdbDbiStream> DbiStream; Optional<PdbTpiStream> TpiStream; Optional<PdbTpiStream> IpiStream; + Optional<PdbPublicsStream> PublicsStream; Optional<std::vector<StringRef>> StringTable; @@ -118,6 +123,7 @@ LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::StreamBlockList) LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbInfoStream) LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiStream) LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbTpiStream) +LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbPublicsStream) LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::NamedStreamMapping) LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbModiStream) LLVM_YAML_DECLARE_MAPPING_TRAITS(pdb::yaml::PdbDbiModuleInfo) diff --git a/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp b/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp index 651cb8b7649e..f009f53a3932 100644 --- a/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp +++ b/tools/llvm-pdbutil/PrettyClassDefinitionDumper.cpp @@ -51,6 +51,13 @@ void ClassDefinitionDumper::prettyPrintClassIntro(const ClassLayout &Layout) { uint32_t Size = Layout.getSize(); const PDBSymbolTypeUDT &Class = Layout.getClass(); + if (Layout.getClass().isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Layout.getClass().isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + if (Layout.getClass().isUnalignedType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " "; WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); WithColor(Printer, PDB_ColorItem::Comment).get() << " [sizeof = " << Size diff --git a/tools/llvm-pdbutil/PrettyCompilandDumper.cpp b/tools/llvm-pdbutil/PrettyCompilandDumper.cpp index 0d99c9b1245c..94a0b2d5e780 100644 --- a/tools/llvm-pdbutil/PrettyCompilandDumper.cpp +++ b/tools/llvm-pdbutil/PrettyCompilandDumper.cpp @@ -28,6 +28,7 @@ #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" #include "llvm/DebugInfo/PDB/PDBSymbolUnknown.h" +#include "llvm/DebugInfo/PDB/PDBSymbolUsingNamespace.h" #include "llvm/Support/Format.h" #include "llvm/Support/Path.h" #include "llvm/Support/raw_ostream.h" @@ -216,3 +217,13 @@ void CompilandDumper::dump(const PDBSymbolUnknown &Symbol) { Printer.NewLine(); Printer << "unknown (" << Symbol.getSymTag() << ")"; } + +void CompilandDumper::dump(const PDBSymbolUsingNamespace &Symbol) { + if (Printer.IsSymbolExcluded(Symbol.getName())) + return; + + Printer.NewLine(); + Printer << "using namespace "; + std::string Name = Symbol.getName(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << Name; +} diff --git a/tools/llvm-pdbutil/PrettyCompilandDumper.h b/tools/llvm-pdbutil/PrettyCompilandDumper.h index cae196e9d134..1a840e49607c 100644 --- a/tools/llvm-pdbutil/PrettyCompilandDumper.h +++ b/tools/llvm-pdbutil/PrettyCompilandDumper.h @@ -34,6 +34,7 @@ public: void dump(const PDBSymbolThunk &Symbol) override; void dump(const PDBSymbolTypeTypedef &Symbol) override; void dump(const PDBSymbolUnknown &Symbol) override; + void dump(const PDBSymbolUsingNamespace &Symbol) override; private: LinePrinter &Printer; diff --git a/tools/llvm-pdbutil/PrettyEnumDumper.cpp b/tools/llvm-pdbutil/PrettyEnumDumper.cpp index bf22e75e3949..f4cbd3f8fa14 100644 --- a/tools/llvm-pdbutil/PrettyEnumDumper.cpp +++ b/tools/llvm-pdbutil/PrettyEnumDumper.cpp @@ -23,6 +23,18 @@ using namespace llvm::pdb; EnumDumper::EnumDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} void EnumDumper::start(const PDBSymbolTypeEnum &Symbol) { + if (Symbol.getUnmodifiedTypeId() != 0) { + if (Symbol.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Symbol.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + if (Symbol.isUnalignedType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned "; + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; + WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); + return; + } + WithColor(Printer, PDB_ColorItem::Keyword).get() << "enum "; WithColor(Printer, PDB_ColorItem::Type).get() << Symbol.getName(); if (!opts::pretty::NoEnumDefs) { diff --git a/tools/llvm-pdbutil/PrettyFunctionDumper.cpp b/tools/llvm-pdbutil/PrettyFunctionDumper.cpp index 177d8a009a2b..836ede41054e 100644 --- a/tools/llvm-pdbutil/PrettyFunctionDumper.cpp +++ b/tools/llvm-pdbutil/PrettyFunctionDumper.cpp @@ -53,7 +53,10 @@ FunctionDumper::FunctionDumper(LinePrinter &P) void FunctionDumper::start(const PDBSymbolTypeFunctionSig &Symbol, const char *Name, PointerType Pointer) { auto ReturnType = Symbol.getReturnType(); - ReturnType->dump(*this); + if (!ReturnType) + Printer << "<unknown-type>"; + else + ReturnType->dump(*this); Printer << " "; uint32_t ClassParentId = Symbol.getClassParentId(); auto ClassParent = @@ -225,9 +228,10 @@ void FunctionDumper::dump(const PDBSymbolTypeFunctionArg &Symbol) { // through to the real thing and dump it. uint32_t TypeId = Symbol.getTypeId(); auto Type = Symbol.getSession().getSymbolById(TypeId); - if (!Type) - return; - Type->dump(*this); + if (Type) + Printer << "<unknown-type>"; + else + Type->dump(*this); } void FunctionDumper::dump(const PDBSymbolTypeTypedef &Symbol) { diff --git a/tools/llvm-pdbutil/PrettyTypeDumper.cpp b/tools/llvm-pdbutil/PrettyTypeDumper.cpp index 663a608fe429..daf3cd45b327 100644 --- a/tools/llvm-pdbutil/PrettyTypeDumper.cpp +++ b/tools/llvm-pdbutil/PrettyTypeDumper.cpp @@ -13,13 +13,17 @@ #include "PrettyBuiltinDumper.h" #include "PrettyClassDefinitionDumper.h" #include "PrettyEnumDumper.h" +#include "PrettyFunctionDumper.h" #include "PrettyTypedefDumper.h" #include "llvm-pdbutil.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBSymbolExe.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeArray.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeBuiltin.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypePointer.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "llvm/DebugInfo/PDB/UDTLayout.h" @@ -128,36 +132,85 @@ filterAndSortClassDefs(LinePrinter &Printer, Enumerator &E, } if (Comp) - llvm::sort(Filtered.begin(), Filtered.end(), Comp); + llvm::sort(Filtered, Comp); return Filtered; } TypeDumper::TypeDumper(LinePrinter &P) : PDBSymDumper(true), Printer(P) {} -void TypeDumper::start(const PDBSymbolExe &Exe) { - if (opts::pretty::Enums) { - if (auto Enums = Exe.findAllChildren<PDBSymbolTypeEnum>()) { +template <typename T> +static bool isTypeExcluded(LinePrinter &Printer, const T &Symbol) { + return false; +} + +static bool isTypeExcluded(LinePrinter &Printer, + const PDBSymbolTypeEnum &Enum) { + if (Printer.IsTypeExcluded(Enum.getName(), Enum.getLength())) + return true; + // Dump member enums when dumping their class definition. + if (nullptr != Enum.getClassParent()) + return true; + return false; +} + +static bool isTypeExcluded(LinePrinter &Printer, + const PDBSymbolTypeTypedef &Typedef) { + return Printer.IsTypeExcluded(Typedef.getName(), Typedef.getLength()); +} + +template <typename SymbolT> +static void dumpSymbolCategory(LinePrinter &Printer, const PDBSymbolExe &Exe, + TypeDumper &TD, StringRef Label) { + if (auto Children = Exe.findAllChildren<SymbolT>()) { + Printer.NewLine(); + WithColor(Printer, PDB_ColorItem::Identifier).get() << Label; + Printer << ": (" << Children->getChildCount() << " items)"; + Printer.Indent(); + while (auto Child = Children->getNext()) { + if (isTypeExcluded(Printer, *Child)) + continue; + Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Identifier).get() << "Enums"; - Printer << ": (" << Enums->getChildCount() << " items)"; - Printer.Indent(); - while (auto Enum = Enums->getNext()) - Enum->dump(*this); - Printer.Unindent(); + Child->dump(TD); } + Printer.Unindent(); } +} - if (opts::pretty::Typedefs) { - if (auto Typedefs = Exe.findAllChildren<PDBSymbolTypeTypedef>()) { - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Identifier).get() << "Typedefs"; - Printer << ": (" << Typedefs->getChildCount() << " items)"; - Printer.Indent(); - while (auto Typedef = Typedefs->getNext()) - Typedef->dump(*this); - Printer.Unindent(); - } +static void printClassDecl(LinePrinter &Printer, + const PDBSymbolTypeUDT &Class) { + if (Class.getUnmodifiedTypeId() != 0) { + if (Class.isConstType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "const "; + if (Class.isVolatileType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "volatile "; + if (Class.isUnalignedType()) + WithColor(Printer, PDB_ColorItem::Keyword).get() << "unaligned "; } + WithColor(Printer, PDB_ColorItem::Keyword).get() << Class.getUdtKind() << " "; + WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); +} + +void TypeDumper::start(const PDBSymbolExe &Exe) { + if (opts::pretty::Enums) + dumpSymbolCategory<PDBSymbolTypeEnum>(Printer, Exe, *this, "Enums"); + + if (opts::pretty::Funcsigs) + dumpSymbolCategory<PDBSymbolTypeFunctionSig>(Printer, Exe, *this, + "Function Signatures"); + + if (opts::pretty::Typedefs) + dumpSymbolCategory<PDBSymbolTypeTypedef>(Printer, Exe, *this, "Typedefs"); + + if (opts::pretty::Arrays) + dumpSymbolCategory<PDBSymbolTypeArray>(Printer, Exe, *this, "Arrays"); + + if (opts::pretty::Pointers) + dumpSymbolCategory<PDBSymbolTypePointer>(Printer, Exe, *this, "Pointers"); + + if (opts::pretty::VTShapes) + dumpSymbolCategory<PDBSymbolTypeVTableShape>(Printer, Exe, *this, + "VFTable Shapes"); if (opts::pretty::Classes) { if (auto Classes = Exe.findAllChildren<PDBSymbolTypeUDT>()) { @@ -196,11 +249,16 @@ void TypeDumper::start(const PDBSymbolExe &Exe) { dumpClassLayout(*Class); } else { while (auto Class = Classes->getNext()) { - if (Class->getUnmodifiedTypeId() != 0) + if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) continue; - if (Printer.IsTypeExcluded(Class->getName(), Class->getLength())) + // No point duplicating a full class layout. Just print the modified + // declaration and continue. + if (Class->getUnmodifiedTypeId() != 0) { + Printer.NewLine(); + printClassDecl(Printer, *Class); continue; + } auto Layout = llvm::make_unique<ClassLayout>(std::move(Class)); if (Layout->deepPaddingSize() < opts::pretty::PaddingThreshold) @@ -218,35 +276,83 @@ void TypeDumper::start(const PDBSymbolExe &Exe) { void TypeDumper::dump(const PDBSymbolTypeEnum &Symbol) { assert(opts::pretty::Enums); - if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) - return; - // Dump member enums when dumping their class definition. - if (nullptr != Symbol.getClassParent()) - return; - - Printer.NewLine(); EnumDumper Dumper(Printer); Dumper.start(Symbol); } +void TypeDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { + BuiltinDumper BD(Printer); + BD.start(Symbol); +} + +void TypeDumper::dump(const PDBSymbolTypeUDT &Symbol) { + printClassDecl(Printer, Symbol); +} + void TypeDumper::dump(const PDBSymbolTypeTypedef &Symbol) { assert(opts::pretty::Typedefs); - if (Printer.IsTypeExcluded(Symbol.getName(), Symbol.getLength())) - return; - - Printer.NewLine(); TypedefDumper Dumper(Printer); Dumper.start(Symbol); } +void TypeDumper::dump(const PDBSymbolTypeArray &Symbol) { + auto ElementType = Symbol.getElementType(); + + ElementType->dump(*this); + Printer << "["; + WithColor(Printer, PDB_ColorItem::LiteralValue).get() << Symbol.getCount(); + Printer << "]"; +} + +void TypeDumper::dump(const PDBSymbolTypeFunctionSig &Symbol) { + FunctionDumper Dumper(Printer); + Dumper.start(Symbol, nullptr, FunctionDumper::PointerType::None); +} + +void TypeDumper::dump(const PDBSymbolTypePointer &Symbol) { + std::unique_ptr<PDBSymbol> P = Symbol.getPointeeType(); + + if (auto *FS = dyn_cast<PDBSymbolTypeFunctionSig>(P.get())) { + FunctionDumper Dumper(Printer); + FunctionDumper::PointerType PT = + Symbol.isReference() ? FunctionDumper::PointerType::Reference + : FunctionDumper::PointerType::Pointer; + Dumper.start(*FS, nullptr, PT); + return; + } + + if (auto *UDT = dyn_cast<PDBSymbolTypeUDT>(P.get())) { + printClassDecl(Printer, *UDT); + } else if (P) { + P->dump(*this); + } + + if (auto Parent = Symbol.getClassParent()) { + auto UDT = llvm::unique_dyn_cast<PDBSymbolTypeUDT>(std::move(Parent)); + if (UDT) + Printer << " " << UDT->getName() << "::"; + } + + if (Symbol.isReference()) + Printer << "&"; + else if (Symbol.isRValueReference()) + Printer << "&&"; + else + Printer << "*"; +} + +void TypeDumper::dump(const PDBSymbolTypeVTableShape &Symbol) { + Printer.format("<vtshape ({0} methods)>", Symbol.getCount()); +} + void TypeDumper::dumpClassLayout(const ClassLayout &Class) { assert(opts::pretty::Classes); if (opts::pretty::ClassFormat == opts::pretty::ClassDefinitionFormat::None) { - Printer.NewLine(); - WithColor(Printer, PDB_ColorItem::Keyword).get() << "class "; - WithColor(Printer, PDB_ColorItem::Identifier).get() << Class.getName(); + WithColor(Printer, PDB_ColorItem::Keyword).get() + << Class.getClass().getUdtKind() << " "; + WithColor(Printer, PDB_ColorItem::Type).get() << Class.getName(); } else { ClassDefinitionDumper Dumper(Printer); Dumper.start(Class); diff --git a/tools/llvm-pdbutil/PrettyTypeDumper.h b/tools/llvm-pdbutil/PrettyTypeDumper.h index 68a2f0246eba..36e586fea7e3 100644 --- a/tools/llvm-pdbutil/PrettyTypeDumper.h +++ b/tools/llvm-pdbutil/PrettyTypeDumper.h @@ -25,6 +25,12 @@ public: void dump(const PDBSymbolTypeEnum &Symbol) override; void dump(const PDBSymbolTypeTypedef &Symbol) override; + void dump(const PDBSymbolTypeFunctionSig &Symbol) override; + void dump(const PDBSymbolTypeArray &Symbol) override; + void dump(const PDBSymbolTypeBuiltin &Symbol) override; + void dump(const PDBSymbolTypePointer &Symbol) override; + void dump(const PDBSymbolTypeVTableShape &Symbol) override; + void dump(const PDBSymbolTypeUDT &Symbol) override; void dumpClassLayout(const ClassLayout &Class); diff --git a/tools/llvm-pdbutil/PrettyTypedefDumper.cpp b/tools/llvm-pdbutil/PrettyTypedefDumper.cpp index 65443d6bca90..2b3f3691ed98 100644 --- a/tools/llvm-pdbutil/PrettyTypedefDumper.cpp +++ b/tools/llvm-pdbutil/PrettyTypedefDumper.cpp @@ -12,6 +12,7 @@ #include "LinePrinter.h" #include "PrettyBuiltinDumper.h" #include "PrettyFunctionDumper.h" +#include "PrettyTypeDumper.h" #include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" @@ -35,7 +36,10 @@ void TypedefDumper::start(const PDBSymbolTypeTypedef &Symbol) { << Symbol.getName(); } -void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) {} +void TypedefDumper::dump(const PDBSymbolTypeArray &Symbol) { + TypeDumper Dumper(Printer); + Dumper.dump(Symbol); +} void TypedefDumper::dump(const PDBSymbolTypeBuiltin &Symbol) { BuiltinDumper Dumper(Printer); diff --git a/tools/llvm-pdbutil/YAMLOutputStyle.cpp b/tools/llvm-pdbutil/YAMLOutputStyle.cpp index a7afbf1242c5..62b5c428d410 100644 --- a/tools/llvm-pdbutil/YAMLOutputStyle.cpp +++ b/tools/llvm-pdbutil/YAMLOutputStyle.cpp @@ -18,10 +18,13 @@ #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" #include "llvm/DebugInfo/MSF/MappedBlockStream.h" #include "llvm/DebugInfo/PDB/Native/DbiStream.h" +#include "llvm/DebugInfo/PDB/Native/GlobalsStream.h" #include "llvm/DebugInfo/PDB/Native/InfoStream.h" #include "llvm/DebugInfo/PDB/Native/ModuleDebugStream.h" #include "llvm/DebugInfo/PDB/Native/PDBFile.h" +#include "llvm/DebugInfo/PDB/Native/PublicsStream.h" #include "llvm/DebugInfo/PDB/Native/RawConstants.h" +#include "llvm/DebugInfo/PDB/Native/SymbolStream.h" #include "llvm/DebugInfo/PDB/Native/TpiStream.h" using namespace llvm; @@ -68,6 +71,9 @@ Error YAMLOutputStyle::dump() { if (auto EC = dumpIpiStream()) return EC; + if (auto EC = dumpPublics()) + return EC; + flush(); return Error::success(); } @@ -191,6 +197,9 @@ Error YAMLOutputStyle::dumpDbiStream() { if (!opts::pdb2yaml::DbiStream) return Error::success(); + if (!File.hasPDBDbiStream()) + return Error::success(); + auto DbiS = File.getPDBDbiStream(); if (!DbiS) return DbiS.takeError(); @@ -323,6 +332,42 @@ Error YAMLOutputStyle::dumpIpiStream() { return Error::success(); } +Error YAMLOutputStyle::dumpPublics() { + if (!opts::pdb2yaml::PublicsStream) + return Error::success(); + + Obj.PublicsStream.emplace(); + auto ExpectedPublics = File.getPDBPublicsStream(); + if (!ExpectedPublics) { + llvm::consumeError(ExpectedPublics.takeError()); + return Error::success(); + } + + PublicsStream &Publics = *ExpectedPublics; + const GSIHashTable &PublicsTable = Publics.getPublicsTable(); + + auto ExpectedSyms = File.getPDBSymbolStream(); + if (!ExpectedSyms) { + llvm::consumeError(ExpectedSyms.takeError()); + return Error::success(); + } + + BinaryStreamRef SymStream = + ExpectedSyms->getSymbolArray().getUnderlyingStream(); + for (uint32_t PubSymOff : PublicsTable) { + Expected<CVSymbol> Sym = readSymbolFromStream(SymStream, PubSymOff); + if (!Sym) + return Sym.takeError(); + auto ES = CodeViewYAML::SymbolRecord::fromCodeViewSymbol(*Sym); + if (!ES) + return ES.takeError(); + + Obj.PublicsStream->PubSyms.push_back(*ES); + } + + return Error::success(); +} + void YAMLOutputStyle::flush() { Out << Obj; outs().flush(); diff --git a/tools/llvm-pdbutil/YAMLOutputStyle.h b/tools/llvm-pdbutil/YAMLOutputStyle.h index 3690e3529d4a..a5ad3355d2ab 100644 --- a/tools/llvm-pdbutil/YAMLOutputStyle.h +++ b/tools/llvm-pdbutil/YAMLOutputStyle.h @@ -35,6 +35,7 @@ private: Error dumpDbiStream(); Error dumpTpiStream(); Error dumpIpiStream(); + Error dumpPublics(); void flush(); diff --git a/tools/llvm-pdbutil/llvm-pdbutil.cpp b/tools/llvm-pdbutil/llvm-pdbutil.cpp index 5b0d21f83db7..76f61a2a95a7 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.cpp +++ b/tools/llvm-pdbutil/llvm-pdbutil.cpp @@ -13,7 +13,6 @@ #include "llvm-pdbutil.h" -#include "Analyze.h" #include "BytesOutputStyle.h" #include "DumpOutputStyle.h" #include "ExplainOutputStyle.h" @@ -46,7 +45,6 @@ #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" #include "llvm/DebugInfo/CodeView/TypeStreamMerger.h" #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" @@ -71,6 +69,8 @@ #include "llvm/DebugInfo/PDB/PDBSymbolPublicSymbol.h" #include "llvm/DebugInfo/PDB/PDBSymbolThunk.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeEnum.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionArg.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeFunctionSig.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeTypedef.h" #include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "llvm/Support/BinaryByteStream.h" @@ -102,6 +102,9 @@ namespace opts { cl::SubCommand DumpSubcommand("dump", "Dump MSF and CodeView debug info"); cl::SubCommand BytesSubcommand("bytes", "Dump raw bytes from the PDB file"); +cl::SubCommand DiaDumpSubcommand("diadump", + "Dump debug information using a DIA-like API"); + cl::SubCommand PrettySubcommand("pretty", "Dump semantic information about types and symbols"); @@ -113,10 +116,6 @@ cl::SubCommand PdbToYamlSubcommand("pdb2yaml", "Generate a detailed YAML description of a PDB File"); -cl::SubCommand - AnalyzeSubcommand("analyze", - "Analyze various aspects of a PDB's structure"); - cl::SubCommand MergeSubcommand("merge", "Merge multiple PDBs into a single PDB"); @@ -155,6 +154,48 @@ cl::ValuesClass ChunkValues = cl::values( "Any subsection not covered by another option"), clEnumValN(ModuleSubsection::All, "all", "All known subsections")); +namespace diadump { +cl::list<std::string> InputFilenames(cl::Positional, + cl::desc("<input PDB files>"), + cl::OneOrMore, cl::sub(DiaDumpSubcommand)); + +cl::opt<bool> Native("native", cl::desc("Use native PDB reader instead of DIA"), + cl::sub(DiaDumpSubcommand)); + +static cl::opt<bool> + ShowClassHierarchy("hierarchy", cl::desc("Show lexical and class parents"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> NoSymIndexIds( + "no-ids", + cl::desc("Don't show any SymIndexId fields (overrides -hierarchy)"), + cl::sub(DiaDumpSubcommand)); + +static cl::opt<bool> + Recurse("recurse", + cl::desc("When dumping a SymIndexId, dump the full details of the " + "corresponding record"), + cl::sub(DiaDumpSubcommand)); + +static cl::opt<bool> Enums("enums", cl::desc("Dump enum types"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> Pointers("pointers", cl::desc("Dump enum types"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> UDTs("udts", cl::desc("Dump udt types"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> Compilands("compilands", + cl::desc("Dump compiland information"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> Funcsigs("funcsigs", + cl::desc("Dump function signature information"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> Arrays("arrays", cl::desc("Dump array types"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> VTShapes("vtshapes", cl::desc("Dump virtual table shapes"), + cl::sub(DiaDumpSubcommand)); +static cl::opt<bool> Typedefs("typedefs", cl::desc("Dump typedefs"), + cl::sub(DiaDumpSubcommand)); +} // namespace diadump + namespace pretty { cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input PDB files>"), @@ -201,6 +242,15 @@ cl::opt<bool> Enums("enums", cl::desc("Display enum types"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); cl::opt<bool> Typedefs("typedefs", cl::desc("Display typedef types"), cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Funcsigs("funcsigs", cl::desc("Display function signatures"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Pointers("pointers", cl::desc("Display pointer types"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> Arrays("arrays", cl::desc("Display arrays"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); +cl::opt<bool> VTShapes("vtshapes", cl::desc("Display vftable shapes"), + cl::cat(TypeCategory), cl::sub(PrettySubcommand)); + cl::opt<SymbolSortMode> SymbolOrder( "symbol-order", cl::desc("symbol sort order"), cl::init(SymbolSortMode::None), @@ -432,6 +482,12 @@ cl::opt<bool> DumpTypeExtras("type-extras", cl::desc("dump type hashes and index offsets"), cl::cat(TypeOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DontResolveForwardRefs( + "dont-resolve-forward-refs", + cl::desc("When dumping type records for classes, unions, enums, and " + "structs, don't try to resolve forward references"), + cl::cat(TypeOptions), cl::sub(DumpSubcommand)); + cl::list<uint32_t> DumpTypeIndex( "type-index", cl::ZeroOrMore, cl::CommaSeparated, cl::desc("only dump types with the specified hexadecimal type index"), @@ -465,6 +521,11 @@ cl::opt<bool> DumpGlobals("globals", cl::desc("dump Globals symbol records"), cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); cl::opt<bool> DumpGlobalExtras("global-extras", cl::desc("dump Globals hashes"), cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); +cl::list<std::string> DumpGlobalNames( + "global-name", + cl::desc( + "With -globals, only dump globals whose name matches the given value"), + cl::cat(SymbolOptions), cl::sub(DumpSubcommand), cl::ZeroOrMore); cl::opt<bool> DumpPublics("publics", cl::desc("dump Publics stream data"), cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); cl::opt<bool> DumpPublicExtras("public-extras", @@ -482,6 +543,9 @@ cl::opt<bool> cl::desc("dump CodeView symbol record raw bytes"), cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); +cl::opt<bool> DumpFpo("fpo", cl::desc("dump FPO records"), + cl::cat(SymbolOptions), cl::sub(DumpSubcommand)); + // MODULE & FILE OPTIONS cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"), cl::cat(FileOptions), cl::sub(DumpSubcommand)); @@ -594,6 +658,10 @@ cl::opt<bool> IpiStream("ipi-stream", cl::desc("Dump the IPI Stream (Stream 5)"), cl::sub(PdbToYamlSubcommand), cl::init(false)); +cl::opt<bool> PublicsStream("publics-stream", + cl::desc("Dump the Publics Stream"), + cl::sub(PdbToYamlSubcommand), cl::init(false)); + // MODULE & FILE OPTIONS cl::opt<bool> DumpModules("modules", cl::desc("dump compiland information"), cl::cat(FileOptions), cl::sub(PdbToYamlSubcommand)); @@ -613,14 +681,6 @@ cl::list<std::string> InputFilename(cl::Positional, cl::sub(PdbToYamlSubcommand)); } // namespace pdb2yaml -namespace analyze { -cl::opt<bool> StringTable("hash-collisions", cl::desc("Find hash collisions"), - cl::sub(AnalyzeSubcommand), cl::init(false)); -cl::list<std::string> InputFilename(cl::Positional, - cl::desc("<input PDB file>"), cl::Required, - cl::sub(AnalyzeSubcommand)); -} - namespace merge { cl::list<std::string> InputFilenames(cl::Positional, cl::desc("<input PDB files>"), @@ -681,7 +741,7 @@ static void yamlToPdb(StringRef Path) { /*RequiresNullTerminator=*/false); if (ErrorOrBuffer.getError()) { - ExitOnErr(make_error<GenericError>(generic_error_code::invalid_path, Path)); + ExitOnErr(createFileError(Path, errorCodeToError(ErrorOrBuffer.getError()))); } std::unique_ptr<MemoryBuffer> &Buffer = ErrorOrBuffer.get(); @@ -781,7 +841,8 @@ static void yamlToPdb(StringRef Path) { Builder.getStringTableBuilder().setStrings(*Strings.strings()); - ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile)); + codeview::GUID IgnoredOutGuid; + ExitOnErr(Builder.commit(opts::yaml2pdb::YamlPdbOutputFile, &IgnoredOutGuid)); } static PDBFile &loadPDB(StringRef Path, std::unique_ptr<IPDBSession> &Session) { @@ -817,14 +878,6 @@ static void dumpBytes(StringRef Path) { ExitOnErr(O->dump()); } -static void dumpAnalysis(StringRef Path) { - std::unique_ptr<IPDBSession> Session; - auto &File = loadPDB(Path, Session); - auto O = llvm::make_unique<AnalysisStyle>(File); - - ExitOnErr(O->dump()); -} - bool opts::pretty::shouldDumpSymLevel(SymLevel Search) { if (SymTypes.empty()) return true; @@ -924,6 +977,69 @@ static void dumpInjectedSources(LinePrinter &Printer, IPDBSession &Session) { } } +template <typename OuterT, typename ChildT> +void diaDumpChildren(PDBSymbol &Outer, PdbSymbolIdField Ids, + PdbSymbolIdField Recurse) { + OuterT *ConcreteOuter = dyn_cast<OuterT>(&Outer); + if (!ConcreteOuter) + return; + + auto Children = ConcreteOuter->template findAllChildren<ChildT>(); + while (auto Child = Children->getNext()) { + outs() << " {"; + Child->defaultDump(outs(), 4, Ids, Recurse); + outs() << "\n }\n"; + } +} + +static void dumpDia(StringRef Path) { + std::unique_ptr<IPDBSession> Session; + + const auto ReaderType = + opts::diadump::Native ? PDB_ReaderType::Native : PDB_ReaderType::DIA; + ExitOnErr(loadDataForPDB(ReaderType, Path, Session)); + + auto GlobalScope = Session->getGlobalScope(); + + std::vector<PDB_SymType> SymTypes; + + if (opts::diadump::Compilands) + SymTypes.push_back(PDB_SymType::Compiland); + if (opts::diadump::Enums) + SymTypes.push_back(PDB_SymType::Enum); + if (opts::diadump::Pointers) + SymTypes.push_back(PDB_SymType::PointerType); + if (opts::diadump::UDTs) + SymTypes.push_back(PDB_SymType::UDT); + if (opts::diadump::Funcsigs) + SymTypes.push_back(PDB_SymType::FunctionSig); + if (opts::diadump::Arrays) + SymTypes.push_back(PDB_SymType::ArrayType); + if (opts::diadump::VTShapes) + SymTypes.push_back(PDB_SymType::VTableShape); + if (opts::diadump::Typedefs) + SymTypes.push_back(PDB_SymType::Typedef); + PdbSymbolIdField Ids = opts::diadump::NoSymIndexIds ? PdbSymbolIdField::None + : PdbSymbolIdField::All; + + PdbSymbolIdField Recurse = PdbSymbolIdField::None; + if (opts::diadump::Recurse) + Recurse = PdbSymbolIdField::All; + if (!opts::diadump::ShowClassHierarchy) + Ids &= ~(PdbSymbolIdField::ClassParent | PdbSymbolIdField::LexicalParent); + + for (PDB_SymType ST : SymTypes) { + auto Children = GlobalScope->findAllChildren(ST); + while (auto Child = Children->getNext()) { + outs() << "{"; + Child->defaultDump(outs(), 2, Ids, Recurse); + + diaDumpChildren<PDBSymbolTypeEnum, PDBSymbolData>(*Child, Ids, Recurse); + outs() << "\n}\n"; + } + } +} + static void dumpPretty(StringRef Path) { std::unique_ptr<IPDBSession> Session; @@ -1055,7 +1171,9 @@ static void dumpPretty(StringRef Path) { Printer.NewLine(); WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---COMPILANDS---"; - if (auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>()) { + auto Compilands = GlobalScope->findAllChildren<PDBSymbolCompiland>(); + + if (Compilands) { Printer.Indent(); CompilandDumper Dumper(Printer); CompilandDumpFlags options = CompilandDumper::Flags::None; @@ -1067,7 +1185,9 @@ static void dumpPretty(StringRef Path) { } } - if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs) { + if (opts::pretty::Classes || opts::pretty::Enums || opts::pretty::Typedefs || + opts::pretty::Funcsigs || opts::pretty::Pointers || + opts::pretty::Arrays || opts::pretty::VTShapes) { Printer.NewLine(); WithColor(Printer, PDB_ColorItem::SectionHeader).get() << "---TYPES---"; Printer.Indent(); @@ -1104,8 +1224,7 @@ static void dumpPretty(StringRef Path) { 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); + llvm::sort(Funcs, opts::pretty::compareFunctionSymbols); for (const auto &Func : Funcs) { Printer.NewLine(); Dumper.start(*Func, FunctionDumper::PointerType::None); @@ -1123,8 +1242,7 @@ static void dumpPretty(StringRef Path) { 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); + llvm::sort(Datas, opts::pretty::compareDataSymbols); for (const auto &Var : Datas) Dumper.start(*Var); } @@ -1162,6 +1280,7 @@ static void dumpPretty(StringRef Path) { dumpInjectedSources(Printer, *Session); } + Printer.NewLine(); outs().flush(); } @@ -1211,7 +1330,9 @@ static void mergePdbs() { OutFile = opts::merge::InputFilenames[0]; llvm::sys::path::replace_extension(OutFile, "merged.pdb"); } - ExitOnErr(Builder.commit(OutFile)); + + codeview::GUID IgnoredOutGuid; + ExitOnErr(Builder.commit(OutFile, &IgnoredOutGuid)); } static void explain() { @@ -1323,6 +1444,7 @@ int main(int Argc, const char **Argv) { if (opts::DumpSubcommand) { if (opts::dump::RawAll) { opts::dump::DumpGlobals = true; + opts::dump::DumpFpo = true; opts::dump::DumpInlineeLines = true; opts::dump::DumpIds = true; opts::dump::DumpIdExtras = true; @@ -1356,6 +1478,7 @@ int main(int Argc, const char **Argv) { opts::pdb2yaml::DbiStream = true; opts::pdb2yaml::TpiStream = true; opts::pdb2yaml::IpiStream = true; + opts::pdb2yaml::PublicsStream = true; opts::pdb2yaml::DumpModules = true; opts::pdb2yaml::DumpModuleFiles = true; opts::pdb2yaml::DumpModuleSyms = true; @@ -1382,8 +1505,8 @@ int main(int Argc, const char **Argv) { opts::yaml2pdb::YamlPdbOutputFile = OutputFilename.str(); } yamlToPdb(opts::yaml2pdb::InputFilename); - } else if (opts::AnalyzeSubcommand) { - dumpAnalysis(opts::analyze::InputFilename.front()); + } else if (opts::DiaDumpSubcommand) { + llvm::for_each(opts::diadump::InputFilenames, dumpDia); } else if (opts::PrettySubcommand) { if (opts::pretty::Lines) opts::pretty::Compilands = true; @@ -1401,6 +1524,8 @@ int main(int Argc, const char **Argv) { opts::pretty::Classes = true; opts::pretty::Typedefs = true; opts::pretty::Enums = true; + opts::pretty::Pointers = true; + opts::pretty::Funcsigs = true; } // When adding filters for excluded compilands and types, we need to diff --git a/tools/llvm-pdbutil/llvm-pdbutil.h b/tools/llvm-pdbutil/llvm-pdbutil.h index 7496adaeb62f..a57cc51d7fd7 100644 --- a/tools/llvm-pdbutil/llvm-pdbutil.h +++ b/tools/llvm-pdbutil/llvm-pdbutil.h @@ -82,7 +82,11 @@ extern llvm::cl::opt<bool> Symbols; extern llvm::cl::opt<bool> Globals; extern llvm::cl::opt<bool> Classes; extern llvm::cl::opt<bool> Enums; +extern llvm::cl::opt<bool> Funcsigs; +extern llvm::cl::opt<bool> Arrays; extern llvm::cl::opt<bool> Typedefs; +extern llvm::cl::opt<bool> Pointers; +extern llvm::cl::opt<bool> VTShapes; extern llvm::cl::opt<bool> All; extern llvm::cl::opt<bool> ExcludeCompilerGenerated; @@ -160,10 +164,12 @@ extern llvm::cl::opt<bool> DumpIdExtras; extern llvm::cl::list<uint32_t> DumpIdIndex; extern llvm::cl::opt<uint32_t> DumpModi; extern llvm::cl::opt<bool> JustMyCode; +extern llvm::cl::opt<bool> DontResolveForwardRefs; 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::list<std::string> DumpGlobalNames; extern llvm::cl::opt<bool> DumpGlobalExtras; extern llvm::cl::opt<bool> DumpPublics; extern llvm::cl::opt<bool> DumpPublicExtras; @@ -171,6 +177,7 @@ extern llvm::cl::opt<bool> DumpSectionContribs; extern llvm::cl::opt<bool> DumpSectionMap; extern llvm::cl::opt<bool> DumpModules; extern llvm::cl::opt<bool> DumpModuleFiles; +extern llvm::cl::opt<bool> DumpFpo; extern llvm::cl::opt<bool> RawAll; } @@ -185,6 +192,7 @@ extern llvm::cl::opt<bool> PdbStream; extern llvm::cl::opt<bool> DbiStream; extern llvm::cl::opt<bool> TpiStream; extern llvm::cl::opt<bool> IpiStream; +extern llvm::cl::opt<bool> PublicsStream; extern llvm::cl::list<std::string> InputFilename; extern llvm::cl::opt<bool> DumpModules; extern llvm::cl::opt<bool> DumpModuleFiles; diff --git a/tools/llvm-profdata/llvm-profdata.cpp b/tools/llvm-profdata/llvm-profdata.cpp index 1a0b9e127bbc..c25cbc2b64df 100644 --- a/tools/llvm-profdata/llvm-profdata.cpp +++ b/tools/llvm-profdata/llvm-profdata.cpp @@ -123,6 +123,47 @@ static void handleMergeWriterError(Error E, StringRef WhenceFile = "", } } +namespace { +/// A remapper from original symbol names to new symbol names based on a file +/// containing a list of mappings from old name to new name. +class SymbolRemapper { + std::unique_ptr<MemoryBuffer> File; + DenseMap<StringRef, StringRef> RemappingTable; + +public: + /// Build a SymbolRemapper from a file containing a list of old/new symbols. + static std::unique_ptr<SymbolRemapper> create(StringRef InputFile) { + auto BufOrError = MemoryBuffer::getFileOrSTDIN(InputFile); + if (!BufOrError) + exitWithErrorCode(BufOrError.getError(), InputFile); + + auto Remapper = llvm::make_unique<SymbolRemapper>(); + Remapper->File = std::move(BufOrError.get()); + + for (line_iterator LineIt(*Remapper->File, /*SkipBlanks=*/true, '#'); + !LineIt.is_at_eof(); ++LineIt) { + std::pair<StringRef, StringRef> Parts = LineIt->split(' '); + if (Parts.first.empty() || Parts.second.empty() || + Parts.second.count(' ')) { + exitWithError("unexpected line in remapping file", + (InputFile + ":" + Twine(LineIt.line_number())).str(), + "expected 'old_symbol new_symbol'"); + } + Remapper->RemappingTable.insert(Parts); + } + return Remapper; + } + + /// Attempt to map the given old symbol into a new symbol. + /// + /// \return The new symbol, or \p Name if no such symbol was found. + StringRef operator()(StringRef Name) { + StringRef New = RemappingTable.lookup(Name); + return New.empty() ? Name : New; + } +}; +} + struct WeightedFile { std::string Filename; uint64_t Weight; @@ -161,7 +202,8 @@ static bool isFatalError(instrprof_error IPE) { } /// Load an input into a writer context. -static void loadInput(const WeightedFile &Input, WriterContext *WC) { +static void loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, + WriterContext *WC) { std::unique_lock<std::mutex> CtxGuard{WC->Lock}; // If there's a pending hard error, don't do more work. @@ -192,6 +234,8 @@ static void loadInput(const WeightedFile &Input, WriterContext *WC) { } for (auto &I : *Reader) { + if (Remapper) + I.Name = (*Remapper)(I.Name); const StringRef FuncName = I.Name; bool Reported = false; WC->Writer.addRecord(std::move(I), Input.Weight, [&](Error E) { @@ -236,6 +280,7 @@ static void mergeWriterContexts(WriterContext *Dst, WriterContext *Src) { } static void mergeInstrProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat, bool OutputSparse, unsigned NumThreads) { @@ -267,14 +312,14 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, if (NumThreads == 1) { for (const auto &Input : Inputs) - loadInput(Input, Contexts[0].get()); + loadInput(Input, Remapper, Contexts[0].get()); } else { ThreadPool Pool(NumThreads); // Load the inputs in parallel (N/NumThreads serial steps). unsigned Ctx = 0; for (const auto &Input : Inputs) { - Pool.async(loadInput, Input, Contexts[Ctx].get()); + Pool.async(loadInput, Input, Remapper, Contexts[Ctx].get()); Ctx = (Ctx + 1) % NumThreads; } Pool.wait(); @@ -322,11 +367,43 @@ static void mergeInstrProfile(const WeightedFileVector &Inputs, } } +/// Make a copy of the given function samples with all symbol names remapped +/// by the provided symbol remapper. +static sampleprof::FunctionSamples +remapSamples(const sampleprof::FunctionSamples &Samples, + SymbolRemapper &Remapper, sampleprof_error &Error) { + sampleprof::FunctionSamples Result; + Result.setName(Remapper(Samples.getName())); + Result.addTotalSamples(Samples.getTotalSamples()); + Result.addHeadSamples(Samples.getHeadSamples()); + for (const auto &BodySample : Samples.getBodySamples()) { + Result.addBodySamples(BodySample.first.LineOffset, + BodySample.first.Discriminator, + BodySample.second.getSamples()); + for (const auto &Target : BodySample.second.getCallTargets()) { + Result.addCalledTargetSamples(BodySample.first.LineOffset, + BodySample.first.Discriminator, + Remapper(Target.first()), Target.second); + } + } + for (const auto &CallsiteSamples : Samples.getCallsiteSamples()) { + sampleprof::FunctionSamplesMap &Target = + Result.functionSamplesAt(CallsiteSamples.first); + for (const auto &Callsite : CallsiteSamples.second) { + sampleprof::FunctionSamples Remapped = + remapSamples(Callsite.second, Remapper, Error); + MergeResult(Error, Target[Remapped.getName()].merge(Remapped)); + } + } + return Result; +} + static sampleprof::SampleProfileFormat FormatMap[] = { sampleprof::SPF_None, sampleprof::SPF_Text, sampleprof::SPF_Compact_Binary, sampleprof::SPF_GCC, sampleprof::SPF_Binary}; static void mergeSampleProfile(const WeightedFileVector &Inputs, + SymbolRemapper *Remapper, StringRef OutputFilename, ProfileFormat OutputFormat) { using namespace sampleprof; @@ -357,9 +434,13 @@ static void mergeSampleProfile(const WeightedFileVector &Inputs, for (StringMap<FunctionSamples>::iterator I = Profiles.begin(), E = Profiles.end(); I != E; ++I) { - StringRef FName = I->first(); - FunctionSamples &Samples = I->second; - sampleprof_error Result = ProfileMap[FName].merge(Samples, Input.Weight); + sampleprof_error Result = sampleprof_error::success; + FunctionSamples Remapped = + Remapper ? remapSamples(I->second, *Remapper, Result) + : FunctionSamples(); + FunctionSamples &Samples = Remapper ? Remapped : I->second; + StringRef FName = Samples.getName(); + MergeResult(Result, ProfileMap[FName].merge(Samples, Input.Weight)); if (Result != sampleprof_error::success) { std::error_code EC = make_error_code(Result); handleMergeWriterError(errorCodeToError(EC), Input.Filename, FName); @@ -461,6 +542,10 @@ static int merge_main(int argc, const char *argv[]) { cl::opt<bool> DumpInputFileList( "dump-input-file-list", cl::init(false), cl::Hidden, cl::desc("Dump the list of input files and their weights, then exit")); + cl::opt<std::string> RemappingFile("remapping-file", cl::value_desc("file"), + cl::desc("Symbol remapping file")); + cl::alias RemappingFileA("r", cl::desc("Alias for --remapping-file"), + cl::aliasopt(RemappingFile)); cl::opt<std::string> OutputFilename("output", cl::value_desc("output"), cl::init("-"), cl::Required, cl::desc("Output file")); @@ -509,11 +594,16 @@ static int merge_main(int argc, const char *argv[]) { return 0; } + std::unique_ptr<SymbolRemapper> Remapper; + if (!RemappingFile.empty()) + Remapper = SymbolRemapper::create(RemappingFile); + if (ProfileKind == instr) - mergeInstrProfile(WeightedInputs, OutputFilename, OutputFormat, - OutputSparse, NumThreads); + mergeInstrProfile(WeightedInputs, Remapper.get(), OutputFilename, + OutputFormat, OutputSparse, NumThreads); else - mergeSampleProfile(WeightedInputs, OutputFilename, OutputFormat); + mergeSampleProfile(WeightedInputs, Remapper.get(), OutputFilename, + OutputFormat); return 0; } @@ -543,13 +633,21 @@ static void traverseAllValueSites(const InstrProfRecord &Func, uint32_t VK, Stats.ValueSitesHistogram.resize(NV, 0); Stats.ValueSitesHistogram[NV - 1]++; } + + uint64_t SiteSum = 0; + for (uint32_t V = 0; V < NV; V++) + SiteSum += VD[V].Count; + if (SiteSum == 0) + SiteSum = 1; + for (uint32_t V = 0; V < NV; V++) { - OS << "\t[ " << I << ", "; + OS << "\t[ " << format("%2u", I) << ", "; if (Symtab == nullptr) - OS << VD[V].Value; + OS << format("%4u", VD[V].Value); else OS << Symtab->getFuncName(VD[V].Value); - OS << ", " << VD[V].Count << " ]\n"; + OS << ", " << format("%10" PRId64, VD[V].Count) << " ] (" + << format("%.2f%%", (VD[V].Count * 100.0 / SiteSum)) << ")\n"; } } } @@ -572,9 +670,9 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, uint32_t TopN, bool ShowIndirectCallTargets, bool ShowMemOPSizes, bool ShowDetailedSummary, std::vector<uint32_t> DetailedSummaryCutoffs, - bool ShowAllFunctions, - const std::string &ShowFunction, bool TextFormat, - raw_fd_ostream &OS) { + bool ShowAllFunctions, uint64_t ValueCutoff, + bool OnlyListBelow, const std::string &ShowFunction, + bool TextFormat, raw_fd_ostream &OS) { auto ReaderOrErr = InstrProfReader::create(Filename); std::vector<uint32_t> Cutoffs = std::move(DetailedSummaryCutoffs); if (ShowDetailedSummary && Cutoffs.empty()) { @@ -587,6 +685,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, auto Reader = std::move(ReaderOrErr.get()); bool IsIRInstr = Reader->isIRLevelProfile(); size_t ShownFunctions = 0; + size_t BelowCutoffFunctions = 0; int NumVPKind = IPVK_Last - IPVK_First + 1; std::vector<ValueSitesStats> VPStats(NumVPKind); @@ -600,12 +699,21 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, decltype(MinCmp)> HottestFuncs(MinCmp); + if (!TextFormat && OnlyListBelow) { + OS << "The list of functions with the maximum counter less than " + << ValueCutoff << ":\n"; + } + + // Add marker so that IR-level instrumentation round-trips properly. + if (TextFormat && IsIRInstr) + OS << ":ir\n"; + for (const auto &Func : *Reader) { bool Show = ShowAllFunctions || (!ShowFunction.empty() && Func.Name.find(ShowFunction) != Func.Name.npos); - bool doTextFormatDump = (Show && ShowCounts && TextFormat); + bool doTextFormatDump = (Show && TextFormat); if (doTextFormatDump) { InstrProfSymtab &Symtab = Reader->getSymtab(); @@ -617,11 +725,24 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, assert(Func.Counts.size() > 0 && "function missing entry counter"); Builder.addRecord(Func); - if (TopN) { - uint64_t FuncMax = 0; - for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) - FuncMax = std::max(FuncMax, Func.Counts[I]); + uint64_t FuncMax = 0; + uint64_t FuncSum = 0; + for (size_t I = 0, E = Func.Counts.size(); I < E; ++I) { + FuncMax = std::max(FuncMax, Func.Counts[I]); + FuncSum += Func.Counts[I]; + } + if (FuncMax < ValueCutoff) { + ++BelowCutoffFunctions; + if (OnlyListBelow) { + OS << " " << Func.Name << ": (Max = " << FuncMax + << " Sum = " << FuncSum << ")\n"; + } + continue; + } else if (OnlyListBelow) + continue; + + if (TopN) { if (HottestFuncs.size() == TopN) { if (HottestFuncs.top().second < FuncMax) { HottestFuncs.pop(); @@ -632,7 +753,6 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, } if (Show) { - if (!ShownFunctions) OS << "Counters:\n"; @@ -679,7 +799,7 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, if (Reader->hasError()) exitWithError(Reader->getError(), Filename); - if (ShowCounts && TextFormat) + if (TextFormat) return 0; std::unique_ptr<ProfileSummary> PS(Builder.getSummary()); OS << "Instrumentation level: " @@ -687,6 +807,12 @@ static int showInstrProfile(const std::string &Filename, bool ShowCounts, if (ShowAllFunctions || !ShowFunction.empty()) OS << "Functions shown: " << ShownFunctions << "\n"; OS << "Total functions: " << PS->getNumFunctions() << "\n"; + if (ValueCutoff > 0) { + OS << "Number of functions with maximum count (< " << ValueCutoff + << "): " << BelowCutoffFunctions << "\n"; + OS << "Number of functions with maximum count (>= " << ValueCutoff + << "): " << PS->getNumFunctions() - BelowCutoffFunctions << "\n"; + } OS << "Maximum function count: " << PS->getMaxFunctionCount() << "\n"; OS << "Maximum internal block count: " << PS->getMaxInternalCount() << "\n"; @@ -788,7 +914,14 @@ static int show_main(int argc, const char *argv[]) { cl::opt<uint32_t> TopNFunctions( "topn", cl::init(0), cl::desc("Show the list of functions with the largest internal counts")); - + cl::opt<uint32_t> ValueCutoff( + "value-cutoff", cl::init(0), + cl::desc("Set the count value cutoff. Functions with the maximum count " + "less than this value will not be printed out. (Default is 0)")); + cl::opt<bool> OnlyListBelow( + "list-below-cutoff", cl::init(false), + cl::desc("Only output names of functions whose max count values are " + "below the cutoff value")); cl::ParseCommandLineOptions(argc, argv, "LLVM profile data summary\n"); if (OutputFilename.empty()) @@ -808,7 +941,8 @@ static int show_main(int argc, const char *argv[]) { return showInstrProfile(Filename, ShowCounts, TopNFunctions, ShowIndirectCallTargets, ShowMemOPSizes, ShowDetailedSummary, DetailedSummaryCutoffs, - ShowAllFunctions, ShowFunction, TextFormat, OS); + ShowAllFunctions, ValueCutoff, OnlyListBelow, + ShowFunction, TextFormat, OS); else return showSampleProfile(Filename, ShowCounts, ShowAllFunctions, ShowFunction, OS); diff --git a/tools/llvm-rc/Opts.td b/tools/llvm-rc/Opts.td index 11f40f571037..3ff5ac2d4980 100644 --- a/tools/llvm-rc/Opts.td +++ b/tools/llvm-rc/Opts.td @@ -4,7 +4,7 @@ include "llvm/Option/OptParser.td" // These options seem to be important for the tool // and should be implemented. -def FILEOUT : Separate<[ "/", "-" ], "FO">, +def FILEOUT : JoinedOrSeparate<[ "/", "-" ], "FO">, HelpText<"Change the output file location.">; def DEFINE : Separate<[ "/", "-" ], "D">, diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp index 4b561940d2ec..7fe95669083b 100644 --- a/tools/llvm-rc/ResourceFileWriter.cpp +++ b/tools/llvm-rc/ResourceFileWriter.cpp @@ -514,6 +514,11 @@ Error ResourceFileWriter::visitCharacteristicsStmt( return Error::success(); } +Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) { + ObjectData.ExStyle = Stmt->Value; + return Error::success(); +} + Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) { RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size")); RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight")); @@ -982,7 +987,8 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, padStream(sizeof(uint32_t)); auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type); - uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0); + IntWithNotMask CtlStyle(TypeInfo.Style); + CtlStyle |= Ctl.Style.getValueOr(RCInt(0)); uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0); // DIALOG(EX) item header prefix. @@ -990,7 +996,7 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, struct { ulittle32_t Style; ulittle32_t ExtStyle; - } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)}; + } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)}; writeObject(Prefix); } else { struct { @@ -998,7 +1004,7 @@ Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, ulittle32_t ExtStyle; ulittle32_t Style; } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle), - ulittle32_t(CtlStyle)}; + ulittle32_t(CtlStyle.getValue())}; writeObject(Prefix); } @@ -1065,6 +1071,7 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { UsedStyle |= StyleCaptionFlag; const uint16_t DialogExMagic = 0xFFFF; + uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0); // Write DIALOG(EX) header prefix. These are pretty different. if (!Res->IsExtended) { @@ -1083,7 +1090,7 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { ulittle32_t Style; ulittle32_t ExtStyle; } Prefix{ulittle32_t(UsedStyle), - ulittle32_t(0)}; // As of now, we don't keep EXSTYLE. + ulittle32_t(ExStyle)}; writeObject(Prefix); } else { @@ -1094,7 +1101,7 @@ Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { ulittle32_t ExtStyle; ulittle32_t Style; } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic), - ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)}; + ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)}; writeObject(Prefix); } @@ -1502,6 +1509,10 @@ ResourceFileWriter::loadFile(StringRef File) const { SmallString<128> Cwd; std::unique_ptr<MemoryBuffer> Result; + // 0. The file path is absolute and the file exists. + if (sys::path::is_absolute(File)) + return errorOrToExpected(MemoryBuffer::getFile(File, -1, false)); + // 1. The current working directory. sys::fs::current_path(Cwd); Path.assign(Cwd.begin(), Cwd.end()); @@ -1510,8 +1521,7 @@ ResourceFileWriter::loadFile(StringRef File) const { return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); // 2. The directory of the input resource file, if it is different from the - // current - // working directory. + // current working directory. StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath); Path.assign(InputFileDir.begin(), InputFileDir.end()); sys::path::append(Path, File); diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h index 4c31d018bde4..36d57da11eb6 100644 --- a/tools/llvm-rc/ResourceFileWriter.h +++ b/tools/llvm-rc/ResourceFileWriter.h @@ -63,6 +63,7 @@ public: Error visitCaptionStmt(const CaptionStmt *) override; Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; Error visitClassStmt(const ClassStmt *) override; + Error visitExStyleStmt(const ExStyleStmt *) override; Error visitFontStmt(const FontStmt *) override; Error visitLanguageStmt(const LanguageResource *) override; Error visitStyleStmt(const StyleStmt *) override; @@ -80,6 +81,7 @@ public: uint32_t VersionInfo; Optional<uint32_t> Style; + Optional<uint32_t> ExStyle; StringRef Caption; struct FontInfo { uint32_t Size; diff --git a/tools/llvm-rc/ResourceScriptParser.cpp b/tools/llvm-rc/ResourceScriptParser.cpp index 8cc0b50933c2..c66fc4fc2e70 100644 --- a/tools/llvm-rc/ResourceScriptParser.cpp +++ b/tools/llvm-rc/ResourceScriptParser.cpp @@ -114,16 +114,23 @@ void RCParser::consume() { // An integer description might consist of a single integer or // an arithmetic expression evaluating to the integer. The expressions -// can contain the following tokens: <int> ( ) + - | & ~. Their meaning -// is the same as in C++. +// can contain the following tokens: <int> ( ) + - | & ~ not. Their meaning +// is the same as in C++ except for 'not' expression. // The operators in the original RC implementation have the following // precedence: -// 1) Unary operators (- ~), +// 1) Unary operators (- ~ not), // 2) Binary operators (+ - & |), with no precedence. // +// 'not' expression is mostly useful for style values. It evaluates to 0, +// but value given to the operator is stored separately from integer value. +// It's mostly useful for control style expressions and causes bits from +// default control style to be excluded from generated style. For binary +// operators the mask from the right operand is applied to the left operand +// and masks from both operands are combined in operator result. +// // The following grammar is used to parse the expressions Exp1: // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2 -// Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). +// Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). // (More conveniently, Exp1 is a non-empty sequence of Exp2 expressions, // separated by binary operators.) // @@ -139,12 +146,15 @@ void RCParser::consume() { // 1 => 01 00, -1 => ff ff, --1 => 01 00, ---1 => ff ff; // 1 => 01 00, ~1 => fe ff, ~~1 => 01 00, ~~~1 => fe ff. -Expected<RCInt> RCParser::readInt() { return parseIntExpr1(); } +Expected<RCInt> RCParser::readInt() { + ASSIGN_OR_RETURN(Value, parseIntExpr1()); + return (*Value).getValue(); +} -Expected<RCInt> RCParser::parseIntExpr1() { +Expected<IntWithNotMask> RCParser::parseIntExpr1() { // Exp1 ::= Exp2 || Exp1 + Exp2 || Exp1 - Exp2 || Exp1 | Exp2 || Exp1 & Exp2. ASSIGN_OR_RETURN(FirstResult, parseIntExpr2()); - RCInt Result = *FirstResult; + IntWithNotMask Result = *FirstResult; while (!isEof() && look().isBinaryOp()) { auto OpToken = read(); @@ -175,8 +185,8 @@ Expected<RCInt> RCParser::parseIntExpr1() { return Result; } -Expected<RCInt> RCParser::parseIntExpr2() { - // Exp2 ::= -Exp2 || ~Exp2 || Int || (Exp1). +Expected<IntWithNotMask> RCParser::parseIntExpr2() { + // Exp2 ::= -Exp2 || ~Exp2 || not Expr2 || Int || (Exp1). static const char ErrorMsg[] = "'-', '~', integer or '('"; if (isEof()) @@ -205,6 +215,13 @@ Expected<RCInt> RCParser::parseIntExpr2() { return *Result; } + case Kind::Identifier: { + if (!read().value().equals_lower("not")) + return getExpectedError(ErrorMsg, true); + ASSIGN_OR_RETURN(Result, parseIntExpr2()); + return IntWithNotMask(0, (*Result).getValue()); + } + default: return getExpectedError(ErrorMsg); } @@ -388,6 +405,8 @@ RCParser::parseSingleOptionalStatement(OptStmtType StmtsType) { return parseCaptionStmt(); if (TypeToken->equals_lower("CLASS")) return parseClassStmt(); + if (TypeToken->equals_lower("EXSTYLE")) + return parseExStyleStmt(); if (TypeToken->equals_lower("FONT")) return parseFontStmt(StmtsType); if (TypeToken->equals_lower("STYLE")) @@ -537,13 +556,13 @@ Expected<Control> RCParser::parseControl() { RETURN_IF_ERROR(consumeType(Kind::Comma)); IntOrString Class; - Optional<uint32_t> Style; + Optional<IntWithNotMask> 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()); + ASSIGN_OR_RETURN(StyleVal, parseIntExpr1()); RETURN_IF_ERROR(consumeType(Kind::Comma)); Style = *StyleVal; } else { @@ -555,7 +574,7 @@ Expected<Control> RCParser::parseControl() { if (ClassUpper != "CONTROL") { if (consumeOptionalType(Kind::Comma)) { - ASSIGN_OR_RETURN(Val, readInt()); + ASSIGN_OR_RETURN(Val, parseIntExpr1()); Style = *Val; } } @@ -817,6 +836,11 @@ RCParser::ParseOptionType RCParser::parseStyleStmt() { return llvm::make_unique<StyleStmt>(*Arg); } +RCParser::ParseOptionType RCParser::parseExStyleStmt() { + ASSIGN_OR_RETURN(Arg, readInt()); + return llvm::make_unique<ExStyleStmt>(*Arg); +} + Error RCParser::getExpectedError(const Twine &Message, bool IsAlreadyRead) { return make_error<ParserError>( Message, IsAlreadyRead ? std::prev(CurLoc) : CurLoc, End); diff --git a/tools/llvm-rc/ResourceScriptParser.h b/tools/llvm-rc/ResourceScriptParser.h index 3dd110f7e384..20d8ff9624d9 100644 --- a/tools/llvm-rc/ResourceScriptParser.h +++ b/tools/llvm-rc/ResourceScriptParser.h @@ -89,8 +89,8 @@ private: Expected<IntOrString> readTypeOrName(); // Parse an integer or an identifier. // Helper integer expression parsing methods. - Expected<RCInt> parseIntExpr1(); - Expected<RCInt> parseIntExpr2(); + Expected<IntWithNotMask> parseIntExpr1(); + Expected<IntWithNotMask> parseIntExpr2(); // Advance the state by one, discarding the current token. // If the discarded token had an incorrect type, fail. @@ -172,6 +172,7 @@ private: ParseOptionType parseVersionStmt(); ParseOptionType parseCaptionStmt(); ParseOptionType parseClassStmt(); + ParseOptionType parseExStyleStmt(); ParseOptionType parseFontStmt(OptStmtType DialogType); ParseOptionType parseStyleStmt(); diff --git a/tools/llvm-rc/ResourceScriptStmt.cpp b/tools/llvm-rc/ResourceScriptStmt.cpp index 728c24b36693..7b8b0def4c9a 100644 --- a/tools/llvm-rc/ResourceScriptStmt.cpp +++ b/tools/llvm-rc/ResourceScriptStmt.cpp @@ -151,7 +151,7 @@ raw_ostream &Control::log(raw_ostream &OS) const { << ", loc: (" << X << ", " << Y << "), size: [" << Width << ", " << Height << "]"; if (Style) - OS << ", style: " << *Style; + OS << ", style: " << (*Style).getValue(); if (ExtStyle) OS << ", ext. style: " << *ExtStyle; if (HelpID) @@ -283,5 +283,9 @@ raw_ostream &StyleStmt::log(raw_ostream &OS) const { return OS << "Style: " << Value << "\n"; } +raw_ostream &ExStyleStmt::log(raw_ostream &OS) const { + return OS << "ExStyle: " << Value << "\n"; +} + } // namespace rc } // namespace llvm diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index 2071ac6a9a3a..3ba1f6619eb8 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -67,6 +67,59 @@ public: } }; +class IntWithNotMask { +private: + RCInt Value; + int32_t NotMask; + +public: + IntWithNotMask() : IntWithNotMask(RCInt(0)) {} + IntWithNotMask(RCInt Value, int32_t NotMask = 0) : Value(Value), NotMask(NotMask) {} + + RCInt getValue() const { + return Value; + } + + uint32_t getNotMask() const { + return NotMask; + } + + IntWithNotMask &operator+=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value += Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask &operator-=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value -= Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask &operator|=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value |= Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask &operator&=(const IntWithNotMask &Rhs) { + Value &= ~Rhs.NotMask; + Value &= Rhs.Value; + NotMask |= Rhs.NotMask; + return *this; + } + + IntWithNotMask operator-() const { return {-Value, NotMask}; } + IntWithNotMask operator~() const { return {~Value, 0}; } + + friend raw_ostream &operator<<(raw_ostream &OS, const IntWithNotMask &Int) { + return OS << Int.Value; + } +}; + // A class holding a name - either an integer or a reference to the string. class IntOrString { private: @@ -556,7 +609,8 @@ public: StringRef Type; IntOrString Title; uint32_t ID, X, Y, Width, Height; - Optional<uint32_t> Style, ExtStyle, HelpID; + Optional<IntWithNotMask> Style; + Optional<uint32_t> ExtStyle, HelpID; IntOrString Class; // Control classes as described in DLGITEMTEMPLATEEX documentation. @@ -580,7 +634,7 @@ 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<IntWithNotMask> ItemStyle, Optional<uint32_t> ExtItemStyle, Optional<uint32_t> CtlHelpID, IntOrString CtlClass) : Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY), Width(ItemWidth), Height(ItemHeight), Style(ItemStyle), @@ -866,6 +920,19 @@ public: Error visit(Visitor *V) const override { return V->visitStyleStmt(this); } }; +// EXSTYLE optional statement. +// +// Ref: docs.microsoft.com/en-us/windows/desktop/menurc/exstyle-statement +class ExStyleStmt : public OptionalStmt { +public: + uint32_t Value; + + ExStyleStmt(uint32_t ExStyle) : Value(ExStyle) {} + raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "EXSTYLE"; } + Error visit(Visitor *V) const override { return V->visitExStyleStmt(this); } +}; + // CLASS optional statement. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380883(v=vs.85).aspx diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h index 53ef3c5cf7df..2117b12d8f5f 100644 --- a/tools/llvm-rc/ResourceVisitor.h +++ b/tools/llvm-rc/ResourceVisitor.h @@ -24,6 +24,7 @@ class RCResource; class CaptionStmt; class ClassStmt; class CharacteristicsStmt; +class ExStyleStmt; class FontStmt; class LanguageResource; class StyleStmt; @@ -46,6 +47,7 @@ public: virtual Error visitCaptionStmt(const CaptionStmt *) = 0; virtual Error visitClassStmt(const ClassStmt *) = 0; virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0; + virtual Error visitExStyleStmt(const ExStyleStmt *) = 0; virtual Error visitFontStmt(const FontStmt *) = 0; virtual Error visitLanguageStmt(const LanguageResource *) = 0; virtual Error visitStyleStmt(const StyleStmt *) = 0; diff --git a/tools/llvm-rc/llvm-rc.cpp b/tools/llvm-rc/llvm-rc.cpp index 0448f4519b4c..54997e900a23 100644 --- a/tools/llvm-rc/llvm-rc.cpp +++ b/tools/llvm-rc/llvm-rc.cpp @@ -31,6 +31,7 @@ #include "llvm/Support/Signals.h" #include "llvm/Support/raw_ostream.h" +#include <algorithm> #include <system_error> using namespace llvm; @@ -85,18 +86,23 @@ int main(int Argc, const char **Argv) { RcOptTable T; unsigned MAI, MAC; - ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, Argc - 1); + const char **DashDash = std::find_if( + Argv + 1, Argv + Argc, [](StringRef Str) { return Str == "--"; }); + ArrayRef<const char *> ArgsArr = makeArrayRef(Argv + 1, DashDash); + opt::InputArgList InputArgs = T.ParseArgs(ArgsArr, MAI, MAC); // The tool prints nothing when invoked with no command-line arguments. if (InputArgs.hasArg(OPT_HELP)) { - T.PrintHelp(outs(), "rc", "Resource Converter", false); + T.PrintHelp(outs(), "rc [options] file...", "Resource Converter", false); return 0; } const bool BeVerbose = InputArgs.hasArg(OPT_VERBOSE); std::vector<std::string> InArgsInfo = InputArgs.getAllArgValues(OPT_INPUT); + if (DashDash != Argv + Argc) + InArgsInfo.insert(InArgsInfo.end(), DashDash + 1, Argv + Argc); if (InArgsInfo.size() != 1) { fatalError("Exactly one input file should be provided."); } diff --git a/tools/llvm-readobj/ARMWinEHPrinter.cpp b/tools/llvm-readobj/ARMWinEHPrinter.cpp index a90840b22c8d..4b823b816c35 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.cpp +++ b/tools/llvm-readobj/ARMWinEHPrinter.cpp @@ -118,31 +118,57 @@ const size_t Decoder::PDataEntrySize = sizeof(RuntimeFunction); // TODO name the uops more appropriately const Decoder::RingEntry Decoder::Ring[] = { - { 0x80, 0x00, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit) - { 0xc0, 0x80, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit) - { 0xf0, 0xc0, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit) - { 0xf8, 0xd0, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit) - { 0xf8, 0xd8, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit) - { 0xf8, 0xe0, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit) - { 0xfc, 0xe8, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit) - { 0xfe, 0xec, &Decoder::opcode_1110110L }, // UOP_POP (16-bit) - { 0xff, 0xee, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit) + { 0x80, 0x00, 1, &Decoder::opcode_0xxxxxxx }, // UOP_STACK_FREE (16-bit) + { 0xc0, 0x80, 2, &Decoder::opcode_10Lxxxxx }, // UOP_POP (32-bit) + { 0xf0, 0xc0, 1, &Decoder::opcode_1100xxxx }, // UOP_STACK_SAVE (16-bit) + { 0xf8, 0xd0, 1, &Decoder::opcode_11010Lxx }, // UOP_POP (16-bit) + { 0xf8, 0xd8, 1, &Decoder::opcode_11011Lxx }, // UOP_POP (32-bit) + { 0xf8, 0xe0, 1, &Decoder::opcode_11100xxx }, // UOP_VPOP (32-bit) + { 0xfc, 0xe8, 2, &Decoder::opcode_111010xx }, // UOP_STACK_FREE (32-bit) + { 0xfe, 0xec, 2, &Decoder::opcode_1110110L }, // UOP_POP (16-bit) + { 0xff, 0xee, 2, &Decoder::opcode_11101110 }, // UOP_MICROSOFT_SPECIFIC (16-bit) // UOP_PUSH_MACHINE_FRAME // UOP_PUSH_CONTEXT // UOP_PUSH_TRAP_FRAME // UOP_REDZONE_RESTORE_LR - { 0xff, 0xef, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit) - { 0xff, 0xf5, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit) - { 0xff, 0xf6, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit) - { 0xff, 0xf7, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit) - { 0xff, 0xf8, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit) - { 0xff, 0xf9, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit) - { 0xff, 0xfa, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit) - { 0xff, 0xfb, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit) - { 0xff, 0xfc, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit) - { 0xff, 0xfd, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END - { 0xff, 0xfe, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END - { 0xff, 0xff, &Decoder::opcode_11111111 }, // UOP_END + { 0xff, 0xef, 2, &Decoder::opcode_11101111 }, // UOP_LDRPC_POSTINC (32-bit) + { 0xff, 0xf5, 2, &Decoder::opcode_11110101 }, // UOP_VPOP (32-bit) + { 0xff, 0xf6, 2, &Decoder::opcode_11110110 }, // UOP_VPOP (32-bit) + { 0xff, 0xf7, 3, &Decoder::opcode_11110111 }, // UOP_STACK_RESTORE (16-bit) + { 0xff, 0xf8, 4, &Decoder::opcode_11111000 }, // UOP_STACK_RESTORE (16-bit) + { 0xff, 0xf9, 3, &Decoder::opcode_11111001 }, // UOP_STACK_RESTORE (32-bit) + { 0xff, 0xfa, 4, &Decoder::opcode_11111010 }, // UOP_STACK_RESTORE (32-bit) + { 0xff, 0xfb, 1, &Decoder::opcode_11111011 }, // UOP_NOP (16-bit) + { 0xff, 0xfc, 1, &Decoder::opcode_11111100 }, // UOP_NOP (32-bit) + { 0xff, 0xfd, 1, &Decoder::opcode_11111101 }, // UOP_NOP (16-bit) / END + { 0xff, 0xfe, 1, &Decoder::opcode_11111110 }, // UOP_NOP (32-bit) / END + { 0xff, 0xff, 1, &Decoder::opcode_11111111 }, // UOP_END +}; + + +// Unwind opcodes for ARM64. +// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling +const Decoder::RingEntry Decoder::Ring64[] = { + { 0xe0, 0x00, 1, &Decoder::opcode_alloc_s }, + { 0xe0, 0x20, 1, &Decoder::opcode_save_r19r20_x }, + { 0xc0, 0x40, 1, &Decoder::opcode_save_fplr }, + { 0xc0, 0x80, 1, &Decoder::opcode_save_fplr_x }, + { 0xf8, 0xc0, 2, &Decoder::opcode_alloc_m }, + { 0xfc, 0xc8, 2, &Decoder::opcode_save_regp }, + { 0xfc, 0xcc, 2, &Decoder::opcode_save_regp_x }, + { 0xfc, 0xd0, 2, &Decoder::opcode_save_reg }, + { 0xfe, 0xd4, 2, &Decoder::opcode_save_reg_x }, + { 0xfe, 0xd6, 2, &Decoder::opcode_save_lrpair }, + { 0xfe, 0xd8, 2, &Decoder::opcode_save_fregp }, + { 0xfe, 0xda, 2, &Decoder::opcode_save_fregp_x }, + { 0xfe, 0xdc, 2, &Decoder::opcode_save_freg }, + { 0xff, 0xde, 2, &Decoder::opcode_save_freg_x }, + { 0xff, 0xe0, 4, &Decoder::opcode_alloc_l }, + { 0xff, 0xe1, 1, &Decoder::opcode_setfp }, + { 0xff, 0xe2, 2, &Decoder::opcode_addfp }, + { 0xff, 0xe3, 1, &Decoder::opcode_nop }, + { 0xff, 0xe4, 1, &Decoder::opcode_end }, + { 0xff, 0xe5, 1, &Decoder::opcode_end_c }, }; void Decoder::printRegisters(const std::pair<uint16_t, uint32_t> &RegisterMask) { @@ -493,18 +519,291 @@ bool Decoder::opcode_11111111(const uint8_t *OC, unsigned &Offset, return true; } +// ARM64 unwind codes start here. +bool Decoder::opcode_alloc_s(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t NumBytes = (OC[Offset] & 0x1F) << 4; + SW.startLine() << format("0x%02x ; %s sp, #%u\n", OC[Offset], + static_cast<const char *>(Prologue ? "sub" : "add"), + NumBytes); + ++Offset; + return false; +} + +bool Decoder::opcode_save_r19r20_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Off = (OC[Offset] & 0x1F) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x ; stp x19, x20, [sp, #-%u]!\n", OC[Offset], Off); + else + SW.startLine() << format( + "0x%02x ; ldp x19, x20, [sp], #%u\n", OC[Offset], Off); + ++Offset; + return false; +} + +bool Decoder::opcode_save_fplr(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Off = (OC[Offset] & 0x3F) << 3; + SW.startLine() << format( + "0x%02x ; %s x29, x30, [sp, #%u]\n", OC[Offset], + static_cast<const char *>(Prologue ? "stp" : "ldp"), Off); + ++Offset; + return false; +} + +bool Decoder::opcode_save_fplr_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Off = ((OC[Offset] & 0x3F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x ; stp x29, x30, [sp, #-%u]!\n", OC[Offset], Off); + else + SW.startLine() << format( + "0x%02x ; ldp x29, x30, [sp], #%u\n", OC[Offset], Off); + ++Offset; + return false; +} + +bool Decoder::opcode_alloc_m(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t NumBytes = ((OC[Offset] & 0x07) << 8); + NumBytes |= (OC[Offset + 1] & 0xFF); + NumBytes <<= 4; + SW.startLine() << format("0x%02x%02x ; %s sp, #%u\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "sub" : "add"), + NumBytes); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_regp(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = ((OC[Offset] & 0x03) << 8); + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 19; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format( + "0x%02x%02x ; %s x%u, x%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "stp" : "ldp"), Reg, Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_regp_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = ((OC[Offset] & 0x03) << 8); + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 19; + uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x%02x ; stp x%u, x%u, [sp, #-%u]!\n", + OC[Offset], OC[Offset + 1], Reg, + Reg + 1, Off); + else + SW.startLine() << format( + "0x%02x%02x ; ldp x%u, x%u, [sp], #%u\n", + OC[Offset], OC[Offset + 1], Reg, + Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_reg(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x03) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 19; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s x%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "str" : "ldr"), + Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_reg_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xE0); + Reg >>= 5; + Reg += 19; + uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3; + if (Prologue) + SW.startLine() << format("0x%02x%02x ; str x%u, [sp, #%u]!\n", + OC[Offset], OC[Offset + 1], Reg, Off); + else + SW.startLine() << format("0x%02x%02x ; ldr x%u, [sp], #%u\n", + OC[Offset], OC[Offset + 1], Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_lrpair(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg *= 2; + Reg += 19; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s x%u, lr, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "stp" : "ldp"), + Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_fregp(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 8; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s d%u, d%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "stp" : "ldp"), + Reg, Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_fregp_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 8; + uint32_t Off = ((OC[Offset + 1] & 0x3F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x%02x ; stp d%u, d%u, [sp, #-%u]!\n", OC[Offset], + OC[Offset + 1], Reg, Reg + 1, Off); + else + SW.startLine() << format( + "0x%02x%02x ; ldp d%u, d%u, [sp], #%u\n", OC[Offset], + OC[Offset + 1], Reg, Reg + 1, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_freg(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = (OC[Offset] & 0x01) << 8; + Reg |= (OC[Offset + 1] & 0xC0); + Reg >>= 6; + Reg += 8; + uint32_t Off = (OC[Offset + 1] & 0x3F) << 3; + SW.startLine() << format("0x%02x%02x ; %s d%u, [sp, #%u]\n", + OC[Offset], OC[Offset + 1], + static_cast<const char *>(Prologue ? "str" : "ldr"), + Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_save_freg_x(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + uint32_t Reg = ((OC[Offset + 1] & 0xE0) >> 5) + 8; + uint32_t Off = ((OC[Offset + 1] & 0x1F) + 1) << 3; + if (Prologue) + SW.startLine() << format( + "0x%02x%02x ; str d%u, [sp, #-%u]!\n", OC[Offset], + OC[Offset + 1], Reg, Off); + else + SW.startLine() << format( + "0x%02x%02x ; ldr d%u, [sp], #%u\n", OC[Offset], + OC[Offset + 1], Reg, Off); + Offset += 2; + return false; +} + +bool Decoder::opcode_alloc_l(const uint8_t *OC, unsigned &Offset, + unsigned Length, bool Prologue) { + unsigned Off = + (OC[Offset + 1] << 16) | (OC[Offset + 2] << 8) | (OC[Offset + 3] << 0); + Off <<= 4; + SW.startLine() << format( + "0x%02x%02x%02x%02x ; %s sp, #%u\n", OC[Offset], OC[Offset + 1], + OC[Offset + 2], OC[Offset + 3], + static_cast<const char *>(Prologue ? "sub" : "add"), Off); + Offset += 4; + return false; +} + +bool Decoder::opcode_setfp(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; mov fp, sp\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_addfp(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + unsigned NumBytes = OC[Offset + 1] << 3; + SW.startLine() << format("0x%02x%02x ; add fp, sp, #%u\n", + OC[Offset], OC[Offset + 1], NumBytes); + Offset += 2; + return false; +} + +bool Decoder::opcode_nop(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; nop\n", OC[Offset]); + ++Offset; + return false; +} + +bool Decoder::opcode_end(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; end\n", OC[Offset]); + ++Offset; + return true; +} + +bool Decoder::opcode_end_c(const uint8_t *OC, unsigned &Offset, unsigned Length, + bool Prologue) { + SW.startLine() << format("0x%02x ; end_c\n", OC[Offset]); + ++Offset; + return true; +} + void Decoder::decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, bool Prologue) { assert((!Prologue || Offset == 0) && "prologue should always use offset 0"); - + const RingEntry* DecodeRing = isAArch64 ? Ring64 : Ring; bool Terminated = false; for (unsigned OI = Offset, OE = Opcodes.size(); !Terminated && OI < OE; ) { for (unsigned DI = 0;; ++DI) { - if ((Opcodes[OI] & Ring[DI].Mask) == Ring[DI].Value) { - Terminated = (this->*Ring[DI].Routine)(Opcodes.data(), OI, 0, Prologue); + if ((isAArch64 && (DI >= array_lengthof(Ring64))) || + (!isAArch64 && (DI >= array_lengthof(Ring)))) { + SW.startLine() << format("0x%02x ; Bad opcode!\n", + Opcodes.data()[OI]); + ++OI; + break; + } + + if ((Opcodes[OI] & DecodeRing[DI].Mask) == DecodeRing[DI].Value) { + if (OI + DecodeRing[DI].Length > OE) { + SW.startLine() << format("Opcode 0x%02x goes past the unwind data\n", + Opcodes[OI]); + OI += DecodeRing[DI].Length; + break; + } + Terminated = + (this->*DecodeRing[DI].Routine)(Opcodes.data(), OI, 0, Prologue); break; } - assert(DI < array_lengthof(Ring) && "unhandled opcode"); } } } @@ -520,22 +819,36 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, uint64_t Offset = VA - SectionVA; const ulittle32_t *Data = reinterpret_cast<const ulittle32_t *>(Contents.data() + Offset); - const ExceptionDataRecord XData(Data); + // Sanity check to ensure that the .xdata header is present. + // A header is one or two words, followed by at least one word to describe + // the unwind codes. Applicable to both ARM and AArch64. + if (Contents.size() - Offset < 8) + report_fatal_error(".xdata must be at least 8 bytes in size"); + + const ExceptionDataRecord XData(Data, isAArch64); DictScope XRS(SW, "ExceptionData"); - SW.printNumber("FunctionLength", XData.FunctionLength() << 1); + SW.printNumber("FunctionLength", + isAArch64 ? XData.FunctionLengthInBytesAArch64() : + XData.FunctionLengthInBytesARM()); SW.printNumber("Version", XData.Vers()); SW.printBoolean("ExceptionData", XData.X()); SW.printBoolean("EpiloguePacked", XData.E()); - SW.printBoolean("Fragment", XData.F()); + if (!isAArch64) + SW.printBoolean("Fragment", XData.F()); SW.printNumber(XData.E() ? "EpilogueOffset" : "EpilogueScopes", XData.EpilogueCount()); - SW.printNumber("ByteCodeLength", - static_cast<uint64_t>(XData.CodeWords() * sizeof(uint32_t))); + uint64_t ByteCodeLength = XData.CodeWords() * sizeof(uint32_t); + SW.printNumber("ByteCodeLength", ByteCodeLength); + + if ((int64_t)(Contents.size() - Offset - 4 * HeaderWords(XData) - + (XData.E() ? 0 : XData.EpilogueCount() * 4) - + (XData.X() ? 8 : 0)) < (int64_t)ByteCodeLength) + report_fatal_error("Malformed unwind data"); if (XData.E()) { ArrayRef<uint8_t> UC = XData.UnwindByteCode(); - if (!XData.F()) { + if (isAArch64 || !XData.F()) { ListScope PS(SW, "Prologue"); decodeOpcodes(UC, 0, /*Prologue=*/true); } @@ -544,16 +857,27 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, decodeOpcodes(UC, XData.EpilogueCount(), /*Prologue=*/false); } } else { + { + ListScope PS(SW, "Prologue"); + decodeOpcodes(XData.UnwindByteCode(), 0, /*Prologue=*/true); + } ArrayRef<ulittle32_t> EpilogueScopes = XData.EpilogueScopes(); ListScope ESS(SW, "EpilogueScopes"); for (const EpilogueScope ES : EpilogueScopes) { DictScope ESES(SW, "EpilogueScope"); SW.printNumber("StartOffset", ES.EpilogueStartOffset()); - SW.printNumber("Condition", ES.Condition()); - SW.printNumber("EpilogueStartIndex", ES.EpilogueStartIndex()); + if (!isAArch64) + SW.printNumber("Condition", ES.Condition()); + SW.printNumber("EpilogueStartIndex", + isAArch64 ? ES.EpilogueStartIndexAArch64() + : ES.EpilogueStartIndexARM()); + if (ES.ES & ~0xffc3ffff) + SW.printNumber("ReservedBits", (ES.ES >> 18) & 0xF); ListScope Opcodes(SW, "Opcodes"); - decodeOpcodes(XData.UnwindByteCode(), ES.EpilogueStartIndex(), + decodeOpcodes(XData.UnwindByteCode(), + isAArch64 ? ES.EpilogueStartIndexAArch64() + : ES.EpilogueStartIndexARM(), /*Prologue=*/false); } } @@ -565,16 +889,21 @@ bool Decoder::dumpXDataRecord(const COFFObjectFile &COFF, + (XData.E() ? 0 : XData.EpilogueCount()) + XData.CodeWords(); - ErrorOr<SymbolRef> Symbol = - getRelocatedSymbol(COFF, Section, HandlerOffset * sizeof(uint32_t)); + ErrorOr<SymbolRef> Symbol = getRelocatedSymbol( + COFF, Section, Offset + HandlerOffset * sizeof(uint32_t)); if (!Symbol) Symbol = getSymbol(COFF, Address, /*FunctionOnly=*/true); + if (!Symbol) { + ListScope EHS(SW, "ExceptionHandler"); + SW.printString("Routine", "(null)"); + return true; + } Expected<StringRef> Name = Symbol->getName(); if (!Name) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(Name.takeError(), OS, ""); + logAllUnhandledErrors(Name.takeError(), OS); OS.flush(); report_fatal_error(Buf); } @@ -613,7 +942,7 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, if (!FunctionNameOrErr) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS, ""); + logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS); OS.flush(); report_fatal_error(Buf); } @@ -622,16 +951,13 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, if (!FunctionAddressOrErr) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS, ""); + logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); OS.flush(); report_fatal_error(Buf); } FunctionAddress = *FunctionAddressOrErr; } else { - const pe32_header *PEHeader; - if (COFF.getPE32Header(PEHeader)) - return false; - FunctionAddress = PEHeader->ImageBase + RF.BeginAddress; + FunctionAddress = COFF.getImageBase() + RF.BeginAddress; } SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); @@ -641,7 +967,7 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, if (!Name) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(Name.takeError(), OS, ""); + logAllUnhandledErrors(Name.takeError(), OS); OS.flush(); report_fatal_error(Buf); } @@ -650,7 +976,7 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, if (!AddressOrErr) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(AddressOrErr.takeError(), OS, ""); + logAllUnhandledErrors(AddressOrErr.takeError(), OS); OS.flush(); report_fatal_error(Buf); } @@ -666,22 +992,18 @@ bool Decoder::dumpUnpackedEntry(const COFFObjectFile &COFF, } section_iterator SI = *SIOrErr; - return dumpXDataRecord(COFF, *SI, FunctionAddress, Address); + // FIXME: Do we need to add an offset from the relocation? + return dumpXDataRecord(COFF, *SI, FunctionAddress, + RF.ExceptionInformationRVA()); } else { - const pe32_header *PEHeader; - if (COFF.getPE32Header(PEHeader)) - return false; - - uint64_t Address = PEHeader->ImageBase + RF.ExceptionInformationRVA(); + uint64_t Address = COFF.getImageBase() + RF.ExceptionInformationRVA(); SW.printString("ExceptionRecord", formatSymbol("", Address)); - ErrorOr<SectionRef> Section = - getSectionContaining(COFF, RF.ExceptionInformationRVA()); + ErrorOr<SectionRef> Section = getSectionContaining(COFF, Address); if (!Section) return false; - return dumpXDataRecord(COFF, *Section, FunctionAddress, - RF.ExceptionInformationRVA()); + return dumpXDataRecord(COFF, *Section, FunctionAddress, Address); } } @@ -703,7 +1025,7 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, if (!FunctionNameOrErr) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS, ""); + logAllUnhandledErrors(FunctionNameOrErr.takeError(), OS); OS.flush(); report_fatal_error(Buf); } @@ -712,7 +1034,7 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, if (!FunctionAddressOrErr) { std::string Buf; llvm::raw_string_ostream OS(Buf); - logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS, ""); + logAllUnhandledErrors(FunctionAddressOrErr.takeError(), OS); OS.flush(); report_fatal_error(Buf); } @@ -725,8 +1047,9 @@ bool Decoder::dumpPackedEntry(const object::COFFObjectFile &COFF, } SW.printString("Function", formatSymbol(FunctionName, FunctionAddress)); - SW.printBoolean("Fragment", - RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); + if (!isAArch64) + SW.printBoolean("Fragment", + RF.Flag() == RuntimeFunctionFlag::RFF_PackedFragment); SW.printNumber("FunctionLength", RF.FunctionLength()); SW.startLine() << "ReturnType: " << RF.Ret() << '\n'; SW.printBoolean("HomedParameters", RF.H()); @@ -749,6 +1072,10 @@ bool Decoder::dumpProcedureDataEntry(const COFFObjectFile &COFF, DictScope RFS(SW, "RuntimeFunction"); if (Entry.Flag() == RuntimeFunctionFlag::RFF_Unpacked) return dumpUnpackedEntry(COFF, Section, Offset, Index, Entry); + if (isAArch64) { + SW.startLine() << "Packed unwind data not yet supported for ARM64\n"; + return true; + } return dumpPackedEntry(COFF, Section, Offset, Index, Entry); } diff --git a/tools/llvm-readobj/ARMWinEHPrinter.h b/tools/llvm-readobj/ARMWinEHPrinter.h index 95f521702268..e271a1e6fe77 100644 --- a/tools/llvm-readobj/ARMWinEHPrinter.h +++ b/tools/llvm-readobj/ARMWinEHPrinter.h @@ -24,13 +24,16 @@ class Decoder { ScopedPrinter &SW; raw_ostream &OS; + bool isAArch64; struct RingEntry { uint8_t Mask; uint8_t Value; + uint8_t Length; bool (Decoder::*Routine)(const uint8_t *, unsigned &, unsigned, bool); }; static const RingEntry Ring[]; + static const RingEntry Ring64[]; bool opcode_0xxxxxxx(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); @@ -75,6 +78,50 @@ class Decoder { bool opcode_11111111(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, bool Prologue); + // ARM64 unwind codes start here. + bool opcode_alloc_s(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_save_r19r20_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fplr(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fplr_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_alloc_m(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_save_regp(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_regp_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_reg(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_reg_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_lrpair(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fregp(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_fregp_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_freg(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_save_freg_x(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + bool opcode_alloc_l(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_setfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_addfp(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_nop(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_end(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_end_c(const uint8_t *Opcodes, unsigned &Offset, unsigned Length, + bool Prologue); + bool opcode_save_next(const uint8_t *Opcodes, unsigned &Offset, + unsigned Length, bool Prologue); + void decodeOpcodes(ArrayRef<uint8_t> Opcodes, unsigned Offset, bool Prologue); @@ -107,7 +154,9 @@ class Decoder { const object::SectionRef Section); public: - Decoder(ScopedPrinter &SW) : SW(SW), OS(SW.getOStream()) {} + Decoder(ScopedPrinter &SW, bool isAArch64) : SW(SW), + OS(SW.getOStream()), + isAArch64(isAArch64) {} std::error_code dumpProcedureData(const object::COFFObjectFile &COFF); }; } diff --git a/tools/llvm-readobj/COFFDumper.cpp b/tools/llvm-readobj/COFFDumper.cpp index 0ed4ccd09f6f..3e2626dad118 100644 --- a/tools/llvm-readobj/COFFDumper.cpp +++ b/tools/llvm-readobj/COFFDumper.cpp @@ -50,6 +50,7 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/ScopedPrinter.h" +#include "llvm/Support/LEB128.h" #include "llvm/Support/Win64EH.h" #include "llvm/Support/raw_ostream.h" @@ -78,7 +79,7 @@ public: : ObjDumper(Writer), Obj(Obj), Writer(Writer), Types(100) {} void printFileHeaders() override; - void printSections() override; + void printSectionHeaders() override; void printRelocations() override; void printSymbols() override; void printDynamicSymbols() override; @@ -98,6 +99,7 @@ public: mergeCodeViewTypes(llvm::codeview::MergingTypeTableBuilder &CVIDs, llvm::codeview::MergingTypeTableBuilder &CVTypes) override; void printStackMap() const override; + void printAddrsig() override; private: void printSymbol(const SymbolRef &Sym); void printRelocation(const SectionRef &Section, const RelocationRef &Reloc, @@ -177,6 +179,10 @@ private: DebugStringTableSubsectionRef CVStringTable; + /// Track the compilation CPU type. S_COMPILE3 symbol records typically come + /// first, but if we don't see one, just assume an X64 CPU type. It is common. + CPUType CompilationCPUType = CPUType::X64; + ScopedPrinter &Writer; BinaryByteStream TypeContents; LazyRandomTypeCollection Types; @@ -607,8 +613,7 @@ void COFFDumper::cacheRelocations() { RelocMap[Section].push_back(Reloc); // Sort relocations by address. - llvm::sort(RelocMap[Section].begin(), RelocMap[Section].end(), - relocAddressLess); + llvm::sort(RelocMap[Section], relocAddressLess); } } @@ -749,7 +754,7 @@ void COFFDumper::printCOFFDebugDirectory() { W.printNumber("PDBAge", DebugInfo->PDB70.Age); W.printString("PDBFileName", PDBFileName); } - } else { + } else if (D.SizeOfData != 0) { // FIXME: Type values of 12 and 13 are commonly observed but are not in // the documented type enum. Figure out what they mean. ArrayRef<uint8_t> RawData; @@ -954,7 +959,7 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, StringMap<StringRef> FunctionLineTables; ListScope D(W, "CodeViewDebugInfo"); - // Print the section to allow correlation with printSections. + // Print the section to allow correlation with printSectionHeaders. W.printNumber("Section", SectionName, Obj->getSectionID(Section)); uint32_t Magic; @@ -1060,10 +1065,28 @@ void COFFDumper::printCodeViewSymbolSection(StringRef SectionName, W.printHex("LocalSize", FD.LocalSize); W.printHex("ParamsSize", FD.ParamsSize); W.printHex("MaxStackSize", FD.MaxStackSize); - W.printString("FrameFunc", FrameFunc); W.printHex("PrologSize", FD.PrologSize); W.printHex("SavedRegsSize", FD.SavedRegsSize); W.printFlags("Flags", FD.Flags, makeArrayRef(FrameDataFlags)); + + // The FrameFunc string is a small RPN program. It can be broken up into + // statements that end in the '=' operator, which assigns the value on + // the top of the stack to the previously pushed variable. Variables can + // be temporary values ($T0) or physical registers ($esp). Print each + // assignment on its own line to make these programs easier to read. + { + ListScope FFS(W, "FrameFunc"); + while (!FrameFunc.empty()) { + size_t EqOrEnd = FrameFunc.find('='); + if (EqOrEnd == StringRef::npos) + EqOrEnd = FrameFunc.size(); + else + ++EqOrEnd; + StringRef Stmt = FrameFunc.substr(0, EqOrEnd); + W.printString(Stmt); + FrameFunc = FrameFunc.drop_front(EqOrEnd).trim(); + } + } } break; } @@ -1130,7 +1153,7 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, auto CODD = llvm::make_unique<COFFObjectDumpDelegate>(*this, Section, Obj, SectionContents); CVSymbolDumper CVSD(W, Types, CodeViewContainer::ObjectFile, std::move(CODD), - opts::CodeViewSubsectionBytes); + CompilationCPUType, opts::CodeViewSubsectionBytes); CVSymbolArray Symbols; BinaryStreamReader Reader(BinaryData, llvm::support::little); if (auto EC = Reader.readArray(Symbols, Reader.getLength())) { @@ -1143,6 +1166,7 @@ void COFFDumper::printCodeViewSymbolsSubsection(StringRef Subsection, W.flush(); error(std::move(EC)); } + CompilationCPUType = CVSD.getCompilationCPUType(); W.flush(); } @@ -1224,7 +1248,9 @@ void COFFDumper::mergeCodeViewTypes(MergingTypeTableBuilder &CVIDs, error(object_error::parse_failed); } SmallVector<TypeIndex, 128> SourceToDest; - if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types)) + Optional<uint32_t> PCHSignature; + if (auto EC = mergeTypeAndIdRecords(CVIDs, CVTypes, SourceToDest, Types, + PCHSignature)) return error(std::move(EC)); } } @@ -1253,7 +1279,7 @@ void COFFDumper::printCodeViewTypeSection(StringRef SectionName, W.flush(); } -void COFFDumper::printSections() { +void COFFDumper::printSectionHeaders() { ListScope SectionsD(W, "Sections"); int SectionNumber = 0; for (const SectionRef &Sec : Obj->sections()) { @@ -1339,10 +1365,12 @@ void COFFDumper::printRelocation(const SectionRef &Section, StringRef SymbolName; Reloc.getTypeName(RelocName); symbol_iterator Symbol = Reloc.getSymbol(); + int64_t SymbolIndex = -1; if (Symbol != Obj->symbol_end()) { Expected<StringRef> SymbolNameOrErr = Symbol->getName(); error(errorToErrorCode(SymbolNameOrErr.takeError())); SymbolName = *SymbolNameOrErr; + SymbolIndex = Obj->getSymbolIndex(Obj->getCOFFSymbol(*Symbol)); } if (opts::ExpandRelocs) { @@ -1350,11 +1378,13 @@ void COFFDumper::printRelocation(const SectionRef &Section, W.printHex("Offset", Offset); W.printNumber("Type", RelocName, RelocType); W.printString("Symbol", SymbolName.empty() ? "-" : SymbolName); + W.printNumber("SymbolIndex", SymbolIndex); } else { raw_ostream& OS = W.startLine(); OS << W.hex(Offset) << " " << RelocName << " " << (SymbolName.empty() ? "-" : SymbolName) + << " (" << SymbolIndex << ")" << "\n"; } } @@ -1525,8 +1555,10 @@ void COFFDumper::printUnwindInfo() { Dumper.printData(Ctx); break; } + case COFF::IMAGE_FILE_MACHINE_ARM64: case COFF::IMAGE_FILE_MACHINE_ARMNT: { - ARM::WinEH::Decoder Decoder(W); + ARM::WinEH::Decoder Decoder(W, Obj->getMachine() == + COFF::IMAGE_FILE_MACHINE_ARM64); Decoder.dumpProcedureData(*Obj); break; } @@ -1830,6 +1862,49 @@ void COFFDumper::printStackMap() const { StackMapV2Parser<support::big>(StackMapContentsArray)); } +void COFFDumper::printAddrsig() { + object::SectionRef AddrsigSection; + for (auto Sec : Obj->sections()) { + StringRef Name; + Sec.getName(Name); + if (Name == ".llvm_addrsig") { + AddrsigSection = Sec; + break; + } + } + + if (AddrsigSection == object::SectionRef()) + return; + + StringRef AddrsigContents; + AddrsigSection.getContents(AddrsigContents); + ArrayRef<uint8_t> AddrsigContentsArray( + reinterpret_cast<const uint8_t*>(AddrsigContents.data()), + AddrsigContents.size()); + + ListScope L(W, "Addrsig"); + auto *Cur = reinterpret_cast<const uint8_t *>(AddrsigContents.begin()); + auto *End = reinterpret_cast<const uint8_t *>(AddrsigContents.end()); + while (Cur != End) { + unsigned Size; + const char *Err; + uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err); + if (Err) + reportError(Err); + + Expected<COFFSymbolRef> Sym = Obj->getSymbol(SymIndex); + StringRef SymName; + std::error_code EC = errorToErrorCode(Sym.takeError()); + if (EC || (EC = Obj->getSymbolName(*Sym, SymName))) { + SymName = ""; + error(EC); + } + + W.printNumber("Sym", SymName, SymIndex); + Cur += Size; + } +} + void llvm::dumpCodeViewMergedTypes( ScopedPrinter &Writer, llvm::codeview::MergingTypeTableBuilder &IDTable, llvm::codeview::MergingTypeTableBuilder &CVTypes) { diff --git a/tools/llvm-readobj/DwarfCFIEHPrinter.h b/tools/llvm-readobj/DwarfCFIEHPrinter.h index 5a1eef1d007d..d91d764c4d0a 100644 --- a/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ b/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -16,6 +16,7 @@ #include "llvm/BinaryFormat/Dwarf.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ELFTypes.h" +#include "llvm/Object/ELFObjectFile.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/Debug.h" @@ -31,15 +32,15 @@ namespace DwarfCFIEH { template <typename ELFT> class PrinterContext { ScopedPrinter &W; - const object::ELFFile<ELFT> *Obj; + const object::ELFObjectFile<ELFT> *ObjF; 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) {} + PrinterContext(ScopedPrinter &W, const object::ELFObjectFile<ELFT> *ObjF) + : W(W), ObjF(ObjF) {} void printUnwindInformation() const; }; @@ -59,6 +60,7 @@ static const typename ELFO::Elf_Shdr *findSectionByAddress(const ELFO *Obj, template <typename ELFT> void PrinterContext<ELFT>::printUnwindInformation() const { + const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); const typename ELFT::Phdr *EHFramePhdr = nullptr; auto PHs = Obj->program_headers(); @@ -101,6 +103,7 @@ void PrinterContext<ELFT>::printEHFrameHdr(uint64_t EHFrameHdrOffset, W.startLine() << format("Offset: 0x%" PRIx64 "\n", EHFrameHdrOffset); W.startLine() << format("Size: 0x%" PRIx64 "\n", EHFrameHdrSize); + const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); const auto *EHFrameHdrShdr = findSectionByAddress(Obj, EHFrameHdrAddress); if (EHFrameHdrShdr) { auto SectionName = Obj->getSectionName(EHFrameHdrShdr); @@ -173,6 +176,7 @@ void PrinterContext<ELFT>::printEHFrame( ShOffset, Address); W.indent(); + const object::ELFFile<ELFT> *Obj = ObjF->getELFFile(); auto Result = Obj->getSectionContents(EHFrameShdr); if (Error E = Result.takeError()) reportError(toString(std::move(E))); @@ -183,7 +187,8 @@ void PrinterContext<ELFT>::printEHFrame( Contents.size()), ELFT::TargetEndianness == support::endianness::little, ELFT::Is64Bits ? 8 : 4); - DWARFDebugFrame EHFrame(/*IsEH=*/true, /*EHFrameAddress=*/Address); + DWARFDebugFrame EHFrame(Triple::ArchType(ObjF->getArch()), /*IsEH=*/true, + /*EHFrameAddress=*/Address); EHFrame.parse(DE); for (const auto &Entry : EHFrame) { diff --git a/tools/llvm-readobj/ELFDumper.cpp b/tools/llvm-readobj/ELFDumper.cpp index 645ec2d7e04b..93254717e921 100644 --- a/tools/llvm-readobj/ELFDumper.cpp +++ b/tools/llvm-readobj/ELFDumper.cpp @@ -22,12 +22,13 @@ #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/BinaryFormat/AMDGPUMetadataVerifier.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/ELF.h" #include "llvm/Object/ELFObjectFile.h" @@ -43,6 +44,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" +#include "llvm/Support/FormatVariadic.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" @@ -139,10 +141,10 @@ struct DynRegionInfo { template<typename ELFT> class ELFDumper : public ObjDumper { public: - ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer); + ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, ScopedPrinter &Writer); void printFileHeaders() override; - void printSections() override; + void printSectionHeaders() override; void printRelocations() override; void printDynamicRelocations() override; void printSymbols() override; @@ -181,6 +183,7 @@ private: TYPEDEF_ELF_TYPES(ELFT) DynRegionInfo checkDRI(DynRegionInfo DRI) { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); if (DRI.Addr < Obj->base() || (const uint8_t *)DRI.Addr + DRI.Size > Obj->base() + Obj->getBufSize()) error(llvm::object::object_error::parse_failed); @@ -188,11 +191,11 @@ private: } DynRegionInfo createDRIFrom(const Elf_Phdr *P, uintX_t EntSize) { - return checkDRI({Obj->base() + P->p_offset, P->p_filesz, EntSize}); + return checkDRI({ObjF->getELFFile()->base() + P->p_offset, P->p_filesz, EntSize}); } DynRegionInfo createDRIFrom(const Elf_Shdr *S) { - return checkDRI({Obj->base() + S->sh_offset, S->sh_size, S->sh_entsize}); + return checkDRI({ObjF->getELFFile()->base() + S->sh_offset, S->sh_size, S->sh_entsize}); } void parseDynamicTable(ArrayRef<const Elf_Phdr *> LoadSegments); @@ -206,7 +209,7 @@ private: void LoadVersionNeeds(const Elf_Shdr *ec) const; void LoadVersionDefs(const Elf_Shdr *sec) const; - const ELFO *Obj; + const object::ELFObjectFile<ELFT> *ObjF; DynRegionInfo DynRelRegion; DynRegionInfo DynRelaRegion; DynRegionInfo DynRelrRegion; @@ -289,6 +292,7 @@ void ELFDumper<ELFT>::printSymbolsHelper(bool IsDynamic) const { StringRef StrTable, SymtabName; size_t Entries = 0; Elf_Sym_Range Syms(nullptr, nullptr); + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); if (IsDynamic) { StrTable = DynamicStringTable; Syms = dynamic_symbols(); @@ -323,7 +327,7 @@ public: virtual void printFileHeaders(const ELFFile<ELFT> *Obj) = 0; virtual void printGroupSections(const ELFFile<ELFT> *Obj) = 0; virtual void printRelocations(const ELFFile<ELFT> *Obj) = 0; - virtual void printSections(const ELFFile<ELFT> *Obj) = 0; + virtual void printSectionHeaders(const ELFFile<ELFT> *Obj) = 0; virtual void printSymbols(const ELFFile<ELFT> *Obj) = 0; virtual void printDynamicSymbols(const ELFFile<ELFT> *Obj) = 0; virtual void printDynamicRelocations(const ELFFile<ELFT> *Obj) = 0; @@ -358,7 +362,7 @@ public: void printFileHeaders(const ELFO *Obj) override; void printGroupSections(const ELFFile<ELFT> *Obj) override; void printRelocations(const ELFO *Obj) override; - void printSections(const ELFO *Obj) override; + void printSectionHeaders(const ELFO *Obj) override; void printSymbols(const ELFO *Obj) override; void printDynamicSymbols(const ELFO *Obj) override; void printDynamicRelocations(const ELFO *Obj) override; @@ -390,6 +394,33 @@ private: return to_hexString(Value, false); } + template <typename T, typename TEnum> + std::string printFlags(T Value, ArrayRef<EnumEntry<TEnum>> EnumValues, + TEnum EnumMask1 = {}, TEnum EnumMask2 = {}, + TEnum EnumMask3 = {}) { + std::string Str; + for (const auto &Flag : EnumValues) { + if (Flag.Value == 0) + continue; + + TEnum EnumMask{}; + if (Flag.Value & EnumMask1) + EnumMask = EnumMask1; + else if (Flag.Value & EnumMask2) + EnumMask = EnumMask2; + else if (Flag.Value & EnumMask3) + EnumMask = EnumMask3; + bool IsEnum = (Flag.Value & EnumMask) != 0; + if ((!IsEnum && (Value & Flag.Value) == Flag.Value) || + (IsEnum && (Value & EnumMask) == Flag.Value)) { + if (!Str.empty()) + Str += ", "; + Str += Flag.AltName; + } + } + return Str; + } + formatted_raw_ostream &printField(struct Field F) { if (F.Column != 0) OS.PadToColumn(F.Column); @@ -424,7 +455,7 @@ public: void printGroupSections(const ELFFile<ELFT> *Obj) override; void printRelocations(const ELFO *Obj) override; void printRelocations(const Elf_Shdr *Sec, const ELFO *Obj); - void printSections(const ELFO *Obj) override; + void printSectionHeaders(const ELFO *Obj) override; void printSymbols(const ELFO *Obj) override; void printDynamicSymbols(const ELFO *Obj) override; void printDynamicRelocations(const ELFO *Obj) override; @@ -451,7 +482,7 @@ private: namespace llvm { template <class ELFT> -static std::error_code createELFDumper(const ELFFile<ELFT> *Obj, +static std::error_code createELFDumper(const ELFObjectFile<ELFT> *Obj, ScopedPrinter &Writer, std::unique_ptr<ObjDumper> &Result) { Result.reset(new ELFDumper<ELFT>(Obj, Writer)); @@ -463,19 +494,19 @@ std::error_code createELFDumper(const object::ObjectFile *Obj, std::unique_ptr<ObjDumper> &Result) { // Little-endian 32-bit if (const ELF32LEObjectFile *ELFObj = dyn_cast<ELF32LEObjectFile>(Obj)) - return createELFDumper(ELFObj->getELFFile(), Writer, Result); + return createELFDumper(ELFObj, Writer, Result); // Big-endian 32-bit if (const ELF32BEObjectFile *ELFObj = dyn_cast<ELF32BEObjectFile>(Obj)) - return createELFDumper(ELFObj->getELFFile(), Writer, Result); + return createELFDumper(ELFObj, Writer, Result); // Little-endian 64-bit if (const ELF64LEObjectFile *ELFObj = dyn_cast<ELF64LEObjectFile>(Obj)) - return createELFDumper(ELFObj->getELFFile(), Writer, Result); + return createELFDumper(ELFObj, Writer, Result); // Big-endian 64-bit if (const ELF64BEObjectFile *ELFObj = dyn_cast<ELF64BEObjectFile>(Obj)) - return createELFDumper(ELFObj->getELFFile(), Writer, Result); + return createELFDumper(ELFObj, Writer, Result); return readobj_error::unsupported_obj_file_format; } @@ -488,7 +519,7 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionNeeds(const Elf_Shdr *sec) const { unsigned vn_size = sec->sh_size; // Size of section in bytes unsigned vn_count = sec->sh_info; // Number of Verneed entries - const char *sec_start = (const char *)Obj->base() + sec->sh_offset; + const char *sec_start = (const char *)ObjF->getELFFile()->base() + sec->sh_offset; const char *sec_end = sec_start + vn_size; // The first Verneed entry is at the start of the section. const char *p = sec_start; @@ -522,7 +553,7 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionDefs(const Elf_Shdr *sec) const { unsigned vd_size = sec->sh_size; // Size of section in bytes unsigned vd_count = sec->sh_info; // Number of Verdef entries - const char *sec_start = (const char *)Obj->base() + sec->sh_offset; + const char *sec_start = (const char *)ObjF->getELFFile()->base() + sec->sh_offset; const char *sec_end = sec_start + vd_size; // The first Verdef entry is at the start of the section. const char *p = sec_start; @@ -547,7 +578,7 @@ template <class ELFT> void ELFDumper<ELFT>::LoadVersionMap() const { return; // Has the VersionMap already been loaded? - if (VersionMap.size() > 0) + if (!VersionMap.empty()) return; // The first two version indexes are reserved. @@ -611,9 +642,12 @@ static void printVersionDefinitionSection(ELFDumper<ELFT> *Dumper, // is determined by DT_VERDEFNUM tag. unsigned VerDefsNum = 0; for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) { - if (Dyn.d_tag == DT_VERDEFNUM) + if (Dyn.d_tag == DT_VERDEFNUM) { VerDefsNum = Dyn.d_un.d_val; + break; + } } + const uint8_t *SecStartAddress = (const uint8_t *)Obj->base() + Sec->sh_offset; const uint8_t *SecEndAddress = SecStartAddress + Sec->sh_size; @@ -664,9 +698,12 @@ static void printVersionDependencySection(ELFDumper<ELFT> *Dumper, return; unsigned VerNeedNum = 0; - for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) - if (Dyn.d_tag == DT_VERNEEDNUM) + for (const typename ELFO::Elf_Dyn &Dyn : Dumper->dynamic_table()) { + if (Dyn.d_tag == DT_VERNEEDNUM) { VerNeedNum = Dyn.d_un.d_val; + break; + } + } const uint8_t *SecData = (const uint8_t *)Obj->base() + Sec->sh_offset; const typename ELFO::Elf_Shdr *StrTab = @@ -700,13 +737,13 @@ static void printVersionDependencySection(ELFDumper<ELFT> *Dumper, template <typename ELFT> void ELFDumper<ELFT>::printVersionInfo() { // Dump version symbol section. - printVersionSymbolSection(this, Obj, dot_gnu_version_sec, W); + printVersionSymbolSection(this, ObjF->getELFFile(), dot_gnu_version_sec, W); // Dump version definition section. - printVersionDefinitionSection(this, Obj, dot_gnu_version_d_sec, W); + printVersionDefinitionSection(this, ObjF->getELFFile(), dot_gnu_version_d_sec, W); // Dump version dependency section. - printVersionDependencySection(this, Obj, dot_gnu_version_r_sec, W); + printVersionDependencySection(this, ObjF->getELFFile(), dot_gnu_version_r_sec, W); } template <typename ELFT> @@ -727,7 +764,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, // Get the corresponding version index entry const Elf_Versym *vs = unwrapOrError( - Obj->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index)); + ObjF->getELFFile()->template getEntry<Elf_Versym>(dot_gnu_version_sec, entry_index)); size_t version_index = vs->vs_index & ELF::VERSYM_VERSION; // Special markers for unversioned symbols. @@ -760,6 +797,7 @@ StringRef ELFDumper<ELFT>::getSymbolVersion(StringRef StrTab, template <typename ELFT> StringRef ELFDumper<ELFT>::getStaticSymbolName(uint32_t Index) const { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); StringRef StrTable = unwrapOrError(Obj->getStringTableForSymtab(*DotSymtabSec)); Elf_Sym_Range Syms = unwrapOrError(Obj->symbols(DotSymtabSec)); if (Index >= Syms.size()) @@ -780,8 +818,10 @@ std::string ELFDumper<ELFT>::getFullSymbolName(const Elf_Sym *Symbol, bool IsDefault; StringRef Version = getSymbolVersion(StrTable, &*Symbol, IsDefault); - FullSymbolName += (IsDefault ? "@@" : "@"); - FullSymbolName += Version; + if (!Version.empty()) { + FullSymbolName += (IsDefault ? "@@" : "@"); + FullSymbolName += Version; + } return FullSymbolName; } @@ -807,6 +847,7 @@ void ELFDumper<ELFT>::getSectionNameIndex(const Elf_Sym *Symbol, if (SectionIndex == SHN_XINDEX) SectionIndex = unwrapOrError(object::getExtendedSymbolTableIndex<ELFT>( Symbol, FirstSym, ShndxTable)); + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const typename ELFT::Shdr *Sec = unwrapOrError(Obj->getSection(SectionIndex)); SectionName = unwrapOrError(Obj->getSectionName(Sec)); @@ -1167,6 +1208,7 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) { switch (Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_ARM_EXIDX); } + break; case ELF::EM_MIPS: case ELF::EM_MIPS_RS3_LE: switch (Type) { @@ -1175,6 +1217,7 @@ static const char *getElfSegmentType(unsigned Arch, unsigned Type) { LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_OPTIONS); LLVM_READOBJ_ENUM_CASE(ELF, PT_MIPS_ABIFLAGS); } + break; } switch (Type) { @@ -1221,7 +1264,7 @@ static std::string getElfPtType(unsigned Arch, unsigned Type) { case ELF::EM_ARM: if (Type == ELF::PT_ARM_EXIDX) return "EXIDX"; - return ""; + break; case ELF::EM_MIPS: case ELF::EM_MIPS_RS3_LE: switch (Type) { @@ -1234,7 +1277,7 @@ static std::string getElfPtType(unsigned Arch, unsigned Type) { case PT_MIPS_ABIFLAGS: return "ABIFLAGS"; } - return ""; + break; } } return std::string("<unknown>: ") + to_string(format_hex(Type, 1)); @@ -1247,49 +1290,49 @@ static const EnumEntry<unsigned> ElfSegmentFlags[] = { }; static const EnumEntry<unsigned> ElfHeaderMipsFlags[] = { - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_NOREORDER), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_PIC), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_CPIC), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI2), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_32BITMODE), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_FP64), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_NAN2008), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_O32), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_O64), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_EABI32), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ABI_EABI64), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_3900), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4010), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4100), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4650), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4120), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_4111), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_SB1), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_XLR), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON2), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_OCTEON3), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5400), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5900), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_5500), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_9000), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS2E), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS2F), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MACH_LS3A), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_MICROMIPS), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_ASE_M16), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_ASE_MDMX), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_1), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_2), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_3), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_4), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_5), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_32), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_32R2), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R2), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_32R6), - LLVM_READOBJ_ENUM_ENT(ELF, EF_MIPS_ARCH_64R6) + ENUM_ENT(EF_MIPS_NOREORDER, "noreorder"), + ENUM_ENT(EF_MIPS_PIC, "pic"), + ENUM_ENT(EF_MIPS_CPIC, "cpic"), + ENUM_ENT(EF_MIPS_ABI2, "abi2"), + ENUM_ENT(EF_MIPS_32BITMODE, "32bitmode"), + ENUM_ENT(EF_MIPS_FP64, "fp64"), + ENUM_ENT(EF_MIPS_NAN2008, "nan2008"), + ENUM_ENT(EF_MIPS_ABI_O32, "o32"), + ENUM_ENT(EF_MIPS_ABI_O64, "o64"), + ENUM_ENT(EF_MIPS_ABI_EABI32, "eabi32"), + ENUM_ENT(EF_MIPS_ABI_EABI64, "eabi64"), + ENUM_ENT(EF_MIPS_MACH_3900, "3900"), + ENUM_ENT(EF_MIPS_MACH_4010, "4010"), + ENUM_ENT(EF_MIPS_MACH_4100, "4100"), + ENUM_ENT(EF_MIPS_MACH_4650, "4650"), + ENUM_ENT(EF_MIPS_MACH_4120, "4120"), + ENUM_ENT(EF_MIPS_MACH_4111, "4111"), + ENUM_ENT(EF_MIPS_MACH_SB1, "sb1"), + ENUM_ENT(EF_MIPS_MACH_OCTEON, "octeon"), + ENUM_ENT(EF_MIPS_MACH_XLR, "xlr"), + ENUM_ENT(EF_MIPS_MACH_OCTEON2, "octeon2"), + ENUM_ENT(EF_MIPS_MACH_OCTEON3, "octeon3"), + ENUM_ENT(EF_MIPS_MACH_5400, "5400"), + ENUM_ENT(EF_MIPS_MACH_5900, "5900"), + ENUM_ENT(EF_MIPS_MACH_5500, "5500"), + ENUM_ENT(EF_MIPS_MACH_9000, "9000"), + ENUM_ENT(EF_MIPS_MACH_LS2E, "loongson-2e"), + ENUM_ENT(EF_MIPS_MACH_LS2F, "loongson-2f"), + ENUM_ENT(EF_MIPS_MACH_LS3A, "loongson-3a"), + ENUM_ENT(EF_MIPS_MICROMIPS, "micromips"), + ENUM_ENT(EF_MIPS_ARCH_ASE_M16, "mips16"), + ENUM_ENT(EF_MIPS_ARCH_ASE_MDMX, "mdmx"), + ENUM_ENT(EF_MIPS_ARCH_1, "mips1"), + ENUM_ENT(EF_MIPS_ARCH_2, "mips2"), + ENUM_ENT(EF_MIPS_ARCH_3, "mips3"), + ENUM_ENT(EF_MIPS_ARCH_4, "mips4"), + ENUM_ENT(EF_MIPS_ARCH_5, "mips5"), + ENUM_ENT(EF_MIPS_ARCH_32, "mips32"), + ENUM_ENT(EF_MIPS_ARCH_64, "mips64"), + ENUM_ENT(EF_MIPS_ARCH_32R2, "mips32r2"), + ENUM_ENT(EF_MIPS_ARCH_64R2, "mips64r2"), + ENUM_ENT(EF_MIPS_ARCH_32R6, "mips32r6"), + ENUM_ENT(EF_MIPS_ARCH_64R6, "mips64r6") }; static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { @@ -1325,15 +1368,17 @@ static const EnumEntry<unsigned> ElfHeaderAMDGPUFlags[] = { 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) + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_MACH_AMDGCN_GFX909), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_XNACK), + LLVM_READOBJ_ENUM_ENT(ELF, EF_AMDGPU_SRAM_ECC) }; static const EnumEntry<unsigned> ElfHeaderRISCVFlags[] = { - LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_RVC), - LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_FLOAT_ABI_SINGLE), - LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_FLOAT_ABI_DOUBLE), - LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_FLOAT_ABI_QUAD), - LLVM_READOBJ_ENUM_ENT(ELF, EF_RISCV_RVE) + ENUM_ENT(EF_RISCV_RVC, "RVC"), + ENUM_ENT(EF_RISCV_FLOAT_ABI_SINGLE, "single-float ABI"), + ENUM_ENT(EF_RISCV_FLOAT_ABI_DOUBLE, "double-float ABI"), + ENUM_ENT(EF_RISCV_FLOAT_ABI_QUAD, "quad-float ABI"), + ENUM_ENT(EF_RISCV_RVE, "RVE") }; static const EnumEntry<unsigned> ElfSymOtherFlags[] = { @@ -1375,9 +1420,11 @@ static const char *getElfMipsOptionsOdkType(unsigned Odk) { } template <typename ELFT> -ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer) - : ObjDumper(Writer), Obj(Obj) { +ELFDumper<ELFT>::ELFDumper(const object::ELFObjectFile<ELFT> *ObjF, + ScopedPrinter &Writer) + : ObjDumper(Writer), ObjF(ObjF) { SmallVector<const Elf_Phdr *, 4> LoadSegments; + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); for (const Elf_Phdr &Phdr : unwrapOrError(Obj->program_headers())) { if (Phdr.p_type == ELF::PT_DYNAMIC) { DynamicTable = createDRIFrom(&Phdr, sizeof(Elf_Dyn)); @@ -1423,7 +1470,7 @@ ELFDumper<ELFT>::ELFDumper(const ELFFile<ELFT> *Obj, ScopedPrinter &Writer) break; case ELF::SHT_LLVM_CALL_GRAPH_PROFILE: if (DotCGProfileSec != nullptr) - reportError("Multiple .note.llvm.cgprofile"); + reportError("Multiple .llvm.call-graph-profile"); DotCGProfileSec = &Sec; break; case ELF::SHT_LLVM_ADDRSIG: @@ -1446,19 +1493,10 @@ template <typename ELFT> void ELFDumper<ELFT>::parseDynamicTable( ArrayRef<const Elf_Phdr *> LoadSegments) { auto toMappedAddr = [&](uint64_t VAddr) -> const uint8_t * { - const Elf_Phdr *const *I = - std::upper_bound(LoadSegments.begin(), LoadSegments.end(), VAddr, - [](uint64_t VAddr, const Elf_Phdr_Impl<ELFT> *Phdr) { - return VAddr < Phdr->p_vaddr; - }); - if (I == LoadSegments.begin()) - report_fatal_error("Virtual address is not in any segment"); - --I; - const Elf_Phdr &Phdr = **I; - uint64_t Delta = VAddr - Phdr.p_vaddr; - if (Delta >= Phdr.p_filesz) - report_fatal_error("Virtual address is not in any segment"); - return Obj->base() + Phdr.p_offset + Delta; + auto MappedAddrOrError = ObjF->getELFFile()->toMappedAddr(VAddr); + if (!MappedAddrOrError) + report_fatal_error(MappedAddrOrError.takeError()); + return MappedAddrOrError.get(); }; uint64_t SONameOffset = 0; @@ -1557,51 +1595,51 @@ typename ELFDumper<ELFT>::Elf_Relr_Range ELFDumper<ELFT>::dyn_relrs() const { template<class ELFT> void ELFDumper<ELFT>::printFileHeaders() { - ELFDumperStyle->printFileHeaders(Obj); + ELFDumperStyle->printFileHeaders(ObjF->getELFFile()); } template<class ELFT> -void ELFDumper<ELFT>::printSections() { - ELFDumperStyle->printSections(Obj); +void ELFDumper<ELFT>::printSectionHeaders() { + ELFDumperStyle->printSectionHeaders(ObjF->getELFFile()); } template<class ELFT> void ELFDumper<ELFT>::printRelocations() { - ELFDumperStyle->printRelocations(Obj); + ELFDumperStyle->printRelocations(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printProgramHeaders() { - ELFDumperStyle->printProgramHeaders(Obj); + ELFDumperStyle->printProgramHeaders(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printDynamicRelocations() { - ELFDumperStyle->printDynamicRelocations(Obj); + ELFDumperStyle->printDynamicRelocations(ObjF->getELFFile()); } template<class ELFT> void ELFDumper<ELFT>::printSymbols() { - ELFDumperStyle->printSymbols(Obj); + ELFDumperStyle->printSymbols(ObjF->getELFFile()); } template<class ELFT> void ELFDumper<ELFT>::printDynamicSymbols() { - ELFDumperStyle->printDynamicSymbols(Obj); + ELFDumperStyle->printDynamicSymbols(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printHashHistogram() { - ELFDumperStyle->printHashHistogram(Obj); + ELFDumperStyle->printHashHistogram(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printCGProfile() { - ELFDumperStyle->printCGProfile(Obj); + ELFDumperStyle->printCGProfile(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printNotes() { - ELFDumperStyle->printNotes(Obj); + ELFDumperStyle->printNotes(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printELFLinkerOptions() { - ELFDumperStyle->printELFLinkerOptions(Obj); + ELFDumperStyle->printELFLinkerOptions(ObjF->getELFFile()); } static const char *getTypeString(unsigned Arch, uint64_t Type) { @@ -1610,29 +1648,32 @@ static const char *getTypeString(unsigned Arch, uint64_t Type) { case EM_HEXAGON: switch (Type) { #define HEXAGON_DYNAMIC_TAG(name, value) \ - case DT_##name: \ - return #name; + case DT_##name: \ + return #name; #include "llvm/BinaryFormat/DynamicTags.def" #undef HEXAGON_DYNAMIC_TAG } + break; case EM_MIPS: switch (Type) { #define MIPS_DYNAMIC_TAG(name, value) \ - case DT_##name: \ - return #name; + case DT_##name: \ + return #name; #include "llvm/BinaryFormat/DynamicTags.def" #undef MIPS_DYNAMIC_TAG } + break; - case EM_PPC64: - switch(Type) { + 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 } + break; } #undef DYNAMIC_TAG switch (Type) { @@ -1842,9 +1883,9 @@ void ELFDumper<ELFT>::printValue(uint64_t Type, uint64_t Value) { template<class ELFT> void ELFDumper<ELFT>::printUnwindInfo() { - const unsigned Machine = Obj->getHeader()->e_machine; + const unsigned Machine = ObjF->getELFFile()->getHeader()->e_machine; if (Machine == EM_386 || Machine == EM_X86_64) { - DwarfCFIEH::PrinterContext<ELFT> Ctx(W, Obj); + DwarfCFIEH::PrinterContext<ELFT> Ctx(W, ObjF); return Ctx.printUnwindInformation(); } W.startLine() << "UnwindInfo not implemented.\n"; @@ -1853,6 +1894,7 @@ void ELFDumper<ELFT>::printUnwindInfo() { namespace { template <> void ELFDumper<ELF32LE>::printUnwindInfo() { + const ELFFile<ELF32LE> *Obj = ObjF->getELFFile(); const unsigned Machine = Obj->getHeader()->e_machine; if (Machine == EM_ARM) { ARM::EHABI::PrinterContext<ELF32LE> Ctx(W, Obj, DotSymtabSec); @@ -1895,7 +1937,7 @@ void ELFDumper<ELFT>::printDynamicTable() { uintX_t Tag = Entry.getTag(); ++I; W.startLine() << " " << format_hex(Tag, Is64 ? 18 : 10, opts::Output != opts::GNU) << " " - << format("%-21s", getTypeString(Obj->getHeader()->e_machine, Tag)); + << format("%-21s", getTypeString(ObjF->getELFFile()->getHeader()->e_machine, Tag)); printValue(Tag, Entry.getVal()); OS << "\n"; } @@ -1962,6 +2004,7 @@ void ELFDumper<ELFT>::printAttributes() { namespace { template <> void ELFDumper<ELF32LE>::printAttributes() { + const ELFFile<ELF32LE> *Obj = ObjF->getELFFile(); if (Obj->getHeader()->e_machine != EM_ARM) { W.startLine() << "Attributes not implemented.\n"; return; @@ -2247,6 +2290,7 @@ MipsGOTParser<ELFT>::getPltSym(const Entry *E) const { } template <class ELFT> void ELFDumper<ELFT>::printMipsPLTGOT() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); if (Obj->getHeader()->e_machine != EM_MIPS) reportError("MIPS PLT GOT is available for MIPS targets only"); @@ -2331,6 +2375,7 @@ static int getMipsRegisterSize(uint8_t Flag) { } template <class ELFT> void ELFDumper<ELFT>::printMipsABIFlags() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.abiflags"); if (!Shdr) { W.startLine() << "There is no .MIPS.abiflags section in the file.\n"; @@ -2376,6 +2421,7 @@ static void printMipsReginfoData(ScopedPrinter &W, } template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const Elf_Shdr *Shdr = findSectionByName(*Obj, ".reginfo"); if (!Shdr) { W.startLine() << "There is no .reginfo section in the file.\n"; @@ -2393,6 +2439,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsReginfo() { } template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const Elf_Shdr *Shdr = findSectionByName(*Obj, ".MIPS.options"); if (!Shdr) { W.startLine() << "There is no .MIPS.options section in the file.\n"; @@ -2422,6 +2469,7 @@ template <class ELFT> void ELFDumper<ELFT>::printMipsOptions() { } template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { + const ELFFile<ELFT> *Obj = ObjF->getELFFile(); const Elf_Shdr *StackMapSection = nullptr; for (const auto &Sec : unwrapOrError(Obj->sections())) { StringRef Name = unwrapOrError(Obj->getSectionName(&Sec)); @@ -2442,11 +2490,11 @@ template <class ELFT> void ELFDumper<ELFT>::printStackMap() const { } template <class ELFT> void ELFDumper<ELFT>::printGroupSections() { - ELFDumperStyle->printGroupSections(Obj); + ELFDumperStyle->printGroupSections(ObjF->getELFFile()); } template <class ELFT> void ELFDumper<ELFT>::printAddrsig() { - ELFDumperStyle->printAddrsig(Obj); + ELFDumperStyle->printAddrsig(ObjF->getELFFile()); } static inline void printFields(formatted_raw_ostream &OS, StringRef Str1, @@ -2517,7 +2565,17 @@ template <class ELFT> void GNUStyle<ELFT>::printFileHeaders(const ELFO *Obj) { printFields(OS, "Start of program headers:", Str); Str = to_string(e->e_shoff) + " (bytes into file)"; printFields(OS, "Start of section headers:", Str); + std::string ElfFlags; + if (e->e_machine == EM_MIPS) + ElfFlags = + printFlags(e->e_flags, makeArrayRef(ElfHeaderMipsFlags), + unsigned(ELF::EF_MIPS_ARCH), unsigned(ELF::EF_MIPS_ABI), + unsigned(ELF::EF_MIPS_MACH)); + else if (e->e_machine == EM_RISCV) + ElfFlags = printFlags(e->e_flags, makeArrayRef(ElfHeaderRISCVFlags)); Str = "0x" + to_hexString(e->e_flags); + if (!ElfFlags.empty()) + Str = Str + ", " + ElfFlags; printFields(OS, "Flags:", Str); Str = to_string(e->e_ehsize) + " (bytes)"; printFields(OS, "Size of this header:", Str); @@ -2791,11 +2849,13 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { case SHT_ARM_OVERLAYSECTION: return "ARM_OVERLAYSECTION"; } + break; case EM_X86_64: switch (Type) { case SHT_X86_64_UNWIND: return "X86_64_UNWIND"; } + break; case EM_MIPS: case EM_MIPS_RS3_LE: switch (Type) { @@ -2808,6 +2868,7 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { case SHT_MIPS_DWARF: return "SHT_MIPS_DWARF"; } + break; } switch (Type) { case SHT_NULL: @@ -2872,7 +2933,8 @@ std::string getSectionTypeString(unsigned Arch, unsigned Type) { return ""; } -template <class ELFT> void GNUStyle<ELFT>::printSections(const ELFO *Obj) { +template <class ELFT> +void GNUStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { size_t SectionIndex = 0; std::string Number, Type, Size, Address, Offset, Flags, Link, Info, EntrySize, Alignment; @@ -3583,7 +3645,7 @@ static std::string getFreeBSDNoteTypeName(const uint32_t NT) { return OS.str(); } -static std::string getAMDGPUNoteTypeName(const uint32_t NT) { +static std::string getAMDNoteTypeName(const uint32_t NT) { static const struct { uint32_t ID; const char *Name; @@ -3606,41 +3668,52 @@ static std::string getAMDGPUNoteTypeName(const uint32_t NT) { return OS.str(); } +static std::string getAMDGPUNoteTypeName(const uint32_t NT) { + if (NT == ELF::NT_AMDGPU_METADATA) + return std::string("NT_AMDGPU_METADATA (AMDGPU Metadata)"); + + std::string string; + raw_string_ostream OS(string); + OS << format("Unknown note type (0x%08x)", NT); + return OS.str(); +} + template <typename ELFT> -static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize, - ArrayRef<uint8_t> Data) { +static std::string getGNUProperty(uint32_t Type, uint32_t DataSize, + ArrayRef<uint8_t> Data) { + std::string str; + raw_string_ostream OS(str); switch (Type) { default: - OS << format(" <application-specific type 0x%x>\n", Type); - return; + OS << format("<application-specific type 0x%x>", Type); + return OS.str(); case GNU_PROPERTY_STACK_SIZE: { - OS << " stack size: "; + OS << "stack size: "; if (DataSize == sizeof(typename ELFT::uint)) - OS << format("0x%llx\n", - (uint64_t)(*(const typename ELFT::Addr *)Data.data())); + OS << formatv("{0:x}", + (uint64_t)(*(const typename ELFT::Addr *)Data.data())); else - OS << format("<corrupt length: 0x%x>\n", DataSize); - break; + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); } case GNU_PROPERTY_NO_COPY_ON_PROTECTED: - OS << " no copy on protected"; + OS << "no copy on protected"; if (DataSize) OS << format(" <corrupt length: 0x%x>", DataSize); - OS << "\n"; - break; + return OS.str(); case GNU_PROPERTY_X86_FEATURE_1_AND: - OS << " X86 features: "; + OS << "X86 features: "; if (DataSize != 4 && DataSize != 8) { - OS << format("<corrupt length: 0x%x>\n", DataSize); - break; + OS << format("<corrupt length: 0x%x>", DataSize); + return OS.str(); } 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; + OS << "none"; + return OS.str(); } if (CFProtection & GNU_PROPERTY_X86_FEATURE_1_IBT) { OS << "IBT"; @@ -3656,105 +3729,177 @@ static void printGNUProperty(raw_ostream &OS, uint32_t Type, uint32_t DataSize, } if (CFProtection) OS << format("<unknown flags: 0x%llx>", CFProtection); - OS << "\n"; - break; + return OS.str(); } } template <typename ELFT> -static void printGNUNote(raw_ostream &OS, uint32_t NoteType, - ArrayRef<typename ELFT::Word> Words, size_t Size) { +static SmallVector<std::string, 4> +getGNUPropertyList(ArrayRef<uint8_t> Arr) { using Elf_Word = typename ELFT::Word; + SmallVector<std::string, 4> Properties; + 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)); + std::string str; + raw_string_ostream OS(str); + if (Arr.size() < PaddedSize) { + OS << format("<corrupt type (0x%x) datasz: 0x%x>", Type, DataSize); + Properties.push_back(OS.str()); + break; + } + Properties.push_back( + getGNUProperty<ELFT>(Type, DataSize, Arr.take_front(PaddedSize))); + Arr = Arr.drop_front(PaddedSize); + } + + if (!Arr.empty()) + Properties.push_back("<corrupted GNU_PROPERTY_TYPE_0>"); + + return Properties; +} + +struct GNUAbiTag { + std::string OSName; + std::string ABI; + bool IsValid; +}; + +template <typename ELFT> +static GNUAbiTag getGNUAbiTag(ArrayRef<uint8_t> Desc) { + typedef typename ELFT::Word Elf_Word; + + ArrayRef<Elf_Word> Words(reinterpret_cast<const Elf_Word*>(Desc.begin()), + reinterpret_cast<const Elf_Word*>(Desc.end())); + + if (Words.size() < 4) + return {"", "", /*IsValid=*/false}; + + static const char *OSNames[] = { + "Linux", "Hurd", "Solaris", "FreeBSD", "NetBSD", "Syllable", "NaCl", + }; + StringRef OSName = "Unknown"; + if (Words[0] < array_lengthof(OSNames)) + OSName = OSNames[Words[0]]; + uint32_t Major = Words[1], Minor = Words[2], Patch = Words[3]; + std::string str; + raw_string_ostream ABI(str); + ABI << Major << "." << Minor << "." << Patch; + return {OSName, ABI.str(), /*IsValid=*/true}; +} + +static std::string getGNUBuildId(ArrayRef<uint8_t> Desc) { + std::string str; + raw_string_ostream OS(str); + for (const auto &B : Desc) + OS << format_hex_no_prefix(B, 2); + return OS.str(); +} + +static StringRef getGNUGoldVersion(ArrayRef<uint8_t> Desc) { + return StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); +} + +template <typename ELFT> +static void printGNUNote(raw_ostream &OS, uint32_t NoteType, + ArrayRef<uint8_t> Desc) { switch (NoteType) { default: return; case ELF::NT_GNU_ABI_TAG: { - static const char *OSNames[] = { - "Linux", "Hurd", "Solaris", "FreeBSD", "NetBSD", "Syllable", "NaCl", - }; - - StringRef OSName = "Unknown"; - if (Words[0] < array_lengthof(OSNames)) - OSName = OSNames[Words[0]]; - uint32_t Major = Words[1], Minor = Words[2], Patch = Words[3]; - - if (Words.size() < 4) + const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); + if (!AbiTag.IsValid) OS << " <corrupt GNU_ABI_TAG>"; else - OS << " OS: " << OSName << ", ABI: " << Major << "." << Minor << "." - << Patch; + OS << " OS: " << AbiTag.OSName << ", ABI: " << AbiTag.ABI; break; } case ELF::NT_GNU_BUILD_ID: { - OS << " Build ID: "; - ArrayRef<uint8_t> ID(reinterpret_cast<const uint8_t *>(Words.data()), Size); - for (const auto &B : ID) - OS << format_hex_no_prefix(B, 2); + OS << " Build ID: " << getGNUBuildId(Desc); break; } case ELF::NT_GNU_GOLD_VERSION: - OS << " Version: " - << StringRef(reinterpret_cast<const char *>(Words.data()), Size); + OS << " Version: " << getGNUGoldVersion(Desc); 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>"; + for (const auto &Property : getGNUPropertyList<ELFT>(Desc)) + OS << " " << Property << "\n"; break; } OS << '\n'; } +struct AMDNote { + std::string Type; + std::string Value; +}; + template <typename ELFT> -static void printAMDGPUNote(raw_ostream &OS, uint32_t NoteType, - ArrayRef<typename ELFT::Word> Words, size_t Size) { +static AMDNote getAMDNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { switch (NoteType) { default: - return; - case ELF::NT_AMD_AMDGPU_HSA_METADATA: - OS << " HSA Metadata:\n" - << StringRef(reinterpret_cast<const char *>(Words.data()), Size); - break; - case ELF::NT_AMD_AMDGPU_ISA: - OS << " ISA Version:\n" - << " " - << StringRef(reinterpret_cast<const char *>(Words.data()), Size); - break; - case ELF::NT_AMD_AMDGPU_PAL_METADATA: - const uint32_t *PALMetadataBegin = reinterpret_cast<const uint32_t *>(Words.data()); - const uint32_t *PALMetadataEnd = PALMetadataBegin + Size; - std::vector<uint32_t> PALMetadata(PALMetadataBegin, PALMetadataEnd); - std::string PALMetadataString; - auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString); - OS << " PAL Metadata:\n"; - if (Error) { - OS << " Invalid"; - return; - } - OS << PALMetadataString; - break; + return {"", ""}; + case ELF::NT_AMD_AMDGPU_HSA_METADATA: + return {"HSA Metadata", + std::string(reinterpret_cast<const char *>(Desc.data()), + Desc.size())}; + case ELF::NT_AMD_AMDGPU_ISA: + return {"ISA Version", + std::string(reinterpret_cast<const char *>(Desc.data()), + Desc.size())}; + case ELF::NT_AMD_AMDGPU_PAL_METADATA: + const uint32_t *PALMetadataBegin = + reinterpret_cast<const uint32_t *>(Desc.data()); + const uint32_t *PALMetadataEnd = PALMetadataBegin + Desc.size(); + std::vector<uint32_t> PALMetadata(PALMetadataBegin, PALMetadataEnd); + std::string PALMetadataString; + auto Error = AMDGPU::PALMD::toString(PALMetadata, PALMetadataString); + if (Error) { + return {"PAL Metadata", "Invalid"}; + } + return {"PAL Metadata", PALMetadataString}; + } +} + +struct AMDGPUNote { + std::string Type; + std::string Value; +}; + +template <typename ELFT> +static AMDGPUNote getAMDGPUNote(uint32_t NoteType, ArrayRef<uint8_t> Desc) { + switch (NoteType) { + default: + return {"", ""}; + case ELF::NT_AMDGPU_METADATA: + auto MsgPackString = + StringRef(reinterpret_cast<const char *>(Desc.data()), Desc.size()); + msgpack::Reader MsgPackReader(MsgPackString); + auto OptMsgPackNodeOrErr = msgpack::Node::read(MsgPackReader); + if (errorToBool(OptMsgPackNodeOrErr.takeError())) + return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + auto &OptMsgPackNode = *OptMsgPackNodeOrErr; + if (!OptMsgPackNode) + return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + auto &MsgPackNode = *OptMsgPackNode; + + AMDGPU::HSAMD::V3::MetadataVerifier Verifier(true); + if (!Verifier.verify(*MsgPackNode)) + return {"AMDGPU Metadata", "Invalid AMDGPU Metadata"}; + + std::string HSAMetadataString; + raw_string_ostream StrOS(HSAMetadataString); + yaml::Output YOut(StrOS); + YOut << MsgPackNode; + + return {"AMDGPU Metadata", StrOS.str()}; } - OS.flush(); } template <class ELFT> @@ -3771,7 +3916,7 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { auto ProcessNote = [&](const Elf_Note &Note) { StringRef Name = Note.getName(); - ArrayRef<Elf_Word> Descriptor = Note.getDesc(); + ArrayRef<uint8_t> Descriptor = Note.getDesc(); Elf_Word Type = Note.getType(); OS << " " << Name << std::string(22 - Name.size(), ' ') @@ -3779,12 +3924,19 @@ void GNUStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { if (Name == "GNU") { OS << getGNUNoteTypeName(Type) << '\n'; - printGNUNote<ELFT>(OS, Type, Descriptor, Descriptor.size()); + printGNUNote<ELFT>(OS, Type, Descriptor); } else if (Name == "FreeBSD") { OS << getFreeBSDNoteTypeName(Type) << '\n'; } else if (Name == "AMD") { + OS << getAMDNoteTypeName(Type) << '\n'; + const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + OS << " " << N.Type << ":\n " << N.Value << '\n'; + } else if (Name == "AMDGPU") { OS << getAMDGPUNoteTypeName(Type) << '\n'; - printAMDGPUNote<ELFT>(OS, Type, Descriptor, Descriptor.size()); + const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + OS << " " << N.Type << ":\n " << N.Value << '\n'; } else { OS << "Unknown note type: (" << format_hex(Type, 10) << ')'; } @@ -4123,7 +4275,8 @@ void LLVMStyle<ELFT>::printRelocation(const ELFO *Obj, Elf_Rela Rel, } } -template <class ELFT> void LLVMStyle<ELFT>::printSections(const ELFO *Obj) { +template <class ELFT> +void LLVMStyle<ELFT>::printSectionHeaders(const ELFO *Obj) { ListScope SectionsD(W, "Sections"); int SectionIndex = -1; @@ -4379,7 +4532,7 @@ void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { while (Cur != End) { unsigned Size; const char *Err; - uint64_t SymIndex = decodeULEB128(Cur, &Size, Contents.end(), &Err); + uint64_t SymIndex = decodeULEB128(Cur, &Size, End, &Err); if (Err) reportError(Err); W.printNumber("Sym", this->dumper()->getStaticSymbolName(SymIndex), @@ -4388,9 +4541,103 @@ void LLVMStyle<ELFT>::printAddrsig(const ELFFile<ELFT> *Obj) { } } +template <typename ELFT> +static void printGNUNoteLLVMStyle(uint32_t NoteType, + ArrayRef<uint8_t> Desc, + ScopedPrinter &W) { + switch (NoteType) { + default: + return; + case ELF::NT_GNU_ABI_TAG: { + const GNUAbiTag &AbiTag = getGNUAbiTag<ELFT>(Desc); + if (!AbiTag.IsValid) { + W.printString("ABI", "<corrupt GNU_ABI_TAG>"); + } else { + W.printString("OS", AbiTag.OSName); + W.printString("ABI", AbiTag.ABI); + } + break; + } + case ELF::NT_GNU_BUILD_ID: { + W.printString("Build ID", getGNUBuildId(Desc)); + break; + } + case ELF::NT_GNU_GOLD_VERSION: + W.printString("Version", getGNUGoldVersion(Desc)); + break; + case ELF::NT_GNU_PROPERTY_TYPE_0: + ListScope D(W, "Property"); + for (const auto &Property : getGNUPropertyList<ELFT>(Desc)) + W.printString(Property); + break; + } +} + template <class ELFT> void LLVMStyle<ELFT>::printNotes(const ELFFile<ELFT> *Obj) { - W.startLine() << "printNotes not implemented!\n"; + ListScope L(W, "Notes"); + const Elf_Ehdr *e = Obj->getHeader(); + bool IsCore = e->e_type == ELF::ET_CORE; + + auto PrintHeader = [&](const typename ELFT::Off Offset, + const typename ELFT::Addr Size) { + W.printHex("Offset", Offset); + W.printHex("Size", Size); + }; + + auto ProcessNote = [&](const Elf_Note &Note) { + DictScope D2(W, "Note"); + StringRef Name = Note.getName(); + ArrayRef<uint8_t> Descriptor = Note.getDesc(); + Elf_Word Type = Note.getType(); + + W.printString("Owner", Name); + W.printHex("Data size", Descriptor.size()); + if (Name == "GNU") { + W.printString("Type", getGNUNoteTypeName(Type)); + printGNUNoteLLVMStyle<ELFT>(Type, Descriptor, W); + } else if (Name == "FreeBSD") { + W.printString("Type", getFreeBSDNoteTypeName(Type)); + } else if (Name == "AMD") { + W.printString("Type", getAMDNoteTypeName(Type)); + const AMDNote N = getAMDNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + W.printString(N.Type, N.Value); + } else if (Name == "AMDGPU") { + W.printString("Type", getAMDGPUNoteTypeName(Type)); + const AMDGPUNote N = getAMDGPUNote<ELFT>(Type, Descriptor); + if (!N.Type.empty()) + W.printString(N.Type, N.Value); + } else { + W.getOStream() << "Unknown note type: (" << format_hex(Type, 10) << ')'; + } + }; + + if (IsCore) { + for (const auto &P : unwrapOrError(Obj->program_headers())) { + if (P.p_type != PT_NOTE) + continue; + DictScope D(W, "NoteSection"); + 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) + continue; + DictScope D(W, "NoteSection"); + 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> diff --git a/tools/llvm-readobj/MachODumper.cpp b/tools/llvm-readobj/MachODumper.cpp index 69ef1556f78d..35e4cfcb6b10 100644 --- a/tools/llvm-readobj/MachODumper.cpp +++ b/tools/llvm-readobj/MachODumper.cpp @@ -32,7 +32,7 @@ public: : ObjDumper(Writer), Obj(Obj) {} void printFileHeaders() override; - void printSections() override; + void printSectionHeaders() override; void printRelocations() override; void printSymbols() override; void printDynamicSymbols() override; @@ -59,7 +59,7 @@ private: void printRelocation(const MachOObjectFile *Obj, const RelocationRef &Reloc); - void printSections(const MachOObjectFile *Obj); + void printSectionHeaders(const MachOObjectFile *Obj); const MachOObjectFile *Obj; }; @@ -428,11 +428,9 @@ void MachODumper::printFileHeaders(const MachHeader &Header) { W.printFlags("Flags", Header.flags, makeArrayRef(MachOHeaderFlags)); } -void MachODumper::printSections() { - return printSections(Obj); -} +void MachODumper::printSectionHeaders() { return printSectionHeaders(Obj); } -void MachODumper::printSections(const MachOObjectFile *Obj) { +void MachODumper::printSectionHeaders(const MachOObjectFile *Obj) { ListScope Group(W, "Sections"); int SectionIndex = -1; diff --git a/tools/llvm-readobj/ObjDumper.h b/tools/llvm-readobj/ObjDumper.h index 8c3a7bec73be..13de563469ab 100644 --- a/tools/llvm-readobj/ObjDumper.h +++ b/tools/llvm-readobj/ObjDumper.h @@ -33,7 +33,7 @@ public: virtual ~ObjDumper(); virtual void printFileHeaders() = 0; - virtual void printSections() = 0; + virtual void printSectionHeaders() = 0; virtual void printRelocations() = 0; virtual void printSymbols() = 0; virtual void printDynamicSymbols() = 0; diff --git a/tools/llvm-readobj/WasmDumper.cpp b/tools/llvm-readobj/WasmDumper.cpp index ce224836225e..79d3db4e2d29 100644 --- a/tools/llvm-readobj/WasmDumper.cpp +++ b/tools/llvm-readobj/WasmDumper.cpp @@ -23,28 +23,21 @@ using namespace object; namespace { static const EnumEntry<unsigned> WasmSymbolTypes[] = { -#define ENUM_ENTRY(X) { #X, wasm::WASM_SYMBOL_TYPE_##X } - ENUM_ENTRY(FUNCTION), - ENUM_ENTRY(DATA), - ENUM_ENTRY(GLOBAL), - ENUM_ENTRY(SECTION), +#define ENUM_ENTRY(X) \ + { #X, wasm::WASM_SYMBOL_TYPE_##X } + ENUM_ENTRY(FUNCTION), ENUM_ENTRY(DATA), ENUM_ENTRY(GLOBAL), + ENUM_ENTRY(SECTION), ENUM_ENTRY(EVENT), #undef ENUM_ENTRY }; static const EnumEntry<uint32_t> WasmSectionTypes[] = { -#define ENUM_ENTRY(X) { #X, wasm::WASM_SEC_##X } - ENUM_ENTRY(CUSTOM), - ENUM_ENTRY(TYPE), - ENUM_ENTRY(IMPORT), - ENUM_ENTRY(FUNCTION), - ENUM_ENTRY(TABLE), - ENUM_ENTRY(MEMORY), - ENUM_ENTRY(GLOBAL), - ENUM_ENTRY(EXPORT), - ENUM_ENTRY(START), - ENUM_ENTRY(ELEM), - ENUM_ENTRY(CODE), - ENUM_ENTRY(DATA), +#define ENUM_ENTRY(X) \ + { #X, wasm::WASM_SEC_##X } + ENUM_ENTRY(CUSTOM), ENUM_ENTRY(TYPE), ENUM_ENTRY(IMPORT), + ENUM_ENTRY(FUNCTION), ENUM_ENTRY(TABLE), ENUM_ENTRY(MEMORY), + ENUM_ENTRY(GLOBAL), ENUM_ENTRY(EVENT), ENUM_ENTRY(EXPORT), + ENUM_ENTRY(START), ENUM_ENTRY(ELEM), ENUM_ENTRY(CODE), + ENUM_ENTRY(DATA), #undef ENUM_ENTRY }; @@ -54,7 +47,7 @@ public: : ObjDumper(Writer), Obj(Obj) {} void printFileHeaders() override; - void printSections() override; + void printSectionHeaders() override; void printRelocations() override; void printSymbols() override; void printDynamicSymbols() override { llvm_unreachable("unimplemented"); } @@ -108,7 +101,7 @@ void WasmDumper::printRelocation(const SectionRef &Section, if (HasAddend) W.printNumber("Addend", WasmReloc.Addend); } else { - raw_ostream& OS = W.startLine(); + raw_ostream &OS = W.startLine(); OS << W.hex(Reloc.getOffset()) << " " << RelocTypeName << " "; if (!SymName.empty()) OS << SymName; @@ -154,7 +147,7 @@ void WasmDumper::printSymbols() { printSymbol(Symbol); } -void WasmDumper::printSections() { +void WasmDumper::printSectionHeaders() { ListScope Group(W, "Sections"); for (const SectionRef &Section : Obj->sections()) { const WasmSection &WasmSec = Obj->getWasmSection(Section); @@ -169,7 +162,7 @@ void WasmDumper::printSections() { const wasm::WasmLinkingData &LinkingData = Obj->linkingData(); if (!LinkingData.InitFunctions.empty()) { ListScope Group(W, "InitFunctions"); - for (const wasm::WasmInitFunc &F: LinkingData.InitFunctions) + for (const wasm::WasmInitFunc &F : LinkingData.InitFunctions) W.startLine() << F.Symbol << " (priority=" << F.Priority << ")\n"; } } @@ -177,7 +170,7 @@ void WasmDumper::printSections() { case wasm::WASM_SEC_DATA: { ListScope Group(W, "Segments"); for (const WasmSegment &Segment : Obj->dataSegments()) { - const wasm::WasmDataSegment& Seg = Segment.Data; + const wasm::WasmDataSegment &Seg = Segment.Data; DictScope Group(W, "Segment"); if (!Seg.Name.empty()) W.printString("Name", Seg.Name); @@ -219,7 +212,7 @@ void WasmDumper::printSymbol(const SymbolRef &Sym) { W.printHex("Flags", Symbol.Info.Flags); } -} +} // namespace namespace llvm { diff --git a/tools/llvm-readobj/llvm-readobj.cpp b/tools/llvm-readobj/llvm-readobj.cpp index a7236c02b8ae..81ce7a590364 100644 --- a/tools/llvm-readobj/llvm-readobj.cpp +++ b/tools/llvm-readobj/llvm-readobj.cpp @@ -48,58 +48,72 @@ namespace opts { cl::desc("<input object files>"), cl::ZeroOrMore); + // -all, -a + cl::opt<bool> + All("all", + cl::desc("Equivalent to setting: --file-headers, --program-headers, " + "--section-headers, --symbols, --relocations, " + "--dynamic-table, --notes, --version-info, --unwind, " + "--section-groups and --elf-hash-histogram.")); + cl::alias AllShort("a", cl::desc("Alias for --all"), cl::aliasopt(All)); + + // --headers -e + cl::opt<bool> + Headers("headers", + cl::desc("Equivalent to setting: --file-headers, --program-headers, " + "--section-headers")); + cl::alias HeadersShort("e", cl::desc("Alias for --headers"), + cl::aliasopt(Headers)); + // -wide, -W - cl::opt<bool> WideOutput("wide", - cl::desc("Ignored for compatibility with GNU readelf")); + cl::opt<bool> + WideOutput("wide", cl::desc("Ignored for compatibility with GNU readelf"), + cl::Hidden); cl::alias WideOutputShort("W", cl::desc("Alias for --wide"), cl::aliasopt(WideOutput)); - // -file-headers, -h + // -file-headers, -file-header, -h cl::opt<bool> FileHeaders("file-headers", cl::desc("Display file headers ")); - cl::alias FileHeadersShort("h", - cl::desc("Alias for --file-headers"), - cl::aliasopt(FileHeaders)); - - // -sections, -s, -S - // Note: In GNU readelf, -s means --symbols! - cl::opt<bool> Sections("sections", - cl::desc("Display all sections.")); - cl::alias SectionsShort("s", - cl::desc("Alias for --sections"), - cl::aliasopt(Sections)); - cl::alias SectionsShortUpper("S", - cl::desc("Alias for --sections"), - cl::aliasopt(Sections)); - - // -section-relocations, -sr + cl::alias FileHeadersShort("h", cl::desc("Alias for --file-headers"), + cl::aliasopt(FileHeaders), cl::NotHidden); + cl::alias FileHeadersSingular("file-header", + cl::desc("Alias for --file-headers"), + cl::aliasopt(FileHeaders)); + + // -section-headers, -sections, -S + // Also -s in llvm-readobj mode. + cl::opt<bool> SectionHeaders("section-headers", + cl::desc("Display all section headers.")); + cl::alias SectionsShortUpper("S", cl::desc("Alias for --section-headers"), + cl::aliasopt(SectionHeaders), cl::NotHidden); + cl::alias SectionHeadersAlias("sections", + cl::desc("Alias for --section-headers"), + cl::aliasopt(SectionHeaders), cl::NotHidden); + + // -section-relocations + // Also -sr in llvm-readobj mode. cl::opt<bool> SectionRelocations("section-relocations", cl::desc("Display relocations for each section shown.")); - cl::alias SectionRelocationsShort("sr", - cl::desc("Alias for --section-relocations"), - cl::aliasopt(SectionRelocations)); - // -section-symbols, -st + // -section-symbols + // Also -st in llvm-readobj mode. cl::opt<bool> SectionSymbols("section-symbols", cl::desc("Display symbols for each section shown.")); - cl::alias SectionSymbolsShort("st", - cl::desc("Alias for --section-symbols"), - cl::aliasopt(SectionSymbols)); - // -section-data, -sd + // -section-data + // Also -sd in llvm-readobj mode. cl::opt<bool> SectionData("section-data", cl::desc("Display section data for each section shown.")); - cl::alias SectionDataShort("sd", - cl::desc("Alias for --section-data"), - cl::aliasopt(SectionData)); - // -relocations, -r + // -relocations, -relocs, -r cl::opt<bool> Relocations("relocations", cl::desc("Display the relocation entries in the file")); - cl::alias RelocationsShort("r", - cl::desc("Alias for --relocations"), - cl::aliasopt(Relocations)); + cl::alias RelocationsShort("r", cl::desc("Alias for --relocations"), + cl::aliasopt(Relocations), cl::NotHidden); + cl::alias RelocationsGNU("relocs", cl::desc("Alias for --relocations"), + cl::aliasopt(Relocations)); // -notes, -n cl::opt<bool> Notes("notes", cl::desc("Display the ELF notes in the file")); @@ -109,19 +123,19 @@ namespace opts { cl::opt<bool> DynRelocs("dyn-relocations", cl::desc("Display the dynamic relocation entries in the file")); - // -symbols, -t + // -symbols + // Also -s in llvm-readelf mode, or -t in llvm-readobj mode. cl::opt<bool> Symbols("symbols", cl::desc("Display the symbol table")); - cl::alias SymbolsShort("t", - cl::desc("Alias for --symbols"), - cl::aliasopt(Symbols)); + cl::alias SymbolsGNU("syms", cl::desc("Alias for --symbols"), + cl::aliasopt(Symbols)); - // -dyn-symbols, -dt + // -dyn-symbols, -dyn-syms + // Also -dt in llvm-readobj mode. cl::opt<bool> DynamicSymbols("dyn-symbols", cl::desc("Display the dynamic symbol table")); - cl::alias DynamicSymbolsShort("dt", - cl::desc("Alias for --dyn-symbols"), - cl::aliasopt(DynamicSymbols)); + cl::alias DynSymsGNU("dyn-syms", cl::desc("Alias for --dyn-symbols"), + cl::aliasopt(DynamicSymbols)); // -unwind, -u cl::opt<bool> UnwindInfo("unwind", @@ -130,29 +144,33 @@ namespace opts { cl::desc("Alias for --unwind"), cl::aliasopt(UnwindInfo)); - // -dynamic-table + // -dynamic-table, -dynamic, -d cl::opt<bool> DynamicTable("dynamic-table", cl::desc("Display the ELF .dynamic section table")); cl::alias DynamicTableShort("d", cl::desc("Alias for --dynamic-table"), + cl::aliasopt(DynamicTable), cl::NotHidden); + cl::alias DynamicTableAlias("dynamic", cl::desc("Alias for --dynamic-table"), cl::aliasopt(DynamicTable)); // -needed-libs cl::opt<bool> NeededLibraries("needed-libs", cl::desc("Display the needed libraries")); - // -program-headers + // -program-headers, -segments, -l cl::opt<bool> ProgramHeaders("program-headers", cl::desc("Display ELF program headers")); cl::alias ProgramHeadersShort("l", cl::desc("Alias for --program-headers"), - cl::aliasopt(ProgramHeaders)); + cl::aliasopt(ProgramHeaders), cl::NotHidden); + cl::alias SegmentsAlias("segments", cl::desc("Alias for --program-headers"), + cl::aliasopt(ProgramHeaders)); - // -string-dump + // -string-dump, -p 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 + // -hex-dump, -x cl::list<std::string> HexDump("hex-dump", cl::desc("<number|name>"), cl::ZeroOrMore); cl::alias HexDumpShort("x", cl::desc("Alias for --hex-dump"), @@ -188,11 +206,9 @@ namespace opts { "codeview-subsection-bytes", cl::desc("Dump raw contents of codeview debug sections and records")); - // -arm-attributes, -a + // -arm-attributes cl::opt<bool> ARMAttributes("arm-attributes", cl::desc("Display the ARM attributes section")); - cl::alias ARMAttributesShort("a", cl::desc("Alias for --arm-attributes"), - cl::aliasopt(ARMAttributes)); // -mips-plt-got cl::opt<bool> @@ -283,28 +299,40 @@ namespace opts { PrintStackMap("stackmap", cl::desc("Display contents of stackmap section")); - // -version-info + // -version-info, -V cl::opt<bool> VersionInfo("version-info", cl::desc("Display ELF version sections (if present)")); cl::alias VersionInfoShort("V", cl::desc("Alias for -version-info"), cl::aliasopt(VersionInfo)); + // -elf-section-groups, -section-groups, -g cl::opt<bool> SectionGroups("elf-section-groups", cl::desc("Display ELF section group contents")); + cl::alias SectionGroupsAlias("section-groups", + cl::desc("Alias for -elf-sections-groups"), + cl::aliasopt(SectionGroups)); cl::alias SectionGroupsShort("g", cl::desc("Alias for -elf-sections-groups"), cl::aliasopt(SectionGroups)); + + // -elf-hash-histogram, -histogram, -I cl::opt<bool> HashHistogram( "elf-hash-histogram", cl::desc("Display bucket list histogram for hash sections")); cl::alias HashHistogramShort("I", cl::desc("Alias for -elf-hash-histogram"), cl::aliasopt(HashHistogram)); + cl::alias HistogramAlias("histogram", + cl::desc("Alias for --elf-hash-histogram"), + cl::aliasopt(HashHistogram)); + // -elf-cg-profile cl::opt<bool> CGProfile("elf-cg-profile", cl::desc("Display callgraph profile section")); - cl::opt<bool> Addrsig("elf-addrsig", + // -addrsig + cl::opt<bool> Addrsig("addrsig", cl::desc("Display address-significance table")); + // -elf-output-style cl::opt<OutputStyleTy> Output("elf-output-style", cl::desc("Specify ELF dump style"), cl::values(clEnumVal(LLVM, "LLVM default style"), @@ -418,8 +446,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) { if (opts::FileHeaders) Dumper->printFileHeaders(); - if (opts::Sections) - Dumper->printSections(); + if (opts::SectionHeaders) + Dumper->printSectionHeaders(); if (opts::Relocations) Dumper->printRelocations(); if (opts::DynRelocs) @@ -492,6 +520,8 @@ static void dumpObject(const ObjectFile *Obj, ScopedPrinter &Writer) { Dumper->printCOFFResources(); if (opts::COFFLoadConfig) Dumper->printCOFFLoadConfig(); + if (opts::Addrsig) + Dumper->printAddrsig(); if (opts::CodeView) Dumper->printCodeViewDebugInfo(); if (opts::CodeViewMergedTypes) @@ -586,21 +616,87 @@ static void dumpInput(StringRef File) { reportError(File, readobj_error::unrecognized_file_format); } +/// Registers aliases that should only be allowed by readobj. +static void registerReadobjAliases() { + // -s has meant --sections for a very long time in llvm-readobj despite + // meaning --symbols in readelf. + static cl::alias SectionsShort("s", cl::desc("Alias for --section-headers"), + cl::aliasopt(opts::SectionHeaders), + cl::NotHidden); + + // Only register -t in llvm-readobj, as readelf reserves it for + // --section-details (not implemented yet). + static cl::alias SymbolsShort("t", cl::desc("Alias for --symbols"), + cl::aliasopt(opts::Symbols), cl::NotHidden); + + // The following two-letter aliases are only provided for readobj, as readelf + // allows single-letter args to be grouped together. + static cl::alias SectionRelocationsShort( + "sr", cl::desc("Alias for --section-relocations"), + cl::aliasopt(opts::SectionRelocations)); + static cl::alias SectionDataShort("sd", cl::desc("Alias for --section-data"), + cl::aliasopt(opts::SectionData)); + static cl::alias SectionSymbolsShort("st", + cl::desc("Alias for --section-symbols"), + cl::aliasopt(opts::SectionSymbols)); + static cl::alias DynamicSymbolsShort("dt", + cl::desc("Alias for --dyn-symbols"), + cl::aliasopt(opts::DynamicSymbols)); +} + +/// Registers aliases that should only be allowed by readelf. +static void registerReadelfAliases() { + // -s is here because for readobj it means --sections. + static cl::alias SymbolsShort("s", cl::desc("Alias for --symbols"), + cl::aliasopt(opts::Symbols), cl::NotHidden, + cl::Grouping); + + // Allow all single letter flags to be grouped together. + for (auto &OptEntry : cl::getRegisteredOptions()) { + StringRef ArgName = OptEntry.getKey(); + cl::Option *Option = OptEntry.getValue(); + if (ArgName.size() == 1) + Option->setFormattingFlag(cl::Grouping); + } +} + int main(int argc, const char *argv[]) { InitLLVM X(argc, argv); // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); - opts::WideOutput.setHiddenFlag(cl::Hidden); - - if (sys::path::stem(argv[0]).find("readelf") != StringRef::npos) + if (sys::path::stem(argv[0]).contains("readelf")) { opts::Output = opts::GNU; + registerReadelfAliases(); + } else { + registerReadobjAliases(); + } cl::ParseCommandLineOptions(argc, argv, "LLVM Object Reader\n"); + if (opts::All) { + opts::FileHeaders = true; + opts::ProgramHeaders = true; + opts::SectionHeaders = true; + opts::Symbols = true; + opts::Relocations = true; + opts::DynamicTable = true; + opts::Notes = true; + opts::VersionInfo = true; + opts::UnwindInfo = true; + opts::SectionGroups = true; + opts::HashHistogram = true; + } + + if (opts::Headers) { + opts::FileHeaders = true; + opts::ProgramHeaders = true; + opts::SectionHeaders = true; + } + // Default to stdin if no filename is specified. - if (opts::InputFilenames.size() == 0) + if (opts::InputFilenames.empty()) opts::InputFilenames.push_back("-"); llvm::for_each(opts::InputFilenames, dumpInput); diff --git a/tools/llvm-readobj/llvm-readobj.h b/tools/llvm-readobj/llvm-readobj.h index 374ffd03e13a..92ed098dc642 100644 --- a/tools/llvm-readobj/llvm-readobj.h +++ b/tools/llvm-readobj/llvm-readobj.h @@ -40,7 +40,7 @@ namespace llvm { return *EO; std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(EO.takeError(), OS, ""); + logAllUnhandledErrors(EO.takeError(), OS); OS.flush(); reportError(Buf); } @@ -49,22 +49,13 @@ namespace llvm { } // namespace llvm namespace opts { - extern llvm::cl::list<std::string> InputFilenames; - extern llvm::cl::opt<bool> FileHeaders; - extern llvm::cl::opt<bool> Sections; extern llvm::cl::opt<bool> SectionRelocations; extern llvm::cl::opt<bool> SectionSymbols; extern llvm::cl::opt<bool> SectionData; - extern llvm::cl::opt<bool> Relocations; - extern llvm::cl::opt<bool> Symbols; 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; - extern llvm::cl::opt<bool> MipsPLTGOT; enum OutputStyleTy { LLVM, GNU }; extern llvm::cl::opt<OutputStyleTy> Output; } // namespace opts diff --git a/tools/llvm-rtdyld/llvm-rtdyld.cpp b/tools/llvm-rtdyld/llvm-rtdyld.cpp index 54db1ec113fc..975638ed82d1 100644 --- a/tools/llvm-rtdyld/llvm-rtdyld.cpp +++ b/tools/llvm-rtdyld/llvm-rtdyld.cpp @@ -88,25 +88,30 @@ CheckFiles("check", cl::desc("File containing RuntimeDyld verifier checks."), cl::ZeroOrMore); -static cl::opt<uint64_t> +// Tracking BUG: 19665 +// http://llvm.org/bugs/show_bug.cgi?id=19665 +// +// Do not change these options to cl::opt<uint64_t> since this silently breaks +// argument parsing. +static cl::opt<unsigned long long> PreallocMemory("preallocate", cl::desc("Allocate memory upfront rather than on-demand"), cl::init(0)); -static cl::opt<uint64_t> +static cl::opt<unsigned long long> TargetAddrStart("target-addr-start", cl::desc("For -verify only: start of phony target address " "range."), cl::init(4096), // Start at "page 1" - no allocating at "null". cl::Hidden); -static cl::opt<uint64_t> +static cl::opt<unsigned long long> TargetAddrEnd("target-addr-end", cl::desc("For -verify only: end of phony target address range."), cl::init(~0ULL), cl::Hidden); -static cl::opt<uint64_t> +static cl::opt<unsigned long long> TargetSectionSep("target-section-sep", cl::desc("For -verify only: Separation between sections in " "phony target address space."), @@ -304,7 +309,7 @@ static int printLineInfoForInput(bool LoadObjects, bool UseDebugObj) { if (!MaybeObj) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(MaybeObj.takeError(), OS, ""); + logAllUnhandledErrors(MaybeObj.takeError(), OS); OS.flush(); ErrorAndExit("unable to create object file: '" + Buf + "'"); } @@ -433,7 +438,7 @@ static int executeInput() { if (!MaybeObj) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(MaybeObj.takeError(), OS, ""); + logAllUnhandledErrors(MaybeObj.takeError(), OS); OS.flush(); ErrorAndExit("unable to create object file: '" + Buf + "'"); } @@ -577,7 +582,11 @@ static void remapSectionsAndSymbols(const llvm::Triple &TargetTriple, if (LoadAddr && *LoadAddr != static_cast<uint64_t>( reinterpret_cast<uintptr_t>(Tmp->first))) { - AlreadyAllocated[*LoadAddr] = Tmp->second; + // A section will have a LoadAddr of 0 if it wasn't loaded for whatever + // reason (e.g. zero byte COFF sections). Don't include those sections in + // the allocation map. + if (*LoadAddr != 0) + AlreadyAllocated[*LoadAddr] = Tmp->second; Worklist.erase(Tmp); } } @@ -701,7 +710,7 @@ static int linkAndVerify() { if (!MaybeObj) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(MaybeObj.takeError(), OS, ""); + logAllUnhandledErrors(MaybeObj.takeError(), OS); OS.flush(); ErrorAndExit("unable to create object file: '" + Buf + "'"); } diff --git a/tools/llvm-shlib/CMakeLists.txt b/tools/llvm-shlib/CMakeLists.txt index 836024eb8101..187066e5ded2 100644 --- a/tools/llvm-shlib/CMakeLists.txt +++ b/tools/llvm-shlib/CMakeLists.txt @@ -6,81 +6,88 @@ set(SOURCES libllvm.cpp ) -llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) - if(LLVM_LINK_LLVM_DYLIB AND LLVM_DYLIB_EXPORTED_SYMBOL_FILE) message(WARNING "Using LLVM_LINK_LLVM_DYLIB with LLVM_DYLIB_EXPORTED_SYMBOL_FILE may not work. Use at your own risk.") endif() -# libLLVM.so should not have any dependencies on any other LLVM -# shared libraries. When using the "all" pseudo-component, -# LLVM_AVAILABLE_LIBS is added to the dependencies, which may -# contain shared libraries (e.g. libLTO). -# -# Also exclude libLLVMTableGen for the following reasons: -# - it is only used by internal *-tblgen utilities; -# - it pollutes the global options space. -foreach(lib ${LIB_NAMES}) - get_target_property(t ${lib} TYPE) - if("${lib}" STREQUAL "LLVMTableGen") - elseif("x${t}" STREQUAL "xSTATIC_LIBRARY") - list(APPEND FILTERED_LIB_NAMES ${lib}) +if(LLVM_BUILD_LLVM_DYLIB) + if(MSVC) + message(FATAL_ERROR "Generating libLLVM is not supported on MSVC") endif() -endforeach() -set(LIB_NAMES ${FILTERED_LIB_NAMES}) - -if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) - set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE}) - add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) -endif() -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( - ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in - ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map) - - # GNU ld doesn't resolve symbols in the version script. - set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) - if (NOT LLVM_LINKER_IS_SOLARISLD) - # Solaris ld does not accept global: *; so there is no way to version *all* global symbols - set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES}) + llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) + + # libLLVM.so should not have any dependencies on any other LLVM + # shared libraries. When using the "all" pseudo-component, + # LLVM_AVAILABLE_LIBS is added to the dependencies, which may + # contain shared libraries (e.g. libLTO). + # + # Also exclude libLLVMTableGen for the following reasons: + # - it is only used by internal *-tblgen utilities; + # - it pollutes the global options space. + foreach(lib ${LIB_NAMES}) + get_target_property(t ${lib} TYPE) + if("${lib}" STREQUAL "LLVMTableGen") + elseif("x${t}" STREQUAL "xSTATIC_LIBRARY") + list(APPEND FILTERED_LIB_NAMES ${lib}) + endif() + endforeach() + set(LIB_NAMES ${FILTERED_LIB_NAMES}) + + if(LLVM_DYLIB_EXPORTED_SYMBOL_FILE) + set(LLVM_EXPORTED_SYMBOL_FILE ${LLVM_DYLIB_EXPORTED_SYMBOL_FILE}) + add_custom_target(libLLVMExports DEPENDS ${LLVM_EXPORTED_SYMBOL_FILE}) endif() -elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) -endif() -target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) + 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 "GNU") + 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( + ${CMAKE_CURRENT_SOURCE_DIR}/simple_version_script.map.in + ${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map) + + # GNU ld doesn't resolve symbols in the version script. + set(LIB_NAMES -Wl,--whole-archive ${LIB_NAMES} -Wl,--no-whole-archive) + if (NOT LLVM_LINKER_IS_SOLARISLD) + # Solaris ld does not accept global: *; so there is no way to version *all* global symbols + set(LIB_NAMES -Wl,--version-script,${LLVM_LIBRARY_DIR}/tools/llvm-shlib/simple_version_script.map ${LIB_NAMES}) + endif() + elseif("${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") + set(LIB_NAMES -Wl,-all_load ${LIB_NAMES}) + endif() -if (LLVM_DYLIB_SYMBOL_VERSIONING) - set_property(TARGET LLVM APPEND_STRING PROPERTY LINK_FLAGS " -Wl,--default-symver") -endif() + target_link_libraries(LLVM PRIVATE ${LIB_NAMES}) -if (APPLE) - set_property(TARGET LLVM APPEND_STRING PROPERTY - LINK_FLAGS - " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") -endif() + if (APPLE) + set_property(TARGET LLVM APPEND_STRING PROPERTY + LINK_FLAGS + " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") + endif() -if(TARGET libLLVMExports) - add_dependencies(LLVM libLLVMExports) + if(TARGET libLLVMExports) + add_dependencies(LLVM libLLVMExports) + endif() endif() -if(LLVM_BUILD_LLVM_C_DYLIB) - # To get the export list for a single llvm library: - # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports - +if(LLVM_BUILD_LLVM_C_DYLIB AND NOT MSVC) if(NOT APPLE) message(FATAL_ERROR "Generating libLLVM-c is only supported on Darwin") endif() + if(NOT LLVM_BUILD_LLVM_DYLIB) + message(FATAL_ERROR "Generating libLLVM-c requires LLVM_BUILD_LLVM_C_DYLIB on Darwin") + endif() + + # To get the export list for a single llvm library: + # nm ${LIB_PATH} | awk "/T _LLVM/ { print $3 }" | sort -u | sed -e "s/^_//g" > ${LIB_PATH}.exports + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/libllvm-c.exports) set(LIB_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) @@ -108,3 +115,40 @@ if(LLVM_BUILD_LLVM_C_DYLIB) " -compatibility_version 1 -current_version ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH} -Wl,-reexport_library ${LIB_PATH}") endif() +if(MSVC) + # Build the LLVM-C.dll library that exports the C API. + + set(LLVM_LINK_COMPONENTS + ${LLVM_DYLIB_COMPONENTS} + ) + + llvm_map_components_to_libnames(LIB_NAMES ${LLVM_DYLIB_COMPONENTS}) + list(REMOVE_DUPLICATES LIB_NAMES) + + # The python script needs to know whether symbols are prefixed with underscores or not. + if(LLVM_HOST_TRIPLE STREQUAL "i686-pc-win32") + set(GEN_UNDERSCORE "--underscore") + else() + set(GEN_UNDERSCORE "") + endif() + + # Get the full name to the libs so the python script understands them. + foreach(lib ${LIB_NAMES}) + list(APPEND FULL_LIB_NAMES ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib/${lib}.lib) + endforeach() + + # Generate the exports file dynamically. + set(GEN_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/gen-msvc-exports.py) + + set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/libllvm-c.exports) + + add_custom_command(OUTPUT ${LLVM_EXPORTED_SYMBOL_FILE} + COMMAND ${PYTHON_EXECUTABLE} ${GEN_SCRIPT} ${FULL_LIB_NAMES} ${GEN_UNDERSCORE} --nm ${LLVM_TOOLS_BINARY_DIR}/llvm-nm -o ${LLVM_EXPORTED_SYMBOL_FILE} + DEPENDS ${LIB_NAMES} llvm-nm + COMMENT "Generating export list for LLVM-C" + VERBATIM ) + + # Finally link the target. + add_llvm_library(LLVM-C SHARED ${SOURCES} DEPENDS intrinsics_gen) + +endif() diff --git a/tools/llvm-shlib/gen-msvc-exports.py b/tools/llvm-shlib/gen-msvc-exports.py new file mode 100644 index 000000000000..6f0a6786d34f --- /dev/null +++ b/tools/llvm-shlib/gen-msvc-exports.py @@ -0,0 +1,106 @@ +#===- gen-msvc-exports.py - Generate C API export file -------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# +# Generate an export file from a list of given LIB files. This only exports symbols +# that start with LLVM, so it only exports the LLVM C API. +# +# To have CMake run this, set LLVM_BUILD_LLVM_C_DYLIB to on while +# building on Windows. +# +# To run manually, build LLVM with Visual Studio, use a Command prompt +# to navigate to the directory with the .lib files (Debug\lib etc). Then run +# python C:\Path\To\gen-msvc-exports.py --nm ..\bin\llvm-nm.exe LLVM*.lib +# +# If you're generating a 32 bit DLL, use the `--underscore` flag. +# If you want to use a different `llvm-nm` executable, pass the path +# with the `--nm` flag. +# +# You can use the --output flag to set the name of the export file. +# +#===------------------------------------------------------------------------===# +from tempfile import mkstemp +from contextlib import contextmanager +from subprocess import check_call +import argparse +import os +import re + + +_UNDERSCORE_REGEX = { + False: re.compile(r"^\w+\s+T\s+(LLVM.*)$"), + True: re.compile(r"^\w+\s+T\s+_(LLVM.*)$") +} + + +@contextmanager +def removing(path): + try: + yield path + finally: + os.unlink(path) + + +def touch_tempfile(*args, **kwargs): + fd, name = mkstemp(*args, **kwargs) + os.close(fd) + return name + + +def gen_llvm_c_export(output, underscore, libs, nm): + """Generate the export file for the LLVM-C DLL. + + Run `nm` for each lib in `libs`, and output an export file + to `output`. If `underscore` is true, symbols will + be assumed to be prefixed with an underscore. + """ + with removing(touch_tempfile(prefix='dumpout', suffix='.txt')) as dumpout: + + # Get the right regex. + p = _UNDERSCORE_REGEX[underscore] + + with open(output, 'w+t') as output_f: + + # For each lib get the LLVM* functions it exports. + for lib in libs: + # Call dumpbin. + with open(dumpout, 'w+t') as dumpout_f: + check_call([nm, '-g', lib], stdout=dumpout_f) + + # Get the matching lines. + with open(dumpout) as dumpbin: + for line in dumpbin: + m = p.match(line) + if m is not None: + output_f.write(m.group(1) + '\n') + + +def main(): + parser = argparse.ArgumentParser('gen-msvc-exports') + + parser.add_argument( + '-o', '--output', help='output filename', default='LLVM-C.exports' + ) + parser.add_argument('-u', '--underscore', + help='labels are prefixed with an underscore (use for 32 bit DLLs)', + action='store_true' + ) + parser.add_argument( + '--nm', help='path to the llvm-nm executable', default='llvm-nm' + ) + parser.add_argument( + 'libs', metavar='LIBS', nargs='+', help='list of libraries to generate export from' + ) + + ns = parser.parse_args() + + gen_llvm_c_export(ns.output, ns.underscore, ns.libs, ns.nm) + + +if __name__ == '__main__': + main() diff --git a/tools/llvm-size/llvm-size.cpp b/tools/llvm-size/llvm-size.cpp index 67c81ec9e7cf..5d638443451f 100644 --- a/tools/llvm-size/llvm-size.cpp +++ b/tools/llvm-size/llvm-size.cpp @@ -71,9 +71,11 @@ ArchFlags("arch", cl::desc("architecture(s) from a Mach-O file to dump"), static bool ArchAll = false; enum RadixTy { octal = 8, decimal = 10, hexadecimal = 16 }; -static cl::opt<unsigned int> -Radix("radix", cl::desc("Print size in radix. Only 8, 10, and 16 are valid"), - cl::init(decimal)); +static cl::opt<RadixTy> Radix( + "radix", cl::desc("Print size in radix"), cl::init(decimal), + cl::values(clEnumValN(octal, "8", "Print size in octal"), + clEnumValN(decimal, "10", "Print size in decimal"), + clEnumValN(hexadecimal, "16", "Print size in hexadecimal"))); static cl::opt<RadixTy> RadixShort(cl::desc("Print size in radix:"), @@ -138,7 +140,7 @@ static void error(llvm::Error E, StringRef FileName, const Archive::Child &C, std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << " " << Buf << "\n"; } @@ -156,7 +158,7 @@ static void error(llvm::Error E, StringRef FileName, std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(std::move(E), OS, ""); + logAllUnhandledErrors(std::move(E), OS); OS.flush(); errs() << " " << Buf << "\n"; } @@ -455,8 +457,8 @@ static void printObjectSectionSizes(ObjectFile *Obj) { // Make one pass over the section table to calculate sizes. for (const SectionRef &Section : Obj->sections()) { uint64_t size = Section.getSize(); - bool isText = Section.isText(); - bool isData = Section.isData(); + bool isText = Section.isBerkeleyText(); + bool isData = Section.isBerkeleyData(); bool isBSS = Section.isBSS(); if (isText) total_text += size; @@ -479,19 +481,25 @@ static void printObjectSectionSizes(ObjectFile *Obj) { } if (!BerkeleyHeaderPrinted) { - outs() << " text data bss " - << (Radix == octal ? "oct" : "dec") << " hex filename\n"; + outs() << " text\t" + " data\t" + " bss\t" + " " + << (Radix == octal ? "oct" : "dec") + << "\t" + " hex\t" + "filename\n"; BerkeleyHeaderPrinted = true; } // Print result. - fmt << "%#7" << radix_fmt << " " - << "%#7" << radix_fmt << " " - << "%#7" << radix_fmt << " "; + fmt << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t"; outs() << format(fmt.str().c_str(), total_text, total_data, total_bss); fmtbuf.clear(); - fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << " " - << "%7" PRIx64 " "; + fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t" + << "%7" PRIx64 "\t"; outs() << format(fmt.str().c_str(), total, total); } } @@ -570,7 +578,7 @@ static void printFileSectionSizes(StringRef file) { } else if (MachOUniversalBinary *UB = dyn_cast<MachOUniversalBinary>(&Bin)) { // If we have a list of architecture flags specified dump only those. - if (!ArchAll && ArchFlags.size() != 0) { + if (!ArchAll && !ArchFlags.empty()) { // Look for a slice in the universal binary that matches each ArchFlag. bool ArchFound; for (unsigned i = 0; i < ArchFlags.size(); ++i) { @@ -839,14 +847,14 @@ static void printBerkelyTotals() { std::string fmtbuf; raw_string_ostream fmt(fmtbuf); const char *radix_fmt = getRadixFmt(); - fmt << "%#7" << radix_fmt << " " - << "%#7" << radix_fmt << " " - << "%#7" << radix_fmt << " "; + fmt << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t" + << "%#7" << radix_fmt << "\t"; outs() << format(fmt.str().c_str(), TotalObjectText, TotalObjectData, TotalObjectBss); fmtbuf.clear(); - fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << " " - << "%7" PRIx64 " "; + fmt << "%7" << (Radix == octal ? PRIo64 : PRIu64) << "\t" + << "%7" PRIx64 "\t"; outs() << format(fmt.str().c_str(), TotalObjectTotal, TotalObjectTotal) << "(TOTALS)\n"; } @@ -859,21 +867,21 @@ int main(int argc, char **argv) { if (OutputFormatShort.getNumOccurrences()) OutputFormat = static_cast<OutputFormatTy>(OutputFormatShort); if (RadixShort.getNumOccurrences()) - Radix = RadixShort; + Radix = RadixShort.getValue(); - for (unsigned i = 0; i < ArchFlags.size(); ++i) { - if (ArchFlags[i] == "all") { + for (StringRef Arch : ArchFlags) { + if (Arch == "all") { ArchAll = true; } else { - if (!MachOObjectFile::isValidArch(ArchFlags[i])) { + if (!MachOObjectFile::isValidArch(Arch)) { outs() << ToolName << ": for the -arch option: Unknown architecture " - << "named '" << ArchFlags[i] << "'"; + << "named '" << Arch << "'"; return 1; } } } - if (InputFilenames.size() == 0) + if (InputFilenames.empty()) InputFilenames.push_back("a.out"); MoreThanOneFile = InputFilenames.size() > 1; diff --git a/tools/llvm-stress/llvm-stress.cpp b/tools/llvm-stress/llvm-stress.cpp index d8ec11251ff6..c29b7a7f7e46 100644 --- a/tools/llvm-stress/llvm-stress.cpp +++ b/tools/llvm-stress/llvm-stress.cpp @@ -356,8 +356,8 @@ struct StoreModifier: public Modifier { void Act() override { // Try to use predefined pointers. If non-exist, use undef pointer value; Value *Ptr = getRandomPointerValue(); - Type *Tp = Ptr->getType(); - Value *Val = getRandomValue(Tp->getContainedType(0)); + PointerType *Tp = cast<PointerType>(Ptr->getType()); + Value *Val = getRandomValue(Tp->getElementType()); Type *ValTy = Val->getType(); // Do not store vectors of i1s because they are unsupported diff --git a/tools/llvm-strings/llvm-strings.cpp b/tools/llvm-strings/llvm-strings.cpp index 8e2d213bcc73..cdc2a6ef033b 100644 --- a/tools/llvm-strings/llvm-strings.cpp +++ b/tools/llvm-strings/llvm-strings.cpp @@ -60,27 +60,27 @@ static void strings(raw_ostream &OS, StringRef FileName, StringRef Contents) { if (L.size() < static_cast<size_t>(MinLength)) return; if (PrintFileName) - OS << FileName << ":"; + OS << FileName << ": "; switch (Radix) { case none: break; case octal: - OS << format("%8o", Offset); + OS << format("%7o ", Offset); break; case hexadecimal: - OS << format("%8x", Offset); + OS << format("%7x ", Offset); break; case decimal: - OS << format("%8u", Offset); + OS << format("%7u ", Offset); break; } - OS << " " << L << '\n'; + OS << L << '\n'; }; const char *B = Contents.begin(); const char *P = nullptr, *E = nullptr, *S = nullptr; for (P = Contents.begin(), E = Contents.end(); P < E; ++P) { - if (std::isgraph(*P) || std::isblank(*P)) { + if (isPrint(*P) || *P == '\t') { if (S == nullptr) S = P; } else if (S) { diff --git a/tools/llvm-symbolizer/llvm-symbolizer.cpp b/tools/llvm-symbolizer/llvm-symbolizer.cpp index 6d40a5403504..9d19f994b739 100644 --- a/tools/llvm-symbolizer/llvm-symbolizer.cpp +++ b/tools/llvm-symbolizer/llvm-symbolizer.cpp @@ -55,17 +55,29 @@ static cl::opt<bool> ClPrintInlining("inlining", cl::init(true), cl::desc("Print all inlined frames for a given address")); +// -demangle, -C static cl::opt<bool> ClDemangle("demangle", cl::init(true), cl::desc("Demangle function names")); +static cl::alias +ClDemangleShort("C", cl::desc("Alias for -demangle"), + cl::NotHidden, cl::aliasopt(ClDemangle)); static cl::opt<std::string> ClDefaultArch("default-arch", cl::init(""), cl::desc("Default architecture " "(for multi-arch objects)")); +// -obj, -exe, -e static cl::opt<std::string> ClBinaryName("obj", cl::init(""), cl::desc("Path to object file to be symbolized (if not provided, " "object file should be specified for each input line)")); +static cl::alias +ClBinaryNameAliasExe("exe", cl::desc("Alias for -obj"), + cl::NotHidden, cl::aliasopt(ClBinaryName)); +static cl::alias +ClBinaryNameAliasE("e", cl::desc("Alias for -obj"), + cl::NotHidden, cl::aliasopt(ClBinaryName)); + static cl::opt<std::string> ClDwpName("dwp", cl::init(""), @@ -75,13 +87,25 @@ static cl::list<std::string> ClDsymHint("dsym-hint", cl::ZeroOrMore, cl::desc("Path to .dSYM bundles to search for debug info for the " "object files")); -static cl::opt<bool> - ClPrintAddress("print-address", cl::init(false), - cl::desc("Show address before line information")); +// -print-address, -addresses, -a +static cl::opt<bool> +ClPrintAddress("print-address", cl::init(false), + cl::desc("Show address before line information")); +static cl::alias +ClPrintAddressAliasAddresses("addresses", cl::desc("Alias for -print-address"), + cl::NotHidden, cl::aliasopt(ClPrintAddress)); +static cl::alias +ClPrintAddressAliasA("a", cl::desc("Alias for -print-address"), + cl::NotHidden, cl::aliasopt(ClPrintAddress)); + +// -pretty-print, -p static cl::opt<bool> ClPrettyPrint("pretty-print", cl::init(false), cl::desc("Make the output more human friendly")); +static cl::alias ClPrettyPrintShort("p", cl::desc("Alias for -pretty-print"), + cl::NotHidden, + cl::aliasopt(ClPrettyPrint)); static cl::opt<int> ClPrintSourceContextLines( "print-source-context-lines", cl::init(0), @@ -90,6 +114,10 @@ static cl::opt<int> ClPrintSourceContextLines( static cl::opt<bool> ClVerbose("verbose", cl::init(false), cl::desc("Print verbose line info")); +static cl::list<std::string> ClInputAddresses(cl::Positional, + cl::desc("<input addresses>..."), + cl::ZeroOrMore); + template<typename T> static bool error(Expected<T> &ResOrErr) { if (ResOrErr) @@ -137,6 +165,38 @@ static bool parseCommand(StringRef InputString, bool &IsData, return !StringRef(pos, offset_length).getAsInteger(0, ModuleOffset); } +static void symbolizeInput(StringRef InputString, LLVMSymbolizer &Symbolizer, + DIPrinter &Printer) { + bool IsData = false; + std::string ModuleName; + uint64_t ModuleOffset = 0; + if (!parseCommand(StringRef(InputString), IsData, ModuleName, ModuleOffset)) { + outs() << InputString; + return; + } + + if (ClPrintAddress) { + outs() << "0x"; + outs().write_hex(ModuleOffset); + StringRef Delimiter = ClPrettyPrint ? ": " : "\n"; + outs() << Delimiter; + } + if (IsData) { + auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset); + Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); + } else if (ClPrintInlining) { + auto ResOrErr = + Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset, ClDwpName); + Printer << (error(ResOrErr) ? DIInliningInfo() : ResOrErr.get()); + } else { + auto ResOrErr = + Symbolizer.symbolizeCode(ModuleName, ModuleOffset, ClDwpName); + Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); + } + outs() << "\n"; + outs().flush(); +} + int main(int argc, char **argv) { InitLLVM X(argc, argv); @@ -159,43 +219,15 @@ int main(int argc, char **argv) { DIPrinter Printer(outs(), ClPrintFunctions != FunctionNameKind::None, ClPrettyPrint, ClPrintSourceContextLines, ClVerbose); - const int kMaxInputStringLength = 1024; - char InputString[kMaxInputStringLength]; - - while (true) { - if (!fgets(InputString, sizeof(InputString), stdin)) - break; - - bool IsData = false; - std::string ModuleName; - uint64_t ModuleOffset = 0; - if (!parseCommand(StringRef(InputString), IsData, ModuleName, - ModuleOffset)) { - outs() << InputString; - continue; - } + if (ClInputAddresses.empty()) { + const int kMaxInputStringLength = 1024; + char InputString[kMaxInputStringLength]; - if (ClPrintAddress) { - outs() << "0x"; - outs().write_hex(ModuleOffset); - StringRef Delimiter = ClPrettyPrint ? ": " : "\n"; - outs() << Delimiter; - } - if (IsData) { - auto ResOrErr = Symbolizer.symbolizeData(ModuleName, ModuleOffset); - Printer << (error(ResOrErr) ? DIGlobal() : ResOrErr.get()); - } else if (ClPrintInlining) { - auto ResOrErr = - Symbolizer.symbolizeInlinedCode(ModuleName, ModuleOffset, ClDwpName); - Printer << (error(ResOrErr) ? DIInliningInfo() - : ResOrErr.get()); - } else { - auto ResOrErr = - Symbolizer.symbolizeCode(ModuleName, ModuleOffset, ClDwpName); - Printer << (error(ResOrErr) ? DILineInfo() : ResOrErr.get()); - } - outs() << "\n"; - outs().flush(); + while (fgets(InputString, sizeof(InputString), stdin)) + symbolizeInput(InputString, Symbolizer, Printer); + } else { + for (StringRef Address : ClInputAddresses) + symbolizeInput(Address, Symbolizer, Printer); } return 0; diff --git a/tools/llvm-undname/llvm-undname.cpp b/tools/llvm-undname/llvm-undname.cpp index 2124ec169455..60520c8f7be0 100644 --- a/tools/llvm-undname/llvm-undname.cpp +++ b/tools/llvm-undname/llvm-undname.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/CommandLine.h" #include "llvm/Support/InitLLVM.h" #include "llvm/Support/Process.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <cstdio> #include <cstring> @@ -26,17 +27,25 @@ using namespace llvm; +cl::opt<bool> DumpBackReferences("backrefs", cl::Optional, + cl::desc("dump backreferences"), cl::Hidden, + cl::init(false)); 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); + MSDemangleFlags Flags = MSDF_None; + if (DumpBackReferences) + Flags = MSDemangleFlags(Flags | MSDF_DumpBackrefs); + + char *ResultBuf = + microsoftDemangle(S.c_str(), nullptr, nullptr, &Status, Flags); if (Status == llvm::demangle_success) { outs() << ResultBuf << "\n"; outs().flush(); } else { - errs() << "Error: Invalid mangled name\n"; + WithColor::error() << "Invalid mangled name\n"; } std::free(ResultBuf); } diff --git a/tools/llvm-xray/CMakeLists.txt b/tools/llvm-xray/CMakeLists.txt index 66b7f21c43fb..4b056d10758f 100644 --- a/tools/llvm-xray/CMakeLists.txt +++ b/tools/llvm-xray/CMakeLists.txt @@ -14,6 +14,7 @@ add_llvm_tool(llvm-xray xray-color-helper.cpp xray-converter.cpp xray-extract.cpp + xray-fdr-dump.cpp xray-graph-diff.cpp xray-graph.cpp xray-registry.cpp diff --git a/tools/llvm-xray/xray-account.cpp b/tools/llvm-xray/xray-account.cpp index 2776a8888481..9985c9adcf6c 100644 --- a/tools/llvm-xray/xray-account.cpp +++ b/tools/llvm-xray/xray-account.cpp @@ -146,6 +146,10 @@ bool LatencyAccountant::accountRecord(const XRayRecord &Record) { auto &ThreadStack = PerThreadFunctionStack[Record.TId]; switch (Record.Type) { + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + // TODO: Support custom and typed event accounting in the future. + return true; case RecordTypes::ENTER: case RecordTypes::ENTER_ARG: { ThreadStack.emplace_back(Record.FuncId, Record.TSC); @@ -255,9 +259,18 @@ ResultRow getStats(std::vector<uint64_t> &Timings) { } // namespace +using TupleType = std::tuple<int32_t, uint64_t, ResultRow>; + +template <typename F> +static void sortByKey(std::vector<TupleType> &Results, F Fn) { + bool ASC = AccountSortOrder == SortDirection::ASCENDING; + llvm::sort(Results, [=](const TupleType &L, const TupleType &R) { + return ASC ? Fn(L) < Fn(R) : Fn(L) > Fn(R); + }); +} + template <class F> void LatencyAccountant::exportStats(const XRayFileHeader &Header, F Fn) const { - using TupleType = std::tuple<int32_t, uint64_t, ResultRow>; std::vector<TupleType> Results; Results.reserve(FunctionLatencies.size()); for (auto FT : FunctionLatencies) { @@ -282,84 +295,38 @@ void LatencyAccountant::exportStats(const XRayFileHeader &Header, F Fn) const { // Sort the data according to user-provided flags. switch (AccountSortOutput) { case SortField::FUNCID: - 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"); - }); + sortByKey(Results, [](const TupleType &X) { return std::get<0>(X); }); break; case SortField::COUNT: - 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"); - }); + sortByKey(Results, [](const TupleType &X) { return std::get<1>(X); }); + break; + case SortField::MIN: + sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Min; }); + break; + case SortField::MED: + sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Median; }); + break; + case SortField::PCT90: + sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Pct90; }); + break; + case SortField::PCT99: + sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Pct99; }); break; - default: - // Here we need to look into the ResultRow for the rest of the data that - // we want to sort by. - 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"); - } - }); + case SortField::MAX: + sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Max; }); break; + case SortField::SUM: + sortByKey(Results, [](const TupleType &X) { return std::get<2>(X).Sum; }); + break; + case SortField::FUNC: + llvm_unreachable("Not implemented"); } - if (AccountTop > 0) - Results.erase(Results.begin() + AccountTop.getValue(), Results.end()); + if (AccountTop > 0) { + auto MaxTop = + std::min(AccountTop.getValue(), static_cast<int>(Results.size())); + Results.erase(Results.begin() + MaxTop, Results.end()); + } for (const auto &R : Results) Fn(std::get<0>(R), std::get<1>(R), std::get<2>(R)); @@ -417,19 +384,25 @@ namespace llvm { template <> struct format_provider<llvm::xray::RecordTypes> { static void format(const llvm::xray::RecordTypes &T, raw_ostream &Stream, StringRef Style) { - switch(T) { - case RecordTypes::ENTER: - Stream << "enter"; - break; - case RecordTypes::ENTER_ARG: - Stream << "enter-arg"; - break; - case RecordTypes::EXIT: - Stream << "exit"; - break; - case RecordTypes::TAIL_EXIT: - Stream << "tail-exit"; - break; + switch (T) { + case RecordTypes::ENTER: + Stream << "enter"; + break; + case RecordTypes::ENTER_ARG: + Stream << "enter-arg"; + break; + case RecordTypes::EXIT: + Stream << "exit"; + break; + case RecordTypes::TAIL_EXIT: + Stream << "tail-exit"; + break; + case RecordTypes::CUSTOM_EVENT: + Stream << "custom-event"; + break; + case RecordTypes::TYPED_EVENT: + Stream << "typed-event"; + break; } } }; diff --git a/tools/llvm-xray/xray-converter.cpp b/tools/llvm-xray/xray-converter.cpp index 90e14d0d8896..3f153b99bc93 100644 --- a/tools/llvm-xray/xray-converter.cpp +++ b/tools/llvm-xray/xray-converter.cpp @@ -18,6 +18,7 @@ #include "llvm/Support/EndianStream.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" +#include "llvm/Support/JSON.h" #include "llvm/Support/ScopedPrinter.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" @@ -91,9 +92,10 @@ 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.PId, R.CallArgs}); + R.TSC, R.TId, R.PId, R.CallArgs, R.Data}); } Output Out(OS, nullptr, 0); + Out.setWriteDefaultValues(false); Out << Trace; } @@ -122,21 +124,27 @@ void TraceConverter::exportAsRAWv1(const Trace &Records, raw_ostream &OS) { // Then write out the rest of the records, still in an endian-appropriate // format. for (const auto &R : Records) { - Writer.write(R.RecordType); - // The on disk naive raw format uses 8 bit CPUs, but the record has 16. - // There's no choice but truncation. - Writer.write(static_cast<uint8_t>(R.CPU)); switch (R.Type) { case RecordTypes::ENTER: case RecordTypes::ENTER_ARG: + Writer.write(R.RecordType); + Writer.write(static_cast<uint8_t>(R.CPU)); Writer.write(uint8_t{0}); break; case RecordTypes::EXIT: + Writer.write(R.RecordType); + Writer.write(static_cast<uint8_t>(R.CPU)); Writer.write(uint8_t{1}); break; case RecordTypes::TAIL_EXIT: + Writer.write(R.RecordType); + Writer.write(static_cast<uint8_t>(R.CPU)); Writer.write(uint8_t{2}); break; + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + // Skip custom and typed event records for v1 logs. + continue; } Writer.write(R.FuncId); Writer.write(R.TSC); @@ -234,31 +242,6 @@ StackTrieNode *findOrCreateStackNode( return CurrentStack; } -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 << " "; - 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 void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, @@ -269,18 +252,14 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, unsigned id_counter = 0; - OS << "{\n \"traceEvents\": ["; DenseMap<uint32_t, StackTrieNode *> StackCursorByThreadId{}; DenseMap<uint32_t, SmallVector<StackTrieNode *, 4>> StackRootsByThreadId{}; DenseMap<unsigned, StackTrieNode *> StacksByStackId{}; std::forward_list<StackTrieNode> NodeStore{}; - int loop_count = 0; - for (const auto &R : Records) { - if (loop_count++ == 0) - OS << "\n"; - else - OS << ",\n"; + // Create a JSON Array which will hold all trace events. + json::Array TraceEvents; + for (const auto &R : Records) { // Chrome trace event format always wants data in micros. // CyclesPerMicro = CycleHertz / 10^6 // TSC / CyclesPerMicro == TSC * 10^6 / CycleHertz == MicroTimestamp @@ -292,6 +271,10 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, double EventTimestampUs = double(1000000) / CycleFreq * double(R.TSC); StackTrieNode *&StackCursor = StackCursorByThreadId[R.TId]; switch (R.Type) { + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + // TODO: Support typed and custom event rendering on Chrome Trace Viewer. + break; case RecordTypes::ENTER: case RecordTypes::ENTER_ARG: StackCursor = findOrCreateStackNode(StackCursor, R.FuncId, R.TId, @@ -301,8 +284,15 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, // 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(Version, OS, R.FuncId, R.TId, R.PId, Symbolize, - FuncIdHelper, EventTimestampUs, *StackCursor, "B"); + TraceEvents.push_back(json::Object({ + {"name", Symbolize ? FuncIdHelper.SymbolOrNumber(R.FuncId) + : llvm::to_string(R.FuncId)}, + {"ph", "B"}, + {"tid", llvm::to_string(R.TId)}, + {"pid", llvm::to_string(Version >= 3 ? R.PId : 1)}, + {"ts", llvm::formatv("{0:f4}", EventTimestampUs)}, + {"sf", llvm::to_string(StackCursor->ExtraData.id)}, + })); break; case RecordTypes::EXIT: case RecordTypes::TAIL_EXIT: @@ -313,43 +303,51 @@ void TraceConverter::exportAsChromeTraceEventFormat(const Trace &Records, // (And/Or in loop termination below) StackTrieNode *PreviousCursor = nullptr; do { - if (PreviousCursor != nullptr) { - OS << ",\n"; - } - writeTraceViewerRecord(Version, OS, StackCursor->FuncId, R.TId, R.PId, - Symbolize, FuncIdHelper, EventTimestampUs, - *StackCursor, "E"); + TraceEvents.push_back(json::Object({ + {"name", Symbolize + ? FuncIdHelper.SymbolOrNumber(StackCursor->FuncId) + : llvm::to_string(StackCursor->FuncId)}, + {"ph", "E"}, + {"tid", llvm::to_string(R.TId)}, + {"pid", llvm::to_string(Version >= 3 ? R.PId : 1)}, + {"ts", llvm::formatv("{0:f4}", EventTimestampUs)}, + {"sf", llvm::to_string(StackCursor->ExtraData.id)}, + })); PreviousCursor = StackCursor; StackCursor = StackCursor->Parent; } while (PreviousCursor->FuncId != R.FuncId && StackCursor != nullptr); break; } } - OS << "\n ],\n"; // Close the Trace Events array. - OS << " " - << "\"displayTimeUnit\": \"ns\",\n"; // The stackFrames dictionary substantially reduces size of the output file by // avoiding repeating the entire call stack of function names for each entry. - OS << R"( "stackFrames": {)"; - int stack_frame_count = 0; - for (auto map_iter : StacksByStackId) { - if (stack_frame_count++ == 0) - OS << "\n"; - else - OS << ",\n"; - OS << " "; - OS << llvm::formatv( - R"("{0}" : { "name" : "{1}")", map_iter.first, - (Symbolize ? FuncIdHelper.SymbolOrNumber(map_iter.second->FuncId) - : llvm::to_string(map_iter.second->FuncId))); - if (map_iter.second->Parent != nullptr) - OS << llvm::formatv(R"(, "parent": "{0}")", - map_iter.second->Parent->ExtraData.id); - OS << " }"; + json::Object StackFrames; + for (const auto &Stack : StacksByStackId) { + const auto &StackId = Stack.first; + const auto &StackFunctionNode = Stack.second; + json::Object::iterator It; + std::tie(It, std::ignore) = StackFrames.insert({ + llvm::to_string(StackId), + json::Object{ + {"name", + Symbolize ? FuncIdHelper.SymbolOrNumber(StackFunctionNode->FuncId) + : llvm::to_string(StackFunctionNode->FuncId)}}, + }); + + if (StackFunctionNode->Parent != nullptr) + It->second.getAsObject()->insert( + {"parent", llvm::to_string(StackFunctionNode->Parent->ExtraData.id)}); } - OS << "\n }\n"; // Close the stack frames map. - OS << "}\n"; // Close the JSON entry. + + json::Object TraceJSON{ + {"displayTimeUnit", "ns"}, + {"traceEvents", std::move(TraceEvents)}, + {"stackFrames", std::move(StackFrames)}, + }; + + // Pretty-print the JSON using two spaces for indentations. + OS << formatv("{0:2}", json::Value(std::move(TraceJSON))); } namespace llvm { diff --git a/tools/llvm-xray/xray-fdr-dump.cpp b/tools/llvm-xray/xray-fdr-dump.cpp new file mode 100644 index 000000000000..389825605b62 --- /dev/null +++ b/tools/llvm-xray/xray-fdr-dump.cpp @@ -0,0 +1,119 @@ +//===- xray-fdr-dump.cpp: XRay FDR Trace Dump Tool ------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implements the FDR trace dumping tool, using the libraries for handling FDR +// mode traces specifically. +// +//===----------------------------------------------------------------------===// +#include "xray-registry.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/XRay/BlockIndexer.h" +#include "llvm/XRay/BlockPrinter.h" +#include "llvm/XRay/BlockVerifier.h" +#include "llvm/XRay/FDRRecordConsumer.h" +#include "llvm/XRay/FDRRecordProducer.h" +#include "llvm/XRay/FDRRecords.h" +#include "llvm/XRay/FileHeaderReader.h" +#include "llvm/XRay/RecordPrinter.h" + +using namespace llvm; +using namespace xray; + +static cl::SubCommand Dump("fdr-dump", "FDR Trace Dump"); +static cl::opt<std::string> DumpInput(cl::Positional, + cl::desc("<xray fdr mode log>"), + cl::Required, cl::sub(Dump)); +static cl::opt<bool> DumpVerify("verify", + cl::desc("verify structure of the log"), + cl::init(false), cl::sub(Dump)); + +static CommandRegistration Unused(&Dump, []() -> Error { + // Open the file provided. + int Fd; + if (auto EC = sys::fs::openFileForRead(DumpInput, Fd)) + return createStringError(EC, "Cannot open file '%s' for read.", + DumpInput.c_str()); + + uint64_t FileSize; + if (auto EC = sys::fs::file_size(DumpInput, FileSize)) + return createStringError(EC, "Failed to get file size for '%s'.", + DumpInput.c_str()); + + std::error_code EC; + sys::fs::mapped_file_region MappedFile( + Fd, sys::fs::mapped_file_region::mapmode::readonly, FileSize, 0, EC); + + DataExtractor DE(StringRef(MappedFile.data(), MappedFile.size()), true, 8); + uint32_t OffsetPtr = 0; + + auto FileHeaderOrError = readBinaryFormatHeader(DE, OffsetPtr); + if (!FileHeaderOrError) + return FileHeaderOrError.takeError(); + auto &H = FileHeaderOrError.get(); + + FileBasedRecordProducer P(H, DE, OffsetPtr); + + RecordPrinter RP(outs(), "\n"); + if (!DumpVerify) { + PipelineConsumer C({&RP}); + while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { + auto R = P.produce(); + if (!R) + return R.takeError(); + if (auto E = C.consume(std::move(R.get()))) + return E; + } + return Error::success(); + } + + BlockPrinter BP(outs(), RP); + std::vector<std::unique_ptr<Record>> Records; + LogBuilderConsumer C(Records); + while (DE.isValidOffsetForDataOfSize(OffsetPtr, 1)) { + auto R = P.produce(); + if (!R) { + // Print records we've found so far. + for (auto &Ptr : Records) + if (auto E = Ptr->apply(RP)) + return joinErrors(std::move(E), R.takeError()); + return R.takeError(); + } + if (auto E = C.consume(std::move(R.get()))) + return E; + } + + // Once we have a trace, we then index the blocks. + BlockIndexer::Index Index; + BlockIndexer BI(Index); + for (auto &Ptr : Records) + if (auto E = Ptr->apply(BI)) + return E; + + if (auto E = BI.flush()) + return E; + + // Then we validate while printing each block. + BlockVerifier BV; + for (auto ProcessThreadBlocks : Index) { + auto &Blocks = ProcessThreadBlocks.second; + for (auto &B : Blocks) { + for (auto *R : B.Records) { + if (auto E = R->apply(BV)) + return E; + if (auto E = R->apply(BP)) + return E; + } + BV.reset(); + BP.reset(); + } + } + outs().flush(); + return Error::success(); +}); diff --git a/tools/llvm-xray/xray-graph.cpp b/tools/llvm-xray/xray-graph.cpp index c619bf86299b..fe49cca20d57 100644 --- a/tools/llvm-xray/xray-graph.cpp +++ b/tools/llvm-xray/xray-graph.cpp @@ -246,6 +246,10 @@ Error GraphRenderer::accountRecord(const XRayRecord &Record) { updateStat(G[Record.FuncId].S, D); break; } + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + // TODO: Support custom and typed events in the graph processing? + break; } return Error::success(); diff --git a/tools/llvm-xray/xray-stacks.cpp b/tools/llvm-xray/xray-stacks.cpp index 1a6069780a31..d3af9e25e6f2 100644 --- a/tools/llvm-xray/xray-stacks.cpp +++ b/tools/llvm-xray/xray-stacks.cpp @@ -366,6 +366,9 @@ public: AccountRecordState *state) { auto &TS = ThreadStackMap[R.TId]; switch (R.Type) { + case RecordTypes::CUSTOM_EVENT: + case RecordTypes::TYPED_EVENT: + return AccountRecordStatus::OK; case RecordTypes::ENTER: case RecordTypes::ENTER_ARG: { state->wasLastRecordExit = false; @@ -734,7 +737,7 @@ static CommandRegistration Unused(&Stack, []() -> Error { Twine("Failed loading input file '") + Filename + "'", std::make_error_code(std::errc::invalid_argument)), TraceOrErr.takeError()); - logAllUnhandledErrors(TraceOrErr.takeError(), errs(), ""); + logAllUnhandledErrors(TraceOrErr.takeError(), errs()); continue; } auto &T = *TraceOrErr; diff --git a/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt b/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt new file mode 100644 index 000000000000..34027431697f --- /dev/null +++ b/tools/llvm-yaml-numeric-parser-fuzzer/CMakeLists.txt @@ -0,0 +1,9 @@ +set(LLVM_LINK_COMPONENTS + Support + FuzzMutate +) + +add_llvm_fuzzer(llvm-yaml-numeric-parser-fuzzer + yaml-numeric-parser-fuzzer.cpp + DUMMY_MAIN DummyYAMLNumericParserFuzzer.cpp + ) diff --git a/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp b/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp new file mode 100644 index 000000000000..3396168acec0 --- /dev/null +++ b/tools/llvm-yaml-numeric-parser-fuzzer/DummyYAMLNumericParserFuzzer.cpp @@ -0,0 +1,19 @@ +//===--- DummyYAMLNumericParserFuzzer.cpp ---------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Implementation of main so we can build and test without linking libFuzzer. +// +//===----------------------------------------------------------------------===// + +#include "llvm/FuzzMutate/FuzzerCLI.h" + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int main(int argc, char *argv[]) { + return llvm::runFuzzerOnInputs(argc, argv, LLVMFuzzerTestOneInput); +} diff --git a/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp b/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp new file mode 100644 index 000000000000..9134d7628ec9 --- /dev/null +++ b/tools/llvm-yaml-numeric-parser-fuzzer/yaml-numeric-parser-fuzzer.cpp @@ -0,0 +1,47 @@ +//===--- special-case-list-fuzzer.cpp - Fuzzer for special case lists -----===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Regex.h" +#include "llvm/Support/YAMLTraits.h" +#include <cassert> +#include <string> + +llvm::Regex Infinity("^[-+]?(\\.inf|\\.Inf|\\.INF)$"); +llvm::Regex Base8("^0o[0-7]+$"); +llvm::Regex Base16("^0x[0-9a-fA-F]+$"); +llvm::Regex Float("^[-+]?(\\.[0-9]+|[0-9]+(\\.[0-9]*)?)([eE][-+]?[0-9]+)?$"); + +inline bool isNumericRegex(llvm::StringRef S) { + + if (S.equals(".nan") || S.equals(".NaN") || S.equals(".NAN")) + return true; + + if (Infinity.match(S)) + return true; + + if (Base8.match(S)) + return true; + + if (Base16.match(S)) + return true; + + if (Float.match(S)) + return true; + + return false; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + std::string Input(reinterpret_cast<const char *>(Data), Size); + Input.erase(std::remove(Input.begin(), Input.end(), 0), Input.end()); + if (!Input.empty() && llvm::yaml::isNumeric(Input) != isNumericRegex(Input)) + LLVM_BUILTIN_TRAP; + return 0; +} diff --git a/tools/lto/lto.cpp b/tools/lto/lto.cpp index f9b518ca039b..0a3ba98957e7 100644 --- a/tools/lto/lto.cpp +++ b/tools/lto/lto.cpp @@ -591,6 +591,13 @@ void thinlto_codegen_set_cache_size_bytes( return unwrap(cg)->setCacheMaxSizeBytes(MaxSizeBytes); } +void thinlto_codegen_set_cache_size_megabytes( + thinlto_code_gen_t cg, unsigned MaxSizeMegabytes) { + uint64_t MaxSizeBytes = MaxSizeMegabytes; + MaxSizeBytes *= 1024 * 1024; + return unwrap(cg)->setCacheMaxSizeBytes(MaxSizeBytes); +} + void thinlto_codegen_set_cache_size_files( thinlto_code_gen_t cg, unsigned MaxSizeFiles) { return unwrap(cg)->setCacheMaxSizeFiles(MaxSizeFiles); diff --git a/tools/lto/lto.exports b/tools/lto/lto.exports index abde3894cb20..cb881d104fb2 100644 --- a/tools/lto/lto.exports +++ b/tools/lto/lto.exports @@ -46,6 +46,7 @@ LLVMCreateDisasmCPU LLVMDisasmDispose LLVMDisasmInstruction LLVMSetDisasmOptions +LLVMCreateDisasmCPUFeatures thinlto_create_codegen thinlto_codegen_dispose thinlto_codegen_add_module @@ -58,6 +59,7 @@ 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_megabytes thinlto_codegen_set_cache_size_files thinlto_codegen_set_savetemps_dir thinlto_codegen_set_cpu diff --git a/tools/msbuild/.gitignore b/tools/msbuild/.gitignore index 01c1f5a9d0b9..692d171a21a0 100644 --- a/tools/msbuild/.gitignore +++ b/tools/msbuild/.gitignore @@ -1,2 +1,6 @@ -bin
-obj
+obj/
+bin/
+.vs/
+Key.snk
+packages/
+*.csproj.user
diff --git a/tools/msbuild/LLVM.Cpp.Common.props b/tools/msbuild/LLVM.Cpp.Common.props index ffc1270a85c9..3420b77cfffa 100644 --- a/tools/msbuild/LLVM.Cpp.Common.props +++ b/tools/msbuild/LLVM.Cpp.Common.props @@ -1,75 +1,77 @@ -<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> +<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>
+ <LLVMInstallDir Condition="'$(LLVMInstallDir)' != ''">$(LLVMInstallDir)\</LLVMInstallDir>
+ <ClangClExecutable>$(LLVMInstallDir)bin\clang-cl.exe</ClangClExecutable>
+ <LldLinkExecutable>$(LLVMInstallDir)bin\lld-link.exe</LldLinkExecutable>
+ <UseClangCl>true</UseClangCl>
+ <UseLldLink>true</UseLldLink>
+ </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>
+ </ClCompile>
+ </ItemDefinitionGroup>
+</Project>
diff --git a/tools/msbuild/LLVM.Cpp.Common.targets b/tools/msbuild/LLVM.Cpp.Common.targets index 1edc08d2a325..5870a3d4c594 100644 --- a/tools/msbuild/LLVM.Cpp.Common.targets +++ b/tools/msbuild/LLVM.Cpp.Common.targets @@ -1,184 +1,131 @@ -<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> +<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 Condition="$(UseClangCl)">$(ClangClExecutable)</CLToolExe>
+ <LinkToolExe Condition="$(UseLldLink)">$(LldLinkExecutable)</LinkToolExe>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PropertyPageSchema Include="$(VCTargetsPath)$(LangID)\llvm-general.xml">
+ <Context>Project</Context>
+ </PropertyPageSchema>
+ </ItemGroup>
+
+ <!-- Take any clang-specific options that the user wants to pass and stick them onto the
+ general purpose list of command line flags. -->
+ <ItemDefinitionGroup Condition="$(UseClangCl)">
+ <ClCompile>
+ <AdditionalOptions>-m$(PlatformArchitecture) $(ClangClAdditionalOptions) %(AdditionalOptions)</AdditionalOptions>
+ </ClCompile>
+ </ItemDefinitionGroup>
+
+ <ItemDefinitionGroup Condition="$(UseLldLink)">
+ <Link>
+ <AdditionalOptions>$(LldLinkAdditionalOptions) %(AdditionalOptions)</AdditionalOptions>
+ </Link>
+ </ItemDefinitionGroup>
+
+ <!-- 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" Condition="$(UseClangCl)">
+ <!-- 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/llvm-general.xml b/tools/msbuild/llvm-general.xml index 3b72c1250da2..fc04374afb9b 100644 --- a/tools/msbuild/llvm-general.xml +++ b/tools/msbuild/llvm-general.xml @@ -9,17 +9,47 @@ <Category Name="General" DisplayName="General" Description="General" /> </Rule.Categories> <Rule.DataSource> - <DataSource Persistence="ProjectFile" Label="Configuration" /> + <DataSource Persistence="ProjectFile" Label="LLVM" /> </Rule.DataSource> + <BoolProperty Name="UseClangCl" + DisplayName="Use clang-cl" + Description="Use clang-cl for compiling. If this option is disabled, the Microsoft compiler (cl.exe) will be used instead." + Category="General" + Default="true"> + </BoolProperty> <StringProperty Name="ClangClExecutable" - DisplayName="Clang-CL Executable" + 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> + + <StringProperty Name="ClangClAdditionalOptions" + DisplayName="Additional Compiler Options" + Description="Additional options to pass to clang. This is essentially the same as C/C++ > Command Line > Additional Options, except that it is safe to put options here that will be rejected by cl.exe in case you switch toolchains back and forth." + Category="General"> + </StringProperty> + + <BoolProperty Name="UseLldLink" + DisplayName="Use lld-link" + Description="Use lld-link for linking. If this option is disabled, the Microsoft linker (link.exe) will be used instead." + Category="General" + Default="true"> + </BoolProperty> + <StringProperty Name="LldLinkExecutable" + DisplayName="lld-link Executable" + Description="Specifies the path to lld-link.exe." + Category="General" + Default="$(LLVMInstallDir)bin\lld-link.exe" + Subtype="file"> + </StringProperty> + + <StringProperty Name="LldLinkAdditionalOptions" + DisplayName="Additional Linker Options" + Description="Additional options to pass to lld-link. This is essentially the same as General > Linker > Command Line > Additional Options, except that it is safe to put options here that will be rejected by link.exe in case you switch toolchains back and forth." + Category="General"> + </StringProperty> + </Rule> diff --git a/tools/msbuild/llvm.csproj b/tools/msbuild/llvm.csproj index a614bb2f22ba..39e898d73682 100644 --- a/tools/msbuild/llvm.csproj +++ b/tools/msbuild/llvm.csproj @@ -64,6 +64,7 @@ <None Include="source.extension.vsixmanifest">
<SubType>Designer</SubType>
</None>
+ <None Include="Key.snk" />
<Content Include="Platformx64\Toolset.props">
<IncludeInVSIX>true</IncludeInVSIX>
<InstallRoot>VCTargets</InstallRoot>
@@ -90,11 +91,7 @@ </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>
- -->
+ <PropertyGroup>
+ <PreBuildEvent>if not exist $(ProjectDir)Key.snk ("$(TargetFrameworkSDKToolsDirectory)\x64\sn.exe" -k $(ProjectDir)Key.snk)</PreBuildEvent>
+ </PropertyGroup>
</Project>
\ No newline at end of file diff --git a/tools/msbuild/source.extension.vsixmanifest b/tools/msbuild/source.extension.vsixmanifest index 28be6a4242c2..c5b778c7ba1d 100644 --- a/tools/msbuild/source.extension.vsixmanifest +++ b/tools/msbuild/source.extension.vsixmanifest @@ -1,15 +1,15 @@ <?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" />
+ <Identity Id="llvm.d29c51f0-961f-4e20-aad6-356af569907f" Version="1.0.340780" Language="en-US" Publisher="LLVM Extensions" />
<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" />
+ <InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0,16.0)" />
+ <InstallationTarget Version="[15.0,16.0)" Id="Microsoft.VisualStudio.Pro" />
+ <InstallationTarget Version="[15.0,16.0)" Id="Microsoft.VisualStudio.Enterprise" />
</Installation>
<Dependencies>
<Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
diff --git a/tools/obj2yaml/Error.cpp b/tools/obj2yaml/Error.cpp index 399b563fd9c9..2b6a815fe2f5 100644 --- a/tools/obj2yaml/Error.cpp +++ b/tools/obj2yaml/Error.cpp @@ -53,7 +53,7 @@ const std::error_category &obj2yaml_category() { char Obj2YamlError::ID = 0; -void Obj2YamlError::log(raw_ostream &OS) const { OS << ErrMsg << "\n"; } +void Obj2YamlError::log(raw_ostream &OS) const { OS << ErrMsg; } std::error_code Obj2YamlError::convertToErrorCode() const { return std::error_code(static_cast<int>(Code), obj2yaml_category()); diff --git a/tools/obj2yaml/coff2yaml.cpp b/tools/obj2yaml/coff2yaml.cpp index e7e9e19fd3dc..3b44780fac57 100644 --- a/tools/obj2yaml/coff2yaml.cpp +++ b/tools/obj2yaml/coff2yaml.cpp @@ -8,6 +8,7 @@ //===----------------------------------------------------------------------===// #include "obj2yaml.h" +#include "llvm/ADT/StringMap.h" #include "llvm/DebugInfo/CodeView/DebugChecksumsSubsection.h" #include "llvm/DebugInfo/CodeView/DebugStringTableSubsection.h" #include "llvm/DebugInfo/CodeView/StringsAndChecksums.h" @@ -142,12 +143,24 @@ void COFFDumper::dumpSections(unsigned NumSections) { codeview::StringsAndChecksumsRef SC; initializeFileAndStringTable(Obj, SC); + StringMap<bool> SymbolUnique; + for (const auto &S : Obj.symbols()) { + object::COFFSymbolRef Symbol = Obj.getCOFFSymbol(S); + StringRef Name; + Obj.getSymbolName(Symbol, Name); + StringMap<bool>::iterator It; + bool Inserted; + std::tie(It, Inserted) = SymbolUnique.insert(std::make_pair(Name, true)); + if (!Inserted) + It->second = false; + } + for (const auto &ObjSection : Obj.sections()) { const object::coff_section *COFFSection = Obj.getCOFFSection(ObjSection); COFFYAML::Section NewYAMLSection; ObjSection.getName(NewYAMLSection.Name); NewYAMLSection.Header.Characteristics = COFFSection->Characteristics; - NewYAMLSection.Header.VirtualAddress = ObjSection.getAddress(); + NewYAMLSection.Header.VirtualAddress = COFFSection->VirtualAddress; NewYAMLSection.Header.VirtualSize = COFFSection->VirtualSize; NewYAMLSection.Header.NumberOfLineNumbers = COFFSection->NumberOfLinenumbers; @@ -188,11 +201,14 @@ void COFFDumper::dumpSections(unsigned NumSections) { if (!SymbolNameOrErr) { std::string Buf; raw_string_ostream OS(Buf); - logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS, ""); + logAllUnhandledErrors(SymbolNameOrErr.takeError(), OS); OS.flush(); report_fatal_error(Buf); } - Rel.SymbolName = *SymbolNameOrErr; + if (SymbolUnique.lookup(*SymbolNameOrErr)) + Rel.SymbolName = *SymbolNameOrErr; + else + Rel.SymbolTableIndex = reloc->SymbolTableIndex; Rel.VirtualAddress = reloc->VirtualAddress; Rel.Type = reloc->Type; Relocations.push_back(Rel); diff --git a/tools/obj2yaml/dwarf2yaml.cpp b/tools/obj2yaml/dwarf2yaml.cpp index 91cec8b7c6ca..1ce6c036e6f5 100644 --- a/tools/obj2yaml/dwarf2yaml.cpp +++ b/tools/obj2yaml/dwarf2yaml.cpp @@ -81,8 +81,9 @@ void dumpDebugARanges(DWARFContext &DCtx, DWARFYAML::Data &Y) { } void dumpPubSection(DWARFContext &DCtx, DWARFYAML::PubSection &Y, - StringRef Section) { - DataExtractor PubSectionData(Section, DCtx.isLittleEndian(), 0); + DWARFSection Section) { + DWARFDataExtractor PubSectionData(DCtx.getDWARFObj(), Section, + DCtx.isLittleEndian(), 0); uint32_t Offset = 0; dumpInitialLength(PubSectionData, Offset, Y.Length); Y.Version = PubSectionData.getU16(&Offset); diff --git a/tools/obj2yaml/elf2yaml.cpp b/tools/obj2yaml/elf2yaml.cpp index dea4d1b31827..48ecee02c4d0 100644 --- a/tools/obj2yaml/elf2yaml.cpp +++ b/tools/obj2yaml/elf2yaml.cpp @@ -114,6 +114,7 @@ template <class ELFT> ErrorOr<ELFYAML::Object *> ELFDumper<ELFT>::dump() { Y->Header.Class = ELFYAML::ELF_ELFCLASS(Obj.getHeader()->getFileClass()); Y->Header.Data = ELFYAML::ELF_ELFDATA(Obj.getHeader()->getDataEncoding()); Y->Header.OSABI = Obj.getHeader()->e_ident[ELF::EI_OSABI]; + Y->Header.ABIVersion = Obj.getHeader()->e_ident[ELF::EI_ABIVERSION]; Y->Header.Type = Obj.getHeader()->e_type; Y->Header.Machine = Obj.getHeader()->e_machine; Y->Header.Flags = Obj.getHeader()->e_flags; diff --git a/tools/obj2yaml/obj2yaml.cpp b/tools/obj2yaml/obj2yaml.cpp index 76786158d989..459572a57344 100644 --- a/tools/obj2yaml/obj2yaml.cpp +++ b/tools/obj2yaml/obj2yaml.cpp @@ -50,7 +50,7 @@ static void reportError(StringRef Input, Error Err) { Input = "<stdin>"; std::string ErrMsg; raw_string_ostream OS(ErrMsg); - logAllUnhandledErrors(std::move(Err), OS, ""); + logAllUnhandledErrors(std::move(Err), OS); OS.flush(); errs() << "Error reading file: " << Input << ": " << ErrMsg; errs().flush(); diff --git a/tools/obj2yaml/wasm2yaml.cpp b/tools/obj2yaml/wasm2yaml.cpp index dbaf1a2848f5..7581bbef50ad 100644 --- a/tools/obj2yaml/wasm2yaml.cpp +++ b/tools/obj2yaml/wasm2yaml.cpp @@ -49,11 +49,23 @@ static WasmYAML::Limits make_limits(const wasm::WasmLimits &Limits) { return L; } -std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const WasmSection &WasmSec) { +std::unique_ptr<WasmYAML::CustomSection> +WasmDumper::dumpCustomSection(const WasmSection &WasmSec) { std::unique_ptr<WasmYAML::CustomSection> CustomSec; - if (WasmSec.Name == "name") { - std::unique_ptr<WasmYAML::NameSection> NameSec = make_unique<WasmYAML::NameSection>(); - for (const llvm::wasm::WasmFunctionName &Func: Obj.debugNames()) { + if (WasmSec.Name == "dylink") { + std::unique_ptr<WasmYAML::DylinkSection> DylinkSec = + make_unique<WasmYAML::DylinkSection>(); + const wasm::WasmDylinkInfo& Info = Obj.dylinkInfo(); + DylinkSec->MemorySize = Info.MemorySize; + DylinkSec->MemoryAlignment = Info.MemoryAlignment; + DylinkSec->TableSize = Info.TableSize; + DylinkSec->TableAlignment = Info.TableAlignment; + DylinkSec->Needed = Info.Needed; + CustomSec = std::move(DylinkSec); + } else if (WasmSec.Name == "name") { + std::unique_ptr<WasmYAML::NameSection> NameSec = + make_unique<WasmYAML::NameSection>(); + for (const llvm::wasm::WasmFunctionName &Func : Obj.debugNames()) { WasmYAML::NameEntry NameEntry; NameEntry.Name = Func.Name; NameEntry.Index = Func.Index; @@ -61,7 +73,8 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was } CustomSec = std::move(NameSec); } else if (WasmSec.Name == "linking") { - std::unique_ptr<WasmYAML::LinkingSection> LinkingSec = make_unique<WasmYAML::LinkingSection>(); + std::unique_ptr<WasmYAML::LinkingSection> LinkingSec = + make_unique<WasmYAML::LinkingSection>(); LinkingSec->Version = Obj.linkingData().Version; ArrayRef<StringRef> Comdats = Obj.linkingData().Comdats; @@ -70,7 +83,7 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was 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}); + WasmYAML::ComdatEntry{wasm::WASM_COMDAT_FUNCTION, Func.Index}); } } @@ -104,6 +117,7 @@ std::unique_ptr<WasmYAML::CustomSection> WasmDumper::dumpCustomSection(const Was break; case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: + case wasm::WASM_SYMBOL_TYPE_EVENT: Info.ElementIndex = Symbol.ElementIndex; break; case wasm::WASM_SYMBOL_TYPE_SECTION: @@ -152,9 +166,13 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { for (const auto &FunctionSig : Obj.types()) { WasmYAML::Signature Sig; Sig.Index = Index++; - Sig.ReturnType = FunctionSig.ReturnType; - for (const auto &ParamType : FunctionSig.ParamTypes) - Sig.ParamTypes.push_back(ParamType); + Sig.ReturnType = wasm::WASM_TYPE_NORESULT; + assert(FunctionSig.Returns.size() <= 1 && + "Functions with multiple returns are not supported"); + if (FunctionSig.Returns.size()) + Sig.ReturnType = static_cast<uint32_t>(FunctionSig.Returns[0]); + for (const auto &ParamType : FunctionSig.Params) + Sig.ParamTypes.push_back(static_cast<uint32_t>(ParamType)); TypeSec->Signatures.push_back(Sig); } S = std::move(TypeSec); @@ -175,6 +193,10 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { Im.GlobalImport.Type = Import.Global.Type; Im.GlobalImport.Mutable = Import.Global.Mutable; break; + case wasm::WASM_EXTERNAL_EVENT: + Im.EventImport.Attribute = Import.Event.Attribute; + Im.EventImport.SigIndex = Import.Event.SigIndex; + break; case wasm::WASM_EXTERNAL_TABLE: Im.TableImport = make_table(Import.Table); break; @@ -224,6 +246,18 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { S = std::move(GlobalSec); break; } + case wasm::WASM_SEC_EVENT: { + auto EventSec = make_unique<WasmYAML::EventSection>(); + for (auto &Event : Obj.events()) { + WasmYAML::Event E; + E.Index = Event.Index; + E.Attribute = Event.Type.Attribute; + E.SigIndex = Event.Type.SigIndex; + EventSec->Events.push_back(E); + } + S = std::move(EventSec); + break; + } case wasm::WASM_SEC_START: { auto StartSec = make_unique<WasmYAML::StartSection>(); StartSec->StartFunction = Obj.startFunction(); @@ -290,7 +324,7 @@ ErrorOr<WasmYAML::Object *> WasmDumper::dump() { llvm_unreachable("Unknown section type"); break; } - for (const wasm::WasmRelocation &Reloc: WasmSec.Relocations) { + for (const wasm::WasmRelocation &Reloc : WasmSec.Relocations) { WasmYAML::Relocation R; R.Type = Reloc.Type; R.Index = Reloc.Index; diff --git a/tools/opt-remarks/CMakeLists.txt b/tools/opt-remarks/CMakeLists.txt new file mode 100644 index 000000000000..a87beae1e893 --- /dev/null +++ b/tools/opt-remarks/CMakeLists.txt @@ -0,0 +1,22 @@ +set(LLVM_LINK_COMPONENTS + OptRemarks + ) + +set(SOURCES + liboptremarks.cpp + ) + +set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/OptRemarks.exports) + +add_llvm_library(OptRemarks SHARED ${SOURCES}) + +install(FILES ${LLVM_MAIN_INCLUDE_DIR}/llvm-c/OptRemarks.h + DESTINATION include/llvm-c + COMPONENT OptRemarks) + +if (APPLE) + set(OPTREMARKS_VERSION ${LLVM_VERSION_MAJOR}) + set_property(TARGET OptRemarks APPEND_STRING PROPERTY + LINK_FLAGS + " -compatibility_version 1 -current_version ${OPTREMARKS_VERSION}.${LLVM_VERSION_MINOR}.${LLVM_VERSION_PATCH}") +endif() diff --git a/tools/opt-remarks/OptRemarks.exports b/tools/opt-remarks/OptRemarks.exports new file mode 100644 index 000000000000..c3f678d754fc --- /dev/null +++ b/tools/opt-remarks/OptRemarks.exports @@ -0,0 +1,6 @@ +LLVMOptRemarkParserCreate +LLVMOptRemarkParserGetNext +LLVMOptRemarkParserHasError +LLVMOptRemarkParserGetErrorMessage +LLVMOptRemarkParserDispose +LLVMOptRemarkVersion diff --git a/tools/llvm-mca/HWEventListener.cpp b/tools/opt-remarks/liboptremarks.cpp index f27a04a9a980..13acada06ac2 100644 --- a/tools/llvm-mca/HWEventListener.cpp +++ b/tools/opt-remarks/liboptremarks.cpp @@ -1,4 +1,4 @@ -//===----------------------- HWEventListener.cpp ----------------*- C++ -*-===// +//===-liboptremarks.cpp - LLVM Opt-Remarks Shared Library -----------------===// // // The LLVM Compiler Infrastructure // @@ -6,16 +6,13 @@ // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// -/// \file -/// -/// This file defines a vtable anchor for class HWEventListener. -/// +// +// Provide a library to work with optimization remarks. +// //===----------------------------------------------------------------------===// -#include "HWEventListener.h" - -namespace mca { +#include "llvm-c/OptRemarks.h" -// Anchor the vtable here. -void HWEventListener::anchor() {} -} // namespace mca +extern uint32_t LLVMOptRemarkVersion(void) { + return OPT_REMARKS_API_VERSION; +} diff --git a/tools/opt-viewer/opt-diff.py b/tools/opt-viewer/opt-diff.py index f3bfd1860b91..36e81a5d569a 100755 --- a/tools/opt-viewer/opt-diff.py +++ b/tools/opt-viewer/opt-diff.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python from __future__ import print_function diff --git a/tools/opt-viewer/opt-stats.py b/tools/opt-viewer/opt-stats.py index 03de23bdb275..f4ee3a7d44e6 100755 --- a/tools/opt-viewer/opt-stats.py +++ b/tools/opt-viewer/opt-stats.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python from __future__ import print_function diff --git a/tools/opt-viewer/opt-viewer.py b/tools/opt-viewer/opt-viewer.py index 4887043e0f96..f6582506311c 100755 --- a/tools/opt-viewer/opt-viewer.py +++ b/tools/opt-viewer/opt-viewer.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python from __future__ import print_function @@ -72,7 +72,10 @@ class SourceFileRenderer: file_text = stream.read() if self.no_highlight: - html_highlighted = file_text.decode('utf-8') + if sys.version_info.major >= 3: + html_highlighted = file_text + else: + html_highlighted = file_text.decode('utf-8') else: html_highlighted = highlight( file_text, diff --git a/tools/opt-viewer/optpmap.py b/tools/opt-viewer/optpmap.py index db6b079b3a6d..ff3e683f3d06 100644 --- a/tools/opt-viewer/optpmap.py +++ b/tools/opt-viewer/optpmap.py @@ -42,7 +42,7 @@ def pmap(func, iterable, processes, should_print_progress, *args, **kwargs): func_and_args = [(func, arg, should_print_progress,) for arg in iterable] if processes == 1: - result = map(_wrapped_func, func_and_args, *args, **kwargs) + result = list(map(_wrapped_func, func_and_args, *args, **kwargs)) else: pool = multiprocessing.Pool(initializer=_init, initargs=(_current, _total,), diff --git a/tools/opt-viewer/optrecord.py b/tools/opt-viewer/optrecord.py index 8cf22ee3f05c..0193d25704c5 100644 --- a/tools/opt-viewer/optrecord.py +++ b/tools/opt-viewer/optrecord.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python from __future__ import print_function diff --git a/tools/opt/Debugify.cpp b/tools/opt/Debugify.cpp index 6c3cdc75e334..3b1effba1592 100644 --- a/tools/opt/Debugify.cpp +++ b/tools/opt/Debugify.cpp @@ -96,11 +96,12 @@ bool applyDebugifyMetadata(Module &M, 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, /*isDefinition=*/true, NextLine, - DINode::FlagZero, /*isOptimized=*/true); + DISubprogram::DISPFlags SPFlags = + DISubprogram::SPFlagDefinition | DISubprogram::SPFlagOptimized; + if (F.hasPrivateLinkage() || F.hasInternalLinkage()) + SPFlags |= DISubprogram::SPFlagLocalToUnit; + auto SP = DIB.createFunction(CU, F.getName(), F.getName(), File, NextLine, + SPType, NextLine, DINode::FlagZero, SPFlags); F.setSubprogram(SP); for (BasicBlock &BB : F) { // Attach debug locations. diff --git a/tools/opt/NewPMDriver.cpp b/tools/opt/NewPMDriver.cpp index a91d4cb5f9cd..211a3b151fe1 100644 --- a/tools/opt/NewPMDriver.cpp +++ b/tools/opt/NewPMDriver.cpp @@ -13,8 +13,8 @@ /// //===----------------------------------------------------------------------===// -#include "Debugify.h" #include "NewPMDriver.h" +#include "Debugify.h" #include "PassPrinters.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/AliasAnalysis.h" @@ -29,6 +29,7 @@ #include "llvm/IR/Verifier.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Passes/PassPlugin.h" +#include "llvm/Passes/StandardInstrumentations.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/ToolOutputFile.h" @@ -94,6 +95,12 @@ static cl::opt<std::string> PipelineStartEPPipeline( cl::desc("A textual description of the function pass pipeline inserted at " "the PipelineStart extension point into default pipelines"), cl::Hidden); +static cl::opt<std::string> OptimizerLastEPPipeline( + "passes-ep-optimizer-last", + cl::desc("A textual description of the function pass pipeline inserted at " + "the OptimizerLast 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, @@ -107,24 +114,30 @@ static cl::opt<PGOKind> PGOKindFlag( "Use sampled profile to guide PGO."))); static cl::opt<std::string> ProfileFile( "profile-file", cl::desc("Path to the profile."), cl::Hidden); +static cl::opt<std::string> + ProfileRemappingFile("profile-remapping-file", + cl::desc("Path to the profile remapping file."), + cl::Hidden); static cl::opt<bool> DebugInfoForProfiling( "new-pm-debug-info-for-profiling", cl::init(false), cl::Hidden, cl::desc("Emit special debug info to enable PGO profile generation.")); /// @}} template <typename PassManagerT> -bool tryParsePipelineText(PassBuilder &PB, StringRef PipelineText) { - if (PipelineText.empty()) +bool tryParsePipelineText(PassBuilder &PB, + const cl::opt<std::string> &PipelineOpt) { + if (PipelineOpt.empty()) return false; // Verify the pipeline is parseable: PassManagerT PM; - if (PB.parsePassPipeline(PM, PipelineText)) - return true; - - errs() << "Could not parse pipeline '" << PipelineText - << "'. I'm going to igore it.\n"; - return false; + if (auto Err = PB.parsePassPipeline(PM, PipelineOpt)) { + errs() << "Could not parse -" << PipelineOpt.ArgStr + << " pipeline: " << toString(std::move(Err)) + << "... I'm going to ignore it.\n"; + return false; + } + return true; } /// If one of the EPPipeline command line options was given, register callbacks @@ -132,50 +145,69 @@ bool tryParsePipelineText(PassBuilder &PB, StringRef PipelineText) { static void registerEPCallbacks(PassBuilder &PB, bool VerifyEachPass, bool DebugLogging) { if (tryParsePipelineText<FunctionPassManager>(PB, PeepholeEPPipeline)) - PB.registerPeepholeEPCallback([&PB, VerifyEachPass, DebugLogging]( - FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { - PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass, - DebugLogging); - }); + PB.registerPeepholeEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse PeepholeEP pipeline: "); + Err(PB.parsePassPipeline(PM, PeepholeEPPipeline, VerifyEachPass, + DebugLogging)); + }); if (tryParsePipelineText<LoopPassManager>(PB, LateLoopOptimizationsEPPipeline)) PB.registerLateLoopOptimizationsEPCallback( [&PB, VerifyEachPass, DebugLogging]( LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { - PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline, - VerifyEachPass, DebugLogging); + ExitOnError Err("Unable to parse LateLoopOptimizationsEP pipeline: "); + Err(PB.parsePassPipeline(PM, LateLoopOptimizationsEPPipeline, + VerifyEachPass, DebugLogging)); }); if (tryParsePipelineText<LoopPassManager>(PB, LoopOptimizerEndEPPipeline)) - PB.registerLoopOptimizerEndEPCallback([&PB, VerifyEachPass, DebugLogging]( - LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { - PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline, VerifyEachPass, - DebugLogging); - }); + PB.registerLoopOptimizerEndEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + LoopPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse LoopOptimizerEndEP pipeline: "); + Err(PB.parsePassPipeline(PM, LoopOptimizerEndEPPipeline, + VerifyEachPass, DebugLogging)); + }); if (tryParsePipelineText<FunctionPassManager>(PB, ScalarOptimizerLateEPPipeline)) PB.registerScalarOptimizerLateEPCallback( [&PB, VerifyEachPass, DebugLogging]( FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { - PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline, - VerifyEachPass, DebugLogging); + ExitOnError Err("Unable to parse ScalarOptimizerLateEP pipeline: "); + Err(PB.parsePassPipeline(PM, ScalarOptimizerLateEPPipeline, + VerifyEachPass, DebugLogging)); }); if (tryParsePipelineText<CGSCCPassManager>(PB, CGSCCOptimizerLateEPPipeline)) - PB.registerCGSCCOptimizerLateEPCallback([&PB, VerifyEachPass, DebugLogging]( - CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) { - PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline, VerifyEachPass, - DebugLogging); - }); + PB.registerCGSCCOptimizerLateEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + CGSCCPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse CGSCCOptimizerLateEP pipeline: "); + Err(PB.parsePassPipeline(PM, CGSCCOptimizerLateEPPipeline, + VerifyEachPass, DebugLogging)); + }); if (tryParsePipelineText<FunctionPassManager>(PB, VectorizerStartEPPipeline)) - PB.registerVectorizerStartEPCallback([&PB, VerifyEachPass, DebugLogging]( - FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { - PB.parsePassPipeline(PM, VectorizerStartEPPipeline, VerifyEachPass, - DebugLogging); - }); + PB.registerVectorizerStartEPCallback( + [&PB, VerifyEachPass, DebugLogging]( + FunctionPassManager &PM, PassBuilder::OptimizationLevel Level) { + ExitOnError Err("Unable to parse VectorizerStartEP pipeline: "); + Err(PB.parsePassPipeline(PM, VectorizerStartEPPipeline, + VerifyEachPass, DebugLogging)); + }); if (tryParsePipelineText<ModulePassManager>(PB, PipelineStartEPPipeline)) PB.registerPipelineStartEPCallback( [&PB, VerifyEachPass, DebugLogging](ModulePassManager &PM) { - PB.parsePassPipeline(PM, PipelineStartEPPipeline, VerifyEachPass, - DebugLogging); + ExitOnError Err("Unable to parse PipelineStartEP pipeline: "); + Err(PB.parsePassPipeline(PM, PipelineStartEPPipeline, VerifyEachPass, + DebugLogging)); + }); + if (tryParsePipelineText<FunctionPassManager>(PB, OptimizerLastEPPipeline)) + PB.registerOptimizerLastEPCallback( + [&PB, VerifyEachPass, DebugLogging](FunctionPassManager &PM, + PassBuilder::OptimizationLevel) { + ExitOnError Err("Unable to parse OptimizerLastEP pipeline: "); + Err(PB.parsePassPipeline(PM, OptimizerLastEPPipeline, VerifyEachPass, + DebugLogging)); }); } @@ -199,21 +231,25 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, Optional<PGOOptions> P; switch (PGOKindFlag) { case InstrGen: - P = PGOOptions(ProfileFile, "", "", true); + P = PGOOptions(ProfileFile, "", "", "", true); break; case InstrUse: - P = PGOOptions("", ProfileFile, "", false); + P = PGOOptions("", ProfileFile, "", ProfileRemappingFile, false); break; case SampleUse: - P = PGOOptions("", "", ProfileFile, false); + P = PGOOptions("", "", ProfileFile, ProfileRemappingFile, false); break; case NoPGO: if (DebugInfoForProfiling) - P = PGOOptions("", "", "", false, true); + P = PGOOptions("", "", "", "", false, true); else P = None; } - PassBuilder PB(TM, P); + PassInstrumentationCallbacks PIC; + StandardInstrumentations SI; + SI.registerCallbacks(PIC); + + PassBuilder PB(TM, P, &PIC); registerEPCallbacks(PB, VerifyEachPass, DebugPM); // Load requested pass plugins and let them register pass builder callbacks @@ -249,8 +285,8 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, // Specially handle the alias analysis manager so that we can register // a custom pipeline of AA passes with it. AAManager AA; - if (!PB.parseAAPipeline(AA, AAPipeline)) { - errs() << Arg0 << ": unable to parse AA pipeline description.\n"; + if (auto Err = PB.parseAAPipeline(AA, AAPipeline)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; return false; } @@ -275,8 +311,9 @@ bool llvm::runPassPipeline(StringRef Arg0, Module &M, TargetMachine *TM, if (EnableDebugify) MPM.addPass(NewPMDebugifyPass()); - if (!PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) { - errs() << Arg0 << ": unable to parse pass pipeline description.\n"; + if (auto Err = + PB.parsePassPipeline(MPM, PassPipeline, VerifyEachPass, DebugPM)) { + errs() << Arg0 << ": " << toString(std::move(Err)) << "\n"; return false; } diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp index 6e287b6c0ab6..a4967a234d9c 100644 --- a/tools/opt/opt.cpp +++ b/tools/opt/opt.cpp @@ -103,6 +103,10 @@ static cl::opt<bool> OutputThinLTOBC("thinlto-bc", cl::desc("Write output as ThinLTO-ready bitcode")); +static cl::opt<bool> + SplitLTOUnit("thinlto-split-lto-unit", + cl::desc("Enable splitting of a ThinLTO LTOUnit")); + static cl::opt<std::string> ThinLinkBitcodeFile( "thin-link-bitcode-file", cl::value_desc("filename"), cl::desc( @@ -463,6 +467,7 @@ int main(int argc, char **argv) { initializePreISelIntrinsicLoweringLegacyPassPass(Registry); initializeGlobalMergePass(Registry); initializeIndirectBrExpandPassPass(Registry); + initializeInterleavedLoadCombinePass(Registry); initializeInterleavedAccessPass(Registry); initializeEntryExitInstrumenterPass(Registry); initializePostInlineEntryExitInstrumenterPass(Registry); @@ -595,6 +600,9 @@ int main(int argc, char **argv) { if (CheckBitcodeOutputToConsole(Out->os(), !Quiet)) NoOutput = true; + if (OutputThinLTOBC) + M->addModuleFlag(Module::Error, "EnableSplitLTOUnit", SplitLTOUnit); + if (PassPipeline.getNumOccurrences() > 0) { OutputKind OK = OK_NoOutput; if (!NoOutput) diff --git a/tools/sancov/coverage-report-server.py b/tools/sancov/coverage-report-server.py index a2e161d0de58..2fb70eecfcfa 100755 --- a/tools/sancov/coverage-report-server.py +++ b/tools/sancov/coverage-report-server.py @@ -22,6 +22,8 @@ Other options: --host host_name - host name to bind server to (127.0.0.1) ''' +from __future__ import print_function + import argparse import http.server import json diff --git a/tools/sancov/sancov.cpp b/tools/sancov/sancov.cpp index 0bddd35a96f2..e8935d1be212 100644 --- a/tools/sancov/sancov.cpp +++ b/tools/sancov/sancov.cpp @@ -766,6 +766,19 @@ findSanitizerCovFunctions(const object::ObjectFile &O) { return Result; } +static uint64_t getPreviousInstructionPc(uint64_t PC, + Triple TheTriple) { + if (TheTriple.isARM()) { + return (PC - 3) & (~1); + } else if (TheTriple.isAArch64()) { + return PC - 4; + } else if (TheTriple.isMIPS()) { + return PC - 8; + } else { + return PC - 1; + } +} + // Locate addresses of all coverage points in a file. Coverage point // is defined as the 'address of instruction following __sanitizer_cov // call - 1'. @@ -832,7 +845,7 @@ static void getObjectCoveragePoints(const object::ObjectFile &O, } uint64_t Addr = Index + SectionAddr; // Sanitizer coverage uses the address of the next instruction - 1. - uint64_t CovPoint = Addr + Size - 1; + uint64_t CovPoint = getPreviousInstructionPc(Addr + Size, TheTriple); uint64_t Target; if (MIA->isCall(Inst) && MIA->evaluateBranch(Inst, SectionAddr + Index, Size, Target) && diff --git a/tools/sanstats/sanstats.cpp b/tools/sanstats/sanstats.cpp index 71f0207bab50..a345e9f06bed 100644 --- a/tools/sanstats/sanstats.cpp +++ b/tools/sanstats/sanstats.cpp @@ -15,7 +15,9 @@ #include "llvm/DebugInfo/Symbolize/Symbolize.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorOr.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" #include "llvm/Transforms/Utils/SanitizerStats.h" #include <stdint.h> @@ -52,7 +54,11 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) { ++Begin; if (Begin == End) return nullptr; - StringRef Filename(FilenameBegin, Begin - FilenameBegin); + std::string Filename(FilenameBegin, Begin - FilenameBegin); + + if (!llvm::sys::fs::exists(Filename)) + Filename = std::string(llvm::sys::path::parent_path(ClInputFile)) + + std::string(llvm::sys::path::filename(Filename)); ++Begin; if (Begin == End) @@ -81,8 +87,9 @@ const char *ReadModule(char SizeofPtr, const char *Begin, const char *End) { // remove one from the address to get the correct DI. if (Expected<DILineInfo> LineInfo = Symbolizer.symbolizeCode(Filename, Addr - 1)) { - llvm::outs() << LineInfo->FileName << ':' << LineInfo->Line << ' ' - << LineInfo->FunctionName << ' '; + llvm::outs() << format_hex(Addr - 1, 18) << ' ' << LineInfo->FileName + << ':' << LineInfo->Line << ' ' << LineInfo->FunctionName + << ' '; } else { logAllUnhandledErrors(LineInfo.takeError(), llvm::outs(), "<error> "); } diff --git a/tools/xcode-toolchain/CMakeLists.txt b/tools/xcode-toolchain/CMakeLists.txt index 0ae5e374fe9f..6167f5f6bdd7 100644 --- a/tools/xcode-toolchain/CMakeLists.txt +++ b/tools/xcode-toolchain/CMakeLists.txt @@ -100,7 +100,7 @@ add_llvm_install_targets(install-xcode-toolchain PREFIX ${LLVMToolchainDir}/usr/) if(LLVM_DISTRIBUTION_COMPONENTS) - if(CMAKE_CONFIGURATION_TYPES) + if(LLVM_ENABLE_IDE) message(FATAL_ERROR "LLVM_DISTRIBUTION_COMPONENTS cannot be specified with multi-configuration generators (i.e. Xcode or Visual Studio)") endif() diff --git a/tools/yaml2obj/yaml2coff.cpp b/tools/yaml2obj/yaml2coff.cpp index befa01b369c0..4fe9ab090414 100644 --- a/tools/yaml2obj/yaml2coff.cpp +++ b/tools/yaml2obj/yaml2coff.cpp @@ -24,6 +24,7 @@ #include "llvm/Support/Endian.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Support/WithColor.h" #include "llvm/Support/raw_ostream.h" #include <vector> @@ -46,7 +47,8 @@ struct COFFParser { bool isPE() const { return Obj.OptionalHeader.hasValue(); } bool is64Bit() const { - return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64; + return Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_AMD64 || + Obj.Header.Machine == COFF::IMAGE_FILE_MACHINE_ARM64; } uint32_t getFileAlignment() const { @@ -519,7 +521,15 @@ static bool writeCOFF(COFFParser &CP, raw_ostream &OS) { assert(S.Header.SizeOfRawData >= S.SectionData.binary_size()); OS << num_zeros(S.Header.SizeOfRawData - S.SectionData.binary_size()); for (const COFFYAML::Relocation &R : S.Relocations) { - uint32_t SymbolTableIndex = SymbolTableIndexMap[R.SymbolName]; + uint32_t SymbolTableIndex; + if (R.SymbolTableIndex) { + if (!R.SymbolName.empty()) + WithColor::error() + << "Both SymbolName and SymbolTableIndex specified\n"; + SymbolTableIndex = *R.SymbolTableIndex; + } else { + SymbolTableIndex = SymbolTableIndexMap[R.SymbolName]; + } OS << binary_le(R.VirtualAddress) << binary_le(SymbolTableIndex) << binary_le(R.Type); diff --git a/tools/yaml2obj/yaml2elf.cpp b/tools/yaml2obj/yaml2elf.cpp index 7ca5ac206c9e..5e23bc6f0850 100644 --- a/tools/yaml2obj/yaml2elf.cpp +++ b/tools/yaml2obj/yaml2elf.cpp @@ -196,7 +196,7 @@ void ELFState<ELFT>::initELFHeader(Elf_Ehdr &Header) { Header.e_ident[EI_DATA] = IsLittleEndian ? ELFDATA2LSB : ELFDATA2MSB; Header.e_ident[EI_VERSION] = EV_CURRENT; Header.e_ident[EI_OSABI] = Doc.Header.OSABI; - Header.e_ident[EI_ABIVERSION] = 0; + Header.e_ident[EI_ABIVERSION] = Doc.Header.ABIVersion; Header.e_type = Doc.Header.Type; Header.e_machine = Doc.Header.Machine; Header.e_version = EV_CURRENT; @@ -226,6 +226,16 @@ void ELFState<ELFT>::initProgramHeaders(std::vector<Elf_Phdr> &PHeaders) { } } +static bool convertSectionIndex(NameToIdxMap &SN2I, StringRef SecName, + StringRef IndexSrc, unsigned &IndexDest) { + if (SN2I.lookup(IndexSrc, IndexDest) && !to_integer(IndexSrc, IndexDest)) { + WithColor::error() << "Unknown section referenced: '" << IndexSrc + << "' at YAML section '" << SecName << "'.\n"; + return false; + } + return true; +} + template <class ELFT> bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, ContiguousBlobAccumulator &CBA) { @@ -245,11 +255,8 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, if (!Sec->Link.empty()) { unsigned Index; - if (SN2I.lookup(Sec->Link, Index)) { - WithColor::error() << "Unknown section referenced: '" << Sec->Link - << "' at YAML section '" << Sec->Name << "'.\n"; + if (!convertSectionIndex(SN2I, Sec->Name, Sec->Link, Index)) return false; - } SHeader.sh_link = Index; } @@ -261,20 +268,14 @@ bool ELFState<ELFT>::initSectionHeaders(std::vector<Elf_Shdr> &SHeaders, SHeader.sh_link = getDotSymTabSecNo(); unsigned Index; - if (SN2I.lookup(S->Info, Index)) { - if (S->Info.getAsInteger(0, Index)) { - WithColor::error() << "Unknown section referenced: '" << S->Info - << "' at YAML section '" << S->Name << "'.\n"; - return false; - } - } + if (!convertSectionIndex(SN2I, S->Name, S->Info, Index)) + return false; SHeader.sh_info = Index; - if (!writeSectionContent(SHeader, *S, CBA)) return false; } else if (auto S = dyn_cast<ELFYAML::Group>(Sec.get())) { unsigned SymIdx; - if (SymN2I.lookup(S->Info, SymIdx)) { + if (SymN2I.lookup(S->Info, SymIdx) && !to_integer(S->Info, SymIdx)) { WithColor::error() << "Unknown symbol referenced: '" << S->Info << "' at YAML section '" << S->Name << "'.\n"; return false; @@ -461,7 +462,9 @@ ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, Section.Content.writeAsBinary(OS); for (auto i = Section.Content.binary_size(); i < Section.Size; ++i) OS.write(0); - if (Section.Type == llvm::ELF::SHT_RELR) + if (Section.EntSize) + SHeader.sh_entsize = *Section.EntSize; + else 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); @@ -535,12 +538,9 @@ bool ELFState<ELFT>::writeSectionContent(Elf_Shdr &SHeader, unsigned int sectionIndex = 0; if (member.sectionNameOrType == "GRP_COMDAT") sectionIndex = llvm::ELF::GRP_COMDAT; - else if (SN2I.lookup(member.sectionNameOrType, sectionIndex)) { - WithColor::error() << "Unknown section referenced: '" - << member.sectionNameOrType << "' at YAML section' " - << Section.Name << "\n"; + else if (!convertSectionIndex(SN2I, Section.Name, member.sectionNameOrType, + sectionIndex)) return false; - } SIdx = sectionIndex; OS.write((const char *)&SIdx, sizeof(SIdx)); } @@ -685,7 +685,8 @@ template <class ELFT> bool ELFState<ELFT>::hasDynamicSymbols() const { Doc.DynamicSymbols.Local.size() > 0; } -template <class ELFT> SmallVector<const char *, 5> ELFState<ELFT>::implicitSectionNames() const { +template <class ELFT> +SmallVector<const char *, 5> ELFState<ELFT>::implicitSectionNames() const { if (!hasDynamicSymbols()) return {".symtab", ".strtab", ".shstrtab"}; return {".symtab", ".strtab", ".shstrtab", ".dynsym", ".dynstr"}; diff --git a/tools/yaml2obj/yaml2macho.cpp b/tools/yaml2obj/yaml2macho.cpp index 23fe946f1da3..1f36431c05de 100644 --- a/tools/yaml2obj/yaml2macho.cpp +++ b/tools/yaml2obj/yaml2macho.cpp @@ -417,10 +417,9 @@ Error MachOWriter::writeLinkEditData(raw_ostream &OS) { } } - llvm::sort(WriteQueue.begin(), WriteQueue.end(), - [](const writeOperation &a, const writeOperation &b) { - return a.first < b.first; - }); + llvm::sort(WriteQueue, [](const writeOperation &a, const writeOperation &b) { + return a.first < b.first; + }); for (auto writeOp : WriteQueue) { ZeroToOffset(OS, writeOp.first); diff --git a/tools/yaml2obj/yaml2wasm.cpp b/tools/yaml2obj/yaml2wasm.cpp index a7ec25e31c73..2d3e3b71f086 100644 --- a/tools/yaml2obj/yaml2wasm.cpp +++ b/tools/yaml2obj/yaml2wasm.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// // +#include "llvm/Object/Wasm.h" #include "llvm/ObjectYAML/ObjectYAML.h" #include "llvm/Support/Endian.h" #include "llvm/Support/LEB128.h" @@ -37,6 +38,7 @@ private: int writeSectionContent(raw_ostream &OS, WasmYAML::TableSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::MemorySection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::GlobalSection &Section); + int writeSectionContent(raw_ostream &OS, WasmYAML::EventSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::ExportSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::StartSection &Section); int writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section); @@ -44,11 +46,13 @@ private: int writeSectionContent(raw_ostream &OS, WasmYAML::DataSection &Section); // Custom section types + int writeSectionContent(raw_ostream &OS, WasmYAML::DylinkSection &Section); 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; + uint32_t NumImportedEvents = 0; }; static int writeUint64(raw_ostream &OS, uint64_t Value) { @@ -101,7 +105,7 @@ static int writeInitExpr(const wasm::WasmInitExpr &InitExpr, raw_ostream &OS) { case wasm::WASM_OPCODE_F64_CONST: writeUint64(OS, InitExpr.Value.Float64); break; - case wasm::WASM_OPCODE_GET_GLOBAL: + case wasm::WASM_OPCODE_GLOBAL_GET: encodeULEB128(InitExpr.Value.Global, OS); break; default: @@ -127,12 +131,25 @@ public: OutString.clear(); } - raw_ostream& GetStream() { - return StringStream; - } + raw_ostream &GetStream() { return StringStream; } }; -int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &Section) { +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::DylinkSection &Section) { + writeStringRef(Section.Name, OS); + encodeULEB128(Section.MemorySize, OS); + encodeULEB128(Section.MemoryAlignment, OS); + encodeULEB128(Section.TableSize, OS); + encodeULEB128(Section.TableAlignment, OS); + encodeULEB128(Section.Needed.size(), OS); + for (StringRef Needed : Section.Needed) { + writeStringRef(Needed, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::LinkingSection &Section) { writeStringRef(Section.Name, OS); encodeULEB128(Section.Version, OS); @@ -153,6 +170,7 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &S switch (Info.Kind) { case wasm::WASM_SYMBOL_TYPE_FUNCTION: case wasm::WASM_SYMBOL_TYPE_GLOBAL: + case wasm::WASM_SYMBOL_TYPE_EVENT: encodeULEB128(Info.ElementIndex, SubSection.GetStream()); if ((Info.Flags & wasm::WASM_SYMBOL_UNDEFINED) == 0) writeStringRef(Info.Name, SubSection.GetStream()); @@ -218,7 +236,8 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::LinkingSection &S return 0; } -int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Section) { +int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::NameSection &Section) { writeStringRef(Section.Name, OS); if (Section.FunctionNames.size()) { writeUint8(OS, wasm::WASM_NAMES_FUNCTION); @@ -238,7 +257,10 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::NameSection &Sect int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::CustomSection &Section) { - if (auto S = dyn_cast<WasmYAML::NameSection>(&Section)) { + if (auto S = dyn_cast<WasmYAML::DylinkSection>(&Section)) { + if (auto Err = writeSectionContent(OS, *S)) + return Err; + } else if (auto S = dyn_cast<WasmYAML::NameSection>(&Section)) { if (auto Err = writeSectionContent(OS, *S)) return Err; } else if (auto S = dyn_cast<WasmYAML::LinkingSection>(&Section)) { @@ -292,11 +314,16 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, writeUint8(OS, Import.GlobalImport.Mutable); NumImportedGlobals++; break; + case wasm::WASM_EXTERNAL_EVENT: + writeUint32(OS, Import.EventImport.Attribute); + writeUint32(OS, Import.EventImport.SigIndex); + NumImportedGlobals++; + break; case wasm::WASM_EXTERNAL_MEMORY: writeLimits(Import.Memory, OS); break; case wasm::WASM_EXTERNAL_TABLE: - writeUint8(OS,Import.TableImport.ElemType); + writeUint8(OS, Import.TableImport.ElemType); writeLimits(Import.TableImport.TableLimits, OS); break; default: @@ -370,6 +397,22 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, } int WasmWriter::writeSectionContent(raw_ostream &OS, + WasmYAML::EventSection &Section) { + encodeULEB128(Section.Events.size(), OS); + uint32_t ExpectedIndex = NumImportedEvents; + for (auto &Event : Section.Events) { + if (Event.Index != ExpectedIndex) { + errs() << "Unexpected event index: " << Event.Index << "\n"; + return 1; + } + ++ExpectedIndex; + encodeULEB128(Event.Attribute, OS); + encodeULEB128(Event.SigIndex, OS); + } + return 0; +} + +int WasmWriter::writeSectionContent(raw_ostream &OS, WasmYAML::ElemSection &Section) { encodeULEB128(Section.Segments.size(), OS); for (auto &Segment : Section.Segments) { @@ -428,64 +471,61 @@ int WasmWriter::writeSectionContent(raw_ostream &OS, int WasmWriter::writeRelocSection(raw_ostream &OS, WasmYAML::Section &Sec, uint32_t SectionIndex) { switch (Sec.Type) { - case wasm::WASM_SEC_CODE: - writeStringRef("reloc.CODE", OS); - break; - case wasm::WASM_SEC_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"); + case wasm::WASM_SEC_CODE: + writeStringRef("reloc.CODE", OS); + break; + case wasm::WASM_SEC_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; } encodeULEB128(SectionIndex, OS); encodeULEB128(Sec.Relocations.size(), OS); - for (auto Reloc: Sec.Relocations) { + for (auto Reloc : Sec.Relocations) { 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); + 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); } } return 0; } - int WasmWriter::writeWasm(raw_ostream &OS) { // Write headers OS.write(wasm::WasmMagic, sizeof(wasm::WasmMagic)); writeUint32(OS, Obj.Header.Version); // Write each section - uint32_t LastType = 0; + llvm::object::WasmSectionOrderChecker Checker; for (const std::unique_ptr<WasmYAML::Section> &Sec : Obj.Sections) { - 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; + StringRef SecName = ""; + if (auto S = dyn_cast<WasmYAML::CustomSection>(Sec.get())) + SecName = S->Name; + if (!Checker.isValidSectionOrder(Sec->Type, SecName)) { + errs() << "Out of order section type: " << Sec->Type << "\n"; + return 1; } - encodeULEB128(Sec->Type, OS); std::string OutString; raw_string_ostream StringStream(OutString); @@ -510,6 +550,9 @@ int WasmWriter::writeWasm(raw_ostream &OS) { } else if (auto S = dyn_cast<WasmYAML::GlobalSection>(Sec.get())) { if (auto Err = writeSectionContent(StringStream, *S)) return Err; + } else if (auto S = dyn_cast<WasmYAML::EventSection>(Sec.get())) { + if (auto Err = writeSectionContent(StringStream, *S)) + return Err; } else if (auto S = dyn_cast<WasmYAML::ExportSection>(Sec.get())) { if (auto Err = writeSectionContent(StringStream, *S)) return Err; |
