Chromium Code Reviews| 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 // __ ret(); | |
|
jvoung (off chromium)
2015/07/10 18:17:31
Maybe the example should have __ ret() after, sinc
John
2015/07/10 23:26:36
A very unfortunate example. Done.
| |
| 56 // __ mov(GPRRegister::Reg_Eax, 1); | |
| 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); | |
|
jvoung (off chromium)
2015/07/10 18:17:31
What's the need for this latter assert, if there i
John
2015/07/10 23:26:36
Yes, exactly: I want verifyBytes to report all err
| |
| 80 } | |
| 81 }; | |
| 82 | |
| 83 TEST_F(AssemblerX8632LowLevelTest, Ret) { | |
| 84 __ ret(); | |
| 85 | |
| 86 constexpr size_t ByteCount = 1; | |
| 87 EXPECT_EQ(ByteCount, codeBytesSize()); | |
| 88 assert(ByteCount == codeBytesSize()); | |
|
jvoung (off chromium)
2015/07/10 18:17:31
Question for here and below -- why not use ASSERT_
John
2015/07/10 23:26:36
I don't like ASSERT_<<cond>>, they only work withi
| |
| 89 | |
| 90 verifyBytes<ByteCount>(codeBytes(), 0xc3); | |
| 91 } | |
| 92 | |
| 93 TEST_F(AssemblerX8632LowLevelTest, CallImm4) { | |
| 94 __ call(Immediate(4)); | |
| 95 | |
| 96 constexpr size_t ByteCount = 5; | |
| 97 EXPECT_EQ(ByteCount, codeBytesSize()); | |
| 98 assert(ByteCount == codeBytesSize()); | |
| 99 | |
| 100 verifyBytes<ByteCount>(codeBytes(), 0xe8, 0x00, 0x00, 0x00, 0x00); | |
| 101 } | |
| 102 | |
| 103 TEST_F(AssemblerX8632LowLevelTest, PushRegs) { | |
| 104 __ popl(GPRRegister::Encoded_Reg_eax); | |
|
jvoung (off chromium)
2015/07/10 18:17:32
This test is called PushRegs, but the method used
John
2015/07/10 23:26:36
I was tired... Done.
| |
| 105 __ popl(GPRRegister::Encoded_Reg_ebx); | |
| 106 __ popl(GPRRegister::Encoded_Reg_ecx); | |
| 107 __ popl(GPRRegister::Encoded_Reg_edx); | |
| 108 __ popl(GPRRegister::Encoded_Reg_edi); | |
| 109 __ popl(GPRRegister::Encoded_Reg_esi); | |
| 110 __ popl(GPRRegister::Encoded_Reg_ebp); | |
| 111 | |
| 112 constexpr size_t ByteCount = 7; | |
| 113 EXPECT_EQ(ByteCount, codeBytesSize()); | |
| 114 assert(ByteCount == codeBytesSize()); | |
| 115 | |
| 116 constexpr uint8_t PushOpcode = 0x58; | |
|
jvoung (off chromium)
2015/07/10 18:17:31
PopOpcode?
John
2015/07/10 23:26:36
Done.
| |
| 117 verifyBytes<ByteCount>(codeBytes(), PushOpcode | GPRRegister::Encoded_Reg_eax, | |
| 118 PushOpcode | GPRRegister::Encoded_Reg_ebx, | |
| 119 PushOpcode | GPRRegister::Encoded_Reg_ecx, | |
| 120 PushOpcode | GPRRegister::Encoded_Reg_edx, | |
| 121 PushOpcode | GPRRegister::Encoded_Reg_edi, | |
| 122 PushOpcode | GPRRegister::Encoded_Reg_esi, | |
| 123 PushOpcode | GPRRegister::Encoded_Reg_ebp); | |
| 124 } | |
| 125 | |
| 126 TEST_F(AssemblerX8632LowLevelTest, PopRegs) { | |
|
jvoung (off chromium)
2015/07/10 18:17:31
Rename this to "PushRegs"?
John
2015/07/10 23:26:36
Done.
| |
| 127 __ pushl(GPRRegister::Encoded_Reg_eax); | |
| 128 __ pushl(GPRRegister::Encoded_Reg_ebx); | |
| 129 __ pushl(GPRRegister::Encoded_Reg_ecx); | |
| 130 __ pushl(GPRRegister::Encoded_Reg_edx); | |
| 131 __ pushl(GPRRegister::Encoded_Reg_edi); | |
| 132 __ pushl(GPRRegister::Encoded_Reg_esi); | |
| 133 __ pushl(GPRRegister::Encoded_Reg_ebp); | |
| 134 | |
| 135 constexpr size_t ByteCount = 7; | |
| 136 EXPECT_EQ(ByteCount, codeBytesSize()); | |
| 137 assert(ByteCount == codeBytesSize()); | |
| 138 | |
| 139 constexpr uint8_t PopOpcode = 0x50; | |
| 140 verifyBytes<ByteCount>(codeBytes(), PopOpcode | GPRRegister::Encoded_Reg_eax, | |
| 141 PopOpcode | GPRRegister::Encoded_Reg_ebx, | |
| 142 PopOpcode | GPRRegister::Encoded_Reg_ecx, | |
| 143 PopOpcode | GPRRegister::Encoded_Reg_edx, | |
| 144 PopOpcode | GPRRegister::Encoded_Reg_edi, | |
| 145 PopOpcode | GPRRegister::Encoded_Reg_esi, | |
| 146 PopOpcode | GPRRegister::Encoded_Reg_ebp); | |
| 147 } | |
| 148 | |
| 149 TEST_F(AssemblerX8632LowLevelTest, MovRegisterZero) { | |
| 150 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x00)); | |
| 151 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(0x00)); | |
| 152 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(0x00)); | |
| 153 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(0x00)); | |
| 154 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(0x00)); | |
| 155 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x00)); | |
| 156 | |
| 157 constexpr size_t MovReg32BitImmBytes = 5; | |
| 158 constexpr size_t ByteCount = 6 * MovReg32BitImmBytes; | |
| 159 EXPECT_EQ(ByteCount, codeBytesSize()); | |
| 160 assert(ByteCount == codeBytesSize()); | |
| 161 | |
| 162 constexpr uint8_t MovOpcode = 0xb8; | |
| 163 verifyBytes<ByteCount>( | |
| 164 codeBytes(), MovOpcode | GPRRegister::Encoded_Reg_eax, 0x00, 0x00, 0x00, | |
| 165 0x00, MovOpcode | GPRRegister::Encoded_Reg_ebx, 0x00, 0x00, 0x00, 0x00, | |
| 166 MovOpcode | GPRRegister::Encoded_Reg_ecx, 0x00, 0x00, 0x00, 0x00, | |
| 167 MovOpcode | GPRRegister::Encoded_Reg_edx, 0x00, 0x00, 0x00, 0x00, | |
| 168 MovOpcode | GPRRegister::Encoded_Reg_edi, 0x00, 0x00, 0x00, 0x00, | |
| 169 MovOpcode | GPRRegister::Encoded_Reg_esi, 0x00, 0x00, 0x00, 0x00); | |
| 170 } | |
| 171 | |
| 172 TEST_F(AssemblerX8632LowLevelTest, CmpRegReg) { | |
| 173 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_eax, | |
| 174 GPRRegister::Encoded_Reg_ebx); | |
| 175 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ebx, | |
| 176 GPRRegister::Encoded_Reg_ecx); | |
| 177 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_ecx, | |
| 178 GPRRegister::Encoded_Reg_edx); | |
| 179 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edx, | |
| 180 GPRRegister::Encoded_Reg_edi); | |
| 181 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_edi, | |
| 182 GPRRegister::Encoded_Reg_esi); | |
| 183 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_esi, | |
| 184 GPRRegister::Encoded_Reg_eax); | |
| 185 | |
| 186 const size_t CmpRegRegBytes = 2; | |
| 187 const size_t ByteCount = 6 * CmpRegRegBytes; | |
| 188 EXPECT_EQ(ByteCount, codeBytesSize()); | |
| 189 assert(ByteCount == codeBytesSize()); | |
| 190 | |
| 191 constexpr size_t CmpOpcode = 0x3b; | |
| 192 constexpr size_t ModRm = 0xC0 /* Register Addressing */; | |
| 193 verifyBytes<ByteCount>( | |
| 194 codeBytes(), CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_eax << 3) | | |
| 195 GPRRegister::Encoded_Reg_ebx, | |
| 196 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ebx << 3) | | |
| 197 GPRRegister::Encoded_Reg_ecx, | |
| 198 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_ecx << 3) | | |
| 199 GPRRegister::Encoded_Reg_edx, | |
| 200 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edx << 3) | | |
| 201 GPRRegister::Encoded_Reg_edi, | |
| 202 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_edi << 3) | | |
| 203 GPRRegister::Encoded_Reg_esi, | |
| 204 CmpOpcode, ModRm | (GPRRegister::Encoded_Reg_esi << 3) | | |
| 205 GPRRegister::Encoded_Reg_eax); | |
| 206 } | |
| 207 | |
| 208 // After these tests we should have a sane environment; we know the following | |
| 209 // work: | |
| 210 // | |
| 211 // (*) zeroing eax, ebx, ecx, edx, edi, and esi; | |
| 212 // (*) call $4 instruction (used for ip materialization); | |
| 213 // (*) register push and pop; | |
| 214 // (*) cmp reg, reg; and | |
| 215 // (*) returning from functions. | |
| 216 // | |
| 217 // We can now dive into testing each emitting method in AssemblerX8632. Each | |
| 218 // test will emit some instructions performing the test. The assembled | |
| 219 // instructions will operate in a "safe" environment. In particular, all | |
| 220 // registers (except for esp, and ebp) will be zeroed out before they are | |
| 221 // executed. ebp will be used as a frame pointer so the jitted routine has a | |
| 222 // temporary scratch memory; and esp will not be modified (i.e., the test | |
| 223 // function will use the same stack as the program running the tests. | |
| 224 // | |
| 225 // Upon return from the jitted code, an epilogue will save the final register | |
| 226 // values before restoring them. | |
| 227 // | |
| 228 // The jitted code will look like the following: | |
| 229 // | |
| 230 // test: | |
| 231 // push %eax | |
| 232 // push %ebx | |
| 233 // push %ecx | |
| 234 // push %edx | |
| 235 // push %edi | |
| 236 // push %esi | |
| 237 // push %ebp | |
| 238 // call test$materialize_ip | |
| 239 // test$materialize_ip: | |
| 240 // mov $0, %eax | |
| 241 // mov $0, %ebx | |
| 242 // mov $0, %ecx | |
| 243 // mov $0, %edx | |
| 244 // mov $0, %edi | |
| 245 // mov $0, %esi | |
| 246 // pop %ebp | |
|
jvoung (off chromium)
2015/07/10 18:17:32
__ popl(GPRRegister::Encoded_Reg_ebp) in the code
John
2015/07/10 23:26:36
Done.
| |
| 247 // | |
| 248 // << test code goes here >> | |
| 249 // | |
| 250 // mov %eax, $(1M + 0 - (test$materialize_ip - test))(%ebp,,) | |
|
jvoung (off chromium)
2015/07/10 18:17:31
nit: indentation seems to change here (not sure if
jvoung (off chromium)
2015/07/10 18:17:32
I think these slots need a bit more explanation.
John
2015/07/10 23:26:36
Done.
John
2015/07/10 23:26:36
But I already explain about the scratchpad area ab
| |
| 251 // mov %ebx, $(1M + 4 - (test$materialize_ip - test))(%ebp,,) | |
| 252 // mov %ecx, $(1M + 8 - (test$materialize_ip - test))(%ebp,,) | |
| 253 // mov %edx, $(1M + 12 - (test$materialize_ip - test))(%ebp,,) | |
| 254 // mov %edi, $(1M + 16 - (test$materialize_ip - test))(%ebp,,) | |
| 255 // mov %esi, $(1M + 20 - (test$materialize_ip - test))(%ebp,,) | |
| 256 // | |
| 257 // pop %ebp | |
| 258 // pop %esi | |
| 259 // pop %edi | |
| 260 // pop %edx | |
| 261 // pop %ecx | |
| 262 // pop %ebx | |
| 263 // pop %eax | |
| 264 // ret | |
| 265 // | |
| 266 // This instruction sequence allows the AssemblerX8632 test to be run in both | |
| 267 // X86-32 and X86-64 hosts. In particular, the first | |
| 268 // | |
| 269 // pop %ebp | |
| 270 // | |
| 271 // instruction will populate ebp in x86-32 and rbp in x86-64 with the return | |
| 272 // address pushed by the call to test$materialize_ip, which will have the | |
| 273 // appropriate size. To prevent the upper 32 bits from being cleared in x86-64, | |
| 274 // the jitted code should not perform any arithmetic with ebp (not that | |
|
jvoung (off chromium)
2015/07/10 18:17:32
not that -> note that
John
2015/07/10 23:26:36
Done.
| |
| 275 // specifying a displacement in the addressing mode is OK -- immediates are | |
| 276 // sign-extenteded.) | |
|
jvoung (off chromium)
2015/07/10 18:17:32
extenteded -> extended
John
2015/07/10 23:26:36
Done.
| |
| 277 // | |
| 278 // TODO(jpp): test the | |
| 279 // | |
| 280 // mov %reg, slot | |
| 281 // | |
| 282 // encodings using the low level assembler test. | |
| 283 class AssemblerX8632Test : public AssemblerX8632TestBase { | |
| 284 protected: | |
| 285 AssemblerX8632Test() { reset(); } | |
| 286 | |
| 287 void reset() { | |
| 288 AssemblerX8632TestBase::reset(); | |
| 289 | |
| 290 NeedsEpilogue = true; | |
| 291 // 6 dwords are allocated for saving the GPR state after the jitted code | |
| 292 // runs. | |
| 293 NumAllocatedDwords = 6; | |
| 294 addPrologue(); | |
| 295 } | |
| 296 | |
| 297 // AssembledBuffer is a wrapper around a PROT_EXEC mmap'ed buffer. This buffer | |
| 298 // contains both the test code as well as prologue/epilogue, and the | |
| 299 // scratchpad area that tests may use -- all tests use this scraychpad area | |
|
jvoung (off chromium)
2015/07/10 18:17:32
scraychpad -> scratchpad
John
2015/07/10 23:26:36
Done.
| |
| 300 // for storing the processor's registers after the tests executed. This class | |
| 301 // also exposes helper methods for reading the register state after test | |
| 302 // execution, as well as for reading the scratchpad area. | |
| 303 class AssembledBuffer { | |
| 304 AssembledBuffer() = delete; | |
| 305 AssembledBuffer(const AssembledBuffer &) = delete; | |
| 306 AssembledBuffer &operator=(const AssembledBuffer &) = delete; | |
| 307 | |
| 308 public: | |
| 309 static constexpr uint32_t MaximumCodeSize = 1 << 20; | |
| 310 static constexpr uint32_t EaxSlot = 0; | |
| 311 static constexpr uint32_t EbxSlot = 1; | |
| 312 static constexpr uint32_t EcxSlot = 2; | |
| 313 static constexpr uint32_t EdxSlot = 3; | |
| 314 static constexpr uint32_t EdiSlot = 4; | |
| 315 static constexpr uint32_t EsiSlot = 5; | |
| 316 | |
| 317 explicit AssembledBuffer(const uint8_t *Data, const size_t MySize, | |
|
jvoung (off chromium)
2015/07/10 18:17:32
doesn't need "explicit" w/ multiple params?
John
2015/07/10 23:26:36
Done.
| |
| 318 const size_t ExtraStorageDwords) | |
| 319 : Size(MaximumCodeSize + 4 * ExtraStorageDwords) { | |
| 320 // MaxCodeSize is needed because EXPECT_LT needs a symbol with a name -- | |
| 321 // probably a compiler bug? | |
| 322 uint32_t MaxCodeSize = MaximumCodeSize; | |
| 323 EXPECT_LT(MySize, MaxCodeSize); | |
| 324 assert(MySize < MaximumCodeSize); | |
| 325 ExecutableData = mmap(nullptr, Size, PROT_WRITE | PROT_READ | PROT_EXEC, | |
| 326 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); | |
| 327 EXPECT_NE(MAP_FAILED, ExecutableData) << strerror(errno); | |
| 328 assert(MAP_FAILED != ExecutableData); | |
| 329 std::memcpy(ExecutableData, Data, MySize); | |
| 330 } | |
| 331 | |
| 332 // We allow AssembledBuffer to be moved so that we can return objects of | |
| 333 // this type. | |
| 334 AssembledBuffer(AssembledBuffer &&Buffer) | |
| 335 : ExecutableData(Buffer.ExecutableData), Size(Buffer.Size) { | |
| 336 Buffer.ExecutableData = nullptr; | |
| 337 Buffer.Size = 0; | |
| 338 } | |
| 339 | |
| 340 AssembledBuffer &operator=(AssembledBuffer &&Buffer) { | |
| 341 ExecutableData = Buffer.ExecutableData; | |
| 342 Buffer.ExecutableData = nullptr; | |
| 343 Size = Buffer.Size; | |
| 344 Buffer.Size = 0; | |
| 345 return *this; | |
| 346 } | |
| 347 | |
| 348 ~AssembledBuffer() { | |
| 349 if (ExecutableData != nullptr) { | |
| 350 munmap(ExecutableData, Size); | |
| 351 ExecutableData = nullptr; | |
| 352 } | |
| 353 } | |
| 354 | |
| 355 void run() const { reinterpret_cast<void (*)()>(ExecutableData)(); } | |
| 356 | |
| 357 uint32_t eax() const { return contentsOfDword(AssembledBuffer::EaxSlot); } | |
| 358 | |
| 359 uint32_t ebx() const { return contentsOfDword(AssembledBuffer::EbxSlot); } | |
| 360 | |
| 361 uint32_t ecx() const { return contentsOfDword(AssembledBuffer::EcxSlot); } | |
| 362 | |
| 363 uint32_t edx() const { return contentsOfDword(AssembledBuffer::EdxSlot); } | |
| 364 | |
| 365 uint32_t edi() const { return contentsOfDword(AssembledBuffer::EdiSlot); } | |
| 366 | |
| 367 uint32_t esi() const { return contentsOfDword(AssembledBuffer::EsiSlot); } | |
| 368 | |
| 369 // contentsOfDword is used for reading the values in the scratchpad area. | |
| 370 // Valid arguments are the dword ids returned by | |
| 371 // AssemblerX8632Test::allocateDword() -- other inputs are considered | |
| 372 // invalid, and are not guaranteed to work if the implementation changes. | |
| 373 uint32_t contentsOfDword(uint32_t Dword) const { | |
| 374 return *reinterpret_cast<uint32_t *>( | |
| 375 static_cast<uint8_t *>(ExecutableData) + dwordOffset(Dword)); | |
| 376 } | |
| 377 | |
| 378 private: | |
| 379 static uint32_t dwordOffset(uint32_t Index) { | |
| 380 return MaximumCodeSize + (Index * 4); | |
| 381 } | |
| 382 | |
| 383 void *ExecutableData = nullptr; | |
| 384 size_t Size; | |
| 385 }; | |
| 386 | |
| 387 // assemble created an AssembledBuffer with the jitted code. The first time | |
| 388 // assemble is executed it will add the epilogue to the jitted code (which is | |
| 389 // the reason why this method is not const qualified. | |
| 390 AssembledBuffer assemble() { | |
| 391 if (NeedsEpilogue) { | |
|
jvoung (off chromium)
2015/07/10 18:17:32
Is it ever kosher to run assemble() twice?
John
2015/07/10 23:26:36
Yes! :)
assemble **could** be a const method, if
| |
| 392 addEpilogue(); | |
| 393 } | |
| 394 | |
| 395 NeedsEpilogue = false; | |
| 396 return AssembledBuffer(codeBytes(), codeBytesSize(), NumAllocatedDwords); | |
| 397 } | |
| 398 | |
| 399 // Allocates a new dword slot in the test's scratchpad area. | |
| 400 uint32_t allocateDword() { return NumAllocatedDwords++; } | |
| 401 | |
| 402 Address dwordAddress(uint32_t Dword) { | |
| 403 return Address(GPRRegister::Encoded_Reg_ebp, dwordDisp(Dword)); | |
| 404 } | |
| 405 | |
| 406 private: | |
| 407 // e??SlotAddress returns an AssemblerX8632::Traits::Address that can be used | |
| 408 // by the test cases to encode an address operand for accessing the slot for | |
| 409 // the spcified register. These are all private for, when jitting the test | |
|
jvoung (off chromium)
2015/07/10 18:17:32
spcified -> specified
John
2015/07/10 23:26:36
Done.
| |
| 410 // code, tests should not tamper with these values. Besides, during the test | |
| 411 // execution these slots' contents are undefined and should not be accessed. | |
| 412 Address eaxSlotAddress() { return dwordAddress(AssembledBuffer::EaxSlot); } | |
| 413 Address ebxSlotAddress() { return dwordAddress(AssembledBuffer::EbxSlot); } | |
| 414 Address ecxSlotAddress() { return dwordAddress(AssembledBuffer::EcxSlot); } | |
| 415 Address edxSlotAddress() { return dwordAddress(AssembledBuffer::EdxSlot); } | |
| 416 Address ediSlotAddress() { return dwordAddress(AssembledBuffer::EdiSlot); } | |
| 417 Address esiSlotAddress() { return dwordAddress(AssembledBuffer::EsiSlot); } | |
| 418 | |
| 419 // Returns the displacement that should be used when accessing the specified | |
| 420 // Dword in the scratchpad area. It needs to adjust for the initial | |
| 421 // instructions that are emitted before the call that materializes the IP | |
| 422 // register. | |
| 423 uint32_t dwordDisp(uint32_t Dword) const { | |
| 424 EXPECT_LT(Dword, NumAllocatedDwords); | |
| 425 assert(Dword < NumAllocatedDwords); | |
| 426 static constexpr uint8_t PushBytes = 1; | |
| 427 static constexpr uint8_t CallImmBytes = 5; | |
| 428 return AssembledBuffer::MaximumCodeSize + (Dword * 4) - | |
| 429 (7 * PushBytes + CallImmBytes); | |
| 430 } | |
| 431 | |
| 432 void addPrologue() { | |
| 433 __ pushl(GPRRegister::Encoded_Reg_eax); | |
| 434 __ pushl(GPRRegister::Encoded_Reg_ebx); | |
| 435 __ pushl(GPRRegister::Encoded_Reg_ecx); | |
| 436 __ pushl(GPRRegister::Encoded_Reg_edx); | |
| 437 __ pushl(GPRRegister::Encoded_Reg_edi); | |
| 438 __ pushl(GPRRegister::Encoded_Reg_esi); | |
| 439 __ pushl(GPRRegister::Encoded_Reg_ebp); | |
| 440 | |
| 441 __ call(Immediate(4)); | |
| 442 __ popl(GPRRegister::Encoded_Reg_ebp); | |
| 443 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x00)); | |
| 444 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(0x00)); | |
| 445 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(0x00)); | |
| 446 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(0x00)); | |
| 447 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(0x00)); | |
| 448 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x00)); | |
| 449 } | |
| 450 | |
| 451 void addEpilogue() { | |
| 452 __ mov(IceType_i32, eaxSlotAddress(), GPRRegister::Encoded_Reg_eax); | |
| 453 __ mov(IceType_i32, ebxSlotAddress(), GPRRegister::Encoded_Reg_ebx); | |
| 454 __ mov(IceType_i32, ecxSlotAddress(), GPRRegister::Encoded_Reg_ecx); | |
| 455 __ mov(IceType_i32, edxSlotAddress(), GPRRegister::Encoded_Reg_edx); | |
| 456 __ mov(IceType_i32, ediSlotAddress(), GPRRegister::Encoded_Reg_edi); | |
| 457 __ mov(IceType_i32, esiSlotAddress(), GPRRegister::Encoded_Reg_esi); | |
| 458 | |
| 459 __ popl(GPRRegister::Encoded_Reg_ebp); | |
| 460 __ popl(GPRRegister::Encoded_Reg_esi); | |
| 461 __ popl(GPRRegister::Encoded_Reg_edi); | |
| 462 __ popl(GPRRegister::Encoded_Reg_edx); | |
| 463 __ popl(GPRRegister::Encoded_Reg_ecx); | |
| 464 __ popl(GPRRegister::Encoded_Reg_ebx); | |
| 465 __ popl(GPRRegister::Encoded_Reg_eax); | |
| 466 | |
| 467 __ ret(); | |
| 468 } | |
| 469 | |
| 470 bool NeedsEpilogue; | |
| 471 uint32_t NumAllocatedDwords; | |
| 472 }; | |
| 473 | |
| 474 TEST_F(AssemblerX8632Test, MovRegImm) { | |
| 475 constexpr uint32_t ExpectedEax = 0x000000FFul; | |
| 476 constexpr uint32_t ExpectedEbx = 0x0000FF00ul; | |
| 477 constexpr uint32_t ExpectedEcx = 0x00FF0000ul; | |
| 478 constexpr uint32_t ExpectedEdx = 0xFF000000ul; | |
| 479 constexpr uint32_t ExpectedEdi = 0x6AAA0006ul; | |
| 480 constexpr uint32_t ExpectedEsi = 0x6000AAA6ul; | |
| 481 | |
| 482 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ExpectedEax)); | |
| 483 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(ExpectedEbx)); | |
| 484 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(ExpectedEcx)); | |
| 485 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(ExpectedEdx)); | |
| 486 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(ExpectedEdi)); | |
| 487 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedEsi)); | |
| 488 | |
| 489 AssembledBuffer test = assemble(); | |
| 490 test.run(); | |
| 491 EXPECT_EQ(ExpectedEax, test.eax()); | |
| 492 EXPECT_EQ(ExpectedEbx, test.ebx()); | |
| 493 EXPECT_EQ(ExpectedEcx, test.ecx()); | |
| 494 EXPECT_EQ(ExpectedEdx, test.edx()); | |
| 495 EXPECT_EQ(ExpectedEdi, test.edi()); | |
| 496 EXPECT_EQ(ExpectedEsi, test.esi()); | |
| 497 } | |
| 498 | |
| 499 TEST_F(AssemblerX8632Test, MovMemImm) { | |
| 500 const uint32_t T0 = allocateDword(); | |
| 501 constexpr uint32_t ExpectedT0 = 0x00111100ul; | |
| 502 const uint32_t T1 = allocateDword(); | |
| 503 constexpr uint32_t ExpectedT1 = 0x00222200ul; | |
| 504 const uint32_t T2 = allocateDword(); | |
| 505 constexpr uint32_t ExpectedT2 = 0x03333000ul; | |
| 506 const uint32_t T3 = allocateDword(); | |
| 507 constexpr uint32_t ExpectedT3 = 0x00444400ul; | |
| 508 | |
| 509 __ mov(IceType_i32, dwordAddress(T0), Immediate(ExpectedT0)); | |
| 510 __ mov(IceType_i32, dwordAddress(T1), Immediate(ExpectedT1)); | |
| 511 __ mov(IceType_i32, dwordAddress(T2), Immediate(ExpectedT2)); | |
| 512 __ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3)); | |
| 513 | |
| 514 AssembledBuffer test = assemble(); | |
| 515 test.run(); | |
| 516 EXPECT_EQ(0ul, test.eax()); | |
| 517 EXPECT_EQ(0ul, test.ebx()); | |
| 518 EXPECT_EQ(0ul, test.ecx()); | |
| 519 EXPECT_EQ(0ul, test.edx()); | |
| 520 EXPECT_EQ(0ul, test.edi()); | |
| 521 EXPECT_EQ(0ul, test.esi()); | |
| 522 EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0)); | |
| 523 EXPECT_EQ(ExpectedT1, test.contentsOfDword(T1)); | |
| 524 EXPECT_EQ(ExpectedT2, test.contentsOfDword(T2)); | |
| 525 EXPECT_EQ(ExpectedT3, test.contentsOfDword(T3)); | |
| 526 } | |
| 527 | |
| 528 TEST_F(AssemblerX8632Test, MovMemReg) { | |
| 529 const uint32_t T0 = allocateDword(); | |
| 530 constexpr uint32_t ExpectedT0 = 0x00111100ul; | |
| 531 const uint32_t T1 = allocateDword(); | |
| 532 constexpr uint32_t ExpectedT1 = 0x00222200ul; | |
| 533 const uint32_t T2 = allocateDword(); | |
| 534 constexpr uint32_t ExpectedT2 = 0x00333300ul; | |
| 535 const uint32_t T3 = allocateDword(); | |
| 536 constexpr uint32_t ExpectedT3 = 0x00444400ul; | |
| 537 const uint32_t T4 = allocateDword(); | |
| 538 constexpr uint32_t ExpectedT4 = 0x00555500ul; | |
| 539 const uint32_t T5 = allocateDword(); | |
| 540 constexpr uint32_t ExpectedT5 = 0x00666600ul; | |
| 541 | |
| 542 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(ExpectedT0)); | |
| 543 __ mov(IceType_i32, dwordAddress(T0), GPRRegister::Encoded_Reg_eax); | |
| 544 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, Immediate(ExpectedT1)); | |
| 545 __ mov(IceType_i32, dwordAddress(T1), GPRRegister::Encoded_Reg_ebx); | |
| 546 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, Immediate(ExpectedT2)); | |
| 547 __ mov(IceType_i32, dwordAddress(T2), GPRRegister::Encoded_Reg_ecx); | |
| 548 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, Immediate(ExpectedT3)); | |
| 549 __ mov(IceType_i32, dwordAddress(T3), GPRRegister::Encoded_Reg_edx); | |
| 550 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, Immediate(ExpectedT4)); | |
| 551 __ mov(IceType_i32, dwordAddress(T4), GPRRegister::Encoded_Reg_edi); | |
| 552 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(ExpectedT5)); | |
| 553 __ mov(IceType_i32, dwordAddress(T5), GPRRegister::Encoded_Reg_esi); | |
| 554 | |
| 555 AssembledBuffer test = assemble(); | |
| 556 test.run(); | |
| 557 EXPECT_EQ(ExpectedT0, test.contentsOfDword(T0)); | |
| 558 EXPECT_EQ(ExpectedT1, test.contentsOfDword(T1)); | |
| 559 EXPECT_EQ(ExpectedT2, test.contentsOfDword(T2)); | |
| 560 EXPECT_EQ(ExpectedT3, test.contentsOfDword(T3)); | |
| 561 EXPECT_EQ(ExpectedT4, test.contentsOfDword(T4)); | |
| 562 EXPECT_EQ(ExpectedT5, test.contentsOfDword(T5)); | |
| 563 } | |
| 564 | |
| 565 TEST_F(AssemblerX8632Test, MovRegReg) { | |
| 566 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, Immediate(0x20)); | |
| 567 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, | |
| 568 GPRRegister::Encoded_Reg_eax); | |
| 569 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, | |
| 570 GPRRegister::Encoded_Reg_ebx); | |
| 571 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, | |
| 572 GPRRegister::Encoded_Reg_ecx); | |
| 573 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, | |
| 574 GPRRegister::Encoded_Reg_edx); | |
| 575 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, | |
| 576 GPRRegister::Encoded_Reg_edi); | |
| 577 | |
| 578 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, Immediate(0x55000000ul)); | |
| 579 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, | |
| 580 GPRRegister::Encoded_Reg_esi); | |
| 581 | |
| 582 AssembledBuffer test = assemble(); | |
| 583 test.run(); | |
| 584 EXPECT_EQ(0x55000000ul, test.eax()); | |
| 585 EXPECT_EQ(0x20ul, test.ebx()); | |
| 586 EXPECT_EQ(0x20ul, test.ecx()); | |
| 587 EXPECT_EQ(0x20ul, test.edx()); | |
| 588 EXPECT_EQ(0x20ul, test.edi()); | |
| 589 EXPECT_EQ(0x55000000ul, test.esi()); | |
| 590 } | |
| 591 | |
| 592 TEST_F(AssemblerX8632Test, MovRegMem) { | |
| 593 const uint32_t T0 = allocateDword(); | |
| 594 constexpr uint32_t ExpectedT0 = 0x00111100ul; | |
| 595 const uint32_t T1 = allocateDword(); | |
| 596 constexpr uint32_t ExpectedT1 = 0x00222200ul; | |
| 597 const uint32_t T2 = allocateDword(); | |
| 598 constexpr uint32_t ExpectedT2 = 0x00333300ul; | |
| 599 const uint32_t T3 = allocateDword(); | |
| 600 constexpr uint32_t ExpectedT3 = 0x00444400ul; | |
| 601 const uint32_t T4 = allocateDword(); | |
| 602 constexpr uint32_t ExpectedT4 = 0x00555500ul; | |
| 603 const uint32_t T5 = allocateDword(); | |
| 604 constexpr uint32_t ExpectedT5 = 0x00666600ul; | |
| 605 | |
| 606 __ mov(IceType_i32, dwordAddress(T0), Immediate(ExpectedT0)); | |
| 607 __ mov(IceType_i32, GPRRegister::Encoded_Reg_eax, dwordAddress(T0)); | |
| 608 | |
| 609 __ mov(IceType_i32, dwordAddress(T1), Immediate(ExpectedT1)); | |
| 610 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ebx, dwordAddress(T1)); | |
| 611 | |
| 612 __ mov(IceType_i32, dwordAddress(T2), Immediate(ExpectedT2)); | |
| 613 __ mov(IceType_i32, GPRRegister::Encoded_Reg_ecx, dwordAddress(T2)); | |
| 614 | |
| 615 __ mov(IceType_i32, dwordAddress(T3), Immediate(ExpectedT3)); | |
| 616 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edx, dwordAddress(T3)); | |
| 617 | |
| 618 __ mov(IceType_i32, dwordAddress(T4), Immediate(ExpectedT4)); | |
| 619 __ mov(IceType_i32, GPRRegister::Encoded_Reg_edi, dwordAddress(T4)); | |
| 620 | |
| 621 __ mov(IceType_i32, dwordAddress(T5), Immediate(ExpectedT5)); | |
| 622 __ mov(IceType_i32, GPRRegister::Encoded_Reg_esi, dwordAddress(T5)); | |
| 623 | |
| 624 AssembledBuffer test = assemble(); | |
| 625 test.run(); | |
| 626 EXPECT_EQ(ExpectedT0, test.eax()); | |
| 627 EXPECT_EQ(ExpectedT1, test.ebx()); | |
| 628 EXPECT_EQ(ExpectedT2, test.ecx()); | |
| 629 EXPECT_EQ(ExpectedT3, test.edx()); | |
| 630 EXPECT_EQ(ExpectedT4, test.edi()); | |
| 631 EXPECT_EQ(ExpectedT5, test.esi()); | |
| 632 } | |
| 633 | |
| 634 TEST_F(AssemblerX8632Test, J) { | |
| 635 #define TestJ(C, Near, Src0, Value0, Src1, Value1, Dest) \ | |
| 636 do { \ | |
| 637 const bool NearJmp = std::strcmp(#Near, "Near") == 0; \ | |
| 638 Label ShouldBeTaken; \ | |
| 639 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src0, Immediate(Value0)); \ | |
| 640 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Src1, Immediate(Value1)); \ | |
| 641 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0)); \ | |
|
jvoung (off chromium)
2015/07/10 18:17:32
How about set to something other than 0 (and other
John
2015/07/10 23:26:36
Done.
| |
| 642 __ cmp(IceType_i32, GPRRegister::Encoded_Reg_##Src0, \ | |
| 643 GPRRegister::Encoded_Reg_##Src1); \ | |
| 644 __ j(Cond::Br_##C, &ShouldBeTaken, NearJmp); \ | |
| 645 __ mov(IceType_i32, GPRRegister::Encoded_Reg_##Dest, Immediate(0xC0FFEE)); \ | |
| 646 __ bind(&ShouldBeTaken); \ | |
| 647 AssembledBuffer test = assemble(); \ | |
| 648 test.run(); \ | |
| 649 EXPECT_EQ(Value0, test.Src0()); \ | |
| 650 EXPECT_EQ(Value1, test.Src1()); \ | |
| 651 EXPECT_EQ(0ul, test.Dest()); \ | |
| 652 reset(); \ | |
| 653 } while (0) | |
| 654 | |
| 655 TestJ(o, Near, eax, 0x80000000ul, ebx, 0x1ul, edi); | |
| 656 TestJ(o, Far, eax, 0x80000000ul, ebx, 0x1ul, edi); | |
| 657 TestJ(no, Near, ecx, 0x1ul, edx, 0x1ul, esi); | |
| 658 TestJ(no, Far, ecx, 0x1ul, edx, 0x1ul, esi); | |
| 659 TestJ(b, Near, eax, 0x1ul, ebx, 0x80000000ul, edi); | |
| 660 TestJ(b, Far, eax, 0x1ul, ebx, 0x80000000ul, edi); | |
| 661 TestJ(l, Near, eax, 0x80000000ul, ebx, 0x1ul, edi); | |
| 662 TestJ(l, Far, eax, 0x80000000ul, ebx, 0x1ul, edi); | |
| 663 | |
| 664 #undef TestJ | |
| 665 } | |
| 666 | |
| 667 #undef __ | |
| 668 | |
| 669 } // end of anonymous namespace | |
| 670 } // end of namespace X8632 | |
| 671 } // end of namespace Ice | |
| OLD | NEW |