1 //===-- SnippetGeneratorTest.cpp --------------------------------*- C++ -*-===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 #include "../Common/AssemblerUtils.h"
12 #include "LlvmState.h"
13 #include "MCInstrDescView.h"
14 #include "RegisterAliasing.h"
16 #include "X86InstrInfo.h"
18 #include <unordered_set>
22 void InitializeX86ExegesisTarget();
27 using testing::ElementsAre
;
28 using testing::HasSubstr
;
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
{
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 SnippetGeneratorT
>
56 class SnippetGeneratorTest
: public X86SnippetGeneratorTest
{
58 SnippetGeneratorTest() : Generator(State
) {}
60 CodeTemplate
checkAndGetCodeTemplate(unsigned Opcode
) {
61 randomGenerator().seed(0); // Initialize seed.
62 auto CodeTemplateOrError
= Generator
.generateCodeTemplate(Opcode
);
63 EXPECT_FALSE(CodeTemplateOrError
.takeError()); // Valid configuration.
64 return std::move(CodeTemplateOrError
.get());
67 SnippetGeneratorT Generator
;
70 using LatencySnippetGeneratorTest
=
71 SnippetGeneratorTest
<LatencySnippetGenerator
>;
73 using UopsSnippetGeneratorTest
= SnippetGeneratorTest
<UopsSnippetGenerator
>;
75 TEST_F(LatencySnippetGeneratorTest
, ImplicitSelfDependency
) {
76 // ADC16i16 self alias because of implicit use and def.
78 // explicit use 0 : imm
80 // implicit def : EFLAGS
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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
89 EXPECT_THAT(CT
.Info
, HasSubstr("implicit"));
90 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
91 const InstructionBuilder
&IB
= CT
.Instructions
[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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
107 EXPECT_THAT(CT
.Info
, HasSubstr("explicit"));
108 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
109 const InstructionBuilder
&IB
= CT
.Instructions
[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
) {
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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
124 EXPECT_THAT(CT
.Info
, HasSubstr("cycle through"));
125 ASSERT_THAT(CT
.Instructions
, SizeIs(2));
126 const InstructionBuilder
&IB
= CT
.Instructions
[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(CT
.Instructions
[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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
138 EXPECT_THAT(CT
.Info
, HasSubstr("cycle through"));
139 ASSERT_THAT(CT
.Instructions
, SizeIs(2));
140 const InstructionBuilder
&IB
= CT
.Instructions
[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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
153 EXPECT_THAT(CT
.Info
, HasSubstr("parallel"));
154 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
155 const InstructionBuilder
&IB
= CT
.Instructions
[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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
170 EXPECT_THAT(CT
.Info
, HasSubstr("serial"));
171 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
172 const InstructionBuilder
&IB
= CT
.Instructions
[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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
187 EXPECT_THAT(CT
.Info
, HasSubstr("static renaming"));
188 constexpr const unsigned kInstructionCount
= 15;
189 ASSERT_THAT(CT
.Instructions
, SizeIs(kInstructionCount
));
190 std::unordered_set
<unsigned> AllDefRegisters
;
191 for (const auto &IB
: CT
.Instructions
) {
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
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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
210 EXPECT_THAT(CT
.Info
, HasSubstr("no tied variables"));
211 ASSERT_THAT(CT
.Instructions
, SizeIs(1));
212 const InstructionBuilder
&IB
= CT
.Instructions
[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 CodeTemplate CT
= checkAndGetCodeTemplate(Opcode
);
226 EXPECT_THAT(CT
.Info
, HasSubstr("no tied variables"));
227 ASSERT_THAT(CT
.Instructions
,
228 SizeIs(UopsSnippetGenerator::kMinNumDifferentAddresses
));
229 const InstructionBuilder
&IB
= CT
.Instructions
[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
= Generator
.generateCodeTemplate(Opcode
).takeError();
242 EXPECT_TRUE((bool)Error
);
243 llvm::consumeError(std::move(Error
));
246 class FakeSnippetGenerator
: public SnippetGenerator
{
248 FakeSnippetGenerator(const LLVMState
&State
) : SnippetGenerator(State
) {}
250 Instruction
createInstruction(unsigned Opcode
) {
251 return Instruction(State
.getInstrInfo().get(Opcode
), RATC
);
255 llvm::Expected
<CodeTemplate
>
256 generateCodeTemplate(unsigned Opcode
) const override
{
257 return llvm::make_error
<llvm::StringError
>("not implemented",
258 llvm::inconvertibleErrorCode());
262 using FakeSnippetGeneratorTest
= SnippetGeneratorTest
<FakeSnippetGenerator
>;
264 testing::Matcher
<const RegisterValue
&> IsRegisterValue(unsigned Reg
,
266 return testing::AllOf(testing::Field(&RegisterValue::Register
, Reg
),
267 testing::Field(&RegisterValue::Value
, Value
));
270 TEST_F(FakeSnippetGeneratorTest
, ComputeRegisterInitialValuesAdd16ri
) {
272 // explicit def 0 : reg RegClass=GR16
273 // explicit use 1 : reg RegClass=GR16 | TIED_TO:0
274 // explicit use 2 : imm
275 // implicit def : EFLAGS
276 InstructionBuilder
IB(Generator
.createInstruction(llvm::X86::ADD16ri
));
277 IB
.getValueFor(IB
.Instr
.Variables
[0]) =
278 llvm::MCOperand::createReg(llvm::X86::AX
);
279 std::vector
<InstructionBuilder
> Snippet
;
280 Snippet
.push_back(std::move(IB
));
281 const auto RIV
= Generator
.computeRegisterInitialValues(Snippet
);
282 EXPECT_THAT(RIV
, ElementsAre(IsRegisterValue(llvm::X86::AX
, llvm::APInt())));
285 TEST_F(FakeSnippetGeneratorTest
, ComputeRegisterInitialValuesAdd64rr
) {
288 // add64rr rax, rax, rbx
289 // -> only rbx needs defining.
290 std::vector
<InstructionBuilder
> Snippet
;
292 InstructionBuilder
Mov(Generator
.createInstruction(llvm::X86::MOV64ri
));
293 Mov
.getValueFor(Mov
.Instr
.Variables
[0]) =
294 llvm::MCOperand::createReg(llvm::X86::RAX
);
295 Mov
.getValueFor(Mov
.Instr
.Variables
[1]) = llvm::MCOperand::createImm(42);
296 Snippet
.push_back(std::move(Mov
));
299 InstructionBuilder
Add(Generator
.createInstruction(llvm::X86::ADD64rr
));
300 Add
.getValueFor(Add
.Instr
.Variables
[0]) =
301 llvm::MCOperand::createReg(llvm::X86::RAX
);
302 Add
.getValueFor(Add
.Instr
.Variables
[1]) =
303 llvm::MCOperand::createReg(llvm::X86::RBX
);
304 Snippet
.push_back(std::move(Add
));
307 const auto RIV
= Generator
.computeRegisterInitialValues(Snippet
);
308 EXPECT_THAT(RIV
, ElementsAre(IsRegisterValue(llvm::X86::RBX
, llvm::APInt())));
312 } // namespace exegesis