Index: tools/llvm-exegesis/lib/CodeTemplate.h =================================================================== --- tools/llvm-exegesis/lib/CodeTemplate.h +++ tools/llvm-exegesis/lib/CodeTemplate.h @@ -17,6 +17,7 @@ #define LLVM_TOOLS_LLVM_EXEGESIS_CODETEMPLATE_H #include "MCInstrDescView.h" +#include "llvm/ADT/BitmaskEnum.h" namespace exegesis { @@ -45,9 +46,30 @@ llvm::SmallVector VariableValues; }; +enum class Execution : uint8_t { + UNKNOWN = 0U, + SERIAL_VIA_IMPLICIT_REGS = 1u << 0, + SERIAL_VIA_TIED_REGS = 1u << 1, + SERIAL_VIA_MEMORY_INSTR = 1u << 2, + SERIAL_VIA_EXPLICIT_REGS = 1u << 3, + SERIAL_VIA_NON_MEMORY_INSTR = 1u << 4, + PARALLEL_VIA_LACKING_OPERANDS = 1u << 5, + PARALLEL_VIA_EXPLICIT_REGS = 1u << 6, + LLVM_MARK_AS_BITMASK_ENUM(/*Largest*/ PARALLEL_VIA_EXPLICIT_REGS) +}; +// Returns a human readable string for the enum. +llvm::StringRef getName(Execution Execution); +// Returns a sequence of increasing power of two corresponding to all the +// Execution flags. +llvm::ArrayRef getAllExecutionBits(); +// Decomposes Execution into individual set bits. +llvm::SmallVector getIndividualExecutionBits(Execution); + +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 -// BenchmarkRunner to instantiate it many times with specific values to study +// SnippetGenerator to instantiate it many times with specific values to study // their impact on instruction's performance. struct CodeTemplate { CodeTemplate() = default; @@ -57,6 +79,7 @@ CodeTemplate(const CodeTemplate &) = delete; CodeTemplate &operator=(const CodeTemplate &) = delete; + enum Execution Execution = Execution::UNKNOWN; // Some information about how this template has been created. std::string Info; // The list of the instructions for this template. Index: tools/llvm-exegesis/lib/CodeTemplate.cpp =================================================================== --- tools/llvm-exegesis/lib/CodeTemplate.cpp +++ tools/llvm-exegesis/lib/CodeTemplate.cpp @@ -65,4 +65,47 @@ return Result; } +llvm::StringRef getName(Execution Execution) { + switch (Execution) { + case Execution::UNKNOWN: + return "UNKNOWN"; + case Execution::SERIAL_VIA_IMPLICIT_REGS: + return "SERIAL_VIA_IMPLICIT_REGS"; + case Execution::SERIAL_VIA_TIED_REGS: + return "SERIAL_VIA_TIED_REGS"; + case Execution::SERIAL_VIA_MEMORY_INSTR: + return "SERIAL_VIA_MEMORY_INSTR"; + case Execution::SERIAL_VIA_EXPLICIT_REGS: + return "SERIAL_VIA_EXPLICIT_REGS"; + case Execution::SERIAL_VIA_NON_MEMORY_INSTR: + return "SERIAL_VIA_NON_MEMORY_INSTR"; + case Execution::PARALLEL_VIA_LACKING_OPERANDS: + return "PARALLEL_VIA_LACKING_OPERANDS"; + case Execution::PARALLEL_VIA_EXPLICIT_REGS: + return "PARALLEL_VIA_EXPLICIT_REGS"; + } + llvm_unreachable("Missing enum case"); +} + +llvm::ArrayRef getAllExecutionBits() { + return { + Execution::SERIAL_VIA_IMPLICIT_REGS, + Execution::SERIAL_VIA_TIED_REGS, + Execution::SERIAL_VIA_MEMORY_INSTR, + Execution::SERIAL_VIA_EXPLICIT_REGS, + Execution::SERIAL_VIA_NON_MEMORY_INSTR, + Execution::PARALLEL_VIA_LACKING_OPERANDS, + Execution::PARALLEL_VIA_EXPLICIT_REGS, + }; +} + +llvm::SmallVector +getIndividualExecutionBits(Execution Execution) { + llvm::SmallVector Result; + for (const auto Bit : getAllExecutionBits()) + if ((Execution & Bit) == Bit) + Result.push_back(Bit); + return Result; +} + } // namespace exegesis Index: tools/llvm-exegesis/lib/Latency.cpp =================================================================== --- tools/llvm-exegesis/lib/Latency.cpp +++ tools/llvm-exegesis/lib/Latency.cpp @@ -20,53 +20,144 @@ namespace exegesis { -LatencySnippetGenerator::~LatencySnippetGenerator() = default; +struct ExecutionClass { + Execution Mask; + const char *Description; +} static const kExecutionClasses[] = { + {Execution::SERIAL_VIA_IMPLICIT_REGS | Execution::SERIAL_VIA_TIED_REGS, + "Repeating a single implicitly serial instruction"}, + {Execution::SERIAL_VIA_EXPLICIT_REGS, + "Repeating a single explicitly serial instruction"}, + {Execution::SERIAL_VIA_MEMORY_INSTR | + Execution::SERIAL_VIA_NON_MEMORY_INSTR, + "Repeating two instructions"}, +}; -llvm::Expected> -generateTwoInstructionPrototypes(const LLVMState &State, - const Instruction &Instr) { +static constexpr size_t kMaxAliasingInstructions = 10; + +static std::vector +computeAliasingInstructions(const LLVMState &State, const Instruction &Instr, + size_t MaxAliasingInstructions) { + // Randomly iterate the set of instructions. std::vector Opcodes; Opcodes.resize(State.getInstrInfo().getNumOpcodes()); std::iota(Opcodes.begin(), Opcodes.end(), 0U); std::shuffle(Opcodes.begin(), Opcodes.end(), randomGenerator()); + + std::vector AliasingInstructions; for (const unsigned OtherOpcode : Opcodes) { - if (OtherOpcode == Instr.Description->Opcode) + if (OtherOpcode == Instr.Description->getOpcode()) continue; const Instruction OtherInstr(State, OtherOpcode); if (OtherInstr.hasMemoryOperands()) continue; - const AliasingConfigurations Forward(Instr, OtherInstr); - const AliasingConfigurations Back(OtherInstr, Instr); - if (Forward.empty() || Back.empty()) - continue; - InstructionTemplate ThisIT(Instr); - InstructionTemplate OtherIT(OtherInstr); - if (!Forward.hasImplicitAliasing()) - setRandomAliasing(Forward, ThisIT, OtherIT); - if (!Back.hasImplicitAliasing()) - setRandomAliasing(Back, OtherIT, ThisIT); + if (Instr.hasAliasingRegistersThrough(OtherInstr)) + AliasingInstructions.push_back(std::move(OtherInstr)); + if (AliasingInstructions.size() >= MaxAliasingInstructions) + break; + } + return AliasingInstructions; +} + +static Execution getExecutionBits(const Instruction &Instr) { + Execution EB; + if (Instr.hasAliasingImplicitRegisters()) + EB |= Execution::SERIAL_VIA_IMPLICIT_REGS; + if (Instr.hasTiedRegisters()) + EB |= Execution::SERIAL_VIA_TIED_REGS; + if (Instr.hasMemoryOperands()) + EB |= Execution::SERIAL_VIA_MEMORY_INSTR; + else { + if (Instr.hasAliasingRegisters()) + EB |= Execution::SERIAL_VIA_EXPLICIT_REGS; + if (Instr.hasOneUseOrOneDef()) + EB |= Execution::SERIAL_VIA_NON_MEMORY_INSTR; + } + return EB; +} + +static void appendCodeTemplates(const LLVMState &State, + const Instruction &Instr, + Execution ExecutionBit, + llvm::StringRef ExecutionClassDescription, + std::vector &CodeTemplates) { + switch (ExecutionBit) { + case Execution::SERIAL_VIA_IMPLICIT_REGS: + // Nothing to do, the instruction is always serial. + LLVM_FALLTHROUGH; + case Execution::SERIAL_VIA_TIED_REGS: { + // We picking whatever value for the tied variable will make the instruction + // serial. CodeTemplate CT; - CT.Info = llvm::formatv("creating cycle through {0}.", - State.getInstrInfo().getName(OtherOpcode)); - CT.Instructions.push_back(std::move(ThisIT)); - CT.Instructions.push_back(std::move(OtherIT)); - return getSingleton(CT); + CT.Execution = ExecutionBit; + CT.Info = ExecutionClassDescription; + CT.Instructions.push_back(Instr); + CodeTemplates.push_back(std::move(CT)); + return; + } + case Execution::SERIAL_VIA_MEMORY_INSTR: { + // Add memory instruction to alias. + // TODO: Implement me. + return; + } + case Execution::SERIAL_VIA_EXPLICIT_REGS: { + // Select head to toe memory instruction. + 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 = ExecutionBit; + CT.Info = ExecutionClassDescription; + CT.Instructions.push_back(std::move(IT)); + CodeTemplates.push_back(std::move(CT)); + return; + } + case Execution::SERIAL_VIA_NON_MEMORY_INSTR: { + // Select head to toe 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 = ExecutionBit; + 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"); } - return llvm::make_error( - "Infeasible : Didn't find any scheme to make the instruction serial"); } +LatencySnippetGenerator::~LatencySnippetGenerator() = default; + llvm::Expected> LatencySnippetGenerator::generateCodeTemplates(const Instruction &Instr) const { - if (Instr.hasMemoryOperands()) + std::vector Results; + const Execution EB = getExecutionBits(Instr); + for (const auto EC : kExecutionClasses) { + for (const auto ExecutionBit : getIndividualExecutionBits(EB & EC.Mask)) + appendCodeTemplates(State, Instr, ExecutionBit, EC.Description, Results); + if (!Results.empty()) + break; + } + if (Results.empty()) return llvm::make_error( - "Infeasible : has memory operands"); - return llvm::handleExpected( // - generateSelfAliasingCodeTemplates(Instr), - [this, &Instr]() { - return generateTwoInstructionPrototypes(State, Instr); - }, - [](const BenchmarkFailure &) { /*Consume Error*/ }); + "No strategy found to make the execution serial"); + return std::move(Results); } const char *LatencyBenchmarkRunner::getCounterName() const { Index: tools/llvm-exegesis/lib/MCInstrDescView.h =================================================================== --- tools/llvm-exegesis/lib/MCInstrDescView.h +++ tools/llvm-exegesis/lib/MCInstrDescView.h @@ -125,6 +125,11 @@ // reads or write the same memory region. bool hasMemoryOperands() const; + // 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; + // Convenient function to help with debugging. void dump(const llvm::MCRegisterInfo &RegInfo, llvm::raw_ostream &Stream) const; Index: tools/llvm-exegesis/lib/MCInstrDescView.cpp =================================================================== --- tools/llvm-exegesis/lib/MCInstrDescView.cpp +++ tools/llvm-exegesis/lib/MCInstrDescView.cpp @@ -197,6 +197,10 @@ return AllDefRegs.anyCommon(AllUseRegs); } +bool Instruction::hasOneUseOrOneDef() const { + return AllDefRegs.count() || AllUseRegs.count(); +} + void Instruction::dump(const llvm::MCRegisterInfo &RegInfo, llvm::raw_ostream &Stream) const { Stream << "- " << Name << "\n"; Index: unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp =================================================================== --- unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -63,7 +63,7 @@ auto CodeTemplateOrError = Generator.generateCodeTemplates(Instr); EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration. auto &CodeTemplate = CodeTemplateOrError.get(); - EXPECT_EQ(CodeTemplate.size(), 1U); + EXPECT_THAT(CodeTemplate, testing::SizeIs(testing::Ge(1U))); return std::move(CodeTemplate.front()); } @@ -75,21 +75,23 @@ using UopsSnippetGeneratorTest = SnippetGeneratorTest; -TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) { - // ADC16i16 self alias because of implicit use and def. - - // explicit use 0 : imm - // implicit def : AX - // implicit def : EFLAGS - // implicit use : AX - // implicit use : EFLAGS +TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependencyThroughImplicitReg) { + // - ADC16i16 + // - Op0 Explicit Use Immediate + // - Op1 Implicit Def Reg(AX) + // - Op2 Implicit Def Reg(EFLAGS) + // - Op3 Implicit Use Reg(AX) + // - Op4 Implicit Use Reg(EFLAGS) + // - Var0 [Op0] + // - hasAliasingImplicitRegisters (execution is always serial) + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::ADC16i16; 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 CodeTemplate CT = checkAndGetCodeTemplate(Opcode); - EXPECT_THAT(CT.Info, HasSubstr("implicit")); + EXPECT_THAT(CT.Execution, Execution::SERIAL_VIA_IMPLICIT_REGS); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -97,34 +99,60 @@ EXPECT_THAT(IT.VariableValues[0], IsInvalid()) << "Immediate is not set"; } -TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) { - // ADD16ri self alias because Op0 and Op1 are tied together. - - // explicit def 0 : reg RegClass=GR16 - // explicit use 1 : reg RegClass=GR16 | TIED_TO:0 - // explicit use 2 : imm - // implicit def : EFLAGS +TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependencyThroughTiedRegs) { + // - ADD16ri + // - Op0 Explicit Def RegClass(GR16) + // - Op1 Explicit Use RegClass(GR16) TiedToOp0 + // - Op2 Explicit Use Immediate + // - Op3 Implicit Def Reg(EFLAGS) + // - Var0 [Op0,Op1] + // - Var1 [Op2] + // - hasTiedRegisters (execution is always serial) + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::ADD16ri; EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS); const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); - EXPECT_THAT(CT.Info, HasSubstr("explicit")); + EXPECT_THAT(CT.Execution, Execution::SERIAL_VIA_TIED_REGS); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); ASSERT_THAT(IT.VariableValues, SizeIs(2)); - EXPECT_THAT(IT.VariableValues[0], IsReg()) << "Operand 0 and 1"; + EXPECT_THAT(IT.VariableValues[0], IsInvalid()) << "Operand 1 is not set"; EXPECT_THAT(IT.VariableValues[1], IsInvalid()) << "Operand 2 is not set"; } -TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) { - // CMP64rr - // explicit use 0 : reg RegClass=GR64 - // explicit use 1 : reg RegClass=GR64 - // implicit def : EFLAGS +TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependencyThroughExplicitRegs) { + // - VXORPSrr + // - Op0 Explicit Def RegClass(VR128) + // - Op1 Explicit Use RegClass(VR128) + // - Op2 Explicit Use RegClass(VR128) + // - Var0 [Op0] + // - Var1 [Op1] + // - Var2 [Op2] + // - hasAliasingRegisters + const unsigned Opcode = llvm::X86::VXORPSrr; + const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); + EXPECT_THAT(CT.Execution, Execution::SERIAL_VIA_EXPLICIT_REGS); + ASSERT_THAT(CT.Instructions, SizeIs(1)); + const InstructionTemplate &IT = CT.Instructions[0]; + EXPECT_THAT(IT.getOpcode(), Opcode); + ASSERT_THAT(IT.VariableValues, SizeIs(3)); + EXPECT_THAT(IT.VariableValues, + AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()), + ElementsAre(IsReg(), IsReg(), IsInvalid()))) + << "Op0 is either set to Op1 or to Op2"; +} +TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) { + // - CMP64rr + // - Op0 Explicit Use RegClass(GR64) + // - Op1 Explicit Use RegClass(GR64) + // - Op2 Implicit Def Reg(EFLAGS) + // - Var0 [Op0] + // - Var1 [Op1] const unsigned Opcode = llvm::X86::CMP64rr; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); - EXPECT_THAT(CT.Info, HasSubstr("cycle through")); + EXPECT_THAT(CT.Execution, Execution::SERIAL_VIA_NON_MEMORY_INSTR); ASSERT_THAT(CT.Instructions, SizeIs(2)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -136,9 +164,12 @@ } TEST_F(LatencySnippetGeneratorTest, LAHF) { + // - LAHF + // - Op0 Implicit Def Reg(AH) + // - Op1 Implicit Use Reg(EFLAGS) const unsigned Opcode = llvm::X86::LAHF; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); - EXPECT_THAT(CT.Info, HasSubstr("cycle through")); + EXPECT_THAT(CT.Execution, Execution::SERIAL_VIA_NON_MEMORY_INSTR); ASSERT_THAT(CT.Instructions, SizeIs(2)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -146,14 +177,15 @@ } TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) { - // BNDCL32rr is parallel no matter what. - - // explicit use 0 : reg RegClass=BNDR - // explicit use 1 : reg RegClass=GR32 - + // - BNDCL32rr + // - Op0 Explicit Use RegClass(BNDR) + // - Op1 Explicit Use RegClass(GR32) + // - Var0 [Op0] + // - Var1 [Op1] const unsigned Opcode = llvm::X86::BNDCL32rr; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); EXPECT_THAT(CT.Info, HasSubstr("parallel")); + EXPECT_THAT(CT.Execution, Execution::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -163,14 +195,16 @@ } TEST_F(UopsSnippetGeneratorTest, SerialInstruction) { - // CDQ is serial no matter what. - - // implicit def : EAX - // implicit def : EDX - // implicit use : EAX + // - CDQ + // - Op0 Implicit Def Reg(EAX) + // - Op1 Implicit Def Reg(EDX) + // - Op2 Implicit Use Reg(EAX) + // - hasAliasingImplicitRegisters (execution is always serial) + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::CDQ; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); EXPECT_THAT(CT.Info, HasSubstr("serial")); + EXPECT_THAT(CT.Execution, Execution::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -181,13 +215,19 @@ // CMOVA32rr has tied variables, we enumerate the possible values to execute // as many in parallel as possible. - // explicit def 0 : reg RegClass=GR32 - // explicit use 1 : reg RegClass=GR32 | TIED_TO:0 - // explicit use 2 : reg RegClass=GR32 - // implicit use : EFLAGS + // - CMOVA32rr + // - Op0 Explicit Def RegClass(GR32) + // - Op1 Explicit Use RegClass(GR32) TiedToOp0 + // - Op2 Explicit Use RegClass(GR32) + // - Op3 Implicit Use Reg(EFLAGS) + // - Var0 [Op0,Op1] + // - Var1 [Op2] + // - hasTiedRegisters (execution is always serial) + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::CMOVA32rr; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); EXPECT_THAT(CT.Info, HasSubstr("static renaming")); + EXPECT_THAT(CT.Execution, Execution::UNKNOWN); constexpr const unsigned kInstructionCount = 15; ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount)); std::unordered_set AllDefRegisters; @@ -203,14 +243,21 @@ // CMOV_GR32 has no tied variables, we make sure def and use are different // from each other. - // explicit def 0 : reg RegClass=GR32 - // explicit use 1 : reg RegClass=GR32 - // explicit use 2 : reg RegClass=GR32 - // explicit use 3 : imm - // implicit use : EFLAGS + // - CMOV_GR32 + // - Op0 Explicit Def RegClass(GR32) + // - Op1 Explicit Use RegClass(GR32) + // - Op2 Explicit Use RegClass(GR32) + // - Op3 Explicit Use Immediate + // - Op4 Implicit Use Reg(EFLAGS) + // - Var0 [Op0] + // - Var1 [Op1] + // - Var2 [Op2] + // - Var3 [Op3] + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::CMOV_GR32; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); EXPECT_THAT(CT.Info, HasSubstr("no tied variables")); + EXPECT_THAT(CT.Execution, Execution::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -224,9 +271,25 @@ TEST_F(UopsSnippetGeneratorTest, MemoryUse) { // Mov32rm reads from memory. + // - MOV32rm + // - Op0 Explicit Def RegClass(GR32) + // - Op1 Explicit Use Memory RegClass(GR8) + // - Op2 Explicit Use Memory + // - Op3 Explicit Use Memory RegClass(GRH8) + // - Op4 Explicit Use Memory + // - Op5 Explicit Use Memory RegClass(SEGMENT_REG) + // - Var0 [Op0] + // - Var1 [Op1] + // - Var2 [Op2] + // - Var3 [Op3] + // - Var4 [Op4] + // - Var5 [Op5] + // - hasMemoryOperands + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::MOV32rm; const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); EXPECT_THAT(CT.Info, HasSubstr("no tied variables")); + EXPECT_THAT(CT.Execution, Execution::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses)); const InstructionTemplate &IT = CT.Instructions[0]; @@ -240,6 +303,21 @@ TEST_F(UopsSnippetGeneratorTest, MemoryUse_Movsb) { // MOVSB writes to scratch memory register. + // - MOVSB + // - Op0 Explicit Use Memory RegClass(GR8) + // - Op1 Explicit Use Memory RegClass(GR8) + // - Op2 Explicit Use Memory RegClass(SEGMENT_REG) + // - Op3 Implicit Def Reg(EDI) + // - Op4 Implicit Def Reg(ESI) + // - Op5 Implicit Use Reg(EDI) + // - Op6 Implicit Use Reg(ESI) + // - Op7 Implicit Use Reg(DF) + // - Var0 [Op0] + // - Var1 [Op1] + // - Var2 [Op2] + // - hasMemoryOperands + // - hasAliasingImplicitRegisters (execution is always serial) + // - hasAliasingRegisters const unsigned Opcode = llvm::X86::MOVSB; const Instruction Instr(State, Opcode); auto Error = Generator.generateCodeTemplates(Instr).takeError();