blob: d746bfc756fb878536266a73548c699f479ee6be [file] [log] [blame]
Guillaume Chateletc9f727b2018-06-13 13:24:41 +00001//===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9
10#include "../Common/AssemblerUtils.h"
11#include "Latency.h"
12#include "LlvmState.h"
13#include "MCInstrDescView.h"
14#include "RegisterAliasing.h"
15#include "Uops.h"
16#include "X86InstrInfo.h"
17
18#include <unordered_set>
19
20namespace exegesis {
21namespace {
22
Guillaume Chatelet1ebb6752018-06-20 11:09:36 +000023using testing::AnyOf;
24using testing::ElementsAre;
Guillaume Chateletef6cef52018-06-20 08:52:30 +000025using testing::HasSubstr;
26using testing::Not;
27using testing::SizeIs;
28
29MATCHER(IsInvalid, "") { return !arg.isValid(); }
30MATCHER(IsReg, "") { return arg.isReg(); }
31
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000032class X86SnippetGeneratorTest : public ::testing::Test {
33protected:
34 X86SnippetGeneratorTest()
Guillaume Chateletb391f242018-06-13 14:07:36 +000035 : State("x86_64-unknown-linux", "haswell"),
36 MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000037
38 static void SetUpTestCase() {
39 LLVMInitializeX86TargetInfo();
40 LLVMInitializeX86TargetMC();
41 LLVMInitializeX86Target();
42 LLVMInitializeX86AsmPrinter();
43 }
44
45 const LLVMState State;
46 const llvm::MCInstrInfo &MCInstrInfo;
47 const llvm::MCRegisterInfo &MCRegisterInfo;
48};
49
Guillaume Chateletef6cef52018-06-20 08:52:30 +000050template <typename BenchmarkRunner>
51class SnippetGeneratorTest : public X86SnippetGeneratorTest {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000052protected:
Guillaume Chateletef6cef52018-06-20 08:52:30 +000053 SnippetGeneratorTest() : Runner(State) {}
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000054
Guillaume Chateletef6cef52018-06-20 08:52:30 +000055 SnippetPrototype checkAndGetConfigurations(unsigned Opcode) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000056 randomGenerator().seed(0); // Initialize seed.
Guillaume Chateletef6cef52018-06-20 08:52:30 +000057 auto ProtoOrError = Runner.generatePrototype(Opcode);
58 EXPECT_FALSE(ProtoOrError.takeError()); // Valid configuration.
59 return std::move(ProtoOrError.get());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000060 }
61
Guillaume Chateletef6cef52018-06-20 08:52:30 +000062 BenchmarkRunner Runner;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000063};
64
Guillaume Chateletef6cef52018-06-20 08:52:30 +000065using LatencySnippetGeneratorTest =
66 SnippetGeneratorTest<LatencyBenchmarkRunner>;
67
68using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsBenchmarkRunner>;
69
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000070TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
71 // ADC16i16 self alias because of implicit use and def.
72
73 // explicit use 0 : imm
74 // implicit def : AX
75 // implicit def : EFLAGS
76 // implicit use : AX
77 // implicit use : EFLAGS
78 const unsigned Opcode = llvm::X86::ADC16i16;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000079 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
80 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
81 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
82 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
Guillaume Chateletef6cef52018-06-20 08:52:30 +000083 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
84 EXPECT_THAT(Proto.Explanation, HasSubstr("implicit"));
85 ASSERT_THAT(Proto.Snippet, SizeIs(1));
86 const InstructionInstance &II = Proto.Snippet[0];
87 EXPECT_THAT(II.getOpcode(), Opcode);
88 ASSERT_THAT(II.VariableValues, SizeIs(1)); // Imm.
89 EXPECT_THAT(II.VariableValues[0], IsInvalid()) << "Immediate is not set";
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000090}
91
92TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
93 // ADD16ri self alias because Op0 and Op1 are tied together.
94
95 // explicit def 0 : reg RegClass=GR16
96 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
97 // explicit use 2 : imm
98 // implicit def : EFLAGS
99 const unsigned Opcode = llvm::X86::ADD16ri;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000100 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000101 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
102 EXPECT_THAT(Proto.Explanation, HasSubstr("explicit"));
103 ASSERT_THAT(Proto.Snippet, SizeIs(1));
104 const InstructionInstance &II = Proto.Snippet[0];
105 EXPECT_THAT(II.getOpcode(), Opcode);
106 ASSERT_THAT(II.VariableValues, SizeIs(2));
107 EXPECT_THAT(II.VariableValues[0], IsReg()) << "Operand 0 and 1";
108 EXPECT_THAT(II.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000109}
110
111TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
112 // CMP64rr
113 // explicit use 0 : reg RegClass=GR64
114 // explicit use 1 : reg RegClass=GR64
115 // implicit def : EFLAGS
116
117 const unsigned Opcode = llvm::X86::CMP64rr;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000118 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
119 EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
120 ASSERT_THAT(Proto.Snippet, SizeIs(2));
121 const InstructionInstance &II = Proto.Snippet[0];
122 EXPECT_THAT(II.getOpcode(), Opcode);
123 ASSERT_THAT(II.VariableValues, SizeIs(2));
Guillaume Chatelet1ebb6752018-06-20 11:09:36 +0000124 EXPECT_THAT(II.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
125 ElementsAre(IsInvalid(), IsReg())));
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000126 EXPECT_THAT(Proto.Snippet[1].getOpcode(), Not(Opcode));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000127 // TODO: check that the two instructions alias each other.
128}
129
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000130TEST_F(LatencySnippetGeneratorTest, LAHF) {
131 const unsigned Opcode = llvm::X86::LAHF;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000132 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
133 EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
134 ASSERT_THAT(Proto.Snippet, SizeIs(2));
135 const InstructionInstance &II = Proto.Snippet[0];
136 EXPECT_THAT(II.getOpcode(), Opcode);
137 ASSERT_THAT(II.VariableValues, SizeIs(0));
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000138}
139
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000140TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000141 // BNDCL32rr is parallel no matter what.
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000142
143 // explicit use 0 : reg RegClass=BNDR
144 // explicit use 1 : reg RegClass=GR32
145
146 const unsigned Opcode = llvm::X86::BNDCL32rr;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000147 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
148 EXPECT_THAT(Proto.Explanation, HasSubstr("parallel"));
149 ASSERT_THAT(Proto.Snippet, SizeIs(1));
150 const InstructionInstance &II = Proto.Snippet[0];
151 EXPECT_THAT(II.getOpcode(), Opcode);
152 ASSERT_THAT(II.VariableValues, SizeIs(2));
153 EXPECT_THAT(II.VariableValues[0], IsInvalid());
154 EXPECT_THAT(II.VariableValues[1], IsInvalid());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000155}
156
157TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
158 // CDQ is serial no matter what.
159
160 // implicit def : EAX
161 // implicit def : EDX
162 // implicit use : EAX
163 const unsigned Opcode = llvm::X86::CDQ;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000164 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
165 EXPECT_THAT(Proto.Explanation, HasSubstr("serial"));
166 ASSERT_THAT(Proto.Snippet, SizeIs(1));
167 const InstructionInstance &II = Proto.Snippet[0];
168 EXPECT_THAT(II.getOpcode(), Opcode);
169 ASSERT_THAT(II.VariableValues, SizeIs(0));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000170}
171
172TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
173 // CMOVA32rr has tied variables, we enumarate the possible values to execute
174 // as many in parallel as possible.
175
176 // explicit def 0 : reg RegClass=GR32
177 // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
178 // explicit use 2 : reg RegClass=GR32
179 // implicit use : EFLAGS
180 const unsigned Opcode = llvm::X86::CMOVA32rr;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000181 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
182 EXPECT_THAT(Proto.Explanation, HasSubstr("static renaming"));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000183 constexpr const unsigned kInstructionCount = 15;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000184 ASSERT_THAT(Proto.Snippet, SizeIs(kInstructionCount));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000185 std::unordered_set<unsigned> AllDefRegisters;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000186 for (const auto &II : Proto.Snippet) {
187 ASSERT_THAT(II.VariableValues, SizeIs(2));
188 AllDefRegisters.insert(II.VariableValues[0].getReg());
189 }
190 EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000191 << "Each instruction writes to a different register";
192}
193
194TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
195 // CMOV_GR32 has no tied variables, we make sure def and use are different
196 // from each other.
197
198 // explicit def 0 : reg RegClass=GR32
199 // explicit use 1 : reg RegClass=GR32
200 // explicit use 2 : reg RegClass=GR32
201 // explicit use 3 : imm
202 // implicit use : EFLAGS
203 const unsigned Opcode = llvm::X86::CMOV_GR32;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000204 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
205 EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables"));
206 ASSERT_THAT(Proto.Snippet, SizeIs(1));
207 const InstructionInstance &II = Proto.Snippet[0];
208 EXPECT_THAT(II.getOpcode(), Opcode);
209 ASSERT_THAT(II.VariableValues, SizeIs(4));
210 EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[1].getReg()))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000211 << "Def is different from first Use";
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000212 EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[2].getReg()))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000213 << "Def is different from second Use";
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000214 EXPECT_THAT(II.VariableValues[3], IsInvalid());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000215}
216
217} // namespace
218} // namespace exegesis