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