1 // Copyright 2013 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/v8.h" 6 7 #if V8_TARGET_ARCH_ARM64 8 9 #include "src/bootstrapper.h" 10 #include "src/codegen.h" 11 #include "src/cpu-profiler.h" 12 #include "src/debug.h" 13 #include "src/isolate-inl.h" 14 #include "src/runtime.h" 15 16 namespace v8 { 17 namespace internal { 18 19 // Define a fake double underscore to use with the ASM_UNIMPLEMENTED macros. 20 #define __ 21 22 23 MacroAssembler::MacroAssembler(Isolate* arg_isolate, 24 byte * buffer, 25 unsigned buffer_size) 26 : Assembler(arg_isolate, buffer, buffer_size), 27 generating_stub_(false), 28 #if DEBUG 29 allow_macro_instructions_(true), 30 #endif 31 has_frame_(false), 32 use_real_aborts_(true), 33 sp_(jssp), 34 tmp_list_(DefaultTmpList()), 35 fptmp_list_(DefaultFPTmpList()) { 36 if (isolate() != NULL) { 37 code_object_ = Handle<Object>(isolate()->heap()->undefined_value(), 38 isolate()); 39 } 40 } 41 42 43 CPURegList MacroAssembler::DefaultTmpList() { 44 return CPURegList(ip0, ip1); 45 } 46 47 48 CPURegList MacroAssembler::DefaultFPTmpList() { 49 return CPURegList(fp_scratch1, fp_scratch2); 50 } 51 52 53 void MacroAssembler::LogicalMacro(const Register& rd, 54 const Register& rn, 55 const Operand& operand, 56 LogicalOp op) { 57 UseScratchRegisterScope temps(this); 58 59 if (operand.NeedsRelocation(this)) { 60 Register temp = temps.AcquireX(); 61 Ldr(temp, operand.immediate()); 62 Logical(rd, rn, temp, op); 63 64 } else if (operand.IsImmediate()) { 65 int64_t immediate = operand.ImmediateValue(); 66 unsigned reg_size = rd.SizeInBits(); 67 ASSERT(rd.Is64Bits() || is_uint32(immediate)); 68 69 // If the operation is NOT, invert the operation and immediate. 70 if ((op & NOT) == NOT) { 71 op = static_cast<LogicalOp>(op & ~NOT); 72 immediate = ~immediate; 73 if (rd.Is32Bits()) { 74 immediate &= kWRegMask; 75 } 76 } 77 78 // Special cases for all set or all clear immediates. 79 if (immediate == 0) { 80 switch (op) { 81 case AND: 82 Mov(rd, 0); 83 return; 84 case ORR: // Fall through. 85 case EOR: 86 Mov(rd, rn); 87 return; 88 case ANDS: // Fall through. 89 case BICS: 90 break; 91 default: 92 UNREACHABLE(); 93 } 94 } else if ((rd.Is64Bits() && (immediate == -1L)) || 95 (rd.Is32Bits() && (immediate == 0xffffffffL))) { 96 switch (op) { 97 case AND: 98 Mov(rd, rn); 99 return; 100 case ORR: 101 Mov(rd, immediate); 102 return; 103 case EOR: 104 Mvn(rd, rn); 105 return; 106 case ANDS: // Fall through. 107 case BICS: 108 break; 109 default: 110 UNREACHABLE(); 111 } 112 } 113 114 unsigned n, imm_s, imm_r; 115 if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) { 116 // Immediate can be encoded in the instruction. 117 LogicalImmediate(rd, rn, n, imm_s, imm_r, op); 118 } else { 119 // Immediate can't be encoded: synthesize using move immediate. 120 Register temp = temps.AcquireSameSizeAs(rn); 121 Mov(temp, immediate); 122 if (rd.Is(csp)) { 123 // If rd is the stack pointer we cannot use it as the destination 124 // register so we use the temp register as an intermediate again. 125 Logical(temp, rn, temp, op); 126 Mov(csp, temp); 127 AssertStackConsistency(); 128 } else { 129 Logical(rd, rn, temp, op); 130 } 131 } 132 133 } else if (operand.IsExtendedRegister()) { 134 ASSERT(operand.reg().SizeInBits() <= rd.SizeInBits()); 135 // Add/sub extended supports shift <= 4. We want to support exactly the 136 // same modes here. 137 ASSERT(operand.shift_amount() <= 4); 138 ASSERT(operand.reg().Is64Bits() || 139 ((operand.extend() != UXTX) && (operand.extend() != SXTX))); 140 Register temp = temps.AcquireSameSizeAs(rn); 141 EmitExtendShift(temp, operand.reg(), operand.extend(), 142 operand.shift_amount()); 143 Logical(rd, rn, temp, op); 144 145 } else { 146 // The operand can be encoded in the instruction. 147 ASSERT(operand.IsShiftedRegister()); 148 Logical(rd, rn, operand, op); 149 } 150 } 151 152 153 void MacroAssembler::Mov(const Register& rd, uint64_t imm) { 154 ASSERT(allow_macro_instructions_); 155 ASSERT(is_uint32(imm) || is_int32(imm) || rd.Is64Bits()); 156 ASSERT(!rd.IsZero()); 157 158 // TODO(all) extend to support more immediates. 159 // 160 // Immediates on Aarch64 can be produced using an initial value, and zero to 161 // three move keep operations. 162 // 163 // Initial values can be generated with: 164 // 1. 64-bit move zero (movz). 165 // 2. 32-bit move inverted (movn). 166 // 3. 64-bit move inverted. 167 // 4. 32-bit orr immediate. 168 // 5. 64-bit orr immediate. 169 // Move-keep may then be used to modify each of the 16-bit half-words. 170 // 171 // The code below supports all five initial value generators, and 172 // applying move-keep operations to move-zero and move-inverted initial 173 // values. 174 175 unsigned reg_size = rd.SizeInBits(); 176 unsigned n, imm_s, imm_r; 177 if (IsImmMovz(imm, reg_size) && !rd.IsSP()) { 178 // Immediate can be represented in a move zero instruction. Movz can't 179 // write to the stack pointer. 180 movz(rd, imm); 181 } else if (IsImmMovn(imm, reg_size) && !rd.IsSP()) { 182 // Immediate can be represented in a move inverted instruction. Movn can't 183 // write to the stack pointer. 184 movn(rd, rd.Is64Bits() ? ~imm : (~imm & kWRegMask)); 185 } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) { 186 // Immediate can be represented in a logical orr instruction. 187 LogicalImmediate(rd, AppropriateZeroRegFor(rd), n, imm_s, imm_r, ORR); 188 } else { 189 // Generic immediate case. Imm will be represented by 190 // [imm3, imm2, imm1, imm0], where each imm is 16 bits. 191 // A move-zero or move-inverted is generated for the first non-zero or 192 // non-0xffff immX, and a move-keep for subsequent non-zero immX. 193 194 uint64_t ignored_halfword = 0; 195 bool invert_move = false; 196 // If the number of 0xffff halfwords is greater than the number of 0x0000 197 // halfwords, it's more efficient to use move-inverted. 198 if (CountClearHalfWords(~imm, reg_size) > 199 CountClearHalfWords(imm, reg_size)) { 200 ignored_halfword = 0xffffL; 201 invert_move = true; 202 } 203 204 // Mov instructions can't move immediate values into the stack pointer, so 205 // set up a temporary register, if needed. 206 UseScratchRegisterScope temps(this); 207 Register temp = rd.IsSP() ? temps.AcquireSameSizeAs(rd) : rd; 208 209 // Iterate through the halfwords. Use movn/movz for the first non-ignored 210 // halfword, and movk for subsequent halfwords. 211 ASSERT((reg_size % 16) == 0); 212 bool first_mov_done = false; 213 for (unsigned i = 0; i < (rd.SizeInBits() / 16); i++) { 214 uint64_t imm16 = (imm >> (16 * i)) & 0xffffL; 215 if (imm16 != ignored_halfword) { 216 if (!first_mov_done) { 217 if (invert_move) { 218 movn(temp, (~imm16) & 0xffffL, 16 * i); 219 } else { 220 movz(temp, imm16, 16 * i); 221 } 222 first_mov_done = true; 223 } else { 224 // Construct a wider constant. 225 movk(temp, imm16, 16 * i); 226 } 227 } 228 } 229 ASSERT(first_mov_done); 230 231 // Move the temporary if the original destination register was the stack 232 // pointer. 233 if (rd.IsSP()) { 234 mov(rd, temp); 235 AssertStackConsistency(); 236 } 237 } 238 } 239 240 241 void MacroAssembler::Mov(const Register& rd, 242 const Operand& operand, 243 DiscardMoveMode discard_mode) { 244 ASSERT(allow_macro_instructions_); 245 ASSERT(!rd.IsZero()); 246 247 // Provide a swap register for instructions that need to write into the 248 // system stack pointer (and can't do this inherently). 249 UseScratchRegisterScope temps(this); 250 Register dst = (rd.IsSP()) ? temps.AcquireSameSizeAs(rd) : rd; 251 252 if (operand.NeedsRelocation(this)) { 253 Ldr(dst, operand.immediate()); 254 255 } else if (operand.IsImmediate()) { 256 // Call the macro assembler for generic immediates. 257 Mov(dst, operand.ImmediateValue()); 258 259 } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { 260 // Emit a shift instruction if moving a shifted register. This operation 261 // could also be achieved using an orr instruction (like orn used by Mvn), 262 // but using a shift instruction makes the disassembly clearer. 263 EmitShift(dst, operand.reg(), operand.shift(), operand.shift_amount()); 264 265 } else if (operand.IsExtendedRegister()) { 266 // Emit an extend instruction if moving an extended register. This handles 267 // extend with post-shift operations, too. 268 EmitExtendShift(dst, operand.reg(), operand.extend(), 269 operand.shift_amount()); 270 271 } else { 272 // Otherwise, emit a register move only if the registers are distinct, or 273 // if they are not X registers. 274 // 275 // Note that mov(w0, w0) is not a no-op because it clears the top word of 276 // x0. A flag is provided (kDiscardForSameWReg) if a move between the same W 277 // registers is not required to clear the top word of the X register. In 278 // this case, the instruction is discarded. 279 // 280 // If csp is an operand, add #0 is emitted, otherwise, orr #0. 281 if (!rd.Is(operand.reg()) || (rd.Is32Bits() && 282 (discard_mode == kDontDiscardForSameWReg))) { 283 Assembler::mov(rd, operand.reg()); 284 } 285 // This case can handle writes into the system stack pointer directly. 286 dst = rd; 287 } 288 289 // Copy the result to the system stack pointer. 290 if (!dst.Is(rd)) { 291 ASSERT(rd.IsSP()); 292 Assembler::mov(rd, dst); 293 } 294 } 295 296 297 void MacroAssembler::Mvn(const Register& rd, const Operand& operand) { 298 ASSERT(allow_macro_instructions_); 299 300 if (operand.NeedsRelocation(this)) { 301 Ldr(rd, operand.immediate()); 302 mvn(rd, rd); 303 304 } else if (operand.IsImmediate()) { 305 // Call the macro assembler for generic immediates. 306 Mov(rd, ~operand.ImmediateValue()); 307 308 } else if (operand.IsExtendedRegister()) { 309 // Emit two instructions for the extend case. This differs from Mov, as 310 // the extend and invert can't be achieved in one instruction. 311 EmitExtendShift(rd, operand.reg(), operand.extend(), 312 operand.shift_amount()); 313 mvn(rd, rd); 314 315 } else { 316 mvn(rd, operand); 317 } 318 } 319 320 321 unsigned MacroAssembler::CountClearHalfWords(uint64_t imm, unsigned reg_size) { 322 ASSERT((reg_size % 8) == 0); 323 int count = 0; 324 for (unsigned i = 0; i < (reg_size / 16); i++) { 325 if ((imm & 0xffff) == 0) { 326 count++; 327 } 328 imm >>= 16; 329 } 330 return count; 331 } 332 333 334 // The movz instruction can generate immediates containing an arbitrary 16-bit 335 // half-word, with remaining bits clear, eg. 0x00001234, 0x0000123400000000. 336 bool MacroAssembler::IsImmMovz(uint64_t imm, unsigned reg_size) { 337 ASSERT((reg_size == kXRegSizeInBits) || (reg_size == kWRegSizeInBits)); 338 return CountClearHalfWords(imm, reg_size) >= ((reg_size / 16) - 1); 339 } 340 341 342 // The movn instruction can generate immediates containing an arbitrary 16-bit 343 // half-word, with remaining bits set, eg. 0xffff1234, 0xffff1234ffffffff. 344 bool MacroAssembler::IsImmMovn(uint64_t imm, unsigned reg_size) { 345 return IsImmMovz(~imm, reg_size); 346 } 347 348 349 void MacroAssembler::ConditionalCompareMacro(const Register& rn, 350 const Operand& operand, 351 StatusFlags nzcv, 352 Condition cond, 353 ConditionalCompareOp op) { 354 ASSERT((cond != al) && (cond != nv)); 355 if (operand.NeedsRelocation(this)) { 356 UseScratchRegisterScope temps(this); 357 Register temp = temps.AcquireX(); 358 Ldr(temp, operand.immediate()); 359 ConditionalCompareMacro(rn, temp, nzcv, cond, op); 360 361 } else if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) || 362 (operand.IsImmediate() && 363 IsImmConditionalCompare(operand.ImmediateValue()))) { 364 // The immediate can be encoded in the instruction, or the operand is an 365 // unshifted register: call the assembler. 366 ConditionalCompare(rn, operand, nzcv, cond, op); 367 368 } else { 369 // The operand isn't directly supported by the instruction: perform the 370 // operation on a temporary register. 371 UseScratchRegisterScope temps(this); 372 Register temp = temps.AcquireSameSizeAs(rn); 373 Mov(temp, operand); 374 ConditionalCompare(rn, temp, nzcv, cond, op); 375 } 376 } 377 378 379 void MacroAssembler::Csel(const Register& rd, 380 const Register& rn, 381 const Operand& operand, 382 Condition cond) { 383 ASSERT(allow_macro_instructions_); 384 ASSERT(!rd.IsZero()); 385 ASSERT((cond != al) && (cond != nv)); 386 if (operand.IsImmediate()) { 387 // Immediate argument. Handle special cases of 0, 1 and -1 using zero 388 // register. 389 int64_t imm = operand.ImmediateValue(); 390 Register zr = AppropriateZeroRegFor(rn); 391 if (imm == 0) { 392 csel(rd, rn, zr, cond); 393 } else if (imm == 1) { 394 csinc(rd, rn, zr, cond); 395 } else if (imm == -1) { 396 csinv(rd, rn, zr, cond); 397 } else { 398 UseScratchRegisterScope temps(this); 399 Register temp = temps.AcquireSameSizeAs(rn); 400 Mov(temp, imm); 401 csel(rd, rn, temp, cond); 402 } 403 } else if (operand.IsShiftedRegister() && (operand.shift_amount() == 0)) { 404 // Unshifted register argument. 405 csel(rd, rn, operand.reg(), cond); 406 } else { 407 // All other arguments. 408 UseScratchRegisterScope temps(this); 409 Register temp = temps.AcquireSameSizeAs(rn); 410 Mov(temp, operand); 411 csel(rd, rn, temp, cond); 412 } 413 } 414 415 416 void MacroAssembler::AddSubMacro(const Register& rd, 417 const Register& rn, 418 const Operand& operand, 419 FlagsUpdate S, 420 AddSubOp op) { 421 if (operand.IsZero() && rd.Is(rn) && rd.Is64Bits() && rn.Is64Bits() && 422 !operand.NeedsRelocation(this) && (S == LeaveFlags)) { 423 // The instruction would be a nop. Avoid generating useless code. 424 return; 425 } 426 427 if (operand.NeedsRelocation(this)) { 428 UseScratchRegisterScope temps(this); 429 Register temp = temps.AcquireX(); 430 Ldr(temp, operand.immediate()); 431 AddSubMacro(rd, rn, temp, S, op); 432 } else if ((operand.IsImmediate() && 433 !IsImmAddSub(operand.ImmediateValue())) || 434 (rn.IsZero() && !operand.IsShiftedRegister()) || 435 (operand.IsShiftedRegister() && (operand.shift() == ROR))) { 436 UseScratchRegisterScope temps(this); 437 Register temp = temps.AcquireSameSizeAs(rn); 438 Mov(temp, operand); 439 AddSub(rd, rn, temp, S, op); 440 } else { 441 AddSub(rd, rn, operand, S, op); 442 } 443 } 444 445 446 void MacroAssembler::AddSubWithCarryMacro(const Register& rd, 447 const Register& rn, 448 const Operand& operand, 449 FlagsUpdate S, 450 AddSubWithCarryOp op) { 451 ASSERT(rd.SizeInBits() == rn.SizeInBits()); 452 UseScratchRegisterScope temps(this); 453 454 if (operand.NeedsRelocation(this)) { 455 Register temp = temps.AcquireX(); 456 Ldr(temp, operand.immediate()); 457 AddSubWithCarryMacro(rd, rn, temp, S, op); 458 459 } else if (operand.IsImmediate() || 460 (operand.IsShiftedRegister() && (operand.shift() == ROR))) { 461 // Add/sub with carry (immediate or ROR shifted register.) 462 Register temp = temps.AcquireSameSizeAs(rn); 463 Mov(temp, operand); 464 AddSubWithCarry(rd, rn, temp, S, op); 465 466 } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) { 467 // Add/sub with carry (shifted register). 468 ASSERT(operand.reg().SizeInBits() == rd.SizeInBits()); 469 ASSERT(operand.shift() != ROR); 470 ASSERT(is_uintn(operand.shift_amount(), 471 rd.SizeInBits() == kXRegSizeInBits ? kXRegSizeInBitsLog2 472 : kWRegSizeInBitsLog2)); 473 Register temp = temps.AcquireSameSizeAs(rn); 474 EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount()); 475 AddSubWithCarry(rd, rn, temp, S, op); 476 477 } else if (operand.IsExtendedRegister()) { 478 // Add/sub with carry (extended register). 479 ASSERT(operand.reg().SizeInBits() <= rd.SizeInBits()); 480 // Add/sub extended supports a shift <= 4. We want to support exactly the 481 // same modes. 482 ASSERT(operand.shift_amount() <= 4); 483 ASSERT(operand.reg().Is64Bits() || 484 ((operand.extend() != UXTX) && (operand.extend() != SXTX))); 485 Register temp = temps.AcquireSameSizeAs(rn); 486 EmitExtendShift(temp, operand.reg(), operand.extend(), 487 operand.shift_amount()); 488 AddSubWithCarry(rd, rn, temp, S, op); 489 490 } else { 491 // The addressing mode is directly supported by the instruction. 492 AddSubWithCarry(rd, rn, operand, S, op); 493 } 494 } 495 496 497 void MacroAssembler::LoadStoreMacro(const CPURegister& rt, 498 const MemOperand& addr, 499 LoadStoreOp op) { 500 int64_t offset = addr.offset(); 501 LSDataSize size = CalcLSDataSize(op); 502 503 // Check if an immediate offset fits in the immediate field of the 504 // appropriate instruction. If not, emit two instructions to perform 505 // the operation. 506 if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, size) && 507 !IsImmLSUnscaled(offset)) { 508 // Immediate offset that can't be encoded using unsigned or unscaled 509 // addressing modes. 510 UseScratchRegisterScope temps(this); 511 Register temp = temps.AcquireSameSizeAs(addr.base()); 512 Mov(temp, addr.offset()); 513 LoadStore(rt, MemOperand(addr.base(), temp), op); 514 } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) { 515 // Post-index beyond unscaled addressing range. 516 LoadStore(rt, MemOperand(addr.base()), op); 517 add(addr.base(), addr.base(), offset); 518 } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) { 519 // Pre-index beyond unscaled addressing range. 520 add(addr.base(), addr.base(), offset); 521 LoadStore(rt, MemOperand(addr.base()), op); 522 } else { 523 // Encodable in one load/store instruction. 524 LoadStore(rt, addr, op); 525 } 526 } 527 528 529 void MacroAssembler::Load(const Register& rt, 530 const MemOperand& addr, 531 Representation r) { 532 ASSERT(!r.IsDouble()); 533 534 if (r.IsInteger8()) { 535 Ldrsb(rt, addr); 536 } else if (r.IsUInteger8()) { 537 Ldrb(rt, addr); 538 } else if (r.IsInteger16()) { 539 Ldrsh(rt, addr); 540 } else if (r.IsUInteger16()) { 541 Ldrh(rt, addr); 542 } else if (r.IsInteger32()) { 543 Ldr(rt.W(), addr); 544 } else { 545 ASSERT(rt.Is64Bits()); 546 Ldr(rt, addr); 547 } 548 } 549 550 551 void MacroAssembler::Store(const Register& rt, 552 const MemOperand& addr, 553 Representation r) { 554 ASSERT(!r.IsDouble()); 555 556 if (r.IsInteger8() || r.IsUInteger8()) { 557 Strb(rt, addr); 558 } else if (r.IsInteger16() || r.IsUInteger16()) { 559 Strh(rt, addr); 560 } else if (r.IsInteger32()) { 561 Str(rt.W(), addr); 562 } else { 563 ASSERT(rt.Is64Bits()); 564 if (r.IsHeapObject()) { 565 AssertNotSmi(rt); 566 } else if (r.IsSmi()) { 567 AssertSmi(rt); 568 } 569 Str(rt, addr); 570 } 571 } 572 573 574 bool MacroAssembler::NeedExtraInstructionsOrRegisterBranch( 575 Label *label, ImmBranchType b_type) { 576 bool need_longer_range = false; 577 // There are two situations in which we care about the offset being out of 578 // range: 579 // - The label is bound but too far away. 580 // - The label is not bound but linked, and the previous branch 581 // instruction in the chain is too far away. 582 if (label->is_bound() || label->is_linked()) { 583 need_longer_range = 584 !Instruction::IsValidImmPCOffset(b_type, label->pos() - pc_offset()); 585 } 586 if (!need_longer_range && !label->is_bound()) { 587 int max_reachable_pc = pc_offset() + Instruction::ImmBranchRange(b_type); 588 unresolved_branches_.insert( 589 std::pair<int, FarBranchInfo>(max_reachable_pc, 590 FarBranchInfo(pc_offset(), label))); 591 // Also maintain the next pool check. 592 next_veneer_pool_check_ = 593 Min(next_veneer_pool_check_, 594 max_reachable_pc - kVeneerDistanceCheckMargin); 595 } 596 return need_longer_range; 597 } 598 599 600 void MacroAssembler::Adr(const Register& rd, Label* label, AdrHint hint) { 601 ASSERT(allow_macro_instructions_); 602 ASSERT(!rd.IsZero()); 603 604 if (hint == kAdrNear) { 605 adr(rd, label); 606 return; 607 } 608 609 ASSERT(hint == kAdrFar); 610 UseScratchRegisterScope temps(this); 611 Register scratch = temps.AcquireX(); 612 ASSERT(!AreAliased(rd, scratch)); 613 614 if (label->is_bound()) { 615 int label_offset = label->pos() - pc_offset(); 616 if (Instruction::IsValidPCRelOffset(label_offset)) { 617 adr(rd, label); 618 } else { 619 ASSERT(label_offset <= 0); 620 int min_adr_offset = -(1 << (Instruction::ImmPCRelRangeBitwidth - 1)); 621 adr(rd, min_adr_offset); 622 Add(rd, rd, label_offset - min_adr_offset); 623 } 624 } else { 625 InstructionAccurateScope scope( 626 this, PatchingAssembler::kAdrFarPatchableNInstrs); 627 adr(rd, label); 628 for (int i = 0; i < PatchingAssembler::kAdrFarPatchableNNops; ++i) { 629 nop(ADR_FAR_NOP); 630 } 631 movz(scratch, 0); 632 add(rd, rd, scratch); 633 } 634 } 635 636 637 void MacroAssembler::B(Label* label, BranchType type, Register reg, int bit) { 638 ASSERT((reg.Is(NoReg) || type >= kBranchTypeFirstUsingReg) && 639 (bit == -1 || type >= kBranchTypeFirstUsingBit)); 640 if (kBranchTypeFirstCondition <= type && type <= kBranchTypeLastCondition) { 641 B(static_cast<Condition>(type), label); 642 } else { 643 switch (type) { 644 case always: B(label); break; 645 case never: break; 646 case reg_zero: Cbz(reg, label); break; 647 case reg_not_zero: Cbnz(reg, label); break; 648 case reg_bit_clear: Tbz(reg, bit, label); break; 649 case reg_bit_set: Tbnz(reg, bit, label); break; 650 default: 651 UNREACHABLE(); 652 } 653 } 654 } 655 656 657 void MacroAssembler::B(Label* label, Condition cond) { 658 ASSERT(allow_macro_instructions_); 659 ASSERT((cond != al) && (cond != nv)); 660 661 Label done; 662 bool need_extra_instructions = 663 NeedExtraInstructionsOrRegisterBranch(label, CondBranchType); 664 665 if (need_extra_instructions) { 666 b(&done, NegateCondition(cond)); 667 B(label); 668 } else { 669 b(label, cond); 670 } 671 bind(&done); 672 } 673 674 675 void MacroAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) { 676 ASSERT(allow_macro_instructions_); 677 678 Label done; 679 bool need_extra_instructions = 680 NeedExtraInstructionsOrRegisterBranch(label, TestBranchType); 681 682 if (need_extra_instructions) { 683 tbz(rt, bit_pos, &done); 684 B(label); 685 } else { 686 tbnz(rt, bit_pos, label); 687 } 688 bind(&done); 689 } 690 691 692 void MacroAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) { 693 ASSERT(allow_macro_instructions_); 694 695 Label done; 696 bool need_extra_instructions = 697 NeedExtraInstructionsOrRegisterBranch(label, TestBranchType); 698 699 if (need_extra_instructions) { 700 tbnz(rt, bit_pos, &done); 701 B(label); 702 } else { 703 tbz(rt, bit_pos, label); 704 } 705 bind(&done); 706 } 707 708 709 void MacroAssembler::Cbnz(const Register& rt, Label* label) { 710 ASSERT(allow_macro_instructions_); 711 712 Label done; 713 bool need_extra_instructions = 714 NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType); 715 716 if (need_extra_instructions) { 717 cbz(rt, &done); 718 B(label); 719 } else { 720 cbnz(rt, label); 721 } 722 bind(&done); 723 } 724 725 726 void MacroAssembler::Cbz(const Register& rt, Label* label) { 727 ASSERT(allow_macro_instructions_); 728 729 Label done; 730 bool need_extra_instructions = 731 NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType); 732 733 if (need_extra_instructions) { 734 cbnz(rt, &done); 735 B(label); 736 } else { 737 cbz(rt, label); 738 } 739 bind(&done); 740 } 741 742 743 // Pseudo-instructions. 744 745 746 void MacroAssembler::Abs(const Register& rd, const Register& rm, 747 Label* is_not_representable, 748 Label* is_representable) { 749 ASSERT(allow_macro_instructions_); 750 ASSERT(AreSameSizeAndType(rd, rm)); 751 752 Cmp(rm, 1); 753 Cneg(rd, rm, lt); 754 755 // If the comparison sets the v flag, the input was the smallest value 756 // representable by rm, and the mathematical result of abs(rm) is not 757 // representable using two's complement. 758 if ((is_not_representable != NULL) && (is_representable != NULL)) { 759 B(is_not_representable, vs); 760 B(is_representable); 761 } else if (is_not_representable != NULL) { 762 B(is_not_representable, vs); 763 } else if (is_representable != NULL) { 764 B(is_representable, vc); 765 } 766 } 767 768 769 // Abstracted stack operations. 770 771 772 void MacroAssembler::Push(const CPURegister& src0, const CPURegister& src1, 773 const CPURegister& src2, const CPURegister& src3) { 774 ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); 775 776 int count = 1 + src1.IsValid() + src2.IsValid() + src3.IsValid(); 777 int size = src0.SizeInBytes(); 778 779 PushPreamble(count, size); 780 PushHelper(count, size, src0, src1, src2, src3); 781 } 782 783 784 void MacroAssembler::Push(const CPURegister& src0, const CPURegister& src1, 785 const CPURegister& src2, const CPURegister& src3, 786 const CPURegister& src4, const CPURegister& src5, 787 const CPURegister& src6, const CPURegister& src7) { 788 ASSERT(AreSameSizeAndType(src0, src1, src2, src3, src4, src5, src6, src7)); 789 790 int count = 5 + src5.IsValid() + src6.IsValid() + src6.IsValid(); 791 int size = src0.SizeInBytes(); 792 793 PushPreamble(count, size); 794 PushHelper(4, size, src0, src1, src2, src3); 795 PushHelper(count - 4, size, src4, src5, src6, src7); 796 } 797 798 799 void MacroAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1, 800 const CPURegister& dst2, const CPURegister& dst3) { 801 // It is not valid to pop into the same register more than once in one 802 // instruction, not even into the zero register. 803 ASSERT(!AreAliased(dst0, dst1, dst2, dst3)); 804 ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); 805 ASSERT(dst0.IsValid()); 806 807 int count = 1 + dst1.IsValid() + dst2.IsValid() + dst3.IsValid(); 808 int size = dst0.SizeInBytes(); 809 810 PopHelper(count, size, dst0, dst1, dst2, dst3); 811 PopPostamble(count, size); 812 } 813 814 815 void MacroAssembler::PushPopQueue::PushQueued( 816 PreambleDirective preamble_directive) { 817 if (queued_.empty()) return; 818 819 if (preamble_directive == WITH_PREAMBLE) { 820 masm_->PushPreamble(size_); 821 } 822 823 int count = queued_.size(); 824 int index = 0; 825 while (index < count) { 826 // PushHelper can only handle registers with the same size and type, and it 827 // can handle only four at a time. Batch them up accordingly. 828 CPURegister batch[4] = {NoReg, NoReg, NoReg, NoReg}; 829 int batch_index = 0; 830 do { 831 batch[batch_index++] = queued_[index++]; 832 } while ((batch_index < 4) && (index < count) && 833 batch[0].IsSameSizeAndType(queued_[index])); 834 835 masm_->PushHelper(batch_index, batch[0].SizeInBytes(), 836 batch[0], batch[1], batch[2], batch[3]); 837 } 838 839 queued_.clear(); 840 } 841 842 843 void MacroAssembler::PushPopQueue::PopQueued() { 844 if (queued_.empty()) return; 845 846 int count = queued_.size(); 847 int index = 0; 848 while (index < count) { 849 // PopHelper can only handle registers with the same size and type, and it 850 // can handle only four at a time. Batch them up accordingly. 851 CPURegister batch[4] = {NoReg, NoReg, NoReg, NoReg}; 852 int batch_index = 0; 853 do { 854 batch[batch_index++] = queued_[index++]; 855 } while ((batch_index < 4) && (index < count) && 856 batch[0].IsSameSizeAndType(queued_[index])); 857 858 masm_->PopHelper(batch_index, batch[0].SizeInBytes(), 859 batch[0], batch[1], batch[2], batch[3]); 860 } 861 862 masm_->PopPostamble(size_); 863 queued_.clear(); 864 } 865 866 867 void MacroAssembler::PushCPURegList(CPURegList registers) { 868 int size = registers.RegisterSizeInBytes(); 869 870 PushPreamble(registers.Count(), size); 871 // Push up to four registers at a time because if the current stack pointer is 872 // csp and reg_size is 32, registers must be pushed in blocks of four in order 873 // to maintain the 16-byte alignment for csp. 874 while (!registers.IsEmpty()) { 875 int count_before = registers.Count(); 876 const CPURegister& src0 = registers.PopHighestIndex(); 877 const CPURegister& src1 = registers.PopHighestIndex(); 878 const CPURegister& src2 = registers.PopHighestIndex(); 879 const CPURegister& src3 = registers.PopHighestIndex(); 880 int count = count_before - registers.Count(); 881 PushHelper(count, size, src0, src1, src2, src3); 882 } 883 } 884 885 886 void MacroAssembler::PopCPURegList(CPURegList registers) { 887 int size = registers.RegisterSizeInBytes(); 888 889 // Pop up to four registers at a time because if the current stack pointer is 890 // csp and reg_size is 32, registers must be pushed in blocks of four in 891 // order to maintain the 16-byte alignment for csp. 892 while (!registers.IsEmpty()) { 893 int count_before = registers.Count(); 894 const CPURegister& dst0 = registers.PopLowestIndex(); 895 const CPURegister& dst1 = registers.PopLowestIndex(); 896 const CPURegister& dst2 = registers.PopLowestIndex(); 897 const CPURegister& dst3 = registers.PopLowestIndex(); 898 int count = count_before - registers.Count(); 899 PopHelper(count, size, dst0, dst1, dst2, dst3); 900 } 901 PopPostamble(registers.Count(), size); 902 } 903 904 905 void MacroAssembler::PushMultipleTimes(CPURegister src, int count) { 906 int size = src.SizeInBytes(); 907 908 PushPreamble(count, size); 909 910 if (FLAG_optimize_for_size && count > 8) { 911 UseScratchRegisterScope temps(this); 912 Register temp = temps.AcquireX(); 913 914 Label loop; 915 __ Mov(temp, count / 2); 916 __ Bind(&loop); 917 PushHelper(2, size, src, src, NoReg, NoReg); 918 __ Subs(temp, temp, 1); 919 __ B(ne, &loop); 920 921 count %= 2; 922 } 923 924 // Push up to four registers at a time if possible because if the current 925 // stack pointer is csp and the register size is 32, registers must be pushed 926 // in blocks of four in order to maintain the 16-byte alignment for csp. 927 while (count >= 4) { 928 PushHelper(4, size, src, src, src, src); 929 count -= 4; 930 } 931 if (count >= 2) { 932 PushHelper(2, size, src, src, NoReg, NoReg); 933 count -= 2; 934 } 935 if (count == 1) { 936 PushHelper(1, size, src, NoReg, NoReg, NoReg); 937 count -= 1; 938 } 939 ASSERT(count == 0); 940 } 941 942 943 void MacroAssembler::PushMultipleTimes(CPURegister src, Register count) { 944 PushPreamble(Operand(count, UXTW, WhichPowerOf2(src.SizeInBytes()))); 945 946 UseScratchRegisterScope temps(this); 947 Register temp = temps.AcquireSameSizeAs(count); 948 949 if (FLAG_optimize_for_size) { 950 Label loop, done; 951 952 Subs(temp, count, 1); 953 B(mi, &done); 954 955 // Push all registers individually, to save code size. 956 Bind(&loop); 957 Subs(temp, temp, 1); 958 PushHelper(1, src.SizeInBytes(), src, NoReg, NoReg, NoReg); 959 B(pl, &loop); 960 961 Bind(&done); 962 } else { 963 Label loop, leftover2, leftover1, done; 964 965 Subs(temp, count, 4); 966 B(mi, &leftover2); 967 968 // Push groups of four first. 969 Bind(&loop); 970 Subs(temp, temp, 4); 971 PushHelper(4, src.SizeInBytes(), src, src, src, src); 972 B(pl, &loop); 973 974 // Push groups of two. 975 Bind(&leftover2); 976 Tbz(count, 1, &leftover1); 977 PushHelper(2, src.SizeInBytes(), src, src, NoReg, NoReg); 978 979 // Push the last one (if required). 980 Bind(&leftover1); 981 Tbz(count, 0, &done); 982 PushHelper(1, src.SizeInBytes(), src, NoReg, NoReg, NoReg); 983 984 Bind(&done); 985 } 986 } 987 988 989 void MacroAssembler::PushHelper(int count, int size, 990 const CPURegister& src0, 991 const CPURegister& src1, 992 const CPURegister& src2, 993 const CPURegister& src3) { 994 // Ensure that we don't unintentially modify scratch or debug registers. 995 InstructionAccurateScope scope(this); 996 997 ASSERT(AreSameSizeAndType(src0, src1, src2, src3)); 998 ASSERT(size == src0.SizeInBytes()); 999 1000 // When pushing multiple registers, the store order is chosen such that 1001 // Push(a, b) is equivalent to Push(a) followed by Push(b). 1002 switch (count) { 1003 case 1: 1004 ASSERT(src1.IsNone() && src2.IsNone() && src3.IsNone()); 1005 str(src0, MemOperand(StackPointer(), -1 * size, PreIndex)); 1006 break; 1007 case 2: 1008 ASSERT(src2.IsNone() && src3.IsNone()); 1009 stp(src1, src0, MemOperand(StackPointer(), -2 * size, PreIndex)); 1010 break; 1011 case 3: 1012 ASSERT(src3.IsNone()); 1013 stp(src2, src1, MemOperand(StackPointer(), -3 * size, PreIndex)); 1014 str(src0, MemOperand(StackPointer(), 2 * size)); 1015 break; 1016 case 4: 1017 // Skip over 4 * size, then fill in the gap. This allows four W registers 1018 // to be pushed using csp, whilst maintaining 16-byte alignment for csp 1019 // at all times. 1020 stp(src3, src2, MemOperand(StackPointer(), -4 * size, PreIndex)); 1021 stp(src1, src0, MemOperand(StackPointer(), 2 * size)); 1022 break; 1023 default: 1024 UNREACHABLE(); 1025 } 1026 } 1027 1028 1029 void MacroAssembler::PopHelper(int count, int size, 1030 const CPURegister& dst0, 1031 const CPURegister& dst1, 1032 const CPURegister& dst2, 1033 const CPURegister& dst3) { 1034 // Ensure that we don't unintentially modify scratch or debug registers. 1035 InstructionAccurateScope scope(this); 1036 1037 ASSERT(AreSameSizeAndType(dst0, dst1, dst2, dst3)); 1038 ASSERT(size == dst0.SizeInBytes()); 1039 1040 // When popping multiple registers, the load order is chosen such that 1041 // Pop(a, b) is equivalent to Pop(a) followed by Pop(b). 1042 switch (count) { 1043 case 1: 1044 ASSERT(dst1.IsNone() && dst2.IsNone() && dst3.IsNone()); 1045 ldr(dst0, MemOperand(StackPointer(), 1 * size, PostIndex)); 1046 break; 1047 case 2: 1048 ASSERT(dst2.IsNone() && dst3.IsNone()); 1049 ldp(dst0, dst1, MemOperand(StackPointer(), 2 * size, PostIndex)); 1050 break; 1051 case 3: 1052 ASSERT(dst3.IsNone()); 1053 ldr(dst2, MemOperand(StackPointer(), 2 * size)); 1054 ldp(dst0, dst1, MemOperand(StackPointer(), 3 * size, PostIndex)); 1055 break; 1056 case 4: 1057 // Load the higher addresses first, then load the lower addresses and 1058 // skip the whole block in the second instruction. This allows four W 1059 // registers to be popped using csp, whilst maintaining 16-byte alignment 1060 // for csp at all times. 1061 ldp(dst2, dst3, MemOperand(StackPointer(), 2 * size)); 1062 ldp(dst0, dst1, MemOperand(StackPointer(), 4 * size, PostIndex)); 1063 break; 1064 default: 1065 UNREACHABLE(); 1066 } 1067 } 1068 1069 1070 void MacroAssembler::PushPreamble(Operand total_size) { 1071 if (csp.Is(StackPointer())) { 1072 // If the current stack pointer is csp, then it must be aligned to 16 bytes 1073 // on entry and the total size of the specified registers must also be a 1074 // multiple of 16 bytes. 1075 if (total_size.IsImmediate()) { 1076 ASSERT((total_size.ImmediateValue() % 16) == 0); 1077 } 1078 1079 // Don't check access size for non-immediate sizes. It's difficult to do 1080 // well, and it will be caught by hardware (or the simulator) anyway. 1081 } else { 1082 // Even if the current stack pointer is not the system stack pointer (csp), 1083 // the system stack pointer will still be modified in order to comply with 1084 // ABI rules about accessing memory below the system stack pointer. 1085 BumpSystemStackPointer(total_size); 1086 } 1087 } 1088 1089 1090 void MacroAssembler::PopPostamble(Operand total_size) { 1091 if (csp.Is(StackPointer())) { 1092 // If the current stack pointer is csp, then it must be aligned to 16 bytes 1093 // on entry and the total size of the specified registers must also be a 1094 // multiple of 16 bytes. 1095 if (total_size.IsImmediate()) { 1096 ASSERT((total_size.ImmediateValue() % 16) == 0); 1097 } 1098 1099 // Don't check access size for non-immediate sizes. It's difficult to do 1100 // well, and it will be caught by hardware (or the simulator) anyway. 1101 } else if (emit_debug_code()) { 1102 // It is safe to leave csp where it is when unwinding the JavaScript stack, 1103 // but if we keep it matching StackPointer, the simulator can detect memory 1104 // accesses in the now-free part of the stack. 1105 SyncSystemStackPointer(); 1106 } 1107 } 1108 1109 1110 void MacroAssembler::Poke(const CPURegister& src, const Operand& offset) { 1111 if (offset.IsImmediate()) { 1112 ASSERT(offset.ImmediateValue() >= 0); 1113 } else if (emit_debug_code()) { 1114 Cmp(xzr, offset); 1115 Check(le, kStackAccessBelowStackPointer); 1116 } 1117 1118 Str(src, MemOperand(StackPointer(), offset)); 1119 } 1120 1121 1122 void MacroAssembler::Peek(const CPURegister& dst, const Operand& offset) { 1123 if (offset.IsImmediate()) { 1124 ASSERT(offset.ImmediateValue() >= 0); 1125 } else if (emit_debug_code()) { 1126 Cmp(xzr, offset); 1127 Check(le, kStackAccessBelowStackPointer); 1128 } 1129 1130 Ldr(dst, MemOperand(StackPointer(), offset)); 1131 } 1132 1133 1134 void MacroAssembler::PokePair(const CPURegister& src1, 1135 const CPURegister& src2, 1136 int offset) { 1137 ASSERT(AreSameSizeAndType(src1, src2)); 1138 ASSERT((offset >= 0) && ((offset % src1.SizeInBytes()) == 0)); 1139 Stp(src1, src2, MemOperand(StackPointer(), offset)); 1140 } 1141 1142 1143 void MacroAssembler::PeekPair(const CPURegister& dst1, 1144 const CPURegister& dst2, 1145 int offset) { 1146 ASSERT(AreSameSizeAndType(dst1, dst2)); 1147 ASSERT((offset >= 0) && ((offset % dst1.SizeInBytes()) == 0)); 1148 Ldp(dst1, dst2, MemOperand(StackPointer(), offset)); 1149 } 1150 1151 1152 void MacroAssembler::PushCalleeSavedRegisters() { 1153 // Ensure that the macro-assembler doesn't use any scratch registers. 1154 InstructionAccurateScope scope(this); 1155 1156 // This method must not be called unless the current stack pointer is the 1157 // system stack pointer (csp). 1158 ASSERT(csp.Is(StackPointer())); 1159 1160 MemOperand tos(csp, -2 * kXRegSize, PreIndex); 1161 1162 stp(d14, d15, tos); 1163 stp(d12, d13, tos); 1164 stp(d10, d11, tos); 1165 stp(d8, d9, tos); 1166 1167 stp(x29, x30, tos); 1168 stp(x27, x28, tos); // x28 = jssp 1169 stp(x25, x26, tos); 1170 stp(x23, x24, tos); 1171 stp(x21, x22, tos); 1172 stp(x19, x20, tos); 1173 } 1174 1175 1176 void MacroAssembler::PopCalleeSavedRegisters() { 1177 // Ensure that the macro-assembler doesn't use any scratch registers. 1178 InstructionAccurateScope scope(this); 1179 1180 // This method must not be called unless the current stack pointer is the 1181 // system stack pointer (csp). 1182 ASSERT(csp.Is(StackPointer())); 1183 1184 MemOperand tos(csp, 2 * kXRegSize, PostIndex); 1185 1186 ldp(x19, x20, tos); 1187 ldp(x21, x22, tos); 1188 ldp(x23, x24, tos); 1189 ldp(x25, x26, tos); 1190 ldp(x27, x28, tos); // x28 = jssp 1191 ldp(x29, x30, tos); 1192 1193 ldp(d8, d9, tos); 1194 ldp(d10, d11, tos); 1195 ldp(d12, d13, tos); 1196 ldp(d14, d15, tos); 1197 } 1198 1199 1200 void MacroAssembler::AssertStackConsistency() { 1201 // Avoid emitting code when !use_real_abort() since non-real aborts cause too 1202 // much code to be generated. 1203 if (emit_debug_code() && use_real_aborts()) { 1204 if (csp.Is(StackPointer()) || CpuFeatures::IsSupported(ALWAYS_ALIGN_CSP)) { 1205 // Always check the alignment of csp if ALWAYS_ALIGN_CSP is true. We 1206 // can't check the alignment of csp without using a scratch register (or 1207 // clobbering the flags), but the processor (or simulator) will abort if 1208 // it is not properly aligned during a load. 1209 ldr(xzr, MemOperand(csp, 0)); 1210 } 1211 if (FLAG_enable_slow_asserts && !csp.Is(StackPointer())) { 1212 Label ok; 1213 // Check that csp <= StackPointer(), preserving all registers and NZCV. 1214 sub(StackPointer(), csp, StackPointer()); 1215 cbz(StackPointer(), &ok); // Ok if csp == StackPointer(). 1216 tbnz(StackPointer(), kXSignBit, &ok); // Ok if csp < StackPointer(). 1217 1218 // Avoid generating AssertStackConsistency checks for the Push in Abort. 1219 { DontEmitDebugCodeScope dont_emit_debug_code_scope(this); 1220 Abort(kTheCurrentStackPointerIsBelowCsp); 1221 } 1222 1223 bind(&ok); 1224 // Restore StackPointer(). 1225 sub(StackPointer(), csp, StackPointer()); 1226 } 1227 } 1228 } 1229 1230 1231 void MacroAssembler::AssertFPCRState(Register fpcr) { 1232 if (emit_debug_code()) { 1233 Label unexpected_mode, done; 1234 UseScratchRegisterScope temps(this); 1235 if (fpcr.IsNone()) { 1236 fpcr = temps.AcquireX(); 1237 Mrs(fpcr, FPCR); 1238 } 1239 1240 // Settings overridden by ConfiugreFPCR(): 1241 // - Assert that default-NaN mode is set. 1242 Tbz(fpcr, DN_offset, &unexpected_mode); 1243 1244 // Settings left to their default values: 1245 // - Assert that flush-to-zero is not set. 1246 Tbnz(fpcr, FZ_offset, &unexpected_mode); 1247 // - Assert that the rounding mode is nearest-with-ties-to-even. 1248 STATIC_ASSERT(FPTieEven == 0); 1249 Tst(fpcr, RMode_mask); 1250 B(eq, &done); 1251 1252 Bind(&unexpected_mode); 1253 Abort(kUnexpectedFPCRMode); 1254 1255 Bind(&done); 1256 } 1257 } 1258 1259 1260 void MacroAssembler::ConfigureFPCR() { 1261 UseScratchRegisterScope temps(this); 1262 Register fpcr = temps.AcquireX(); 1263 Mrs(fpcr, FPCR); 1264 1265 // If necessary, enable default-NaN mode. The default values of the other FPCR 1266 // options should be suitable, and AssertFPCRState will verify that. 1267 Label no_write_required; 1268 Tbnz(fpcr, DN_offset, &no_write_required); 1269 1270 Orr(fpcr, fpcr, DN_mask); 1271 Msr(FPCR, fpcr); 1272 1273 Bind(&no_write_required); 1274 AssertFPCRState(fpcr); 1275 } 1276 1277 1278 void MacroAssembler::CanonicalizeNaN(const FPRegister& dst, 1279 const FPRegister& src) { 1280 AssertFPCRState(); 1281 1282 // With DN=1 and RMode=FPTieEven, subtracting 0.0 preserves all inputs except 1283 // for NaNs, which become the default NaN. We use fsub rather than fadd 1284 // because sub preserves -0.0 inputs: -0.0 + 0.0 = 0.0, but -0.0 - 0.0 = -0.0. 1285 Fsub(dst, src, fp_zero); 1286 } 1287 1288 1289 void MacroAssembler::LoadRoot(CPURegister destination, 1290 Heap::RootListIndex index) { 1291 // TODO(jbramley): Most root values are constants, and can be synthesized 1292 // without a load. Refer to the ARM back end for details. 1293 Ldr(destination, MemOperand(root, index << kPointerSizeLog2)); 1294 } 1295 1296 1297 void MacroAssembler::StoreRoot(Register source, 1298 Heap::RootListIndex index) { 1299 Str(source, MemOperand(root, index << kPointerSizeLog2)); 1300 } 1301 1302 1303 void MacroAssembler::LoadTrueFalseRoots(Register true_root, 1304 Register false_root) { 1305 STATIC_ASSERT((Heap::kTrueValueRootIndex + 1) == Heap::kFalseValueRootIndex); 1306 Ldp(true_root, false_root, 1307 MemOperand(root, Heap::kTrueValueRootIndex << kPointerSizeLog2)); 1308 } 1309 1310 1311 void MacroAssembler::LoadHeapObject(Register result, 1312 Handle<HeapObject> object) { 1313 AllowDeferredHandleDereference using_raw_address; 1314 if (isolate()->heap()->InNewSpace(*object)) { 1315 Handle<Cell> cell = isolate()->factory()->NewCell(object); 1316 Mov(result, Operand(cell)); 1317 Ldr(result, FieldMemOperand(result, Cell::kValueOffset)); 1318 } else { 1319 Mov(result, Operand(object)); 1320 } 1321 } 1322 1323 1324 void MacroAssembler::LoadInstanceDescriptors(Register map, 1325 Register descriptors) { 1326 Ldr(descriptors, FieldMemOperand(map, Map::kDescriptorsOffset)); 1327 } 1328 1329 1330 void MacroAssembler::NumberOfOwnDescriptors(Register dst, Register map) { 1331 Ldr(dst, FieldMemOperand(map, Map::kBitField3Offset)); 1332 DecodeField<Map::NumberOfOwnDescriptorsBits>(dst); 1333 } 1334 1335 1336 void MacroAssembler::EnumLengthUntagged(Register dst, Register map) { 1337 STATIC_ASSERT(Map::EnumLengthBits::kShift == 0); 1338 Ldrsw(dst, FieldMemOperand(map, Map::kBitField3Offset)); 1339 And(dst, dst, Map::EnumLengthBits::kMask); 1340 } 1341 1342 1343 void MacroAssembler::EnumLengthSmi(Register dst, Register map) { 1344 EnumLengthUntagged(dst, map); 1345 SmiTag(dst, dst); 1346 } 1347 1348 1349 void MacroAssembler::CheckEnumCache(Register object, 1350 Register null_value, 1351 Register scratch0, 1352 Register scratch1, 1353 Register scratch2, 1354 Register scratch3, 1355 Label* call_runtime) { 1356 ASSERT(!AreAliased(object, null_value, scratch0, scratch1, scratch2, 1357 scratch3)); 1358 1359 Register empty_fixed_array_value = scratch0; 1360 Register current_object = scratch1; 1361 1362 LoadRoot(empty_fixed_array_value, Heap::kEmptyFixedArrayRootIndex); 1363 Label next, start; 1364 1365 Mov(current_object, object); 1366 1367 // Check if the enum length field is properly initialized, indicating that 1368 // there is an enum cache. 1369 Register map = scratch2; 1370 Register enum_length = scratch3; 1371 Ldr(map, FieldMemOperand(current_object, HeapObject::kMapOffset)); 1372 1373 EnumLengthUntagged(enum_length, map); 1374 Cmp(enum_length, kInvalidEnumCacheSentinel); 1375 B(eq, call_runtime); 1376 1377 B(&start); 1378 1379 Bind(&next); 1380 Ldr(map, FieldMemOperand(current_object, HeapObject::kMapOffset)); 1381 1382 // For all objects but the receiver, check that the cache is empty. 1383 EnumLengthUntagged(enum_length, map); 1384 Cbnz(enum_length, call_runtime); 1385 1386 Bind(&start); 1387 1388 // Check that there are no elements. Register current_object contains the 1389 // current JS object we've reached through the prototype chain. 1390 Label no_elements; 1391 Ldr(current_object, FieldMemOperand(current_object, 1392 JSObject::kElementsOffset)); 1393 Cmp(current_object, empty_fixed_array_value); 1394 B(eq, &no_elements); 1395 1396 // Second chance, the object may be using the empty slow element dictionary. 1397 CompareRoot(current_object, Heap::kEmptySlowElementDictionaryRootIndex); 1398 B(ne, call_runtime); 1399 1400 Bind(&no_elements); 1401 Ldr(current_object, FieldMemOperand(map, Map::kPrototypeOffset)); 1402 Cmp(current_object, null_value); 1403 B(ne, &next); 1404 } 1405 1406 1407 void MacroAssembler::TestJSArrayForAllocationMemento(Register receiver, 1408 Register scratch1, 1409 Register scratch2, 1410 Label* no_memento_found) { 1411 ExternalReference new_space_start = 1412 ExternalReference::new_space_start(isolate()); 1413 ExternalReference new_space_allocation_top = 1414 ExternalReference::new_space_allocation_top_address(isolate()); 1415 1416 Add(scratch1, receiver, 1417 JSArray::kSize + AllocationMemento::kSize - kHeapObjectTag); 1418 Cmp(scratch1, new_space_start); 1419 B(lt, no_memento_found); 1420 1421 Mov(scratch2, new_space_allocation_top); 1422 Ldr(scratch2, MemOperand(scratch2)); 1423 Cmp(scratch1, scratch2); 1424 B(gt, no_memento_found); 1425 1426 Ldr(scratch1, MemOperand(scratch1, -AllocationMemento::kSize)); 1427 Cmp(scratch1, 1428 Operand(isolate()->factory()->allocation_memento_map())); 1429 } 1430 1431 1432 void MacroAssembler::JumpToHandlerEntry(Register exception, 1433 Register object, 1434 Register state, 1435 Register scratch1, 1436 Register scratch2) { 1437 // Handler expects argument in x0. 1438 ASSERT(exception.Is(x0)); 1439 1440 // Compute the handler entry address and jump to it. The handler table is 1441 // a fixed array of (smi-tagged) code offsets. 1442 Ldr(scratch1, FieldMemOperand(object, Code::kHandlerTableOffset)); 1443 Add(scratch1, scratch1, FixedArray::kHeaderSize - kHeapObjectTag); 1444 STATIC_ASSERT(StackHandler::kKindWidth < kPointerSizeLog2); 1445 Lsr(scratch2, state, StackHandler::kKindWidth); 1446 Ldr(scratch2, MemOperand(scratch1, scratch2, LSL, kPointerSizeLog2)); 1447 Add(scratch1, object, Code::kHeaderSize - kHeapObjectTag); 1448 Add(scratch1, scratch1, Operand::UntagSmi(scratch2)); 1449 Br(scratch1); 1450 } 1451 1452 1453 void MacroAssembler::InNewSpace(Register object, 1454 Condition cond, 1455 Label* branch) { 1456 ASSERT(cond == eq || cond == ne); 1457 UseScratchRegisterScope temps(this); 1458 Register temp = temps.AcquireX(); 1459 And(temp, object, ExternalReference::new_space_mask(isolate())); 1460 Cmp(temp, ExternalReference::new_space_start(isolate())); 1461 B(cond, branch); 1462 } 1463 1464 1465 void MacroAssembler::Throw(Register value, 1466 Register scratch1, 1467 Register scratch2, 1468 Register scratch3, 1469 Register scratch4) { 1470 // Adjust this code if not the case. 1471 STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); 1472 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); 1473 STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); 1474 STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); 1475 STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); 1476 STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); 1477 1478 // The handler expects the exception in x0. 1479 ASSERT(value.Is(x0)); 1480 1481 // Drop the stack pointer to the top of the top handler. 1482 ASSERT(jssp.Is(StackPointer())); 1483 Mov(scratch1, Operand(ExternalReference(Isolate::kHandlerAddress, 1484 isolate()))); 1485 Ldr(jssp, MemOperand(scratch1)); 1486 // Restore the next handler. 1487 Pop(scratch2); 1488 Str(scratch2, MemOperand(scratch1)); 1489 1490 // Get the code object and state. Restore the context and frame pointer. 1491 Register object = scratch1; 1492 Register state = scratch2; 1493 Pop(object, state, cp, fp); 1494 1495 // If the handler is a JS frame, restore the context to the frame. 1496 // (kind == ENTRY) == (fp == 0) == (cp == 0), so we could test either fp 1497 // or cp. 1498 Label not_js_frame; 1499 Cbz(cp, ¬_js_frame); 1500 Str(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); 1501 Bind(¬_js_frame); 1502 1503 JumpToHandlerEntry(value, object, state, scratch3, scratch4); 1504 } 1505 1506 1507 void MacroAssembler::ThrowUncatchable(Register value, 1508 Register scratch1, 1509 Register scratch2, 1510 Register scratch3, 1511 Register scratch4) { 1512 // Adjust this code if not the case. 1513 STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); 1514 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); 1515 STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); 1516 STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); 1517 STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); 1518 STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); 1519 1520 // The handler expects the exception in x0. 1521 ASSERT(value.Is(x0)); 1522 1523 // Drop the stack pointer to the top of the top stack handler. 1524 ASSERT(jssp.Is(StackPointer())); 1525 Mov(scratch1, Operand(ExternalReference(Isolate::kHandlerAddress, 1526 isolate()))); 1527 Ldr(jssp, MemOperand(scratch1)); 1528 1529 // Unwind the handlers until the ENTRY handler is found. 1530 Label fetch_next, check_kind; 1531 B(&check_kind); 1532 Bind(&fetch_next); 1533 Peek(jssp, StackHandlerConstants::kNextOffset); 1534 1535 Bind(&check_kind); 1536 STATIC_ASSERT(StackHandler::JS_ENTRY == 0); 1537 Peek(scratch2, StackHandlerConstants::kStateOffset); 1538 TestAndBranchIfAnySet(scratch2, StackHandler::KindField::kMask, &fetch_next); 1539 1540 // Set the top handler address to next handler past the top ENTRY handler. 1541 Pop(scratch2); 1542 Str(scratch2, MemOperand(scratch1)); 1543 1544 // Get the code object and state. Clear the context and frame pointer (0 was 1545 // saved in the handler). 1546 Register object = scratch1; 1547 Register state = scratch2; 1548 Pop(object, state, cp, fp); 1549 1550 JumpToHandlerEntry(value, object, state, scratch3, scratch4); 1551 } 1552 1553 1554 void MacroAssembler::SmiAbs(const Register& smi, Label* slow) { 1555 ASSERT(smi.Is64Bits()); 1556 Abs(smi, smi, slow); 1557 } 1558 1559 1560 void MacroAssembler::AssertSmi(Register object, BailoutReason reason) { 1561 if (emit_debug_code()) { 1562 STATIC_ASSERT(kSmiTag == 0); 1563 Tst(object, kSmiTagMask); 1564 Check(eq, reason); 1565 } 1566 } 1567 1568 1569 void MacroAssembler::AssertNotSmi(Register object, BailoutReason reason) { 1570 if (emit_debug_code()) { 1571 STATIC_ASSERT(kSmiTag == 0); 1572 Tst(object, kSmiTagMask); 1573 Check(ne, reason); 1574 } 1575 } 1576 1577 1578 void MacroAssembler::AssertName(Register object) { 1579 if (emit_debug_code()) { 1580 AssertNotSmi(object, kOperandIsASmiAndNotAName); 1581 1582 UseScratchRegisterScope temps(this); 1583 Register temp = temps.AcquireX(); 1584 1585 Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); 1586 CompareInstanceType(temp, temp, LAST_NAME_TYPE); 1587 Check(ls, kOperandIsNotAName); 1588 } 1589 } 1590 1591 1592 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object, 1593 Register scratch) { 1594 if (emit_debug_code()) { 1595 Label done_checking; 1596 AssertNotSmi(object); 1597 JumpIfRoot(object, Heap::kUndefinedValueRootIndex, &done_checking); 1598 Ldr(scratch, FieldMemOperand(object, HeapObject::kMapOffset)); 1599 CompareRoot(scratch, Heap::kAllocationSiteMapRootIndex); 1600 Assert(eq, kExpectedUndefinedOrCell); 1601 Bind(&done_checking); 1602 } 1603 } 1604 1605 1606 void MacroAssembler::AssertString(Register object) { 1607 if (emit_debug_code()) { 1608 UseScratchRegisterScope temps(this); 1609 Register temp = temps.AcquireX(); 1610 STATIC_ASSERT(kSmiTag == 0); 1611 Tst(object, kSmiTagMask); 1612 Check(ne, kOperandIsASmiAndNotAString); 1613 Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); 1614 CompareInstanceType(temp, temp, FIRST_NONSTRING_TYPE); 1615 Check(lo, kOperandIsNotAString); 1616 } 1617 } 1618 1619 1620 void MacroAssembler::CallStub(CodeStub* stub, TypeFeedbackId ast_id) { 1621 ASSERT(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. 1622 Call(stub->GetCode(), RelocInfo::CODE_TARGET, ast_id); 1623 } 1624 1625 1626 void MacroAssembler::TailCallStub(CodeStub* stub) { 1627 Jump(stub->GetCode(), RelocInfo::CODE_TARGET); 1628 } 1629 1630 1631 void MacroAssembler::CallRuntime(const Runtime::Function* f, 1632 int num_arguments, 1633 SaveFPRegsMode save_doubles) { 1634 // All arguments must be on the stack before this function is called. 1635 // x0 holds the return value after the call. 1636 1637 // Check that the number of arguments matches what the function expects. 1638 // If f->nargs is -1, the function can accept a variable number of arguments. 1639 CHECK(f->nargs < 0 || f->nargs == num_arguments); 1640 1641 // Place the necessary arguments. 1642 Mov(x0, num_arguments); 1643 Mov(x1, ExternalReference(f, isolate())); 1644 1645 CEntryStub stub(isolate(), 1, save_doubles); 1646 CallStub(&stub); 1647 } 1648 1649 1650 static int AddressOffset(ExternalReference ref0, ExternalReference ref1) { 1651 return ref0.address() - ref1.address(); 1652 } 1653 1654 1655 void MacroAssembler::CallApiFunctionAndReturn( 1656 Register function_address, 1657 ExternalReference thunk_ref, 1658 int stack_space, 1659 int spill_offset, 1660 MemOperand return_value_operand, 1661 MemOperand* context_restore_operand) { 1662 ASM_LOCATION("CallApiFunctionAndReturn"); 1663 ExternalReference next_address = 1664 ExternalReference::handle_scope_next_address(isolate()); 1665 const int kNextOffset = 0; 1666 const int kLimitOffset = AddressOffset( 1667 ExternalReference::handle_scope_limit_address(isolate()), 1668 next_address); 1669 const int kLevelOffset = AddressOffset( 1670 ExternalReference::handle_scope_level_address(isolate()), 1671 next_address); 1672 1673 ASSERT(function_address.is(x1) || function_address.is(x2)); 1674 1675 Label profiler_disabled; 1676 Label end_profiler_check; 1677 Mov(x10, ExternalReference::is_profiling_address(isolate())); 1678 Ldrb(w10, MemOperand(x10)); 1679 Cbz(w10, &profiler_disabled); 1680 Mov(x3, thunk_ref); 1681 B(&end_profiler_check); 1682 1683 Bind(&profiler_disabled); 1684 Mov(x3, function_address); 1685 Bind(&end_profiler_check); 1686 1687 // Save the callee-save registers we are going to use. 1688 // TODO(all): Is this necessary? ARM doesn't do it. 1689 STATIC_ASSERT(kCallApiFunctionSpillSpace == 4); 1690 Poke(x19, (spill_offset + 0) * kXRegSize); 1691 Poke(x20, (spill_offset + 1) * kXRegSize); 1692 Poke(x21, (spill_offset + 2) * kXRegSize); 1693 Poke(x22, (spill_offset + 3) * kXRegSize); 1694 1695 // Allocate HandleScope in callee-save registers. 1696 // We will need to restore the HandleScope after the call to the API function, 1697 // by allocating it in callee-save registers they will be preserved by C code. 1698 Register handle_scope_base = x22; 1699 Register next_address_reg = x19; 1700 Register limit_reg = x20; 1701 Register level_reg = w21; 1702 1703 Mov(handle_scope_base, next_address); 1704 Ldr(next_address_reg, MemOperand(handle_scope_base, kNextOffset)); 1705 Ldr(limit_reg, MemOperand(handle_scope_base, kLimitOffset)); 1706 Ldr(level_reg, MemOperand(handle_scope_base, kLevelOffset)); 1707 Add(level_reg, level_reg, 1); 1708 Str(level_reg, MemOperand(handle_scope_base, kLevelOffset)); 1709 1710 if (FLAG_log_timer_events) { 1711 FrameScope frame(this, StackFrame::MANUAL); 1712 PushSafepointRegisters(); 1713 Mov(x0, ExternalReference::isolate_address(isolate())); 1714 CallCFunction(ExternalReference::log_enter_external_function(isolate()), 1); 1715 PopSafepointRegisters(); 1716 } 1717 1718 // Native call returns to the DirectCEntry stub which redirects to the 1719 // return address pushed on stack (could have moved after GC). 1720 // DirectCEntry stub itself is generated early and never moves. 1721 DirectCEntryStub stub(isolate()); 1722 stub.GenerateCall(this, x3); 1723 1724 if (FLAG_log_timer_events) { 1725 FrameScope frame(this, StackFrame::MANUAL); 1726 PushSafepointRegisters(); 1727 Mov(x0, ExternalReference::isolate_address(isolate())); 1728 CallCFunction(ExternalReference::log_leave_external_function(isolate()), 1); 1729 PopSafepointRegisters(); 1730 } 1731 1732 Label promote_scheduled_exception; 1733 Label exception_handled; 1734 Label delete_allocated_handles; 1735 Label leave_exit_frame; 1736 Label return_value_loaded; 1737 1738 // Load value from ReturnValue. 1739 Ldr(x0, return_value_operand); 1740 Bind(&return_value_loaded); 1741 // No more valid handles (the result handle was the last one). Restore 1742 // previous handle scope. 1743 Str(next_address_reg, MemOperand(handle_scope_base, kNextOffset)); 1744 if (emit_debug_code()) { 1745 Ldr(w1, MemOperand(handle_scope_base, kLevelOffset)); 1746 Cmp(w1, level_reg); 1747 Check(eq, kUnexpectedLevelAfterReturnFromApiCall); 1748 } 1749 Sub(level_reg, level_reg, 1); 1750 Str(level_reg, MemOperand(handle_scope_base, kLevelOffset)); 1751 Ldr(x1, MemOperand(handle_scope_base, kLimitOffset)); 1752 Cmp(limit_reg, x1); 1753 B(ne, &delete_allocated_handles); 1754 1755 Bind(&leave_exit_frame); 1756 // Restore callee-saved registers. 1757 Peek(x19, (spill_offset + 0) * kXRegSize); 1758 Peek(x20, (spill_offset + 1) * kXRegSize); 1759 Peek(x21, (spill_offset + 2) * kXRegSize); 1760 Peek(x22, (spill_offset + 3) * kXRegSize); 1761 1762 // Check if the function scheduled an exception. 1763 Mov(x5, ExternalReference::scheduled_exception_address(isolate())); 1764 Ldr(x5, MemOperand(x5)); 1765 JumpIfNotRoot(x5, Heap::kTheHoleValueRootIndex, &promote_scheduled_exception); 1766 Bind(&exception_handled); 1767 1768 bool restore_context = context_restore_operand != NULL; 1769 if (restore_context) { 1770 Ldr(cp, *context_restore_operand); 1771 } 1772 1773 LeaveExitFrame(false, x1, !restore_context); 1774 Drop(stack_space); 1775 Ret(); 1776 1777 Bind(&promote_scheduled_exception); 1778 { 1779 FrameScope frame(this, StackFrame::INTERNAL); 1780 CallExternalReference( 1781 ExternalReference( 1782 Runtime::kHiddenPromoteScheduledException, isolate()), 0); 1783 } 1784 B(&exception_handled); 1785 1786 // HandleScope limit has changed. Delete allocated extensions. 1787 Bind(&delete_allocated_handles); 1788 Str(limit_reg, MemOperand(handle_scope_base, kLimitOffset)); 1789 // Save the return value in a callee-save register. 1790 Register saved_result = x19; 1791 Mov(saved_result, x0); 1792 Mov(x0, ExternalReference::isolate_address(isolate())); 1793 CallCFunction( 1794 ExternalReference::delete_handle_scope_extensions(isolate()), 1); 1795 Mov(x0, saved_result); 1796 B(&leave_exit_frame); 1797 } 1798 1799 1800 void MacroAssembler::CallExternalReference(const ExternalReference& ext, 1801 int num_arguments) { 1802 Mov(x0, num_arguments); 1803 Mov(x1, ext); 1804 1805 CEntryStub stub(isolate(), 1); 1806 CallStub(&stub); 1807 } 1808 1809 1810 void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin) { 1811 Mov(x1, builtin); 1812 CEntryStub stub(isolate(), 1); 1813 Jump(stub.GetCode(), RelocInfo::CODE_TARGET); 1814 } 1815 1816 1817 void MacroAssembler::GetBuiltinFunction(Register target, 1818 Builtins::JavaScript id) { 1819 // Load the builtins object into target register. 1820 Ldr(target, GlobalObjectMemOperand()); 1821 Ldr(target, FieldMemOperand(target, GlobalObject::kBuiltinsOffset)); 1822 // Load the JavaScript builtin function from the builtins object. 1823 Ldr(target, FieldMemOperand(target, 1824 JSBuiltinsObject::OffsetOfFunctionWithId(id))); 1825 } 1826 1827 1828 void MacroAssembler::GetBuiltinEntry(Register target, 1829 Register function, 1830 Builtins::JavaScript id) { 1831 ASSERT(!AreAliased(target, function)); 1832 GetBuiltinFunction(function, id); 1833 // Load the code entry point from the builtins object. 1834 Ldr(target, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); 1835 } 1836 1837 1838 void MacroAssembler::InvokeBuiltin(Builtins::JavaScript id, 1839 InvokeFlag flag, 1840 const CallWrapper& call_wrapper) { 1841 ASM_LOCATION("MacroAssembler::InvokeBuiltin"); 1842 // You can't call a builtin without a valid frame. 1843 ASSERT(flag == JUMP_FUNCTION || has_frame()); 1844 1845 // Get the builtin entry in x2 and setup the function object in x1. 1846 GetBuiltinEntry(x2, x1, id); 1847 if (flag == CALL_FUNCTION) { 1848 call_wrapper.BeforeCall(CallSize(x2)); 1849 Call(x2); 1850 call_wrapper.AfterCall(); 1851 } else { 1852 ASSERT(flag == JUMP_FUNCTION); 1853 Jump(x2); 1854 } 1855 } 1856 1857 1858 void MacroAssembler::TailCallExternalReference(const ExternalReference& ext, 1859 int num_arguments, 1860 int result_size) { 1861 // TODO(1236192): Most runtime routines don't need the number of 1862 // arguments passed in because it is constant. At some point we 1863 // should remove this need and make the runtime routine entry code 1864 // smarter. 1865 Mov(x0, num_arguments); 1866 JumpToExternalReference(ext); 1867 } 1868 1869 1870 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid, 1871 int num_arguments, 1872 int result_size) { 1873 TailCallExternalReference(ExternalReference(fid, isolate()), 1874 num_arguments, 1875 result_size); 1876 } 1877 1878 1879 void MacroAssembler::InitializeNewString(Register string, 1880 Register length, 1881 Heap::RootListIndex map_index, 1882 Register scratch1, 1883 Register scratch2) { 1884 ASSERT(!AreAliased(string, length, scratch1, scratch2)); 1885 LoadRoot(scratch2, map_index); 1886 SmiTag(scratch1, length); 1887 Str(scratch2, FieldMemOperand(string, HeapObject::kMapOffset)); 1888 1889 Mov(scratch2, String::kEmptyHashField); 1890 Str(scratch1, FieldMemOperand(string, String::kLengthOffset)); 1891 Str(scratch2, FieldMemOperand(string, String::kHashFieldOffset)); 1892 } 1893 1894 1895 int MacroAssembler::ActivationFrameAlignment() { 1896 #if V8_HOST_ARCH_ARM64 1897 // Running on the real platform. Use the alignment as mandated by the local 1898 // environment. 1899 // Note: This will break if we ever start generating snapshots on one ARM 1900 // platform for another ARM platform with a different alignment. 1901 return OS::ActivationFrameAlignment(); 1902 #else // V8_HOST_ARCH_ARM64 1903 // If we are using the simulator then we should always align to the expected 1904 // alignment. As the simulator is used to generate snapshots we do not know 1905 // if the target platform will need alignment, so this is controlled from a 1906 // flag. 1907 return FLAG_sim_stack_alignment; 1908 #endif // V8_HOST_ARCH_ARM64 1909 } 1910 1911 1912 void MacroAssembler::CallCFunction(ExternalReference function, 1913 int num_of_reg_args) { 1914 CallCFunction(function, num_of_reg_args, 0); 1915 } 1916 1917 1918 void MacroAssembler::CallCFunction(ExternalReference function, 1919 int num_of_reg_args, 1920 int num_of_double_args) { 1921 UseScratchRegisterScope temps(this); 1922 Register temp = temps.AcquireX(); 1923 Mov(temp, function); 1924 CallCFunction(temp, num_of_reg_args, num_of_double_args); 1925 } 1926 1927 1928 void MacroAssembler::CallCFunction(Register function, 1929 int num_of_reg_args, 1930 int num_of_double_args) { 1931 ASSERT(has_frame()); 1932 // We can pass 8 integer arguments in registers. If we need to pass more than 1933 // that, we'll need to implement support for passing them on the stack. 1934 ASSERT(num_of_reg_args <= 8); 1935 1936 // If we're passing doubles, we're limited to the following prototypes 1937 // (defined by ExternalReference::Type): 1938 // BUILTIN_COMPARE_CALL: int f(double, double) 1939 // BUILTIN_FP_FP_CALL: double f(double, double) 1940 // BUILTIN_FP_CALL: double f(double) 1941 // BUILTIN_FP_INT_CALL: double f(double, int) 1942 if (num_of_double_args > 0) { 1943 ASSERT(num_of_reg_args <= 1); 1944 ASSERT((num_of_double_args + num_of_reg_args) <= 2); 1945 } 1946 1947 1948 // If the stack pointer is not csp, we need to derive an aligned csp from the 1949 // current stack pointer. 1950 const Register old_stack_pointer = StackPointer(); 1951 if (!csp.Is(old_stack_pointer)) { 1952 AssertStackConsistency(); 1953 1954 int sp_alignment = ActivationFrameAlignment(); 1955 // The ABI mandates at least 16-byte alignment. 1956 ASSERT(sp_alignment >= 16); 1957 ASSERT(IsPowerOf2(sp_alignment)); 1958 1959 // The current stack pointer is a callee saved register, and is preserved 1960 // across the call. 1961 ASSERT(kCalleeSaved.IncludesAliasOf(old_stack_pointer)); 1962 1963 // Align and synchronize the system stack pointer with jssp. 1964 Bic(csp, old_stack_pointer, sp_alignment - 1); 1965 SetStackPointer(csp); 1966 } 1967 1968 // Call directly. The function called cannot cause a GC, or allow preemption, 1969 // so the return address in the link register stays correct. 1970 Call(function); 1971 1972 if (!csp.Is(old_stack_pointer)) { 1973 if (emit_debug_code()) { 1974 // Because the stack pointer must be aligned on a 16-byte boundary, the 1975 // aligned csp can be up to 12 bytes below the jssp. This is the case 1976 // where we only pushed one W register on top of an aligned jssp. 1977 UseScratchRegisterScope temps(this); 1978 Register temp = temps.AcquireX(); 1979 ASSERT(ActivationFrameAlignment() == 16); 1980 Sub(temp, csp, old_stack_pointer); 1981 // We want temp <= 0 && temp >= -12. 1982 Cmp(temp, 0); 1983 Ccmp(temp, -12, NFlag, le); 1984 Check(ge, kTheStackWasCorruptedByMacroAssemblerCall); 1985 } 1986 SetStackPointer(old_stack_pointer); 1987 } 1988 } 1989 1990 1991 void MacroAssembler::Jump(Register target) { 1992 Br(target); 1993 } 1994 1995 1996 void MacroAssembler::Jump(intptr_t target, RelocInfo::Mode rmode) { 1997 UseScratchRegisterScope temps(this); 1998 Register temp = temps.AcquireX(); 1999 Mov(temp, Operand(target, rmode)); 2000 Br(temp); 2001 } 2002 2003 2004 void MacroAssembler::Jump(Address target, RelocInfo::Mode rmode) { 2005 ASSERT(!RelocInfo::IsCodeTarget(rmode)); 2006 Jump(reinterpret_cast<intptr_t>(target), rmode); 2007 } 2008 2009 2010 void MacroAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode) { 2011 ASSERT(RelocInfo::IsCodeTarget(rmode)); 2012 AllowDeferredHandleDereference embedding_raw_address; 2013 Jump(reinterpret_cast<intptr_t>(code.location()), rmode); 2014 } 2015 2016 2017 void MacroAssembler::Call(Register target) { 2018 BlockPoolsScope scope(this); 2019 #ifdef DEBUG 2020 Label start_call; 2021 Bind(&start_call); 2022 #endif 2023 2024 Blr(target); 2025 2026 #ifdef DEBUG 2027 AssertSizeOfCodeGeneratedSince(&start_call, CallSize(target)); 2028 #endif 2029 } 2030 2031 2032 void MacroAssembler::Call(Label* target) { 2033 BlockPoolsScope scope(this); 2034 #ifdef DEBUG 2035 Label start_call; 2036 Bind(&start_call); 2037 #endif 2038 2039 Bl(target); 2040 2041 #ifdef DEBUG 2042 AssertSizeOfCodeGeneratedSince(&start_call, CallSize(target)); 2043 #endif 2044 } 2045 2046 2047 // MacroAssembler::CallSize is sensitive to changes in this function, as it 2048 // requires to know how many instructions are used to branch to the target. 2049 void MacroAssembler::Call(Address target, RelocInfo::Mode rmode) { 2050 BlockPoolsScope scope(this); 2051 #ifdef DEBUG 2052 Label start_call; 2053 Bind(&start_call); 2054 #endif 2055 // Statement positions are expected to be recorded when the target 2056 // address is loaded. 2057 positions_recorder()->WriteRecordedPositions(); 2058 2059 // Addresses always have 64 bits, so we shouldn't encounter NONE32. 2060 ASSERT(rmode != RelocInfo::NONE32); 2061 2062 UseScratchRegisterScope temps(this); 2063 Register temp = temps.AcquireX(); 2064 2065 if (rmode == RelocInfo::NONE64) { 2066 // Addresses are 48 bits so we never need to load the upper 16 bits. 2067 uint64_t imm = reinterpret_cast<uint64_t>(target); 2068 // If we don't use ARM tagged addresses, the 16 higher bits must be 0. 2069 ASSERT(((imm >> 48) & 0xffff) == 0); 2070 movz(temp, (imm >> 0) & 0xffff, 0); 2071 movk(temp, (imm >> 16) & 0xffff, 16); 2072 movk(temp, (imm >> 32) & 0xffff, 32); 2073 } else { 2074 Ldr(temp, Immediate(reinterpret_cast<intptr_t>(target), rmode)); 2075 } 2076 Blr(temp); 2077 #ifdef DEBUG 2078 AssertSizeOfCodeGeneratedSince(&start_call, CallSize(target, rmode)); 2079 #endif 2080 } 2081 2082 2083 void MacroAssembler::Call(Handle<Code> code, 2084 RelocInfo::Mode rmode, 2085 TypeFeedbackId ast_id) { 2086 #ifdef DEBUG 2087 Label start_call; 2088 Bind(&start_call); 2089 #endif 2090 2091 if ((rmode == RelocInfo::CODE_TARGET) && (!ast_id.IsNone())) { 2092 SetRecordedAstId(ast_id); 2093 rmode = RelocInfo::CODE_TARGET_WITH_ID; 2094 } 2095 2096 AllowDeferredHandleDereference embedding_raw_address; 2097 Call(reinterpret_cast<Address>(code.location()), rmode); 2098 2099 #ifdef DEBUG 2100 // Check the size of the code generated. 2101 AssertSizeOfCodeGeneratedSince(&start_call, CallSize(code, rmode, ast_id)); 2102 #endif 2103 } 2104 2105 2106 int MacroAssembler::CallSize(Register target) { 2107 USE(target); 2108 return kInstructionSize; 2109 } 2110 2111 2112 int MacroAssembler::CallSize(Label* target) { 2113 USE(target); 2114 return kInstructionSize; 2115 } 2116 2117 2118 int MacroAssembler::CallSize(Address target, RelocInfo::Mode rmode) { 2119 USE(target); 2120 2121 // Addresses always have 64 bits, so we shouldn't encounter NONE32. 2122 ASSERT(rmode != RelocInfo::NONE32); 2123 2124 if (rmode == RelocInfo::NONE64) { 2125 return kCallSizeWithoutRelocation; 2126 } else { 2127 return kCallSizeWithRelocation; 2128 } 2129 } 2130 2131 2132 int MacroAssembler::CallSize(Handle<Code> code, 2133 RelocInfo::Mode rmode, 2134 TypeFeedbackId ast_id) { 2135 USE(code); 2136 USE(ast_id); 2137 2138 // Addresses always have 64 bits, so we shouldn't encounter NONE32. 2139 ASSERT(rmode != RelocInfo::NONE32); 2140 2141 if (rmode == RelocInfo::NONE64) { 2142 return kCallSizeWithoutRelocation; 2143 } else { 2144 return kCallSizeWithRelocation; 2145 } 2146 } 2147 2148 2149 2150 2151 2152 void MacroAssembler::JumpForHeapNumber(Register object, 2153 Register heap_number_map, 2154 Label* on_heap_number, 2155 Label* on_not_heap_number) { 2156 ASSERT(on_heap_number || on_not_heap_number); 2157 AssertNotSmi(object); 2158 2159 UseScratchRegisterScope temps(this); 2160 Register temp = temps.AcquireX(); 2161 2162 // Load the HeapNumber map if it is not passed. 2163 if (heap_number_map.Is(NoReg)) { 2164 heap_number_map = temps.AcquireX(); 2165 LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); 2166 } else { 2167 AssertRegisterIsRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); 2168 } 2169 2170 ASSERT(!AreAliased(temp, heap_number_map)); 2171 2172 Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); 2173 Cmp(temp, heap_number_map); 2174 2175 if (on_heap_number) { 2176 B(eq, on_heap_number); 2177 } 2178 if (on_not_heap_number) { 2179 B(ne, on_not_heap_number); 2180 } 2181 } 2182 2183 2184 void MacroAssembler::JumpIfHeapNumber(Register object, 2185 Label* on_heap_number, 2186 Register heap_number_map) { 2187 JumpForHeapNumber(object, 2188 heap_number_map, 2189 on_heap_number, 2190 NULL); 2191 } 2192 2193 2194 void MacroAssembler::JumpIfNotHeapNumber(Register object, 2195 Label* on_not_heap_number, 2196 Register heap_number_map) { 2197 JumpForHeapNumber(object, 2198 heap_number_map, 2199 NULL, 2200 on_not_heap_number); 2201 } 2202 2203 2204 void MacroAssembler::LookupNumberStringCache(Register object, 2205 Register result, 2206 Register scratch1, 2207 Register scratch2, 2208 Register scratch3, 2209 Label* not_found) { 2210 ASSERT(!AreAliased(object, result, scratch1, scratch2, scratch3)); 2211 2212 // Use of registers. Register result is used as a temporary. 2213 Register number_string_cache = result; 2214 Register mask = scratch3; 2215 2216 // Load the number string cache. 2217 LoadRoot(number_string_cache, Heap::kNumberStringCacheRootIndex); 2218 2219 // Make the hash mask from the length of the number string cache. It 2220 // contains two elements (number and string) for each cache entry. 2221 Ldrsw(mask, UntagSmiFieldMemOperand(number_string_cache, 2222 FixedArray::kLengthOffset)); 2223 Asr(mask, mask, 1); // Divide length by two. 2224 Sub(mask, mask, 1); // Make mask. 2225 2226 // Calculate the entry in the number string cache. The hash value in the 2227 // number string cache for smis is just the smi value, and the hash for 2228 // doubles is the xor of the upper and lower words. See 2229 // Heap::GetNumberStringCache. 2230 Label is_smi; 2231 Label load_result_from_cache; 2232 2233 JumpIfSmi(object, &is_smi); 2234 CheckMap(object, scratch1, Heap::kHeapNumberMapRootIndex, not_found, 2235 DONT_DO_SMI_CHECK); 2236 2237 STATIC_ASSERT(kDoubleSize == (kWRegSize * 2)); 2238 Add(scratch1, object, HeapNumber::kValueOffset - kHeapObjectTag); 2239 Ldp(scratch1.W(), scratch2.W(), MemOperand(scratch1)); 2240 Eor(scratch1, scratch1, scratch2); 2241 And(scratch1, scratch1, mask); 2242 2243 // Calculate address of entry in string cache: each entry consists of two 2244 // pointer sized fields. 2245 Add(scratch1, number_string_cache, 2246 Operand(scratch1, LSL, kPointerSizeLog2 + 1)); 2247 2248 Register probe = mask; 2249 Ldr(probe, FieldMemOperand(scratch1, FixedArray::kHeaderSize)); 2250 JumpIfSmi(probe, not_found); 2251 Ldr(d0, FieldMemOperand(object, HeapNumber::kValueOffset)); 2252 Ldr(d1, FieldMemOperand(probe, HeapNumber::kValueOffset)); 2253 Fcmp(d0, d1); 2254 B(ne, not_found); 2255 B(&load_result_from_cache); 2256 2257 Bind(&is_smi); 2258 Register scratch = scratch1; 2259 And(scratch, mask, Operand::UntagSmi(object)); 2260 // Calculate address of entry in string cache: each entry consists 2261 // of two pointer sized fields. 2262 Add(scratch, number_string_cache, 2263 Operand(scratch, LSL, kPointerSizeLog2 + 1)); 2264 2265 // Check if the entry is the smi we are looking for. 2266 Ldr(probe, FieldMemOperand(scratch, FixedArray::kHeaderSize)); 2267 Cmp(object, probe); 2268 B(ne, not_found); 2269 2270 // Get the result from the cache. 2271 Bind(&load_result_from_cache); 2272 Ldr(result, FieldMemOperand(scratch, FixedArray::kHeaderSize + kPointerSize)); 2273 IncrementCounter(isolate()->counters()->number_to_string_native(), 1, 2274 scratch1, scratch2); 2275 } 2276 2277 2278 void MacroAssembler::TryRepresentDoubleAsInt(Register as_int, 2279 FPRegister value, 2280 FPRegister scratch_d, 2281 Label* on_successful_conversion, 2282 Label* on_failed_conversion) { 2283 // Convert to an int and back again, then compare with the original value. 2284 Fcvtzs(as_int, value); 2285 Scvtf(scratch_d, as_int); 2286 Fcmp(value, scratch_d); 2287 2288 if (on_successful_conversion) { 2289 B(on_successful_conversion, eq); 2290 } 2291 if (on_failed_conversion) { 2292 B(on_failed_conversion, ne); 2293 } 2294 } 2295 2296 2297 void MacroAssembler::TestForMinusZero(DoubleRegister input) { 2298 UseScratchRegisterScope temps(this); 2299 Register temp = temps.AcquireX(); 2300 // Floating point -0.0 is kMinInt as an integer, so subtracting 1 (cmp) will 2301 // cause overflow. 2302 Fmov(temp, input); 2303 Cmp(temp, 1); 2304 } 2305 2306 2307 void MacroAssembler::JumpIfMinusZero(DoubleRegister input, 2308 Label* on_negative_zero) { 2309 TestForMinusZero(input); 2310 B(vs, on_negative_zero); 2311 } 2312 2313 2314 void MacroAssembler::JumpIfMinusZero(Register input, 2315 Label* on_negative_zero) { 2316 ASSERT(input.Is64Bits()); 2317 // Floating point value is in an integer register. Detect -0.0 by subtracting 2318 // 1 (cmp), which will cause overflow. 2319 Cmp(input, 1); 2320 B(vs, on_negative_zero); 2321 } 2322 2323 2324 void MacroAssembler::ClampInt32ToUint8(Register output, Register input) { 2325 // Clamp the value to [0..255]. 2326 Cmp(input.W(), Operand(input.W(), UXTB)); 2327 // If input < input & 0xff, it must be < 0, so saturate to 0. 2328 Csel(output.W(), wzr, input.W(), lt); 2329 // If input <= input & 0xff, it must be <= 255. Otherwise, saturate to 255. 2330 Csel(output.W(), output.W(), 255, le); 2331 } 2332 2333 2334 void MacroAssembler::ClampInt32ToUint8(Register in_out) { 2335 ClampInt32ToUint8(in_out, in_out); 2336 } 2337 2338 2339 void MacroAssembler::ClampDoubleToUint8(Register output, 2340 DoubleRegister input, 2341 DoubleRegister dbl_scratch) { 2342 // This conversion follows the WebIDL "[Clamp]" rules for PIXEL types: 2343 // - Inputs lower than 0 (including -infinity) produce 0. 2344 // - Inputs higher than 255 (including +infinity) produce 255. 2345 // Also, it seems that PIXEL types use round-to-nearest rather than 2346 // round-towards-zero. 2347 2348 // Squash +infinity before the conversion, since Fcvtnu will normally 2349 // convert it to 0. 2350 Fmov(dbl_scratch, 255); 2351 Fmin(dbl_scratch, dbl_scratch, input); 2352 2353 // Convert double to unsigned integer. Values less than zero become zero. 2354 // Values greater than 255 have already been clamped to 255. 2355 Fcvtnu(output, dbl_scratch); 2356 } 2357 2358 2359 void MacroAssembler::CopyFieldsLoopPairsHelper(Register dst, 2360 Register src, 2361 unsigned count, 2362 Register scratch1, 2363 Register scratch2, 2364 Register scratch3, 2365 Register scratch4, 2366 Register scratch5) { 2367 // Untag src and dst into scratch registers. 2368 // Copy src->dst in a tight loop. 2369 ASSERT(!AreAliased(dst, src, 2370 scratch1, scratch2, scratch3, scratch4, scratch5)); 2371 ASSERT(count >= 2); 2372 2373 const Register& remaining = scratch3; 2374 Mov(remaining, count / 2); 2375 2376 const Register& dst_untagged = scratch1; 2377 const Register& src_untagged = scratch2; 2378 Sub(dst_untagged, dst, kHeapObjectTag); 2379 Sub(src_untagged, src, kHeapObjectTag); 2380 2381 // Copy fields in pairs. 2382 Label loop; 2383 Bind(&loop); 2384 Ldp(scratch4, scratch5, 2385 MemOperand(src_untagged, kXRegSize* 2, PostIndex)); 2386 Stp(scratch4, scratch5, 2387 MemOperand(dst_untagged, kXRegSize* 2, PostIndex)); 2388 Sub(remaining, remaining, 1); 2389 Cbnz(remaining, &loop); 2390 2391 // Handle the leftovers. 2392 if (count & 1) { 2393 Ldr(scratch4, MemOperand(src_untagged)); 2394 Str(scratch4, MemOperand(dst_untagged)); 2395 } 2396 } 2397 2398 2399 void MacroAssembler::CopyFieldsUnrolledPairsHelper(Register dst, 2400 Register src, 2401 unsigned count, 2402 Register scratch1, 2403 Register scratch2, 2404 Register scratch3, 2405 Register scratch4) { 2406 // Untag src and dst into scratch registers. 2407 // Copy src->dst in an unrolled loop. 2408 ASSERT(!AreAliased(dst, src, scratch1, scratch2, scratch3, scratch4)); 2409 2410 const Register& dst_untagged = scratch1; 2411 const Register& src_untagged = scratch2; 2412 sub(dst_untagged, dst, kHeapObjectTag); 2413 sub(src_untagged, src, kHeapObjectTag); 2414 2415 // Copy fields in pairs. 2416 for (unsigned i = 0; i < count / 2; i++) { 2417 Ldp(scratch3, scratch4, MemOperand(src_untagged, kXRegSize * 2, PostIndex)); 2418 Stp(scratch3, scratch4, MemOperand(dst_untagged, kXRegSize * 2, PostIndex)); 2419 } 2420 2421 // Handle the leftovers. 2422 if (count & 1) { 2423 Ldr(scratch3, MemOperand(src_untagged)); 2424 Str(scratch3, MemOperand(dst_untagged)); 2425 } 2426 } 2427 2428 2429 void MacroAssembler::CopyFieldsUnrolledHelper(Register dst, 2430 Register src, 2431 unsigned count, 2432 Register scratch1, 2433 Register scratch2, 2434 Register scratch3) { 2435 // Untag src and dst into scratch registers. 2436 // Copy src->dst in an unrolled loop. 2437 ASSERT(!AreAliased(dst, src, scratch1, scratch2, scratch3)); 2438 2439 const Register& dst_untagged = scratch1; 2440 const Register& src_untagged = scratch2; 2441 Sub(dst_untagged, dst, kHeapObjectTag); 2442 Sub(src_untagged, src, kHeapObjectTag); 2443 2444 // Copy fields one by one. 2445 for (unsigned i = 0; i < count; i++) { 2446 Ldr(scratch3, MemOperand(src_untagged, kXRegSize, PostIndex)); 2447 Str(scratch3, MemOperand(dst_untagged, kXRegSize, PostIndex)); 2448 } 2449 } 2450 2451 2452 void MacroAssembler::CopyFields(Register dst, Register src, CPURegList temps, 2453 unsigned count) { 2454 // One of two methods is used: 2455 // 2456 // For high 'count' values where many scratch registers are available: 2457 // Untag src and dst into scratch registers. 2458 // Copy src->dst in a tight loop. 2459 // 2460 // For low 'count' values or where few scratch registers are available: 2461 // Untag src and dst into scratch registers. 2462 // Copy src->dst in an unrolled loop. 2463 // 2464 // In both cases, fields are copied in pairs if possible, and left-overs are 2465 // handled separately. 2466 ASSERT(!AreAliased(dst, src)); 2467 ASSERT(!temps.IncludesAliasOf(dst)); 2468 ASSERT(!temps.IncludesAliasOf(src)); 2469 ASSERT(!temps.IncludesAliasOf(xzr)); 2470 2471 if (emit_debug_code()) { 2472 Cmp(dst, src); 2473 Check(ne, kTheSourceAndDestinationAreTheSame); 2474 } 2475 2476 // The value of 'count' at which a loop will be generated (if there are 2477 // enough scratch registers). 2478 static const unsigned kLoopThreshold = 8; 2479 2480 UseScratchRegisterScope masm_temps(this); 2481 if ((temps.Count() >= 3) && (count >= kLoopThreshold)) { 2482 CopyFieldsLoopPairsHelper(dst, src, count, 2483 Register(temps.PopLowestIndex()), 2484 Register(temps.PopLowestIndex()), 2485 Register(temps.PopLowestIndex()), 2486 masm_temps.AcquireX(), 2487 masm_temps.AcquireX()); 2488 } else if (temps.Count() >= 2) { 2489 CopyFieldsUnrolledPairsHelper(dst, src, count, 2490 Register(temps.PopLowestIndex()), 2491 Register(temps.PopLowestIndex()), 2492 masm_temps.AcquireX(), 2493 masm_temps.AcquireX()); 2494 } else if (temps.Count() == 1) { 2495 CopyFieldsUnrolledHelper(dst, src, count, 2496 Register(temps.PopLowestIndex()), 2497 masm_temps.AcquireX(), 2498 masm_temps.AcquireX()); 2499 } else { 2500 UNREACHABLE(); 2501 } 2502 } 2503 2504 2505 void MacroAssembler::CopyBytes(Register dst, 2506 Register src, 2507 Register length, 2508 Register scratch, 2509 CopyHint hint) { 2510 UseScratchRegisterScope temps(this); 2511 Register tmp1 = temps.AcquireX(); 2512 Register tmp2 = temps.AcquireX(); 2513 ASSERT(!AreAliased(src, dst, length, scratch, tmp1, tmp2)); 2514 ASSERT(!AreAliased(src, dst, csp)); 2515 2516 if (emit_debug_code()) { 2517 // Check copy length. 2518 Cmp(length, 0); 2519 Assert(ge, kUnexpectedNegativeValue); 2520 2521 // Check src and dst buffers don't overlap. 2522 Add(scratch, src, length); // Calculate end of src buffer. 2523 Cmp(scratch, dst); 2524 Add(scratch, dst, length); // Calculate end of dst buffer. 2525 Ccmp(scratch, src, ZFlag, gt); 2526 Assert(le, kCopyBuffersOverlap); 2527 } 2528 2529 Label short_copy, short_loop, bulk_loop, done; 2530 2531 if ((hint == kCopyLong || hint == kCopyUnknown) && !FLAG_optimize_for_size) { 2532 Register bulk_length = scratch; 2533 int pair_size = 2 * kXRegSize; 2534 int pair_mask = pair_size - 1; 2535 2536 Bic(bulk_length, length, pair_mask); 2537 Cbz(bulk_length, &short_copy); 2538 Bind(&bulk_loop); 2539 Sub(bulk_length, bulk_length, pair_size); 2540 Ldp(tmp1, tmp2, MemOperand(src, pair_size, PostIndex)); 2541 Stp(tmp1, tmp2, MemOperand(dst, pair_size, PostIndex)); 2542 Cbnz(bulk_length, &bulk_loop); 2543 2544 And(length, length, pair_mask); 2545 } 2546 2547 Bind(&short_copy); 2548 Cbz(length, &done); 2549 Bind(&short_loop); 2550 Sub(length, length, 1); 2551 Ldrb(tmp1, MemOperand(src, 1, PostIndex)); 2552 Strb(tmp1, MemOperand(dst, 1, PostIndex)); 2553 Cbnz(length, &short_loop); 2554 2555 2556 Bind(&done); 2557 } 2558 2559 2560 void MacroAssembler::FillFields(Register dst, 2561 Register field_count, 2562 Register filler) { 2563 ASSERT(!dst.Is(csp)); 2564 UseScratchRegisterScope temps(this); 2565 Register field_ptr = temps.AcquireX(); 2566 Register counter = temps.AcquireX(); 2567 Label done; 2568 2569 // Decrement count. If the result < zero, count was zero, and there's nothing 2570 // to do. If count was one, flags are set to fail the gt condition at the end 2571 // of the pairs loop. 2572 Subs(counter, field_count, 1); 2573 B(lt, &done); 2574 2575 // There's at least one field to fill, so do this unconditionally. 2576 Str(filler, MemOperand(dst, kPointerSize, PostIndex)); 2577 2578 // If the bottom bit of counter is set, there are an even number of fields to 2579 // fill, so pull the start pointer back by one field, allowing the pairs loop 2580 // to overwrite the field that was stored above. 2581 And(field_ptr, counter, 1); 2582 Sub(field_ptr, dst, Operand(field_ptr, LSL, kPointerSizeLog2)); 2583 2584 // Store filler to memory in pairs. 2585 Label entry, loop; 2586 B(&entry); 2587 Bind(&loop); 2588 Stp(filler, filler, MemOperand(field_ptr, 2 * kPointerSize, PostIndex)); 2589 Subs(counter, counter, 2); 2590 Bind(&entry); 2591 B(gt, &loop); 2592 2593 Bind(&done); 2594 } 2595 2596 2597 void MacroAssembler::JumpIfEitherIsNotSequentialAsciiStrings( 2598 Register first, 2599 Register second, 2600 Register scratch1, 2601 Register scratch2, 2602 Label* failure, 2603 SmiCheckType smi_check) { 2604 2605 if (smi_check == DO_SMI_CHECK) { 2606 JumpIfEitherSmi(first, second, failure); 2607 } else if (emit_debug_code()) { 2608 ASSERT(smi_check == DONT_DO_SMI_CHECK); 2609 Label not_smi; 2610 JumpIfEitherSmi(first, second, NULL, ¬_smi); 2611 2612 // At least one input is a smi, but the flags indicated a smi check wasn't 2613 // needed. 2614 Abort(kUnexpectedSmi); 2615 2616 Bind(¬_smi); 2617 } 2618 2619 // Test that both first and second are sequential ASCII strings. 2620 Ldr(scratch1, FieldMemOperand(first, HeapObject::kMapOffset)); 2621 Ldr(scratch2, FieldMemOperand(second, HeapObject::kMapOffset)); 2622 Ldrb(scratch1, FieldMemOperand(scratch1, Map::kInstanceTypeOffset)); 2623 Ldrb(scratch2, FieldMemOperand(scratch2, Map::kInstanceTypeOffset)); 2624 2625 JumpIfEitherInstanceTypeIsNotSequentialAscii(scratch1, 2626 scratch2, 2627 scratch1, 2628 scratch2, 2629 failure); 2630 } 2631 2632 2633 void MacroAssembler::JumpIfEitherInstanceTypeIsNotSequentialAscii( 2634 Register first, 2635 Register second, 2636 Register scratch1, 2637 Register scratch2, 2638 Label* failure) { 2639 ASSERT(!AreAliased(scratch1, second)); 2640 ASSERT(!AreAliased(scratch1, scratch2)); 2641 static const int kFlatAsciiStringMask = 2642 kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask; 2643 static const int kFlatAsciiStringTag = ASCII_STRING_TYPE; 2644 And(scratch1, first, kFlatAsciiStringMask); 2645 And(scratch2, second, kFlatAsciiStringMask); 2646 Cmp(scratch1, kFlatAsciiStringTag); 2647 Ccmp(scratch2, kFlatAsciiStringTag, NoFlag, eq); 2648 B(ne, failure); 2649 } 2650 2651 2652 void MacroAssembler::JumpIfInstanceTypeIsNotSequentialAscii(Register type, 2653 Register scratch, 2654 Label* failure) { 2655 const int kFlatAsciiStringMask = 2656 kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask; 2657 const int kFlatAsciiStringTag = 2658 kStringTag | kOneByteStringTag | kSeqStringTag; 2659 And(scratch, type, kFlatAsciiStringMask); 2660 Cmp(scratch, kFlatAsciiStringTag); 2661 B(ne, failure); 2662 } 2663 2664 2665 void MacroAssembler::JumpIfBothInstanceTypesAreNotSequentialAscii( 2666 Register first, 2667 Register second, 2668 Register scratch1, 2669 Register scratch2, 2670 Label* failure) { 2671 ASSERT(!AreAliased(first, second, scratch1, scratch2)); 2672 const int kFlatAsciiStringMask = 2673 kIsNotStringMask | kStringEncodingMask | kStringRepresentationMask; 2674 const int kFlatAsciiStringTag = 2675 kStringTag | kOneByteStringTag | kSeqStringTag; 2676 And(scratch1, first, kFlatAsciiStringMask); 2677 And(scratch2, second, kFlatAsciiStringMask); 2678 Cmp(scratch1, kFlatAsciiStringTag); 2679 Ccmp(scratch2, kFlatAsciiStringTag, NoFlag, eq); 2680 B(ne, failure); 2681 } 2682 2683 2684 void MacroAssembler::JumpIfNotUniqueName(Register type, 2685 Label* not_unique_name) { 2686 STATIC_ASSERT((kInternalizedTag == 0) && (kStringTag == 0)); 2687 // if ((type is string && type is internalized) || type == SYMBOL_TYPE) { 2688 // continue 2689 // } else { 2690 // goto not_unique_name 2691 // } 2692 Tst(type, kIsNotStringMask | kIsNotInternalizedMask); 2693 Ccmp(type, SYMBOL_TYPE, ZFlag, ne); 2694 B(ne, not_unique_name); 2695 } 2696 2697 2698 void MacroAssembler::InvokePrologue(const ParameterCount& expected, 2699 const ParameterCount& actual, 2700 Handle<Code> code_constant, 2701 Register code_reg, 2702 Label* done, 2703 InvokeFlag flag, 2704 bool* definitely_mismatches, 2705 const CallWrapper& call_wrapper) { 2706 bool definitely_matches = false; 2707 *definitely_mismatches = false; 2708 Label regular_invoke; 2709 2710 // Check whether the expected and actual arguments count match. If not, 2711 // setup registers according to contract with ArgumentsAdaptorTrampoline: 2712 // x0: actual arguments count. 2713 // x1: function (passed through to callee). 2714 // x2: expected arguments count. 2715 2716 // The code below is made a lot easier because the calling code already sets 2717 // up actual and expected registers according to the contract if values are 2718 // passed in registers. 2719 ASSERT(actual.is_immediate() || actual.reg().is(x0)); 2720 ASSERT(expected.is_immediate() || expected.reg().is(x2)); 2721 ASSERT((!code_constant.is_null() && code_reg.is(no_reg)) || code_reg.is(x3)); 2722 2723 if (expected.is_immediate()) { 2724 ASSERT(actual.is_immediate()); 2725 if (expected.immediate() == actual.immediate()) { 2726 definitely_matches = true; 2727 2728 } else { 2729 Mov(x0, actual.immediate()); 2730 if (expected.immediate() == 2731 SharedFunctionInfo::kDontAdaptArgumentsSentinel) { 2732 // Don't worry about adapting arguments for builtins that 2733 // don't want that done. Skip adaption code by making it look 2734 // like we have a match between expected and actual number of 2735 // arguments. 2736 definitely_matches = true; 2737 } else { 2738 *definitely_mismatches = true; 2739 // Set up x2 for the argument adaptor. 2740 Mov(x2, expected.immediate()); 2741 } 2742 } 2743 2744 } else { // expected is a register. 2745 Operand actual_op = actual.is_immediate() ? Operand(actual.immediate()) 2746 : Operand(actual.reg()); 2747 // If actual == expected perform a regular invocation. 2748 Cmp(expected.reg(), actual_op); 2749 B(eq, ®ular_invoke); 2750 // Otherwise set up x0 for the argument adaptor. 2751 Mov(x0, actual_op); 2752 } 2753 2754 // If the argument counts may mismatch, generate a call to the argument 2755 // adaptor. 2756 if (!definitely_matches) { 2757 if (!code_constant.is_null()) { 2758 Mov(x3, Operand(code_constant)); 2759 Add(x3, x3, Code::kHeaderSize - kHeapObjectTag); 2760 } 2761 2762 Handle<Code> adaptor = 2763 isolate()->builtins()->ArgumentsAdaptorTrampoline(); 2764 if (flag == CALL_FUNCTION) { 2765 call_wrapper.BeforeCall(CallSize(adaptor)); 2766 Call(adaptor); 2767 call_wrapper.AfterCall(); 2768 if (!*definitely_mismatches) { 2769 // If the arg counts don't match, no extra code is emitted by 2770 // MAsm::InvokeCode and we can just fall through. 2771 B(done); 2772 } 2773 } else { 2774 Jump(adaptor, RelocInfo::CODE_TARGET); 2775 } 2776 } 2777 Bind(®ular_invoke); 2778 } 2779 2780 2781 void MacroAssembler::InvokeCode(Register code, 2782 const ParameterCount& expected, 2783 const ParameterCount& actual, 2784 InvokeFlag flag, 2785 const CallWrapper& call_wrapper) { 2786 // You can't call a function without a valid frame. 2787 ASSERT(flag == JUMP_FUNCTION || has_frame()); 2788 2789 Label done; 2790 2791 bool definitely_mismatches = false; 2792 InvokePrologue(expected, actual, Handle<Code>::null(), code, &done, flag, 2793 &definitely_mismatches, call_wrapper); 2794 2795 // If we are certain that actual != expected, then we know InvokePrologue will 2796 // have handled the call through the argument adaptor mechanism. 2797 // The called function expects the call kind in x5. 2798 if (!definitely_mismatches) { 2799 if (flag == CALL_FUNCTION) { 2800 call_wrapper.BeforeCall(CallSize(code)); 2801 Call(code); 2802 call_wrapper.AfterCall(); 2803 } else { 2804 ASSERT(flag == JUMP_FUNCTION); 2805 Jump(code); 2806 } 2807 } 2808 2809 // Continue here if InvokePrologue does handle the invocation due to 2810 // mismatched parameter counts. 2811 Bind(&done); 2812 } 2813 2814 2815 void MacroAssembler::InvokeFunction(Register function, 2816 const ParameterCount& actual, 2817 InvokeFlag flag, 2818 const CallWrapper& call_wrapper) { 2819 // You can't call a function without a valid frame. 2820 ASSERT(flag == JUMP_FUNCTION || has_frame()); 2821 2822 // Contract with called JS functions requires that function is passed in x1. 2823 // (See FullCodeGenerator::Generate().) 2824 ASSERT(function.is(x1)); 2825 2826 Register expected_reg = x2; 2827 Register code_reg = x3; 2828 2829 Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); 2830 // The number of arguments is stored as an int32_t, and -1 is a marker 2831 // (SharedFunctionInfo::kDontAdaptArgumentsSentinel), so we need sign 2832 // extension to correctly handle it. 2833 Ldr(expected_reg, FieldMemOperand(function, 2834 JSFunction::kSharedFunctionInfoOffset)); 2835 Ldrsw(expected_reg, 2836 FieldMemOperand(expected_reg, 2837 SharedFunctionInfo::kFormalParameterCountOffset)); 2838 Ldr(code_reg, 2839 FieldMemOperand(function, JSFunction::kCodeEntryOffset)); 2840 2841 ParameterCount expected(expected_reg); 2842 InvokeCode(code_reg, expected, actual, flag, call_wrapper); 2843 } 2844 2845 2846 void MacroAssembler::InvokeFunction(Register function, 2847 const ParameterCount& expected, 2848 const ParameterCount& actual, 2849 InvokeFlag flag, 2850 const CallWrapper& call_wrapper) { 2851 // You can't call a function without a valid frame. 2852 ASSERT(flag == JUMP_FUNCTION || has_frame()); 2853 2854 // Contract with called JS functions requires that function is passed in x1. 2855 // (See FullCodeGenerator::Generate().) 2856 ASSERT(function.Is(x1)); 2857 2858 Register code_reg = x3; 2859 2860 // Set up the context. 2861 Ldr(cp, FieldMemOperand(function, JSFunction::kContextOffset)); 2862 2863 // We call indirectly through the code field in the function to 2864 // allow recompilation to take effect without changing any of the 2865 // call sites. 2866 Ldr(code_reg, FieldMemOperand(function, JSFunction::kCodeEntryOffset)); 2867 InvokeCode(code_reg, expected, actual, flag, call_wrapper); 2868 } 2869 2870 2871 void MacroAssembler::InvokeFunction(Handle<JSFunction> function, 2872 const ParameterCount& expected, 2873 const ParameterCount& actual, 2874 InvokeFlag flag, 2875 const CallWrapper& call_wrapper) { 2876 // Contract with called JS functions requires that function is passed in x1. 2877 // (See FullCodeGenerator::Generate().) 2878 __ LoadObject(x1, function); 2879 InvokeFunction(x1, expected, actual, flag, call_wrapper); 2880 } 2881 2882 2883 void MacroAssembler::TryConvertDoubleToInt64(Register result, 2884 DoubleRegister double_input, 2885 Label* done) { 2886 // Try to convert with an FPU convert instruction. It's trivial to compute 2887 // the modulo operation on an integer register so we convert to a 64-bit 2888 // integer. 2889 // 2890 // Fcvtzs will saturate to INT64_MIN (0x800...00) or INT64_MAX (0x7ff...ff) 2891 // when the double is out of range. NaNs and infinities will be converted to 0 2892 // (as ECMA-262 requires). 2893 Fcvtzs(result.X(), double_input); 2894 2895 // The values INT64_MIN (0x800...00) or INT64_MAX (0x7ff...ff) are not 2896 // representable using a double, so if the result is one of those then we know 2897 // that saturation occured, and we need to manually handle the conversion. 2898 // 2899 // It is easy to detect INT64_MIN and INT64_MAX because adding or subtracting 2900 // 1 will cause signed overflow. 2901 Cmp(result.X(), 1); 2902 Ccmp(result.X(), -1, VFlag, vc); 2903 2904 B(vc, done); 2905 } 2906 2907 2908 void MacroAssembler::TruncateDoubleToI(Register result, 2909 DoubleRegister double_input) { 2910 Label done; 2911 ASSERT(jssp.Is(StackPointer())); 2912 2913 // Try to convert the double to an int64. If successful, the bottom 32 bits 2914 // contain our truncated int32 result. 2915 TryConvertDoubleToInt64(result, double_input, &done); 2916 2917 // If we fell through then inline version didn't succeed - call stub instead. 2918 Push(lr); 2919 Push(double_input); // Put input on stack. 2920 2921 DoubleToIStub stub(isolate(), 2922 jssp, 2923 result, 2924 0, 2925 true, // is_truncating 2926 true); // skip_fastpath 2927 CallStub(&stub); // DoubleToIStub preserves any registers it needs to clobber 2928 2929 Drop(1, kDoubleSize); // Drop the double input on the stack. 2930 Pop(lr); 2931 2932 Bind(&done); 2933 } 2934 2935 2936 void MacroAssembler::TruncateHeapNumberToI(Register result, 2937 Register object) { 2938 Label done; 2939 ASSERT(!result.is(object)); 2940 ASSERT(jssp.Is(StackPointer())); 2941 2942 Ldr(fp_scratch, FieldMemOperand(object, HeapNumber::kValueOffset)); 2943 2944 // Try to convert the double to an int64. If successful, the bottom 32 bits 2945 // contain our truncated int32 result. 2946 TryConvertDoubleToInt64(result, fp_scratch, &done); 2947 2948 // If we fell through then inline version didn't succeed - call stub instead. 2949 Push(lr); 2950 DoubleToIStub stub(isolate(), 2951 object, 2952 result, 2953 HeapNumber::kValueOffset - kHeapObjectTag, 2954 true, // is_truncating 2955 true); // skip_fastpath 2956 CallStub(&stub); // DoubleToIStub preserves any registers it needs to clobber 2957 Pop(lr); 2958 2959 Bind(&done); 2960 } 2961 2962 2963 void MacroAssembler::StubPrologue() { 2964 ASSERT(StackPointer().Is(jssp)); 2965 UseScratchRegisterScope temps(this); 2966 Register temp = temps.AcquireX(); 2967 __ Mov(temp, Smi::FromInt(StackFrame::STUB)); 2968 // Compiled stubs don't age, and so they don't need the predictable code 2969 // ageing sequence. 2970 __ Push(lr, fp, cp, temp); 2971 __ Add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp); 2972 } 2973 2974 2975 void MacroAssembler::Prologue(bool code_pre_aging) { 2976 if (code_pre_aging) { 2977 Code* stub = Code::GetPreAgedCodeAgeStub(isolate()); 2978 __ EmitCodeAgeSequence(stub); 2979 } else { 2980 __ EmitFrameSetupForCodeAgePatching(); 2981 } 2982 } 2983 2984 2985 void MacroAssembler::EnterFrame(StackFrame::Type type) { 2986 ASSERT(jssp.Is(StackPointer())); 2987 UseScratchRegisterScope temps(this); 2988 Register type_reg = temps.AcquireX(); 2989 Register code_reg = temps.AcquireX(); 2990 2991 Push(lr, fp, cp); 2992 Mov(type_reg, Smi::FromInt(type)); 2993 Mov(code_reg, Operand(CodeObject())); 2994 Push(type_reg, code_reg); 2995 // jssp[4] : lr 2996 // jssp[3] : fp 2997 // jssp[2] : cp 2998 // jssp[1] : type 2999 // jssp[0] : code object 3000 3001 // Adjust FP to point to saved FP. 3002 Add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp + kPointerSize); 3003 } 3004 3005 3006 void MacroAssembler::LeaveFrame(StackFrame::Type type) { 3007 ASSERT(jssp.Is(StackPointer())); 3008 // Drop the execution stack down to the frame pointer and restore 3009 // the caller frame pointer and return address. 3010 Mov(jssp, fp); 3011 AssertStackConsistency(); 3012 Pop(fp, lr); 3013 } 3014 3015 3016 void MacroAssembler::ExitFramePreserveFPRegs() { 3017 PushCPURegList(kCallerSavedFP); 3018 } 3019 3020 3021 void MacroAssembler::ExitFrameRestoreFPRegs() { 3022 // Read the registers from the stack without popping them. The stack pointer 3023 // will be reset as part of the unwinding process. 3024 CPURegList saved_fp_regs = kCallerSavedFP; 3025 ASSERT(saved_fp_regs.Count() % 2 == 0); 3026 3027 int offset = ExitFrameConstants::kLastExitFrameField; 3028 while (!saved_fp_regs.IsEmpty()) { 3029 const CPURegister& dst0 = saved_fp_regs.PopHighestIndex(); 3030 const CPURegister& dst1 = saved_fp_regs.PopHighestIndex(); 3031 offset -= 2 * kDRegSize; 3032 Ldp(dst1, dst0, MemOperand(fp, offset)); 3033 } 3034 } 3035 3036 3037 void MacroAssembler::EnterExitFrame(bool save_doubles, 3038 const Register& scratch, 3039 int extra_space) { 3040 ASSERT(jssp.Is(StackPointer())); 3041 3042 // Set up the new stack frame. 3043 Mov(scratch, Operand(CodeObject())); 3044 Push(lr, fp); 3045 Mov(fp, StackPointer()); 3046 Push(xzr, scratch); 3047 // fp[8]: CallerPC (lr) 3048 // fp -> fp[0]: CallerFP (old fp) 3049 // fp[-8]: Space reserved for SPOffset. 3050 // jssp -> fp[-16]: CodeObject() 3051 STATIC_ASSERT((2 * kPointerSize) == 3052 ExitFrameConstants::kCallerSPDisplacement); 3053 STATIC_ASSERT((1 * kPointerSize) == ExitFrameConstants::kCallerPCOffset); 3054 STATIC_ASSERT((0 * kPointerSize) == ExitFrameConstants::kCallerFPOffset); 3055 STATIC_ASSERT((-1 * kPointerSize) == ExitFrameConstants::kSPOffset); 3056 STATIC_ASSERT((-2 * kPointerSize) == ExitFrameConstants::kCodeOffset); 3057 3058 // Save the frame pointer and context pointer in the top frame. 3059 Mov(scratch, Operand(ExternalReference(Isolate::kCEntryFPAddress, 3060 isolate()))); 3061 Str(fp, MemOperand(scratch)); 3062 Mov(scratch, Operand(ExternalReference(Isolate::kContextAddress, 3063 isolate()))); 3064 Str(cp, MemOperand(scratch)); 3065 3066 STATIC_ASSERT((-2 * kPointerSize) == 3067 ExitFrameConstants::kLastExitFrameField); 3068 if (save_doubles) { 3069 ExitFramePreserveFPRegs(); 3070 } 3071 3072 // Reserve space for the return address and for user requested memory. 3073 // We do this before aligning to make sure that we end up correctly 3074 // aligned with the minimum of wasted space. 3075 Claim(extra_space + 1, kXRegSize); 3076 // fp[8]: CallerPC (lr) 3077 // fp -> fp[0]: CallerFP (old fp) 3078 // fp[-8]: Space reserved for SPOffset. 3079 // fp[-16]: CodeObject() 3080 // fp[-16 - fp_size]: Saved doubles (if save_doubles is true). 3081 // jssp[8]: Extra space reserved for caller (if extra_space != 0). 3082 // jssp -> jssp[0]: Space reserved for the return address. 3083 3084 // Align and synchronize the system stack pointer with jssp. 3085 AlignAndSetCSPForFrame(); 3086 ASSERT(csp.Is(StackPointer())); 3087 3088 // fp[8]: CallerPC (lr) 3089 // fp -> fp[0]: CallerFP (old fp) 3090 // fp[-8]: Space reserved for SPOffset. 3091 // fp[-16]: CodeObject() 3092 // fp[-16 - fp_size]: Saved doubles (if save_doubles is true). 3093 // csp[8]: Memory reserved for the caller if extra_space != 0. 3094 // Alignment padding, if necessary. 3095 // csp -> csp[0]: Space reserved for the return address. 3096 3097 // ExitFrame::GetStateForFramePointer expects to find the return address at 3098 // the memory address immediately below the pointer stored in SPOffset. 3099 // It is not safe to derive much else from SPOffset, because the size of the 3100 // padding can vary. 3101 Add(scratch, csp, kXRegSize); 3102 Str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset)); 3103 } 3104 3105 3106 // Leave the current exit frame. 3107 void MacroAssembler::LeaveExitFrame(bool restore_doubles, 3108 const Register& scratch, 3109 bool restore_context) { 3110 ASSERT(csp.Is(StackPointer())); 3111 3112 if (restore_doubles) { 3113 ExitFrameRestoreFPRegs(); 3114 } 3115 3116 // Restore the context pointer from the top frame. 3117 if (restore_context) { 3118 Mov(scratch, Operand(ExternalReference(Isolate::kContextAddress, 3119 isolate()))); 3120 Ldr(cp, MemOperand(scratch)); 3121 } 3122 3123 if (emit_debug_code()) { 3124 // Also emit debug code to clear the cp in the top frame. 3125 Mov(scratch, Operand(ExternalReference(Isolate::kContextAddress, 3126 isolate()))); 3127 Str(xzr, MemOperand(scratch)); 3128 } 3129 // Clear the frame pointer from the top frame. 3130 Mov(scratch, Operand(ExternalReference(Isolate::kCEntryFPAddress, 3131 isolate()))); 3132 Str(xzr, MemOperand(scratch)); 3133 3134 // Pop the exit frame. 3135 // fp[8]: CallerPC (lr) 3136 // fp -> fp[0]: CallerFP (old fp) 3137 // fp[...]: The rest of the frame. 3138 Mov(jssp, fp); 3139 SetStackPointer(jssp); 3140 AssertStackConsistency(); 3141 Pop(fp, lr); 3142 } 3143 3144 3145 void MacroAssembler::SetCounter(StatsCounter* counter, int value, 3146 Register scratch1, Register scratch2) { 3147 if (FLAG_native_code_counters && counter->Enabled()) { 3148 Mov(scratch1, value); 3149 Mov(scratch2, ExternalReference(counter)); 3150 Str(scratch1, MemOperand(scratch2)); 3151 } 3152 } 3153 3154 3155 void MacroAssembler::IncrementCounter(StatsCounter* counter, int value, 3156 Register scratch1, Register scratch2) { 3157 ASSERT(value != 0); 3158 if (FLAG_native_code_counters && counter->Enabled()) { 3159 Mov(scratch2, ExternalReference(counter)); 3160 Ldr(scratch1, MemOperand(scratch2)); 3161 Add(scratch1, scratch1, value); 3162 Str(scratch1, MemOperand(scratch2)); 3163 } 3164 } 3165 3166 3167 void MacroAssembler::DecrementCounter(StatsCounter* counter, int value, 3168 Register scratch1, Register scratch2) { 3169 IncrementCounter(counter, -value, scratch1, scratch2); 3170 } 3171 3172 3173 void MacroAssembler::LoadContext(Register dst, int context_chain_length) { 3174 if (context_chain_length > 0) { 3175 // Move up the chain of contexts to the context containing the slot. 3176 Ldr(dst, MemOperand(cp, Context::SlotOffset(Context::PREVIOUS_INDEX))); 3177 for (int i = 1; i < context_chain_length; i++) { 3178 Ldr(dst, MemOperand(dst, Context::SlotOffset(Context::PREVIOUS_INDEX))); 3179 } 3180 } else { 3181 // Slot is in the current function context. Move it into the 3182 // destination register in case we store into it (the write barrier 3183 // cannot be allowed to destroy the context in cp). 3184 Mov(dst, cp); 3185 } 3186 } 3187 3188 3189 void MacroAssembler::DebugBreak() { 3190 Mov(x0, 0); 3191 Mov(x1, ExternalReference(Runtime::kDebugBreak, isolate())); 3192 CEntryStub ces(isolate(), 1); 3193 ASSERT(AllowThisStubCall(&ces)); 3194 Call(ces.GetCode(), RelocInfo::DEBUG_BREAK); 3195 } 3196 3197 3198 void MacroAssembler::PushTryHandler(StackHandler::Kind kind, 3199 int handler_index) { 3200 ASSERT(jssp.Is(StackPointer())); 3201 // Adjust this code if the asserts don't hold. 3202 STATIC_ASSERT(StackHandlerConstants::kSize == 5 * kPointerSize); 3203 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); 3204 STATIC_ASSERT(StackHandlerConstants::kCodeOffset == 1 * kPointerSize); 3205 STATIC_ASSERT(StackHandlerConstants::kStateOffset == 2 * kPointerSize); 3206 STATIC_ASSERT(StackHandlerConstants::kContextOffset == 3 * kPointerSize); 3207 STATIC_ASSERT(StackHandlerConstants::kFPOffset == 4 * kPointerSize); 3208 3209 // For the JSEntry handler, we must preserve the live registers x0-x4. 3210 // (See JSEntryStub::GenerateBody().) 3211 3212 unsigned state = 3213 StackHandler::IndexField::encode(handler_index) | 3214 StackHandler::KindField::encode(kind); 3215 3216 // Set up the code object and the state for pushing. 3217 Mov(x10, Operand(CodeObject())); 3218 Mov(x11, state); 3219 3220 // Push the frame pointer, context, state, and code object. 3221 if (kind == StackHandler::JS_ENTRY) { 3222 ASSERT(Smi::FromInt(0) == 0); 3223 Push(xzr, xzr, x11, x10); 3224 } else { 3225 Push(fp, cp, x11, x10); 3226 } 3227 3228 // Link the current handler as the next handler. 3229 Mov(x11, ExternalReference(Isolate::kHandlerAddress, isolate())); 3230 Ldr(x10, MemOperand(x11)); 3231 Push(x10); 3232 // Set this new handler as the current one. 3233 Str(jssp, MemOperand(x11)); 3234 } 3235 3236 3237 void MacroAssembler::PopTryHandler() { 3238 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); 3239 Pop(x10); 3240 Mov(x11, ExternalReference(Isolate::kHandlerAddress, isolate())); 3241 Drop(StackHandlerConstants::kSize - kXRegSize, kByteSizeInBytes); 3242 Str(x10, MemOperand(x11)); 3243 } 3244 3245 3246 void MacroAssembler::Allocate(int object_size, 3247 Register result, 3248 Register scratch1, 3249 Register scratch2, 3250 Label* gc_required, 3251 AllocationFlags flags) { 3252 ASSERT(object_size <= Page::kMaxRegularHeapObjectSize); 3253 if (!FLAG_inline_new) { 3254 if (emit_debug_code()) { 3255 // Trash the registers to simulate an allocation failure. 3256 // We apply salt to the original zap value to easily spot the values. 3257 Mov(result, (kDebugZapValue & ~0xffL) | 0x11L); 3258 Mov(scratch1, (kDebugZapValue & ~0xffL) | 0x21L); 3259 Mov(scratch2, (kDebugZapValue & ~0xffL) | 0x21L); 3260 } 3261 B(gc_required); 3262 return; 3263 } 3264 3265 UseScratchRegisterScope temps(this); 3266 Register scratch3 = temps.AcquireX(); 3267 3268 ASSERT(!AreAliased(result, scratch1, scratch2, scratch3)); 3269 ASSERT(result.Is64Bits() && scratch1.Is64Bits() && scratch2.Is64Bits()); 3270 3271 // Make object size into bytes. 3272 if ((flags & SIZE_IN_WORDS) != 0) { 3273 object_size *= kPointerSize; 3274 } 3275 ASSERT(0 == (object_size & kObjectAlignmentMask)); 3276 3277 // Check relative positions of allocation top and limit addresses. 3278 // The values must be adjacent in memory to allow the use of LDP. 3279 ExternalReference heap_allocation_top = 3280 AllocationUtils::GetAllocationTopReference(isolate(), flags); 3281 ExternalReference heap_allocation_limit = 3282 AllocationUtils::GetAllocationLimitReference(isolate(), flags); 3283 intptr_t top = reinterpret_cast<intptr_t>(heap_allocation_top.address()); 3284 intptr_t limit = reinterpret_cast<intptr_t>(heap_allocation_limit.address()); 3285 ASSERT((limit - top) == kPointerSize); 3286 3287 // Set up allocation top address and object size registers. 3288 Register top_address = scratch1; 3289 Register allocation_limit = scratch2; 3290 Mov(top_address, Operand(heap_allocation_top)); 3291 3292 if ((flags & RESULT_CONTAINS_TOP) == 0) { 3293 // Load allocation top into result and the allocation limit. 3294 Ldp(result, allocation_limit, MemOperand(top_address)); 3295 } else { 3296 if (emit_debug_code()) { 3297 // Assert that result actually contains top on entry. 3298 Ldr(scratch3, MemOperand(top_address)); 3299 Cmp(result, scratch3); 3300 Check(eq, kUnexpectedAllocationTop); 3301 } 3302 // Load the allocation limit. 'result' already contains the allocation top. 3303 Ldr(allocation_limit, MemOperand(top_address, limit - top)); 3304 } 3305 3306 // We can ignore DOUBLE_ALIGNMENT flags here because doubles and pointers have 3307 // the same alignment on ARM64. 3308 STATIC_ASSERT(kPointerAlignment == kDoubleAlignment); 3309 3310 // Calculate new top and bail out if new space is exhausted. 3311 Adds(scratch3, result, object_size); 3312 Ccmp(scratch3, allocation_limit, CFlag, cc); 3313 B(hi, gc_required); 3314 Str(scratch3, MemOperand(top_address)); 3315 3316 // Tag the object if requested. 3317 if ((flags & TAG_OBJECT) != 0) { 3318 ObjectTag(result, result); 3319 } 3320 } 3321 3322 3323 void MacroAssembler::Allocate(Register object_size, 3324 Register result, 3325 Register scratch1, 3326 Register scratch2, 3327 Label* gc_required, 3328 AllocationFlags flags) { 3329 if (!FLAG_inline_new) { 3330 if (emit_debug_code()) { 3331 // Trash the registers to simulate an allocation failure. 3332 // We apply salt to the original zap value to easily spot the values. 3333 Mov(result, (kDebugZapValue & ~0xffL) | 0x11L); 3334 Mov(scratch1, (kDebugZapValue & ~0xffL) | 0x21L); 3335 Mov(scratch2, (kDebugZapValue & ~0xffL) | 0x21L); 3336 } 3337 B(gc_required); 3338 return; 3339 } 3340 3341 UseScratchRegisterScope temps(this); 3342 Register scratch3 = temps.AcquireX(); 3343 3344 ASSERT(!AreAliased(object_size, result, scratch1, scratch2, scratch3)); 3345 ASSERT(object_size.Is64Bits() && result.Is64Bits() && 3346 scratch1.Is64Bits() && scratch2.Is64Bits()); 3347 3348 // Check relative positions of allocation top and limit addresses. 3349 // The values must be adjacent in memory to allow the use of LDP. 3350 ExternalReference heap_allocation_top = 3351 AllocationUtils::GetAllocationTopReference(isolate(), flags); 3352 ExternalReference heap_allocation_limit = 3353 AllocationUtils::GetAllocationLimitReference(isolate(), flags); 3354 intptr_t top = reinterpret_cast<intptr_t>(heap_allocation_top.address()); 3355 intptr_t limit = reinterpret_cast<intptr_t>(heap_allocation_limit.address()); 3356 ASSERT((limit - top) == kPointerSize); 3357 3358 // Set up allocation top address and object size registers. 3359 Register top_address = scratch1; 3360 Register allocation_limit = scratch2; 3361 Mov(top_address, heap_allocation_top); 3362 3363 if ((flags & RESULT_CONTAINS_TOP) == 0) { 3364 // Load allocation top into result and the allocation limit. 3365 Ldp(result, allocation_limit, MemOperand(top_address)); 3366 } else { 3367 if (emit_debug_code()) { 3368 // Assert that result actually contains top on entry. 3369 Ldr(scratch3, MemOperand(top_address)); 3370 Cmp(result, scratch3); 3371 Check(eq, kUnexpectedAllocationTop); 3372 } 3373 // Load the allocation limit. 'result' already contains the allocation top. 3374 Ldr(allocation_limit, MemOperand(top_address, limit - top)); 3375 } 3376 3377 // We can ignore DOUBLE_ALIGNMENT flags here because doubles and pointers have 3378 // the same alignment on ARM64. 3379 STATIC_ASSERT(kPointerAlignment == kDoubleAlignment); 3380 3381 // Calculate new top and bail out if new space is exhausted 3382 if ((flags & SIZE_IN_WORDS) != 0) { 3383 Adds(scratch3, result, Operand(object_size, LSL, kPointerSizeLog2)); 3384 } else { 3385 Adds(scratch3, result, object_size); 3386 } 3387 3388 if (emit_debug_code()) { 3389 Tst(scratch3, kObjectAlignmentMask); 3390 Check(eq, kUnalignedAllocationInNewSpace); 3391 } 3392 3393 Ccmp(scratch3, allocation_limit, CFlag, cc); 3394 B(hi, gc_required); 3395 Str(scratch3, MemOperand(top_address)); 3396 3397 // Tag the object if requested. 3398 if ((flags & TAG_OBJECT) != 0) { 3399 ObjectTag(result, result); 3400 } 3401 } 3402 3403 3404 void MacroAssembler::UndoAllocationInNewSpace(Register object, 3405 Register scratch) { 3406 ExternalReference new_space_allocation_top = 3407 ExternalReference::new_space_allocation_top_address(isolate()); 3408 3409 // Make sure the object has no tag before resetting top. 3410 Bic(object, object, kHeapObjectTagMask); 3411 #ifdef DEBUG 3412 // Check that the object un-allocated is below the current top. 3413 Mov(scratch, new_space_allocation_top); 3414 Ldr(scratch, MemOperand(scratch)); 3415 Cmp(object, scratch); 3416 Check(lt, kUndoAllocationOfNonAllocatedMemory); 3417 #endif 3418 // Write the address of the object to un-allocate as the current top. 3419 Mov(scratch, new_space_allocation_top); 3420 Str(object, MemOperand(scratch)); 3421 } 3422 3423 3424 void MacroAssembler::AllocateTwoByteString(Register result, 3425 Register length, 3426 Register scratch1, 3427 Register scratch2, 3428 Register scratch3, 3429 Label* gc_required) { 3430 ASSERT(!AreAliased(result, length, scratch1, scratch2, scratch3)); 3431 // Calculate the number of bytes needed for the characters in the string while 3432 // observing object alignment. 3433 STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); 3434 Add(scratch1, length, length); // Length in bytes, not chars. 3435 Add(scratch1, scratch1, kObjectAlignmentMask + SeqTwoByteString::kHeaderSize); 3436 Bic(scratch1, scratch1, kObjectAlignmentMask); 3437 3438 // Allocate two-byte string in new space. 3439 Allocate(scratch1, 3440 result, 3441 scratch2, 3442 scratch3, 3443 gc_required, 3444 TAG_OBJECT); 3445 3446 // Set the map, length and hash field. 3447 InitializeNewString(result, 3448 length, 3449 Heap::kStringMapRootIndex, 3450 scratch1, 3451 scratch2); 3452 } 3453 3454 3455 void MacroAssembler::AllocateAsciiString(Register result, 3456 Register length, 3457 Register scratch1, 3458 Register scratch2, 3459 Register scratch3, 3460 Label* gc_required) { 3461 ASSERT(!AreAliased(result, length, scratch1, scratch2, scratch3)); 3462 // Calculate the number of bytes needed for the characters in the string while 3463 // observing object alignment. 3464 STATIC_ASSERT((SeqOneByteString::kHeaderSize & kObjectAlignmentMask) == 0); 3465 STATIC_ASSERT(kCharSize == 1); 3466 Add(scratch1, length, kObjectAlignmentMask + SeqOneByteString::kHeaderSize); 3467 Bic(scratch1, scratch1, kObjectAlignmentMask); 3468 3469 // Allocate ASCII string in new space. 3470 Allocate(scratch1, 3471 result, 3472 scratch2, 3473 scratch3, 3474 gc_required, 3475 TAG_OBJECT); 3476 3477 // Set the map, length and hash field. 3478 InitializeNewString(result, 3479 length, 3480 Heap::kAsciiStringMapRootIndex, 3481 scratch1, 3482 scratch2); 3483 } 3484 3485 3486 void MacroAssembler::AllocateTwoByteConsString(Register result, 3487 Register length, 3488 Register scratch1, 3489 Register scratch2, 3490 Label* gc_required) { 3491 Allocate(ConsString::kSize, result, scratch1, scratch2, gc_required, 3492 TAG_OBJECT); 3493 3494 InitializeNewString(result, 3495 length, 3496 Heap::kConsStringMapRootIndex, 3497 scratch1, 3498 scratch2); 3499 } 3500 3501 3502 void MacroAssembler::AllocateAsciiConsString(Register result, 3503 Register length, 3504 Register scratch1, 3505 Register scratch2, 3506 Label* gc_required) { 3507 Allocate(ConsString::kSize, 3508 result, 3509 scratch1, 3510 scratch2, 3511 gc_required, 3512 TAG_OBJECT); 3513 3514 InitializeNewString(result, 3515 length, 3516 Heap::kConsAsciiStringMapRootIndex, 3517 scratch1, 3518 scratch2); 3519 } 3520 3521 3522 void MacroAssembler::AllocateTwoByteSlicedString(Register result, 3523 Register length, 3524 Register scratch1, 3525 Register scratch2, 3526 Label* gc_required) { 3527 ASSERT(!AreAliased(result, length, scratch1, scratch2)); 3528 Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required, 3529 TAG_OBJECT); 3530 3531 InitializeNewString(result, 3532 length, 3533 Heap::kSlicedStringMapRootIndex, 3534 scratch1, 3535 scratch2); 3536 } 3537 3538 3539 void MacroAssembler::AllocateAsciiSlicedString(Register result, 3540 Register length, 3541 Register scratch1, 3542 Register scratch2, 3543 Label* gc_required) { 3544 ASSERT(!AreAliased(result, length, scratch1, scratch2)); 3545 Allocate(SlicedString::kSize, result, scratch1, scratch2, gc_required, 3546 TAG_OBJECT); 3547 3548 InitializeNewString(result, 3549 length, 3550 Heap::kSlicedAsciiStringMapRootIndex, 3551 scratch1, 3552 scratch2); 3553 } 3554 3555 3556 // Allocates a heap number or jumps to the need_gc label if the young space 3557 // is full and a scavenge is needed. 3558 void MacroAssembler::AllocateHeapNumber(Register result, 3559 Label* gc_required, 3560 Register scratch1, 3561 Register scratch2, 3562 CPURegister value, 3563 CPURegister heap_number_map) { 3564 ASSERT(!value.IsValid() || value.Is64Bits()); 3565 UseScratchRegisterScope temps(this); 3566 3567 // Allocate an object in the heap for the heap number and tag it as a heap 3568 // object. 3569 Allocate(HeapNumber::kSize, result, scratch1, scratch2, gc_required, 3570 NO_ALLOCATION_FLAGS); 3571 3572 // Prepare the heap number map. 3573 if (!heap_number_map.IsValid()) { 3574 // If we have a valid value register, use the same type of register to store 3575 // the map so we can use STP to store both in one instruction. 3576 if (value.IsValid() && value.IsFPRegister()) { 3577 heap_number_map = temps.AcquireD(); 3578 } else { 3579 heap_number_map = scratch1; 3580 } 3581 LoadRoot(heap_number_map, Heap::kHeapNumberMapRootIndex); 3582 } 3583 if (emit_debug_code()) { 3584 Register map; 3585 if (heap_number_map.IsFPRegister()) { 3586 map = scratch1; 3587 Fmov(map, DoubleRegister(heap_number_map)); 3588 } else { 3589 map = Register(heap_number_map); 3590 } 3591 AssertRegisterIsRoot(map, Heap::kHeapNumberMapRootIndex); 3592 } 3593 3594 // Store the heap number map and the value in the allocated object. 3595 if (value.IsSameSizeAndType(heap_number_map)) { 3596 STATIC_ASSERT(HeapObject::kMapOffset + kPointerSize == 3597 HeapNumber::kValueOffset); 3598 Stp(heap_number_map, value, MemOperand(result, HeapObject::kMapOffset)); 3599 } else { 3600 Str(heap_number_map, MemOperand(result, HeapObject::kMapOffset)); 3601 if (value.IsValid()) { 3602 Str(value, MemOperand(result, HeapNumber::kValueOffset)); 3603 } 3604 } 3605 ObjectTag(result, result); 3606 } 3607 3608 3609 void MacroAssembler::JumpIfObjectType(Register object, 3610 Register map, 3611 Register type_reg, 3612 InstanceType type, 3613 Label* if_cond_pass, 3614 Condition cond) { 3615 CompareObjectType(object, map, type_reg, type); 3616 B(cond, if_cond_pass); 3617 } 3618 3619 3620 void MacroAssembler::JumpIfNotObjectType(Register object, 3621 Register map, 3622 Register type_reg, 3623 InstanceType type, 3624 Label* if_not_object) { 3625 JumpIfObjectType(object, map, type_reg, type, if_not_object, ne); 3626 } 3627 3628 3629 // Sets condition flags based on comparison, and returns type in type_reg. 3630 void MacroAssembler::CompareObjectType(Register object, 3631 Register map, 3632 Register type_reg, 3633 InstanceType type) { 3634 Ldr(map, FieldMemOperand(object, HeapObject::kMapOffset)); 3635 CompareInstanceType(map, type_reg, type); 3636 } 3637 3638 3639 // Sets condition flags based on comparison, and returns type in type_reg. 3640 void MacroAssembler::CompareInstanceType(Register map, 3641 Register type_reg, 3642 InstanceType type) { 3643 Ldrb(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); 3644 Cmp(type_reg, type); 3645 } 3646 3647 3648 void MacroAssembler::CompareMap(Register obj, 3649 Register scratch, 3650 Handle<Map> map) { 3651 Ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); 3652 CompareMap(scratch, map); 3653 } 3654 3655 3656 void MacroAssembler::CompareMap(Register obj_map, 3657 Handle<Map> map) { 3658 Cmp(obj_map, Operand(map)); 3659 } 3660 3661 3662 void MacroAssembler::CheckMap(Register obj, 3663 Register scratch, 3664 Handle<Map> map, 3665 Label* fail, 3666 SmiCheckType smi_check_type) { 3667 if (smi_check_type == DO_SMI_CHECK) { 3668 JumpIfSmi(obj, fail); 3669 } 3670 3671 CompareMap(obj, scratch, map); 3672 B(ne, fail); 3673 } 3674 3675 3676 void MacroAssembler::CheckMap(Register obj, 3677 Register scratch, 3678 Heap::RootListIndex index, 3679 Label* fail, 3680 SmiCheckType smi_check_type) { 3681 if (smi_check_type == DO_SMI_CHECK) { 3682 JumpIfSmi(obj, fail); 3683 } 3684 Ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); 3685 JumpIfNotRoot(scratch, index, fail); 3686 } 3687 3688 3689 void MacroAssembler::CheckMap(Register obj_map, 3690 Handle<Map> map, 3691 Label* fail, 3692 SmiCheckType smi_check_type) { 3693 if (smi_check_type == DO_SMI_CHECK) { 3694 JumpIfSmi(obj_map, fail); 3695 } 3696 3697 CompareMap(obj_map, map); 3698 B(ne, fail); 3699 } 3700 3701 3702 void MacroAssembler::DispatchMap(Register obj, 3703 Register scratch, 3704 Handle<Map> map, 3705 Handle<Code> success, 3706 SmiCheckType smi_check_type) { 3707 Label fail; 3708 if (smi_check_type == DO_SMI_CHECK) { 3709 JumpIfSmi(obj, &fail); 3710 } 3711 Ldr(scratch, FieldMemOperand(obj, HeapObject::kMapOffset)); 3712 Cmp(scratch, Operand(map)); 3713 B(ne, &fail); 3714 Jump(success, RelocInfo::CODE_TARGET); 3715 Bind(&fail); 3716 } 3717 3718 3719 void MacroAssembler::TestMapBitfield(Register object, uint64_t mask) { 3720 UseScratchRegisterScope temps(this); 3721 Register temp = temps.AcquireX(); 3722 Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); 3723 Ldrb(temp, FieldMemOperand(temp, Map::kBitFieldOffset)); 3724 Tst(temp, mask); 3725 } 3726 3727 3728 void MacroAssembler::LoadElementsKindFromMap(Register result, Register map) { 3729 // Load the map's "bit field 2". 3730 __ Ldrb(result, FieldMemOperand(map, Map::kBitField2Offset)); 3731 // Retrieve elements_kind from bit field 2. 3732 DecodeField<Map::ElementsKindBits>(result); 3733 } 3734 3735 3736 void MacroAssembler::TryGetFunctionPrototype(Register function, 3737 Register result, 3738 Register scratch, 3739 Label* miss, 3740 BoundFunctionAction action) { 3741 ASSERT(!AreAliased(function, result, scratch)); 3742 3743 // Check that the receiver isn't a smi. 3744 JumpIfSmi(function, miss); 3745 3746 // Check that the function really is a function. Load map into result reg. 3747 JumpIfNotObjectType(function, result, scratch, JS_FUNCTION_TYPE, miss); 3748 3749 if (action == kMissOnBoundFunction) { 3750 Register scratch_w = scratch.W(); 3751 Ldr(scratch, 3752 FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset)); 3753 // On 64-bit platforms, compiler hints field is not a smi. See definition of 3754 // kCompilerHintsOffset in src/objects.h. 3755 Ldr(scratch_w, 3756 FieldMemOperand(scratch, SharedFunctionInfo::kCompilerHintsOffset)); 3757 Tbnz(scratch, SharedFunctionInfo::kBoundFunction, miss); 3758 } 3759 3760 // Make sure that the function has an instance prototype. 3761 Label non_instance; 3762 Ldrb(scratch, FieldMemOperand(result, Map::kBitFieldOffset)); 3763 Tbnz(scratch, Map::kHasNonInstancePrototype, &non_instance); 3764 3765 // Get the prototype or initial map from the function. 3766 Ldr(result, 3767 FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); 3768 3769 // If the prototype or initial map is the hole, don't return it and simply 3770 // miss the cache instead. This will allow us to allocate a prototype object 3771 // on-demand in the runtime system. 3772 JumpIfRoot(result, Heap::kTheHoleValueRootIndex, miss); 3773 3774 // If the function does not have an initial map, we're done. 3775 Label done; 3776 JumpIfNotObjectType(result, scratch, scratch, MAP_TYPE, &done); 3777 3778 // Get the prototype from the initial map. 3779 Ldr(result, FieldMemOperand(result, Map::kPrototypeOffset)); 3780 B(&done); 3781 3782 // Non-instance prototype: fetch prototype from constructor field in initial 3783 // map. 3784 Bind(&non_instance); 3785 Ldr(result, FieldMemOperand(result, Map::kConstructorOffset)); 3786 3787 // All done. 3788 Bind(&done); 3789 } 3790 3791 3792 void MacroAssembler::CompareRoot(const Register& obj, 3793 Heap::RootListIndex index) { 3794 UseScratchRegisterScope temps(this); 3795 Register temp = temps.AcquireX(); 3796 ASSERT(!AreAliased(obj, temp)); 3797 LoadRoot(temp, index); 3798 Cmp(obj, temp); 3799 } 3800 3801 3802 void MacroAssembler::JumpIfRoot(const Register& obj, 3803 Heap::RootListIndex index, 3804 Label* if_equal) { 3805 CompareRoot(obj, index); 3806 B(eq, if_equal); 3807 } 3808 3809 3810 void MacroAssembler::JumpIfNotRoot(const Register& obj, 3811 Heap::RootListIndex index, 3812 Label* if_not_equal) { 3813 CompareRoot(obj, index); 3814 B(ne, if_not_equal); 3815 } 3816 3817 3818 void MacroAssembler::CompareAndSplit(const Register& lhs, 3819 const Operand& rhs, 3820 Condition cond, 3821 Label* if_true, 3822 Label* if_false, 3823 Label* fall_through) { 3824 if ((if_true == if_false) && (if_false == fall_through)) { 3825 // Fall through. 3826 } else if (if_true == if_false) { 3827 B(if_true); 3828 } else if (if_false == fall_through) { 3829 CompareAndBranch(lhs, rhs, cond, if_true); 3830 } else if (if_true == fall_through) { 3831 CompareAndBranch(lhs, rhs, NegateCondition(cond), if_false); 3832 } else { 3833 CompareAndBranch(lhs, rhs, cond, if_true); 3834 B(if_false); 3835 } 3836 } 3837 3838 3839 void MacroAssembler::TestAndSplit(const Register& reg, 3840 uint64_t bit_pattern, 3841 Label* if_all_clear, 3842 Label* if_any_set, 3843 Label* fall_through) { 3844 if ((if_all_clear == if_any_set) && (if_any_set == fall_through)) { 3845 // Fall through. 3846 } else if (if_all_clear == if_any_set) { 3847 B(if_all_clear); 3848 } else if (if_all_clear == fall_through) { 3849 TestAndBranchIfAnySet(reg, bit_pattern, if_any_set); 3850 } else if (if_any_set == fall_through) { 3851 TestAndBranchIfAllClear(reg, bit_pattern, if_all_clear); 3852 } else { 3853 TestAndBranchIfAnySet(reg, bit_pattern, if_any_set); 3854 B(if_all_clear); 3855 } 3856 } 3857 3858 3859 void MacroAssembler::CheckFastElements(Register map, 3860 Register scratch, 3861 Label* fail) { 3862 STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); 3863 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); 3864 STATIC_ASSERT(FAST_ELEMENTS == 2); 3865 STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); 3866 Ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); 3867 Cmp(scratch, Map::kMaximumBitField2FastHoleyElementValue); 3868 B(hi, fail); 3869 } 3870 3871 3872 void MacroAssembler::CheckFastObjectElements(Register map, 3873 Register scratch, 3874 Label* fail) { 3875 STATIC_ASSERT(FAST_SMI_ELEMENTS == 0); 3876 STATIC_ASSERT(FAST_HOLEY_SMI_ELEMENTS == 1); 3877 STATIC_ASSERT(FAST_ELEMENTS == 2); 3878 STATIC_ASSERT(FAST_HOLEY_ELEMENTS == 3); 3879 Ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); 3880 Cmp(scratch, Operand(Map::kMaximumBitField2FastHoleySmiElementValue)); 3881 // If cond==ls, set cond=hi, otherwise compare. 3882 Ccmp(scratch, 3883 Operand(Map::kMaximumBitField2FastHoleyElementValue), CFlag, hi); 3884 B(hi, fail); 3885 } 3886 3887 3888 // Note: The ARM version of this clobbers elements_reg, but this version does 3889 // not. Some uses of this in ARM64 assume that elements_reg will be preserved. 3890 void MacroAssembler::StoreNumberToDoubleElements(Register value_reg, 3891 Register key_reg, 3892 Register elements_reg, 3893 Register scratch1, 3894 FPRegister fpscratch1, 3895 Label* fail, 3896 int elements_offset) { 3897 ASSERT(!AreAliased(value_reg, key_reg, elements_reg, scratch1)); 3898 Label store_num; 3899 3900 // Speculatively convert the smi to a double - all smis can be exactly 3901 // represented as a double. 3902 SmiUntagToDouble(fpscratch1, value_reg, kSpeculativeUntag); 3903 3904 // If value_reg is a smi, we're done. 3905 JumpIfSmi(value_reg, &store_num); 3906 3907 // Ensure that the object is a heap number. 3908 CheckMap(value_reg, scratch1, isolate()->factory()->heap_number_map(), 3909 fail, DONT_DO_SMI_CHECK); 3910 3911 Ldr(fpscratch1, FieldMemOperand(value_reg, HeapNumber::kValueOffset)); 3912 3913 // Canonicalize NaNs. 3914 CanonicalizeNaN(fpscratch1); 3915 3916 // Store the result. 3917 Bind(&store_num); 3918 Add(scratch1, elements_reg, 3919 Operand::UntagSmiAndScale(key_reg, kDoubleSizeLog2)); 3920 Str(fpscratch1, 3921 FieldMemOperand(scratch1, 3922 FixedDoubleArray::kHeaderSize - elements_offset)); 3923 } 3924 3925 3926 bool MacroAssembler::AllowThisStubCall(CodeStub* stub) { 3927 return has_frame_ || !stub->SometimesSetsUpAFrame(); 3928 } 3929 3930 3931 void MacroAssembler::IndexFromHash(Register hash, Register index) { 3932 // If the hash field contains an array index pick it out. The assert checks 3933 // that the constants for the maximum number of digits for an array index 3934 // cached in the hash field and the number of bits reserved for it does not 3935 // conflict. 3936 ASSERT(TenToThe(String::kMaxCachedArrayIndexLength) < 3937 (1 << String::kArrayIndexValueBits)); 3938 DecodeField<String::ArrayIndexValueBits>(index, hash); 3939 SmiTag(index, index); 3940 } 3941 3942 3943 void MacroAssembler::EmitSeqStringSetCharCheck( 3944 Register string, 3945 Register index, 3946 SeqStringSetCharCheckIndexType index_type, 3947 Register scratch, 3948 uint32_t encoding_mask) { 3949 ASSERT(!AreAliased(string, index, scratch)); 3950 3951 if (index_type == kIndexIsSmi) { 3952 AssertSmi(index); 3953 } 3954 3955 // Check that string is an object. 3956 AssertNotSmi(string, kNonObject); 3957 3958 // Check that string has an appropriate map. 3959 Ldr(scratch, FieldMemOperand(string, HeapObject::kMapOffset)); 3960 Ldrb(scratch, FieldMemOperand(scratch, Map::kInstanceTypeOffset)); 3961 3962 And(scratch, scratch, kStringRepresentationMask | kStringEncodingMask); 3963 Cmp(scratch, encoding_mask); 3964 Check(eq, kUnexpectedStringType); 3965 3966 Ldr(scratch, FieldMemOperand(string, String::kLengthOffset)); 3967 Cmp(index, index_type == kIndexIsSmi ? scratch : Operand::UntagSmi(scratch)); 3968 Check(lt, kIndexIsTooLarge); 3969 3970 ASSERT_EQ(0, Smi::FromInt(0)); 3971 Cmp(index, 0); 3972 Check(ge, kIndexIsNegative); 3973 } 3974 3975 3976 void MacroAssembler::CheckAccessGlobalProxy(Register holder_reg, 3977 Register scratch1, 3978 Register scratch2, 3979 Label* miss) { 3980 ASSERT(!AreAliased(holder_reg, scratch1, scratch2)); 3981 Label same_contexts; 3982 3983 // Load current lexical context from the stack frame. 3984 Ldr(scratch1, MemOperand(fp, StandardFrameConstants::kContextOffset)); 3985 // In debug mode, make sure the lexical context is set. 3986 #ifdef DEBUG 3987 Cmp(scratch1, 0); 3988 Check(ne, kWeShouldNotHaveAnEmptyLexicalContext); 3989 #endif 3990 3991 // Load the native context of the current context. 3992 int offset = 3993 Context::kHeaderSize + Context::GLOBAL_OBJECT_INDEX * kPointerSize; 3994 Ldr(scratch1, FieldMemOperand(scratch1, offset)); 3995 Ldr(scratch1, FieldMemOperand(scratch1, GlobalObject::kNativeContextOffset)); 3996 3997 // Check the context is a native context. 3998 if (emit_debug_code()) { 3999 // Read the first word and compare to the global_context_map. 4000 Ldr(scratch2, FieldMemOperand(scratch1, HeapObject::kMapOffset)); 4001 CompareRoot(scratch2, Heap::kNativeContextMapRootIndex); 4002 Check(eq, kExpectedNativeContext); 4003 } 4004 4005 // Check if both contexts are the same. 4006 Ldr(scratch2, FieldMemOperand(holder_reg, 4007 JSGlobalProxy::kNativeContextOffset)); 4008 Cmp(scratch1, scratch2); 4009 B(&same_contexts, eq); 4010 4011 // Check the context is a native context. 4012 if (emit_debug_code()) { 4013 // We're short on scratch registers here, so use holder_reg as a scratch. 4014 Push(holder_reg); 4015 Register scratch3 = holder_reg; 4016 4017 CompareRoot(scratch2, Heap::kNullValueRootIndex); 4018 Check(ne, kExpectedNonNullContext); 4019 4020 Ldr(scratch3, FieldMemOperand(scratch2, HeapObject::kMapOffset)); 4021 CompareRoot(scratch3, Heap::kNativeContextMapRootIndex); 4022 Check(eq, kExpectedNativeContext); 4023 Pop(holder_reg); 4024 } 4025 4026 // Check that the security token in the calling global object is 4027 // compatible with the security token in the receiving global 4028 // object. 4029 int token_offset = Context::kHeaderSize + 4030 Context::SECURITY_TOKEN_INDEX * kPointerSize; 4031 4032 Ldr(scratch1, FieldMemOperand(scratch1, token_offset)); 4033 Ldr(scratch2, FieldMemOperand(scratch2, token_offset)); 4034 Cmp(scratch1, scratch2); 4035 B(miss, ne); 4036 4037 Bind(&same_contexts); 4038 } 4039 4040 4041 // Compute the hash code from the untagged key. This must be kept in sync with 4042 // ComputeIntegerHash in utils.h and KeyedLoadGenericElementStub in 4043 // code-stub-hydrogen.cc 4044 void MacroAssembler::GetNumberHash(Register key, Register scratch) { 4045 ASSERT(!AreAliased(key, scratch)); 4046 4047 // Xor original key with a seed. 4048 LoadRoot(scratch, Heap::kHashSeedRootIndex); 4049 Eor(key, key, Operand::UntagSmi(scratch)); 4050 4051 // The algorithm uses 32-bit integer values. 4052 key = key.W(); 4053 scratch = scratch.W(); 4054 4055 // Compute the hash code from the untagged key. This must be kept in sync 4056 // with ComputeIntegerHash in utils.h. 4057 // 4058 // hash = ~hash + (hash <<1 15); 4059 Mvn(scratch, key); 4060 Add(key, scratch, Operand(key, LSL, 15)); 4061 // hash = hash ^ (hash >> 12); 4062 Eor(key, key, Operand(key, LSR, 12)); 4063 // hash = hash + (hash << 2); 4064 Add(key, key, Operand(key, LSL, 2)); 4065 // hash = hash ^ (hash >> 4); 4066 Eor(key, key, Operand(key, LSR, 4)); 4067 // hash = hash * 2057; 4068 Mov(scratch, Operand(key, LSL, 11)); 4069 Add(key, key, Operand(key, LSL, 3)); 4070 Add(key, key, scratch); 4071 // hash = hash ^ (hash >> 16); 4072 Eor(key, key, Operand(key, LSR, 16)); 4073 } 4074 4075 4076 void MacroAssembler::LoadFromNumberDictionary(Label* miss, 4077 Register elements, 4078 Register key, 4079 Register result, 4080 Register scratch0, 4081 Register scratch1, 4082 Register scratch2, 4083 Register scratch3) { 4084 ASSERT(!AreAliased(elements, key, scratch0, scratch1, scratch2, scratch3)); 4085 4086 Label done; 4087 4088 SmiUntag(scratch0, key); 4089 GetNumberHash(scratch0, scratch1); 4090 4091 // Compute the capacity mask. 4092 Ldrsw(scratch1, 4093 UntagSmiFieldMemOperand(elements, 4094 SeededNumberDictionary::kCapacityOffset)); 4095 Sub(scratch1, scratch1, 1); 4096 4097 // Generate an unrolled loop that performs a few probes before giving up. 4098 for (int i = 0; i < kNumberDictionaryProbes; i++) { 4099 // Compute the masked index: (hash + i + i * i) & mask. 4100 if (i > 0) { 4101 Add(scratch2, scratch0, SeededNumberDictionary::GetProbeOffset(i)); 4102 } else { 4103 Mov(scratch2, scratch0); 4104 } 4105 And(scratch2, scratch2, scratch1); 4106 4107 // Scale the index by multiplying by the element size. 4108 ASSERT(SeededNumberDictionary::kEntrySize == 3); 4109 Add(scratch2, scratch2, Operand(scratch2, LSL, 1)); 4110 4111 // Check if the key is identical to the name. 4112 Add(scratch2, elements, Operand(scratch2, LSL, kPointerSizeLog2)); 4113 Ldr(scratch3, 4114 FieldMemOperand(scratch2, 4115 SeededNumberDictionary::kElementsStartOffset)); 4116 Cmp(key, scratch3); 4117 if (i != (kNumberDictionaryProbes - 1)) { 4118 B(eq, &done); 4119 } else { 4120 B(ne, miss); 4121 } 4122 } 4123 4124 Bind(&done); 4125 // Check that the value is a normal property. 4126 const int kDetailsOffset = 4127 SeededNumberDictionary::kElementsStartOffset + 2 * kPointerSize; 4128 Ldrsw(scratch1, UntagSmiFieldMemOperand(scratch2, kDetailsOffset)); 4129 TestAndBranchIfAnySet(scratch1, PropertyDetails::TypeField::kMask, miss); 4130 4131 // Get the value at the masked, scaled index and return. 4132 const int kValueOffset = 4133 SeededNumberDictionary::kElementsStartOffset + kPointerSize; 4134 Ldr(result, FieldMemOperand(scratch2, kValueOffset)); 4135 } 4136 4137 4138 void MacroAssembler::RememberedSetHelper(Register object, // For debug tests. 4139 Register address, 4140 Register scratch1, 4141 SaveFPRegsMode fp_mode, 4142 RememberedSetFinalAction and_then) { 4143 ASSERT(!AreAliased(object, address, scratch1)); 4144 Label done, store_buffer_overflow; 4145 if (emit_debug_code()) { 4146 Label ok; 4147 JumpIfNotInNewSpace(object, &ok); 4148 Abort(kRememberedSetPointerInNewSpace); 4149 bind(&ok); 4150 } 4151 UseScratchRegisterScope temps(this); 4152 Register scratch2 = temps.AcquireX(); 4153 4154 // Load store buffer top. 4155 Mov(scratch2, ExternalReference::store_buffer_top(isolate())); 4156 Ldr(scratch1, MemOperand(scratch2)); 4157 // Store pointer to buffer and increment buffer top. 4158 Str(address, MemOperand(scratch1, kPointerSize, PostIndex)); 4159 // Write back new top of buffer. 4160 Str(scratch1, MemOperand(scratch2)); 4161 // Call stub on end of buffer. 4162 // Check for end of buffer. 4163 ASSERT(StoreBuffer::kStoreBufferOverflowBit == 4164 (1 << (14 + kPointerSizeLog2))); 4165 if (and_then == kFallThroughAtEnd) { 4166 Tbz(scratch1, (14 + kPointerSizeLog2), &done); 4167 } else { 4168 ASSERT(and_then == kReturnAtEnd); 4169 Tbnz(scratch1, (14 + kPointerSizeLog2), &store_buffer_overflow); 4170 Ret(); 4171 } 4172 4173 Bind(&store_buffer_overflow); 4174 Push(lr); 4175 StoreBufferOverflowStub store_buffer_overflow_stub = 4176 StoreBufferOverflowStub(isolate(), fp_mode); 4177 CallStub(&store_buffer_overflow_stub); 4178 Pop(lr); 4179 4180 Bind(&done); 4181 if (and_then == kReturnAtEnd) { 4182 Ret(); 4183 } 4184 } 4185 4186 4187 void MacroAssembler::PopSafepointRegisters() { 4188 const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; 4189 PopXRegList(kSafepointSavedRegisters); 4190 Drop(num_unsaved); 4191 } 4192 4193 4194 void MacroAssembler::PushSafepointRegisters() { 4195 // Safepoints expect a block of kNumSafepointRegisters values on the stack, so 4196 // adjust the stack for unsaved registers. 4197 const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; 4198 ASSERT(num_unsaved >= 0); 4199 Claim(num_unsaved); 4200 PushXRegList(kSafepointSavedRegisters); 4201 } 4202 4203 4204 void MacroAssembler::PushSafepointRegistersAndDoubles() { 4205 PushSafepointRegisters(); 4206 PushCPURegList(CPURegList(CPURegister::kFPRegister, kDRegSizeInBits, 4207 FPRegister::kAllocatableFPRegisters)); 4208 } 4209 4210 4211 void MacroAssembler::PopSafepointRegistersAndDoubles() { 4212 PopCPURegList(CPURegList(CPURegister::kFPRegister, kDRegSizeInBits, 4213 FPRegister::kAllocatableFPRegisters)); 4214 PopSafepointRegisters(); 4215 } 4216 4217 4218 int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { 4219 // Make sure the safepoint registers list is what we expect. 4220 ASSERT(CPURegList::GetSafepointSavedRegisters().list() == 0x6ffcffff); 4221 4222 // Safepoint registers are stored contiguously on the stack, but not all the 4223 // registers are saved. The following registers are excluded: 4224 // - x16 and x17 (ip0 and ip1) because they shouldn't be preserved outside of 4225 // the macro assembler. 4226 // - x28 (jssp) because JS stack pointer doesn't need to be included in 4227 // safepoint registers. 4228 // - x31 (csp) because the system stack pointer doesn't need to be included 4229 // in safepoint registers. 4230 // 4231 // This function implements the mapping of register code to index into the 4232 // safepoint register slots. 4233 if ((reg_code >= 0) && (reg_code <= 15)) { 4234 return reg_code; 4235 } else if ((reg_code >= 18) && (reg_code <= 27)) { 4236 // Skip ip0 and ip1. 4237 return reg_code - 2; 4238 } else if ((reg_code == 29) || (reg_code == 30)) { 4239 // Also skip jssp. 4240 return reg_code - 3; 4241 } else { 4242 // This register has no safepoint register slot. 4243 UNREACHABLE(); 4244 return -1; 4245 } 4246 } 4247 4248 4249 void MacroAssembler::CheckPageFlagSet(const Register& object, 4250 const Register& scratch, 4251 int mask, 4252 Label* if_any_set) { 4253 And(scratch, object, ~Page::kPageAlignmentMask); 4254 Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); 4255 TestAndBranchIfAnySet(scratch, mask, if_any_set); 4256 } 4257 4258 4259 void MacroAssembler::CheckPageFlagClear(const Register& object, 4260 const Register& scratch, 4261 int mask, 4262 Label* if_all_clear) { 4263 And(scratch, object, ~Page::kPageAlignmentMask); 4264 Ldr(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); 4265 TestAndBranchIfAllClear(scratch, mask, if_all_clear); 4266 } 4267 4268 4269 void MacroAssembler::RecordWriteField( 4270 Register object, 4271 int offset, 4272 Register value, 4273 Register scratch, 4274 LinkRegisterStatus lr_status, 4275 SaveFPRegsMode save_fp, 4276 RememberedSetAction remembered_set_action, 4277 SmiCheck smi_check, 4278 PointersToHereCheck pointers_to_here_check_for_value) { 4279 // First, check if a write barrier is even needed. The tests below 4280 // catch stores of Smis. 4281 Label done; 4282 4283 // Skip the barrier if writing a smi. 4284 if (smi_check == INLINE_SMI_CHECK) { 4285 JumpIfSmi(value, &done); 4286 } 4287 4288 // Although the object register is tagged, the offset is relative to the start 4289 // of the object, so offset must be a multiple of kPointerSize. 4290 ASSERT(IsAligned(offset, kPointerSize)); 4291 4292 Add(scratch, object, offset - kHeapObjectTag); 4293 if (emit_debug_code()) { 4294 Label ok; 4295 Tst(scratch, (1 << kPointerSizeLog2) - 1); 4296 B(eq, &ok); 4297 Abort(kUnalignedCellInWriteBarrier); 4298 Bind(&ok); 4299 } 4300 4301 RecordWrite(object, 4302 scratch, 4303 value, 4304 lr_status, 4305 save_fp, 4306 remembered_set_action, 4307 OMIT_SMI_CHECK, 4308 pointers_to_here_check_for_value); 4309 4310 Bind(&done); 4311 4312 // Clobber clobbered input registers when running with the debug-code flag 4313 // turned on to provoke errors. 4314 if (emit_debug_code()) { 4315 Mov(value, Operand(BitCast<int64_t>(kZapValue + 4))); 4316 Mov(scratch, Operand(BitCast<int64_t>(kZapValue + 8))); 4317 } 4318 } 4319 4320 4321 // Will clobber: object, map, dst. 4322 // If lr_status is kLRHasBeenSaved, lr will also be clobbered. 4323 void MacroAssembler::RecordWriteForMap(Register object, 4324 Register map, 4325 Register dst, 4326 LinkRegisterStatus lr_status, 4327 SaveFPRegsMode fp_mode) { 4328 ASM_LOCATION("MacroAssembler::RecordWrite"); 4329 ASSERT(!AreAliased(object, map)); 4330 4331 if (emit_debug_code()) { 4332 UseScratchRegisterScope temps(this); 4333 Register temp = temps.AcquireX(); 4334 4335 CompareMap(map, temp, isolate()->factory()->meta_map()); 4336 Check(eq, kWrongAddressOrValuePassedToRecordWrite); 4337 } 4338 4339 if (!FLAG_incremental_marking) { 4340 return; 4341 } 4342 4343 if (emit_debug_code()) { 4344 UseScratchRegisterScope temps(this); 4345 Register temp = temps.AcquireX(); 4346 4347 Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); 4348 Cmp(temp, map); 4349 Check(eq, kWrongAddressOrValuePassedToRecordWrite); 4350 } 4351 4352 // Count number of write barriers in generated code. 4353 isolate()->counters()->write_barriers_static()->Increment(); 4354 // TODO(mstarzinger): Dynamic counter missing. 4355 4356 // First, check if a write barrier is even needed. The tests below 4357 // catch stores of smis and stores into the young generation. 4358 Label done; 4359 4360 // A single check of the map's pages interesting flag suffices, since it is 4361 // only set during incremental collection, and then it's also guaranteed that 4362 // the from object's page's interesting flag is also set. This optimization 4363 // relies on the fact that maps can never be in new space. 4364 CheckPageFlagClear(map, 4365 map, // Used as scratch. 4366 MemoryChunk::kPointersToHereAreInterestingMask, 4367 &done); 4368 4369 // Record the actual write. 4370 if (lr_status == kLRHasNotBeenSaved) { 4371 Push(lr); 4372 } 4373 Add(dst, object, HeapObject::kMapOffset - kHeapObjectTag); 4374 RecordWriteStub stub(isolate(), object, map, dst, OMIT_REMEMBERED_SET, 4375 fp_mode); 4376 CallStub(&stub); 4377 if (lr_status == kLRHasNotBeenSaved) { 4378 Pop(lr); 4379 } 4380 4381 Bind(&done); 4382 4383 // Clobber clobbered registers when running with the debug-code flag 4384 // turned on to provoke errors. 4385 if (emit_debug_code()) { 4386 Mov(dst, Operand(BitCast<int64_t>(kZapValue + 12))); 4387 Mov(map, Operand(BitCast<int64_t>(kZapValue + 16))); 4388 } 4389 } 4390 4391 4392 // Will clobber: object, address, value. 4393 // If lr_status is kLRHasBeenSaved, lr will also be clobbered. 4394 // 4395 // The register 'object' contains a heap object pointer. The heap object tag is 4396 // shifted away. 4397 void MacroAssembler::RecordWrite( 4398 Register object, 4399 Register address, 4400 Register value, 4401 LinkRegisterStatus lr_status, 4402 SaveFPRegsMode fp_mode, 4403 RememberedSetAction remembered_set_action, 4404 SmiCheck smi_check, 4405 PointersToHereCheck pointers_to_here_check_for_value) { 4406 ASM_LOCATION("MacroAssembler::RecordWrite"); 4407 ASSERT(!AreAliased(object, value)); 4408 4409 if (emit_debug_code()) { 4410 UseScratchRegisterScope temps(this); 4411 Register temp = temps.AcquireX(); 4412 4413 Ldr(temp, MemOperand(address)); 4414 Cmp(temp, value); 4415 Check(eq, kWrongAddressOrValuePassedToRecordWrite); 4416 } 4417 4418 // Count number of write barriers in generated code. 4419 isolate()->counters()->write_barriers_static()->Increment(); 4420 // TODO(mstarzinger): Dynamic counter missing. 4421 4422 // First, check if a write barrier is even needed. The tests below 4423 // catch stores of smis and stores into the young generation. 4424 Label done; 4425 4426 if (smi_check == INLINE_SMI_CHECK) { 4427 ASSERT_EQ(0, kSmiTag); 4428 JumpIfSmi(value, &done); 4429 } 4430 4431 if (pointers_to_here_check_for_value != kPointersToHereAreAlwaysInteresting) { 4432 CheckPageFlagClear(value, 4433 value, // Used as scratch. 4434 MemoryChunk::kPointersToHereAreInterestingMask, 4435 &done); 4436 } 4437 CheckPageFlagClear(object, 4438 value, // Used as scratch. 4439 MemoryChunk::kPointersFromHereAreInterestingMask, 4440 &done); 4441 4442 // Record the actual write. 4443 if (lr_status == kLRHasNotBeenSaved) { 4444 Push(lr); 4445 } 4446 RecordWriteStub stub(isolate(), object, value, address, remembered_set_action, 4447 fp_mode); 4448 CallStub(&stub); 4449 if (lr_status == kLRHasNotBeenSaved) { 4450 Pop(lr); 4451 } 4452 4453 Bind(&done); 4454 4455 // Clobber clobbered registers when running with the debug-code flag 4456 // turned on to provoke errors. 4457 if (emit_debug_code()) { 4458 Mov(address, Operand(BitCast<int64_t>(kZapValue + 12))); 4459 Mov(value, Operand(BitCast<int64_t>(kZapValue + 16))); 4460 } 4461 } 4462 4463 4464 void MacroAssembler::AssertHasValidColor(const Register& reg) { 4465 if (emit_debug_code()) { 4466 // The bit sequence is backward. The first character in the string 4467 // represents the least significant bit. 4468 ASSERT(strcmp(Marking::kImpossibleBitPattern, "01") == 0); 4469 4470 Label color_is_valid; 4471 Tbnz(reg, 0, &color_is_valid); 4472 Tbz(reg, 1, &color_is_valid); 4473 Abort(kUnexpectedColorFound); 4474 Bind(&color_is_valid); 4475 } 4476 } 4477 4478 4479 void MacroAssembler::GetMarkBits(Register addr_reg, 4480 Register bitmap_reg, 4481 Register shift_reg) { 4482 ASSERT(!AreAliased(addr_reg, bitmap_reg, shift_reg)); 4483 ASSERT(addr_reg.Is64Bits() && bitmap_reg.Is64Bits() && shift_reg.Is64Bits()); 4484 // addr_reg is divided into fields: 4485 // |63 page base 20|19 high 8|7 shift 3|2 0| 4486 // 'high' gives the index of the cell holding color bits for the object. 4487 // 'shift' gives the offset in the cell for this object's color. 4488 const int kShiftBits = kPointerSizeLog2 + Bitmap::kBitsPerCellLog2; 4489 UseScratchRegisterScope temps(this); 4490 Register temp = temps.AcquireX(); 4491 Ubfx(temp, addr_reg, kShiftBits, kPageSizeBits - kShiftBits); 4492 Bic(bitmap_reg, addr_reg, Page::kPageAlignmentMask); 4493 Add(bitmap_reg, bitmap_reg, Operand(temp, LSL, Bitmap::kBytesPerCellLog2)); 4494 // bitmap_reg: 4495 // |63 page base 20|19 zeros 15|14 high 3|2 0| 4496 Ubfx(shift_reg, addr_reg, kPointerSizeLog2, Bitmap::kBitsPerCellLog2); 4497 } 4498 4499 4500 void MacroAssembler::HasColor(Register object, 4501 Register bitmap_scratch, 4502 Register shift_scratch, 4503 Label* has_color, 4504 int first_bit, 4505 int second_bit) { 4506 // See mark-compact.h for color definitions. 4507 ASSERT(!AreAliased(object, bitmap_scratch, shift_scratch)); 4508 4509 GetMarkBits(object, bitmap_scratch, shift_scratch); 4510 Ldr(bitmap_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); 4511 // Shift the bitmap down to get the color of the object in bits [1:0]. 4512 Lsr(bitmap_scratch, bitmap_scratch, shift_scratch); 4513 4514 AssertHasValidColor(bitmap_scratch); 4515 4516 // These bit sequences are backwards. The first character in the string 4517 // represents the least significant bit. 4518 ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); 4519 ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); 4520 ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); 4521 4522 // Check for the color. 4523 if (first_bit == 0) { 4524 // Checking for white. 4525 ASSERT(second_bit == 0); 4526 // We only need to test the first bit. 4527 Tbz(bitmap_scratch, 0, has_color); 4528 } else { 4529 Label other_color; 4530 // Checking for grey or black. 4531 Tbz(bitmap_scratch, 0, &other_color); 4532 if (second_bit == 0) { 4533 Tbz(bitmap_scratch, 1, has_color); 4534 } else { 4535 Tbnz(bitmap_scratch, 1, has_color); 4536 } 4537 Bind(&other_color); 4538 } 4539 4540 // Fall through if it does not have the right color. 4541 } 4542 4543 4544 void MacroAssembler::CheckMapDeprecated(Handle<Map> map, 4545 Register scratch, 4546 Label* if_deprecated) { 4547 if (map->CanBeDeprecated()) { 4548 Mov(scratch, Operand(map)); 4549 Ldrsw(scratch, FieldMemOperand(scratch, Map::kBitField3Offset)); 4550 TestAndBranchIfAnySet(scratch, Map::Deprecated::kMask, if_deprecated); 4551 } 4552 } 4553 4554 4555 void MacroAssembler::JumpIfBlack(Register object, 4556 Register scratch0, 4557 Register scratch1, 4558 Label* on_black) { 4559 ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); 4560 HasColor(object, scratch0, scratch1, on_black, 1, 0); // kBlackBitPattern. 4561 } 4562 4563 4564 void MacroAssembler::JumpIfDictionaryInPrototypeChain( 4565 Register object, 4566 Register scratch0, 4567 Register scratch1, 4568 Label* found) { 4569 ASSERT(!AreAliased(object, scratch0, scratch1)); 4570 Factory* factory = isolate()->factory(); 4571 Register current = scratch0; 4572 Label loop_again; 4573 4574 // Scratch contains elements pointer. 4575 Mov(current, object); 4576 4577 // Loop based on the map going up the prototype chain. 4578 Bind(&loop_again); 4579 Ldr(current, FieldMemOperand(current, HeapObject::kMapOffset)); 4580 Ldrb(scratch1, FieldMemOperand(current, Map::kBitField2Offset)); 4581 DecodeField<Map::ElementsKindBits>(scratch1); 4582 CompareAndBranch(scratch1, DICTIONARY_ELEMENTS, eq, found); 4583 Ldr(current, FieldMemOperand(current, Map::kPrototypeOffset)); 4584 CompareAndBranch(current, Operand(factory->null_value()), ne, &loop_again); 4585 } 4586 4587 4588 void MacroAssembler::GetRelocatedValueLocation(Register ldr_location, 4589 Register result) { 4590 ASSERT(!result.Is(ldr_location)); 4591 const uint32_t kLdrLitOffset_lsb = 5; 4592 const uint32_t kLdrLitOffset_width = 19; 4593 Ldr(result, MemOperand(ldr_location)); 4594 if (emit_debug_code()) { 4595 And(result, result, LoadLiteralFMask); 4596 Cmp(result, LoadLiteralFixed); 4597 Check(eq, kTheInstructionToPatchShouldBeAnLdrLiteral); 4598 // The instruction was clobbered. Reload it. 4599 Ldr(result, MemOperand(ldr_location)); 4600 } 4601 Sbfx(result, result, kLdrLitOffset_lsb, kLdrLitOffset_width); 4602 Add(result, ldr_location, Operand(result, LSL, kWordSizeInBytesLog2)); 4603 } 4604 4605 4606 void MacroAssembler::EnsureNotWhite( 4607 Register value, 4608 Register bitmap_scratch, 4609 Register shift_scratch, 4610 Register load_scratch, 4611 Register length_scratch, 4612 Label* value_is_white_and_not_data) { 4613 ASSERT(!AreAliased( 4614 value, bitmap_scratch, shift_scratch, load_scratch, length_scratch)); 4615 4616 // These bit sequences are backwards. The first character in the string 4617 // represents the least significant bit. 4618 ASSERT(strcmp(Marking::kWhiteBitPattern, "00") == 0); 4619 ASSERT(strcmp(Marking::kBlackBitPattern, "10") == 0); 4620 ASSERT(strcmp(Marking::kGreyBitPattern, "11") == 0); 4621 4622 GetMarkBits(value, bitmap_scratch, shift_scratch); 4623 Ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); 4624 Lsr(load_scratch, load_scratch, shift_scratch); 4625 4626 AssertHasValidColor(load_scratch); 4627 4628 // If the value is black or grey we don't need to do anything. 4629 // Since both black and grey have a 1 in the first position and white does 4630 // not have a 1 there we only need to check one bit. 4631 Label done; 4632 Tbnz(load_scratch, 0, &done); 4633 4634 // Value is white. We check whether it is data that doesn't need scanning. 4635 Register map = load_scratch; // Holds map while checking type. 4636 Label is_data_object; 4637 4638 // Check for heap-number. 4639 Ldr(map, FieldMemOperand(value, HeapObject::kMapOffset)); 4640 Mov(length_scratch, HeapNumber::kSize); 4641 JumpIfRoot(map, Heap::kHeapNumberMapRootIndex, &is_data_object); 4642 4643 // Check for strings. 4644 ASSERT(kIsIndirectStringTag == 1 && kIsIndirectStringMask == 1); 4645 ASSERT(kNotStringTag == 0x80 && kIsNotStringMask == 0x80); 4646 // If it's a string and it's not a cons string then it's an object containing 4647 // no GC pointers. 4648 Register instance_type = load_scratch; 4649 Ldrb(instance_type, FieldMemOperand(map, Map::kInstanceTypeOffset)); 4650 TestAndBranchIfAnySet(instance_type, 4651 kIsIndirectStringMask | kIsNotStringMask, 4652 value_is_white_and_not_data); 4653 4654 // It's a non-indirect (non-cons and non-slice) string. 4655 // If it's external, the length is just ExternalString::kSize. 4656 // Otherwise it's String::kHeaderSize + string->length() * (1 or 2). 4657 // External strings are the only ones with the kExternalStringTag bit 4658 // set. 4659 ASSERT_EQ(0, kSeqStringTag & kExternalStringTag); 4660 ASSERT_EQ(0, kConsStringTag & kExternalStringTag); 4661 Mov(length_scratch, ExternalString::kSize); 4662 TestAndBranchIfAnySet(instance_type, kExternalStringTag, &is_data_object); 4663 4664 // Sequential string, either ASCII or UC16. 4665 // For ASCII (char-size of 1) we shift the smi tag away to get the length. 4666 // For UC16 (char-size of 2) we just leave the smi tag in place, thereby 4667 // getting the length multiplied by 2. 4668 ASSERT(kOneByteStringTag == 4 && kStringEncodingMask == 4); 4669 Ldrsw(length_scratch, UntagSmiFieldMemOperand(value, 4670 String::kLengthOffset)); 4671 Tst(instance_type, kStringEncodingMask); 4672 Cset(load_scratch, eq); 4673 Lsl(length_scratch, length_scratch, load_scratch); 4674 Add(length_scratch, 4675 length_scratch, 4676 SeqString::kHeaderSize + kObjectAlignmentMask); 4677 Bic(length_scratch, length_scratch, kObjectAlignmentMask); 4678 4679 Bind(&is_data_object); 4680 // Value is a data object, and it is white. Mark it black. Since we know 4681 // that the object is white we can make it black by flipping one bit. 4682 Register mask = shift_scratch; 4683 Mov(load_scratch, 1); 4684 Lsl(mask, load_scratch, shift_scratch); 4685 4686 Ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); 4687 Orr(load_scratch, load_scratch, mask); 4688 Str(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kHeaderSize)); 4689 4690 Bic(bitmap_scratch, bitmap_scratch, Page::kPageAlignmentMask); 4691 Ldr(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); 4692 Add(load_scratch, load_scratch, length_scratch); 4693 Str(load_scratch, MemOperand(bitmap_scratch, MemoryChunk::kLiveBytesOffset)); 4694 4695 Bind(&done); 4696 } 4697 4698 4699 void MacroAssembler::Assert(Condition cond, BailoutReason reason) { 4700 if (emit_debug_code()) { 4701 Check(cond, reason); 4702 } 4703 } 4704 4705 4706 4707 void MacroAssembler::AssertRegisterIsClear(Register reg, BailoutReason reason) { 4708 if (emit_debug_code()) { 4709 CheckRegisterIsClear(reg, reason); 4710 } 4711 } 4712 4713 4714 void MacroAssembler::AssertRegisterIsRoot(Register reg, 4715 Heap::RootListIndex index, 4716 BailoutReason reason) { 4717 if (emit_debug_code()) { 4718 CompareRoot(reg, index); 4719 Check(eq, reason); 4720 } 4721 } 4722 4723 4724 void MacroAssembler::AssertFastElements(Register elements) { 4725 if (emit_debug_code()) { 4726 UseScratchRegisterScope temps(this); 4727 Register temp = temps.AcquireX(); 4728 Label ok; 4729 Ldr(temp, FieldMemOperand(elements, HeapObject::kMapOffset)); 4730 JumpIfRoot(temp, Heap::kFixedArrayMapRootIndex, &ok); 4731 JumpIfRoot(temp, Heap::kFixedDoubleArrayMapRootIndex, &ok); 4732 JumpIfRoot(temp, Heap::kFixedCOWArrayMapRootIndex, &ok); 4733 Abort(kJSObjectWithFastElementsMapHasSlowElements); 4734 Bind(&ok); 4735 } 4736 } 4737 4738 4739 void MacroAssembler::AssertIsString(const Register& object) { 4740 if (emit_debug_code()) { 4741 UseScratchRegisterScope temps(this); 4742 Register temp = temps.AcquireX(); 4743 STATIC_ASSERT(kSmiTag == 0); 4744 Tst(object, kSmiTagMask); 4745 Check(ne, kOperandIsNotAString); 4746 Ldr(temp, FieldMemOperand(object, HeapObject::kMapOffset)); 4747 CompareInstanceType(temp, temp, FIRST_NONSTRING_TYPE); 4748 Check(lo, kOperandIsNotAString); 4749 } 4750 } 4751 4752 4753 void MacroAssembler::Check(Condition cond, BailoutReason reason) { 4754 Label ok; 4755 B(cond, &ok); 4756 Abort(reason); 4757 // Will not return here. 4758 Bind(&ok); 4759 } 4760 4761 4762 void MacroAssembler::CheckRegisterIsClear(Register reg, BailoutReason reason) { 4763 Label ok; 4764 Cbz(reg, &ok); 4765 Abort(reason); 4766 // Will not return here. 4767 Bind(&ok); 4768 } 4769 4770 4771 void MacroAssembler::Abort(BailoutReason reason) { 4772 #ifdef DEBUG 4773 RecordComment("Abort message: "); 4774 RecordComment(GetBailoutReason(reason)); 4775 4776 if (FLAG_trap_on_abort) { 4777 Brk(0); 4778 return; 4779 } 4780 #endif 4781 4782 // Abort is used in some contexts where csp is the stack pointer. In order to 4783 // simplify the CallRuntime code, make sure that jssp is the stack pointer. 4784 // There is no risk of register corruption here because Abort doesn't return. 4785 Register old_stack_pointer = StackPointer(); 4786 SetStackPointer(jssp); 4787 Mov(jssp, old_stack_pointer); 4788 4789 // We need some scratch registers for the MacroAssembler, so make sure we have 4790 // some. This is safe here because Abort never returns. 4791 RegList old_tmp_list = TmpList()->list(); 4792 TmpList()->Combine(MacroAssembler::DefaultTmpList()); 4793 4794 if (use_real_aborts()) { 4795 // Avoid infinite recursion; Push contains some assertions that use Abort. 4796 NoUseRealAbortsScope no_real_aborts(this); 4797 4798 Mov(x0, Smi::FromInt(reason)); 4799 Push(x0); 4800 4801 if (!has_frame_) { 4802 // We don't actually want to generate a pile of code for this, so just 4803 // claim there is a stack frame, without generating one. 4804 FrameScope scope(this, StackFrame::NONE); 4805 CallRuntime(Runtime::kAbort, 1); 4806 } else { 4807 CallRuntime(Runtime::kAbort, 1); 4808 } 4809 } else { 4810 // Load the string to pass to Printf. 4811 Label msg_address; 4812 Adr(x0, &msg_address); 4813 4814 // Call Printf directly to report the error. 4815 CallPrintf(); 4816 4817 // We need a way to stop execution on both the simulator and real hardware, 4818 // and Unreachable() is the best option. 4819 Unreachable(); 4820 4821 // Emit the message string directly in the instruction stream. 4822 { 4823 BlockPoolsScope scope(this); 4824 Bind(&msg_address); 4825 EmitStringData(GetBailoutReason(reason)); 4826 } 4827 } 4828 4829 SetStackPointer(old_stack_pointer); 4830 TmpList()->set_list(old_tmp_list); 4831 } 4832 4833 4834 void MacroAssembler::LoadTransitionedArrayMapConditional( 4835 ElementsKind expected_kind, 4836 ElementsKind transitioned_kind, 4837 Register map_in_out, 4838 Register scratch1, 4839 Register scratch2, 4840 Label* no_map_match) { 4841 // Load the global or builtins object from the current context. 4842 Ldr(scratch1, GlobalObjectMemOperand()); 4843 Ldr(scratch1, FieldMemOperand(scratch1, GlobalObject::kNativeContextOffset)); 4844 4845 // Check that the function's map is the same as the expected cached map. 4846 Ldr(scratch1, ContextMemOperand(scratch1, Context::JS_ARRAY_MAPS_INDEX)); 4847 size_t offset = (expected_kind * kPointerSize) + FixedArrayBase::kHeaderSize; 4848 Ldr(scratch2, FieldMemOperand(scratch1, offset)); 4849 Cmp(map_in_out, scratch2); 4850 B(ne, no_map_match); 4851 4852 // Use the transitioned cached map. 4853 offset = (transitioned_kind * kPointerSize) + FixedArrayBase::kHeaderSize; 4854 Ldr(map_in_out, FieldMemOperand(scratch1, offset)); 4855 } 4856 4857 4858 void MacroAssembler::LoadGlobalFunction(int index, Register function) { 4859 // Load the global or builtins object from the current context. 4860 Ldr(function, GlobalObjectMemOperand()); 4861 // Load the native context from the global or builtins object. 4862 Ldr(function, FieldMemOperand(function, 4863 GlobalObject::kNativeContextOffset)); 4864 // Load the function from the native context. 4865 Ldr(function, ContextMemOperand(function, index)); 4866 } 4867 4868 4869 void MacroAssembler::LoadGlobalFunctionInitialMap(Register function, 4870 Register map, 4871 Register scratch) { 4872 // Load the initial map. The global functions all have initial maps. 4873 Ldr(map, FieldMemOperand(function, JSFunction::kPrototypeOrInitialMapOffset)); 4874 if (emit_debug_code()) { 4875 Label ok, fail; 4876 CheckMap(map, scratch, Heap::kMetaMapRootIndex, &fail, DO_SMI_CHECK); 4877 B(&ok); 4878 Bind(&fail); 4879 Abort(kGlobalFunctionsMustHaveInitialMap); 4880 Bind(&ok); 4881 } 4882 } 4883 4884 4885 // This is the main Printf implementation. All other Printf variants call 4886 // PrintfNoPreserve after setting up one or more PreserveRegisterScopes. 4887 void MacroAssembler::PrintfNoPreserve(const char * format, 4888 const CPURegister& arg0, 4889 const CPURegister& arg1, 4890 const CPURegister& arg2, 4891 const CPURegister& arg3) { 4892 // We cannot handle a caller-saved stack pointer. It doesn't make much sense 4893 // in most cases anyway, so this restriction shouldn't be too serious. 4894 ASSERT(!kCallerSaved.IncludesAliasOf(__ StackPointer())); 4895 4896 // The provided arguments, and their proper procedure-call standard registers. 4897 CPURegister args[kPrintfMaxArgCount] = {arg0, arg1, arg2, arg3}; 4898 CPURegister pcs[kPrintfMaxArgCount] = {NoReg, NoReg, NoReg, NoReg}; 4899 4900 int arg_count = kPrintfMaxArgCount; 4901 4902 // The PCS varargs registers for printf. Note that x0 is used for the printf 4903 // format string. 4904 static const CPURegList kPCSVarargs = 4905 CPURegList(CPURegister::kRegister, kXRegSizeInBits, 1, arg_count); 4906 static const CPURegList kPCSVarargsFP = 4907 CPURegList(CPURegister::kFPRegister, kDRegSizeInBits, 0, arg_count - 1); 4908 4909 // We can use caller-saved registers as scratch values, except for the 4910 // arguments and the PCS registers where they might need to go. 4911 CPURegList tmp_list = kCallerSaved; 4912 tmp_list.Remove(x0); // Used to pass the format string. 4913 tmp_list.Remove(kPCSVarargs); 4914 tmp_list.Remove(arg0, arg1, arg2, arg3); 4915 4916 CPURegList fp_tmp_list = kCallerSavedFP; 4917 fp_tmp_list.Remove(kPCSVarargsFP); 4918 fp_tmp_list.Remove(arg0, arg1, arg2, arg3); 4919 4920 // Override the MacroAssembler's scratch register list. The lists will be 4921 // reset automatically at the end of the UseScratchRegisterScope. 4922 UseScratchRegisterScope temps(this); 4923 TmpList()->set_list(tmp_list.list()); 4924 FPTmpList()->set_list(fp_tmp_list.list()); 4925 4926 // Copies of the printf vararg registers that we can pop from. 4927 CPURegList pcs_varargs = kPCSVarargs; 4928 CPURegList pcs_varargs_fp = kPCSVarargsFP; 4929 4930 // Place the arguments. There are lots of clever tricks and optimizations we 4931 // could use here, but Printf is a debug tool so instead we just try to keep 4932 // it simple: Move each input that isn't already in the right place to a 4933 // scratch register, then move everything back. 4934 for (unsigned i = 0; i < kPrintfMaxArgCount; i++) { 4935 // Work out the proper PCS register for this argument. 4936 if (args[i].IsRegister()) { 4937 pcs[i] = pcs_varargs.PopLowestIndex().X(); 4938 // We might only need a W register here. We need to know the size of the 4939 // argument so we can properly encode it for the simulator call. 4940 if (args[i].Is32Bits()) pcs[i] = pcs[i].W(); 4941 } else if (args[i].IsFPRegister()) { 4942 // In C, floats are always cast to doubles for varargs calls. 4943 pcs[i] = pcs_varargs_fp.PopLowestIndex().D(); 4944 } else { 4945 ASSERT(args[i].IsNone()); 4946 arg_count = i; 4947 break; 4948 } 4949 4950 // If the argument is already in the right place, leave it where it is. 4951 if (args[i].Aliases(pcs[i])) continue; 4952 4953 // Otherwise, if the argument is in a PCS argument register, allocate an 4954 // appropriate scratch register and then move it out of the way. 4955 if (kPCSVarargs.IncludesAliasOf(args[i]) || 4956 kPCSVarargsFP.IncludesAliasOf(args[i])) { 4957 if (args[i].IsRegister()) { 4958 Register old_arg = Register(args[i]); 4959 Register new_arg = temps.AcquireSameSizeAs(old_arg); 4960 Mov(new_arg, old_arg); 4961 args[i] = new_arg; 4962 } else { 4963 FPRegister old_arg = FPRegister(args[i]); 4964 FPRegister new_arg = temps.AcquireSameSizeAs(old_arg); 4965 Fmov(new_arg, old_arg); 4966 args[i] = new_arg; 4967 } 4968 } 4969 } 4970 4971 // Do a second pass to move values into their final positions and perform any 4972 // conversions that may be required. 4973 for (int i = 0; i < arg_count; i++) { 4974 ASSERT(pcs[i].type() == args[i].type()); 4975 if (pcs[i].IsRegister()) { 4976 Mov(Register(pcs[i]), Register(args[i]), kDiscardForSameWReg); 4977 } else { 4978 ASSERT(pcs[i].IsFPRegister()); 4979 if (pcs[i].SizeInBytes() == args[i].SizeInBytes()) { 4980 Fmov(FPRegister(pcs[i]), FPRegister(args[i])); 4981 } else { 4982 Fcvt(FPRegister(pcs[i]), FPRegister(args[i])); 4983 } 4984 } 4985 } 4986 4987 // Load the format string into x0, as per the procedure-call standard. 4988 // 4989 // To make the code as portable as possible, the format string is encoded 4990 // directly in the instruction stream. It might be cleaner to encode it in a 4991 // literal pool, but since Printf is usually used for debugging, it is 4992 // beneficial for it to be minimally dependent on other features. 4993 Label format_address; 4994 Adr(x0, &format_address); 4995 4996 // Emit the format string directly in the instruction stream. 4997 { BlockPoolsScope scope(this); 4998 Label after_data; 4999 B(&after_data); 5000 Bind(&format_address); 5001 EmitStringData(format); 5002 Unreachable(); 5003 Bind(&after_data); 5004 } 5005 5006 // We don't pass any arguments on the stack, but we still need to align the C 5007 // stack pointer to a 16-byte boundary for PCS compliance. 5008 if (!csp.Is(StackPointer())) { 5009 Bic(csp, StackPointer(), 0xf); 5010 } 5011 5012 CallPrintf(arg_count, pcs); 5013 } 5014 5015 5016 void MacroAssembler::CallPrintf(int arg_count, const CPURegister * args) { 5017 // A call to printf needs special handling for the simulator, since the system 5018 // printf function will use a different instruction set and the procedure-call 5019 // standard will not be compatible. 5020 #ifdef USE_SIMULATOR 5021 { InstructionAccurateScope scope(this, kPrintfLength / kInstructionSize); 5022 hlt(kImmExceptionIsPrintf); 5023 dc32(arg_count); // kPrintfArgCountOffset 5024 5025 // Determine the argument pattern. 5026 uint32_t arg_pattern_list = 0; 5027 for (int i = 0; i < arg_count; i++) { 5028 uint32_t arg_pattern; 5029 if (args[i].IsRegister()) { 5030 arg_pattern = args[i].Is32Bits() ? kPrintfArgW : kPrintfArgX; 5031 } else { 5032 ASSERT(args[i].Is64Bits()); 5033 arg_pattern = kPrintfArgD; 5034 } 5035 ASSERT(arg_pattern < (1 << kPrintfArgPatternBits)); 5036 arg_pattern_list |= (arg_pattern << (kPrintfArgPatternBits * i)); 5037 } 5038 dc32(arg_pattern_list); // kPrintfArgPatternListOffset 5039 } 5040 #else 5041 Call(FUNCTION_ADDR(printf), RelocInfo::EXTERNAL_REFERENCE); 5042 #endif 5043 } 5044 5045 5046 void MacroAssembler::Printf(const char * format, 5047 CPURegister arg0, 5048 CPURegister arg1, 5049 CPURegister arg2, 5050 CPURegister arg3) { 5051 // We can only print sp if it is the current stack pointer. 5052 if (!csp.Is(StackPointer())) { 5053 ASSERT(!csp.Aliases(arg0)); 5054 ASSERT(!csp.Aliases(arg1)); 5055 ASSERT(!csp.Aliases(arg2)); 5056 ASSERT(!csp.Aliases(arg3)); 5057 } 5058 5059 // Printf is expected to preserve all registers, so make sure that none are 5060 // available as scratch registers until we've preserved them. 5061 RegList old_tmp_list = TmpList()->list(); 5062 RegList old_fp_tmp_list = FPTmpList()->list(); 5063 TmpList()->set_list(0); 5064 FPTmpList()->set_list(0); 5065 5066 // Preserve all caller-saved registers as well as NZCV. 5067 // If csp is the stack pointer, PushCPURegList asserts that the size of each 5068 // list is a multiple of 16 bytes. 5069 PushCPURegList(kCallerSaved); 5070 PushCPURegList(kCallerSavedFP); 5071 5072 // We can use caller-saved registers as scratch values (except for argN). 5073 CPURegList tmp_list = kCallerSaved; 5074 CPURegList fp_tmp_list = kCallerSavedFP; 5075 tmp_list.Remove(arg0, arg1, arg2, arg3); 5076 fp_tmp_list.Remove(arg0, arg1, arg2, arg3); 5077 TmpList()->set_list(tmp_list.list()); 5078 FPTmpList()->set_list(fp_tmp_list.list()); 5079 5080 { UseScratchRegisterScope temps(this); 5081 // If any of the arguments are the current stack pointer, allocate a new 5082 // register for them, and adjust the value to compensate for pushing the 5083 // caller-saved registers. 5084 bool arg0_sp = StackPointer().Aliases(arg0); 5085 bool arg1_sp = StackPointer().Aliases(arg1); 5086 bool arg2_sp = StackPointer().Aliases(arg2); 5087 bool arg3_sp = StackPointer().Aliases(arg3); 5088 if (arg0_sp || arg1_sp || arg2_sp || arg3_sp) { 5089 // Allocate a register to hold the original stack pointer value, to pass 5090 // to PrintfNoPreserve as an argument. 5091 Register arg_sp = temps.AcquireX(); 5092 Add(arg_sp, StackPointer(), 5093 kCallerSaved.TotalSizeInBytes() + kCallerSavedFP.TotalSizeInBytes()); 5094 if (arg0_sp) arg0 = Register::Create(arg_sp.code(), arg0.SizeInBits()); 5095 if (arg1_sp) arg1 = Register::Create(arg_sp.code(), arg1.SizeInBits()); 5096 if (arg2_sp) arg2 = Register::Create(arg_sp.code(), arg2.SizeInBits()); 5097 if (arg3_sp) arg3 = Register::Create(arg_sp.code(), arg3.SizeInBits()); 5098 } 5099 5100 // Preserve NZCV. 5101 { UseScratchRegisterScope temps(this); 5102 Register tmp = temps.AcquireX(); 5103 Mrs(tmp, NZCV); 5104 Push(tmp, xzr); 5105 } 5106 5107 PrintfNoPreserve(format, arg0, arg1, arg2, arg3); 5108 5109 // Restore NZCV. 5110 { UseScratchRegisterScope temps(this); 5111 Register tmp = temps.AcquireX(); 5112 Pop(xzr, tmp); 5113 Msr(NZCV, tmp); 5114 } 5115 } 5116 5117 PopCPURegList(kCallerSavedFP); 5118 PopCPURegList(kCallerSaved); 5119 5120 TmpList()->set_list(old_tmp_list); 5121 FPTmpList()->set_list(old_fp_tmp_list); 5122 } 5123 5124 5125 void MacroAssembler::EmitFrameSetupForCodeAgePatching() { 5126 // TODO(jbramley): Other architectures use the internal memcpy to copy the 5127 // sequence. If this is a performance bottleneck, we should consider caching 5128 // the sequence and copying it in the same way. 5129 InstructionAccurateScope scope(this, 5130 kNoCodeAgeSequenceLength / kInstructionSize); 5131 ASSERT(jssp.Is(StackPointer())); 5132 EmitFrameSetupForCodeAgePatching(this); 5133 } 5134 5135 5136 5137 void MacroAssembler::EmitCodeAgeSequence(Code* stub) { 5138 InstructionAccurateScope scope(this, 5139 kNoCodeAgeSequenceLength / kInstructionSize); 5140 ASSERT(jssp.Is(StackPointer())); 5141 EmitCodeAgeSequence(this, stub); 5142 } 5143 5144 5145 #undef __ 5146 #define __ assm-> 5147 5148 5149 void MacroAssembler::EmitFrameSetupForCodeAgePatching(Assembler * assm) { 5150 Label start; 5151 __ bind(&start); 5152 5153 // We can do this sequence using four instructions, but the code ageing 5154 // sequence that patches it needs five, so we use the extra space to try to 5155 // simplify some addressing modes and remove some dependencies (compared to 5156 // using two stp instructions with write-back). 5157 __ sub(jssp, jssp, 4 * kXRegSize); 5158 __ sub(csp, csp, 4 * kXRegSize); 5159 __ stp(x1, cp, MemOperand(jssp, 0 * kXRegSize)); 5160 __ stp(fp, lr, MemOperand(jssp, 2 * kXRegSize)); 5161 __ add(fp, jssp, StandardFrameConstants::kFixedFrameSizeFromFp); 5162 5163 __ AssertSizeOfCodeGeneratedSince(&start, kNoCodeAgeSequenceLength); 5164 } 5165 5166 5167 void MacroAssembler::EmitCodeAgeSequence(Assembler * assm, 5168 Code * stub) { 5169 Label start; 5170 __ bind(&start); 5171 // When the stub is called, the sequence is replaced with the young sequence 5172 // (as in EmitFrameSetupForCodeAgePatching). After the code is replaced, the 5173 // stub jumps to &start, stored in x0. The young sequence does not call the 5174 // stub so there is no infinite loop here. 5175 // 5176 // A branch (br) is used rather than a call (blr) because this code replaces 5177 // the frame setup code that would normally preserve lr. 5178 __ ldr_pcrel(ip0, kCodeAgeStubEntryOffset >> kLoadLiteralScaleLog2); 5179 __ adr(x0, &start); 5180 __ br(ip0); 5181 // IsCodeAgeSequence in codegen-arm64.cc assumes that the code generated up 5182 // until now (kCodeAgeStubEntryOffset) is the same for all code age sequences. 5183 __ AssertSizeOfCodeGeneratedSince(&start, kCodeAgeStubEntryOffset); 5184 if (stub) { 5185 __ dc64(reinterpret_cast<uint64_t>(stub->instruction_start())); 5186 __ AssertSizeOfCodeGeneratedSince(&start, kNoCodeAgeSequenceLength); 5187 } 5188 } 5189 5190 5191 bool MacroAssembler::IsYoungSequence(Isolate* isolate, byte* sequence) { 5192 bool is_young = isolate->code_aging_helper()->IsYoung(sequence); 5193 ASSERT(is_young || 5194 isolate->code_aging_helper()->IsOld(sequence)); 5195 return is_young; 5196 } 5197 5198 5199 void MacroAssembler::TruncatingDiv(Register result, 5200 Register dividend, 5201 int32_t divisor) { 5202 ASSERT(!AreAliased(result, dividend)); 5203 ASSERT(result.Is32Bits() && dividend.Is32Bits()); 5204 MultiplierAndShift ms(divisor); 5205 Mov(result, ms.multiplier()); 5206 Smull(result.X(), dividend, result); 5207 Asr(result.X(), result.X(), 32); 5208 if (divisor > 0 && ms.multiplier() < 0) Add(result, result, dividend); 5209 if (divisor < 0 && ms.multiplier() > 0) Sub(result, result, dividend); 5210 if (ms.shift() > 0) Asr(result, result, ms.shift()); 5211 Add(result, result, Operand(dividend, LSR, 31)); 5212 } 5213 5214 5215 #undef __ 5216 5217 5218 UseScratchRegisterScope::~UseScratchRegisterScope() { 5219 available_->set_list(old_available_); 5220 availablefp_->set_list(old_availablefp_); 5221 } 5222 5223 5224 Register UseScratchRegisterScope::AcquireSameSizeAs(const Register& reg) { 5225 int code = AcquireNextAvailable(available_).code(); 5226 return Register::Create(code, reg.SizeInBits()); 5227 } 5228 5229 5230 FPRegister UseScratchRegisterScope::AcquireSameSizeAs(const FPRegister& reg) { 5231 int code = AcquireNextAvailable(availablefp_).code(); 5232 return FPRegister::Create(code, reg.SizeInBits()); 5233 } 5234 5235 5236 CPURegister UseScratchRegisterScope::AcquireNextAvailable( 5237 CPURegList* available) { 5238 CHECK(!available->IsEmpty()); 5239 CPURegister result = available->PopLowestIndex(); 5240 ASSERT(!AreAliased(result, xzr, csp)); 5241 return result; 5242 } 5243 5244 5245 CPURegister UseScratchRegisterScope::UnsafeAcquire(CPURegList* available, 5246 const CPURegister& reg) { 5247 ASSERT(available->IncludesAliasOf(reg)); 5248 available->Remove(reg); 5249 return reg; 5250 } 5251 5252 5253 #define __ masm-> 5254 5255 5256 void InlineSmiCheckInfo::Emit(MacroAssembler* masm, const Register& reg, 5257 const Label* smi_check) { 5258 Assembler::BlockPoolsScope scope(masm); 5259 if (reg.IsValid()) { 5260 ASSERT(smi_check->is_bound()); 5261 ASSERT(reg.Is64Bits()); 5262 5263 // Encode the register (x0-x30) in the lowest 5 bits, then the offset to 5264 // 'check' in the other bits. The possible offset is limited in that we 5265 // use BitField to pack the data, and the underlying data type is a 5266 // uint32_t. 5267 uint32_t delta = __ InstructionsGeneratedSince(smi_check); 5268 __ InlineData(RegisterBits::encode(reg.code()) | DeltaBits::encode(delta)); 5269 } else { 5270 ASSERT(!smi_check->is_bound()); 5271 5272 // An offset of 0 indicates that there is no patch site. 5273 __ InlineData(0); 5274 } 5275 } 5276 5277 5278 InlineSmiCheckInfo::InlineSmiCheckInfo(Address info) 5279 : reg_(NoReg), smi_check_(NULL) { 5280 InstructionSequence* inline_data = InstructionSequence::At(info); 5281 ASSERT(inline_data->IsInlineData()); 5282 if (inline_data->IsInlineData()) { 5283 uint64_t payload = inline_data->InlineData(); 5284 // We use BitField to decode the payload, and BitField can only handle 5285 // 32-bit values. 5286 ASSERT(is_uint32(payload)); 5287 if (payload != 0) { 5288 int reg_code = RegisterBits::decode(payload); 5289 reg_ = Register::XRegFromCode(reg_code); 5290 uint64_t smi_check_delta = DeltaBits::decode(payload); 5291 ASSERT(smi_check_delta != 0); 5292 smi_check_ = inline_data->preceding(smi_check_delta); 5293 } 5294 } 5295 } 5296 5297 5298 #undef __ 5299 5300 5301 } } // namespace v8::internal 5302 5303 #endif // V8_TARGET_ARCH_ARM64 5304