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
38 namespace v8 {
39 namespace internal {
40
IsZero(const Operand & rt)41 static 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
RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3) const49 int 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
PushCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3)65 int 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
PopCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3)82 int 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
LoadRoot(Register destination,RootIndex index)99 void TurboAssembler::LoadRoot(Register destination, RootIndex index) {
100 Ld(destination, MemOperand(s6, RootRegisterOffsetForRootIndex(index)));
101 }
102
LoadRoot(Register destination,RootIndex index,Condition cond,Register src1,const Operand & src2)103 void 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
PushCommonFrame(Register marker_reg)110 void 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
PushStandardFrame(Register function_reg)120 void 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.
RecordWriteField(Register object,int offset,Register value,Register dst,RAStatus ra_status,SaveFPRegsMode save_fp,RememberedSetAction remembered_set_action,SmiCheck smi_check)135 void 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
MaybeSaveRegisters(RegList registers)179 void TurboAssembler::MaybeSaveRegisters(RegList registers) {
180 if (registers.is_empty()) return;
181 MultiPush(registers);
182 }
183
MaybeRestoreRegisters(RegList registers)184 void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
185 if (registers.is_empty()) return;
186 MultiPop(registers);
187 }
188
CallEphemeronKeyBarrier(Register object,Register slot_address,SaveFPRegsMode fp_mode)189 void 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
CallRecordWriteStubSaveRegisters(Register object,Register slot_address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode,StubCallMode mode)213 void 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
CallRecordWriteStub(Register object,Register slot_address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode,StubCallMode mode)237 void 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.
271 void 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
338 void 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
355 void 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
372 void 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
398 void 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
426 void 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
439 void 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
462 void 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
485 void 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
508 void 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
531 void 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
544 void 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
557 void 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
570 void 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
583 void 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
596 void 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
619 void 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
642 void 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
665 void 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
678 void 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
706 void 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
719 void 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
742 void 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
755 void 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
778 void 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
806 void 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
834 void 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
851 void 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
868 void 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
885 void 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
898 void TurboAssembler::Neg(Register rs, const Operand& rt) {
899 dsubu(rs, zero_reg, rt.rm());
900 }
901
902 void 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
920 void 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
944 void 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
959 void 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
974 void TurboAssembler::Sge(Register rd, Register rs, const Operand& rt) {
975 Slt(rd, rs, rt);
976 xori(rd, rd, 1);
977 }
978
979 void TurboAssembler::Sgeu(Register rd, Register rs, const Operand& rt) {
980 Sltu(rd, rs, rt);
981 xori(rd, rd, 1);
982 }
983
984 void 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
998 void 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
1012 void 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
1024 void 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
1038 void MacroAssembler::Pref(int32_t hint, const MemOperand& rs) {
1039 pref(hint, rs);
1040 }
1041
1042 void 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
1055 void 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
1071 void 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
1082 void 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
1096 void 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
1112 void 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
1125 void 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
1149 void 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
1159 void 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
1176 void 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
1210 void 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
1244 void 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
1273 void 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.
1300 void 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
1308 void 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.
1325 void 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
1332 void 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
1343 void 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
1354 void 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
1366 void 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
1378 void TurboAssembler::Lb(Register rd, const MemOperand& rs) {
1379 MemOperand source = rs;
1380 AdjustBaseAndOffset(&source);
1381 lb(rd, source);
1382 }
1383
1384 void TurboAssembler::Lbu(Register rd, const MemOperand& rs) {
1385 MemOperand source = rs;
1386 AdjustBaseAndOffset(&source);
1387 lbu(rd, source);
1388 }
1389
1390 void TurboAssembler::Sb(Register rd, const MemOperand& rs) {
1391 MemOperand source = rs;
1392 AdjustBaseAndOffset(&source);
1393 sb(rd, source);
1394 }
1395
1396 void TurboAssembler::Lh(Register rd, const MemOperand& rs) {
1397 MemOperand source = rs;
1398 AdjustBaseAndOffset(&source);
1399 lh(rd, source);
1400 }
1401
1402 void TurboAssembler::Lhu(Register rd, const MemOperand& rs) {
1403 MemOperand source = rs;
1404 AdjustBaseAndOffset(&source);
1405 lhu(rd, source);
1406 }
1407
1408 void TurboAssembler::Sh(Register rd, const MemOperand& rs) {
1409 MemOperand source = rs;
1410 AdjustBaseAndOffset(&source);
1411 sh(rd, source);
1412 }
1413
1414 void TurboAssembler::Lw(Register rd, const MemOperand& rs) {
1415 MemOperand source = rs;
1416 AdjustBaseAndOffset(&source);
1417 lw(rd, source);
1418 }
1419
1420 void TurboAssembler::Lwu(Register rd, const MemOperand& rs) {
1421 MemOperand source = rs;
1422 AdjustBaseAndOffset(&source);
1423 lwu(rd, source);
1424 }
1425
1426 void TurboAssembler::Sw(Register rd, const MemOperand& rs) {
1427 MemOperand source = rs;
1428 AdjustBaseAndOffset(&source);
1429 sw(rd, source);
1430 }
1431
1432 void TurboAssembler::Ld(Register rd, const MemOperand& rs) {
1433 MemOperand source = rs;
1434 AdjustBaseAndOffset(&source);
1435 ld(rd, source);
1436 }
1437
1438 void TurboAssembler::Sd(Register rd, const MemOperand& rs) {
1439 MemOperand source = rs;
1440 AdjustBaseAndOffset(&source);
1441 sd(rd, source);
1442 }
1443
1444 void TurboAssembler::Lwc1(FPURegister fd, const MemOperand& src) {
1445 MemOperand tmp = src;
1446 AdjustBaseAndOffset(&tmp);
1447 lwc1(fd, tmp);
1448 }
1449
1450 void TurboAssembler::Swc1(FPURegister fs, const MemOperand& src) {
1451 MemOperand tmp = src;
1452 AdjustBaseAndOffset(&tmp);
1453 swc1(fs, tmp);
1454 }
1455
1456 void TurboAssembler::Ldc1(FPURegister fd, const MemOperand& src) {
1457 MemOperand tmp = src;
1458 AdjustBaseAndOffset(&tmp);
1459 ldc1(fd, tmp);
1460 }
1461
1462 void TurboAssembler::Sdc1(FPURegister fs, const MemOperand& src) {
1463 MemOperand tmp = src;
1464 AdjustBaseAndOffset(&tmp);
1465 sdc1(fs, tmp);
1466 }
1467
1468 void 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
1482 void 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
1496 void 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
1510 void 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
1524 void 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
1535 void 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
1546 void TurboAssembler::li(Register dst, const StringConstantBase* string,
1547 LiFlags mode) {
1548 li(dst, Operand::EmbeddedStringConstant(string), mode);
1549 }
1550
1551 static 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
1560 void 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
1573 static 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
1584 int 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.
1679 void 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
1857 void 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
1919 void 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
1932 void 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
1944 void 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
1957 void 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
1969 void 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
1982 void 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
1994 void 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
2001 void 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
2014 void 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
2022 void 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
2035 void 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
2057 void 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
2069 void 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
2094 void 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
2119 void 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
2126 void 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
2139 void 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
2146 void 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
2174 void 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
2181 void 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
2193 void 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
2200 void 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
2228 void MacroAssembler::Round_l_d(FPURegister fd, FPURegister fs) {
2229 round_l_d(fd, fs);
2230 }
2231
2232 void MacroAssembler::Floor_l_d(FPURegister fd, FPURegister fs) {
2233 floor_l_d(fd, fs);
2234 }
2235
2236 void MacroAssembler::Ceil_l_d(FPURegister fd, FPURegister fs) {
2237 ceil_l_d(fd, fs);
2238 }
2239
2240 void MacroAssembler::Trunc_l_d(FPURegister fd, FPURegister fs) {
2241 trunc_l_d(fd, fs);
2242 }
2243
2244 void 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
2260 void 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
2267 void 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
2274 void 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
2281 void 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
2288 void MacroAssembler::Trunc_w_d(FPURegister fd, FPURegister fs) {
2289 trunc_w_d(fd, fs);
2290 }
2291
2292 void MacroAssembler::Round_w_d(FPURegister fd, FPURegister fs) {
2293 round_w_d(fd, fs);
2294 }
2295
2296 void MacroAssembler::Floor_w_d(FPURegister fd, FPURegister fs) {
2297 floor_w_d(fd, fs);
2298 }
2299
2300 void MacroAssembler::Ceil_w_d(FPURegister fd, FPURegister fs) {
2301 ceil_w_d(fd, fs);
2302 }
2303
2304 void 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
2340 void 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
2375 void 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
2430 void 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
2489 template <typename RoundFunc>
2490 void 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
2522 void 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
2529 void 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
2536 void 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
2543 void 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
2550 template <typename RoundFunc>
2551 void 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
2586 void 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
2593 void 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
2600 void 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
2607 void 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
2614 void 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
2640 void 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
2684 void 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
2698 void 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
2713 void 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
2738 void 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
2758 void 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
2774 void 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
2790 void 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
2797 void 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
2804 void 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
2811 void 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
2818 void 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
2829 void TurboAssembler::CompareIsNanF(SecondaryField sizeField, FPURegister cmp1,
2830 FPURegister cmp2) {
2831 CompareF(sizeField, UN, cmp1, cmp2);
2832 }
2833
2834 void 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
2845 void 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
2856 void 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
2869 void 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
2882 void 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
2904 void 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
2961 void 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
2970 void 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
2977 void 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
3010 void 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
3021 void 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
3032 void 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
3124 void 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
3133 void 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
3142 void 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
3151 void 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
3160 void TurboAssembler::Movt(Register rd, Register rs, uint16_t cc) {
3161 movt(rd, rs, cc);
3162 }
3163
3164 void TurboAssembler::Movf(Register rd, Register rs, uint16_t cc) {
3165 movf(rd, rs, cc);
3166 }
3167
3168 void TurboAssembler::Clz(Register rd, Register rs) { clz(rd, rs); }
3169
3170 void TurboAssembler::Dclz(Register rd, Register rs) { dclz(rd, rs); }
3171
3172 void 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
3198 void 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
3224 void 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
3276 void 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
3306 void 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
3326 void 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
3364 void TurboAssembler::Branch(int32_t offset, BranchDelaySlot bdslot) {
3365 DCHECK_EQ(kArchVariant, kMips64r6 ? is_int26(offset) : is_int16(offset));
3366 BranchShort(offset, bdslot);
3367 }
3368
3369 void 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
3376 void 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
3392 void 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
3423 void 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
3431 void 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
3441 void 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
3447 void 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
3457 void 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
3465 int32_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
3474 Register 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
3487 bool 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
3494 bool 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
3502 bool 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
3715 bool 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
3852 bool 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
3875 void 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
3880 void TurboAssembler::BranchShort(Label* L, Condition cond, Register rs,
3881 const Operand& rt, BranchDelaySlot bdslot) {
3882 BranchShortCheck(0, L, cond, rs, rt, bdslot);
3883 }
3884
3885 void TurboAssembler::BranchAndLink(int32_t offset, BranchDelaySlot bdslot) {
3886 BranchAndLinkShort(offset, bdslot);
3887 }
3888
3889 void 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
3896 void 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
3912 void 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
3935 void 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
3945 void 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
3951 void 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
3962 void 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
3970 bool 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.
4112 bool 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
4202 bool 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
4226 void 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
4236 void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
4237 Ld(destination, MemOperand(kRootRegister, offset));
4238 }
4239
4240 void 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
4249 void 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
4273 void 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
4290 void 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
4296 void 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
4323 void 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.
4329 void 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
4354 void 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
4368 void 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
4375 void 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
4403 void 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 }
4415 void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin,
4416 Register destination) {
4417 Ld(destination, EntryFromBuiltinAsOperand(builtin));
4418 }
4419 MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
4420 DCHECK(root_array_available());
4421 return MemOperand(kRootRegister,
4422 IsolateData::BuiltinEntrySlotOffset(builtin));
4423 }
4424
4425 void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
4426 ASM_CODE_COMMENT(this);
4427 LoadEntryFromBuiltinIndex(builtin_index);
4428 Call(builtin_index);
4429 }
4430 void TurboAssembler::CallBuiltin(Builtin builtin) {
4431 RecordCommentForOffHeapTrampoline(builtin);
4432 Call(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET);
4433 RecordComment("]");
4434 }
4435
4436 void 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
4456 void 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
4498 void TurboAssembler::Ret(Condition cond, Register rs, const Operand& rt,
4499 BranchDelaySlot bd) {
4500 Jump(ra, cond, rs, rt, bd);
4501 }
4502
4503 void 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
4527 void 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
4546 void 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
4566 void 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
4590 void 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
4606 void 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
4622 void 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
4638 void 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
4657 void 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
4669 void TurboAssembler::Call(Label* target) { BranchAndLink(target); }
4670
4671 void TurboAssembler::LoadAddress(Register dst, Label* target) {
4672 uint64_t address = jump_address(target);
4673 li(dst, address);
4674 }
4675
4676 void TurboAssembler::Push(Smi smi) {
4677 UseScratchRegisterScope temps(this);
4678 Register scratch = temps.Acquire();
4679 li(scratch, Operand(smi));
4680 push(scratch);
4681 }
4682
4683 void TurboAssembler::Push(Handle<HeapObject> handle) {
4684 UseScratchRegisterScope temps(this);
4685 Register scratch = temps.Acquire();
4686 li(scratch, Operand(handle));
4687 push(scratch);
4688 }
4689
4690 void 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
4720 void 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
4737 void 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
4750 void TurboAssembler::FPUCanonicalizeNaN(const DoubleRegister dst,
4751 const DoubleRegister src) {
4752 sub_d(dst, src, kDoubleRegZero);
4753 }
4754
4755 void 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
4767 void 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
4779 void 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
4791 void 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
4803 void 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
4829 void 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
4845 void 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
4863 void 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
4935 void 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
4977 void 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
5018 void 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
5040 void 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
5061 void MacroAssembler::GetObjectType(Register object, Register map,
5062 Register type_reg) {
5063 LoadMap(map, object);
5064 Lhu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
5065 }
5066
5067 void 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
5077 void 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
5108 void 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
5139 void 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
5169 void 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
5190 void 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
5200 void 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
5209 void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
5210 li(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
5211 Jump(kOffHeapTrampolineRegister);
5212 }
5213
5214 void 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
5221 void 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
5237 void 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
5256 void TurboAssembler::Trap() { stop(); }
5257 void TurboAssembler::DebugBreak() { stop(); }
5258
5259 void TurboAssembler::Assert(Condition cc, AbortReason reason, Register rs,
5260 Operand rt) {
5261 if (FLAG_debug_code) Check(cc, reason, rs, rt);
5262 }
5263
5264 void 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
5273 void 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
5324 void TurboAssembler::LoadMap(Register destination, Register object) {
5325 Ld(destination, FieldMemOperand(object, HeapObject::kMapOffset));
5326 }
5327
5328 void 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
5335 void 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
5342 void TurboAssembler::Prologue() { PushStandardFrame(a1); }
5343
5344 void 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
5358 void 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
5365 void 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
5444 void 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
5499 int 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
5515 void 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
5537 void 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
5547 void 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
5556 void 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
5565 void 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
5576 void 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
5587 void 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
5603 void 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
5620 void 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
5638 void 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
5652 void 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
5679 void 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
5694 void 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
5740 void TurboAssembler::Float32MaxOutOfLine(FPURegister dst, FPURegister src1,
5741 FPURegister src2) {
5742 add_s(dst, src1, src2);
5743 }
5744
5745 void 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
5791 void TurboAssembler::Float32MinOutOfLine(FPURegister dst, FPURegister src1,
5792 FPURegister src2) {
5793 add_s(dst, src1, src2);
5794 }
5795
5796 void 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
5841 void TurboAssembler::Float64MaxOutOfLine(FPURegister dst, FPURegister src1,
5842 FPURegister src2) {
5843 add_d(dst, src1, src2);
5844 }
5845
5846 void 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
5891 void TurboAssembler::Float64MinOutOfLine(FPURegister dst, FPURegister src1,
5892 FPURegister src2) {
5893 add_d(dst, src1, src2);
5894 }
5895
5896 static const int kRegisterPassedArguments = 8;
5897
5898 int 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
5912 void 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
5940 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
5941 Register scratch) {
5942 PrepareCallCFunction(num_reg_arguments, 0, scratch);
5943 }
5944
5945 void 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
5954 void 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
5960 void TurboAssembler::CallCFunction(ExternalReference function,
5961 int num_arguments) {
5962 CallCFunction(function, num_arguments, 0);
5963 }
5964
5965 void TurboAssembler::CallCFunction(Register function, int num_arguments) {
5966 CallCFunction(function, num_arguments, 0);
5967 }
5968
5969 void 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
6064 void 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
6073 Register 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
6088 void 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
6108 void 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
6121 void 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
6166 void TurboAssembler::CallCodeObject(Register code_object) {
6167 ASM_CODE_COMMENT(this);
6168 LoadCodeObjectEntry(code_object, code_object);
6169 Call(code_object);
6170 }
6171
6172 void 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