1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <limits.h>  // For LONG_MIN, LONG_MAX.
6 
7 #if V8_TARGET_ARCH_ARM
8 
9 #include "src/base/bits.h"
10 #include "src/base/division-by-constant.h"
11 #include "src/base/numbers/double.h"
12 #include "src/base/utils/random-number-generator.h"
13 #include "src/codegen/assembler-inl.h"
14 #include "src/codegen/callable.h"
15 #include "src/codegen/code-factory.h"
16 #include "src/codegen/external-reference-table.h"
17 #include "src/codegen/interface-descriptors-inl.h"
18 #include "src/codegen/macro-assembler.h"
19 #include "src/codegen/register-configuration.h"
20 #include "src/debug/debug.h"
21 #include "src/deoptimizer/deoptimizer.h"
22 #include "src/execution/frames-inl.h"
23 #include "src/heap/memory-chunk.h"
24 #include "src/init/bootstrapper.h"
25 #include "src/logging/counters.h"
26 #include "src/objects/objects-inl.h"
27 #include "src/runtime/runtime.h"
28 #include "src/snapshot/snapshot.h"
29 
30 #if V8_ENABLE_WEBASSEMBLY
31 #include "src/wasm/wasm-code-manager.h"
32 #endif  // V8_ENABLE_WEBASSEMBLY
33 
34 // Satisfy cpplint check, but don't include platform-specific header. It is
35 // included recursively via macro-assembler.h.
36 #if 0
37 #include "src/codegen/arm/macro-assembler-arm.h"
38 #endif
39 
40 namespace v8 {
41 namespace internal {
42 
RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, Register exclusion2, Register exclusion3) const43 int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
44                                                     Register exclusion1,
45                                                     Register exclusion2,
46                                                     Register exclusion3) const {
47   int bytes = 0;
48   RegList exclusions = {exclusion1, exclusion2, exclusion3};
49   RegList list = (kCallerSaved | lr) - exclusions;
50 
51   bytes += list.Count() * kPointerSize;
52 
53   if (fp_mode == SaveFPRegsMode::kSave) {
54     bytes += DwVfpRegister::kNumRegisters * DwVfpRegister::kSizeInBytes;
55   }
56 
57   return bytes;
58 }
59 
PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, Register exclusion2, Register exclusion3)60 int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
61                                     Register exclusion2, Register exclusion3) {
62   ASM_CODE_COMMENT(this);
63   int bytes = 0;
64   RegList exclusions = {exclusion1, exclusion2, exclusion3};
65   RegList list = (kCallerSaved | lr) - exclusions;
66   stm(db_w, sp, list);
67 
68   bytes += list.Count() * kPointerSize;
69 
70   if (fp_mode == SaveFPRegsMode::kSave) {
71     SaveFPRegs(sp, lr);
72     bytes += DwVfpRegister::kNumRegisters * DwVfpRegister::kSizeInBytes;
73   }
74 
75   return bytes;
76 }
77 
PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1, Register exclusion2, Register exclusion3)78 int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion1,
79                                    Register exclusion2, Register exclusion3) {
80   ASM_CODE_COMMENT(this);
81   int bytes = 0;
82   if (fp_mode == SaveFPRegsMode::kSave) {
83     RestoreFPRegs(sp, lr);
84     bytes += DwVfpRegister::kNumRegisters * DwVfpRegister::kSizeInBytes;
85   }
86 
87   RegList exclusions = {exclusion1, exclusion2, exclusion3};
88   RegList list = (kCallerSaved | lr) - exclusions;
89   ldm(ia_w, sp, list);
90 
91   bytes += list.Count() * kPointerSize;
92 
93   return bytes;
94 }
95 
LoadFromConstantsTable(Register destination, int constant_index)96 void TurboAssembler::LoadFromConstantsTable(Register destination,
97                                             int constant_index) {
98   DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable));
99 
100   const uint32_t offset =
101       FixedArray::kHeaderSize + constant_index * kPointerSize - kHeapObjectTag;
102 
103   LoadRoot(destination, RootIndex::kBuiltinsConstantsTable);
104   ldr(destination, MemOperand(destination, offset));
105 }
106 
LoadRootRelative(Register destination, int32_t offset)107 void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
108   ldr(destination, MemOperand(kRootRegister, offset));
109 }
110 
LoadRootRegisterOffset(Register destination, intptr_t offset)111 void TurboAssembler::LoadRootRegisterOffset(Register destination,
112                                             intptr_t offset) {
113   if (offset == 0) {
114     Move(destination, kRootRegister);
115   } else {
116     add(destination, kRootRegister, Operand(offset));
117   }
118 }
119 
Jump(Register target, Condition cond)120 void TurboAssembler::Jump(Register target, Condition cond) { bx(target, cond); }
121 
Jump(intptr_t target, RelocInfo::Mode rmode, Condition cond)122 void TurboAssembler::Jump(intptr_t target, RelocInfo::Mode rmode,
123                           Condition cond) {
124   mov(pc, Operand(target, rmode), LeaveCC, cond);
125 }
126 
Jump(Address target, RelocInfo::Mode rmode, Condition cond)127 void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode,
128                           Condition cond) {
129   DCHECK(!RelocInfo::IsCodeTarget(rmode));
130   Jump(static_cast<intptr_t>(target), rmode, cond);
131 }
132 
Jump(Handle<Code> code, RelocInfo::Mode rmode, Condition cond)133 void TurboAssembler::Jump(Handle<Code> code, RelocInfo::Mode rmode,
134                           Condition cond) {
135   DCHECK(RelocInfo::IsCodeTarget(rmode));
136   DCHECK_IMPLIES(options().isolate_independent_code,
137                  Builtins::IsIsolateIndependentBuiltin(*code));
138   DCHECK_IMPLIES(options().use_pc_relative_calls_and_jumps,
139                  Builtins::IsIsolateIndependentBuiltin(*code));
140 
141   Builtin builtin = Builtin::kNoBuiltinId;
142   bool target_is_builtin =
143       isolate()->builtins()->IsBuiltinHandle(code, &builtin);
144 
145   if (options().use_pc_relative_calls_and_jumps && target_is_builtin) {
146     int32_t code_target_index = AddCodeTarget(code);
147     b(code_target_index * kInstrSize, cond, RelocInfo::RELATIVE_CODE_TARGET);
148     return;
149   } else if (root_array_available_ && options().isolate_independent_code) {
150     // This branch is taken only for specific cctests, where we force isolate
151     // creation at runtime. At this point, Code space isn't restricted to a
152     // size s.t. pc-relative calls may be used.
153     UseScratchRegisterScope temps(this);
154     Register scratch = temps.Acquire();
155     int offset = IsolateData::BuiltinEntrySlotOffset(code->builtin_id());
156     ldr(scratch, MemOperand(kRootRegister, offset));
157     Jump(scratch, cond);
158     return;
159   } else if (options().inline_offheap_trampolines && target_is_builtin) {
160     // Inline the trampoline.
161     RecordCommentForOffHeapTrampoline(builtin);
162     // Use ip directly instead of using UseScratchRegisterScope, as we do not
163     // preserve scratch registers across calls.
164     mov(ip, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
165     Jump(ip, cond);
166     return;
167   }
168 
169   // 'code' is always generated ARM code, never THUMB code
170   Jump(static_cast<intptr_t>(code.address()), rmode, cond);
171 }
172 
Jump(const ExternalReference& reference)173 void TurboAssembler::Jump(const ExternalReference& reference) {
174   UseScratchRegisterScope temps(this);
175   Register scratch = temps.Acquire();
176   Move(scratch, reference);
177   Jump(scratch);
178 }
179 
Call(Register target, Condition cond)180 void TurboAssembler::Call(Register target, Condition cond) {
181   // Block constant pool for the call instruction sequence.
182   BlockConstPoolScope block_const_pool(this);
183   blx(target, cond);
184 }
185 
Call(Address target, RelocInfo::Mode rmode, Condition cond, TargetAddressStorageMode mode, bool check_constant_pool)186 void TurboAssembler::Call(Address target, RelocInfo::Mode rmode, Condition cond,
187                           TargetAddressStorageMode mode,
188                           bool check_constant_pool) {
189   // Check if we have to emit the constant pool before we block it.
190   if (check_constant_pool) MaybeCheckConstPool();
191   // Block constant pool for the call instruction sequence.
192   BlockConstPoolScope block_const_pool(this);
193 
194   bool old_predictable_code_size = predictable_code_size();
195   if (mode == NEVER_INLINE_TARGET_ADDRESS) {
196     set_predictable_code_size(true);
197   }
198 
199   // Use ip directly instead of using UseScratchRegisterScope, as we do not
200   // preserve scratch registers across calls.
201 
202   // Call sequence on V7 or later may be :
203   //  movw  ip, #... @ call address low 16
204   //  movt  ip, #... @ call address high 16
205   //  blx   ip
206   //                      @ return address
207   // Or for pre-V7 or values that may be back-patched
208   // to avoid ICache flushes:
209   //  ldr   ip, [pc, #...] @ call address
210   //  blx   ip
211   //                      @ return address
212 
213   mov(ip, Operand(target, rmode));
214   blx(ip, cond);
215 
216   if (mode == NEVER_INLINE_TARGET_ADDRESS) {
217     set_predictable_code_size(old_predictable_code_size);
218   }
219 }
220 
Call(Handle<Code> code, RelocInfo::Mode rmode, Condition cond, TargetAddressStorageMode mode, bool check_constant_pool)221 void TurboAssembler::Call(Handle<Code> code, RelocInfo::Mode rmode,
222                           Condition cond, TargetAddressStorageMode mode,
223                           bool check_constant_pool) {
224   DCHECK(RelocInfo::IsCodeTarget(rmode));
225   DCHECK_IMPLIES(options().isolate_independent_code,
226                  Builtins::IsIsolateIndependentBuiltin(*code));
227   DCHECK_IMPLIES(options().use_pc_relative_calls_and_jumps,
228                  Builtins::IsIsolateIndependentBuiltin(*code));
229 
230   Builtin builtin = Builtin::kNoBuiltinId;
231   bool target_is_builtin =
232       isolate()->builtins()->IsBuiltinHandle(code, &builtin);
233 
234   if (target_is_builtin && options().use_pc_relative_calls_and_jumps) {
235     int32_t code_target_index = AddCodeTarget(code);
236     bl(code_target_index * kInstrSize, cond, RelocInfo::RELATIVE_CODE_TARGET);
237     return;
238   } else if (root_array_available_ && options().isolate_independent_code) {
239     // This branch is taken only for specific cctests, where we force isolate
240     // creation at runtime. At this point, Code space isn't restricted to a
241     // size s.t. pc-relative calls may be used.
242     int offset = IsolateData::BuiltinEntrySlotOffset(code->builtin_id());
243     ldr(ip, MemOperand(kRootRegister, offset));
244     Call(ip, cond);
245     return;
246   } else if (target_is_builtin && options().inline_offheap_trampolines) {
247     // Inline the trampoline.
248     CallBuiltin(builtin);
249     return;
250   }
251 
252   // 'code' is always generated ARM code, never THUMB code
253   DCHECK(code->IsExecutable());
254   Call(code.address(), rmode, cond, mode);
255 }
256 
LoadEntryFromBuiltinIndex(Register builtin_index)257 void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) {
258   ASM_CODE_COMMENT(this);
259   STATIC_ASSERT(kSystemPointerSize == 4);
260   STATIC_ASSERT(kSmiShiftSize == 0);
261   STATIC_ASSERT(kSmiTagSize == 1);
262   STATIC_ASSERT(kSmiTag == 0);
263 
264   // The builtin_index register contains the builtin index as a Smi.
265   // Untagging is folded into the indexing operand below.
266   mov(builtin_index,
267       Operand(builtin_index, LSL, kSystemPointerSizeLog2 - kSmiTagSize));
268   add(builtin_index, builtin_index,
269       Operand(IsolateData::builtin_entry_table_offset()));
270   ldr(builtin_index, MemOperand(kRootRegister, builtin_index));
271 }
272 
CallBuiltinByIndex(Register builtin_index)273 void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
274   LoadEntryFromBuiltinIndex(builtin_index);
275   Call(builtin_index);
276 }
277 
LoadEntryFromBuiltin(Builtin builtin, Register destination)278 void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin,
279                                           Register destination) {
280   ASM_CODE_COMMENT(this);
281   ldr(destination, EntryFromBuiltinAsOperand(builtin));
282 }
283 
EntryFromBuiltinAsOperand(Builtin builtin)284 MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
285   ASM_CODE_COMMENT(this);
286   DCHECK(root_array_available());
287   return MemOperand(kRootRegister,
288                     IsolateData::BuiltinEntrySlotOffset(builtin));
289 }
290 
CallBuiltin(Builtin builtin, Condition cond)291 void TurboAssembler::CallBuiltin(Builtin builtin, Condition cond) {
292   ASM_CODE_COMMENT_STRING(this, CommentForOffHeapTrampoline("call", builtin));
293   DCHECK(Builtins::IsBuiltinId(builtin));
294   // Use ip directly instead of using UseScratchRegisterScope, as we do not
295   // preserve scratch registers across calls.
296   mov(ip, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
297   Call(ip, cond);
298 }
299 
LoadCodeObjectEntry(Register destination, Register code_object)300 void TurboAssembler::LoadCodeObjectEntry(Register destination,
301                                          Register code_object) {
302   ASM_CODE_COMMENT(this);
303   // Code objects are called differently depending on whether we are generating
304   // builtin code (which will later be embedded into the binary) or compiling
305   // user JS code at runtime.
306   // * Builtin code runs in --jitless mode and thus must not call into on-heap
307   //   Code targets. Instead, we dispatch through the builtins entry table.
308   // * Codegen at runtime does not have this restriction and we can use the
309   //   shorter, branchless instruction sequence. The assumption here is that
310   //   targets are usually generated code and not builtin Code objects.
311 
312   if (options().isolate_independent_code) {
313     DCHECK(root_array_available());
314     Label if_code_is_off_heap, out;
315 
316     {
317       UseScratchRegisterScope temps(this);
318       Register scratch = temps.Acquire();
319 
320       DCHECK(!AreAliased(destination, scratch));
321       DCHECK(!AreAliased(code_object, scratch));
322 
323       // Check whether the Code object is an off-heap trampoline. If so, call
324       // its (off-heap) entry point directly without going through the (on-heap)
325       // trampoline.  Otherwise, just call the Code object as always.
326       ldr(scratch, FieldMemOperand(code_object, Code::kFlagsOffset));
327       tst(scratch, Operand(Code::IsOffHeapTrampoline::kMask));
328       b(ne, &if_code_is_off_heap);
329 
330       // Not an off-heap trampoline, the entry point is at
331       // Code::raw_instruction_start().
332       add(destination, code_object,
333           Operand(Code::kHeaderSize - kHeapObjectTag));
334       jmp(&out);
335 
336       // An off-heap trampoline, the entry point is loaded from the builtin
337       // entry table.
338       bind(&if_code_is_off_heap);
339       ldr(scratch, FieldMemOperand(code_object, Code::kBuiltinIndexOffset));
340       lsl(destination, scratch, Operand(kSystemPointerSizeLog2));
341     }
342     add(destination, destination, kRootRegister);
343     ldr(destination,
344         MemOperand(destination, IsolateData::builtin_entry_table_offset()));
345 
346     bind(&out);
347   } else {
348     add(destination, code_object, Operand(Code::kHeaderSize - kHeapObjectTag));
349   }
350 }
351 
CallCodeObject(Register code_object)352 void TurboAssembler::CallCodeObject(Register code_object) {
353   ASM_CODE_COMMENT(this);
354   LoadCodeObjectEntry(code_object, code_object);
355   Call(code_object);
356 }
357 
JumpCodeObject(Register code_object, JumpMode jump_mode)358 void TurboAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) {
359   ASM_CODE_COMMENT(this);
360   DCHECK_EQ(JumpMode::kJump, jump_mode);
361   LoadCodeObjectEntry(code_object, code_object);
362   Jump(code_object);
363 }
364 
StoreReturnAddressAndCall(Register target)365 void TurboAssembler::StoreReturnAddressAndCall(Register target) {
366   ASM_CODE_COMMENT(this);
367   // This generates the final instruction sequence for calls to C functions
368   // once an exit frame has been constructed.
369   //
370   // Note that this assumes the caller code (i.e. the Code object currently
371   // being generated) is immovable or that the callee function cannot trigger
372   // GC, since the callee function will return to it.
373 
374   // Compute the return address in lr to return to after the jump below. The pc
375   // is already at '+ 8' from the current instruction; but return is after three
376   // instructions, so add another 4 to pc to get the return address.
377   Assembler::BlockConstPoolScope block_const_pool(this);
378   add(lr, pc, Operand(4));
379   str(lr, MemOperand(sp));
380   Call(target);
381 }
382 
Ret(Condition cond)383 void TurboAssembler::Ret(Condition cond) { bx(lr, cond); }
384 
Drop(int count, Condition cond)385 void TurboAssembler::Drop(int count, Condition cond) {
386   if (count > 0) {
387     add(sp, sp, Operand(count * kPointerSize), LeaveCC, cond);
388   }
389 }
390 
Drop(Register count, Condition cond)391 void TurboAssembler::Drop(Register count, Condition cond) {
392   add(sp, sp, Operand(count, LSL, kPointerSizeLog2), LeaveCC, cond);
393 }
394 
Ret(int drop, Condition cond)395 void TurboAssembler::Ret(int drop, Condition cond) {
396   Drop(drop, cond);
397   Ret(cond);
398 }
399 
Call(Label* target)400 void TurboAssembler::Call(Label* target) { bl(target); }
401 
Push(Handle<HeapObject> handle)402 void TurboAssembler::Push(Handle<HeapObject> handle) {
403   UseScratchRegisterScope temps(this);
404   Register scratch = temps.Acquire();
405   mov(scratch, Operand(handle));
406   push(scratch);
407 }
408 
Push(Smi smi)409 void TurboAssembler::Push(Smi smi) {
410   UseScratchRegisterScope temps(this);
411   Register scratch = temps.Acquire();
412   mov(scratch, Operand(smi));
413   push(scratch);
414 }
415 
PushArray(Register array, Register size, Register scratch, PushArrayOrder order)416 void TurboAssembler::PushArray(Register array, Register size, Register scratch,
417                                PushArrayOrder order) {
418   ASM_CODE_COMMENT(this);
419   UseScratchRegisterScope temps(this);
420   Register counter = scratch;
421   Register tmp = temps.Acquire();
422   DCHECK(!AreAliased(array, size, counter, tmp));
423   Label loop, entry;
424   if (order == PushArrayOrder::kReverse) {
425     mov(counter, Operand(0));
426     b(&entry);
427     bind(&loop);
428     ldr(tmp, MemOperand(array, counter, LSL, kSystemPointerSizeLog2));
429     push(tmp);
430     add(counter, counter, Operand(1));
431     bind(&entry);
432     cmp(counter, size);
433     b(lt, &loop);
434   } else {
435     mov(counter, size);
436     b(&entry);
437     bind(&loop);
438     ldr(tmp, MemOperand(array, counter, LSL, kSystemPointerSizeLog2));
439     push(tmp);
440     bind(&entry);
441     sub(counter, counter, Operand(1), SetCC);
442     b(ge, &loop);
443   }
444 }
445 
Move(Register dst, Smi smi)446 void TurboAssembler::Move(Register dst, Smi smi) { mov(dst, Operand(smi)); }
447 
Move(Register dst, Handle<HeapObject> value)448 void TurboAssembler::Move(Register dst, Handle<HeapObject> value) {
449   // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
450   // non-isolate-independent code. In many cases it might be cheaper than
451   // embedding the relocatable value.
452   if (root_array_available_ && options().isolate_independent_code) {
453     IndirectLoadConstant(dst, value);
454     return;
455   }
456   mov(dst, Operand(value));
457 }
458 
Move(Register dst, ExternalReference reference)459 void TurboAssembler::Move(Register dst, ExternalReference reference) {
460   // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
461   // non-isolate-independent code. In many cases it might be cheaper than
462   // embedding the relocatable value.
463   if (root_array_available_ && options().isolate_independent_code) {
464     IndirectLoadExternalReference(dst, reference);
465     return;
466   }
467   mov(dst, Operand(reference));
468 }
469 
Move(Register dst, Register src, Condition cond)470 void TurboAssembler::Move(Register dst, Register src, Condition cond) {
471   if (dst != src) {
472     mov(dst, src, LeaveCC, cond);
473   }
474 }
475 
Move(SwVfpRegister dst, SwVfpRegister src, Condition cond)476 void TurboAssembler::Move(SwVfpRegister dst, SwVfpRegister src,
477                           Condition cond) {
478   if (dst != src) {
479     vmov(dst, src, cond);
480   }
481 }
482 
Move(DwVfpRegister dst, DwVfpRegister src, Condition cond)483 void TurboAssembler::Move(DwVfpRegister dst, DwVfpRegister src,
484                           Condition cond) {
485   if (dst != src) {
486     vmov(dst, src, cond);
487   }
488 }
489 
Move(QwNeonRegister dst, QwNeonRegister src)490 void TurboAssembler::Move(QwNeonRegister dst, QwNeonRegister src) {
491   if (dst != src) {
492     vmov(dst, src);
493   }
494 }
495 
MovePair(Register dst0, Register src0, Register dst1, Register src1)496 void TurboAssembler::MovePair(Register dst0, Register src0, Register dst1,
497                               Register src1) {
498   DCHECK_NE(dst0, dst1);
499   if (dst0 != src1) {
500     Move(dst0, src0);
501     Move(dst1, src1);
502   } else if (dst1 != src0) {
503     // Swap the order of the moves to resolve the overlap.
504     Move(dst1, src1);
505     Move(dst0, src0);
506   } else {
507     // Worse case scenario, this is a swap.
508     Swap(dst0, src0);
509   }
510 }
511 
Swap(Register srcdst0, Register srcdst1)512 void TurboAssembler::Swap(Register srcdst0, Register srcdst1) {
513   DCHECK(srcdst0 != srcdst1);
514   UseScratchRegisterScope temps(this);
515   Register scratch = temps.Acquire();
516   mov(scratch, srcdst0);
517   mov(srcdst0, srcdst1);
518   mov(srcdst1, scratch);
519 }
520 
Swap(DwVfpRegister srcdst0, DwVfpRegister srcdst1)521 void TurboAssembler::Swap(DwVfpRegister srcdst0, DwVfpRegister srcdst1) {
522   DCHECK(srcdst0 != srcdst1);
523   DCHECK(VfpRegisterIsAvailable(srcdst0));
524   DCHECK(VfpRegisterIsAvailable(srcdst1));
525 
526   if (CpuFeatures::IsSupported(NEON)) {
527     vswp(srcdst0, srcdst1);
528   } else {
529     UseScratchRegisterScope temps(this);
530     DwVfpRegister scratch = temps.AcquireD();
531     vmov(scratch, srcdst0);
532     vmov(srcdst0, srcdst1);
533     vmov(srcdst1, scratch);
534   }
535 }
536 
Swap(QwNeonRegister srcdst0, QwNeonRegister srcdst1)537 void TurboAssembler::Swap(QwNeonRegister srcdst0, QwNeonRegister srcdst1) {
538   DCHECK(srcdst0 != srcdst1);
539   vswp(srcdst0, srcdst1);
540 }
541 
Mls(Register dst, Register src1, Register src2, Register srcA, Condition cond)542 void MacroAssembler::Mls(Register dst, Register src1, Register src2,
543                          Register srcA, Condition cond) {
544   if (CpuFeatures::IsSupported(ARMv7)) {
545     CpuFeatureScope scope(this, ARMv7);
546     mls(dst, src1, src2, srcA, cond);
547   } else {
548     UseScratchRegisterScope temps(this);
549     Register scratch = temps.Acquire();
550     DCHECK(srcA != scratch);
551     mul(scratch, src1, src2, LeaveCC, cond);
552     sub(dst, srcA, scratch, LeaveCC, cond);
553   }
554 }
555 
And(Register dst, Register src1, const Operand& src2, Condition cond)556 void MacroAssembler::And(Register dst, Register src1, const Operand& src2,
557                          Condition cond) {
558   if (!src2.IsRegister() && !src2.MustOutputRelocInfo(this) &&
559       src2.immediate() == 0) {
560     mov(dst, Operand::Zero(), LeaveCC, cond);
561   } else if (!(src2.InstructionsRequired(this) == 1) &&
562              !src2.MustOutputRelocInfo(this) &&
563              CpuFeatures::IsSupported(ARMv7) &&
564              base::bits::IsPowerOfTwo(src2.immediate() + 1)) {
565     CpuFeatureScope scope(this, ARMv7);
566     ubfx(dst, src1, 0,
567          base::bits::WhichPowerOfTwo(static_cast<uint32_t>(src2.immediate()) +
568                                      1),
569          cond);
570   } else {
571     and_(dst, src1, src2, LeaveCC, cond);
572   }
573 }
574 
Ubfx(Register dst, Register src1, int lsb, int width, Condition cond)575 void MacroAssembler::Ubfx(Register dst, Register src1, int lsb, int width,
576                           Condition cond) {
577   DCHECK_LT(lsb, 32);
578   if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
579     int mask = (1u << (width + lsb)) - 1u - ((1u << lsb) - 1u);
580     and_(dst, src1, Operand(mask), LeaveCC, cond);
581     if (lsb != 0) {
582       mov(dst, Operand(dst, LSR, lsb), LeaveCC, cond);
583     }
584   } else {
585     CpuFeatureScope scope(this, ARMv7);
586     ubfx(dst, src1, lsb, width, cond);
587   }
588 }
589 
Sbfx(Register dst, Register src1, int lsb, int width, Condition cond)590 void MacroAssembler::Sbfx(Register dst, Register src1, int lsb, int width,
591                           Condition cond) {
592   DCHECK_LT(lsb, 32);
593   if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
594     int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
595     and_(dst, src1, Operand(mask), LeaveCC, cond);
596     int shift_up = 32 - lsb - width;
597     int shift_down = lsb + shift_up;
598     if (shift_up != 0) {
599       mov(dst, Operand(dst, LSL, shift_up), LeaveCC, cond);
600     }
601     if (shift_down != 0) {
602       mov(dst, Operand(dst, ASR, shift_down), LeaveCC, cond);
603     }
604   } else {
605     CpuFeatureScope scope(this, ARMv7);
606     sbfx(dst, src1, lsb, width, cond);
607   }
608 }
609 
Bfc(Register dst, Register src, int lsb, int width, Condition cond)610 void TurboAssembler::Bfc(Register dst, Register src, int lsb, int width,
611                          Condition cond) {
612   DCHECK_LT(lsb, 32);
613   if (!CpuFeatures::IsSupported(ARMv7) || predictable_code_size()) {
614     int mask = (1 << (width + lsb)) - 1 - ((1 << lsb) - 1);
615     bic(dst, src, Operand(mask));
616   } else {
617     CpuFeatureScope scope(this, ARMv7);
618     Move(dst, src, cond);
619     bfc(dst, lsb, width, cond);
620   }
621 }
622 
LoadRoot(Register destination, RootIndex index, Condition cond)623 void TurboAssembler::LoadRoot(Register destination, RootIndex index,
624                               Condition cond) {
625   ldr(destination,
626       MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index)), cond);
627 }
628 
RecordWriteField(Register object, int offset, Register value, LinkRegisterStatus lr_status, SaveFPRegsMode save_fp, RememberedSetAction remembered_set_action, SmiCheck smi_check)629 void MacroAssembler::RecordWriteField(Register object, int offset,
630                                       Register value,
631                                       LinkRegisterStatus lr_status,
632                                       SaveFPRegsMode save_fp,
633                                       RememberedSetAction remembered_set_action,
634                                       SmiCheck smi_check) {
635   ASM_CODE_COMMENT(this);
636   // First, check if a write barrier is even needed. The tests below
637   // catch stores of Smis.
638   Label done;
639 
640   // Skip barrier if writing a smi.
641   if (smi_check == SmiCheck::kInline) {
642     JumpIfSmi(value, &done);
643   }
644 
645   // Although the object register is tagged, the offset is relative to the start
646   // of the object, so so offset must be a multiple of kPointerSize.
647   DCHECK(IsAligned(offset, kPointerSize));
648 
649   if (FLAG_debug_code) {
650     ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
651     Label ok;
652     UseScratchRegisterScope temps(this);
653     Register scratch = temps.Acquire();
654     DCHECK(!AreAliased(object, value, scratch));
655     add(scratch, object, Operand(offset - kHeapObjectTag));
656     tst(scratch, Operand(kPointerSize - 1));
657     b(eq, &ok);
658     stop();
659     bind(&ok);
660   }
661 
662   RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
663               save_fp, remembered_set_action, SmiCheck::kOmit);
664 
665   bind(&done);
666 }
667 
MaybeSaveRegisters(RegList registers)668 void TurboAssembler::MaybeSaveRegisters(RegList registers) {
669   if (registers.is_empty()) return;
670   ASM_CODE_COMMENT(this);
671   stm(db_w, sp, registers);
672 }
673 
MaybeRestoreRegisters(RegList registers)674 void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
675   if (registers.is_empty()) return;
676   ASM_CODE_COMMENT(this);
677   ldm(ia_w, sp, registers);
678 }
679 
CallEphemeronKeyBarrier(Register object, Operand offset, SaveFPRegsMode fp_mode)680 void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
681                                              SaveFPRegsMode fp_mode) {
682   ASM_CODE_COMMENT(this);
683   RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
684   MaybeSaveRegisters(registers);
685 
686   Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
687   Register slot_address_parameter =
688       WriteBarrierDescriptor::SlotAddressRegister();
689   MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
690 
691   Call(isolate()->builtins()->code_handle(
692            Builtins::GetEphemeronKeyBarrierStub(fp_mode)),
693        RelocInfo::CODE_TARGET);
694   MaybeRestoreRegisters(registers);
695 }
696 
CallRecordWriteStubSaveRegisters( Register object, Operand offset, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, StubCallMode mode)697 void TurboAssembler::CallRecordWriteStubSaveRegisters(
698     Register object, Operand offset, RememberedSetAction remembered_set_action,
699     SaveFPRegsMode fp_mode, StubCallMode mode) {
700   ASM_CODE_COMMENT(this);
701   RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
702   MaybeSaveRegisters(registers);
703 
704   Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
705   Register slot_address_parameter =
706       WriteBarrierDescriptor::SlotAddressRegister();
707   MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
708 
709   CallRecordWriteStub(object_parameter, slot_address_parameter,
710                       remembered_set_action, fp_mode, mode);
711 
712   MaybeRestoreRegisters(registers);
713 }
714 
CallRecordWriteStub( Register object, Register slot_address, RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode, StubCallMode mode)715 void TurboAssembler::CallRecordWriteStub(
716     Register object, Register slot_address,
717     RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
718     StubCallMode mode) {
719   ASM_CODE_COMMENT(this);
720   DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
721   DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
722 #if V8_ENABLE_WEBASSEMBLY
723   if (mode == StubCallMode::kCallWasmRuntimeStub) {
724     auto wasm_target =
725         wasm::WasmCode::GetRecordWriteStub(remembered_set_action, fp_mode);
726     Call(wasm_target, RelocInfo::WASM_STUB_CALL);
727 #else
728   if (false) {
729 #endif
730   } else {
731     Builtin builtin =
732         Builtins::GetRecordWriteStub(remembered_set_action, fp_mode);
733     if (options().inline_offheap_trampolines) {
734       CallBuiltin(builtin);
735     } else {
736       Handle<Code> code_target = isolate()->builtins()->code_handle(builtin);
737       Call(code_target, RelocInfo::CODE_TARGET);
738     }
739   }
740 }
741 
742 void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
743                                        Register object, Operand offset) {
744   DCHECK_NE(dst_object, dst_slot);
745   DCHECK(offset.IsRegister() || offset.IsImmediate());
746   // If `offset` is a register, it cannot overlap with `object`.
747   DCHECK_IMPLIES(offset.IsRegister(), offset.rm() != object);
748 
749   // If the slot register does not overlap with the object register, we can
750   // overwrite it.
751   if (dst_slot != object) {
752     add(dst_slot, object, offset);
753     Move(dst_object, object);
754     return;
755   }
756 
757   DCHECK_EQ(dst_slot, object);
758 
759   // If the destination object register does not overlap with the offset
760   // register, we can overwrite it.
761   if (!offset.IsRegister() || (offset.rm() != dst_object)) {
762     Move(dst_object, dst_slot);
763     add(dst_slot, dst_slot, offset);
764     return;
765   }
766 
767   DCHECK_EQ(dst_object, offset.rm());
768 
769   // We only have `dst_slot` and `dst_object` left as distinct registers so we
770   // have to swap them. We write this as a add+sub sequence to avoid using a
771   // scratch register.
772   add(dst_slot, dst_slot, dst_object);
773   sub(dst_object, dst_slot, dst_object);
774 }
775 
776 // The register 'object' contains a heap object pointer. The heap object tag is
777 // shifted away. A scratch register also needs to be available.
778 void MacroAssembler::RecordWrite(Register object, Operand offset,
779                                  Register value, LinkRegisterStatus lr_status,
780                                  SaveFPRegsMode fp_mode,
781                                  RememberedSetAction remembered_set_action,
782                                  SmiCheck smi_check) {
783   DCHECK(!AreAliased(object, value));
784   if (FLAG_debug_code) {
785     ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
786     UseScratchRegisterScope temps(this);
787     Register scratch = temps.Acquire();
788     DCHECK(!AreAliased(object, value, scratch));
789     add(scratch, object, offset);
790     ldr(scratch, MemOperand(scratch));
791     cmp(scratch, value);
792     Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
793   }
794 
795   if ((remembered_set_action == RememberedSetAction::kOmit &&
796        !FLAG_incremental_marking) ||
797       FLAG_disable_write_barriers) {
798     return;
799   }
800 
801   // First, check if a write barrier is even needed. The tests below
802   // catch stores of smis and stores into the young generation.
803   Label done;
804 
805   if (smi_check == SmiCheck::kInline) {
806     JumpIfSmi(value, &done);
807   }
808 
809   CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, eq,
810                 &done);
811   CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, eq,
812                 &done);
813 
814   // Record the actual write.
815   if (lr_status == kLRHasNotBeenSaved) {
816     push(lr);
817   }
818 
819   Register slot_address = WriteBarrierDescriptor::SlotAddressRegister();
820   DCHECK(!AreAliased(object, value, slot_address));
821   DCHECK(!offset.IsRegister());
822   add(slot_address, object, offset);
823   CallRecordWriteStub(object, slot_address, remembered_set_action, fp_mode);
824   if (lr_status == kLRHasNotBeenSaved) {
825     pop(lr);
826   }
827 
828   if (FLAG_debug_code) Move(slot_address, Operand(kZapValue));
829 
830   bind(&done);
831 }
832 
833 void TurboAssembler::PushCommonFrame(Register marker_reg) {
834   ASM_CODE_COMMENT(this);
835   if (marker_reg.is_valid()) {
836     if (marker_reg.code() > fp.code()) {
837       stm(db_w, sp, {fp, lr});
838       mov(fp, Operand(sp));
839       Push(marker_reg);
840     } else {
841       stm(db_w, sp, {marker_reg, fp, lr});
842       add(fp, sp, Operand(kPointerSize));
843     }
844   } else {
845     stm(db_w, sp, {fp, lr});
846     mov(fp, sp);
847   }
848 }
849 
850 void TurboAssembler::PushStandardFrame(Register function_reg) {
851   ASM_CODE_COMMENT(this);
852   DCHECK(!function_reg.is_valid() || function_reg.code() < cp.code());
853   stm(db_w, sp, {function_reg, cp, fp, lr});
854   int offset = -StandardFrameConstants::kContextOffset;
855   offset += function_reg.is_valid() ? kPointerSize : 0;
856   add(fp, sp, Operand(offset));
857   Push(kJavaScriptCallArgCountRegister);
858 }
859 
860 void TurboAssembler::VFPCanonicalizeNaN(const DwVfpRegister dst,
861                                         const DwVfpRegister src,
862                                         const Condition cond) {
863   // Subtracting 0.0 preserves all inputs except for signalling NaNs, which
864   // become quiet NaNs. We use vsub rather than vadd because vsub preserves -0.0
865   // inputs: -0.0 + 0.0 = 0.0, but -0.0 - 0.0 = -0.0.
866   vsub(dst, src, kDoubleRegZero, cond);
867 }
868 
869 void TurboAssembler::VFPCompareAndSetFlags(const SwVfpRegister src1,
870                                            const SwVfpRegister src2,
871                                            const Condition cond) {
872   // Compare and move FPSCR flags to the normal condition flags.
873   VFPCompareAndLoadFlags(src1, src2, pc, cond);
874 }
875 
876 void TurboAssembler::VFPCompareAndSetFlags(const SwVfpRegister src1,
877                                            const float src2,
878                                            const Condition cond) {
879   // Compare and move FPSCR flags to the normal condition flags.
880   VFPCompareAndLoadFlags(src1, src2, pc, cond);
881 }
882 
883 void TurboAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
884                                            const DwVfpRegister src2,
885                                            const Condition cond) {
886   // Compare and move FPSCR flags to the normal condition flags.
887   VFPCompareAndLoadFlags(src1, src2, pc, cond);
888 }
889 
890 void TurboAssembler::VFPCompareAndSetFlags(const DwVfpRegister src1,
891                                            const double src2,
892                                            const Condition cond) {
893   // Compare and move FPSCR flags to the normal condition flags.
894   VFPCompareAndLoadFlags(src1, src2, pc, cond);
895 }
896 
897 void TurboAssembler::VFPCompareAndLoadFlags(const SwVfpRegister src1,
898                                             const SwVfpRegister src2,
899                                             const Register fpscr_flags,
900                                             const Condition cond) {
901   // Compare and load FPSCR.
902   vcmp(src1, src2, cond);
903   vmrs(fpscr_flags, cond);
904 }
905 
906 void TurboAssembler::VFPCompareAndLoadFlags(const SwVfpRegister src1,
907                                             const float src2,
908                                             const Register fpscr_flags,
909                                             const Condition cond) {
910   // Compare and load FPSCR.
911   vcmp(src1, src2, cond);
912   vmrs(fpscr_flags, cond);
913 }
914 
915 void TurboAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
916                                             const DwVfpRegister src2,
917                                             const Register fpscr_flags,
918                                             const Condition cond) {
919   // Compare and load FPSCR.
920   vcmp(src1, src2, cond);
921   vmrs(fpscr_flags, cond);
922 }
923 
924 void TurboAssembler::VFPCompareAndLoadFlags(const DwVfpRegister src1,
925                                             const double src2,
926                                             const Register fpscr_flags,
927                                             const Condition cond) {
928   // Compare and load FPSCR.
929   vcmp(src1, src2, cond);
930   vmrs(fpscr_flags, cond);
931 }
932 
933 void TurboAssembler::VmovHigh(Register dst, DwVfpRegister src) {
934   if (src.code() < 16) {
935     const LowDwVfpRegister loc = LowDwVfpRegister::from_code(src.code());
936     vmov(dst, loc.high());
937   } else {
938     vmov(NeonS32, dst, src, 1);
939   }
940 }
941 
942 void TurboAssembler::VmovHigh(DwVfpRegister dst, Register src) {
943   if (dst.code() < 16) {
944     const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
945     vmov(loc.high(), src);
946   } else {
947     vmov(NeonS32, dst, 1, src);
948   }
949 }
950 
951 void TurboAssembler::VmovLow(Register dst, DwVfpRegister src) {
952   if (src.code() < 16) {
953     const LowDwVfpRegister loc = LowDwVfpRegister::from_code(src.code());
954     vmov(dst, loc.low());
955   } else {
956     vmov(NeonS32, dst, src, 0);
957   }
958 }
959 
960 void TurboAssembler::VmovLow(DwVfpRegister dst, Register src) {
961   if (dst.code() < 16) {
962     const LowDwVfpRegister loc = LowDwVfpRegister::from_code(dst.code());
963     vmov(loc.low(), src);
964   } else {
965     vmov(NeonS32, dst, 0, src);
966   }
967 }
968 
969 void TurboAssembler::VmovExtended(Register dst, int src_code) {
970   DCHECK_LE(SwVfpRegister::kNumRegisters, src_code);
971   DCHECK_GT(SwVfpRegister::kNumRegisters * 2, src_code);
972   if (src_code & 0x1) {
973     VmovHigh(dst, DwVfpRegister::from_code(src_code / 2));
974   } else {
975     VmovLow(dst, DwVfpRegister::from_code(src_code / 2));
976   }
977 }
978 
979 void TurboAssembler::VmovExtended(int dst_code, Register src) {
980   DCHECK_LE(SwVfpRegister::kNumRegisters, dst_code);
981   DCHECK_GT(SwVfpRegister::kNumRegisters * 2, dst_code);
982   if (dst_code & 0x1) {
983     VmovHigh(DwVfpRegister::from_code(dst_code / 2), src);
984   } else {
985     VmovLow(DwVfpRegister::from_code(dst_code / 2), src);
986   }
987 }
988 
989 void TurboAssembler::VmovExtended(int dst_code, int src_code) {
990   if (src_code == dst_code) return;
991 
992   if (src_code < SwVfpRegister::kNumRegisters &&
993       dst_code < SwVfpRegister::kNumRegisters) {
994     // src and dst are both s-registers.
995     vmov(SwVfpRegister::from_code(dst_code),
996          SwVfpRegister::from_code(src_code));
997     return;
998   }
999   DwVfpRegister dst_d_reg = DwVfpRegister::from_code(dst_code / 2);
1000   DwVfpRegister src_d_reg = DwVfpRegister::from_code(src_code / 2);
1001   int dst_offset = dst_code & 1;
1002   int src_offset = src_code & 1;
1003   if (CpuFeatures::IsSupported(NEON)) {
1004     UseScratchRegisterScope temps(this);
1005     DwVfpRegister scratch = temps.AcquireD();
1006     // On Neon we can shift and insert from d-registers.
1007     if (src_offset == dst_offset) {
1008       // Offsets are the same, use vdup to copy the source to the opposite lane.
1009       vdup(Neon32, scratch, src_d_reg, src_offset);
1010       // Here we are extending the lifetime of scratch.
1011       src_d_reg = scratch;
1012       src_offset = dst_offset ^ 1;
1013     }
1014     if (dst_offset) {
1015       if (dst_d_reg == src_d_reg) {
1016         vdup(Neon32, dst_d_reg, src_d_reg, 0);
1017       } else {
1018         vsli(Neon64, dst_d_reg, src_d_reg, 32);
1019       }
1020     } else {
1021       if (dst_d_reg == src_d_reg) {
1022         vdup(Neon32, dst_d_reg, src_d_reg, 1);
1023       } else {
1024         vsri(Neon64, dst_d_reg, src_d_reg, 32);
1025       }
1026     }
1027     return;
1028   }
1029 
1030   // Without Neon, use the scratch registers to move src and/or dst into
1031   // s-registers.
1032   UseScratchRegisterScope temps(this);
1033   LowDwVfpRegister d_scratch = temps.AcquireLowD();
1034   LowDwVfpRegister d_scratch2 = temps.AcquireLowD();
1035   int s_scratch_code = d_scratch.low().code();
1036   int s_scratch_code2 = d_scratch2.low().code();
1037   if (src_code < SwVfpRegister::kNumRegisters) {
1038     // src is an s-register, dst is not.
1039     vmov(d_scratch, dst_d_reg);
1040     vmov(SwVfpRegister::from_code(s_scratch_code + dst_offset),
1041          SwVfpRegister::from_code(src_code));
1042     vmov(dst_d_reg, d_scratch);
1043   } else if (dst_code < SwVfpRegister::kNumRegisters) {
1044     // dst is an s-register, src is not.
1045     vmov(d_scratch, src_d_reg);
1046     vmov(SwVfpRegister::from_code(dst_code),
1047          SwVfpRegister::from_code(s_scratch_code + src_offset));
1048   } else {
1049     // Neither src or dst are s-registers. Both scratch double registers are
1050     // available when there are 32 VFP registers.
1051     vmov(d_scratch, src_d_reg);
1052     vmov(d_scratch2, dst_d_reg);
1053     vmov(SwVfpRegister::from_code(s_scratch_code + dst_offset),
1054          SwVfpRegister::from_code(s_scratch_code2 + src_offset));
1055     vmov(dst_d_reg, d_scratch2);
1056   }
1057 }
1058 
1059 void TurboAssembler::VmovExtended(int dst_code, const MemOperand& src) {
1060   if (dst_code < SwVfpRegister::kNumRegisters) {
1061     vldr(SwVfpRegister::from_code(dst_code), src);
1062   } else {
1063     UseScratchRegisterScope temps(this);
1064     LowDwVfpRegister scratch = temps.AcquireLowD();
1065     // TODO(bbudge) If Neon supported, use load single lane form of vld1.
1066     int dst_s_code = scratch.low().code() + (dst_code & 1);
1067     vmov(scratch, DwVfpRegister::from_code(dst_code / 2));
1068     vldr(SwVfpRegister::from_code(dst_s_code), src);
1069     vmov(DwVfpRegister::from_code(dst_code / 2), scratch);
1070   }
1071 }
1072 
1073 void TurboAssembler::VmovExtended(const MemOperand& dst, int src_code) {
1074   if (src_code < SwVfpRegister::kNumRegisters) {
1075     vstr(SwVfpRegister::from_code(src_code), dst);
1076   } else {
1077     // TODO(bbudge) If Neon supported, use store single lane form of vst1.
1078     UseScratchRegisterScope temps(this);
1079     LowDwVfpRegister scratch = temps.AcquireLowD();
1080     int src_s_code = scratch.low().code() + (src_code & 1);
1081     vmov(scratch, DwVfpRegister::from_code(src_code / 2));
1082     vstr(SwVfpRegister::from_code(src_s_code), dst);
1083   }
1084 }
1085 
1086 void TurboAssembler::ExtractLane(Register dst, QwNeonRegister src,
1087                                  NeonDataType dt, int lane) {
1088   int size = NeonSz(dt);  // 0, 1, 2
1089   int byte = lane << size;
1090   int double_word = byte >> kDoubleSizeLog2;
1091   int double_byte = byte & (kDoubleSize - 1);
1092   int double_lane = double_byte >> size;
1093   DwVfpRegister double_source =
1094       DwVfpRegister::from_code(src.code() * 2 + double_word);
1095   vmov(dt, dst, double_source, double_lane);
1096 }
1097 
1098 void TurboAssembler::ExtractLane(Register dst, DwVfpRegister src,
1099                                  NeonDataType dt, int lane) {
1100   int size = NeonSz(dt);  // 0, 1, 2
1101   int byte = lane << size;
1102   int double_byte = byte & (kDoubleSize - 1);
1103   int double_lane = double_byte >> size;
1104   vmov(dt, dst, src, double_lane);
1105 }
1106 
1107 void TurboAssembler::ExtractLane(SwVfpRegister dst, QwNeonRegister src,
1108                                  int lane) {
1109   int s_code = src.code() * 4 + lane;
1110   VmovExtended(dst.code(), s_code);
1111 }
1112 
1113 void TurboAssembler::ExtractLane(DwVfpRegister dst, QwNeonRegister src,
1114                                  int lane) {
1115   DwVfpRegister double_dst = DwVfpRegister::from_code(src.code() * 2 + lane);
1116   vmov(dst, double_dst);
1117 }
1118 
1119 void TurboAssembler::ReplaceLane(QwNeonRegister dst, QwNeonRegister src,
1120                                  Register src_lane, NeonDataType dt, int lane) {
1121   Move(dst, src);
1122   int size = NeonSz(dt);  // 0, 1, 2
1123   int byte = lane << size;
1124   int double_word = byte >> kDoubleSizeLog2;
1125   int double_byte = byte & (kDoubleSize - 1);
1126   int double_lane = double_byte >> size;
1127   DwVfpRegister double_dst =
1128       DwVfpRegister::from_code(dst.code() * 2 + double_word);
1129   vmov(dt, double_dst, double_lane, src_lane);
1130 }
1131 
1132 void TurboAssembler::ReplaceLane(QwNeonRegister dst, QwNeonRegister src,
1133                                  SwVfpRegister src_lane, int lane) {
1134   Move(dst, src);
1135   int s_code = dst.code() * 4 + lane;
1136   VmovExtended(s_code, src_lane.code());
1137 }
1138 
1139 void TurboAssembler::ReplaceLane(QwNeonRegister dst, QwNeonRegister src,
1140                                  DwVfpRegister src_lane, int lane) {
1141   Move(dst, src);
1142   DwVfpRegister double_dst = DwVfpRegister::from_code(dst.code() * 2 + lane);
1143   vmov(double_dst, src_lane);
1144 }
1145 
1146 void TurboAssembler::LoadLane(NeonSize sz, NeonListOperand dst_list,
1147                               uint8_t lane, NeonMemOperand src) {
1148   if (sz == Neon64) {
1149     // vld1s is not valid for Neon64.
1150     vld1(Neon64, dst_list, src);
1151   } else {
1152     vld1s(sz, dst_list, lane, src);
1153   }
1154 }
1155 
1156 void TurboAssembler::StoreLane(NeonSize sz, NeonListOperand src_list,
1157                                uint8_t lane, NeonMemOperand dst) {
1158   if (sz == Neon64) {
1159     // vst1s is not valid for Neon64.
1160     vst1(Neon64, src_list, dst);
1161   } else {
1162     vst1s(sz, src_list, lane, dst);
1163   }
1164 }
1165 
1166 void TurboAssembler::LslPair(Register dst_low, Register dst_high,
1167                              Register src_low, Register src_high,
1168                              Register shift) {
1169   DCHECK(!AreAliased(dst_high, src_low));
1170   DCHECK(!AreAliased(dst_high, shift));
1171   UseScratchRegisterScope temps(this);
1172   Register scratch = temps.Acquire();
1173 
1174   Label less_than_32;
1175   Label done;
1176   rsb(scratch, shift, Operand(32), SetCC);
1177   b(gt, &less_than_32);
1178   // If shift >= 32
1179   and_(scratch, shift, Operand(0x1F));
1180   lsl(dst_high, src_low, Operand(scratch));
1181   mov(dst_low, Operand(0));
1182   jmp(&done);
1183   bind(&less_than_32);
1184   // If shift < 32
1185   lsl(dst_high, src_high, Operand(shift));
1186   orr(dst_high, dst_high, Operand(src_low, LSR, scratch));
1187   lsl(dst_low, src_low, Operand(shift));
1188   bind(&done);
1189 }
1190 
1191 void TurboAssembler::LslPair(Register dst_low, Register dst_high,
1192                              Register src_low, Register src_high,
1193                              uint32_t shift) {
1194   DCHECK_GE(63, shift);
1195   DCHECK(!AreAliased(dst_high, src_low));
1196 
1197   if (shift == 0) {
1198     Move(dst_high, src_high);
1199     Move(dst_low, src_low);
1200   } else if (shift == 32) {
1201     Move(dst_high, src_low);
1202     Move(dst_low, Operand(0));
1203   } else if (shift >= 32) {
1204     shift &= 0x1F;
1205     lsl(dst_high, src_low, Operand(shift));
1206     mov(dst_low, Operand(0));
1207   } else {
1208     lsl(dst_high, src_high, Operand(shift));
1209     orr(dst_high, dst_high, Operand(src_low, LSR, 32 - shift));
1210     lsl(dst_low, src_low, Operand(shift));
1211   }
1212 }
1213 
1214 void TurboAssembler::LsrPair(Register dst_low, Register dst_high,
1215                              Register src_low, Register src_high,
1216                              Register shift) {
1217   DCHECK(!AreAliased(dst_low, src_high));
1218   DCHECK(!AreAliased(dst_low, shift));
1219   UseScratchRegisterScope temps(this);
1220   Register scratch = temps.Acquire();
1221 
1222   Label less_than_32;
1223   Label done;
1224   rsb(scratch, shift, Operand(32), SetCC);
1225   b(gt, &less_than_32);
1226   // If shift >= 32
1227   and_(scratch, shift, Operand(0x1F));
1228   lsr(dst_low, src_high, Operand(scratch));
1229   mov(dst_high, Operand(0));
1230   jmp(&done);
1231   bind(&less_than_32);
1232   // If shift < 32
1233 
1234   lsr(dst_low, src_low, Operand(shift));
1235   orr(dst_low, dst_low, Operand(src_high, LSL, scratch));
1236   lsr(dst_high, src_high, Operand(shift));
1237   bind(&done);
1238 }
1239 
1240 void TurboAssembler::LsrPair(Register dst_low, Register dst_high,
1241                              Register src_low, Register src_high,
1242                              uint32_t shift) {
1243   DCHECK_GE(63, shift);
1244   DCHECK(!AreAliased(dst_low, src_high));
1245 
1246   if (shift == 32) {
1247     mov(dst_low, src_high);
1248     mov(dst_high, Operand(0));
1249   } else if (shift > 32) {
1250     shift &= 0x1F;
1251     lsr(dst_low, src_high, Operand(shift));
1252     mov(dst_high, Operand(0));
1253   } else if (shift == 0) {
1254     Move(dst_low, src_low);
1255     Move(dst_high, src_high);
1256   } else {
1257     lsr(dst_low, src_low, Operand(shift));
1258     orr(dst_low, dst_low, Operand(src_high, LSL, 32 - shift));
1259     lsr(dst_high, src_high, Operand(shift));
1260   }
1261 }
1262 
1263 void TurboAssembler::AsrPair(Register dst_low, Register dst_high,
1264                              Register src_low, Register src_high,
1265                              Register shift) {
1266   DCHECK(!AreAliased(dst_low, src_high));
1267   DCHECK(!AreAliased(dst_low, shift));
1268   UseScratchRegisterScope temps(this);
1269   Register scratch = temps.Acquire();
1270 
1271   Label less_than_32;
1272   Label done;
1273   rsb(scratch, shift, Operand(32), SetCC);
1274   b(gt, &less_than_32);
1275   // If shift >= 32
1276   and_(scratch, shift, Operand(0x1F));
1277   asr(dst_low, src_high, Operand(scratch));
1278   asr(dst_high, src_high, Operand(31));
1279   jmp(&done);
1280   bind(&less_than_32);
1281   // If shift < 32
1282   lsr(dst_low, src_low, Operand(shift));
1283   orr(dst_low, dst_low, Operand(src_high, LSL, scratch));
1284   asr(dst_high, src_high, Operand(shift));
1285   bind(&done);
1286 }
1287 
1288 void TurboAssembler::AsrPair(Register dst_low, Register dst_high,
1289                              Register src_low, Register src_high,
1290                              uint32_t shift) {
1291   DCHECK_GE(63, shift);
1292   DCHECK(!AreAliased(dst_low, src_high));
1293 
1294   if (shift == 32) {
1295     mov(dst_low, src_high);
1296     asr(dst_high, src_high, Operand(31));
1297   } else if (shift > 32) {
1298     shift &= 0x1F;
1299     asr(dst_low, src_high, Operand(shift));
1300     asr(dst_high, src_high, Operand(31));
1301   } else if (shift == 0) {
1302     Move(dst_low, src_low);
1303     Move(dst_high, src_high);
1304   } else {
1305     lsr(dst_low, src_low, Operand(shift));
1306     orr(dst_low, dst_low, Operand(src_high, LSL, 32 - shift));
1307     asr(dst_high, src_high, Operand(shift));
1308   }
1309 }
1310 
1311 void TurboAssembler::StubPrologue(StackFrame::Type type) {
1312   ASM_CODE_COMMENT(this);
1313   UseScratchRegisterScope temps(this);
1314   Register scratch = temps.Acquire();
1315   mov(scratch, Operand(StackFrame::TypeToMarker(type)));
1316   PushCommonFrame(scratch);
1317 }
1318 
1319 void TurboAssembler::Prologue() { PushStandardFrame(r1); }
1320 
1321 void TurboAssembler::DropArguments(Register count, ArgumentsCountType type,
1322                                    ArgumentsCountMode mode) {
1323   int receiver_bytes = (mode == kCountExcludesReceiver) ? kPointerSize : 0;
1324   switch (type) {
1325     case kCountIsInteger: {
1326       add(sp, sp, Operand(count, LSL, kPointerSizeLog2), LeaveCC);
1327       break;
1328     }
1329     case kCountIsSmi: {
1330       STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0);
1331       add(sp, sp, Operand(count, LSL, kPointerSizeLog2 - kSmiTagSize), LeaveCC);
1332       break;
1333     }
1334     case kCountIsBytes: {
1335       add(sp, sp, count, LeaveCC);
1336       break;
1337     }
1338   }
1339   if (receiver_bytes != 0) {
1340     add(sp, sp, Operand(receiver_bytes), LeaveCC);
1341   }
1342 }
1343 
1344 void TurboAssembler::DropArgumentsAndPushNewReceiver(Register argc,
1345                                                      Register receiver,
1346                                                      ArgumentsCountType type,
1347                                                      ArgumentsCountMode mode) {
1348   DCHECK(!AreAliased(argc, receiver));
1349   if (mode == kCountExcludesReceiver) {
1350     // Drop arguments without receiver and override old receiver.
1351     DropArguments(argc, type, kCountIncludesReceiver);
1352     str(receiver, MemOperand(sp, 0));
1353   } else {
1354     DropArguments(argc, type, mode);
1355     push(receiver);
1356   }
1357 }
1358 
1359 void TurboAssembler::EnterFrame(StackFrame::Type type,
1360                                 bool load_constant_pool_pointer_reg) {
1361   ASM_CODE_COMMENT(this);
1362   // r0-r3: preserved
1363   UseScratchRegisterScope temps(this);
1364   Register scratch = no_reg;
1365   if (!StackFrame::IsJavaScript(type)) {
1366     scratch = temps.Acquire();
1367     mov(scratch, Operand(StackFrame::TypeToMarker(type)));
1368   }
1369   PushCommonFrame(scratch);
1370 #if V8_ENABLE_WEBASSEMBLY
1371   if (type == StackFrame::WASM) Push(kWasmInstanceRegister);
1372 #endif  // V8_ENABLE_WEBASSEMBLY
1373 }
1374 
1375 int TurboAssembler::LeaveFrame(StackFrame::Type type) {
1376   ASM_CODE_COMMENT(this);
1377   // r0: preserved
1378   // r1: preserved
1379   // r2: preserved
1380 
1381   // Drop the execution stack down to the frame pointer and restore
1382   // the caller frame pointer and return address.
1383   mov(sp, fp);
1384   int frame_ends = pc_offset();
1385   ldm(ia_w, sp, {fp, lr});
1386   return frame_ends;
1387 }
1388 
1389 #ifdef V8_OS_WIN
1390 void TurboAssembler::AllocateStackSpace(Register bytes_scratch) {
1391   // "Functions that allocate 4 KB or more on the stack must ensure that each
1392   // page prior to the final page is touched in order." Source:
1393   // https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions?view=vs-2019#stack
1394   ASM_CODE_COMMENT(this);
1395   UseScratchRegisterScope temps(this);
1396   DwVfpRegister scratch = temps.AcquireD();
1397   Label check_offset;
1398   Label touch_next_page;
1399   jmp(&check_offset);
1400   bind(&touch_next_page);
1401   sub(sp, sp, Operand(kStackPageSize));
1402   // Just to touch the page, before we increment further.
1403   vldr(scratch, MemOperand(sp));
1404   sub(bytes_scratch, bytes_scratch, Operand(kStackPageSize));
1405 
1406   bind(&check_offset);
1407   cmp(bytes_scratch, Operand(kStackPageSize));
1408   b(gt, &touch_next_page);
1409 
1410   sub(sp, sp, bytes_scratch);
1411 }
1412 
1413 void TurboAssembler::AllocateStackSpace(int bytes) {
1414   ASM_CODE_COMMENT(this);
1415   DCHECK_GE(bytes, 0);
1416   UseScratchRegisterScope temps(this);
1417   DwVfpRegister scratch = no_dreg;
1418   while (bytes > kStackPageSize) {
1419     if (scratch == no_dreg) {
1420       scratch = temps.AcquireD();
1421     }
1422     sub(sp, sp, Operand(kStackPageSize));
1423     vldr(scratch, MemOperand(sp));
1424     bytes -= kStackPageSize;
1425   }
1426   if (bytes == 0) return;
1427   sub(sp, sp, Operand(bytes));
1428 }
1429 #endif
1430 
1431 void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space,
1432                                     StackFrame::Type frame_type) {
1433   ASM_CODE_COMMENT(this);
1434   DCHECK(frame_type == StackFrame::EXIT ||
1435          frame_type == StackFrame::BUILTIN_EXIT);
1436   UseScratchRegisterScope temps(this);
1437   Register scratch = temps.Acquire();
1438 
1439   // Set up the frame structure on the stack.
1440   DCHECK_EQ(2 * kPointerSize, ExitFrameConstants::kCallerSPDisplacement);
1441   DCHECK_EQ(1 * kPointerSize, ExitFrameConstants::kCallerPCOffset);
1442   DCHECK_EQ(0 * kPointerSize, ExitFrameConstants::kCallerFPOffset);
1443   mov(scratch, Operand(StackFrame::TypeToMarker(frame_type)));
1444   PushCommonFrame(scratch);
1445   // Reserve room for saved entry sp.
1446   sub(sp, fp, Operand(ExitFrameConstants::kFixedFrameSizeFromFp));
1447   if (FLAG_debug_code) {
1448     mov(scratch, Operand::Zero());
1449     str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset));
1450   }
1451 
1452   // Save the frame pointer and the context in top.
1453   Move(scratch, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
1454                                           isolate()));
1455   str(fp, MemOperand(scratch));
1456   Move(scratch,
1457        ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
1458   str(cp, MemOperand(scratch));
1459 
1460   // Optionally save all double registers.
1461   if (save_doubles) {
1462     SaveFPRegs(sp, scratch);
1463     // Note that d0 will be accessible at
1464     //   fp - ExitFrameConstants::kFrameSize -
1465     //   DwVfpRegister::kNumRegisters * kDoubleSize,
1466     // since the sp slot and code slot were pushed after the fp.
1467   }
1468 
1469   // Reserve place for the return address and stack space and align the frame
1470   // preparing for calling the runtime function.
1471   const int frame_alignment = MacroAssembler::ActivationFrameAlignment();
1472   AllocateStackSpace((stack_space + 1) * kPointerSize);
1473   if (frame_alignment > 0) {
1474     DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
1475     and_(sp, sp, Operand(-frame_alignment));
1476   }
1477 
1478   // Set the exit frame sp value to point just before the return address
1479   // location.
1480   add(scratch, sp, Operand(kPointerSize));
1481   str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset));
1482 }
1483 
1484 int TurboAssembler::ActivationFrameAlignment() {
1485 #if V8_HOST_ARCH_ARM
1486   // Running on the real platform. Use the alignment as mandated by the local
1487   // environment.
1488   // Note: This will break if we ever start generating snapshots on one ARM
1489   // platform for another ARM platform with a different alignment.
1490   return base::OS::ActivationFrameAlignment();
1491 #else   // V8_HOST_ARCH_ARM
1492   // If we are using the simulator then we should always align to the expected
1493   // alignment. As the simulator is used to generate snapshots we do not know
1494   // if the target platform will need alignment, so this is controlled from a
1495   // flag.
1496   return FLAG_sim_stack_alignment;
1497 #endif  // V8_HOST_ARCH_ARM
1498 }
1499 
1500 void MacroAssembler::LeaveExitFrame(bool save_doubles, Register argument_count,
1501                                     bool argument_count_is_length) {
1502   ASM_CODE_COMMENT(this);
1503   ConstantPoolUnavailableScope constant_pool_unavailable(this);
1504   UseScratchRegisterScope temps(this);
1505   Register scratch = temps.Acquire();
1506 
1507   // Optionally restore all double registers.
1508   if (save_doubles) {
1509     // Calculate the stack location of the saved doubles and restore them.
1510     const int offset = ExitFrameConstants::kFixedFrameSizeFromFp;
1511     sub(r3, fp, Operand(offset + DwVfpRegister::kNumRegisters * kDoubleSize));
1512     RestoreFPRegs(r3, scratch);
1513   }
1514 
1515   // Clear top frame.
1516   mov(r3, Operand::Zero());
1517   Move(scratch, ExternalReference::Create(IsolateAddressId::kCEntryFPAddress,
1518                                           isolate()));
1519   str(r3, MemOperand(scratch));
1520 
1521   // Restore current context from top and clear it in debug mode.
1522   Move(scratch,
1523        ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
1524   ldr(cp, MemOperand(scratch));
1525 #ifdef DEBUG
1526   mov(r3, Operand(Context::kInvalidContext));
1527   Move(scratch,
1528        ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
1529   str(r3, MemOperand(scratch));
1530 #endif
1531 
1532   // Tear down the exit frame, pop the arguments, and return.
1533   mov(sp, Operand(fp));
1534   ldm(ia_w, sp, {fp, lr});
1535   if (argument_count.is_valid()) {
1536     if (argument_count_is_length) {
1537       add(sp, sp, argument_count);
1538     } else {
1539       add(sp, sp, Operand(argument_count, LSL, kPointerSizeLog2));
1540     }
1541   }
1542 }
1543 
1544 void TurboAssembler::MovFromFloatResult(const DwVfpRegister dst) {
1545   if (use_eabi_hardfloat()) {
1546     Move(dst, d0);
1547   } else {
1548     vmov(dst, r0, r1);
1549   }
1550 }
1551 
1552 // On ARM this is just a synonym to make the purpose clear.
1553 void TurboAssembler::MovFromFloatParameter(DwVfpRegister dst) {
1554   MovFromFloatResult(dst);
1555 }
1556 
1557 void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) {
1558   ASM_CODE_COMMENT(this);
1559   DCHECK(root_array_available());
1560   Isolate* isolate = this->isolate();
1561   ExternalReference limit =
1562       kind == StackLimitKind::kRealStackLimit
1563           ? ExternalReference::address_of_real_jslimit(isolate)
1564           : ExternalReference::address_of_jslimit(isolate);
1565   DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit));
1566 
1567   intptr_t offset =
1568       TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit);
1569   CHECK(is_int32(offset));
1570   ldr(destination, MemOperand(kRootRegister, offset));
1571 }
1572 
1573 void MacroAssembler::StackOverflowCheck(Register num_args, Register scratch,
1574                                         Label* stack_overflow) {
1575   ASM_CODE_COMMENT(this);
1576   // Check the stack for overflow. We are not trying to catch
1577   // interruptions (e.g. debug break and preemption) here, so the "real stack
1578   // limit" is checked.
1579   LoadStackLimit(scratch, StackLimitKind::kRealStackLimit);
1580   // Make scratch the space we have left. The stack might already be overflowed
1581   // here which will cause scratch to become negative.
1582   sub(scratch, sp, scratch);
1583   // Check if the arguments will overflow the stack.
1584   cmp(scratch, Operand(num_args, LSL, kPointerSizeLog2));
1585   b(le, stack_overflow);  // Signed comparison.
1586 }
1587 
1588 void MacroAssembler::InvokePrologue(Register expected_parameter_count,
1589                                     Register actual_parameter_count,
1590                                     Label* done, InvokeType type) {
1591   ASM_CODE_COMMENT(this);
1592   Label regular_invoke;
1593   //  r0: actual arguments count
1594   //  r1: function (passed through to callee)
1595   //  r2: expected arguments count
1596   DCHECK_EQ(actual_parameter_count, r0);
1597   DCHECK_EQ(expected_parameter_count, r2);
1598 
1599   // If the expected parameter count is equal to the adaptor sentinel, no need
1600   // to push undefined value as arguments.
1601   if (kDontAdaptArgumentsSentinel != 0) {
1602     cmp(expected_parameter_count, Operand(kDontAdaptArgumentsSentinel));
1603     b(eq, &regular_invoke);
1604   }
1605 
1606   // If overapplication or if the actual argument count is equal to the
1607   // formal parameter count, no need to push extra undefined values.
1608   sub(expected_parameter_count, expected_parameter_count,
1609       actual_parameter_count, SetCC);
1610   b(le, &regular_invoke);
1611 
1612   Label stack_overflow;
1613   Register scratch = r4;
1614   StackOverflowCheck(expected_parameter_count, scratch, &stack_overflow);
1615 
1616   // Underapplication. Move the arguments already in the stack, including the
1617   // receiver and the return address.
1618   {
1619     Label copy, check;
1620     Register num = r5, src = r6, dest = r9;  // r7 and r8 are context and root.
1621     mov(src, sp);
1622     // Update stack pointer.
1623     lsl(scratch, expected_parameter_count, Operand(kSystemPointerSizeLog2));
1624     AllocateStackSpace(scratch);
1625     mov(dest, sp);
1626     mov(num, actual_parameter_count);
1627     b(&check);
1628     bind(&copy);
1629     ldr(scratch, MemOperand(src, kSystemPointerSize, PostIndex));
1630     str(scratch, MemOperand(dest, kSystemPointerSize, PostIndex));
1631     sub(num, num, Operand(1), SetCC);
1632     bind(&check);
1633     b(gt, &copy);
1634   }
1635 
1636   // Fill remaining expected arguments with undefined values.
1637   LoadRoot(scratch, RootIndex::kUndefinedValue);
1638   {
1639     Label loop;
1640     bind(&loop);
1641     str(scratch, MemOperand(r9, kSystemPointerSize, PostIndex));
1642     sub(expected_parameter_count, expected_parameter_count, Operand(1), SetCC);
1643     b(gt, &loop);
1644   }
1645   b(&regular_invoke);
1646 
1647   bind(&stack_overflow);
1648   {
1649     FrameScope frame(
1650         this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
1651     CallRuntime(Runtime::kThrowStackOverflow);
1652     bkpt(0);
1653   }
1654 
1655   bind(&regular_invoke);
1656 }
1657 
1658 void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target,
1659                                              Register expected_parameter_count,
1660                                              Register actual_parameter_count) {
1661   ASM_CODE_COMMENT(this);
1662   // Load receiver to pass it later to DebugOnFunctionCall hook.
1663   ldr(r4, ReceiverOperand(actual_parameter_count));
1664   FrameScope frame(
1665       this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
1666 
1667   SmiTag(expected_parameter_count);
1668   Push(expected_parameter_count);
1669 
1670   SmiTag(actual_parameter_count);
1671   Push(actual_parameter_count);
1672 
1673   if (new_target.is_valid()) {
1674     Push(new_target);
1675   }
1676   Push(fun);
1677   Push(fun);
1678   Push(r4);
1679   CallRuntime(Runtime::kDebugOnFunctionCall);
1680   Pop(fun);
1681   if (new_target.is_valid()) {
1682     Pop(new_target);
1683   }
1684 
1685   Pop(actual_parameter_count);
1686   SmiUntag(actual_parameter_count);
1687 
1688   Pop(expected_parameter_count);
1689   SmiUntag(expected_parameter_count);
1690 }
1691 
1692 void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
1693                                         Register expected_parameter_count,
1694                                         Register actual_parameter_count,
1695                                         InvokeType type) {
1696   ASM_CODE_COMMENT(this);
1697   // You can't call a function without a valid frame.
1698   DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1699   DCHECK_EQ(function, r1);
1700   DCHECK_IMPLIES(new_target.is_valid(), new_target == r3);
1701 
1702   // On function call, call into the debugger if necessary.
1703   Label debug_hook, continue_after_hook;
1704   {
1705     ExternalReference debug_hook_active =
1706         ExternalReference::debug_hook_on_function_call_address(isolate());
1707     Move(r4, debug_hook_active);
1708     ldrsb(r4, MemOperand(r4));
1709     cmp(r4, Operand(0));
1710     b(ne, &debug_hook);
1711   }
1712   bind(&continue_after_hook);
1713 
1714   // Clear the new.target register if not given.
1715   if (!new_target.is_valid()) {
1716     LoadRoot(r3, RootIndex::kUndefinedValue);
1717   }
1718 
1719   Label done;
1720   InvokePrologue(expected_parameter_count, actual_parameter_count, &done, type);
1721   // We call indirectly through the code field in the function to
1722   // allow recompilation to take effect without changing any of the
1723   // call sites.
1724   Register code = kJavaScriptCallCodeStartRegister;
1725   ldr(code, FieldMemOperand(function, JSFunction::kCodeOffset));
1726   switch (type) {
1727     case InvokeType::kCall:
1728       CallCodeObject(code);
1729       break;
1730     case InvokeType::kJump:
1731       JumpCodeObject(code);
1732       break;
1733   }
1734   b(&done);
1735 
1736   // Deferred debug hook.
1737   bind(&debug_hook);
1738   CallDebugOnFunctionCall(function, new_target, expected_parameter_count,
1739                           actual_parameter_count);
1740   b(&continue_after_hook);
1741 
1742   // Continue here if InvokePrologue does handle the invocation due to
1743   // mismatched parameter counts.
1744   bind(&done);
1745 }
1746 
1747 void MacroAssembler::InvokeFunctionWithNewTarget(
1748     Register fun, Register new_target, Register actual_parameter_count,
1749     InvokeType type) {
1750   ASM_CODE_COMMENT(this);
1751   // You can't call a function without a valid frame.
1752   DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1753 
1754   // Contract with called JS functions requires that function is passed in r1.
1755   DCHECK_EQ(fun, r1);
1756 
1757   Register expected_reg = r2;
1758   Register temp_reg = r4;
1759 
1760   ldr(temp_reg, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
1761   ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
1762   ldrh(expected_reg,
1763        FieldMemOperand(temp_reg,
1764                        SharedFunctionInfo::kFormalParameterCountOffset));
1765 
1766   InvokeFunctionCode(fun, new_target, expected_reg, actual_parameter_count,
1767                      type);
1768 }
1769 
1770 void MacroAssembler::InvokeFunction(Register function,
1771                                     Register expected_parameter_count,
1772                                     Register actual_parameter_count,
1773                                     InvokeType type) {
1774   ASM_CODE_COMMENT(this);
1775   // You can't call a function without a valid frame.
1776   DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
1777 
1778   // Contract with called JS functions requires that function is passed in r1.
1779   DCHECK_EQ(function, r1);
1780 
1781   // Get the function and setup the context.
1782   ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
1783 
1784   InvokeFunctionCode(r1, no_reg, expected_parameter_count,
1785                      actual_parameter_count, type);
1786 }
1787 
1788 void MacroAssembler::PushStackHandler() {
1789   ASM_CODE_COMMENT(this);
1790   // Adjust this code if not the case.
1791   STATIC_ASSERT(StackHandlerConstants::kSize == 2 * kPointerSize);
1792   STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0 * kPointerSize);
1793 
1794   Push(Smi::zero());  // Padding.
1795   // Link the current handler as the next handler.
1796   Move(r6,
1797        ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate()));
1798   ldr(r5, MemOperand(r6));
1799   push(r5);
1800   // Set this new handler as the current one.
1801   str(sp, MemOperand(r6));
1802 }
1803 
1804 void MacroAssembler::PopStackHandler() {
1805   ASM_CODE_COMMENT(this);
1806   UseScratchRegisterScope temps(this);
1807   Register scratch = temps.Acquire();
1808   STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0);
1809   pop(r1);
1810   Move(scratch,
1811        ExternalReference::Create(IsolateAddressId::kHandlerAddress, isolate()));
1812   str(r1, MemOperand(scratch));
1813   add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize));
1814 }
1815 
1816 void MacroAssembler::CompareObjectType(Register object, Register map,
1817                                        Register type_reg, InstanceType type) {
1818   ASM_CODE_COMMENT(this);
1819   UseScratchRegisterScope temps(this);
1820   const Register temp = type_reg == no_reg ? temps.Acquire() : type_reg;
1821 
1822   LoadMap(map, object);
1823   CompareInstanceType(map, temp, type);
1824 }
1825 
1826 void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
1827                                          InstanceType type) {
1828   ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
1829   cmp(type_reg, Operand(type));
1830 }
1831 
1832 void MacroAssembler::CompareRange(Register value, unsigned lower_limit,
1833                                   unsigned higher_limit) {
1834   ASM_CODE_COMMENT(this);
1835   DCHECK_LT(lower_limit, higher_limit);
1836   if (lower_limit != 0) {
1837     UseScratchRegisterScope temps(this);
1838     Register scratch = temps.Acquire();
1839     sub(scratch, value, Operand(lower_limit));
1840     cmp(scratch, Operand(higher_limit - lower_limit));
1841   } else {
1842     cmp(value, Operand(higher_limit));
1843   }
1844 }
1845 void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg,
1846                                               InstanceType lower_limit,
1847                                               InstanceType higher_limit) {
1848   ASM_CODE_COMMENT(this);
1849   DCHECK_LT(lower_limit, higher_limit);
1850   ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
1851   CompareRange(type_reg, lower_limit, higher_limit);
1852 }
1853 
1854 void MacroAssembler::CompareRoot(Register obj, RootIndex index) {
1855   UseScratchRegisterScope temps(this);
1856   Register scratch = temps.Acquire();
1857   DCHECK(obj != scratch);
1858   LoadRoot(scratch, index);
1859   cmp(obj, scratch);
1860 }
1861 
1862 void MacroAssembler::JumpIfIsInRange(Register value, unsigned lower_limit,
1863                                      unsigned higher_limit,
1864                                      Label* on_in_range) {
1865   ASM_CODE_COMMENT(this);
1866   CompareRange(value, lower_limit, higher_limit);
1867   b(ls, on_in_range);
1868 }
1869 
1870 void TurboAssembler::TryInlineTruncateDoubleToI(Register result,
1871                                                 DwVfpRegister double_input,
1872                                                 Label* done) {
1873   ASM_CODE_COMMENT(this);
1874   UseScratchRegisterScope temps(this);
1875   SwVfpRegister single_scratch = SwVfpRegister::no_reg();
1876   if (temps.CanAcquireVfp<SwVfpRegister>()) {
1877     single_scratch = temps.AcquireS();
1878   } else {
1879     // Re-use the input as a scratch register. However, we can only do this if
1880     // the input register is d0-d15 as there are no s32+ registers.
1881     DCHECK_LT(double_input.code(), LowDwVfpRegister::kNumRegisters);
1882     LowDwVfpRegister double_scratch =
1883         LowDwVfpRegister::from_code(double_input.code());
1884     single_scratch = double_scratch.low();
1885   }
1886   vcvt_s32_f64(single_scratch, double_input);
1887   vmov(result, single_scratch);
1888 
1889   Register scratch = temps.Acquire();
1890   // If result is not saturated (0x7FFFFFFF or 0x80000000), we are done.
1891   sub(scratch, result, Operand(1));
1892   cmp(scratch, Operand(0x7FFFFFFE));
1893   b(lt, done);
1894 }
1895 
1896 void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone,
1897                                        Register result,
1898                                        DwVfpRegister double_input,
1899                                        StubCallMode stub_mode) {
1900   ASM_CODE_COMMENT(this);
1901   Label done;
1902 
1903   TryInlineTruncateDoubleToI(result, double_input, &done);
1904 
1905   // If we fell through then inline version didn't succeed - call stub instead.
1906   push(lr);
1907   AllocateStackSpace(kDoubleSize);  // Put input on stack.
1908   vstr(double_input, MemOperand(sp, 0));
1909 
1910 #if V8_ENABLE_WEBASSEMBLY
1911   if (stub_mode == StubCallMode::kCallWasmRuntimeStub) {
1912     Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL);
1913 #else
1914   // For balance.
1915   if (false) {
1916 #endif  // V8_ENABLE_WEBASSEMBLY
1917   } else if (options().inline_offheap_trampolines) {
1918     CallBuiltin(Builtin::kDoubleToI);
1919   } else {
1920     Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET);
1921   }
1922   ldr(result, MemOperand(sp, 0));
1923 
1924   add(sp, sp, Operand(kDoubleSize));
1925   pop(lr);
1926 
1927   bind(&done);
1928 }
1929 
1930 void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
1931                                  SaveFPRegsMode save_doubles) {
1932   ASM_CODE_COMMENT(this);
1933   // All parameters are on the stack.  r0 has the return value after call.
1934 
1935   // If the expected number of arguments of the runtime function is
1936   // constant, we check that the actual number of arguments match the
1937   // expectation.
1938   CHECK(f->nargs < 0 || f->nargs == num_arguments);
1939 
1940   // TODO(1236192): Most runtime routines don't need the number of
1941   // arguments passed in because it is constant. At some point we
1942   // should remove this need and make the runtime routine entry code
1943   // smarter.
1944   mov(r0, Operand(num_arguments));
1945   Move(r1, ExternalReference::Create(f));
1946   Handle<Code> code =
1947       CodeFactory::CEntry(isolate(), f->result_size, save_doubles);
1948   Call(code, RelocInfo::CODE_TARGET);
1949 }
1950 
1951 void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
1952   ASM_CODE_COMMENT(this);
1953   const Runtime::Function* function = Runtime::FunctionForId(fid);
1954   DCHECK_EQ(1, function->result_size);
1955   if (function->nargs >= 0) {
1956     // TODO(1236192): Most runtime routines don't need the number of
1957     // arguments passed in because it is constant. At some point we
1958     // should remove this need and make the runtime routine entry code
1959     // smarter.
1960     mov(r0, Operand(function->nargs));
1961   }
1962   JumpToExternalReference(ExternalReference::Create(fid));
1963 }
1964 
1965 void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
1966                                              bool builtin_exit_frame) {
1967 #if defined(__thumb__)
1968   // Thumb mode builtin.
1969   DCHECK_EQ(builtin.address() & 1, 1);
1970 #endif
1971   Move(r1, builtin);
1972   Handle<Code> code = CodeFactory::CEntry(isolate(), 1, SaveFPRegsMode::kIgnore,
1973                                           ArgvMode::kStack, builtin_exit_frame);
1974   Jump(code, RelocInfo::CODE_TARGET);
1975 }
1976 
1977 void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
1978   mov(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
1979   Jump(kOffHeapTrampolineRegister);
1980 }
1981 
1982 void MacroAssembler::LoadWeakValue(Register out, Register in,
1983                                    Label* target_if_cleared) {
1984   cmp(in, Operand(kClearedWeakHeapObjectLower32));
1985   b(eq, target_if_cleared);
1986 
1987   and_(out, in, Operand(~kWeakHeapObjectMask));
1988 }
1989 
1990 void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value,
1991                                           Register scratch1,
1992                                           Register scratch2) {
1993   DCHECK_GT(value, 0);
1994   if (FLAG_native_code_counters && counter->Enabled()) {
1995     ASM_CODE_COMMENT(this);
1996     Move(scratch2, ExternalReference::Create(counter));
1997     ldr(scratch1, MemOperand(scratch2));
1998     add(scratch1, scratch1, Operand(value));
1999     str(scratch1, MemOperand(scratch2));
2000   }
2001 }
2002 
2003 void MacroAssembler::EmitDecrementCounter(StatsCounter* counter, int value,
2004                                           Register scratch1,
2005                                           Register scratch2) {
2006   DCHECK_GT(value, 0);
2007   if (FLAG_native_code_counters && counter->Enabled()) {
2008     ASM_CODE_COMMENT(this);
2009     Move(scratch2, ExternalReference::Create(counter));
2010     ldr(scratch1, MemOperand(scratch2));
2011     sub(scratch1, scratch1, Operand(value));
2012     str(scratch1, MemOperand(scratch2));
2013   }
2014 }
2015 
2016 void TurboAssembler::Assert(Condition cond, AbortReason reason) {
2017   if (FLAG_debug_code) Check(cond, reason);
2018 }
2019 
2020 void TurboAssembler::AssertUnreachable(AbortReason reason) {
2021   if (FLAG_debug_code) Abort(reason);
2022 }
2023 
2024 void TurboAssembler::Check(Condition cond, AbortReason reason) {
2025   Label L;
2026   b(cond, &L);
2027   Abort(reason);
2028   // will not return here
2029   bind(&L);
2030 }
2031 
2032 void TurboAssembler::Abort(AbortReason reason) {
2033   ASM_CODE_COMMENT(this);
2034   Label abort_start;
2035   bind(&abort_start);
2036   if (FLAG_code_comments) {
2037     const char* msg = GetAbortReason(reason);
2038     RecordComment("Abort message: ");
2039     RecordComment(msg);
2040   }
2041 
2042   // Avoid emitting call to builtin if requested.
2043   if (trap_on_abort()) {
2044     stop();
2045     return;
2046   }
2047 
2048   if (should_abort_hard()) {
2049     // We don't care if we constructed a frame. Just pretend we did.
2050     FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE);
2051     Move32BitImmediate(r0, Operand(static_cast<int>(reason)));
2052     PrepareCallCFunction(1, 0, r1);
2053     Move(r1, ExternalReference::abort_with_reason());
2054     // Use Call directly to avoid any unneeded overhead. The function won't
2055     // return anyway.
2056     Call(r1);
2057     return;
2058   }
2059 
2060   Move(r1, Smi::FromInt(static_cast<int>(reason)));
2061 
2062   // Disable stub call restrictions to always allow calls to abort.
2063   if (!has_frame()) {
2064     // We don't actually want to generate a pile of code for this, so just
2065     // claim there is a stack frame, without generating one.
2066     FrameScope scope(this, StackFrame::NO_FRAME_TYPE);
2067     Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
2068   } else {
2069     Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
2070   }
2071   // will not return here
2072 }
2073 
2074 void TurboAssembler::LoadMap(Register destination, Register object) {
2075   ldr(destination, FieldMemOperand(object, HeapObject::kMapOffset));
2076 }
2077 
2078 void MacroAssembler::LoadGlobalProxy(Register dst) {
2079   ASM_CODE_COMMENT(this);
2080   LoadNativeContextSlot(dst, Context::GLOBAL_PROXY_INDEX);
2081 }
2082 
2083 void MacroAssembler::LoadNativeContextSlot(Register dst, int index) {
2084   ASM_CODE_COMMENT(this);
2085   LoadMap(dst, cp);
2086   ldr(dst, FieldMemOperand(
2087                dst, Map::kConstructorOrBackPointerOrNativeContextOffset));
2088   ldr(dst, MemOperand(dst, Context::SlotOffset(index)));
2089 }
2090 
2091 void TurboAssembler::InitializeRootRegister() {
2092   ASM_CODE_COMMENT(this);
2093   ExternalReference isolate_root = ExternalReference::isolate_root(isolate());
2094   mov(kRootRegister, Operand(isolate_root));
2095 }
2096 
2097 void MacroAssembler::SmiTag(Register reg, SBit s) {
2098   add(reg, reg, Operand(reg), s);
2099 }
2100 
2101 void MacroAssembler::SmiTag(Register dst, Register src, SBit s) {
2102   add(dst, src, Operand(src), s);
2103 }
2104 
2105 void MacroAssembler::SmiTst(Register value) {
2106   tst(value, Operand(kSmiTagMask));
2107 }
2108 
2109 void TurboAssembler::JumpIfSmi(Register value, Label* smi_label) {
2110   tst(value, Operand(kSmiTagMask));
2111   b(eq, smi_label);
2112 }
2113 
2114 void TurboAssembler::JumpIfEqual(Register x, int32_t y, Label* dest) {
2115   cmp(x, Operand(y));
2116   b(eq, dest);
2117 }
2118 
2119 void TurboAssembler::JumpIfLessThan(Register x, int32_t y, Label* dest) {
2120   cmp(x, Operand(y));
2121   b(lt, dest);
2122 }
2123 
2124 void MacroAssembler::JumpIfNotSmi(Register value, Label* not_smi_label) {
2125   tst(value, Operand(kSmiTagMask));
2126   b(ne, not_smi_label);
2127 }
2128 
2129 void MacroAssembler::AssertNotSmi(Register object) {
2130   if (!FLAG_debug_code) return;
2131   ASM_CODE_COMMENT(this);
2132   STATIC_ASSERT(kSmiTag == 0);
2133   tst(object, Operand(kSmiTagMask));
2134   Check(ne, AbortReason::kOperandIsASmi);
2135 }
2136 
2137 void MacroAssembler::AssertSmi(Register object) {
2138   if (!FLAG_debug_code) return;
2139   ASM_CODE_COMMENT(this);
2140   STATIC_ASSERT(kSmiTag == 0);
2141   tst(object, Operand(kSmiTagMask));
2142   Check(eq, AbortReason::kOperandIsNotASmi);
2143 }
2144 
2145 void MacroAssembler::AssertConstructor(Register object) {
2146   if (!FLAG_debug_code) return;
2147   ASM_CODE_COMMENT(this);
2148   STATIC_ASSERT(kSmiTag == 0);
2149   tst(object, Operand(kSmiTagMask));
2150   Check(ne, AbortReason::kOperandIsASmiAndNotAConstructor);
2151   push(object);
2152   LoadMap(object, object);
2153   ldrb(object, FieldMemOperand(object, Map::kBitFieldOffset));
2154   tst(object, Operand(Map::Bits1::IsConstructorBit::kMask));
2155   pop(object);
2156   Check(ne, AbortReason::kOperandIsNotAConstructor);
2157 }
2158 
2159 void MacroAssembler::AssertFunction(Register object) {
2160   if (!FLAG_debug_code) return;
2161   ASM_CODE_COMMENT(this);
2162   STATIC_ASSERT(kSmiTag == 0);
2163   tst(object, Operand(kSmiTagMask));
2164   Check(ne, AbortReason::kOperandIsASmiAndNotAFunction);
2165   push(object);
2166   LoadMap(object, object);
2167   CompareInstanceTypeRange(object, object, FIRST_JS_FUNCTION_TYPE,
2168                            LAST_JS_FUNCTION_TYPE);
2169   pop(object);
2170   Check(ls, AbortReason::kOperandIsNotAFunction);
2171 }
2172 
2173 void MacroAssembler::AssertCallableFunction(Register object) {
2174   if (!FLAG_debug_code) return;
2175   ASM_CODE_COMMENT(this);
2176   STATIC_ASSERT(kSmiTag == 0);
2177   tst(object, Operand(kSmiTagMask));
2178   Check(ne, AbortReason::kOperandIsASmiAndNotAFunction);
2179   push(object);
2180   LoadMap(object, object);
2181   CompareInstanceTypeRange(object, object, FIRST_CALLABLE_JS_FUNCTION_TYPE,
2182                            LAST_CALLABLE_JS_FUNCTION_TYPE);
2183   pop(object);
2184   Check(ls, AbortReason::kOperandIsNotACallableFunction);
2185 }
2186 
2187 void MacroAssembler::AssertBoundFunction(Register object) {
2188   if (!FLAG_debug_code) return;
2189   ASM_CODE_COMMENT(this);
2190   STATIC_ASSERT(kSmiTag == 0);
2191   tst(object, Operand(kSmiTagMask));
2192   Check(ne, AbortReason::kOperandIsASmiAndNotABoundFunction);
2193   push(object);
2194   CompareObjectType(object, object, object, JS_BOUND_FUNCTION_TYPE);
2195   pop(object);
2196   Check(eq, AbortReason::kOperandIsNotABoundFunction);
2197 }
2198 
2199 void MacroAssembler::AssertGeneratorObject(Register object) {
2200   if (!FLAG_debug_code) return;
2201   ASM_CODE_COMMENT(this);
2202   tst(object, Operand(kSmiTagMask));
2203   Check(ne, AbortReason::kOperandIsASmiAndNotAGeneratorObject);
2204 
2205   // Load map
2206   Register map = object;
2207   push(object);
2208   LoadMap(map, object);
2209 
2210   // Check if JSGeneratorObject
2211   Label do_check;
2212   Register instance_type = object;
2213   CompareInstanceType(map, instance_type, JS_GENERATOR_OBJECT_TYPE);
2214   b(eq, &do_check);
2215 
2216   // Check if JSAsyncFunctionObject (See MacroAssembler::CompareInstanceType)
2217   cmp(instance_type, Operand(JS_ASYNC_FUNCTION_OBJECT_TYPE));
2218   b(eq, &do_check);
2219 
2220   // Check if JSAsyncGeneratorObject (See MacroAssembler::CompareInstanceType)
2221   cmp(instance_type, Operand(JS_ASYNC_GENERATOR_OBJECT_TYPE));
2222 
2223   bind(&do_check);
2224   // Restore generator object to register and perform assertion
2225   pop(object);
2226   Check(eq, AbortReason::kOperandIsNotAGeneratorObject);
2227 }
2228 
2229 void MacroAssembler::AssertUndefinedOrAllocationSite(Register object,
2230                                                      Register scratch) {
2231   if (!FLAG_debug_code) return;
2232   ASM_CODE_COMMENT(this);
2233   Label done_checking;
2234   AssertNotSmi(object);
2235   CompareRoot(object, RootIndex::kUndefinedValue);
2236   b(eq, &done_checking);
2237   LoadMap(scratch, object);
2238   CompareInstanceType(scratch, scratch, ALLOCATION_SITE_TYPE);
2239   Assert(eq, AbortReason::kExpectedUndefinedOrCell);
2240   bind(&done_checking);
2241 }
2242 
2243 void TurboAssembler::CheckFor32DRegs(Register scratch) {
2244   ASM_CODE_COMMENT(this);
2245   Move(scratch, ExternalReference::cpu_features());
2246   ldr(scratch, MemOperand(scratch));
2247   tst(scratch, Operand(1u << VFP32DREGS));
2248 }
2249 
2250 void TurboAssembler::SaveFPRegs(Register location, Register scratch) {
2251   ASM_CODE_COMMENT(this);
2252   CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2253   CheckFor32DRegs(scratch);
2254   vstm(db_w, location, d16, d31, ne);
2255   sub(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2256   vstm(db_w, location, d0, d15);
2257 }
2258 
2259 void TurboAssembler::RestoreFPRegs(Register location, Register scratch) {
2260   ASM_CODE_COMMENT(this);
2261   CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2262   CheckFor32DRegs(scratch);
2263   vldm(ia_w, location, d0, d15);
2264   vldm(ia_w, location, d16, d31, ne);
2265   add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2266 }
2267 
2268 void TurboAssembler::SaveFPRegsToHeap(Register location, Register scratch) {
2269   ASM_CODE_COMMENT(this);
2270   CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2271   CheckFor32DRegs(scratch);
2272   vstm(ia_w, location, d0, d15);
2273   vstm(ia_w, location, d16, d31, ne);
2274   add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2275 }
2276 
2277 void TurboAssembler::RestoreFPRegsFromHeap(Register location,
2278                                            Register scratch) {
2279   ASM_CODE_COMMENT(this);
2280   CpuFeatureScope scope(this, VFP32DREGS, CpuFeatureScope::kDontCheckSupported);
2281   CheckFor32DRegs(scratch);
2282   vldm(ia_w, location, d0, d15);
2283   vldm(ia_w, location, d16, d31, ne);
2284   add(location, location, Operand(16 * kDoubleSize), LeaveCC, eq);
2285 }
2286 
2287 template <typename T>
2288 void TurboAssembler::FloatMaxHelper(T result, T left, T right,
2289                                     Label* out_of_line) {
2290   // This trivial case is caught sooner, so that the out-of-line code can be
2291   // completely avoided.
2292   DCHECK(left != right);
2293 
2294   if (CpuFeatures::IsSupported(ARMv8)) {
2295     CpuFeatureScope scope(this, ARMv8);
2296     VFPCompareAndSetFlags(left, right);
2297     b(vs, out_of_line);
2298     vmaxnm(result, left, right);
2299   } else {
2300     Label done;
2301     VFPCompareAndSetFlags(left, right);
2302     b(vs, out_of_line);
2303     // Avoid a conditional instruction if the result register is unique.
2304     bool aliased_result_reg = result == left || result == right;
2305     Move(result, right, aliased_result_reg ? mi : al);
2306     Move(result, left, gt);
2307     b(ne, &done);
2308     // Left and right are equal, but check for +/-0.
2309     VFPCompareAndSetFlags(left, 0.0);
2310     b(eq, out_of_line);
2311     // The arguments are equal and not zero, so it doesn't matter which input we
2312     // pick. We have already moved one input into the result (if it didn't
2313     // already alias) so there's nothing more to do.
2314     bind(&done);
2315   }
2316 }
2317 
2318 template <typename T>
2319 void TurboAssembler::FloatMaxOutOfLineHelper(T result, T left, T right) {
2320   DCHECK(left != right);
2321 
2322   // ARMv8: At least one of left and right is a NaN.
2323   // Anything else: At least one of left and right is a NaN, or both left and
2324   // right are zeroes with unknown sign.
2325 
2326   // If left and right are +/-0, select the one with the most positive sign.
2327   // If left or right are NaN, vadd propagates the appropriate one.
2328   vadd(result, left, right);
2329 }
2330 
2331 template <typename T>
2332 void TurboAssembler::FloatMinHelper(T result, T left, T right,
2333                                     Label* out_of_line) {
2334   // This trivial case is caught sooner, so that the out-of-line code can be
2335   // completely avoided.
2336   DCHECK(left != right);
2337 
2338   if (CpuFeatures::IsSupported(ARMv8)) {
2339     CpuFeatureScope scope(this, ARMv8);
2340     VFPCompareAndSetFlags(left, right);
2341     b(vs, out_of_line);
2342     vminnm(result, left, right);
2343   } else {
2344     Label done;
2345     VFPCompareAndSetFlags(left, right);
2346     b(vs, out_of_line);
2347     // Avoid a conditional instruction if the result register is unique.
2348     bool aliased_result_reg = result == left || result == right;
2349     Move(result, left, aliased_result_reg ? mi : al);
2350     Move(result, right, gt);
2351     b(ne, &done);
2352     // Left and right are equal, but check for +/-0.
2353     VFPCompareAndSetFlags(left, 0.0);
2354     // If the arguments are equal and not zero, it doesn't matter which input we
2355     // pick. We have already moved one input into the result (if it didn't
2356     // already alias) so there's nothing more to do.
2357     b(ne, &done);
2358     // At this point, both left and right are either 0 or -0.
2359     // We could use a single 'vorr' instruction here if we had NEON support.
2360     // The algorithm used is -((-L) + (-R)), which is most efficiently expressed
2361     // as -((-L) - R).
2362     if (left == result) {
2363       DCHECK(right != result);
2364       vneg(result, left);
2365       vsub(result, result, right);
2366       vneg(result, result);
2367     } else {
2368       DCHECK(left != result);
2369       vneg(result, right);
2370       vsub(result, result, left);
2371       vneg(result, result);
2372     }
2373     bind(&done);
2374   }
2375 }
2376 
2377 template <typename T>
2378 void TurboAssembler::FloatMinOutOfLineHelper(T result, T left, T right) {
2379   DCHECK(left != right);
2380 
2381   // At least one of left and right is a NaN. Use vadd to propagate the NaN
2382   // appropriately. +/-0 is handled inline.
2383   vadd(result, left, right);
2384 }
2385 
2386 void TurboAssembler::FloatMax(SwVfpRegister result, SwVfpRegister left,
2387                               SwVfpRegister right, Label* out_of_line) {
2388   FloatMaxHelper(result, left, right, out_of_line);
2389 }
2390 
2391 void TurboAssembler::FloatMin(SwVfpRegister result, SwVfpRegister left,
2392                               SwVfpRegister right, Label* out_of_line) {
2393   FloatMinHelper(result, left, right, out_of_line);
2394 }
2395 
2396 void TurboAssembler::FloatMax(DwVfpRegister result, DwVfpRegister left,
2397                               DwVfpRegister right, Label* out_of_line) {
2398   FloatMaxHelper(result, left, right, out_of_line);
2399 }
2400 
2401 void TurboAssembler::FloatMin(DwVfpRegister result, DwVfpRegister left,
2402                               DwVfpRegister right, Label* out_of_line) {
2403   FloatMinHelper(result, left, right, out_of_line);
2404 }
2405 
2406 void TurboAssembler::FloatMaxOutOfLine(SwVfpRegister result, SwVfpRegister left,
2407                                        SwVfpRegister right) {
2408   FloatMaxOutOfLineHelper(result, left, right);
2409 }
2410 
2411 void TurboAssembler::FloatMinOutOfLine(SwVfpRegister result, SwVfpRegister left,
2412                                        SwVfpRegister right) {
2413   FloatMinOutOfLineHelper(result, left, right);
2414 }
2415 
2416 void TurboAssembler::FloatMaxOutOfLine(DwVfpRegister result, DwVfpRegister left,
2417                                        DwVfpRegister right) {
2418   FloatMaxOutOfLineHelper(result, left, right);
2419 }
2420 
2421 void TurboAssembler::FloatMinOutOfLine(DwVfpRegister result, DwVfpRegister left,
2422                                        DwVfpRegister right) {
2423   FloatMinOutOfLineHelper(result, left, right);
2424 }
2425 
2426 static const int kRegisterPassedArguments = 4;
2427 // The hardfloat calling convention passes double arguments in registers d0-d7.
2428 static const int kDoubleRegisterPassedArguments = 8;
2429 
2430 int TurboAssembler::CalculateStackPassedWords(int num_reg_arguments,
2431                                               int num_double_arguments) {
2432   int stack_passed_words = 0;
2433   if (use_eabi_hardfloat()) {
2434     // In the hard floating point calling convention, we can use the first 8
2435     // registers to pass doubles.
2436     if (num_double_arguments > kDoubleRegisterPassedArguments) {
2437       stack_passed_words +=
2438           2 * (num_double_arguments - kDoubleRegisterPassedArguments);
2439     }
2440   } else {
2441     // In the soft floating point calling convention, every double
2442     // argument is passed using two registers.
2443     num_reg_arguments += 2 * num_double_arguments;
2444   }
2445   // Up to four simple arguments are passed in registers r0..r3.
2446   if (num_reg_arguments > kRegisterPassedArguments) {
2447     stack_passed_words += num_reg_arguments - kRegisterPassedArguments;
2448   }
2449   return stack_passed_words;
2450 }
2451 
2452 void TurboAssembler::PrepareCallCFunction(int num_reg_arguments,
2453                                           int num_double_arguments,
2454                                           Register scratch) {
2455   ASM_CODE_COMMENT(this);
2456   int frame_alignment = ActivationFrameAlignment();
2457   int stack_passed_arguments =
2458       CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
2459   if (frame_alignment > kPointerSize) {
2460     UseScratchRegisterScope temps(this);
2461     if (!scratch.is_valid()) scratch = temps.Acquire();
2462     // Make stack end at alignment and make room for num_arguments - 4 words
2463     // and the original value of sp.
2464     mov(scratch, sp);
2465     AllocateStackSpace((stack_passed_arguments + 1) * kPointerSize);
2466     DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
2467     and_(sp, sp, Operand(-frame_alignment));
2468     str(scratch, MemOperand(sp, stack_passed_arguments * kPointerSize));
2469   } else if (stack_passed_arguments > 0) {
2470     AllocateStackSpace(stack_passed_arguments * kPointerSize);
2471   }
2472 }
2473 
2474 void TurboAssembler::MovToFloatParameter(DwVfpRegister src) {
2475   DCHECK(src == d0);
2476   if (!use_eabi_hardfloat()) {
2477     vmov(r0, r1, src);
2478   }
2479 }
2480 
2481 // On ARM this is just a synonym to make the purpose clear.
2482 void TurboAssembler::MovToFloatResult(DwVfpRegister src) {
2483   MovToFloatParameter(src);
2484 }
2485 
2486 void TurboAssembler::MovToFloatParameters(DwVfpRegister src1,
2487                                           DwVfpRegister src2) {
2488   DCHECK(src1 == d0);
2489   DCHECK(src2 == d1);
2490   if (!use_eabi_hardfloat()) {
2491     vmov(r0, r1, src1);
2492     vmov(r2, r3, src2);
2493   }
2494 }
2495 
2496 void TurboAssembler::CallCFunction(ExternalReference function,
2497                                    int num_reg_arguments,
2498                                    int num_double_arguments) {
2499   UseScratchRegisterScope temps(this);
2500   Register scratch = temps.Acquire();
2501   Move(scratch, function);
2502   CallCFunctionHelper(scratch, num_reg_arguments, num_double_arguments);
2503 }
2504 
2505 void TurboAssembler::CallCFunction(Register function, int num_reg_arguments,
2506                                    int num_double_arguments) {
2507   CallCFunctionHelper(function, num_reg_arguments, num_double_arguments);
2508 }
2509 
2510 void TurboAssembler::CallCFunction(ExternalReference function,
2511                                    int num_arguments) {
2512   CallCFunction(function, num_arguments, 0);
2513 }
2514 
2515 void TurboAssembler::CallCFunction(Register function, int num_arguments) {
2516   CallCFunction(function, num_arguments, 0);
2517 }
2518 
2519 void TurboAssembler::CallCFunctionHelper(Register function,
2520                                          int num_reg_arguments,
2521                                          int num_double_arguments) {
2522   ASM_CODE_COMMENT(this);
2523   DCHECK_LE(num_reg_arguments + num_double_arguments, kMaxCParameters);
2524   DCHECK(has_frame());
2525   // Make sure that the stack is aligned before calling a C function unless
2526   // running in the simulator. The simulator has its own alignment check which
2527   // provides more information.
2528 #if V8_HOST_ARCH_ARM
2529   if (FLAG_debug_code) {
2530     int frame_alignment = base::OS::ActivationFrameAlignment();
2531     int frame_alignment_mask = frame_alignment - 1;
2532     if (frame_alignment > kPointerSize) {
2533       ASM_CODE_COMMENT_STRING(this, "Check stack alignment");
2534       DCHECK(base::bits::IsPowerOfTwo(frame_alignment));
2535       Label alignment_as_expected;
2536       tst(sp, Operand(frame_alignment_mask));
2537       b(eq, &alignment_as_expected);
2538       // Don't use Check here, as it will call Runtime_Abort possibly
2539       // re-entering here.
2540       stop();
2541       bind(&alignment_as_expected);
2542     }
2543   }
2544 #endif
2545 
2546   // Save the frame pointer and PC so that the stack layout remains iterable,
2547   // even without an ExitFrame which normally exists between JS and C frames.
2548   Register addr_scratch = r4;
2549   // See x64 code for reasoning about how to address the isolate data fields.
2550   if (root_array_available()) {
2551     str(pc,
2552         MemOperand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset()));
2553     str(fp,
2554         MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
2555   } else {
2556     DCHECK_NOT_NULL(isolate());
2557     Push(addr_scratch);
2558 
2559     Move(addr_scratch,
2560          ExternalReference::fast_c_call_caller_pc_address(isolate()));
2561     str(pc, MemOperand(addr_scratch));
2562     Move(addr_scratch,
2563          ExternalReference::fast_c_call_caller_fp_address(isolate()));
2564     str(fp, MemOperand(addr_scratch));
2565 
2566     Pop(addr_scratch);
2567   }
2568 
2569   // Just call directly. The function called cannot cause a GC, or
2570   // allow preemption, so the return address in the link register
2571   // stays correct.
2572   Call(function);
2573 
2574   // We don't unset the PC; the FP is the source of truth.
2575   Register zero_scratch = r5;
2576   Push(zero_scratch);
2577   mov(zero_scratch, Operand::Zero());
2578 
2579   if (root_array_available()) {
2580     str(zero_scratch,
2581         MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
2582   } else {
2583     DCHECK_NOT_NULL(isolate());
2584     Push(addr_scratch);
2585     Move(addr_scratch,
2586          ExternalReference::fast_c_call_caller_fp_address(isolate()));
2587     str(zero_scratch, MemOperand(addr_scratch));
2588     Pop(addr_scratch);
2589   }
2590 
2591   Pop(zero_scratch);
2592 
2593   int stack_passed_arguments =
2594       CalculateStackPassedWords(num_reg_arguments, num_double_arguments);
2595   if (ActivationFrameAlignment() > kPointerSize) {
2596     ldr(sp, MemOperand(sp, stack_passed_arguments * kPointerSize));
2597   } else {
2598     add(sp, sp, Operand(stack_passed_arguments * kPointerSize));
2599   }
2600 }
2601 
2602 void TurboAssembler::CheckPageFlag(Register object, int mask, Condition cc,
2603                                    Label* condition_met) {
2604   ASM_CODE_COMMENT(this);
2605   UseScratchRegisterScope temps(this);
2606   Register scratch = temps.Acquire();
2607   DCHECK(!AreAliased(object, scratch));
2608   DCHECK(cc == eq || cc == ne);
2609   Bfc(scratch, object, 0, kPageSizeBits);
2610   ldr(scratch, MemOperand(scratch, BasicMemoryChunk::kFlagsOffset));
2611   tst(scratch, Operand(mask));
2612   b(cc, condition_met);
2613 }
2614 
2615 Register GetRegisterThatIsNotOneOf(Register reg1, Register reg2, Register reg3,
2616                                    Register reg4, Register reg5,
2617                                    Register reg6) {
2618   RegList regs = {reg1, reg2, reg3, reg4, reg5, reg6};
2619 
2620   const RegisterConfiguration* config = RegisterConfiguration::Default();
2621   for (int i = 0; i < config->num_allocatable_general_registers(); ++i) {
2622     int code = config->GetAllocatableGeneralCode(i);
2623     Register candidate = Register::from_code(code);
2624     if (regs.has(candidate)) continue;
2625     return candidate;
2626   }
2627   UNREACHABLE();
2628 }
2629 
2630 void TurboAssembler::ComputeCodeStartAddress(Register dst) {
2631   ASM_CODE_COMMENT(this);
2632   // We can use the register pc - 8 for the address of the current instruction.
2633   sub(dst, pc, Operand(pc_offset() + Instruction::kPcLoadDelta));
2634 }
2635 
2636 void TurboAssembler::CallForDeoptimization(Builtin target, int, Label* exit,
2637                                            DeoptimizeKind kind, Label* ret,
2638                                            Label*) {
2639   ASM_CODE_COMMENT(this);
2640 
2641   // All constants should have been emitted prior to deoptimization exit
2642   // emission. See PrepareForDeoptimizationExits.
2643   DCHECK(!has_pending_constants());
2644   BlockConstPoolScope block_const_pool(this);
2645 
2646   CHECK_LE(target, Builtins::kLastTier0);
2647   ldr(ip,
2648       MemOperand(kRootRegister, IsolateData::BuiltinEntrySlotOffset(target)));
2649   Call(ip);
2650   DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
2651             (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize
2652                                             : Deoptimizer::kEagerDeoptExitSize);
2653 
2654   // The above code must not emit constants either.
2655   DCHECK(!has_pending_constants());
2656 }
2657 
2658 void TurboAssembler::Trap() { stop(); }
2659 void TurboAssembler::DebugBreak() { stop(); }
2660 
2661 void TurboAssembler::I64x2BitMask(Register dst, QwNeonRegister src) {
2662   UseScratchRegisterScope temps(this);
2663   QwNeonRegister tmp1 = temps.AcquireQ();
2664   Register tmp = temps.Acquire();
2665 
2666   vshr(NeonU64, tmp1, src, 63);
2667   vmov(NeonU32, dst, tmp1.low(), 0);
2668   vmov(NeonU32, tmp, tmp1.high(), 0);
2669   add(dst, dst, Operand(tmp, LSL, 1));
2670 }
2671 
2672 void TurboAssembler::I64x2Eq(QwNeonRegister dst, QwNeonRegister src1,
2673                              QwNeonRegister src2) {
2674   UseScratchRegisterScope temps(this);
2675   Simd128Register scratch = temps.AcquireQ();
2676   vceq(Neon32, dst, src1, src2);
2677   vrev64(Neon32, scratch, dst);
2678   vand(dst, dst, scratch);
2679 }
2680 
2681 void TurboAssembler::I64x2Ne(QwNeonRegister dst, QwNeonRegister src1,
2682                              QwNeonRegister src2) {
2683   UseScratchRegisterScope temps(this);
2684   Simd128Register tmp = temps.AcquireQ();
2685   vceq(Neon32, dst, src1, src2);
2686   vrev64(Neon32, tmp, dst);
2687   vmvn(dst, dst);
2688   vorn(dst, dst, tmp);
2689 }
2690 
2691 void TurboAssembler::I64x2GtS(QwNeonRegister dst, QwNeonRegister src1,
2692                               QwNeonRegister src2) {
2693   ASM_CODE_COMMENT(this);
2694   vqsub(NeonS64, dst, src2, src1);
2695   vshr(NeonS64, dst, dst, 63);
2696 }
2697 
2698 void TurboAssembler::I64x2GeS(QwNeonRegister dst, QwNeonRegister src1,
2699                               QwNeonRegister src2) {
2700   ASM_CODE_COMMENT(this);
2701   vqsub(NeonS64, dst, src1, src2);
2702   vshr(NeonS64, dst, dst, 63);
2703   vmvn(dst, dst);
2704 }
2705 
2706 void TurboAssembler::I64x2AllTrue(Register dst, QwNeonRegister src) {
2707   ASM_CODE_COMMENT(this);
2708   UseScratchRegisterScope temps(this);
2709   QwNeonRegister tmp = temps.AcquireQ();
2710   // src = | a | b | c | d |
2711   // tmp = | max(a,b) | max(c,d) | ...
2712   vpmax(NeonU32, tmp.low(), src.low(), src.high());
2713   // tmp = | max(a,b) == 0 | max(c,d) == 0 | ...
2714   vceq(Neon32, tmp, tmp, 0);
2715   // tmp = | max(a,b) == 0 or max(c,d) == 0 | ...
2716   vpmax(NeonU32, tmp.low(), tmp.low(), tmp.low());
2717   // dst = (max(a,b) == 0 || max(c,d) == 0)
2718   // dst will either be -1 or 0.
2719   vmov(NeonS32, dst, tmp.low(), 0);
2720   // dst = !dst (-1 -> 0, 0 -> 1)
2721   add(dst, dst, Operand(1));
2722   // This works because:
2723   // !dst
2724   // = !(max(a,b) == 0 || max(c,d) == 0)
2725   // = max(a,b) != 0 && max(c,d) != 0
2726   // = (a != 0 || b != 0) && (c != 0 || d != 0)
2727   // = defintion of i64x2.all_true.
2728 }
2729 
2730 void TurboAssembler::I64x2Abs(QwNeonRegister dst, QwNeonRegister src) {
2731   ASM_CODE_COMMENT(this);
2732   UseScratchRegisterScope temps(this);
2733   Simd128Register tmp = temps.AcquireQ();
2734   vshr(NeonS64, tmp, src, 63);
2735   veor(dst, src, tmp);
2736   vsub(Neon64, dst, dst, tmp);
2737 }
2738 
2739 namespace {
2740 using AssemblerFunc = void (Assembler::*)(DwVfpRegister, SwVfpRegister,
2741                                           VFPConversionMode, const Condition);
2742 // Helper function for f64x2 convert low instructions.
2743 // This ensures that we do not overwrite src, if dst == src.
2744 void F64x2ConvertLowHelper(Assembler* assm, QwNeonRegister dst,
2745                            QwNeonRegister src, AssemblerFunc convert_fn) {
2746   LowDwVfpRegister src_d = LowDwVfpRegister::from_code(src.low().code());
2747   UseScratchRegisterScope temps(assm);
2748   if (dst == src) {
2749     LowDwVfpRegister tmp = temps.AcquireLowD();
2750     assm->vmov(tmp, src_d);
2751     src_d = tmp;
2752   }
2753   // Default arguments are not part of the function type
2754   (assm->*convert_fn)(dst.low(), src_d.low(), kDefaultRoundToZero, al);
2755   (assm->*convert_fn)(dst.high(), src_d.high(), kDefaultRoundToZero, al);
2756 }
2757 }  // namespace
2758 
2759 void TurboAssembler::F64x2ConvertLowI32x4S(QwNeonRegister dst,
2760                                            QwNeonRegister src) {
2761   F64x2ConvertLowHelper(this, dst, src, &Assembler::vcvt_f64_s32);
2762 }
2763 
2764 void TurboAssembler::F64x2ConvertLowI32x4U(QwNeonRegister dst,
2765                                            QwNeonRegister src) {
2766   F64x2ConvertLowHelper(this, dst, src, &Assembler::vcvt_f64_u32);
2767 }
2768 
2769 void TurboAssembler::F64x2PromoteLowF32x4(QwNeonRegister dst,
2770                                           QwNeonRegister src) {
2771   F64x2ConvertLowHelper(this, dst, src, &Assembler::vcvt_f64_f32);
2772 }
2773 
2774 }  // namespace internal
2775 }  // namespace v8
2776 
2777 #endif  // V8_TARGET_ARCH_ARM
2778