1 // Copyright 2012 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 <limits.h> // For LONG_MIN, LONG_MAX. 6 7 #if V8_TARGET_ARCH_MIPS 8 9 #include "src/base/bits.h" 10 #include "src/base/division-by-constant.h" 11 #include "src/bootstrapper.h" 12 #include "src/callable.h" 13 #include "src/code-factory.h" 14 #include "src/code-stubs.h" 15 #include "src/debug/debug.h" 16 #include "src/external-reference-table.h" 17 #include "src/frames-inl.h" 18 #include "src/instruction-stream.h" 19 #include "src/mips/assembler-mips-inl.h" 20 #include "src/mips/macro-assembler-mips.h" 21 #include "src/register-configuration.h" 22 #include "src/runtime/runtime.h" 23 #include "src/snapshot/snapshot.h" 24 #include "src/wasm/wasm-code-manager.h" 25 26 namespace v8 { 27 namespace internal { 28 29 MacroAssembler::MacroAssembler(Isolate* isolate, 30 const AssemblerOptions& options, void* buffer, 31 int size, CodeObjectRequired create_code_object) 32 : TurboAssembler(isolate, options, buffer, size, create_code_object) { 33 if (create_code_object == CodeObjectRequired::kYes) { 34 // Unlike TurboAssembler, which can be used off the main thread and may not 35 // allocate, macro assembler creates its own copy of the self-reference 36 // marker in order to disambiguate between self-references during nested 37 // code generation (e.g.: codegen of the current object triggers stub 38 // compilation through CodeStub::GetCode()). 39 code_object_ = Handle<HeapObject>::New( 40 *isolate->factory()->NewSelfReferenceMarker(), isolate); 41 } 42 } 43 44 static inline bool IsZero(const Operand& rt) { 45 if (rt.is_reg()) { 46 return rt.rm() == zero_reg; 47 } else { 48 return rt.immediate() == 0; 49 } 50 } 51 52 int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, 53 Register exclusion1, 54 Register exclusion2, 55 Register exclusion3) const { 56 int bytes = 0; 57 RegList exclusions = 0; 58 if (exclusion1 != no_reg) { 59 exclusions |= exclusion1.bit(); 60 if (exclusion2 != no_reg) { 61 exclusions |= exclusion2.bit(); 62 if (exclusion3 != no_reg) { 63 exclusions |= exclusion3.bit(); 64 } 65 } 66 } 67 68 RegList list = kJSCallerSaved & ~exclusions; 69 bytes += NumRegs(list) * kPointerSize; 70 71 if (fp_mode == kSaveFPRegs) { 72 bytes += NumRegs(kCallerSavedFPU) * kDoubleSize; 73 } 74 75 return bytes; 76 } 77 78 int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, 79 Register exclusion2, Register exclusion3) { 80 int bytes = 0; 81 RegList exclusions = 0; 82 if (exclusion1 != no_reg) { 83 exclusions |= exclusion1.bit(); 84 if (exclusion2 != no_reg) { 85 exclusions |= exclusion2.bit(); 86 if (exclusion3 != no_reg) { 87 exclusions |= exclusion3.bit(); 88 } 89 } 90 } 91 92 RegList list = kJSCallerSaved & ~exclusions; 93 MultiPush(list); 94 bytes += NumRegs(list) * kPointerSize; 95 96 if (fp_mode == kSaveFPRegs) { 97 MultiPushFPU(kCallerSavedFPU); 98 bytes += NumRegs(kCallerSavedFPU) * kDoubleSize; 99 } 100 101 return bytes; 102 } 103 104 int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, 105 Register exclusion2, Register exclusion3) { 106 int bytes = 0; 107 if (fp_mode == kSaveFPRegs) { 108 MultiPopFPU(kCallerSavedFPU); 109 bytes += NumRegs(kCallerSavedFPU) * kDoubleSize; 110 } 111 112 RegList exclusions = 0; 113 if (exclusion1 != no_reg) { 114 exclusions |= exclusion1.bit(); 115 if (exclusion2 != no_reg) { 116 exclusions |= exclusion2.bit(); 117 if (exclusion3 != no_reg) { 118 exclusions |= exclusion3.bit(); 119 } 120 } 121 } 122 123 RegList list = kJSCallerSaved & ~exclusions; 124 MultiPop(list); 125 bytes += NumRegs(list) * kPointerSize; 126 127 return bytes; 128 } 129 130 void TurboAssembler::LoadRoot(Register destination, Heap::RootListIndex index) { 131 lw(destination, MemOperand(kRootRegister, RootRegisterOffset(index))); 132 } 133 134 void TurboAssembler::LoadRoot(Register destination, Heap::RootListIndex index, 135 Condition cond, Register src1, 136 const Operand& src2) { 137 Branch(2, NegateCondition(cond), src1, src2); 138 lw(destination, MemOperand(kRootRegister, RootRegisterOffset(index))); 139 } 140 141 142 void TurboAssembler::PushCommonFrame(Register marker_reg) { 143 if (marker_reg.is_valid()) { 144 Push(ra, fp, marker_reg); 145 Addu(fp, sp, Operand(kPointerSize)); 146 } else { 147 Push(ra, fp); 148 mov(fp, sp); 149 } 150 } 151 152 void TurboAssembler::PushStandardFrame(Register function_reg) { 153 int offset = -StandardFrameConstants::kContextOffset; 154 if (function_reg.is_valid()) { 155 Push(ra, fp, cp, function_reg); 156 offset += kPointerSize; 157 } else { 158 Push(ra, fp, cp); 159 } 160 Addu(fp, sp, Operand(offset)); 161 } 162 163 // Push and pop all registers that can hold pointers. 164 void MacroAssembler::PushSafepointRegisters() { 165 // Safepoints expect a block of kNumSafepointRegisters values on the 166 // stack, so adjust the stack for unsaved registers. 167 const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; 168 DCHECK_GE(num_unsaved, 0); 169 if (num_unsaved > 0) { 170 Subu(sp, sp, Operand(num_unsaved * kPointerSize)); 171 } 172 MultiPush(kSafepointSavedRegisters); 173 } 174 175 176 void MacroAssembler::PopSafepointRegisters() { 177 const int num_unsaved = kNumSafepointRegisters - kNumSafepointSavedRegisters; 178 MultiPop(kSafepointSavedRegisters); 179 if (num_unsaved > 0) { 180 Addu(sp, sp, Operand(num_unsaved * kPointerSize)); 181 } 182 } 183 184 int MacroAssembler::SafepointRegisterStackIndex(int reg_code) { 185 // The registers are pushed starting with the highest encoding, 186 // which means that lowest encodings are closest to the stack pointer. 187 return kSafepointRegisterStackIndexMap[reg_code]; 188 } 189 190 191 // Clobbers object, dst, value, and ra, if (ra_status == kRAHasBeenSaved) 192 // The register 'object' contains a heap object pointer. The heap object 193 // tag is shifted away. 194 void MacroAssembler::RecordWriteField(Register object, int offset, 195 Register value, Register dst, 196 RAStatus ra_status, 197 SaveFPRegsMode save_fp, 198 RememberedSetAction remembered_set_action, 199 SmiCheck smi_check) { 200 DCHECK(!AreAliased(value, dst, t8, object)); 201 // First, check if a write barrier is even needed. The tests below 202 // catch stores of Smis. 203 Label done; 204 205 // Skip barrier if writing a smi. 206 if (smi_check == INLINE_SMI_CHECK) { 207 JumpIfSmi(value, &done); 208 } 209 210 // Although the object register is tagged, the offset is relative to the start 211 // of the object, so so offset must be a multiple of kPointerSize. 212 DCHECK(IsAligned(offset, kPointerSize)); 213 214 Addu(dst, object, Operand(offset - kHeapObjectTag)); 215 if (emit_debug_code()) { 216 BlockTrampolinePoolScope block_trampoline_pool(this); 217 Label ok; 218 And(t8, dst, Operand(kPointerSize - 1)); 219 Branch(&ok, eq, t8, Operand(zero_reg)); 220 stop("Unaligned cell in write barrier"); 221 bind(&ok); 222 } 223 224 RecordWrite(object, dst, value, ra_status, save_fp, remembered_set_action, 225 OMIT_SMI_CHECK); 226 227 bind(&done); 228 229 // Clobber clobbered input registers when running with the debug-code flag 230 // turned on to provoke errors. 231 if (emit_debug_code()) { 232 li(value, Operand(bit_cast<int32_t>(kZapValue + 4))); 233 li(dst, Operand(bit_cast<int32_t>(kZapValue + 8))); 234 } 235 } 236 237 void TurboAssembler::SaveRegisters(RegList registers) { 238 DCHECK_GT(NumRegs(registers), 0); 239 RegList regs = 0; 240 for (int i = 0; i < Register::kNumRegisters; ++i) { 241 if ((registers >> i) & 1u) { 242 regs |= Register::from_code(i).bit(); 243 } 244 } 245 MultiPush(regs); 246 } 247 248 void TurboAssembler::RestoreRegisters(RegList registers) { 249 DCHECK_GT(NumRegs(registers), 0); 250 RegList regs = 0; 251 for (int i = 0; i < Register::kNumRegisters; ++i) { 252 if ((registers >> i) & 1u) { 253 regs |= Register::from_code(i).bit(); 254 } 255 } 256 MultiPop(regs); 257 } 258 259 void TurboAssembler::CallRecordWriteStub( 260 Register object, Register address, 261 RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode) { 262 // TODO(albertnetymk): For now we ignore remembered_set_action and fp_mode, 263 // i.e. always emit remember set and save FP registers in RecordWriteStub. If 264 // large performance regression is observed, we should use these values to 265 // avoid unnecessary work. 266 267 Callable const callable = 268 Builtins::CallableFor(isolate(), Builtins::kRecordWrite); 269 RegList registers = callable.descriptor().allocatable_registers(); 270 271 SaveRegisters(registers); 272 Register object_parameter(callable.descriptor().GetRegisterParameter( 273 RecordWriteDescriptor::kObject)); 274 Register slot_parameter( 275 callable.descriptor().GetRegisterParameter(RecordWriteDescriptor::kSlot)); 276 Register isolate_parameter(callable.descriptor().GetRegisterParameter( 277 RecordWriteDescriptor::kIsolate)); 278 Register remembered_set_parameter(callable.descriptor().GetRegisterParameter( 279 RecordWriteDescriptor::kRememberedSet)); 280 Register fp_mode_parameter(callable.descriptor().GetRegisterParameter( 281 RecordWriteDescriptor::kFPMode)); 282 283 Push(object); 284 Push(address); 285 286 Pop(slot_parameter); 287 Pop(object_parameter); 288 289 li(isolate_parameter, ExternalReference::isolate_address(isolate())); 290 Move(remembered_set_parameter, Smi::FromEnum(remembered_set_action)); 291 Move(fp_mode_parameter, Smi::FromEnum(fp_mode)); 292 Call(callable.code(), RelocInfo::CODE_TARGET); 293 294 RestoreRegisters(registers); 295 } 296 297 // Clobbers object, address, value, and ra, if (ra_status == kRAHasBeenSaved) 298 // The register 'object' contains a heap object pointer. The heap object 299 // tag is shifted away. 300 void MacroAssembler::RecordWrite(Register object, Register address, 301 Register value, RAStatus ra_status, 302 SaveFPRegsMode fp_mode, 303 RememberedSetAction remembered_set_action, 304 SmiCheck smi_check) { 305 DCHECK(!AreAliased(object, address, value, t8)); 306 DCHECK(!AreAliased(object, address, value, t9)); 307 308 if (emit_debug_code()) { 309 UseScratchRegisterScope temps(this); 310 Register scratch = temps.Acquire(); 311 lw(scratch, MemOperand(address)); 312 Assert(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite, scratch, 313 Operand(value)); 314 } 315 316 if (remembered_set_action == OMIT_REMEMBERED_SET && 317 !FLAG_incremental_marking) { 318 return; 319 } 320 321 // First, check if a write barrier is even needed. The tests below 322 // catch stores of smis and stores into the young generation. 323 Label done; 324 325 if (smi_check == INLINE_SMI_CHECK) { 326 DCHECK_EQ(0, kSmiTag); 327 JumpIfSmi(value, &done); 328 } 329 330 CheckPageFlag(value, 331 value, // Used as scratch. 332 MemoryChunk::kPointersToHereAreInterestingMask, eq, &done); 333 CheckPageFlag(object, 334 value, // Used as scratch. 335 MemoryChunk::kPointersFromHereAreInterestingMask, 336 eq, 337 &done); 338 339 // Record the actual write. 340 if (ra_status == kRAHasNotBeenSaved) { 341 push(ra); 342 } 343 CallRecordWriteStub(object, address, remembered_set_action, fp_mode); 344 if (ra_status == kRAHasNotBeenSaved) { 345 pop(ra); 346 } 347 348 bind(&done); 349 350 { 351 // Count number of write barriers in generated code. 352 isolate()->counters()->write_barriers_static()->Increment(); 353 UseScratchRegisterScope temps(this); 354 Register scratch = temps.Acquire(); 355 IncrementCounter(isolate()->counters()->write_barriers_dynamic(), 1, 356 scratch, value); 357 } 358 359 // Clobber clobbered registers when running with the debug-code flag 360 // turned on to provoke errors. 361 if (emit_debug_code()) { 362 li(address, Operand(bit_cast<int32_t>(kZapValue + 12))); 363 li(value, Operand(bit_cast<int32_t>(kZapValue + 16))); 364 } 365 } 366 367 // --------------------------------------------------------------------------- 368 // Instruction macros. 369 370 void TurboAssembler::Addu(Register rd, Register rs, const Operand& rt) { 371 if (rt.is_reg()) { 372 addu(rd, rs, rt.rm()); 373 } else { 374 if (is_int16(rt.immediate()) && !MustUseReg(rt.rmode())) { 375 addiu(rd, rs, rt.immediate()); 376 } else { 377 // li handles the relocation. 378 UseScratchRegisterScope temps(this); 379 Register scratch = temps.Acquire(); 380 DCHECK(rs != scratch); 381 li(scratch, rt); 382 addu(rd, rs, scratch); 383 } 384 } 385 } 386 387 void TurboAssembler::Subu(Register rd, Register rs, const Operand& rt) { 388 if (rt.is_reg()) { 389 subu(rd, rs, rt.rm()); 390 } else { 391 if (is_int16(-rt.immediate()) && !MustUseReg(rt.rmode())) { 392 addiu(rd, rs, -rt.immediate()); // No subiu instr, use addiu(x, y, -imm). 393 } else if (!(-rt.immediate() & kHiMask) && 394 !MustUseReg(rt.rmode())) { // Use load 395 // -imm and addu for cases where loading -imm generates one instruction. 396 UseScratchRegisterScope temps(this); 397 Register scratch = temps.Acquire(); 398 DCHECK(rs != scratch); 399 li(scratch, -rt.immediate()); 400 addu(rd, rs, scratch); 401 } else { 402 // li handles the relocation. 403 UseScratchRegisterScope temps(this); 404 Register scratch = temps.Acquire(); 405 DCHECK(rs != scratch); 406 li(scratch, rt); 407 subu(rd, rs, scratch); 408 } 409 } 410 } 411 412 void TurboAssembler::Mul(Register rd, Register rs, const Operand& rt) { 413 if (rt.is_reg()) { 414 if (IsMipsArchVariant(kLoongson)) { 415 mult(rs, rt.rm()); 416 mflo(rd); 417 } else { 418 mul(rd, rs, rt.rm()); 419 } 420 } else { 421 // li handles the relocation. 422 UseScratchRegisterScope temps(this); 423 Register scratch = temps.Acquire(); 424 DCHECK(rs != scratch); 425 li(scratch, rt); 426 if (IsMipsArchVariant(kLoongson)) { 427 mult(rs, scratch); 428 mflo(rd); 429 } else { 430 mul(rd, rs, scratch); 431 } 432 } 433 } 434 435 void TurboAssembler::Mul(Register rd_hi, Register rd_lo, Register rs, 436 const Operand& rt) { 437 if (rt.is_reg()) { 438 if (!IsMipsArchVariant(kMips32r6)) { 439 mult(rs, rt.rm()); 440 mflo(rd_lo); 441 mfhi(rd_hi); 442 } else { 443 if (rd_lo == rs) { 444 DCHECK(rd_hi != rs); 445 DCHECK(rd_hi != rt.rm() && rd_lo != rt.rm()); 446 muh(rd_hi, rs, rt.rm()); 447 mul(rd_lo, rs, rt.rm()); 448 } else { 449 DCHECK(rd_hi != rt.rm() && rd_lo != rt.rm()); 450 mul(rd_lo, rs, rt.rm()); 451 muh(rd_hi, rs, rt.rm()); 452 } 453 } 454 } else { 455 // li handles the relocation. 456 UseScratchRegisterScope temps(this); 457 Register scratch = temps.Acquire(); 458 DCHECK(rs != scratch); 459 li(scratch, rt); 460 if (!IsMipsArchVariant(kMips32r6)) { 461 mult(rs, scratch); 462 mflo(rd_lo); 463 mfhi(rd_hi); 464 } else { 465 if (rd_lo == rs) { 466 DCHECK(rd_hi != rs); 467 DCHECK(rd_hi != scratch && rd_lo != scratch); 468 muh(rd_hi, rs, scratch); 469 mul(rd_lo, rs, scratch); 470 } else { 471 DCHECK(rd_hi != scratch && rd_lo != scratch); 472 mul(rd_lo, rs, scratch); 473 muh(rd_hi, rs, scratch); 474 } 475 } 476 } 477 } 478 479 void TurboAssembler::Mulu(Register rd_hi, Register rd_lo, Register rs, 480 const Operand& rt) { 481 Register reg = no_reg; 482 UseScratchRegisterScope temps(this); 483 Register scratch = temps.Acquire(); 484 if (rt.is_reg()) { 485 reg = rt.rm(); 486 } else { 487 DCHECK(rs != scratch); 488 reg = scratch; 489 li(reg, rt); 490 } 491 492 if (!IsMipsArchVariant(kMips32r6)) { 493 multu(rs, reg); 494 mflo(rd_lo); 495 mfhi(rd_hi); 496 } else { 497 if (rd_lo == rs) { 498 DCHECK(rd_hi != rs); 499 DCHECK(rd_hi != reg && rd_lo != reg); 500 muhu(rd_hi, rs, reg); 501 mulu(rd_lo, rs, reg); 502 } else { 503 DCHECK(rd_hi != reg && rd_lo != reg); 504 mulu(rd_lo, rs, reg); 505 muhu(rd_hi, rs, reg); 506 } 507 } 508 } 509 510 void TurboAssembler::Mulh(Register rd, Register rs, const Operand& rt) { 511 if (rt.is_reg()) { 512 if (!IsMipsArchVariant(kMips32r6)) { 513 mult(rs, rt.rm()); 514 mfhi(rd); 515 } else { 516 muh(rd, rs, rt.rm()); 517 } 518 } else { 519 // li handles the relocation. 520 UseScratchRegisterScope temps(this); 521 Register scratch = temps.Acquire(); 522 DCHECK(rs != scratch); 523 li(scratch, rt); 524 if (!IsMipsArchVariant(kMips32r6)) { 525 mult(rs, scratch); 526 mfhi(rd); 527 } else { 528 muh(rd, rs, scratch); 529 } 530 } 531 } 532 533 void TurboAssembler::Mult(Register rs, const Operand& rt) { 534 if (rt.is_reg()) { 535 mult(rs, rt.rm()); 536 } else { 537 // li handles the relocation. 538 UseScratchRegisterScope temps(this); 539 Register scratch = temps.Acquire(); 540 DCHECK(rs != scratch); 541 li(scratch, rt); 542 mult(rs, scratch); 543 } 544 } 545 546 void TurboAssembler::Mulhu(Register rd, Register rs, const Operand& rt) { 547 if (rt.is_reg()) { 548 if (!IsMipsArchVariant(kMips32r6)) { 549 multu(rs, rt.rm()); 550 mfhi(rd); 551 } else { 552 muhu(rd, rs, rt.rm()); 553 } 554 } else { 555 // li handles the relocation. 556 UseScratchRegisterScope temps(this); 557 Register scratch = temps.Acquire(); 558 DCHECK(rs != scratch); 559 li(scratch, rt); 560 if (!IsMipsArchVariant(kMips32r6)) { 561 multu(rs, scratch); 562 mfhi(rd); 563 } else { 564 muhu(rd, rs, scratch); 565 } 566 } 567 } 568 569 void TurboAssembler::Multu(Register rs, const Operand& rt) { 570 if (rt.is_reg()) { 571 multu(rs, rt.rm()); 572 } else { 573 // li handles the relocation. 574 UseScratchRegisterScope temps(this); 575 Register scratch = temps.Acquire(); 576 DCHECK(rs != scratch); 577 li(scratch, rt); 578 multu(rs, scratch); 579 } 580 } 581 582 void TurboAssembler::Div(Register rs, const Operand& rt) { 583 if (rt.is_reg()) { 584 div(rs, rt.rm()); 585 } else { 586 // li handles the relocation. 587 UseScratchRegisterScope temps(this); 588 Register scratch = temps.Acquire(); 589 DCHECK(rs != scratch); 590 li(scratch, rt); 591 div(rs, scratch); 592 } 593 } 594 595 void TurboAssembler::Div(Register rem, Register res, Register rs, 596 const Operand& rt) { 597 if (rt.is_reg()) { 598 if (!IsMipsArchVariant(kMips32r6)) { 599 div(rs, rt.rm()); 600 mflo(res); 601 mfhi(rem); 602 } else { 603 div(res, rs, rt.rm()); 604 mod(rem, rs, rt.rm()); 605 } 606 } else { 607 // li handles the relocation. 608 UseScratchRegisterScope temps(this); 609 Register scratch = temps.Acquire(); 610 DCHECK(rs != scratch); 611 li(scratch, rt); 612 if (!IsMipsArchVariant(kMips32r6)) { 613 div(rs, scratch); 614 mflo(res); 615 mfhi(rem); 616 } else { 617 div(res, rs, scratch); 618 mod(rem, rs, scratch); 619 } 620 } 621 } 622 623 void TurboAssembler::Div(Register res, Register rs, const Operand& rt) { 624 if (rt.is_reg()) { 625 if (!IsMipsArchVariant(kMips32r6)) { 626 div(rs, rt.rm()); 627 mflo(res); 628 } else { 629 div(res, rs, rt.rm()); 630 } 631 } else { 632 // li handles the relocation. 633 UseScratchRegisterScope temps(this); 634 Register scratch = temps.Acquire(); 635 DCHECK(rs != scratch); 636 li(scratch, rt); 637 if (!IsMipsArchVariant(kMips32r6)) { 638 div(rs, scratch); 639 mflo(res); 640 } else { 641 div(res, rs, scratch); 642 } 643 } 644 } 645 646 void TurboAssembler::Mod(Register rd, Register rs, const Operand& rt) { 647 if (rt.is_reg()) { 648 if (!IsMipsArchVariant(kMips32r6)) { 649 div(rs, rt.rm()); 650 mfhi(rd); 651 } else { 652 mod(rd, rs, rt.rm()); 653 } 654 } else { 655 // li handles the relocation. 656 UseScratchRegisterScope temps(this); 657 Register scratch = temps.Acquire(); 658 DCHECK(rs != scratch); 659 li(scratch, rt); 660 if (!IsMipsArchVariant(kMips32r6)) { 661 div(rs, scratch); 662 mfhi(rd); 663 } else { 664 mod(rd, rs, scratch); 665 } 666 } 667 } 668 669 void TurboAssembler::Modu(Register rd, Register rs, const Operand& rt) { 670 if (rt.is_reg()) { 671 if (!IsMipsArchVariant(kMips32r6)) { 672 divu(rs, rt.rm()); 673 mfhi(rd); 674 } else { 675 modu(rd, rs, rt.rm()); 676 } 677 } else { 678 // li handles the relocation. 679 UseScratchRegisterScope temps(this); 680 Register scratch = temps.Acquire(); 681 DCHECK(rs != scratch); 682 li(scratch, rt); 683 if (!IsMipsArchVariant(kMips32r6)) { 684 divu(rs, scratch); 685 mfhi(rd); 686 } else { 687 modu(rd, rs, scratch); 688 } 689 } 690 } 691 692 void TurboAssembler::Divu(Register rs, const Operand& rt) { 693 if (rt.is_reg()) { 694 divu(rs, rt.rm()); 695 } else { 696 // li handles the relocation. 697 UseScratchRegisterScope temps(this); 698 Register scratch = temps.Acquire(); 699 DCHECK(rs != scratch); 700 li(scratch, rt); 701 divu(rs, scratch); 702 } 703 } 704 705 void TurboAssembler::Divu(Register res, Register rs, const Operand& rt) { 706 if (rt.is_reg()) { 707 if (!IsMipsArchVariant(kMips32r6)) { 708 divu(rs, rt.rm()); 709 mflo(res); 710 } else { 711 divu(res, rs, rt.rm()); 712 } 713 } else { 714 // li handles the relocation. 715 UseScratchRegisterScope temps(this); 716 Register scratch = temps.Acquire(); 717 DCHECK(rs != scratch); 718 li(scratch, rt); 719 if (!IsMipsArchVariant(kMips32r6)) { 720 divu(rs, scratch); 721 mflo(res); 722 } else { 723 divu(res, rs, scratch); 724 } 725 } 726 } 727 728 void TurboAssembler::And(Register rd, Register rs, const Operand& rt) { 729 if (rt.is_reg()) { 730 and_(rd, rs, rt.rm()); 731 } else { 732 if (is_uint16(rt.immediate()) && !MustUseReg(rt.rmode())) { 733 andi(rd, rs, rt.immediate()); 734 } else { 735 // li handles the relocation. 736 UseScratchRegisterScope temps(this); 737 Register scratch = temps.Acquire(); 738 DCHECK(rs != scratch); 739 li(scratch, rt); 740 and_(rd, rs, scratch); 741 } 742 } 743 } 744 745 void TurboAssembler::Or(Register rd, Register rs, const Operand& rt) { 746 if (rt.is_reg()) { 747 or_(rd, rs, rt.rm()); 748 } else { 749 if (is_uint16(rt.immediate()) && !MustUseReg(rt.rmode())) { 750 ori(rd, rs, rt.immediate()); 751 } else { 752 // li handles the relocation. 753 UseScratchRegisterScope temps(this); 754 Register scratch = temps.Acquire(); 755 DCHECK(rs != scratch); 756 li(scratch, rt); 757 or_(rd, rs, scratch); 758 } 759 } 760 } 761 762 void TurboAssembler::Xor(Register rd, Register rs, const Operand& rt) { 763 if (rt.is_reg()) { 764 xor_(rd, rs, rt.rm()); 765 } else { 766 if (is_uint16(rt.immediate()) && !MustUseReg(rt.rmode())) { 767 xori(rd, rs, rt.immediate()); 768 } else { 769 // li handles the relocation. 770 UseScratchRegisterScope temps(this); 771 Register scratch = temps.Acquire(); 772 DCHECK(rs != scratch); 773 li(scratch, rt); 774 xor_(rd, rs, scratch); 775 } 776 } 777 } 778 779 void TurboAssembler::Nor(Register rd, Register rs, const Operand& rt) { 780 if (rt.is_reg()) { 781 nor(rd, rs, rt.rm()); 782 } else { 783 // li handles the relocation. 784 UseScratchRegisterScope temps(this); 785 Register scratch = temps.Acquire(); 786 DCHECK(rs != scratch); 787 li(scratch, rt); 788 nor(rd, rs, scratch); 789 } 790 } 791 792 void TurboAssembler::Neg(Register rs, const Operand& rt) { 793 subu(rs, zero_reg, rt.rm()); 794 } 795 796 void TurboAssembler::Slt(Register rd, Register rs, const Operand& rt) { 797 if (rt.is_reg()) { 798 slt(rd, rs, rt.rm()); 799 } else { 800 if (is_int16(rt.immediate()) && !MustUseReg(rt.rmode())) { 801 slti(rd, rs, rt.immediate()); 802 } else { 803 // li handles the relocation. 804 BlockTrampolinePoolScope block_trampoline_pool(this); 805 UseScratchRegisterScope temps(this); 806 Register scratch = rd == at ? t8 : temps.Acquire(); 807 DCHECK(rs != scratch); 808 li(scratch, rt); 809 slt(rd, rs, scratch); 810 } 811 } 812 } 813 814 void TurboAssembler::Sltu(Register rd, Register rs, const Operand& rt) { 815 if (rt.is_reg()) { 816 sltu(rd, rs, rt.rm()); 817 } else { 818 const uint32_t int16_min = std::numeric_limits<int16_t>::min(); 819 if (is_uint15(rt.immediate()) && !MustUseReg(rt.rmode())) { 820 // Imm range is: [0, 32767]. 821 sltiu(rd, rs, rt.immediate()); 822 } else if (is_uint15(rt.immediate() - int16_min) && 823 !MustUseReg(rt.rmode())) { 824 // Imm range is: [max_unsigned-32767,max_unsigned]. 825 sltiu(rd, rs, static_cast<uint16_t>(rt.immediate())); 826 } else { 827 // li handles the relocation. 828 BlockTrampolinePoolScope block_trampoline_pool(this); 829 UseScratchRegisterScope temps(this); 830 Register scratch = rd == at ? t8 : temps.Acquire(); 831 DCHECK(rs != scratch); 832 li(scratch, rt); 833 sltu(rd, rs, scratch); 834 } 835 } 836 } 837 838 void TurboAssembler::Sle(Register rd, Register rs, const Operand& rt) { 839 if (rt.is_reg()) { 840 slt(rd, rt.rm(), rs); 841 } else { 842 // li handles the relocation. 843 BlockTrampolinePoolScope block_trampoline_pool(this); 844 UseScratchRegisterScope temps(this); 845 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 846 DCHECK(rs != scratch); 847 li(scratch, rt); 848 slt(rd, scratch, rs); 849 } 850 xori(rd, rd, 1); 851 } 852 853 void TurboAssembler::Sleu(Register rd, Register rs, const Operand& rt) { 854 if (rt.is_reg()) { 855 sltu(rd, rt.rm(), rs); 856 } else { 857 // li handles the relocation. 858 BlockTrampolinePoolScope block_trampoline_pool(this); 859 UseScratchRegisterScope temps(this); 860 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 861 DCHECK(rs != scratch); 862 li(scratch, rt); 863 sltu(rd, scratch, rs); 864 } 865 xori(rd, rd, 1); 866 } 867 868 void TurboAssembler::Sge(Register rd, Register rs, const Operand& rt) { 869 Slt(rd, rs, rt); 870 xori(rd, rd, 1); 871 } 872 873 void TurboAssembler::Sgeu(Register rd, Register rs, const Operand& rt) { 874 Sltu(rd, rs, rt); 875 xori(rd, rd, 1); 876 } 877 878 void TurboAssembler::Sgt(Register rd, Register rs, const Operand& rt) { 879 if (rt.is_reg()) { 880 slt(rd, rt.rm(), rs); 881 } else { 882 // li handles the relocation. 883 BlockTrampolinePoolScope block_trampoline_pool(this); 884 UseScratchRegisterScope temps(this); 885 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 886 DCHECK(rs != scratch); 887 li(scratch, rt); 888 slt(rd, scratch, rs); 889 } 890 } 891 892 void TurboAssembler::Sgtu(Register rd, Register rs, const Operand& rt) { 893 if (rt.is_reg()) { 894 sltu(rd, rt.rm(), rs); 895 } else { 896 // li handles the relocation. 897 BlockTrampolinePoolScope block_trampoline_pool(this); 898 UseScratchRegisterScope temps(this); 899 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 900 DCHECK(rs != scratch); 901 li(scratch, rt); 902 sltu(rd, scratch, rs); 903 } 904 } 905 906 void TurboAssembler::Ror(Register rd, Register rs, const Operand& rt) { 907 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 908 if (rt.is_reg()) { 909 rotrv(rd, rs, rt.rm()); 910 } else { 911 rotr(rd, rs, rt.immediate() & 0x1F); 912 } 913 } else { 914 if (rt.is_reg()) { 915 BlockTrampolinePoolScope block_trampoline_pool(this); 916 UseScratchRegisterScope temps(this); 917 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 918 subu(scratch, zero_reg, rt.rm()); 919 sllv(scratch, rs, scratch); 920 srlv(rd, rs, rt.rm()); 921 or_(rd, rd, scratch); 922 } else { 923 if (rt.immediate() == 0) { 924 srl(rd, rs, 0); 925 } else { 926 UseScratchRegisterScope temps(this); 927 Register scratch = temps.Acquire(); 928 srl(scratch, rs, rt.immediate() & 0x1F); 929 sll(rd, rs, (0x20 - (rt.immediate() & 0x1F)) & 0x1F); 930 or_(rd, rd, scratch); 931 } 932 } 933 } 934 } 935 936 937 void MacroAssembler::Pref(int32_t hint, const MemOperand& rs) { 938 if (IsMipsArchVariant(kLoongson)) { 939 lw(zero_reg, rs); 940 } else { 941 pref(hint, rs); 942 } 943 } 944 945 void TurboAssembler::Lsa(Register rd, Register rt, Register rs, uint8_t sa, 946 Register scratch) { 947 DCHECK(sa >= 1 && sa <= 31); 948 if (IsMipsArchVariant(kMips32r6) && sa <= 4) { 949 lsa(rd, rt, rs, sa - 1); 950 } else { 951 Register tmp = rd == rt ? scratch : rd; 952 DCHECK(tmp != rt); 953 sll(tmp, rs, sa); 954 Addu(rd, rt, tmp); 955 } 956 } 957 958 void TurboAssembler::Bovc(Register rs, Register rt, Label* L) { 959 if (is_trampoline_emitted()) { 960 Label skip; 961 bnvc(rs, rt, &skip); 962 BranchLong(L, PROTECT); 963 bind(&skip); 964 } else { 965 bovc(rs, rt, L); 966 } 967 } 968 969 void TurboAssembler::Bnvc(Register rs, Register rt, Label* L) { 970 if (is_trampoline_emitted()) { 971 Label skip; 972 bovc(rs, rt, &skip); 973 BranchLong(L, PROTECT); 974 bind(&skip); 975 } else { 976 bnvc(rs, rt, L); 977 } 978 } 979 980 // ------------Pseudo-instructions------------- 981 982 // Word Swap Byte 983 void TurboAssembler::ByteSwapSigned(Register dest, Register src, 984 int operand_size) { 985 DCHECK(operand_size == 2 || operand_size == 4); 986 987 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 988 if (operand_size == 2) { 989 wsbh(dest, src); 990 seh(dest, dest); 991 } else { 992 wsbh(dest, src); 993 rotr(dest, dest, 16); 994 } 995 } else if (IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kLoongson)) { 996 if (operand_size == 2) { 997 DCHECK(src != at && dest != at); 998 srl(at, src, 8); 999 andi(at, at, 0xFF); 1000 sll(dest, src, 8); 1001 or_(dest, dest, at); 1002 1003 // Sign-extension 1004 sll(dest, dest, 16); 1005 sra(dest, dest, 16); 1006 } else { 1007 BlockTrampolinePoolScope block_trampoline_pool(this); 1008 Register tmp = at; 1009 Register tmp2 = t8; 1010 DCHECK(dest != tmp && dest != tmp2); 1011 DCHECK(src != tmp && src != tmp2); 1012 1013 andi(tmp2, src, 0xFF); 1014 sll(tmp, tmp2, 24); 1015 1016 andi(tmp2, src, 0xFF00); 1017 sll(tmp2, tmp2, 8); 1018 or_(tmp, tmp, tmp2); 1019 1020 srl(tmp2, src, 8); 1021 andi(tmp2, tmp2, 0xFF00); 1022 or_(tmp, tmp, tmp2); 1023 1024 srl(tmp2, src, 24); 1025 or_(dest, tmp, tmp2); 1026 } 1027 } 1028 } 1029 1030 void TurboAssembler::ByteSwapUnsigned(Register dest, Register src, 1031 int operand_size) { 1032 DCHECK_EQ(operand_size, 2); 1033 1034 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1035 wsbh(dest, src); 1036 andi(dest, dest, 0xFFFF); 1037 } else if (IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kLoongson)) { 1038 DCHECK(src != at && dest != at); 1039 srl(at, src, 8); 1040 andi(at, at, 0xFF); 1041 sll(dest, src, 8); 1042 or_(dest, dest, at); 1043 1044 // Zero-extension 1045 andi(dest, dest, 0xFFFF); 1046 } 1047 } 1048 1049 void TurboAssembler::Ulw(Register rd, const MemOperand& rs) { 1050 DCHECK(rd != at); 1051 DCHECK(rs.rm() != at); 1052 if (IsMipsArchVariant(kMips32r6)) { 1053 lw(rd, rs); 1054 } else { 1055 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1056 IsMipsArchVariant(kLoongson)); 1057 DCHECK(kMipsLwrOffset <= 3 && kMipsLwlOffset <= 3); 1058 MemOperand source = rs; 1059 // Adjust offset for two accesses and check if offset + 3 fits into int16_t. 1060 AdjustBaseAndOffset(source, OffsetAccessType::TWO_ACCESSES, 3); 1061 if (rd != source.rm()) { 1062 lwr(rd, MemOperand(source.rm(), source.offset() + kMipsLwrOffset)); 1063 lwl(rd, MemOperand(source.rm(), source.offset() + kMipsLwlOffset)); 1064 } else { 1065 UseScratchRegisterScope temps(this); 1066 Register scratch = temps.Acquire(); 1067 lwr(scratch, MemOperand(rs.rm(), rs.offset() + kMipsLwrOffset)); 1068 lwl(scratch, MemOperand(rs.rm(), rs.offset() + kMipsLwlOffset)); 1069 mov(rd, scratch); 1070 } 1071 } 1072 } 1073 1074 void TurboAssembler::Usw(Register rd, const MemOperand& rs) { 1075 DCHECK(rd != at); 1076 DCHECK(rs.rm() != at); 1077 DCHECK(rd != rs.rm()); 1078 if (IsMipsArchVariant(kMips32r6)) { 1079 sw(rd, rs); 1080 } else { 1081 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1082 IsMipsArchVariant(kLoongson)); 1083 DCHECK(kMipsSwrOffset <= 3 && kMipsSwlOffset <= 3); 1084 MemOperand source = rs; 1085 // Adjust offset for two accesses and check if offset + 3 fits into int16_t. 1086 AdjustBaseAndOffset(source, OffsetAccessType::TWO_ACCESSES, 3); 1087 swr(rd, MemOperand(source.rm(), source.offset() + kMipsSwrOffset)); 1088 swl(rd, MemOperand(source.rm(), source.offset() + kMipsSwlOffset)); 1089 } 1090 } 1091 1092 void TurboAssembler::Ulh(Register rd, const MemOperand& rs) { 1093 DCHECK(rd != at); 1094 DCHECK(rs.rm() != at); 1095 if (IsMipsArchVariant(kMips32r6)) { 1096 lh(rd, rs); 1097 } else { 1098 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1099 IsMipsArchVariant(kLoongson)); 1100 MemOperand source = rs; 1101 // Adjust offset for two accesses and check if offset + 1 fits into int16_t. 1102 AdjustBaseAndOffset(source, OffsetAccessType::TWO_ACCESSES, 1); 1103 UseScratchRegisterScope temps(this); 1104 Register scratch = temps.Acquire(); 1105 if (source.rm() == scratch) { 1106 #if defined(V8_TARGET_LITTLE_ENDIAN) 1107 lb(rd, MemOperand(source.rm(), source.offset() + 1)); 1108 lbu(scratch, source); 1109 #elif defined(V8_TARGET_BIG_ENDIAN) 1110 lb(rd, source); 1111 lbu(scratch, MemOperand(source.rm(), source.offset() + 1)); 1112 #endif 1113 } else { 1114 #if defined(V8_TARGET_LITTLE_ENDIAN) 1115 lbu(scratch, source); 1116 lb(rd, MemOperand(source.rm(), source.offset() + 1)); 1117 #elif defined(V8_TARGET_BIG_ENDIAN) 1118 lbu(scratch, MemOperand(source.rm(), source.offset() + 1)); 1119 lb(rd, source); 1120 #endif 1121 } 1122 sll(rd, rd, 8); 1123 or_(rd, rd, scratch); 1124 } 1125 } 1126 1127 void TurboAssembler::Ulhu(Register rd, const MemOperand& rs) { 1128 DCHECK(rd != at); 1129 DCHECK(rs.rm() != at); 1130 if (IsMipsArchVariant(kMips32r6)) { 1131 lhu(rd, rs); 1132 } else { 1133 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1134 IsMipsArchVariant(kLoongson)); 1135 MemOperand source = rs; 1136 // Adjust offset for two accesses and check if offset + 1 fits into int16_t. 1137 AdjustBaseAndOffset(source, OffsetAccessType::TWO_ACCESSES, 1); 1138 UseScratchRegisterScope temps(this); 1139 Register scratch = temps.Acquire(); 1140 if (source.rm() == scratch) { 1141 #if defined(V8_TARGET_LITTLE_ENDIAN) 1142 lbu(rd, MemOperand(source.rm(), source.offset() + 1)); 1143 lbu(scratch, source); 1144 #elif defined(V8_TARGET_BIG_ENDIAN) 1145 lbu(rd, source); 1146 lbu(scratch, MemOperand(source.rm(), source.offset() + 1)); 1147 #endif 1148 } else { 1149 #if defined(V8_TARGET_LITTLE_ENDIAN) 1150 lbu(scratch, source); 1151 lbu(rd, MemOperand(source.rm(), source.offset() + 1)); 1152 #elif defined(V8_TARGET_BIG_ENDIAN) 1153 lbu(scratch, MemOperand(source.rm(), source.offset() + 1)); 1154 lbu(rd, source); 1155 #endif 1156 } 1157 sll(rd, rd, 8); 1158 or_(rd, rd, scratch); 1159 } 1160 } 1161 1162 void TurboAssembler::Ush(Register rd, const MemOperand& rs, Register scratch) { 1163 DCHECK(rd != at); 1164 DCHECK(rs.rm() != at); 1165 DCHECK(rs.rm() != scratch); 1166 DCHECK(scratch != at); 1167 if (IsMipsArchVariant(kMips32r6)) { 1168 sh(rd, rs); 1169 } else { 1170 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1171 IsMipsArchVariant(kLoongson)); 1172 MemOperand source = rs; 1173 // Adjust offset for two accesses and check if offset + 1 fits into int16_t. 1174 AdjustBaseAndOffset(source, OffsetAccessType::TWO_ACCESSES, 1); 1175 1176 if (scratch != rd) { 1177 mov(scratch, rd); 1178 } 1179 1180 #if defined(V8_TARGET_LITTLE_ENDIAN) 1181 sb(scratch, source); 1182 srl(scratch, scratch, 8); 1183 sb(scratch, MemOperand(source.rm(), source.offset() + 1)); 1184 #elif defined(V8_TARGET_BIG_ENDIAN) 1185 sb(scratch, MemOperand(source.rm(), source.offset() + 1)); 1186 srl(scratch, scratch, 8); 1187 sb(scratch, source); 1188 #endif 1189 } 1190 } 1191 1192 void TurboAssembler::Ulwc1(FPURegister fd, const MemOperand& rs, 1193 Register scratch) { 1194 if (IsMipsArchVariant(kMips32r6)) { 1195 lwc1(fd, rs); 1196 } else { 1197 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1198 IsMipsArchVariant(kLoongson)); 1199 Ulw(scratch, rs); 1200 mtc1(scratch, fd); 1201 } 1202 } 1203 1204 void TurboAssembler::Uswc1(FPURegister fd, const MemOperand& rs, 1205 Register scratch) { 1206 if (IsMipsArchVariant(kMips32r6)) { 1207 swc1(fd, rs); 1208 } else { 1209 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1210 IsMipsArchVariant(kLoongson)); 1211 mfc1(scratch, fd); 1212 Usw(scratch, rs); 1213 } 1214 } 1215 1216 void TurboAssembler::Uldc1(FPURegister fd, const MemOperand& rs, 1217 Register scratch) { 1218 DCHECK(scratch != at); 1219 if (IsMipsArchVariant(kMips32r6)) { 1220 Ldc1(fd, rs); 1221 } else { 1222 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1223 IsMipsArchVariant(kLoongson)); 1224 Ulw(scratch, MemOperand(rs.rm(), rs.offset() + Register::kMantissaOffset)); 1225 mtc1(scratch, fd); 1226 Ulw(scratch, MemOperand(rs.rm(), rs.offset() + Register::kExponentOffset)); 1227 Mthc1(scratch, fd); 1228 } 1229 } 1230 1231 void TurboAssembler::Usdc1(FPURegister fd, const MemOperand& rs, 1232 Register scratch) { 1233 DCHECK(scratch != at); 1234 if (IsMipsArchVariant(kMips32r6)) { 1235 Sdc1(fd, rs); 1236 } else { 1237 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1238 IsMipsArchVariant(kLoongson)); 1239 mfc1(scratch, fd); 1240 Usw(scratch, MemOperand(rs.rm(), rs.offset() + Register::kMantissaOffset)); 1241 Mfhc1(scratch, fd); 1242 Usw(scratch, MemOperand(rs.rm(), rs.offset() + Register::kExponentOffset)); 1243 } 1244 } 1245 1246 void TurboAssembler::Ldc1(FPURegister fd, const MemOperand& src) { 1247 // Workaround for non-8-byte alignment of HeapNumber, convert 64-bit 1248 // load to two 32-bit loads. 1249 { 1250 BlockTrampolinePoolScope block_trampoline_pool(this); 1251 DCHECK(Register::kMantissaOffset <= 4 && Register::kExponentOffset <= 4); 1252 MemOperand tmp = src; 1253 AdjustBaseAndOffset(tmp, OffsetAccessType::TWO_ACCESSES); 1254 lwc1(fd, MemOperand(tmp.rm(), tmp.offset() + Register::kMantissaOffset)); 1255 if (IsFp32Mode()) { // fp32 mode. 1256 FPURegister nextfpreg = FPURegister::from_code(fd.code() + 1); 1257 lwc1(nextfpreg, 1258 MemOperand(tmp.rm(), tmp.offset() + Register::kExponentOffset)); 1259 } else { 1260 DCHECK(IsFp64Mode() || IsFpxxMode()); 1261 // Currently we support FPXX and FP64 on Mips32r2 and Mips32r6 1262 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); 1263 UseScratchRegisterScope temps(this); 1264 Register scratch = temps.Acquire(); 1265 DCHECK(src.rm() != scratch); 1266 lw(scratch, 1267 MemOperand(tmp.rm(), tmp.offset() + Register::kExponentOffset)); 1268 Mthc1(scratch, fd); 1269 } 1270 } 1271 CheckTrampolinePoolQuick(1); 1272 } 1273 1274 void TurboAssembler::Sdc1(FPURegister fd, const MemOperand& src) { 1275 // Workaround for non-8-byte alignment of HeapNumber, convert 64-bit 1276 // store to two 32-bit stores. 1277 { 1278 BlockTrampolinePoolScope block_trampoline_pool(this); 1279 DCHECK(Register::kMantissaOffset <= 4 && Register::kExponentOffset <= 4); 1280 MemOperand tmp = src; 1281 AdjustBaseAndOffset(tmp, OffsetAccessType::TWO_ACCESSES); 1282 swc1(fd, MemOperand(tmp.rm(), tmp.offset() + Register::kMantissaOffset)); 1283 if (IsFp32Mode()) { // fp32 mode. 1284 FPURegister nextfpreg = FPURegister::from_code(fd.code() + 1); 1285 swc1(nextfpreg, 1286 MemOperand(tmp.rm(), tmp.offset() + Register::kExponentOffset)); 1287 } else { 1288 BlockTrampolinePoolScope block_trampoline_pool(this); 1289 DCHECK(IsFp64Mode() || IsFpxxMode()); 1290 // Currently we support FPXX and FP64 on Mips32r2 and Mips32r6 1291 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); 1292 DCHECK(src.rm() != t8); 1293 Mfhc1(t8, fd); 1294 sw(t8, MemOperand(tmp.rm(), tmp.offset() + Register::kExponentOffset)); 1295 } 1296 } 1297 CheckTrampolinePoolQuick(1); 1298 } 1299 1300 void TurboAssembler::Ll(Register rd, const MemOperand& rs) { 1301 bool is_one_instruction = IsMipsArchVariant(kMips32r6) 1302 ? is_int9(rs.offset()) 1303 : is_int16(rs.offset()); 1304 if (is_one_instruction) { 1305 ll(rd, rs); 1306 } else { 1307 UseScratchRegisterScope temps(this); 1308 Register scratch = temps.Acquire(); 1309 li(scratch, rs.offset()); 1310 addu(scratch, scratch, rs.rm()); 1311 ll(rd, MemOperand(scratch, 0)); 1312 } 1313 } 1314 1315 void TurboAssembler::Sc(Register rd, const MemOperand& rs) { 1316 bool is_one_instruction = IsMipsArchVariant(kMips32r6) 1317 ? is_int9(rs.offset()) 1318 : is_int16(rs.offset()); 1319 if (is_one_instruction) { 1320 sc(rd, rs); 1321 } else { 1322 UseScratchRegisterScope temps(this); 1323 Register scratch = temps.Acquire(); 1324 li(scratch, rs.offset()); 1325 addu(scratch, scratch, rs.rm()); 1326 sc(rd, MemOperand(scratch, 0)); 1327 } 1328 } 1329 1330 void TurboAssembler::li(Register dst, Handle<HeapObject> value, LiFlags mode) { 1331 if (FLAG_embedded_builtins) { 1332 if (root_array_available_ && options().isolate_independent_code) { 1333 IndirectLoadConstant(dst, value); 1334 return; 1335 } 1336 } 1337 li(dst, Operand(value), mode); 1338 } 1339 1340 void TurboAssembler::li(Register dst, ExternalReference value, LiFlags mode) { 1341 if (FLAG_embedded_builtins) { 1342 if (root_array_available_ && options().isolate_independent_code) { 1343 IndirectLoadExternalReference(dst, value); 1344 return; 1345 } 1346 } 1347 li(dst, Operand(value), mode); 1348 } 1349 1350 void TurboAssembler::li(Register rd, Operand j, LiFlags mode) { 1351 DCHECK(!j.is_reg()); 1352 BlockTrampolinePoolScope block_trampoline_pool(this); 1353 if (!MustUseReg(j.rmode()) && mode == OPTIMIZE_SIZE) { 1354 // Normal load of an immediate value which does not need Relocation Info. 1355 if (is_int16(j.immediate())) { 1356 addiu(rd, zero_reg, j.immediate()); 1357 } else if (!(j.immediate() & kHiMask)) { 1358 ori(rd, zero_reg, j.immediate()); 1359 } else { 1360 lui(rd, (j.immediate() >> kLuiShift) & kImm16Mask); 1361 if (j.immediate() & kImm16Mask) { 1362 ori(rd, rd, (j.immediate() & kImm16Mask)); 1363 } 1364 } 1365 } else { 1366 int32_t immediate; 1367 if (j.IsHeapObjectRequest()) { 1368 RequestHeapObject(j.heap_object_request()); 1369 immediate = 0; 1370 } else { 1371 immediate = j.immediate(); 1372 } 1373 1374 if (MustUseReg(j.rmode())) { 1375 RecordRelocInfo(j.rmode(), immediate); 1376 } 1377 // We always need the same number of instructions as we may need to patch 1378 // this code to load another value which may need 2 instructions to load. 1379 1380 lui(rd, (immediate >> kLuiShift) & kImm16Mask); 1381 ori(rd, rd, (immediate & kImm16Mask)); 1382 } 1383 } 1384 1385 void TurboAssembler::MultiPush(RegList regs) { 1386 int16_t num_to_push = base::bits::CountPopulation(regs); 1387 int16_t stack_offset = num_to_push * kPointerSize; 1388 1389 Subu(sp, sp, Operand(stack_offset)); 1390 for (int16_t i = kNumRegisters - 1; i >= 0; i--) { 1391 if ((regs & (1 << i)) != 0) { 1392 stack_offset -= kPointerSize; 1393 sw(ToRegister(i), MemOperand(sp, stack_offset)); 1394 } 1395 } 1396 } 1397 1398 1399 void TurboAssembler::MultiPop(RegList regs) { 1400 int16_t stack_offset = 0; 1401 1402 for (int16_t i = 0; i < kNumRegisters; i++) { 1403 if ((regs & (1 << i)) != 0) { 1404 lw(ToRegister(i), MemOperand(sp, stack_offset)); 1405 stack_offset += kPointerSize; 1406 } 1407 } 1408 addiu(sp, sp, stack_offset); 1409 } 1410 1411 1412 void TurboAssembler::MultiPushFPU(RegList regs) { 1413 int16_t num_to_push = base::bits::CountPopulation(regs); 1414 int16_t stack_offset = num_to_push * kDoubleSize; 1415 1416 Subu(sp, sp, Operand(stack_offset)); 1417 for (int16_t i = kNumRegisters - 1; i >= 0; i--) { 1418 if ((regs & (1 << i)) != 0) { 1419 stack_offset -= kDoubleSize; 1420 Sdc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); 1421 } 1422 } 1423 } 1424 1425 1426 void TurboAssembler::MultiPopFPU(RegList regs) { 1427 int16_t stack_offset = 0; 1428 1429 for (int16_t i = 0; i < kNumRegisters; i++) { 1430 if ((regs & (1 << i)) != 0) { 1431 Ldc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); 1432 stack_offset += kDoubleSize; 1433 } 1434 } 1435 addiu(sp, sp, stack_offset); 1436 } 1437 1438 void TurboAssembler::AddPair(Register dst_low, Register dst_high, 1439 Register left_low, Register left_high, 1440 Register right_low, Register right_high, 1441 Register scratch1, Register scratch2) { 1442 BlockTrampolinePoolScope block_trampoline_pool(this); 1443 Register scratch3 = t8; 1444 Addu(scratch1, left_low, right_low); 1445 Sltu(scratch3, scratch1, left_low); 1446 Addu(scratch2, left_high, right_high); 1447 Addu(dst_high, scratch2, scratch3); 1448 Move(dst_low, scratch1); 1449 } 1450 1451 void TurboAssembler::SubPair(Register dst_low, Register dst_high, 1452 Register left_low, Register left_high, 1453 Register right_low, Register right_high, 1454 Register scratch1, Register scratch2) { 1455 BlockTrampolinePoolScope block_trampoline_pool(this); 1456 Register scratch3 = t8; 1457 Sltu(scratch3, left_low, right_low); 1458 Subu(scratch1, left_low, right_low); 1459 Subu(scratch2, left_high, right_high); 1460 Subu(dst_high, scratch2, scratch3); 1461 Move(dst_low, scratch1); 1462 } 1463 1464 void TurboAssembler::MulPair(Register dst_low, Register dst_high, 1465 Register left_low, Register left_high, 1466 Register right_low, Register right_high, 1467 Register scratch1, Register scratch2) { 1468 BlockTrampolinePoolScope block_trampoline_pool(this); 1469 Register scratch3 = t8; 1470 Mulu(scratch2, scratch1, left_low, right_low); 1471 Mul(scratch3, left_low, right_high); 1472 Addu(scratch2, scratch2, scratch3); 1473 Mul(scratch3, left_high, right_low); 1474 Addu(dst_high, scratch2, scratch3); 1475 Move(dst_low, scratch1); 1476 } 1477 1478 void TurboAssembler::ShlPair(Register dst_low, Register dst_high, 1479 Register src_low, Register src_high, 1480 Register shift, Register scratch1, 1481 Register scratch2) { 1482 BlockTrampolinePoolScope block_trampoline_pool(this); 1483 Label done; 1484 Register scratch3 = t8; 1485 And(scratch3, shift, 0x3F); 1486 sllv(dst_low, src_low, scratch3); 1487 Nor(scratch2, zero_reg, scratch3); 1488 srl(scratch1, src_low, 1); 1489 srlv(scratch1, scratch1, scratch2); 1490 sllv(dst_high, src_high, scratch3); 1491 Or(dst_high, dst_high, scratch1); 1492 And(scratch1, scratch3, 32); 1493 if (IsMipsArchVariant(kLoongson) || IsMipsArchVariant(kMips32r6)) { 1494 Branch(&done, eq, scratch1, Operand(zero_reg)); 1495 mov(dst_high, dst_low); 1496 mov(dst_low, zero_reg); 1497 } else { 1498 movn(dst_high, dst_low, scratch1); 1499 movn(dst_low, zero_reg, scratch1); 1500 } 1501 bind(&done); 1502 } 1503 1504 void TurboAssembler::ShlPair(Register dst_low, Register dst_high, 1505 Register src_low, Register src_high, 1506 uint32_t shift, Register scratch) { 1507 shift = shift & 0x3F; 1508 if (shift == 0) { 1509 mov(dst_low, src_low); 1510 mov(dst_high, src_high); 1511 } else if (shift < 32) { 1512 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1513 srl(dst_high, src_low, 32 - shift); 1514 Ins(dst_high, src_high, shift, 32 - shift); 1515 sll(dst_low, src_low, shift); 1516 } else { 1517 sll(dst_high, src_high, shift); 1518 sll(dst_low, src_low, shift); 1519 srl(scratch, src_low, 32 - shift); 1520 Or(dst_high, dst_high, scratch); 1521 } 1522 } else if (shift == 32) { 1523 mov(dst_low, zero_reg); 1524 mov(dst_high, src_low); 1525 } else { 1526 shift = shift - 32; 1527 mov(dst_low, zero_reg); 1528 sll(dst_high, src_low, shift); 1529 } 1530 } 1531 1532 void TurboAssembler::ShrPair(Register dst_low, Register dst_high, 1533 Register src_low, Register src_high, 1534 Register shift, Register scratch1, 1535 Register scratch2) { 1536 BlockTrampolinePoolScope block_trampoline_pool(this); 1537 Label done; 1538 Register scratch3 = t8; 1539 And(scratch3, shift, 0x3F); 1540 srlv(dst_high, src_high, scratch3); 1541 Nor(scratch2, zero_reg, scratch3); 1542 sll(scratch1, src_high, 1); 1543 sllv(scratch1, scratch1, scratch2); 1544 srlv(dst_low, src_low, scratch3); 1545 Or(dst_low, dst_low, scratch1); 1546 And(scratch1, scratch3, 32); 1547 if (IsMipsArchVariant(kLoongson) || IsMipsArchVariant(kMips32r6)) { 1548 Branch(&done, eq, scratch1, Operand(zero_reg)); 1549 mov(dst_low, dst_high); 1550 mov(dst_high, zero_reg); 1551 } else { 1552 movn(dst_low, dst_high, scratch1); 1553 movn(dst_high, zero_reg, scratch1); 1554 } 1555 bind(&done); 1556 } 1557 1558 void TurboAssembler::ShrPair(Register dst_low, Register dst_high, 1559 Register src_low, Register src_high, 1560 uint32_t shift, Register scratch) { 1561 shift = shift & 0x3F; 1562 if (shift == 0) { 1563 mov(dst_low, src_low); 1564 mov(dst_high, src_high); 1565 } else if (shift < 32) { 1566 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1567 srl(dst_low, src_low, shift); 1568 Ins(dst_low, src_high, 32 - shift, shift); 1569 srl(dst_high, src_high, shift); 1570 } else { 1571 srl(dst_high, src_high, shift); 1572 srl(dst_low, src_low, shift); 1573 shift = 32 - shift; 1574 sll(scratch, src_high, shift); 1575 Or(dst_low, dst_low, scratch); 1576 } 1577 } else if (shift == 32) { 1578 mov(dst_high, zero_reg); 1579 mov(dst_low, src_high); 1580 } else { 1581 shift = shift - 32; 1582 mov(dst_high, zero_reg); 1583 srl(dst_low, src_high, shift); 1584 } 1585 } 1586 1587 void TurboAssembler::SarPair(Register dst_low, Register dst_high, 1588 Register src_low, Register src_high, 1589 Register shift, Register scratch1, 1590 Register scratch2) { 1591 BlockTrampolinePoolScope block_trampoline_pool(this); 1592 Label done; 1593 Register scratch3 = t8; 1594 And(scratch3, shift, 0x3F); 1595 srav(dst_high, src_high, scratch3); 1596 Nor(scratch2, zero_reg, scratch3); 1597 sll(scratch1, src_high, 1); 1598 sllv(scratch1, scratch1, scratch2); 1599 srlv(dst_low, src_low, scratch3); 1600 Or(dst_low, dst_low, scratch1); 1601 And(scratch1, scratch3, 32); 1602 Branch(&done, eq, scratch1, Operand(zero_reg)); 1603 mov(dst_low, dst_high); 1604 sra(dst_high, dst_high, 31); 1605 bind(&done); 1606 } 1607 1608 void TurboAssembler::SarPair(Register dst_low, Register dst_high, 1609 Register src_low, Register src_high, 1610 uint32_t shift, Register scratch) { 1611 shift = shift & 0x3F; 1612 if (shift == 0) { 1613 mov(dst_low, src_low); 1614 mov(dst_high, src_high); 1615 } else if (shift < 32) { 1616 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1617 srl(dst_low, src_low, shift); 1618 Ins(dst_low, src_high, 32 - shift, shift); 1619 sra(dst_high, src_high, shift); 1620 } else { 1621 sra(dst_high, src_high, shift); 1622 srl(dst_low, src_low, shift); 1623 shift = 32 - shift; 1624 sll(scratch, src_high, shift); 1625 Or(dst_low, dst_low, scratch); 1626 } 1627 } else if (shift == 32) { 1628 sra(dst_high, src_high, 31); 1629 mov(dst_low, src_high); 1630 } else { 1631 shift = shift - 32; 1632 sra(dst_high, src_high, 31); 1633 sra(dst_low, src_high, shift); 1634 } 1635 } 1636 1637 void TurboAssembler::Ext(Register rt, Register rs, uint16_t pos, 1638 uint16_t size) { 1639 DCHECK_LT(pos, 32); 1640 DCHECK_LT(pos + size, 33); 1641 1642 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1643 ext_(rt, rs, pos, size); 1644 } else { 1645 // Move rs to rt and shift it left then right to get the 1646 // desired bitfield on the right side and zeroes on the left. 1647 int shift_left = 32 - (pos + size); 1648 sll(rt, rs, shift_left); // Acts as a move if shift_left == 0. 1649 1650 int shift_right = 32 - size; 1651 if (shift_right > 0) { 1652 srl(rt, rt, shift_right); 1653 } 1654 } 1655 } 1656 1657 void TurboAssembler::Ins(Register rt, Register rs, uint16_t pos, 1658 uint16_t size) { 1659 DCHECK_LT(pos, 32); 1660 DCHECK_LE(pos + size, 32); 1661 DCHECK_NE(size, 0); 1662 1663 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1664 ins_(rt, rs, pos, size); 1665 } else { 1666 DCHECK(rt != t8 && rs != t8); 1667 BlockTrampolinePoolScope block_trampoline_pool(this); 1668 UseScratchRegisterScope temps(this); 1669 Register scratch = temps.Acquire(); 1670 Subu(scratch, zero_reg, Operand(1)); 1671 srl(scratch, scratch, 32 - size); 1672 and_(t8, rs, scratch); 1673 sll(t8, t8, pos); 1674 sll(scratch, scratch, pos); 1675 nor(scratch, scratch, zero_reg); 1676 and_(scratch, rt, scratch); 1677 or_(rt, t8, scratch); 1678 } 1679 } 1680 1681 void TurboAssembler::ExtractBits(Register dest, Register source, Register pos, 1682 int size, bool sign_extend) { 1683 srav(dest, source, pos); 1684 Ext(dest, dest, 0, size); 1685 if (size == 8) { 1686 if (sign_extend) { 1687 Seb(dest, dest); 1688 } 1689 } else if (size == 16) { 1690 if (sign_extend) { 1691 Seh(dest, dest); 1692 } 1693 } else { 1694 UNREACHABLE(); 1695 } 1696 } 1697 1698 void TurboAssembler::InsertBits(Register dest, Register source, Register pos, 1699 int size) { 1700 Ror(dest, dest, pos); 1701 Ins(dest, source, 0, size); 1702 { 1703 UseScratchRegisterScope temps(this); 1704 Register scratch = temps.Acquire(); 1705 Subu(scratch, pos, Operand(32)); 1706 Neg(scratch, Operand(scratch)); 1707 Ror(dest, dest, scratch); 1708 } 1709 } 1710 1711 void TurboAssembler::Seb(Register rd, Register rt) { 1712 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1713 seb(rd, rt); 1714 } else { 1715 DCHECK(IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kLoongson)); 1716 sll(rd, rt, 24); 1717 sra(rd, rd, 24); 1718 } 1719 } 1720 1721 void TurboAssembler::Seh(Register rd, Register rt) { 1722 if (IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)) { 1723 seh(rd, rt); 1724 } else { 1725 DCHECK(IsMipsArchVariant(kMips32r1) || IsMipsArchVariant(kLoongson)); 1726 sll(rd, rt, 16); 1727 sra(rd, rd, 16); 1728 } 1729 } 1730 1731 void TurboAssembler::Neg_s(FPURegister fd, FPURegister fs) { 1732 if (IsMipsArchVariant(kMips32r6)) { 1733 // r6 neg_s changes the sign for NaN-like operands as well. 1734 neg_s(fd, fs); 1735 } else { 1736 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1737 IsMipsArchVariant(kLoongson)); 1738 BlockTrampolinePoolScope block_trampoline_pool(this); 1739 Label is_nan, done; 1740 Register scratch1 = t8; 1741 Register scratch2 = t9; 1742 CompareIsNanF32(fs, fs); 1743 BranchTrueShortF(&is_nan); 1744 Branch(USE_DELAY_SLOT, &done); 1745 // For NaN input, neg_s will return the same NaN value, 1746 // while the sign has to be changed separately. 1747 neg_s(fd, fs); // In delay slot. 1748 bind(&is_nan); 1749 mfc1(scratch1, fs); 1750 li(scratch2, kBinary32SignMask); 1751 Xor(scratch1, scratch1, scratch2); 1752 mtc1(scratch1, fd); 1753 bind(&done); 1754 } 1755 } 1756 1757 void TurboAssembler::Neg_d(FPURegister fd, FPURegister fs) { 1758 if (IsMipsArchVariant(kMips32r6)) { 1759 // r6 neg_d changes the sign for NaN-like operands as well. 1760 neg_d(fd, fs); 1761 } else { 1762 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r1) || 1763 IsMipsArchVariant(kLoongson)); 1764 BlockTrampolinePoolScope block_trampoline_pool(this); 1765 Label is_nan, done; 1766 Register scratch1 = t8; 1767 Register scratch2 = t9; 1768 CompareIsNanF64(fs, fs); 1769 BranchTrueShortF(&is_nan); 1770 Branch(USE_DELAY_SLOT, &done); 1771 // For NaN input, neg_d will return the same NaN value, 1772 // while the sign has to be changed separately. 1773 neg_d(fd, fs); // In delay slot. 1774 bind(&is_nan); 1775 Move(fd, fs); 1776 Mfhc1(scratch1, fd); 1777 li(scratch2, HeapNumber::kSignMask); 1778 Xor(scratch1, scratch1, scratch2); 1779 Mthc1(scratch1, fd); 1780 bind(&done); 1781 } 1782 } 1783 1784 void TurboAssembler::Cvt_d_uw(FPURegister fd, Register rs, 1785 FPURegister scratch) { 1786 // In FP64Mode we do conversion from long. 1787 if (IsFp64Mode()) { 1788 mtc1(rs, scratch); 1789 Mthc1(zero_reg, scratch); 1790 cvt_d_l(fd, scratch); 1791 } else { 1792 // Convert rs to a FP value in fd. 1793 DCHECK(fd != scratch); 1794 DCHECK(rs != at); 1795 1796 Label msb_clear, conversion_done; 1797 // For a value which is < 2^31, regard it as a signed positve word. 1798 Branch(&msb_clear, ge, rs, Operand(zero_reg), USE_DELAY_SLOT); 1799 mtc1(rs, fd); 1800 { 1801 UseScratchRegisterScope temps(this); 1802 Register scratch1 = temps.Acquire(); 1803 li(scratch1, 0x41F00000); // FP value: 2^32. 1804 1805 // For unsigned inputs > 2^31, we convert to double as a signed int32, 1806 // then add 2^32 to move it back to unsigned value in range 2^31..2^31-1. 1807 mtc1(zero_reg, scratch); 1808 Mthc1(scratch1, scratch); 1809 } 1810 1811 cvt_d_w(fd, fd); 1812 1813 Branch(USE_DELAY_SLOT, &conversion_done); 1814 add_d(fd, fd, scratch); 1815 1816 bind(&msb_clear); 1817 cvt_d_w(fd, fd); 1818 1819 bind(&conversion_done); 1820 } 1821 } 1822 1823 void TurboAssembler::Trunc_uw_d(FPURegister fd, FPURegister fs, 1824 FPURegister scratch) { 1825 BlockTrampolinePoolScope block_trampoline_pool(this); 1826 Trunc_uw_d(t8, fs, scratch); 1827 mtc1(t8, fd); 1828 } 1829 1830 void TurboAssembler::Trunc_uw_s(FPURegister fd, FPURegister fs, 1831 FPURegister scratch) { 1832 BlockTrampolinePoolScope block_trampoline_pool(this); 1833 Trunc_uw_s(t8, fs, scratch); 1834 mtc1(t8, fd); 1835 } 1836 1837 void TurboAssembler::Trunc_w_d(FPURegister fd, FPURegister fs) { 1838 if (IsMipsArchVariant(kLoongson) && fd == fs) { 1839 BlockTrampolinePoolScope block_trampoline_pool(this); 1840 Mfhc1(t8, fs); 1841 trunc_w_d(fd, fs); 1842 Mthc1(t8, fs); 1843 } else { 1844 trunc_w_d(fd, fs); 1845 } 1846 } 1847 1848 void TurboAssembler::Round_w_d(FPURegister fd, FPURegister fs) { 1849 if (IsMipsArchVariant(kLoongson) && fd == fs) { 1850 BlockTrampolinePoolScope block_trampoline_pool(this); 1851 Mfhc1(t8, fs); 1852 round_w_d(fd, fs); 1853 Mthc1(t8, fs); 1854 } else { 1855 round_w_d(fd, fs); 1856 } 1857 } 1858 1859 void TurboAssembler::Floor_w_d(FPURegister fd, FPURegister fs) { 1860 if (IsMipsArchVariant(kLoongson) && fd == fs) { 1861 BlockTrampolinePoolScope block_trampoline_pool(this); 1862 Mfhc1(t8, fs); 1863 floor_w_d(fd, fs); 1864 Mthc1(t8, fs); 1865 } else { 1866 floor_w_d(fd, fs); 1867 } 1868 } 1869 1870 void TurboAssembler::Ceil_w_d(FPURegister fd, FPURegister fs) { 1871 if (IsMipsArchVariant(kLoongson) && fd == fs) { 1872 BlockTrampolinePoolScope block_trampoline_pool(this); 1873 Mfhc1(t8, fs); 1874 ceil_w_d(fd, fs); 1875 Mthc1(t8, fs); 1876 } else { 1877 ceil_w_d(fd, fs); 1878 } 1879 } 1880 1881 void TurboAssembler::Trunc_uw_d(Register rd, FPURegister fs, 1882 FPURegister scratch) { 1883 DCHECK(fs != scratch); 1884 DCHECK(rd != at); 1885 1886 { 1887 // Load 2^31 into scratch as its float representation. 1888 UseScratchRegisterScope temps(this); 1889 Register scratch1 = temps.Acquire(); 1890 li(scratch1, 0x41E00000); 1891 mtc1(zero_reg, scratch); 1892 Mthc1(scratch1, scratch); 1893 } 1894 // Test if scratch > fs. 1895 // If fs < 2^31 we can convert it normally. 1896 Label simple_convert; 1897 CompareF64(OLT, fs, scratch); 1898 BranchTrueShortF(&simple_convert); 1899 1900 // First we subtract 2^31 from fs, then trunc it to rd 1901 // and add 2^31 to rd. 1902 sub_d(scratch, fs, scratch); 1903 trunc_w_d(scratch, scratch); 1904 mfc1(rd, scratch); 1905 Or(rd, rd, 1 << 31); 1906 1907 Label done; 1908 Branch(&done); 1909 // Simple conversion. 1910 bind(&simple_convert); 1911 trunc_w_d(scratch, fs); 1912 mfc1(rd, scratch); 1913 1914 bind(&done); 1915 } 1916 1917 void TurboAssembler::Trunc_uw_s(Register rd, FPURegister fs, 1918 FPURegister scratch) { 1919 DCHECK(fs != scratch); 1920 DCHECK(rd != at); 1921 1922 { 1923 // Load 2^31 into scratch as its float representation. 1924 UseScratchRegisterScope temps(this); 1925 Register scratch1 = temps.Acquire(); 1926 li(scratch1, 0x4F000000); 1927 mtc1(scratch1, scratch); 1928 } 1929 // Test if scratch > fs. 1930 // If fs < 2^31 we can convert it normally. 1931 Label simple_convert; 1932 CompareF32(OLT, fs, scratch); 1933 BranchTrueShortF(&simple_convert); 1934 1935 // First we subtract 2^31 from fs, then trunc it to rd 1936 // and add 2^31 to rd. 1937 sub_s(scratch, fs, scratch); 1938 trunc_w_s(scratch, scratch); 1939 mfc1(rd, scratch); 1940 Or(rd, rd, 1 << 31); 1941 1942 Label done; 1943 Branch(&done); 1944 // Simple conversion. 1945 bind(&simple_convert); 1946 trunc_w_s(scratch, fs); 1947 mfc1(rd, scratch); 1948 1949 bind(&done); 1950 } 1951 1952 template <typename RoundFunc> 1953 void TurboAssembler::RoundDouble(FPURegister dst, FPURegister src, 1954 FPURoundingMode mode, RoundFunc round) { 1955 BlockTrampolinePoolScope block_trampoline_pool(this); 1956 Register scratch = t8; 1957 Register scratch2 = t9; 1958 if (IsMipsArchVariant(kMips32r6)) { 1959 cfc1(scratch, FCSR); 1960 li(at, Operand(mode)); 1961 ctc1(at, FCSR); 1962 rint_d(dst, src); 1963 ctc1(scratch, FCSR); 1964 } else { 1965 Label done; 1966 Mfhc1(scratch, src); 1967 Ext(at, scratch, HeapNumber::kExponentShift, HeapNumber::kExponentBits); 1968 Branch(USE_DELAY_SLOT, &done, hs, at, 1969 Operand(HeapNumber::kExponentBias + HeapNumber::kMantissaBits)); 1970 mov_d(dst, src); 1971 round(this, dst, src); 1972 Move(at, scratch2, dst); 1973 or_(at, at, scratch2); 1974 Branch(USE_DELAY_SLOT, &done, ne, at, Operand(zero_reg)); 1975 cvt_d_l(dst, dst); 1976 srl(at, scratch, 31); 1977 sll(at, at, 31); 1978 Mthc1(at, dst); 1979 bind(&done); 1980 } 1981 } 1982 1983 void TurboAssembler::Floor_d_d(FPURegister dst, FPURegister src) { 1984 RoundDouble(dst, src, mode_floor, 1985 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 1986 tasm->floor_l_d(dst, src); 1987 }); 1988 } 1989 1990 void TurboAssembler::Ceil_d_d(FPURegister dst, FPURegister src) { 1991 RoundDouble(dst, src, mode_ceil, 1992 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 1993 tasm->ceil_l_d(dst, src); 1994 }); 1995 } 1996 1997 void TurboAssembler::Trunc_d_d(FPURegister dst, FPURegister src) { 1998 RoundDouble(dst, src, mode_trunc, 1999 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 2000 tasm->trunc_l_d(dst, src); 2001 }); 2002 } 2003 2004 void TurboAssembler::Round_d_d(FPURegister dst, FPURegister src) { 2005 RoundDouble(dst, src, mode_round, 2006 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 2007 tasm->round_l_d(dst, src); 2008 }); 2009 } 2010 2011 template <typename RoundFunc> 2012 void TurboAssembler::RoundFloat(FPURegister dst, FPURegister src, 2013 FPURoundingMode mode, RoundFunc round) { 2014 BlockTrampolinePoolScope block_trampoline_pool(this); 2015 Register scratch = t8; 2016 if (IsMipsArchVariant(kMips32r6)) { 2017 cfc1(scratch, FCSR); 2018 li(at, Operand(mode)); 2019 ctc1(at, FCSR); 2020 rint_s(dst, src); 2021 ctc1(scratch, FCSR); 2022 } else { 2023 int32_t kFloat32ExponentBias = 127; 2024 int32_t kFloat32MantissaBits = 23; 2025 int32_t kFloat32ExponentBits = 8; 2026 Label done; 2027 mfc1(scratch, src); 2028 Ext(at, scratch, kFloat32MantissaBits, kFloat32ExponentBits); 2029 Branch(USE_DELAY_SLOT, &done, hs, at, 2030 Operand(kFloat32ExponentBias + kFloat32MantissaBits)); 2031 mov_s(dst, src); 2032 round(this, dst, src); 2033 mfc1(at, dst); 2034 Branch(USE_DELAY_SLOT, &done, ne, at, Operand(zero_reg)); 2035 cvt_s_w(dst, dst); 2036 srl(at, scratch, 31); 2037 sll(at, at, 31); 2038 mtc1(at, dst); 2039 bind(&done); 2040 } 2041 } 2042 2043 void TurboAssembler::Floor_s_s(FPURegister dst, FPURegister src) { 2044 RoundFloat(dst, src, mode_floor, 2045 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 2046 tasm->floor_w_s(dst, src); 2047 }); 2048 } 2049 2050 void TurboAssembler::Ceil_s_s(FPURegister dst, FPURegister src) { 2051 RoundFloat(dst, src, mode_ceil, 2052 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 2053 tasm->ceil_w_s(dst, src); 2054 }); 2055 } 2056 2057 void TurboAssembler::Trunc_s_s(FPURegister dst, FPURegister src) { 2058 RoundFloat(dst, src, mode_trunc, 2059 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 2060 tasm->trunc_w_s(dst, src); 2061 }); 2062 } 2063 2064 void TurboAssembler::Round_s_s(FPURegister dst, FPURegister src) { 2065 RoundFloat(dst, src, mode_round, 2066 [](TurboAssembler* tasm, FPURegister dst, FPURegister src) { 2067 tasm->round_w_s(dst, src); 2068 }); 2069 } 2070 2071 void TurboAssembler::Mthc1(Register rt, FPURegister fs) { 2072 if (IsFp32Mode()) { 2073 mtc1(rt, fs.high()); 2074 } else { 2075 DCHECK(IsFp64Mode() || IsFpxxMode()); 2076 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); 2077 mthc1(rt, fs); 2078 } 2079 } 2080 2081 void TurboAssembler::Mfhc1(Register rt, FPURegister fs) { 2082 if (IsFp32Mode()) { 2083 mfc1(rt, fs.high()); 2084 } else { 2085 DCHECK(IsFp64Mode() || IsFpxxMode()); 2086 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); 2087 mfhc1(rt, fs); 2088 } 2089 } 2090 2091 void TurboAssembler::Madd_s(FPURegister fd, FPURegister fr, FPURegister fs, 2092 FPURegister ft, FPURegister scratch) { 2093 if (IsMipsArchVariant(kMips32r2)) { 2094 madd_s(fd, fr, fs, ft); 2095 } else { 2096 DCHECK(fr != scratch && fs != scratch && ft != scratch); 2097 mul_s(scratch, fs, ft); 2098 add_s(fd, fr, scratch); 2099 } 2100 } 2101 2102 void TurboAssembler::Madd_d(FPURegister fd, FPURegister fr, FPURegister fs, 2103 FPURegister ft, FPURegister scratch) { 2104 if (IsMipsArchVariant(kMips32r2)) { 2105 madd_d(fd, fr, fs, ft); 2106 } else { 2107 DCHECK(fr != scratch && fs != scratch && ft != scratch); 2108 mul_d(scratch, fs, ft); 2109 add_d(fd, fr, scratch); 2110 } 2111 } 2112 2113 void TurboAssembler::Msub_s(FPURegister fd, FPURegister fr, FPURegister fs, 2114 FPURegister ft, FPURegister scratch) { 2115 if (IsMipsArchVariant(kMips32r2)) { 2116 msub_s(fd, fr, fs, ft); 2117 } else { 2118 DCHECK(fr != scratch && fs != scratch && ft != scratch); 2119 mul_s(scratch, fs, ft); 2120 sub_s(fd, scratch, fr); 2121 } 2122 } 2123 2124 void TurboAssembler::Msub_d(FPURegister fd, FPURegister fr, FPURegister fs, 2125 FPURegister ft, FPURegister scratch) { 2126 if (IsMipsArchVariant(kMips32r2)) { 2127 msub_d(fd, fr, fs, ft); 2128 } else { 2129 DCHECK(fr != scratch && fs != scratch && ft != scratch); 2130 mul_d(scratch, fs, ft); 2131 sub_d(fd, scratch, fr); 2132 } 2133 } 2134 2135 void TurboAssembler::CompareF(SecondaryField sizeField, FPUCondition cc, 2136 FPURegister cmp1, FPURegister cmp2) { 2137 if (IsMipsArchVariant(kMips32r6)) { 2138 sizeField = sizeField == D ? L : W; 2139 DCHECK(cmp1 != kDoubleCompareReg && cmp2 != kDoubleCompareReg); 2140 cmp(cc, sizeField, kDoubleCompareReg, cmp1, cmp2); 2141 } else { 2142 c(cc, sizeField, cmp1, cmp2); 2143 } 2144 } 2145 2146 void TurboAssembler::CompareIsNanF(SecondaryField sizeField, FPURegister cmp1, 2147 FPURegister cmp2) { 2148 CompareF(sizeField, UN, cmp1, cmp2); 2149 } 2150 2151 void TurboAssembler::BranchTrueShortF(Label* target, BranchDelaySlot bd) { 2152 if (IsMipsArchVariant(kMips32r6)) { 2153 bc1nez(target, kDoubleCompareReg); 2154 } else { 2155 bc1t(target); 2156 } 2157 if (bd == PROTECT) { 2158 nop(); 2159 } 2160 } 2161 2162 void TurboAssembler::BranchFalseShortF(Label* target, BranchDelaySlot bd) { 2163 if (IsMipsArchVariant(kMips32r6)) { 2164 bc1eqz(target, kDoubleCompareReg); 2165 } else { 2166 bc1f(target); 2167 } 2168 if (bd == PROTECT) { 2169 nop(); 2170 } 2171 } 2172 2173 void TurboAssembler::BranchTrueF(Label* target, BranchDelaySlot bd) { 2174 bool long_branch = 2175 target->is_bound() ? !is_near(target) : is_trampoline_emitted(); 2176 if (long_branch) { 2177 Label skip; 2178 BranchFalseShortF(&skip); 2179 BranchLong(target, bd); 2180 bind(&skip); 2181 } else { 2182 BranchTrueShortF(target, bd); 2183 } 2184 } 2185 2186 void TurboAssembler::BranchFalseF(Label* target, BranchDelaySlot bd) { 2187 bool long_branch = 2188 target->is_bound() ? !is_near(target) : is_trampoline_emitted(); 2189 if (long_branch) { 2190 Label skip; 2191 BranchTrueShortF(&skip); 2192 BranchLong(target, bd); 2193 bind(&skip); 2194 } else { 2195 BranchFalseShortF(target, bd); 2196 } 2197 } 2198 2199 void TurboAssembler::BranchMSA(Label* target, MSABranchDF df, 2200 MSABranchCondition cond, MSARegister wt, 2201 BranchDelaySlot bd) { 2202 { 2203 BlockTrampolinePoolScope block_trampoline_pool(this); 2204 2205 if (target) { 2206 bool long_branch = 2207 target->is_bound() ? !is_near(target) : is_trampoline_emitted(); 2208 if (long_branch) { 2209 Label skip; 2210 MSABranchCondition neg_cond = NegateMSABranchCondition(cond); 2211 BranchShortMSA(df, &skip, neg_cond, wt, bd); 2212 BranchLong(target, bd); 2213 bind(&skip); 2214 } else { 2215 BranchShortMSA(df, target, cond, wt, bd); 2216 } 2217 } 2218 } 2219 } 2220 2221 void TurboAssembler::BranchShortMSA(MSABranchDF df, Label* target, 2222 MSABranchCondition cond, MSARegister wt, 2223 BranchDelaySlot bd) { 2224 if (IsMipsArchVariant(kMips32r6)) { 2225 BlockTrampolinePoolScope block_trampoline_pool(this); 2226 if (target) { 2227 switch (cond) { 2228 case all_not_zero: 2229 switch (df) { 2230 case MSA_BRANCH_D: 2231 bnz_d(wt, target); 2232 break; 2233 case MSA_BRANCH_W: 2234 bnz_w(wt, target); 2235 break; 2236 case MSA_BRANCH_H: 2237 bnz_h(wt, target); 2238 break; 2239 case MSA_BRANCH_B: 2240 default: 2241 bnz_b(wt, target); 2242 } 2243 break; 2244 case one_elem_not_zero: 2245 bnz_v(wt, target); 2246 break; 2247 case one_elem_zero: 2248 switch (df) { 2249 case MSA_BRANCH_D: 2250 bz_d(wt, target); 2251 break; 2252 case MSA_BRANCH_W: 2253 bz_w(wt, target); 2254 break; 2255 case MSA_BRANCH_H: 2256 bz_h(wt, target); 2257 break; 2258 case MSA_BRANCH_B: 2259 default: 2260 bz_b(wt, target); 2261 } 2262 break; 2263 case all_zero: 2264 bz_v(wt, target); 2265 break; 2266 default: 2267 UNREACHABLE(); 2268 } 2269 } 2270 } 2271 if (bd == PROTECT) { 2272 nop(); 2273 } 2274 } 2275 2276 void TurboAssembler::FmoveLow(FPURegister dst, Register src_low) { 2277 if (IsFp32Mode()) { 2278 mtc1(src_low, dst); 2279 } else { 2280 DCHECK(IsFp64Mode() || IsFpxxMode()); 2281 DCHECK(IsMipsArchVariant(kMips32r2) || IsMipsArchVariant(kMips32r6)); 2282 UseScratchRegisterScope temps(this); 2283 Register scratch = temps.Acquire(); 2284 DCHECK(src_low != scratch); 2285 mfhc1(scratch, dst); 2286 mtc1(src_low, dst); 2287 mthc1(scratch, dst); 2288 } 2289 } 2290 2291 void TurboAssembler::Move(FPURegister dst, uint32_t src) { 2292 UseScratchRegisterScope temps(this); 2293 Register scratch = temps.Acquire(); 2294 li(scratch, Operand(static_cast<int32_t>(src))); 2295 mtc1(scratch, dst); 2296 } 2297 2298 void TurboAssembler::Move(FPURegister dst, uint64_t src) { 2299 // Handle special values first. 2300 if (src == bit_cast<uint64_t>(0.0) && has_double_zero_reg_set_) { 2301 mov_d(dst, kDoubleRegZero); 2302 } else if (src == bit_cast<uint64_t>(-0.0) && has_double_zero_reg_set_) { 2303 Neg_d(dst, kDoubleRegZero); 2304 } else { 2305 uint32_t lo = src & 0xFFFFFFFF; 2306 uint32_t hi = src >> 32; 2307 // Move the low part of the double into the lower of the corresponding FPU 2308 // register of FPU register pair. 2309 if (lo != 0) { 2310 UseScratchRegisterScope temps(this); 2311 Register scratch = temps.Acquire(); 2312 li(scratch, Operand(lo)); 2313 mtc1(scratch, dst); 2314 } else { 2315 mtc1(zero_reg, dst); 2316 } 2317 // Move the high part of the double into the higher of the corresponding FPU 2318 // register of FPU register pair. 2319 if (hi != 0) { 2320 UseScratchRegisterScope temps(this); 2321 Register scratch = temps.Acquire(); 2322 li(scratch, Operand(hi)); 2323 Mthc1(scratch, dst); 2324 } else { 2325 Mthc1(zero_reg, dst); 2326 } 2327 if (dst == kDoubleRegZero) has_double_zero_reg_set_ = true; 2328 } 2329 } 2330 2331 void TurboAssembler::LoadZeroOnCondition(Register rd, Register rs, 2332 const Operand& rt, Condition cond) { 2333 BlockTrampolinePoolScope block_trampoline_pool(this); 2334 switch (cond) { 2335 case cc_always: 2336 mov(rd, zero_reg); 2337 break; 2338 case eq: 2339 if (rs == zero_reg) { 2340 if (rt.is_reg()) { 2341 LoadZeroIfConditionZero(rd, rt.rm()); 2342 } else { 2343 if (rt.immediate() == 0) { 2344 mov(rd, zero_reg); 2345 } else { 2346 nop(); 2347 } 2348 } 2349 } else if (IsZero(rt)) { 2350 LoadZeroIfConditionZero(rd, rs); 2351 } else { 2352 Subu(t9, rs, rt); 2353 LoadZeroIfConditionZero(rd, t9); 2354 } 2355 break; 2356 case ne: 2357 if (rs == zero_reg) { 2358 if (rt.is_reg()) { 2359 LoadZeroIfConditionNotZero(rd, rt.rm()); 2360 } else { 2361 if (rt.immediate() != 0) { 2362 mov(rd, zero_reg); 2363 } else { 2364 nop(); 2365 } 2366 } 2367 } else if (IsZero(rt)) { 2368 LoadZeroIfConditionNotZero(rd, rs); 2369 } else { 2370 Subu(t9, rs, rt); 2371 LoadZeroIfConditionNotZero(rd, t9); 2372 } 2373 break; 2374 2375 // Signed comparison. 2376 case greater: 2377 Sgt(t9, rs, rt); 2378 LoadZeroIfConditionNotZero(rd, t9); 2379 break; 2380 case greater_equal: 2381 Sge(t9, rs, rt); 2382 LoadZeroIfConditionNotZero(rd, t9); 2383 // rs >= rt 2384 break; 2385 case less: 2386 Slt(t9, rs, rt); 2387 LoadZeroIfConditionNotZero(rd, t9); 2388 // rs < rt 2389 break; 2390 case less_equal: 2391 Sle(t9, rs, rt); 2392 LoadZeroIfConditionNotZero(rd, t9); 2393 // rs <= rt 2394 break; 2395 2396 // Unsigned comparison. 2397 case Ugreater: 2398 Sgtu(t9, rs, rt); 2399 LoadZeroIfConditionNotZero(rd, t9); 2400 // rs > rt 2401 break; 2402 2403 case Ugreater_equal: 2404 Sgeu(t9, rs, rt); 2405 LoadZeroIfConditionNotZero(rd, t9); 2406 // rs >= rt 2407 break; 2408 case Uless: 2409 Sltu(t9, rs, rt); 2410 LoadZeroIfConditionNotZero(rd, t9); 2411 // rs < rt 2412 break; 2413 case Uless_equal: 2414 Sleu(t9, rs, rt); 2415 LoadZeroIfConditionNotZero(rd, t9); 2416 // rs <= rt 2417 break; 2418 default: 2419 UNREACHABLE(); 2420 } 2421 } 2422 2423 void TurboAssembler::LoadZeroIfConditionNotZero(Register dest, 2424 Register condition) { 2425 if (IsMipsArchVariant(kMips32r6)) { 2426 seleqz(dest, dest, condition); 2427 } else { 2428 Movn(dest, zero_reg, condition); 2429 } 2430 } 2431 2432 void TurboAssembler::LoadZeroIfConditionZero(Register dest, 2433 Register condition) { 2434 if (IsMipsArchVariant(kMips32r6)) { 2435 selnez(dest, dest, condition); 2436 } else { 2437 Movz(dest, zero_reg, condition); 2438 } 2439 } 2440 2441 void TurboAssembler::LoadZeroIfFPUCondition(Register dest) { 2442 if (IsMipsArchVariant(kMips32r6)) { 2443 mfc1(kScratchReg, kDoubleCompareReg); 2444 LoadZeroIfConditionNotZero(dest, kScratchReg); 2445 } else { 2446 Movt(dest, zero_reg); 2447 } 2448 } 2449 2450 void TurboAssembler::LoadZeroIfNotFPUCondition(Register dest) { 2451 if (IsMipsArchVariant(kMips32r6)) { 2452 mfc1(kScratchReg, kDoubleCompareReg); 2453 LoadZeroIfConditionZero(dest, kScratchReg); 2454 } else { 2455 Movf(dest, zero_reg); 2456 } 2457 } 2458 2459 void TurboAssembler::Movz(Register rd, Register rs, Register rt) { 2460 if (IsMipsArchVariant(kLoongson) || IsMipsArchVariant(kMips32r6)) { 2461 Label done; 2462 Branch(&done, ne, rt, Operand(zero_reg)); 2463 mov(rd, rs); 2464 bind(&done); 2465 } else { 2466 movz(rd, rs, rt); 2467 } 2468 } 2469 2470 void TurboAssembler::Movn(Register rd, Register rs, Register rt) { 2471 if (IsMipsArchVariant(kLoongson) || IsMipsArchVariant(kMips32r6)) { 2472 Label done; 2473 Branch(&done, eq, rt, Operand(zero_reg)); 2474 mov(rd, rs); 2475 bind(&done); 2476 } else { 2477 movn(rd, rs, rt); 2478 } 2479 } 2480 2481 void TurboAssembler::Movt(Register rd, Register rs, uint16_t cc) { 2482 if (IsMipsArchVariant(kLoongson)) { 2483 BlockTrampolinePoolScope block_trampoline_pool(this); 2484 // Tests an FP condition code and then conditionally move rs to rd. 2485 // We do not currently use any FPU cc bit other than bit 0. 2486 DCHECK_EQ(cc, 0); 2487 DCHECK(rs != t8 && rd != t8); 2488 Label done; 2489 Register scratch = t8; 2490 // For testing purposes we need to fetch content of the FCSR register and 2491 // than test its cc (floating point condition code) bit (for cc = 0, it is 2492 // 24. bit of the FCSR). 2493 cfc1(scratch, FCSR); 2494 // For the MIPS I, II and III architectures, the contents of scratch is 2495 // UNPREDICTABLE for the instruction immediately following CFC1. 2496 nop(); 2497 srl(scratch, scratch, 16); 2498 andi(scratch, scratch, 0x0080); 2499 Branch(&done, eq, scratch, Operand(zero_reg)); 2500 mov(rd, rs); 2501 bind(&done); 2502 } else { 2503 movt(rd, rs, cc); 2504 } 2505 } 2506 2507 void TurboAssembler::Movf(Register rd, Register rs, uint16_t cc) { 2508 if (IsMipsArchVariant(kLoongson)) { 2509 BlockTrampolinePoolScope block_trampoline_pool(this); 2510 // Tests an FP condition code and then conditionally move rs to rd. 2511 // We do not currently use any FPU cc bit other than bit 0. 2512 DCHECK_EQ(cc, 0); 2513 DCHECK(rs != t8 && rd != t8); 2514 Label done; 2515 Register scratch = t8; 2516 // For testing purposes we need to fetch content of the FCSR register and 2517 // than test its cc (floating point condition code) bit (for cc = 0, it is 2518 // 24. bit of the FCSR). 2519 cfc1(scratch, FCSR); 2520 // For the MIPS I, II and III architectures, the contents of scratch is 2521 // UNPREDICTABLE for the instruction immediately following CFC1. 2522 nop(); 2523 srl(scratch, scratch, 16); 2524 andi(scratch, scratch, 0x0080); 2525 Branch(&done, ne, scratch, Operand(zero_reg)); 2526 mov(rd, rs); 2527 bind(&done); 2528 } else { 2529 movf(rd, rs, cc); 2530 } 2531 } 2532 2533 void TurboAssembler::Clz(Register rd, Register rs) { 2534 if (IsMipsArchVariant(kLoongson)) { 2535 BlockTrampolinePoolScope block_trampoline_pool(this); 2536 DCHECK(rd != t8 && rd != t9 && rs != t8 && rs != t9); 2537 Register mask = t8; 2538 Register scratch = t9; 2539 Label loop, end; 2540 { 2541 UseScratchRegisterScope temps(this); 2542 Register scratch1 = temps.Acquire(); 2543 mov(scratch1, rs); 2544 mov(rd, zero_reg); 2545 lui(mask, 0x8000); 2546 bind(&loop); 2547 and_(scratch, scratch1, mask); 2548 } 2549 Branch(&end, ne, scratch, Operand(zero_reg)); 2550 addiu(rd, rd, 1); 2551 Branch(&loop, ne, mask, Operand(zero_reg), USE_DELAY_SLOT); 2552 srl(mask, mask, 1); 2553 bind(&end); 2554 } else { 2555 clz(rd, rs); 2556 } 2557 } 2558 2559 void TurboAssembler::Ctz(Register rd, Register rs) { 2560 if (IsMipsArchVariant(kMips32r6)) { 2561 // We don't have an instruction to count the number of trailing zeroes. 2562 // Start by flipping the bits end-for-end so we can count the number of 2563 // leading zeroes instead. 2564 Ror(rd, rs, 16); 2565 wsbh(rd, rd); 2566 bitswap(rd, rd); 2567 Clz(rd, rd); 2568 } else { 2569 // Convert trailing zeroes to trailing ones, and bits to their left 2570 // to zeroes. 2571 UseScratchRegisterScope temps(this); 2572 Register scratch = temps.Acquire(); 2573 Addu(scratch, rs, -1); 2574 Xor(rd, scratch, rs); 2575 And(rd, rd, scratch); 2576 // Count number of leading zeroes. 2577 Clz(rd, rd); 2578 // Subtract number of leading zeroes from 32 to get number of trailing 2579 // ones. Remember that the trailing ones were formerly trailing zeroes. 2580 li(scratch, 32); 2581 Subu(rd, scratch, rd); 2582 } 2583 } 2584 2585 void TurboAssembler::Popcnt(Register rd, Register rs) { 2586 // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel 2587 // 2588 // A generalization of the best bit counting method to integers of 2589 // bit-widths up to 128 (parameterized by type T) is this: 2590 // 2591 // v = v - ((v >> 1) & (T)~(T)0/3); // temp 2592 // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp 2593 // v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp 2594 // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count 2595 // 2596 // For comparison, for 32-bit quantities, this algorithm can be executed 2597 // using 20 MIPS instructions (the calls to LoadConst32() generate two 2598 // machine instructions each for the values being used in this algorithm). 2599 // A(n unrolled) loop-based algorithm requires 25 instructions. 2600 // 2601 // For 64-bit quantities, this algorithm gets executed twice, (once 2602 // for in_lo, and again for in_hi), but saves a few instructions 2603 // because the mask values only have to be loaded once. Using this 2604 // algorithm the count for a 64-bit operand can be performed in 29 2605 // instructions compared to a loop-based algorithm which requires 47 2606 // instructions. 2607 uint32_t B0 = 0x55555555; // (T)~(T)0/3 2608 uint32_t B1 = 0x33333333; // (T)~(T)0/15*3 2609 uint32_t B2 = 0x0F0F0F0F; // (T)~(T)0/255*15 2610 uint32_t value = 0x01010101; // (T)~(T)0/255 2611 uint32_t shift = 24; // (sizeof(T) - 1) * BITS_PER_BYTE 2612 BlockTrampolinePoolScope block_trampoline_pool(this); 2613 UseScratchRegisterScope temps(this); 2614 Register scratch = temps.Acquire(); 2615 Register scratch2 = t8; 2616 srl(scratch, rs, 1); 2617 li(scratch2, B0); 2618 And(scratch, scratch, scratch2); 2619 Subu(scratch, rs, scratch); 2620 li(scratch2, B1); 2621 And(rd, scratch, scratch2); 2622 srl(scratch, scratch, 2); 2623 And(scratch, scratch, scratch2); 2624 Addu(scratch, rd, scratch); 2625 srl(rd, scratch, 4); 2626 Addu(rd, rd, scratch); 2627 li(scratch2, B2); 2628 And(rd, rd, scratch2); 2629 li(scratch, value); 2630 Mul(rd, rd, scratch); 2631 srl(rd, rd, shift); 2632 } 2633 2634 void MacroAssembler::EmitFPUTruncate(FPURoundingMode rounding_mode, 2635 Register result, 2636 DoubleRegister double_input, 2637 Register scratch, 2638 DoubleRegister double_scratch, 2639 Register except_flag, 2640 CheckForInexactConversion check_inexact) { 2641 DCHECK(result != scratch); 2642 DCHECK(double_input != double_scratch); 2643 DCHECK(except_flag != scratch); 2644 2645 Label done; 2646 2647 // Clear the except flag (0 = no exception) 2648 mov(except_flag, zero_reg); 2649 2650 // Test for values that can be exactly represented as a signed 32-bit integer. 2651 cvt_w_d(double_scratch, double_input); 2652 mfc1(result, double_scratch); 2653 cvt_d_w(double_scratch, double_scratch); 2654 CompareF64(EQ, double_input, double_scratch); 2655 BranchTrueShortF(&done); 2656 2657 int32_t except_mask = kFCSRFlagMask; // Assume interested in all exceptions. 2658 2659 if (check_inexact == kDontCheckForInexactConversion) { 2660 // Ignore inexact exceptions. 2661 except_mask &= ~kFCSRInexactFlagMask; 2662 } 2663 2664 // Save FCSR. 2665 cfc1(scratch, FCSR); 2666 // Disable FPU exceptions. 2667 ctc1(zero_reg, FCSR); 2668 2669 // Do operation based on rounding mode. 2670 switch (rounding_mode) { 2671 case kRoundToNearest: 2672 Round_w_d(double_scratch, double_input); 2673 break; 2674 case kRoundToZero: 2675 Trunc_w_d(double_scratch, double_input); 2676 break; 2677 case kRoundToPlusInf: 2678 Ceil_w_d(double_scratch, double_input); 2679 break; 2680 case kRoundToMinusInf: 2681 Floor_w_d(double_scratch, double_input); 2682 break; 2683 } // End of switch-statement. 2684 2685 // Retrieve FCSR. 2686 cfc1(except_flag, FCSR); 2687 // Restore FCSR. 2688 ctc1(scratch, FCSR); 2689 // Move the converted value into the result register. 2690 mfc1(result, double_scratch); 2691 2692 // Check for fpu exceptions. 2693 And(except_flag, except_flag, Operand(except_mask)); 2694 2695 bind(&done); 2696 } 2697 2698 void TurboAssembler::TryInlineTruncateDoubleToI(Register result, 2699 DoubleRegister double_input, 2700 Label* done) { 2701 BlockTrampolinePoolScope block_trampoline_pool(this); 2702 DoubleRegister single_scratch = kScratchDoubleReg.low(); 2703 UseScratchRegisterScope temps(this); 2704 Register scratch = temps.Acquire(); 2705 Register scratch2 = t9; 2706 2707 // Clear cumulative exception flags and save the FCSR. 2708 cfc1(scratch2, FCSR); 2709 ctc1(zero_reg, FCSR); 2710 // Try a conversion to a signed integer. 2711 trunc_w_d(single_scratch, double_input); 2712 mfc1(result, single_scratch); 2713 // Retrieve and restore the FCSR. 2714 cfc1(scratch, FCSR); 2715 ctc1(scratch2, FCSR); 2716 // Check for overflow and NaNs. 2717 And(scratch, 2718 scratch, 2719 kFCSROverflowFlagMask | kFCSRUnderflowFlagMask | kFCSRInvalidOpFlagMask); 2720 // If we had no exceptions we are done. 2721 Branch(done, eq, scratch, Operand(zero_reg)); 2722 } 2723 2724 void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone, 2725 Register result, 2726 DoubleRegister double_input, 2727 StubCallMode stub_mode) { 2728 Label done; 2729 2730 TryInlineTruncateDoubleToI(result, double_input, &done); 2731 2732 // If we fell through then inline version didn't succeed - call stub instead. 2733 push(ra); 2734 Subu(sp, sp, Operand(kDoubleSize)); // Put input on stack. 2735 Sdc1(double_input, MemOperand(sp, 0)); 2736 2737 if (stub_mode == StubCallMode::kCallWasmRuntimeStub) { 2738 Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL); 2739 } else { 2740 Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET); 2741 } 2742 lw(result, MemOperand(sp, 0)); 2743 2744 Addu(sp, sp, Operand(kDoubleSize)); 2745 pop(ra); 2746 2747 bind(&done); 2748 } 2749 2750 // Emulated condtional branches do not emit a nop in the branch delay slot. 2751 // 2752 // BRANCH_ARGS_CHECK checks that conditional jump arguments are correct. 2753 #define BRANCH_ARGS_CHECK(cond, rs, rt) \ 2754 DCHECK((cond == cc_always && rs == zero_reg && rt.rm() == zero_reg) || \ 2755 (cond != cc_always && (rs != zero_reg || rt.rm() != zero_reg))) 2756 2757 void TurboAssembler::Branch(int32_t offset, BranchDelaySlot bdslot) { 2758 DCHECK(IsMipsArchVariant(kMips32r6) ? is_int26(offset) : is_int16(offset)); 2759 BranchShort(offset, bdslot); 2760 } 2761 2762 void TurboAssembler::Branch(int32_t offset, Condition cond, Register rs, 2763 const Operand& rt, BranchDelaySlot bdslot) { 2764 bool is_near = BranchShortCheck(offset, nullptr, cond, rs, rt, bdslot); 2765 DCHECK(is_near); 2766 USE(is_near); 2767 } 2768 2769 void TurboAssembler::Branch(Label* L, BranchDelaySlot bdslot) { 2770 if (L->is_bound()) { 2771 if (is_near_branch(L)) { 2772 BranchShort(L, bdslot); 2773 } else { 2774 BranchLong(L, bdslot); 2775 } 2776 } else { 2777 if (is_trampoline_emitted()) { 2778 BranchLong(L, bdslot); 2779 } else { 2780 BranchShort(L, bdslot); 2781 } 2782 } 2783 } 2784 2785 void TurboAssembler::Branch(Label* L, Condition cond, Register rs, 2786 const Operand& rt, BranchDelaySlot bdslot) { 2787 if (L->is_bound()) { 2788 if (!BranchShortCheck(0, L, cond, rs, rt, bdslot)) { 2789 if (cond != cc_always) { 2790 Label skip; 2791 Condition neg_cond = NegateCondition(cond); 2792 BranchShort(&skip, neg_cond, rs, rt); 2793 BranchLong(L, bdslot); 2794 bind(&skip); 2795 } else { 2796 BranchLong(L, bdslot); 2797 } 2798 } 2799 } else { 2800 if (is_trampoline_emitted()) { 2801 if (cond != cc_always) { 2802 Label skip; 2803 Condition neg_cond = NegateCondition(cond); 2804 BranchShort(&skip, neg_cond, rs, rt); 2805 BranchLong(L, bdslot); 2806 bind(&skip); 2807 } else { 2808 BranchLong(L, bdslot); 2809 } 2810 } else { 2811 BranchShort(L, cond, rs, rt, bdslot); 2812 } 2813 } 2814 } 2815 2816 void TurboAssembler::Branch(Label* L, Condition cond, Register rs, 2817 Heap::RootListIndex index, BranchDelaySlot bdslot) { 2818 UseScratchRegisterScope temps(this); 2819 Register scratch = temps.Acquire(); 2820 LoadRoot(scratch, index); 2821 Branch(L, cond, rs, Operand(scratch), bdslot); 2822 } 2823 2824 void TurboAssembler::BranchShortHelper(int16_t offset, Label* L, 2825 BranchDelaySlot bdslot) { 2826 DCHECK(L == nullptr || offset == 0); 2827 offset = GetOffset(offset, L, OffsetSize::kOffset16); 2828 b(offset); 2829 2830 // Emit a nop in the branch delay slot if required. 2831 if (bdslot == PROTECT) 2832 nop(); 2833 } 2834 2835 void TurboAssembler::BranchShortHelperR6(int32_t offset, Label* L) { 2836 DCHECK(L == nullptr || offset == 0); 2837 offset = GetOffset(offset, L, OffsetSize::kOffset26); 2838 bc(offset); 2839 } 2840 2841 void TurboAssembler::BranchShort(int32_t offset, BranchDelaySlot bdslot) { 2842 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 2843 DCHECK(is_int26(offset)); 2844 BranchShortHelperR6(offset, nullptr); 2845 } else { 2846 DCHECK(is_int16(offset)); 2847 BranchShortHelper(offset, nullptr, bdslot); 2848 } 2849 } 2850 2851 void TurboAssembler::BranchShort(Label* L, BranchDelaySlot bdslot) { 2852 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 2853 BranchShortHelperR6(0, L); 2854 } else { 2855 BranchShortHelper(0, L, bdslot); 2856 } 2857 } 2858 2859 2860 int32_t TurboAssembler::GetOffset(int32_t offset, Label* L, OffsetSize bits) { 2861 if (L) { 2862 offset = branch_offset_helper(L, bits) >> 2; 2863 } else { 2864 DCHECK(is_intn(offset, bits)); 2865 } 2866 return offset; 2867 } 2868 2869 Register TurboAssembler::GetRtAsRegisterHelper(const Operand& rt, 2870 Register scratch) { 2871 Register r2 = no_reg; 2872 if (rt.is_reg()) { 2873 r2 = rt.rm(); 2874 } else { 2875 r2 = scratch; 2876 li(r2, rt); 2877 } 2878 2879 return r2; 2880 } 2881 2882 bool TurboAssembler::CalculateOffset(Label* L, int32_t& offset, 2883 OffsetSize bits) { 2884 if (!is_near(L, bits)) return false; 2885 offset = GetOffset(offset, L, bits); 2886 return true; 2887 } 2888 2889 bool TurboAssembler::CalculateOffset(Label* L, int32_t& offset, OffsetSize bits, 2890 Register& scratch, const Operand& rt) { 2891 if (!is_near(L, bits)) return false; 2892 scratch = GetRtAsRegisterHelper(rt, scratch); 2893 offset = GetOffset(offset, L, bits); 2894 return true; 2895 } 2896 2897 bool TurboAssembler::BranchShortHelperR6(int32_t offset, Label* L, 2898 Condition cond, Register rs, 2899 const Operand& rt) { 2900 DCHECK(L == nullptr || offset == 0); 2901 UseScratchRegisterScope temps(this); 2902 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 2903 2904 // Be careful to always use shifted_branch_offset only just before the 2905 // branch instruction, as the location will be remember for patching the 2906 // target. 2907 { 2908 BlockTrampolinePoolScope block_trampoline_pool(this); 2909 switch (cond) { 2910 case cc_always: 2911 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 2912 bc(offset); 2913 break; 2914 case eq: 2915 if (rt.is_reg() && rs.code() == rt.rm().code()) { 2916 // Pre R6 beq is used here to make the code patchable. Otherwise bc 2917 // should be used which has no condition field so is not patchable. 2918 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2919 return false; 2920 beq(rs, scratch, offset); 2921 nop(); 2922 } else if (IsZero(rt)) { 2923 if (!CalculateOffset(L, offset, OffsetSize::kOffset21)) return false; 2924 beqzc(rs, offset); 2925 } else { 2926 // We don't want any other register but scratch clobbered. 2927 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2928 return false; 2929 beqc(rs, scratch, offset); 2930 } 2931 break; 2932 case ne: 2933 if (rt.is_reg() && rs.code() == rt.rm().code()) { 2934 // Pre R6 bne is used here to make the code patchable. Otherwise we 2935 // should not generate any instruction. 2936 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2937 return false; 2938 bne(rs, scratch, offset); 2939 nop(); 2940 } else if (IsZero(rt)) { 2941 if (!CalculateOffset(L, offset, OffsetSize::kOffset21)) return false; 2942 bnezc(rs, offset); 2943 } else { 2944 // We don't want any other register but scratch clobbered. 2945 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2946 return false; 2947 bnec(rs, scratch, offset); 2948 } 2949 break; 2950 2951 // Signed comparison. 2952 case greater: 2953 // rs > rt 2954 if (rt.is_reg() && rs.code() == rt.rm().code()) { 2955 break; // No code needs to be emitted. 2956 } else if (rs == zero_reg) { 2957 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2958 return false; 2959 bltzc(scratch, offset); 2960 } else if (IsZero(rt)) { 2961 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 2962 bgtzc(rs, offset); 2963 } else { 2964 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2965 return false; 2966 DCHECK(rs != scratch); 2967 bltc(scratch, rs, offset); 2968 } 2969 break; 2970 case greater_equal: 2971 // rs >= rt 2972 if (rt.is_reg() && rs.code() == rt.rm().code()) { 2973 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 2974 bc(offset); 2975 } else if (rs == zero_reg) { 2976 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2977 return false; 2978 blezc(scratch, offset); 2979 } else if (IsZero(rt)) { 2980 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 2981 bgezc(rs, offset); 2982 } else { 2983 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2984 return false; 2985 DCHECK(rs != scratch); 2986 bgec(rs, scratch, offset); 2987 } 2988 break; 2989 case less: 2990 // rs < rt 2991 if (rt.is_reg() && rs.code() == rt.rm().code()) { 2992 break; // No code needs to be emitted. 2993 } else if (rs == zero_reg) { 2994 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 2995 return false; 2996 bgtzc(scratch, offset); 2997 } else if (IsZero(rt)) { 2998 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 2999 bltzc(rs, offset); 3000 } else { 3001 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3002 return false; 3003 DCHECK(rs != scratch); 3004 bltc(rs, scratch, offset); 3005 } 3006 break; 3007 case less_equal: 3008 // rs <= rt 3009 if (rt.is_reg() && rs.code() == rt.rm().code()) { 3010 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3011 bc(offset); 3012 } else if (rs == zero_reg) { 3013 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3014 return false; 3015 bgezc(scratch, offset); 3016 } else if (IsZero(rt)) { 3017 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 3018 blezc(rs, offset); 3019 } else { 3020 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3021 return false; 3022 DCHECK(rs != scratch); 3023 bgec(scratch, rs, offset); 3024 } 3025 break; 3026 3027 // Unsigned comparison. 3028 case Ugreater: 3029 // rs > rt 3030 if (rt.is_reg() && rs.code() == rt.rm().code()) { 3031 break; // No code needs to be emitted. 3032 } else if (rs == zero_reg) { 3033 if (!CalculateOffset(L, offset, OffsetSize::kOffset21, scratch, rt)) 3034 return false; 3035 bnezc(scratch, offset); 3036 } else if (IsZero(rt)) { 3037 if (!CalculateOffset(L, offset, OffsetSize::kOffset21)) return false; 3038 bnezc(rs, offset); 3039 } else { 3040 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3041 return false; 3042 DCHECK(rs != scratch); 3043 bltuc(scratch, rs, offset); 3044 } 3045 break; 3046 case Ugreater_equal: 3047 // rs >= rt 3048 if (rt.is_reg() && rs.code() == rt.rm().code()) { 3049 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3050 bc(offset); 3051 } else if (rs == zero_reg) { 3052 if (!CalculateOffset(L, offset, OffsetSize::kOffset21, scratch, rt)) 3053 return false; 3054 beqzc(scratch, offset); 3055 } else if (IsZero(rt)) { 3056 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3057 bc(offset); 3058 } else { 3059 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3060 return false; 3061 DCHECK(rs != scratch); 3062 bgeuc(rs, scratch, offset); 3063 } 3064 break; 3065 case Uless: 3066 // rs < rt 3067 if (rt.is_reg() && rs.code() == rt.rm().code()) { 3068 break; // No code needs to be emitted. 3069 } else if (rs == zero_reg) { 3070 if (!CalculateOffset(L, offset, OffsetSize::kOffset21, scratch, rt)) 3071 return false; 3072 bnezc(scratch, offset); 3073 } else if (IsZero(rt)) { 3074 break; // No code needs to be emitted. 3075 } else { 3076 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3077 return false; 3078 DCHECK(rs != scratch); 3079 bltuc(rs, scratch, offset); 3080 } 3081 break; 3082 case Uless_equal: 3083 // rs <= rt 3084 if (rt.is_reg() && rs.code() == rt.rm().code()) { 3085 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3086 bc(offset); 3087 } else if (rs == zero_reg) { 3088 if (!CalculateOffset(L, offset, OffsetSize::kOffset26, scratch, rt)) 3089 return false; 3090 bc(offset); 3091 } else if (IsZero(rt)) { 3092 if (!CalculateOffset(L, offset, OffsetSize::kOffset21)) return false; 3093 beqzc(rs, offset); 3094 } else { 3095 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3096 return false; 3097 DCHECK(rs != scratch); 3098 bgeuc(scratch, rs, offset); 3099 } 3100 break; 3101 default: 3102 UNREACHABLE(); 3103 } 3104 } 3105 CheckTrampolinePoolQuick(1); 3106 return true; 3107 } 3108 3109 bool TurboAssembler::BranchShortHelper(int16_t offset, Label* L, Condition cond, 3110 Register rs, const Operand& rt, 3111 BranchDelaySlot bdslot) { 3112 DCHECK(L == nullptr || offset == 0); 3113 if (!is_near(L, OffsetSize::kOffset16)) return false; 3114 3115 UseScratchRegisterScope temps(this); 3116 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 3117 int32_t offset32; 3118 3119 // Be careful to always use shifted_branch_offset only just before the 3120 // branch instruction, as the location will be remember for patching the 3121 // target. 3122 { 3123 BlockTrampolinePoolScope block_trampoline_pool(this); 3124 switch (cond) { 3125 case cc_always: 3126 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3127 b(offset32); 3128 break; 3129 case eq: 3130 if (IsZero(rt)) { 3131 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3132 beq(rs, zero_reg, offset32); 3133 } else { 3134 // We don't want any other register but scratch clobbered. 3135 scratch = GetRtAsRegisterHelper(rt, scratch); 3136 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3137 beq(rs, scratch, offset32); 3138 } 3139 break; 3140 case ne: 3141 if (IsZero(rt)) { 3142 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3143 bne(rs, zero_reg, offset32); 3144 } else { 3145 // We don't want any other register but scratch clobbered. 3146 scratch = GetRtAsRegisterHelper(rt, scratch); 3147 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3148 bne(rs, scratch, offset32); 3149 } 3150 break; 3151 3152 // Signed comparison. 3153 case greater: 3154 if (IsZero(rt)) { 3155 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3156 bgtz(rs, offset32); 3157 } else { 3158 Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3159 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3160 bne(scratch, zero_reg, offset32); 3161 } 3162 break; 3163 case greater_equal: 3164 if (IsZero(rt)) { 3165 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3166 bgez(rs, offset32); 3167 } else { 3168 Slt(scratch, rs, rt); 3169 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3170 beq(scratch, zero_reg, offset32); 3171 } 3172 break; 3173 case less: 3174 if (IsZero(rt)) { 3175 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3176 bltz(rs, offset32); 3177 } else { 3178 Slt(scratch, rs, rt); 3179 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3180 bne(scratch, zero_reg, offset32); 3181 } 3182 break; 3183 case less_equal: 3184 if (IsZero(rt)) { 3185 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3186 blez(rs, offset32); 3187 } else { 3188 Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3189 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3190 beq(scratch, zero_reg, offset32); 3191 } 3192 break; 3193 3194 // Unsigned comparison. 3195 case Ugreater: 3196 if (IsZero(rt)) { 3197 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3198 bne(rs, zero_reg, offset32); 3199 } else { 3200 Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3201 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3202 bne(scratch, zero_reg, offset32); 3203 } 3204 break; 3205 case Ugreater_equal: 3206 if (IsZero(rt)) { 3207 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3208 b(offset32); 3209 } else { 3210 Sltu(scratch, rs, rt); 3211 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3212 beq(scratch, zero_reg, offset32); 3213 } 3214 break; 3215 case Uless: 3216 if (IsZero(rt)) { 3217 return true; // No code needs to be emitted. 3218 } else { 3219 Sltu(scratch, rs, rt); 3220 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3221 bne(scratch, zero_reg, offset32); 3222 } 3223 break; 3224 case Uless_equal: 3225 if (IsZero(rt)) { 3226 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3227 beq(rs, zero_reg, offset32); 3228 } else { 3229 Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3230 offset32 = GetOffset(offset, L, OffsetSize::kOffset16); 3231 beq(scratch, zero_reg, offset32); 3232 } 3233 break; 3234 default: 3235 UNREACHABLE(); 3236 } 3237 } 3238 // Emit a nop in the branch delay slot if required. 3239 if (bdslot == PROTECT) 3240 nop(); 3241 3242 return true; 3243 } 3244 3245 bool TurboAssembler::BranchShortCheck(int32_t offset, Label* L, Condition cond, 3246 Register rs, const Operand& rt, 3247 BranchDelaySlot bdslot) { 3248 BRANCH_ARGS_CHECK(cond, rs, rt); 3249 if (!L) { 3250 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 3251 DCHECK(is_int26(offset)); 3252 return BranchShortHelperR6(offset, nullptr, cond, rs, rt); 3253 } else { 3254 DCHECK(is_int16(offset)); 3255 return BranchShortHelper(offset, nullptr, cond, rs, rt, bdslot); 3256 } 3257 } else { 3258 DCHECK_EQ(offset, 0); 3259 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 3260 return BranchShortHelperR6(0, L, cond, rs, rt); 3261 } else { 3262 return BranchShortHelper(0, L, cond, rs, rt, bdslot); 3263 } 3264 } 3265 return false; 3266 } 3267 3268 void TurboAssembler::BranchShort(int32_t offset, Condition cond, Register rs, 3269 const Operand& rt, BranchDelaySlot bdslot) { 3270 BranchShortCheck(offset, nullptr, cond, rs, rt, bdslot); 3271 } 3272 3273 void TurboAssembler::BranchShort(Label* L, Condition cond, Register rs, 3274 const Operand& rt, BranchDelaySlot bdslot) { 3275 BranchShortCheck(0, L, cond, rs, rt, bdslot); 3276 } 3277 3278 void TurboAssembler::BranchAndLink(int32_t offset, BranchDelaySlot bdslot) { 3279 BranchAndLinkShort(offset, bdslot); 3280 } 3281 3282 void TurboAssembler::BranchAndLink(int32_t offset, Condition cond, Register rs, 3283 const Operand& rt, BranchDelaySlot bdslot) { 3284 bool is_near = BranchAndLinkShortCheck(offset, nullptr, cond, rs, rt, bdslot); 3285 DCHECK(is_near); 3286 USE(is_near); 3287 } 3288 3289 void TurboAssembler::BranchAndLink(Label* L, BranchDelaySlot bdslot) { 3290 if (L->is_bound()) { 3291 if (is_near_branch(L)) { 3292 BranchAndLinkShort(L, bdslot); 3293 } else { 3294 BranchAndLinkLong(L, bdslot); 3295 } 3296 } else { 3297 if (is_trampoline_emitted()) { 3298 BranchAndLinkLong(L, bdslot); 3299 } else { 3300 BranchAndLinkShort(L, bdslot); 3301 } 3302 } 3303 } 3304 3305 void TurboAssembler::BranchAndLink(Label* L, Condition cond, Register rs, 3306 const Operand& rt, BranchDelaySlot bdslot) { 3307 if (L->is_bound()) { 3308 if (!BranchAndLinkShortCheck(0, L, cond, rs, rt, bdslot)) { 3309 Label skip; 3310 Condition neg_cond = NegateCondition(cond); 3311 BranchShort(&skip, neg_cond, rs, rt); 3312 BranchAndLinkLong(L, bdslot); 3313 bind(&skip); 3314 } 3315 } else { 3316 if (is_trampoline_emitted()) { 3317 Label skip; 3318 Condition neg_cond = NegateCondition(cond); 3319 BranchShort(&skip, neg_cond, rs, rt); 3320 BranchAndLinkLong(L, bdslot); 3321 bind(&skip); 3322 } else { 3323 BranchAndLinkShortCheck(0, L, cond, rs, rt, bdslot); 3324 } 3325 } 3326 } 3327 3328 void TurboAssembler::BranchAndLinkShortHelper(int16_t offset, Label* L, 3329 BranchDelaySlot bdslot) { 3330 DCHECK(L == nullptr || offset == 0); 3331 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3332 bal(offset); 3333 3334 // Emit a nop in the branch delay slot if required. 3335 if (bdslot == PROTECT) 3336 nop(); 3337 } 3338 3339 void TurboAssembler::BranchAndLinkShortHelperR6(int32_t offset, Label* L) { 3340 DCHECK(L == nullptr || offset == 0); 3341 offset = GetOffset(offset, L, OffsetSize::kOffset26); 3342 balc(offset); 3343 } 3344 3345 void TurboAssembler::BranchAndLinkShort(int32_t offset, 3346 BranchDelaySlot bdslot) { 3347 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 3348 DCHECK(is_int26(offset)); 3349 BranchAndLinkShortHelperR6(offset, nullptr); 3350 } else { 3351 DCHECK(is_int16(offset)); 3352 BranchAndLinkShortHelper(offset, nullptr, bdslot); 3353 } 3354 } 3355 3356 void TurboAssembler::BranchAndLinkShort(Label* L, BranchDelaySlot bdslot) { 3357 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 3358 BranchAndLinkShortHelperR6(0, L); 3359 } else { 3360 BranchAndLinkShortHelper(0, L, bdslot); 3361 } 3362 } 3363 3364 bool TurboAssembler::BranchAndLinkShortHelperR6(int32_t offset, Label* L, 3365 Condition cond, Register rs, 3366 const Operand& rt) { 3367 DCHECK(L == nullptr || offset == 0); 3368 UseScratchRegisterScope temps(this); 3369 Register scratch = temps.hasAvailable() ? temps.Acquire() : t8; 3370 OffsetSize bits = OffsetSize::kOffset16; 3371 3372 BlockTrampolinePoolScope block_trampoline_pool(this); 3373 DCHECK((cond == cc_always && is_int26(offset)) || is_int16(offset)); 3374 switch (cond) { 3375 case cc_always: 3376 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3377 balc(offset); 3378 break; 3379 case eq: 3380 if (!is_near(L, bits)) return false; 3381 Subu(scratch, rs, rt); 3382 offset = GetOffset(offset, L, bits); 3383 beqzalc(scratch, offset); 3384 break; 3385 case ne: 3386 if (!is_near(L, bits)) return false; 3387 Subu(scratch, rs, rt); 3388 offset = GetOffset(offset, L, bits); 3389 bnezalc(scratch, offset); 3390 break; 3391 3392 // Signed comparison. 3393 case greater: 3394 // rs > rt 3395 if (rs.code() == rt.rm().code()) { 3396 break; // No code needs to be emitted. 3397 } else if (rs == zero_reg) { 3398 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3399 return false; 3400 bltzalc(scratch, offset); 3401 } else if (IsZero(rt)) { 3402 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 3403 bgtzalc(rs, offset); 3404 } else { 3405 if (!is_near(L, bits)) return false; 3406 Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3407 offset = GetOffset(offset, L, bits); 3408 bnezalc(scratch, offset); 3409 } 3410 break; 3411 case greater_equal: 3412 // rs >= rt 3413 if (rs.code() == rt.rm().code()) { 3414 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3415 balc(offset); 3416 } else if (rs == zero_reg) { 3417 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3418 return false; 3419 blezalc(scratch, offset); 3420 } else if (IsZero(rt)) { 3421 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 3422 bgezalc(rs, offset); 3423 } else { 3424 if (!is_near(L, bits)) return false; 3425 Slt(scratch, rs, rt); 3426 offset = GetOffset(offset, L, bits); 3427 beqzalc(scratch, offset); 3428 } 3429 break; 3430 case less: 3431 // rs < rt 3432 if (rs.code() == rt.rm().code()) { 3433 break; // No code needs to be emitted. 3434 } else if (rs == zero_reg) { 3435 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3436 return false; 3437 bgtzalc(scratch, offset); 3438 } else if (IsZero(rt)) { 3439 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 3440 bltzalc(rs, offset); 3441 } else { 3442 if (!is_near(L, bits)) return false; 3443 Slt(scratch, rs, rt); 3444 offset = GetOffset(offset, L, bits); 3445 bnezalc(scratch, offset); 3446 } 3447 break; 3448 case less_equal: 3449 // rs <= r2 3450 if (rs.code() == rt.rm().code()) { 3451 if (!CalculateOffset(L, offset, OffsetSize::kOffset26)) return false; 3452 balc(offset); 3453 } else if (rs == zero_reg) { 3454 if (!CalculateOffset(L, offset, OffsetSize::kOffset16, scratch, rt)) 3455 return false; 3456 bgezalc(scratch, offset); 3457 } else if (IsZero(rt)) { 3458 if (!CalculateOffset(L, offset, OffsetSize::kOffset16)) return false; 3459 blezalc(rs, offset); 3460 } else { 3461 if (!is_near(L, bits)) return false; 3462 Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3463 offset = GetOffset(offset, L, bits); 3464 beqzalc(scratch, offset); 3465 } 3466 break; 3467 3468 3469 // Unsigned comparison. 3470 case Ugreater: 3471 // rs > r2 3472 if (!is_near(L, bits)) return false; 3473 Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3474 offset = GetOffset(offset, L, bits); 3475 bnezalc(scratch, offset); 3476 break; 3477 case Ugreater_equal: 3478 // rs >= r2 3479 if (!is_near(L, bits)) return false; 3480 Sltu(scratch, rs, rt); 3481 offset = GetOffset(offset, L, bits); 3482 beqzalc(scratch, offset); 3483 break; 3484 case Uless: 3485 // rs < r2 3486 if (!is_near(L, bits)) return false; 3487 Sltu(scratch, rs, rt); 3488 offset = GetOffset(offset, L, bits); 3489 bnezalc(scratch, offset); 3490 break; 3491 case Uless_equal: 3492 // rs <= r2 3493 if (!is_near(L, bits)) return false; 3494 Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3495 offset = GetOffset(offset, L, bits); 3496 beqzalc(scratch, offset); 3497 break; 3498 default: 3499 UNREACHABLE(); 3500 } 3501 return true; 3502 } 3503 3504 // Pre r6 we need to use a bgezal or bltzal, but they can't be used directly 3505 // with the slt instructions. We could use sub or add instead but we would miss 3506 // overflow cases, so we keep slt and add an intermediate third instruction. 3507 bool TurboAssembler::BranchAndLinkShortHelper(int16_t offset, Label* L, 3508 Condition cond, Register rs, 3509 const Operand& rt, 3510 BranchDelaySlot bdslot) { 3511 DCHECK(L == nullptr || offset == 0); 3512 if (!is_near(L, OffsetSize::kOffset16)) return false; 3513 3514 Register scratch = t8; 3515 BlockTrampolinePoolScope block_trampoline_pool(this); 3516 3517 switch (cond) { 3518 case cc_always: 3519 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3520 bal(offset); 3521 break; 3522 case eq: 3523 bne(rs, GetRtAsRegisterHelper(rt, scratch), 2); 3524 nop(); 3525 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3526 bal(offset); 3527 break; 3528 case ne: 3529 beq(rs, GetRtAsRegisterHelper(rt, scratch), 2); 3530 nop(); 3531 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3532 bal(offset); 3533 break; 3534 3535 // Signed comparison. 3536 case greater: 3537 Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3538 addiu(scratch, scratch, -1); 3539 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3540 bgezal(scratch, offset); 3541 break; 3542 case greater_equal: 3543 Slt(scratch, rs, rt); 3544 addiu(scratch, scratch, -1); 3545 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3546 bltzal(scratch, offset); 3547 break; 3548 case less: 3549 Slt(scratch, rs, rt); 3550 addiu(scratch, scratch, -1); 3551 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3552 bgezal(scratch, offset); 3553 break; 3554 case less_equal: 3555 Slt(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3556 addiu(scratch, scratch, -1); 3557 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3558 bltzal(scratch, offset); 3559 break; 3560 3561 // Unsigned comparison. 3562 case Ugreater: 3563 Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3564 addiu(scratch, scratch, -1); 3565 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3566 bgezal(scratch, offset); 3567 break; 3568 case Ugreater_equal: 3569 Sltu(scratch, rs, rt); 3570 addiu(scratch, scratch, -1); 3571 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3572 bltzal(scratch, offset); 3573 break; 3574 case Uless: 3575 Sltu(scratch, rs, rt); 3576 addiu(scratch, scratch, -1); 3577 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3578 bgezal(scratch, offset); 3579 break; 3580 case Uless_equal: 3581 Sltu(scratch, GetRtAsRegisterHelper(rt, scratch), rs); 3582 addiu(scratch, scratch, -1); 3583 offset = GetOffset(offset, L, OffsetSize::kOffset16); 3584 bltzal(scratch, offset); 3585 break; 3586 3587 default: 3588 UNREACHABLE(); 3589 } 3590 3591 // Emit a nop in the branch delay slot if required. 3592 if (bdslot == PROTECT) 3593 nop(); 3594 3595 return true; 3596 } 3597 3598 bool TurboAssembler::BranchAndLinkShortCheck(int32_t offset, Label* L, 3599 Condition cond, Register rs, 3600 const Operand& rt, 3601 BranchDelaySlot bdslot) { 3602 BRANCH_ARGS_CHECK(cond, rs, rt); 3603 3604 if (!L) { 3605 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 3606 DCHECK(is_int26(offset)); 3607 return BranchAndLinkShortHelperR6(offset, nullptr, cond, rs, rt); 3608 } else { 3609 DCHECK(is_int16(offset)); 3610 return BranchAndLinkShortHelper(offset, nullptr, cond, rs, rt, bdslot); 3611 } 3612 } else { 3613 DCHECK_EQ(offset, 0); 3614 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT) { 3615 return BranchAndLinkShortHelperR6(0, L, cond, rs, rt); 3616 } else { 3617 return BranchAndLinkShortHelper(0, L, cond, rs, rt, bdslot); 3618 } 3619 } 3620 return false; 3621 } 3622 3623 void TurboAssembler::LoadFromConstantsTable(Register destination, 3624 int constant_index) { 3625 DCHECK(isolate()->heap()->RootCanBeTreatedAsConstant( 3626 Heap::kBuiltinsConstantsTableRootIndex)); 3627 LoadRoot(destination, Heap::kBuiltinsConstantsTableRootIndex); 3628 lw(destination, 3629 FieldMemOperand(destination, 3630 FixedArray::kHeaderSize + constant_index * kPointerSize)); 3631 } 3632 3633 void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) { 3634 lw(destination, MemOperand(kRootRegister, offset)); 3635 } 3636 3637 void TurboAssembler::LoadRootRegisterOffset(Register destination, 3638 intptr_t offset) { 3639 if (offset == 0) { 3640 Move(destination, kRootRegister); 3641 } else { 3642 Addu(destination, kRootRegister, offset); 3643 } 3644 } 3645 3646 void TurboAssembler::Jump(Register target, int16_t offset, Condition cond, 3647 Register rs, const Operand& rt, BranchDelaySlot bd) { 3648 BlockTrampolinePoolScope block_trampoline_pool(this); 3649 DCHECK(is_int16(offset)); 3650 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT) { 3651 if (cond == cc_always) { 3652 jic(target, offset); 3653 } else { 3654 BRANCH_ARGS_CHECK(cond, rs, rt); 3655 Branch(2, NegateCondition(cond), rs, rt); 3656 jic(target, offset); 3657 } 3658 } else { 3659 if (offset != 0) { 3660 Addu(target, target, offset); 3661 } 3662 if (cond == cc_always) { 3663 jr(target); 3664 } else { 3665 BRANCH_ARGS_CHECK(cond, rs, rt); 3666 Branch(2, NegateCondition(cond), rs, rt); 3667 jr(target); 3668 } 3669 // Emit a nop in the branch delay slot if required. 3670 if (bd == PROTECT) nop(); 3671 } 3672 } 3673 3674 void TurboAssembler::Jump(Register target, Register base, int16_t offset, 3675 Condition cond, Register rs, const Operand& rt, 3676 BranchDelaySlot bd) { 3677 DCHECK(is_int16(offset)); 3678 BlockTrampolinePoolScope block_trampoline_pool(this); 3679 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT) { 3680 if (cond == cc_always) { 3681 jic(base, offset); 3682 } else { 3683 BRANCH_ARGS_CHECK(cond, rs, rt); 3684 Branch(2, NegateCondition(cond), rs, rt); 3685 jic(base, offset); 3686 } 3687 } else { 3688 if (offset != 0) { 3689 Addu(target, base, offset); 3690 } else { // Call through target 3691 if (target != base) mov(target, base); 3692 } 3693 if (cond == cc_always) { 3694 jr(target); 3695 } else { 3696 BRANCH_ARGS_CHECK(cond, rs, rt); 3697 Branch(2, NegateCondition(cond), rs, rt); 3698 jr(target); 3699 } 3700 // Emit a nop in the branch delay slot if required. 3701 if (bd == PROTECT) nop(); 3702 } 3703 } 3704 3705 void TurboAssembler::Jump(Register target, const Operand& offset, 3706 Condition cond, Register rs, const Operand& rt, 3707 BranchDelaySlot bd) { 3708 BlockTrampolinePoolScope block_trampoline_pool(this); 3709 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT && 3710 !is_int16(offset.immediate())) { 3711 uint32_t aui_offset, jic_offset; 3712 Assembler::UnpackTargetAddressUnsigned(offset.immediate(), aui_offset, 3713 jic_offset); 3714 RecordRelocInfo(RelocInfo::EXTERNAL_REFERENCE, offset.immediate()); 3715 aui(target, target, aui_offset); 3716 if (cond == cc_always) { 3717 jic(target, jic_offset); 3718 } else { 3719 BRANCH_ARGS_CHECK(cond, rs, rt); 3720 Branch(2, NegateCondition(cond), rs, rt); 3721 jic(target, jic_offset); 3722 } 3723 } else { 3724 if (offset.immediate() != 0) { 3725 Addu(target, target, offset); 3726 } 3727 if (cond == cc_always) { 3728 jr(target); 3729 } else { 3730 BRANCH_ARGS_CHECK(cond, rs, rt); 3731 Branch(2, NegateCondition(cond), rs, rt); 3732 jr(target); 3733 } 3734 // Emit a nop in the branch delay slot if required. 3735 if (bd == PROTECT) nop(); 3736 } 3737 } 3738 3739 void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode, 3740 Condition cond, Register rs, const Operand& rt, 3741 BranchDelaySlot bd) { 3742 BlockTrampolinePoolScope block_trampoline_pool(this); 3743 Label skip; 3744 if (cond != cc_always) { 3745 Branch(USE_DELAY_SLOT, &skip, NegateCondition(cond), rs, rt); 3746 } 3747 // The first instruction of 'li' may be placed in the delay slot. 3748 // This is not an issue, t9 is expected to be clobbered anyway. 3749 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT) { 3750 uint32_t lui_offset, jic_offset; 3751 UnpackTargetAddressUnsigned(target, lui_offset, jic_offset); 3752 if (MustUseReg(rmode)) { 3753 RecordRelocInfo(rmode, target); 3754 } 3755 lui(t9, lui_offset); 3756 Jump(t9, jic_offset, al, zero_reg, Operand(zero_reg), bd); 3757 } else { 3758 li(t9, Operand(target, rmode)); 3759 Jump(t9, 0, al, zero_reg, Operand(zero_reg), bd); 3760 } 3761 bind(&skip); 3762 } 3763 3764 void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode, Condition cond, 3765 Register rs, const Operand& rt, BranchDelaySlot bd) { 3766 DCHECK(!RelocInfo::IsCodeTarget(rmode)); 3767 Jump(static_cast<intptr_t>(target), rmode, cond, rs, rt, bd); 3768 } 3769 3770 void TurboAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode, 3771 Condition cond, Register rs, const Operand& rt, 3772 BranchDelaySlot bd) { 3773 DCHECK(RelocInfo::IsCodeTarget(rmode)); 3774 BlockTrampolinePoolScope block_trampoline_pool(this); 3775 if (FLAG_embedded_builtins) { 3776 if (root_array_available_ && options().isolate_independent_code) { 3777 IndirectLoadConstant(t9, code); 3778 Jump(t9, Code::kHeaderSize - kHeapObjectTag, cond, rs, rt, bd); 3779 return; 3780 } else if (options().inline_offheap_trampolines) { 3781 int builtin_index = Builtins::kNoBuiltinId; 3782 if (isolate()->builtins()->IsBuiltinHandle(code, &builtin_index) && 3783 Builtins::IsIsolateIndependent(builtin_index)) { 3784 // Inline the trampoline. 3785 RecordCommentForOffHeapTrampoline(builtin_index); 3786 CHECK_NE(builtin_index, Builtins::kNoBuiltinId); 3787 EmbeddedData d = EmbeddedData::FromBlob(); 3788 Address entry = d.InstructionStartOfBuiltin(builtin_index); 3789 li(t9, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); 3790 Jump(t9, 0, cond, rs, rt, bd); 3791 return; 3792 } 3793 } 3794 } 3795 Jump(static_cast<intptr_t>(code.address()), rmode, cond, rs, rt, bd); 3796 } 3797 3798 // Note: To call gcc-compiled C code on mips, you must call through t9. 3799 void TurboAssembler::Call(Register target, int16_t offset, Condition cond, 3800 Register rs, const Operand& rt, BranchDelaySlot bd) { 3801 DCHECK(is_int16(offset)); 3802 BlockTrampolinePoolScope block_trampoline_pool(this); 3803 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT) { 3804 if (cond == cc_always) { 3805 jialc(target, offset); 3806 } else { 3807 BRANCH_ARGS_CHECK(cond, rs, rt); 3808 Branch(2, NegateCondition(cond), rs, rt); 3809 jialc(target, offset); 3810 } 3811 } else { 3812 if (offset != 0) { 3813 Addu(target, target, offset); 3814 } 3815 if (cond == cc_always) { 3816 jalr(target); 3817 } else { 3818 BRANCH_ARGS_CHECK(cond, rs, rt); 3819 Branch(2, NegateCondition(cond), rs, rt); 3820 jalr(target); 3821 } 3822 // Emit a nop in the branch delay slot if required. 3823 if (bd == PROTECT) nop(); 3824 } 3825 } 3826 3827 // Note: To call gcc-compiled C code on mips, you must call through t9. 3828 void TurboAssembler::Call(Register target, Register base, int16_t offset, 3829 Condition cond, Register rs, const Operand& rt, 3830 BranchDelaySlot bd) { 3831 DCHECK(is_uint16(offset)); 3832 BlockTrampolinePoolScope block_trampoline_pool(this); 3833 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT) { 3834 if (cond == cc_always) { 3835 jialc(base, offset); 3836 } else { 3837 BRANCH_ARGS_CHECK(cond, rs, rt); 3838 Branch(2, NegateCondition(cond), rs, rt); 3839 jialc(base, offset); 3840 } 3841 } else { 3842 if (offset != 0) { 3843 Addu(target, base, offset); 3844 } else { // Call through target 3845 if (target != base) mov(target, base); 3846 } 3847 if (cond == cc_always) { 3848 jalr(target); 3849 } else { 3850 BRANCH_ARGS_CHECK(cond, rs, rt); 3851 Branch(2, NegateCondition(cond), rs, rt); 3852 jalr(target); 3853 } 3854 // Emit a nop in the branch delay slot if required. 3855 if (bd == PROTECT) nop(); 3856 } 3857 } 3858 3859 void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond, 3860 Register rs, const Operand& rt, BranchDelaySlot bd) { 3861 CheckBuffer(); 3862 BlockTrampolinePoolScope block_trampoline_pool(this); 3863 int32_t target_int = static_cast<int32_t>(target); 3864 if (IsMipsArchVariant(kMips32r6) && bd == PROTECT && cond == cc_always) { 3865 uint32_t lui_offset, jialc_offset; 3866 UnpackTargetAddressUnsigned(target_int, lui_offset, jialc_offset); 3867 if (MustUseReg(rmode)) { 3868 RecordRelocInfo(rmode, target_int); 3869 } 3870 lui(t9, lui_offset); 3871 Call(t9, jialc_offset, cond, rs, rt, bd); 3872 } else { 3873 li(t9, Operand(target_int, rmode), CONSTANT_SIZE); 3874 Call(t9, 0, cond, rs, rt, bd); 3875 } 3876 } 3877 3878 void TurboAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode, 3879 Condition cond, Register rs, const Operand& rt, 3880 BranchDelaySlot bd) { 3881 BlockTrampolinePoolScope block_trampoline_pool(this); 3882 if (FLAG_embedded_builtins) { 3883 if (root_array_available_ && options().isolate_independent_code) { 3884 IndirectLoadConstant(t9, code); 3885 Call(t9, Code::kHeaderSize - kHeapObjectTag, cond, rs, rt, bd); 3886 return; 3887 } else if (options().inline_offheap_trampolines) { 3888 int builtin_index = Builtins::kNoBuiltinId; 3889 if (isolate()->builtins()->IsBuiltinHandle(code, &builtin_index) && 3890 Builtins::IsIsolateIndependent(builtin_index)) { 3891 // Inline the trampoline. 3892 RecordCommentForOffHeapTrampoline(builtin_index); 3893 CHECK_NE(builtin_index, Builtins::kNoBuiltinId); 3894 EmbeddedData d = EmbeddedData::FromBlob(); 3895 Address entry = d.InstructionStartOfBuiltin(builtin_index); 3896 li(t9, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); 3897 Call(t9, 0, cond, rs, rt, bd); 3898 return; 3899 } 3900 } 3901 } 3902 DCHECK(RelocInfo::IsCodeTarget(rmode)); 3903 AllowDeferredHandleDereference embedding_raw_address; 3904 Call(code.address(), rmode, cond, rs, rt, bd); 3905 } 3906 3907 void TurboAssembler::Ret(Condition cond, Register rs, const Operand& rt, 3908 BranchDelaySlot bd) { 3909 Jump(ra, 0, cond, rs, rt, bd); 3910 } 3911 3912 void TurboAssembler::BranchLong(Label* L, BranchDelaySlot bdslot) { 3913 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT && 3914 (!L->is_bound() || is_near_r6(L))) { 3915 BranchShortHelperR6(0, L); 3916 } else { 3917 // Generate position independent long branch. 3918 BlockTrampolinePoolScope block_trampoline_pool(this); 3919 int32_t imm32; 3920 imm32 = branch_long_offset(L); 3921 or_(t8, ra, zero_reg); 3922 nal(); // Read PC into ra register. 3923 lui(t9, (imm32 & kHiMask) >> kLuiShift); // Branch delay slot. 3924 ori(t9, t9, (imm32 & kImm16Mask)); 3925 addu(t9, ra, t9); 3926 if (bdslot == USE_DELAY_SLOT) { 3927 or_(ra, t8, zero_reg); 3928 } 3929 jr(t9); 3930 // Emit a or_ in the branch delay slot if it's protected. 3931 if (bdslot == PROTECT) or_(ra, t8, zero_reg); 3932 } 3933 } 3934 3935 void TurboAssembler::BranchAndLinkLong(Label* L, BranchDelaySlot bdslot) { 3936 if (IsMipsArchVariant(kMips32r6) && bdslot == PROTECT && 3937 (!L->is_bound() || is_near_r6(L))) { 3938 BranchAndLinkShortHelperR6(0, L); 3939 } else { 3940 // Generate position independent long branch and link. 3941 BlockTrampolinePoolScope block_trampoline_pool(this); 3942 int32_t imm32; 3943 imm32 = branch_long_offset(L); 3944 lui(t8, (imm32 & kHiMask) >> kLuiShift); 3945 nal(); // Read PC into ra register. 3946 ori(t8, t8, (imm32 & kImm16Mask)); // Branch delay slot. 3947 addu(t8, ra, t8); 3948 jalr(t8); 3949 // Emit a nop in the branch delay slot if required. 3950 if (bdslot == PROTECT) nop(); 3951 } 3952 } 3953 3954 void TurboAssembler::DropAndRet(int drop) { 3955 DCHECK(is_int16(drop * kPointerSize)); 3956 Ret(USE_DELAY_SLOT); 3957 addiu(sp, sp, drop * kPointerSize); 3958 } 3959 3960 void TurboAssembler::DropAndRet(int drop, Condition cond, Register r1, 3961 const Operand& r2) { 3962 // Both Drop and Ret need to be conditional. 3963 Label skip; 3964 if (cond != cc_always) { 3965 Branch(&skip, NegateCondition(cond), r1, r2); 3966 } 3967 3968 Drop(drop); 3969 Ret(); 3970 3971 if (cond != cc_always) { 3972 bind(&skip); 3973 } 3974 } 3975 3976 void TurboAssembler::Drop(int count, Condition cond, Register reg, 3977 const Operand& op) { 3978 if (count <= 0) { 3979 return; 3980 } 3981 3982 Label skip; 3983 3984 if (cond != al) { 3985 Branch(&skip, NegateCondition(cond), reg, op); 3986 } 3987 3988 Addu(sp, sp, Operand(count * kPointerSize)); 3989 3990 if (cond != al) { 3991 bind(&skip); 3992 } 3993 } 3994 3995 3996 3997 void MacroAssembler::Swap(Register reg1, 3998 Register reg2, 3999 Register scratch) { 4000 if (scratch == no_reg) { 4001 Xor(reg1, reg1, Operand(reg2)); 4002 Xor(reg2, reg2, Operand(reg1)); 4003 Xor(reg1, reg1, Operand(reg2)); 4004 } else { 4005 mov(scratch, reg1); 4006 mov(reg1, reg2); 4007 mov(reg2, scratch); 4008 } 4009 } 4010 4011 void TurboAssembler::Call(Label* target) { BranchAndLink(target); } 4012 4013 void TurboAssembler::Push(Handle<HeapObject> handle) { 4014 UseScratchRegisterScope temps(this); 4015 Register scratch = temps.Acquire(); 4016 li(scratch, Operand(handle)); 4017 push(scratch); 4018 } 4019 4020 void TurboAssembler::Push(Smi* smi) { 4021 UseScratchRegisterScope temps(this); 4022 Register scratch = temps.Acquire(); 4023 li(scratch, Operand(smi)); 4024 push(scratch); 4025 } 4026 4027 void MacroAssembler::MaybeDropFrames() { 4028 // Check whether we need to drop frames to restart a function on the stack. 4029 li(a1, ExternalReference::debug_restart_fp_address(isolate())); 4030 lw(a1, MemOperand(a1)); 4031 Jump(BUILTIN_CODE(isolate(), FrameDropperTrampoline), RelocInfo::CODE_TARGET, 4032 ne, a1, Operand(zero_reg)); 4033 } 4034 4035 // --------------------------------------------------------------------------- 4036 // Exception handling. 4037 4038 void MacroAssembler::PushStackHandler() { 4039 // Adjust this code if not the case. 4040 STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize); 4041 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize); 4042 4043 Push(Smi::kZero); // Padding. 4044 4045 // Link the current handler as the next handler. 4046 li(t2, 4047 ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate())); 4048 lw(t1, MemOperand(t2)); 4049 push(t1); 4050 4051 // Set this new handler as the current one. 4052 sw(sp, MemOperand(t2)); 4053 } 4054 4055 4056 void MacroAssembler::PopStackHandler() { 4057 STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); 4058 pop(a1); 4059 Addu(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize)); 4060 UseScratchRegisterScope temps(this); 4061 Register scratch = temps.Acquire(); 4062 li(scratch, 4063 ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate())); 4064 sw(a1, MemOperand(scratch)); 4065 } 4066 4067 void TurboAssembler::FPUCanonicalizeNaN(const DoubleRegister dst, 4068 const DoubleRegister src) { 4069 sub_d(dst, src, kDoubleRegZero); 4070 } 4071 4072 void TurboAssembler::MovFromFloatResult(DoubleRegister dst) { 4073 if (IsMipsSoftFloatABI) { 4074 if (kArchEndian == kLittle) { 4075 Move(dst, v0, v1); 4076 } else { 4077 Move(dst, v1, v0); 4078 } 4079 } else { 4080 Move(dst, f0); // Reg f0 is o32 ABI FP return value. 4081 } 4082 } 4083 4084 void TurboAssembler::MovFromFloatParameter(DoubleRegister dst) { 4085 if (IsMipsSoftFloatABI) { 4086 if (kArchEndian == kLittle) { 4087 Move(dst, a0, a1); 4088 } else { 4089 Move(dst, a1, a0); 4090 } 4091 } else { 4092 Move(dst, f12); // Reg f12 is o32 ABI FP first argument value. 4093 } 4094 } 4095 4096 void TurboAssembler::MovToFloatParameter(DoubleRegister src) { 4097 if (!IsMipsSoftFloatABI) { 4098 Move(f12, src); 4099 } else { 4100 if (kArchEndian == kLittle) { 4101 Move(a0, a1, src); 4102 } else { 4103 Move(a1, a0, src); 4104 } 4105 } 4106 } 4107 4108 void TurboAssembler::MovToFloatResult(DoubleRegister src) { 4109 if (!IsMipsSoftFloatABI) { 4110 Move(f0, src); 4111 } else { 4112 if (kArchEndian == kLittle) { 4113 Move(v0, v1, src); 4114 } else { 4115 Move(v1, v0, src); 4116 } 4117 } 4118 } 4119 4120 void TurboAssembler::MovToFloatParameters(DoubleRegister src1, 4121 DoubleRegister src2) { 4122 if (!IsMipsSoftFloatABI) { 4123 if (src2 == f12) { 4124 DCHECK(src1 != f14); 4125 Move(f14, src2); 4126 Move(f12, src1); 4127 } else { 4128 Move(f12, src1); 4129 Move(f14, src2); 4130 } 4131 } else { 4132 if (kArchEndian == kLittle) { 4133 Move(a0, a1, src1); 4134 Move(a2, a3, src2); 4135 } else { 4136 Move(a1, a0, src1); 4137 Move(a3, a2, src2); 4138 } 4139 } 4140 } 4141 4142 4143 // ----------------------------------------------------------------------------- 4144 // JavaScript invokes. 4145 4146 void TurboAssembler::PrepareForTailCall(const ParameterCount& callee_args_count, 4147 Register caller_args_count_reg, 4148 Register scratch0, Register scratch1) { 4149 #if DEBUG 4150 if (callee_args_count.is_reg()) { 4151 DCHECK(!AreAliased(callee_args_count.reg(), caller_args_count_reg, scratch0, 4152 scratch1)); 4153 } else { 4154 DCHECK(!AreAliased(caller_args_count_reg, scratch0, scratch1)); 4155 } 4156 #endif 4157 4158 // Calculate the end of destination area where we will put the arguments 4159 // after we drop current frame. We add kPointerSize to count the receiver 4160 // argument which is not included into formal parameters count. 4161 Register dst_reg = scratch0; 4162 Lsa(dst_reg, fp, caller_args_count_reg, kPointerSizeLog2); 4163 Addu(dst_reg, dst_reg, 4164 Operand(StandardFrameConstants::kCallerSPOffset + kPointerSize)); 4165 4166 Register src_reg = caller_args_count_reg; 4167 // Calculate the end of source area. +kPointerSize is for the receiver. 4168 if (callee_args_count.is_reg()) { 4169 Lsa(src_reg, sp, callee_args_count.reg(), kPointerSizeLog2); 4170 Addu(src_reg, src_reg, Operand(kPointerSize)); 4171 } else { 4172 Addu(src_reg, sp, 4173 Operand((callee_args_count.immediate() + 1) * kPointerSize)); 4174 } 4175 4176 if (FLAG_debug_code) { 4177 Check(lo, AbortReason::kStackAccessBelowStackPointer, src_reg, 4178 Operand(dst_reg)); 4179 } 4180 4181 // Restore caller's frame pointer and return address now as they will be 4182 // overwritten by the copying loop. 4183 lw(ra, MemOperand(fp, StandardFrameConstants::kCallerPCOffset)); 4184 lw(fp, MemOperand(fp, StandardFrameConstants::kCallerFPOffset)); 4185 4186 // Now copy callee arguments to the caller frame going backwards to avoid 4187 // callee arguments corruption (source and destination areas could overlap). 4188 4189 // Both src_reg and dst_reg are pointing to the word after the one to copy, 4190 // so they must be pre-decremented in the loop. 4191 Register tmp_reg = scratch1; 4192 Label loop, entry; 4193 Branch(&entry); 4194 bind(&loop); 4195 Subu(src_reg, src_reg, Operand(kPointerSize)); 4196 Subu(dst_reg, dst_reg, Operand(kPointerSize)); 4197 lw(tmp_reg, MemOperand(src_reg)); 4198 sw(tmp_reg, MemOperand(dst_reg)); 4199 bind(&entry); 4200 Branch(&loop, ne, sp, Operand(src_reg)); 4201 4202 // Leave current frame. 4203 mov(sp, dst_reg); 4204 } 4205 4206 void MacroAssembler::InvokePrologue(const ParameterCount& expected, 4207 const ParameterCount& actual, Label* done, 4208 bool* definitely_mismatches, 4209 InvokeFlag flag) { 4210 bool definitely_matches = false; 4211 *definitely_mismatches = false; 4212 Label regular_invoke; 4213 4214 // Check whether the expected and actual arguments count match. If not, 4215 // setup registers according to contract with ArgumentsAdaptorTrampoline: 4216 // a0: actual arguments count 4217 // a1: function (passed through to callee) 4218 // a2: expected arguments count 4219 4220 // The code below is made a lot easier because the calling code already sets 4221 // up actual and expected registers according to the contract if values are 4222 // passed in registers. 4223 DCHECK(actual.is_immediate() || actual.reg() == a0); 4224 DCHECK(expected.is_immediate() || expected.reg() == a2); 4225 4226 if (expected.is_immediate()) { 4227 DCHECK(actual.is_immediate()); 4228 li(a0, Operand(actual.immediate())); 4229 if (expected.immediate() == actual.immediate()) { 4230 definitely_matches = true; 4231 } else { 4232 const int sentinel = SharedFunctionInfo::kDontAdaptArgumentsSentinel; 4233 if (expected.immediate() == sentinel) { 4234 // Don't worry about adapting arguments for builtins that 4235 // don't want that done. Skip adaption code by making it look 4236 // like we have a match between expected and actual number of 4237 // arguments. 4238 definitely_matches = true; 4239 } else { 4240 *definitely_mismatches = true; 4241 li(a2, Operand(expected.immediate())); 4242 } 4243 } 4244 } else if (actual.is_immediate()) { 4245 li(a0, Operand(actual.immediate())); 4246 Branch(®ular_invoke, eq, expected.reg(), Operand(a0)); 4247 } else { 4248 Branch(®ular_invoke, eq, expected.reg(), Operand(actual.reg())); 4249 } 4250 4251 if (!definitely_matches) { 4252 Handle<Code> adaptor = BUILTIN_CODE(isolate(), ArgumentsAdaptorTrampoline); 4253 if (flag == CALL_FUNCTION) { 4254 Call(adaptor); 4255 if (!*definitely_mismatches) { 4256 Branch(done); 4257 } 4258 } else { 4259 Jump(adaptor, RelocInfo::CODE_TARGET); 4260 } 4261 bind(®ular_invoke); 4262 } 4263 } 4264 4265 void MacroAssembler::CheckDebugHook(Register fun, Register new_target, 4266 const ParameterCount& expected, 4267 const ParameterCount& actual) { 4268 Label skip_hook; 4269 li(t0, ExternalReference::debug_hook_on_function_call_address(isolate())); 4270 lb(t0, MemOperand(t0)); 4271 Branch(&skip_hook, eq, t0, Operand(zero_reg)); 4272 4273 { 4274 // Load receiver to pass it later to DebugOnFunctionCall hook. 4275 if (actual.is_reg()) { 4276 mov(t0, actual.reg()); 4277 } else { 4278 li(t0, actual.immediate()); 4279 } 4280 Lsa(at, sp, t0, kPointerSizeLog2); 4281 lw(t0, MemOperand(at)); 4282 FrameScope frame(this, 4283 has_frame() ? StackFrame::NONE : StackFrame::INTERNAL); 4284 if (expected.is_reg()) { 4285 SmiTag(expected.reg()); 4286 Push(expected.reg()); 4287 } 4288 if (actual.is_reg()) { 4289 SmiTag(actual.reg()); 4290 Push(actual.reg()); 4291 } 4292 if (new_target.is_valid()) { 4293 Push(new_target); 4294 } 4295 Push(fun); 4296 Push(fun); 4297 Push(t0); 4298 CallRuntime(Runtime::kDebugOnFunctionCall); 4299 Pop(fun); 4300 if (new_target.is_valid()) { 4301 Pop(new_target); 4302 } 4303 if (actual.is_reg()) { 4304 Pop(actual.reg()); 4305 SmiUntag(actual.reg()); 4306 } 4307 if (expected.is_reg()) { 4308 Pop(expected.reg()); 4309 SmiUntag(expected.reg()); 4310 } 4311 } 4312 bind(&skip_hook); 4313 } 4314 4315 void MacroAssembler::InvokeFunctionCode(Register function, Register new_target, 4316 const ParameterCount& expected, 4317 const ParameterCount& actual, 4318 InvokeFlag flag) { 4319 // You can't call a function without a valid frame. 4320 DCHECK(flag == JUMP_FUNCTION || has_frame()); 4321 DCHECK(function == a1); 4322 DCHECK_IMPLIES(new_target.is_valid(), new_target == a3); 4323 4324 // On function call, call into the debugger if necessary. 4325 CheckDebugHook(function, new_target, expected, actual); 4326 4327 // Clear the new.target register if not given. 4328 if (!new_target.is_valid()) { 4329 LoadRoot(a3, Heap::kUndefinedValueRootIndex); 4330 } 4331 4332 Label done; 4333 bool definitely_mismatches = false; 4334 InvokePrologue(expected, actual, &done, &definitely_mismatches, flag); 4335 if (!definitely_mismatches) { 4336 // We call indirectly through the code field in the function to 4337 // allow recompilation to take effect without changing any of the 4338 // call sites. 4339 Register code = kJavaScriptCallCodeStartRegister; 4340 lw(code, FieldMemOperand(function, JSFunction::kCodeOffset)); 4341 if (flag == CALL_FUNCTION) { 4342 Addu(code, code, Code::kHeaderSize - kHeapObjectTag); 4343 Call(code); 4344 } else { 4345 DCHECK(flag == JUMP_FUNCTION); 4346 Addu(code, code, Code::kHeaderSize - kHeapObjectTag); 4347 Jump(code); 4348 } 4349 // Continue here if InvokePrologue does handle the invocation due to 4350 // mismatched parameter counts. 4351 bind(&done); 4352 } 4353 } 4354 4355 void MacroAssembler::InvokeFunction(Register function, Register new_target, 4356 const ParameterCount& actual, 4357 InvokeFlag flag) { 4358 // You can't call a function without a valid frame. 4359 DCHECK(flag == JUMP_FUNCTION || has_frame()); 4360 4361 // Contract with called JS functions requires that function is passed in a1. 4362 DCHECK(function == a1); 4363 Register expected_reg = a2; 4364 Register temp_reg = t0; 4365 4366 lw(temp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset)); 4367 lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); 4368 lhu(expected_reg, 4369 FieldMemOperand(temp_reg, 4370 SharedFunctionInfo::kFormalParameterCountOffset)); 4371 4372 ParameterCount expected(expected_reg); 4373 InvokeFunctionCode(function, new_target, expected, actual, flag); 4374 } 4375 4376 void MacroAssembler::InvokeFunction(Register function, 4377 const ParameterCount& expected, 4378 const ParameterCount& actual, 4379 InvokeFlag flag) { 4380 // You can't call a function without a valid frame. 4381 DCHECK(flag == JUMP_FUNCTION || has_frame()); 4382 4383 // Contract with called JS functions requires that function is passed in a1. 4384 DCHECK(function == a1); 4385 4386 // Get the function and setup the context. 4387 lw(cp, FieldMemOperand(a1, JSFunction::kContextOffset)); 4388 4389 InvokeFunctionCode(a1, no_reg, expected, actual, flag); 4390 } 4391 4392 4393 // --------------------------------------------------------------------------- 4394 // Support functions. 4395 4396 void MacroAssembler::GetObjectType(Register object, 4397 Register map, 4398 Register type_reg) { 4399 lw(map, FieldMemOperand(object, HeapObject::kMapOffset)); 4400 lhu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset)); 4401 } 4402 4403 4404 // ----------------------------------------------------------------------------- 4405 // Runtime calls. 4406 4407 void MacroAssembler::CallStub(CodeStub* stub, 4408 Condition cond, 4409 Register r1, 4410 const Operand& r2, 4411 BranchDelaySlot bd) { 4412 DCHECK(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. 4413 Call(stub->GetCode(), RelocInfo::CODE_TARGET, cond, r1, r2, bd); 4414 } 4415 4416 void TurboAssembler::CallStubDelayed(CodeStub* stub, Condition cond, 4417 Register r1, const Operand& r2, 4418 BranchDelaySlot bd) { 4419 DCHECK(AllowThisStubCall(stub)); // Stub calls are not allowed in some stubs. 4420 4421 BlockTrampolinePoolScope block_trampoline_pool(this); 4422 UseScratchRegisterScope temps(this); 4423 Register scratch = temps.Acquire(); 4424 li(scratch, Operand::EmbeddedCode(stub)); 4425 Call(scratch); 4426 } 4427 4428 void MacroAssembler::TailCallStub(CodeStub* stub, 4429 Condition cond, 4430 Register r1, 4431 const Operand& r2, 4432 BranchDelaySlot bd) { 4433 Jump(stub->GetCode(), RelocInfo::CODE_TARGET, cond, r1, r2, bd); 4434 } 4435 4436 bool TurboAssembler::AllowThisStubCall(CodeStub* stub) { 4437 return has_frame() || !stub->SometimesSetsUpAFrame(); 4438 } 4439 4440 void TurboAssembler::AddOverflow(Register dst, Register left, 4441 const Operand& right, Register overflow) { 4442 BlockTrampolinePoolScope block_trampoline_pool(this); 4443 Register right_reg = no_reg; 4444 Register scratch = t8; 4445 if (!right.is_reg()) { 4446 li(at, Operand(right)); 4447 right_reg = at; 4448 } else { 4449 right_reg = right.rm(); 4450 } 4451 4452 DCHECK(left != scratch && right_reg != scratch && dst != scratch && 4453 overflow != scratch); 4454 DCHECK(overflow != left && overflow != right_reg); 4455 4456 if (dst == left || dst == right_reg) { 4457 addu(scratch, left, right_reg); 4458 xor_(overflow, scratch, left); 4459 xor_(at, scratch, right_reg); 4460 and_(overflow, overflow, at); 4461 mov(dst, scratch); 4462 } else { 4463 addu(dst, left, right_reg); 4464 xor_(overflow, dst, left); 4465 xor_(at, dst, right_reg); 4466 and_(overflow, overflow, at); 4467 } 4468 } 4469 4470 void TurboAssembler::SubOverflow(Register dst, Register left, 4471 const Operand& right, Register overflow) { 4472 BlockTrampolinePoolScope block_trampoline_pool(this); 4473 Register right_reg = no_reg; 4474 Register scratch = t8; 4475 if (!right.is_reg()) { 4476 li(at, Operand(right)); 4477 right_reg = at; 4478 } else { 4479 right_reg = right.rm(); 4480 } 4481 4482 DCHECK(left != scratch && right_reg != scratch && dst != scratch && 4483 overflow != scratch); 4484 DCHECK(overflow != left && overflow != right_reg); 4485 4486 if (dst == left || dst == right_reg) { 4487 subu(scratch, left, right_reg); 4488 xor_(overflow, left, scratch); 4489 xor_(at, left, right_reg); 4490 and_(overflow, overflow, at); 4491 mov(dst, scratch); 4492 } else { 4493 subu(dst, left, right_reg); 4494 xor_(overflow, left, dst); 4495 xor_(at, left, right_reg); 4496 and_(overflow, overflow, at); 4497 } 4498 } 4499 4500 void TurboAssembler::MulOverflow(Register dst, Register left, 4501 const Operand& right, Register overflow) { 4502 BlockTrampolinePoolScope block_trampoline_pool(this); 4503 Register right_reg = no_reg; 4504 Register scratch = t8; 4505 Register scratch2 = t9; 4506 if (!right.is_reg()) { 4507 li(at, Operand(right)); 4508 right_reg = at; 4509 } else { 4510 right_reg = right.rm(); 4511 } 4512 4513 DCHECK(left != scratch && right_reg != scratch && dst != scratch && 4514 overflow != scratch); 4515 DCHECK(overflow != left && overflow != right_reg); 4516 4517 if (dst == left || dst == right_reg) { 4518 Mul(overflow, scratch2, left, right_reg); 4519 sra(scratch, scratch2, 31); 4520 xor_(overflow, overflow, scratch); 4521 mov(dst, scratch2); 4522 } else { 4523 Mul(overflow, dst, left, right_reg); 4524 sra(scratch, dst, 31); 4525 xor_(overflow, overflow, scratch); 4526 } 4527 } 4528 4529 void TurboAssembler::CallRuntimeWithCEntry(Runtime::FunctionId fid, 4530 Register centry) { 4531 const Runtime::Function* f = Runtime::FunctionForId(fid); 4532 // TODO(1236192): Most runtime routines don't need the number of 4533 // arguments passed in because it is constant. At some point we 4534 // should remove this need and make the runtime routine entry code 4535 // smarter. 4536 PrepareCEntryArgs(f->nargs); 4537 PrepareCEntryFunction(ExternalReference::Create(f)); 4538 DCHECK(!AreAliased(centry, a0, a1)); 4539 Call(centry, Code::kHeaderSize - kHeapObjectTag); 4540 } 4541 4542 void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments, 4543 SaveFPRegsMode save_doubles) { 4544 // All parameters are on the stack. v0 has the return value after call. 4545 4546 // If the expected number of arguments of the runtime function is 4547 // constant, we check that the actual number of arguments match the 4548 // expectation. 4549 CHECK(f->nargs < 0 || f->nargs == num_arguments); 4550 4551 // TODO(1236192): Most runtime routines don't need the number of 4552 // arguments passed in because it is constant. At some point we 4553 // should remove this need and make the runtime routine entry code 4554 // smarter. 4555 PrepareCEntryArgs(num_arguments); 4556 PrepareCEntryFunction(ExternalReference::Create(f)); 4557 Handle<Code> code = 4558 CodeFactory::CEntry(isolate(), f->result_size, save_doubles); 4559 Call(code, RelocInfo::CODE_TARGET); 4560 } 4561 4562 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) { 4563 const Runtime::Function* function = Runtime::FunctionForId(fid); 4564 DCHECK_EQ(1, function->result_size); 4565 if (function->nargs >= 0) { 4566 PrepareCEntryArgs(function->nargs); 4567 } 4568 JumpToExternalReference(ExternalReference::Create(fid)); 4569 } 4570 4571 void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin, 4572 BranchDelaySlot bd, 4573 bool builtin_exit_frame) { 4574 PrepareCEntryFunction(builtin); 4575 Handle<Code> code = CodeFactory::CEntry(isolate(), 1, kDontSaveFPRegs, 4576 kArgvOnStack, builtin_exit_frame); 4577 Jump(code, RelocInfo::CODE_TARGET, al, zero_reg, Operand(zero_reg), bd); 4578 } 4579 4580 void MacroAssembler::JumpToInstructionStream(Address entry) { 4581 li(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET)); 4582 Jump(kOffHeapTrampolineRegister); 4583 } 4584 4585 void MacroAssembler::LoadWeakValue(Register out, Register in, 4586 Label* target_if_cleared) { 4587 Branch(target_if_cleared, eq, in, Operand(kClearedWeakHeapObject)); 4588 4589 And(out, in, Operand(~kWeakHeapObjectMask)); 4590 } 4591 4592 void MacroAssembler::IncrementCounter(StatsCounter* counter, int value, 4593 Register scratch1, Register scratch2) { 4594 DCHECK_GT(value, 0); 4595 if (FLAG_native_code_counters && counter->Enabled()) { 4596 li(scratch2, ExternalReference::Create(counter)); 4597 lw(scratch1, MemOperand(scratch2)); 4598 Addu(scratch1, scratch1, Operand(value)); 4599 sw(scratch1, MemOperand(scratch2)); 4600 } 4601 } 4602 4603 4604 void MacroAssembler::DecrementCounter(StatsCounter* counter, int value, 4605 Register scratch1, Register scratch2) { 4606 DCHECK_GT(value, 0); 4607 if (FLAG_native_code_counters && counter->Enabled()) { 4608 li(scratch2, ExternalReference::Create(counter)); 4609 lw(scratch1, MemOperand(scratch2)); 4610 Subu(scratch1, scratch1, Operand(value)); 4611 sw(scratch1, MemOperand(scratch2)); 4612 } 4613 } 4614 4615 4616 // ----------------------------------------------------------------------------- 4617 // Debugging. 4618 4619 void TurboAssembler::Assert(Condition cc, AbortReason reason, Register rs, 4620 Operand rt) { 4621 if (emit_debug_code()) 4622 Check(cc, reason, rs, rt); 4623 } 4624 4625 void TurboAssembler::Check(Condition cc, AbortReason reason, Register rs, 4626 Operand rt) { 4627 Label L; 4628 Branch(&L, cc, rs, rt); 4629 Abort(reason); 4630 // Will not return here. 4631 bind(&L); 4632 } 4633 4634 void TurboAssembler::Abort(AbortReason reason) { 4635 Label abort_start; 4636 bind(&abort_start); 4637 const char* msg = GetAbortReason(reason); 4638 #ifdef DEBUG 4639 RecordComment("Abort message: "); 4640 RecordComment(msg); 4641 #endif 4642 4643 // Avoid emitting call to builtin if requested. 4644 if (trap_on_abort()) { 4645 stop(msg); 4646 return; 4647 } 4648 4649 if (should_abort_hard()) { 4650 // We don't care if we constructed a frame. Just pretend we did. 4651 FrameScope assume_frame(this, StackFrame::NONE); 4652 PrepareCallCFunction(0, a0); 4653 li(a0, Operand(static_cast<int>(reason))); 4654 CallCFunction(ExternalReference::abort_with_reason(), 1); 4655 return; 4656 } 4657 4658 Move(a0, Smi::FromInt(static_cast<int>(reason))); 4659 4660 // Disable stub call restrictions to always allow calls to abort. 4661 if (!has_frame_) { 4662 // We don't actually want to generate a pile of code for this, so just 4663 // claim there is a stack frame, without generating one. 4664 FrameScope scope(this, StackFrame::NONE); 4665 Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET); 4666 } else { 4667 Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET); 4668 } 4669 // Will not return here. 4670 if (is_trampoline_pool_blocked()) { 4671 // If the calling code cares about the exact number of 4672 // instructions generated, we insert padding here to keep the size 4673 // of the Abort macro constant. 4674 // Currently in debug mode with debug_code enabled the number of 4675 // generated instructions is 10, so we use this as a maximum value. 4676 static const int kExpectedAbortInstructions = 10; 4677 int abort_instructions = InstructionsGeneratedSince(&abort_start); 4678 DCHECK_LE(abort_instructions, kExpectedAbortInstructions); 4679 while (abort_instructions++ < kExpectedAbortInstructions) { 4680 nop(); 4681 } 4682 } 4683 } 4684 4685 void MacroAssembler::LoadNativeContextSlot(int index, Register dst) { 4686 lw(dst, NativeContextMemOperand()); 4687 lw(dst, ContextMemOperand(dst, index)); 4688 } 4689 4690 void TurboAssembler::StubPrologue(StackFrame::Type type) { 4691 UseScratchRegisterScope temps(this); 4692 Register scratch = temps.Acquire(); 4693 li(scratch, Operand(StackFrame::TypeToMarker(type))); 4694 PushCommonFrame(scratch); 4695 } 4696 4697 void TurboAssembler::Prologue() { PushStandardFrame(a1); } 4698 4699 void TurboAssembler::EnterFrame(StackFrame::Type type) { 4700 BlockTrampolinePoolScope block_trampoline_pool(this); 4701 int stack_offset = -3 * kPointerSize; 4702 const int fp_offset = 1 * kPointerSize; 4703 addiu(sp, sp, stack_offset); 4704 stack_offset = -stack_offset - kPointerSize; 4705 sw(ra, MemOperand(sp, stack_offset)); 4706 stack_offset -= kPointerSize; 4707 sw(fp, MemOperand(sp, stack_offset)); 4708 stack_offset -= kPointerSize; 4709 li(t9, Operand(StackFrame::TypeToMarker(type))); 4710 sw(t9, MemOperand(sp, stack_offset)); 4711 // Adjust FP to point to saved FP. 4712 DCHECK_EQ(stack_offset, 0); 4713 Addu(fp, sp, Operand(fp_offset)); 4714 } 4715 4716 void TurboAssembler::LeaveFrame(StackFrame::Type type) { 4717 addiu(sp, fp, 2 * kPointerSize); 4718 lw(ra, MemOperand(fp, 1 * kPointerSize)); 4719 lw(fp, MemOperand(fp, 0 * kPointerSize)); 4720 } 4721 4722 void MacroAssembler::EnterBuiltinFrame(Register context, Register target, 4723 Register argc) { 4724 Push(ra, fp); 4725 Move(fp, sp); 4726 Push(context, target, argc); 4727 } 4728 4729 void MacroAssembler::LeaveBuiltinFrame(Register context, Register target, 4730 Register argc) { 4731 Pop(context, target, argc); 4732 Pop(ra, fp); 4733 } 4734 4735 void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space, 4736 StackFrame::Type frame_type) { 4737 BlockTrampolinePoolScope block_trampoline_pool(this); 4738 DCHECK(frame_type == StackFrame::EXIT || 4739 frame_type == StackFrame::BUILTIN_EXIT); 4740 4741 // Set up the frame structure on the stack. 4742 STATIC_ASSERT(2 * kPointerSize == ExitFrameConstants::kCallerSPDisplacement); 4743 STATIC_ASSERT(1 * kPointerSize == ExitFrameConstants::kCallerPCOffset); 4744 STATIC_ASSERT(0 * kPointerSize == ExitFrameConstants::kCallerFPOffset); 4745 4746 // This is how the stack will look: 4747 // fp + 2 (==kCallerSPDisplacement) - old stack's end 4748 // [fp + 1 (==kCallerPCOffset)] - saved old ra 4749 // [fp + 0 (==kCallerFPOffset)] - saved old fp 4750 // [fp - 1 StackFrame::EXIT Smi 4751 // [fp - 2 (==kSPOffset)] - sp of the called function 4752 // [fp - 3 (==kCodeOffset)] - CodeObject 4753 // fp - (2 + stack_space + alignment) == sp == [fp - kSPOffset] - top of the 4754 // new stack (will contain saved ra) 4755 4756 // Save registers and reserve room for saved entry sp and code object. 4757 addiu(sp, sp, -2 * kPointerSize - ExitFrameConstants::kFixedFrameSizeFromFp); 4758 sw(ra, MemOperand(sp, 4 * kPointerSize)); 4759 sw(fp, MemOperand(sp, 3 * kPointerSize)); 4760 { 4761 UseScratchRegisterScope temps(this); 4762 Register scratch = temps.Acquire(); 4763 li(scratch, Operand(StackFrame::TypeToMarker(frame_type))); 4764 sw(scratch, MemOperand(sp, 2 * kPointerSize)); 4765 } 4766 // Set up new frame pointer. 4767 addiu(fp, sp, ExitFrameConstants::kFixedFrameSizeFromFp); 4768 4769 if (emit_debug_code()) { 4770 sw(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset)); 4771 } 4772 4773 // Accessed from ExitFrame::code_slot. 4774 li(t8, CodeObject(), CONSTANT_SIZE); 4775 sw(t8, MemOperand(fp, ExitFrameConstants::kCodeOffset)); 4776 4777 // Save the frame pointer and the context in top. 4778 li(t8, 4779 ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate())); 4780 sw(fp, MemOperand(t8)); 4781 li(t8, 4782 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); 4783 sw(cp, MemOperand(t8)); 4784 4785 const int frame_alignment = MacroAssembler::ActivationFrameAlignment(); 4786 if (save_doubles) { 4787 // The stack must be align to 0 modulo 8 for stores with sdc1. 4788 DCHECK_EQ(kDoubleSize, frame_alignment); 4789 if (frame_alignment > 0) { 4790 DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); 4791 And(sp, sp, Operand(-frame_alignment)); // Align stack. 4792 } 4793 int space = FPURegister::kNumRegisters * kDoubleSize; 4794 Subu(sp, sp, Operand(space)); 4795 // Remember: we only need to save every 2nd double FPU value. 4796 for (int i = 0; i < FPURegister::kNumRegisters; i += 2) { 4797 FPURegister reg = FPURegister::from_code(i); 4798 Sdc1(reg, MemOperand(sp, i * kDoubleSize)); 4799 } 4800 } 4801 4802 // Reserve place for the return address, stack space and an optional slot 4803 // (used by the DirectCEntryStub to hold the return value if a struct is 4804 // returned) and align the frame preparing for calling the runtime function. 4805 DCHECK_GE(stack_space, 0); 4806 Subu(sp, sp, Operand((stack_space + 2) * kPointerSize)); 4807 if (frame_alignment > 0) { 4808 DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); 4809 And(sp, sp, Operand(-frame_alignment)); // Align stack. 4810 } 4811 4812 // Set the exit frame sp value to point just before the return address 4813 // location. 4814 UseScratchRegisterScope temps(this); 4815 Register scratch = temps.Acquire(); 4816 addiu(scratch, sp, kPointerSize); 4817 sw(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset)); 4818 } 4819 4820 void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count, 4821 bool do_return, 4822 bool argument_count_is_length) { 4823 BlockTrampolinePoolScope block_trampoline_pool(this); 4824 // Optionally restore all double registers. 4825 if (save_doubles) { 4826 // Remember: we only need to restore every 2nd double FPU value. 4827 lw(t8, MemOperand(fp, ExitFrameConstants::kSPOffset)); 4828 for (int i = 0; i < FPURegister::kNumRegisters; i += 2) { 4829 FPURegister reg = FPURegister::from_code(i); 4830 Ldc1(reg, MemOperand(t8, i * kDoubleSize + kPointerSize)); 4831 } 4832 } 4833 4834 // Clear top frame. 4835 li(t8, 4836 ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate())); 4837 sw(zero_reg, MemOperand(t8)); 4838 4839 // Restore current context from top and clear it in debug mode. 4840 li(t8, 4841 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); 4842 lw(cp, MemOperand(t8)); 4843 4844 #ifdef DEBUG 4845 li(t8, 4846 ExternalReference::Create(IsolateAddressId::kContextAddress, isolate())); 4847 sw(a3, MemOperand(t8)); 4848 #endif 4849 4850 // Pop the arguments, restore registers, and return. 4851 mov(sp, fp); // Respect ABI stack constraint. 4852 lw(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset)); 4853 lw(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset)); 4854 4855 if (argument_count.is_valid()) { 4856 if (argument_count_is_length) { 4857 addu(sp, sp, argument_count); 4858 } else { 4859 Lsa(sp, sp, argument_count, kPointerSizeLog2, t8); 4860 } 4861 } 4862 4863 if (do_return) { 4864 Ret(USE_DELAY_SLOT); 4865 // If returning, the instruction in the delay slot will be the addiu below. 4866 } 4867 addiu(sp, sp, 8); 4868 } 4869 4870 int TurboAssembler::ActivationFrameAlignment() { 4871 #if V8_HOST_ARCH_MIPS 4872 // Running on the real platform. Use the alignment as mandated by the local 4873 // environment. 4874 // Note: This will break if we ever start generating snapshots on one Mips 4875 // platform for another Mips platform with a different alignment. 4876 return base::OS::ActivationFrameAlignment(); 4877 #else // V8_HOST_ARCH_MIPS 4878 // If we are using the simulator then we should always align to the expected 4879 // alignment. As the simulator is used to generate snapshots we do not know 4880 // if the target platform will need alignment, so this is controlled from a 4881 // flag. 4882 return FLAG_sim_stack_alignment; 4883 #endif // V8_HOST_ARCH_MIPS 4884 } 4885 4886 4887 void MacroAssembler::AssertStackIsAligned() { 4888 if (emit_debug_code()) { 4889 const int frame_alignment = ActivationFrameAlignment(); 4890 const int frame_alignment_mask = frame_alignment - 1; 4891 4892 if (frame_alignment > kPointerSize) { 4893 Label alignment_as_expected; 4894 DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); 4895 UseScratchRegisterScope temps(this); 4896 Register scratch = temps.Acquire(); 4897 andi(scratch, sp, frame_alignment_mask); 4898 Branch(&alignment_as_expected, eq, scratch, Operand(zero_reg)); 4899 // Don't use Check here, as it will call Runtime_Abort re-entering here. 4900 stop("Unexpected stack alignment"); 4901 bind(&alignment_as_expected); 4902 } 4903 } 4904 } 4905 4906 void MacroAssembler::UntagAndJumpIfSmi(Register dst, 4907 Register src, 4908 Label* smi_case) { 4909 UseScratchRegisterScope temps(this); 4910 Register scratch = temps.Acquire(); 4911 JumpIfSmi(src, smi_case, scratch, USE_DELAY_SLOT); 4912 SmiUntag(dst, src); 4913 } 4914 4915 void TurboAssembler::JumpIfSmi(Register value, Label* smi_label, 4916 Register scratch, BranchDelaySlot bd) { 4917 DCHECK_EQ(0, kSmiTag); 4918 andi(scratch, value, kSmiTagMask); 4919 Branch(bd, smi_label, eq, scratch, Operand(zero_reg)); 4920 } 4921 4922 void MacroAssembler::JumpIfNotSmi(Register value, 4923 Label* not_smi_label, 4924 Register scratch, 4925 BranchDelaySlot bd) { 4926 DCHECK_EQ(0, kSmiTag); 4927 andi(scratch, value, kSmiTagMask); 4928 Branch(bd, not_smi_label, ne, scratch, Operand(zero_reg)); 4929 } 4930 4931 4932 void MacroAssembler::JumpIfEitherSmi(Register reg1, 4933 Register reg2, 4934 Label* on_either_smi) { 4935 STATIC_ASSERT(kSmiTag == 0); 4936 DCHECK_EQ(1, kSmiTagMask); 4937 // Both Smi tags must be 1 (not Smi). 4938 UseScratchRegisterScope temps(this); 4939 Register scratch = temps.Acquire(); 4940 and_(scratch, reg1, reg2); 4941 JumpIfSmi(scratch, on_either_smi); 4942 } 4943 4944 void MacroAssembler::AssertNotSmi(Register object) { 4945 if (emit_debug_code()) { 4946 STATIC_ASSERT(kSmiTag == 0); 4947 UseScratchRegisterScope temps(this); 4948 Register scratch = temps.Acquire(); 4949 andi(scratch, object, kSmiTagMask); 4950 Check(ne, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg)); 4951 } 4952 } 4953 4954 4955 void MacroAssembler::AssertSmi(Register object) { 4956 if (emit_debug_code()) { 4957 STATIC_ASSERT(kSmiTag == 0); 4958 UseScratchRegisterScope temps(this); 4959 Register scratch = temps.Acquire(); 4960 andi(scratch, object, kSmiTagMask); 4961 Check(eq, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg)); 4962 } 4963 } 4964 4965 void MacroAssembler::AssertConstructor(Register object) { 4966 if (emit_debug_code()) { 4967 BlockTrampolinePoolScope block_trampoline_pool(this); 4968 STATIC_ASSERT(kSmiTag == 0); 4969 SmiTst(object, t8); 4970 Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor, t8, 4971 Operand(zero_reg)); 4972 4973 lw(t8, FieldMemOperand(object, HeapObject::kMapOffset)); 4974 lbu(t8, FieldMemOperand(t8, Map::kBitFieldOffset)); 4975 And(t8, t8, Operand(Map::IsConstructorBit::kMask)); 4976 Check(ne, AbortReason::kOperandIsNotAConstructor, t8, Operand(zero_reg)); 4977 } 4978 } 4979 4980 void MacroAssembler::AssertFunction(Register object) { 4981 if (emit_debug_code()) { 4982 BlockTrampolinePoolScope block_trampoline_pool(this); 4983 STATIC_ASSERT(kSmiTag == 0); 4984 SmiTst(object, t8); 4985 Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, t8, 4986 Operand(zero_reg)); 4987 GetObjectType(object, t8, t8); 4988 Check(eq, AbortReason::kOperandIsNotAFunction, t8, 4989 Operand(JS_FUNCTION_TYPE)); 4990 } 4991 } 4992 4993 4994 void MacroAssembler::AssertBoundFunction(Register object) { 4995 if (emit_debug_code()) { 4996 BlockTrampolinePoolScope block_trampoline_pool(this); 4997 STATIC_ASSERT(kSmiTag == 0); 4998 SmiTst(object, t8); 4999 Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction, t8, 5000 Operand(zero_reg)); 5001 GetObjectType(object, t8, t8); 5002 Check(eq, AbortReason::kOperandIsNotABoundFunction, t8, 5003 Operand(JS_BOUND_FUNCTION_TYPE)); 5004 } 5005 } 5006 5007 void MacroAssembler::AssertGeneratorObject(Register object) { 5008 if (!emit_debug_code()) return; 5009 BlockTrampolinePoolScope block_trampoline_pool(this); 5010 STATIC_ASSERT(kSmiTag == 0); 5011 SmiTst(object, t8); 5012 Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject, t8, 5013 Operand(zero_reg)); 5014 5015 GetObjectType(object, t8, t8); 5016 5017 Label done; 5018 5019 // Check if JSGeneratorObject 5020 Branch(&done, eq, t8, Operand(JS_GENERATOR_OBJECT_TYPE)); 5021 5022 // Check if JSAsyncGeneratorObject 5023 Branch(&done, eq, t8, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE)); 5024 5025 Abort(AbortReason::kOperandIsNotAGeneratorObject); 5026 5027 bind(&done); 5028 } 5029 5030 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object, 5031 Register scratch) { 5032 if (emit_debug_code()) { 5033 Label done_checking; 5034 AssertNotSmi(object); 5035 LoadRoot(scratch, Heap::kUndefinedValueRootIndex); 5036 Branch(&done_checking, eq, object, Operand(scratch)); 5037 GetObjectType(object, scratch, scratch); 5038 Assert(eq, AbortReason::kExpectedUndefinedOrCell, scratch, 5039 Operand(ALLOCATION_SITE_TYPE)); 5040 bind(&done_checking); 5041 } 5042 } 5043 5044 5045 void TurboAssembler::Float32Max(FPURegister dst, FPURegister src1, 5046 FPURegister src2, Label* out_of_line) { 5047 if (src1 == src2) { 5048 Move_s(dst, src1); 5049 return; 5050 } 5051 5052 // Check if one of operands is NaN. 5053 CompareIsNanF32(src1, src2); 5054 BranchTrueF(out_of_line); 5055 5056 if (IsMipsArchVariant(kMips32r6)) { 5057 max_s(dst, src1, src2); 5058 } else { 5059 Label return_left, return_right, done; 5060 5061 CompareF32(OLT, src1, src2); 5062 BranchTrueShortF(&return_right); 5063 CompareF32(OLT, src2, src1); 5064 BranchTrueShortF(&return_left); 5065 5066 // Operands are equal, but check for +/-0. 5067 { 5068 BlockTrampolinePoolScope block_trampoline_pool(this); 5069 mfc1(t8, src1); 5070 Branch(&return_left, eq, t8, Operand(zero_reg)); 5071 Branch(&return_right); 5072 } 5073 5074 bind(&return_right); 5075 if (src2 != dst) { 5076 Move_s(dst, src2); 5077 } 5078 Branch(&done); 5079 5080 bind(&return_left); 5081 if (src1 != dst) { 5082 Move_s(dst, src1); 5083 } 5084 5085 bind(&done); 5086 } 5087 } 5088 5089 void TurboAssembler::Float32MaxOutOfLine(FPURegister dst, FPURegister src1, 5090 FPURegister src2) { 5091 add_s(dst, src1, src2); 5092 } 5093 5094 void TurboAssembler::Float32Min(FPURegister dst, FPURegister src1, 5095 FPURegister src2, Label* out_of_line) { 5096 if (src1 == src2) { 5097 Move_s(dst, src1); 5098 return; 5099 } 5100 5101 // Check if one of operands is NaN. 5102 CompareIsNanF32(src1, src2); 5103 BranchTrueF(out_of_line); 5104 5105 if (IsMipsArchVariant(kMips32r6)) { 5106 min_s(dst, src1, src2); 5107 } else { 5108 Label return_left, return_right, done; 5109 5110 CompareF32(OLT, src1, src2); 5111 BranchTrueShortF(&return_left); 5112 CompareF32(OLT, src2, src1); 5113 BranchTrueShortF(&return_right); 5114 5115 // Left equals right => check for -0. 5116 { 5117 BlockTrampolinePoolScope block_trampoline_pool(this); 5118 mfc1(t8, src1); 5119 Branch(&return_right, eq, t8, Operand(zero_reg)); 5120 Branch(&return_left); 5121 } 5122 5123 bind(&return_right); 5124 if (src2 != dst) { 5125 Move_s(dst, src2); 5126 } 5127 Branch(&done); 5128 5129 bind(&return_left); 5130 if (src1 != dst) { 5131 Move_s(dst, src1); 5132 } 5133 5134 bind(&done); 5135 } 5136 } 5137 5138 void TurboAssembler::Float32MinOutOfLine(FPURegister dst, FPURegister src1, 5139 FPURegister src2) { 5140 add_s(dst, src1, src2); 5141 } 5142 5143 void TurboAssembler::Float64Max(DoubleRegister dst, DoubleRegister src1, 5144 DoubleRegister src2, Label* out_of_line) { 5145 if (src1 == src2) { 5146 Move_d(dst, src1); 5147 return; 5148 } 5149 5150 // Check if one of operands is NaN. 5151 CompareIsNanF64(src1, src2); 5152 BranchTrueF(out_of_line); 5153 5154 if (IsMipsArchVariant(kMips32r6)) { 5155 max_d(dst, src1, src2); 5156 } else { 5157 Label return_left, return_right, done; 5158 5159 CompareF64(OLT, src1, src2); 5160 BranchTrueShortF(&return_right); 5161 CompareF64(OLT, src2, src1); 5162 BranchTrueShortF(&return_left); 5163 5164 // Left equals right => check for -0. 5165 { 5166 BlockTrampolinePoolScope block_trampoline_pool(this); 5167 Mfhc1(t8, src1); 5168 Branch(&return_left, eq, t8, Operand(zero_reg)); 5169 Branch(&return_right); 5170 } 5171 5172 bind(&return_right); 5173 if (src2 != dst) { 5174 Move_d(dst, src2); 5175 } 5176 Branch(&done); 5177 5178 bind(&return_left); 5179 if (src1 != dst) { 5180 Move_d(dst, src1); 5181 } 5182 5183 bind(&done); 5184 } 5185 } 5186 5187 void TurboAssembler::Float64MaxOutOfLine(DoubleRegister dst, 5188 DoubleRegister src1, 5189 DoubleRegister src2) { 5190 add_d(dst, src1, src2); 5191 } 5192 5193 void TurboAssembler::Float64Min(DoubleRegister dst, DoubleRegister src1, 5194 DoubleRegister src2, Label* out_of_line) { 5195 if (src1 == src2) { 5196 Move_d(dst, src1); 5197 return; 5198 } 5199 5200 // Check if one of operands is NaN. 5201 CompareIsNanF64(src1, src2); 5202 BranchTrueF(out_of_line); 5203 5204 if (IsMipsArchVariant(kMips32r6)) { 5205 min_d(dst, src1, src2); 5206 } else { 5207 Label return_left, return_right, done; 5208 5209 CompareF64(OLT, src1, src2); 5210 BranchTrueShortF(&return_left); 5211 CompareF64(OLT, src2, src1); 5212 BranchTrueShortF(&return_right); 5213 5214 // Left equals right => check for -0. 5215 { 5216 BlockTrampolinePoolScope block_trampoline_pool(this); 5217 Mfhc1(t8, src1); 5218 Branch(&return_right, eq, t8, Operand(zero_reg)); 5219 Branch(&return_left); 5220 } 5221 5222 bind(&return_right); 5223 if (src2 != dst) { 5224 Move_d(dst, src2); 5225 } 5226 Branch(&done); 5227 5228 bind(&return_left); 5229 if (src1 != dst) { 5230 Move_d(dst, src1); 5231 } 5232 5233 bind(&done); 5234 } 5235 } 5236 5237 void TurboAssembler::Float64MinOutOfLine(DoubleRegister dst, 5238 DoubleRegister src1, 5239 DoubleRegister src2) { 5240 add_d(dst, src1, src2); 5241 } 5242 5243 static const int kRegisterPassedArguments = 4; 5244 5245 int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments, 5246 int num_double_arguments) { 5247 int stack_passed_words = 0; 5248 num_reg_arguments += 2 * num_double_arguments; 5249 5250 // Up to four simple arguments are passed in registers a0..a3. 5251 if (num_reg_arguments > kRegisterPassedArguments) { 5252 stack_passed_words += num_reg_arguments - kRegisterPassedArguments; 5253 } 5254 stack_passed_words += kCArgSlotCount; 5255 return stack_passed_words; 5256 } 5257 5258 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments, 5259 int num_double_arguments, 5260 Register scratch) { 5261 int frame_alignment = ActivationFrameAlignment(); 5262 5263 // Up to four simple arguments are passed in registers a0..a3. 5264 // Those four arguments must have reserved argument slots on the stack for 5265 // mips, even though those argument slots are not normally used. 5266 // Remaining arguments are pushed on the stack, above (higher address than) 5267 // the argument slots. 5268 int stack_passed_arguments = CalculateStackPassedWords( 5269 num_reg_arguments, num_double_arguments); 5270 if (frame_alignment > kPointerSize) { 5271 // Make stack end at alignment and make room for num_arguments - 4 words 5272 // and the original value of sp. 5273 mov(scratch, sp); 5274 Subu(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize)); 5275 DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); 5276 And(sp, sp, Operand(-frame_alignment)); 5277 sw(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize)); 5278 } else { 5279 Subu(sp, sp, Operand(stack_passed_arguments * kPointerSize)); 5280 } 5281 } 5282 5283 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments, 5284 Register scratch) { 5285 PrepareCallCFunction(num_reg_arguments, 0, scratch); 5286 } 5287 5288 void TurboAssembler::CallCFunction(ExternalReference function, 5289 int num_reg_arguments, 5290 int num_double_arguments) { 5291 // Linux/MIPS convention demands that register t9 contains 5292 // the address of the function being call in case of 5293 // Position independent code 5294 BlockTrampolinePoolScope block_trampoline_pool(this); 5295 li(t9, function); 5296 CallCFunctionHelper(t9, 0, num_reg_arguments, num_double_arguments); 5297 } 5298 5299 void TurboAssembler::CallCFunction(Register function, int num_reg_arguments, 5300 int num_double_arguments) { 5301 CallCFunctionHelper(function, 0, num_reg_arguments, num_double_arguments); 5302 } 5303 5304 void TurboAssembler::CallCFunction(ExternalReference function, 5305 int num_arguments) { 5306 CallCFunction(function, num_arguments, 0); 5307 } 5308 5309 void TurboAssembler::CallCFunction(Register function, int num_arguments) { 5310 CallCFunction(function, num_arguments, 0); 5311 } 5312 5313 void TurboAssembler::CallCFunctionHelper(Register function_base, 5314 int16_t function_offset, 5315 int num_reg_arguments, 5316 int num_double_arguments) { 5317 DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters); 5318 DCHECK(has_frame()); 5319 // Make sure that the stack is aligned before calling a C function unless 5320 // running in the simulator. The simulator has its own alignment check which 5321 // provides more information. 5322 // The argument stots are presumed to have been set up by 5323 // PrepareCallCFunction. The C function must be called via t9, for mips ABI. 5324 5325 #if V8_HOST_ARCH_MIPS 5326 if (emit_debug_code()) { 5327 int frame_alignment = base::OS::ActivationFrameAlignment(); 5328 int frame_alignment_mask = frame_alignment - 1; 5329 if (frame_alignment > kPointerSize) { 5330 DCHECK(base::bits::IsPowerOfTwo(frame_alignment)); 5331 Label alignment_as_expected; 5332 UseScratchRegisterScope temps(this); 5333 Register scratch = temps.Acquire(); 5334 And(scratch, sp, Operand(frame_alignment_mask)); 5335 Branch(&alignment_as_expected, eq, scratch, Operand(zero_reg)); 5336 // Don't use Check here, as it will call Runtime_Abort possibly 5337 // re-entering here. 5338 stop("Unexpected alignment in CallCFunction"); 5339 bind(&alignment_as_expected); 5340 } 5341 } 5342 #endif // V8_HOST_ARCH_MIPS 5343 5344 // Just call directly. The function called cannot cause a GC, or 5345 // allow preemption, so the return address in the link register 5346 // stays correct. 5347 5348 { 5349 BlockTrampolinePoolScope block_trampoline_pool(this); 5350 if (function_base != t9) { 5351 mov(t9, function_base); 5352 function_base = t9; 5353 } 5354 5355 if (function_offset != 0) { 5356 addiu(t9, t9, function_offset); 5357 function_offset = 0; 5358 } 5359 5360 Call(function_base, function_offset); 5361 } 5362 5363 int stack_passed_arguments = CalculateStackPassedWords( 5364 num_reg_arguments, num_double_arguments); 5365 5366 if (base::OS::ActivationFrameAlignment() > kPointerSize) { 5367 lw(sp, MemOperand(sp, stack_passed_arguments * kPointerSize)); 5368 } else { 5369 Addu(sp, sp, Operand(stack_passed_arguments * kPointerSize)); 5370 } 5371 } 5372 5373 5374 #undef BRANCH_ARGS_CHECK 5375 5376 void TurboAssembler::CheckPageFlag(Register object, Register scratch, int mask, 5377 Condition cc, Label* condition_met) { 5378 And(scratch, object, Operand(~kPageAlignmentMask)); 5379 lw(scratch, MemOperand(scratch, MemoryChunk::kFlagsOffset)); 5380 And(scratch, scratch, Operand(mask)); 5381 Branch(condition_met, cc, scratch, Operand(zero_reg)); 5382 } 5383 5384 Register GetRegisterThatIsNotOneOf(Register reg1, 5385 Register reg2, 5386 Register reg3, 5387 Register reg4, 5388 Register reg5, 5389 Register reg6) { 5390 RegList regs = 0; 5391 if (reg1.is_valid()) regs |= reg1.bit(); 5392 if (reg2.is_valid()) regs |= reg2.bit(); 5393 if (reg3.is_valid()) regs |= reg3.bit(); 5394 if (reg4.is_valid()) regs |= reg4.bit(); 5395 if (reg5.is_valid()) regs |= reg5.bit(); 5396 if (reg6.is_valid()) regs |= reg6.bit(); 5397 5398 const RegisterConfiguration* config = RegisterConfiguration::Default(); 5399 for (int i = 0; i < config->num_allocatable_general_registers(); ++i) { 5400 int code = config->GetAllocatableGeneralCode(i); 5401 Register candidate = Register::from_code(code); 5402 if (regs & candidate.bit()) continue; 5403 return candidate; 5404 } 5405 UNREACHABLE(); 5406 } 5407 5408 void TurboAssembler::ComputeCodeStartAddress(Register dst) { 5409 // This push on ra and the pop below together ensure that we restore the 5410 // register ra, which is needed while computing the code start address. 5411 push(ra); 5412 5413 // The nal instruction puts the address of the current instruction into 5414 // the return address (ra) register, which we can use later on. 5415 if (IsMipsArchVariant(kMips32r6)) { 5416 addiupc(ra, 1); 5417 } else { 5418 nal(); 5419 nop(); 5420 } 5421 int pc = pc_offset(); 5422 li(dst, pc); 5423 subu(dst, ra, dst); 5424 5425 pop(ra); // Restore ra 5426 } 5427 5428 void TurboAssembler::ResetSpeculationPoisonRegister() { 5429 li(kSpeculationPoisonRegister, -1); 5430 } 5431 5432 } // namespace internal 5433 } // namespace v8 5434 5435 #endif // V8_TARGET_ARCH_MIPS 5436