Home | History | Annotate | Download | only in AssemblerX8632
      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 
     48   AssemblerX8632TestBase() { reset(); }
     49 
     50   void reset() { Assembler = makeUnique<AssemblerX8632>(); }
     51 
     52   AssemblerX8632 *assembler() const { return Assembler.get(); }
     53 
     54   size_t codeBytesSize() const { return Assembler->getBufferView().size(); }
     55 
     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.
     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>
     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>
    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>
    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>
    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>
    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>
    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 
    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.
    525   template <typename C> static PackedArith<C> packedAs(const Dqword &D) {
    526     return PackedArith<C>(D);
    527   }
    528 
    529   AssemblerX8632Test() { reset(); }
    530 
    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 
    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.
    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 
    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 
    626     void run() const { reinterpret_cast<void (*)()>(ExecutableData)(); }
    627 
    628     uint32_t eax() const { return contentsOfDword(AssembledTest::EaxSlot); }
    629 
    630     uint32_t ebx() const { return contentsOfDword(AssembledTest::EbxSlot); }
    631 
    632     uint32_t ecx() const { return contentsOfDword(AssembledTest::EcxSlot); }
    633 
    634     uint32_t edx() const { return contentsOfDword(AssembledTest::EdxSlot); }
    635 
    636     uint32_t edi() const { return contentsOfDword(AssembledTest::EdiSlot); }
    637 
    638     uint32_t esi() const { return contentsOfDword(AssembledTest::EsiSlot); }
    639 
    640     uint32_t ebp() const { return contentsOfDword(AssembledTest::EbpSlot); }
    641 
    642     uint32_t esp() const { return contentsOfDword(AssembledTest::EspSlot); }
    643 
    644     template <typename T> T xmm0() const {
    645       return xmm<T>(AssembledTest::Xmm0Slot);
    646     }
    647 
    648     template <typename T> T xmm1() const {
    649       return xmm<T>(AssembledTest::Xmm1Slot);
    650     }
    651 
    652     template <typename T> T xmm2() const {
    653       return xmm<T>(AssembledTest::Xmm2Slot);
    654     }
    655 
    656     template <typename T> T xmm3() const {
    657       return xmm<T>(AssembledTest::Xmm3Slot);
    658     }
    659 
    660     template <typename T> T xmm4() const {
    661       return xmm<T>(AssembledTest::Xmm4Slot);
    662     }
    663 
    664     template <typename T> T xmm5() const {
    665       return xmm<T>(AssembledTest::Xmm5Slot);
    666     }
    667 
    668     template <typename T> T xmm6() const {
    669       return xmm<T>(AssembledTest::Xmm6Slot);
    670     }
    671 
    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>
    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>
    689     T contentsOfQword(uint32_t InitialDword) const {
    690       return *reinterpret_cast<T *>(static_cast<uint8_t *>(ExecutableData) +
    691                                     dwordOffset(InitialDword));
    692     }
    693 
    694     Dqword contentsOfDqword(uint32_t InitialDword) const {
    695       return *reinterpret_cast<Dqword *>(
    696                  static_cast<uint8_t *>(ExecutableData) +
    697                  dwordOffset(InitialDword));
    698     }
    699 
    700     template <typename T = uint32_t, typename = typename std::enable_if<
    701                                          sizeof(T) == sizeof(uint32_t)>::type>
    702     void setDwordTo(uint32_t Dword, T value) {
    703       *reinterpret_cast<uint32_t *>(static_cast<uint8_t *>(ExecutableData) +
    704                                     dwordOffset(Dword)) =
    705           *reinterpret_cast<uint32_t *>(&value);
    706     }
    707 
    708     template <typename T = uint64_t, typename = typename std::enable_if<
    709                                          sizeof(T) == sizeof(uint64_t)>::type>
    710     void setQwordTo(uint32_t InitialDword, T value) {
    711       *reinterpret_cast<uint64_t *>(static_cast<uint8_t *>(ExecutableData) +
    712                                     dwordOffset(InitialDword)) =
    713           *reinterpret_cast<uint64_t *>(&value);
    714     }
    715 
    716     void setDqwordTo(uint32_t InitialDword, const Dqword &qdword) {
    717       setQwordTo(InitialDword, qdword.U64[0]);
    718       setQwordTo(InitialDword + 2, qdword.U64[1]);
    719     }
    720 
    721   private:
    722     template <typename T>
    723     typename std::enable_if<std::is_same<T, Dqword>::value, Dqword>::type
    724     xmm(uint8_t Slot) const {
    725       return contentsOfDqword(Slot);
    726     }
    727 
    728     template <typename T>
    729     typename std::enable_if<!std::is_same<T, Dqword>::value, T>::type
    730     xmm(uint8_t Slot) const {
    731       constexpr bool TIs64Bit = sizeof(T) == sizeof(uint64_t);
    732       using _64BitType = typename std::conditional<TIs64Bit, T, uint64_t>::type;
    733       using _32BitType = typename std::conditional<TIs64Bit, uint32_t, T>::type;
    734       if (TIs64Bit) {
    735         return contentsOfQword<_64BitType>(Slot);
    736       }
    737       return contentsOfDword<_32BitType>(Slot);
    738     }
    739 
    740     static uint32_t dwordOffset(uint32_t Index) {
    741       return MaximumCodeSize + (Index * 4);
    742     }
    743 
    744     void *ExecutableData = nullptr;
    745     size_t Size;
    746   };
    747 
    748   // assemble created an AssembledTest with the jitted code. The first time
    749   // assemble is executed it will add the epilogue to the jitted code (which is
    750   // the reason why this method is not const qualified.
    751   AssembledTest assemble() {
    752     if (NeedsEpilogue) {
    753       addEpilogue();
    754     }
    755     NeedsEpilogue = false;
    756 
    757     for (const auto *Fixup : assembler()->fixups()) {
    758       Fixup->emitOffset(assembler());
    759     }
    760 
    761     return AssembledTest(codeBytes(), codeBytesSize(), NumAllocatedDwords);
    762   }
    763 
    764   // Allocates a new dword slot in the test's scratchpad area.
    765   uint32_t allocateDword() { return NumAllocatedDwords++; }
    766 
    767   // Allocates a new qword slot in the test's scratchpad area.
    768   uint32_t allocateQword() {
    769     uint32_t InitialDword = allocateDword();
    770     allocateDword();
    771     return InitialDword;
    772   }
    773 
    774   // Allocates a new dqword slot in the test's scratchpad area.
    775   uint32_t allocateDqword() {
    776     uint32_t InitialDword = allocateQword();
    777     allocateQword();
    778     return InitialDword;
    779   }
    780 
    781   Address dwordAddress(uint32_t Dword) {
    782     return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword), nullptr);
    783   }
    784 
    785 private:
    786   // e??SlotAddress returns an AssemblerX8632::Traits::Address that can be used
    787   // by the test cases to encode an address operand for accessing the slot for
    788   // the specified register. These are all private for, when jitting the test
    789   // code, tests should not tamper with these values. Besides, during the test
    790   // execution these slots' contents are undefined and should not be accessed.
    791   Address eaxSlotAddress() { return dwordAddress(AssembledTest::EaxSlot); }
    792   Address ebxSlotAddress() { return dwordAddress(AssembledTest::EbxSlot); }
    793   Address ecxSlotAddress() { return dwordAddress(AssembledTest::EcxSlot); }
    794   Address edxSlotAddress() { return dwordAddress(AssembledTest::EdxSlot); }
    795   Address ediSlotAddress() { return dwordAddress(AssembledTest::EdiSlot); }
    796   Address esiSlotAddress() { return dwordAddress(AssembledTest::EsiSlot); }
    797   Address ebpSlotAddress() { return dwordAddress(AssembledTest::EbpSlot); }
    798   Address espSlotAddress() { return dwordAddress(AssembledTest::EspSlot); }
    799   Address xmm0SlotAddress() { return dwordAddress(AssembledTest::Xmm0Slot); }
    800   Address xmm1SlotAddress() { return dwordAddress(AssembledTest::Xmm1Slot); }
    801   Address xmm2SlotAddress() { return dwordAddress(AssembledTest::Xmm2Slot); }
    802   Address xmm3SlotAddress() { return dwordAddress(AssembledTest::Xmm3Slot); }
    803   Address xmm4SlotAddress() { return dwordAddress(AssembledTest::Xmm4Slot); }
    804   Address xmm5SlotAddress() { return dwordAddress(AssembledTest::Xmm5Slot); }
    805   Address xmm6SlotAddress() { return dwordAddress(AssembledTest::Xmm6Slot); }
    806   Address xmm7SlotAddress() { return dwordAddress(AssembledTest::Xmm7Slot); }
    807 
    808   // Returns the displacement that should be used when accessing the specified
    809   // Dword in the scratchpad area. It needs to adjust for the initial
    810   // instructions that are emitted before the call that materializes the IP
    811   // register.
    812   uint32_t dwordDisp(uint32_t Dword) const {
    813     EXPECT_LT(Dword, NumAllocatedDwords);
    814     assert(Dword < NumAllocatedDwords);
    815     static constexpr uint8_t PushBytes = 1;
    816     static constexpr uint8_t CallImmBytes = 5;
    817     return AssembledTest::MaximumCodeSize + (Dword * 4) -
    818            (7 * PushBytes + CallImmBytes);
    819   }
    820 
    821   void addPrologue() {
    822     __ pushl(GPRRegister::Encoded_Reg_eax);
    823     __ pushl(GPRRegister::Encoded_Reg_ebx);
    824     __ pushl(GPRRegister::Encoded_Reg_ecx);
    825     __ pushl(GPRRegister::Encoded_Reg_edx);
    826     __ pushl(GPRRegister::Encoded_Reg_edi);
    827     __ pushl(GPRRegister::Encoded_Reg_esi);
    828     __ pushl(GPRRegister::Encoded_Reg_ebp);
    829 
    830     __ call(Immediate(4));
    831     __ popl(GPRRegister::Encoded_Reg_ebp);
    832     __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x00));
    833     __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(0x00));
    834     __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(0x00));
    835     __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(0x00));
    836     __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(0x00));
    837     __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x00));
    838   }
    839 
    840   void addEpilogue() {
    841     __ mov(IceType_i32, eaxSlotAddress(), GPRRegister::Encoded_Reg_eax);
    842     __ mov(IceType_i32, ebxSlotAddress(), GPRRegister::Encoded_Reg_ebx);
    843     __ mov(IceType_i32, ecxSlotAddress(), GPRRegister::Encoded_Reg_ecx);
    844     __ mov(IceType_i32, edxSlotAddress(), GPRRegister::Encoded_Reg_edx);
    845     __ mov(IceType_i32, ediSlotAddress(), GPRRegister::Encoded_Reg_edi);
    846     __ mov(IceType_i32, esiSlotAddress(), GPRRegister::Encoded_Reg_esi);
    847     __ mov(IceType_i32, ebpSlotAddress(), GPRRegister::Encoded_Reg_ebp);
    848     __ mov(IceType_i32, espSlotAddress(), GPRRegister::Encoded_Reg_esp);
    849     __ movups(xmm0SlotAddress(), XmmRegister::Encoded_Reg_xmm0);
    850     __ movups(xmm1SlotAddress(), XmmRegister::Encoded_Reg_xmm1);
    851     __ movups(xmm2SlotAddress(), XmmRegister::Encoded_Reg_xmm2);
    852     __ movups(xmm3SlotAddress(), XmmRegister::Encoded_Reg_xmm3);
    853     __ movups(xmm4SlotAddress(), XmmRegister::Encoded_Reg_xmm4);
    854     __ movups(xmm5SlotAddress(), XmmRegister::Encoded_Reg_xmm5);
    855     __ movups(xmm6SlotAddress(), XmmRegister::Encoded_Reg_xmm6);
    856     __ movups(xmm7SlotAddress(), XmmRegister::Encoded_Reg_xmm7);
    857 
    858     __ popl(GPRRegister::Encoded_Reg_ebp);
    859     __ popl(GPRRegister::Encoded_Reg_esi);
    860     __ popl(GPRRegister::Encoded_Reg_edi);
    861     __ popl(GPRRegister::Encoded_Reg_edx);
    862     __ popl(GPRRegister::Encoded_Reg_ecx);
    863     __ popl(GPRRegister::Encoded_Reg_ebx);
    864     __ popl(GPRRegister::Encoded_Reg_eax);
    865 
    866     __ ret();
    867   }
    868 
    869   bool NeedsEpilogue;
    870   uint32_t NumAllocatedDwords;
    871 };
    872 
    873 } // end of namespace Test
    874 } // end of namespace X8632
    875 } // end of namespace Ice
    876 
    877 #endif // ASSEMBLERX8632_TESTUTIL_H_
    878