xref: /llvm-project/llvm/unittests/tools/llvm-exegesis/X86/SnippetGeneratorTest.cpp (revision 3173a4fc3aca3b8b21e9064cc38383be3539b3a4)
1 //===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "../Common/AssemblerUtils.h"
10 #include "LlvmState.h"
11 #include "MCInstrDescView.h"
12 #include "ParallelSnippetGenerator.h"
13 #include "RegisterAliasing.h"
14 #include "SerialSnippetGenerator.h"
15 #include "TestBase.h"
16 #include "X86InstrInfo.h"
17 #include "llvm/ADT/SetOperations.h"
18 
19 #include <unordered_set>
20 
21 namespace llvm {
22 namespace exegesis {
23 
24 void InitializeX86ExegesisTarget();
25 
26 namespace {
27 
28 using testing::AnyOf;
29 using testing::ElementsAre;
30 using testing::Gt;
31 using testing::HasSubstr;
32 using testing::IsEmpty;
33 using testing::Not;
34 using testing::SizeIs;
35 
36 MATCHER(IsInvalid, "") { return !arg.isValid(); }
37 MATCHER(IsReg, "") { return arg.isReg(); }
38 
39 template <typename SnippetGeneratorT>
40 class X86SnippetGeneratorTest : public X86TestBase {
41 protected:
42   X86SnippetGeneratorTest() : Generator(State, SnippetGenerator::Options()),
43 	                      InstrInfo(State.getInstrInfo()) {}
44 
45   std::vector<CodeTemplate> checkAndGetCodeTemplates(unsigned Opcode) {
46     randomGenerator().seed(0); // Initialize seed.
47     const Instruction &Instr = State.getIC().getInstr(Opcode);
48     auto CodeTemplateOrError = Generator.generateCodeTemplates(
49         &Instr, State.getRATC().emptyRegisters());
50     EXPECT_FALSE(CodeTemplateOrError.takeError()); // Valid configuration.
51     return std::move(CodeTemplateOrError.get());
52   }
53 
54   SnippetGeneratorT Generator;
55   const MCInstrInfo &InstrInfo;
56 };
57 
58 using X86SerialSnippetGeneratorTest = X86SnippetGeneratorTest<SerialSnippetGenerator>;
59 
60 using X86ParallelSnippetGeneratorTest =
61     X86SnippetGeneratorTest<ParallelSnippetGenerator>;
62 
63 TEST_F(X86SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughImplicitReg) {
64   // - ADC16i16
65   // - Op0 Explicit Use Immediate
66   // - Op1 Implicit Def Reg(AX)
67   // - Op2 Implicit Def Reg(EFLAGS)
68   // - Op3 Implicit Use Reg(AX)
69   // - Op4 Implicit Use Reg(EFLAGS)
70   // - Var0 [Op0]
71   // - hasAliasingImplicitRegisters (execution is always serial)
72   // - hasAliasingRegisters
73   const unsigned Opcode = X86::ADC16i16;
74   EXPECT_THAT(InstrInfo.get(Opcode).implicit_defs()[0], X86::AX);
75   EXPECT_THAT(InstrInfo.get(Opcode).implicit_defs()[1], X86::EFLAGS);
76   EXPECT_THAT(InstrInfo.get(Opcode).implicit_uses()[0], X86::AX);
77   EXPECT_THAT(InstrInfo.get(Opcode).implicit_uses()[1], X86::EFLAGS);
78   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
79   ASSERT_THAT(CodeTemplates, SizeIs(1));
80   const auto &CT = CodeTemplates[0];
81   EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS);
82   ASSERT_THAT(CT.Instructions, SizeIs(1));
83   const InstructionTemplate &IT = CT.Instructions[0];
84   EXPECT_THAT(IT.getOpcode(), Opcode);
85   ASSERT_THAT(IT.getVariableValues(), SizeIs(1)); // Imm.
86   EXPECT_THAT(IT.getVariableValues()[0], IsInvalid()) << "Immediate is not set";
87 }
88 
89 TEST_F(X86SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughTiedRegs) {
90   // - ADD16ri
91   // - Op0 Explicit Def RegClass(GR16)
92   // - Op1 Explicit Use RegClass(GR16) TiedToOp0
93   // - Op2 Explicit Use Immediate
94   // - Op3 Implicit Def Reg(EFLAGS)
95   // - Var0 [Op0,Op1]
96   // - Var1 [Op2]
97   // - hasTiedRegisters (execution is always serial)
98   // - hasAliasingRegisters
99   const unsigned Opcode = X86::ADD16ri;
100   EXPECT_THAT(InstrInfo.get(Opcode).implicit_defs()[0], X86::EFLAGS);
101   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
102   ASSERT_THAT(CodeTemplates, SizeIs(1));
103   const auto &CT = CodeTemplates[0];
104   EXPECT_THAT(CT.Execution, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS);
105   ASSERT_THAT(CT.Instructions, SizeIs(1));
106   const InstructionTemplate &IT = CT.Instructions[0];
107   EXPECT_THAT(IT.getOpcode(), Opcode);
108   ASSERT_THAT(IT.getVariableValues(), SizeIs(2));
109   EXPECT_THAT(IT.getVariableValues()[0], IsInvalid()) << "Operand 1 is not set";
110   EXPECT_THAT(IT.getVariableValues()[1], IsInvalid()) << "Operand 2 is not set";
111 }
112 
113 TEST_F(X86SerialSnippetGeneratorTest, ImplicitSelfDependencyThroughExplicitRegs) {
114   // - VXORPSrr
115   // - Op0 Explicit Def RegClass(VR128)
116   // - Op1 Explicit Use RegClass(VR128)
117   // - Op2 Explicit Use RegClass(VR128)
118   // - Var0 [Op0]
119   // - Var1 [Op1]
120   // - Var2 [Op2]
121   // - hasAliasingRegisters
122   const unsigned Opcode = X86::VXORPSrr;
123   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
124   ASSERT_THAT(CodeTemplates, SizeIs(1));
125   const auto &CT = CodeTemplates[0];
126   EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS);
127   ASSERT_THAT(CT.Instructions, SizeIs(1));
128   const InstructionTemplate &IT = CT.Instructions[0];
129   EXPECT_THAT(IT.getOpcode(), Opcode);
130   ASSERT_THAT(IT.getVariableValues(), SizeIs(3));
131   EXPECT_THAT(IT.getVariableValues(),
132               AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),
133                     ElementsAre(IsReg(), IsReg(), IsInvalid())))
134       << "Op0 is either set to Op1 or to Op2";
135 }
136 
137 TEST_F(X86SerialSnippetGeneratorTest,
138        ImplicitSelfDependencyThroughExplicitRegsForbidAll) {
139   // - VXORPSrr
140   // - Op0 Explicit Def RegClass(VR128)
141   // - Op1 Explicit Use RegClass(VR128)
142   // - Op2 Explicit Use RegClass(VR128)
143   // - Var0 [Op0]
144   // - Var1 [Op1]
145   // - Var2 [Op2]
146   // - hasAliasingRegisters
147   const unsigned Opcode = X86::VXORPSrr;
148   randomGenerator().seed(0); // Initialize seed.
149   const Instruction &Instr = State.getIC().getInstr(Opcode);
150   auto AllRegisters = State.getRATC().emptyRegisters();
151   AllRegisters.flip();
152   auto Error =
153       Generator.generateCodeTemplates(&Instr, AllRegisters).takeError();
154   EXPECT_TRUE((bool)Error);
155   consumeError(std::move(Error));
156 }
157 
158 TEST_F(X86SerialSnippetGeneratorTest,
159        ImplicitSelfDependencyThroughExplicitRegsForbidAlmostAll) {
160   // - VXORPSrr
161   // - Op0 Explicit Def RegClass(VR128)
162   // - Op1 Explicit Use RegClass(VR128)
163   // - Op2 Explicit Use RegClass(VR128)
164   // - Var0 [Op0]
165   // - Var1 [Op1]
166   // - Var2 [Op2]
167   // - hasAliasingRegisters
168   const unsigned Opcode = X86::VXORPSrr;
169   randomGenerator().seed(0); // Initialize seed.
170   const Instruction &Instr = State.getIC().getInstr(Opcode);
171   auto ForbiddenRegisters = State.getRATC().emptyRegisters();
172   ForbiddenRegisters.flip();
173   ForbiddenRegisters.reset(X86::XMM0);
174   auto Error = Generator.generateCodeTemplates(&Instr, ForbiddenRegisters);
175   EXPECT_FALSE((bool)Error.takeError());
176   auto CodeTemplates = std::move(Error.get());
177   ASSERT_THAT(CodeTemplates, SizeIs(Gt(0U))) << "Templates are available";
178   for (const auto &CT : CodeTemplates) {
179     EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS);
180     ASSERT_THAT(CT.Instructions, SizeIs(1));
181     const InstructionTemplate &IT = CT.Instructions[0];
182     EXPECT_THAT(IT.getOpcode(), Opcode);
183     ASSERT_THAT(IT.getVariableValues(), SizeIs(3));
184     for (const auto &Var : IT.getVariableValues()) {
185       if (Var.isReg()) {
186         EXPECT_FALSE(ForbiddenRegisters[Var.getReg().id()]);
187       }
188     }
189   }
190 }
191 
192 TEST_F(X86SerialSnippetGeneratorTest, DependencyThroughOtherOpcode) {
193   // - CMP64rr
194   // - Op0 Explicit Use RegClass(GR64)
195   // - Op1 Explicit Use RegClass(GR64)
196   // - Op2 Implicit Def Reg(EFLAGS)
197   // - Var0 [Op0]
198   // - Var1 [Op1]
199   const unsigned Opcode = X86::CMP64rr;
200   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
201   ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available";
202   for (const auto &CT : CodeTemplates) {
203     EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR);
204     ASSERT_THAT(CT.Instructions, SizeIs(2));
205     const InstructionTemplate &IT = CT.Instructions[0];
206     EXPECT_THAT(IT.getOpcode(), Opcode);
207     ASSERT_THAT(IT.getVariableValues(), SizeIs(2));
208     EXPECT_THAT(IT.getVariableValues(),
209                 AnyOf(ElementsAre(IsReg(), IsInvalid()),
210                       ElementsAre(IsInvalid(), IsReg())));
211     EXPECT_THAT(CT.Instructions[1].getOpcode(), Not(Opcode));
212     // TODO: check that the two instructions alias each other.
213   }
214 }
215 
216 TEST_F(X86SerialSnippetGeneratorTest, LAHF) {
217   // - LAHF
218   // - Op0 Implicit Def Reg(AH)
219   // - Op1 Implicit Use Reg(EFLAGS)
220   const unsigned Opcode = X86::LAHF;
221   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
222   ASSERT_THAT(CodeTemplates, SizeIs(Gt(1U))) << "Many templates are available";
223   for (const auto &CT : CodeTemplates) {
224     EXPECT_THAT(CT.Execution, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR);
225     ASSERT_THAT(CT.Instructions, SizeIs(2));
226     const InstructionTemplate &IT = CT.Instructions[0];
227     EXPECT_THAT(IT.getOpcode(), Opcode);
228     ASSERT_THAT(IT.getVariableValues(), SizeIs(0));
229   }
230 }
231 
232 TEST_F(X86SerialSnippetGeneratorTest, VCVTUSI642SDZrrb_Int) {
233   // - VCVTUSI642SDZrrb_Int
234   // - Op0 Explicit Def RegClass(VR128X)
235   // - Op1 Explicit Use RegClass(VR128X)
236   // - Op2 Explicit Use STATIC_ROUNDING
237   // - Op2 Explicit Use RegClass(GR64)
238   // - Op4 Implicit Use Reg(MXSCR)
239   const unsigned Opcode = X86::VCVTUSI642SDZrrb_Int;
240   const Instruction &Instr = State.getIC().getInstr(Opcode);
241   std::vector<BenchmarkCode> Configs;
242   auto Error = Generator.generateConfigurations(
243       &Instr, Configs, State.getRATC().emptyRegisters());
244   ASSERT_FALSE(Error);
245   ASSERT_THAT(Configs, SizeIs(1));
246   const BenchmarkCode &BC = Configs[0];
247   ASSERT_THAT(BC.Key.Instructions, SizeIs(1));
248   ASSERT_TRUE(BC.Key.Instructions[0].getOperand(3).isImm());
249 }
250 
251 TEST_F(X86ParallelSnippetGeneratorTest, SerialInstruction) {
252   // - CDQ
253   // - Op0 Implicit Def Reg(EAX)
254   // - Op1 Implicit Def Reg(EDX)
255   // - Op2 Implicit Use Reg(EAX)
256   // - hasAliasingImplicitRegisters (execution is always serial)
257   // - hasAliasingRegisters
258   const unsigned Opcode = X86::CDQ;
259   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
260   ASSERT_THAT(CodeTemplates, SizeIs(1));
261   const auto &CT = CodeTemplates[0];
262   EXPECT_THAT(CT.Info, HasSubstr("serial"));
263   EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);
264   ASSERT_THAT(CT.Instructions, SizeIs(1));
265   const InstructionTemplate &IT = CT.Instructions[0];
266   EXPECT_THAT(IT.getOpcode(), Opcode);
267   ASSERT_THAT(IT.getVariableValues(), SizeIs(0));
268 }
269 
270 TEST_F(X86ParallelSnippetGeneratorTest, ReadAfterWrite_CMOV32rr) {
271   // CMOV32rr has tied variables, we enumerate the possible values to execute
272   // as many in parallel as possible.
273 
274   // - CMOV32rr
275   // - Op0 Explicit Def RegClass(GR32)
276   // - Op1 Explicit Use RegClass(GR32) TiedToOp0
277   // - Op2 Explicit Use RegClass(GR32)
278   // - Op3 Explicit Use Immediate
279   // - Op3 Implicit Use Reg(EFLAGS)
280   // - Var0 [Op0,Op1]
281   // - Var1 [Op2]
282   // - hasTiedRegisters (execution is always serial)
283   // - hasAliasingRegisters
284   const unsigned Opcode = X86::CMOV32rr;
285   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
286   ASSERT_THAT(CodeTemplates, SizeIs(2));
287   for (const auto &CT : CodeTemplates) {
288     EXPECT_THAT(CT.Info, HasSubstr("avoiding Read-After-Write issue"));
289     EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);
290     ASSERT_GT(CT.Instructions.size(), 1U);
291     std::set<MCRegister> AllDefRegisters;
292     std::set<MCRegister> AllUseRegisters;
293     for (const auto &IT : CT.Instructions) {
294       ASSERT_THAT(IT.getVariableValues(), SizeIs(3));
295       AllDefRegisters.insert(IT.getVariableValues()[0].getReg());
296       AllUseRegisters.insert(IT.getVariableValues()[1].getReg());
297     }
298     EXPECT_THAT(AllDefRegisters, SizeIs(CT.Instructions.size()))
299         << "Each instruction writes to a different register";
300     EXPECT_THAT(AllUseRegisters, Not(IsEmpty()))
301         << "In total, some other registers are used";
302     auto AllDefAndUseRegs = AllUseRegisters;
303     llvm::set_intersect(AllDefAndUseRegs, AllDefRegisters); // A := A ^ B
304     EXPECT_THAT(AllDefAndUseRegs, IsEmpty())
305         << "No instruction uses any register defined by any of the "
306            "instructions";
307   }
308 }
309 
310 TEST_F(X86ParallelSnippetGeneratorTest, ReadAfterWrite_VFMADD132PDr) {
311   // VFMADD132PDr has tied variables, we enumerate the possible values
312   // to execute as many in parallel as possible.
313 
314   // - VFMADD132PDr
315   // - Op0 Explicit Def RegClass(XMM)
316   // - Op1 Explicit Use RegClass(XMM) TiedToOp0
317   // - Op2 Explicit Use RegClass(XMM)
318   // - Op3 Explicit Use RegClass(XMM)
319   // - Var0 [Op0,Op1]
320   // - Var1 [Op2]
321   // - Var2 [Op3]
322   // - hasTiedRegisters (execution is always serial)
323   // - hasAliasingRegisters
324   const unsigned Opcode = X86::VFMADD132PDr;
325   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
326   ASSERT_THAT(CodeTemplates, SizeIs(3));
327   for (const auto &CT : CodeTemplates) {
328     EXPECT_THAT(CT.Info, HasSubstr("avoiding Read-After-Write issue"));
329     EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);
330     ASSERT_GT(CT.Instructions.size(), 1U);
331     std::set<MCRegister> AllDefRegisters;
332     std::set<MCRegister> AllUseRegisters;
333     for (const auto &IT : CT.Instructions) {
334       ASSERT_THAT(IT.getVariableValues(), SizeIs(3));
335       AllDefRegisters.insert(IT.getVariableValues()[0].getReg());
336       AllUseRegisters.insert(IT.getVariableValues()[1].getReg());
337       AllUseRegisters.insert(IT.getVariableValues()[2].getReg());
338     }
339     EXPECT_THAT(AllDefRegisters, SizeIs(CT.Instructions.size()))
340         << "Each instruction writes to a different register";
341     EXPECT_THAT(AllUseRegisters, Not(IsEmpty()))
342         << "In total, some other registers are used";
343     auto AllDefAndUseRegs = AllUseRegisters;
344     llvm::set_intersect(AllDefAndUseRegs, AllDefRegisters); // A := A ^ B
345     EXPECT_THAT(AllDefAndUseRegs, IsEmpty())
346         << "No instruction uses any register defined by any of the "
347            "instructions";
348   }
349 }
350 
351 TEST_F(X86ParallelSnippetGeneratorTest, NoTiedVariables) {
352   // CMOV_GR32 has no tied variables, we make sure def and use are different
353   // from each other.
354 
355   // - CMOV_GR32
356   // - Op0 Explicit Def RegClass(GR32)
357   // - Op1 Explicit Use RegClass(GR32)
358   // - Op2 Explicit Use RegClass(GR32)
359   // - Op3 Explicit Use Immediate
360   // - Op4 Implicit Use Reg(EFLAGS)
361   // - Var0 [Op0]
362   // - Var1 [Op1]
363   // - Var2 [Op2]
364   // - Var3 [Op3]
365   // - hasAliasingRegisters
366   const unsigned Opcode = X86::CMOV_GR32;
367   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
368   ASSERT_THAT(CodeTemplates, SizeIs(2));
369   for (const auto &CT : CodeTemplates) {
370     EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
371     EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);
372     ASSERT_THAT(CT.Instructions, SizeIs(1));
373     const InstructionTemplate &IT = CT.Instructions[0];
374     EXPECT_THAT(IT.getOpcode(), Opcode);
375     ASSERT_THAT(IT.getVariableValues(), SizeIs(4));
376     EXPECT_THAT(IT.getVariableValues()[0].getReg(),
377                 Not(IT.getVariableValues()[1].getReg()))
378         << "Def is different from first Use";
379     EXPECT_THAT(IT.getVariableValues()[0].getReg(),
380                 Not(IT.getVariableValues()[2].getReg()))
381         << "Def is different from second Use";
382     EXPECT_THAT(IT.getVariableValues()[3], IsInvalid());
383   }
384 }
385 
386 TEST_F(X86ParallelSnippetGeneratorTest, MemoryUse) {
387   // Mov32rm reads from memory.
388   // - MOV32rm
389   // - Op0 Explicit Def RegClass(GR32)
390   // - Op1 Explicit Use Memory RegClass(GR8)
391   // - Op2 Explicit Use Memory
392   // - Op3 Explicit Use Memory RegClass(GRH8)
393   // - Op4 Explicit Use Memory
394   // - Op5 Explicit Use Memory RegClass(SEGMENT_REG)
395   // - Var0 [Op0]
396   // - Var1 [Op1]
397   // - Var2 [Op2]
398   // - Var3 [Op3]
399   // - Var4 [Op4]
400   // - Var5 [Op5]
401   // - hasMemoryOperands
402   // - hasAliasingRegisters
403   const unsigned Opcode = X86::MOV32rm;
404   const auto CodeTemplates = checkAndGetCodeTemplates(Opcode);
405   ASSERT_THAT(CodeTemplates, SizeIs(1));
406   for (const auto &CT : CodeTemplates) {
407     EXPECT_THAT(CT.Info, HasSubstr("no tied variables"));
408     EXPECT_THAT(CT.Execution, ExecutionMode::UNKNOWN);
409     ASSERT_THAT(CT.Instructions,
410                 SizeIs(ParallelSnippetGenerator::kMinNumDifferentAddresses));
411     const InstructionTemplate &IT = CT.Instructions[0];
412     EXPECT_THAT(IT.getOpcode(), Opcode);
413     ASSERT_THAT(IT.getVariableValues(), SizeIs(6));
414     EXPECT_EQ(IT.getVariableValues()[2].getImm(), 1);
415     EXPECT_FALSE(IT.getVariableValues()[3].getReg().isValid());
416     EXPECT_EQ(IT.getVariableValues()[4].getImm(), 0);
417     EXPECT_FALSE(IT.getVariableValues()[5].getReg().isValid());
418   }
419 }
420 
421 TEST_F(X86ParallelSnippetGeneratorTest, MOV16ms) {
422   const unsigned Opcode = X86::MOV16ms;
423   const Instruction &Instr = State.getIC().getInstr(Opcode);
424   std::vector<BenchmarkCode> Benchmarks;
425   auto Err = Generator.generateConfigurations(&Instr, Benchmarks,
426                                               State.getRATC().emptyRegisters());
427   EXPECT_TRUE((bool)Err);
428   EXPECT_THAT(toString(std::move(Err)),
429               testing::HasSubstr("no available registers"));
430 }
431 
432 TEST_F(X86ParallelSnippetGeneratorTest,
433        AvoidSerializingThroughImplicitRegisters) {
434   // MULX32rr implicitly uses EDX. We should not select that register to avoid
435   // serialization.
436   const unsigned Opcode = X86::MULX32rr;
437   randomGenerator().seed(0); // Initialize seed.
438   const Instruction &Instr = State.getIC().getInstr(Opcode);
439   // Forbid all registers but RDX/EDX/DX/DH/DL. The only option would be to
440   // choose that register, but that would serialize the instruction, so we
441   // should be returning an error.
442   auto AllRegisters = State.getRATC().emptyRegisters();
443   AllRegisters.flip();
444   AllRegisters.reset(X86::RDX);
445   AllRegisters.reset(X86::EDX);
446   AllRegisters.reset(X86::DX);
447   AllRegisters.reset(X86::DH);
448   AllRegisters.reset(X86::DL);
449   auto Err = Generator.generateCodeTemplates(&Instr, AllRegisters);
450   EXPECT_FALSE((bool)Err);
451   EXPECT_THAT(toString(Err.takeError()),
452               testing::HasSubstr("Failed to produce any snippet"));
453 }
454 
455 class X86FakeSnippetGenerator : public SnippetGenerator {
456 public:
457   X86FakeSnippetGenerator(const LLVMState &State, const Options &Opts)
458       : SnippetGenerator(State, Opts) {}
459 
460   const Instruction &getInstr(unsigned Opcode) {
461     return State.getIC().getInstr(Opcode);
462   }
463 
464   InstructionTemplate getInstructionTemplate(unsigned Opcode) {
465     return {&getInstr(Opcode)};
466   }
467 
468 private:
469   Expected<std::vector<CodeTemplate>>
470   generateCodeTemplates(InstructionTemplate, const BitVector &) const override {
471     return make_error<StringError>("not implemented", inconvertibleErrorCode());
472   }
473 };
474 
475 using X86FakeSnippetGeneratorTest = X86SnippetGeneratorTest<X86FakeSnippetGenerator>;
476 
477 testing::Matcher<const RegisterValue &> IsRegisterValue(unsigned Reg,
478                                                         APInt Value) {
479   return testing::AllOf(testing::Field(&RegisterValue::Register, Reg),
480                         testing::Field(&RegisterValue::Value, Value));
481 }
482 
483 TEST_F(X86FakeSnippetGeneratorTest, MemoryUse_Movsb) {
484   // MOVSB writes to scratch memory register.
485   // - MOVSB
486   // - Op0 Explicit Use Memory RegClass(GR8)
487   // - Op1 Explicit Use Memory RegClass(GR8)
488   // - Op2 Explicit Use Memory RegClass(SEGMENT_REG)
489   // - Op3 Implicit Def Reg(EDI)
490   // - Op4 Implicit Def Reg(ESI)
491   // - Op5 Implicit Use Reg(EDI)
492   // - Op6 Implicit Use Reg(ESI)
493   // - Op7 Implicit Use Reg(DF)
494   // - Var0 [Op0]
495   // - Var1 [Op1]
496   // - Var2 [Op2]
497   // - hasMemoryOperands
498   // - hasAliasingImplicitRegisters (execution is always serial)
499   // - hasAliasingRegisters
500   const unsigned Opcode = X86::MOVSB;
501   const Instruction &Instr = State.getIC().getInstr(Opcode);
502   std::vector<BenchmarkCode> Benchmarks;
503   auto Error = Generator.generateConfigurations(
504       &Instr, Benchmarks, State.getRATC().emptyRegisters());
505   EXPECT_TRUE((bool)Error);
506   consumeError(std::move(Error));
507 }
508 
509 TEST_F(X86FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd16ri) {
510   // ADD16ri:
511   // explicit def 0       : reg RegClass=GR16
512   // explicit use 1       : reg RegClass=GR16 | TIED_TO:0
513   // explicit use 2       : imm
514   // implicit def         : EFLAGS
515   InstructionTemplate IT = Generator.getInstructionTemplate(X86::ADD16ri);
516   IT.getValueFor(IT.getInstr().Variables[0]) = MCOperand::createReg(X86::AX);
517   std::vector<InstructionTemplate> Snippet;
518   Snippet.push_back(std::move(IT));
519   const auto RIV = Generator.computeRegisterInitialValues(Snippet);
520   EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(X86::AX, APInt())));
521 }
522 
523 TEST_F(X86FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd64rr) {
524   // ADD64rr:
525   //  mov64ri rax, 42
526   //  add64rr rax, rax, rbx
527   // -> only rbx needs defining.
528   std::vector<InstructionTemplate> Snippet;
529   {
530     InstructionTemplate Mov = Generator.getInstructionTemplate(X86::MOV64ri);
531     Mov.getValueFor(Mov.getInstr().Variables[0]) =
532         MCOperand::createReg(X86::RAX);
533     Mov.getValueFor(Mov.getInstr().Variables[1]) = MCOperand::createImm(42);
534     Snippet.push_back(std::move(Mov));
535   }
536   {
537     InstructionTemplate Add = Generator.getInstructionTemplate(X86::ADD64rr);
538     Add.getValueFor(Add.getInstr().Variables[0]) =
539         MCOperand::createReg(X86::RAX);
540     Add.getValueFor(Add.getInstr().Variables[1]) =
541         MCOperand::createReg(X86::RBX);
542     Snippet.push_back(std::move(Add));
543   }
544 
545   const auto RIV = Generator.computeRegisterInitialValues(Snippet);
546   EXPECT_THAT(RIV, ElementsAre(IsRegisterValue(X86::RBX, APInt())));
547 }
548 
549 } // namespace
550 } // namespace exegesis
551 } // namespace llvm
552