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