[obj2yaml] - Stop triggering UB when dumping corrupted strings.
[llvm-complete.git] / unittests / tools / llvm-exegesis / X86 / SnippetGeneratorTest.cpp
blob463eec2c21ddc3830ad0c033ff9cf6635d511ecf
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 //===----------------------------------------------------------------------===//
9 #include "../Common/AssemblerUtils.h"
10 #include "Latency.h"
11 #include "LlvmState.h"
12 #include "MCInstrDescView.h"
13 #include "RegisterAliasing.h"
14 #include "TestBase.h"
15 #include "Uops.h"
16 #include "X86InstrInfo.h"
18 #include <unordered_set>
20 namespace llvm {
21 namespace exegesis {
23 void InitializeX86ExegesisTarget();
25 namespace {
27 using testing::AnyOf;
28 using testing::ElementsAre;
29 using testing::Gt;
30 using testing::HasSubstr;
31 using testing::Not;
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 {
39 protected:
40 X86SnippetGeneratorTest() : InstrInfo(State.getInstrInfo()) {}
42 const MCInstrInfo &InstrInfo;
45 template <typename SnippetGeneratorT>
46 class SnippetGeneratorTest : public X86SnippetGeneratorTest {
47 protected:
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) {
68 // - ADC16i16
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)
74 // - Var0 [Op0]
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) {
94 // - ADD16ri
95 // - Op0 Explicit Def RegClass(GR16)
96 // - Op1 Explicit Use RegClass(GR16) TiedToOp0
97 // - Op2 Explicit Use Immediate
98 // - Op3 Implicit Def Reg(EFLAGS)
99 // - Var0 [Op0,Op1]
100 // - Var1 [Op2]
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) {
118 // - VXORPSrr
119 // - Op0 Explicit Def RegClass(VR128)
120 // - Op1 Explicit Use RegClass(VR128)
121 // - Op2 Explicit Use RegClass(VR128)
122 // - Var0 [Op0]
123 // - Var1 [Op1]
124 // - Var2 [Op2]
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) {
143 // - VXORPSrr
144 // - Op0 Explicit Def RegClass(VR128)
145 // - Op1 Explicit Use RegClass(VR128)
146 // - Op2 Explicit Use RegClass(VR128)
147 // - Var0 [Op0]
148 // - Var1 [Op1]
149 // - Var2 [Op2]
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();
155 AllRegisters.flip();
156 auto Error = Generator.generateCodeTemplates(Instr, AllRegisters).takeError();
157 EXPECT_TRUE((bool)Error);
158 consumeError(std::move(Error));
161 TEST_F(LatencySnippetGeneratorTest, DependencyThroughOtherOpcode) {
162 // - CMP64rr
163 // - Op0 Explicit Use RegClass(GR64)
164 // - Op1 Explicit Use RegClass(GR64)
165 // - Op2 Implicit Def Reg(EFLAGS)
166 // - Var0 [Op0]
167 // - Var1 [Op1]
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) {
185 // - 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) {
201 // - BNDCL32rr
202 // - Op0 Explicit Use RegClass(BNDR)
203 // - Op1 Explicit Use RegClass(GR32)
204 // - Var0 [Op0]
205 // - Var1 [Op1]
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) {
221 // - CDQ
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.
243 // - CMOV32rr
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)
249 // - Var0 [Op0,Op1]
250 // - Var1 [Op2]
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
272 // from each other.
274 // - CMOV_GR32
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)
280 // - Var0 [Op0]
281 // - Var1 [Op1]
282 // - Var2 [Op2]
283 // - Var3 [Op3]
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.
304 // - MOV32rm
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)
311 // - Var0 [Op0]
312 // - Var1 [Op1]
313 // - Var2 [Op2]
314 // - Var3 [Op3]
315 // - Var4 [Op4]
316 // - Var5 [Op5]
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 {
337 public:
338 FakeSnippetGenerator(const LLVMState &State, const Options &Opts)
339 : SnippetGenerator(State, Opts) {}
341 Instruction createInstruction(unsigned Opcode) {
342 return State.getIC().getInstr(Opcode);
345 private:
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,
355 APInt Value) {
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.
362 // - MOVSB
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)
371 // - Var0 [Op0]
372 // - Var1 [Op1]
373 // - Var2 [Op2]
374 // - hasMemoryOperands
375 // - hasAliasingImplicitRegisters (execution is always serial)
376 // - hasAliasingRegisters
377 const unsigned Opcode = X86::MOVSB;
378 const Instruction &Instr = State.getIC().getInstr(Opcode);
379 auto Error =
380 Generator.generateConfigurations(Instr, State.getRATC().emptyRegisters())
381 .takeError();
382 EXPECT_TRUE((bool)Error);
383 consumeError(std::move(Error));
386 TEST_F(FakeSnippetGeneratorTest, ComputeRegisterInitialValuesAdd16ri) {
387 // ADD16ri:
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) {
401 // ADD64rr:
402 // mov64ri rax, 42
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())));
423 } // namespace
424 } // namespace exegesis
425 } // namespace llvm