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