diff options
author | Clement Courbet <courbet@google.com> | 2018-06-25 13:12:02 +0000 |
---|---|---|
committer | Clement Courbet <courbet@google.com> | 2018-06-25 13:12:02 +0000 |
commit | 15fac97085c3f1d11ae035207128c57401c2ec57 (patch) | |
tree | 34dbfc46d4a5193bd8d819140ce5663ef1a05b7e /tools/llvm-exegesis | |
parent | 647fa5f2e686fabf182f051badf8dcfc58f15ada (diff) |
[llvm-exegesis] Generate snippet setup code.
Summary:
This ensures that the snippet always sees the same values for registers,
making measurements reproducible.
This will also allow exploring different values.
Reviewers: gchatelet
Subscribers: tschuett, llvm-commits
Differential Revision: https://reviews.llvm.org/D48542
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@335465 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-exegesis')
-rw-r--r-- | tools/llvm-exegesis/lib/Assembler.cpp | 38 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/Assembler.h | 6 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/BenchmarkRunner.cpp | 67 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/BenchmarkRunner.h | 14 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/LlvmState.cpp | 1 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/LlvmState.h | 1 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/MCInstrDescView.cpp | 16 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/MCInstrDescView.h | 11 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/Target.h | 7 | ||||
-rw-r--r-- | tools/llvm-exegesis/lib/X86/Target.cpp | 82 |
10 files changed, 220 insertions, 23 deletions
diff --git a/tools/llvm-exegesis/lib/Assembler.cpp b/tools/llvm-exegesis/lib/Assembler.cpp index 05a13dbe9a1..f5be120f281 100644 --- a/tools/llvm-exegesis/lib/Assembler.cpp +++ b/tools/llvm-exegesis/lib/Assembler.cpp @@ -28,6 +28,21 @@ 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, bool &IsComplete) { + IsComplete = true; + std::vector<llvm::MCInst> Result; + for (const unsigned Reg : RegsToDef) { + // Load a constant in the register. + const auto Code = ET.setRegToConstant(Reg); + if (Code.empty()) + IsComplete = false; + Result.insert(Result.end(), Code.begin(), Code.end()); + } + return Result; +} + // Small utility function to add named passes. static bool addPass(llvm::PassManagerBase &PM, llvm::StringRef PassName, llvm::TargetPassConfig &TPC) { @@ -123,7 +138,9 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM) { return MF.getSubtarget().getRegisterInfo()->getReservedRegs(MF); } -void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM, +void assembleToStream(const ExegesisTarget *ET, + std::unique_ptr<llvm::LLVMTargetMachine> TM, + llvm::ArrayRef<unsigned> RegsToDef, llvm::ArrayRef<llvm::MCInst> Instructions, llvm::raw_pwrite_stream &AsmStream) { std::unique_ptr<llvm::LLVMContext> Context = @@ -140,9 +157,20 @@ void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM, auto &Properties = MF.getProperties(); Properties.set(llvm::MachineFunctionProperties::Property::NoVRegs); Properties.reset(llvm::MachineFunctionProperties::Property::IsSSA); - // FIXME: Remove this when we assign all used registers as config step. This - // essentially disables checks that used registers are def'ed somewhere. - Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); + std::vector<llvm::MCInst> SnippetWithSetup; + bool IsSnippetSetupComplete; + if (ET) { + SnippetWithSetup = + generateSnippetSetupCode(RegsToDef, *ET, IsSnippetSetupComplete); + SnippetWithSetup.insert(SnippetWithSetup.end(), Instructions.begin(), + Instructions.end()); + Instructions = SnippetWithSetup; + } + // If the snippet setup is not complete, we disable liveliness tracking. This + // means that we won't know what values are in the registers. + if (!IsSnippetSetupComplete) + Properties.reset(llvm::MachineFunctionProperties::Property::TracksLiveness); + // prologue/epilogue pass needs the reserved registers to be frozen, this // is usually done by the SelectionDAGISel pass. MF.getRegInfo().freezeReservedRegs(MF); @@ -162,7 +190,7 @@ void assembleToStream(std::unique_ptr<llvm::LLVMTargetMachine> TM, PM.add(MMI.release()); TPC->printAndVerify("MachineFunctionGenerator::assemble"); // Add target-specific passes. - if (const auto *ET = ExegesisTarget::lookup(TM->getTargetTriple())) { + if (ET) { ET->addTargetSpecificPasses(PM); TPC->printAndVerify("After ExegesisTarget::addTargetSpecificPasses"); } diff --git a/tools/llvm-exegesis/lib/Assembler.h b/tools/llvm-exegesis/lib/Assembler.h index 932542ca504..fb39053dbce 100644 --- a/tools/llvm-exegesis/lib/Assembler.h +++ b/tools/llvm-exegesis/lib/Assembler.h @@ -33,6 +33,8 @@ namespace exegesis { +class ExegesisTarget; + // Gather the set of reserved registers (depends on function's calling // convention and target machine). llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); @@ -41,7 +43,9 @@ llvm::BitVector getFunctionReservedRegs(const llvm::TargetMachine &TM); // 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(std::unique_ptr<llvm::LLVMTargetMachine> TM, +void assembleToStream(const ExegesisTarget *ET, + std::unique_ptr<llvm::LLVMTargetMachine> TM, + llvm::ArrayRef<unsigned> RegsToDef, llvm::ArrayRef<llvm::MCInst> Instructions, llvm::raw_pwrite_stream &AsmStream); diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp index 063d72e2911..94b24732f6f 100644 --- a/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ b/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -78,10 +78,11 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, // Repeat the snippet until there are at least NumInstructions in the // resulting code. The snippet is always repeated at least once. - const auto GenerateInstructions = [&Snippet](const int MinInstructions) { - std::vector<llvm::MCInst> Code = Snippet; + const auto GenerateInstructions = [&Configuration]( + const int MinInstructions) { + std::vector<llvm::MCInst> Code = Configuration.Snippet; for (int I = 0; I < MinInstructions; ++I) - Code.push_back(Snippet[I % Snippet.size()]); + Code.push_back(Configuration.Snippet[I % Configuration.Snippet.size()]); return Code; }; @@ -91,7 +92,8 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, constexpr const int kMinInstructionsForSnippet = 16; { auto ObjectFilePath = - writeObjectFile(GenerateInstructions(kMinInstructionsForSnippet)); + writeObjectFile(Configuration.SnippetSetup, + GenerateInstructions(kMinInstructionsForSnippet)); if (llvm::Error E = ObjectFilePath.takeError()) { InstrBenchmark.Error = llvm::toString(std::move(E)); return InstrBenchmark; @@ -105,7 +107,8 @@ BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, // Assemble NumRepetitions instructions repetitions of the snippet for // measurements. auto ObjectFilePath = - writeObjectFile(GenerateInstructions(InstrBenchmark.NumRepetitions)); + writeObjectFile(Configuration.SnippetSetup, + GenerateInstructions(InstrBenchmark.NumRepetitions)); if (llvm::Error E = ObjectFilePath.takeError()) { InstrBenchmark.Error = llvm::toString(std::move(E)); return InstrBenchmark; @@ -126,22 +129,68 @@ BenchmarkRunner::generateConfigurations(unsigned Opcode) const { // TODO: Generate as many configurations as needed here. BenchmarkConfiguration Configuration; Configuration.Info = Prototype.Explanation; - for (InstructionInstance &II : Prototype.Snippet) - Configuration.Snippet.push_back(II.randomizeUnsetVariablesAndBuild()); + 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(llvm::ArrayRef<llvm::MCInst> Code) const { +BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + llvm::ArrayRef<llvm::MCInst> Code) const { int ResultFD = 0; llvm::SmallString<256> ResultPath; if (llvm::Error E = llvm::errorCodeToError(llvm::sys::fs::createTemporaryFile( "snippet", "o", ResultFD, ResultPath))) return std::move(E); llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); - assembleToStream(State.createTargetMachine(), Code, OFS); + assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), + Setup.RegsToDef, Code, OFS); return ResultPath.str(); } diff --git a/tools/llvm-exegesis/lib/BenchmarkRunner.h b/tools/llvm-exegesis/lib/BenchmarkRunner.h index f23a8a458fd..4e8ba502b9b 100644 --- a/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ b/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -39,7 +39,10 @@ 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. - std::vector<llvm::MCInst> SnippetSetup; + struct Setup { + std::vector<unsigned> RegsToDef; + }; + Setup SnippetSetup; // The sequence of instructions that are to be repeated. std::vector<llvm::MCInst> Snippet; @@ -71,6 +74,10 @@ public: run(unsigned Opcode, const InstructionFilter &Filter, unsigned NumRepetitions); + // Given a snippet, computes which registers the setup code needs to define. + std::vector<unsigned> + computeRegsToDef(const std::vector<InstructionInstance> &Snippet) const; + protected: const LLVMState &State; const llvm::MCInstrInfo &MCInstrInfo; @@ -96,9 +103,8 @@ private: const unsigned NumRepetitions) const = 0; llvm::Expected<std::string> - writeObjectFile(llvm::ArrayRef<llvm::MCInst> Code) const; - llvm::Expected<ExecutableFunction> - createExecutableFunction(llvm::ArrayRef<llvm::MCInst> Code) const; + writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + llvm::ArrayRef<llvm::MCInst> Code) const; }; } // namespace exegesis diff --git a/tools/llvm-exegesis/lib/LlvmState.cpp b/tools/llvm-exegesis/lib/LlvmState.cpp index efb5c831157..9b30d78b0c9 100644 --- a/tools/llvm-exegesis/lib/LlvmState.cpp +++ b/tools/llvm-exegesis/lib/LlvmState.cpp @@ -29,6 +29,7 @@ LLVMState::LLVMState(const std::string &Triple, const std::string &CpuName) { TargetMachine.reset(static_cast<llvm::LLVMTargetMachine *>( TheTarget->createTargetMachine(Triple, CpuName, /*Features*/ "", Options, llvm::Reloc::Model::Static))); + TheExegesisTarget = ExegesisTarget::lookup(TargetMachine->getTargetTriple()); } LLVMState::LLVMState() diff --git a/tools/llvm-exegesis/lib/LlvmState.h b/tools/llvm-exegesis/lib/LlvmState.h index 5c09014a814..18c92b835ca 100644 --- a/tools/llvm-exegesis/lib/LlvmState.h +++ b/tools/llvm-exegesis/lib/LlvmState.h @@ -15,6 +15,7 @@ #ifndef LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H #define LLVM_TOOLS_LLVM_EXEGESIS_LLVMSTATE_H +#include "Target.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstrInfo.h" diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.cpp b/tools/llvm-exegesis/lib/MCInstrDescView.cpp index 5678fcea94d..fc0d5b327b8 100644 --- a/tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ b/tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -99,11 +99,22 @@ llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) { return VariableValues[Var.Index]; } +const llvm::MCOperand & +InstructionInstance::getValueFor(const Variable &Var) const { + return VariableValues[Var.Index]; +} + llvm::MCOperand &InstructionInstance::getValueFor(const Operand &Op) { assert(Op.VariableIndex >= 0); return getValueFor(Instr.Variables[Op.VariableIndex]); } +const llvm::MCOperand & +InstructionInstance::getValueFor(const Operand &Op) const { + assert(Op.VariableIndex >= 0); + return getValueFor(Instr.Variables[Op.VariableIndex]); +} + // forward declaration. static void randomize(const Instruction &Instr, const Variable &Var, llvm::MCOperand &AssignedValue); @@ -118,12 +129,15 @@ bool InstructionInstance::hasImmediateVariables() const { }); } -llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() { +void InstructionInstance::randomizeUnsetVariables() { for (const Variable &Var : Instr.Variables) { llvm::MCOperand &AssignedValue = getValueFor(Var); if (!AssignedValue.isValid()) randomize(Instr, Var, AssignedValue); } +} + +llvm::MCInst InstructionInstance::build() const { llvm::MCInst Result; Result.setOpcode(Instr.Description->Opcode); for (const auto &Op : Instr.Operands) diff --git a/tools/llvm-exegesis/lib/MCInstrDescView.h b/tools/llvm-exegesis/lib/MCInstrDescView.h index 893b786762f..c36582ac72d 100644 --- a/tools/llvm-exegesis/lib/MCInstrDescView.h +++ b/tools/llvm-exegesis/lib/MCInstrDescView.h @@ -93,12 +93,17 @@ struct InstructionInstance { unsigned getOpcode() const; llvm::MCOperand &getValueFor(const Variable &Var); + const llvm::MCOperand &getValueFor(const Variable &Var) const; llvm::MCOperand &getValueFor(const Operand &Op); + const llvm::MCOperand &getValueFor(const Operand &Op) const; bool hasImmediateVariables() const; - // Assigns a Random Value to all Variables that are still Invalid and returns - // the instance as an llvm::MCInst. - llvm::MCInst randomizeUnsetVariablesAndBuild(); + // Assigns a Random Value to all Variables that are still Invalid. + void randomizeUnsetVariables(); + + // Returns the instance as an llvm::MCInst. The InstructionInstance must be + // fully allocated (no invalid variables). + llvm::MCInst build() const; Instruction Instr; llvm::SmallVector<llvm::MCOperand, 4> VariableValues; diff --git a/tools/llvm-exegesis/lib/Target.h b/tools/llvm-exegesis/lib/Target.h index 73426797ab6..1d35824daed 100644 --- a/tools/llvm-exegesis/lib/Target.h +++ b/tools/llvm-exegesis/lib/Target.h @@ -20,6 +20,8 @@ #include "llvm/ADT/Triple.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/LegacyPassManager.h" +#include "llvm/MC/MCInst.h" +#include "llvm/MC/MCRegisterInfo.h" namespace exegesis { @@ -28,6 +30,11 @@ public: // Targets can use this to add target-specific passes in assembleToStream(); virtual void addTargetSpecificPasses(llvm::PassManagerBase &PM) const {} + // Generates code to move a constant into a the given register. + virtual std::vector<llvm::MCInst> setRegToConstant(unsigned Reg) const { + return {}; + } + // Returns the ExegesisTarget for the given triple or nullptr if the target // does not exist. static const ExegesisTarget *lookup(llvm::Triple TT); diff --git a/tools/llvm-exegesis/lib/X86/Target.cpp b/tools/llvm-exegesis/lib/X86/Target.cpp index 26a54591fb0..591edd2afae 100644 --- a/tools/llvm-exegesis/lib/X86/Target.cpp +++ b/tools/llvm-exegesis/lib/X86/Target.cpp @@ -8,7 +8,10 @@ //===----------------------------------------------------------------------===// #include "../Target.h" +#include "MCTargetDesc/X86MCTargetDesc.h" #include "X86.h" +#include "X86RegisterInfo.h" +#include "llvm/MC/MCInstBuilder.h" namespace exegesis { @@ -22,9 +25,88 @@ class ExegesisX86Target : public ExegesisTarget { // PM.add(llvm::createX86FloatingPointStackifierPass()); } + std::vector<llvm::MCInst> + setRegToConstant(const unsigned Reg) const override { + // FIXME: Handle FP stack: + // llvm::X86::RFP32RegClass + // llvm::X86::RFP64RegClass + // llvm::X86::RFP80RegClass + 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)}; + } + if (llvm::X86::VR128XRegClass.contains(Reg)) { + return setVectorRegToConstant(Reg, 16, llvm::X86::VMOVDQUrm); + } + if (llvm::X86::VR256XRegClass.contains(Reg)) { + return setVectorRegToConstant(Reg, 32, llvm::X86::VMOVDQUYrm); + } + if (llvm::X86::VR512RegClass.contains(Reg)) { + return setVectorRegToConstant(Reg, 64, llvm::X86::VMOVDQU64Zrm); + } + return {}; + } + bool matchesArch(llvm::Triple::ArchType Arch) const override { return Arch == llvm::Triple::x86_64 || Arch == llvm::Triple::x86; } + +private: + // setRegToConstant() specialized for a vector register of size + // `RegSizeBytes`. `RMOpcode` is the opcode used to do a memory -> vector + // register load. + static std::vector<llvm::MCInst> + setVectorRegToConstant(const unsigned Reg, const unsigned RegSizeBytes, + const unsigned RMOpcode) { + // There is no instruction to directly set XMM, go through memory. + // Since vector values can be interpreted as integers of various sizes (8 + // to 64 bits) as well as floats and double, so we chose an immediate + // value that has set bits for all byte values and is a normal float/ + // double. 0x40404040 is ~32.5 when interpreted as a double and ~3.0f when + // interpreted as a float. + constexpr const uint64_t kImmValue = 0x40404040ull; + std::vector<llvm::MCInst> Result; + // Allocate scratch memory on the stack. + Result.push_back(llvm::MCInstBuilder(llvm::X86::SUB64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(RegSizeBytes)); + // Fill scratch memory. + for (unsigned Disp = 0; Disp < RegSizeBytes; Disp += 4) { + Result.push_back(llvm::MCInstBuilder(llvm::X86::MOV32mi) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(Disp) // Disp + .addReg(0) // Segment + // Immediate. + .addImm(kImmValue)); + } + // Load Reg from scratch memory. + Result.push_back(llvm::MCInstBuilder(RMOpcode) + .addReg(Reg) + // Address = ESP + .addReg(llvm::X86::RSP) // BaseReg + .addImm(1) // ScaleAmt + .addReg(0) // IndexReg + .addImm(0) // Disp + .addReg(0)); // Segment + // Release scratch memory. + Result.push_back(llvm::MCInstBuilder(llvm::X86::ADD64ri8) + .addReg(llvm::X86::RSP) + .addReg(llvm::X86::RSP) + .addImm(RegSizeBytes)); + return Result; + } }; } // namespace |