blob: 77de5e78926ea58f0d9fb6edd84476d055f19467 [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;
Clement Courbeta51efc22018-06-25 13:12:02 +000028using testing::UnorderedElementsAre;
Guillaume Chateletef6cef52018-06-20 08:52:30 +000029
30MATCHER(IsInvalid, "") { return !arg.isValid(); }
31MATCHER(IsReg, "") { return arg.isReg(); }
32
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000033class X86SnippetGeneratorTest : public ::testing::Test {
34protected:
35 X86SnippetGeneratorTest()
Guillaume Chateletb391f242018-06-13 14:07:36 +000036 : State("x86_64-unknown-linux", "haswell"),
37 MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000038
39 static void SetUpTestCase() {
40 LLVMInitializeX86TargetInfo();
41 LLVMInitializeX86TargetMC();
42 LLVMInitializeX86Target();
43 LLVMInitializeX86AsmPrinter();
44 }
45
46 const LLVMState State;
47 const llvm::MCInstrInfo &MCInstrInfo;
48 const llvm::MCRegisterInfo &MCRegisterInfo;
49};
50
Guillaume Chateletef6cef52018-06-20 08:52:30 +000051template <typename BenchmarkRunner>
52class SnippetGeneratorTest : public X86SnippetGeneratorTest {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000053protected:
Guillaume Chateletef6cef52018-06-20 08:52:30 +000054 SnippetGeneratorTest() : Runner(State) {}
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000055
Guillaume Chateletef6cef52018-06-20 08:52:30 +000056 SnippetPrototype checkAndGetConfigurations(unsigned Opcode) {
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000057 randomGenerator().seed(0); // Initialize seed.
Guillaume Chateletef6cef52018-06-20 08:52:30 +000058 auto ProtoOrError = Runner.generatePrototype(Opcode);
59 EXPECT_FALSE(ProtoOrError.takeError()); // Valid configuration.
60 return std::move(ProtoOrError.get());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000061 }
62
Guillaume Chateletef6cef52018-06-20 08:52:30 +000063 BenchmarkRunner Runner;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000064};
65
Guillaume Chateletef6cef52018-06-20 08:52:30 +000066using LatencySnippetGeneratorTest =
67 SnippetGeneratorTest<LatencyBenchmarkRunner>;
68
69using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsBenchmarkRunner>;
70
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000071TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
72 // ADC16i16 self alias because of implicit use and def.
73
74 // explicit use 0 : imm
75 // implicit def : AX
76 // implicit def : EFLAGS
77 // implicit use : AX
78 // implicit use : EFLAGS
79 const unsigned Opcode = llvm::X86::ADC16i16;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000080 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
81 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
82 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
83 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
Guillaume Chateletef6cef52018-06-20 08:52:30 +000084 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
85 EXPECT_THAT(Proto.Explanation, HasSubstr("implicit"));
86 ASSERT_THAT(Proto.Snippet, SizeIs(1));
87 const InstructionInstance &II = Proto.Snippet[0];
88 EXPECT_THAT(II.getOpcode(), Opcode);
89 ASSERT_THAT(II.VariableValues, SizeIs(1)); // Imm.
90 EXPECT_THAT(II.VariableValues[0], IsInvalid()) << "Immediate is not set";
Guillaume Chateletc9f727b2018-06-13 13:24:41 +000091}
92
93TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
94 // ADD16ri self alias because Op0 and Op1 are tied together.
95
96 // explicit def 0 : reg RegClass=GR16
97 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
98 // explicit use 2 : imm
99 // implicit def : EFLAGS
100 const unsigned Opcode = llvm::X86::ADD16ri;
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000101 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000102 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
103 EXPECT_THAT(Proto.Explanation, HasSubstr("explicit"));
104 ASSERT_THAT(Proto.Snippet, SizeIs(1));
105 const InstructionInstance &II = Proto.Snippet[0];
106 EXPECT_THAT(II.getOpcode(), Opcode);
107 ASSERT_THAT(II.VariableValues, SizeIs(2));
108 EXPECT_THAT(II.VariableValues[0], IsReg()) << "Operand 0 and 1";
109 EXPECT_THAT(II.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000110}
111
112TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
113 // CMP64rr
114 // explicit use 0 : reg RegClass=GR64
115 // explicit use 1 : reg RegClass=GR64
116 // implicit def : EFLAGS
117
118 const unsigned Opcode = llvm::X86::CMP64rr;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000119 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
120 EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
121 ASSERT_THAT(Proto.Snippet, SizeIs(2));
122 const InstructionInstance &II = Proto.Snippet[0];
123 EXPECT_THAT(II.getOpcode(), Opcode);
124 ASSERT_THAT(II.VariableValues, SizeIs(2));
Guillaume Chatelet1ebb6752018-06-20 11:09:36 +0000125 EXPECT_THAT(II.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
126 ElementsAre(IsInvalid(), IsReg())));
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000127 EXPECT_THAT(Proto.Snippet[1].getOpcode(), Not(Opcode));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000128 // TODO: check that the two instructions alias each other.
129}
130
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000131TEST_F(LatencySnippetGeneratorTest, LAHF) {
132 const unsigned Opcode = llvm::X86::LAHF;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000133 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
134 EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
135 ASSERT_THAT(Proto.Snippet, SizeIs(2));
136 const InstructionInstance &II = Proto.Snippet[0];
137 EXPECT_THAT(II.getOpcode(), Opcode);
138 ASSERT_THAT(II.VariableValues, SizeIs(0));
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000139}
140
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000141TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000142 // BNDCL32rr is parallel no matter what.
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000143
144 // explicit use 0 : reg RegClass=BNDR
145 // explicit use 1 : reg RegClass=GR32
146
147 const unsigned Opcode = llvm::X86::BNDCL32rr;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000148 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
149 EXPECT_THAT(Proto.Explanation, HasSubstr("parallel"));
150 ASSERT_THAT(Proto.Snippet, SizeIs(1));
151 const InstructionInstance &II = Proto.Snippet[0];
152 EXPECT_THAT(II.getOpcode(), Opcode);
153 ASSERT_THAT(II.VariableValues, SizeIs(2));
154 EXPECT_THAT(II.VariableValues[0], IsInvalid());
155 EXPECT_THAT(II.VariableValues[1], IsInvalid());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000156}
157
158TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
159 // CDQ is serial no matter what.
160
161 // implicit def : EAX
162 // implicit def : EDX
163 // implicit use : EAX
164 const unsigned Opcode = llvm::X86::CDQ;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000165 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
166 EXPECT_THAT(Proto.Explanation, HasSubstr("serial"));
167 ASSERT_THAT(Proto.Snippet, SizeIs(1));
168 const InstructionInstance &II = Proto.Snippet[0];
169 EXPECT_THAT(II.getOpcode(), Opcode);
170 ASSERT_THAT(II.VariableValues, SizeIs(0));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000171}
172
173TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
174 // CMOVA32rr has tied variables, we enumarate the possible values to execute
175 // as many in parallel as possible.
176
177 // explicit def 0 : reg RegClass=GR32
178 // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
179 // explicit use 2 : reg RegClass=GR32
180 // implicit use : EFLAGS
181 const unsigned Opcode = llvm::X86::CMOVA32rr;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000182 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
183 EXPECT_THAT(Proto.Explanation, HasSubstr("static renaming"));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000184 constexpr const unsigned kInstructionCount = 15;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000185 ASSERT_THAT(Proto.Snippet, SizeIs(kInstructionCount));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000186 std::unordered_set<unsigned> AllDefRegisters;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000187 for (const auto &II : Proto.Snippet) {
188 ASSERT_THAT(II.VariableValues, SizeIs(2));
189 AllDefRegisters.insert(II.VariableValues[0].getReg());
190 }
191 EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000192 << "Each instruction writes to a different register";
193}
194
195TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
196 // CMOV_GR32 has no tied variables, we make sure def and use are different
197 // from each other.
198
199 // explicit def 0 : reg RegClass=GR32
200 // explicit use 1 : reg RegClass=GR32
201 // explicit use 2 : reg RegClass=GR32
202 // explicit use 3 : imm
203 // implicit use : EFLAGS
204 const unsigned Opcode = llvm::X86::CMOV_GR32;
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000205 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
206 EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables"));
207 ASSERT_THAT(Proto.Snippet, SizeIs(1));
208 const InstructionInstance &II = Proto.Snippet[0];
209 EXPECT_THAT(II.getOpcode(), Opcode);
210 ASSERT_THAT(II.VariableValues, SizeIs(4));
211 EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[1].getReg()))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000212 << "Def is different from first Use";
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000213 EXPECT_THAT(II.VariableValues[0].getReg(), Not(II.VariableValues[2].getReg()))
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000214 << "Def is different from second Use";
Guillaume Chateletef6cef52018-06-20 08:52:30 +0000215 EXPECT_THAT(II.VariableValues[3], IsInvalid());
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000216}
217
Clement Courbeta51efc22018-06-25 13:12:02 +0000218class FakeBenchmarkRunner : public BenchmarkRunner {
219public:
220 using BenchmarkRunner::BenchmarkRunner;
221
222 Instruction createInstruction(unsigned Opcode) {
Clement Courbet0e8bf4e2018-06-25 13:44:27 +0000223 return Instruction(State.getInstrInfo().get(Opcode), RATC);
Clement Courbeta51efc22018-06-25 13:12:02 +0000224 }
225
226private:
227 InstructionBenchmark::ModeE getMode() const override {
228 return InstructionBenchmark::Unknown;
229 }
230
231 llvm::Expected<SnippetPrototype>
232 generatePrototype(unsigned Opcode) const override {
233 return llvm::make_error<llvm::StringError>("not implemented",
234 llvm::inconvertibleErrorCode());
235 }
236
237 std::vector<BenchmarkMeasure>
238 runMeasurements(const ExecutableFunction &EF,
239 const unsigned NumRepetitions) const override {
240 return {};
241 }
242};
243
244using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeBenchmarkRunner>;
245
246TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd16ri) {
247 // ADD16ri:
248 // explicit def 0 : reg RegClass=GR16
249 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
250 // explicit use 2 : imm
251 // implicit def : EFLAGS
252 InstructionInstance II(Runner.createInstruction(llvm::X86::ADD16ri));
253 II.getValueFor(II.Instr.Variables[0]) =
254 llvm::MCOperand::createReg(llvm::X86::AX);
255 std::vector<InstructionInstance> Snippet;
256 Snippet.push_back(std::move(II));
257 const auto RegsToDef = Runner.computeRegsToDef(Snippet);
258 EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::AX));
259}
260
261TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd64rr) {
262 // ADD64rr:
263 // mov64ri rax, 42
264 // add64rr rax, rax, rbx
265 // -> only rbx needs defining.
266 std::vector<InstructionInstance> Snippet;
267 {
268 InstructionInstance Mov(Runner.createInstruction(llvm::X86::MOV64ri));
269 Mov.getValueFor(Mov.Instr.Variables[0]) =
270 llvm::MCOperand::createReg(llvm::X86::RAX);
271 Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
272 Snippet.push_back(std::move(Mov));
273 }
274 {
275 InstructionInstance Add(Runner.createInstruction(llvm::X86::ADD64rr));
276 Add.getValueFor(Add.Instr.Variables[0]) =
277 llvm::MCOperand::createReg(llvm::X86::RAX);
278 Add.getValueFor(Add.Instr.Variables[1]) =
279 llvm::MCOperand::createReg(llvm::X86::RBX);
280 Snippet.push_back(std::move(Add));
281 }
282
283 const auto RegsToDef = Runner.computeRegsToDef(Snippet);
284 EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::RBX));
285}
286
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000287} // namespace
288} // namespace exegesis