1 //===- subzero/unittest/unittest/AssemblerX8632/TestUtil.h ------*- C++ -*-===// 2 // 3 // The Subzero Code Generator 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // Utility classes for testing the X8632 Assembler. 11 // 12 //===----------------------------------------------------------------------===// 13 14 #ifndef ASSEMBLERX8632_TESTUTIL_H_ 15 #define ASSEMBLERX8632_TESTUTIL_H_ 16 17 #include "IceAssemblerX8632.h" 18 #include "IceDefs.h" 19 20 #include "gtest/gtest.h" 21 22 #if defined(__unix__) 23 #include <sys/mman.h> 24 #elif defined(_WIN32) 25 #define NOMINMAX 26 #include <Windows.h> 27 #else 28 #error "Platform unsupported" 29 #endif 30 31 #include <cassert> 32 33 namespace Ice { 34 namespace X8632 { 35 namespace Test { 36 37 class AssemblerX8632TestBase : public ::testing::Test { 38 protected: 39 using Address = AssemblerX8632::Traits::Address; 40 using Cond = AssemblerX8632::Traits::Cond; 41 using GPRRegister = AssemblerX8632::Traits::GPRRegister; 42 using ByteRegister = AssemblerX8632::Traits::ByteRegister; 43 using Label = ::Ice::X8632::Label; 44 using Traits = AssemblerX8632::Traits; 45 using XmmRegister = AssemblerX8632::Traits::XmmRegister; 46 using X87STRegister = AssemblerX8632::Traits::X87STRegister; 47 AssemblerX8632TestBase()48 AssemblerX8632TestBase() { reset(); } 49 reset()50 void reset() { Assembler = makeUnique<AssemblerX8632>(); } 51 assembler()52 AssemblerX8632 *assembler() const { return Assembler.get(); } 53 codeBytesSize()54 size_t codeBytesSize() const { return Assembler->getBufferView().size(); } 55 codeBytes()56 const uint8_t *codeBytes() const { 57 return static_cast<const uint8_t *>( 58 static_cast<const void *>(Assembler->getBufferView().data())); 59 } 60 61 private: 62 std::unique_ptr<AssemblerX8632> Assembler; 63 }; 64 65 // __ is a helper macro. It allows test cases to emit X8632 assembly 66 // instructions with 67 // 68 // __ mov(GPRRegister::Reg_Eax, 1); 69 // __ ret(); 70 // 71 // and so on. The idea of having this was "stolen" from dart's unit tests. 72 #define __ (this->assembler())-> 73 74 // AssemblerX8632LowLevelTest verify that the "basic" instructions the tests 75 // rely on are encoded correctly. Therefore, instead of executing the assembled 76 // code, these tests will verify that the assembled bytes are sane. 77 class AssemblerX8632LowLevelTest : public AssemblerX8632TestBase { 78 protected: 79 // verifyBytes is a template helper that takes a Buffer, and a variable number 80 // of bytes. As the name indicates, it is used to verify the bytes for an 81 // instruction encoding. verifyBytes(const uint8_t *)82 template <int N, int I> static bool verifyBytes(const uint8_t *) { 83 static_assert(I == N, "Invalid template instantiation."); 84 return true; 85 } 86 87 template <int N, int I = 0, typename... Args> verifyBytes(const uint8_t * Buffer,uint8_t Byte,Args...OtherBytes)88 static bool verifyBytes(const uint8_t *Buffer, uint8_t Byte, 89 Args... OtherBytes) { 90 static_assert(I < N, "Invalid template instantiation."); 91 EXPECT_EQ(Byte, Buffer[I]) << "Byte " << (I + 1) << " of " << N; 92 return verifyBytes<N, I + 1>(Buffer, OtherBytes...) && Buffer[I] == Byte; 93 } 94 }; 95 96 // After these tests we should have a sane environment; we know the following 97 // work: 98 // 99 // (*) zeroing eax, ebx, ecx, edx, edi, and esi; 100 // (*) call $4 instruction (used for ip materialization); 101 // (*) register push and pop; 102 // (*) cmp reg, reg; and 103 // (*) returning from functions. 104 // 105 // We can now dive into testing each emitting method in AssemblerX8632. Each 106 // test will emit some instructions for performing the test. The assembled 107 // instructions will operate in a "safe" environment. All x86-32 registers are 108 // spilled to the program stack, and the registers are then zeroed out, with the 109 // exception of %esp and %ebp. 110 // 111 // The jitted code and the unittest code will share the same stack. Therefore, 112 // test harnesses need to ensure it does not leave anything it pushed on the 113 // stack. 114 // 115 // %ebp is initialized with a pointer for rIP-based addressing. This pointer is 116 // used for position-independent access to a scratchpad area for use in tests. 117 // This mechanism is used because the test framework needs to generate addresses 118 // that work on both x86-32 and x86-64 hosts, but are encodable using our x86-32 119 // assembler. This is made possible because the encoding for 120 // 121 // pushq %rax (x86-64 only) 122 // 123 // is the same as the one for 124 // 125 // pushl %eax (x86-32 only; not encodable in x86-64) 126 // 127 // Likewise, the encodings for 128 // 129 // movl offset(%ebp), %reg (32-bit only) 130 // movl <src>, offset(%ebp) (32-bit only) 131 // 132 // and 133 // 134 // movl offset(%rbp), %reg (64-bit only) 135 // movl <src>, offset(%rbp) (64-bit only) 136 // 137 // are also the same. 138 // 139 // We use a call instruction in order to generate a natural sized address on the 140 // stack. Said address is then removed from the stack with a pop %rBP, which can 141 // then be used to address memory safely in either x86-32 or x86-64, as long as 142 // the test code does not perform any arithmetic operation that writes to %rBP. 143 // This PC materialization technique is very common in x86-32 PIC. 144 // 145 // %rBP is used to provide the tests with a scratchpad area that can safely and 146 // portably be written to and read from. This scratchpad area is also used to 147 // store the "final" values in eax, ebx, ecx, edx, esi, and edi, allowing the 148 // harnesses access to 6 "return values" instead of the usual single return 149 // value supported by C++. 150 // 151 // The jitted code will look like the following: 152 // 153 // test: 154 // push %eax 155 // push %ebx 156 // push %ecx 157 // push %edx 158 // push %edi 159 // push %esi 160 // push %ebp 161 // call test$materialize_ip 162 // test$materialize_ip: <<------- %eBP will point here 163 // pop %ebp 164 // mov $0, %eax 165 // mov $0, %ebx 166 // mov $0, %ecx 167 // mov $0, %edx 168 // mov $0, %edi 169 // mov $0, %esi 170 // 171 // << test code goes here >> 172 // 173 // mov %eax, { 0 + $ScratchpadOffset}(%ebp) 174 // mov %ebx, { 4 + $ScratchpadOffset}(%ebp) 175 // mov %ecx, { 8 + $ScratchpadOffset}(%ebp) 176 // mov %edx, {12 + $ScratchpadOffset}(%ebp) 177 // mov %edi, {16 + $ScratchpadOffset}(%ebp) 178 // mov %esi, {20 + $ScratchpadOffset}(%ebp) 179 // mov %ebp, {24 + $ScratchpadOffset}(%ebp) 180 // mov %esp, {28 + $ScratchpadOffset}(%ebp) 181 // movups %xmm0, {32 + $ScratchpadOffset}(%ebp) 182 // movups %xmm1, {48 + $ScratchpadOffset}(%ebp) 183 // movups %xmm2, {64 + $ScratchpadOffset}(%ebp) 184 // movusp %xmm3, {80 + $ScratchpadOffset}(%ebp) 185 // movusp %xmm4, {96 + $ScratchpadOffset}(%ebp) 186 // movusp %xmm5, {112 + $ScratchpadOffset}(%ebp) 187 // movusp %xmm6, {128 + $ScratchpadOffset}(%ebp) 188 // movusp %xmm7, {144 + $ScratchpadOffset}(%ebp) 189 // 190 // pop %ebp 191 // pop %esi 192 // pop %edi 193 // pop %edx 194 // pop %ecx 195 // pop %ebx 196 // pop %eax 197 // ret 198 // 199 // << ... >> 200 // 201 // scratchpad: <<------- accessed via $Offset(%ebp) 202 // 203 // << test scratch area >> 204 // 205 // TODO(jpp): test the 206 // 207 // mov %reg, $Offset(%ebp) 208 // movups %xmm, $Offset(%ebp) 209 // 210 // encodings using the low level assembler test ensuring that the register 211 // values can be written to the scratchpad area. 212 class AssemblerX8632Test : public AssemblerX8632TestBase { 213 protected: 214 // Dqword is used to represent 128-bit data types. The Dqword's contents are 215 // the same as the contents read from memory. Tests can then use the union 216 // members to verify the tests' outputs. 217 // 218 // NOTE: We want sizeof(Dqword) == sizeof(uint64_t) * 2. In other words, we 219 // want Dqword's contents to be **exactly** what the memory contents were so 220 // that we can do, e.g., 221 // 222 // ... 223 // float Ret[4]; 224 // // populate Ret 225 // return *reinterpret_cast<Dqword *>(&Ret); 226 // 227 // While being an ugly hack, this kind of return statements are used 228 // extensively in the PackedArith (see below) class. 229 union Dqword { 230 template <typename T0, typename T1, typename T2, typename T3, 231 typename = typename std::enable_if< 232 std::is_floating_point<T0>::value>::type> Dqword(T0 F0,T1 F1,T2 F2,T3 F3)233 Dqword(T0 F0, T1 F1, T2 F2, T3 F3) { 234 F32[0] = F0; 235 F32[1] = F1; 236 F32[2] = F2; 237 F32[3] = F3; 238 } 239 240 template <typename T> Dqword(typename std::enable_if<std::is_same<T,int32_t>::value,T>::type I0,T I1,T I2,T I3)241 Dqword(typename std::enable_if<std::is_same<T, int32_t>::value, T>::type I0, 242 T I1, T I2, T I3) { 243 I32[0] = I0; 244 I32[1] = I1; 245 I32[2] = I2; 246 I32[3] = I3; 247 } 248 249 template <typename T> Dqword(typename std::enable_if<std::is_same<T,uint64_t>::value,T>::type U64_0,T U64_1)250 Dqword(typename std::enable_if<std::is_same<T, uint64_t>::value, T>::type 251 U64_0, 252 T U64_1) { 253 U64[0] = U64_0; 254 U64[1] = U64_1; 255 } 256 257 template <typename T> Dqword(typename std::enable_if<std::is_same<T,double>::value,T>::type D0,T D1)258 Dqword(typename std::enable_if<std::is_same<T, double>::value, T>::type D0, 259 T D1) { 260 F64[0] = D0; 261 F64[1] = D1; 262 } 263 264 bool operator==(const Dqword &Rhs) const { 265 return std::memcmp(this, &Rhs, sizeof(*this)) == 0; 266 } 267 268 double F64[2]; 269 uint64_t U64[2]; 270 int64_t I64[2]; 271 272 float F32[4]; 273 uint32_t U32[4]; 274 int32_t I32[4]; 275 276 uint16_t U16[8]; 277 int16_t I16[8]; 278 279 uint8_t U8[16]; 280 int8_t I8[16]; 281 282 private: 283 Dqword() = delete; 284 }; 285 286 // As stated, we want this condition to hold, so we assert. 287 static_assert(sizeof(Dqword) == 2 * sizeof(uint64_t), 288 "Dqword has the wrong size."); 289 290 // PackedArith is an interface provider for Dqwords. PackedArith's C argument 291 // is the undelying Dqword's type, which is then used so that we can define 292 // operators in terms of C++ operators on the underlying elements' type. 293 template <typename C> class PackedArith { 294 public: 295 static constexpr uint32_t N = sizeof(Dqword) / sizeof(C); 296 static_assert(N * sizeof(C) == sizeof(Dqword), 297 "Invalid template paramenter."); 298 static_assert((N & 1) == 0, "N should be divisible by 2"); 299 300 #define DefinePackedComparisonOperator(Op) \ 301 template <typename Container = C, int Size = N> \ 302 typename std::enable_if<std::is_floating_point<Container>::value, \ 303 Dqword>::type \ 304 operator Op(const Dqword &Rhs) const { \ 305 using ElemType = \ 306 typename std::conditional<std::is_same<float, Container>::value, \ 307 int32_t, int64_t>::type; \ 308 static_assert(sizeof(ElemType) == sizeof(Container), \ 309 "Check ElemType definition."); \ 310 const ElemType *const RhsPtr = \ 311 reinterpret_cast<const ElemType *const>(&Rhs); \ 312 const ElemType *const LhsPtr = \ 313 reinterpret_cast<const ElemType *const>(&Lhs); \ 314 ElemType Ret[N]; \ 315 for (uint32_t i = 0; i < N; ++i) { \ 316 Ret[i] = (LhsPtr[i] Op RhsPtr[i]) ? -1 : 0; \ 317 } \ 318 return *reinterpret_cast<Dqword *>(&Ret); \ 319 } 320 321 DefinePackedComparisonOperator(<); 322 DefinePackedComparisonOperator(<=); 323 DefinePackedComparisonOperator(>); 324 DefinePackedComparisonOperator(>=); 325 DefinePackedComparisonOperator(==); 326 DefinePackedComparisonOperator(!=); 327 328 #undef DefinePackedComparisonOperator 329 330 #define DefinePackedOrdUnordComparisonOperator(Op, Ordered) \ 331 template <typename Container = C, int Size = N> \ 332 typename std::enable_if<std::is_floating_point<Container>::value, \ 333 Dqword>::type \ 334 Op(const Dqword &Rhs) const { \ 335 using ElemType = \ 336 typename std::conditional<std::is_same<float, Container>::value, \ 337 int32_t, int64_t>::type; \ 338 static_assert(sizeof(ElemType) == sizeof(Container), \ 339 "Check ElemType definition."); \ 340 const Container *const RhsPtr = \ 341 reinterpret_cast<const Container *const>(&Rhs); \ 342 const Container *const LhsPtr = \ 343 reinterpret_cast<const Container *const>(&Lhs); \ 344 ElemType Ret[N]; \ 345 for (uint32_t i = 0; i < N; ++i) { \ 346 Ret[i] = (!(LhsPtr[i] == LhsPtr[i]) || !(RhsPtr[i] == RhsPtr[i])) != \ 347 (Ordered) \ 348 ? -1 \ 349 : 0; \ 350 } \ 351 return *reinterpret_cast<Dqword *>(&Ret); \ 352 } 353 354 DefinePackedOrdUnordComparisonOperator(ord, true); 355 DefinePackedOrdUnordComparisonOperator(unord, false); 356 #undef DefinePackedOrdUnordComparisonOperator 357 358 #define DefinePackedArithOperator(Op, RhsIndexChanges, NeedsInt) \ 359 template <typename Container = C, int Size = N> \ 360 Dqword operator Op(const Dqword &Rhs) const { \ 361 using ElemTypeForFp = typename std::conditional< \ 362 !(NeedsInt), Container, \ 363 typename std::conditional< \ 364 std::is_same<Container, float>::value, uint32_t, \ 365 typename std::conditional<std::is_same<Container, double>::value, \ 366 uint64_t, void>::type>::type>::type; \ 367 using ElemType = \ 368 typename std::conditional<std::is_integral<Container>::value, \ 369 Container, ElemTypeForFp>::type; \ 370 static_assert(!std::is_same<void, ElemType>::value, \ 371 "Check ElemType definition."); \ 372 const ElemType *const RhsPtr = \ 373 reinterpret_cast<const ElemType *const>(&Rhs); \ 374 const ElemType *const LhsPtr = \ 375 reinterpret_cast<const ElemType *const>(&Lhs); \ 376 ElemType Ret[N]; \ 377 for (uint32_t i = 0; i < N; ++i) { \ 378 Ret[i] = LhsPtr[i] Op RhsPtr[(RhsIndexChanges) ? i : 0]; \ 379 } \ 380 return *reinterpret_cast<Dqword *>(&Ret); \ 381 } 382 383 DefinePackedArithOperator(>>, false, true); 384 DefinePackedArithOperator(<<, false, true); 385 DefinePackedArithOperator(+, true, false); 386 DefinePackedArithOperator(-, true, false); 387 DefinePackedArithOperator(/, true, false); 388 DefinePackedArithOperator(&, true, true); 389 DefinePackedArithOperator(|, true, true); 390 DefinePackedArithOperator(^, true, true); 391 392 #undef DefinePackedArithOperator 393 394 #define DefinePackedArithShiftImm(Op) \ 395 template <typename Container = C, int Size = N> \ 396 Dqword operator Op(uint8_t imm) const { \ 397 const Container *const LhsPtr = \ 398 reinterpret_cast<const Container *const>(&Lhs); \ 399 Container Ret[N]; \ 400 for (uint32_t i = 0; i < N; ++i) { \ 401 Ret[i] = LhsPtr[i] Op imm; \ 402 } \ 403 return *reinterpret_cast<Dqword *>(&Ret); \ 404 } 405 406 DefinePackedArithShiftImm(>>); 407 DefinePackedArithShiftImm(<<); 408 409 #undef DefinePackedArithShiftImm 410 411 template <typename Container = C, int Size = N> 412 typename std::enable_if<std::is_signed<Container>::value || 413 std::is_floating_point<Container>::value, 414 Dqword>::type 415 operator*(const Dqword &Rhs) const { 416 static_assert((std::is_integral<Container>::value && 417 sizeof(Container) < sizeof(uint64_t)) || 418 std::is_floating_point<Container>::value, 419 "* is only defined for i(8|16|32), and fp types."); 420 421 const Container *const RhsPtr = 422 reinterpret_cast<const Container *const>(&Rhs); 423 const Container *const LhsPtr = 424 reinterpret_cast<const Container *const>(&Lhs); 425 Container Ret[Size]; 426 for (uint32_t i = 0; i < Size; ++i) { 427 Ret[i] = LhsPtr[i] * RhsPtr[i]; 428 } 429 return *reinterpret_cast<Dqword *>(&Ret); 430 } 431 432 template <typename Container = C, int Size = N, 433 typename = typename std::enable_if< 434 !std::is_signed<Container>::value>::type> 435 Dqword operator*(const Dqword &Rhs) const { 436 static_assert(std::is_integral<Container>::value && 437 sizeof(Container) < sizeof(uint64_t), 438 "* is only defined for ui(8|16|32)"); 439 using NextType = typename std::conditional< 440 sizeof(Container) == 1, uint16_t, 441 typename std::conditional<sizeof(Container) == 2, uint32_t, 442 uint64_t>::type>::type; 443 static_assert(sizeof(Container) * 2 == sizeof(NextType), 444 "Unexpected size"); 445 446 const Container *const RhsPtr = 447 reinterpret_cast<const Container *const>(&Rhs); 448 const Container *const LhsPtr = 449 reinterpret_cast<const Container *const>(&Lhs); 450 NextType Ret[Size / 2]; 451 for (uint32_t i = 0; i < Size; i += 2) { 452 Ret[i / 2] = 453 static_cast<NextType>(LhsPtr[i]) * static_cast<NextType>(RhsPtr[i]); 454 } 455 return *reinterpret_cast<Dqword *>(&Ret); 456 } 457 458 template <typename Container = C, int Size = N> 459 PackedArith<Container> operator~() const { 460 const Container *const LhsPtr = 461 reinterpret_cast<const Container *const>(&Lhs); 462 Container Ret[Size]; 463 for (uint32_t i = 0; i < Size; ++i) { 464 Ret[i] = ~LhsPtr[i]; 465 } 466 return PackedArith<Container>(*reinterpret_cast<Dqword *>(&Ret)); 467 } 468 469 #define MinMaxOperations(Name, Suffix) \ 470 template <typename Container = C, int Size = N> \ 471 Dqword Name##Suffix(const Dqword &Rhs) const { \ 472 static_assert(std::is_floating_point<Container>::value, \ 473 #Name #Suffix "ps is only available for fp."); \ 474 const Container *const RhsPtr = \ 475 reinterpret_cast<const Container *const>(&Rhs); \ 476 const Container *const LhsPtr = \ 477 reinterpret_cast<const Container *const>(&Lhs); \ 478 Container Ret[Size]; \ 479 for (uint32_t i = 0; i < Size; ++i) { \ 480 Ret[i] = std::Name(LhsPtr[i], RhsPtr[i]); \ 481 } \ 482 return *reinterpret_cast<Dqword *>(&Ret); \ 483 } 484 485 MinMaxOperations(max, ps); 486 MinMaxOperations(max, pd); 487 MinMaxOperations(min, ps); 488 MinMaxOperations(min, pd); 489 #undef MinMaxOperations 490 491 template <typename Container = C, int Size = N> blendWith(const Dqword & Rhs,const Dqword & Mask)492 Dqword blendWith(const Dqword &Rhs, const Dqword &Mask) const { 493 using MaskType = typename std::conditional< 494 sizeof(Container) == 1, int8_t, 495 typename std::conditional<sizeof(Container) == 2, int16_t, 496 int32_t>::type>::type; 497 static_assert(sizeof(MaskType) == sizeof(Container), 498 "MaskType has the wrong size."); 499 const Container *const RhsPtr = 500 reinterpret_cast<const Container *const>(&Rhs); 501 const Container *const LhsPtr = 502 reinterpret_cast<const Container *const>(&Lhs); 503 const MaskType *const MaskPtr = 504 reinterpret_cast<const MaskType *const>(&Mask); 505 Container Ret[Size]; 506 for (int i = 0; i < Size; ++i) { 507 Ret[i] = ((MaskPtr[i] < 0) ? RhsPtr : LhsPtr)[i]; 508 } 509 return *reinterpret_cast<Dqword *>(&Ret); 510 } 511 512 private: 513 // The AssemblerX8632Test class needs to be a friend so that it can create 514 // PackedArith objects (see below.) 515 friend class AssemblerX8632Test; 516 PackedArith(const Dqword & MyLhs)517 explicit PackedArith(const Dqword &MyLhs) : Lhs(MyLhs) {} 518 519 // Lhs can't be a & because operator~ returns a temporary object that needs 520 // access to its own Dqword. 521 const Dqword Lhs; 522 }; 523 524 // Named constructor for PackedArith objects. packedAs(const Dqword & D)525 template <typename C> static PackedArith<C> packedAs(const Dqword &D) { 526 return PackedArith<C>(D); 527 } 528 AssemblerX8632Test()529 AssemblerX8632Test() { reset(); } 530 reset()531 void reset() { 532 AssemblerX8632TestBase::reset(); 533 534 NeedsEpilogue = true; 535 // These dwords are allocated for saving the GPR state after the jitted code 536 // runs. 537 NumAllocatedDwords = AssembledTest::ScratchpadSlots; 538 addPrologue(); 539 } 540 541 // AssembledTest is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer 542 // contains both the test code as well as prologue/epilogue, and the 543 // scratchpad area that tests may use -- all tests use this scratchpad area 544 // for storing the processor's registers after the tests executed. This class 545 // also exposes helper methods for reading the register state after test 546 // execution, as well as for reading the scratchpad area. 547 class AssembledTest { 548 AssembledTest() = delete; 549 AssembledTest(const AssembledTest &) = delete; 550 AssembledTest &operator=(const AssembledTest &) = delete; 551 552 public: 553 static constexpr uint32_t MaximumCodeSize = 1 << 20; 554 static constexpr uint32_t EaxSlot = 0; 555 static constexpr uint32_t EbxSlot = 1; 556 static constexpr uint32_t EcxSlot = 2; 557 static constexpr uint32_t EdxSlot = 3; 558 static constexpr uint32_t EdiSlot = 4; 559 static constexpr uint32_t EsiSlot = 5; 560 static constexpr uint32_t EbpSlot = 6; 561 static constexpr uint32_t EspSlot = 7; 562 // save 4 dwords for each xmm registers. 563 static constexpr uint32_t Xmm0Slot = 8; 564 static constexpr uint32_t Xmm1Slot = 12; 565 static constexpr uint32_t Xmm2Slot = 16; 566 static constexpr uint32_t Xmm3Slot = 20; 567 static constexpr uint32_t Xmm4Slot = 24; 568 static constexpr uint32_t Xmm5Slot = 28; 569 static constexpr uint32_t Xmm6Slot = 32; 570 static constexpr uint32_t Xmm7Slot = 36; 571 static constexpr uint32_t ScratchpadSlots = 40; 572 AssembledTest(const uint8_t * Data,const size_t MySize,const size_t ExtraStorageDwords)573 AssembledTest(const uint8_t *Data, const size_t MySize, 574 const size_t ExtraStorageDwords) 575 : Size(MaximumCodeSize + 4 * ExtraStorageDwords) { 576 // MaxCodeSize is needed because EXPECT_LT needs a symbol with a name -- 577 // probably a compiler bug? 578 uint32_t MaxCodeSize = MaximumCodeSize; 579 EXPECT_LT(MySize, MaxCodeSize); 580 assert(MySize < MaximumCodeSize); 581 582 #if defined(__unix__) 583 ExecutableData = mmap(nullptr, Size, PROT_WRITE | PROT_READ | PROT_EXEC, 584 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 585 EXPECT_NE(MAP_FAILED, ExecutableData) << strerror(errno); 586 assert(MAP_FAILED != ExecutableData); 587 #elif defined(_WIN32) 588 ExecutableData = VirtualAlloc(NULL, Size, MEM_COMMIT | MEM_RESERVE, 589 PAGE_EXECUTE_READWRITE); 590 EXPECT_NE(nullptr, ExecutableData) << strerror(errno); 591 assert(nullptr != ExecutableData); 592 #else 593 #error "Platform unsupported" 594 #endif 595 596 std::memcpy(ExecutableData, Data, MySize); 597 } 598 599 // We allow AssembledTest to be moved so that we can return objects of 600 // this type. AssembledTest(AssembledTest && Buffer)601 AssembledTest(AssembledTest &&Buffer) 602 : ExecutableData(Buffer.ExecutableData), Size(Buffer.Size) { 603 Buffer.ExecutableData = nullptr; 604 Buffer.Size = 0; 605 } 606 607 AssembledTest &operator=(AssembledTest &&Buffer) { 608 ExecutableData = Buffer.ExecutableData; 609 Buffer.ExecutableData = nullptr; 610 Size = Buffer.Size; 611 Buffer.Size = 0; 612 return *this; 613 } 614 ~AssembledTest()615 ~AssembledTest() { 616 if (ExecutableData != nullptr) { 617 #if defined(__unix__) 618 munmap(ExecutableData, Size); 619 #elif defined(_WIN32) 620 VirtualFree(ExecutableData, 0, MEM_RELEASE); 621 #endif 622 ExecutableData = nullptr; 623 } 624 } 625 run()626 void run() const { reinterpret_cast<void (*)()>(ExecutableData)(); } 627 eax()628 uint32_t eax() const { return contentsOfDword(AssembledTest::EaxSlot); } 629 ebx()630 uint32_t ebx() const { return contentsOfDword(AssembledTest::EbxSlot); } 631 ecx()632 uint32_t ecx() const { return contentsOfDword(AssembledTest::EcxSlot); } 633 edx()634 uint32_t edx() const { return contentsOfDword(AssembledTest::EdxSlot); } 635 edi()636 uint32_t edi() const { return contentsOfDword(AssembledTest::EdiSlot); } 637 esi()638 uint32_t esi() const { return contentsOfDword(AssembledTest::EsiSlot); } 639 ebp()640 uint32_t ebp() const { return contentsOfDword(AssembledTest::EbpSlot); } 641 esp()642 uint32_t esp() const { return contentsOfDword(AssembledTest::EspSlot); } 643 xmm0()644 template <typename T> T xmm0() const { 645 return xmm<T>(AssembledTest::Xmm0Slot); 646 } 647 xmm1()648 template <typename T> T xmm1() const { 649 return xmm<T>(AssembledTest::Xmm1Slot); 650 } 651 xmm2()652 template <typename T> T xmm2() const { 653 return xmm<T>(AssembledTest::Xmm2Slot); 654 } 655 xmm3()656 template <typename T> T xmm3() const { 657 return xmm<T>(AssembledTest::Xmm3Slot); 658 } 659 xmm4()660 template <typename T> T xmm4() const { 661 return xmm<T>(AssembledTest::Xmm4Slot); 662 } 663 xmm5()664 template <typename T> T xmm5() const { 665 return xmm<T>(AssembledTest::Xmm5Slot); 666 } 667 xmm6()668 template <typename T> T xmm6() const { 669 return xmm<T>(AssembledTest::Xmm6Slot); 670 } 671 xmm7()672 template <typename T> T xmm7() const { 673 return xmm<T>(AssembledTest::Xmm7Slot); 674 } 675 676 // contentsOfDword is used for reading the values in the scratchpad area. 677 // Valid arguments are the dword ids returned by 678 // AssemblerX8632Test::allocateDword() -- other inputs are considered 679 // invalid, and are not guaranteed to work if the implementation changes. 680 template <typename T = uint32_t, typename = typename std::enable_if< 681 sizeof(T) == sizeof(uint32_t)>::type> contentsOfDword(uint32_t Dword)682 T contentsOfDword(uint32_t Dword) const { 683 return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) + 684 dwordOffset(Dword)); 685 } 686 687 template <typename T = uint64_t, typename = typename std::enable_if< 688 sizeof(T) == sizeof(uint64_t)>::type> contentsOfQword(uint32_t InitialDword)689 T contentsOfQword(uint32_t InitialDword) const { 690 return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) + 691 dwordOffset(InitialDword)); 692 } 693 contentsOfDqword(uint32_t InitialDword)694 Dqword contentsOfDqword(uint32_t InitialDword) const { 695 return *reinterpret_cast<Dqword *>( 696 static_cast<uint8_t *>(ExecutableData) + dwordOffset(InitialDword)); 697 } 698 699 template <typename T = uint32_t, typename = typename std::enable_if< 700 sizeof(T) == sizeof(uint32_t)>::type> setDwordTo(uint32_t Dword,T value)701 void setDwordTo(uint32_t Dword, T value) { 702 *reinterpret_cast<uint32_t *>(static_cast<uint8_t *>(ExecutableData) + 703 dwordOffset(Dword)) = 704 *reinterpret_cast<uint32_t *>(&value); 705 } 706 707 template <typename T = uint64_t, typename = typename std::enable_if< 708 sizeof(T) == sizeof(uint64_t)>::type> setQwordTo(uint32_t InitialDword,T value)709 void setQwordTo(uint32_t InitialDword, T value) { 710 *reinterpret_cast<uint64_t *>(static_cast<uint8_t *>(ExecutableData) + 711 dwordOffset(InitialDword)) = 712 *reinterpret_cast<uint64_t *>(&value); 713 } 714 setDqwordTo(uint32_t InitialDword,const Dqword & qdword)715 void setDqwordTo(uint32_t InitialDword, const Dqword &qdword) { 716 setQwordTo(InitialDword, qdword.U64[0]); 717 setQwordTo(InitialDword + 2, qdword.U64[1]); 718 } 719 720 private: 721 template <typename T> 722 typename std::enable_if<std::is_same<T, Dqword>::value, Dqword>::type xmm(uint8_t Slot)723 xmm(uint8_t Slot) const { 724 return contentsOfDqword(Slot); 725 } 726 727 template <typename T> 728 typename std::enable_if<!std::is_same<T, Dqword>::value, T>::type xmm(uint8_t Slot)729 xmm(uint8_t Slot) const { 730 constexpr bool TIs64Bit = sizeof(T) == sizeof(uint64_t); 731 using _64BitType = typename std::conditional<TIs64Bit, T, uint64_t>::type; 732 using _32BitType = typename std::conditional<TIs64Bit, uint32_t, T>::type; 733 if (TIs64Bit) { 734 return contentsOfQword<_64BitType>(Slot); 735 } 736 return contentsOfDword<_32BitType>(Slot); 737 } 738 dwordOffset(uint32_t Index)739 static uint32_t dwordOffset(uint32_t Index) { 740 return MaximumCodeSize + (Index * 4); 741 } 742 743 void *ExecutableData = nullptr; 744 size_t Size; 745 }; 746 747 // assemble created an AssembledTest with the jitted code. The first time 748 // assemble is executed it will add the epilogue to the jitted code (which is 749 // the reason why this method is not const qualified. assemble()750 AssembledTest assemble() { 751 if (NeedsEpilogue) { 752 addEpilogue(); 753 } 754 NeedsEpilogue = false; 755 756 for (const auto *Fixup : assembler()->fixups()) { 757 Fixup->emitOffset(assembler()); 758 } 759 760 return AssembledTest(codeBytes(), codeBytesSize(), NumAllocatedDwords); 761 } 762 763 // Allocates a new dword slot in the test's scratchpad area. allocateDword()764 uint32_t allocateDword() { return NumAllocatedDwords++; } 765 766 // Allocates a new qword slot in the test's scratchpad area. allocateQword()767 uint32_t allocateQword() { 768 uint32_t InitialDword = allocateDword(); 769 allocateDword(); 770 return InitialDword; 771 } 772 773 // Allocates a new dqword slot in the test's scratchpad area. allocateDqword()774 uint32_t allocateDqword() { 775 uint32_t InitialDword = allocateQword(); 776 allocateQword(); 777 return InitialDword; 778 } 779 dwordAddress(uint32_t Dword)780 Address dwordAddress(uint32_t Dword) { 781 return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword), nullptr); 782 } 783 784 private: 785 // e??SlotAddress returns an AssemblerX8632::Traits::Address that can be used 786 // by the test cases to encode an address operand for accessing the slot for 787 // the specified register. These are all private for, when jitting the test 788 // code, tests should not tamper with these values. Besides, during the test 789 // execution these slots' contents are undefined and should not be accessed. eaxSlotAddress()790 Address eaxSlotAddress() { return dwordAddress(AssembledTest::EaxSlot); } ebxSlotAddress()791 Address ebxSlotAddress() { return dwordAddress(AssembledTest::EbxSlot); } ecxSlotAddress()792 Address ecxSlotAddress() { return dwordAddress(AssembledTest::EcxSlot); } edxSlotAddress()793 Address edxSlotAddress() { return dwordAddress(AssembledTest::EdxSlot); } ediSlotAddress()794 Address ediSlotAddress() { return dwordAddress(AssembledTest::EdiSlot); } esiSlotAddress()795 Address esiSlotAddress() { return dwordAddress(AssembledTest::EsiSlot); } ebpSlotAddress()796 Address ebpSlotAddress() { return dwordAddress(AssembledTest::EbpSlot); } espSlotAddress()797 Address espSlotAddress() { return dwordAddress(AssembledTest::EspSlot); } xmm0SlotAddress()798 Address xmm0SlotAddress() { return dwordAddress(AssembledTest::Xmm0Slot); } xmm1SlotAddress()799 Address xmm1SlotAddress() { return dwordAddress(AssembledTest::Xmm1Slot); } xmm2SlotAddress()800 Address xmm2SlotAddress() { return dwordAddress(AssembledTest::Xmm2Slot); } xmm3SlotAddress()801 Address xmm3SlotAddress() { return dwordAddress(AssembledTest::Xmm3Slot); } xmm4SlotAddress()802 Address xmm4SlotAddress() { return dwordAddress(AssembledTest::Xmm4Slot); } xmm5SlotAddress()803 Address xmm5SlotAddress() { return dwordAddress(AssembledTest::Xmm5Slot); } xmm6SlotAddress()804 Address xmm6SlotAddress() { return dwordAddress(AssembledTest::Xmm6Slot); } xmm7SlotAddress()805 Address xmm7SlotAddress() { return dwordAddress(AssembledTest::Xmm7Slot); } 806 807 // Returns the displacement that should be used when accessing the specified 808 // Dword in the scratchpad area. It needs to adjust for the initial 809 // instructions that are emitted before the call that materializes the IP 810 // register. dwordDisp(uint32_t Dword)811 uint32_t dwordDisp(uint32_t Dword) const { 812 EXPECT_LT(Dword, NumAllocatedDwords); 813 assert(Dword < NumAllocatedDwords); 814 static constexpr uint8_t PushBytes = 1; 815 static constexpr uint8_t CallImmBytes = 5; 816 return AssembledTest::MaximumCodeSize + (Dword * 4) - 817 (7 * PushBytes + CallImmBytes); 818 } 819 addPrologue()820 void addPrologue() { 821 __ pushl(GPRRegister::Encoded_Reg_eax); 822 __ pushl(GPRRegister::Encoded_Reg_ebx); 823 __ pushl(GPRRegister::Encoded_Reg_ecx); 824 __ pushl(GPRRegister::Encoded_Reg_edx); 825 __ pushl(GPRRegister::Encoded_Reg_edi); 826 __ pushl(GPRRegister::Encoded_Reg_esi); 827 __ pushl(GPRRegister::Encoded_Reg_ebp); 828 829 __ call(Immediate(4)); 830 __ popl(GPRRegister::Encoded_Reg_ebp); 831 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x00)); 832 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(0x00)); 833 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(0x00)); 834 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(0x00)); 835 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(0x00)); 836 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x00)); 837 } 838 addEpilogue()839 void addEpilogue() { 840 __ mov(IceType_i32, eaxSlotAddress(), GPRRegister::Encoded_Reg_eax); 841 __ mov(IceType_i32, ebxSlotAddress(), GPRRegister::Encoded_Reg_ebx); 842 __ mov(IceType_i32, ecxSlotAddress(), GPRRegister::Encoded_Reg_ecx); 843 __ mov(IceType_i32, edxSlotAddress(), GPRRegister::Encoded_Reg_edx); 844 __ mov(IceType_i32, ediSlotAddress(), GPRRegister::Encoded_Reg_edi); 845 __ mov(IceType_i32, esiSlotAddress(), GPRRegister::Encoded_Reg_esi); 846 __ mov(IceType_i32, ebpSlotAddress(), GPRRegister::Encoded_Reg_ebp); 847 __ mov(IceType_i32, espSlotAddress(), GPRRegister::Encoded_Reg_esp); 848 __ movups(xmm0SlotAddress(), XmmRegister::Encoded_Reg_xmm0); 849 __ movups(xmm1SlotAddress(), XmmRegister::Encoded_Reg_xmm1); 850 __ movups(xmm2SlotAddress(), XmmRegister::Encoded_Reg_xmm2); 851 __ movups(xmm3SlotAddress(), XmmRegister::Encoded_Reg_xmm3); 852 __ movups(xmm4SlotAddress(), XmmRegister::Encoded_Reg_xmm4); 853 __ movups(xmm5SlotAddress(), XmmRegister::Encoded_Reg_xmm5); 854 __ movups(xmm6SlotAddress(), XmmRegister::Encoded_Reg_xmm6); 855 __ movups(xmm7SlotAddress(), XmmRegister::Encoded_Reg_xmm7); 856 857 __ popl(GPRRegister::Encoded_Reg_ebp); 858 __ popl(GPRRegister::Encoded_Reg_esi); 859 __ popl(GPRRegister::Encoded_Reg_edi); 860 __ popl(GPRRegister::Encoded_Reg_edx); 861 __ popl(GPRRegister::Encoded_Reg_ecx); 862 __ popl(GPRRegister::Encoded_Reg_ebx); 863 __ popl(GPRRegister::Encoded_Reg_eax); 864 865 __ ret(); 866 } 867 868 bool NeedsEpilogue; 869 uint32_t NumAllocatedDwords; 870 }; 871 872 } // end of namespace Test 873 } // end of namespace X8632 874 } // end of namespace Ice 875 876 #endif // ASSEMBLERX8632_TESTUTIL_H_ 877