diff options
Diffstat (limited to 'llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp')
-rw-r--r-- | llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp | 237 |
1 files changed, 168 insertions, 69 deletions
diff --git a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp index 6cc24a02cfc..4b3fa5455a3 100644 --- a/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp +++ b/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp @@ -25,6 +25,7 @@ namespace { using testing::AnyOf; using testing::ElementsAre; +using testing::Gt; using testing::HasSubstr; using testing::Not; using testing::SizeIs; @@ -57,14 +58,12 @@ class SnippetGeneratorTest : public X86SnippetGeneratorTest { protected: SnippetGeneratorTest() : Generator(State) {} - CodeTemplate checkAndGetCodeTemplate(unsigned Opcode) { + std::vector<CodeTemplate> checkAndGetCodeTemplates(unsigned Opcode) { randomGenerator().seed(0); // Initialize seed. const Instruction Instr(State, Opcode); auto CodeTemplateOrError = Generator.generateCodeTemplates(Instr); EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration. - auto &CodeTemplate = CodeTemplateOrError.get(); - EXPECT_EQ(CodeTemplate.size(), 1U); - return std::move(CodeTemplate.front()); + return std::move(CodeTemplateOrError.get()); } SnippetGeneratorT Generator; @@ -75,21 +74,25 @@ using LatencySnippetGeneratorTest = using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsSnippetGenerator>; -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")); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; + EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -97,63 +100,105 @@ TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) { 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")); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; + EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS); 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 - - const unsigned Opcode = llvm::X86::CMP64rr; - const CodeTemplate CT = checkAndGetCodeTemplate(Opcode); - EXPECT_THAT(CT.Info, HasSubstr("cycle through")); - ASSERT_THAT(CT.Instructions, SizeIs(2)); +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 auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; + EXPECT_THAT(CT.Execution, ExecutionMode::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(2)); - EXPECT_THAT(IT.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()), - ElementsAre(IsInvalid(), IsReg()))); - EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode)); - // TODO: check that the two instructions alias each other. + 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 auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available"; + for (const auto &CT : CodeTemplates) { + EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR); + ASSERT_THAT(CT.Instructions, SizeIs(2)); + const InstructionTemplate &IT = CT.Instructions[0]; + EXPECT_THAT(IT.getOpcode(), Opcode); + ASSERT_THAT(IT.VariableValues, SizeIs(2)); + EXPECT_THAT(IT.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()), + ElementsAre(IsInvalid(), IsReg()))); + EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode)); + // TODO: check that the two instructions alias each other. + } } 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")); - ASSERT_THAT(CT.Instructions, SizeIs(2)); - const InstructionTemplate &IT = CT.Instructions[0]; - EXPECT_THAT(IT.getOpcode(), Opcode); - ASSERT_THAT(IT.VariableValues, SizeIs(0)); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available"; + for (const auto &CT : CodeTemplates) { + EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR); + ASSERT_THAT(CT.Instructions, SizeIs(2)); + const InstructionTemplate &IT = CT.Instructions[0]; + EXPECT_THAT(IT.getOpcode(), Opcode); + ASSERT_THAT(IT.VariableValues, SizeIs(0)); + } } 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); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; EXPECT_THAT(CT.Info, HasSubstr("parallel")); + EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -163,14 +208,18 @@ TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) { } 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); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; EXPECT_THAT(CT.Info, HasSubstr("serial")); + EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -181,13 +230,21 @@ TEST_F(UopsSnippetGeneratorTest, StaticRenaming) { // 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); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; EXPECT_THAT(CT.Info, HasSubstr("static renaming")); + EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN); constexpr const unsigned kInstructionCount = 15; ASSERT_THAT(CT.Instructions, SizeIs(kInstructionCount)); std::unordered_set<unsigned> AllDefRegisters; @@ -203,14 +260,23 @@ TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) { // 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); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; EXPECT_THAT(CT.Info, HasSubstr("no tied variables")); + EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(1)); const InstructionTemplate &IT = CT.Instructions[0]; EXPECT_THAT(IT.getOpcode(), Opcode); @@ -224,9 +290,27 @@ TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) { 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); + const auto CodeTemplates = checkAndGetCodeTemplates(Opcode); + ASSERT_THAT(CodeTemplates, SizeIs(1)); + const auto &CT = CodeTemplates[0]; EXPECT_THAT(CT.Info, HasSubstr("no tied variables")); + EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN); ASSERT_THAT(CT.Instructions, SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses)); const InstructionTemplate &IT = CT.Instructions[0]; @@ -240,6 +324,21 @@ TEST_F(UopsSnippetGeneratorTest, MemoryUse) { 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(); |