[llvm-exegesis] Rename InstructionInstance into InstructionBuilder.
[llvm-core.git] / unittests / tools / llvm-exegesis / X86 / SnippetGeneratorTest.cpp
blob7452b838beb6092897f48f8088b9cdc5d0ab1ee2
1 //===-- 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 //===----------------------------------------------------------------------===//
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"
18 #include <unordered_set>
20 namespace exegesis {
22 void InitializeX86ExegesisTarget();
24 namespace {
26 using testing::AnyOf;
27 using testing::ElementsAre;
28 using testing::HasSubstr;
29 using testing::Not;
30 using testing::SizeIs;
31 using testing::UnorderedElementsAre;
33 MATCHER(IsInvalid, "") { return !arg.isValid(); }
34 MATCHER(IsReg, "") { return arg.isReg(); }
36 class X86SnippetGeneratorTest : public ::testing::Test {
37 protected:
38 X86SnippetGeneratorTest()
39 : State("x86_64-unknown-linux", "haswell"),
40 MCInstrInfo(State.getInstrInfo()), MCRegisterInfo(State.getRegInfo()) {}
42 static void SetUpTestCase() {
43 LLVMInitializeX86TargetInfo();
44 LLVMInitializeX86TargetMC();
45 LLVMInitializeX86Target();
46 LLVMInitializeX86AsmPrinter();
47 InitializeX86ExegesisTarget();
50 const LLVMState State;
51 const llvm::MCInstrInfo &MCInstrInfo;
52 const llvm::MCRegisterInfo &MCRegisterInfo;
55 template <typename BenchmarkRunner>
56 class SnippetGeneratorTest : public X86SnippetGeneratorTest {
57 protected:
58 SnippetGeneratorTest() : Runner(State) {}
60 SnippetPrototype checkAndGetConfigurations(unsigned Opcode) {
61 randomGenerator().seed(0); // Initialize seed.
62 auto ProtoOrError = Runner.generatePrototype(Opcode);
63 EXPECT_FALSE(ProtoOrError.takeError()); // Valid configuration.
64 return std::move(ProtoOrError.get());
67 BenchmarkRunner Runner;
70 using LatencySnippetGeneratorTest =
71 SnippetGeneratorTest<LatencyBenchmarkRunner>;
73 using UopsSnippetGeneratorTest = SnippetGeneratorTest<UopsBenchmarkRunner>;
75 TEST_F(LatencySnippetGeneratorTest, ImplicitSelfDependency) {
76 // ADC16i16 self alias because of implicit use and def.
78 // explicit use 0 : imm
79 // implicit def : AX
80 // implicit def : EFLAGS
81 // implicit use : AX
82 // implicit use : EFLAGS
83 const unsigned Opcode = llvm::X86::ADC16i16;
84 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::AX);
85 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[1], llvm::X86::EFLAGS);
86 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[0], llvm::X86::AX);
87 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitUses()[1], llvm::X86::EFLAGS);
88 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
89 EXPECT_THAT(Proto.Explanation, HasSubstr("implicit"));
90 ASSERT_THAT(Proto.Snippet, SizeIs(1));
91 const InstructionBuilder &IB = Proto.Snippet[0];
92 EXPECT_THAT(IB.getOpcode(), Opcode);
93 ASSERT_THAT(IB.VariableValues, SizeIs(1)); // Imm.
94 EXPECT_THAT(IB.VariableValues[0], IsInvalid()) << "Immediate is not set";
97 TEST_F(LatencySnippetGeneratorTest, ExplicitSelfDependency) {
98 // ADD16ri self alias because Op0 and Op1 are tied together.
100 // explicit def 0 : reg RegClass=GR16
101 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
102 // explicit use 2 : imm
103 // implicit def : EFLAGS
104 const unsigned Opcode = llvm::X86::ADD16ri;
105 EXPECT_THAT(MCInstrInfo.get(Opcode).getImplicitDefs()[0], llvm::X86::EFLAGS);
106 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
107 EXPECT_THAT(Proto.Explanation, HasSubstr("explicit"));
108 ASSERT_THAT(Proto.Snippet, SizeIs(1));
109 const InstructionBuilder &IB = Proto.Snippet[0];
110 EXPECT_THAT(IB.getOpcode(), Opcode);
111 ASSERT_THAT(IB.VariableValues, SizeIs(2));
112 EXPECT_THAT(IB.VariableValues[0], IsReg()) << "Operand 0 and 1";
113 EXPECT_THAT(IB.VariableValues[1], IsInvalid()) << "Operand 2 is not set";
116 TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
117 // CMP64rr
118 // explicit use 0 : reg RegClass=GR64
119 // explicit use 1 : reg RegClass=GR64
120 // implicit def : EFLAGS
122 const unsigned Opcode = llvm::X86::CMP64rr;
123 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
124 EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
125 ASSERT_THAT(Proto.Snippet, SizeIs(2));
126 const InstructionBuilder &IB = Proto.Snippet[0];
127 EXPECT_THAT(IB.getOpcode(), Opcode);
128 ASSERT_THAT(IB.VariableValues, SizeIs(2));
129 EXPECT_THAT(IB.VariableValues, AnyOf(ElementsAre(IsReg(), IsInvalid()),
130 ElementsAre(IsInvalid(), IsReg())));
131 EXPECT_THAT(Proto.Snippet[1].getOpcode(), Not(Opcode));
132 // TODO: check that the two instructions alias each other.
135 TEST_F(LatencySnippetGeneratorTest, LAHF) {
136 const unsigned Opcode = llvm::X86::LAHF;
137 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
138 EXPECT_THAT(Proto.Explanation, HasSubstr("cycle through"));
139 ASSERT_THAT(Proto.Snippet, SizeIs(2));
140 const InstructionBuilder &IB = Proto.Snippet[0];
141 EXPECT_THAT(IB.getOpcode(), Opcode);
142 ASSERT_THAT(IB.VariableValues, SizeIs(0));
145 TEST_F(UopsSnippetGeneratorTest, ParallelInstruction) {
146 // BNDCL32rr is parallel no matter what.
148 // explicit use 0 : reg RegClass=BNDR
149 // explicit use 1 : reg RegClass=GR32
151 const unsigned Opcode = llvm::X86::BNDCL32rr;
152 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
153 EXPECT_THAT(Proto.Explanation, HasSubstr("parallel"));
154 ASSERT_THAT(Proto.Snippet, SizeIs(1));
155 const InstructionBuilder &IB = Proto.Snippet[0];
156 EXPECT_THAT(IB.getOpcode(), Opcode);
157 ASSERT_THAT(IB.VariableValues, SizeIs(2));
158 EXPECT_THAT(IB.VariableValues[0], IsInvalid());
159 EXPECT_THAT(IB.VariableValues[1], IsInvalid());
162 TEST_F(UopsSnippetGeneratorTest, SerialInstruction) {
163 // CDQ is serial no matter what.
165 // implicit def : EAX
166 // implicit def : EDX
167 // implicit use : EAX
168 const unsigned Opcode = llvm::X86::CDQ;
169 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
170 EXPECT_THAT(Proto.Explanation, HasSubstr("serial"));
171 ASSERT_THAT(Proto.Snippet, SizeIs(1));
172 const InstructionBuilder &IB = Proto.Snippet[0];
173 EXPECT_THAT(IB.getOpcode(), Opcode);
174 ASSERT_THAT(IB.VariableValues, SizeIs(0));
177 TEST_F(UopsSnippetGeneratorTest, StaticRenaming) {
178 // CMOVA32rr has tied variables, we enumarate the possible values to execute
179 // as many in parallel as possible.
181 // explicit def 0 : reg RegClass=GR32
182 // explicit use 1 : reg RegClass=GR32 | TIED_TO:0
183 // explicit use 2 : reg RegClass=GR32
184 // implicit use : EFLAGS
185 const unsigned Opcode = llvm::X86::CMOVA32rr;
186 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
187 EXPECT_THAT(Proto.Explanation, HasSubstr("static renaming"));
188 constexpr const unsigned kInstructionCount = 15;
189 ASSERT_THAT(Proto.Snippet, SizeIs(kInstructionCount));
190 std::unordered_set<unsigned> AllDefRegisters;
191 for (const auto &IB : Proto.Snippet) {
192 ASSERT_THAT(IB.VariableValues, SizeIs(2));
193 AllDefRegisters.insert(IB.VariableValues[0].getReg());
195 EXPECT_THAT(AllDefRegisters, SizeIs(kInstructionCount))
196 << "Each instruction writes to a different register";
199 TEST_F(UopsSnippetGeneratorTest, NoTiedVariables) {
200 // CMOV_GR32 has no tied variables, we make sure def and use are different
201 // from each other.
203 // explicit def 0 : reg RegClass=GR32
204 // explicit use 1 : reg RegClass=GR32
205 // explicit use 2 : reg RegClass=GR32
206 // explicit use 3 : imm
207 // implicit use : EFLAGS
208 const unsigned Opcode = llvm::X86::CMOV_GR32;
209 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
210 EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables"));
211 ASSERT_THAT(Proto.Snippet, SizeIs(1));
212 const InstructionBuilder &IB = Proto.Snippet[0];
213 EXPECT_THAT(IB.getOpcode(), Opcode);
214 ASSERT_THAT(IB.VariableValues, SizeIs(4));
215 EXPECT_THAT(IB.VariableValues[0].getReg(), Not(IB.VariableValues[1].getReg()))
216 << "Def is different from first Use";
217 EXPECT_THAT(IB.VariableValues[0].getReg(), Not(IB.VariableValues[2].getReg()))
218 << "Def is different from second Use";
219 EXPECT_THAT(IB.VariableValues[3], IsInvalid());
222 TEST_F(UopsSnippetGeneratorTest, MemoryUse) {
223 // Mov32rm reads from memory.
224 const unsigned Opcode = llvm::X86::MOV32rm;
225 const SnippetPrototype Proto = checkAndGetConfigurations(Opcode);
226 EXPECT_THAT(Proto.Explanation, HasSubstr("no tied variables"));
227 ASSERT_THAT(Proto.Snippet,
228 SizeIs(UopsBenchmarkRunner::kMinNumDifferentAddresses));
229 const InstructionBuilder &IB = Proto.Snippet[0];
230 EXPECT_THAT(IB.getOpcode(), Opcode);
231 ASSERT_THAT(IB.VariableValues, SizeIs(6));
232 EXPECT_EQ(IB.VariableValues[2].getImm(), 1);
233 EXPECT_EQ(IB.VariableValues[3].getReg(), 0u);
234 EXPECT_EQ(IB.VariableValues[4].getImm(), 0);
235 EXPECT_EQ(IB.VariableValues[5].getReg(), 0u);
238 TEST_F(UopsSnippetGeneratorTest, MemoryUse_Movsb) {
239 // MOVSB writes to scratch memory register.
240 const unsigned Opcode = llvm::X86::MOVSB;
241 auto Error = Runner.generatePrototype(Opcode).takeError();
242 EXPECT_TRUE((bool)Error);
243 llvm::consumeError(std::move(Error));
246 class FakeBenchmarkRunner : public BenchmarkRunner {
247 public:
248 FakeBenchmarkRunner(const LLVMState &State)
249 : BenchmarkRunner(State, InstructionBenchmark::Unknown) {}
251 Instruction createInstruction(unsigned Opcode) {
252 return Instruction(State.getInstrInfo().get(Opcode), RATC);
255 private:
256 llvm::Expected<SnippetPrototype>
257 generatePrototype(unsigned Opcode) const override {
258 return llvm::make_error<llvm::StringError>("not implemented",
259 llvm::inconvertibleErrorCode());
262 std::vector<BenchmarkMeasure>
263 runMeasurements(const ExecutableFunction &EF, ScratchSpace &Scratch,
264 const unsigned NumRepetitions) const override {
265 return {};
269 using FakeSnippetGeneratorTest = SnippetGeneratorTest<FakeBenchmarkRunner>;
271 TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd16ri) {
272 // ADD16ri:
273 // explicit def 0 : reg RegClass=GR16
274 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
275 // explicit use 2 : imm
276 // implicit def : EFLAGS
277 InstructionBuilder IB(Runner.createInstruction(llvm::X86::ADD16ri));
278 IB.getValueFor(IB.Instr.Variables[0]) =
279 llvm::MCOperand::createReg(llvm::X86::AX);
280 std::vector<InstructionBuilder> Snippet;
281 Snippet.push_back(std::move(IB));
282 const auto RegsToDef = Runner.computeRegsToDef(Snippet);
283 EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::AX));
286 TEST_F(FakeSnippetGeneratorTest, ComputeRegsToDefAdd64rr) {
287 // ADD64rr:
288 // mov64ri rax, 42
289 // add64rr rax, rax, rbx
290 // -> only rbx needs defining.
291 std::vector<InstructionBuilder> Snippet;
293 InstructionBuilder Mov(Runner.createInstruction(llvm::X86::MOV64ri));
294 Mov.getValueFor(Mov.Instr.Variables[0]) =
295 llvm::MCOperand::createReg(llvm::X86::RAX);
296 Mov.getValueFor(Mov.Instr.Variables[1]) = llvm::MCOperand::createImm(42);
297 Snippet.push_back(std::move(Mov));
300 InstructionBuilder Add(Runner.createInstruction(llvm::X86::ADD64rr));
301 Add.getValueFor(Add.Instr.Variables[0]) =
302 llvm::MCOperand::createReg(llvm::X86::RAX);
303 Add.getValueFor(Add.Instr.Variables[1]) =
304 llvm::MCOperand::createReg(llvm::X86::RBX);
305 Snippet.push_back(std::move(Add));
308 const auto RegsToDef = Runner.computeRegsToDef(Snippet);
309 EXPECT_THAT(RegsToDef, UnorderedElementsAre(llvm::X86::RBX));
312 } // namespace
313 } // namespace exegesis