blob: 0e4a3fb8f1767b2c43440bef5a268f328aad0a9d [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);
106 EXPECT_THAT(Conf.Info, testing::HasSubstr("cycle through CMOVLE16rr"));
107 ASSERT_THAT(Conf.Snippet, testing::SizeIs(2));
108 // TODO: check that the two instructions alias each other.
109}
110
111class UopsSnippetGeneratorTest : public X86SnippetGeneratorTest {
112protected:
113 UopsSnippetGeneratorTest() : Runner(State) {}
114
115 BenchmarkConfiguration checkAndGetConfiguration(unsigned Opcode) {
116 randomGenerator().seed(0); // Initialize seed.
117 auto ConfOrError = Runner.generateConfiguration(Opcode);
118 EXPECT_FALSE(ConfOrError.takeError()); // Valid configuration.
119 return ConfOrError.get();
120 }
121
122 UopsBenchmarkRunner Runner;
123};
124
125TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
126 // BNDCL32rr is parallelno matter what.
127
128 // explicit use 0 : reg RegClass=BNDR
129 // explicit use 1 : reg RegClass=GR32
130
131 const unsigned Opcode = llvm::X86::BNDCL32rr;
132 auto Conf = checkAndGetConfiguration(Opcode);
133 EXPECT_THAT(Conf.Info, testing::HasSubstr("parallel"));
134 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
135 const llvm::MCInst Instr = Conf.Snippet[0];
136 EXPECT_THAT(Instr.getOpcode(), Opcode);
137}
138
139TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
140 // CDQ is serial no matter what.
141
142 // implicit def : EAX
143 // implicit def : EDX
144 // implicit use : EAX
145 const unsigned Opcode = llvm::X86::CDQ;
146 auto Conf = checkAndGetConfiguration(Opcode);
147 EXPECT_THAT(Conf.Info, testing::HasSubstr("serial"));
148 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
149 const llvm::MCInst Instr = Conf.Snippet[0];
150 EXPECT_THAT(Instr.getOpcode(), Opcode);
151}
152
153TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
154 // CMOVA32rr has tied variables, we enumarate the possible values to execute
155 // as many in parallel as possible.
156
157 // explicit def 0 : reg RegClass=GR32
158 // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
159 // explicit use 2 : reg RegClass=GR32
160 // implicit use : EFLAGS
161 const unsigned Opcode = llvm::X86::CMOVA32rr;
162 auto Conf = checkAndGetConfiguration(Opcode);
163 EXPECT_THAT(Conf.Info, testing::HasSubstr("static renaming"));
164 constexpr const unsigned kInstructionCount = 15;
165 ASSERT_THAT(Conf.Snippet, testing::SizeIs(kInstructionCount));
166 std::unordered_set<unsigned> AllDefRegisters;
167 for (const auto &Inst : Conf.Snippet)
168 AllDefRegisters.insert(Inst.getOperand(0).getReg());
169 EXPECT_THAT(AllDefRegisters, testing::SizeIs(kInstructionCount))
170 << "Each instruction writes to a different register";
171}
172
173TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
174 // CMOV_GR32 has no tied variables, we make sure def and use are different
175 // from each other.
176
177 // explicit def 0 : reg RegClass=GR32
178 // explicit use 1 : reg RegClass=GR32
179 // explicit use 2 : reg RegClass=GR32
180 // explicit use 3 : imm
181 // implicit use : EFLAGS
182 const unsigned Opcode = llvm::X86::CMOV_GR32;
183 auto Conf = checkAndGetConfiguration(Opcode);
184 EXPECT_THAT(Conf.Info, testing::HasSubstr("no tied variables"));
185 ASSERT_THAT(Conf.Snippet, testing::SizeIs(1));
186 const llvm::MCInst Instr = Conf.Snippet[0];
187 EXPECT_THAT(Instr.getOpcode(), Opcode);
188 EXPECT_THAT(Instr.getNumOperands(), 4);
189 EXPECT_THAT(Instr.getOperand(0).getReg(),
190 testing::Not(Instr.getOperand(1).getReg()))
191 << "Def is different from first Use";
192 EXPECT_THAT(Instr.getOperand(0).getReg(),
193 testing::Not(Instr.getOperand(2).getReg()))
194 << "Def is different from second Use";
195 EXPECT_THAT(Instr.getOperand(3).getImm(), 1);
196}
197
198} // namespace
199} // namespace exegesis