Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(93)

Side by Side Diff: unittest/AssemblerX8632/TestUtil.h

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

Powered by Google App Engine
This is Rietveld 408576698