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