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