blob: d020d4cf309fa5c44175e936b5dd8979fe2ef000 [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
23class X86SnippetGeneratorTest : public ::testing::Test {
24protected:
25 X86SnippetGeneratorTest()
26 : MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
27
28 static void SetUpTestCase() {
29 LLVMInitializeX86TargetInfo();
30 LLVMInitializeX86TargetMC();
31 LLVMInitializeX86Target();
32 LLVMInitializeX86AsmPrinter();
33 }
34
35 const LLVMState State;
36 const llvm::MCInstrInfo &MCInstrInfo;
37 const llvm::MCRegisterInfo &MCRegisterInfo;
38};
39
40class LatencySnippetGeneratorTest : public X86SnippetGeneratorTest {
41protected:
42 LatencySnippetGeneratorTest() : Runner(State) {}
43
44 BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
45 randomGenerator().seed(0); // Initialize seed.
46 auto ConfOrError = Runner.generateConfiguration(Opcode);
47 EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
48 return ConfOrError.get();
49 }
50
51 LatencyBenchmarkRunner Runner;
52};
53
54TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
55 // ADC16i16 self alias because of implicit use and def.
56
57 // explicit use 0 : imm
58 // implicit def : AX
59 // implicit def : EFLAGS
60 // implicit use : AX
61 // implicit use : EFLAGS
62 const unsigned Opcode = llvm::X86::ADC16i16;
63 auto Conf = checkAndGetConfiguration(Opcode);
64 EXPECT_THAT(Conf.Info, testing::HasSubstr("implicit"));
65 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
66 const llvm::MCInst Instr = Conf.Snippet[0];
67 EXPECT_THAT(Instr.getOpcode(), Opcode);
68 EXPECT_THAT(Instr.getNumOperands(), 1);
69 EXPECT_TRUE(Instr.getOperand(0).isImm()); // Use
70 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
71 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
72 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
73 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
74}
75
76TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
77 // ADD16ri self alias because Op0 and Op1 are tied together.
78
79 // explicit def 0 : reg RegClass=GR16
80 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
81 // explicit use 2 : imm
82 // implicit def : EFLAGS
83 const unsigned Opcode = llvm::X86::ADD16ri;
84 auto Conf = checkAndGetConfiguration(Opcode);
85 EXPECT_THAT(Conf.Info, testing::HasSubstr("explicit"));
86 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
87 const llvm::MCInst Instr = Conf.Snippet[0];
88 EXPECT_THAT(Instr.getOpcode(), Opcode);
89 EXPECT_THAT(Instr.getNumOperands(), 3);
90 EXPECT_TRUE(Instr.getOperand(0).isReg());
91 EXPECT_TRUE(Instr.getOperand(1).isReg());
92 EXPECT_THAT(Instr.getOperand(0).getReg(), Instr.getOperand(1).getReg())
93 << "Op0 and Op1 should have the same value";
94 EXPECT_TRUE(Instr.getOperand(2).isImm());
95 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
96}
97
98TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
99 // CMP64rr
100 // explicit use 0 : reg RegClass=GR64
101 // explicit use 1 : reg RegClass=GR64
102 // implicit def : EFLAGS
103
104 const unsigned Opcode = llvm::X86::CMP64rr;
105 auto Conf = checkAndGetConfiguration(Opcode);
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000106 EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through"));
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000107 ASSERT_THAT(Conf.Snippet, testing::SizeIs(2));
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000108 const llvm::MCInst Instr = Conf.Snippet[0];
109 EXPECT_THAT(Instr.getOpcode(), Opcode);
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000110 // TODO: check that the two instructions alias each other.
111}
112
Guillaume Chatelet60e3d582018-06-13 13:53:56 +0000113TEST_F(LatencySnippetGeneratorTest, LAHF) {
114 const unsigned Opcode = llvm::X86::LAHF;
115 auto Conf = checkAndGetConfiguration(Opcode);
116 EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through"));
117 ASSERT_THAT(Conf.Snippet, testing::SizeIs(2));
118 const llvm::MCInst Instr = Conf.Snippet[0];
119 EXPECT_THAT(Instr.getOpcode(), Opcode);
120}
121
Guillaume Chateletc9f727b2018-06-13 13:24:41 +0000122class UopsSnippetGeneratorTest : public X86SnippetGeneratorTest {
123protected:
124 UopsSnippetGeneratorTest() : Runner(State) {}
125
126 BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
127 randomGenerator().seed(0); // Initialize seed.
128 auto ConfOrError = Runner.generateConfiguration(Opcode);
129 EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
130 return ConfOrError.get();
131 }
132
133 UopsBenchmarkRunner Runner;
134};
135
136TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
137 // BNDCL32rr is parallelno matter what.
138
139 // explicit use 0 : reg RegClass=BNDR
140 // explicit use 1 : reg RegClass=GR32
141
142 const unsigned Opcode = llvm::X86::BNDCL32rr;
143 auto Conf = checkAndGetConfiguration(Opcode);
144 EXPECT_THAT(Conf.Info, testing::HasSubstr("parallel"));
145 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
146 const llvm::MCInst Instr = Conf.Snippet[0];
147 EXPECT_THAT(Instr.getOpcode(), Opcode);
148}
149
150TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
151 // CDQ is serial no matter what.
152
153 // implicit def : EAX
154 // implicit def : EDX
155 // implicit use : EAX
156 const unsigned Opcode = llvm::X86::CDQ;
157 auto Conf = checkAndGetConfiguration(Opcode);
158 EXPECT_THAT(Conf.Info, testing::HasSubstr("serial"));
159 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
160 const llvm::MCInst Instr = Conf.Snippet[0];
161 EXPECT_THAT(Instr.getOpcode(), Opcode);
162}
163
164TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
165 // CMOVA32rr has tied variables, we enumarate the possible values to execute
166 // as many in parallel as possible.
167
168 // explicit def 0 : reg RegClass=GR32
169 // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
170 // explicit use 2 : reg RegClass=GR32
171 // implicit use : EFLAGS
172 const unsigned Opcode = llvm::X86::CMOVA32rr;
173 auto Conf = checkAndGetConfiguration(Opcode);
174 EXPECT_THAT(Conf.Info, testing::HasSubstr("static renaming"));
175 constexpr const unsigned kInstructionCount = 15;
176 ASSERT_THAT(Conf.Snippet, testing::SizeIs(kInstructionCount));
177 std::unordered_set<unsigned> AllDefRegisters;
178 for (const auto &Inst : Conf.Snippet)
179 AllDefRegisters.insert(Inst.getOperand(0).getReg());
180 EXPECT_THAT(AllDefRegisters, testing::SizeIs(kInstructionCount))
181 << "Each instruction writes to a different register";
182}
183
184TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
185 // CMOV_GR32 has no tied variables, we make sure def and use are different
186 // from each other.
187
188 // explicit def 0 : reg RegClass=GR32
189 // explicit use 1 : reg RegClass=GR32
190 // explicit use 2 : reg RegClass=GR32
191 // explicit use 3 : imm
192 // implicit use : EFLAGS
193 const unsigned Opcode = llvm::X86::CMOV_GR32;
194 auto Conf = checkAndGetConfiguration(Opcode);
195 EXPECT_THAT(Conf.Info, testing::HasSubstr("no tied variables"));
196 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
197 const llvm::MCInst Instr = Conf.Snippet[0];
198 EXPECT_THAT(Instr.getOpcode(), Opcode);
199 EXPECT_THAT(Instr.getNumOperands(), 4);
200 EXPECT_THAT(Instr.getOperand(0).getReg(),
201 testing::Not(Instr.getOperand(1).getReg()))
202 << "Def is different from first Use";
203 EXPECT_THAT(Instr.getOperand(0).getReg(),
204 testing::Not(Instr.getOperand(2).getReg()))
205 << "Def is different from second Use";
206 EXPECT_THAT(Instr.getOperand(3).getImm(), 1);
207}
208
209} // namespace
210} // namespace exegesis