1 //===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
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
7 //===----------------------------------------------------------------------===//
9 #include "../Common/AssemblerUtils.h"
11 #include "LlvmState.h"
12 #include "MCInstrDescView.h"
13 #include "RegisterAliasing.h"
16 #include "X86InstrInfo.h"
18 #include <unordered_set>
23 void InitializeX86ExegesisTarget();
28 using testing::ElementsAre
;
30 using testing::HasSubstr
;
32 using testing::SizeIs
;
33 using testing::UnorderedElementsAre
;
35 MATCHER(IsInvalid
, "") { return !arg
.isValid(); }
36 MATCHER(IsReg
, "") { return arg
.isReg(); }
38 class X86SnippetGeneratorTest
: public X86TestBase
{
40 X86SnippetGeneratorTest() : InstrInfo(State
.getInstrInfo()) {}
42 const MCInstrInfo
&InstrInfo
;
45 template <typename SnippetGeneratorT
>
46 class SnippetGeneratorTest
: public X86SnippetGeneratorTest
{
48 SnippetGeneratorTest() : Generator(State
, SnippetGenerator::Options()) {}
50 std::vector
<CodeTemplate
> checkAndGetCodeTemplates(unsigned Opcode
) {
51 randomGenerator().seed(0); // Initialize seed.
52 const Instruction
&Instr
= State
.getIC().getInstr(Opcode
);
53 auto CodeTemplateOrError
= Generator
.generateCodeTemplates(
54 Instr
, State
.getRATC().emptyRegisters());
55 EXPECT_FALSE(CodeTemplateOrError
.takeError()); // Valid configuration.
56 return std::move(CodeTemplateOrError
.get());
59 SnippetGeneratorT Generator
;
62 using LatencySnippetGeneratorTest
=
63 SnippetGeneratorTest
<LatencySnippetGenerator
>;
65 using UopsSnippetGeneratorTest
= SnippetGeneratorTest
<UopsSnippetGenerator
>;
67 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependencyThroughImplicitReg
) {
69 // - Op0 Explicit Use Immediate
70 // - Op1 Implicit Def Reg(AX)
71 // - Op2 Implicit Def Reg(EFLAGS)
72 // - Op3 Implicit Use Reg(AX)
73 // - Op4 Implicit Use Reg(EFLAGS)
75 // - hasAliasingImplicitRegisters (execution is always serial)
76 // - hasAliasingRegisters
77 const unsigned Opcode
= X86::ADC16i16
;
78 EXPECT_THAT(InstrInfo
.get(Opcode
).getImplicitDefs()[0], X86::AX
);
79 EXPECT_THAT(InstrInfo
.get(Opcode
).getImplicitDefs()[1], X86::EFLAGS
);
80 EXPECT_THAT(InstrInfo
.get(Opcode
).getImplicitUses()[0], X86::AX
);
81 EXPECT_THAT(InstrInfo
.get(Opcode
).getImplicitUses()[1], X86::EFLAGS
);
82 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
83 ASSERT_THAT(CodeTemplates
, SizeIs(1));
84 const auto &CT
= CodeTemplates
[0];
85 EXPECT_THAT(CT
.Execution
, ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS
);
86 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
87 const InstructionTemplate
&IT
= CT
.Instructions
[0];
88 EXPECT_THAT(IT
.getOpcode(), Opcode
);
89 ASSERT_THAT(IT
.VariableValues
, SizeIs(1)); // Imm.
90 EXPECT_THAT(IT
.VariableValues
[0], IsInvalid()) << "Immediate is not set";
93 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependencyThroughTiedRegs
) {
95 // - Op0 Explicit Def RegClass(GR16)
96 // - Op1 Explicit Use RegClass(GR16) TiedToOp0
97 // - Op2 Explicit Use Immediate
98 // - Op3 Implicit Def Reg(EFLAGS)
101 // - hasTiedRegisters (execution is always serial)
102 // - hasAliasingRegisters
103 const unsigned Opcode
= X86::ADD16ri
;
104 EXPECT_THAT(InstrInfo
.get(Opcode
).getImplicitDefs()[0], X86::EFLAGS
);
105 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
106 ASSERT_THAT(CodeTemplates
, SizeIs(1));
107 const auto &CT
= CodeTemplates
[0];
108 EXPECT_THAT(CT
.Execution
, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS
);
109 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
110 const InstructionTemplate
&IT
= CT
.Instructions
[0];
111 EXPECT_THAT(IT
.getOpcode(), Opcode
);
112 ASSERT_THAT(IT
.VariableValues
, SizeIs(2));
113 EXPECT_THAT(IT
.VariableValues
[0], IsInvalid()) << "Operand 1 is not set";
114 EXPECT_THAT(IT
.VariableValues
[1], IsInvalid()) << "Operand 2 is not set";
117 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependencyThroughExplicitRegs
) {
119 // - Op0 Explicit Def RegClass(VR128)
120 // - Op1 Explicit Use RegClass(VR128)
121 // - Op2 Explicit Use RegClass(VR128)
125 // - hasAliasingRegisters
126 const unsigned Opcode
= X86::VXORPSrr
;
127 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
128 ASSERT_THAT(CodeTemplates
, SizeIs(1));
129 const auto &CT
= CodeTemplates
[0];
130 EXPECT_THAT(CT
.Execution
, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS
);
131 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
132 const InstructionTemplate
&IT
= CT
.Instructions
[0];
133 EXPECT_THAT(IT
.getOpcode(), Opcode
);
134 ASSERT_THAT(IT
.VariableValues
, SizeIs(3));
135 EXPECT_THAT(IT
.VariableValues
,
136 AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),
137 ElementsAre(IsReg(), IsReg(), IsInvalid())))
138 << "Op0 is either set to Op1 or to Op2";
141 TEST_F(LatencySnippetGeneratorTest
,
142 ImplicitSelfDependencyThroughExplicitRegsForbidAll
) {
144 // - Op0 Explicit Def RegClass(VR128)
145 // - Op1 Explicit Use RegClass(VR128)
146 // - Op2 Explicit Use RegClass(VR128)
150 // - hasAliasingRegisters
151 const unsigned Opcode
= X86::VXORPSrr
;
152 randomGenerator().seed(0); // Initialize seed.
153 const Instruction
&Instr
= State
.getIC().getInstr(Opcode
);
154 auto AllRegisters
= State
.getRATC().emptyRegisters();
156 auto Error
= Generator
.generateCodeTemplates(Instr
, AllRegisters
).takeError();
157 EXPECT_TRUE((bool)Error
);
158 consumeError(std::move(Error
));
161 TEST_F(LatencySnippetGeneratorTest
, DependencyThroughOtherOpcode
) {
163 // - Op0 Explicit Use RegClass(GR64)
164 // - Op1 Explicit Use RegClass(GR64)
165 // - Op2 Implicit Def Reg(EFLAGS)
168 const unsigned Opcode
= X86::CMP64rr
;
169 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
170 ASSERT_THAT(CodeTemplates
, SizeIs(Gt(1U))) << "Many templates are available";
171 for (const auto &CT
: CodeTemplates
) {
172 EXPECT_THAT(CT
.Execution
, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR
);
173 ASSERT_THAT(CT
.Instructions
, SizeIs(2));
174 const InstructionTemplate
&IT
= CT
.Instructions
[0];
175 EXPECT_THAT(IT
.getOpcode(), Opcode
);
176 ASSERT_THAT(IT
.VariableValues
, SizeIs(2));
177 EXPECT_THAT(IT
.VariableValues
, AnyOf(ElementsAre(IsReg(), IsInvalid()),
178 ElementsAre(IsInvalid(), IsReg())));
179 EXPECT_THAT(CT
.Instructions
[1].getOpcode(), Not(Opcode
));
180 // TODO: check that the two instructions alias each other.
184 TEST_F(LatencySnippetGeneratorTest
, LAHF
) {
186 // - Op0 Implicit Def Reg(AH)
187 // - Op1 Implicit Use Reg(EFLAGS)
188 const unsigned Opcode
= X86::LAHF
;
189 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
190 ASSERT_THAT(CodeTemplates
, SizeIs(Gt(1U))) << "Many templates are available";
191 for (const auto &CT
: CodeTemplates
) {
192 EXPECT_THAT(CT
.Execution
, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR
);
193 ASSERT_THAT(CT
.Instructions
, SizeIs(2));
194 const InstructionTemplate
&IT
= CT
.Instructions
[0];
195 EXPECT_THAT(IT
.getOpcode(), Opcode
);
196 ASSERT_THAT(IT
.VariableValues
, SizeIs(0));
200 TEST_F(UopsSnippetGeneratorTest
, ParallelInstruction
) {
202 // - Op0 Explicit Use RegClass(BNDR)
203 // - Op1 Explicit Use RegClass(GR32)
206 const unsigned Opcode
= X86::BNDCL32rr
;
207 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
208 ASSERT_THAT(CodeTemplates
, SizeIs(1));
209 const auto &CT
= CodeTemplates
[0];
210 EXPECT_THAT(CT
.Info
, HasSubstr("parallel"));
211 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
212 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
213 const InstructionTemplate
&IT
= CT
.Instructions
[0];
214 EXPECT_THAT(IT
.getOpcode(), Opcode
);
215 ASSERT_THAT(IT
.VariableValues
, SizeIs(2));
216 EXPECT_THAT(IT
.VariableValues
[0], IsInvalid());
217 EXPECT_THAT(IT
.VariableValues
[1], IsInvalid());
220 TEST_F(UopsSnippetGeneratorTest
, SerialInstruction
) {
222 // - Op0 Implicit Def Reg(EAX)
223 // - Op1 Implicit Def Reg(EDX)
224 // - Op2 Implicit Use Reg(EAX)
225 // - hasAliasingImplicitRegisters (execution is always serial)
226 // - hasAliasingRegisters
227 const unsigned Opcode
= X86::CDQ
;
228 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
229 ASSERT_THAT(CodeTemplates
, SizeIs(1));
230 const auto &CT
= CodeTemplates
[0];
231 EXPECT_THAT(CT
.Info
, HasSubstr("serial"));
232 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
233 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
234 const InstructionTemplate
&IT
= CT
.Instructions
[0];
235 EXPECT_THAT(IT
.getOpcode(), Opcode
);
236 ASSERT_THAT(IT
.VariableValues
, SizeIs(0));
239 TEST_F(UopsSnippetGeneratorTest
, StaticRenaming
) {
240 // CMOV32rr has tied variables, we enumerate the possible values to execute
241 // as many in parallel as possible.
244 // - Op0 Explicit Def RegClass(GR32)
245 // - Op1 Explicit Use RegClass(GR32) TiedToOp0
246 // - Op2 Explicit Use RegClass(GR32)
247 // - Op3 Explicit Use Immediate
248 // - Op3 Implicit Use Reg(EFLAGS)
251 // - hasTiedRegisters (execution is always serial)
252 // - hasAliasingRegisters
253 const unsigned Opcode
= X86::CMOV32rr
;
254 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
255 ASSERT_THAT(CodeTemplates
, SizeIs(1));
256 const auto &CT
= CodeTemplates
[0];
257 EXPECT_THAT(CT
.Info
, HasSubstr("static renaming"));
258 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
259 constexpr const unsigned kInstructionCount
= 15;
260 ASSERT_THAT(CT
.Instructions
, SizeIs(kInstructionCount
));
261 std::unordered_set
<unsigned> AllDefRegisters
;
262 for (const auto &IT
: CT
.Instructions
) {
263 ASSERT_THAT(IT
.VariableValues
, SizeIs(3));
264 AllDefRegisters
.insert(IT
.VariableValues
[0].getReg());
266 EXPECT_THAT(AllDefRegisters
, SizeIs(kInstructionCount
))
267 << "Each instruction writes to a different register";
270 TEST_F(UopsSnippetGeneratorTest
, NoTiedVariables
) {
271 // CMOV_GR32 has no tied variables, we make sure def and use are different
275 // - Op0 Explicit Def RegClass(GR32)
276 // - Op1 Explicit Use RegClass(GR32)
277 // - Op2 Explicit Use RegClass(GR32)
278 // - Op3 Explicit Use Immediate
279 // - Op4 Implicit Use Reg(EFLAGS)
284 // - hasAliasingRegisters
285 const unsigned Opcode
= X86::CMOV_GR32
;
286 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
287 ASSERT_THAT(CodeTemplates
, SizeIs(1));
288 const auto &CT
= CodeTemplates
[0];
289 EXPECT_THAT(CT
.Info
, HasSubstr("no tied variables"));
290 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
291 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
292 const InstructionTemplate
&IT
= CT
.Instructions
[0];
293 EXPECT_THAT(IT
.getOpcode(), Opcode
);
294 ASSERT_THAT(IT
.VariableValues
, SizeIs(4));
295 EXPECT_THAT(IT
.VariableValues
[0].getReg(), Not(IT
.VariableValues
[1].getReg()))
296 << "Def is different from first Use";
297 EXPECT_THAT(IT
.VariableValues
[0].getReg(), Not(IT
.VariableValues
[2].getReg()))
298 << "Def is different from second Use";
299 EXPECT_THAT(IT
.VariableValues
[3], IsInvalid());
302 TEST_F(UopsSnippetGeneratorTest
, MemoryUse
) {
303 // Mov32rm reads from memory.
305 // - Op0 Explicit Def RegClass(GR32)
306 // - Op1 Explicit Use Memory RegClass(GR8)
307 // - Op2 Explicit Use Memory
308 // - Op3 Explicit Use Memory RegClass(GRH8)
309 // - Op4 Explicit Use Memory
310 // - Op5 Explicit Use Memory RegClass(SEGMENT_REG)
317 // - hasMemoryOperands
318 // - hasAliasingRegisters
319 const unsigned Opcode
= X86::MOV32rm
;
320 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
321 ASSERT_THAT(CodeTemplates
, SizeIs(1));
322 const auto &CT
= CodeTemplates
[0];
323 EXPECT_THAT(CT
.Info
, HasSubstr("no tied variables"));
324 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
325 ASSERT_THAT(CT
.Instructions
,
326 SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses
));
327 const InstructionTemplate
&IT
= CT
.Instructions
[0];
328 EXPECT_THAT(IT
.getOpcode(), Opcode
);
329 ASSERT_THAT(IT
.VariableValues
, SizeIs(6));
330 EXPECT_EQ(IT
.VariableValues
[2].getImm(), 1);
331 EXPECT_EQ(IT
.VariableValues
[3].getReg(), 0u);
332 EXPECT_EQ(IT
.VariableValues
[4].getImm(), 0);
333 EXPECT_EQ(IT
.VariableValues
[5].getReg(), 0u);
336 class FakeSnippetGenerator
: public SnippetGenerator
{
338 FakeSnippetGenerator(const LLVMState
&State
, const Options
&Opts
)
339 : SnippetGenerator(State
, Opts
) {}
341 Instruction
createInstruction(unsigned Opcode
) {
342 return State
.getIC().getInstr(Opcode
);
346 Expected
<std::vector
<CodeTemplate
>>
347 generateCodeTemplates(const Instruction
&, const BitVector
&) const override
{
348 return make_error
<StringError
>("not implemented", inconvertibleErrorCode());
352 using FakeSnippetGeneratorTest
= SnippetGeneratorTest
<FakeSnippetGenerator
>;
354 testing::Matcher
<const RegisterValue
&> IsRegisterValue(unsigned Reg
,
356 return testing::AllOf(testing::Field(&RegisterValue::Register
, Reg
),
357 testing::Field(&RegisterValue::Value
, Value
));
360 TEST_F(FakeSnippetGeneratorTest
, MemoryUse_Movsb
) {
361 // MOVSB writes to scratch memory register.
363 // - Op0 Explicit Use Memory RegClass(GR8)
364 // - Op1 Explicit Use Memory RegClass(GR8)
365 // - Op2 Explicit Use Memory RegClass(SEGMENT_REG)
366 // - Op3 Implicit Def Reg(EDI)
367 // - Op4 Implicit Def Reg(ESI)
368 // - Op5 Implicit Use Reg(EDI)
369 // - Op6 Implicit Use Reg(ESI)
370 // - Op7 Implicit Use Reg(DF)
374 // - hasMemoryOperands
375 // - hasAliasingImplicitRegisters (execution is always serial)
376 // - hasAliasingRegisters
377 const unsigned Opcode
= X86::MOVSB
;
378 const Instruction
&Instr
= State
.getIC().getInstr(Opcode
);
380 Generator
.generateConfigurations(Instr
, State
.getRATC().emptyRegisters())
382 EXPECT_TRUE((bool)Error
);
383 consumeError(std::move(Error
));
386 TEST_F(FakeSnippetGeneratorTest
, ComputeRegisterInitialValuesAdd16ri
) {
388 // explicit def 0 : reg RegClass=GR16
389 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
390 // explicit use 2 : imm
391 // implicit def : EFLAGS
392 InstructionTemplate
IT(Generator
.createInstruction(X86::ADD16ri
));
393 IT
.getValueFor(IT
.Instr
.Variables
[0]) = MCOperand::createReg(X86::AX
);
394 std::vector
<InstructionTemplate
> Snippet
;
395 Snippet
.push_back(std::move(IT
));
396 const auto RIV
= Generator
.computeRegisterInitialValues(Snippet
);
397 EXPECT_THAT(RIV
, ElementsAre(IsRegisterValue(X86::AX
, APInt())));
400 TEST_F(FakeSnippetGeneratorTest
, ComputeRegisterInitialValuesAdd64rr
) {
403 // add64rr rax, rax, rbx
404 // -> only rbx needs defining.
405 std::vector
<InstructionTemplate
> Snippet
;
407 InstructionTemplate
Mov(Generator
.createInstruction(X86::MOV64ri
));
408 Mov
.getValueFor(Mov
.Instr
.Variables
[0]) = MCOperand::createReg(X86::RAX
);
409 Mov
.getValueFor(Mov
.Instr
.Variables
[1]) = MCOperand::createImm(42);
410 Snippet
.push_back(std::move(Mov
));
413 InstructionTemplate
Add(Generator
.createInstruction(X86::ADD64rr
));
414 Add
.getValueFor(Add
.Instr
.Variables
[0]) = MCOperand::createReg(X86::RAX
);
415 Add
.getValueFor(Add
.Instr
.Variables
[1]) = MCOperand::createReg(X86::RBX
);
416 Snippet
.push_back(std::move(Add
));
419 const auto RIV
= Generator
.computeRegisterInitialValues(Snippet
);
420 EXPECT_THAT(RIV
, ElementsAre(IsRegisterValue(X86::RBX
, APInt())));
424 } // namespace exegesis