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 @@ -19,6 +19,7 @@ #include "Assembler.h" #include "BenchmarkResult.h" #include "LlvmState.h" +#include "MCInstrDescView.h" #include "RegisterAliasing.h" #include "llvm/MC/MCInst.h" #include "llvm/Support/Error.h" @@ -80,10 +81,15 @@ InstructionBenchmark runOne(const BenchmarkConfiguration &Configuration, unsigned Opcode, unsigned NumRepetitions) const; + // Calls generatePrototype and expands the SnippetPrototype into one or more + // BenchmarkConfiguration. + llvm::Expected> + generateConfigurations(unsigned Opcode) const; + virtual InstructionBenchmark::ModeE getMode() const = 0; - virtual llvm::Expected> - createConfigurations(unsigned Opcode) const = 0; + virtual llvm::Expected + generatePrototype(unsigned Opcode) const = 0; virtual std::vector runMeasurements(const ExecutableFunction &EF, 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,7 +47,7 @@ return std::move(E); llvm::Expected> ConfigurationOrError = - createConfigurations(Opcode); + generateConfigurations(Opcode); if (llvm::Error E = ConfigurationOrError.takeError()) return std::move(E); @@ -113,6 +113,20 @@ return InstrBenchmark; } +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 (InstructionInstance &II : Prototype.Snippet) + Configuration.Snippet.push_back(II.randomizeUnsetVariablesAndBuild()); + return std::vector{Configuration}; + } else + return E.takeError(); +} + llvm::Expected BenchmarkRunner::writeObjectFile(llvm::ArrayRef Code) const { int ResultFD = 0; 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 @@ -25,25 +25,22 @@ using BenchmarkRunner::BenchmarkRunner; ~LatencyBenchmarkRunner() override; - llvm::Expected - generateConfiguration(unsigned Opcode) const; + llvm::Expected + generatePrototype(unsigned Opcode) const override; private: llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const; - llvm::Expected generateSelfAliasingConfiguration( + llvm::Expected generateSelfAliasingPrototype( const Instruction &Instr, const AliasingConfigurations &SelfAliasing) const; - llvm::Expected generateTwoInstructionConfiguration( + llvm::Expected generateTwoInstructionPrototype( const Instruction &Instr, const AliasingConfigurations &SelfAliasing) const; InstructionBenchmark::ModeE getMode() const override; - llvm::Expected> - createConfigurations(unsigned OpcodeIndex) const override; - std::vector runMeasurements(const ExecutableFunction &EF, const unsigned NumRepetitions) const override; 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 @@ -16,6 +16,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstBuilder.h" +#include "llvm/Support/FormatVariadic.h" namespace exegesis { @@ -47,26 +48,27 @@ return llvm::Error::success(); } -llvm::Expected -LatencyBenchmarkRunner::generateSelfAliasingConfiguration( +llvm::Expected +LatencyBenchmarkRunner::generateSelfAliasingPrototype( const Instruction &Instr, const AliasingConfigurations &SelfAliasing) const { - BenchmarkConfiguration Conf; + SnippetPrototype Prototype; InstructionInstance II(Instr); if (SelfAliasing.hasImplicitAliasing()) { - Conf.Info = "implicit Self cycles, picking random values."; + Prototype.Explanation = "implicit Self cycles, picking random values."; } else { - Conf.Info = "explicit self cycles, selecting one aliasing Conf."; + 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); } - Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()}; - return Conf; + Prototype.Snippet.push_back(std::move(II)); + return Prototype; } -llvm::Expected -LatencyBenchmarkRunner::generateTwoInstructionConfiguration( +llvm::Expected +LatencyBenchmarkRunner::generateTwoInstructionPrototype( const Instruction &Instr, const AliasingConfigurations &SelfAliasing) const { std::vector Opcodes; @@ -74,7 +76,7 @@ std::iota(Opcodes.begin(), Opcodes.end(), 0U); std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator()); for (const unsigned OtherOpcode : Opcodes) { - if (OtherOpcode == Instr.Description.Opcode) + if (OtherOpcode == Instr.Description->Opcode) continue; const auto &OtherInstrDesc = MCInstrInfo.get(OtherOpcode); if (auto E = isInfeasible(OtherInstrDesc)) { @@ -92,21 +94,19 @@ setRandomAliasing(Forward, ThisII, OtherII); if (!Back.hasImplicitAliasing()) setRandomAliasing(Back, OtherII, ThisII); - BenchmarkConfiguration Conf; - Conf.Info = llvm::Twine("creating cycle through ") - .concat(MCInstrInfo.getName(OtherOpcode)) - .concat(".") - .str(); - Conf.Snippet.push_back(ThisII.randomizeUnsetVariablesAndBuild()); - Conf.Snippet.push_back(OtherII.randomizeUnsetVariablesAndBuild()); - return Conf; + SnippetPrototype Prototype; + Prototype.Explanation = llvm::formatv("creating cycle through {0}.", + MCInstrInfo.getName(OtherOpcode)); + Prototype.Snippet.push_back(std::move(ThisII)); + Prototype.Snippet.push_back(std::move(OtherII)); + return Prototype; } return llvm::make_error( "Infeasible : Didn't find any scheme to make the instruction serial"); } -llvm::Expected -LatencyBenchmarkRunner::generateConfiguration(unsigned Opcode) const { +llvm::Expected +LatencyBenchmarkRunner::generatePrototype(unsigned Opcode) const { const auto &InstrDesc = MCInstrInfo.get(Opcode); if (auto E = isInfeasible(InstrDesc)) return std::move(E); @@ -114,20 +114,12 @@ const AliasingConfigurations SelfAliasing(Instr, Instr); if (SelfAliasing.empty()) { // No self aliasing, trying to create a dependency through another opcode. - return generateTwoInstructionConfiguration(Instr, SelfAliasing); + return generateTwoInstructionPrototype(Instr, SelfAliasing); } else { - return generateSelfAliasingConfiguration(Instr, SelfAliasing); + return generateSelfAliasingPrototype(Instr, SelfAliasing); } } -llvm::Expected> -LatencyBenchmarkRunner::createConfigurations(unsigned Opcode) const { - if (auto E = generateConfiguration(Opcode)) - return std::vector{E.get()}; - else - return E.takeError(); -} - std::vector LatencyBenchmarkRunner::runMeasurements(const ExecutableFunction &Function, const unsigned NumRepetitions) const { 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 @@ -35,7 +35,8 @@ // A variable represents the value associated to an Operand or a set of Operands // if they are tied together. struct Variable { - llvm::SmallVector TiedOperands; + // The indices of the operands tied to this Variable. + llvm::SmallVector TiedOperands; llvm::MCOperand AssignedValue; // The index of this Variable in Instruction.Variables and its associated // Value in InstructionInstance.VariableValues. @@ -71,7 +72,7 @@ Instruction(const llvm::MCInstrDesc &MCInstrDesc, const RegisterAliasingTrackerCache &ATC); - const llvm::MCInstrDesc &Description; + const llvm::MCInstrDesc *Description; // Never nullptr. llvm::SmallVector Operands; llvm::SmallVector Variables; llvm::BitVector DefRegisters; // The union of the aliased def registers. @@ -82,17 +83,46 @@ struct InstructionInstance { InstructionInstance(const Instruction &Instr); + // No copy. + InstructionInstance(const InstructionInstance &) = delete; + InstructionInstance &operator=(const InstructionInstance &) = delete; + + // Moving is OK. + InstructionInstance(InstructionInstance &&) noexcept; + InstructionInstance &operator=(InstructionInstance &&) noexcept; + + unsigned getOpcode() const; llvm::MCOperand &getValueFor(const Variable &Var); llvm::MCOperand &getValueFor(const Operand &Op); + 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(); - const Instruction &Instr; + 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 &&) noexcept; + SnippetPrototype &operator=(SnippetPrototype &&) noexcept; + + std::string Explanation; + std::vector Snippet; +}; + // Represents the assignment of a Register to an Operand. struct RegisterOperandAssignment { RegisterOperandAssignment(const Operand *Operand, llvm::MCPhysReg Reg) 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 @@ -19,7 +19,7 @@ Instruction::Instruction(const llvm::MCInstrDesc &MCInstrDesc, const RegisterAliasingTrackerCache &RATC) - : Description(MCInstrDesc) { + : Description(&MCInstrDesc) { unsigned OpIndex = 0; for (; OpIndex < MCInstrDesc.getNumOperands(); ++OpIndex) { const auto &OpInfo = MCInstrDesc.opInfo_begin()[OpIndex]; @@ -71,7 +71,7 @@ // Assigning Operands to Variables. for (auto &Op : Operands) if (Op.VariableIndex >= 0) - Variables[Op.VariableIndex].TiedOperands.push_back(&Op); + Variables[Op.VariableIndex].TiedOperands.push_back(Op.Index); // Processing Aliasing. DefRegisters = RATC.emptyRegisters(); UseRegisters = RATC.emptyRegisters(); @@ -86,6 +86,16 @@ InstructionInstance::InstructionInstance(const Instruction &Instr) : Instr(Instr), VariableValues(Instr.Variables.size()) {} +InstructionInstance::InstructionInstance(InstructionInstance &&) noexcept = + default; + +InstructionInstance &InstructionInstance:: +operator=(InstructionInstance &&) noexcept = default; + +unsigned InstructionInstance::getOpcode() const { + return Instr.Description->getOpcode(); +} + llvm::MCOperand &InstructionInstance::getValueFor(const Variable &Var) { return VariableValues[Var.Index]; } @@ -96,22 +106,37 @@ } // forward declaration. -static void randomize(const Variable &Var, llvm::MCOperand &AssignedValue); +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; + }); +} llvm::MCInst InstructionInstance::randomizeUnsetVariablesAndBuild() { for (const Variable &Var : Instr.Variables) { llvm::MCOperand &AssignedValue = getValueFor(Var); if (!AssignedValue.isValid()) - randomize(Var, AssignedValue); + randomize(Instr, Var, AssignedValue); } llvm::MCInst Result; - Result.setOpcode(Instr.Description.Opcode); + Result.setOpcode(Instr.Description->Opcode); for (const auto &Op : Instr.Operands) if (Op.IsExplicit) Result.addOperand(getValueFor(Op)); return Result; } +SnippetPrototype::SnippetPrototype(SnippetPrototype &&) = default; + +SnippetPrototype &SnippetPrototype::operator=(SnippetPrototype &&) = default; + bool RegisterOperandAssignment:: operator==(const RegisterOperandAssignment &Other) const { return std::tie(Op, Reg) == std::tie(Other.Op, Other.Reg); @@ -183,10 +208,10 @@ return Container[randomIndex(Container.size())]; } -static void randomize(const Variable &Var, llvm::MCOperand &AssignedValue) { +static void randomize(const Instruction &Instr, const Variable &Var, + llvm::MCOperand &AssignedValue) { assert(!Var.TiedOperands.empty()); - assert(Var.TiedOperands.front() != nullptr); - const Operand &Op = *Var.TiedOperands.front(); + const Operand &Op = Instr.Operands[Var.TiedOperands.front()]; assert(Op.Info != nullptr); const auto &OpInfo = *Op.Info; switch (OpInfo.OperandType) { 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 @@ -24,17 +24,14 @@ using BenchmarkRunner::BenchmarkRunner; ~UopsBenchmarkRunner() override; - llvm::Expected - generateConfiguration(unsigned Opcode) const; + llvm::Expected + generatePrototype(unsigned Opcode) const override; private: llvm::Error isInfeasible(const llvm::MCInstrDesc &MCInstrDesc) const; InstructionBenchmark::ModeE getMode() const override; - llvm::Expected> - createConfigurations(unsigned Opcode) const override; - std::vector runMeasurements(const ExecutableFunction &EF, const unsigned NumRepetitions) const override; 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 @@ -102,11 +102,12 @@ } // Returns whether this Variable ties Use and Def operands together. -static bool hasTiedOperands(const Variable &Var) { +static bool hasTiedOperands(const Instruction &Instr, const Variable &Var) { bool HasUse = false; bool HasDef = false; - for (const Operand *Op : Var.TiedOperands) { - if (Op->IsDef) + for (const unsigned OpIndex : Var.TiedOperands) { + const Operand &Op = Instr.Operands[OpIndex]; + if (Op.IsDef) HasDef = true; else HasUse = true; @@ -118,7 +119,7 @@ getTiedVariables(const Instruction &Instr) { llvm::SmallVector Result; for (const auto &Var : Instr.Variables) - if (hasTiedOperands(Var)) + if (hasTiedOperands(Instr, Var)) Result.push_back(&Var); return Result; } @@ -135,26 +136,24 @@ return InstructionBenchmark::Uops; } -llvm::Expected -UopsBenchmarkRunner::generateConfiguration(unsigned Opcode) const { +llvm::Expected +UopsBenchmarkRunner::generatePrototype(unsigned Opcode) const { const auto &InstrDesc = MCInstrInfo.get(Opcode); if (auto E = isInfeasible(InstrDesc)) return std::move(E); const Instruction Instr(InstrDesc, RATC); const AliasingConfigurations SelfAliasing(Instr, Instr); if (SelfAliasing.empty()) { - InstructionInstance II(Instr); - BenchmarkConfiguration Conf; - Conf.Info = "instruction is parallel, repeating a random one."; - Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()}; - return Conf; + SnippetPrototype Prototype; + Prototype.Explanation = "instruction is parallel, repeating a random one."; + Prototype.Snippet.emplace_back(Instr); + return Prototype; } if (SelfAliasing.hasImplicitAliasing()) { - InstructionInstance II(Instr); - BenchmarkConfiguration Conf; - Conf.Info = "instruction is serial, repeating a random one."; - Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()}; - return Conf; + SnippetPrototype Prototype; + Prototype.Explanation = "instruction is serial, repeating a random one."; + Prototype.Snippet.emplace_back(Instr); + return Prototype; } const auto TiedVariables = getTiedVariables(Instr); if (!TiedVariables.empty()) { @@ -162,19 +161,20 @@ return llvm::make_error( "Infeasible : don't know how to handle several tied variables", llvm::inconvertibleErrorCode()); - BenchmarkConfiguration Conf; - Conf.Info = "instruction has tied variables using static renaming."; const Variable *Var = TiedVariables.front(); assert(Var); assert(!Var->TiedOperands.empty()); - const Operand &Operand = *Var->TiedOperands.front(); - assert(Operand.Tracker); - for (const llvm::MCPhysReg Reg : Operand.Tracker->sourceBits().set_bits()) { - InstructionInstance II(Instr); - II.getValueFor(*Var) = llvm::MCOperand::createReg(Reg); - Conf.Snippet.push_back(II.randomizeUnsetVariablesAndBuild()); + const Operand &Op = Instr.Operands[Var->TiedOperands.front()]; + assert(Op.Tracker); + SnippetPrototype Prototype; + Prototype.Explanation = + "instruction has tied variables using static renaming."; + for (const llvm::MCPhysReg Reg : Op.Tracker->sourceBits().set_bits()) { + Prototype.Snippet.emplace_back(Instr); + Prototype.Snippet.back().getValueFor(*Var) = + llvm::MCOperand::createReg(Reg); } - return Conf; + return Prototype; } InstructionInstance II(Instr); // No tied variables, we pick random values for defs. @@ -201,19 +201,11 @@ II.getValueFor(Op) = llvm::MCOperand::createReg(RandomReg); } } - BenchmarkConfiguration Conf; - Conf.Info = + SnippetPrototype Prototype; + Prototype.Explanation = "instruction has no tied variables picking Uses different from defs"; - Conf.Snippet = {II.randomizeUnsetVariablesAndBuild()}; - return Conf; -} - -llvm::Expected> -UopsBenchmarkRunner::createConfigurations(unsigned Opcode) const { - if (auto E = generateConfiguration(Opcode)) - return std::vector{E.get()}; - else - return E.takeError(); + Prototype.Snippet.push_back(std::move(II)); + return Prototype; } std::vector 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 @@ -20,6 +20,13 @@ namespace exegesis { namespace { +using testing::HasSubstr; +using testing::Not; +using testing::SizeIs; + +MATCHER(IsInvalid, "") { return !arg.isValid(); } +MATCHER(IsReg, "") { return arg.isReg(); } + class X86SnippetGeneratorTest : public ::testing::Test { protected: X86SnippetGeneratorTest() @@ -38,20 +45,26 @@ const llvm::MCRegisterInfo &MCRegisterInfo; }; -class LatencySnippetGeneratorTest : public X86SnippetGeneratorTest { +template +class SnippetGeneratorTest : public X86SnippetGeneratorTest { protected: - LatencySnippetGeneratorTest() : Runner(State) {} + SnippetGeneratorTest() : Runner(State) {} - BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) { + SnippetPrototype checkAndGetConfigurations(unsigned Opcode) { randomGenerator().seed(0); // Initialize seed. - auto ConfOrError = Runner.generateConfiguration(Opcode); - EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration. - return ConfOrError.get(); + auto ProtoOrError = Runner.generatePrototype(Opcode); + EXPECT_FALSE(ProtoOrError.takeError()); // Valid configuration. + return std::move(ProtoOrError.get()); } - LatencyBenchmarkRunner Runner; + BenchmarkRunner Runner; }; +using LatencySnippetGeneratorTest = + SnippetGeneratorTest; + +using UopsSnippetGeneratorTest = SnippetGeneratorTest; + TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) { // ADC16i16 self alias because of implicit use and def. @@ -61,17 +74,17 @@ // implicit use : AX // implicit use : EFLAGS const unsigned Opcode = llvm::X86::ADC16i16; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("implicit")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(1)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); - EXPECT_THAT(Instr.getNumOperands(), 1); - EXPECT_TRUE(Instr.getOperand(0).isImm()); // Use EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX); 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 InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(1)); // Imm. + EXPECT_THAT(II.VariableValues[0], IsInvalid()) << "Immediate is not set"; } TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) { @@ -82,18 +95,15 @@ // explicit use 2 : imm // implicit def : EFLAGS const unsigned Opcode = llvm::X86::ADD16ri; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("explicit")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(1)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); - EXPECT_THAT(Instr.getNumOperands(), 3); - EXPECT_TRUE(Instr.getOperand(0).isReg()); - EXPECT_TRUE(Instr.getOperand(1).isReg()); - EXPECT_THAT(Instr.getOperand(0).getReg(), Instr.getOperand(1).getReg()) - << "Op0 and Op1 should have the same value"; - EXPECT_TRUE(Instr.getOperand(2).isImm()); 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 InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(2)); + EXPECT_THAT(II.VariableValues[0], IsReg()) << "Operand 0 and 1"; + EXPECT_THAT(II.VariableValues[1], IsInvalid()) << "Operand 2 is not set"; } TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) { @@ -103,49 +113,43 @@ // implicit def : EFLAGS const unsigned Opcode = llvm::X86::CMP64rr; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(2)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); + const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); + EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through")); + ASSERT_THAT(Proto.Snippet, SizeIs(2)); + const InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(2)); + EXPECT_THAT(II.VariableValues[0], IsReg()); + EXPECT_THAT(II.VariableValues[1], IsInvalid()); + EXPECT_THAT(Proto.Snippet[1].getOpcode(), Not(Opcode)); // TODO: check that the two instructions alias each other. } TEST_F(LatencySnippetGeneratorTest, LAHF) { const unsigned Opcode = llvm::X86::LAHF; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(2)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); + const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); + EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through")); + ASSERT_THAT(Proto.Snippet, SizeIs(2)); + const InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(0)); } -class UopsSnippetGeneratorTest : public X86SnippetGeneratorTest { -protected: - UopsSnippetGeneratorTest() : Runner(State) {} - - BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) { - randomGenerator().seed(0); // Initialize seed. - auto ConfOrError = Runner.generateConfiguration(Opcode); - EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration. - return ConfOrError.get(); - } - - UopsBenchmarkRunner Runner; -}; - TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) { - // BNDCL32rr is parallelno matter what. + // BNDCL32rr is parallel no matter what. // explicit use 0 : reg RegClass=BNDR // explicit use 1 : reg RegClass=GR32 const unsigned Opcode = llvm::X86::BNDCL32rr; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("parallel")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(1)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); + const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); + EXPECT_THAT(Proto.Explanation, HasSubstr("parallel")); + ASSERT_THAT(Proto.Snippet, SizeIs(1)); + const InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(2)); + EXPECT_THAT(II.VariableValues[0], IsInvalid()); + EXPECT_THAT(II.VariableValues[1], IsInvalid()); } TEST_F(UopsSnippetGeneratorTest, SerialInstruction) { @@ -155,11 +159,12 @@ // implicit def : EDX // implicit use : EAX const unsigned Opcode = llvm::X86::CDQ; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("serial")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(1)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); + const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); + EXPECT_THAT(Proto.Explanation, HasSubstr("serial")); + ASSERT_THAT(Proto.Snippet, SizeIs(1)); + const InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(0)); } TEST_F(UopsSnippetGeneratorTest, StaticRenaming) { @@ -171,14 +176,16 @@ // explicit use 2 : reg RegClass=GR32 // implicit use : EFLAGS const unsigned Opcode = llvm::X86::CMOVA32rr; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("static renaming")); + const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); + EXPECT_THAT(Proto.Explanation, HasSubstr("static renaming")); constexpr const unsigned kInstructionCount = 15; - ASSERT_THAT(Conf.Snippet, testing::SizeIs(kInstructionCount)); + ASSERT_THAT(Proto.Snippet, SizeIs(kInstructionCount)); std::unordered_set AllDefRegisters; - for (const auto &Inst : Conf.Snippet) - AllDefRegisters.insert(Inst.getOperand(0).getReg()); - EXPECT_THAT(AllDefRegisters, testing::SizeIs(kInstructionCount)) + for (const auto &II : Proto.Snippet) { + ASSERT_THAT(II.VariableValues, SizeIs(2)); + AllDefRegisters.insert(II.VariableValues[0].getReg()); + } + EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount)) << "Each instruction writes to a different register"; } @@ -192,19 +199,17 @@ // explicit use 3 : imm // implicit use : EFLAGS const unsigned Opcode = llvm::X86::CMOV_GR32; - auto Conf = checkAndGetConfiguration(Opcode); - EXPECT_THAT(Conf.Info, testing::HasSubstr("no tied variables")); - ASSERT_THAT(Conf.Snippet, testing::SizeIs(1)); - const llvm::MCInst Instr = Conf.Snippet[0]; - EXPECT_THAT(Instr.getOpcode(), Opcode); - EXPECT_THAT(Instr.getNumOperands(), 4); - EXPECT_THAT(Instr.getOperand(0).getReg(), - testing::Not(Instr.getOperand(1).getReg())) + const SnippetPrototype Proto = checkAndGetConfigurations(Opcode); + EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables")); + ASSERT_THAT(Proto.Snippet, SizeIs(1)); + const InstructionInstance &II = Proto.Snippet[0]; + EXPECT_THAT(II.getOpcode(), Opcode); + ASSERT_THAT(II.VariableValues, SizeIs(4)); + EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[1].getReg())) << "Def is different from first Use"; - EXPECT_THAT(Instr.getOperand(0).getReg(), - testing::Not(Instr.getOperand(2).getReg())) + EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[2].getReg())) << "Def is different from second Use"; - EXPECT_THAT(Instr.getOperand(3).getImm(), 1); + EXPECT_THAT(II.VariableValues[3], IsInvalid()); } } // namespace