Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 | |
| 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 (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 | |
| 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 (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 | |
| 97 | bool Equal32(uint32_t expected, const RegisterDump* core, const Register& reg) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 98 | CHECK(reg.Is32Bits()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 115 | CHECK(reg.Is64Bits()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 124 | CHECK(fpreg.Is32Bits()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 141 | CHECK(fpreg.Is64Bits()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 149 | CHECK(reg0.Is64Bits() && reg1.Is64Bits()); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 177 | CHECK((expected & ~NZCVFlag) == 0); |
| 178 | CHECK((result & ~NZCVFlag) == 0); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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, 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. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 234 | CHECK(CountSetBits(list, kNumberOfRegisters) == reg_count); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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, 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. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 261 | CHECK(CountSetBits(list, kNumberOfFPRegisters) == reg_count); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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, kXRegSizeInBits); |
| 272 | // We should never write into csp here. |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 273 | CHECK(!xn.Is(csp)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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, 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 | |
| 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) { |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 323 | CHECK(__ StackPointer().Is(csp)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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; |
Ben Murdoch | 4a90d5f | 2016-03-22 12:00:34 +0000 | [diff] [blame] | 399 | CHECK(!AreAliased(dump_base, dump, tmp, dump2_base, dump2)); |
Ben Murdoch | b8a8cc1 | 2014-11-26 15:28:44 +0000 | [diff] [blame] | 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 | } |