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 "src/v8.h" 29 30#include "src/arm64/utils-arm64.h" 31#include "src/macro-assembler.h" 32#include "test/cctest/cctest.h" 33#include "test/cctest/test-utils-arm64.h" 34 35using namespace v8::internal; 36 37 38#define __ masm-> 39 40 41bool 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 51bool 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 61bool EqualFP32(float expected, const RegisterDump*, float result) { 62 if (float_to_rawbits(expected) == float_to_rawbits(result)) { 63 return true; 64 } else { 65 if (std::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 79bool EqualFP64(double expected, const RegisterDump*, double result) { 80 if (double_to_rawbits(expected) == double_to_rawbits(result)) { 81 return true; 82 } 83 84 if (std::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 97bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg) { 98 CHECK(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 112bool Equal64(uint64_t expected, 113 const RegisterDump* core, 114 const Register& reg) { 115 CHECK(reg.Is64Bits()); 116 uint64_t result = core->xreg(reg.code()); 117 return Equal64(expected, core, result); 118} 119 120 121bool EqualFP32(float expected, 122 const RegisterDump* core, 123 const FPRegister& fpreg) { 124 CHECK(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 138bool EqualFP64(double expected, 139 const RegisterDump* core, 140 const FPRegister& fpreg) { 141 CHECK(fpreg.Is64Bits()); 142 return EqualFP64(expected, core, core->dreg(fpreg.code())); 143} 144 145 146bool Equal64(const Register& reg0, 147 const RegisterDump* core, 148 const Register& reg1) { 149 CHECK(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 156static char FlagN(uint32_t flags) { 157 return (flags & NFlag) ? 'N' : 'n'; 158} 159 160 161static char FlagZ(uint32_t flags) { 162 return (flags & ZFlag) ? 'Z' : 'z'; 163} 164 165 166static char FlagC(uint32_t flags) { 167 return (flags & CFlag) ? 'C' : 'c'; 168} 169 170 171static char FlagV(uint32_t flags) { 172 return (flags & VFlag) ? 'V' : 'v'; 173} 174 175 176bool EqualNzcv(uint32_t expected, uint32_t result) { 177 CHECK((expected & ~NZCVFlag) == 0); 178 CHECK((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 190bool 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 213RegList 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, kXRegSizeInBits); 225 } 226 if (w) { 227 w[i] = Register::Create(n, kWRegSizeInBits); 228 } 229 list |= (1UL << n); 230 i++; 231 } 232 } 233 // Check that we got enough registers. 234 CHECK(CountSetBits(list, kNumberOfRegisters) == reg_count); 235 236 return list; 237} 238 239 240RegList 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, kDRegSizeInBits); 252 } 253 if (s) { 254 s[i] = FPRegister::Create(n, kSRegSizeInBits); 255 } 256 list |= (1UL << n); 257 i++; 258 } 259 } 260 // Check that we got enough registers. 261 CHECK(CountSetBits(list, kNumberOfFPRegisters) == reg_count); 262 263 return list; 264} 265 266 267void 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, kXRegSizeInBits); 272 // We should never write into csp here. 273 CHECK(!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 290void 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, kDRegSizeInBits); 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 309void 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 322void RegisterDump::Dump(MacroAssembler* masm) { 323 CHECK(__ StackPointer().Is(csp)); 324 325 // Ensure that we don't unintentionally clobber any registers. 326 RegList old_tmp_list = masm->TmpList()->list(); 327 RegList old_fptmp_list = masm->FPTmpList()->list(); 328 masm->TmpList()->set_list(0); 329 masm->FPTmpList()->set_list(0); 330 331 // Preserve some temporary registers. 332 Register dump_base = x0; 333 Register dump = x1; 334 Register tmp = x2; 335 Register dump_base_w = dump_base.W(); 336 Register dump_w = dump.W(); 337 Register tmp_w = tmp.W(); 338 339 // Offsets into the dump_ structure. 340 const int x_offset = offsetof(dump_t, x_); 341 const int w_offset = offsetof(dump_t, w_); 342 const int d_offset = offsetof(dump_t, d_); 343 const int s_offset = offsetof(dump_t, s_); 344 const int sp_offset = offsetof(dump_t, sp_); 345 const int wsp_offset = offsetof(dump_t, wsp_); 346 const int flags_offset = offsetof(dump_t, flags_); 347 348 __ Push(xzr, dump_base, dump, tmp); 349 350 // Load the address where we will dump the state. 351 __ Mov(dump_base, reinterpret_cast<uint64_t>(&dump_)); 352 353 // Dump the stack pointer (csp and wcsp). 354 // The stack pointer cannot be stored directly; it needs to be moved into 355 // another register first. Also, we pushed four X registers, so we need to 356 // compensate here. 357 __ Add(tmp, csp, 4 * kXRegSize); 358 __ Str(tmp, MemOperand(dump_base, sp_offset)); 359 __ Add(tmp_w, wcsp, 4 * kXRegSize); 360 __ Str(tmp_w, MemOperand(dump_base, wsp_offset)); 361 362 // Dump X registers. 363 __ Add(dump, dump_base, x_offset); 364 for (unsigned i = 0; i < kNumberOfRegisters; i += 2) { 365 __ Stp(Register::XRegFromCode(i), Register::XRegFromCode(i + 1), 366 MemOperand(dump, i * kXRegSize)); 367 } 368 369 // Dump W registers. 370 __ Add(dump, dump_base, w_offset); 371 for (unsigned i = 0; i < kNumberOfRegisters; i += 2) { 372 __ Stp(Register::WRegFromCode(i), Register::WRegFromCode(i + 1), 373 MemOperand(dump, i * kWRegSize)); 374 } 375 376 // Dump D registers. 377 __ Add(dump, dump_base, d_offset); 378 for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) { 379 __ Stp(FPRegister::DRegFromCode(i), FPRegister::DRegFromCode(i + 1), 380 MemOperand(dump, i * kDRegSize)); 381 } 382 383 // Dump S registers. 384 __ Add(dump, dump_base, s_offset); 385 for (unsigned i = 0; i < kNumberOfFPRegisters; i += 2) { 386 __ Stp(FPRegister::SRegFromCode(i), FPRegister::SRegFromCode(i + 1), 387 MemOperand(dump, i * kSRegSize)); 388 } 389 390 // Dump the flags. 391 __ Mrs(tmp, NZCV); 392 __ Str(tmp, MemOperand(dump_base, flags_offset)); 393 394 // To dump the values that were in tmp amd dump, we need a new scratch 395 // register. We can use any of the already dumped registers since we can 396 // easily restore them. 397 Register dump2_base = x10; 398 Register dump2 = x11; 399 CHECK(!AreAliased(dump_base, dump, tmp, dump2_base, dump2)); 400 401 // Don't lose the dump_ address. 402 __ Mov(dump2_base, dump_base); 403 404 __ Pop(tmp, dump, dump_base, xzr); 405 406 __ Add(dump2, dump2_base, w_offset); 407 __ Str(dump_base_w, MemOperand(dump2, dump_base.code() * kWRegSize)); 408 __ Str(dump_w, MemOperand(dump2, dump.code() * kWRegSize)); 409 __ Str(tmp_w, MemOperand(dump2, tmp.code() * kWRegSize)); 410 411 __ Add(dump2, dump2_base, x_offset); 412 __ Str(dump_base, MemOperand(dump2, dump_base.code() * kXRegSize)); 413 __ Str(dump, MemOperand(dump2, dump.code() * kXRegSize)); 414 __ Str(tmp, MemOperand(dump2, tmp.code() * kXRegSize)); 415 416 // Finally, restore dump2_base and dump2. 417 __ Ldr(dump2_base, MemOperand(dump2, dump2_base.code() * kXRegSize)); 418 __ Ldr(dump2, MemOperand(dump2, dump2.code() * kXRegSize)); 419 420 // Restore the MacroAssembler's scratch registers. 421 masm->TmpList()->set_list(old_tmp_list); 422 masm->FPTmpList()->set_list(old_fptmp_list); 423 424 completed_ = true; 425} 426