1// Copyright 2013 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#if V8_TARGET_ARCH_ARM64
6
7#include "src/base/bits.h"
8#include "src/base/division-by-constant.h"
9#include "src/codegen/assembler.h"
10#include "src/codegen/callable.h"
11#include "src/codegen/code-factory.h"
12#include "src/codegen/external-reference-table.h"
13#include "src/codegen/interface-descriptors-inl.h"
14#include "src/codegen/macro-assembler-inl.h"
15#include "src/codegen/register-configuration.h"
16#include "src/codegen/reloc-info.h"
17#include "src/debug/debug.h"
18#include "src/deoptimizer/deoptimizer.h"
19#include "src/execution/frame-constants.h"
20#include "src/execution/frames-inl.h"
21#include "src/heap/memory-chunk.h"
22#include "src/init/bootstrapper.h"
23#include "src/logging/counters.h"
24#include "src/runtime/runtime.h"
25#include "src/snapshot/snapshot.h"
26
27#if V8_ENABLE_WEBASSEMBLY
28#include "src/wasm/wasm-code-manager.h"
29#endif  // V8_ENABLE_WEBASSEMBLY
30
31// Satisfy cpplint check, but don't include platform-specific header. It is
32// included recursively via macro-assembler.h.
33#if 0
34#include "src/base/platform/wrappers.h"
35#include "src/codegen/arm64/macro-assembler-arm64.h"
36#endif
37
38namespace v8 {
39namespace internal {
40
41CPURegList TurboAssembler::DefaultTmpList() { return CPURegList(ip0, ip1); }
42
43CPURegList TurboAssembler::DefaultFPTmpList() {
44  return CPURegList(fp_scratch1, fp_scratch2);
45}
46
47namespace {
48
49// For WebAssembly we care about the full floating point register. If we are not
50// running Wasm, we can get away with saving half of those registers.
51#if V8_ENABLE_WEBASSEMBLY
52constexpr int kStackSavedSavedFPSizeInBits = kQRegSizeInBits;
53#else
54constexpr int kStackSavedSavedFPSizeInBits = kDRegSizeInBits;
55#endif  // V8_ENABLE_WEBASSEMBLY
56
57}  // namespace
58
59void TurboAssembler::PushCPURegList(CPURegList registers) {
60  // If LR was stored here, we would need to sign it if
61  // V8_ENABLE_CONTROL_FLOW_INTEGRITY is on.
62  DCHECK(!registers.IncludesAliasOf(lr));
63
64  int size = registers.RegisterSizeInBytes();
65  DCHECK_EQ(0, (size * registers.Count()) % 16);
66
67  // Push up to four registers at a time.
68  while (!registers.IsEmpty()) {
69    int count_before = registers.Count();
70    const CPURegister& src0 = registers.PopHighestIndex();
71    const CPURegister& src1 = registers.PopHighestIndex();
72    const CPURegister& src2 = registers.PopHighestIndex();
73    const CPURegister& src3 = registers.PopHighestIndex();
74    int count = count_before - registers.Count();
75    PushHelper(count, size, src0, src1, src2, src3);
76  }
77}
78
79void TurboAssembler::PopCPURegList(CPURegList registers) {
80  int size = registers.RegisterSizeInBytes();
81  DCHECK_EQ(0, (size * registers.Count()) % 16);
82
83  // If LR was loaded here, we would need to authenticate it if
84  // V8_ENABLE_CONTROL_FLOW_INTEGRITY is on.
85  DCHECK(!registers.IncludesAliasOf(lr));
86
87  // Pop up to four registers at a time.
88  while (!registers.IsEmpty()) {
89    int count_before = registers.Count();
90    const CPURegister& dst0 = registers.PopLowestIndex();
91    const CPURegister& dst1 = registers.PopLowestIndex();
92    const CPURegister& dst2 = registers.PopLowestIndex();
93    const CPURegister& dst3 = registers.PopLowestIndex();
94    int count = count_before - registers.Count();
95    PopHelper(count, size, dst0, dst1, dst2, dst3);
96  }
97}
98
99int TurboAssembler::RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,
100                                                    Register exclusion) const {
101  auto list = kCallerSaved;
102  list.Remove(exclusion);
103  list.Align();
104
105  int bytes = list.TotalSizeInBytes();
106
107  if (fp_mode == SaveFPRegsMode::kSave) {
108    auto fp_list = CPURegList::GetCallerSavedV(kStackSavedSavedFPSizeInBits);
109    DCHECK_EQ(fp_list.Count() % 2, 0);
110    bytes += fp_list.TotalSizeInBytes();
111  }
112  return bytes;
113}
114
115int TurboAssembler::PushCallerSaved(SaveFPRegsMode fp_mode,
116                                    Register exclusion) {
117  ASM_CODE_COMMENT(this);
118  auto list = kCallerSaved;
119  list.Remove(exclusion);
120  list.Align();
121
122  PushCPURegList(list);
123
124  int bytes = list.TotalSizeInBytes();
125
126  if (fp_mode == SaveFPRegsMode::kSave) {
127    auto fp_list = CPURegList::GetCallerSavedV(kStackSavedSavedFPSizeInBits);
128    DCHECK_EQ(fp_list.Count() % 2, 0);
129    PushCPURegList(fp_list);
130    bytes += fp_list.TotalSizeInBytes();
131  }
132  return bytes;
133}
134
135int TurboAssembler::PopCallerSaved(SaveFPRegsMode fp_mode, Register exclusion) {
136  ASM_CODE_COMMENT(this);
137  int bytes = 0;
138  if (fp_mode == SaveFPRegsMode::kSave) {
139    auto fp_list = CPURegList::GetCallerSavedV(kStackSavedSavedFPSizeInBits);
140    DCHECK_EQ(fp_list.Count() % 2, 0);
141    PopCPURegList(fp_list);
142    bytes += fp_list.TotalSizeInBytes();
143  }
144
145  auto list = kCallerSaved;
146  list.Remove(exclusion);
147  list.Align();
148
149  PopCPURegList(list);
150  bytes += list.TotalSizeInBytes();
151
152  return bytes;
153}
154
155void TurboAssembler::LogicalMacro(const Register& rd, const Register& rn,
156                                  const Operand& operand, LogicalOp op) {
157  ASM_CODE_COMMENT(this);
158  UseScratchRegisterScope temps(this);
159
160  if (operand.NeedsRelocation(this)) {
161    Register temp = temps.AcquireX();
162    Ldr(temp, operand.immediate());
163    Logical(rd, rn, temp, op);
164
165  } else if (operand.IsImmediate()) {
166    int64_t immediate = operand.ImmediateValue();
167    unsigned reg_size = rd.SizeInBits();
168
169    // If the operation is NOT, invert the operation and immediate.
170    if ((op & NOT) == NOT) {
171      op = static_cast<LogicalOp>(op & ~NOT);
172      immediate = ~immediate;
173    }
174
175    // Ignore the top 32 bits of an immediate if we're moving to a W register.
176    if (rd.Is32Bits()) {
177      // Check that the top 32 bits are consistent.
178      DCHECK(((immediate >> kWRegSizeInBits) == 0) ||
179             ((immediate >> kWRegSizeInBits) == -1));
180      immediate &= kWRegMask;
181    }
182
183    DCHECK(rd.Is64Bits() || is_uint32(immediate));
184
185    // Special cases for all set or all clear immediates.
186    if (immediate == 0) {
187      switch (op) {
188        case AND:
189          Mov(rd, 0);
190          return;
191        case ORR:  // Fall through.
192        case EOR:
193          Mov(rd, rn);
194          return;
195        case ANDS:  // Fall through.
196        case BICS:
197          break;
198        default:
199          UNREACHABLE();
200      }
201    } else if ((rd.Is64Bits() && (immediate == -1L)) ||
202               (rd.Is32Bits() && (immediate == 0xFFFFFFFFL))) {
203      switch (op) {
204        case AND:
205          Mov(rd, rn);
206          return;
207        case ORR:
208          Mov(rd, immediate);
209          return;
210        case EOR:
211          Mvn(rd, rn);
212          return;
213        case ANDS:  // Fall through.
214        case BICS:
215          break;
216        default:
217          UNREACHABLE();
218      }
219    }
220
221    unsigned n, imm_s, imm_r;
222    if (IsImmLogical(immediate, reg_size, &n, &imm_s, &imm_r)) {
223      // Immediate can be encoded in the instruction.
224      LogicalImmediate(rd, rn, n, imm_s, imm_r, op);
225    } else {
226      // Immediate can't be encoded: synthesize using move immediate.
227      Register temp = temps.AcquireSameSizeAs(rn);
228
229      // If the left-hand input is the stack pointer, we can't pre-shift the
230      // immediate, as the encoding won't allow the subsequent post shift.
231      PreShiftImmMode mode = rn == sp ? kNoShift : kAnyShift;
232      Operand imm_operand = MoveImmediateForShiftedOp(temp, immediate, mode);
233
234      if (rd.IsSP()) {
235        // If rd is the stack pointer we cannot use it as the destination
236        // register so we use the temp register as an intermediate again.
237        Logical(temp, rn, imm_operand, op);
238        Mov(sp, temp);
239      } else {
240        Logical(rd, rn, imm_operand, op);
241      }
242    }
243
244  } else if (operand.IsExtendedRegister()) {
245    DCHECK(operand.reg().SizeInBits() <= rd.SizeInBits());
246    // Add/sub extended supports shift <= 4. We want to support exactly the
247    // same modes here.
248    DCHECK_LE(operand.shift_amount(), 4);
249    DCHECK(operand.reg().Is64Bits() ||
250           ((operand.extend() != UXTX) && (operand.extend() != SXTX)));
251    Register temp = temps.AcquireSameSizeAs(rn);
252    EmitExtendShift(temp, operand.reg(), operand.extend(),
253                    operand.shift_amount());
254    Logical(rd, rn, temp, op);
255
256  } else {
257    // The operand can be encoded in the instruction.
258    DCHECK(operand.IsShiftedRegister());
259    Logical(rd, rn, operand, op);
260  }
261}
262
263void TurboAssembler::Mov(const Register& rd, uint64_t imm) {
264  DCHECK(allow_macro_instructions());
265  DCHECK(is_uint32(imm) || is_int32(imm) || rd.Is64Bits());
266  DCHECK(!rd.IsZero());
267
268  // TODO(all) extend to support more immediates.
269  //
270  // Immediates on Aarch64 can be produced using an initial value, and zero to
271  // three move keep operations.
272  //
273  // Initial values can be generated with:
274  //  1. 64-bit move zero (movz).
275  //  2. 32-bit move inverted (movn).
276  //  3. 64-bit move inverted.
277  //  4. 32-bit orr immediate.
278  //  5. 64-bit orr immediate.
279  // Move-keep may then be used to modify each of the 16-bit half-words.
280  //
281  // The code below supports all five initial value generators, and
282  // applying move-keep operations to move-zero and move-inverted initial
283  // values.
284
285  // Try to move the immediate in one instruction, and if that fails, switch to
286  // using multiple instructions.
287  if (!TryOneInstrMoveImmediate(rd, imm)) {
288    unsigned reg_size = rd.SizeInBits();
289
290    // Generic immediate case. Imm will be represented by
291    //   [imm3, imm2, imm1, imm0], where each imm is 16 bits.
292    // A move-zero or move-inverted is generated for the first non-zero or
293    // non-0xFFFF immX, and a move-keep for subsequent non-zero immX.
294
295    uint64_t ignored_halfword = 0;
296    bool invert_move = false;
297    // If the number of 0xFFFF halfwords is greater than the number of 0x0000
298    // halfwords, it's more efficient to use move-inverted.
299    if (CountSetHalfWords(imm, reg_size) > CountSetHalfWords(~imm, reg_size)) {
300      ignored_halfword = 0xFFFFL;
301      invert_move = true;
302    }
303
304    // Mov instructions can't move immediate values into the stack pointer, so
305    // set up a temporary register, if needed.
306    UseScratchRegisterScope temps(this);
307    Register temp = rd.IsSP() ? temps.AcquireSameSizeAs(rd) : rd;
308
309    // Iterate through the halfwords. Use movn/movz for the first non-ignored
310    // halfword, and movk for subsequent halfwords.
311    DCHECK_EQ(reg_size % 16, 0);
312    bool first_mov_done = false;
313    for (int i = 0; i < (rd.SizeInBits() / 16); i++) {
314      uint64_t imm16 = (imm >> (16 * i)) & 0xFFFFL;
315      if (imm16 != ignored_halfword) {
316        if (!first_mov_done) {
317          if (invert_move) {
318            movn(temp, (~imm16) & 0xFFFFL, 16 * i);
319          } else {
320            movz(temp, imm16, 16 * i);
321          }
322          first_mov_done = true;
323        } else {
324          // Construct a wider constant.
325          movk(temp, imm16, 16 * i);
326        }
327      }
328    }
329    DCHECK(first_mov_done);
330
331    // Move the temporary if the original destination register was the stack
332    // pointer.
333    if (rd.IsSP()) {
334      mov(rd, temp);
335    }
336  }
337}
338
339void TurboAssembler::Mov(const Register& rd, const Operand& operand,
340                         DiscardMoveMode discard_mode) {
341  DCHECK(allow_macro_instructions());
342  DCHECK(!rd.IsZero());
343
344  // Provide a swap register for instructions that need to write into the
345  // system stack pointer (and can't do this inherently).
346  UseScratchRegisterScope temps(this);
347  Register dst = (rd.IsSP()) ? temps.AcquireSameSizeAs(rd) : rd;
348
349  if (operand.NeedsRelocation(this)) {
350    // TODO(jgruber,v8:8887): Also consider a root-relative load when generating
351    // non-isolate-independent code. In many cases it might be cheaper than
352    // embedding the relocatable value.
353    if (root_array_available_ && options().isolate_independent_code) {
354      if (operand.ImmediateRMode() == RelocInfo::EXTERNAL_REFERENCE) {
355        Address addr = static_cast<Address>(operand.ImmediateValue());
356        ExternalReference reference = bit_cast<ExternalReference>(addr);
357        IndirectLoadExternalReference(rd, reference);
358        return;
359      } else if (RelocInfo::IsEmbeddedObjectMode(operand.ImmediateRMode())) {
360        Handle<HeapObject> x(
361            reinterpret_cast<Address*>(operand.ImmediateValue()));
362        // TODO(v8:9706): Fix-it! This load will always uncompress the value
363        // even when we are loading a compressed embedded object.
364        IndirectLoadConstant(rd.X(), x);
365        return;
366      }
367    }
368    Ldr(dst, operand);
369  } else if (operand.IsImmediate()) {
370    // Call the macro assembler for generic immediates.
371    Mov(dst, operand.ImmediateValue());
372  } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) {
373    // Emit a shift instruction if moving a shifted register. This operation
374    // could also be achieved using an orr instruction (like orn used by Mvn),
375    // but using a shift instruction makes the disassembly clearer.
376    EmitShift(dst, operand.reg(), operand.shift(), operand.shift_amount());
377  } else if (operand.IsExtendedRegister()) {
378    // Emit an extend instruction if moving an extended register. This handles
379    // extend with post-shift operations, too.
380    EmitExtendShift(dst, operand.reg(), operand.extend(),
381                    operand.shift_amount());
382  } else {
383    // Otherwise, emit a register move only if the registers are distinct, or
384    // if they are not X registers.
385    //
386    // Note that mov(w0, w0) is not a no-op because it clears the top word of
387    // x0. A flag is provided (kDiscardForSameWReg) if a move between the same W
388    // registers is not required to clear the top word of the X register. In
389    // this case, the instruction is discarded.
390    //
391    // If sp is an operand, add #0 is emitted, otherwise, orr #0.
392    if (rd != operand.reg() ||
393        (rd.Is32Bits() && (discard_mode == kDontDiscardForSameWReg))) {
394      Assembler::mov(rd, operand.reg());
395    }
396    // This case can handle writes into the system stack pointer directly.
397    dst = rd;
398  }
399
400  // Copy the result to the system stack pointer.
401  if (dst != rd) {
402    DCHECK(rd.IsSP());
403    Assembler::mov(rd, dst);
404  }
405}
406
407void TurboAssembler::Mov(const Register& rd, Smi smi) {
408  return Mov(rd, Operand(smi));
409}
410
411void TurboAssembler::Movi16bitHelper(const VRegister& vd, uint64_t imm) {
412  DCHECK(is_uint16(imm));
413  int byte1 = (imm & 0xFF);
414  int byte2 = ((imm >> 8) & 0xFF);
415  if (byte1 == byte2) {
416    movi(vd.Is64Bits() ? vd.V8B() : vd.V16B(), byte1);
417  } else if (byte1 == 0) {
418    movi(vd, byte2, LSL, 8);
419  } else if (byte2 == 0) {
420    movi(vd, byte1);
421  } else if (byte1 == 0xFF) {
422    mvni(vd, ~byte2 & 0xFF, LSL, 8);
423  } else if (byte2 == 0xFF) {
424    mvni(vd, ~byte1 & 0xFF);
425  } else {
426    UseScratchRegisterScope temps(this);
427    Register temp = temps.AcquireW();
428    movz(temp, imm);
429    dup(vd, temp);
430  }
431}
432
433void TurboAssembler::Movi32bitHelper(const VRegister& vd, uint64_t imm) {
434  DCHECK(is_uint32(imm));
435
436  uint8_t bytes[sizeof(imm)];
437  memcpy(bytes, &imm, sizeof(imm));
438
439  // All bytes are either 0x00 or 0xFF.
440  {
441    bool all0orff = true;
442    for (int i = 0; i < 4; ++i) {
443      if ((bytes[i] != 0) && (bytes[i] != 0xFF)) {
444        all0orff = false;
445        break;
446      }
447    }
448
449    if (all0orff == true) {
450      movi(vd.Is64Bits() ? vd.V1D() : vd.V2D(), ((imm << 32) | imm));
451      return;
452    }
453  }
454
455  // Of the 4 bytes, only one byte is non-zero.
456  for (int i = 0; i < 4; i++) {
457    if ((imm & (0xFF << (i * 8))) == imm) {
458      movi(vd, bytes[i], LSL, i * 8);
459      return;
460    }
461  }
462
463  // Of the 4 bytes, only one byte is not 0xFF.
464  for (int i = 0; i < 4; i++) {
465    uint32_t mask = ~(0xFF << (i * 8));
466    if ((imm & mask) == mask) {
467      mvni(vd, ~bytes[i] & 0xFF, LSL, i * 8);
468      return;
469    }
470  }
471
472  // Immediate is of the form 0x00MMFFFF.
473  if ((imm & 0xFF00FFFF) == 0x0000FFFF) {
474    movi(vd, bytes[2], MSL, 16);
475    return;
476  }
477
478  // Immediate is of the form 0x0000MMFF.
479  if ((imm & 0xFFFF00FF) == 0x000000FF) {
480    movi(vd, bytes[1], MSL, 8);
481    return;
482  }
483
484  // Immediate is of the form 0xFFMM0000.
485  if ((imm & 0xFF00FFFF) == 0xFF000000) {
486    mvni(vd, ~bytes[2] & 0xFF, MSL, 16);
487    return;
488  }
489  // Immediate is of the form 0xFFFFMM00.
490  if ((imm & 0xFFFF00FF) == 0xFFFF0000) {
491    mvni(vd, ~bytes[1] & 0xFF, MSL, 8);
492    return;
493  }
494
495  // Top and bottom 16-bits are equal.
496  if (((imm >> 16) & 0xFFFF) == (imm & 0xFFFF)) {
497    Movi16bitHelper(vd.Is64Bits() ? vd.V4H() : vd.V8H(), imm & 0xFFFF);
498    return;
499  }
500
501  // Default case.
502  {
503    UseScratchRegisterScope temps(this);
504    Register temp = temps.AcquireW();
505    Mov(temp, imm);
506    dup(vd, temp);
507  }
508}
509
510void TurboAssembler::Movi64bitHelper(const VRegister& vd, uint64_t imm) {
511  // All bytes are either 0x00 or 0xFF.
512  {
513    bool all0orff = true;
514    for (int i = 0; i < 8; ++i) {
515      int byteval = (imm >> (i * 8)) & 0xFF;
516      if (byteval != 0 && byteval != 0xFF) {
517        all0orff = false;
518        break;
519      }
520    }
521    if (all0orff == true) {
522      movi(vd, imm);
523      return;
524    }
525  }
526
527  // Top and bottom 32-bits are equal.
528  if (((imm >> 32) & 0xFFFFFFFF) == (imm & 0xFFFFFFFF)) {
529    Movi32bitHelper(vd.Is64Bits() ? vd.V2S() : vd.V4S(), imm & 0xFFFFFFFF);
530    return;
531  }
532
533  // Default case.
534  {
535    UseScratchRegisterScope temps(this);
536    Register temp = temps.AcquireX();
537    Mov(temp, imm);
538    if (vd.Is1D()) {
539      mov(vd.D(), 0, temp);
540    } else {
541      dup(vd.V2D(), temp);
542    }
543  }
544}
545
546void TurboAssembler::Movi(const VRegister& vd, uint64_t imm, Shift shift,
547                          int shift_amount) {
548  DCHECK(allow_macro_instructions());
549  if (shift_amount != 0 || shift != LSL) {
550    movi(vd, imm, shift, shift_amount);
551  } else if (vd.Is8B() || vd.Is16B()) {
552    // 8-bit immediate.
553    DCHECK(is_uint8(imm));
554    movi(vd, imm);
555  } else if (vd.Is4H() || vd.Is8H()) {
556    // 16-bit immediate.
557    Movi16bitHelper(vd, imm);
558  } else if (vd.Is2S() || vd.Is4S()) {
559    // 32-bit immediate.
560    Movi32bitHelper(vd, imm);
561  } else {
562    // 64-bit immediate.
563    Movi64bitHelper(vd, imm);
564  }
565}
566
567void TurboAssembler::Movi(const VRegister& vd, uint64_t hi, uint64_t lo) {
568  // TODO(v8:11033): Move 128-bit values in a more efficient way.
569  DCHECK(vd.Is128Bits());
570  Movi(vd.V2D(), lo);
571  if (lo != hi) {
572    UseScratchRegisterScope temps(this);
573    Register temp = temps.AcquireX();
574    Mov(temp, hi);
575    Ins(vd.V2D(), 1, temp);
576  }
577}
578
579void TurboAssembler::Mvn(const Register& rd, const Operand& operand) {
580  DCHECK(allow_macro_instructions());
581
582  if (operand.NeedsRelocation(this)) {
583    Ldr(rd, operand.immediate());
584    mvn(rd, rd);
585
586  } else if (operand.IsImmediate()) {
587    // Call the macro assembler for generic immediates.
588    Mov(rd, ~operand.ImmediateValue());
589
590  } else if (operand.IsExtendedRegister()) {
591    // Emit two instructions for the extend case. This differs from Mov, as
592    // the extend and invert can't be achieved in one instruction.
593    EmitExtendShift(rd, operand.reg(), operand.extend(),
594                    operand.shift_amount());
595    mvn(rd, rd);
596
597  } else {
598    mvn(rd, operand);
599  }
600}
601
602unsigned TurboAssembler::CountSetHalfWords(uint64_t imm, unsigned reg_size) {
603  DCHECK_EQ(reg_size % 16, 0);
604
605#define HALFWORD(idx) (((imm >> ((idx)*16)) & 0xFFFF) ? 1u : 0u)
606  switch (reg_size / 16) {
607    case 1:
608      return HALFWORD(0);
609    case 2:
610      return HALFWORD(0) + HALFWORD(1);
611    case 4:
612      return HALFWORD(0) + HALFWORD(1) + HALFWORD(2) + HALFWORD(3);
613  }
614#undef HALFWORD
615  UNREACHABLE();
616}
617
618// The movz instruction can generate immediates containing an arbitrary 16-bit
619// half-word, with remaining bits clear, eg. 0x00001234, 0x0000123400000000.
620bool TurboAssembler::IsImmMovz(uint64_t imm, unsigned reg_size) {
621  DCHECK((reg_size == kXRegSizeInBits) || (reg_size == kWRegSizeInBits));
622  return CountSetHalfWords(imm, reg_size) <= 1;
623}
624
625// The movn instruction can generate immediates containing an arbitrary 16-bit
626// half-word, with remaining bits set, eg. 0xFFFF1234, 0xFFFF1234FFFFFFFF.
627bool TurboAssembler::IsImmMovn(uint64_t imm, unsigned reg_size) {
628  return IsImmMovz(~imm, reg_size);
629}
630
631void TurboAssembler::ConditionalCompareMacro(const Register& rn,
632                                             const Operand& operand,
633                                             StatusFlags nzcv, Condition cond,
634                                             ConditionalCompareOp op) {
635  DCHECK((cond != al) && (cond != nv));
636  if (operand.NeedsRelocation(this)) {
637    UseScratchRegisterScope temps(this);
638    Register temp = temps.AcquireX();
639    Ldr(temp, operand.immediate());
640    ConditionalCompareMacro(rn, temp, nzcv, cond, op);
641
642  } else if ((operand.IsShiftedRegister() && (operand.shift_amount() == 0)) ||
643             (operand.IsImmediate() &&
644              IsImmConditionalCompare(operand.ImmediateValue()))) {
645    // The immediate can be encoded in the instruction, or the operand is an
646    // unshifted register: call the assembler.
647    ConditionalCompare(rn, operand, nzcv, cond, op);
648
649  } else {
650    // The operand isn't directly supported by the instruction: perform the
651    // operation on a temporary register.
652    UseScratchRegisterScope temps(this);
653    Register temp = temps.AcquireSameSizeAs(rn);
654    Mov(temp, operand);
655    ConditionalCompare(rn, temp, nzcv, cond, op);
656  }
657}
658
659void TurboAssembler::Csel(const Register& rd, const Register& rn,
660                          const Operand& operand, Condition cond) {
661  DCHECK(allow_macro_instructions());
662  DCHECK(!rd.IsZero());
663  DCHECK((cond != al) && (cond != nv));
664  if (operand.IsImmediate()) {
665    // Immediate argument. Handle special cases of 0, 1 and -1 using zero
666    // register.
667    int64_t imm = operand.ImmediateValue();
668    Register zr = AppropriateZeroRegFor(rn);
669    if (imm == 0) {
670      csel(rd, rn, zr, cond);
671    } else if (imm == 1) {
672      csinc(rd, rn, zr, cond);
673    } else if (imm == -1) {
674      csinv(rd, rn, zr, cond);
675    } else {
676      UseScratchRegisterScope temps(this);
677      Register temp = temps.AcquireSameSizeAs(rn);
678      Mov(temp, imm);
679      csel(rd, rn, temp, cond);
680    }
681  } else if (operand.IsShiftedRegister() && (operand.shift_amount() == 0)) {
682    // Unshifted register argument.
683    csel(rd, rn, operand.reg(), cond);
684  } else {
685    // All other arguments.
686    UseScratchRegisterScope temps(this);
687    Register temp = temps.AcquireSameSizeAs(rn);
688    Mov(temp, operand);
689    csel(rd, rn, temp, cond);
690  }
691}
692
693bool TurboAssembler::TryOneInstrMoveImmediate(const Register& dst,
694                                              int64_t imm) {
695  unsigned n, imm_s, imm_r;
696  int reg_size = dst.SizeInBits();
697  if (IsImmMovz(imm, reg_size) && !dst.IsSP()) {
698    // Immediate can be represented in a move zero instruction. Movz can't write
699    // to the stack pointer.
700    movz(dst, imm);
701    return true;
702  } else if (IsImmMovn(imm, reg_size) && !dst.IsSP()) {
703    // Immediate can be represented in a move not instruction. Movn can't write
704    // to the stack pointer.
705    movn(dst, dst.Is64Bits() ? ~imm : (~imm & kWRegMask));
706    return true;
707  } else if (IsImmLogical(imm, reg_size, &n, &imm_s, &imm_r)) {
708    // Immediate can be represented in a logical orr instruction.
709    LogicalImmediate(dst, AppropriateZeroRegFor(dst), n, imm_s, imm_r, ORR);
710    return true;
711  }
712  return false;
713}
714
715Operand TurboAssembler::MoveImmediateForShiftedOp(const Register& dst,
716                                                  int64_t imm,
717                                                  PreShiftImmMode mode) {
718  int reg_size = dst.SizeInBits();
719  // Encode the immediate in a single move instruction, if possible.
720  if (TryOneInstrMoveImmediate(dst, imm)) {
721    // The move was successful; nothing to do here.
722  } else {
723    // Pre-shift the immediate to the least-significant bits of the register.
724    int shift_low;
725    if (reg_size == 64) {
726      shift_low = base::bits::CountTrailingZeros(imm);
727    } else {
728      DCHECK_EQ(reg_size, 32);
729      shift_low = base::bits::CountTrailingZeros(static_cast<uint32_t>(imm));
730    }
731
732    if (mode == kLimitShiftForSP) {
733      // When applied to the stack pointer, the subsequent arithmetic operation
734      // can use the extend form to shift left by a maximum of four bits. Right
735      // shifts are not allowed, so we filter them out later before the new
736      // immediate is tested.
737      shift_low = std::min(shift_low, 4);
738    }
739    int64_t imm_low = imm >> shift_low;
740
741    // Pre-shift the immediate to the most-significant bits of the register. We
742    // insert set bits in the least-significant bits, as this creates a
743    // different immediate that may be encodable using movn or orr-immediate.
744    // If this new immediate is encodable, the set bits will be eliminated by
745    // the post shift on the following instruction.
746    int shift_high = CountLeadingZeros(imm, reg_size);
747    int64_t imm_high = (imm << shift_high) | ((INT64_C(1) << shift_high) - 1);
748
749    if ((mode != kNoShift) && TryOneInstrMoveImmediate(dst, imm_low)) {
750      // The new immediate has been moved into the destination's low bits:
751      // return a new leftward-shifting operand.
752      return Operand(dst, LSL, shift_low);
753    } else if ((mode == kAnyShift) && TryOneInstrMoveImmediate(dst, imm_high)) {
754      // The new immediate has been moved into the destination's high bits:
755      // return a new rightward-shifting operand.
756      return Operand(dst, LSR, shift_high);
757    } else {
758      // Use the generic move operation to set up the immediate.
759      Mov(dst, imm);
760    }
761  }
762  return Operand(dst);
763}
764
765void TurboAssembler::AddSubMacro(const Register& rd, const Register& rn,
766                                 const Operand& operand, FlagsUpdate S,
767                                 AddSubOp op) {
768  if (operand.IsZero() && rd == rn && rd.Is64Bits() && rn.Is64Bits() &&
769      !operand.NeedsRelocation(this) && (S == LeaveFlags)) {
770    // The instruction would be a nop. Avoid generating useless code.
771    return;
772  }
773
774  if (operand.NeedsRelocation(this)) {
775    UseScratchRegisterScope temps(this);
776    Register temp = temps.AcquireX();
777    Ldr(temp, operand.immediate());
778    AddSubMacro(rd, rn, temp, S, op);
779  } else if ((operand.IsImmediate() &&
780              !IsImmAddSub(operand.ImmediateValue())) ||
781             (rn.IsZero() && !operand.IsShiftedRegister()) ||
782             (operand.IsShiftedRegister() && (operand.shift() == ROR))) {
783    UseScratchRegisterScope temps(this);
784    Register temp = temps.AcquireSameSizeAs(rn);
785    if (operand.IsImmediate()) {
786      PreShiftImmMode mode = kAnyShift;
787
788      // If the destination or source register is the stack pointer, we can
789      // only pre-shift the immediate right by values supported in the add/sub
790      // extend encoding.
791      if (rd == sp) {
792        // If the destination is SP and flags will be set, we can't pre-shift
793        // the immediate at all.
794        mode = (S == SetFlags) ? kNoShift : kLimitShiftForSP;
795      } else if (rn == sp) {
796        mode = kLimitShiftForSP;
797      }
798
799      Operand imm_operand =
800          MoveImmediateForShiftedOp(temp, operand.ImmediateValue(), mode);
801      AddSub(rd, rn, imm_operand, S, op);
802    } else {
803      Mov(temp, operand);
804      AddSub(rd, rn, temp, S, op);
805    }
806  } else {
807    AddSub(rd, rn, operand, S, op);
808  }
809}
810
811void TurboAssembler::AddSubWithCarryMacro(const Register& rd,
812                                          const Register& rn,
813                                          const Operand& operand, FlagsUpdate S,
814                                          AddSubWithCarryOp op) {
815  DCHECK(rd.SizeInBits() == rn.SizeInBits());
816  UseScratchRegisterScope temps(this);
817
818  if (operand.NeedsRelocation(this)) {
819    Register temp = temps.AcquireX();
820    Ldr(temp, operand.immediate());
821    AddSubWithCarryMacro(rd, rn, temp, S, op);
822
823  } else if (operand.IsImmediate() ||
824             (operand.IsShiftedRegister() && (operand.shift() == ROR))) {
825    // Add/sub with carry (immediate or ROR shifted register.)
826    Register temp = temps.AcquireSameSizeAs(rn);
827    Mov(temp, operand);
828    AddSubWithCarry(rd, rn, temp, S, op);
829
830  } else if (operand.IsShiftedRegister() && (operand.shift_amount() != 0)) {
831    // Add/sub with carry (shifted register).
832    DCHECK(operand.reg().SizeInBits() == rd.SizeInBits());
833    DCHECK(operand.shift() != ROR);
834    DCHECK(is_uintn(operand.shift_amount(), rd.SizeInBits() == kXRegSizeInBits
835                                                ? kXRegSizeInBitsLog2
836                                                : kWRegSizeInBitsLog2));
837    Register temp = temps.AcquireSameSizeAs(rn);
838    EmitShift(temp, operand.reg(), operand.shift(), operand.shift_amount());
839    AddSubWithCarry(rd, rn, temp, S, op);
840
841  } else if (operand.IsExtendedRegister()) {
842    // Add/sub with carry (extended register).
843    DCHECK(operand.reg().SizeInBits() <= rd.SizeInBits());
844    // Add/sub extended supports a shift <= 4. We want to support exactly the
845    // same modes.
846    DCHECK_LE(operand.shift_amount(), 4);
847    DCHECK(operand.reg().Is64Bits() ||
848           ((operand.extend() != UXTX) && (operand.extend() != SXTX)));
849    Register temp = temps.AcquireSameSizeAs(rn);
850    EmitExtendShift(temp, operand.reg(), operand.extend(),
851                    operand.shift_amount());
852    AddSubWithCarry(rd, rn, temp, S, op);
853
854  } else {
855    // The addressing mode is directly supported by the instruction.
856    AddSubWithCarry(rd, rn, operand, S, op);
857  }
858}
859
860void TurboAssembler::LoadStoreMacro(const CPURegister& rt,
861                                    const MemOperand& addr, LoadStoreOp op) {
862  int64_t offset = addr.offset();
863  unsigned size = CalcLSDataSize(op);
864
865  // Check if an immediate offset fits in the immediate field of the
866  // appropriate instruction. If not, emit two instructions to perform
867  // the operation.
868  if (addr.IsImmediateOffset() && !IsImmLSScaled(offset, size) &&
869      !IsImmLSUnscaled(offset)) {
870    // Immediate offset that can't be encoded using unsigned or unscaled
871    // addressing modes.
872    UseScratchRegisterScope temps(this);
873    Register temp = temps.AcquireSameSizeAs(addr.base());
874    Mov(temp, addr.offset());
875    LoadStore(rt, MemOperand(addr.base(), temp), op);
876  } else if (addr.IsPostIndex() && !IsImmLSUnscaled(offset)) {
877    // Post-index beyond unscaled addressing range.
878    LoadStore(rt, MemOperand(addr.base()), op);
879    add(addr.base(), addr.base(), offset);
880  } else if (addr.IsPreIndex() && !IsImmLSUnscaled(offset)) {
881    // Pre-index beyond unscaled addressing range.
882    add(addr.base(), addr.base(), offset);
883    LoadStore(rt, MemOperand(addr.base()), op);
884  } else {
885    // Encodable in one load/store instruction.
886    LoadStore(rt, addr, op);
887  }
888}
889
890void TurboAssembler::LoadStorePairMacro(const CPURegister& rt,
891                                        const CPURegister& rt2,
892                                        const MemOperand& addr,
893                                        LoadStorePairOp op) {
894  // TODO(all): Should we support register offset for load-store-pair?
895  DCHECK(!addr.IsRegisterOffset());
896
897  int64_t offset = addr.offset();
898  unsigned size = CalcLSPairDataSize(op);
899
900  // Check if the offset fits in the immediate field of the appropriate
901  // instruction. If not, emit two instructions to perform the operation.
902  if (IsImmLSPair(offset, size)) {
903    // Encodable in one load/store pair instruction.
904    LoadStorePair(rt, rt2, addr, op);
905  } else {
906    Register base = addr.base();
907    if (addr.IsImmediateOffset()) {
908      UseScratchRegisterScope temps(this);
909      Register temp = temps.AcquireSameSizeAs(base);
910      Add(temp, base, offset);
911      LoadStorePair(rt, rt2, MemOperand(temp), op);
912    } else if (addr.IsPostIndex()) {
913      LoadStorePair(rt, rt2, MemOperand(base), op);
914      Add(base, base, offset);
915    } else {
916      DCHECK(addr.IsPreIndex());
917      Add(base, base, offset);
918      LoadStorePair(rt, rt2, MemOperand(base), op);
919    }
920  }
921}
922
923bool TurboAssembler::NeedExtraInstructionsOrRegisterBranch(
924    Label* label, ImmBranchType b_type) {
925  bool need_longer_range = false;
926  // There are two situations in which we care about the offset being out of
927  // range:
928  //  - The label is bound but too far away.
929  //  - The label is not bound but linked, and the previous branch
930  //    instruction in the chain is too far away.
931  if (label->is_bound() || label->is_linked()) {
932    need_longer_range =
933        !Instruction::IsValidImmPCOffset(b_type, label->pos() - pc_offset());
934  }
935  if (!need_longer_range && !label->is_bound()) {
936    int max_reachable_pc = pc_offset() + Instruction::ImmBranchRange(b_type);
937    unresolved_branches_.insert(std::pair<int, FarBranchInfo>(
938        max_reachable_pc, FarBranchInfo(pc_offset(), label)));
939    // Also maintain the next pool check.
940    next_veneer_pool_check_ = std::min(
941        next_veneer_pool_check_, max_reachable_pc - kVeneerDistanceCheckMargin);
942  }
943  return need_longer_range;
944}
945
946void TurboAssembler::Adr(const Register& rd, Label* label, AdrHint hint) {
947  DCHECK(allow_macro_instructions());
948  DCHECK(!rd.IsZero());
949
950  if (hint == kAdrNear) {
951    adr(rd, label);
952    return;
953  }
954
955  DCHECK_EQ(hint, kAdrFar);
956  if (label->is_bound()) {
957    int label_offset = label->pos() - pc_offset();
958    if (Instruction::IsValidPCRelOffset(label_offset)) {
959      adr(rd, label);
960    } else {
961      DCHECK_LE(label_offset, 0);
962      int min_adr_offset = -(1 << (Instruction::ImmPCRelRangeBitwidth - 1));
963      adr(rd, min_adr_offset);
964      Add(rd, rd, label_offset - min_adr_offset);
965    }
966  } else {
967    UseScratchRegisterScope temps(this);
968    Register scratch = temps.AcquireX();
969
970    InstructionAccurateScope scope(this,
971                                   PatchingAssembler::kAdrFarPatchableNInstrs);
972    adr(rd, label);
973    for (int i = 0; i < PatchingAssembler::kAdrFarPatchableNNops; ++i) {
974      nop(ADR_FAR_NOP);
975    }
976    movz(scratch, 0);
977  }
978}
979
980void TurboAssembler::B(Label* label, BranchType type, Register reg, int bit) {
981  DCHECK((reg == NoReg || type >= kBranchTypeFirstUsingReg) &&
982         (bit == -1 || type >= kBranchTypeFirstUsingBit));
983  if (kBranchTypeFirstCondition <= type && type <= kBranchTypeLastCondition) {
984    B(static_cast<Condition>(type), label);
985  } else {
986    switch (type) {
987      case always:
988        B(label);
989        break;
990      case never:
991        break;
992      case reg_zero:
993        Cbz(reg, label);
994        break;
995      case reg_not_zero:
996        Cbnz(reg, label);
997        break;
998      case reg_bit_clear:
999        Tbz(reg, bit, label);
1000        break;
1001      case reg_bit_set:
1002        Tbnz(reg, bit, label);
1003        break;
1004      default:
1005        UNREACHABLE();
1006    }
1007  }
1008}
1009
1010void TurboAssembler::B(Label* label, Condition cond) {
1011  DCHECK(allow_macro_instructions());
1012  DCHECK((cond != al) && (cond != nv));
1013
1014  Label done;
1015  bool need_extra_instructions =
1016      NeedExtraInstructionsOrRegisterBranch(label, CondBranchType);
1017
1018  if (need_extra_instructions) {
1019    b(&done, NegateCondition(cond));
1020    B(label);
1021  } else {
1022    b(label, cond);
1023  }
1024  bind(&done);
1025}
1026
1027void TurboAssembler::Tbnz(const Register& rt, unsigned bit_pos, Label* label) {
1028  DCHECK(allow_macro_instructions());
1029
1030  Label done;
1031  bool need_extra_instructions =
1032      NeedExtraInstructionsOrRegisterBranch(label, TestBranchType);
1033
1034  if (need_extra_instructions) {
1035    tbz(rt, bit_pos, &done);
1036    B(label);
1037  } else {
1038    tbnz(rt, bit_pos, label);
1039  }
1040  bind(&done);
1041}
1042
1043void TurboAssembler::Tbz(const Register& rt, unsigned bit_pos, Label* label) {
1044  DCHECK(allow_macro_instructions());
1045
1046  Label done;
1047  bool need_extra_instructions =
1048      NeedExtraInstructionsOrRegisterBranch(label, TestBranchType);
1049
1050  if (need_extra_instructions) {
1051    tbnz(rt, bit_pos, &done);
1052    B(label);
1053  } else {
1054    tbz(rt, bit_pos, label);
1055  }
1056  bind(&done);
1057}
1058
1059void TurboAssembler::Cbnz(const Register& rt, Label* label) {
1060  DCHECK(allow_macro_instructions());
1061
1062  Label done;
1063  bool need_extra_instructions =
1064      NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType);
1065
1066  if (need_extra_instructions) {
1067    cbz(rt, &done);
1068    B(label);
1069  } else {
1070    cbnz(rt, label);
1071  }
1072  bind(&done);
1073}
1074
1075void TurboAssembler::Cbz(const Register& rt, Label* label) {
1076  DCHECK(allow_macro_instructions());
1077
1078  Label done;
1079  bool need_extra_instructions =
1080      NeedExtraInstructionsOrRegisterBranch(label, CompareBranchType);
1081
1082  if (need_extra_instructions) {
1083    cbnz(rt, &done);
1084    B(label);
1085  } else {
1086    cbz(rt, label);
1087  }
1088  bind(&done);
1089}
1090
1091// Pseudo-instructions.
1092
1093void TurboAssembler::Abs(const Register& rd, const Register& rm,
1094                         Label* is_not_representable, Label* is_representable) {
1095  DCHECK(allow_macro_instructions());
1096  DCHECK(AreSameSizeAndType(rd, rm));
1097
1098  Cmp(rm, 1);
1099  Cneg(rd, rm, lt);
1100
1101  // If the comparison sets the v flag, the input was the smallest value
1102  // representable by rm, and the mathematical result of abs(rm) is not
1103  // representable using two's complement.
1104  if ((is_not_representable != nullptr) && (is_representable != nullptr)) {
1105    B(is_not_representable, vs);
1106    B(is_representable);
1107  } else if (is_not_representable != nullptr) {
1108    B(is_not_representable, vs);
1109  } else if (is_representable != nullptr) {
1110    B(is_representable, vc);
1111  }
1112}
1113
1114// Abstracted stack operations.
1115
1116void TurboAssembler::Push(const CPURegister& src0, const CPURegister& src1,
1117                          const CPURegister& src2, const CPURegister& src3,
1118                          const CPURegister& src4, const CPURegister& src5,
1119                          const CPURegister& src6, const CPURegister& src7) {
1120  DCHECK(AreSameSizeAndType(src0, src1, src2, src3, src4, src5, src6, src7));
1121
1122  int count = 5 + src5.is_valid() + src6.is_valid() + src6.is_valid();
1123  int size = src0.SizeInBytes();
1124  DCHECK_EQ(0, (size * count) % 16);
1125
1126  PushHelper(4, size, src0, src1, src2, src3);
1127  PushHelper(count - 4, size, src4, src5, src6, src7);
1128}
1129
1130void TurboAssembler::Pop(const CPURegister& dst0, const CPURegister& dst1,
1131                         const CPURegister& dst2, const CPURegister& dst3,
1132                         const CPURegister& dst4, const CPURegister& dst5,
1133                         const CPURegister& dst6, const CPURegister& dst7) {
1134  // It is not valid to pop into the same register more than once in one
1135  // instruction, not even into the zero register.
1136  DCHECK(!AreAliased(dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7));
1137  DCHECK(AreSameSizeAndType(dst0, dst1, dst2, dst3, dst4, dst5, dst6, dst7));
1138  DCHECK(dst0.is_valid());
1139
1140  int count = 5 + dst5.is_valid() + dst6.is_valid() + dst7.is_valid();
1141  int size = dst0.SizeInBytes();
1142  DCHECK_EQ(0, (size * count) % 16);
1143
1144  PopHelper(4, size, dst0, dst1, dst2, dst3);
1145  PopHelper(count - 4, size, dst4, dst5, dst6, dst7);
1146}
1147
1148void MacroAssembler::PushMultipleTimes(CPURegister src, Register count) {
1149  UseScratchRegisterScope temps(this);
1150  Register temp = temps.AcquireSameSizeAs(count);
1151
1152  Label loop, leftover2, leftover1, done;
1153
1154  Subs(temp, count, 4);
1155  B(mi, &leftover2);
1156
1157  // Push groups of four first.
1158  Bind(&loop);
1159  Subs(temp, temp, 4);
1160  PushHelper(4, src.SizeInBytes(), src, src, src, src);
1161  B(pl, &loop);
1162
1163  // Push groups of two.
1164  Bind(&leftover2);
1165  Tbz(count, 1, &leftover1);
1166  PushHelper(2, src.SizeInBytes(), src, src, NoReg, NoReg);
1167
1168  // Push the last one (if required).
1169  Bind(&leftover1);
1170  Tbz(count, 0, &done);
1171  PushHelper(1, src.SizeInBytes(), src, NoReg, NoReg, NoReg);
1172
1173  Bind(&done);
1174}
1175
1176void TurboAssembler::PushHelper(int count, int size, const CPURegister& src0,
1177                                const CPURegister& src1,
1178                                const CPURegister& src2,
1179                                const CPURegister& src3) {
1180  // Ensure that we don't unintentially modify scratch or debug registers.
1181  InstructionAccurateScope scope(this);
1182
1183  DCHECK(AreSameSizeAndType(src0, src1, src2, src3));
1184  DCHECK(size == src0.SizeInBytes());
1185
1186  // When pushing multiple registers, the store order is chosen such that
1187  // Push(a, b) is equivalent to Push(a) followed by Push(b).
1188  switch (count) {
1189    case 1:
1190      DCHECK(src1.IsNone() && src2.IsNone() && src3.IsNone());
1191      str(src0, MemOperand(sp, -1 * size, PreIndex));
1192      break;
1193    case 2:
1194      DCHECK(src2.IsNone() && src3.IsNone());
1195      stp(src1, src0, MemOperand(sp, -2 * size, PreIndex));
1196      break;
1197    case 3:
1198      DCHECK(src3.IsNone());
1199      stp(src2, src1, MemOperand(sp, -3 * size, PreIndex));
1200      str(src0, MemOperand(sp, 2 * size));
1201      break;
1202    case 4:
1203      // Skip over 4 * size, then fill in the gap. This allows four W registers
1204      // to be pushed using sp, whilst maintaining 16-byte alignment for sp
1205      // at all times.
1206      stp(src3, src2, MemOperand(sp, -4 * size, PreIndex));
1207      stp(src1, src0, MemOperand(sp, 2 * size));
1208      break;
1209    default:
1210      UNREACHABLE();
1211  }
1212}
1213
1214void TurboAssembler::PopHelper(int count, int size, const CPURegister& dst0,
1215                               const CPURegister& dst1, const CPURegister& dst2,
1216                               const CPURegister& dst3) {
1217  // Ensure that we don't unintentially modify scratch or debug registers.
1218  InstructionAccurateScope scope(this);
1219
1220  DCHECK(AreSameSizeAndType(dst0, dst1, dst2, dst3));
1221  DCHECK(size == dst0.SizeInBytes());
1222
1223  // When popping multiple registers, the load order is chosen such that
1224  // Pop(a, b) is equivalent to Pop(a) followed by Pop(b).
1225  switch (count) {
1226    case 1:
1227      DCHECK(dst1.IsNone() && dst2.IsNone() && dst3.IsNone());
1228      ldr(dst0, MemOperand(sp, 1 * size, PostIndex));
1229      break;
1230    case 2:
1231      DCHECK(dst2.IsNone() && dst3.IsNone());
1232      ldp(dst0, dst1, MemOperand(sp, 2 * size, PostIndex));
1233      break;
1234    case 3:
1235      DCHECK(dst3.IsNone());
1236      ldr(dst2, MemOperand(sp, 2 * size));
1237      ldp(dst0, dst1, MemOperand(sp, 3 * size, PostIndex));
1238      break;
1239    case 4:
1240      // Load the higher addresses first, then load the lower addresses and
1241      // skip the whole block in the second instruction. This allows four W
1242      // registers to be popped using sp, whilst maintaining 16-byte alignment
1243      // for sp at all times.
1244      ldp(dst2, dst3, MemOperand(sp, 2 * size));
1245      ldp(dst0, dst1, MemOperand(sp, 4 * size, PostIndex));
1246      break;
1247    default:
1248      UNREACHABLE();
1249  }
1250}
1251
1252void TurboAssembler::PokePair(const CPURegister& src1, const CPURegister& src2,
1253                              int offset) {
1254  DCHECK(AreSameSizeAndType(src1, src2));
1255  DCHECK((offset >= 0) && ((offset % src1.SizeInBytes()) == 0));
1256  Stp(src1, src2, MemOperand(sp, offset));
1257}
1258
1259void MacroAssembler::PeekPair(const CPURegister& dst1, const CPURegister& dst2,
1260                              int offset) {
1261  DCHECK(AreSameSizeAndType(dst1, dst2));
1262  DCHECK((offset >= 0) && ((offset % dst1.SizeInBytes()) == 0));
1263  Ldp(dst1, dst2, MemOperand(sp, offset));
1264}
1265
1266void MacroAssembler::PushCalleeSavedRegisters() {
1267  ASM_CODE_COMMENT(this);
1268  // Ensure that the macro-assembler doesn't use any scratch registers.
1269  InstructionAccurateScope scope(this);
1270
1271  MemOperand tos(sp, -2 * static_cast<int>(kXRegSize), PreIndex);
1272
1273  stp(d14, d15, tos);
1274  stp(d12, d13, tos);
1275  stp(d10, d11, tos);
1276  stp(d8, d9, tos);
1277
1278  stp(x27, x28, tos);
1279  stp(x25, x26, tos);
1280  stp(x23, x24, tos);
1281  stp(x21, x22, tos);
1282  stp(x19, x20, tos);
1283
1284  STATIC_ASSERT(
1285      EntryFrameConstants::kCalleeSavedRegisterBytesPushedBeforeFpLrPair ==
1286      18 * kSystemPointerSize);
1287
1288#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
1289    // Use the stack pointer's value immediately before pushing the LR as the
1290    // context for signing it. This is what the StackFrameIterator expects.
1291    pacibsp();
1292#endif
1293
1294    stp(x29, x30, tos);  // fp, lr
1295
1296    STATIC_ASSERT(
1297        EntryFrameConstants::kCalleeSavedRegisterBytesPushedAfterFpLrPair == 0);
1298}
1299
1300void MacroAssembler::PopCalleeSavedRegisters() {
1301  ASM_CODE_COMMENT(this);
1302  // Ensure that the macro-assembler doesn't use any scratch registers.
1303  InstructionAccurateScope scope(this);
1304
1305  MemOperand tos(sp, 2 * kXRegSize, PostIndex);
1306
1307  ldp(x29, x30, tos);  // fp, lr
1308
1309#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
1310                       // The context (stack pointer value) for authenticating
1311                       // the LR here must
1312  // match the one used for signing it (see `PushCalleeSavedRegisters`).
1313  autibsp();
1314#endif
1315
1316    ldp(x19, x20, tos);
1317    ldp(x21, x22, tos);
1318    ldp(x23, x24, tos);
1319    ldp(x25, x26, tos);
1320    ldp(x27, x28, tos);
1321
1322    ldp(d8, d9, tos);
1323    ldp(d10, d11, tos);
1324    ldp(d12, d13, tos);
1325    ldp(d14, d15, tos);
1326}
1327
1328void TurboAssembler::AssertSpAligned() {
1329  if (!FLAG_debug_code) return;
1330  ASM_CODE_COMMENT(this);
1331  HardAbortScope hard_abort(this);  // Avoid calls to Abort.
1332  // Arm64 requires the stack pointer to be 16-byte aligned prior to address
1333  // calculation.
1334  UseScratchRegisterScope scope(this);
1335  Register temp = scope.AcquireX();
1336  Mov(temp, sp);
1337  Tst(temp, 15);
1338  Check(eq, AbortReason::kUnexpectedStackPointer);
1339}
1340
1341void TurboAssembler::CopySlots(int dst, Register src, Register slot_count) {
1342  DCHECK(!src.IsZero());
1343  UseScratchRegisterScope scope(this);
1344  Register dst_reg = scope.AcquireX();
1345  SlotAddress(dst_reg, dst);
1346  SlotAddress(src, src);
1347  CopyDoubleWords(dst_reg, src, slot_count);
1348}
1349
1350void TurboAssembler::CopySlots(Register dst, Register src,
1351                               Register slot_count) {
1352  DCHECK(!dst.IsZero() && !src.IsZero());
1353  SlotAddress(dst, dst);
1354  SlotAddress(src, src);
1355  CopyDoubleWords(dst, src, slot_count);
1356}
1357
1358void TurboAssembler::CopyDoubleWords(Register dst, Register src, Register count,
1359                                     CopyDoubleWordsMode mode) {
1360  ASM_CODE_COMMENT(this);
1361  DCHECK(!AreAliased(dst, src, count));
1362
1363  if (FLAG_debug_code) {
1364    Register pointer1 = dst;
1365    Register pointer2 = src;
1366    if (mode == kSrcLessThanDst) {
1367      pointer1 = src;
1368      pointer2 = dst;
1369    }
1370    // Copy requires pointer1 < pointer2 || (pointer1 - pointer2) >= count.
1371    Label pointer1_below_pointer2;
1372    Subs(pointer1, pointer1, pointer2);
1373    B(lt, &pointer1_below_pointer2);
1374    Cmp(pointer1, count);
1375    Check(ge, AbortReason::kOffsetOutOfRange);
1376    Bind(&pointer1_below_pointer2);
1377    Add(pointer1, pointer1, pointer2);
1378  }
1379  static_assert(kSystemPointerSize == kDRegSize,
1380                "pointers must be the same size as doubles");
1381
1382  if (mode == kDstLessThanSrcAndReverse) {
1383    Add(src, src, Operand(count, LSL, kSystemPointerSizeLog2));
1384    Sub(src, src, kSystemPointerSize);
1385  }
1386
1387  int src_direction = (mode == kDstLessThanSrc) ? 1 : -1;
1388  int dst_direction = (mode == kSrcLessThanDst) ? -1 : 1;
1389
1390  UseScratchRegisterScope scope(this);
1391  VRegister temp0 = scope.AcquireD();
1392  VRegister temp1 = scope.AcquireD();
1393
1394  Label pairs, loop, done;
1395
1396  Tbz(count, 0, &pairs);
1397  Ldr(temp0, MemOperand(src, src_direction * kSystemPointerSize, PostIndex));
1398  Sub(count, count, 1);
1399  Str(temp0, MemOperand(dst, dst_direction * kSystemPointerSize, PostIndex));
1400
1401  Bind(&pairs);
1402  if (mode == kSrcLessThanDst) {
1403    // Adjust pointers for post-index ldp/stp with negative offset:
1404    Sub(dst, dst, kSystemPointerSize);
1405    Sub(src, src, kSystemPointerSize);
1406  } else if (mode == kDstLessThanSrcAndReverse) {
1407    Sub(src, src, kSystemPointerSize);
1408  }
1409  Bind(&loop);
1410  Cbz(count, &done);
1411  Ldp(temp0, temp1,
1412      MemOperand(src, 2 * src_direction * kSystemPointerSize, PostIndex));
1413  Sub(count, count, 2);
1414  if (mode == kDstLessThanSrcAndReverse) {
1415    Stp(temp1, temp0,
1416        MemOperand(dst, 2 * dst_direction * kSystemPointerSize, PostIndex));
1417  } else {
1418    Stp(temp0, temp1,
1419        MemOperand(dst, 2 * dst_direction * kSystemPointerSize, PostIndex));
1420  }
1421  B(&loop);
1422
1423  // TODO(all): large copies may benefit from using temporary Q registers
1424  // to copy four double words per iteration.
1425
1426  Bind(&done);
1427}
1428
1429void TurboAssembler::SlotAddress(Register dst, int slot_offset) {
1430  Add(dst, sp, slot_offset << kSystemPointerSizeLog2);
1431}
1432
1433void TurboAssembler::SlotAddress(Register dst, Register slot_offset) {
1434  Add(dst, sp, Operand(slot_offset, LSL, kSystemPointerSizeLog2));
1435}
1436
1437void TurboAssembler::AssertFPCRState(Register fpcr) {
1438  if (!FLAG_debug_code) return;
1439  ASM_CODE_COMMENT(this);
1440  Label unexpected_mode, done;
1441  UseScratchRegisterScope temps(this);
1442  if (fpcr.IsNone()) {
1443    fpcr = temps.AcquireX();
1444    Mrs(fpcr, FPCR);
1445  }
1446
1447    // Settings left to their default values:
1448    //   - Assert that flush-to-zero is not set.
1449    Tbnz(fpcr, FZ_offset, &unexpected_mode);
1450    //   - Assert that the rounding mode is nearest-with-ties-to-even.
1451    STATIC_ASSERT(FPTieEven == 0);
1452    Tst(fpcr, RMode_mask);
1453    B(eq, &done);
1454
1455    Bind(&unexpected_mode);
1456    Abort(AbortReason::kUnexpectedFPCRMode);
1457
1458    Bind(&done);
1459}
1460
1461void TurboAssembler::CanonicalizeNaN(const VRegister& dst,
1462                                     const VRegister& src) {
1463  AssertFPCRState();
1464
1465  // Subtracting 0.0 preserves all inputs except for signalling NaNs, which
1466  // become quiet NaNs. We use fsub rather than fadd because fsub preserves -0.0
1467  // inputs: -0.0 + 0.0 = 0.0, but -0.0 - 0.0 = -0.0.
1468  Fsub(dst, src, fp_zero);
1469}
1470
1471void TurboAssembler::LoadRoot(Register destination, RootIndex index) {
1472  ASM_CODE_COMMENT(this);
1473  // TODO(jbramley): Most root values are constants, and can be synthesized
1474  // without a load. Refer to the ARM back end for details.
1475  Ldr(destination,
1476      MemOperand(kRootRegister, RootRegisterOffsetForRootIndex(index)));
1477}
1478
1479void TurboAssembler::PushRoot(RootIndex index) {
1480  ASM_CODE_COMMENT(this);
1481  UseScratchRegisterScope temps(this);
1482  Register tmp = temps.AcquireX();
1483  LoadRoot(tmp, index);
1484  Push(tmp);
1485}
1486
1487void TurboAssembler::Move(Register dst, Smi src) { Mov(dst, src); }
1488void TurboAssembler::Move(Register dst, MemOperand src) { Ldr(dst, src); }
1489void TurboAssembler::Move(Register dst, Register src) {
1490  if (dst == src) return;
1491  Mov(dst, src);
1492}
1493
1494void TurboAssembler::MovePair(Register dst0, Register src0, Register dst1,
1495                              Register src1) {
1496  DCHECK_NE(dst0, dst1);
1497  if (dst0 != src1) {
1498    Mov(dst0, src0);
1499    Mov(dst1, src1);
1500  } else if (dst1 != src0) {
1501    // Swap the order of the moves to resolve the overlap.
1502    Mov(dst1, src1);
1503    Mov(dst0, src0);
1504  } else {
1505    // Worse case scenario, this is a swap.
1506    Swap(dst0, src0);
1507  }
1508}
1509
1510void TurboAssembler::Swap(Register lhs, Register rhs) {
1511  DCHECK(lhs.IsSameSizeAndType(rhs));
1512  DCHECK_NE(lhs, rhs);
1513  UseScratchRegisterScope temps(this);
1514  Register temp = temps.AcquireX();
1515  Mov(temp, rhs);
1516  Mov(rhs, lhs);
1517  Mov(lhs, temp);
1518}
1519
1520void TurboAssembler::Swap(VRegister lhs, VRegister rhs) {
1521  DCHECK(lhs.IsSameSizeAndType(rhs));
1522  DCHECK_NE(lhs, rhs);
1523  UseScratchRegisterScope temps(this);
1524  VRegister temp = VRegister::no_reg();
1525  if (lhs.IsS()) {
1526    temp = temps.AcquireS();
1527  } else if (lhs.IsD()) {
1528    temp = temps.AcquireD();
1529  } else {
1530    DCHECK(lhs.IsQ());
1531    temp = temps.AcquireQ();
1532  }
1533  Mov(temp, rhs);
1534  Mov(rhs, lhs);
1535  Mov(lhs, temp);
1536}
1537
1538void TurboAssembler::AssertSmi(Register object, AbortReason reason) {
1539  if (!FLAG_debug_code) return;
1540  ASM_CODE_COMMENT(this);
1541  STATIC_ASSERT(kSmiTag == 0);
1542  Tst(object, kSmiTagMask);
1543  Check(eq, reason);
1544}
1545
1546void MacroAssembler::AssertNotSmi(Register object, AbortReason reason) {
1547  if (!FLAG_debug_code) return;
1548  ASM_CODE_COMMENT(this);
1549  STATIC_ASSERT(kSmiTag == 0);
1550  Tst(object, kSmiTagMask);
1551  Check(ne, reason);
1552}
1553
1554void MacroAssembler::AssertCodeT(Register object) {
1555  if (!FLAG_debug_code) return;
1556  ASM_CODE_COMMENT(this);
1557  AssertNotSmi(object, AbortReason::kOperandIsNotACodeT);
1558
1559  UseScratchRegisterScope temps(this);
1560  Register temp = temps.AcquireX();
1561
1562  CompareObjectType(object, temp, temp, CODET_TYPE);
1563  Check(eq, AbortReason::kOperandIsNotACodeT);
1564}
1565
1566void MacroAssembler::AssertConstructor(Register object) {
1567  if (!FLAG_debug_code) return;
1568  ASM_CODE_COMMENT(this);
1569  AssertNotSmi(object, AbortReason::kOperandIsASmiAndNotAConstructor);
1570
1571  UseScratchRegisterScope temps(this);
1572  Register temp = temps.AcquireX();
1573
1574  LoadMap(temp, object);
1575  Ldrb(temp, FieldMemOperand(temp, Map::kBitFieldOffset));
1576  Tst(temp, Operand(Map::Bits1::IsConstructorBit::kMask));
1577
1578  Check(ne, AbortReason::kOperandIsNotAConstructor);
1579}
1580
1581void MacroAssembler::AssertFunction(Register object) {
1582  if (!FLAG_debug_code) return;
1583  ASM_CODE_COMMENT(this);
1584  AssertNotSmi(object, AbortReason::kOperandIsASmiAndNotAFunction);
1585
1586  UseScratchRegisterScope temps(this);
1587  Register temp = temps.AcquireX();
1588  LoadMap(temp, object);
1589  CompareInstanceTypeRange(temp, temp, FIRST_JS_FUNCTION_TYPE,
1590                           LAST_JS_FUNCTION_TYPE);
1591  Check(ls, AbortReason::kOperandIsNotAFunction);
1592}
1593
1594void MacroAssembler::AssertCallableFunction(Register object) {
1595  if (!FLAG_debug_code) return;
1596  ASM_CODE_COMMENT(this);
1597  AssertNotSmi(object, AbortReason::kOperandIsASmiAndNotAFunction);
1598
1599  UseScratchRegisterScope temps(this);
1600  Register temp = temps.AcquireX();
1601  LoadMap(temp, object);
1602  CompareInstanceTypeRange(temp, temp, FIRST_CALLABLE_JS_FUNCTION_TYPE,
1603                           LAST_CALLABLE_JS_FUNCTION_TYPE);
1604  Check(ls, AbortReason::kOperandIsNotACallableFunction);
1605}
1606
1607void MacroAssembler::AssertBoundFunction(Register object) {
1608  if (!FLAG_debug_code) return;
1609  ASM_CODE_COMMENT(this);
1610  AssertNotSmi(object, AbortReason::kOperandIsASmiAndNotABoundFunction);
1611
1612  UseScratchRegisterScope temps(this);
1613  Register temp = temps.AcquireX();
1614
1615  CompareObjectType(object, temp, temp, JS_BOUND_FUNCTION_TYPE);
1616  Check(eq, AbortReason::kOperandIsNotABoundFunction);
1617}
1618
1619void MacroAssembler::AssertGeneratorObject(Register object) {
1620  if (!FLAG_debug_code) return;
1621  ASM_CODE_COMMENT(this);
1622  AssertNotSmi(object, AbortReason::kOperandIsASmiAndNotAGeneratorObject);
1623
1624  // Load map
1625  UseScratchRegisterScope temps(this);
1626  Register temp = temps.AcquireX();
1627  LoadMap(temp, object);
1628
1629  Label do_check;
1630  // Load instance type and check if JSGeneratorObject
1631  CompareInstanceType(temp, temp, JS_GENERATOR_OBJECT_TYPE);
1632  B(eq, &do_check);
1633
1634  // Check if JSAsyncFunctionObject
1635  Cmp(temp, JS_ASYNC_FUNCTION_OBJECT_TYPE);
1636  B(eq, &do_check);
1637
1638  // Check if JSAsyncGeneratorObject
1639  Cmp(temp, JS_ASYNC_GENERATOR_OBJECT_TYPE);
1640
1641  bind(&do_check);
1642  // Restore generator object to register and perform assertion
1643  Check(eq, AbortReason::kOperandIsNotAGeneratorObject);
1644}
1645
1646void MacroAssembler::AssertUndefinedOrAllocationSite(Register object) {
1647  if (!FLAG_debug_code) return;
1648  ASM_CODE_COMMENT(this);
1649  UseScratchRegisterScope temps(this);
1650  Register scratch = temps.AcquireX();
1651  Label done_checking;
1652  AssertNotSmi(object);
1653  JumpIfRoot(object, RootIndex::kUndefinedValue, &done_checking);
1654  LoadMap(scratch, object);
1655  CompareInstanceType(scratch, scratch, ALLOCATION_SITE_TYPE);
1656  Assert(eq, AbortReason::kExpectedUndefinedOrCell);
1657  Bind(&done_checking);
1658}
1659
1660void TurboAssembler::AssertPositiveOrZero(Register value) {
1661  if (!FLAG_debug_code) return;
1662  ASM_CODE_COMMENT(this);
1663  Label done;
1664  int sign_bit = value.Is64Bits() ? kXSignBit : kWSignBit;
1665  Tbz(value, sign_bit, &done);
1666  Abort(AbortReason::kUnexpectedNegativeValue);
1667  Bind(&done);
1668}
1669
1670void MacroAssembler::CallRuntime(const Runtime::Function* f, int num_arguments,
1671                                 SaveFPRegsMode save_doubles) {
1672  ASM_CODE_COMMENT(this);
1673  // All arguments must be on the stack before this function is called.
1674  // x0 holds the return value after the call.
1675
1676  // Check that the number of arguments matches what the function expects.
1677  // If f->nargs is -1, the function can accept a variable number of arguments.
1678  CHECK(f->nargs < 0 || f->nargs == num_arguments);
1679
1680  // Place the necessary arguments.
1681  Mov(x0, num_arguments);
1682  Mov(x1, ExternalReference::Create(f));
1683
1684  Handle<CodeT> code =
1685      CodeFactory::CEntry(isolate(), f->result_size, save_doubles);
1686  Call(code, RelocInfo::CODE_TARGET);
1687}
1688
1689void MacroAssembler::JumpToExternalReference(const ExternalReference& builtin,
1690                                             bool builtin_exit_frame) {
1691  ASM_CODE_COMMENT(this);
1692  Mov(x1, builtin);
1693  Handle<CodeT> code =
1694      CodeFactory::CEntry(isolate(), 1, SaveFPRegsMode::kIgnore,
1695                          ArgvMode::kStack, builtin_exit_frame);
1696  Jump(code, RelocInfo::CODE_TARGET);
1697}
1698
1699void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
1700  Ldr(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
1701  Br(kOffHeapTrampolineRegister);
1702}
1703
1704void MacroAssembler::TailCallRuntime(Runtime::FunctionId fid) {
1705  ASM_CODE_COMMENT(this);
1706  const Runtime::Function* function = Runtime::FunctionForId(fid);
1707  DCHECK_EQ(1, function->result_size);
1708  if (function->nargs >= 0) {
1709    // TODO(1236192): Most runtime routines don't need the number of
1710    // arguments passed in because it is constant. At some point we
1711    // should remove this need and make the runtime routine entry code
1712    // smarter.
1713    Mov(x0, function->nargs);
1714  }
1715  JumpToExternalReference(ExternalReference::Create(fid));
1716}
1717
1718int TurboAssembler::ActivationFrameAlignment() {
1719#if V8_HOST_ARCH_ARM64
1720  // Running on the real platform. Use the alignment as mandated by the local
1721  // environment.
1722  // Note: This will break if we ever start generating snapshots on one ARM
1723  // platform for another ARM platform with a different alignment.
1724  return base::OS::ActivationFrameAlignment();
1725#else   // V8_HOST_ARCH_ARM64
1726  // If we are using the simulator then we should always align to the expected
1727  // alignment. As the simulator is used to generate snapshots we do not know
1728  // if the target platform will need alignment, so this is controlled from a
1729  // flag.
1730  return FLAG_sim_stack_alignment;
1731#endif  // V8_HOST_ARCH_ARM64
1732}
1733
1734void TurboAssembler::CallCFunction(ExternalReference function,
1735                                   int num_of_reg_args) {
1736  CallCFunction(function, num_of_reg_args, 0);
1737}
1738
1739void TurboAssembler::CallCFunction(ExternalReference function,
1740                                   int num_of_reg_args,
1741                                   int num_of_double_args) {
1742  ASM_CODE_COMMENT(this);
1743  UseScratchRegisterScope temps(this);
1744  Register temp = temps.AcquireX();
1745  Mov(temp, function);
1746  CallCFunction(temp, num_of_reg_args, num_of_double_args);
1747}
1748
1749static const int kRegisterPassedArguments = 8;
1750static const int kFPRegisterPassedArguments = 8;
1751
1752void TurboAssembler::CallCFunction(Register function, int num_of_reg_args,
1753                                   int num_of_double_args) {
1754  ASM_CODE_COMMENT(this);
1755  DCHECK_LE(num_of_reg_args + num_of_double_args, kMaxCParameters);
1756  DCHECK(has_frame());
1757
1758  // Save the frame pointer and PC so that the stack layout remains iterable,
1759  // even without an ExitFrame which normally exists between JS and C frames.
1760  Register pc_scratch = x4;
1761  Register addr_scratch = x5;
1762  Push(pc_scratch, addr_scratch);
1763
1764  Label get_pc;
1765  Bind(&get_pc);
1766  Adr(pc_scratch, &get_pc);
1767
1768  // See x64 code for reasoning about how to address the isolate data fields.
1769  if (root_array_available()) {
1770    Str(pc_scratch,
1771        MemOperand(kRootRegister, IsolateData::fast_c_call_caller_pc_offset()));
1772    Str(fp,
1773        MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
1774  } else {
1775    DCHECK_NOT_NULL(isolate());
1776    Mov(addr_scratch,
1777        ExternalReference::fast_c_call_caller_pc_address(isolate()));
1778    Str(pc_scratch, MemOperand(addr_scratch));
1779    Mov(addr_scratch,
1780        ExternalReference::fast_c_call_caller_fp_address(isolate()));
1781    Str(fp, MemOperand(addr_scratch));
1782  }
1783
1784  Pop(addr_scratch, pc_scratch);
1785
1786  // Call directly. The function called cannot cause a GC, or allow preemption,
1787  // so the return address in the link register stays correct.
1788  Call(function);
1789
1790  // We don't unset the PC; the FP is the source of truth.
1791  if (root_array_available()) {
1792    Str(xzr,
1793        MemOperand(kRootRegister, IsolateData::fast_c_call_caller_fp_offset()));
1794  } else {
1795    DCHECK_NOT_NULL(isolate());
1796    Push(addr_scratch, xzr);
1797    Mov(addr_scratch,
1798        ExternalReference::fast_c_call_caller_fp_address(isolate()));
1799    Str(xzr, MemOperand(addr_scratch));
1800    Pop(xzr, addr_scratch);
1801  }
1802
1803  if (num_of_reg_args > kRegisterPassedArguments) {
1804    // Drop the register passed arguments.
1805    int claim_slots = RoundUp(num_of_reg_args - kRegisterPassedArguments, 2);
1806    Drop(claim_slots);
1807  }
1808
1809  if (num_of_double_args > kFPRegisterPassedArguments) {
1810    // Drop the register passed arguments.
1811    int claim_slots =
1812        RoundUp(num_of_double_args - kFPRegisterPassedArguments, 2);
1813    Drop(claim_slots);
1814  }
1815}
1816
1817void TurboAssembler::LoadFromConstantsTable(Register destination,
1818                                            int constant_index) {
1819  ASM_CODE_COMMENT(this);
1820  DCHECK(RootsTable::IsImmortalImmovable(RootIndex::kBuiltinsConstantsTable));
1821  LoadRoot(destination, RootIndex::kBuiltinsConstantsTable);
1822  LoadTaggedPointerField(
1823      destination, FieldMemOperand(destination, FixedArray::OffsetOfElementAt(
1824                                                    constant_index)));
1825}
1826
1827void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
1828  Ldr(destination, MemOperand(kRootRegister, offset));
1829}
1830
1831void TurboAssembler::LoadRootRegisterOffset(Register destination,
1832                                            intptr_t offset) {
1833  if (offset == 0) {
1834    Mov(destination, kRootRegister);
1835  } else {
1836    Add(destination, kRootRegister, offset);
1837  }
1838}
1839
1840void TurboAssembler::Jump(Register target, Condition cond) {
1841  if (cond == nv) return;
1842  Label done;
1843  if (cond != al) B(NegateCondition(cond), &done);
1844  Br(target);
1845  Bind(&done);
1846}
1847
1848void TurboAssembler::JumpHelper(int64_t offset, RelocInfo::Mode rmode,
1849                                Condition cond) {
1850  if (cond == nv) return;
1851  Label done;
1852  if (cond != al) B(NegateCondition(cond), &done);
1853  if (CanUseNearCallOrJump(rmode)) {
1854    DCHECK(IsNearCallOffset(offset));
1855    near_jump(static_cast<int>(offset), rmode);
1856  } else {
1857    UseScratchRegisterScope temps(this);
1858    Register temp = temps.AcquireX();
1859    uint64_t imm = reinterpret_cast<uint64_t>(pc_) + offset * kInstrSize;
1860    Mov(temp, Immediate(imm, rmode));
1861    Br(temp);
1862  }
1863  Bind(&done);
1864}
1865
1866// The calculated offset is either:
1867// * the 'target' input unmodified if this is a Wasm call, or
1868// * the offset of the target from the code range start, if this is a call to
1869//   un-embedded builtin, or
1870// * the offset of the target from the current PC, in instructions, for any
1871//   other type of call.
1872int64_t TurboAssembler::CalculateTargetOffset(Address target,
1873                                              RelocInfo::Mode rmode, byte* pc) {
1874  int64_t offset = static_cast<int64_t>(target);
1875  if (rmode == RelocInfo::WASM_CALL || rmode == RelocInfo::WASM_STUB_CALL) {
1876    // The target of WebAssembly calls is still an index instead of an actual
1877    // address at this point, and needs to be encoded as-is.
1878    return offset;
1879  }
1880  if (RelocInfo::IsRuntimeEntry(rmode)) {
1881    // The runtime entry targets are used for generating short builtin calls
1882    // from JIT-compiled code (it's not used during snapshot creation).
1883    // The value is encoded as an offset from the code range (see
1884    // Assembler::runtime_entry_at()).
1885    // Note, that builtin-to-builitin calls use different OFF_HEAP_TARGET mode
1886    // and therefore are encoded differently.
1887    DCHECK_NE(options().code_range_base, 0);
1888    offset -= static_cast<int64_t>(options().code_range_base);
1889  } else {
1890    offset -= reinterpret_cast<int64_t>(pc);
1891  }
1892  DCHECK_EQ(offset % kInstrSize, 0);
1893  offset = offset / static_cast<int>(kInstrSize);
1894  return offset;
1895}
1896
1897void TurboAssembler::Jump(Address target, RelocInfo::Mode rmode,
1898                          Condition cond) {
1899  int64_t offset = CalculateTargetOffset(target, rmode, pc_);
1900  JumpHelper(offset, rmode, cond);
1901}
1902
1903void TurboAssembler::Jump(Handle<CodeT> code, RelocInfo::Mode rmode,
1904                          Condition cond) {
1905  DCHECK(RelocInfo::IsCodeTarget(rmode));
1906  DCHECK_IMPLIES(options().isolate_independent_code,
1907                 Builtins::IsIsolateIndependentBuiltin(FromCodeT(*code)));
1908
1909  if (options().inline_offheap_trampolines) {
1910    Builtin builtin = Builtin::kNoBuiltinId;
1911    if (isolate()->builtins()->IsBuiltinHandle(code, &builtin)) {
1912      // Inline the trampoline.
1913      CHECK_EQ(cond, Condition::al);  // Implement if necessary.
1914      TailCallBuiltin(builtin);
1915      return;
1916    }
1917  }
1918
1919  if (CanUseNearCallOrJump(rmode)) {
1920    EmbeddedObjectIndex index = AddEmbeddedObject(code);
1921    DCHECK(is_int32(index));
1922    JumpHelper(static_cast<int64_t>(index), rmode, cond);
1923  } else {
1924    Jump(code.address(), rmode, cond);
1925  }
1926}
1927
1928void TurboAssembler::Jump(const ExternalReference& reference) {
1929  UseScratchRegisterScope temps(this);
1930  Register scratch = temps.AcquireX();
1931  Mov(scratch, reference);
1932  Jump(scratch);
1933}
1934
1935void TurboAssembler::Call(Register target) {
1936  BlockPoolsScope scope(this);
1937  Blr(target);
1938}
1939
1940void TurboAssembler::Call(Address target, RelocInfo::Mode rmode) {
1941  BlockPoolsScope scope(this);
1942  if (CanUseNearCallOrJump(rmode)) {
1943    int64_t offset = CalculateTargetOffset(target, rmode, pc_);
1944    DCHECK(IsNearCallOffset(offset));
1945    near_call(static_cast<int>(offset), rmode);
1946  } else {
1947    IndirectCall(target, rmode);
1948  }
1949}
1950
1951void TurboAssembler::Call(Handle<CodeT> code, RelocInfo::Mode rmode) {
1952  DCHECK_IMPLIES(options().isolate_independent_code,
1953                 Builtins::IsIsolateIndependentBuiltin(FromCodeT(*code)));
1954  BlockPoolsScope scope(this);
1955
1956  if (options().inline_offheap_trampolines) {
1957    Builtin builtin = Builtin::kNoBuiltinId;
1958    if (isolate()->builtins()->IsBuiltinHandle(code, &builtin)) {
1959      // Inline the trampoline.
1960      CallBuiltin(builtin);
1961      return;
1962    }
1963  }
1964
1965  DCHECK(FromCodeT(*code).IsExecutable());
1966  if (CanUseNearCallOrJump(rmode)) {
1967    EmbeddedObjectIndex index = AddEmbeddedObject(code);
1968    DCHECK(is_int32(index));
1969    near_call(static_cast<int32_t>(index), rmode);
1970  } else {
1971    IndirectCall(code.address(), rmode);
1972  }
1973}
1974
1975void TurboAssembler::Call(ExternalReference target) {
1976  UseScratchRegisterScope temps(this);
1977  Register temp = temps.AcquireX();
1978  Mov(temp, target);
1979  Call(temp);
1980}
1981
1982void TurboAssembler::LoadEntryFromBuiltinIndex(Register builtin_index) {
1983  ASM_CODE_COMMENT(this);
1984  // The builtin_index register contains the builtin index as a Smi.
1985  // Untagging is folded into the indexing operand below.
1986  if (SmiValuesAre32Bits()) {
1987    Asr(builtin_index, builtin_index, kSmiShift - kSystemPointerSizeLog2);
1988    Add(builtin_index, builtin_index,
1989        IsolateData::builtin_entry_table_offset());
1990    Ldr(builtin_index, MemOperand(kRootRegister, builtin_index));
1991  } else {
1992    DCHECK(SmiValuesAre31Bits());
1993    if (COMPRESS_POINTERS_BOOL) {
1994      Add(builtin_index, kRootRegister,
1995          Operand(builtin_index.W(), SXTW, kSystemPointerSizeLog2 - kSmiShift));
1996    } else {
1997      Add(builtin_index, kRootRegister,
1998          Operand(builtin_index, LSL, kSystemPointerSizeLog2 - kSmiShift));
1999    }
2000    Ldr(builtin_index,
2001        MemOperand(builtin_index, IsolateData::builtin_entry_table_offset()));
2002  }
2003}
2004
2005void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin,
2006                                          Register destination) {
2007  Ldr(destination, EntryFromBuiltinAsOperand(builtin));
2008}
2009
2010MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
2011  ASM_CODE_COMMENT(this);
2012  DCHECK(root_array_available());
2013  return MemOperand(kRootRegister,
2014                    IsolateData::BuiltinEntrySlotOffset(builtin));
2015}
2016
2017void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
2018  ASM_CODE_COMMENT(this);
2019  LoadEntryFromBuiltinIndex(builtin_index);
2020  Call(builtin_index);
2021}
2022
2023void TurboAssembler::CallBuiltin(Builtin builtin) {
2024  ASM_CODE_COMMENT(this);
2025  DCHECK(Builtins::IsBuiltinId(builtin));
2026  RecordCommentForOffHeapTrampoline(builtin);
2027  CHECK_NE(builtin, Builtin::kNoBuiltinId);
2028  if (options().short_builtin_calls) {
2029    Call(BuiltinEntry(builtin), RelocInfo::RUNTIME_ENTRY);
2030
2031  } else {
2032    UseScratchRegisterScope temps(this);
2033    Register scratch = temps.AcquireX();
2034    Ldr(scratch, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
2035    Call(scratch);
2036  }
2037}
2038
2039void TurboAssembler::TailCallBuiltin(Builtin builtin) {
2040  ASM_CODE_COMMENT(this);
2041  DCHECK(Builtins::IsBuiltinId(builtin));
2042  RecordCommentForOffHeapTrampoline(builtin);
2043  CHECK_NE(builtin, Builtin::kNoBuiltinId);
2044  if (options().short_builtin_calls) {
2045    Jump(BuiltinEntry(builtin), RelocInfo::RUNTIME_ENTRY);
2046
2047  } else {
2048    // The control flow integrity (CFI) feature allows us to "sign" code entry
2049    // points as a target for calls, jumps or both. Arm64 has special
2050    // instructions for this purpose, so-called "landing pads" (see
2051    // TurboAssembler::CallTarget(), TurboAssembler::JumpTarget() and
2052    // TurboAssembler::JumpOrCallTarget()). Currently, we generate "Call"
2053    // landing pads for CPP builtins. In order to allow tail calling to those
2054    // builtins we have to use a workaround.
2055    // x17 is used to allow using "Call" (i.e. `bti c`) rather than "Jump"
2056    // (i.e. `bti j`) landing pads for the tail-called code.
2057    Register temp = x17;
2058
2059    Ldr(temp, Operand(BuiltinEntry(builtin), RelocInfo::OFF_HEAP_TARGET));
2060    Jump(temp);
2061  }
2062}
2063
2064void TurboAssembler::LoadCodeObjectEntry(Register destination,
2065                                         Register code_object) {
2066  ASM_CODE_COMMENT(this);
2067  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
2068    LoadCodeDataContainerEntry(destination, code_object);
2069    return;
2070  }
2071
2072  // Code objects are called differently depending on whether we are generating
2073  // builtin code (which will later be embedded into the binary) or compiling
2074  // user JS code at runtime.
2075  // * Builtin code runs in --jitless mode and thus must not call into on-heap
2076  //   Code targets. Instead, we dispatch through the builtins entry table.
2077  // * Codegen at runtime does not have this restriction and we can use the
2078  //   shorter, branchless instruction sequence. The assumption here is that
2079  //   targets are usually generated code and not builtin Code objects.
2080
2081  if (options().isolate_independent_code) {
2082    DCHECK(root_array_available());
2083    Label if_code_is_off_heap, out;
2084
2085    UseScratchRegisterScope temps(this);
2086    Register scratch = temps.AcquireX();
2087
2088    DCHECK(!AreAliased(destination, scratch));
2089    DCHECK(!AreAliased(code_object, scratch));
2090
2091    // Check whether the Code object is an off-heap trampoline. If so, call its
2092    // (off-heap) entry point directly without going through the (on-heap)
2093    // trampoline.  Otherwise, just call the Code object as always.
2094
2095    Ldr(scratch.W(), FieldMemOperand(code_object, Code::kFlagsOffset));
2096    TestAndBranchIfAnySet(scratch.W(), Code::IsOffHeapTrampoline::kMask,
2097                          &if_code_is_off_heap);
2098
2099    // Not an off-heap trampoline object, the entry point is at
2100    // Code::raw_instruction_start().
2101    Add(destination, code_object, Code::kHeaderSize - kHeapObjectTag);
2102    B(&out);
2103
2104    // An off-heap trampoline, the entry point is loaded from the builtin entry
2105    // table.
2106    bind(&if_code_is_off_heap);
2107    Ldrsw(scratch, FieldMemOperand(code_object, Code::kBuiltinIndexOffset));
2108    Add(destination, kRootRegister,
2109        Operand(scratch, LSL, kSystemPointerSizeLog2));
2110    Ldr(destination,
2111        MemOperand(destination, IsolateData::builtin_entry_table_offset()));
2112
2113    bind(&out);
2114  } else {
2115    Add(destination, code_object, Code::kHeaderSize - kHeapObjectTag);
2116  }
2117}
2118
2119void TurboAssembler::CallCodeObject(Register code_object) {
2120  ASM_CODE_COMMENT(this);
2121  LoadCodeObjectEntry(code_object, code_object);
2122  Call(code_object);
2123}
2124
2125void TurboAssembler::JumpCodeObject(Register code_object, JumpMode jump_mode) {
2126  ASM_CODE_COMMENT(this);
2127  DCHECK_EQ(JumpMode::kJump, jump_mode);
2128  LoadCodeObjectEntry(code_object, code_object);
2129
2130  UseScratchRegisterScope temps(this);
2131  if (code_object != x17) {
2132    temps.Exclude(x17);
2133    Mov(x17, code_object);
2134  }
2135  Jump(x17);
2136}
2137
2138void TurboAssembler::LoadCodeDataContainerEntry(
2139    Register destination, Register code_data_container_object) {
2140  ASM_CODE_COMMENT(this);
2141  CHECK(V8_EXTERNAL_CODE_SPACE_BOOL);
2142
2143  LoadExternalPointerField(
2144      destination,
2145      FieldMemOperand(code_data_container_object,
2146                      CodeDataContainer::kCodeEntryPointOffset),
2147      kCodeEntryPointTag);
2148}
2149
2150void TurboAssembler::LoadCodeDataContainerCodeNonBuiltin(
2151    Register destination, Register code_data_container_object) {
2152  ASM_CODE_COMMENT(this);
2153  CHECK(V8_EXTERNAL_CODE_SPACE_BOOL);
2154  // Given the fields layout we can read the Code reference as a full word.
2155  STATIC_ASSERT(!V8_EXTERNAL_CODE_SPACE_BOOL ||
2156                (CodeDataContainer::kCodeCageBaseUpper32BitsOffset ==
2157                 CodeDataContainer::kCodeOffset + kTaggedSize));
2158  Ldr(destination, FieldMemOperand(code_data_container_object,
2159                                   CodeDataContainer::kCodeOffset));
2160}
2161
2162void TurboAssembler::CallCodeDataContainerObject(
2163    Register code_data_container_object) {
2164  ASM_CODE_COMMENT(this);
2165  LoadCodeDataContainerEntry(code_data_container_object,
2166                             code_data_container_object);
2167  Call(code_data_container_object);
2168}
2169
2170void TurboAssembler::JumpCodeDataContainerObject(
2171    Register code_data_container_object, JumpMode jump_mode) {
2172  ASM_CODE_COMMENT(this);
2173  DCHECK_EQ(JumpMode::kJump, jump_mode);
2174  LoadCodeDataContainerEntry(code_data_container_object,
2175                             code_data_container_object);
2176  UseScratchRegisterScope temps(this);
2177  if (code_data_container_object != x17) {
2178    temps.Exclude(x17);
2179    Mov(x17, code_data_container_object);
2180  }
2181  Jump(x17);
2182}
2183
2184void TurboAssembler::LoadCodeTEntry(Register destination, Register code) {
2185  ASM_CODE_COMMENT(this);
2186  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
2187    LoadCodeDataContainerEntry(destination, code);
2188  } else {
2189    Add(destination, code, Operand(Code::kHeaderSize - kHeapObjectTag));
2190  }
2191}
2192
2193void TurboAssembler::CallCodeTObject(Register code) {
2194  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
2195    CallCodeDataContainerObject(code);
2196  } else {
2197    CallCodeObject(code);
2198  }
2199}
2200
2201void TurboAssembler::JumpCodeTObject(Register code, JumpMode jump_mode) {
2202  if (V8_EXTERNAL_CODE_SPACE_BOOL) {
2203    JumpCodeDataContainerObject(code, jump_mode);
2204  } else {
2205    JumpCodeObject(code, jump_mode);
2206  }
2207}
2208
2209void TurboAssembler::StoreReturnAddressAndCall(Register target) {
2210  ASM_CODE_COMMENT(this);
2211  // This generates the final instruction sequence for calls to C functions
2212  // once an exit frame has been constructed.
2213  //
2214  // Note that this assumes the caller code (i.e. the Code object currently
2215  // being generated) is immovable or that the callee function cannot trigger
2216  // GC, since the callee function will return to it.
2217
2218  UseScratchRegisterScope temps(this);
2219  temps.Exclude(x16, x17);
2220
2221  Label return_location;
2222  Adr(x17, &return_location);
2223#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
2224  Add(x16, sp, kSystemPointerSize);
2225  Pacib1716();
2226#endif
2227  Poke(x17, 0);
2228
2229  if (FLAG_debug_code) {
2230    ASM_CODE_COMMENT_STRING(this, "Verify fp[kSPOffset]-8");
2231    // Verify that the slot below fp[kSPOffset]-8 points to the signed return
2232    // location.
2233    Ldr(x16, MemOperand(fp, ExitFrameConstants::kSPOffset));
2234    Ldr(x16, MemOperand(x16, -static_cast<int64_t>(kXRegSize)));
2235    Cmp(x16, x17);
2236    Check(eq, AbortReason::kReturnAddressNotFoundInFrame);
2237  }
2238
2239  Blr(target);
2240  Bind(&return_location);
2241}
2242
2243void TurboAssembler::IndirectCall(Address target, RelocInfo::Mode rmode) {
2244  ASM_CODE_COMMENT(this);
2245  UseScratchRegisterScope temps(this);
2246  Register temp = temps.AcquireX();
2247  Mov(temp, Immediate(target, rmode));
2248  Blr(temp);
2249}
2250
2251bool TurboAssembler::IsNearCallOffset(int64_t offset) {
2252  return is_int26(offset);
2253}
2254
2255void TurboAssembler::CallForDeoptimization(
2256    Builtin target, int deopt_id, Label* exit, DeoptimizeKind kind, Label* ret,
2257    Label* jump_deoptimization_entry_label) {
2258  ASM_CODE_COMMENT(this);
2259  BlockPoolsScope scope(this);
2260  bl(jump_deoptimization_entry_label);
2261  DCHECK_EQ(SizeOfCodeGeneratedSince(exit),
2262            (kind == DeoptimizeKind::kLazy) ? Deoptimizer::kLazyDeoptExitSize
2263                                            : Deoptimizer::kEagerDeoptExitSize);
2264}
2265
2266void MacroAssembler::LoadStackLimit(Register destination, StackLimitKind kind) {
2267  ASM_CODE_COMMENT(this);
2268  DCHECK(root_array_available());
2269  Isolate* isolate = this->isolate();
2270  ExternalReference limit =
2271      kind == StackLimitKind::kRealStackLimit
2272          ? ExternalReference::address_of_real_jslimit(isolate)
2273          : ExternalReference::address_of_jslimit(isolate);
2274  DCHECK(TurboAssembler::IsAddressableThroughRootRegister(isolate, limit));
2275
2276  intptr_t offset =
2277      TurboAssembler::RootRegisterOffsetForExternalReference(isolate, limit);
2278  Ldr(destination, MemOperand(kRootRegister, offset));
2279}
2280
2281void MacroAssembler::StackOverflowCheck(Register num_args,
2282                                        Label* stack_overflow) {
2283  ASM_CODE_COMMENT(this);
2284  UseScratchRegisterScope temps(this);
2285  Register scratch = temps.AcquireX();
2286
2287  // Check the stack for overflow.
2288  // We are not trying to catch interruptions (e.g. debug break and
2289  // preemption) here, so the "real stack limit" is checked.
2290
2291  LoadStackLimit(scratch, StackLimitKind::kRealStackLimit);
2292  // Make scratch the space we have left. The stack might already be overflowed
2293  // here which will cause scratch to become negative.
2294  Sub(scratch, sp, scratch);
2295  // Check if the arguments will overflow the stack.
2296  Cmp(scratch, Operand(num_args, LSL, kSystemPointerSizeLog2));
2297  B(le, stack_overflow);
2298}
2299
2300void MacroAssembler::InvokePrologue(Register formal_parameter_count,
2301                                    Register actual_argument_count, Label* done,
2302                                    InvokeType type) {
2303  ASM_CODE_COMMENT(this);
2304  //  x0: actual arguments count.
2305  //  x1: function (passed through to callee).
2306  //  x2: expected arguments count.
2307  //  x3: new target
2308  Label regular_invoke;
2309  DCHECK_EQ(actual_argument_count, x0);
2310  DCHECK_EQ(formal_parameter_count, x2);
2311
2312  // If the formal parameter count is equal to the adaptor sentinel, no need
2313  // to push undefined value as arguments.
2314  if (kDontAdaptArgumentsSentinel != 0) {
2315    Cmp(formal_parameter_count, Operand(kDontAdaptArgumentsSentinel));
2316    B(eq, &regular_invoke);
2317  }
2318
2319  // If overapplication or if the actual argument count is equal to the
2320  // formal parameter count, no need to push extra undefined values.
2321  Register extra_argument_count = x2;
2322  Subs(extra_argument_count, formal_parameter_count, actual_argument_count);
2323  B(le, &regular_invoke);
2324
2325  // The stack pointer in arm64 needs to be 16-byte aligned. We might need to
2326  // (1) add an extra padding or (2) remove (re-use) the extra padding already
2327  // in the stack. Let {slots_to_copy} be the number of slots (arguments) to
2328  // move up in the stack and let {slots_to_claim} be the number of extra stack
2329  // slots to claim.
2330  Label even_extra_count, skip_move;
2331  Register slots_to_copy = x4;
2332  Register slots_to_claim = x5;
2333
2334  Mov(slots_to_copy, actual_argument_count);
2335  Mov(slots_to_claim, extra_argument_count);
2336  Tbz(extra_argument_count, 0, &even_extra_count);
2337
2338  // Calculate {slots_to_claim} when {extra_argument_count} is odd.
2339  // If {actual_argument_count} is even, we need one extra padding slot
2340  // {slots_to_claim = extra_argument_count + 1}.
2341  // If {actual_argument_count} is odd, we know that the
2342  // original arguments will have a padding slot that we can reuse
2343  // {slots_to_claim = extra_argument_count - 1}.
2344  {
2345    Register scratch = x11;
2346    Add(slots_to_claim, extra_argument_count, 1);
2347    And(scratch, actual_argument_count, 1);
2348    Sub(slots_to_claim, slots_to_claim, Operand(scratch, LSL, 1));
2349  }
2350
2351  Bind(&even_extra_count);
2352  Cbz(slots_to_claim, &skip_move);
2353
2354  Label stack_overflow;
2355  StackOverflowCheck(slots_to_claim, &stack_overflow);
2356  Claim(slots_to_claim);
2357
2358  // Move the arguments already in the stack including the receiver.
2359  {
2360    Register src = x6;
2361    Register dst = x7;
2362    SlotAddress(src, slots_to_claim);
2363    SlotAddress(dst, 0);
2364    CopyDoubleWords(dst, src, slots_to_copy);
2365  }
2366
2367  Bind(&skip_move);
2368  Register pointer_next_value = x5;
2369
2370  // Copy extra arguments as undefined values.
2371  {
2372    Label loop;
2373    Register undefined_value = x6;
2374    Register count = x7;
2375    LoadRoot(undefined_value, RootIndex::kUndefinedValue);
2376    SlotAddress(pointer_next_value, actual_argument_count);
2377    Mov(count, extra_argument_count);
2378    Bind(&loop);
2379    Str(undefined_value,
2380        MemOperand(pointer_next_value, kSystemPointerSize, PostIndex));
2381    Subs(count, count, 1);
2382    Cbnz(count, &loop);
2383  }
2384
2385  // Set padding if needed.
2386  {
2387    Label skip;
2388    Register total_args_slots = x4;
2389    Add(total_args_slots, actual_argument_count, extra_argument_count);
2390    Tbz(total_args_slots, 0, &skip);
2391    Str(padreg, MemOperand(pointer_next_value));
2392    Bind(&skip);
2393  }
2394  B(&regular_invoke);
2395
2396  bind(&stack_overflow);
2397  {
2398    FrameScope frame(
2399        this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
2400    CallRuntime(Runtime::kThrowStackOverflow);
2401    Unreachable();
2402  }
2403
2404  Bind(&regular_invoke);
2405}
2406
2407void MacroAssembler::CallDebugOnFunctionCall(Register fun, Register new_target,
2408                                             Register expected_parameter_count,
2409                                             Register actual_parameter_count) {
2410  ASM_CODE_COMMENT(this);
2411  // Load receiver to pass it later to DebugOnFunctionCall hook.
2412  Peek(x4, ReceiverOperand(actual_parameter_count));
2413  FrameScope frame(
2414      this, has_frame() ? StackFrame::NO_FRAME_TYPE : StackFrame::INTERNAL);
2415
2416  if (!new_target.is_valid()) new_target = padreg;
2417
2418  // Save values on stack.
2419  SmiTag(expected_parameter_count);
2420  SmiTag(actual_parameter_count);
2421  Push(expected_parameter_count, actual_parameter_count, new_target, fun);
2422  Push(fun, x4);
2423  CallRuntime(Runtime::kDebugOnFunctionCall);
2424
2425  // Restore values from stack.
2426  Pop(fun, new_target, actual_parameter_count, expected_parameter_count);
2427  SmiUntag(actual_parameter_count);
2428  SmiUntag(expected_parameter_count);
2429}
2430
2431void MacroAssembler::InvokeFunctionCode(Register function, Register new_target,
2432                                        Register expected_parameter_count,
2433                                        Register actual_parameter_count,
2434                                        InvokeType type) {
2435  ASM_CODE_COMMENT(this);
2436  // You can't call a function without a valid frame.
2437  DCHECK_IMPLIES(type == InvokeType::kCall, has_frame());
2438  DCHECK_EQ(function, x1);
2439  DCHECK_IMPLIES(new_target.is_valid(), new_target == x3);
2440
2441  // On function call, call into the debugger if necessary.
2442  Label debug_hook, continue_after_hook;
2443  {
2444    Mov(x4, ExternalReference::debug_hook_on_function_call_address(isolate()));
2445    Ldrsb(x4, MemOperand(x4));
2446    Cbnz(x4, &debug_hook);
2447  }
2448  bind(&continue_after_hook);
2449
2450  // Clear the new.target register if not given.
2451  if (!new_target.is_valid()) {
2452    LoadRoot(x3, RootIndex::kUndefinedValue);
2453  }
2454
2455  Label done;
2456  InvokePrologue(expected_parameter_count, actual_parameter_count, &done, type);
2457
2458  // If actual != expected, InvokePrologue will have handled the call through
2459  // the argument adaptor mechanism.
2460  // The called function expects the call kind in x5.
2461  // We call indirectly through the code field in the function to
2462  // allow recompilation to take effect without changing any of the
2463  // call sites.
2464  Register code = kJavaScriptCallCodeStartRegister;
2465  LoadTaggedPointerField(code,
2466                         FieldMemOperand(function, JSFunction::kCodeOffset));
2467  switch (type) {
2468    case InvokeType::kCall:
2469      CallCodeTObject(code);
2470      break;
2471    case InvokeType::kJump:
2472      JumpCodeTObject(code);
2473      break;
2474  }
2475  B(&done);
2476
2477  // Deferred debug hook.
2478  bind(&debug_hook);
2479  CallDebugOnFunctionCall(function, new_target, expected_parameter_count,
2480                          actual_parameter_count);
2481  B(&continue_after_hook);
2482
2483  // Continue here if InvokePrologue does handle the invocation due to
2484  // mismatched parameter counts.
2485  Bind(&done);
2486}
2487
2488Operand MacroAssembler::ReceiverOperand(Register arg_count) {
2489  return Operand(0);
2490}
2491
2492void MacroAssembler::InvokeFunctionWithNewTarget(
2493    Register function, Register new_target, Register actual_parameter_count,
2494    InvokeType type) {
2495  ASM_CODE_COMMENT(this);
2496  // You can't call a function without a valid frame.
2497  DCHECK(type == InvokeType::kJump || has_frame());
2498
2499  // Contract with called JS functions requires that function is passed in x1.
2500  // (See FullCodeGenerator::Generate().)
2501  DCHECK_EQ(function, x1);
2502
2503  Register expected_parameter_count = x2;
2504
2505  LoadTaggedPointerField(cp,
2506                         FieldMemOperand(function, JSFunction::kContextOffset));
2507  // The number of arguments is stored as an int32_t, and -1 is a marker
2508  // (kDontAdaptArgumentsSentinel), so we need sign
2509  // extension to correctly handle it.
2510  LoadTaggedPointerField(
2511      expected_parameter_count,
2512      FieldMemOperand(function, JSFunction::kSharedFunctionInfoOffset));
2513  Ldrh(expected_parameter_count,
2514       FieldMemOperand(expected_parameter_count,
2515                       SharedFunctionInfo::kFormalParameterCountOffset));
2516
2517  InvokeFunctionCode(function, new_target, expected_parameter_count,
2518                     actual_parameter_count, type);
2519}
2520
2521void MacroAssembler::InvokeFunction(Register function,
2522                                    Register expected_parameter_count,
2523                                    Register actual_parameter_count,
2524                                    InvokeType type) {
2525  ASM_CODE_COMMENT(this);
2526  // You can't call a function without a valid frame.
2527  DCHECK(type == InvokeType::kJump || has_frame());
2528
2529  // Contract with called JS functions requires that function is passed in x1.
2530  // (See FullCodeGenerator::Generate().)
2531  DCHECK_EQ(function, x1);
2532
2533  // Set up the context.
2534  LoadTaggedPointerField(cp,
2535                         FieldMemOperand(function, JSFunction::kContextOffset));
2536
2537  InvokeFunctionCode(function, no_reg, expected_parameter_count,
2538                     actual_parameter_count, type);
2539}
2540
2541void TurboAssembler::TryConvertDoubleToInt64(Register result,
2542                                             DoubleRegister double_input,
2543                                             Label* done) {
2544  ASM_CODE_COMMENT(this);
2545  // Try to convert with an FPU convert instruction. It's trivial to compute
2546  // the modulo operation on an integer register so we convert to a 64-bit
2547  // integer.
2548  //
2549  // Fcvtzs will saturate to INT64_MIN (0x800...00) or INT64_MAX (0x7FF...FF)
2550  // when the double is out of range. NaNs and infinities will be converted to 0
2551  // (as ECMA-262 requires).
2552  Fcvtzs(result.X(), double_input);
2553
2554  // The values INT64_MIN (0x800...00) or INT64_MAX (0x7FF...FF) are not
2555  // representable using a double, so if the result is one of those then we know
2556  // that saturation occurred, and we need to manually handle the conversion.
2557  //
2558  // It is easy to detect INT64_MIN and INT64_MAX because adding or subtracting
2559  // 1 will cause signed overflow.
2560  Cmp(result.X(), 1);
2561  Ccmp(result.X(), -1, VFlag, vc);
2562
2563  B(vc, done);
2564}
2565
2566void TurboAssembler::TruncateDoubleToI(Isolate* isolate, Zone* zone,
2567                                       Register result,
2568                                       DoubleRegister double_input,
2569                                       StubCallMode stub_mode,
2570                                       LinkRegisterStatus lr_status) {
2571  ASM_CODE_COMMENT(this);
2572  if (CpuFeatures::IsSupported(JSCVT)) {
2573    Fjcvtzs(result.W(), double_input);
2574    return;
2575  }
2576
2577  Label done;
2578
2579  // Try to convert the double to an int64. If successful, the bottom 32 bits
2580  // contain our truncated int32 result.
2581  TryConvertDoubleToInt64(result, double_input, &done);
2582
2583  // If we fell through then inline version didn't succeed - call stub instead.
2584  if (lr_status == kLRHasNotBeenSaved) {
2585    Push<TurboAssembler::kSignLR>(lr, double_input);
2586  } else {
2587    Push<TurboAssembler::kDontStoreLR>(xzr, double_input);
2588  }
2589
2590  // DoubleToI preserves any registers it needs to clobber.
2591#if V8_ENABLE_WEBASSEMBLY
2592  if (stub_mode == StubCallMode::kCallWasmRuntimeStub) {
2593    Call(wasm::WasmCode::kDoubleToI, RelocInfo::WASM_STUB_CALL);
2594#else
2595  // For balance.
2596  if (false) {
2597#endif  // V8_ENABLE_WEBASSEMBLY
2598  } else if (options().inline_offheap_trampolines) {
2599    CallBuiltin(Builtin::kDoubleToI);
2600  } else {
2601    Call(BUILTIN_CODE(isolate, DoubleToI), RelocInfo::CODE_TARGET);
2602  }
2603  Ldr(result, MemOperand(sp, 0));
2604
2605  DCHECK_EQ(xzr.SizeInBytes(), double_input.SizeInBytes());
2606
2607  if (lr_status == kLRHasNotBeenSaved) {
2608    // Pop into xzr here to drop the double input on the stack:
2609    Pop<TurboAssembler::kAuthLR>(xzr, lr);
2610  } else {
2611    Drop(2);
2612  }
2613
2614  Bind(&done);
2615  // Keep our invariant that the upper 32 bits are zero.
2616  Uxtw(result.W(), result.W());
2617}
2618
2619void TurboAssembler::Prologue() {
2620  ASM_CODE_COMMENT(this);
2621  Push<TurboAssembler::kSignLR>(lr, fp);
2622  mov(fp, sp);
2623  STATIC_ASSERT(kExtraSlotClaimedByPrologue == 1);
2624  Push(cp, kJSFunctionRegister, kJavaScriptCallArgCountRegister, padreg);
2625}
2626
2627void TurboAssembler::EnterFrame(StackFrame::Type type) {
2628  UseScratchRegisterScope temps(this);
2629
2630  if (type == StackFrame::INTERNAL
2631#if V8_ENABLE_WEBASSEMBLY
2632      || type == StackFrame::WASM_DEBUG_BREAK
2633#endif  // V8_ENABLE_WEBASSEMBLY
2634  ) {
2635    Register type_reg = temps.AcquireX();
2636    Mov(type_reg, StackFrame::TypeToMarker(type));
2637    Push<TurboAssembler::kSignLR>(lr, fp, type_reg, padreg);
2638    const int kFrameSize =
2639        TypedFrameConstants::kFixedFrameSizeFromFp + kSystemPointerSize;
2640    Add(fp, sp, kFrameSize);
2641    // sp[3] : lr
2642    // sp[2] : fp
2643    // sp[1] : type
2644    // sp[0] : for alignment
2645#if V8_ENABLE_WEBASSEMBLY
2646  } else if (type == StackFrame::WASM ||
2647             type == StackFrame::WASM_COMPILE_LAZY ||
2648             type == StackFrame::WASM_EXIT) {
2649    Register type_reg = temps.AcquireX();
2650    Mov(type_reg, StackFrame::TypeToMarker(type));
2651    Push<TurboAssembler::kSignLR>(lr, fp);
2652    Mov(fp, sp);
2653    Push(type_reg, kWasmInstanceRegister);
2654    // sp[3] : lr
2655    // sp[2] : fp
2656    // sp[1] : type
2657    // sp[0] : wasm instance
2658#endif  // V8_ENABLE_WEBASSEMBLY
2659  } else if (type == StackFrame::CONSTRUCT) {
2660    Register type_reg = temps.AcquireX();
2661    Mov(type_reg, StackFrame::TypeToMarker(type));
2662
2663    // Users of this frame type push a context pointer after the type field,
2664    // so do it here to keep the stack pointer aligned.
2665    Push<TurboAssembler::kSignLR>(lr, fp, type_reg, cp);
2666
2667    // The context pointer isn't part of the fixed frame, so add an extra slot
2668    // to account for it.
2669    Add(fp, sp,
2670        TypedFrameConstants::kFixedFrameSizeFromFp + kSystemPointerSize);
2671    // sp[3] : lr
2672    // sp[2] : fp
2673    // sp[1] : type
2674    // sp[0] : cp
2675  } else {
2676    DCHECK(StackFrame::IsJavaScript(type));
2677    // Just push a minimal "machine frame", saving the frame pointer and return
2678    // address, without any markers.
2679    Push<TurboAssembler::kSignLR>(lr, fp);
2680    Mov(fp, sp);
2681    // sp[1] : lr
2682    // sp[0] : fp
2683  }
2684}
2685
2686void TurboAssembler::LeaveFrame(StackFrame::Type type) {
2687  ASM_CODE_COMMENT(this);
2688  // Drop the execution stack down to the frame pointer and restore
2689  // the caller frame pointer and return address.
2690  Mov(sp, fp);
2691  Pop<TurboAssembler::kAuthLR>(fp, lr);
2692}
2693
2694void MacroAssembler::ExitFramePreserveFPRegs() {
2695  ASM_CODE_COMMENT(this);
2696  DCHECK_EQ(kCallerSavedV.Count() % 2, 0);
2697  PushCPURegList(kCallerSavedV);
2698}
2699
2700void MacroAssembler::ExitFrameRestoreFPRegs() {
2701  // Read the registers from the stack without popping them. The stack pointer
2702  // will be reset as part of the unwinding process.
2703  ASM_CODE_COMMENT(this);
2704  CPURegList saved_fp_regs = kCallerSavedV;
2705  DCHECK_EQ(saved_fp_regs.Count() % 2, 0);
2706
2707  int offset = ExitFrameConstants::kLastExitFrameField;
2708  while (!saved_fp_regs.IsEmpty()) {
2709    const CPURegister& dst0 = saved_fp_regs.PopHighestIndex();
2710    const CPURegister& dst1 = saved_fp_regs.PopHighestIndex();
2711    offset -= 2 * kDRegSize;
2712    Ldp(dst1, dst0, MemOperand(fp, offset));
2713  }
2714}
2715
2716void MacroAssembler::EnterExitFrame(bool save_doubles, const Register& scratch,
2717                                    int extra_space,
2718                                    StackFrame::Type frame_type) {
2719  ASM_CODE_COMMENT(this);
2720  DCHECK(frame_type == StackFrame::EXIT ||
2721         frame_type == StackFrame::BUILTIN_EXIT);
2722
2723  // Set up the new stack frame.
2724  Push<TurboAssembler::kSignLR>(lr, fp);
2725  Mov(fp, sp);
2726  Mov(scratch, StackFrame::TypeToMarker(frame_type));
2727  Push(scratch, xzr);
2728  //          fp[8]: CallerPC (lr)
2729  //    fp -> fp[0]: CallerFP (old fp)
2730  //          fp[-8]: STUB marker
2731  //    sp -> fp[-16]: Space reserved for SPOffset.
2732  STATIC_ASSERT((2 * kSystemPointerSize) ==
2733                ExitFrameConstants::kCallerSPOffset);
2734  STATIC_ASSERT((1 * kSystemPointerSize) ==
2735                ExitFrameConstants::kCallerPCOffset);
2736  STATIC_ASSERT((0 * kSystemPointerSize) ==
2737                ExitFrameConstants::kCallerFPOffset);
2738  STATIC_ASSERT((-2 * kSystemPointerSize) == ExitFrameConstants::kSPOffset);
2739
2740  // Save the frame pointer and context pointer in the top frame.
2741  Mov(scratch,
2742      ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate()));
2743  Str(fp, MemOperand(scratch));
2744  Mov(scratch,
2745      ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
2746  Str(cp, MemOperand(scratch));
2747
2748  STATIC_ASSERT((-2 * kSystemPointerSize) ==
2749                ExitFrameConstants::kLastExitFrameField);
2750  if (save_doubles) {
2751    ExitFramePreserveFPRegs();
2752  }
2753
2754  // Round the number of space we need to claim to a multiple of two.
2755  int slots_to_claim = RoundUp(extra_space + 1, 2);
2756
2757  // Reserve space for the return address and for user requested memory.
2758  // We do this before aligning to make sure that we end up correctly
2759  // aligned with the minimum of wasted space.
2760  Claim(slots_to_claim, kXRegSize);
2761  //         fp[8]: CallerPC (lr)
2762  //   fp -> fp[0]: CallerFP (old fp)
2763  //         fp[-8]: STUB marker
2764  //         fp[-16]: Space reserved for SPOffset.
2765  //         fp[-16 - fp_size]: Saved doubles (if save_doubles is true).
2766  //         sp[8]: Extra space reserved for caller (if extra_space != 0).
2767  //   sp -> sp[0]: Space reserved for the return address.
2768
2769  // ExitFrame::GetStateForFramePointer expects to find the return address at
2770  // the memory address immediately below the pointer stored in SPOffset.
2771  // It is not safe to derive much else from SPOffset, because the size of the
2772  // padding can vary.
2773  Add(scratch, sp, kXRegSize);
2774  Str(scratch, MemOperand(fp, ExitFrameConstants::kSPOffset));
2775}
2776
2777// Leave the current exit frame.
2778void MacroAssembler::LeaveExitFrame(bool restore_doubles,
2779                                    const Register& scratch,
2780                                    const Register& scratch2) {
2781  ASM_CODE_COMMENT(this);
2782  if (restore_doubles) {
2783    ExitFrameRestoreFPRegs();
2784  }
2785
2786  // Restore the context pointer from the top frame.
2787  Mov(scratch,
2788      ExternalReference::Create(IsolateAddressId::kContextAddress, isolate()));
2789  Ldr(cp, MemOperand(scratch));
2790
2791  if (FLAG_debug_code) {
2792    // Also emit debug code to clear the cp in the top frame.
2793    Mov(scratch2, Operand(Context::kInvalidContext));
2794    Mov(scratch, ExternalReference::Create(IsolateAddressId::kContextAddress,
2795                                           isolate()));
2796    Str(scratch2, MemOperand(scratch));
2797  }
2798  // Clear the frame pointer from the top frame.
2799  Mov(scratch,
2800      ExternalReference::Create(IsolateAddressId::kCEntryFPAddress, isolate()));
2801  Str(xzr, MemOperand(scratch));
2802
2803  // Pop the exit frame.
2804  //         fp[8]: CallerPC (lr)
2805  //   fp -> fp[0]: CallerFP (old fp)
2806  //         fp[...]: The rest of the frame.
2807  Mov(sp, fp);
2808  Pop<TurboAssembler::kAuthLR>(fp, lr);
2809}
2810
2811void MacroAssembler::LoadGlobalProxy(Register dst) {
2812  ASM_CODE_COMMENT(this);
2813  LoadNativeContextSlot(dst, Context::GLOBAL_PROXY_INDEX);
2814}
2815
2816void MacroAssembler::LoadWeakValue(Register out, Register in,
2817                                   Label* target_if_cleared) {
2818  ASM_CODE_COMMENT(this);
2819  CompareAndBranch(in.W(), Operand(kClearedWeakHeapObjectLower32), eq,
2820                   target_if_cleared);
2821
2822  and_(out, in, Operand(~kWeakHeapObjectMask));
2823}
2824
2825void MacroAssembler::EmitIncrementCounter(StatsCounter* counter, int value,
2826                                          Register scratch1,
2827                                          Register scratch2) {
2828  ASM_CODE_COMMENT(this);
2829  DCHECK_NE(value, 0);
2830  if (FLAG_native_code_counters && counter->Enabled()) {
2831    // This operation has to be exactly 32-bit wide in case the external
2832    // reference table redirects the counter to a uint32_t dummy_stats_counter_
2833    // field.
2834    Mov(scratch2, ExternalReference::Create(counter));
2835    Ldr(scratch1.W(), MemOperand(scratch2));
2836    Add(scratch1.W(), scratch1.W(), value);
2837    Str(scratch1.W(), MemOperand(scratch2));
2838  }
2839}
2840
2841void MacroAssembler::JumpIfObjectType(Register object, Register map,
2842                                      Register type_reg, InstanceType type,
2843                                      Label* if_cond_pass, Condition cond) {
2844  ASM_CODE_COMMENT(this);
2845  CompareObjectType(object, map, type_reg, type);
2846  B(cond, if_cond_pass);
2847}
2848
2849// Sets condition flags based on comparison, and returns type in type_reg.
2850void MacroAssembler::CompareObjectType(Register object, Register map,
2851                                       Register type_reg, InstanceType type) {
2852  ASM_CODE_COMMENT(this);
2853  LoadMap(map, object);
2854  CompareInstanceType(map, type_reg, type);
2855}
2856
2857void TurboAssembler::LoadMap(Register dst, Register object) {
2858  ASM_CODE_COMMENT(this);
2859  LoadTaggedPointerField(dst, FieldMemOperand(object, HeapObject::kMapOffset));
2860}
2861
2862// Sets condition flags based on comparison, and returns type in type_reg.
2863void MacroAssembler::CompareInstanceType(Register map, Register type_reg,
2864                                         InstanceType type) {
2865  ASM_CODE_COMMENT(this);
2866  Ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
2867  Cmp(type_reg, type);
2868}
2869
2870// Sets condition flags based on comparison, and returns type in type_reg.
2871void MacroAssembler::CompareInstanceTypeRange(Register map, Register type_reg,
2872                                              InstanceType lower_limit,
2873                                              InstanceType higher_limit) {
2874  ASM_CODE_COMMENT(this);
2875  DCHECK_LT(lower_limit, higher_limit);
2876  UseScratchRegisterScope temps(this);
2877  Register scratch = temps.AcquireX();
2878  Ldrh(type_reg, FieldMemOperand(map, Map::kInstanceTypeOffset));
2879  Sub(scratch, type_reg, Operand(lower_limit));
2880  Cmp(scratch, Operand(higher_limit - lower_limit));
2881}
2882
2883void MacroAssembler::LoadElementsKindFromMap(Register result, Register map) {
2884  ASM_CODE_COMMENT(this);
2885  // Load the map's "bit field 2".
2886  Ldrb(result, FieldMemOperand(map, Map::kBitField2Offset));
2887  // Retrieve elements_kind from bit field 2.
2888  DecodeField<Map::Bits2::ElementsKindBits>(result);
2889}
2890
2891void MacroAssembler::CompareRoot(const Register& obj, RootIndex index) {
2892  ASM_CODE_COMMENT(this);
2893  UseScratchRegisterScope temps(this);
2894  Register temp = temps.AcquireX();
2895  DCHECK(!AreAliased(obj, temp));
2896  LoadRoot(temp, index);
2897  CmpTagged(obj, temp);
2898}
2899
2900void MacroAssembler::JumpIfRoot(const Register& obj, RootIndex index,
2901                                Label* if_equal) {
2902  CompareRoot(obj, index);
2903  B(eq, if_equal);
2904}
2905
2906void MacroAssembler::JumpIfNotRoot(const Register& obj, RootIndex index,
2907                                   Label* if_not_equal) {
2908  CompareRoot(obj, index);
2909  B(ne, if_not_equal);
2910}
2911
2912void MacroAssembler::JumpIfIsInRange(const Register& value,
2913                                     unsigned lower_limit,
2914                                     unsigned higher_limit,
2915                                     Label* on_in_range) {
2916  ASM_CODE_COMMENT(this);
2917  if (lower_limit != 0) {
2918    UseScratchRegisterScope temps(this);
2919    Register scratch = temps.AcquireW();
2920    Sub(scratch, value, Operand(lower_limit));
2921    CompareAndBranch(scratch, Operand(higher_limit - lower_limit), ls,
2922                     on_in_range);
2923  } else {
2924    CompareAndBranch(value, Operand(higher_limit - lower_limit), ls,
2925                     on_in_range);
2926  }
2927}
2928
2929void TurboAssembler::LoadTaggedPointerField(const Register& destination,
2930                                            const MemOperand& field_operand) {
2931  if (COMPRESS_POINTERS_BOOL) {
2932    DecompressTaggedPointer(destination, field_operand);
2933  } else {
2934    Ldr(destination, field_operand);
2935  }
2936}
2937
2938void TurboAssembler::LoadAnyTaggedField(const Register& destination,
2939                                        const MemOperand& field_operand) {
2940  if (COMPRESS_POINTERS_BOOL) {
2941    DecompressAnyTagged(destination, field_operand);
2942  } else {
2943    Ldr(destination, field_operand);
2944  }
2945}
2946
2947void TurboAssembler::LoadTaggedSignedField(const Register& destination,
2948                                           const MemOperand& field_operand) {
2949  if (COMPRESS_POINTERS_BOOL) {
2950    DecompressTaggedSigned(destination, field_operand);
2951  } else {
2952    Ldr(destination, field_operand);
2953  }
2954}
2955
2956void TurboAssembler::SmiUntagField(Register dst, const MemOperand& src) {
2957  SmiUntag(dst, src);
2958}
2959
2960void TurboAssembler::StoreTaggedField(const Register& value,
2961                                      const MemOperand& dst_field_operand) {
2962  if (COMPRESS_POINTERS_BOOL) {
2963    Str(value.W(), dst_field_operand);
2964  } else {
2965    Str(value, dst_field_operand);
2966  }
2967}
2968
2969void TurboAssembler::AtomicStoreTaggedField(const Register& value,
2970                                            const Register& dst_base,
2971                                            const Register& dst_index,
2972                                            const Register& temp) {
2973  Add(temp, dst_base, dst_index);
2974  if (COMPRESS_POINTERS_BOOL) {
2975    Stlr(value.W(), temp);
2976  } else {
2977    Stlr(value, temp);
2978  }
2979}
2980
2981void TurboAssembler::DecompressTaggedSigned(const Register& destination,
2982                                            const MemOperand& field_operand) {
2983  ASM_CODE_COMMENT(this);
2984  Ldr(destination.W(), field_operand);
2985  if (FLAG_debug_code) {
2986    // Corrupt the top 32 bits. Made up of 16 fixed bits and 16 pc offset bits.
2987    Add(destination, destination,
2988        ((kDebugZapValue << 16) | (pc_offset() & 0xffff)) << 32);
2989  }
2990}
2991
2992void TurboAssembler::DecompressTaggedPointer(const Register& destination,
2993                                             const MemOperand& field_operand) {
2994  ASM_CODE_COMMENT(this);
2995  Ldr(destination.W(), field_operand);
2996  Add(destination, kPtrComprCageBaseRegister, destination);
2997}
2998
2999void TurboAssembler::DecompressTaggedPointer(const Register& destination,
3000                                             const Register& source) {
3001  ASM_CODE_COMMENT(this);
3002  Add(destination, kPtrComprCageBaseRegister, Operand(source, UXTW));
3003}
3004
3005void TurboAssembler::DecompressAnyTagged(const Register& destination,
3006                                         const MemOperand& field_operand) {
3007  ASM_CODE_COMMENT(this);
3008  Ldr(destination.W(), field_operand);
3009  Add(destination, kPtrComprCageBaseRegister, destination);
3010}
3011
3012void TurboAssembler::AtomicDecompressTaggedSigned(const Register& destination,
3013                                                  const Register& base,
3014                                                  const Register& index,
3015                                                  const Register& temp) {
3016  ASM_CODE_COMMENT(this);
3017  Add(temp, base, index);
3018  Ldar(destination.W(), temp);
3019  if (FLAG_debug_code) {
3020    // Corrupt the top 32 bits. Made up of 16 fixed bits and 16 pc offset bits.
3021    Add(destination, destination,
3022        ((kDebugZapValue << 16) | (pc_offset() & 0xffff)) << 32);
3023  }
3024}
3025
3026void TurboAssembler::AtomicDecompressTaggedPointer(const Register& destination,
3027                                                   const Register& base,
3028                                                   const Register& index,
3029                                                   const Register& temp) {
3030  ASM_CODE_COMMENT(this);
3031  Add(temp, base, index);
3032  Ldar(destination.W(), temp);
3033  Add(destination, kPtrComprCageBaseRegister, destination);
3034}
3035
3036void TurboAssembler::AtomicDecompressAnyTagged(const Register& destination,
3037                                               const Register& base,
3038                                               const Register& index,
3039                                               const Register& temp) {
3040  ASM_CODE_COMMENT(this);
3041  Add(temp, base, index);
3042  Ldar(destination.W(), temp);
3043  Add(destination, kPtrComprCageBaseRegister, destination);
3044}
3045
3046void TurboAssembler::CheckPageFlag(const Register& object, int mask,
3047                                   Condition cc, Label* condition_met) {
3048  ASM_CODE_COMMENT(this);
3049  UseScratchRegisterScope temps(this);
3050  Register scratch = temps.AcquireX();
3051  And(scratch, object, ~kPageAlignmentMask);
3052  Ldr(scratch, MemOperand(scratch, BasicMemoryChunk::kFlagsOffset));
3053  if (cc == eq) {
3054    TestAndBranchIfAnySet(scratch, mask, condition_met);
3055  } else {
3056    DCHECK_EQ(cc, ne);
3057    TestAndBranchIfAllClear(scratch, mask, condition_met);
3058  }
3059}
3060
3061void MacroAssembler::RecordWriteField(Register object, int offset,
3062                                      Register value,
3063                                      LinkRegisterStatus lr_status,
3064                                      SaveFPRegsMode save_fp,
3065                                      RememberedSetAction remembered_set_action,
3066                                      SmiCheck smi_check) {
3067  ASM_CODE_COMMENT(this);
3068  DCHECK(!AreAliased(object, value));
3069  // First, check if a write barrier is even needed. The tests below
3070  // catch stores of Smis.
3071  Label done;
3072
3073  // Skip the barrier if writing a smi.
3074  if (smi_check == SmiCheck::kInline) {
3075    JumpIfSmi(value, &done);
3076  }
3077
3078  // Although the object register is tagged, the offset is relative to the start
3079  // of the object, so offset must be a multiple of kTaggedSize.
3080  DCHECK(IsAligned(offset, kTaggedSize));
3081
3082  if (FLAG_debug_code) {
3083    ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
3084    Label ok;
3085    UseScratchRegisterScope temps(this);
3086    Register scratch = temps.AcquireX();
3087    DCHECK(!AreAliased(object, value, scratch));
3088    Add(scratch, object, offset - kHeapObjectTag);
3089    Tst(scratch, kTaggedSize - 1);
3090    B(eq, &ok);
3091    Abort(AbortReason::kUnalignedCellInWriteBarrier);
3092    Bind(&ok);
3093  }
3094
3095  RecordWrite(object, Operand(offset - kHeapObjectTag), value, lr_status,
3096              save_fp, remembered_set_action, SmiCheck::kOmit);
3097
3098  Bind(&done);
3099}
3100
3101void TurboAssembler::EncodeSandboxedPointer(const Register& value) {
3102  ASM_CODE_COMMENT(this);
3103#ifdef V8_SANDBOXED_POINTERS
3104  Sub(value, value, kPtrComprCageBaseRegister);
3105  Mov(value, Operand(value, LSL, kSandboxedPointerShift));
3106#else
3107  UNREACHABLE();
3108#endif
3109}
3110
3111void TurboAssembler::DecodeSandboxedPointer(const Register& value) {
3112  ASM_CODE_COMMENT(this);
3113#ifdef V8_SANDBOXED_POINTERS
3114  Add(value, kPtrComprCageBaseRegister,
3115      Operand(value, LSR, kSandboxedPointerShift));
3116#else
3117  UNREACHABLE();
3118#endif
3119}
3120
3121void TurboAssembler::LoadSandboxedPointerField(
3122    const Register& destination, const MemOperand& field_operand) {
3123  ASM_CODE_COMMENT(this);
3124  Ldr(destination, field_operand);
3125  DecodeSandboxedPointer(destination);
3126}
3127
3128void TurboAssembler::StoreSandboxedPointerField(
3129    const Register& value, const MemOperand& dst_field_operand) {
3130  ASM_CODE_COMMENT(this);
3131  UseScratchRegisterScope temps(this);
3132  Register scratch = temps.AcquireX();
3133  Mov(scratch, value);
3134  EncodeSandboxedPointer(scratch);
3135  Str(scratch, dst_field_operand);
3136}
3137
3138void TurboAssembler::LoadExternalPointerField(Register destination,
3139                                              MemOperand field_operand,
3140                                              ExternalPointerTag tag,
3141                                              Register isolate_root) {
3142  DCHECK(!AreAliased(destination, isolate_root));
3143  ASM_CODE_COMMENT(this);
3144#ifdef V8_SANDBOXED_EXTERNAL_POINTERS
3145  DCHECK_NE(kExternalPointerNullTag, tag);
3146  UseScratchRegisterScope temps(this);
3147  Register external_table = temps.AcquireX();
3148  if (isolate_root == no_reg) {
3149    DCHECK(root_array_available_);
3150    isolate_root = kRootRegister;
3151  }
3152  Ldr(external_table,
3153      MemOperand(isolate_root,
3154                 IsolateData::external_pointer_table_offset() +
3155                     Internals::kExternalPointerTableBufferOffset));
3156  Ldr(destination.W(), field_operand);
3157  // MemOperand doesn't support LSR currently (only LSL), so here we do the
3158  // offset computation separately first.
3159  STATIC_ASSERT(kExternalPointerIndexShift > kSystemPointerSizeLog2);
3160  int shift_amount = kExternalPointerIndexShift - kSystemPointerSizeLog2;
3161  Mov(destination, Operand(destination, LSR, shift_amount));
3162  Ldr(destination, MemOperand(external_table, destination));
3163  And(destination, destination, Immediate(~tag));
3164#else
3165  Ldr(destination, field_operand);
3166#endif  // V8_SANDBOXED_EXTERNAL_POINTERS
3167}
3168
3169void TurboAssembler::MaybeSaveRegisters(RegList registers) {
3170  if (registers.is_empty()) return;
3171  ASM_CODE_COMMENT(this);
3172  CPURegList regs(kXRegSizeInBits, registers);
3173  // If we were saving LR, we might need to sign it.
3174  DCHECK(!regs.IncludesAliasOf(lr));
3175  regs.Align();
3176  PushCPURegList(regs);
3177}
3178
3179void TurboAssembler::MaybeRestoreRegisters(RegList registers) {
3180  if (registers.is_empty()) return;
3181  ASM_CODE_COMMENT(this);
3182  CPURegList regs(kXRegSizeInBits, registers);
3183  // If we were saving LR, we might need to sign it.
3184  DCHECK(!regs.IncludesAliasOf(lr));
3185  regs.Align();
3186  PopCPURegList(regs);
3187}
3188
3189void TurboAssembler::CallEphemeronKeyBarrier(Register object, Operand offset,
3190                                             SaveFPRegsMode fp_mode) {
3191  ASM_CODE_COMMENT(this);
3192  RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
3193  MaybeSaveRegisters(registers);
3194
3195  MoveObjectAndSlot(WriteBarrierDescriptor::ObjectRegister(),
3196                    WriteBarrierDescriptor::SlotAddressRegister(), object,
3197                    offset);
3198
3199  Call(isolate()->builtins()->code_handle(
3200           Builtins::GetEphemeronKeyBarrierStub(fp_mode)),
3201       RelocInfo::CODE_TARGET);
3202  MaybeRestoreRegisters(registers);
3203}
3204
3205void TurboAssembler::CallRecordWriteStubSaveRegisters(
3206    Register object, Operand offset, RememberedSetAction remembered_set_action,
3207    SaveFPRegsMode fp_mode, StubCallMode mode) {
3208  ASM_CODE_COMMENT(this);
3209  RegList registers = WriteBarrierDescriptor::ComputeSavedRegisters(object);
3210  MaybeSaveRegisters(registers);
3211
3212  Register object_parameter = WriteBarrierDescriptor::ObjectRegister();
3213  Register slot_address_parameter =
3214      WriteBarrierDescriptor::SlotAddressRegister();
3215  MoveObjectAndSlot(object_parameter, slot_address_parameter, object, offset);
3216
3217  CallRecordWriteStub(object_parameter, slot_address_parameter,
3218                      remembered_set_action, fp_mode, mode);
3219
3220  MaybeRestoreRegisters(registers);
3221}
3222
3223void TurboAssembler::CallRecordWriteStub(
3224    Register object, Register slot_address,
3225    RememberedSetAction remembered_set_action, SaveFPRegsMode fp_mode,
3226    StubCallMode mode) {
3227  ASM_CODE_COMMENT(this);
3228  DCHECK_EQ(WriteBarrierDescriptor::ObjectRegister(), object);
3229  DCHECK_EQ(WriteBarrierDescriptor::SlotAddressRegister(), slot_address);
3230#if V8_ENABLE_WEBASSEMBLY
3231  if (mode == StubCallMode::kCallWasmRuntimeStub) {
3232    auto wasm_target =
3233        wasm::WasmCode::GetRecordWriteStub(remembered_set_action, fp_mode);
3234    Call(wasm_target, RelocInfo::WASM_STUB_CALL);
3235#else
3236  if (false) {
3237#endif
3238  } else {
3239    Builtin builtin =
3240        Builtins::GetRecordWriteStub(remembered_set_action, fp_mode);
3241    if (options().inline_offheap_trampolines) {
3242      CallBuiltin(builtin);
3243    } else {
3244      Handle<CodeT> code_target = isolate()->builtins()->code_handle(builtin);
3245      Call(code_target, RelocInfo::CODE_TARGET);
3246    }
3247  }
3248}
3249
3250void TurboAssembler::MoveObjectAndSlot(Register dst_object, Register dst_slot,
3251                                       Register object, Operand offset) {
3252  ASM_CODE_COMMENT(this);
3253  DCHECK_NE(dst_object, dst_slot);
3254  // If `offset` is a register, it cannot overlap with `object`.
3255  DCHECK_IMPLIES(!offset.IsImmediate(), offset.reg() != object);
3256
3257  // If the slot register does not overlap with the object register, we can
3258  // overwrite it.
3259  if (dst_slot != object) {
3260    Add(dst_slot, object, offset);
3261    Mov(dst_object, object);
3262    return;
3263  }
3264
3265  DCHECK_EQ(dst_slot, object);
3266
3267  // If the destination object register does not overlap with the offset
3268  // register, we can overwrite it.
3269  if (offset.IsImmediate() || (offset.reg() != dst_object)) {
3270    Mov(dst_object, dst_slot);
3271    Add(dst_slot, dst_slot, offset);
3272    return;
3273  }
3274
3275  DCHECK_EQ(dst_object, offset.reg());
3276
3277  // We only have `dst_slot` and `dst_object` left as distinct registers so we
3278  // have to swap them. We write this as a add+sub sequence to avoid using a
3279  // scratch register.
3280  Add(dst_slot, dst_slot, dst_object);
3281  Sub(dst_object, dst_slot, dst_object);
3282}
3283
3284// If lr_status is kLRHasBeenSaved, lr will be clobbered.
3285//
3286// The register 'object' contains a heap object pointer. The heap object tag is
3287// shifted away.
3288void MacroAssembler::RecordWrite(Register object, Operand offset,
3289                                 Register value, LinkRegisterStatus lr_status,
3290                                 SaveFPRegsMode fp_mode,
3291                                 RememberedSetAction remembered_set_action,
3292                                 SmiCheck smi_check) {
3293  ASM_CODE_COMMENT(this);
3294  ASM_LOCATION_IN_ASSEMBLER("MacroAssembler::RecordWrite");
3295  DCHECK(!AreAliased(object, value));
3296
3297  if (FLAG_debug_code) {
3298    ASM_CODE_COMMENT_STRING(this, "Verify slot_address");
3299    UseScratchRegisterScope temps(this);
3300    Register temp = temps.AcquireX();
3301    DCHECK(!AreAliased(object, value, temp));
3302    Add(temp, object, offset);
3303    LoadTaggedPointerField(temp, MemOperand(temp));
3304    Cmp(temp, value);
3305    Check(eq, AbortReason::kWrongAddressOrValuePassedToRecordWrite);
3306  }
3307
3308  if ((remembered_set_action == RememberedSetAction::kOmit &&
3309       !FLAG_incremental_marking) ||
3310      FLAG_disable_write_barriers) {
3311    return;
3312  }
3313
3314  // First, check if a write barrier is even needed. The tests below
3315  // catch stores of smis and stores into the young generation.
3316  Label done;
3317
3318  if (smi_check == SmiCheck::kInline) {
3319    DCHECK_EQ(0, kSmiTag);
3320    JumpIfSmi(value, &done);
3321  }
3322  CheckPageFlag(value, MemoryChunk::kPointersToHereAreInterestingMask, ne,
3323                &done);
3324
3325  CheckPageFlag(object, MemoryChunk::kPointersFromHereAreInterestingMask, ne,
3326                &done);
3327
3328  // Record the actual write.
3329  if (lr_status == kLRHasNotBeenSaved) {
3330    Push<TurboAssembler::kSignLR>(padreg, lr);
3331  }
3332  Register slot_address = WriteBarrierDescriptor::SlotAddressRegister();
3333  DCHECK(!AreAliased(object, slot_address, value));
3334  // TODO(cbruni): Turn offset into int.
3335  DCHECK(offset.IsImmediate());
3336  Add(slot_address, object, offset);
3337  CallRecordWriteStub(object, slot_address, remembered_set_action, fp_mode);
3338  if (lr_status == kLRHasNotBeenSaved) {
3339    Pop<TurboAssembler::kAuthLR>(lr, padreg);
3340  }
3341  if (FLAG_debug_code) Mov(slot_address, Operand(kZapValue));
3342
3343  Bind(&done);
3344}
3345
3346void TurboAssembler::Assert(Condition cond, AbortReason reason) {
3347  if (FLAG_debug_code) {
3348    Check(cond, reason);
3349  }
3350}
3351
3352void TurboAssembler::AssertUnreachable(AbortReason reason) {
3353  if (FLAG_debug_code) Abort(reason);
3354}
3355
3356void TurboAssembler::Check(Condition cond, AbortReason reason) {
3357  Label ok;
3358  B(cond, &ok);
3359  Abort(reason);
3360  // Will not return here.
3361  Bind(&ok);
3362}
3363
3364void TurboAssembler::Trap() { Brk(0); }
3365void TurboAssembler::DebugBreak() { Debug("DebugBreak", 0, BREAK); }
3366
3367void TurboAssembler::Abort(AbortReason reason) {
3368  ASM_CODE_COMMENT(this);
3369  if (FLAG_code_comments) {
3370    RecordComment("Abort message: ");
3371    RecordComment(GetAbortReason(reason));
3372  }
3373
3374  // Avoid emitting call to builtin if requested.
3375  if (trap_on_abort()) {
3376    Brk(0);
3377    return;
3378  }
3379
3380  // We need some scratch registers for the MacroAssembler, so make sure we have
3381  // some. This is safe here because Abort never returns.
3382  uint64_t old_tmp_list = TmpList()->bits();
3383  TmpList()->Combine(MacroAssembler::DefaultTmpList());
3384
3385  if (should_abort_hard()) {
3386    // We don't care if we constructed a frame. Just pretend we did.
3387    FrameScope assume_frame(this, StackFrame::NO_FRAME_TYPE);
3388    Mov(w0, static_cast<int>(reason));
3389    Call(ExternalReference::abort_with_reason());
3390    return;
3391  }
3392
3393  // Avoid infinite recursion; Push contains some assertions that use Abort.
3394  HardAbortScope hard_aborts(this);
3395
3396  Mov(x1, Smi::FromInt(static_cast<int>(reason)));
3397
3398  if (!has_frame_) {
3399    // We don't actually want to generate a pile of code for this, so just
3400    // claim there is a stack frame, without generating one.
3401    FrameScope scope(this, StackFrame::NO_FRAME_TYPE);
3402    Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
3403  } else {
3404    Call(BUILTIN_CODE(isolate(), Abort), RelocInfo::CODE_TARGET);
3405  }
3406
3407  TmpList()->set_bits(old_tmp_list);
3408}
3409
3410void MacroAssembler::LoadNativeContextSlot(Register dst, int index) {
3411  LoadMap(dst, cp);
3412  LoadTaggedPointerField(
3413      dst, FieldMemOperand(
3414               dst, Map::kConstructorOrBackPointerOrNativeContextOffset));
3415  LoadTaggedPointerField(dst, MemOperand(dst, Context::SlotOffset(index)));
3416}
3417
3418// This is the main Printf implementation. All other Printf variants call
3419// PrintfNoPreserve after setting up one or more PreserveRegisterScopes.
3420void TurboAssembler::PrintfNoPreserve(const char* format,
3421                                      const CPURegister& arg0,
3422                                      const CPURegister& arg1,
3423                                      const CPURegister& arg2,
3424                                      const CPURegister& arg3) {
3425  ASM_CODE_COMMENT(this);
3426  // We cannot handle a caller-saved stack pointer. It doesn't make much sense
3427  // in most cases anyway, so this restriction shouldn't be too serious.
3428  DCHECK(!kCallerSaved.IncludesAliasOf(sp));
3429
3430  // The provided arguments, and their proper procedure-call standard registers.
3431  CPURegister args[kPrintfMaxArgCount] = {arg0, arg1, arg2, arg3};
3432  CPURegister pcs[kPrintfMaxArgCount] = {NoReg, NoReg, NoReg, NoReg};
3433
3434  int arg_count = kPrintfMaxArgCount;
3435
3436  // The PCS varargs registers for printf. Note that x0 is used for the printf
3437  // format string.
3438  static const CPURegList kPCSVarargs =
3439      CPURegList(CPURegister::kRegister, kXRegSizeInBits, 1, arg_count);
3440  static const CPURegList kPCSVarargsFP =
3441      CPURegList(CPURegister::kVRegister, kDRegSizeInBits, 0, arg_count - 1);
3442
3443  // We can use caller-saved registers as scratch values, except for the
3444  // arguments and the PCS registers where they might need to go.
3445  CPURegList tmp_list = kCallerSaved;
3446  tmp_list.Remove(x0);  // Used to pass the format string.
3447  tmp_list.Remove(kPCSVarargs);
3448  tmp_list.Remove(arg0, arg1, arg2, arg3);
3449
3450  CPURegList fp_tmp_list = kCallerSavedV;
3451  fp_tmp_list.Remove(kPCSVarargsFP);
3452  fp_tmp_list.Remove(arg0, arg1, arg2, arg3);
3453
3454  // Override the TurboAssembler's scratch register list. The lists will be
3455  // reset automatically at the end of the UseScratchRegisterScope.
3456  UseScratchRegisterScope temps(this);
3457  TmpList()->set_bits(tmp_list.bits());
3458  FPTmpList()->set_bits(fp_tmp_list.bits());
3459
3460  // Copies of the printf vararg registers that we can pop from.
3461  CPURegList pcs_varargs = kPCSVarargs;
3462#ifndef V8_OS_WIN
3463  CPURegList pcs_varargs_fp = kPCSVarargsFP;
3464#endif
3465
3466  // Place the arguments. There are lots of clever tricks and optimizations we
3467  // could use here, but Printf is a debug tool so instead we just try to keep
3468  // it simple: Move each input that isn't already in the right place to a
3469  // scratch register, then move everything back.
3470  for (unsigned i = 0; i < kPrintfMaxArgCount; i++) {
3471    // Work out the proper PCS register for this argument.
3472    if (args[i].IsRegister()) {
3473      pcs[i] = pcs_varargs.PopLowestIndex().X();
3474      // We might only need a W register here. We need to know the size of the
3475      // argument so we can properly encode it for the simulator call.
3476      if (args[i].Is32Bits()) pcs[i] = pcs[i].W();
3477    } else if (args[i].IsVRegister()) {
3478      // In C, floats are always cast to doubles for varargs calls.
3479#ifdef V8_OS_WIN
3480      // In case of variadic functions SIMD and Floating-point registers
3481      // aren't used. The general x0-x7 should be used instead.
3482      // https://docs.microsoft.com/en-us/cpp/build/arm64-windows-abi-conventions
3483      pcs[i] = pcs_varargs.PopLowestIndex().X();
3484#else
3485      pcs[i] = pcs_varargs_fp.PopLowestIndex().D();
3486#endif
3487    } else {
3488      DCHECK(args[i].IsNone());
3489      arg_count = i;
3490      break;
3491    }
3492
3493    // If the argument is already in the right place, leave it where it is.
3494    if (args[i].Aliases(pcs[i])) continue;
3495
3496    // Otherwise, if the argument is in a PCS argument register, allocate an
3497    // appropriate scratch register and then move it out of the way.
3498    if (kPCSVarargs.IncludesAliasOf(args[i]) ||
3499        kPCSVarargsFP.IncludesAliasOf(args[i])) {
3500      if (args[i].IsRegister()) {
3501        Register old_arg = args[i].Reg();
3502        Register new_arg = temps.AcquireSameSizeAs(old_arg);
3503        Mov(new_arg, old_arg);
3504        args[i] = new_arg;
3505      } else {
3506        VRegister old_arg = args[i].VReg();
3507        VRegister new_arg = temps.AcquireSameSizeAs(old_arg);
3508        Fmov(new_arg, old_arg);
3509        args[i] = new_arg;
3510      }
3511    }
3512  }
3513
3514  // Do a second pass to move values into their final positions and perform any
3515  // conversions that may be required.
3516  for (int i = 0; i < arg_count; i++) {
3517#ifdef V8_OS_WIN
3518    if (args[i].IsVRegister()) {
3519      if (pcs[i].SizeInBytes() != args[i].SizeInBytes()) {
3520        // If the argument is half- or single-precision
3521        // converts to double-precision before that is
3522        // moved into the one of X scratch register.
3523        VRegister temp0 = temps.AcquireD();
3524        Fcvt(temp0.VReg(), args[i].VReg());
3525        Fmov(pcs[i].Reg(), temp0);
3526      } else {
3527        Fmov(pcs[i].Reg(), args[i].VReg());
3528      }
3529    } else {
3530      Mov(pcs[i].Reg(), args[i].Reg(), kDiscardForSameWReg);
3531    }
3532#else
3533    DCHECK(pcs[i].type() == args[i].type());
3534    if (pcs[i].IsRegister()) {
3535      Mov(pcs[i].Reg(), args[i].Reg(), kDiscardForSameWReg);
3536    } else {
3537      DCHECK(pcs[i].IsVRegister());
3538      if (pcs[i].SizeInBytes() == args[i].SizeInBytes()) {
3539        Fmov(pcs[i].VReg(), args[i].VReg());
3540      } else {
3541        Fcvt(pcs[i].VReg(), args[i].VReg());
3542      }
3543    }
3544#endif
3545  }
3546
3547  // Load the format string into x0, as per the procedure-call standard.
3548  //
3549  // To make the code as portable as possible, the format string is encoded
3550  // directly in the instruction stream. It might be cleaner to encode it in a
3551  // literal pool, but since Printf is usually used for debugging, it is
3552  // beneficial for it to be minimally dependent on other features.
3553  Label format_address;
3554  Adr(x0, &format_address);
3555
3556  // Emit the format string directly in the instruction stream.
3557  {
3558    BlockPoolsScope scope(this);
3559    Label after_data;
3560    B(&after_data);
3561    Bind(&format_address);
3562    EmitStringData(format);
3563    Unreachable();
3564    Bind(&after_data);
3565  }
3566
3567  CallPrintf(arg_count, pcs);
3568}
3569
3570void TurboAssembler::CallPrintf(int arg_count, const CPURegister* args) {
3571  ASM_CODE_COMMENT(this);
3572  // A call to printf needs special handling for the simulator, since the system
3573  // printf function will use a different instruction set and the procedure-call
3574  // standard will not be compatible.
3575  if (options().enable_simulator_code) {
3576    InstructionAccurateScope scope(this, kPrintfLength / kInstrSize);
3577    hlt(kImmExceptionIsPrintf);
3578    dc32(arg_count);  // kPrintfArgCountOffset
3579
3580    // Determine the argument pattern.
3581    uint32_t arg_pattern_list = 0;
3582    for (int i = 0; i < arg_count; i++) {
3583      uint32_t arg_pattern;
3584      if (args[i].IsRegister()) {
3585        arg_pattern = args[i].Is32Bits() ? kPrintfArgW : kPrintfArgX;
3586      } else {
3587        DCHECK(args[i].Is64Bits());
3588        arg_pattern = kPrintfArgD;
3589      }
3590      DCHECK(arg_pattern < (1 << kPrintfArgPatternBits));
3591      arg_pattern_list |= (arg_pattern << (kPrintfArgPatternBits * i));
3592    }
3593    dc32(arg_pattern_list);  // kPrintfArgPatternListOffset
3594    return;
3595  }
3596
3597  Call(ExternalReference::printf_function());
3598}
3599
3600void TurboAssembler::Printf(const char* format, CPURegister arg0,
3601                            CPURegister arg1, CPURegister arg2,
3602                            CPURegister arg3) {
3603  ASM_CODE_COMMENT(this);
3604  // Printf is expected to preserve all registers, so make sure that none are
3605  // available as scratch registers until we've preserved them.
3606  uint64_t old_tmp_list = TmpList()->bits();
3607  uint64_t old_fp_tmp_list = FPTmpList()->bits();
3608  TmpList()->set_bits(0);
3609  FPTmpList()->set_bits(0);
3610
3611  CPURegList saved_registers = kCallerSaved;
3612  saved_registers.Align();
3613
3614  // Preserve all caller-saved registers as well as NZCV.
3615  // PushCPURegList asserts that the size of each list is a multiple of 16
3616  // bytes.
3617  PushCPURegList(saved_registers);
3618  PushCPURegList(kCallerSavedV);
3619
3620  // We can use caller-saved registers as scratch values (except for argN).
3621  CPURegList tmp_list = saved_registers;
3622  CPURegList fp_tmp_list = kCallerSavedV;
3623  tmp_list.Remove(arg0, arg1, arg2, arg3);
3624  fp_tmp_list.Remove(arg0, arg1, arg2, arg3);
3625  TmpList()->set_bits(tmp_list.bits());
3626  FPTmpList()->set_bits(fp_tmp_list.bits());
3627
3628  {
3629    UseScratchRegisterScope temps(this);
3630    // If any of the arguments are the current stack pointer, allocate a new
3631    // register for them, and adjust the value to compensate for pushing the
3632    // caller-saved registers.
3633    bool arg0_sp = arg0.is_valid() && sp.Aliases(arg0);
3634    bool arg1_sp = arg1.is_valid() && sp.Aliases(arg1);
3635    bool arg2_sp = arg2.is_valid() && sp.Aliases(arg2);
3636    bool arg3_sp = arg3.is_valid() && sp.Aliases(arg3);
3637    if (arg0_sp || arg1_sp || arg2_sp || arg3_sp) {
3638      // Allocate a register to hold the original stack pointer value, to pass
3639      // to PrintfNoPreserve as an argument.
3640      Register arg_sp = temps.AcquireX();
3641      Add(arg_sp, sp,
3642          saved_registers.TotalSizeInBytes() +
3643              kCallerSavedV.TotalSizeInBytes());
3644      if (arg0_sp) arg0 = Register::Create(arg_sp.code(), arg0.SizeInBits());
3645      if (arg1_sp) arg1 = Register::Create(arg_sp.code(), arg1.SizeInBits());
3646      if (arg2_sp) arg2 = Register::Create(arg_sp.code(), arg2.SizeInBits());
3647      if (arg3_sp) arg3 = Register::Create(arg_sp.code(), arg3.SizeInBits());
3648    }
3649
3650    // Preserve NZCV.
3651    {
3652      UseScratchRegisterScope temps(this);
3653      Register tmp = temps.AcquireX();
3654      Mrs(tmp, NZCV);
3655      Push(tmp, xzr);
3656    }
3657
3658    PrintfNoPreserve(format, arg0, arg1, arg2, arg3);
3659
3660    // Restore NZCV.
3661    {
3662      UseScratchRegisterScope temps(this);
3663      Register tmp = temps.AcquireX();
3664      Pop(xzr, tmp);
3665      Msr(NZCV, tmp);
3666    }
3667  }
3668
3669  PopCPURegList(kCallerSavedV);
3670  PopCPURegList(saved_registers);
3671
3672  TmpList()->set_bits(old_tmp_list);
3673  FPTmpList()->set_bits(old_fp_tmp_list);
3674}
3675
3676UseScratchRegisterScope::~UseScratchRegisterScope() {
3677  available_->set_bits(old_available_);
3678  availablefp_->set_bits(old_availablefp_);
3679}
3680
3681Register UseScratchRegisterScope::AcquireSameSizeAs(const Register& reg) {
3682  int code = AcquireNextAvailable(available_).code();
3683  return Register::Create(code, reg.SizeInBits());
3684}
3685
3686VRegister UseScratchRegisterScope::AcquireSameSizeAs(const VRegister& reg) {
3687  int code = AcquireNextAvailable(availablefp_).code();
3688  return VRegister::Create(code, reg.SizeInBits());
3689}
3690
3691CPURegister UseScratchRegisterScope::AcquireNextAvailable(
3692    CPURegList* available) {
3693  CHECK(!available->IsEmpty());
3694  CPURegister result = available->PopLowestIndex();
3695  DCHECK(!AreAliased(result, xzr, sp));
3696  return result;
3697}
3698
3699void TurboAssembler::ComputeCodeStartAddress(const Register& rd) {
3700  // We can use adr to load a pc relative location.
3701  adr(rd, -pc_offset());
3702}
3703
3704void TurboAssembler::RestoreFPAndLR() {
3705  static_assert(StandardFrameConstants::kCallerFPOffset + kSystemPointerSize ==
3706                    StandardFrameConstants::kCallerPCOffset,
3707                "Offsets must be consecutive for ldp!");
3708#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
3709  // Make sure we can use x16 and x17.
3710  UseScratchRegisterScope temps(this);
3711  temps.Exclude(x16, x17);
3712  // We can load the return address directly into x17.
3713  Add(x16, fp, StandardFrameConstants::kCallerSPOffset);
3714  Ldp(fp, x17, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
3715  Autib1716();
3716  Mov(lr, x17);
3717#else
3718  Ldp(fp, lr, MemOperand(fp, StandardFrameConstants::kCallerFPOffset));
3719#endif
3720}
3721
3722#if V8_ENABLE_WEBASSEMBLY
3723void TurboAssembler::StoreReturnAddressInWasmExitFrame(Label* return_location) {
3724  UseScratchRegisterScope temps(this);
3725  temps.Exclude(x16, x17);
3726  Adr(x17, return_location);
3727#ifdef V8_ENABLE_CONTROL_FLOW_INTEGRITY
3728  Add(x16, fp, WasmExitFrameConstants::kCallingPCOffset + kSystemPointerSize);
3729  Pacib1716();
3730#endif
3731  Str(x17, MemOperand(fp, WasmExitFrameConstants::kCallingPCOffset));
3732}
3733#endif  // V8_ENABLE_WEBASSEMBLY
3734
3735void TurboAssembler::PopcntHelper(Register dst, Register src) {
3736  UseScratchRegisterScope temps(this);
3737  VRegister scratch = temps.AcquireV(kFormat8B);
3738  VRegister tmp = src.Is32Bits() ? scratch.S() : scratch.D();
3739  Fmov(tmp, src);
3740  Cnt(scratch, scratch);
3741  Addv(scratch.B(), scratch);
3742  Fmov(dst, tmp);
3743}
3744
3745void TurboAssembler::I64x2BitMask(Register dst, VRegister src) {
3746  ASM_CODE_COMMENT(this);
3747  UseScratchRegisterScope scope(this);
3748  VRegister tmp1 = scope.AcquireV(kFormat2D);
3749  Register tmp2 = scope.AcquireX();
3750  Ushr(tmp1.V2D(), src.V2D(), 63);
3751  Mov(dst.X(), tmp1.D(), 0);
3752  Mov(tmp2.X(), tmp1.D(), 1);
3753  Add(dst.W(), dst.W(), Operand(tmp2.W(), LSL, 1));
3754}
3755
3756void TurboAssembler::I64x2AllTrue(Register dst, VRegister src) {
3757  ASM_CODE_COMMENT(this);
3758  UseScratchRegisterScope scope(this);
3759  VRegister tmp = scope.AcquireV(kFormat2D);
3760  Cmeq(tmp.V2D(), src.V2D(), 0);
3761  Addp(tmp.D(), tmp);
3762  Fcmp(tmp.D(), tmp.D());
3763  Cset(dst, eq);
3764}
3765
3766}  // namespace internal
3767}  // namespace v8
3768
3769#endif  // V8_TARGET_ARCH_ARM64
3770