Index: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h +++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.h @@ -37,18 +37,17 @@ }; // 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 LiveIns; // The registers that are live on entry. - std::vector RegsToDef; - }; - Setup SnippetSetup; - +struct BenchmarkCode { // The sequence of instructions that are to be repeated. - std::vector Snippet; + std::vector Instructions; + + // Before the code is executed some instructions are added to setup the + // registers initial values. + std::vector RegsToDef; + + // We also need to provide the registers that are live on entry for the + // assembler to generate proper prologue/epilogue. + std::vector LiveIns; // Informations about how this configuration was built. std::string Info; @@ -90,34 +89,34 @@ const LLVMState &State; const RegisterAliasingTrackerCache RATC; - // Generates a single instruction prototype that has a self-dependency. - llvm::Expected - generateSelfAliasingPrototype(const Instruction &Instr) const; - // Generates a single instruction prototype without assignment constraints. - llvm::Expected - generateUnconstrainedPrototype(const Instruction &Instr, - llvm::StringRef Msg) const; + // Generates a single code template that has a self-dependency. + llvm::Expected + generateSelfAliasingCodeTemplate(const Instruction &Instr) const; + // Generates a single code template without assignment constraints. + llvm::Expected + generateUnconstrainedCodeTemplate(const Instruction &Instr, + llvm::StringRef Msg) const; private: // API to be implemented by subclasses. - virtual llvm::Expected - generatePrototype(unsigned Opcode) const = 0; + virtual llvm::Expected + generateCodeTemplate(unsigned Opcode) const = 0; virtual std::vector runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch, const unsigned NumRepetitions) const = 0; // Internal helpers. - InstructionBenchmark runOne(const BenchmarkConfiguration &Configuration, - unsigned Opcode, unsigned NumRepetitions) const; + InstructionBenchmark runConfiguration(const BenchmarkCode &Configuration, + unsigned Opcode, + unsigned NumRepetitions) const; - // Calls generatePrototype and expands the SnippetPrototype into one or more - // BenchmarkConfiguration. - llvm::Expected> + // Calls generateCodeTemplate and expands it into one or more BenchmarkCode. + llvm::Expected> generateConfigurations(unsigned Opcode) const; llvm::Expected - writeObjectFile(const BenchmarkConfiguration::Setup &Setup, + writeObjectFile(const BenchmarkCode &Configuration, llvm::ArrayRef Code) const; const InstructionBenchmark::ModeE Mode; Index: llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/BenchmarkRunner.cpp @@ -47,55 +47,54 @@ return llvm::make_error( "Unsupported opcode: isCall/isReturn"); - llvm::Expected> ConfigurationOrError = + llvm::Expected> ConfigurationOrError = generateConfigurations(Opcode); if (llvm::Error E = ConfigurationOrError.takeError()) return std::move(E); std::vector InstrBenchmarks; - for (const BenchmarkConfiguration &Conf : ConfigurationOrError.get()) - InstrBenchmarks.push_back(runOne(Conf, Opcode, NumRepetitions)); + for (const BenchmarkCode &Conf : ConfigurationOrError.get()) + InstrBenchmarks.push_back(runConfiguration(Conf, Opcode, NumRepetitions)); return InstrBenchmarks; } +// Repeat the snippet until there are at least NumInstructions in the resulting +// code. +static std::vector +GenerateInstructions(const BenchmarkCode &BC, const int MinInstructions) { + std::vector Code = BC.Instructions; + for (int I = 0; I < MinInstructions; ++I) + Code.push_back(BC.Instructions[I % BC.Instructions.size()]); + return Code; +} + InstructionBenchmark -BenchmarkRunner::runOne(const BenchmarkConfiguration &Configuration, - unsigned Opcode, unsigned NumRepetitions) const { +BenchmarkRunner::runConfiguration(const BenchmarkCode &BC, unsigned Opcode, + unsigned NumRepetitions) const { InstructionBenchmark InstrBenchmark; InstrBenchmark.Mode = Mode; InstrBenchmark.CpuName = State.getTargetMachine().getTargetCPU(); InstrBenchmark.LLVMTriple = State.getTargetMachine().getTargetTriple().normalize(); InstrBenchmark.NumRepetitions = NumRepetitions; - InstrBenchmark.Info = Configuration.Info; + InstrBenchmark.Info = BC.Info; - const std::vector &Snippet = Configuration.Snippet; - if (Snippet.empty()) { + const std::vector &Instructions = BC.Instructions; + if (Instructions.empty()) { InstrBenchmark.Error = "Empty snippet"; return InstrBenchmark; } - InstrBenchmark.Key.Instructions = Snippet; - - // Repeat the snippet until there are at least NumInstructions in the - // resulting code. The snippet is always repeated at least once. - const auto GenerateInstructions = [&Configuration]( - const int MinInstructions) { - std::vector 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; // 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,9 +107,8 @@ // 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; @@ -124,37 +122,41 @@ return InstrBenchmark; } -llvm::Expected> +llvm::Expected> 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 (InstructionBuilder &IB : Prototype.Snippet) { - IB.randomizeUnsetVariables( - Prototype.ScratchSpaceReg - ? RATC.getRegister(Prototype.ScratchSpaceReg).aliasedBits() - : RATC.emptyRegisters()); - Configuration.Snippet.push_back(IB.build()); + if (auto E = generateCodeTemplate(Opcode)) { + CodeTemplate &CT = E.get(); + std::vector Output; + // TODO: Generate as many BenchmarkCode as needed. + { + BenchmarkCode BC; + BC.Info = CT.Info; + for (InstructionBuilder &IB : CT.Instructions) { + IB.randomizeUnsetVariables( + CT.ScratchSpacePointerInReg + ? RATC.getRegister(CT.ScratchSpacePointerInReg).aliasedBits() + : RATC.emptyRegisters()); + BC.Instructions.push_back(IB.build()); + } + if (CT.ScratchSpacePointerInReg) + BC.LiveIns.push_back(CT.ScratchSpacePointerInReg); + BC.RegsToDef = computeRegsToDef(CT.Instructions); + Output.push_back(std::move(BC)); } - if (Prototype.ScratchSpaceReg) - Configuration.SnippetSetup.LiveIns.push_back(Prototype.ScratchSpaceReg); - Configuration.SnippetSetup.RegsToDef = computeRegsToDef(Prototype.Snippet); - return std::vector{Configuration}; + return Output; } else return E.takeError(); } std::vector BenchmarkRunner::computeRegsToDef( - const std::vector &Snippet) const { + const std::vector &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 = RATC.emptyRegisters(); std::vector RegsToDef; - for (const InstructionBuilder &IB : Snippet) { + for (const InstructionBuilder &IB : Instructions) { // Returns the register that this Operand sets or uses, or 0 if this is not // a register. const auto GetOpReg = [&IB](const Operand &Op) -> unsigned { @@ -189,7 +191,7 @@ } llvm::Expected -BenchmarkRunner::writeObjectFile(const BenchmarkConfiguration::Setup &Setup, +BenchmarkRunner::writeObjectFile(const BenchmarkCode &BC, llvm::ArrayRef Code) const { int ResultFD = 0; llvm::SmallString<256> ResultPath; @@ -198,38 +200,36 @@ return std::move(E); llvm::raw_fd_ostream OFS(ResultFD, true /*ShouldClose*/); assembleToStream(State.getExegesisTarget(), State.createTargetMachine(), - Setup.LiveIns, Setup.RegsToDef, Code, OFS); + BC.LiveIns, BC.RegsToDef, Code, OFS); return ResultPath.str(); } -llvm::Expected -BenchmarkRunner::generateSelfAliasingPrototype(const Instruction &Instr) const { +llvm::Expected BenchmarkRunner::generateSelfAliasingCodeTemplate( + const Instruction &Instr) const { const AliasingConfigurations SelfAliasing(Instr, Instr); if (SelfAliasing.empty()) { return llvm::make_error("empty self aliasing"); } - SnippetPrototype Prototype; + CodeTemplate CT; InstructionBuilder IB(Instr); if (SelfAliasing.hasImplicitAliasing()) { - Prototype.Explanation = "implicit Self cycles, picking random values."; + CT.Info = "implicit Self cycles, picking random values."; } else { - Prototype.Explanation = - "explicit self cycles, selecting one aliasing Conf."; + 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 IB in the following call. setRandomAliasing(SelfAliasing, IB, IB); } - Prototype.Snippet.push_back(std::move(IB)); - return std::move(Prototype); + CT.Instructions.push_back(std::move(IB)); + return std::move(CT); } -llvm::Expected -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); +llvm::Expected +BenchmarkRunner::generateUnconstrainedCodeTemplate(const Instruction &Instr, + llvm::StringRef Msg) const { + CodeTemplate CT; + CT.Info = llvm::formatv("{0}, repeating an unconstrained assignment", Msg); + CT.Instructions.emplace_back(Instr); + return std::move(CT); } } // namespace exegesis Index: llvm/trunk/tools/llvm-exegesis/lib/Latency.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Latency.h +++ llvm/trunk/tools/llvm-exegesis/lib/Latency.h @@ -26,14 +26,14 @@ : BenchmarkRunner(State, InstructionBenchmark::Latency) {} ~LatencyBenchmarkRunner() override; - llvm::Expected - generatePrototype(unsigned Opcode) const override; + llvm::Expected + generateCodeTemplate(unsigned Opcode) const override; private: llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const; - llvm::Expected generateTwoInstructionPrototype( - const Instruction &Instr) const; + llvm::Expected + generateTwoInstructionPrototype(const Instruction &Instr) const; std::vector runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch, Index: llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/Latency.cpp @@ -42,7 +42,7 @@ return llvm::Error::success(); } -llvm::Expected +llvm::Expected LatencyBenchmarkRunner::generateTwoInstructionPrototype( const Instruction &Instr) const { std::vector Opcodes; @@ -68,28 +68,27 @@ setRandomAliasing(Forward, ThisIB, OtherIB); if (!Back.hasImplicitAliasing()) setRandomAliasing(Back, OtherIB, ThisIB); - SnippetPrototype Prototype; - Prototype.Explanation = - llvm::formatv("creating cycle through {0}.", - State.getInstrInfo().getName(OtherOpcode)); - Prototype.Snippet.push_back(std::move(ThisIB)); - Prototype.Snippet.push_back(std::move(OtherIB)); - return std::move(Prototype); + CodeTemplate CT; + CT.Info = llvm::formatv("creating cycle through {0}.", + State.getInstrInfo().getName(OtherOpcode)); + CT.Instructions.push_back(std::move(ThisIB)); + CT.Instructions.push_back(std::move(OtherIB)); + return std::move(CT); } return llvm::make_error( "Infeasible : Didn't find any scheme to make the instruction serial"); } -llvm::Expected -LatencyBenchmarkRunner::generatePrototype(unsigned Opcode) const { +llvm::Expected +LatencyBenchmarkRunner::generateCodeTemplate(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; + if (auto CT = generateSelfAliasingCodeTemplate(Instr)) + return CT; else - llvm::consumeError(SelfAliasingPrototype.takeError()); + llvm::consumeError(CT.takeError()); // No self aliasing, trying to create a dependency through another opcode. return generateTwoInstructionPrototype(Instr); } Index: llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h +++ llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.h @@ -86,10 +86,10 @@ struct InstructionBuilder { InstructionBuilder(const Instruction &Instr); - InstructionBuilder(const InstructionBuilder &); - InstructionBuilder &operator=(const InstructionBuilder &); - InstructionBuilder(InstructionBuilder &&); - InstructionBuilder &operator=(InstructionBuilder &&); + InstructionBuilder(const InstructionBuilder &); // default + InstructionBuilder &operator=(const InstructionBuilder &); // default + InstructionBuilder(InstructionBuilder &&); // default + InstructionBuilder &operator=(InstructionBuilder &&); // default unsigned getOpcode() const; llvm::MCOperand &getValueFor(const Variable &Var); @@ -102,34 +102,34 @@ // Do not use any of the registers in `ForbiddenRegs`. void randomizeUnsetVariables(const llvm::BitVector &ForbiddenRegs); - // Returns the instance as an llvm::MCInst. The InstructionBuilder must be - // fully allocated (no invalid variables). + // Builds an llvm::MCInst from this InstructionBuilder setting its operands to + // the corresponding variable values. + // Precondition: All VariableValues must be set. llvm::MCInst build() const; Instruction Instr; llvm::SmallVector VariableValues; }; -// A prototype is a set of InstructionInstances with an explanation of how -// it's been built. The prototype can then be randomized to exercice several -// immediate values. It is also used to gather the used registers and define -// their initial values. -struct SnippetPrototype { - SnippetPrototype() = default; - - // No copy. - SnippetPrototype(const SnippetPrototype &) = delete; - SnippetPrototype &operator=(const SnippetPrototype &) = delete; - - // Moving is OK. - SnippetPrototype(SnippetPrototype &&); - SnippetPrototype &operator=(SnippetPrototype &&); - - std::string Explanation; - // If the prototype uses the provided scratch memory, the register in which +// A CodeTemplate is a set of InstructionBuilders that may not be fully +// specified (i.e. some variables are not yet set). +// This allows the BenchmarkRunner 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; + + // Some information about how this template has been created. + std::string Info; + // The list of the instructions for this template. + std::vector 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 ScratchSpaceReg = 0; - std::vector Snippet; + unsigned ScratchSpacePointerInReg = 0; }; // Represents the assignment of a Register to an Operand. Index: llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -159,9 +159,9 @@ return Result; } -SnippetPrototype::SnippetPrototype(SnippetPrototype &&) = default; +CodeTemplate::CodeTemplate(CodeTemplate &&) = default; -SnippetPrototype &SnippetPrototype::operator=(SnippetPrototype &&) = default; +CodeTemplate &CodeTemplate::operator=(CodeTemplate &&) = default; bool RegisterOperandAssignment:: operator==(const RegisterOperandAssignment &Other) const { Index: llvm/trunk/tools/llvm-exegesis/lib/Uops.h =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Uops.h +++ llvm/trunk/tools/llvm-exegesis/lib/Uops.h @@ -25,8 +25,8 @@ : BenchmarkRunner(State, InstructionBenchmark::Uops) {} ~UopsBenchmarkRunner() override; - llvm::Expected - generatePrototype(unsigned Opcode) const override; + llvm::Expected + generateCodeTemplate(unsigned Opcode) const override; static constexpr const size_t kMinNumDifferentAddresses = 6; Index: llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/Uops.cpp @@ -125,47 +125,47 @@ UopsBenchmarkRunner::~UopsBenchmarkRunner() = default; void UopsBenchmarkRunner::instantiateMemoryOperands( - const unsigned ScratchSpaceReg, - std::vector &Snippet) const { - if (ScratchSpaceReg == 0) + const unsigned ScratchSpacePointerInReg, + std::vector &Instructions) const { + if (ScratchSpacePointerInReg == 0) return; // no memory operands. const auto &ET = State.getExegesisTarget(); const unsigned MemStep = ET.getMaxMemoryAccessSize(); - const size_t OriginalSnippetSize = Snippet.size(); + const size_t OriginalInstructionsSize = Instructions.size(); size_t I = 0; - for (InstructionBuilder &IB : Snippet) { - ET.fillMemoryOperands(IB, ScratchSpaceReg, I * MemStep); + for (InstructionBuilder &IB : Instructions) { + ET.fillMemoryOperands(IB, ScratchSpacePointerInReg, I * MemStep); ++I; } - while (Snippet.size() < kMinNumDifferentAddresses) { - InstructionBuilder IB = Snippet[I % OriginalSnippetSize]; - ET.fillMemoryOperands(IB, ScratchSpaceReg, I * MemStep); + while (Instructions.size() < kMinNumDifferentAddresses) { + InstructionBuilder IB = Instructions[I % OriginalInstructionsSize]; + ET.fillMemoryOperands(IB, ScratchSpacePointerInReg, I * MemStep); ++I; - Snippet.push_back(std::move(IB)); + Instructions.push_back(std::move(IB)); } assert(I * MemStep < ScratchSpace::kSize && "not enough scratch space"); } -llvm::Expected -UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const { +llvm::Expected +UopsBenchmarkRunner::generateCodeTemplate(unsigned Opcode) const { const auto &InstrDesc = State.getInstrInfo().get(Opcode); if (auto E = isInfeasible(InstrDesc)) return std::move(E); const Instruction Instr(InstrDesc, RATC); const auto &ET = State.getExegesisTarget(); - SnippetPrototype Prototype; + CodeTemplate CT; const llvm::BitVector *ScratchSpaceAliasedRegs = nullptr; if (Instr.hasMemoryOperands()) { - Prototype.ScratchSpaceReg = + CT.ScratchSpacePointerInReg = ET.getScratchMemoryRegister(State.getTargetMachine().getTargetTriple()); - if (Prototype.ScratchSpaceReg == 0) + if (CT.ScratchSpacePointerInReg == 0) return llvm::make_error( "Infeasible : target does not support memory instructions"); ScratchSpaceAliasedRegs = - &RATC.getRegister(Prototype.ScratchSpaceReg).aliasedBits(); - // If the instruction implicitly writes to ScratchSpaceReg , abort. + &RATC.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.ImplicitReg && @@ -178,16 +178,16 @@ const AliasingConfigurations SelfAliasing(Instr, Instr); InstructionBuilder IB(Instr); if (SelfAliasing.empty()) { - Prototype.Explanation = "instruction is parallel, repeating a random one."; - Prototype.Snippet.push_back(std::move(IB)); - instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet); - return std::move(Prototype); + CT.Info = "instruction is parallel, repeating a random one."; + CT.Instructions.push_back(std::move(IB)); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return std::move(CT); } if (SelfAliasing.hasImplicitAliasing()) { - Prototype.Explanation = "instruction is serial, repeating a random one."; - Prototype.Snippet.push_back(std::move(IB)); - instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet); - return std::move(Prototype); + CT.Info = "instruction is serial, repeating a random one."; + CT.Instructions.push_back(std::move(IB)); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return std::move(CT); } const auto TiedVariables = getTiedVariables(Instr); if (!TiedVariables.empty()) { @@ -200,17 +200,16 @@ assert(!Var->TiedOperands.empty()); const Operand &Op = Instr.Operands[Var->TiedOperands.front()]; assert(Op.Tracker); - Prototype.Explanation = - "instruction has tied variables using static renaming."; + CT.Info = "instruction has tied variables using static renaming."; for (const llvm::MCPhysReg Reg : Op.Tracker->sourceBits().set_bits()) { if (ScratchSpaceAliasedRegs && ScratchSpaceAliasedRegs->test(Reg)) continue; // Do not use the scratch memory address register. InstructionBuilder TmpIB = IB; TmpIB.getValueFor(*Var) = llvm::MCOperand::createReg(Reg); - Prototype.Snippet.push_back(std::move(TmpIB)); + CT.Instructions.push_back(std::move(TmpIB)); } - instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet); - return std::move(Prototype); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return std::move(CT); } // No tied variables, we pick random values for defs. llvm::BitVector Defs(State.getRegInfo().getNumRegs()); @@ -242,11 +241,11 @@ IB.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); } } - Prototype.Explanation = + CT.Info = "instruction has no tied variables picking Uses different from defs"; - Prototype.Snippet.push_back(std::move(IB)); - instantiateMemoryOperands(Prototype.ScratchSpaceReg, Prototype.Snippet); - return std::move(Prototype); + CT.Instructions.push_back(std::move(IB)); + instantiateMemoryOperands(CT.ScratchSpacePointerInReg, CT.Instructions); + return std::move(CT); } std::vector Index: llvm/trunk/tools/llvm-exegesis/lib/X86/Target.cpp =================================================================== --- llvm/trunk/tools/llvm-exegesis/lib/X86/Target.cpp +++ llvm/trunk/tools/llvm-exegesis/lib/X86/Target.cpp @@ -25,8 +25,8 @@ template class X86BenchmarkRunner : public Impl { using Impl::Impl; - llvm::Expected - generatePrototype(unsigned Opcode) const override { + llvm::Expected + generateCodeTemplate(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); @@ -54,7 +54,7 @@ // - `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); + return this->generateSelfAliasingCodeTemplate(Instr); } case llvm::X86II::CompareFP: return Impl::handleCompareFP(Instr); @@ -67,7 +67,7 @@ } // Fallback to generic implementation. - return Impl::Base::generatePrototype(Opcode); + return Impl::Base::generateCodeTemplate(Opcode); } }; @@ -75,12 +75,10 @@ protected: using Base = LatencyBenchmarkRunner; using Base::Base; - llvm::Expected - handleCompareFP(const Instruction &Instr) const { + llvm::Expected handleCompareFP(const Instruction &Instr) const { return llvm::make_error("Unsupported x87 CompareFP"); } - llvm::Expected - handleCondMovFP(const Instruction &Instr) const { + llvm::Expected handleCondMovFP(const Instruction &Instr) const { return llvm::make_error("Unsupported x87 CondMovFP"); } }; @@ -91,14 +89,12 @@ 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 - handleCompareFP(const Instruction &Instr) const { - return generateUnconstrainedPrototype( + llvm::Expected handleCompareFP(const Instruction &Instr) const { + return generateUnconstrainedCodeTemplate( Instr, "instruction does not grow/shrink the FP stack"); } - llvm::Expected - handleCondMovFP(const Instruction &Instr) const { - return generateUnconstrainedPrototype( + llvm::Expected handleCondMovFP(const Instruction &Instr) const { + return generateUnconstrainedCodeTemplate( Instr, "instruction does not grow/shrink the FP stack"); } }; Index: llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp =================================================================== --- llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ llvm/trunk/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -57,22 +57,21 @@ protected: SnippetGeneratorTest() : Runner(State) {} - SnippetPrototype checkAndGetConfigurations(unsigned Opcode) { + CodeTemplate checkAndGetCodeTemplate(unsigned Opcode) { randomGenerator().seed(0); // Initialize seed. - auto ProtoOrError = Runner.generatePrototype(Opcode); - EXPECT_FALSE(ProtoOrError.takeError()); // Valid configuration. - return std::move(ProtoOrError.get()); + auto CodeTemplateOrError = Runner.generateCodeTemplate(Opcode); + EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration. + return std::move(CodeTemplateOrError.get()); } BenchmarkRunner Runner; }; -using LatencySnippetGeneratorTest = - SnippetGeneratorTest; +using LatencyBenchmarkRunnerTest = SnippetGeneratorTest; -using UopsSnippetGeneratorTest = SnippetGeneratorTest; +using UopsBenchmarkRunnerTest = SnippetGeneratorTest; -TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) { +TEST_F(LatencyBenchmarkRunnerTest, ImplicitSelfDependency) { // ADC16i16 self alias because of implicit use and def. // explicit use 0 : imm @@ -85,16 +84,16 @@ EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS); EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX); EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS); - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("implicit")); - ASSERT_THAT(Proto.Snippet, SizeIs(1)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("implicit")); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(1)); // Imm. EXPECT_THAT(IB.VariableValues[0], IsInvalid()) << "Immediate is not set"; } -TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) { +TEST_F(LatencyBenchmarkRunnerTest, ExplicitSelfDependency) { // ADD16ri self alias because Op0 and Op1 are tied together. // explicit def 0 : reg RegClass=GR16 @@ -103,78 +102,78 @@ // implicit def : EFLAGS const unsigned Opcode = llvm::X86::ADD16ri; EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS); - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("explicit")); - ASSERT_THAT(Proto.Snippet, SizeIs(1)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("explicit")); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(2)); EXPECT_THAT(IB.VariableValues[0], IsReg()) << "Operand 0 and 1"; EXPECT_THAT(IB.VariableValues[1], IsInvalid()) << "Operand 2 is not set"; } -TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) { +TEST_F(LatencyBenchmarkRunnerTest, DependencyThroughOtherOpcode) { // CMP64rr // explicit use 0 : reg RegClass=GR64 // explicit use 1 : reg RegClass=GR64 // implicit def : EFLAGS const unsigned Opcode = llvm::X86::CMP64rr; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through")); - ASSERT_THAT(Proto.Snippet, SizeIs(2)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("cycle through")); + ASSERT_THAT(CT.Instructions, SizeIs(2)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(2)); EXPECT_THAT(IB.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()), ElementsAre(IsInvalid(), IsReg()))); - EXPECT_THAT(Proto.Snippet[1].getOpcode(), Not(Opcode)); + EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode)); // TODO: check that the two instructions alias each other. } -TEST_F(LatencySnippetGeneratorTest, LAHF) { +TEST_F(LatencyBenchmarkRunnerTest, LAHF) { const unsigned Opcode = llvm::X86::LAHF; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through")); - ASSERT_THAT(Proto.Snippet, SizeIs(2)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("cycle through")); + ASSERT_THAT(CT.Instructions, SizeIs(2)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(0)); } -TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) { +TEST_F(UopsBenchmarkRunnerTest, ParallelInstruction) { // BNDCL32rr is parallel no matter what. // explicit use 0 : reg RegClass=BNDR // explicit use 1 : reg RegClass=GR32 const unsigned Opcode = llvm::X86::BNDCL32rr; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("parallel")); - ASSERT_THAT(Proto.Snippet, SizeIs(1)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("parallel")); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(2)); EXPECT_THAT(IB.VariableValues[0], IsInvalid()); EXPECT_THAT(IB.VariableValues[1], IsInvalid()); } -TEST_F(UopsSnippetGeneratorTest, SerialInstruction) { +TEST_F(UopsBenchmarkRunnerTest, SerialInstruction) { // CDQ is serial no matter what. // implicit def : EAX // implicit def : EDX // implicit use : EAX const unsigned Opcode = llvm::X86::CDQ; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("serial")); - ASSERT_THAT(Proto.Snippet, SizeIs(1)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("serial")); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(0)); } -TEST_F(UopsSnippetGeneratorTest, StaticRenaming) { +TEST_F(UopsBenchmarkRunnerTest, StaticRenaming) { // CMOVA32rr has tied variables, we enumarate the possible values to execute // as many in parallel as possible. @@ -183,12 +182,12 @@ // explicit use 2 : reg RegClass=GR32 // implicit use : EFLAGS const unsigned Opcode = llvm::X86::CMOVA32rr; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("static renaming")); + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("static renaming")); constexpr const unsigned kInstructionCount = 15; - ASSERT_THAT(Proto.Snippet, SizeIs(kInstructionCount)); + ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount)); std::unordered_set AllDefRegisters; - for (const auto &IB : Proto.Snippet) { + for (const auto &IB : CT.Instructions) { ASSERT_THAT(IB.VariableValues, SizeIs(2)); AllDefRegisters.insert(IB.VariableValues[0].getReg()); } @@ -196,7 +195,7 @@ << "Each instruction writes to a different register"; } -TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) { +TEST_F(UopsBenchmarkRunnerTest, NoTiedVariables) { // CMOV_GR32 has no tied variables, we make sure def and use are different // from each other. @@ -206,10 +205,10 @@ // explicit use 3 : imm // implicit use : EFLAGS const unsigned Opcode = llvm::X86::CMOV_GR32; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables")); - ASSERT_THAT(Proto.Snippet, SizeIs(1)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("no tied variables")); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(4)); EXPECT_THAT(IB.VariableValues[0].getReg(), Not(IB.VariableValues[1].getReg())) @@ -219,14 +218,14 @@ EXPECT_THAT(IB.VariableValues[3], IsInvalid()); } -TEST_F(UopsSnippetGeneratorTest, MemoryUse) { +TEST_F(UopsBenchmarkRunnerTest, MemoryUse) { // Mov32rm reads from memory. const unsigned Opcode = llvm::X86::MOV32rm; - const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); - EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables")); - ASSERT_THAT(Proto.Snippet, + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Info, HasSubstr("no tied variables")); + ASSERT_THAT(CT.Instructions, SizeIs(UopsBenchmarkRunner::kMinNumDifferentAddresses)); - const InstructionBuilder &IB = Proto.Snippet[0]; + const InstructionBuilder &IB = CT.Instructions[0]; EXPECT_THAT(IB.getOpcode(), Opcode); ASSERT_THAT(IB.VariableValues, SizeIs(6)); EXPECT_EQ(IB.VariableValues[2].getImm(), 1); @@ -235,10 +234,10 @@ EXPECT_EQ(IB.VariableValues[5].getReg(), 0u); } -TEST_F(UopsSnippetGeneratorTest, MemoryUse_Movsb) { +TEST_F(UopsBenchmarkRunnerTest, MemoryUse_Movsb) { // MOVSB writes to scratch memory register. const unsigned Opcode = llvm::X86::MOVSB; - auto Error = Runner.generatePrototype(Opcode).takeError(); + auto Error = Runner.generateCodeTemplate(Opcode).takeError(); EXPECT_TRUE((bool)Error); llvm::consumeError(std::move(Error)); } @@ -253,8 +252,8 @@ } private: - llvm::Expected - generatePrototype(unsigned Opcode) const override { + llvm::Expected + generateCodeTemplate(unsigned Opcode) const override { return llvm::make_error("not implemented", llvm::inconvertibleErrorCode()); }