blob: 983a89ad30d5b4a1cf0328d7c880fe34e56a23dc [file] [log] [blame]
John Porto2fea26c2015-07-28 16:28:07 -07001//===- 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"
John Porto4a566862016-01-04 09:33:41 -080018#include "IceDefs.h"
John Porto2fea26c2015-07-28 16:28:07 -070019
20#include "gtest/gtest.h"
21
Nicolas Capens46f4fea2016-10-06 17:25:39 -040022#if defined(__unix__)
John Porto2fea26c2015-07-28 16:28:07 -070023#include <sys/mman.h>
Nicolas Capens46f4fea2016-10-06 17:25:39 -040024#elif defined(_WIN32)
25#define NOMINMAX
26#include <Windows.h>
27#else
28#error "Platform unsupported"
29#endif
30
31#include <cassert>
John Porto2fea26c2015-07-28 16:28:07 -070032
33namespace Ice {
34namespace X8632 {
35namespace Test {
36
37class AssemblerX8632TestBase : public ::testing::Test {
38protected:
39 using Address = AssemblerX8632::Traits::Address;
John Porto2fea26c2015-07-28 16:28:07 -070040 using Cond = AssemblerX8632::Traits::Cond;
41 using GPRRegister = AssemblerX8632::Traits::GPRRegister;
Jim Stichnoth5bff61c2015-10-28 09:26:00 -070042 using ByteRegister = AssemblerX8632::Traits::ByteRegister;
John Porto4a566862016-01-04 09:33:41 -080043 using Label = ::Ice::X8632::Label;
John Porto2fea26c2015-07-28 16:28:07 -070044 using Traits = AssemblerX8632::Traits;
45 using XmmRegister = AssemblerX8632::Traits::XmmRegister;
46 using X87STRegister = AssemblerX8632::Traits::X87STRegister;
47
48 AssemblerX8632TestBase() { reset(); }
49
John Porto4a566862016-01-04 09:33:41 -080050 void reset() { Assembler = makeUnique<AssemblerX8632>(); }
John Porto2fea26c2015-07-28 16:28:07 -070051
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
61private:
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.
77class AssemblerX8632LowLevelTest : public AssemblerX8632TestBase {
78protected:
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.
212class AssemblerX8632Test : public AssemblerX8632TestBase {
213protected:
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);
Nicolas Capens46f4fea2016-10-06 17:25:39 -0400581
582#if defined(__unix__)
John Porto2fea26c2015-07-28 16:28:07 -0700583 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);
Nicolas Capens46f4fea2016-10-06 17:25:39 -0400587#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
John Porto2fea26c2015-07-28 16:28:07 -0700596 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) {
Nicolas Capens46f4fea2016-10-06 17:25:39 -0400617#if defined(__unix__)
John Porto2fea26c2015-07-28 16:28:07 -0700618 munmap(ExecutableData, Size);
Nicolas Capens46f4fea2016-10-06 17:25:39 -0400619#elif defined(_WIN32)
620 VirtualFree(ExecutableData, 0, MEM_RELEASE);
621#endif
John Porto2fea26c2015-07-28 16:28:07 -0700622 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 }
John Porto2fea26c2015-07-28 16:28:07 -0700755 NeedsEpilogue = false;
John Porto6e8d3fa2016-02-04 10:35:20 -0800756
757 for (const auto *Fixup : assembler()->fixups()) {
758 Fixup->emitOffset(assembler());
759 }
760
John Porto2fea26c2015-07-28 16:28:07 -0700761 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) {
David Sehraa0b1a12015-10-27 16:55:40 -0700782 return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword), nullptr);
John Porto2fea26c2015-07-28 16:28:07 -0700783 }
784
785private:
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_