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