OLD | NEW |
(Empty) | |
| 1 // Copyright 2013 the V8 project authors. All rights reserved. |
| 2 // Redistribution and use in source and binary forms, with or without |
| 3 // modification, are permitted provided that the following conditions are |
| 4 // met: |
| 5 // |
| 6 // * Redistributions of source code must retain the above copyright |
| 7 // notice, this list of conditions and the following disclaimer. |
| 8 // * Redistributions in binary form must reproduce the above |
| 9 // copyright notice, this list of conditions and the following |
| 10 // disclaimer in the documentation and/or other materials provided |
| 11 // with the distribution. |
| 12 // * Neither the name of Google Inc. nor the names of its |
| 13 // contributors may be used to endorse or promote products derived |
| 14 // from this software without specific prior written permission. |
| 15 // |
| 16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 27 |
| 28 #include "v8.h" |
| 29 |
| 30 #include "macro-assembler.h" |
| 31 #include "a64/utils-a64.h" |
| 32 #include "cctest.h" |
| 33 #include "test-utils-a64.h" |
| 34 |
| 35 using namespace v8::internal; |
| 36 |
| 37 |
| 38 #define __ masm-> |
| 39 |
| 40 |
| 41 bool Equal32(uint32_t expected, const RegisterDump*, uint32_t result) { |
| 42 if (result != expected) { |
| 43 printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n", |
| 44 expected, result); |
| 45 } |
| 46 |
| 47 return expected == result; |
| 48 } |
| 49 |
| 50 |
| 51 bool Equal64(uint64_t expected, const RegisterDump*, uint64_t result) { |
| 52 if (result != expected) { |
| 53 printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", |
| 54 expected, result); |
| 55 } |
| 56 |
| 57 return expected == result; |
| 58 } |
| 59 |
| 60 |
| 61 bool EqualFP32(float expected, const RegisterDump*, float result) { |
| 62 if (float_to_rawbits(expected) == float_to_rawbits(result)) { |
| 63 return true; |
| 64 } else { |
| 65 if (isnan(expected) || (expected == 0.0)) { |
| 66 printf("Expected 0x%08" PRIx32 "\t Found 0x%08" PRIx32 "\n", |
| 67 float_to_rawbits(expected), float_to_rawbits(result)); |
| 68 } else { |
| 69 printf("Expected %.9f (0x%08" PRIx32 ")\t " |
| 70 "Found %.9f (0x%08" PRIx32 ")\n", |
| 71 expected, float_to_rawbits(expected), |
| 72 result, float_to_rawbits(result)); |
| 73 } |
| 74 return false; |
| 75 } |
| 76 } |
| 77 |
| 78 |
| 79 bool EqualFP64(double expected, const RegisterDump*, double result) { |
| 80 if (double_to_rawbits(expected) == double_to_rawbits(result)) { |
| 81 return true; |
| 82 } |
| 83 |
| 84 if (isnan(expected) || (expected == 0.0)) { |
| 85 printf("Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", |
| 86 double_to_rawbits(expected), double_to_rawbits(result)); |
| 87 } else { |
| 88 printf("Expected %.17f (0x%016" PRIx64 ")\t " |
| 89 "Found %.17f (0x%016" PRIx64 ")\n", |
| 90 expected, double_to_rawbits(expected), |
| 91 result, double_to_rawbits(result)); |
| 92 } |
| 93 return false; |
| 94 } |
| 95 |
| 96 |
| 97 bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg) { |
| 98 ASSERT(reg.Is32Bits()); |
| 99 // Retrieve the corresponding X register so we can check that the upper part |
| 100 // was properly cleared. |
| 101 int64_t result_x = core->xreg(reg.code()); |
| 102 if ((result_x & 0xffffffff00000000L) != 0) { |
| 103 printf("Expected 0x%08" PRIx32 "\t Found 0x%016" PRIx64 "\n", |
| 104 expected, result_x); |
| 105 return false; |
| 106 } |
| 107 uint32_t result_w = core->wreg(reg.code()); |
| 108 return Equal32(expected, core, result_w); |
| 109 } |
| 110 |
| 111 |
| 112 bool Equal64(uint64_t expected, |
| 113 const RegisterDump* core, |
| 114 const Register& reg) { |
| 115 ASSERT(reg.Is64Bits()); |
| 116 uint64_t result = core->xreg(reg.code()); |
| 117 return Equal64(expected, core, result); |
| 118 } |
| 119 |
| 120 |
| 121 bool EqualFP32(float expected, |
| 122 const RegisterDump* core, |
| 123 const FPRegister& fpreg) { |
| 124 ASSERT(fpreg.Is32Bits()); |
| 125 // Retrieve the corresponding D register so we can check that the upper part |
| 126 // was properly cleared. |
| 127 uint64_t result_64 = core->dreg_bits(fpreg.code()); |
| 128 if ((result_64 & 0xffffffff00000000L) != 0) { |
| 129 printf("Expected 0x%08" PRIx32 " (%f)\t Found 0x%016" PRIx64 "\n", |
| 130 float_to_rawbits(expected), expected, result_64); |
| 131 return false; |
| 132 } |
| 133 |
| 134 return EqualFP32(expected, core, core->sreg(fpreg.code())); |
| 135 } |
| 136 |
| 137 |
| 138 bool EqualFP64(double expected, |
| 139 const RegisterDump* core, |
| 140 const FPRegister& fpreg) { |
| 141 ASSERT(fpreg.Is64Bits()); |
| 142 return EqualFP64(expected, core, core->dreg(fpreg.code())); |
| 143 } |
| 144 |
| 145 |
| 146 bool Equal64(const Register& reg0, |
| 147 const RegisterDump* core, |
| 148 const Register& reg1) { |
| 149 ASSERT(reg0.Is64Bits() && reg1.Is64Bits()); |
| 150 int64_t expected = core->xreg(reg0.code()); |
| 151 int64_t result = core->xreg(reg1.code()); |
| 152 return Equal64(expected, core, result); |
| 153 } |
| 154 |
| 155 |
| 156 static char FlagN(uint32_t flags) { |
| 157 return (flags & NFlag) ? 'N' : 'n'; |
| 158 } |
| 159 |
| 160 |
| 161 static char FlagZ(uint32_t flags) { |
| 162 return (flags & ZFlag) ? 'Z' : 'z'; |
| 163 } |
| 164 |
| 165 |
| 166 static char FlagC(uint32_t flags) { |
| 167 return (flags & CFlag) ? 'C' : 'c'; |
| 168 } |
| 169 |
| 170 |
| 171 static char FlagV(uint32_t flags) { |
| 172 return (flags & VFlag) ? 'V' : 'v'; |
| 173 } |
| 174 |
| 175 |
| 176 bool EqualNzcv(uint32_t expected, uint32_t result) { |
| 177 ASSERT((expected & ~NZCVFlag) == 0); |
| 178 ASSERT((result & ~NZCVFlag) == 0); |
| 179 if (result != expected) { |
| 180 printf("Expected: %c%c%c%c\t Found: %c%c%c%c\n", |
| 181 FlagN(expected), FlagZ(expected), FlagC(expected), FlagV(expected), |
| 182 FlagN(result), FlagZ(result), FlagC(result), FlagV(result)); |
| 183 return false; |
| 184 } |
| 185 |
| 186 return true; |
| 187 } |
| 188 |
| 189 |
| 190 bool EqualRegisters(const RegisterDump* a, const RegisterDump* b) { |
| 191 for (unsigned i = 0; i < kNumberOfRegisters; i++) { |
| 192 if (a->xreg(i) != b->xreg(i)) { |
| 193 printf("x%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", |
| 194 i, a->xreg(i), b->xreg(i)); |
| 195 return false; |
| 196 } |
| 197 } |
| 198 |
| 199 for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { |
| 200 uint64_t a_bits = a->dreg_bits(i); |
| 201 uint64_t b_bits = b->dreg_bits(i); |
| 202 if (a_bits != b_bits) { |
| 203 printf("d%d\t Expected 0x%016" PRIx64 "\t Found 0x%016" PRIx64 "\n", |
| 204 i, a_bits, b_bits); |
| 205 return false; |
| 206 } |
| 207 } |
| 208 |
| 209 return true; |
| 210 } |
| 211 |
| 212 |
| 213 RegList PopulateRegisterArray(Register* w, Register* x, Register* r, |
| 214 int reg_size, int reg_count, RegList allowed) { |
| 215 RegList list = 0; |
| 216 int i = 0; |
| 217 for (unsigned n = 0; (n < kNumberOfRegisters) && (i < reg_count); n++) { |
| 218 if (((1UL << n) & allowed) != 0) { |
| 219 // Only assign allowed registers. |
| 220 if (r) { |
| 221 r[i] = Register::Create(n, reg_size); |
| 222 } |
| 223 if (x) { |
| 224 x[i] = Register::Create(n, kXRegSize); |
| 225 } |
| 226 if (w) { |
| 227 w[i] = Register::Create(n, kWRegSize); |
| 228 } |
| 229 list |= (1UL << n); |
| 230 i++; |
| 231 } |
| 232 } |
| 233 // Check that we got enough registers. |
| 234 ASSERT(CountSetBits(list, kNumberOfRegisters) == reg_count); |
| 235 |
| 236 return list; |
| 237 } |
| 238 |
| 239 |
| 240 RegList PopulateFPRegisterArray(FPRegister* s, FPRegister* d, FPRegister* v, |
| 241 int reg_size, int reg_count, RegList allowed) { |
| 242 RegList list = 0; |
| 243 int i = 0; |
| 244 for (unsigned n = 0; (n < kNumberOfFPRegisters) && (i < reg_count); n++) { |
| 245 if (((1UL << n) & allowed) != 0) { |
| 246 // Only assigned allowed registers. |
| 247 if (v) { |
| 248 v[i] = FPRegister::Create(n, reg_size); |
| 249 } |
| 250 if (d) { |
| 251 d[i] = FPRegister::Create(n, kDRegSize); |
| 252 } |
| 253 if (s) { |
| 254 s[i] = FPRegister::Create(n, kSRegSize); |
| 255 } |
| 256 list |= (1UL << n); |
| 257 i++; |
| 258 } |
| 259 } |
| 260 // Check that we got enough registers. |
| 261 ASSERT(CountSetBits(list, kNumberOfFPRegisters) == reg_count); |
| 262 |
| 263 return list; |
| 264 } |
| 265 |
| 266 |
| 267 void Clobber(MacroAssembler* masm, RegList reg_list, uint64_t const value) { |
| 268 Register first = NoReg; |
| 269 for (unsigned i = 0; i < kNumberOfRegisters; i++) { |
| 270 if (reg_list & (1UL << i)) { |
| 271 Register xn = Register::Create(i, kXRegSize); |
| 272 // We should never write into csp here. |
| 273 ASSERT(!xn.Is(csp)); |
| 274 if (!xn.IsZero()) { |
| 275 if (!first.IsValid()) { |
| 276 // This is the first register we've hit, so construct the literal. |
| 277 __ Mov(xn, value); |
| 278 first = xn; |
| 279 } else { |
| 280 // We've already loaded the literal, so re-use the value already |
| 281 // loaded into the first register we hit. |
| 282 __ Mov(xn, first); |
| 283 } |
| 284 } |
| 285 } |
| 286 } |
| 287 } |
| 288 |
| 289 |
| 290 void ClobberFP(MacroAssembler* masm, RegList reg_list, double const value) { |
| 291 FPRegister first = NoFPReg; |
| 292 for (unsigned i = 0; i < kNumberOfFPRegisters; i++) { |
| 293 if (reg_list & (1UL << i)) { |
| 294 FPRegister dn = FPRegister::Create(i, kDRegSize); |
| 295 if (!first.IsValid()) { |
| 296 // This is the first register we've hit, so construct the literal. |
| 297 __ Fmov(dn, value); |
| 298 first = dn; |
| 299 } else { |
| 300 // We've already loaded the literal, so re-use the value already loaded |
| 301 // into the first register we hit. |
| 302 __ Fmov(dn, first); |
| 303 } |
| 304 } |
| 305 } |
| 306 } |
| 307 |
| 308 |
| 309 void Clobber(MacroAssembler* masm, CPURegList reg_list) { |
| 310 if (reg_list.type() == CPURegister::kRegister) { |
| 311 // This will always clobber X registers. |
| 312 Clobber(masm, reg_list.list()); |
| 313 } else if (reg_list.type() == CPURegister::kFPRegister) { |
| 314 // This will always clobber D registers. |
| 315 ClobberFP(masm, reg_list.list()); |
| 316 } else { |
| 317 UNREACHABLE(); |
| 318 } |
| 319 } |
| 320 |
| 321 |
| 322 void RegisterDump::Dump(MacroAssembler* masm) { |
| 323 ASSERT(__ StackPointer().Is(csp)); |
| 324 |
| 325 // Ensure that we don't unintentionally clobber any registers. |
| 326 Register old_tmp0 = __ Tmp0(); |
| 327 Register old_tmp1 = __ Tmp1(); |
| 328 FPRegister old_fptmp0 = __ FPTmp0(); |
| 329 __ SetScratchRegisters(NoReg, NoReg); |
| 330 __ SetFPScratchRegister(NoFPReg); |
| 331 |
| 332 // Preserve some temporary registers. |
| 333 Register dump_base = x0; |
| 334 Register dump = x1; |
| 335 Register tmp = x2; |
| 336 Register dump_base_w = dump_base.W(); |
| 337 Register dump_w = dump.W(); |
| 338 Register tmp_w = tmp.W(); |
| 339 |
| 340 // Offsets into the dump_ structure. |
| 341 const int x_offset = offsetof(dump_t, x_); |
| 342 const int w_offset = offsetof(dump_t, w_); |
| 343 const int d_offset = offsetof(dump_t, d_); |
| 344 const int s_offset = offsetof(dump_t, s_); |
| 345 const int sp_offset = offsetof(dump_t, sp_); |
| 346 const int wsp_offset = offsetof(dump_t, wsp_); |
| 347 const int flags_offset = offsetof(dump_t, flags_); |
| 348 |
| 349 __ Push(xzr, dump_base, dump, tmp); |
| 350 |
| 351 // Load the address where we will dump the state. |
| 352 __ Mov(dump_base, reinterpret_cast<uint64_t>(&dump_)); |
| 353 |
| 354 // Dump the stack pointer (csp and wcsp). |
| 355 // The stack pointer cannot be stored directly; it needs to be moved into |
| 356 // another register first. Also, we pushed four X registers, so we need to |
| 357 // compensate here. |
| 358 __ Add(tmp, csp, 4 * kXRegSizeInBytes); |
| 359 __ Str(tmp, MemOperand(dump_base, sp_offset)); |
| 360 __ Add(tmp_w, wcsp, 4 * kXRegSizeInBytes); |
| 361 __ Str(tmp_w, MemOperand(dump_base, wsp_offset)); |
| 362 |
| 363 // Dump X registers. |
| 364 __ Add(dump, dump_base, x_offset); |
| 365 for (unsigned i = 0; i < kNumberOfRegisters; i += 2) { |
| 366 __ Stp(Register::XRegFromCode(i), Register::XRegFromCode(i + 1), |
| 367 MemOperand(dump, i * kXRegSizeInBytes)); |
| 368 } |
| 369 |
| 370 // Dump W registers. |
| 371 __ Add(dump, dump_base, w_offset); |
| 372 for (unsigned i = 0; i < kNumberOfRegisters; i += 2) { |
| 373 __ Stp(Register::WRegFromCode(i), Register::WRegFromCode(i + 1), |
| 374 MemOperand(dump, i * kWRegSizeInBytes)); |
| 375 } |
| 376 |
| 377 // Dump D registers. |
| 378 __ Add(dump, dump_base, d_offset); |
| 379 for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) { |
| 380 __ Stp(FPRegister::DRegFromCode(i), FPRegister::DRegFromCode(i + 1), |
| 381 MemOperand(dump, i * kDRegSizeInBytes)); |
| 382 } |
| 383 |
| 384 // Dump S registers. |
| 385 __ Add(dump, dump_base, s_offset); |
| 386 for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) { |
| 387 __ Stp(FPRegister::SRegFromCode(i), FPRegister::SRegFromCode(i + 1), |
| 388 MemOperand(dump, i * kSRegSizeInBytes)); |
| 389 } |
| 390 |
| 391 // Dump the flags. |
| 392 __ Mrs(tmp, NZCV); |
| 393 __ Str(tmp, MemOperand(dump_base, flags_offset)); |
| 394 |
| 395 // To dump the values that were in tmp amd dump, we need a new scratch |
| 396 // register. We can use any of the already dumped registers since we can |
| 397 // easily restore them. |
| 398 Register dump2_base = x10; |
| 399 Register dump2 = x11; |
| 400 ASSERT(!AreAliased(dump_base, dump, tmp, dump2_base, dump2)); |
| 401 |
| 402 // Don't lose the dump_ address. |
| 403 __ Mov(dump2_base, dump_base); |
| 404 |
| 405 __ Pop(tmp, dump, dump_base, xzr); |
| 406 |
| 407 __ Add(dump2, dump2_base, w_offset); |
| 408 __ Str(dump_base_w, MemOperand(dump2, dump_base.code() * kWRegSizeInBytes)); |
| 409 __ Str(dump_w, MemOperand(dump2, dump.code() * kWRegSizeInBytes)); |
| 410 __ Str(tmp_w, MemOperand(dump2, tmp.code() * kWRegSizeInBytes)); |
| 411 |
| 412 __ Add(dump2, dump2_base, x_offset); |
| 413 __ Str(dump_base, MemOperand(dump2, dump_base.code() * kXRegSizeInBytes)); |
| 414 __ Str(dump, MemOperand(dump2, dump.code() * kXRegSizeInBytes)); |
| 415 __ Str(tmp, MemOperand(dump2, tmp.code() * kXRegSizeInBytes)); |
| 416 |
| 417 // Finally, restore dump2_base and dump2. |
| 418 __ Ldr(dump2_base, MemOperand(dump2, dump2_base.code() * kXRegSizeInBytes)); |
| 419 __ Ldr(dump2, MemOperand(dump2, dump2.code() * kXRegSizeInBytes)); |
| 420 |
| 421 // Restore the MacroAssembler's scratch registers. |
| 422 __ SetScratchRegisters(old_tmp0, old_tmp1); |
| 423 __ SetFPScratchRegister(old_fptmp0); |
| 424 |
| 425 completed_ = true; |
| 426 } |
OLD | NEW |