• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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_LOONG64
8 
9 #include "src/base/bits.h"
10 #include "src/base/division-by-constant.h"
11 #include "src/codegen/assembler-inl.h"
12 #include "src/codegen/callable.h"
13 #include "src/codegen/code-factory.h"
14 #include "src/codegen/external-reference-table.h"
15 #include "src/codegen/interface-descriptors-inl.h"
16 #include "src/codegen/macro-assembler.h"
17 #include "src/codegen/register-configuration.h"
18 #include "src/debug/debug.h"
19 #include "src/deoptimizer/deoptimizer.h"
20 #include "src/execution/frames-inl.h"
21 #include "src/heap/memory-chunk.h"
22 #include "src/init/bootstrapper.h"
23 #include "src/logging/counters.h"
24 #include "src/objects/heap-number.h"
25 #include "src/runtime/runtime.h"
26 #include "src/snapshot/snapshot.h"
27 
28 #if V8_ENABLE_WEBASSEMBLY
29 #include "src/wasm/wasm-code-manager.h"
30 #endif  // V8_ENABLE_WEBASSEMBLY
31 
32 // Satisfy cpplint check, but don't include platform-specific header. It is
33 // included recursively via macro-assembler.h.
34 #if 0
35 #include "src/codegen/loong64/macro-assembler-loong64.h"
36 #endif
37 
38 namespace v8 {
39 namespace internal {
40 
IsZero(const Operand & rk)41 static inline bool IsZero(const Operand& rk) {
42   if (rk.is_reg()) {
43     return rk.rm() == zero_reg;
44   } else {
45     return rk.immediate() == 0;
46   }
47 }
48 
RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3) const49 int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
50                                                     Register exclusion1,
51                                                     Register exclusion2,
52                                                     Register exclusion3) const {
53   int bytes = 0;
54 
55   RegList exclusions = {exclusion1, exclusion2, exclusion3};
56   RegList list = kJSCallerSaved - exclusions;
57   bytes += list.Count() * kPointerSize;
58 
59   if (fp_mode == SaveFPRegsMode::kSave) {
60     bytes += kCallerSavedFPU.Count() * kDoubleSize;
61   }
62 
63   return bytes;
64 }
65 
PushCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3)66 int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
67                                     Register exclusion2, Register exclusion3) {
68   ASM_CODE_COMMENT(this);
69   int bytes = 0;
70 
71   RegList exclusions = {exclusion1, exclusion2, exclusion3};
72   RegList list = kJSCallerSaved - exclusions;
73   MultiPush(list);
74   bytes += list.Count() * kPointerSize;
75 
76   if (fp_mode == SaveFPRegsMode::kSave) {
77     MultiPushFPU(kCallerSavedFPU);
78     bytes += kCallerSavedFPU.Count() * kDoubleSize;
79   }
80 
81   return bytes;
82 }
83 
PopCallerSaved(SaveFPRegsMode fp_mode,Register exclusion1,Register exclusion2,Register exclusion3)84 int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
85                                    Register exclusion2, Register exclusion3) {
86   ASM_CODE_COMMENT(this);
87   int bytes = 0;
88   if (fp_mode == SaveFPRegsMode::kSave) {
89     MultiPopFPU(kCallerSavedFPU);
90     bytes += kCallerSavedFPU.Count() * kDoubleSize;
91   }
92 
93   RegList exclusions = {exclusion1, exclusion2, exclusion3};
94   RegList list = kJSCallerSaved - exclusions;
95   MultiPop(list);
96   bytes += list.Count() * kPointerSize;
97 
98   return bytes;
99 }
100 
LoadRoot(Register destination,RootIndex index)101 void TurboAssembler::LoadRoot(Register destination, RootIndex index) {
102   Ld_d(destination, MemOperand(s6, RootRegisterOffsetForRootIndex(index)));
103 }
104 
PushCommonFrame(Register marker_reg)105 void TurboAssembler::PushCommonFrame(Register marker_reg) {
106   if (marker_reg.is_valid()) {
107     Push(ra, fp, marker_reg);
108     Add_d(fp, sp, Operand(kPointerSize));
109   } else {
110     Push(ra, fp);
111     mov(fp, sp);
112   }
113 }
114 
PushStandardFrame(Register function_reg)115 void TurboAssembler::PushStandardFrame(Register function_reg) {
116   int offset = -StandardFrameConstants::kContextOffset;
117   if (function_reg.is_valid()) {
118     Push(ra, fp, cp, function_reg, kJavaScriptCallArgCountRegister);
119     offset += 2 * kPointerSize;
120   } else {
121     Push(ra, fp, cp, kJavaScriptCallArgCountRegister);
122     offset += kPointerSize;
123   }
124   Add_d(fp, sp, Operand(offset));
125 }
126 
127 // Clobbers object, dst, value, and ra, if (ra_status == kRAHasBeenSaved)
128 // The register 'object' contains a heap object pointer.  The heap object
129 // tag is shifted away.
RecordWriteField(Register object,int offset,Register value,RAStatus ra_status,SaveFPRegsMode save_fp,RememberedSetAction remembered_set_action,SmiCheck smi_check)130 void MacroAssembler::RecordWriteField(Register object, int offset,
131                                       Register value, RAStatus ra_status,
132                                       SaveFPRegsMode save_fp,
133                                       RememberedSetAction remembered_set_action,
134                                       SmiCheck smi_check) {
135   ASM_CODE_COMMENT(this);
136   // First, check if a write barrier is even needed. The tests below
137   // catch stores of Smis.
138   Label done;
139 
140   // Skip barrier if writing a smi.
141   if (smi_check == SmiCheck::kInline) {
142     JumpIfSmi(value, &done);
143   }
144 
145   // Although the object register is tagged, the offset is relative to the start
146   // of the object, so offset must be a multiple of kPointerSize.
147   DCHECK(IsAligned(offset, kPointerSize));
148 
149   if (FLAG_debug_code) {
150     Label ok;
151     BlockTrampolinePoolScope block_trampoline_pool(this);
152     UseScratchRegisterScope temps(this);
153     Register scratch = temps.Acquire();
154     Add_d(scratch, object, offset - kHeapObjectTag);
155     And(scratch, scratch, Operand(kPointerSize - 1));
156     Branch(&ok, eq, scratch, Operand(zero_reg));
157     Abort(AbortReason::kUnalignedCellInWriteBarrier);
158     bind(&ok);
159   }
160 
161   RecordWrite(object, Operand(offset - kHeapObjectTag), value, ra_status,
162               save_fp, remembered_set_action, SmiCheck::kOmit);
163 
164   bind(&done);
165 }
166 
MaybeSaveRegisters(RegList registers)167 void TurboAssembler::MaybeSaveRegisters(RegList registers) {
168   if (registers.is_empty()) return;
169   MultiPush(registers);
170 }
171 
MaybeRestoreRegisters(RegList registers)172 void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
173   if (registers.is_empty()) return;
174   MultiPop(registers);
175 }
176 
CallEphemeronKeyBarrier(Register object,Operand offset,SaveFPRegsMode fp_mode)177 void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
178                                              SaveFPRegsMode fp_mode) {
179   ASM_CODE_COMMENT(this);
180   RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
181   MaybeSaveRegisters(registers);
182 
183   Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
184   Register slot_address_parameter =
185       WriteBarrierDescriptor::SlotAddressRegister();
186 
187   MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
188 
189   Call(isolate()->builtins()->code_handle(
190            Builtins::GetEphemeronKeyBarrierStub(fp_mode)),
191        RelocInfo::CODE_TARGET);
192   MaybeRestoreRegisters(registers);
193 }
194 
CallRecordWriteStubSaveRegisters(Register object,Operand offset,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode,StubCallMode mode)195 void TurboAssembler::CallRecordWriteStubSaveRegisters(
196     Register object, Operand offset, RememberedSetAction remembered_set_action,
197     SaveFPRegsMode fp_mode, StubCallMode mode) {
198   ASM_CODE_COMMENT(this);
199   RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
200   MaybeSaveRegisters(registers);
201 
202   Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
203   Register slot_address_parameter =
204       WriteBarrierDescriptor::SlotAddressRegister();
205 
206   MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
207 
208   CallRecordWriteStub(object_parameter, slot_address_parameter,
209                       remembered_set_action, fp_mode, mode);
210 
211   MaybeRestoreRegisters(registers);
212 }
213 
CallRecordWriteStub(Register object,Register slot_address,RememberedSetAction remembered_set_action,SaveFPRegsMode fp_mode,StubCallMode mode)214 void TurboAssembler::CallRecordWriteStub(
215     Register object, Register slot_address,
216     RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
217     StubCallMode mode) {
218   // Use CallRecordWriteStubSaveRegisters if the object and slot registers
219   // need to be caller saved.
220   DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
221   DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
222 #if V8_ENABLE_WEBASSEMBLY
223   if (mode == StubCallMode::kCallWasmRuntimeStub) {
224     auto wasm_target =
225         wasm::WasmCode::GetRecordWriteStub(remembered_set_action, fp_mode);
226     Call(wasm_target, RelocInfo::WASM_STUB_CALL);
227 #else
228   if (false) {
229 #endif
230   } else {
231     auto builtin = Builtins::GetRecordWriteStub(remembered_set_action, fp_mode);
232     if (options().inline_offheap_trampolines) {
233       // Inline the trampoline.
234       RecordCommentForOffHeapTrampoline(builtin);
235       UseScratchRegisterScope temps(this);
236       Register scratch = temps.Acquire();
237       li(scratch, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
238       Call(scratch);
239       RecordComment("]");
240     } else {
241       Handle<Code> code_target = isolate()->builtins()->code_handle(builtin);
242       Call(code_target, RelocInfo::CODE_TARGET);
243     }
244   }
245 }
246 
247 void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
248                                        Register object, Operand offset) {
249   ASM_CODE_COMMENT(this);
250   DCHECK_NE(dst_object, dst_slot);
251   // If `offset` is a register, it cannot overlap with `object`.
252   DCHECK_IMPLIES(!offset.IsImmediate(), offset.rm() != object);
253 
254   // If the slot register does not overlap with the object register, we can
255   // overwrite it.
256   if (dst_slot != object) {
257     Add_d(dst_slot, object, offset);
258     mov(dst_object, object);
259     return;
260   }
261 
262   DCHECK_EQ(dst_slot, object);
263 
264   // If the destination object register does not overlap with the offset
265   // register, we can overwrite it.
266   if (offset.IsImmediate() || (offset.rm() != dst_object)) {
267     mov(dst_object, dst_slot);
268     Add_d(dst_slot, dst_slot, offset);
269     return;
270   }
271 
272   DCHECK_EQ(dst_object, offset.rm());
273 
274   // We only have `dst_slot` and `dst_object` left as distinct registers so we
275   // have to swap them. We write this as a add+sub sequence to avoid using a
276   // scratch register.
277   Add_d(dst_slot, dst_slot, dst_object);
278   Sub_d(dst_object, dst_slot, dst_object);
279 }
280 
281 // If lr_status is kLRHasBeenSaved, lr will be clobbered.
282 // TODO(LOONG_dev): LOONG64 Check this comment
283 // Clobbers object, address, value, and ra, if (ra_status == kRAHasBeenSaved)
284 // The register 'object' contains a heap object pointer.  The heap object
285 // tag is shifted away.
286 void MacroAssembler::RecordWrite(Register object, Operand offset,
287                                  Register value, RAStatus ra_status,
288                                  SaveFPRegsMode fp_mode,
289                                  RememberedSetAction remembered_set_action,
290                                  SmiCheck smi_check) {
291   DCHECK(!AreAliased(object, value));
292 
293   if (FLAG_debug_code) {
294     UseScratchRegisterScope temps(this);
295     Register scratch = temps.Acquire();
296     Add_d(scratch, object, offset);
297     Ld_d(scratch, MemOperand(scratch, 0));
298     Assert(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite, scratch,
299            Operand(value));
300   }
301 
302   if ((remembered_set_action == RememberedSetAction::kOmit &&
303        !FLAG_incremental_marking) ||
304       FLAG_disable_write_barriers) {
305     return;
306   }
307 
308   // First, check if a write barrier is even needed. The tests below
309   // catch stores of smis and stores into the young generation.
310   Label done;
311 
312   if (smi_check == SmiCheck::kInline) {
313     DCHECK_EQ(0, kSmiTag);
314     JumpIfSmi(value, &done);
315   }
316 
317   CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, eq,
318                 &done);
319 
320   CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, eq,
321                 &done);
322 
323   // Record the actual write.
324   if (ra_status == kRAHasNotBeenSaved) {
325     Push(ra);
326   }
327 
328   Register slot_address = WriteBarrierDescriptor::SlotAddressRegister();
329   DCHECK(!AreAliased(object, slot_address, value));
330   DCHECK(offset.IsImmediate());
331   Add_d(slot_address, object, offset);
332   CallRecordWriteStub(object, slot_address, remembered_set_action, fp_mode);
333   if (ra_status == kRAHasNotBeenSaved) {
334     Pop(ra);
335   }
336 
337   bind(&done);
338 }
339 
340 // ---------------------------------------------------------------------------
341 // Instruction macros.
342 
343 void TurboAssembler::Add_w(Register rd, Register rj, const Operand& rk) {
344   if (rk.is_reg()) {
345     add_w(rd, rj, rk.rm());
346   } else {
347     if (is_int12(rk.immediate()) && !MustUseReg(rk.rmode())) {
348       addi_w(rd, rj, static_cast<int32_t>(rk.immediate()));
349     } else {
350       // li handles the relocation.
351       UseScratchRegisterScope temps(this);
352       Register scratch = temps.Acquire();
353       DCHECK(rj != scratch);
354       li(scratch, rk);
355       add_w(rd, rj, scratch);
356     }
357   }
358 }
359 
360 void TurboAssembler::Add_d(Register rd, Register rj, const Operand& rk) {
361   if (rk.is_reg()) {
362     add_d(rd, rj, rk.rm());
363   } else {
364     if (is_int12(rk.immediate()) && !MustUseReg(rk.rmode())) {
365       addi_d(rd, rj, static_cast<int32_t>(rk.immediate()));
366     } else {
367       // li handles the relocation.
368       UseScratchRegisterScope temps(this);
369       Register scratch = temps.Acquire();
370       DCHECK(rj != scratch);
371       li(scratch, rk);
372       add_d(rd, rj, scratch);
373     }
374   }
375 }
376 
377 void TurboAssembler::Sub_w(Register rd, Register rj, const Operand& rk) {
378   if (rk.is_reg()) {
379     sub_w(rd, rj, rk.rm());
380   } else {
381     DCHECK(is_int32(rk.immediate()));
382     if (is_int12(-rk.immediate()) && !MustUseReg(rk.rmode())) {
383       // No subi_w instr, use addi_w(x, y, -imm).
384       addi_w(rd, rj, static_cast<int32_t>(-rk.immediate()));
385     } else {
386       UseScratchRegisterScope temps(this);
387       Register scratch = temps.Acquire();
388       DCHECK(rj != scratch);
389       if (-rk.immediate() >> 12 == 0 && !MustUseReg(rk.rmode())) {
390         // Use load -imm and addu when loading -imm generates one instruction.
391         li(scratch, -rk.immediate());
392         add_w(rd, rj, scratch);
393       } else {
394         // li handles the relocation.
395         li(scratch, rk);
396         sub_w(rd, rj, scratch);
397       }
398     }
399   }
400 }
401 
402 void TurboAssembler::Sub_d(Register rd, Register rj, const Operand& rk) {
403   if (rk.is_reg()) {
404     sub_d(rd, rj, rk.rm());
405   } else if (is_int12(-rk.immediate()) && !MustUseReg(rk.rmode())) {
406     // No subi_d instr, use addi_d(x, y, -imm).
407     addi_d(rd, rj, static_cast<int32_t>(-rk.immediate()));
408   } else {
409     DCHECK(rj != t7);
410     int li_count = InstrCountForLi64Bit(rk.immediate());
411     int li_neg_count = InstrCountForLi64Bit(-rk.immediate());
412     if (li_neg_count < li_count && !MustUseReg(rk.rmode())) {
413       // Use load -imm and add_d when loading -imm generates one instruction.
414       DCHECK(rk.immediate() != std::numeric_limits<int32_t>::min());
415       UseScratchRegisterScope temps(this);
416       Register scratch = temps.Acquire();
417       li(scratch, Operand(-rk.immediate()));
418       add_d(rd, rj, scratch);
419     } else {
420       // li handles the relocation.
421       UseScratchRegisterScope temps(this);
422       Register scratch = temps.Acquire();
423       li(scratch, rk);
424       sub_d(rd, rj, scratch);
425     }
426   }
427 }
428 
429 void TurboAssembler::Mul_w(Register rd, Register rj, const Operand& rk) {
430   if (rk.is_reg()) {
431     mul_w(rd, rj, rk.rm());
432   } else {
433     // li handles the relocation.
434     UseScratchRegisterScope temps(this);
435     Register scratch = temps.Acquire();
436     DCHECK(rj != scratch);
437     li(scratch, rk);
438     mul_w(rd, rj, scratch);
439   }
440 }
441 
442 void TurboAssembler::Mulh_w(Register rd, Register rj, const Operand& rk) {
443   if (rk.is_reg()) {
444     mulh_w(rd, rj, rk.rm());
445   } else {
446     // li handles the relocation.
447     UseScratchRegisterScope temps(this);
448     Register scratch = temps.Acquire();
449     DCHECK(rj != scratch);
450     li(scratch, rk);
451     mulh_w(rd, rj, scratch);
452   }
453 }
454 
455 void TurboAssembler::Mulh_wu(Register rd, Register rj, const Operand& rk) {
456   if (rk.is_reg()) {
457     mulh_wu(rd, rj, rk.rm());
458   } else {
459     // li handles the relocation.
460     UseScratchRegisterScope temps(this);
461     Register scratch = temps.Acquire();
462     DCHECK(rj != scratch);
463     li(scratch, rk);
464     mulh_wu(rd, rj, scratch);
465   }
466 }
467 
468 void TurboAssembler::Mul_d(Register rd, Register rj, const Operand& rk) {
469   if (rk.is_reg()) {
470     mul_d(rd, rj, rk.rm());
471   } else {
472     // li handles the relocation.
473     UseScratchRegisterScope temps(this);
474     Register scratch = temps.Acquire();
475     DCHECK(rj != scratch);
476     li(scratch, rk);
477     mul_d(rd, rj, scratch);
478   }
479 }
480 
481 void TurboAssembler::Mulh_d(Register rd, Register rj, const Operand& rk) {
482   if (rk.is_reg()) {
483     mulh_d(rd, rj, rk.rm());
484   } else {
485     // li handles the relocation.
486     UseScratchRegisterScope temps(this);
487     Register scratch = temps.Acquire();
488     DCHECK(rj != scratch);
489     li(scratch, rk);
490     mulh_d(rd, rj, scratch);
491   }
492 }
493 
494 void TurboAssembler::Div_w(Register rd, Register rj, const Operand& rk) {
495   if (rk.is_reg()) {
496     div_w(rd, rj, rk.rm());
497   } else {
498     // li handles the relocation.
499     UseScratchRegisterScope temps(this);
500     Register scratch = temps.Acquire();
501     DCHECK(rj != scratch);
502     li(scratch, rk);
503     div_w(rd, rj, scratch);
504   }
505 }
506 
507 void TurboAssembler::Mod_w(Register rd, Register rj, const Operand& rk) {
508   if (rk.is_reg()) {
509     mod_w(rd, rj, rk.rm());
510   } else {
511     // li handles the relocation.
512     UseScratchRegisterScope temps(this);
513     Register scratch = temps.Acquire();
514     DCHECK(rj != scratch);
515     li(scratch, rk);
516     mod_w(rd, rj, scratch);
517   }
518 }
519 
520 void TurboAssembler::Mod_wu(Register rd, Register rj, const Operand& rk) {
521   if (rk.is_reg()) {
522     mod_wu(rd, rj, rk.rm());
523   } else {
524     // li handles the relocation.
525     UseScratchRegisterScope temps(this);
526     Register scratch = temps.Acquire();
527     DCHECK(rj != scratch);
528     li(scratch, rk);
529     mod_wu(rd, rj, scratch);
530   }
531 }
532 
533 void TurboAssembler::Div_d(Register rd, Register rj, const Operand& rk) {
534   if (rk.is_reg()) {
535     div_d(rd, rj, rk.rm());
536   } else {
537     // li handles the relocation.
538     UseScratchRegisterScope temps(this);
539     Register scratch = temps.Acquire();
540     DCHECK(rj != scratch);
541     li(scratch, rk);
542     div_d(rd, rj, scratch);
543   }
544 }
545 
546 void TurboAssembler::Div_wu(Register rd, Register rj, const Operand& rk) {
547   if (rk.is_reg()) {
548     div_wu(rd, rj, rk.rm());
549   } else {
550     // li handles the relocation.
551     UseScratchRegisterScope temps(this);
552     Register scratch = temps.Acquire();
553     DCHECK(rj != scratch);
554     li(scratch, rk);
555     div_wu(rd, rj, scratch);
556   }
557 }
558 
559 void TurboAssembler::Div_du(Register rd, Register rj, const Operand& rk) {
560   if (rk.is_reg()) {
561     div_du(rd, rj, rk.rm());
562   } else {
563     // li handles the relocation.
564     UseScratchRegisterScope temps(this);
565     Register scratch = temps.Acquire();
566     DCHECK(rj != scratch);
567     li(scratch, rk);
568     div_du(rd, rj, scratch);
569   }
570 }
571 
572 void TurboAssembler::Mod_d(Register rd, Register rj, const Operand& rk) {
573   if (rk.is_reg()) {
574     mod_d(rd, rj, rk.rm());
575   } else {
576     // li handles the relocation.
577     UseScratchRegisterScope temps(this);
578     Register scratch = temps.Acquire();
579     DCHECK(rj != scratch);
580     li(scratch, rk);
581     mod_d(rd, rj, scratch);
582   }
583 }
584 
585 void TurboAssembler::Mod_du(Register rd, Register rj, const Operand& rk) {
586   if (rk.is_reg()) {
587     mod_du(rd, rj, rk.rm());
588   } else {
589     // li handles the relocation.
590     UseScratchRegisterScope temps(this);
591     Register scratch = temps.Acquire();
592     DCHECK(rj != scratch);
593     li(scratch, rk);
594     mod_du(rd, rj, scratch);
595   }
596 }
597 
598 void TurboAssembler::And(Register rd, Register rj, const Operand& rk) {
599   if (rk.is_reg()) {
600     and_(rd, rj, rk.rm());
601   } else {
602     if (is_uint12(rk.immediate()) && !MustUseReg(rk.rmode())) {
603       andi(rd, rj, static_cast<int32_t>(rk.immediate()));
604     } else {
605       // li handles the relocation.
606       UseScratchRegisterScope temps(this);
607       Register scratch = temps.Acquire();
608       DCHECK(rj != scratch);
609       li(scratch, rk);
610       and_(rd, rj, scratch);
611     }
612   }
613 }
614 
615 void TurboAssembler::Or(Register rd, Register rj, const Operand& rk) {
616   if (rk.is_reg()) {
617     or_(rd, rj, rk.rm());
618   } else {
619     if (is_uint12(rk.immediate()) && !MustUseReg(rk.rmode())) {
620       ori(rd, rj, static_cast<int32_t>(rk.immediate()));
621     } else {
622       // li handles the relocation.
623       UseScratchRegisterScope temps(this);
624       Register scratch = temps.Acquire();
625       DCHECK(rj != scratch);
626       li(scratch, rk);
627       or_(rd, rj, scratch);
628     }
629   }
630 }
631 
632 void TurboAssembler::Xor(Register rd, Register rj, const Operand& rk) {
633   if (rk.is_reg()) {
634     xor_(rd, rj, rk.rm());
635   } else {
636     if (is_uint12(rk.immediate()) && !MustUseReg(rk.rmode())) {
637       xori(rd, rj, static_cast<int32_t>(rk.immediate()));
638     } else {
639       // li handles the relocation.
640       UseScratchRegisterScope temps(this);
641       Register scratch = temps.Acquire();
642       DCHECK(rj != scratch);
643       li(scratch, rk);
644       xor_(rd, rj, scratch);
645     }
646   }
647 }
648 
649 void TurboAssembler::Nor(Register rd, Register rj, const Operand& rk) {
650   if (rk.is_reg()) {
651     nor(rd, rj, rk.rm());
652   } else {
653     // li handles the relocation.
654     UseScratchRegisterScope temps(this);
655     Register scratch = temps.Acquire();
656     DCHECK(rj != scratch);
657     li(scratch, rk);
658     nor(rd, rj, scratch);
659   }
660 }
661 
662 void TurboAssembler::Andn(Register rd, Register rj, const Operand& rk) {
663   if (rk.is_reg()) {
664     andn(rd, rj, rk.rm());
665   } else {
666     // li handles the relocation.
667     UseScratchRegisterScope temps(this);
668     Register scratch = temps.Acquire();
669     DCHECK(rj != scratch);
670     li(scratch, rk);
671     andn(rd, rj, scratch);
672   }
673 }
674 
675 void TurboAssembler::Orn(Register rd, Register rj, const Operand& rk) {
676   if (rk.is_reg()) {
677     orn(rd, rj, rk.rm());
678   } else {
679     // li handles the relocation.
680     UseScratchRegisterScope temps(this);
681     Register scratch = temps.Acquire();
682     DCHECK(rj != scratch);
683     li(scratch, rk);
684     orn(rd, rj, scratch);
685   }
686 }
687 
688 void TurboAssembler::Neg(Register rj, const Operand& rk) {
689   DCHECK(rk.is_reg());
690   sub_d(rj, zero_reg, rk.rm());
691 }
692 
693 void TurboAssembler::Slt(Register rd, Register rj, const Operand& rk) {
694   if (rk.is_reg()) {
695     slt(rd, rj, rk.rm());
696   } else {
697     if (is_int12(rk.immediate()) && !MustUseReg(rk.rmode())) {
698       slti(rd, rj, static_cast<int32_t>(rk.immediate()));
699     } else {
700       // li handles the relocation.
701       UseScratchRegisterScope temps(this);
702       BlockTrampolinePoolScope block_trampoline_pool(this);
703       Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
704       DCHECK(rj != scratch);
705       li(scratch, rk);
706       slt(rd, rj, scratch);
707     }
708   }
709 }
710 
711 void TurboAssembler::Sltu(Register rd, Register rj, const Operand& rk) {
712   if (rk.is_reg()) {
713     sltu(rd, rj, rk.rm());
714   } else {
715     if (is_int12(rk.immediate()) && !MustUseReg(rk.rmode())) {
716       sltui(rd, rj, static_cast<int32_t>(rk.immediate()));
717     } else {
718       // li handles the relocation.
719       UseScratchRegisterScope temps(this);
720       BlockTrampolinePoolScope block_trampoline_pool(this);
721       Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
722       DCHECK(rj != scratch);
723       li(scratch, rk);
724       sltu(rd, rj, scratch);
725     }
726   }
727 }
728 
729 void TurboAssembler::Sle(Register rd, Register rj, const Operand& rk) {
730   if (rk.is_reg()) {
731     slt(rd, rk.rm(), rj);
732   } else {
733     // li handles the relocation.
734     UseScratchRegisterScope temps(this);
735     Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
736     BlockTrampolinePoolScope block_trampoline_pool(this);
737     DCHECK(rj != scratch);
738     li(scratch, rk);
739     slt(rd, scratch, rj);
740   }
741   xori(rd, rd, 1);
742 }
743 
744 void TurboAssembler::Sleu(Register rd, Register rj, const Operand& rk) {
745   if (rk.is_reg()) {
746     sltu(rd, rk.rm(), rj);
747   } else {
748     // li handles the relocation.
749     UseScratchRegisterScope temps(this);
750     Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
751     BlockTrampolinePoolScope block_trampoline_pool(this);
752     DCHECK(rj != scratch);
753     li(scratch, rk);
754     sltu(rd, scratch, rj);
755   }
756   xori(rd, rd, 1);
757 }
758 
759 void TurboAssembler::Sge(Register rd, Register rj, const Operand& rk) {
760   Slt(rd, rj, rk);
761   xori(rd, rd, 1);
762 }
763 
764 void TurboAssembler::Sgeu(Register rd, Register rj, const Operand& rk) {
765   Sltu(rd, rj, rk);
766   xori(rd, rd, 1);
767 }
768 
769 void TurboAssembler::Sgt(Register rd, Register rj, const Operand& rk) {
770   if (rk.is_reg()) {
771     slt(rd, rk.rm(), rj);
772   } else {
773     // li handles the relocation.
774     UseScratchRegisterScope temps(this);
775     Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
776     BlockTrampolinePoolScope block_trampoline_pool(this);
777     DCHECK(rj != scratch);
778     li(scratch, rk);
779     slt(rd, scratch, rj);
780   }
781 }
782 
783 void TurboAssembler::Sgtu(Register rd, Register rj, const Operand& rk) {
784   if (rk.is_reg()) {
785     sltu(rd, rk.rm(), rj);
786   } else {
787     // li handles the relocation.
788     UseScratchRegisterScope temps(this);
789     Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
790     BlockTrampolinePoolScope block_trampoline_pool(this);
791     DCHECK(rj != scratch);
792     li(scratch, rk);
793     sltu(rd, scratch, rj);
794   }
795 }
796 
797 void TurboAssembler::Rotr_w(Register rd, Register rj, const Operand& rk) {
798   if (rk.is_reg()) {
799     rotr_w(rd, rj, rk.rm());
800   } else {
801     int64_t ror_value = rk.immediate() % 32;
802     if (ror_value < 0) {
803       ror_value += 32;
804     }
805     rotri_w(rd, rj, ror_value);
806   }
807 }
808 
809 void TurboAssembler::Rotr_d(Register rd, Register rj, const Operand& rk) {
810   if (rk.is_reg()) {
811     rotr_d(rd, rj, rk.rm());
812   } else {
813     int64_t dror_value = rk.immediate() % 64;
814     if (dror_value < 0) dror_value += 64;
815     rotri_d(rd, rj, dror_value);
816   }
817 }
818 
819 void TurboAssembler::Alsl_w(Register rd, Register rj, Register rk, uint8_t sa,
820                             Register scratch) {
821   DCHECK(sa >= 1 && sa <= 31);
822   if (sa <= 4) {
823     alsl_w(rd, rj, rk, sa);
824   } else {
825     Register tmp = rd == rk ? scratch : rd;
826     DCHECK(tmp != rk);
827     slli_w(tmp, rj, sa);
828     add_w(rd, rk, tmp);
829   }
830 }
831 
832 void TurboAssembler::Alsl_d(Register rd, Register rj, Register rk, uint8_t sa,
833                             Register scratch) {
834   DCHECK(sa >= 1 && sa <= 63);
835   if (sa <= 4) {
836     alsl_d(rd, rj, rk, sa);
837   } else {
838     Register tmp = rd == rk ? scratch : rd;
839     DCHECK(tmp != rk);
840     slli_d(tmp, rj, sa);
841     add_d(rd, rk, tmp);
842   }
843 }
844 
845 // ------------Pseudo-instructions-------------
846 
847 // Change endianness
848 void TurboAssembler::ByteSwapSigned(Register dest, Register src,
849                                     int operand_size) {
850   DCHECK(operand_size == 2 || operand_size == 4 || operand_size == 8);
851   if (operand_size == 2) {
852     revb_2h(dest, src);
853     ext_w_h(dest, dest);
854   } else if (operand_size == 4) {
855     revb_2w(dest, src);
856     slli_w(dest, dest, 0);
857   } else {
858     revb_d(dest, dest);
859   }
860 }
861 
862 void TurboAssembler::ByteSwapUnsigned(Register dest, Register src,
863                                       int operand_size) {
864   DCHECK(operand_size == 2 || operand_size == 4);
865   if (operand_size == 2) {
866     revb_2h(dest, src);
867     bstrins_d(dest, zero_reg, 63, 16);
868   } else {
869     revb_2w(dest, src);
870     bstrins_d(dest, zero_reg, 63, 32);
871   }
872 }
873 
874 void TurboAssembler::Ld_b(Register rd, const MemOperand& rj) {
875   MemOperand source = rj;
876   AdjustBaseAndOffset(&source);
877   if (source.hasIndexReg()) {
878     ldx_b(rd, source.base(), source.index());
879   } else {
880     ld_b(rd, source.base(), source.offset());
881   }
882 }
883 
884 void TurboAssembler::Ld_bu(Register rd, const MemOperand& rj) {
885   MemOperand source = rj;
886   AdjustBaseAndOffset(&source);
887   if (source.hasIndexReg()) {
888     ldx_bu(rd, source.base(), source.index());
889   } else {
890     ld_bu(rd, source.base(), source.offset());
891   }
892 }
893 
894 void TurboAssembler::St_b(Register rd, const MemOperand& rj) {
895   MemOperand source = rj;
896   AdjustBaseAndOffset(&source);
897   if (source.hasIndexReg()) {
898     stx_b(rd, source.base(), source.index());
899   } else {
900     st_b(rd, source.base(), source.offset());
901   }
902 }
903 
904 void TurboAssembler::Ld_h(Register rd, const MemOperand& rj) {
905   MemOperand source = rj;
906   AdjustBaseAndOffset(&source);
907   if (source.hasIndexReg()) {
908     ldx_h(rd, source.base(), source.index());
909   } else {
910     ld_h(rd, source.base(), source.offset());
911   }
912 }
913 
914 void TurboAssembler::Ld_hu(Register rd, const MemOperand& rj) {
915   MemOperand source = rj;
916   AdjustBaseAndOffset(&source);
917   if (source.hasIndexReg()) {
918     ldx_hu(rd, source.base(), source.index());
919   } else {
920     ld_hu(rd, source.base(), source.offset());
921   }
922 }
923 
924 void TurboAssembler::St_h(Register rd, const MemOperand& rj) {
925   MemOperand source = rj;
926   AdjustBaseAndOffset(&source);
927   if (source.hasIndexReg()) {
928     stx_h(rd, source.base(), source.index());
929   } else {
930     st_h(rd, source.base(), source.offset());
931   }
932 }
933 
934 void TurboAssembler::Ld_w(Register rd, const MemOperand& rj) {
935   MemOperand source = rj;
936 
937   if (!(source.hasIndexReg()) && is_int16(source.offset()) &&
938       (source.offset() & 0b11) == 0) {
939     ldptr_w(rd, source.base(), source.offset());
940     return;
941   }
942 
943   AdjustBaseAndOffset(&source);
944   if (source.hasIndexReg()) {
945     ldx_w(rd, source.base(), source.index());
946   } else {
947     ld_w(rd, source.base(), source.offset());
948   }
949 }
950 
951 void TurboAssembler::Ld_wu(Register rd, const MemOperand& rj) {
952   MemOperand source = rj;
953   AdjustBaseAndOffset(&source);
954   if (source.hasIndexReg()) {
955     ldx_wu(rd, source.base(), source.index());
956   } else {
957     ld_wu(rd, source.base(), source.offset());
958   }
959 }
960 
961 void TurboAssembler::St_w(Register rd, const MemOperand& rj) {
962   MemOperand source = rj;
963 
964   if (!(source.hasIndexReg()) && is_int16(source.offset()) &&
965       (source.offset() & 0b11) == 0) {
966     stptr_w(rd, source.base(), source.offset());
967     return;
968   }
969 
970   AdjustBaseAndOffset(&source);
971   if (source.hasIndexReg()) {
972     stx_w(rd, source.base(), source.index());
973   } else {
974     st_w(rd, source.base(), source.offset());
975   }
976 }
977 
978 void TurboAssembler::Ld_d(Register rd, const MemOperand& rj) {
979   MemOperand source = rj;
980 
981   if (!(source.hasIndexReg()) && is_int16(source.offset()) &&
982       (source.offset() & 0b11) == 0) {
983     ldptr_d(rd, source.base(), source.offset());
984     return;
985   }
986 
987   AdjustBaseAndOffset(&source);
988   if (source.hasIndexReg()) {
989     ldx_d(rd, source.base(), source.index());
990   } else {
991     ld_d(rd, source.base(), source.offset());
992   }
993 }
994 
995 void TurboAssembler::St_d(Register rd, const MemOperand& rj) {
996   MemOperand source = rj;
997 
998   if (!(source.hasIndexReg()) && is_int16(source.offset()) &&
999       (source.offset() & 0b11) == 0) {
1000     stptr_d(rd, source.base(), source.offset());
1001     return;
1002   }
1003 
1004   AdjustBaseAndOffset(&source);
1005   if (source.hasIndexReg()) {
1006     stx_d(rd, source.base(), source.index());
1007   } else {
1008     st_d(rd, source.base(), source.offset());
1009   }
1010 }
1011 
1012 void TurboAssembler::Fld_s(FPURegister fd, const MemOperand& src) {
1013   MemOperand tmp = src;
1014   AdjustBaseAndOffset(&tmp);
1015   if (tmp.hasIndexReg()) {
1016     fldx_s(fd, tmp.base(), tmp.index());
1017   } else {
1018     fld_s(fd, tmp.base(), tmp.offset());
1019   }
1020 }
1021 
1022 void TurboAssembler::Fst_s(FPURegister fs, const MemOperand& src) {
1023   MemOperand tmp = src;
1024   AdjustBaseAndOffset(&tmp);
1025   if (tmp.hasIndexReg()) {
1026     fstx_s(fs, tmp.base(), tmp.index());
1027   } else {
1028     fst_s(fs, tmp.base(), tmp.offset());
1029   }
1030 }
1031 
1032 void TurboAssembler::Fld_d(FPURegister fd, const MemOperand& src) {
1033   MemOperand tmp = src;
1034   AdjustBaseAndOffset(&tmp);
1035   if (tmp.hasIndexReg()) {
1036     fldx_d(fd, tmp.base(), tmp.index());
1037   } else {
1038     fld_d(fd, tmp.base(), tmp.offset());
1039   }
1040 }
1041 
1042 void TurboAssembler::Fst_d(FPURegister fs, const MemOperand& src) {
1043   MemOperand tmp = src;
1044   AdjustBaseAndOffset(&tmp);
1045   if (tmp.hasIndexReg()) {
1046     fstx_d(fs, tmp.base(), tmp.index());
1047   } else {
1048     fst_d(fs, tmp.base(), tmp.offset());
1049   }
1050 }
1051 
1052 void TurboAssembler::Ll_w(Register rd, const MemOperand& rj) {
1053   DCHECK(!rj.hasIndexReg());
1054   bool is_one_instruction = is_int14(rj.offset());
1055   if (is_one_instruction) {
1056     ll_w(rd, rj.base(), rj.offset());
1057   } else {
1058     UseScratchRegisterScope temps(this);
1059     Register scratch = temps.Acquire();
1060     li(scratch, rj.offset());
1061     add_d(scratch, scratch, rj.base());
1062     ll_w(rd, scratch, 0);
1063   }
1064 }
1065 
1066 void TurboAssembler::Ll_d(Register rd, const MemOperand& rj) {
1067   DCHECK(!rj.hasIndexReg());
1068   bool is_one_instruction = is_int14(rj.offset());
1069   if (is_one_instruction) {
1070     ll_d(rd, rj.base(), rj.offset());
1071   } else {
1072     UseScratchRegisterScope temps(this);
1073     Register scratch = temps.Acquire();
1074     li(scratch, rj.offset());
1075     add_d(scratch, scratch, rj.base());
1076     ll_d(rd, scratch, 0);
1077   }
1078 }
1079 
1080 void TurboAssembler::Sc_w(Register rd, const MemOperand& rj) {
1081   DCHECK(!rj.hasIndexReg());
1082   bool is_one_instruction = is_int14(rj.offset());
1083   if (is_one_instruction) {
1084     sc_w(rd, rj.base(), rj.offset());
1085   } else {
1086     UseScratchRegisterScope temps(this);
1087     Register scratch = temps.Acquire();
1088     li(scratch, rj.offset());
1089     add_d(scratch, scratch, rj.base());
1090     sc_w(rd, scratch, 0);
1091   }
1092 }
1093 
1094 void TurboAssembler::Sc_d(Register rd, const MemOperand& rj) {
1095   DCHECK(!rj.hasIndexReg());
1096   bool is_one_instruction = is_int14(rj.offset());
1097   if (is_one_instruction) {
1098     sc_d(rd, rj.base(), rj.offset());
1099   } else {
1100     UseScratchRegisterScope temps(this);
1101     Register scratch = temps.Acquire();
1102     li(scratch, rj.offset());
1103     add_d(scratch, scratch, rj.base());
1104     sc_d(rd, scratch, 0);
1105   }
1106 }
1107 
1108 void TurboAssembler::li(Register dst, Handle<HeapObject> value, LiFlags mode) {
1109   // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
1110   // non-isolate-independent code. In many cases it might be cheaper than
1111   // embedding the relocatable value.
1112   if (root_array_available_ && options().isolate_independent_code) {
1113     IndirectLoadConstant(dst, value);
1114     return;
1115   }
1116   li(dst, Operand(value), mode);
1117 }
1118 
1119 void TurboAssembler::li(Register dst, ExternalReference value, LiFlags mode) {
1120   // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
1121   // non-isolate-independent code. In many cases it might be cheaper than
1122   // embedding the relocatable value.
1123   if (root_array_available_ && options().isolate_independent_code) {
1124     IndirectLoadExternalReference(dst, value);
1125     return;
1126   }
1127   li(dst, Operand(value), mode);
1128 }
1129 
1130 void TurboAssembler::li(Register dst, const StringConstantBase* string,
1131                         LiFlags mode) {
1132   li(dst, Operand::EmbeddedStringConstant(string), mode);
1133 }
1134 
1135 static inline int InstrCountForLiLower32Bit(int64_t value) {
1136   if (is_int12(static_cast<int32_t>(value)) ||
1137       is_uint12(static_cast<int32_t>(value)) || !(value & kImm12Mask)) {
1138     return 1;
1139   } else {
1140     return 2;
1141   }
1142 }
1143 
1144 void TurboAssembler::LiLower32BitHelper(Register rd, Operand j) {
1145   if (is_int12(static_cast<int32_t>(j.immediate()))) {
1146     addi_d(rd, zero_reg, j.immediate());
1147   } else if (is_uint12(static_cast<int32_t>(j.immediate()))) {
1148     ori(rd, zero_reg, j.immediate() & kImm12Mask);
1149   } else {
1150     lu12i_w(rd, j.immediate() >> 12 & 0xfffff);
1151     if (j.immediate() & kImm12Mask) {
1152       ori(rd, rd, j.immediate() & kImm12Mask);
1153     }
1154   }
1155 }
1156 
1157 int TurboAssembler::InstrCountForLi64Bit(int64_t value) {
1158   if (is_int32(value)) {
1159     return InstrCountForLiLower32Bit(value);
1160   } else if (is_int52(value)) {
1161     return InstrCountForLiLower32Bit(value) + 1;
1162   } else if ((value & 0xffffffffL) == 0) {
1163     // 32 LSBs (Least Significant Bits) all set to zero.
1164     uint8_t tzc = base::bits::CountTrailingZeros32(value >> 32);
1165     uint8_t lzc = base::bits::CountLeadingZeros32(value >> 32);
1166     if (tzc >= 20) {
1167       return 1;
1168     } else if (tzc + lzc > 12) {
1169       return 2;
1170     } else {
1171       return 3;
1172     }
1173   } else {
1174     int64_t imm21 = (value >> 31) & 0x1fffffL;
1175     if (imm21 != 0x1fffffL && imm21 != 0) {
1176       return InstrCountForLiLower32Bit(value) + 2;
1177     } else {
1178       return InstrCountForLiLower32Bit(value) + 1;
1179     }
1180   }
1181   UNREACHABLE();
1182   return INT_MAX;
1183 }
1184 
1185 // All changes to if...else conditions here must be added to
1186 // InstrCountForLi64Bit as well.
1187 void TurboAssembler::li_optimized(Register rd, Operand j, LiFlags mode) {
1188   DCHECK(!j.is_reg());
1189   DCHECK(!MustUseReg(j.rmode()));
1190   DCHECK(mode == OPTIMIZE_SIZE);
1191   int64_t imm = j.immediate();
1192   BlockTrampolinePoolScope block_trampoline_pool(this);
1193   // Normal load of an immediate value which does not need Relocation Info.
1194   if (is_int32(imm)) {
1195     LiLower32BitHelper(rd, j);
1196   } else if (is_int52(imm)) {
1197     LiLower32BitHelper(rd, j);
1198     lu32i_d(rd, imm >> 32 & 0xfffff);
1199   } else if ((imm & 0xffffffffL) == 0) {
1200     // 32 LSBs (Least Significant Bits) all set to zero.
1201     uint8_t tzc = base::bits::CountTrailingZeros32(imm >> 32);
1202     uint8_t lzc = base::bits::CountLeadingZeros32(imm >> 32);
1203     if (tzc >= 20) {
1204       lu52i_d(rd, zero_reg, imm >> 52 & kImm12Mask);
1205     } else if (tzc + lzc > 12) {
1206       int32_t mask = (1 << (32 - tzc)) - 1;
1207       lu12i_w(rd, imm >> (tzc + 32) & mask);
1208       slli_d(rd, rd, tzc + 20);
1209     } else {
1210       xor_(rd, rd, rd);
1211       lu32i_d(rd, imm >> 32 & 0xfffff);
1212       lu52i_d(rd, rd, imm >> 52 & kImm12Mask);
1213     }
1214   } else {
1215     int64_t imm21 = (imm >> 31) & 0x1fffffL;
1216     LiLower32BitHelper(rd, j);
1217     if (imm21 != 0x1fffffL && imm21 != 0) lu32i_d(rd, imm >> 32 & 0xfffff);
1218     lu52i_d(rd, rd, imm >> 52 & kImm12Mask);
1219   }
1220 }
1221 
1222 void TurboAssembler::li(Register rd, Operand j, LiFlags mode) {
1223   DCHECK(!j.is_reg());
1224   BlockTrampolinePoolScope block_trampoline_pool(this);
1225   if (!MustUseReg(j.rmode()) && mode == OPTIMIZE_SIZE) {
1226     li_optimized(rd, j, mode);
1227   } else if (MustUseReg(j.rmode())) {
1228     int64_t immediate;
1229     if (j.IsHeapObjectRequest()) {
1230       RequestHeapObject(j.heap_object_request());
1231       immediate = 0;
1232     } else {
1233       immediate = j.immediate();
1234     }
1235 
1236     RecordRelocInfo(j.rmode(), immediate);
1237     lu12i_w(rd, immediate >> 12 & 0xfffff);
1238     ori(rd, rd, immediate & kImm12Mask);
1239     lu32i_d(rd, immediate >> 32 & 0xfffff);
1240   } else if (mode == ADDRESS_LOAD) {
1241     // We always need the same number of instructions as we may need to patch
1242     // this code to load another value which may need all 3 instructions.
1243     lu12i_w(rd, j.immediate() >> 12 & 0xfffff);
1244     ori(rd, rd, j.immediate() & kImm12Mask);
1245     lu32i_d(rd, j.immediate() >> 32 & 0xfffff);
1246   } else {  // mode == CONSTANT_SIZE - always emit the same instruction
1247             // sequence.
1248     lu12i_w(rd, j.immediate() >> 12 & 0xfffff);
1249     ori(rd, rd, j.immediate() & kImm12Mask);
1250     lu32i_d(rd, j.immediate() >> 32 & 0xfffff);
1251     lu52i_d(rd, rd, j.immediate() >> 52 & kImm12Mask);
1252   }
1253 }
1254 
1255 void TurboAssembler::MultiPush(RegList regs) {
1256   int16_t stack_offset = 0;
1257 
1258   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1259     if ((regs.bits() & (1 << i)) != 0) {
1260       stack_offset -= kPointerSize;
1261       St_d(ToRegister(i), MemOperand(sp, stack_offset));
1262     }
1263   }
1264   addi_d(sp, sp, stack_offset);
1265 }
1266 
1267 void TurboAssembler::MultiPush(RegList regs1, RegList regs2) {
1268   DCHECK((regs1 & regs2).is_empty());
1269   int16_t stack_offset = 0;
1270 
1271   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1272     if ((regs1.bits() & (1 << i)) != 0) {
1273       stack_offset -= kPointerSize;
1274       St_d(ToRegister(i), MemOperand(sp, stack_offset));
1275     }
1276   }
1277   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1278     if ((regs2.bits() & (1 << i)) != 0) {
1279       stack_offset -= kPointerSize;
1280       St_d(ToRegister(i), MemOperand(sp, stack_offset));
1281     }
1282   }
1283   addi_d(sp, sp, stack_offset);
1284 }
1285 
1286 void TurboAssembler::MultiPush(RegList regs1, RegList regs2, RegList regs3) {
1287   DCHECK((regs1 & regs2).is_empty());
1288   DCHECK((regs1 & regs3).is_empty());
1289   DCHECK((regs2 & regs3).is_empty());
1290   int16_t stack_offset = 0;
1291 
1292   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1293     if ((regs1.bits() & (1 << i)) != 0) {
1294       stack_offset -= kPointerSize;
1295       St_d(ToRegister(i), MemOperand(sp, stack_offset));
1296     }
1297   }
1298   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1299     if ((regs2.bits() & (1 << i)) != 0) {
1300       stack_offset -= kPointerSize;
1301       St_d(ToRegister(i), MemOperand(sp, stack_offset));
1302     }
1303   }
1304   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1305     if ((regs3.bits() & (1 << i)) != 0) {
1306       stack_offset -= kPointerSize;
1307       St_d(ToRegister(i), MemOperand(sp, stack_offset));
1308     }
1309   }
1310   addi_d(sp, sp, stack_offset);
1311 }
1312 
1313 void TurboAssembler::MultiPop(RegList regs) {
1314   int16_t stack_offset = 0;
1315 
1316   for (int16_t i = 0; i < kNumRegisters; i++) {
1317     if ((regs.bits() & (1 << i)) != 0) {
1318       Ld_d(ToRegister(i), MemOperand(sp, stack_offset));
1319       stack_offset += kPointerSize;
1320     }
1321   }
1322   addi_d(sp, sp, stack_offset);
1323 }
1324 
1325 void TurboAssembler::MultiPop(RegList regs1, RegList regs2) {
1326   DCHECK((regs1 & regs2).is_empty());
1327   int16_t stack_offset = 0;
1328 
1329   for (int16_t i = 0; i < kNumRegisters; i++) {
1330     if ((regs2.bits() & (1 << i)) != 0) {
1331       Ld_d(ToRegister(i), MemOperand(sp, stack_offset));
1332       stack_offset += kPointerSize;
1333     }
1334   }
1335   for (int16_t i = 0; i < kNumRegisters; i++) {
1336     if ((regs1.bits() & (1 << i)) != 0) {
1337       Ld_d(ToRegister(i), MemOperand(sp, stack_offset));
1338       stack_offset += kPointerSize;
1339     }
1340   }
1341   addi_d(sp, sp, stack_offset);
1342 }
1343 
1344 void TurboAssembler::MultiPop(RegList regs1, RegList regs2, RegList regs3) {
1345   DCHECK((regs1 & regs2).is_empty());
1346   DCHECK((regs1 & regs3).is_empty());
1347   DCHECK((regs2 & regs3).is_empty());
1348   int16_t stack_offset = 0;
1349 
1350   for (int16_t i = 0; i < kNumRegisters; i++) {
1351     if ((regs3.bits() & (1 << i)) != 0) {
1352       Ld_d(ToRegister(i), MemOperand(sp, stack_offset));
1353       stack_offset += kPointerSize;
1354     }
1355   }
1356   for (int16_t i = 0; i < kNumRegisters; i++) {
1357     if ((regs2.bits() & (1 << i)) != 0) {
1358       Ld_d(ToRegister(i), MemOperand(sp, stack_offset));
1359       stack_offset += kPointerSize;
1360     }
1361   }
1362   for (int16_t i = 0; i < kNumRegisters; i++) {
1363     if ((regs1.bits() & (1 << i)) != 0) {
1364       Ld_d(ToRegister(i), MemOperand(sp, stack_offset));
1365       stack_offset += kPointerSize;
1366     }
1367   }
1368   addi_d(sp, sp, stack_offset);
1369 }
1370 
1371 void TurboAssembler::MultiPushFPU(DoubleRegList regs) {
1372   int16_t num_to_push = regs.Count();
1373   int16_t stack_offset = num_to_push * kDoubleSize;
1374 
1375   Sub_d(sp, sp, Operand(stack_offset));
1376   for (int16_t i = kNumRegisters - 1; i >= 0; i--) {
1377     if ((regs.bits() & (1 << i)) != 0) {
1378       stack_offset -= kDoubleSize;
1379       Fst_d(FPURegister::from_code(i), MemOperand(sp, stack_offset));
1380     }
1381   }
1382 }
1383 
1384 void TurboAssembler::MultiPopFPU(DoubleRegList regs) {
1385   int16_t stack_offset = 0;
1386 
1387   for (int16_t i = 0; i < kNumRegisters; i++) {
1388     if ((regs.bits() & (1 << i)) != 0) {
1389       Fld_d(FPURegister::from_code(i), MemOperand(sp, stack_offset));
1390       stack_offset += kDoubleSize;
1391     }
1392   }
1393   addi_d(sp, sp, stack_offset);
1394 }
1395 
1396 void TurboAssembler::Bstrpick_w(Register rk, Register rj, uint16_t msbw,
1397                                 uint16_t lsbw) {
1398   DCHECK_LT(lsbw, msbw);
1399   DCHECK_LT(lsbw, 32);
1400   DCHECK_LT(msbw, 32);
1401   bstrpick_w(rk, rj, msbw, lsbw);
1402 }
1403 
1404 void TurboAssembler::Bstrpick_d(Register rk, Register rj, uint16_t msbw,
1405                                 uint16_t lsbw) {
1406   DCHECK_LT(lsbw, msbw);
1407   DCHECK_LT(lsbw, 64);
1408   DCHECK_LT(msbw, 64);
1409   bstrpick_d(rk, rj, msbw, lsbw);
1410 }
1411 
1412 void TurboAssembler::Neg_s(FPURegister fd, FPURegister fj) { fneg_s(fd, fj); }
1413 
1414 void TurboAssembler::Neg_d(FPURegister fd, FPURegister fj) { fneg_d(fd, fj); }
1415 
1416 void TurboAssembler::Ffint_d_uw(FPURegister fd, FPURegister fj) {
1417   BlockTrampolinePoolScope block_trampoline_pool(this);
1418   movfr2gr_s(t8, fj);
1419   Ffint_d_uw(fd, t8);
1420 }
1421 
1422 void TurboAssembler::Ffint_d_uw(FPURegister fd, Register rj) {
1423   BlockTrampolinePoolScope block_trampoline_pool(this);
1424   DCHECK(rj != t7);
1425 
1426   Bstrpick_d(t7, rj, 31, 0);
1427   movgr2fr_d(fd, t7);
1428   ffint_d_l(fd, fd);
1429 }
1430 
1431 void TurboAssembler::Ffint_d_ul(FPURegister fd, FPURegister fj) {
1432   BlockTrampolinePoolScope block_trampoline_pool(this);
1433   movfr2gr_d(t8, fj);
1434   Ffint_d_ul(fd, t8);
1435 }
1436 
1437 void TurboAssembler::Ffint_d_ul(FPURegister fd, Register rj) {
1438   BlockTrampolinePoolScope block_trampoline_pool(this);
1439   DCHECK(rj != t7);
1440 
1441   Label msb_clear, conversion_done;
1442 
1443   Branch(&msb_clear, ge, rj, Operand(zero_reg));
1444 
1445   // Rj >= 2^63
1446   andi(t7, rj, 1);
1447   srli_d(rj, rj, 1);
1448   or_(t7, t7, rj);
1449   movgr2fr_d(fd, t7);
1450   ffint_d_l(fd, fd);
1451   fadd_d(fd, fd, fd);
1452   Branch(&conversion_done);
1453 
1454   bind(&msb_clear);
1455   // Rs < 2^63, we can do simple conversion.
1456   movgr2fr_d(fd, rj);
1457   ffint_d_l(fd, fd);
1458 
1459   bind(&conversion_done);
1460 }
1461 
1462 void TurboAssembler::Ffint_s_uw(FPURegister fd, FPURegister fj) {
1463   BlockTrampolinePoolScope block_trampoline_pool(this);
1464   movfr2gr_d(t8, fj);
1465   Ffint_s_uw(fd, t8);
1466 }
1467 
1468 void TurboAssembler::Ffint_s_uw(FPURegister fd, Register rj) {
1469   BlockTrampolinePoolScope block_trampoline_pool(this);
1470   DCHECK(rj != t7);
1471 
1472   bstrpick_d(t7, rj, 31, 0);
1473   movgr2fr_d(fd, t7);
1474   ffint_s_l(fd, fd);
1475 }
1476 
1477 void TurboAssembler::Ffint_s_ul(FPURegister fd, FPURegister fj) {
1478   BlockTrampolinePoolScope block_trampoline_pool(this);
1479   movfr2gr_d(t8, fj);
1480   Ffint_s_ul(fd, t8);
1481 }
1482 
1483 void TurboAssembler::Ffint_s_ul(FPURegister fd, Register rj) {
1484   BlockTrampolinePoolScope block_trampoline_pool(this);
1485   DCHECK(rj != t7);
1486 
1487   Label positive, conversion_done;
1488 
1489   Branch(&positive, ge, rj, Operand(zero_reg));
1490 
1491   // Rs >= 2^31.
1492   andi(t7, rj, 1);
1493   srli_d(rj, rj, 1);
1494   or_(t7, t7, rj);
1495   movgr2fr_d(fd, t7);
1496   ffint_s_l(fd, fd);
1497   fadd_s(fd, fd, fd);
1498   Branch(&conversion_done);
1499 
1500   bind(&positive);
1501   // Rs < 2^31, we can do simple conversion.
1502   movgr2fr_d(fd, rj);
1503   ffint_s_l(fd, fd);
1504 
1505   bind(&conversion_done);
1506 }
1507 
1508 void MacroAssembler::Ftintrne_l_d(FPURegister fd, FPURegister fj) {
1509   ftintrne_l_d(fd, fj);
1510 }
1511 
1512 void MacroAssembler::Ftintrm_l_d(FPURegister fd, FPURegister fj) {
1513   ftintrm_l_d(fd, fj);
1514 }
1515 
1516 void MacroAssembler::Ftintrp_l_d(FPURegister fd, FPURegister fj) {
1517   ftintrp_l_d(fd, fj);
1518 }
1519 
1520 void MacroAssembler::Ftintrz_l_d(FPURegister fd, FPURegister fj) {
1521   ftintrz_l_d(fd, fj);
1522 }
1523 
1524 void MacroAssembler::Ftintrz_l_ud(FPURegister fd, FPURegister fj,
1525                                   FPURegister scratch) {
1526   BlockTrampolinePoolScope block_trampoline_pool(this);
1527   // Load to GPR.
1528   movfr2gr_d(t8, fj);
1529   // Reset sign bit.
1530   {
1531     UseScratchRegisterScope temps(this);
1532     Register scratch1 = temps.Acquire();
1533     li(scratch1, 0x7FFFFFFFFFFFFFFFl);
1534     and_(t8, t8, scratch1);
1535   }
1536   movgr2fr_d(scratch, t8);
1537   Ftintrz_l_d(fd, scratch);
1538 }
1539 
1540 void TurboAssembler::Ftintrz_uw_d(FPURegister fd, FPURegister fj,
1541                                   FPURegister scratch) {
1542   BlockTrampolinePoolScope block_trampoline_pool(this);
1543   Ftintrz_uw_d(t8, fj, scratch);
1544   movgr2fr_w(fd, t8);
1545 }
1546 
1547 void TurboAssembler::Ftintrz_uw_s(FPURegister fd, FPURegister fj,
1548                                   FPURegister scratch) {
1549   BlockTrampolinePoolScope block_trampoline_pool(this);
1550   Ftintrz_uw_s(t8, fj, scratch);
1551   movgr2fr_w(fd, t8);
1552 }
1553 
1554 void TurboAssembler::Ftintrz_ul_d(FPURegister fd, FPURegister fj,
1555                                   FPURegister scratch, Register result) {
1556   BlockTrampolinePoolScope block_trampoline_pool(this);
1557   Ftintrz_ul_d(t8, fj, scratch, result);
1558   movgr2fr_d(fd, t8);
1559 }
1560 
1561 void TurboAssembler::Ftintrz_ul_s(FPURegister fd, FPURegister fj,
1562                                   FPURegister scratch, Register result) {
1563   BlockTrampolinePoolScope block_trampoline_pool(this);
1564   Ftintrz_ul_s(t8, fj, scratch, result);
1565   movgr2fr_d(fd, t8);
1566 }
1567 
1568 void MacroAssembler::Ftintrz_w_d(FPURegister fd, FPURegister fj) {
1569   ftintrz_w_d(fd, fj);
1570 }
1571 
1572 void MacroAssembler::Ftintrne_w_d(FPURegister fd, FPURegister fj) {
1573   ftintrne_w_d(fd, fj);
1574 }
1575 
1576 void MacroAssembler::Ftintrm_w_d(FPURegister fd, FPURegister fj) {
1577   ftintrm_w_d(fd, fj);
1578 }
1579 
1580 void MacroAssembler::Ftintrp_w_d(FPURegister fd, FPURegister fj) {
1581   ftintrp_w_d(fd, fj);
1582 }
1583 
1584 void TurboAssembler::Ftintrz_uw_d(Register rd, FPURegister fj,
1585                                   FPURegister scratch) {
1586   DCHECK(fj != scratch);
1587   DCHECK(rd != t7);
1588 
1589   {
1590     // Load 2^31 into scratch as its float representation.
1591     UseScratchRegisterScope temps(this);
1592     Register scratch1 = temps.Acquire();
1593     li(scratch1, 0x41E00000);
1594     movgr2fr_w(scratch, zero_reg);
1595     movgr2frh_w(scratch, scratch1);
1596   }
1597   // Test if scratch > fd.
1598   // If fd < 2^31 we can convert it normally.
1599   Label simple_convert;
1600   CompareF64(fj, scratch, CLT);
1601   BranchTrueShortF(&simple_convert);
1602 
1603   // First we subtract 2^31 from fd, then trunc it to rs
1604   // and add 2^31 to rj.
1605   fsub_d(scratch, fj, scratch);
1606   ftintrz_w_d(scratch, scratch);
1607   movfr2gr_s(rd, scratch);
1608   Or(rd, rd, 1 << 31);
1609 
1610   Label done;
1611   Branch(&done);
1612   // Simple conversion.
1613   bind(&simple_convert);
1614   ftintrz_w_d(scratch, fj);
1615   movfr2gr_s(rd, scratch);
1616 
1617   bind(&done);
1618 }
1619 
1620 void TurboAssembler::Ftintrz_uw_s(Register rd, FPURegister fj,
1621                                   FPURegister scratch) {
1622   DCHECK(fj != scratch);
1623   DCHECK(rd != t7);
1624   {
1625     // Load 2^31 into scratch as its float representation.
1626     UseScratchRegisterScope temps(this);
1627     Register scratch1 = temps.Acquire();
1628     li(scratch1, 0x4F000000);
1629     movgr2fr_w(scratch, scratch1);
1630   }
1631   // Test if scratch > fs.
1632   // If fs < 2^31 we can convert it normally.
1633   Label simple_convert;
1634   CompareF32(fj, scratch, CLT);
1635   BranchTrueShortF(&simple_convert);
1636 
1637   // First we subtract 2^31 from fs, then trunc it to rd
1638   // and add 2^31 to rd.
1639   fsub_s(scratch, fj, scratch);
1640   ftintrz_w_s(scratch, scratch);
1641   movfr2gr_s(rd, scratch);
1642   Or(rd, rd, 1 << 31);
1643 
1644   Label done;
1645   Branch(&done);
1646   // Simple conversion.
1647   bind(&simple_convert);
1648   ftintrz_w_s(scratch, fj);
1649   movfr2gr_s(rd, scratch);
1650 
1651   bind(&done);
1652 }
1653 
1654 void TurboAssembler::Ftintrz_ul_d(Register rd, FPURegister fj,
1655                                   FPURegister scratch, Register result) {
1656   DCHECK(fj != scratch);
1657   DCHECK(result.is_valid() ? !AreAliased(rd, result, t7) : !AreAliased(rd, t7));
1658 
1659   Label simple_convert, done, fail;
1660   if (result.is_valid()) {
1661     mov(result, zero_reg);
1662     Move(scratch, -1.0);
1663     // If fd =< -1 or unordered, then the conversion fails.
1664     CompareF64(fj, scratch, CLE);
1665     BranchTrueShortF(&fail);
1666     CompareIsNanF64(fj, scratch);
1667     BranchTrueShortF(&fail);
1668   }
1669 
1670   // Load 2^63 into scratch as its double representation.
1671   li(t7, 0x43E0000000000000);
1672   movgr2fr_d(scratch, t7);
1673 
1674   // Test if scratch > fs.
1675   // If fs < 2^63 we can convert it normally.
1676   CompareF64(fj, scratch, CLT);
1677   BranchTrueShortF(&simple_convert);
1678 
1679   // First we subtract 2^63 from fs, then trunc it to rd
1680   // and add 2^63 to rd.
1681   fsub_d(scratch, fj, scratch);
1682   ftintrz_l_d(scratch, scratch);
1683   movfr2gr_d(rd, scratch);
1684   Or(rd, rd, Operand(1UL << 63));
1685   Branch(&done);
1686 
1687   // Simple conversion.
1688   bind(&simple_convert);
1689   ftintrz_l_d(scratch, fj);
1690   movfr2gr_d(rd, scratch);
1691 
1692   bind(&done);
1693   if (result.is_valid()) {
1694     // Conversion is failed if the result is negative.
1695     {
1696       UseScratchRegisterScope temps(this);
1697       Register scratch1 = temps.Acquire();
1698       addi_d(scratch1, zero_reg, -1);
1699       srli_d(scratch1, scratch1, 1);  // Load 2^62.
1700       movfr2gr_d(result, scratch);
1701       xor_(result, result, scratch1);
1702     }
1703     Slt(result, zero_reg, result);
1704   }
1705 
1706   bind(&fail);
1707 }
1708 
1709 void TurboAssembler::Ftintrz_ul_s(Register rd, FPURegister fj,
1710                                   FPURegister scratch, Register result) {
1711   DCHECK(fj != scratch);
1712   DCHECK(result.is_valid() ? !AreAliased(rd, result, t7) : !AreAliased(rd, t7));
1713 
1714   Label simple_convert, done, fail;
1715   if (result.is_valid()) {
1716     mov(result, zero_reg);
1717     Move(scratch, -1.0f);
1718     // If fd =< -1 or unordered, then the conversion fails.
1719     CompareF32(fj, scratch, CLE);
1720     BranchTrueShortF(&fail);
1721     CompareIsNanF32(fj, scratch);
1722     BranchTrueShortF(&fail);
1723   }
1724 
1725   {
1726     // Load 2^63 into scratch as its float representation.
1727     UseScratchRegisterScope temps(this);
1728     Register scratch1 = temps.Acquire();
1729     li(scratch1, 0x5F000000);
1730     movgr2fr_w(scratch, scratch1);
1731   }
1732 
1733   // Test if scratch > fs.
1734   // If fs < 2^63 we can convert it normally.
1735   CompareF32(fj, scratch, CLT);
1736   BranchTrueShortF(&simple_convert);
1737 
1738   // First we subtract 2^63 from fs, then trunc it to rd
1739   // and add 2^63 to rd.
1740   fsub_s(scratch, fj, scratch);
1741   ftintrz_l_s(scratch, scratch);
1742   movfr2gr_d(rd, scratch);
1743   Or(rd, rd, Operand(1UL << 63));
1744   Branch(&done);
1745 
1746   // Simple conversion.
1747   bind(&simple_convert);
1748   ftintrz_l_s(scratch, fj);
1749   movfr2gr_d(rd, scratch);
1750 
1751   bind(&done);
1752   if (result.is_valid()) {
1753     // Conversion is failed if the result is negative or unordered.
1754     {
1755       UseScratchRegisterScope temps(this);
1756       Register scratch1 = temps.Acquire();
1757       addi_d(scratch1, zero_reg, -1);
1758       srli_d(scratch1, scratch1, 1);  // Load 2^62.
1759       movfr2gr_d(result, scratch);
1760       xor_(result, result, scratch1);
1761     }
1762     Slt(result, zero_reg, result);
1763   }
1764 
1765   bind(&fail);
1766 }
1767 
1768 void TurboAssembler::RoundDouble(FPURegister dst, FPURegister src,
1769                                  FPURoundingMode mode) {
1770   BlockTrampolinePoolScope block_trampoline_pool(this);
1771   Register scratch = t8;
1772   movfcsr2gr(scratch);
1773   li(t7, Operand(mode));
1774   movgr2fcsr(t7);
1775   frint_d(dst, src);
1776   movgr2fcsr(scratch);
1777 }
1778 
1779 void TurboAssembler::Floor_d(FPURegister dst, FPURegister src) {
1780   RoundDouble(dst, src, mode_floor);
1781 }
1782 
1783 void TurboAssembler::Ceil_d(FPURegister dst, FPURegister src) {
1784   RoundDouble(dst, src, mode_ceil);
1785 }
1786 
1787 void TurboAssembler::Trunc_d(FPURegister dst, FPURegister src) {
1788   RoundDouble(dst, src, mode_trunc);
1789 }
1790 
1791 void TurboAssembler::Round_d(FPURegister dst, FPURegister src) {
1792   RoundDouble(dst, src, mode_round);
1793 }
1794 
1795 void TurboAssembler::RoundFloat(FPURegister dst, FPURegister src,
1796                                 FPURoundingMode mode) {
1797   BlockTrampolinePoolScope block_trampoline_pool(this);
1798   Register scratch = t8;
1799   movfcsr2gr(scratch);
1800   li(t7, Operand(mode));
1801   movgr2fcsr(t7);
1802   frint_s(dst, src);
1803   movgr2fcsr(scratch);
1804 }
1805 
1806 void TurboAssembler::Floor_s(FPURegister dst, FPURegister src) {
1807   RoundFloat(dst, src, mode_floor);
1808 }
1809 
1810 void TurboAssembler::Ceil_s(FPURegister dst, FPURegister src) {
1811   RoundFloat(dst, src, mode_ceil);
1812 }
1813 
1814 void TurboAssembler::Trunc_s(FPURegister dst, FPURegister src) {
1815   RoundFloat(dst, src, mode_trunc);
1816 }
1817 
1818 void TurboAssembler::Round_s(FPURegister dst, FPURegister src) {
1819   RoundFloat(dst, src, mode_round);
1820 }
1821 
1822 void TurboAssembler::CompareF(FPURegister cmp1, FPURegister cmp2,
1823                               FPUCondition cc, CFRegister cd, bool f32) {
1824   if (f32) {
1825     fcmp_cond_s(cc, cmp1, cmp2, cd);
1826   } else {
1827     fcmp_cond_d(cc, cmp1, cmp2, cd);
1828   }
1829 }
1830 
1831 void TurboAssembler::CompareIsNanF(FPURegister cmp1, FPURegister cmp2,
1832                                    CFRegister cd, bool f32) {
1833   CompareF(cmp1, cmp2, CUN, cd, f32);
1834 }
1835 
1836 void TurboAssembler::BranchTrueShortF(Label* target, CFRegister cj) {
1837   bcnez(cj, target);
1838 }
1839 
1840 void TurboAssembler::BranchFalseShortF(Label* target, CFRegister cj) {
1841   bceqz(cj, target);
1842 }
1843 
1844 void TurboAssembler::BranchTrueF(Label* target, CFRegister cj) {
1845   // TODO(yuyin): can be optimzed
1846   bool long_branch = target->is_bound()
1847                          ? !is_near(target, OffsetSize::kOffset21)
1848                          : is_trampoline_emitted();
1849   if (long_branch) {
1850     Label skip;
1851     BranchFalseShortF(&skip, cj);
1852     Branch(target);
1853     bind(&skip);
1854   } else {
1855     BranchTrueShortF(target, cj);
1856   }
1857 }
1858 
1859 void TurboAssembler::BranchFalseF(Label* target, CFRegister cj) {
1860   bool long_branch = target->is_bound()
1861                          ? !is_near(target, OffsetSize::kOffset21)
1862                          : is_trampoline_emitted();
1863   if (long_branch) {
1864     Label skip;
1865     BranchTrueShortF(&skip, cj);
1866     Branch(target);
1867     bind(&skip);
1868   } else {
1869     BranchFalseShortF(target, cj);
1870   }
1871 }
1872 
1873 void TurboAssembler::FmoveLow(FPURegister dst, Register src_low) {
1874   UseScratchRegisterScope temps(this);
1875   Register scratch = temps.Acquire();
1876   DCHECK(src_low != scratch);
1877   movfrh2gr_s(scratch, dst);
1878   movgr2fr_w(dst, src_low);
1879   movgr2frh_w(dst, scratch);
1880 }
1881 
1882 void TurboAssembler::Move(FPURegister dst, uint32_t src) {
1883   UseScratchRegisterScope temps(this);
1884   Register scratch = temps.Acquire();
1885   li(scratch, Operand(static_cast<int32_t>(src)));
1886   movgr2fr_w(dst, scratch);
1887 }
1888 
1889 void TurboAssembler::Move(FPURegister dst, uint64_t src) {
1890   // Handle special values first.
1891   if (src == bit_cast<uint64_t>(0.0) && has_double_zero_reg_set_) {
1892     fmov_d(dst, kDoubleRegZero);
1893   } else if (src == bit_cast<uint64_t>(-0.0) && has_double_zero_reg_set_) {
1894     Neg_d(dst, kDoubleRegZero);
1895   } else {
1896     UseScratchRegisterScope temps(this);
1897     Register scratch = temps.Acquire();
1898     li(scratch, Operand(static_cast<int64_t>(src)));
1899     movgr2fr_d(dst, scratch);
1900     if (dst == kDoubleRegZero) has_double_zero_reg_set_ = true;
1901   }
1902 }
1903 
1904 void TurboAssembler::Movz(Register rd, Register rj, Register rk) {
1905   UseScratchRegisterScope temps(this);
1906   Register scratch = temps.Acquire();
1907   masknez(scratch, rj, rk);
1908   maskeqz(rd, rd, rk);
1909   or_(rd, rd, scratch);
1910 }
1911 
1912 void TurboAssembler::Movn(Register rd, Register rj, Register rk) {
1913   UseScratchRegisterScope temps(this);
1914   Register scratch = temps.Acquire();
1915   maskeqz(scratch, rj, rk);
1916   masknez(rd, rd, rk);
1917   or_(rd, rd, scratch);
1918 }
1919 
1920 void TurboAssembler::LoadZeroOnCondition(Register rd, Register rj,
1921                                          const Operand& rk, Condition cond) {
1922   BlockTrampolinePoolScope block_trampoline_pool(this);
1923   switch (cond) {
1924     case cc_always:
1925       mov(rd, zero_reg);
1926       break;
1927     case eq:
1928       if (rj == zero_reg) {
1929         if (rk.is_reg()) {
1930           LoadZeroIfConditionZero(rd, rk.rm());
1931         } else if (rk.immediate() == 0) {
1932           mov(rd, zero_reg);
1933         }
1934       } else if (IsZero(rk)) {
1935         LoadZeroIfConditionZero(rd, rj);
1936       } else {
1937         Sub_d(t7, rj, rk);
1938         LoadZeroIfConditionZero(rd, t7);
1939       }
1940       break;
1941     case ne:
1942       if (rj == zero_reg) {
1943         if (rk.is_reg()) {
1944           LoadZeroIfConditionNotZero(rd, rk.rm());
1945         } else if (rk.immediate() != 0) {
1946           mov(rd, zero_reg);
1947         }
1948       } else if (IsZero(rk)) {
1949         LoadZeroIfConditionNotZero(rd, rj);
1950       } else {
1951         Sub_d(t7, rj, rk);
1952         LoadZeroIfConditionNotZero(rd, t7);
1953       }
1954       break;
1955 
1956     // Signed comparison.
1957     case greater:
1958       Sgt(t7, rj, rk);
1959       LoadZeroIfConditionNotZero(rd, t7);
1960       break;
1961     case greater_equal:
1962       Sge(t7, rj, rk);
1963       LoadZeroIfConditionNotZero(rd, t7);
1964       // rj >= rk
1965       break;
1966     case less:
1967       Slt(t7, rj, rk);
1968       LoadZeroIfConditionNotZero(rd, t7);
1969       // rj < rk
1970       break;
1971     case less_equal:
1972       Sle(t7, rj, rk);
1973       LoadZeroIfConditionNotZero(rd, t7);
1974       // rj <= rk
1975       break;
1976 
1977     // Unsigned comparison.
1978     case Ugreater:
1979       Sgtu(t7, rj, rk);
1980       LoadZeroIfConditionNotZero(rd, t7);
1981       // rj > rk
1982       break;
1983 
1984     case Ugreater_equal:
1985       Sgeu(t7, rj, rk);
1986       LoadZeroIfConditionNotZero(rd, t7);
1987       // rj >= rk
1988       break;
1989     case Uless:
1990       Sltu(t7, rj, rk);
1991       LoadZeroIfConditionNotZero(rd, t7);
1992       // rj < rk
1993       break;
1994     case Uless_equal:
1995       Sleu(t7, rj, rk);
1996       LoadZeroIfConditionNotZero(rd, t7);
1997       // rj <= rk
1998       break;
1999     default:
2000       UNREACHABLE();
2001   }  // namespace internal
2002 }  // namespace internal
2003 
2004 void TurboAssembler::LoadZeroIfConditionNotZero(Register dest,
2005                                                 Register condition) {
2006   masknez(dest, dest, condition);
2007 }
2008 
2009 void TurboAssembler::LoadZeroIfConditionZero(Register dest,
2010                                              Register condition) {
2011   maskeqz(dest, dest, condition);
2012 }
2013 
2014 void TurboAssembler::LoadZeroIfFPUCondition(Register dest, CFRegister cc) {
2015   UseScratchRegisterScope temps(this);
2016   Register scratch = temps.Acquire();
2017   movcf2gr(scratch, cc);
2018   LoadZeroIfConditionNotZero(dest, scratch);
2019 }
2020 
2021 void TurboAssembler::LoadZeroIfNotFPUCondition(Register dest, CFRegister cc) {
2022   UseScratchRegisterScope temps(this);
2023   Register scratch = temps.Acquire();
2024   movcf2gr(scratch, cc);
2025   LoadZeroIfConditionZero(dest, scratch);
2026 }
2027 
2028 void TurboAssembler::Clz_w(Register rd, Register rj) { clz_w(rd, rj); }
2029 
2030 void TurboAssembler::Clz_d(Register rd, Register rj) { clz_d(rd, rj); }
2031 
2032 void TurboAssembler::Ctz_w(Register rd, Register rj) { ctz_w(rd, rj); }
2033 
2034 void TurboAssembler::Ctz_d(Register rd, Register rj) { ctz_d(rd, rj); }
2035 
2036 // TODO(LOONG_dev): Optimize like arm64, use simd instruction
2037 void TurboAssembler::Popcnt_w(Register rd, Register rj) {
2038   ASM_CODE_COMMENT(this);
2039   // https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
2040   //
2041   // A generalization of the best bit counting method to integers of
2042   // bit-widths up to 128 (parameterized by type T) is this:
2043   //
2044   // v = v - ((v >> 1) & (T)~(T)0/3);                           // temp
2045   // v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3);      // temp
2046   // v = (v + (v >> 4)) & (T)~(T)0/255*15;                      // temp
2047   // c = (T)(v * ((T)~(T)0/255)) >> (sizeof(T) - 1) * BITS_PER_BYTE; //count
2048   //
2049   // There are algorithms which are faster in the cases where very few
2050   // bits are set but the algorithm here attempts to minimize the total
2051   // number of instructions executed even when a large number of bits
2052   // are set.
2053   int32_t B0 = 0x55555555;     // (T)~(T)0/3
2054   int32_t B1 = 0x33333333;     // (T)~(T)0/15*3
2055   int32_t B2 = 0x0F0F0F0F;     // (T)~(T)0/255*15
2056   int32_t value = 0x01010101;  // (T)~(T)0/255
2057   uint32_t shift = 24;         // (sizeof(T) - 1) * BITS_PER_BYTE
2058 
2059   UseScratchRegisterScope temps(this);
2060   BlockTrampolinePoolScope block_trampoline_pool(this);
2061   Register scratch = temps.Acquire();
2062   Register scratch2 = t8;
2063   srli_w(scratch, rj, 1);
2064   li(scratch2, B0);
2065   And(scratch, scratch, scratch2);
2066   Sub_w(scratch, rj, scratch);
2067   li(scratch2, B1);
2068   And(rd, scratch, scratch2);
2069   srli_w(scratch, scratch, 2);
2070   And(scratch, scratch, scratch2);
2071   Add_w(scratch, rd, scratch);
2072   srli_w(rd, scratch, 4);
2073   Add_w(rd, rd, scratch);
2074   li(scratch2, B2);
2075   And(rd, rd, scratch2);
2076   li(scratch, value);
2077   Mul_w(rd, rd, scratch);
2078   srli_w(rd, rd, shift);
2079 }
2080 
2081 void TurboAssembler::Popcnt_d(Register rd, Register rj) {
2082   ASM_CODE_COMMENT(this);
2083   int64_t B0 = 0x5555555555555555l;     // (T)~(T)0/3
2084   int64_t B1 = 0x3333333333333333l;     // (T)~(T)0/15*3
2085   int64_t B2 = 0x0F0F0F0F0F0F0F0Fl;     // (T)~(T)0/255*15
2086   int64_t value = 0x0101010101010101l;  // (T)~(T)0/255
2087   uint32_t shift = 56;                  // (sizeof(T) - 1) * BITS_PER_BYTE
2088 
2089   UseScratchRegisterScope temps(this);
2090   BlockTrampolinePoolScope block_trampoline_pool(this);
2091   Register scratch = temps.Acquire();
2092   Register scratch2 = t8;
2093   srli_d(scratch, rj, 1);
2094   li(scratch2, B0);
2095   And(scratch, scratch, scratch2);
2096   Sub_d(scratch, rj, scratch);
2097   li(scratch2, B1);
2098   And(rd, scratch, scratch2);
2099   srli_d(scratch, scratch, 2);
2100   And(scratch, scratch, scratch2);
2101   Add_d(scratch, rd, scratch);
2102   srli_d(rd, scratch, 4);
2103   Add_d(rd, rd, scratch);
2104   li(scratch2, B2);
2105   And(rd, rd, scratch2);
2106   li(scratch, value);
2107   Mul_d(rd, rd, scratch);
2108   srli_d(rd, rd, shift);
2109 }
2110 
2111 void TurboAssembler::ExtractBits(Register dest, Register source, Register pos,
2112                                  int size, bool sign_extend) {
2113   sra_d(dest, source, pos);
2114   bstrpick_d(dest, dest, size - 1, 0);
2115   if (sign_extend) {
2116     switch (size) {
2117       case 8:
2118         ext_w_b(dest, dest);
2119         break;
2120       case 16:
2121         ext_w_h(dest, dest);
2122         break;
2123       case 32:
2124         // sign-extend word
2125         slli_w(dest, dest, 0);
2126         break;
2127       default:
2128         UNREACHABLE();
2129     }
2130   }
2131 }
2132 
2133 void TurboAssembler::InsertBits(Register dest, Register source, Register pos,
2134                                 int size) {
2135   Rotr_d(dest, dest, pos);
2136   bstrins_d(dest, source, size - 1, 0);
2137   {
2138     UseScratchRegisterScope temps(this);
2139     Register scratch = temps.Acquire();
2140     Sub_d(scratch, zero_reg, pos);
2141     Rotr_d(dest, dest, scratch);
2142   }
2143 }
2144 
2145 void TurboAssembler::TryInlineTruncateDoubleToI(Register result,
2146                                                 DoubleRegister double_input,
2147                                                 Label* done) {
2148   DoubleRegister single_scratch = kScratchDoubleReg.low();
2149   BlockTrampolinePoolScope block_trampoline_pool(this);
2150   UseScratchRegisterScope temps(this);
2151   Register scratch = temps.Acquire();
2152   Register scratch2 = temps.Acquire();
2153 
2154   ftintrz_l_d(single_scratch, double_input);
2155   movfr2gr_d(scratch2, single_scratch);
2156   li(scratch, 1L << 63);
2157   Xor(scratch, scratch, scratch2);
2158   rotri_d(scratch2, scratch, 1);
2159   movfr2gr_s(result, single_scratch);
2160   Branch(done, ne, scratch, Operand(scratch2));
2161 
2162   // Truncate NaN to zero.
2163   CompareIsNanF64(double_input, double_input);
2164   Move(result, zero_reg);
2165   bcnez(FCC0, done);
2166 }
2167 
2168 void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone,
2169                                        Register result,
2170                                        DoubleRegister double_input,
2171                                        StubCallMode stub_mode) {
2172   Label done;
2173 
2174   TryInlineTruncateDoubleToI(result, double_input, &done);
2175 
2176   // If we fell through then inline version didn't succeed - call stub instead.
2177   Sub_d(sp, sp,
2178         Operand(kDoubleSize + kSystemPointerSize));  // Put input on stack.
2179   St_d(ra, MemOperand(sp, kSystemPointerSize));
2180   Fst_d(double_input, MemOperand(sp, 0));
2181 
2182 #if V8_ENABLE_WEBASSEMBLY
2183   if (stub_mode == StubCallMode::kCallWasmRuntimeStub) {
2184     Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL);
2185 #else
2186   // For balance.
2187   if (false) {
2188 #endif  // V8_ENABLE_WEBASSEMBLY
2189   } else {
2190     Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET);
2191   }
2192 
2193   Pop(ra, result);
2194   bind(&done);
2195 }
2196 
2197 // BRANCH_ARGS_CHECK checks that conditional jump arguments are correct.
2198 #define BRANCH_ARGS_CHECK(cond, rj, rk)                                  \
2199   DCHECK((cond == cc_always && rj == zero_reg && rk.rm() == zero_reg) || \
2200          (cond != cc_always && (rj != zero_reg || rk.rm() != zero_reg)))
2201 
2202 void TurboAssembler::Branch(Label* L, bool need_link) {
2203   int offset = GetOffset(L, OffsetSize::kOffset26);
2204   if (need_link) {
2205     bl(offset);
2206   } else {
2207     b(offset);
2208   }
2209 }
2210 
2211 void TurboAssembler::Branch(Label* L, Condition cond, Register rj,
2212                             const Operand& rk, bool need_link) {
2213   if (L->is_bound()) {
2214     BRANCH_ARGS_CHECK(cond, rj, rk);
2215     if (!BranchShortOrFallback(L, cond, rj, rk, need_link)) {
2216       if (cond != cc_always) {
2217         Label skip;
2218         Condition neg_cond = NegateCondition(cond);
2219         BranchShort(&skip, neg_cond, rj, rk, need_link);
2220         Branch(L, need_link);
2221         bind(&skip);
2222       } else {
2223         Branch(L);
2224       }
2225     }
2226   } else {
2227     if (is_trampoline_emitted()) {
2228       if (cond != cc_always) {
2229         Label skip;
2230         Condition neg_cond = NegateCondition(cond);
2231         BranchShort(&skip, neg_cond, rj, rk, need_link);
2232         Branch(L, need_link);
2233         bind(&skip);
2234       } else {
2235         Branch(L);
2236       }
2237     } else {
2238       BranchShort(L, cond, rj, rk, need_link);
2239     }
2240   }
2241 }
2242 
2243 void TurboAssembler::Branch(Label* L, Condition cond, Register rj,
2244                             RootIndex index) {
2245   UseScratchRegisterScope temps(this);
2246   Register scratch = temps.Acquire();
2247   LoadRoot(scratch, index);
2248   Branch(L, cond, rj, Operand(scratch));
2249 }
2250 
2251 int32_t TurboAssembler::GetOffset(Label* L, OffsetSize bits) {
2252   return branch_offset_helper(L, bits) >> 2;
2253 }
2254 
2255 Register TurboAssembler::GetRkAsRegisterHelper(const Operand& rk,
2256                                                Register scratch) {
2257   Register r2 = no_reg;
2258   if (rk.is_reg()) {
2259     r2 = rk.rm();
2260   } else {
2261     r2 = scratch;
2262     li(r2, rk);
2263   }
2264 
2265   return r2;
2266 }
2267 
2268 bool TurboAssembler::BranchShortOrFallback(Label* L, Condition cond,
2269                                            Register rj, const Operand& rk,
2270                                            bool need_link) {
2271   UseScratchRegisterScope temps(this);
2272   BlockTrampolinePoolScope block_trampoline_pool(this);
2273   Register scratch = temps.hasAvailable() ? temps.Acquire() : t8;
2274   DCHECK_NE(rj, zero_reg);
2275 
2276   // Be careful to always use shifted_branch_offset only just before the
2277   // branch instruction, as the location will be remember for patching the
2278   // target.
2279   {
2280     BlockTrampolinePoolScope block_trampoline_pool(this);
2281     int offset = 0;
2282     switch (cond) {
2283       case cc_always:
2284         if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2285         offset = GetOffset(L, OffsetSize::kOffset26);
2286         if (need_link) {
2287           bl(offset);
2288         } else {
2289           b(offset);
2290         }
2291         break;
2292       case eq:
2293         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2294           // beq is used here to make the code patchable. Otherwise b should
2295           // be used which has no condition field so is not patchable.
2296           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2297           if (need_link) pcaddi(ra, 2);
2298           offset = GetOffset(L, OffsetSize::kOffset16);
2299           beq(rj, rj, offset);
2300         } else if (IsZero(rk)) {
2301           if (L->is_bound() && !is_near(L, OffsetSize::kOffset21)) return false;
2302           if (need_link) pcaddi(ra, 2);
2303           offset = GetOffset(L, OffsetSize::kOffset21);
2304           beqz(rj, offset);
2305         } else {
2306           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2307           if (need_link) pcaddi(ra, 2);
2308           // We don't want any other register but scratch clobbered.
2309           Register sc = GetRkAsRegisterHelper(rk, scratch);
2310           offset = GetOffset(L, OffsetSize::kOffset16);
2311           beq(rj, sc, offset);
2312         }
2313         break;
2314       case ne:
2315         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2316           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2317           if (need_link) pcaddi(ra, 2);
2318           // bne is used here to make the code patchable. Otherwise we
2319           // should not generate any instruction.
2320           offset = GetOffset(L, OffsetSize::kOffset16);
2321           bne(rj, rj, offset);
2322         } else if (IsZero(rk)) {
2323           if (L->is_bound() && !is_near(L, OffsetSize::kOffset21)) return false;
2324           if (need_link) pcaddi(ra, 2);
2325           offset = GetOffset(L, OffsetSize::kOffset21);
2326           bnez(rj, offset);
2327         } else {
2328           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2329           if (need_link) pcaddi(ra, 2);
2330           // We don't want any other register but scratch clobbered.
2331           Register sc = GetRkAsRegisterHelper(rk, scratch);
2332           offset = GetOffset(L, OffsetSize::kOffset16);
2333           bne(rj, sc, offset);
2334         }
2335         break;
2336 
2337       // Signed comparison.
2338       case greater:
2339         // rj > rk
2340         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2341           // No code needs to be emitted.
2342         } else if (IsZero(rk)) {
2343           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2344           if (need_link) pcaddi(ra, 2);
2345           offset = GetOffset(L, OffsetSize::kOffset16);
2346           blt(zero_reg, rj, offset);
2347         } else {
2348           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2349           if (need_link) pcaddi(ra, 2);
2350           Register sc = GetRkAsRegisterHelper(rk, scratch);
2351           DCHECK(rj != sc);
2352           offset = GetOffset(L, OffsetSize::kOffset16);
2353           blt(sc, rj, offset);
2354         }
2355         break;
2356       case greater_equal:
2357         // rj >= rk
2358         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2359           if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2360           if (need_link) pcaddi(ra, 2);
2361           offset = GetOffset(L, OffsetSize::kOffset26);
2362           b(offset);
2363         } else if (IsZero(rk)) {
2364           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2365           if (need_link) pcaddi(ra, 2);
2366           offset = GetOffset(L, OffsetSize::kOffset16);
2367           bge(rj, zero_reg, offset);
2368         } else {
2369           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2370           if (need_link) pcaddi(ra, 2);
2371           Register sc = GetRkAsRegisterHelper(rk, scratch);
2372           DCHECK(rj != sc);
2373           offset = GetOffset(L, OffsetSize::kOffset16);
2374           bge(rj, sc, offset);
2375         }
2376         break;
2377       case less:
2378         // rj < rk
2379         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2380           // No code needs to be emitted.
2381         } else if (IsZero(rk)) {
2382           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2383           if (need_link) pcaddi(ra, 2);
2384           offset = GetOffset(L, OffsetSize::kOffset16);
2385           blt(rj, zero_reg, offset);
2386         } else {
2387           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2388           if (need_link) pcaddi(ra, 2);
2389           Register sc = GetRkAsRegisterHelper(rk, scratch);
2390           DCHECK(rj != sc);
2391           offset = GetOffset(L, OffsetSize::kOffset16);
2392           blt(rj, sc, offset);
2393         }
2394         break;
2395       case less_equal:
2396         // rj <= rk
2397         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2398           if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2399           if (need_link) pcaddi(ra, 2);
2400           offset = GetOffset(L, OffsetSize::kOffset26);
2401           b(offset);
2402         } else if (IsZero(rk)) {
2403           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2404           if (need_link) pcaddi(ra, 2);
2405           offset = GetOffset(L, OffsetSize::kOffset16);
2406           bge(zero_reg, rj, offset);
2407         } else {
2408           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2409           if (need_link) pcaddi(ra, 2);
2410           Register sc = GetRkAsRegisterHelper(rk, scratch);
2411           DCHECK(rj != sc);
2412           offset = GetOffset(L, OffsetSize::kOffset16);
2413           bge(sc, rj, offset);
2414         }
2415         break;
2416 
2417       // Unsigned comparison.
2418       case Ugreater:
2419         // rj > rk
2420         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2421           // No code needs to be emitted.
2422         } else if (IsZero(rk)) {
2423           if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2424           if (need_link) pcaddi(ra, 2);
2425           offset = GetOffset(L, OffsetSize::kOffset26);
2426           bnez(rj, offset);
2427         } else {
2428           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2429           if (need_link) pcaddi(ra, 2);
2430           Register sc = GetRkAsRegisterHelper(rk, scratch);
2431           DCHECK(rj != sc);
2432           offset = GetOffset(L, OffsetSize::kOffset16);
2433           bltu(sc, rj, offset);
2434         }
2435         break;
2436       case Ugreater_equal:
2437         // rj >= rk
2438         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2439           if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2440           if (need_link) pcaddi(ra, 2);
2441           offset = GetOffset(L, OffsetSize::kOffset26);
2442           b(offset);
2443         } else if (IsZero(rk)) {
2444           if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2445           if (need_link) pcaddi(ra, 2);
2446           offset = GetOffset(L, OffsetSize::kOffset26);
2447           b(offset);
2448         } else {
2449           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2450           if (need_link) pcaddi(ra, 2);
2451           Register sc = GetRkAsRegisterHelper(rk, scratch);
2452           DCHECK(rj != sc);
2453           offset = GetOffset(L, OffsetSize::kOffset16);
2454           bgeu(rj, sc, offset);
2455         }
2456         break;
2457       case Uless:
2458         // rj < rk
2459         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2460           // No code needs to be emitted.
2461         } else if (IsZero(rk)) {
2462           // No code needs to be emitted.
2463         } else {
2464           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2465           if (need_link) pcaddi(ra, 2);
2466           Register sc = GetRkAsRegisterHelper(rk, scratch);
2467           DCHECK(rj != sc);
2468           offset = GetOffset(L, OffsetSize::kOffset16);
2469           bltu(rj, sc, offset);
2470         }
2471         break;
2472       case Uless_equal:
2473         // rj <= rk
2474         if (rk.is_reg() && rj.code() == rk.rm().code()) {
2475           if (L->is_bound() && !is_near(L, OffsetSize::kOffset26)) return false;
2476           if (need_link) pcaddi(ra, 2);
2477           offset = GetOffset(L, OffsetSize::kOffset26);
2478           b(offset);
2479         } else if (IsZero(rk)) {
2480           if (L->is_bound() && !is_near(L, OffsetSize::kOffset21)) return false;
2481           if (need_link) pcaddi(ra, 2);
2482           beqz(rj, L);
2483         } else {
2484           if (L->is_bound() && !is_near(L, OffsetSize::kOffset16)) return false;
2485           if (need_link) pcaddi(ra, 2);
2486           Register sc = GetRkAsRegisterHelper(rk, scratch);
2487           DCHECK(rj != sc);
2488           offset = GetOffset(L, OffsetSize::kOffset16);
2489           bgeu(sc, rj, offset);
2490         }
2491         break;
2492       default:
2493         UNREACHABLE();
2494     }
2495   }
2496   return true;
2497 }
2498 
2499 void TurboAssembler::BranchShort(Label* L, Condition cond, Register rj,
2500                                  const Operand& rk, bool need_link) {
2501   BRANCH_ARGS_CHECK(cond, rj, rk);
2502   bool result = BranchShortOrFallback(L, cond, rj, rk, need_link);
2503   DCHECK(result);
2504   USE(result);
2505 }
2506 
2507 void TurboAssembler::LoadFromConstantsTable(Register destination,
2508                                             int constant_index) {
2509   ASM_CODE_COMMENT(this);
2510   DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable));
2511   LoadRoot(destination, RootIndex::kBuiltinsConstantsTable);
2512   Ld_d(destination,
2513        FieldMemOperand(destination, FixedArray::kHeaderSize +
2514                                         constant_index * kPointerSize));
2515 }
2516 
2517 void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
2518   Ld_d(destination, MemOperand(kRootRegister, offset));
2519 }
2520 
2521 void TurboAssembler::LoadRootRegisterOffset(Register destination,
2522                                             intptr_t offset) {
2523   if (offset == 0) {
2524     Move(destination, kRootRegister);
2525   } else {
2526     Add_d(destination, kRootRegister, Operand(offset));
2527   }
2528 }
2529 
2530 void TurboAssembler::Jump(Register target, Condition cond, Register rj,
2531                           const Operand& rk) {
2532   BlockTrampolinePoolScope block_trampoline_pool(this);
2533   if (cond == cc_always) {
2534     jirl(zero_reg, target, 0);
2535   } else {
2536     BRANCH_ARGS_CHECK(cond, rj, rk);
2537     Label skip;
2538     Branch(&skip, NegateCondition(cond), rj, rk);
2539     jirl(zero_reg, target, 0);
2540     bind(&skip);
2541   }
2542 }
2543 
2544 void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
2545                           Condition cond, Register rj, const Operand& rk) {
2546   Label skip;
2547   if (cond != cc_always) {
2548     Branch(&skip, NegateCondition(cond), rj, rk);
2549   }
2550   {
2551     BlockTrampolinePoolScope block_trampoline_pool(this);
2552     li(t7, Operand(target, rmode));
2553     jirl(zero_reg, t7, 0);
2554     bind(&skip);
2555   }
2556 }
2557 
2558 void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode, Condition cond,
2559                           Register rj, const Operand& rk) {
2560   DCHECK(!RelocInfo::IsCodeTarget(rmode));
2561   Jump(static_cast<intptr_t>(target), rmode, cond, rj, rk);
2562 }
2563 
2564 void TurboAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode,
2565                           Condition cond, Register rj, const Operand& rk) {
2566   DCHECK(RelocInfo::IsCodeTarget(rmode));
2567 
2568   BlockTrampolinePoolScope block_trampoline_pool(this);
2569   Label skip;
2570   if (cond != cc_always) {
2571     BranchShort(&skip, NegateCondition(cond), rj, rk);
2572   }
2573 
2574   Builtin builtin = Builtin::kNoBuiltinId;
2575   bool target_is_isolate_independent_builtin =
2576       isolate()->builtins()->IsBuiltinHandle(code, &builtin) &&
2577       Builtins::IsIsolateIndependent(builtin);
2578   if (target_is_isolate_independent_builtin &&
2579       options().use_pc_relative_calls_and_jumps) {
2580     int32_t code_target_index = AddCodeTarget(code);
2581     RecordRelocInfo(RelocInfo::RELATIVE_CODE_TARGET);
2582     b(code_target_index);
2583     bind(&skip);
2584     return;
2585   } else if (root_array_available_ && options().isolate_independent_code) {
2586     UNREACHABLE();
2587     /*int offset = code->builtin_index() * kSystemPointerSize +
2588                  IsolateData::builtin_entry_table_offset();
2589     Ld_d(t7, MemOperand(kRootRegister, offset));
2590     Jump(t7, cc_always, rj, rk);
2591     bind(&skip);
2592     return;*/
2593   } else if (options().inline_offheap_trampolines &&
2594              target_is_isolate_independent_builtin) {
2595     // Inline the trampoline.
2596     RecordCommentForOffHeapTrampoline(builtin);
2597     li(t7, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
2598     Jump(t7, cc_always, rj, rk);
2599     bind(&skip);
2600     RecordComment("]");
2601     return;
2602   }
2603 
2604   Jump(static_cast<intptr_t>(code.address()), rmode, cc_always, rj, rk);
2605   bind(&skip);
2606 }
2607 
2608 void TurboAssembler::Jump(const ExternalReference& reference) {
2609   li(t7, reference);
2610   Jump(t7);
2611 }
2612 
2613 // Note: To call gcc-compiled C code on loonarch, you must call through t[0-8].
2614 void TurboAssembler::Call(Register target, Condition cond, Register rj,
2615                           const Operand& rk) {
2616   BlockTrampolinePoolScope block_trampoline_pool(this);
2617   if (cond == cc_always) {
2618     jirl(ra, target, 0);
2619   } else {
2620     BRANCH_ARGS_CHECK(cond, rj, rk);
2621     Label skip;
2622     Branch(&skip, NegateCondition(cond), rj, rk);
2623     jirl(ra, target, 0);
2624     bind(&skip);
2625   }
2626   set_pc_for_safepoint();
2627 }
2628 
2629 void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
2630                                      unsigned higher_limit,
2631                                      Label* on_in_range) {
2632   ASM_CODE_COMMENT(this);
2633   if (lower_limit != 0) {
2634     UseScratchRegisterScope temps(this);
2635     Register scratch = temps.Acquire();
2636     Sub_d(scratch, value, Operand(lower_limit));
2637     Branch(on_in_range, ls, scratch, Operand(higher_limit - lower_limit));
2638   } else {
2639     Branch(on_in_range, ls, value, Operand(higher_limit - lower_limit));
2640   }
2641 }
2642 
2643 void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond,
2644                           Register rj, const Operand& rk) {
2645   BlockTrampolinePoolScope block_trampoline_pool(this);
2646   Label skip;
2647   if (cond != cc_always) {
2648     BranchShort(&skip, NegateCondition(cond), rj, rk);
2649   }
2650   intptr_t offset_diff = target - pc_offset();
2651   if (RelocInfo::IsNoInfo(rmode) && is_int28(offset_diff)) {
2652     bl(offset_diff >> 2);
2653   } else if (RelocInfo::IsNoInfo(rmode) && is_int38(offset_diff)) {
2654     pcaddu18i(t7, static_cast<int32_t>(offset_diff) >> 18);
2655     jirl(ra, t7, (offset_diff & 0x3ffff) >> 2);
2656   } else {
2657     li(t7, Operand(static_cast<int64_t>(target), rmode), ADDRESS_LOAD);
2658     Call(t7, cc_always, rj, rk);
2659   }
2660   bind(&skip);
2661 }
2662 
2663 void TurboAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
2664                           Condition cond, Register rj, const Operand& rk) {
2665   BlockTrampolinePoolScope block_trampoline_pool(this);
2666   Label skip;
2667   if (cond != cc_always) {
2668     BranchShort(&skip, NegateCondition(cond), rj, rk);
2669   }
2670 
2671   Builtin builtin = Builtin::kNoBuiltinId;
2672   bool target_is_isolate_independent_builtin =
2673       isolate()->builtins()->IsBuiltinHandle(code, &builtin) &&
2674       Builtins::IsIsolateIndependent(builtin);
2675 
2676   if (target_is_isolate_independent_builtin &&
2677       options().use_pc_relative_calls_and_jumps) {
2678     int32_t code_target_index = AddCodeTarget(code);
2679     RecordCommentForOffHeapTrampoline(builtin);
2680     RecordRelocInfo(RelocInfo::RELATIVE_CODE_TARGET);
2681     bl(code_target_index);
2682     set_pc_for_safepoint();
2683     bind(&skip);
2684     RecordComment("]");
2685     return;
2686   } else if (root_array_available_ && options().isolate_independent_code) {
2687     UNREACHABLE();
2688     /*int offset = code->builtin_index() * kSystemPointerSize +
2689                  IsolateData::builtin_entry_table_offset();
2690     LoadRootRelative(t7, offset);
2691     Call(t7, cond, rj, rk);
2692     bind(&skip);
2693     return;*/
2694   } else if (options().inline_offheap_trampolines &&
2695              target_is_isolate_independent_builtin) {
2696     // Inline the trampoline.
2697     RecordCommentForOffHeapTrampoline(builtin);
2698     li(t7, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
2699     Call(t7, cond, rj, rk);
2700     bind(&skip);
2701     RecordComment("]");
2702     return;
2703   }
2704 
2705   DCHECK(RelocInfo::IsCodeTarget(rmode));
2706   DCHECK(code->IsExecutable());
2707   Call(code.address(), rmode, cc_always, rj, rk);
2708   bind(&skip);
2709 }
2710 
2711 void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) {
2712   ASM_CODE_COMMENT(this);
2713   STATIC_ASSERT(kSystemPointerSize == 8);
2714   STATIC_ASSERT(kSmiTagSize == 1);
2715   STATIC_ASSERT(kSmiTag == 0);
2716 
2717   // The builtin_index register contains the builtin index as a Smi.
2718   SmiUntag(builtin_index, builtin_index);
2719   Alsl_d(builtin_index, builtin_index, kRootRegister, kSystemPointerSizeLog2,
2720          t7);
2721   Ld_d(builtin_index,
2722        MemOperand(builtin_index, IsolateData::builtin_entry_table_offset()));
2723 }
2724 
2725 void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin,
2726                                           Register destination) {
2727   Ld_d(destination, EntryFromBuiltinAsOperand(builtin));
2728 }
2729 MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
2730   DCHECK(root_array_available());
2731   return MemOperand(kRootRegister,
2732                     IsolateData::BuiltinEntrySlotOffset(builtin));
2733 }
2734 
2735 void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
2736   ASM_CODE_COMMENT(this);
2737   LoadEntryFromBuiltinIndex(builtin_index);
2738   Call(builtin_index);
2739 }
2740 void TurboAssembler::CallBuiltin(Builtin builtin) {
2741   RecordCommentForOffHeapTrampoline(builtin);
2742   Call(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET);
2743   RecordComment("]");
2744 }
2745 
2746 void TurboAssembler::PatchAndJump(Address target) {
2747   ASM_CODE_COMMENT(this);
2748   UseScratchRegisterScope temps(this);
2749   Register scratch = temps.Acquire();
2750   pcaddi(scratch, 4);
2751   Ld_d(t7, MemOperand(scratch, 0));
2752   jirl(zero_reg, t7, 0);
2753   nop();
2754   DCHECK_EQ(reinterpret_cast<uint64_t>(pc_) % 8, 0);
2755   *reinterpret_cast<uint64_t*>(pc_) = target;  // pc_ should be align.
2756   pc_ += sizeof(uint64_t);
2757 }
2758 
2759 void TurboAssembler::StoreReturnAddressAndCall(Register target) {
2760   ASM_CODE_COMMENT(this);
2761   // This generates the final instruction sequence for calls to C functions
2762   // once an exit frame has been constructed.
2763   //
2764   // Note that this assumes the caller code (i.e. the Code object currently
2765   // being generated) is immovable or that the callee function cannot trigger
2766   // GC, since the callee function will return to it.
2767 
2768   Assembler::BlockTrampolinePoolScope block_trampoline_pool(this);
2769   static constexpr int kNumInstructionsToJump = 2;
2770   Label find_ra;
2771   // Adjust the value in ra to point to the correct return location, 2nd
2772   // instruction past the real call into C code (the jirl)), and push it.
2773   // This is the return address of the exit frame.
2774   pcaddi(ra, kNumInstructionsToJump + 1);
2775   bind(&find_ra);
2776 
2777   // This spot was reserved in EnterExitFrame.
2778   St_d(ra, MemOperand(sp, 0));
2779   // Stack is still aligned.
2780 
2781   // TODO(LOONG_dev): can be jirl target? a0 -- a7?
2782   jirl(zero_reg, target, 0);
2783   // Make sure the stored 'ra' points to this position.
2784   DCHECK_EQ(kNumInstructionsToJump, InstructionsGeneratedSince(&find_ra));
2785 }
2786 
2787 void TurboAssembler::DropArguments(Register count, ArgumentsCountType type,
2788                                    ArgumentsCountMode mode, Register scratch) {
2789   switch (type) {
2790     case kCountIsInteger: {
2791       Alsl_d(sp, count, sp, kPointerSizeLog2);
2792       break;
2793     }
2794     case kCountIsSmi: {
2795       STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
2796       DCHECK_NE(scratch, no_reg);
2797       SmiScale(scratch, count, kPointerSizeLog2);
2798       Add_d(sp, sp, scratch);
2799       break;
2800     }
2801     case kCountIsBytes: {
2802       Add_d(sp, sp, count);
2803       break;
2804     }
2805   }
2806   if (mode == kCountExcludesReceiver) {
2807     Add_d(sp, sp, kSystemPointerSize);
2808   }
2809 }
2810 
2811 void TurboAssembler::DropArgumentsAndPushNewReceiver(Register argc,
2812                                                      Register receiver,
2813                                                      ArgumentsCountType type,
2814                                                      ArgumentsCountMode mode,
2815                                                      Register scratch) {
2816   DCHECK(!AreAliased(argc, receiver));
2817   if (mode == kCountExcludesReceiver) {
2818     // Drop arguments without receiver and override old receiver.
2819     DropArguments(argc, type, kCountIncludesReceiver, scratch);
2820     St_d(receiver, MemOperand(sp, 0));
2821   } else {
2822     DropArguments(argc, type, mode, scratch);
2823     Push(receiver);
2824   }
2825 }
2826 
2827 void TurboAssembler::Ret(Condition cond, Register rj, const Operand& rk) {
2828   Jump(ra, cond, rj, rk);
2829 }
2830 
2831 void TurboAssembler::Drop(int count, Condition cond, Register reg,
2832                           const Operand& op) {
2833   if (count <= 0) {
2834     return;
2835   }
2836 
2837   Label skip;
2838 
2839   if (cond != al) {
2840     Branch(&skip, NegateCondition(cond), reg, op);
2841   }
2842 
2843   Add_d(sp, sp, Operand(count * kPointerSize));
2844 
2845   if (cond != al) {
2846     bind(&skip);
2847   }
2848 }
2849 
2850 void MacroAssembler::Swap(Register reg1, Register reg2, Register scratch) {
2851   if (scratch == no_reg) {
2852     Xor(reg1, reg1, Operand(reg2));
2853     Xor(reg2, reg2, Operand(reg1));
2854     Xor(reg1, reg1, Operand(reg2));
2855   } else {
2856     mov(scratch, reg1);
2857     mov(reg1, reg2);
2858     mov(reg2, scratch);
2859   }
2860 }
2861 
2862 void TurboAssembler::Call(Label* target) { Branch(target, true); }
2863 
2864 void TurboAssembler::Push(Smi smi) {
2865   UseScratchRegisterScope temps(this);
2866   Register scratch = temps.Acquire();
2867   li(scratch, Operand(smi));
2868   Push(scratch);
2869 }
2870 
2871 void TurboAssembler::Push(Handle<HeapObject> handle) {
2872   UseScratchRegisterScope temps(this);
2873   Register scratch = temps.Acquire();
2874   li(scratch, Operand(handle));
2875   Push(scratch);
2876 }
2877 
2878 void TurboAssembler::PushArray(Register array, Register size, Register scratch,
2879                                Register scratch2, PushArrayOrder order) {
2880   DCHECK(!AreAliased(array, size, scratch, scratch2));
2881   Label loop, entry;
2882   if (order == PushArrayOrder::kReverse) {
2883     mov(scratch, zero_reg);
2884     jmp(&entry);
2885     bind(&loop);
2886     Alsl_d(scratch2, scratch, array, kPointerSizeLog2, t7);
2887     Ld_d(scratch2, MemOperand(scratch2, 0));
2888     Push(scratch2);
2889     Add_d(scratch, scratch, Operand(1));
2890     bind(&entry);
2891     Branch(&loop, less, scratch, Operand(size));
2892   } else {
2893     mov(scratch, size);
2894     jmp(&entry);
2895     bind(&loop);
2896     Alsl_d(scratch2, scratch, array, kPointerSizeLog2, t7);
2897     Ld_d(scratch2, MemOperand(scratch2, 0));
2898     Push(scratch2);
2899     bind(&entry);
2900     Add_d(scratch, scratch, Operand(-1));
2901     Branch(&loop, greater_equal, scratch, Operand(zero_reg));
2902   }
2903 }
2904 
2905 // ---------------------------------------------------------------------------
2906 // Exception handling.
2907 
2908 void MacroAssembler::PushStackHandler() {
2909   // Adjust this code if not the case.
2910   STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize);
2911   STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
2912 
2913   Push(Smi::zero());  // Padding.
2914 
2915   // Link the current handler as the next handler.
2916   li(t2,
2917      ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate()));
2918   Ld_d(t1, MemOperand(t2, 0));
2919   Push(t1);
2920 
2921   // Set this new handler as the current one.
2922   St_d(sp, MemOperand(t2, 0));
2923 }
2924 
2925 void MacroAssembler::PopStackHandler() {
2926   STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
2927   Pop(a1);
2928   Add_d(sp, sp,
2929         Operand(
2930             static_cast<int64_t>(StackHandlerConstants::kSize - kPointerSize)));
2931   UseScratchRegisterScope temps(this);
2932   Register scratch = temps.Acquire();
2933   li(scratch,
2934      ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate()));
2935   St_d(a1, MemOperand(scratch, 0));
2936 }
2937 
2938 void TurboAssembler::FPUCanonicalizeNaN(const DoubleRegister dst,
2939                                         const DoubleRegister src) {
2940   fsub_d(dst, src, kDoubleRegZero);
2941 }
2942 
2943 // -----------------------------------------------------------------------------
2944 // JavaScript invokes.
2945 
2946 void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) {
2947   ASM_CODE_COMMENT(this);
2948   DCHECK(root_array_available());
2949   Isolate* isolate = this->isolate();
2950   ExternalReference limit =
2951       kind == StackLimitKind::kRealStackLimit
2952           ? ExternalReference::address_of_real_jslimit(isolate)
2953           : ExternalReference::address_of_jslimit(isolate);
2954   DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit));
2955 
2956   intptr_t offset =
2957       TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit);
2958   CHECK(is_int32(offset));
2959   Ld_d(destination, MemOperand(kRootRegister, static_cast<int32_t>(offset)));
2960 }
2961 
2962 void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch1,
2963                                         Register scratch2,
2964                                         Label* stack_overflow) {
2965   ASM_CODE_COMMENT(this);
2966   // Check the stack for overflow. We are not trying to catch
2967   // interruptions (e.g. debug break and preemption) here, so the "real stack
2968   // limit" is checked.
2969 
2970   LoadStackLimit(scratch1, StackLimitKind::kRealStackLimit);
2971   // Make scratch1 the space we have left. The stack might already be overflowed
2972   // here which will cause scratch1 to become negative.
2973   sub_d(scratch1, sp, scratch1);
2974   // Check if the arguments will overflow the stack.
2975   slli_d(scratch2, num_args, kPointerSizeLog2);
2976   // Signed comparison.
2977   Branch(stack_overflow, le, scratch1, Operand(scratch2));
2978 }
2979 
2980 void MacroAssembler::InvokePrologue(Register expected_parameter_count,
2981                                     Register actual_parameter_count,
2982                                     Label* done, InvokeType type) {
2983   ASM_CODE_COMMENT(this);
2984   Label regular_invoke;
2985 
2986   //  a0: actual arguments count
2987   //  a1: function (passed through to callee)
2988   //  a2: expected arguments count
2989 
2990   DCHECK_EQ(actual_parameter_count, a0);
2991   DCHECK_EQ(expected_parameter_count, a2);
2992 
2993   // If the expected parameter count is equal to the adaptor sentinel, no need
2994   // to push undefined value as arguments.
2995   if (kDontAdaptArgumentsSentinel != 0) {
2996     Branch(&regular_invoke, eq, expected_parameter_count,
2997            Operand(kDontAdaptArgumentsSentinel));
2998   }
2999 
3000   // If overapplication or if the actual argument count is equal to the
3001   // formal parameter count, no need to push extra undefined values.
3002   sub_d(expected_parameter_count, expected_parameter_count,
3003         actual_parameter_count);
3004   Branch(&regular_invoke, le, expected_parameter_count, Operand(zero_reg));
3005 
3006   Label stack_overflow;
3007   StackOverflowCheck(expected_parameter_count, t0, t1, &stack_overflow);
3008   // Underapplication. Move the arguments already in the stack, including the
3009   // receiver and the return address.
3010   {
3011     Label copy;
3012     Register src = a6, dest = a7;
3013     mov(src, sp);
3014     slli_d(t0, expected_parameter_count, kSystemPointerSizeLog2);
3015     Sub_d(sp, sp, Operand(t0));
3016     // Update stack pointer.
3017     mov(dest, sp);
3018     mov(t0, actual_parameter_count);
3019     bind(&copy);
3020     Ld_d(t1, MemOperand(src, 0));
3021     St_d(t1, MemOperand(dest, 0));
3022     Sub_d(t0, t0, Operand(1));
3023     Add_d(src, src, Operand(kSystemPointerSize));
3024     Add_d(dest, dest, Operand(kSystemPointerSize));
3025     Branch(&copy, gt, t0, Operand(zero_reg));
3026   }
3027 
3028   // Fill remaining expected arguments with undefined values.
3029   LoadRoot(t0, RootIndex::kUndefinedValue);
3030   {
3031     Label loop;
3032     bind(&loop);
3033     St_d(t0, MemOperand(a7, 0));
3034     Sub_d(expected_parameter_count, expected_parameter_count, Operand(1));
3035     Add_d(a7, a7, Operand(kSystemPointerSize));
3036     Branch(&loop, gt, expected_parameter_count, Operand(zero_reg));
3037   }
3038   b(&regular_invoke);
3039 
3040   bind(&stack_overflow);
3041   {
3042     FrameScope frame(
3043         this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
3044     CallRuntime(Runtime::kThrowStackOverflow);
3045     break_(0xCC);
3046   }
3047 
3048   bind(&regular_invoke);
3049 }
3050 
3051 void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target,
3052                                              Register expected_parameter_count,
3053                                              Register actual_parameter_count) {
3054   // Load receiver to pass it later to DebugOnFunctionCall hook.
3055   LoadReceiver(t0, actual_parameter_count);
3056   FrameScope frame(
3057       this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
3058 
3059   SmiTag(expected_parameter_count);
3060   Push(expected_parameter_count);
3061 
3062   SmiTag(actual_parameter_count);
3063   Push(actual_parameter_count);
3064 
3065   if (new_target.is_valid()) {
3066     Push(new_target);
3067   }
3068   // TODO(LOONG_dev): MultiPush/Pop
3069   Push(fun);
3070   Push(fun);
3071   Push(t0);
3072   CallRuntime(Runtime::kDebugOnFunctionCall);
3073   Pop(fun);
3074   if (new_target.is_valid()) {
3075     Pop(new_target);
3076   }
3077 
3078   Pop(actual_parameter_count);
3079   SmiUntag(actual_parameter_count);
3080 
3081   Pop(expected_parameter_count);
3082   SmiUntag(expected_parameter_count);
3083 }
3084 
3085 void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
3086                                         Register expected_parameter_count,
3087                                         Register actual_parameter_count,
3088                                         InvokeType type) {
3089   // You can't call a function without a valid frame.
3090   DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
3091   DCHECK_EQ(function, a1);
3092   DCHECK_IMPLIES(new_target.is_valid(), new_target == a3);
3093 
3094   // On function call, call into the debugger if necessary.
3095   Label debug_hook, continue_after_hook;
3096   {
3097     li(t0, ExternalReference::debug_hook_on_function_call_address(isolate()));
3098     Ld_b(t0, MemOperand(t0, 0));
3099     BranchShort(&debug_hook, ne, t0, Operand(zero_reg));
3100   }
3101   bind(&continue_after_hook);
3102 
3103   // Clear the new.target register if not given.
3104   if (!new_target.is_valid()) {
3105     LoadRoot(a3, RootIndex::kUndefinedValue);
3106   }
3107 
3108   Label done;
3109   InvokePrologue(expected_parameter_count, actual_parameter_count, &done, type);
3110   // We call indirectly through the code field in the function to
3111   // allow recompilation to take effect without changing any of the
3112   // call sites.
3113   Register code = kJavaScriptCallCodeStartRegister;
3114   Ld_d(code, FieldMemOperand(function, JSFunction::kCodeOffset));
3115   switch (type) {
3116     case InvokeType::kCall:
3117       CallCodeObject(code);
3118       break;
3119     case InvokeType::kJump:
3120       JumpCodeObject(code);
3121       break;
3122   }
3123 
3124   Branch(&done);
3125 
3126   // Deferred debug hook.
3127   bind(&debug_hook);
3128   CallDebugOnFunctionCall(function, new_target, expected_parameter_count,
3129                           actual_parameter_count);
3130   Branch(&continue_after_hook);
3131 
3132   // Continue here if InvokePrologue does handle the invocation due to
3133   // mismatched parameter counts.
3134   bind(&done);
3135 }
3136 
3137 void MacroAssembler::InvokeFunctionWithNewTarget(
3138     Register function, Register new_target, Register actual_parameter_count,
3139     InvokeType type) {
3140   ASM_CODE_COMMENT(this);
3141   // You can't call a function without a valid frame.
3142   DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
3143 
3144   // Contract with called JS functions requires that function is passed in a1.
3145   DCHECK_EQ(function, a1);
3146   Register expected_parameter_count = a2;
3147   Register temp_reg = t0;
3148   Ld_d(temp_reg, FieldMemOperand(a1, JSFunction::kSharedFunctionInfoOffset));
3149   Ld_d(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
3150   // The argument count is stored as uint16_t
3151   Ld_hu(expected_parameter_count,
3152         FieldMemOperand(temp_reg,
3153                         SharedFunctionInfo::kFormalParameterCountOffset));
3154 
3155   InvokeFunctionCode(a1, new_target, expected_parameter_count,
3156                      actual_parameter_count, type);
3157 }
3158 
3159 void MacroAssembler::InvokeFunction(Register function,
3160                                     Register expected_parameter_count,
3161                                     Register actual_parameter_count,
3162                                     InvokeType type) {
3163   ASM_CODE_COMMENT(this);
3164   // You can't call a function without a valid frame.
3165   DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
3166 
3167   // Contract with called JS functions requires that function is passed in a1.
3168   DCHECK_EQ(function, a1);
3169 
3170   // Get the function and setup the context.
3171   Ld_d(cp, FieldMemOperand(a1, JSFunction::kContextOffset));
3172 
3173   InvokeFunctionCode(a1, no_reg, expected_parameter_count,
3174                      actual_parameter_count, type);
3175 }
3176 
3177 // ---------------------------------------------------------------------------
3178 // Support functions.
3179 
3180 void MacroAssembler::GetObjectType(Register object, Register map,
3181                                    Register type_reg) {
3182   LoadMap(map, object);
3183   Ld_hu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
3184 }
3185 
3186 void MacroAssembler::GetInstanceTypeRange(Register map, Register type_reg,
3187                                           InstanceType lower_limit,
3188                                           Register range) {
3189   Ld_hu(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
3190   Sub_d(range, type_reg, Operand(lower_limit));
3191 }
3192 
3193 // -----------------------------------------------------------------------------
3194 // Runtime calls.
3195 
3196 void TurboAssembler::AddOverflow_d(Register dst, Register left,
3197                                    const Operand& right, Register overflow) {
3198   ASM_CODE_COMMENT(this);
3199   BlockTrampolinePoolScope block_trampoline_pool(this);
3200   UseScratchRegisterScope temps(this);
3201   Register scratch = temps.Acquire();
3202   Register scratch2 = temps.Acquire();
3203   Register right_reg = no_reg;
3204   if (!right.is_reg()) {
3205     li(scratch, Operand(right));
3206     right_reg = scratch;
3207   } else {
3208     right_reg = right.rm();
3209   }
3210 
3211   DCHECK(left != scratch2 && right_reg != scratch2 && dst != scratch2 &&
3212          overflow != scratch2);
3213   DCHECK(overflow != left && overflow != right_reg);
3214 
3215   if (dst == left || dst == right_reg) {
3216     add_d(scratch2, left, right_reg);
3217     xor_(overflow, scratch2, left);
3218     xor_(scratch, scratch2, right_reg);
3219     and_(overflow, overflow, scratch);
3220     mov(dst, scratch2);
3221   } else {
3222     add_d(dst, left, right_reg);
3223     xor_(overflow, dst, left);
3224     xor_(scratch, dst, right_reg);
3225     and_(overflow, overflow, scratch);
3226   }
3227 }
3228 
3229 void TurboAssembler::SubOverflow_d(Register dst, Register left,
3230                                    const Operand& right, Register overflow) {
3231   ASM_CODE_COMMENT(this);
3232   BlockTrampolinePoolScope block_trampoline_pool(this);
3233   UseScratchRegisterScope temps(this);
3234   Register scratch = temps.Acquire();
3235   Register scratch2 = temps.Acquire();
3236   Register right_reg = no_reg;
3237   if (!right.is_reg()) {
3238     li(scratch, Operand(right));
3239     right_reg = scratch;
3240   } else {
3241     right_reg = right.rm();
3242   }
3243 
3244   DCHECK(left != scratch2 && right_reg != scratch2 && dst != scratch2 &&
3245          overflow != scratch2);
3246   DCHECK(overflow != left && overflow != right_reg);
3247 
3248   if (dst == left || dst == right_reg) {
3249     Sub_d(scratch2, left, right_reg);
3250     xor_(overflow, left, scratch2);
3251     xor_(scratch, left, right_reg);
3252     and_(overflow, overflow, scratch);
3253     mov(dst, scratch2);
3254   } else {
3255     sub_d(dst, left, right_reg);
3256     xor_(overflow, left, dst);
3257     xor_(scratch, left, right_reg);
3258     and_(overflow, overflow, scratch);
3259   }
3260 }
3261 
3262 void TurboAssembler::MulOverflow_w(Register dst, Register left,
3263                                    const Operand& right, Register overflow) {
3264   ASM_CODE_COMMENT(this);
3265   BlockTrampolinePoolScope block_trampoline_pool(this);
3266   UseScratchRegisterScope temps(this);
3267   Register scratch = temps.Acquire();
3268   Register scratch2 = temps.Acquire();
3269   Register right_reg = no_reg;
3270   if (!right.is_reg()) {
3271     li(scratch, Operand(right));
3272     right_reg = scratch;
3273   } else {
3274     right_reg = right.rm();
3275   }
3276 
3277   DCHECK(left != scratch2 && right_reg != scratch2 && dst != scratch2 &&
3278          overflow != scratch2);
3279   DCHECK(overflow != left && overflow != right_reg);
3280 
3281   if (dst == left || dst == right_reg) {
3282     Mul_w(scratch2, left, right_reg);
3283     Mulh_w(overflow, left, right_reg);
3284     mov(dst, scratch2);
3285   } else {
3286     Mul_w(dst, left, right_reg);
3287     Mulh_w(overflow, left, right_reg);
3288   }
3289 
3290   srai_d(scratch2, dst, 32);
3291   xor_(overflow, overflow, scratch2);
3292 }
3293 
3294 void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
3295                                  SaveFPRegsMode save_doubles) {
3296   ASM_CODE_COMMENT(this);
3297   // All parameters are on the stack. v0 has the return value after call.
3298 
3299   // If the expected number of arguments of the runtime function is
3300   // constant, we check that the actual number of arguments match the
3301   // expectation.
3302   CHECK(f->nargs < 0 || f->nargs == num_arguments);
3303 
3304   // TODO(1236192): Most runtime routines don't need the number of
3305   // arguments passed in because it is constant. At some point we
3306   // should remove this need and make the runtime routine entry code
3307   // smarter.
3308   PrepareCEntryArgs(num_arguments);
3309   PrepareCEntryFunction(ExternalReference::Create(f));
3310   Handle<Code> code =
3311       CodeFactory::CEntry(isolate(), f->result_size, save_doubles);
3312   Call(code, RelocInfo::CODE_TARGET);
3313 }
3314 
3315 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
3316   ASM_CODE_COMMENT(this);
3317   const Runtime::Function* function = Runtime::FunctionForId(fid);
3318   DCHECK_EQ(1, function->result_size);
3319   if (function->nargs >= 0) {
3320     PrepareCEntryArgs(function->nargs);
3321   }
3322   JumpToExternalReference(ExternalReference::Create(fid));
3323 }
3324 
3325 void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
3326                                              bool builtin_exit_frame) {
3327   PrepareCEntryFunction(builtin);
3328   Handle<Code> code = CodeFactory::CEntry(isolate(), 1, SaveFPRegsMode::kIgnore,
3329                                           ArgvMode::kStack, builtin_exit_frame);
3330   Jump(code, RelocInfo::CODE_TARGET, al, zero_reg, Operand(zero_reg));
3331 }
3332 
3333 void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
3334   li(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
3335   Jump(kOffHeapTrampolineRegister);
3336 }
3337 
3338 void MacroAssembler::LoadWeakValue(Register out, Register in,
3339                                    Label* target_if_cleared) {
3340   Branch(target_if_cleared, eq, in, Operand(kClearedWeakHeapObjectLower32));
3341   And(out, in, Operand(~kWeakHeapObjectMask));
3342 }
3343 
3344 void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value,
3345                                           Register scratch1,
3346                                           Register scratch2) {
3347   DCHECK_GT(value, 0);
3348   if (FLAG_native_code_counters && counter->Enabled()) {
3349     ASM_CODE_COMMENT(this);
3350     // This operation has to be exactly 32-bit wide in case the external
3351     // reference table redirects the counter to a uint32_t dummy_stats_counter_
3352     // field.
3353     li(scratch2, ExternalReference::Create(counter));
3354     Ld_w(scratch1, MemOperand(scratch2, 0));
3355     Add_w(scratch1, scratch1, Operand(value));
3356     St_w(scratch1, MemOperand(scratch2, 0));
3357   }
3358 }
3359 
3360 void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value,
3361                                           Register scratch1,
3362                                           Register scratch2) {
3363   DCHECK_GT(value, 0);
3364   if (FLAG_native_code_counters && counter->Enabled()) {
3365     ASM_CODE_COMMENT(this);
3366     // This operation has to be exactly 32-bit wide in case the external
3367     // reference table redirects the counter to a uint32_t dummy_stats_counter_
3368     // field.
3369     li(scratch2, ExternalReference::Create(counter));
3370     Ld_w(scratch1, MemOperand(scratch2, 0));
3371     Sub_w(scratch1, scratch1, Operand(value));
3372     St_w(scratch1, MemOperand(scratch2, 0));
3373   }
3374 }
3375 
3376 // -----------------------------------------------------------------------------
3377 // Debugging.
3378 
3379 void TurboAssembler::Trap() { stop(); }
3380 void TurboAssembler::DebugBreak() { stop(); }
3381 
3382 void TurboAssembler::Assert(Condition cc, AbortReason reason, Register rs,
3383                             Operand rk) {
3384   if (FLAG_debug_code) Check(cc, reason, rs, rk);
3385 }
3386 
3387 void TurboAssembler::Check(Condition cc, AbortReason reason, Register rj,
3388                            Operand rk) {
3389   Label L;
3390   Branch(&L, cc, rj, rk);
3391   Abort(reason);
3392   // Will not return here.
3393   bind(&L);
3394 }
3395 
3396 void TurboAssembler::Abort(AbortReason reason) {
3397   Label abort_start;
3398   bind(&abort_start);
3399   if (FLAG_code_comments) {
3400     const char* msg = GetAbortReason(reason);
3401     RecordComment("Abort message: ");
3402     RecordComment(msg);
3403   }
3404 
3405   // Avoid emitting call to builtin if requested.
3406   if (trap_on_abort()) {
3407     stop();
3408     return;
3409   }
3410 
3411   if (should_abort_hard()) {
3412     // We don't care if we constructed a frame. Just pretend we did.
3413     FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE);
3414     PrepareCallCFunction(0, a0);
3415     li(a0, Operand(static_cast<int>(reason)));
3416     CallCFunction(ExternalReference::abort_with_reason(), 1);
3417     return;
3418   }
3419 
3420   Move(a0, Smi::FromInt(static_cast<int>(reason)));
3421 
3422   // Disable stub call restrictions to always allow calls to abort.
3423   if (!has_frame()) {
3424     // We don't actually want to generate a pile of code for this, so just
3425     // claim there is a stack frame, without generating one.
3426     FrameScope scope(this, StackFrame::NO_FRAME_TYPE);
3427     Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
3428   } else {
3429     Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
3430   }
3431   // Will not return here.
3432   if (is_trampoline_pool_blocked()) {
3433     // If the calling code cares about the exact number of
3434     // instructions generated, we insert padding here to keep the size
3435     // of the Abort macro constant.
3436     // Currently in debug mode with debug_code enabled the number of
3437     // generated instructions is 10, so we use this as a maximum value.
3438     static const int kExpectedAbortInstructions = 10;
3439     int abort_instructions = InstructionsGeneratedSince(&abort_start);
3440     DCHECK_LE(abort_instructions, kExpectedAbortInstructions);
3441     while (abort_instructions++ < kExpectedAbortInstructions) {
3442       nop();
3443     }
3444   }
3445 }
3446 
3447 void TurboAssembler::LoadMap(Register destination, Register object) {
3448   Ld_d(destination, FieldMemOperand(object, HeapObject::kMapOffset));
3449 }
3450 
3451 void MacroAssembler::LoadNativeContextSlot(Register dst, int index) {
3452   LoadMap(dst, cp);
3453   Ld_d(dst, FieldMemOperand(
3454                 dst, Map::kConstructorOrBackPointerOrNativeContextOffset));
3455   Ld_d(dst, MemOperand(dst, Context::SlotOffset(index)));
3456 }
3457 
3458 void TurboAssembler::StubPrologue(StackFrame::Type type) {
3459   UseScratchRegisterScope temps(this);
3460   Register scratch = temps.Acquire();
3461   li(scratch, Operand(StackFrame::TypeToMarker(type)));
3462   PushCommonFrame(scratch);
3463 }
3464 
3465 void TurboAssembler::Prologue() { PushStandardFrame(a1); }
3466 
3467 void TurboAssembler::EnterFrame(StackFrame::Type type) {
3468   ASM_CODE_COMMENT(this);
3469   BlockTrampolinePoolScope block_trampoline_pool(this);
3470   Push(ra, fp);
3471   Move(fp, sp);
3472   if (!StackFrame::IsJavaScript(type)) {
3473     li(kScratchReg, Operand(StackFrame::TypeToMarker(type)));
3474     Push(kScratchReg);
3475   }
3476 #if V8_ENABLE_WEBASSEMBLY
3477   if (type == StackFrame::WASM) Push(kWasmInstanceRegister);
3478 #endif  // V8_ENABLE_WEBASSEMBLY
3479 }
3480 
3481 void TurboAssembler::LeaveFrame(StackFrame::Type type) {
3482   ASM_CODE_COMMENT(this);
3483   addi_d(sp, fp, 2 * kPointerSize);
3484   Ld_d(ra, MemOperand(fp, 1 * kPointerSize));
3485   Ld_d(fp, MemOperand(fp, 0 * kPointerSize));
3486 }
3487 
3488 void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space,
3489                                     StackFrame::Type frame_type) {
3490   ASM_CODE_COMMENT(this);
3491   DCHECK(frame_type == StackFrame::EXIT ||
3492          frame_type == StackFrame::BUILTIN_EXIT);
3493 
3494   // Set up the frame structure on the stack.
3495   STATIC_ASSERT(2 * kPointerSize == ExitFrameConstants::kCallerSPDisplacement);
3496   STATIC_ASSERT(1 * kPointerSize == ExitFrameConstants::kCallerPCOffset);
3497   STATIC_ASSERT(0 * kPointerSize == ExitFrameConstants::kCallerFPOffset);
3498 
3499   // This is how the stack will look:
3500   // fp + 2 (==kCallerSPDisplacement) - old stack's end
3501   // [fp + 1 (==kCallerPCOffset)] - saved old ra
3502   // [fp + 0 (==kCallerFPOffset)] - saved old fp
3503   // [fp - 1 StackFrame::EXIT Smi
3504   // [fp - 2 (==kSPOffset)] - sp of the called function
3505   // fp - (2 + stack_space + alignment) == sp == [fp - kSPOffset] - top of the
3506   //   new stack (will contain saved ra)
3507 
3508   // Save registers and reserve room for saved entry sp.
3509   addi_d(sp, sp, -2 * kPointerSize - ExitFrameConstants::kFixedFrameSizeFromFp);
3510   St_d(ra, MemOperand(sp, 3 * kPointerSize));
3511   St_d(fp, MemOperand(sp, 2 * kPointerSize));
3512   {
3513     UseScratchRegisterScope temps(this);
3514     Register scratch = temps.Acquire();
3515     li(scratch, Operand(StackFrame::TypeToMarker(frame_type)));
3516     St_d(scratch, MemOperand(sp, 1 * kPointerSize));
3517   }
3518   // Set up new frame pointer.
3519   addi_d(fp, sp, ExitFrameConstants::kFixedFrameSizeFromFp);
3520 
3521   if (FLAG_debug_code) {
3522     St_d(zero_reg, MemOperand(fp, ExitFrameConstants::kSPOffset));
3523   }
3524 
3525   {
3526     BlockTrampolinePoolScope block_trampoline_pool(this);
3527     // Save the frame pointer and the context in top.
3528     li(t8, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
3529                                      isolate()));
3530     St_d(fp, MemOperand(t8, 0));
3531     li(t8,
3532        ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
3533     St_d(cp, MemOperand(t8, 0));
3534   }
3535 
3536   const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
3537   if (save_doubles) {
3538     // The stack is already aligned to 0 modulo 8 for stores with sdc1.
3539     int kNumOfSavedRegisters = FPURegister::kNumRegisters / 2;
3540     int space = kNumOfSavedRegisters * kDoubleSize;
3541     Sub_d(sp, sp, Operand(space));
3542     // Remember: we only need to save every 2nd double FPU value.
3543     for (int i = 0; i < kNumOfSavedRegisters; i++) {
3544       FPURegister reg = FPURegister::from_code(2 * i);
3545       Fst_d(reg, MemOperand(sp, i * kDoubleSize));
3546     }
3547   }
3548 
3549   // Reserve place for the return address, stack space and an optional slot
3550   // (used by DirectCEntry to hold the return value if a struct is
3551   // returned) and align the frame preparing for calling the runtime function.
3552   DCHECK_GE(stack_space, 0);
3553   Sub_d(sp, sp, Operand((stack_space + 2) * kPointerSize));
3554   if (frame_alignment > 0) {
3555     DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
3556     And(sp, sp, Operand(-frame_alignment));  // Align stack.
3557   }
3558 
3559   // Set the exit frame sp value to point just before the return address
3560   // location.
3561   UseScratchRegisterScope temps(this);
3562   Register scratch = temps.Acquire();
3563   addi_d(scratch, sp, kPointerSize);
3564   St_d(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset));
3565 }
3566 
3567 void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count,
3568                                     bool do_return,
3569                                     bool argument_count_is_length) {
3570   ASM_CODE_COMMENT(this);
3571   BlockTrampolinePoolScope block_trampoline_pool(this);
3572   // Optionally restore all double registers.
3573   if (save_doubles) {
3574     // Remember: we only need to restore every 2nd double FPU value.
3575     int kNumOfSavedRegisters = FPURegister::kNumRegisters / 2;
3576     Sub_d(t8, fp,
3577           Operand(ExitFrameConstants::kFixedFrameSizeFromFp +
3578                   kNumOfSavedRegisters * kDoubleSize));
3579     for (int i = 0; i < kNumOfSavedRegisters; i++) {
3580       FPURegister reg = FPURegister::from_code(2 * i);
3581       Fld_d(reg, MemOperand(t8, i * kDoubleSize));
3582     }
3583   }
3584 
3585   // Clear top frame.
3586   li(t8,
3587      ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate()));
3588   St_d(zero_reg, MemOperand(t8, 0));
3589 
3590   // Restore current context from top and clear it in debug mode.
3591   li(t8,
3592      ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
3593   Ld_d(cp, MemOperand(t8, 0));
3594 
3595   if (FLAG_debug_code) {
3596     UseScratchRegisterScope temp(this);
3597     Register scratch = temp.Acquire();
3598     li(scratch, Operand(Context::kInvalidContext));
3599     St_d(scratch, MemOperand(t8, 0));
3600   }
3601 
3602   // Pop the arguments, restore registers, and return.
3603   mov(sp, fp);  // Respect ABI stack constraint.
3604   Ld_d(fp, MemOperand(sp, ExitFrameConstants::kCallerFPOffset));
3605   Ld_d(ra, MemOperand(sp, ExitFrameConstants::kCallerPCOffset));
3606 
3607   if (argument_count.is_valid()) {
3608     if (argument_count_is_length) {
3609       add_d(sp, sp, argument_count);
3610     } else {
3611       Alsl_d(sp, argument_count, sp, kPointerSizeLog2, t8);
3612     }
3613   }
3614 
3615   addi_d(sp, sp, 2 * kPointerSize);
3616   if (do_return) {
3617     Ret();
3618   }
3619 }
3620 
3621 int TurboAssembler::ActivationFrameAlignment() {
3622 #if V8_HOST_ARCH_LOONG64
3623   // Running on the real platform. Use the alignment as mandated by the local
3624   // environment.
3625   // Note: This will break if we ever start generating snapshots on one LOONG64
3626   // platform for another LOONG64 platform with a different alignment.
3627   return base::OS::ActivationFrameAlignment();
3628 #else   // V8_HOST_ARCH_LOONG64
3629   // If we are using the simulator then we should always align to the expected
3630   // alignment. As the simulator is used to generate snapshots we do not know
3631   // if the target platform will need alignment, so this is controlled from a
3632   // flag.
3633   return FLAG_sim_stack_alignment;
3634 #endif  // V8_HOST_ARCH_LOONG64
3635 }
3636 
3637 void MacroAssembler::AssertStackIsAligned() {
3638   if (FLAG_debug_code) {
3639     ASM_CODE_COMMENT(this);
3640     const int frame_alignment = ActivationFrameAlignment();
3641     const int frame_alignment_mask = frame_alignment - 1;
3642 
3643     if (frame_alignment > kPointerSize) {
3644       Label alignment_as_expected;
3645       DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
3646       {
3647         UseScratchRegisterScope temps(this);
3648         Register scratch = temps.Acquire();
3649         andi(scratch, sp, frame_alignment_mask);
3650         Branch(&alignment_as_expected, eq, scratch, Operand(zero_reg));
3651       }
3652       // Don't use Check here, as it will call Runtime_Abort re-entering here.
3653       stop();
3654       bind(&alignment_as_expected);
3655     }
3656   }
3657 }
3658 
3659 void TurboAssembler::SmiUntag(Register dst, const MemOperand& src) {
3660   if (SmiValuesAre32Bits()) {
3661     Ld_w(dst, MemOperand(src.base(), SmiWordOffset(src.offset())));
3662   } else {
3663     DCHECK(SmiValuesAre31Bits());
3664     Ld_w(dst, src);
3665     SmiUntag(dst);
3666   }
3667 }
3668 
3669 void TurboAssembler::JumpIfSmi(Register value, Label* smi_label) {
3670   DCHECK_EQ(0, kSmiTag);
3671   UseScratchRegisterScope temps(this);
3672   Register scratch = temps.Acquire();
3673   andi(scratch, value, kSmiTagMask);
3674   Branch(smi_label, eq, scratch, Operand(zero_reg));
3675 }
3676 
3677 void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label) {
3678   DCHECK_EQ(0, kSmiTag);
3679   UseScratchRegisterScope temps(this);
3680   Register scratch = temps.Acquire();
3681   andi(scratch, value, kSmiTagMask);
3682   Branch(not_smi_label, ne, scratch, Operand(zero_reg));
3683 }
3684 
3685 void TurboAssembler::AssertNotSmi(Register object) {
3686   if (FLAG_debug_code) {
3687     ASM_CODE_COMMENT(this);
3688     STATIC_ASSERT(kSmiTag == 0);
3689     UseScratchRegisterScope temps(this);
3690     Register scratch = temps.Acquire();
3691     andi(scratch, object, kSmiTagMask);
3692     Check(ne, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg));
3693   }
3694 }
3695 
3696 void TurboAssembler::AssertSmi(Register object) {
3697   if (FLAG_debug_code) {
3698     ASM_CODE_COMMENT(this);
3699     STATIC_ASSERT(kSmiTag == 0);
3700     UseScratchRegisterScope temps(this);
3701     Register scratch = temps.Acquire();
3702     andi(scratch, object, kSmiTagMask);
3703     Check(eq, AbortReason::kOperandIsASmi, scratch, Operand(zero_reg));
3704   }
3705 }
3706 
3707 void MacroAssembler::AssertConstructor(Register object) {
3708   if (FLAG_debug_code) {
3709     ASM_CODE_COMMENT(this);
3710     BlockTrampolinePoolScope block_trampoline_pool(this);
3711     STATIC_ASSERT(kSmiTag == 0);
3712     SmiTst(object, t8);
3713     Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor, t8,
3714           Operand(zero_reg));
3715 
3716     LoadMap(t8, object);
3717     Ld_bu(t8, FieldMemOperand(t8, Map::kBitFieldOffset));
3718     And(t8, t8, Operand(Map::Bits1::IsConstructorBit::kMask));
3719     Check(ne, AbortReason::kOperandIsNotAConstructor, t8, Operand(zero_reg));
3720   }
3721 }
3722 
3723 void MacroAssembler::AssertFunction(Register object) {
3724   if (FLAG_debug_code) {
3725     ASM_CODE_COMMENT(this);
3726     BlockTrampolinePoolScope block_trampoline_pool(this);
3727     STATIC_ASSERT(kSmiTag == 0);
3728     SmiTst(object, t8);
3729     Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, t8,
3730           Operand(zero_reg));
3731     Push(object);
3732     LoadMap(object, object);
3733     GetInstanceTypeRange(object, object, FIRST_JS_FUNCTION_TYPE, t8);
3734     Check(ls, AbortReason::kOperandIsNotAFunction, t8,
3735           Operand(LAST_JS_FUNCTION_TYPE - FIRST_JS_FUNCTION_TYPE));
3736     Pop(object);
3737   }
3738 }
3739 
3740 void MacroAssembler::AssertCallableFunction(Register object) {
3741   if (FLAG_debug_code) {
3742     ASM_CODE_COMMENT(this);
3743     BlockTrampolinePoolScope block_trampoline_pool(this);
3744     STATIC_ASSERT(kSmiTag == 0);
3745     SmiTst(object, t8);
3746     Check(ne, AbortReason::kOperandIsASmiAndNotAFunction, t8,
3747           Operand(zero_reg));
3748     Push(object);
3749     LoadMap(object, object);
3750     GetInstanceTypeRange(object, object, FIRST_CALLABLE_JS_FUNCTION_TYPE, t8);
3751     Check(ls, AbortReason::kOperandIsNotACallableFunction, t8,
3752           Operand(LAST_CALLABLE_JS_FUNCTION_TYPE -
3753                   FIRST_CALLABLE_JS_FUNCTION_TYPE));
3754     Pop(object);
3755   }
3756 }
3757 
3758 void MacroAssembler::AssertBoundFunction(Register object) {
3759   if (FLAG_debug_code) {
3760     ASM_CODE_COMMENT(this);
3761     BlockTrampolinePoolScope block_trampoline_pool(this);
3762     STATIC_ASSERT(kSmiTag == 0);
3763     SmiTst(object, t8);
3764     Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction, t8,
3765           Operand(zero_reg));
3766     GetObjectType(object, t8, t8);
3767     Check(eq, AbortReason::kOperandIsNotABoundFunction, t8,
3768           Operand(JS_BOUND_FUNCTION_TYPE));
3769   }
3770 }
3771 
3772 void MacroAssembler::AssertGeneratorObject(Register object) {
3773   if (!FLAG_debug_code) return;
3774   ASM_CODE_COMMENT(this);
3775   BlockTrampolinePoolScope block_trampoline_pool(this);
3776   STATIC_ASSERT(kSmiTag == 0);
3777   SmiTst(object, t8);
3778   Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject, t8,
3779         Operand(zero_reg));
3780 
3781   GetObjectType(object, t8, t8);
3782 
3783   Label done;
3784 
3785   // Check if JSGeneratorObject
3786   Branch(&done, eq, t8, Operand(JS_GENERATOR_OBJECT_TYPE));
3787 
3788   // Check if JSAsyncFunctionObject (See MacroAssembler::CompareInstanceType)
3789   Branch(&done, eq, t8, Operand(JS_ASYNC_FUNCTION_OBJECT_TYPE));
3790 
3791   // Check if JSAsyncGeneratorObject
3792   Branch(&done, eq, t8, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE));
3793 
3794   Abort(AbortReason::kOperandIsNotAGeneratorObject);
3795 
3796   bind(&done);
3797 }
3798 
3799 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object,
3800                                                      Register scratch) {
3801   if (FLAG_debug_code) {
3802     ASM_CODE_COMMENT(this);
3803     Label done_checking;
3804     AssertNotSmi(object);
3805     LoadRoot(scratch, RootIndex::kUndefinedValue);
3806     Branch(&done_checking, eq, object, Operand(scratch));
3807     GetObjectType(object, scratch, scratch);
3808     Assert(eq, AbortReason::kExpectedUndefinedOrCell, scratch,
3809            Operand(ALLOCATION_SITE_TYPE));
3810     bind(&done_checking);
3811   }
3812 }
3813 
3814 void TurboAssembler::Float32Max(FPURegister dst, FPURegister src1,
3815                                 FPURegister src2, Label* out_of_line) {
3816   ASM_CODE_COMMENT(this);
3817   if (src1 == src2) {
3818     Move_s(dst, src1);
3819     return;
3820   }
3821 
3822   // Check if one of operands is NaN.
3823   CompareIsNanF32(src1, src2);
3824   BranchTrueF(out_of_line);
3825 
3826   fmax_s(dst, src1, src2);
3827 }
3828 
3829 void TurboAssembler::Float32MaxOutOfLine(FPURegister dst, FPURegister src1,
3830                                          FPURegister src2) {
3831   fadd_s(dst, src1, src2);
3832 }
3833 
3834 void TurboAssembler::Float32Min(FPURegister dst, FPURegister src1,
3835                                 FPURegister src2, Label* out_of_line) {
3836   ASM_CODE_COMMENT(this);
3837   if (src1 == src2) {
3838     Move_s(dst, src1);
3839     return;
3840   }
3841 
3842   // Check if one of operands is NaN.
3843   CompareIsNanF32(src1, src2);
3844   BranchTrueF(out_of_line);
3845 
3846   fmin_s(dst, src1, src2);
3847 }
3848 
3849 void TurboAssembler::Float32MinOutOfLine(FPURegister dst, FPURegister src1,
3850                                          FPURegister src2) {
3851   fadd_s(dst, src1, src2);
3852 }
3853 
3854 void TurboAssembler::Float64Max(FPURegister dst, FPURegister src1,
3855                                 FPURegister src2, Label* out_of_line) {
3856   ASM_CODE_COMMENT(this);
3857   if (src1 == src2) {
3858     Move_d(dst, src1);
3859     return;
3860   }
3861 
3862   // Check if one of operands is NaN.
3863   CompareIsNanF64(src1, src2);
3864   BranchTrueF(out_of_line);
3865 
3866   fmax_d(dst, src1, src2);
3867 }
3868 
3869 void TurboAssembler::Float64MaxOutOfLine(FPURegister dst, FPURegister src1,
3870                                          FPURegister src2) {
3871   fadd_d(dst, src1, src2);
3872 }
3873 
3874 void TurboAssembler::Float64Min(FPURegister dst, FPURegister src1,
3875                                 FPURegister src2, Label* out_of_line) {
3876   ASM_CODE_COMMENT(this);
3877   if (src1 == src2) {
3878     Move_d(dst, src1);
3879     return;
3880   }
3881 
3882   // Check if one of operands is NaN.
3883   CompareIsNanF64(src1, src2);
3884   BranchTrueF(out_of_line);
3885 
3886   fmin_d(dst, src1, src2);
3887 }
3888 
3889 void TurboAssembler::Float64MinOutOfLine(FPURegister dst, FPURegister src1,
3890                                          FPURegister src2) {
3891   fadd_d(dst, src1, src2);
3892 }
3893 
3894 static const int kRegisterPassedArguments = 8;
3895 
3896 int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments,
3897                                               int num_double_arguments) {
3898   int stack_passed_words = 0;
3899   num_reg_arguments += 2 * num_double_arguments;
3900 
3901   // Up to eight simple arguments are passed in registers a0..a7.
3902   if (num_reg_arguments > kRegisterPassedArguments) {
3903     stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
3904   }
3905   return stack_passed_words;
3906 }
3907 
3908 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
3909                                           int num_double_arguments,
3910                                           Register scratch) {
3911   ASM_CODE_COMMENT(this);
3912   int frame_alignment = ActivationFrameAlignment();
3913 
3914   // Up to eight simple arguments in a0..a3, a4..a7, No argument slots.
3915   // Remaining arguments are pushed on the stack.
3916   int stack_passed_arguments =
3917       CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
3918   if (frame_alignment > kPointerSize) {
3919     // Make stack end at alignment and make room for num_arguments - 4 words
3920     // and the original value of sp.
3921     mov(scratch, sp);
3922     Sub_d(sp, sp, Operand((stack_passed_arguments + 1) * kPointerSize));
3923     DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
3924     bstrins_d(sp, zero_reg, std::log2(frame_alignment) - 1, 0);
3925     St_d(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
3926   } else {
3927     Sub_d(sp, sp, Operand(stack_passed_arguments * kPointerSize));
3928   }
3929 }
3930 
3931 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
3932                                           Register scratch) {
3933   PrepareCallCFunction(num_reg_arguments, 0, scratch);
3934 }
3935 
3936 void TurboAssembler::CallCFunction(ExternalReference function,
3937                                    int num_reg_arguments,
3938                                    int num_double_arguments) {
3939   ASM_CODE_COMMENT(this);
3940   BlockTrampolinePoolScope block_trampoline_pool(this);
3941   li(t7, function);
3942   CallCFunctionHelper(t7, num_reg_arguments, num_double_arguments);
3943 }
3944 
3945 void TurboAssembler::CallCFunction(Register function, int num_reg_arguments,
3946                                    int num_double_arguments) {
3947   ASM_CODE_COMMENT(this);
3948   CallCFunctionHelper(function, num_reg_arguments, num_double_arguments);
3949 }
3950 
3951 void TurboAssembler::CallCFunction(ExternalReference function,
3952                                    int num_arguments) {
3953   CallCFunction(function, num_arguments, 0);
3954 }
3955 
3956 void TurboAssembler::CallCFunction(Register function, int num_arguments) {
3957   CallCFunction(function, num_arguments, 0);
3958 }
3959 
3960 void TurboAssembler::CallCFunctionHelper(Register function,
3961                                          int num_reg_arguments,
3962                                          int num_double_arguments) {
3963   DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters);
3964   DCHECK(has_frame());
3965   // Make sure that the stack is aligned before calling a C function unless
3966   // running in the simulator. The simulator has its own alignment check which
3967   // provides more information.
3968 
3969 #if V8_HOST_ARCH_LOONG64
3970   if (FLAG_debug_code) {
3971     int frame_alignment = base::OS::ActivationFrameAlignment();
3972     int frame_alignment_mask = frame_alignment - 1;
3973     if (frame_alignment > kPointerSize) {
3974       DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
3975       Label alignment_as_expected;
3976       {
3977         Register scratch = t8;
3978         And(scratch, sp, Operand(frame_alignment_mask));
3979         Branch(&alignment_as_expected, eq, scratch, Operand(zero_reg));
3980       }
3981       // Don't use Check here, as it will call Runtime_Abort possibly
3982       // re-entering here.
3983       stop();
3984       bind(&alignment_as_expected);
3985     }
3986   }
3987 #endif  // V8_HOST_ARCH_LOONG64
3988 
3989   // Just call directly. The function called cannot cause a GC, or
3990   // allow preemption, so the return address in the link register
3991   // stays correct.
3992   {
3993     BlockTrampolinePoolScope block_trampoline_pool(this);
3994     if (function != t7) {
3995       mov(t7, function);
3996       function = t7;
3997     }
3998 
3999     // Save the frame pointer and PC so that the stack layout remains iterable,
4000     // even without an ExitFrame which normally exists between JS and C frames.
4001     // 't' registers are caller-saved so this is safe as a scratch register.
4002     Register pc_scratch = t1;
4003     Register scratch = t2;
4004     DCHECK(!AreAliased(pc_scratch, scratch, function));
4005 
4006     pcaddi(pc_scratch, 1);
4007 
4008     // See x64 code for reasoning about how to address the isolate data fields.
4009     if (root_array_available()) {
4010       St_d(pc_scratch, MemOperand(kRootRegister,
4011                                   IsolateData::fast_c_call_caller_pc_offset()));
4012       St_d(fp, MemOperand(kRootRegister,
4013                           IsolateData::fast_c_call_caller_fp_offset()));
4014     } else {
4015       DCHECK_NOT_NULL(isolate());
4016       li(scratch, ExternalReference::fast_c_call_caller_pc_address(isolate()));
4017       St_d(pc_scratch, MemOperand(scratch, 0));
4018       li(scratch, ExternalReference::fast_c_call_caller_fp_address(isolate()));
4019       St_d(fp, MemOperand(scratch, 0));
4020     }
4021 
4022     Call(function);
4023 
4024     // We don't unset the PC; the FP is the source of truth.
4025     if (root_array_available()) {
4026       St_d(zero_reg, MemOperand(kRootRegister,
4027                                 IsolateData::fast_c_call_caller_fp_offset()));
4028     } else {
4029       DCHECK_NOT_NULL(isolate());
4030       li(scratch, ExternalReference::fast_c_call_caller_fp_address(isolate()));
4031       St_d(zero_reg, MemOperand(scratch, 0));
4032     }
4033 
4034     int stack_passed_arguments =
4035         CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
4036 
4037     if (base::OS::ActivationFrameAlignment() > kPointerSize) {
4038       Ld_d(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
4039     } else {
4040       Add_d(sp, sp, Operand(stack_passed_arguments * kPointerSize));
4041     }
4042 
4043     set_pc_for_safepoint();
4044   }
4045 }
4046 
4047 #undef BRANCH_ARGS_CHECK
4048 
4049 void TurboAssembler::CheckPageFlag(const Register& object, int mask,
4050                                    Condition cc, Label* condition_met) {
4051   ASM_CODE_COMMENT(this);
4052   UseScratchRegisterScope temps(this);
4053   temps.Include(t8);
4054   Register scratch = temps.Acquire();
4055   And(scratch, object, Operand(~kPageAlignmentMask));
4056   Ld_d(scratch, MemOperand(scratch, BasicMemoryChunk::kFlagsOffset));
4057   And(scratch, scratch, Operand(mask));
4058   Branch(condition_met, cc, scratch, Operand(zero_reg));
4059 }
4060 
4061 Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2, Register reg3,
4062                                    Register reg4, Register reg5,
4063                                    Register reg6) {
4064   RegList regs = {reg1, reg2, reg3, reg4, reg5, reg6};
4065 
4066   const RegisterConfiguration* config = RegisterConfiguration::Default();
4067   for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
4068     int code = config->GetAllocatableGeneralCode(i);
4069     Register candidate = Register::from_code(code);
4070     if (regs.has(candidate)) continue;
4071     return candidate;
4072   }
4073   UNREACHABLE();
4074 }
4075 
4076 void TurboAssembler::ComputeCodeStartAddress(Register dst) {
4077   // TODO(LOONG_dev): range check, add Pcadd macro function?
4078   pcaddi(dst, -pc_offset() >> 2);
4079 }
4080 
4081 void TurboAssembler::CallForDeoptimization(Builtin target, int, Label* exit,
4082                                            DeoptimizeKind kind, Label* ret,
4083                                            Label*) {
4084   ASM_CODE_COMMENT(this);
4085   BlockTrampolinePoolScope block_trampoline_pool(this);
4086   Ld_d(t7,
4087        MemOperand(kRootRegister, IsolateData::BuiltinEntrySlotOffset(target)));
4088   Call(t7);
4089   DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
4090             (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize
4091                                             : Deoptimizer::kEagerDeoptExitSize);
4092 }
4093 
4094 void TurboAssembler::LoadCodeObjectEntry(Register destination,
4095                                          Register code_object) {
4096   ASM_CODE_COMMENT(this);
4097   // Code objects are called differently depending on whether we are generating
4098   // builtin code (which will later be embedded into the binary) or compiling
4099   // user JS code at runtime.
4100   // * Builtin code runs in --jitless mode and thus must not call into on-heap
4101   //   Code targets. Instead, we dispatch through the builtins entry table.
4102   // * Codegen at runtime does not have this restriction and we can use the
4103   //   shorter, branchless instruction sequence. The assumption here is that
4104   //   targets are usually generated code and not builtin Code objects.
4105   if (options().isolate_independent_code) {
4106     DCHECK(root_array_available());
4107     Label if_code_is_off_heap, out;
4108     Register scratch = t8;
4109 
4110     DCHECK(!AreAliased(destination, scratch));
4111     DCHECK(!AreAliased(code_object, scratch));
4112 
4113     // Check whether the Code object is an off-heap trampoline. If so, call its
4114     // (off-heap) entry point directly without going through the (on-heap)
4115     // trampoline.  Otherwise, just call the Code object as always.
4116     Ld_w(scratch, FieldMemOperand(code_object, Code::kFlagsOffset));
4117     And(scratch, scratch, Operand(Code::IsOffHeapTrampoline::kMask));
4118     BranchShort(&if_code_is_off_heap, ne, scratch, Operand(zero_reg));
4119     // Not an off-heap trampoline object, the entry point is at
4120     // Code::raw_instruction_start().
4121     Add_d(destination, code_object, Code::kHeaderSize - kHeapObjectTag);
4122     Branch(&out);
4123 
4124     // An off-heap trampoline, the entry point is loaded from the builtin entry
4125     // table.
4126     bind(&if_code_is_off_heap);
4127     Ld_w(scratch, FieldMemOperand(code_object, Code::kBuiltinIndexOffset));
4128     // TODO(liuyu): don't use scratch_reg in Alsl_d;
4129     Alsl_d(destination, scratch, kRootRegister, kSystemPointerSizeLog2,
4130            zero_reg);
4131     Ld_d(destination,
4132          MemOperand(destination, IsolateData::builtin_entry_table_offset()));
4133 
4134     bind(&out);
4135   } else {
4136     Add_d(destination, code_object, Code::kHeaderSize - kHeapObjectTag);
4137   }
4138 }
4139 
4140 void TurboAssembler::CallCodeObject(Register code_object) {
4141   ASM_CODE_COMMENT(this);
4142   LoadCodeObjectEntry(code_object, code_object);
4143   Call(code_object);
4144 }
4145 
4146 void TurboAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) {
4147   ASM_CODE_COMMENT(this);
4148   DCHECK_EQ(JumpMode::kJump, jump_mode);
4149   LoadCodeObjectEntry(code_object, code_object);
4150   Jump(code_object);
4151 }
4152 
4153 }  // namespace internal
4154 }  // namespace v8
4155 
4156 #endif  // V8_TARGET_ARCH_LOONG64
4157