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"
15 #include "X86InstrInfo.h"
17 #include <unordered_set>
22 void InitializeX86ExegesisTarget();
27 using testing::ElementsAre
;
29 using testing::HasSubstr
;
31 using testing::SizeIs
;
32 using testing::UnorderedElementsAre
;
34 MATCHER(IsInvalid
, "") { return !arg
.isValid(); }
35 MATCHER(IsReg
, "") { return arg
.isReg(); }
37 class X86SnippetGeneratorTest
: public ::testing::Test
{
39 X86SnippetGeneratorTest()
40 : State("x86_64-unknown-linux", "haswell"),
41 MCInstrInfo(State
.getInstrInfo()), MCRegisterInfo(State
.getRegInfo()) {}
43 static void SetUpTestCase() {
44 LLVMInitializeX86TargetInfo();
45 LLVMInitializeX86TargetMC();
46 LLVMInitializeX86Target();
47 LLVMInitializeX86AsmPrinter();
48 InitializeX86ExegesisTarget();
51 const LLVMState State
;
52 const llvm::MCInstrInfo
&MCInstrInfo
;
53 const llvm::MCRegisterInfo
&MCRegisterInfo
;
56 template <typename SnippetGeneratorT
>
57 class SnippetGeneratorTest
: public X86SnippetGeneratorTest
{
59 SnippetGeneratorTest() : Generator(State
) {}
61 std::vector
<CodeTemplate
> checkAndGetCodeTemplates(unsigned Opcode
) {
62 randomGenerator().seed(0); // Initialize seed.
63 const Instruction
&Instr
= State
.getIC().getInstr(Opcode
);
64 auto CodeTemplateOrError
= Generator
.generateCodeTemplates(Instr
);
65 EXPECT_FALSE(CodeTemplateOrError
.takeError()); // Valid configuration.
66 return std::move(CodeTemplateOrError
.get());
69 SnippetGeneratorT Generator
;
72 using LatencySnippetGeneratorTest
=
73 SnippetGeneratorTest
<LatencySnippetGenerator
>;
75 using UopsSnippetGeneratorTest
= SnippetGeneratorTest
<UopsSnippetGenerator
>;
77 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependencyThroughImplicitReg
) {
79 // - Op0 Explicit Use Immediate
80 // - Op1 Implicit Def Reg(AX)
81 // - Op2 Implicit Def Reg(EFLAGS)
82 // - Op3 Implicit Use Reg(AX)
83 // - Op4 Implicit Use Reg(EFLAGS)
85 // - hasAliasingImplicitRegisters (execution is always serial)
86 // - hasAliasingRegisters
87 const unsigned Opcode
= llvm::X86::ADC16i16
;
88 EXPECT_THAT(MCInstrInfo
.get(Opcode
).getImplicitDefs()[0], llvm::X86::AX
);
89 EXPECT_THAT(MCInstrInfo
.get(Opcode
).getImplicitDefs()[1], llvm::X86::EFLAGS
);
90 EXPECT_THAT(MCInstrInfo
.get(Opcode
).getImplicitUses()[0], llvm::X86::AX
);
91 EXPECT_THAT(MCInstrInfo
.get(Opcode
).getImplicitUses()[1], llvm::X86::EFLAGS
);
92 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
93 ASSERT_THAT(CodeTemplates
, SizeIs(1));
94 const auto &CT
= CodeTemplates
[0];
95 EXPECT_THAT(CT
.Execution
, ExecutionMode::ALWAYS_SERIAL_IMPLICIT_REGS_ALIAS
);
96 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
97 const InstructionTemplate
&IT
= CT
.Instructions
[0];
98 EXPECT_THAT(IT
.getOpcode(), Opcode
);
99 ASSERT_THAT(IT
.VariableValues
, SizeIs(1)); // Imm.
100 EXPECT_THAT(IT
.VariableValues
[0], IsInvalid()) << "Immediate is not set";
103 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependencyThroughTiedRegs
) {
105 // - Op0 Explicit Def RegClass(GR16)
106 // - Op1 Explicit Use RegClass(GR16) TiedToOp0
107 // - Op2 Explicit Use Immediate
108 // - Op3 Implicit Def Reg(EFLAGS)
111 // - hasTiedRegisters (execution is always serial)
112 // - hasAliasingRegisters
113 const unsigned Opcode
= llvm::X86::ADD16ri
;
114 EXPECT_THAT(MCInstrInfo
.get(Opcode
).getImplicitDefs()[0], llvm::X86::EFLAGS
);
115 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
116 ASSERT_THAT(CodeTemplates
, SizeIs(1));
117 const auto &CT
= CodeTemplates
[0];
118 EXPECT_THAT(CT
.Execution
, ExecutionMode::ALWAYS_SERIAL_TIED_REGS_ALIAS
);
119 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
120 const InstructionTemplate
&IT
= CT
.Instructions
[0];
121 EXPECT_THAT(IT
.getOpcode(), Opcode
);
122 ASSERT_THAT(IT
.VariableValues
, SizeIs(2));
123 EXPECT_THAT(IT
.VariableValues
[0], IsInvalid()) << "Operand 1 is not set";
124 EXPECT_THAT(IT
.VariableValues
[1], IsInvalid()) << "Operand 2 is not set";
127 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependencyThroughExplicitRegs
) {
129 // - Op0 Explicit Def RegClass(VR128)
130 // - Op1 Explicit Use RegClass(VR128)
131 // - Op2 Explicit Use RegClass(VR128)
135 // - hasAliasingRegisters
136 const unsigned Opcode
= llvm::X86::VXORPSrr
;
137 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
138 ASSERT_THAT(CodeTemplates
, SizeIs(1));
139 const auto &CT
= CodeTemplates
[0];
140 EXPECT_THAT(CT
.Execution
, ExecutionMode::SERIAL_VIA_EXPLICIT_REGS
);
141 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
142 const InstructionTemplate
&IT
= CT
.Instructions
[0];
143 EXPECT_THAT(IT
.getOpcode(), Opcode
);
144 ASSERT_THAT(IT
.VariableValues
, SizeIs(3));
145 EXPECT_THAT(IT
.VariableValues
,
146 AnyOf(ElementsAre(IsReg(), IsInvalid(), IsReg()),
147 ElementsAre(IsReg(), IsReg(), IsInvalid())))
148 << "Op0 is either set to Op1 or to Op2";
151 TEST_F(LatencySnippetGeneratorTest
, DependencyThroughOtherOpcode
) {
153 // - Op0 Explicit Use RegClass(GR64)
154 // - Op1 Explicit Use RegClass(GR64)
155 // - Op2 Implicit Def Reg(EFLAGS)
158 const unsigned Opcode
= llvm::X86::CMP64rr
;
159 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
160 ASSERT_THAT(CodeTemplates
, SizeIs(Gt(1U))) << "Many templates are available";
161 for (const auto &CT
: CodeTemplates
) {
162 EXPECT_THAT(CT
.Execution
, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR
);
163 ASSERT_THAT(CT
.Instructions
, SizeIs(2));
164 const InstructionTemplate
&IT
= CT
.Instructions
[0];
165 EXPECT_THAT(IT
.getOpcode(), Opcode
);
166 ASSERT_THAT(IT
.VariableValues
, SizeIs(2));
167 EXPECT_THAT(IT
.VariableValues
, AnyOf(ElementsAre(IsReg(), IsInvalid()),
168 ElementsAre(IsInvalid(), IsReg())));
169 EXPECT_THAT(CT
.Instructions
[1].getOpcode(), Not(Opcode
));
170 // TODO: check that the two instructions alias each other.
174 TEST_F(LatencySnippetGeneratorTest
, LAHF
) {
176 // - Op0 Implicit Def Reg(AH)
177 // - Op1 Implicit Use Reg(EFLAGS)
178 const unsigned Opcode
= llvm::X86::LAHF
;
179 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
180 ASSERT_THAT(CodeTemplates
, SizeIs(Gt(1U))) << "Many templates are available";
181 for (const auto &CT
: CodeTemplates
) {
182 EXPECT_THAT(CT
.Execution
, ExecutionMode::SERIAL_VIA_NON_MEMORY_INSTR
);
183 ASSERT_THAT(CT
.Instructions
, SizeIs(2));
184 const InstructionTemplate
&IT
= CT
.Instructions
[0];
185 EXPECT_THAT(IT
.getOpcode(), Opcode
);
186 ASSERT_THAT(IT
.VariableValues
, SizeIs(0));
190 TEST_F(UopsSnippetGeneratorTest
, ParallelInstruction
) {
192 // - Op0 Explicit Use RegClass(BNDR)
193 // - Op1 Explicit Use RegClass(GR32)
196 const unsigned Opcode
= llvm::X86::BNDCL32rr
;
197 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
198 ASSERT_THAT(CodeTemplates
, SizeIs(1));
199 const auto &CT
= CodeTemplates
[0];
200 EXPECT_THAT(CT
.Info
, HasSubstr("parallel"));
201 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
202 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
203 const InstructionTemplate
&IT
= CT
.Instructions
[0];
204 EXPECT_THAT(IT
.getOpcode(), Opcode
);
205 ASSERT_THAT(IT
.VariableValues
, SizeIs(2));
206 EXPECT_THAT(IT
.VariableValues
[0], IsInvalid());
207 EXPECT_THAT(IT
.VariableValues
[1], IsInvalid());
210 TEST_F(UopsSnippetGeneratorTest
, SerialInstruction
) {
212 // - Op0 Implicit Def Reg(EAX)
213 // - Op1 Implicit Def Reg(EDX)
214 // - Op2 Implicit Use Reg(EAX)
215 // - hasAliasingImplicitRegisters (execution is always serial)
216 // - hasAliasingRegisters
217 const unsigned Opcode
= llvm::X86::CDQ
;
218 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
219 ASSERT_THAT(CodeTemplates
, SizeIs(1));
220 const auto &CT
= CodeTemplates
[0];
221 EXPECT_THAT(CT
.Info
, HasSubstr("serial"));
222 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
223 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
224 const InstructionTemplate
&IT
= CT
.Instructions
[0];
225 EXPECT_THAT(IT
.getOpcode(), Opcode
);
226 ASSERT_THAT(IT
.VariableValues
, SizeIs(0));
229 TEST_F(UopsSnippetGeneratorTest
, StaticRenaming
) {
230 // CMOV32rr has tied variables, we enumerate the possible values to execute
231 // as many in parallel as possible.
234 // - Op0 Explicit Def RegClass(GR32)
235 // - Op1 Explicit Use RegClass(GR32) TiedToOp0
236 // - Op2 Explicit Use RegClass(GR32)
237 // - Op3 Explicit Use Immediate
238 // - Op3 Implicit Use Reg(EFLAGS)
241 // - hasTiedRegisters (execution is always serial)
242 // - hasAliasingRegisters
243 const unsigned Opcode
= llvm::X86::CMOV32rr
;
244 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
245 ASSERT_THAT(CodeTemplates
, SizeIs(1));
246 const auto &CT
= CodeTemplates
[0];
247 EXPECT_THAT(CT
.Info
, HasSubstr("static renaming"));
248 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
249 constexpr const unsigned kInstructionCount
= 15;
250 ASSERT_THAT(CT
.Instructions
, SizeIs(kInstructionCount
));
251 std::unordered_set
<unsigned> AllDefRegisters
;
252 for (const auto &IT
: CT
.Instructions
) {
253 ASSERT_THAT(IT
.VariableValues
, SizeIs(3));
254 AllDefRegisters
.insert(IT
.VariableValues
[0].getReg());
256 EXPECT_THAT(AllDefRegisters
, SizeIs(kInstructionCount
))
257 << "Each instruction writes to a different register";
260 TEST_F(UopsSnippetGeneratorTest
, NoTiedVariables
) {
261 // CMOV_GR32 has no tied variables, we make sure def and use are different
265 // - Op0 Explicit Def RegClass(GR32)
266 // - Op1 Explicit Use RegClass(GR32)
267 // - Op2 Explicit Use RegClass(GR32)
268 // - Op3 Explicit Use Immediate
269 // - Op4 Implicit Use Reg(EFLAGS)
274 // - hasAliasingRegisters
275 const unsigned Opcode
= llvm::X86::CMOV_GR32
;
276 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
277 ASSERT_THAT(CodeTemplates
, SizeIs(1));
278 const auto &CT
= CodeTemplates
[0];
279 EXPECT_THAT(CT
.Info
, HasSubstr("no tied variables"));
280 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
281 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
282 const InstructionTemplate
&IT
= CT
.Instructions
[0];
283 EXPECT_THAT(IT
.getOpcode(), Opcode
);
284 ASSERT_THAT(IT
.VariableValues
, SizeIs(4));
285 EXPECT_THAT(IT
.VariableValues
[0].getReg(), Not(IT
.VariableValues
[1].getReg()))
286 << "Def is different from first Use";
287 EXPECT_THAT(IT
.VariableValues
[0].getReg(), Not(IT
.VariableValues
[2].getReg()))
288 << "Def is different from second Use";
289 EXPECT_THAT(IT
.VariableValues
[3], IsInvalid());
292 TEST_F(UopsSnippetGeneratorTest
, MemoryUse
) {
293 // Mov32rm reads from memory.
295 // - Op0 Explicit Def RegClass(GR32)
296 // - Op1 Explicit Use Memory RegClass(GR8)
297 // - Op2 Explicit Use Memory
298 // - Op3 Explicit Use Memory RegClass(GRH8)
299 // - Op4 Explicit Use Memory
300 // - Op5 Explicit Use Memory RegClass(SEGMENT_REG)
307 // - hasMemoryOperands
308 // - hasAliasingRegisters
309 const unsigned Opcode
= llvm::X86::MOV32rm
;
310 const auto CodeTemplates
= checkAndGetCodeTemplates(Opcode
);
311 ASSERT_THAT(CodeTemplates
, SizeIs(1));
312 const auto &CT
= CodeTemplates
[0];
313 EXPECT_THAT(CT
.Info
, HasSubstr("no tied variables"));
314 EXPECT_THAT(CT
.Execution
, ExecutionMode::UNKNOWN
);
315 ASSERT_THAT(CT
.Instructions
,
316 SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses
));
317 const InstructionTemplate
&IT
= CT
.Instructions
[0];
318 EXPECT_THAT(IT
.getOpcode(), Opcode
);
319 ASSERT_THAT(IT
.VariableValues
, SizeIs(6));
320 EXPECT_EQ(IT
.VariableValues
[2].getImm(), 1);
321 EXPECT_EQ(IT
.VariableValues
[3].getReg(), 0u);
322 EXPECT_EQ(IT
.VariableValues
[4].getImm(), 0);
323 EXPECT_EQ(IT
.VariableValues
[5].getReg(), 0u);
326 TEST_F(UopsSnippetGeneratorTest
, MemoryUse_Movsb
) {
327 // MOVSB writes to scratch memory register.
329 // - Op0 Explicit Use Memory RegClass(GR8)
330 // - Op1 Explicit Use Memory RegClass(GR8)
331 // - Op2 Explicit Use Memory RegClass(SEGMENT_REG)
332 // - Op3 Implicit Def Reg(EDI)
333 // - Op4 Implicit Def Reg(ESI)
334 // - Op5 Implicit Use Reg(EDI)
335 // - Op6 Implicit Use Reg(ESI)
336 // - Op7 Implicit Use Reg(DF)
340 // - hasMemoryOperands
341 // - hasAliasingImplicitRegisters (execution is always serial)
342 // - hasAliasingRegisters
343 const unsigned Opcode
= llvm::X86::MOVSB
;
344 const Instruction
&Instr
= State
.getIC().getInstr(Opcode
);
345 auto Error
= Generator
.generateCodeTemplates(Instr
).takeError();
346 EXPECT_TRUE((bool)Error
);
347 llvm::consumeError(std::move(Error
));
350 class FakeSnippetGenerator
: public SnippetGenerator
{
352 FakeSnippetGenerator(const LLVMState
&State
) : SnippetGenerator(State
) {}
354 Instruction
createInstruction(unsigned Opcode
) {
355 return State
.getIC().getInstr(Opcode
);
359 llvm::Expected
<std::vector
<CodeTemplate
>>
360 generateCodeTemplates(const Instruction
&Instr
) const override
{
361 return llvm::make_error
<llvm::StringError
>("not implemented",
362 llvm::inconvertibleErrorCode());
366 using FakeSnippetGeneratorTest
= SnippetGeneratorTest
<FakeSnippetGenerator
>;
368 testing::Matcher
<const RegisterValue
&> IsRegisterValue(unsigned Reg
,
370 return testing::AllOf(testing::Field(&RegisterValue::Register
, Reg
),
371 testing::Field(&RegisterValue::Value
, Value
));
374 TEST_F(FakeSnippetGeneratorTest
, ComputeRegisterInitialValuesAdd16ri
) {
376 // explicit def 0 : reg RegClass=GR16
377 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
378 // explicit use 2 : imm
379 // implicit def : EFLAGS
380 InstructionTemplate
IT(Generator
.createInstruction(llvm::X86::ADD16ri
));
381 IT
.getValueFor(IT
.Instr
.Variables
[0]) =
382 llvm::MCOperand::createReg(llvm::X86::AX
);
383 std::vector
<InstructionTemplate
> Snippet
;
384 Snippet
.push_back(std::move(IT
));
385 const auto RIV
= Generator
.computeRegisterInitialValues(Snippet
);
386 EXPECT_THAT(RIV
, ElementsAre(IsRegisterValue(llvm::X86::AX
, llvm::APInt())));
389 TEST_F(FakeSnippetGeneratorTest
, ComputeRegisterInitialValuesAdd64rr
) {
392 // add64rr rax, rax, rbx
393 // -> only rbx needs defining.
394 std::vector
<InstructionTemplate
> Snippet
;
396 InstructionTemplate
Mov(Generator
.createInstruction(llvm::X86::MOV64ri
));
397 Mov
.getValueFor(Mov
.Instr
.Variables
[0]) =
398 llvm::MCOperand::createReg(llvm::X86::RAX
);
399 Mov
.getValueFor(Mov
.Instr
.Variables
[1]) = llvm::MCOperand::createImm(42);
400 Snippet
.push_back(std::move(Mov
));
403 InstructionTemplate
Add(Generator
.createInstruction(llvm::X86::ADD64rr
));
404 Add
.getValueFor(Add
.Instr
.Variables
[0]) =
405 llvm::MCOperand::createReg(llvm::X86::RAX
);
406 Add
.getValueFor(Add
.Instr
.Variables
[1]) =
407 llvm::MCOperand::createReg(llvm::X86::RBX
);
408 Snippet
.push_back(std::move(Add
));
411 const auto RIV
= Generator
.computeRegisterInitialValues(Snippet
);
412 EXPECT_THAT(RIV
, ElementsAre(IsRegisterValue(llvm::X86::RBX
, llvm::APInt())));
416 } // namespace exegesis