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