| OLD | NEW |
| (Empty) |
| 1 //===- subzero/unittest/IceAssemblerX8632.cpp - X8632 Assembler tests -----===// | |
| 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 #include "IceAssemblerX8632.h" | |
| 11 | |
| 12 #include "IceDefs.h" | |
| 13 | |
| 14 #include "gtest/gtest.h" | |
| 15 | |
| 16 #include <cstring> | |
| 17 #include <errno.h> | |
| 18 #include <iostream> | |
| 19 #include <memory> | |
| 20 #include <sys/mman.h> | |
| 21 #include <type_traits> | |
| 22 | |
| 23 namespace Ice { | |
| 24 namespace X8632 { | |
| 25 namespace { | |
| 26 | |
| 27 class AssemblerX8632TestBase : public ::testing::Test { | |
| 28 protected: | |
| 29 using Address = AssemblerX8632::Traits::Address; | |
| 30 using Cond = AssemblerX8632::Traits::Cond; | |
| 31 using GPRRegister = AssemblerX8632::Traits::GPRRegister; | |
| 32 using XmmRegister = AssemblerX8632::Traits::XmmRegister; | |
| 33 using X87STRegister = AssemblerX8632::Traits::X87STRegister; | |
| 34 | |
| 35 AssemblerX8632TestBase() { reset(); } | |
| 36 | |
| 37 void reset() { Assembler.reset(new AssemblerX8632()); } | |
| 38 | |
| 39 AssemblerX8632 *assembler() const { return Assembler.get(); } | |
| 40 | |
| 41 size_t codeBytesSize() const { return Assembler->getBufferView().size(); } | |
| 42 | |
| 43 const uint8_t *codeBytes() const { | |
| 44 return static_cast<const uint8_t *>( | |
| 45 static_cast<const void *>(Assembler->getBufferView().data())); | |
| 46 } | |
| 47 | |
| 48 private: | |
| 49 std::unique_ptr<AssemblerX8632> Assembler; | |
| 50 }; | |
| 51 | |
| 52 // __ is a helper macro. It allows test cases to emit X8632 assembly | |
| 53 // instructions with | |
| 54 // | |
| 55 // __ mov(GPRRegister::Reg_Eax, 1); | |
| 56 // __ ret(); | |
| 57 // | |
| 58 // and so on. The idea of having this was "stolen" from dart's unit tests. | |
| 59 #define __ (this->assembler())-> | |
| 60 | |
| 61 // AssemblerX8632LowLevelTest verify that the "basic" instructions the tests | |
| 62 // rely on are encoded correctly. Therefore, instead of executing the assembled | |
| 63 // code, these tests will verify that the assembled bytes are sane. | |
| 64 class AssemblerX8632LowLevelTest : public AssemblerX8632TestBase { | |
| 65 protected: | |
| 66 // verifyBytes is a template helper that takes a Buffer, and a variable number | |
| 67 // of bytes. As the name indicates, it is used to verify the bytes for an | |
| 68 // instruction encoding. | |
| 69 template <int N, int I> static void verifyBytes(const uint8_t *) { | |
| 70 static_assert(I == N, "Invalid template instantiation."); | |
| 71 } | |
| 72 | |
| 73 template <int N, int I = 0, typename... Args> | |
| 74 static void verifyBytes(const uint8_t *Buffer, uint8_t Byte, | |
| 75 Args... OtherBytes) { | |
| 76 static_assert(I < N, "Invalid template instantiation."); | |
| 77 EXPECT_EQ(Byte, Buffer[I]) << "Byte " << (I + 1) << " of " << N; | |
| 78 verifyBytes<N, I + 1>(Buffer, OtherBytes...); | |
| 79 assert(Buffer[I] == Byte); | |
| 80 } | |
| 81 }; | |
| 82 | |
| 83 TEST_F(AssemblerX8632LowLevelTest, Ret) { | |
| 84 __ ret(); | |
| 85 | |
| 86 constexpr size_t ByteCount = 1; | |
| 87 ASSERT_EQ(ByteCount, codeBytesSize()); | |
| 88 | |
| 89 verifyBytes<ByteCount>(codeBytes(), 0xc3); | |
| 90 } | |
| 91 | |
| 92 TEST_F(AssemblerX8632LowLevelTest, CallImm4) { | |
| 93 __ call(Immediate(4)); | |
| 94 | |
| 95 constexpr size_t ByteCount = 5; | |
| 96 ASSERT_EQ(ByteCount, codeBytesSize()); | |
| 97 | |
| 98 verifyBytes<ByteCount>(codeBytes(), 0xe8, 0x00, 0x00, 0x00, 0x00); | |
| 99 } | |
| 100 | |
| 101 TEST_F(AssemblerX8632LowLevelTest, PopRegs) { | |
| 102 __ popl(GPRRegister::Encoded_Reg_eax); | |
| 103 __ popl(GPRRegister::Encoded_Reg_ebx); | |
| 104 __ popl(GPRRegister::Encoded_Reg_ecx); | |
| 105 __ popl(GPRRegister::Encoded_Reg_edx); | |
| 106 __ popl(GPRRegister::Encoded_Reg_edi); | |
| 107 __ popl(GPRRegister::Encoded_Reg_esi); | |
| 108 __ popl(GPRRegister::Encoded_Reg_ebp); | |
| 109 | |
| 110 constexpr size_t ByteCount = 7; | |
| 111 ASSERT_EQ(ByteCount, codeBytesSize()); | |
| 112 | |
| 113 constexpr uint8_t PopOpcode = 0x58; | |
| 114 verifyBytes<ByteCount>(codeBytes(), PopOpcode | GPRRegister::Encoded_Reg_eax, | |
| 115 PopOpcode | GPRRegister::Encoded_Reg_ebx, | |
| 116 PopOpcode | GPRRegister::Encoded_Reg_ecx, | |
| 117 PopOpcode | GPRRegister::Encoded_Reg_edx, | |
| 118 PopOpcode | GPRRegister::Encoded_Reg_edi, | |
| 119 PopOpcode | GPRRegister::Encoded_Reg_esi, | |
| 120 PopOpcode | GPRRegister::Encoded_Reg_ebp); | |
| 121 } | |
| 122 | |
| 123 TEST_F(AssemblerX8632LowLevelTest, PushRegs) { | |
| 124 __ pushl(GPRRegister::Encoded_Reg_eax); | |
| 125 __ pushl(GPRRegister::Encoded_Reg_ebx); | |
| 126 __ pushl(GPRRegister::Encoded_Reg_ecx); | |
| 127 __ pushl(GPRRegister::Encoded_Reg_edx); | |
| 128 __ pushl(GPRRegister::Encoded_Reg_edi); | |
| 129 __ pushl(GPRRegister::Encoded_Reg_esi); | |
| 130 __ pushl(GPRRegister::Encoded_Reg_ebp); | |
| 131 | |
| 132 constexpr size_t ByteCount = 7; | |
| 133 ASSERT_EQ(ByteCount, codeBytesSize()); | |
| 134 | |
| 135 constexpr uint8_t PushOpcode = 0x50; | |
| 136 verifyBytes<ByteCount>(codeBytes(), PushOpcode | GPRRegister::Encoded_Reg_eax, | |
| 137 PushOpcode | GPRRegister::Encoded_Reg_ebx, | |
| 138 PushOpcode | GPRRegister::Encoded_Reg_ecx, | |
| 139 PushOpcode | GPRRegister::Encoded_Reg_edx, | |
| 140 PushOpcode | GPRRegister::Encoded_Reg_edi, | |
| 141 PushOpcode | GPRRegister::Encoded_Reg_esi, | |
| 142 PushOpcode | GPRRegister::Encoded_Reg_ebp); | |
| 143 } | |
| 144 | |
| 145 TEST_F(AssemblerX8632LowLevelTest, MovRegisterZero) { | |
| 146 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x00)); | |
| 147 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(0x00)); | |
| 148 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(0x00)); | |
| 149 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(0x00)); | |
| 150 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(0x00)); | |
| 151 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x00)); | |
| 152 | |
| 153 constexpr size_t MovReg32BitImmBytes = 5; | |
| 154 constexpr size_t ByteCount = 6 * MovReg32BitImmBytes; | |
| 155 ASSERT_EQ(ByteCount, codeBytesSize()); | |
| 156 | |
| 157 constexpr uint8_t MovOpcode = 0xb8; | |
| 158 verifyBytes<ByteCount>( | |
| 159 codeBytes(), MovOpcode | GPRRegister::Encoded_Reg_eax, 0x00, 0x00, 0x00, | |
| 160 0x00, MovOpcode | GPRRegister::Encoded_Reg_ebx, 0x00, 0x00, 0x00, 0x00, | |
| 161 MovOpcode | GPRRegister::Encoded_Reg_ecx, 0x00, 0x00, 0x00, 0x00, | |
| 162 MovOpcode | GPRRegister::Encoded_Reg_edx, 0x00, 0x00, 0x00, 0x00, | |
| 163 MovOpcode | GPRRegister::Encoded_Reg_edi, 0x00, 0x00, 0x00, 0x00, | |
| 164 MovOpcode | GPRRegister::Encoded_Reg_esi, 0x00, 0x00, 0x00, 0x00); | |
| 165 } | |
| 166 | |
| 167 TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) { | |
| 168 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_eax, | |
| 169 GPRRegister::Encoded_Reg_ebx); | |
| 170 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ebx, | |
| 171 GPRRegister::Encoded_Reg_ecx); | |
| 172 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ecx, | |
| 173 GPRRegister::Encoded_Reg_edx); | |
| 174 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edx, | |
| 175 GPRRegister::Encoded_Reg_edi); | |
| 176 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edi, | |
| 177 GPRRegister::Encoded_Reg_esi); | |
| 178 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_esi, | |
| 179 GPRRegister::Encoded_Reg_eax); | |
| 180 | |
| 181 const size_t CmpRegRegBytes = 2; | |
| 182 const size_t ByteCount = 6 * CmpRegRegBytes; | |
| 183 ASSERT_EQ(ByteCount, codeBytesSize()); | |
| 184 | |
| 185 constexpr size_t CmpOpcode = 0x3b; | |
| 186 constexpr size_t ModRm = 0xC0 /* Register Addressing */; | |
| 187 verifyBytes<ByteCount>( | |
| 188 codeBytes(), CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_eax << 3) | | |
| 189 GPRRegister::Encoded_Reg_ebx, | |
| 190 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ebx << 3) | | |
| 191 GPRRegister::Encoded_Reg_ecx, | |
| 192 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ecx << 3) | | |
| 193 GPRRegister::Encoded_Reg_edx, | |
| 194 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edx << 3) | | |
| 195 GPRRegister::Encoded_Reg_edi, | |
| 196 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edi << 3) | | |
| 197 GPRRegister::Encoded_Reg_esi, | |
| 198 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_esi << 3) | | |
| 199 GPRRegister::Encoded_Reg_eax); | |
| 200 } | |
| 201 | |
| 202 // After these tests we should have a sane environment; we know the following | |
| 203 // work: | |
| 204 // | |
| 205 // (*) zeroing eax, ebx, ecx, edx, edi, and esi; | |
| 206 // (*) call $4 instruction (used for ip materialization); | |
| 207 // (*) register push and pop; | |
| 208 // (*) cmp reg, reg; and | |
| 209 // (*) returning from functions. | |
| 210 // | |
| 211 // We can now dive into testing each emitting method in AssemblerX8632. Each | |
| 212 // test will emit some instructions for performing the test. The assembled | |
| 213 // instructions will operate in a "safe" environment. All x86-32 registers are | |
| 214 // spilled to the program stack, and the registers are then zeroed out, with the | |
| 215 // exception of %esp and %ebp. | |
| 216 // | |
| 217 // The jitted code and the unittest code will share the same stack. Therefore, | |
| 218 // test harnesses need to ensure it does not leave anything it pushed on the | |
| 219 // stack. | |
| 220 // | |
| 221 // %ebp is initialized with a pointer for rIP-based addressing. This pointer is | |
| 222 // used for position-independent access to a scratchpad area for use in tests. | |
| 223 // This mechanism is used because the test framework needs to generate addresses | |
| 224 // that work on both x86-32 and x86-64 hosts, but are encodable using our x86-32 | |
| 225 // assembler. This is made possible because the encoding for | |
| 226 // | |
| 227 // pushq %rax (x86-64 only) | |
| 228 // | |
| 229 // is the same as the one for | |
| 230 // | |
| 231 // pushl %eax (x86-32 only; not encodable in x86-64) | |
| 232 // | |
| 233 // Likewise, the encodings for | |
| 234 // | |
| 235 // movl offset(%ebp), %reg (32-bit only) | |
| 236 // movl <src>, offset(%ebp) (32-bit only) | |
| 237 // | |
| 238 // and | |
| 239 // | |
| 240 // movl offset(%rbp), %reg (64-bit only) | |
| 241 // movl <src>, offset(%rbp) (64-bit only) | |
| 242 // | |
| 243 // are also the same. | |
| 244 // | |
| 245 // We use a call instruction in order to generate a natural sized address on the | |
| 246 // stack. Said address is then removed from the stack with a pop %rBP, which can | |
| 247 // then be used to address memory safely in either x86-32 or x86-64, as long as | |
| 248 // the test code does not perform any arithmetic operation that writes to %rBP. | |
| 249 // This PC materialization technique is very common in x86-32 PIC. | |
| 250 // | |
| 251 // %rBP is used to provide the tests with a scratchpad area that can safely and | |
| 252 // portably be written to and read from. This scratchpad area is also used to | |
| 253 // store the "final" values in eax, ebx, ecx, edx, esi, and edi, allowing the | |
| 254 // harnesses access to 6 "return values" instead of the usual single return | |
| 255 // value supported by C++. | |
| 256 // | |
| 257 // The jitted code will look like the following: | |
| 258 // | |
| 259 // test: | |
| 260 // push %eax | |
| 261 // push %ebx | |
| 262 // push %ecx | |
| 263 // push %edx | |
| 264 // push %edi | |
| 265 // push %esi | |
| 266 // push %ebp | |
| 267 // call test$materialize_ip | |
| 268 // test$materialize_ip: <<------- %eBP will point here | |
| 269 // pop %ebp | |
| 270 // mov $0, %eax | |
| 271 // mov $0, %ebx | |
| 272 // mov $0, %ecx | |
| 273 // mov $0, %edx | |
| 274 // mov $0, %edi | |
| 275 // mov $0, %esi | |
| 276 // | |
| 277 // << test code goes here >> | |
| 278 // | |
| 279 // mov %eax, { 0 + $ScratchpadOffset}(%ebp) | |
| 280 // mov %ebx, { 4 + $ScratchpadOffset}(%ebp) | |
| 281 // mov %ecx, { 8 + $ScratchpadOffset}(%ebp) | |
| 282 // mov %edx, {12 + $ScratchpadOffset}(%ebp) | |
| 283 // mov %edi, {16 + $ScratchpadOffset}(%ebp) | |
| 284 // mov %esi, {20 + $ScratchpadOffset}(%ebp) | |
| 285 // | |
| 286 // pop %ebp | |
| 287 // pop %esi | |
| 288 // pop %edi | |
| 289 // pop %edx | |
| 290 // pop %ecx | |
| 291 // pop %ebx | |
| 292 // pop %eax | |
| 293 // ret | |
| 294 // | |
| 295 // << ... >> | |
| 296 // | |
| 297 // scratchpad: <<------- accessed via $Offset(%ebp) | |
| 298 // | |
| 299 // << test scratch area >> | |
| 300 // | |
| 301 // TODO(jpp): test the | |
| 302 // | |
| 303 // mov %reg, $Offset(%ebp) | |
| 304 // | |
| 305 // encodings using the low level assembler test ensuring that the register | |
| 306 // values can be written to the scratchpad area. | |
| 307 class AssemblerX8632Test : public AssemblerX8632TestBase { | |
| 308 protected: | |
| 309 AssemblerX8632Test() { reset(); } | |
| 310 | |
| 311 void reset() { | |
| 312 AssemblerX8632TestBase::reset(); | |
| 313 | |
| 314 NeedsEpilogue = true; | |
| 315 // 6 dwords are allocated for saving the GPR state after the jitted code | |
| 316 // runs. | |
| 317 NumAllocatedDwords = 6; | |
| 318 addPrologue(); | |
| 319 } | |
| 320 | |
| 321 // AssembledBuffer is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer | |
| 322 // contains both the test code as well as prologue/epilogue, and the | |
| 323 // scratchpad area that tests may use -- all tests use this scratchpad area | |
| 324 // for storing the processor's registers after the tests executed. This class | |
| 325 // also exposes helper methods for reading the register state after test | |
| 326 // execution, as well as for reading the scratchpad area. | |
| 327 class AssembledBuffer { | |
| 328 AssembledBuffer() = delete; | |
| 329 AssembledBuffer(const AssembledBuffer &) = delete; | |
| 330 AssembledBuffer &operator=(const AssembledBuffer &) = delete; | |
| 331 | |
| 332 public: | |
| 333 static constexpr uint32_t MaximumCodeSize = 1 << 20; | |
| 334 static constexpr uint32_t EaxSlot = 0; | |
| 335 static constexpr uint32_t EbxSlot = 1; | |
| 336 static constexpr uint32_t EcxSlot = 2; | |
| 337 static constexpr uint32_t EdxSlot = 3; | |
| 338 static constexpr uint32_t EdiSlot = 4; | |
| 339 static constexpr uint32_t EsiSlot = 5; | |
| 340 | |
| 341 AssembledBuffer(const uint8_t *Data, const size_t MySize, | |
| 342 const size_t ExtraStorageDwords) | |
| 343 : Size(MaximumCodeSize + 4 * ExtraStorageDwords) { | |
| 344 // MaxCodeSize is needed because EXPECT_LT needs a symbol with a name -- | |
| 345 // probably a compiler bug? | |
| 346 uint32_t MaxCodeSize = MaximumCodeSize; | |
| 347 EXPECT_LT(MySize, MaxCodeSize); | |
| 348 assert(MySize < MaximumCodeSize); | |
| 349 ExecutableData = mmap(nullptr, Size, PROT_WRITE | PROT_READ | PROT_EXEC, | |
| 350 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| 351 EXPECT_NE(MAP_FAILED, ExecutableData) << strerror(errno); | |
| 352 assert(MAP_FAILED != ExecutableData); | |
| 353 std::memcpy(ExecutableData, Data, MySize); | |
| 354 } | |
| 355 | |
| 356 // We allow AssembledBuffer to be moved so that we can return objects of | |
| 357 // this type. | |
| 358 AssembledBuffer(AssembledBuffer &&Buffer) | |
| 359 : ExecutableData(Buffer.ExecutableData), Size(Buffer.Size) { | |
| 360 Buffer.ExecutableData = nullptr; | |
| 361 Buffer.Size = 0; | |
| 362 } | |
| 363 | |
| 364 AssembledBuffer &operator=(AssembledBuffer &&Buffer) { | |
| 365 ExecutableData = Buffer.ExecutableData; | |
| 366 Buffer.ExecutableData = nullptr; | |
| 367 Size = Buffer.Size; | |
| 368 Buffer.Size = 0; | |
| 369 return *this; | |
| 370 } | |
| 371 | |
| 372 ~AssembledBuffer() { | |
| 373 if (ExecutableData != nullptr) { | |
| 374 munmap(ExecutableData, Size); | |
| 375 ExecutableData = nullptr; | |
| 376 } | |
| 377 } | |
| 378 | |
| 379 void run() const { reinterpret_cast<void (*)()>(ExecutableData)(); } | |
| 380 | |
| 381 uint32_t eax() const { return contentsOfDword(AssembledBuffer::EaxSlot); } | |
| 382 | |
| 383 uint32_t ebx() const { return contentsOfDword(AssembledBuffer::EbxSlot); } | |
| 384 | |
| 385 uint32_t ecx() const { return contentsOfDword(AssembledBuffer::EcxSlot); } | |
| 386 | |
| 387 uint32_t edx() const { return contentsOfDword(AssembledBuffer::EdxSlot); } | |
| 388 | |
| 389 uint32_t edi() const { return contentsOfDword(AssembledBuffer::EdiSlot); } | |
| 390 | |
| 391 uint32_t esi() const { return contentsOfDword(AssembledBuffer::EsiSlot); } | |
| 392 | |
| 393 // contentsOfDword is used for reading the values in the scratchpad area. | |
| 394 // Valid arguments are the dword ids returned by | |
| 395 // AssemblerX8632Test::allocateDword() -- other inputs are considered | |
| 396 // invalid, and are not guaranteed to work if the implementation changes. | |
| 397 uint32_t contentsOfDword(uint32_t Dword) const { | |
| 398 return *reinterpret_cast<uint32_t *>( | |
| 399 static_cast<uint8_t *>(ExecutableData) + dwordOffset(Dword)); | |
| 400 } | |
| 401 | |
| 402 private: | |
| 403 static uint32_t dwordOffset(uint32_t Index) { | |
| 404 return MaximumCodeSize + (Index * 4); | |
| 405 } | |
| 406 | |
| 407 void *ExecutableData = nullptr; | |
| 408 size_t Size; | |
| 409 }; | |
| 410 | |
| 411 // assemble created an AssembledBuffer with the jitted code. The first time | |
| 412 // assemble is executed it will add the epilogue to the jitted code (which is | |
| 413 // the reason why this method is not const qualified. | |
| 414 AssembledBuffer assemble() { | |
| 415 if (NeedsEpilogue) { | |
| 416 addEpilogue(); | |
| 417 } | |
| 418 | |
| 419 NeedsEpilogue = false; | |
| 420 return AssembledBuffer(codeBytes(), codeBytesSize(), NumAllocatedDwords); | |
| 421 } | |
| 422 | |
| 423 // Allocates a new dword slot in the test's scratchpad area. | |
| 424 uint32_t allocateDword() { return NumAllocatedDwords++; } | |
| 425 | |
| 426 Address dwordAddress(uint32_t Dword) { | |
| 427 return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword)); | |
| 428 } | |
| 429 | |
| 430 private: | |
| 431 // e??SlotAddress returns an AssemblerX8632::Traits::Address that can be used | |
| 432 // by the test cases to encode an address operand for accessing the slot for | |
| 433 // the specified register. These are all private for, when jitting the test | |
| 434 // code, tests should not tamper with these values. Besides, during the test | |
| 435 // execution these slots' contents are undefined and should not be accessed. | |
| 436 Address eaxSlotAddress() { return dwordAddress(AssembledBuffer::EaxSlot); } | |
| 437 Address ebxSlotAddress() { return dwordAddress(AssembledBuffer::EbxSlot); } | |
| 438 Address ecxSlotAddress() { return dwordAddress(AssembledBuffer::EcxSlot); } | |
| 439 Address edxSlotAddress() { return dwordAddress(AssembledBuffer::EdxSlot); } | |
| 440 Address ediSlotAddress() { return dwordAddress(AssembledBuffer::EdiSlot); } | |
| 441 Address esiSlotAddress() { return dwordAddress(AssembledBuffer::EsiSlot); } | |
| 442 | |
| 443 // Returns the displacement that should be used when accessing the specified | |
| 444 // Dword in the scratchpad area. It needs to adjust for the initial | |
| 445 // instructions that are emitted before the call that materializes the IP | |
| 446 // register. | |
| 447 uint32_t dwordDisp(uint32_t Dword) const { | |
| 448 EXPECT_LT(Dword, NumAllocatedDwords); | |
| 449 assert(Dword < NumAllocatedDwords); | |
| 450 static constexpr uint8_t PushBytes = 1; | |
| 451 static constexpr uint8_t CallImmBytes = 5; | |
| 452 return AssembledBuffer::MaximumCodeSize + (Dword * 4) - | |
| 453 (7 * PushBytes + CallImmBytes); | |
| 454 } | |
| 455 | |
| 456 void addPrologue() { | |
| 457 __ pushl(GPRRegister::Encoded_Reg_eax); | |
| 458 __ pushl(GPRRegister::Encoded_Reg_ebx); | |
| 459 __ pushl(GPRRegister::Encoded_Reg_ecx); | |
| 460 __ pushl(GPRRegister::Encoded_Reg_edx); | |
| 461 __ pushl(GPRRegister::Encoded_Reg_edi); | |
| 462 __ pushl(GPRRegister::Encoded_Reg_esi); | |
| 463 __ pushl(GPRRegister::Encoded_Reg_ebp); | |
| 464 | |
| 465 __ call(Immediate(4)); | |
| 466 __ popl(GPRRegister::Encoded_Reg_ebp); | |
| 467 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x00)); | |
| 468 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(0x00)); | |
| 469 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(0x00)); | |
| 470 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(0x00)); | |
| 471 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(0x00)); | |
| 472 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x00)); | |
| 473 } | |
| 474 | |
| 475 void addEpilogue() { | |
| 476 __ mov(IceType_i32, eaxSlotAddress(), GPRRegister::Encoded_Reg_eax); | |
| 477 __ mov(IceType_i32, ebxSlotAddress(), GPRRegister::Encoded_Reg_ebx); | |
| 478 __ mov(IceType_i32, ecxSlotAddress(), GPRRegister::Encoded_Reg_ecx); | |
| 479 __ mov(IceType_i32, edxSlotAddress(), GPRRegister::Encoded_Reg_edx); | |
| 480 __ mov(IceType_i32, ediSlotAddress(), GPRRegister::Encoded_Reg_edi); | |
| 481 __ mov(IceType_i32, esiSlotAddress(), GPRRegister::Encoded_Reg_esi); | |
| 482 | |
| 483 __ popl(GPRRegister::Encoded_Reg_ebp); | |
| 484 __ popl(GPRRegister::Encoded_Reg_esi); | |
| 485 __ popl(GPRRegister::Encoded_Reg_edi); | |
| 486 __ popl(GPRRegister::Encoded_Reg_edx); | |
| 487 __ popl(GPRRegister::Encoded_Reg_ecx); | |
| 488 __ popl(GPRRegister::Encoded_Reg_ebx); | |
| 489 __ popl(GPRRegister::Encoded_Reg_eax); | |
| 490 | |
| 491 __ ret(); | |
| 492 } | |
| 493 | |
| 494 bool NeedsEpilogue; | |
| 495 uint32_t NumAllocatedDwords; | |
| 496 }; | |
| 497 | |
| 498 TEST_F(AssemblerX8632Test, MovRegImm) { | |
| 499 constexpr uint32_t ExpectedEax = 0x000000FFul; | |
| 500 constexpr uint32_t ExpectedEbx = 0x0000FF00ul; | |
| 501 constexpr uint32_t ExpectedEcx = 0x00FF0000ul; | |
| 502 constexpr uint32_t ExpectedEdx = 0xFF000000ul; | |
| 503 constexpr uint32_t ExpectedEdi = 0x6AAA0006ul; | |
| 504 constexpr uint32_t ExpectedEsi = 0x6000AAA6ul; | |
| 505 | |
| 506 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ExpectedEax)); | |
| 507 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(ExpectedEbx)); | |
| 508 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(ExpectedEcx)); | |
| 509 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(ExpectedEdx)); | |
| 510 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(ExpectedEdi)); | |
| 511 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedEsi)); | |
| 512 | |
| 513 AssembledBuffer test = assemble(); | |
| 514 test.run(); | |
| 515 EXPECT_EQ(ExpectedEax, test.eax()); | |
| 516 EXPECT_EQ(ExpectedEbx, test.ebx()); | |
| 517 EXPECT_EQ(ExpectedEcx, test.ecx()); | |
| 518 EXPECT_EQ(ExpectedEdx, test.edx()); | |
| 519 EXPECT_EQ(ExpectedEdi, test.edi()); | |
| 520 EXPECT_EQ(ExpectedEsi, test.esi()); | |
| 521 } | |
| 522 | |
| 523 TEST_F(AssemblerX8632Test, MovMemImm) { | |
| 524 const uint32_t T0 = allocateDword(); | |
| 525 constexpr uint32_t ExpectedT0 = 0x00111100ul; | |
| 526 const uint32_t T1 = allocateDword(); | |
| 527 constexpr uint32_t ExpectedT1 = 0x00222200ul; | |
| 528 const uint32_t T2 = allocateDword(); | |
| 529 constexpr uint32_t ExpectedT2 = 0x03333000ul; | |
| 530 const uint32_t T3 = allocateDword(); | |
| 531 constexpr uint32_t ExpectedT3 = 0x00444400ul; | |
| 532 | |
| 533 __ mov(IceType_i32, dwordAddress(T0), Immediate(ExpectedT0)); | |
| 534 __ mov(IceType_i32, dwordAddress(T1), Immediate(ExpectedT1)); | |
| 535 __ mov(IceType_i32, dwordAddress(T2), Immediate(ExpectedT2)); | |
| 536 __ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3)); | |
| 537 | |
| 538 AssembledBuffer test = assemble(); | |
| 539 test.run(); | |
| 540 EXPECT_EQ(0ul, test.eax()); | |
| 541 EXPECT_EQ(0ul, test.ebx()); | |
| 542 EXPECT_EQ(0ul, test.ecx()); | |
| 543 EXPECT_EQ(0ul, test.edx()); | |
| 544 EXPECT_EQ(0ul, test.edi()); | |
| 545 EXPECT_EQ(0ul, test.esi()); | |
| 546 EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0)); | |
| 547 EXPECT_EQ(ExpectedT1, test.contentsOfDword(T1)); | |
| 548 EXPECT_EQ(ExpectedT2, test.contentsOfDword(T2)); | |
| 549 EXPECT_EQ(ExpectedT3, test.contentsOfDword(T3)); | |
| 550 } | |
| 551 | |
| 552 TEST_F(AssemblerX8632Test, MovMemReg) { | |
| 553 const uint32_t T0 = allocateDword(); | |
| 554 constexpr uint32_t ExpectedT0 = 0x00111100ul; | |
| 555 const uint32_t T1 = allocateDword(); | |
| 556 constexpr uint32_t ExpectedT1 = 0x00222200ul; | |
| 557 const uint32_t T2 = allocateDword(); | |
| 558 constexpr uint32_t ExpectedT2 = 0x00333300ul; | |
| 559 const uint32_t T3 = allocateDword(); | |
| 560 constexpr uint32_t ExpectedT3 = 0x00444400ul; | |
| 561 const uint32_t T4 = allocateDword(); | |
| 562 constexpr uint32_t ExpectedT4 = 0x00555500ul; | |
| 563 const uint32_t T5 = allocateDword(); | |
| 564 constexpr uint32_t ExpectedT5 = 0x00666600ul; | |
| 565 | |
| 566 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ExpectedT0)); | |
| 567 __ mov(IceType_i32, dwordAddress(T0), GPRRegister::Encoded_Reg_eax); | |
| 568 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(ExpectedT1)); | |
| 569 __ mov(IceType_i32, dwordAddress(T1), GPRRegister::Encoded_Reg_ebx); | |
| 570 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(ExpectedT2)); | |
| 571 __ mov(IceType_i32, dwordAddress(T2), GPRRegister::Encoded_Reg_ecx); | |
| 572 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(ExpectedT3)); | |
| 573 __ mov(IceType_i32, dwordAddress(T3), GPRRegister::Encoded_Reg_edx); | |
| 574 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(ExpectedT4)); | |
| 575 __ mov(IceType_i32, dwordAddress(T4), GPRRegister::Encoded_Reg_edi); | |
| 576 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedT5)); | |
| 577 __ mov(IceType_i32, dwordAddress(T5), GPRRegister::Encoded_Reg_esi); | |
| 578 | |
| 579 AssembledBuffer test = assemble(); | |
| 580 test.run(); | |
| 581 EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0)); | |
| 582 EXPECT_EQ(ExpectedT1, test.contentsOfDword(T1)); | |
| 583 EXPECT_EQ(ExpectedT2, test.contentsOfDword(T2)); | |
| 584 EXPECT_EQ(ExpectedT3, test.contentsOfDword(T3)); | |
| 585 EXPECT_EQ(ExpectedT4, test.contentsOfDword(T4)); | |
| 586 EXPECT_EQ(ExpectedT5, test.contentsOfDword(T5)); | |
| 587 } | |
| 588 | |
| 589 TEST_F(AssemblerX8632Test, MovRegReg) { | |
| 590 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x20)); | |
| 591 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, | |
| 592 GPRRegister::Encoded_Reg_eax); | |
| 593 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, | |
| 594 GPRRegister::Encoded_Reg_ebx); | |
| 595 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, | |
| 596 GPRRegister::Encoded_Reg_ecx); | |
| 597 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, | |
| 598 GPRRegister::Encoded_Reg_edx); | |
| 599 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, | |
| 600 GPRRegister::Encoded_Reg_edi); | |
| 601 | |
| 602 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x55000000ul)); | |
| 603 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, | |
| 604 GPRRegister::Encoded_Reg_esi); | |
| 605 | |
| 606 AssembledBuffer test = assemble(); | |
| 607 test.run(); | |
| 608 EXPECT_EQ(0x55000000ul, test.eax()); | |
| 609 EXPECT_EQ(0x20ul, test.ebx()); | |
| 610 EXPECT_EQ(0x20ul, test.ecx()); | |
| 611 EXPECT_EQ(0x20ul, test.edx()); | |
| 612 EXPECT_EQ(0x20ul, test.edi()); | |
| 613 EXPECT_EQ(0x55000000ul, test.esi()); | |
| 614 } | |
| 615 | |
| 616 TEST_F(AssemblerX8632Test, MovRegMem) { | |
| 617 const uint32_t T0 = allocateDword(); | |
| 618 constexpr uint32_t ExpectedT0 = 0x00111100ul; | |
| 619 const uint32_t T1 = allocateDword(); | |
| 620 constexpr uint32_t ExpectedT1 = 0x00222200ul; | |
| 621 const uint32_t T2 = allocateDword(); | |
| 622 constexpr uint32_t ExpectedT2 = 0x00333300ul; | |
| 623 const uint32_t T3 = allocateDword(); | |
| 624 constexpr uint32_t ExpectedT3 = 0x00444400ul; | |
| 625 const uint32_t T4 = allocateDword(); | |
| 626 constexpr uint32_t ExpectedT4 = 0x00555500ul; | |
| 627 const uint32_t T5 = allocateDword(); | |
| 628 constexpr uint32_t ExpectedT5 = 0x00666600ul; | |
| 629 | |
| 630 __ mov(IceType_i32, dwordAddress(T0), Immediate(ExpectedT0)); | |
| 631 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, dwordAddress(T0)); | |
| 632 | |
| 633 __ mov(IceType_i32, dwordAddress(T1), Immediate(ExpectedT1)); | |
| 634 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, dwordAddress(T1)); | |
| 635 | |
| 636 __ mov(IceType_i32, dwordAddress(T2), Immediate(ExpectedT2)); | |
| 637 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, dwordAddress(T2)); | |
| 638 | |
| 639 __ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3)); | |
| 640 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, dwordAddress(T3)); | |
| 641 | |
| 642 __ mov(IceType_i32, dwordAddress(T4), Immediate(ExpectedT4)); | |
| 643 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, dwordAddress(T4)); | |
| 644 | |
| 645 __ mov(IceType_i32, dwordAddress(T5), Immediate(ExpectedT5)); | |
| 646 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, dwordAddress(T5)); | |
| 647 | |
| 648 AssembledBuffer test = assemble(); | |
| 649 test.run(); | |
| 650 EXPECT_EQ(ExpectedT0, test.eax()); | |
| 651 EXPECT_EQ(ExpectedT1, test.ebx()); | |
| 652 EXPECT_EQ(ExpectedT2, test.ecx()); | |
| 653 EXPECT_EQ(ExpectedT3, test.edx()); | |
| 654 EXPECT_EQ(ExpectedT4, test.edi()); | |
| 655 EXPECT_EQ(ExpectedT5, test.esi()); | |
| 656 } | |
| 657 | |
| 658 TEST_F(AssemblerX8632Test, J) { | |
| 659 #define TestJ(C, Near, Src0, Value0, Src1, Value1, Dest) \ | |
| 660 do { \ | |
| 661 const bool NearJmp = std::strcmp(#Near, "Near") == 0; \ | |
| 662 Label ShouldBeTaken; \ | |
| 663 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \ | |
| 664 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \ | |
| 665 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0xBEEF)); \ | |
| 666 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, \ | |
| 667 GPRRegister::Encoded_Reg_##Src1); \ | |
| 668 __ j(Cond::Br_##C, &ShouldBeTaken, NearJmp); \ | |
| 669 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0xC0FFEE)); \ | |
| 670 __ bind(&ShouldBeTaken); \ | |
| 671 AssembledBuffer test = assemble(); \ | |
| 672 test.run(); \ | |
| 673 EXPECT_EQ(Value0, test.Src0()) << "Br_" #C ", " #Near; \ | |
| 674 EXPECT_EQ(Value1, test.Src1()) << "Br_" #C ", " #Near; \ | |
| 675 EXPECT_EQ(0xBEEFul, test.Dest()) << "Br_" #C ", " #Near; \ | |
| 676 reset(); \ | |
| 677 } while (0) | |
| 678 | |
| 679 TestJ(o, Near, eax, 0x80000000ul, ebx, 0x1ul, ecx); | |
| 680 TestJ(o, Far, ebx, 0x80000000ul, ecx, 0x1ul, edx); | |
| 681 TestJ(no, Near, ecx, 0x1ul, edx, 0x1ul, edi); | |
| 682 TestJ(no, Far, edx, 0x1ul, edi, 0x1ul, esi); | |
| 683 TestJ(b, Near, edi, 0x1ul, esi, 0x80000000ul, eax); | |
| 684 TestJ(b, Far, esi, 0x1ul, eax, 0x80000000ul, ebx); | |
| 685 TestJ(ae, Near, eax, 0x80000000ul, ebx, 0x1ul, ecx); | |
| 686 TestJ(ae, Far, ebx, 0x80000000ul, ecx, 0x1ul, edx); | |
| 687 TestJ(e, Near, ecx, 0x80000000ul, edx, 0x80000000ul, edi); | |
| 688 TestJ(e, Far, edx, 0x80000000ul, edi, 0x80000000ul, esi); | |
| 689 TestJ(ne, Near, edi, 0x80000000ul, esi, 0x1ul, eax); | |
| 690 TestJ(ne, Far, esi, 0x80000000ul, eax, 0x1ul, ebx); | |
| 691 TestJ(be, Near, eax, 0x1ul, ebx, 0x80000000ul, ecx); | |
| 692 TestJ(be, Far, ebx, 0x1ul, ecx, 0x80000000ul, edx); | |
| 693 TestJ(a, Near, ecx, 0x80000000ul, edx, 0x1ul, edi); | |
| 694 TestJ(a, Far, edx, 0x80000000ul, edi, 0x1ul, esi); | |
| 695 TestJ(s, Near, edi, 0x1ul, esi, 0x80000000ul, eax); | |
| 696 TestJ(s, Far, esi, 0x1ul, eax, 0x80000000ul, ebx); | |
| 697 TestJ(ns, Near, eax, 0x80000000ul, ebx, 0x1ul, ecx); | |
| 698 TestJ(ns, Far, ebx, 0x80000000ul, ecx, 0x1ul, edx); | |
| 699 TestJ(p, Near, ecx, 0x80000000ul, edx, 0x1ul, edi); | |
| 700 TestJ(p, Far, edx, 0x80000000ul, edi, 0x1ul, esi); | |
| 701 TestJ(np, Near, edi, 0x1ul, esi, 0x80000000ul, eax); | |
| 702 TestJ(np, Far, esi, 0x1ul, eax, 0x80000000ul, ebx); | |
| 703 TestJ(l, Near, eax, 0x80000000ul, ebx, 0x1ul, ecx); | |
| 704 TestJ(l, Far, ebx, 0x80000000ul, ecx, 0x1ul, edx); | |
| 705 TestJ(ge, Near, ecx, 0x1ul, edx, 0x80000000ul, edi); | |
| 706 TestJ(ge, Far, edx, 0x1ul, edi, 0x80000000ul, esi); | |
| 707 TestJ(le, Near, edi, 0x80000000ul, esi, 0x1ul, eax); | |
| 708 TestJ(le, Far, esi, 0x80000000ul, eax, 0x1ul, ebx); | |
| 709 TestJ(g, Near, eax, 0x1ul, ebx, 0x80000000ul, ecx); | |
| 710 TestJ(g, Far, ebx, 0x1ul, ecx, 0x80000000ul, edx); | |
| 711 | |
| 712 #undef TestJ | |
| 713 } | |
| 714 | |
| 715 #undef __ | |
| 716 | |
| 717 } // end of anonymous namespace | |
| 718 } // end of namespace X8632 | |
| 719 } // end of namespace Ice | |
| OLD | NEW |