• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
38 namespace v8 {
39 namespace internal {
40 
DefaultTmpList()41 CPURegList TurboAssembler::DefaultTmpList() { return CPURegList(ip0, ip1); }
42 
DefaultFPTmpList()43 CPURegList TurboAssembler::DefaultFPTmpList() {
44   return CPURegList(fp_scratch1, fp_scratch2);
45 }
46 
47 namespace {
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
52 constexpr int kStackSavedSavedFPSizeInBits = kQRegSizeInBits;
53 #else
54 constexpr int kStackSavedSavedFPSizeInBits = kDRegSizeInBits;
55 #endif  // V8_ENABLE_WEBASSEMBLY
56 
57 }  // namespace
58 
PushCPURegList(CPURegList registers)59 void 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 
PopCPURegList(CPURegList registers)79 void 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 
RequiredStackSizeForCallerSaved(SaveFPRegsMode fp_mode,Register exclusion) const99 int 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 
PushCallerSaved(SaveFPRegsMode fp_mode,Register exclusion)115 int 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 
PopCallerSaved(SaveFPRegsMode fp_mode,Register exclusion)135 int 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 
LogicalMacro(const Register & rd,const Register & rn,const Operand & operand,LogicalOp op)155 void 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 
Mov(const Register & rd,uint64_t imm)263 void 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 
Mov(const Register & rd,const Operand & operand,DiscardMoveMode discard_mode)339 void 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 
Mov(const Register & rd,Smi smi)407 void TurboAssembler::Mov(const Register& rd, Smi smi) {
408   return Mov(rd, Operand(smi));
409 }
410 
Movi16bitHelper(const VRegister & vd,uint64_t imm)411 void 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 
Movi32bitHelper(const VRegister & vd,uint64_t imm)433 void 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 
Movi64bitHelper(const VRegister & vd,uint64_t imm)510 void 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 
Movi(const VRegister & vd,uint64_t imm,Shift shift,int shift_amount)546 void 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 
Movi(const VRegister & vd,uint64_t hi,uint64_t lo)567 void 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 
Mvn(const Register & rd,const Operand & operand)579 void 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 
CountSetHalfWords(uint64_t imm,unsigned reg_size)602 unsigned 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.
IsImmMovz(uint64_t imm,unsigned reg_size)620 bool 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.
IsImmMovn(uint64_t imm,unsigned reg_size)627 bool TurboAssembler::IsImmMovn(uint64_t imm, unsigned reg_size) {
628   return IsImmMovz(~imm, reg_size);
629 }
630 
ConditionalCompareMacro(const Register & rn,const Operand & operand,StatusFlags nzcv,Condition cond,ConditionalCompareOp op)631 void 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 
Csel(const Register & rd,const Register & rn,const Operand & operand,Condition cond)659 void 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 
TryOneInstrMoveImmediate(const Register & dst,int64_t imm)693 bool 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 
MoveImmediateForShiftedOp(const Register & dst,int64_t imm,PreShiftImmMode mode)715 Operand 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 
AddSubMacro(const Register & rd,const Register & rn,const Operand & operand,FlagsUpdate S,AddSubOp op)765 void 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 
AddSubWithCarryMacro(const Register & rd,const Register & rn,const Operand & operand,FlagsUpdate S,AddSubWithCarryOp op)811 void 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 
LoadStoreMacro(const CPURegister & rt,const MemOperand & addr,LoadStoreOp op)860 void 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 
LoadStorePairMacro(const CPURegister & rt,const CPURegister & rt2,const MemOperand & addr,LoadStorePairOp op)890 void 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 
NeedExtraInstructionsOrRegisterBranch(Label * label,ImmBranchType b_type)923 bool 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 
Adr(const Register & rd,Label * label,AdrHint hint)946 void 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 
B(Label * label,BranchType type,Register reg,int bit)980 void 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 
B(Label * label,Condition cond)1010 void 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 
Tbnz(const Register & rt,unsigned bit_pos,Label * label)1027 void 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 
Tbz(const Register & rt,unsigned bit_pos,Label * label)1043 void 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 
Cbnz(const Register & rt,Label * label)1059 void 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 
Cbz(const Register & rt,Label * label)1075 void 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 
Abs(const Register & rd,const Register & rm,Label * is_not_representable,Label * is_representable)1093 void 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 
Push(const CPURegister & src0,const CPURegister & src1,const CPURegister & src2,const CPURegister & src3,const CPURegister & src4,const CPURegister & src5,const CPURegister & src6,const CPURegister & src7)1116 void 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 
Pop(const CPURegister & dst0,const CPURegister & dst1,const CPURegister & dst2,const CPURegister & dst3,const CPURegister & dst4,const CPURegister & dst5,const CPURegister & dst6,const CPURegister & dst7)1130 void 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 
PushMultipleTimes(CPURegister src,Register count)1148 void 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 
PushHelper(int count,int size,const CPURegister & src0,const CPURegister & src1,const CPURegister & src2,const CPURegister & src3)1176 void 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 
PopHelper(int count,int size,const CPURegister & dst0,const CPURegister & dst1,const CPURegister & dst2,const CPURegister & dst3)1214 void 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 
PokePair(const CPURegister & src1,const CPURegister & src2,int offset)1252 void 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 
PeekPair(const CPURegister & dst1,const CPURegister & dst2,int offset)1259 void 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 
PushCalleeSavedRegisters()1266 void 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 
PopCalleeSavedRegisters()1300 void 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 
AssertSpAligned()1328 void 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 
CopySlots(int dst,Register src,Register slot_count)1341 void 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 
CopySlots(Register dst,Register src,Register slot_count)1350 void 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 
CopyDoubleWords(Register dst,Register src,Register count,CopyDoubleWordsMode mode)1358 void 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 
SlotAddress(Register dst,int slot_offset)1429 void TurboAssembler::SlotAddress(Register dst, int slot_offset) {
1430   Add(dst, sp, slot_offset << kSystemPointerSizeLog2);
1431 }
1432 
SlotAddress(Register dst,Register slot_offset)1433 void TurboAssembler::SlotAddress(Register dst, Register slot_offset) {
1434   Add(dst, sp, Operand(slot_offset, LSL, kSystemPointerSizeLog2));
1435 }
1436 
AssertFPCRState(Register fpcr)1437 void 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 
CanonicalizeNaN(const VRegister & dst,const VRegister & src)1461 void 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 
LoadRoot(Register destination,RootIndex index)1471 void 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 
PushRoot(RootIndex index)1479 void 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 
Move(Register dst,Smi src)1487 void TurboAssembler::Move(Register dst, Smi src) { Mov(dst, src); }
Move(Register dst,MemOperand src)1488 void TurboAssembler::Move(Register dst, MemOperand src) { Ldr(dst, src); }
Move(Register dst,Register src)1489 void TurboAssembler::Move(Register dst, Register src) {
1490   if (dst == src) return;
1491   Mov(dst, src);
1492 }
1493 
MovePair(Register dst0,Register src0,Register dst1,Register src1)1494 void 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 
Swap(Register lhs,Register rhs)1510 void 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 
Swap(VRegister lhs,VRegister rhs)1520 void 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 
AssertSmi(Register object,AbortReason reason)1538 void 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 
AssertNotSmi(Register object,AbortReason reason)1546 void 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 
AssertCodeT(Register object)1554 void 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 
AssertConstructor(Register object)1566 void 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 
AssertFunction(Register object)1581 void 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 
AssertCallableFunction(Register object)1594 void 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 
AssertBoundFunction(Register object)1607 void 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 
AssertGeneratorObject(Register object)1619 void 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 
AssertUndefinedOrAllocationSite(Register object)1646 void 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 
AssertPositiveOrZero(Register value)1660 void 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 
CallRuntime(const Runtime::Function * f,int num_arguments,SaveFPRegsMode save_doubles)1670 void 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 
JumpToExternalReference(const ExternalReference & builtin,bool builtin_exit_frame)1689 void 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 
JumpToOffHeapInstructionStream(Address entry)1699 void MacroAssembler::JumpToOffHeapInstructionStream(Address entry) {
1700   Ldr(kOffHeapTrampolineRegister, Operand(entry, RelocInfo::OFF_HEAP_TARGET));
1701   Br(kOffHeapTrampolineRegister);
1702 }
1703 
TailCallRuntime(Runtime::FunctionId fid)1704 void 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 
ActivationFrameAlignment()1718 int 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 
CallCFunction(ExternalReference function,int num_of_reg_args)1734 void TurboAssembler::CallCFunction(ExternalReference function,
1735                                    int num_of_reg_args) {
1736   CallCFunction(function, num_of_reg_args, 0);
1737 }
1738 
CallCFunction(ExternalReference function,int num_of_reg_args,int num_of_double_args)1739 void 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 
1749 static const int kRegisterPassedArguments = 8;
1750 static const int kFPRegisterPassedArguments = 8;
1751 
CallCFunction(Register function,int num_of_reg_args,int num_of_double_args)1752 void 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 
LoadFromConstantsTable(Register destination,int constant_index)1817 void 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 
LoadRootRelative(Register destination,int32_t offset)1827 void TurboAssembler::LoadRootRelative(Register destination, int32_t offset) {
1828   Ldr(destination, MemOperand(kRootRegister, offset));
1829 }
1830 
LoadRootRegisterOffset(Register destination,intptr_t offset)1831 void 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 
Jump(Register target,Condition cond)1840 void 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 
JumpHelper(int64_t offset,RelocInfo::Mode rmode,Condition cond)1848 void 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.
CalculateTargetOffset(Address target,RelocInfo::Mode rmode,byte * pc)1872 int64_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 
Jump(Address target,RelocInfo::Mode rmode,Condition cond)1897 void 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 
Jump(Handle<CodeT> code,RelocInfo::Mode rmode,Condition cond)1903 void 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 
Jump(const ExternalReference & reference)1928 void TurboAssembler::Jump(const ExternalReference& reference) {
1929   UseScratchRegisterScope temps(this);
1930   Register scratch = temps.AcquireX();
1931   Mov(scratch, reference);
1932   Jump(scratch);
1933 }
1934 
Call(Register target)1935 void TurboAssembler::Call(Register target) {
1936   BlockPoolsScope scope(this);
1937   Blr(target);
1938 }
1939 
Call(Address target,RelocInfo::Mode rmode)1940 void 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 
Call(Handle<CodeT> code,RelocInfo::Mode rmode)1951 void 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 
Call(ExternalReference target)1975 void TurboAssembler::Call(ExternalReference target) {
1976   UseScratchRegisterScope temps(this);
1977   Register temp = temps.AcquireX();
1978   Mov(temp, target);
1979   Call(temp);
1980 }
1981 
LoadEntryFromBuiltinIndex(Register builtin_index)1982 void 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 
LoadEntryFromBuiltin(Builtin builtin,Register destination)2005 void TurboAssembler::LoadEntryFromBuiltin(Builtin builtin,
2006                                           Register destination) {
2007   Ldr(destination, EntryFromBuiltinAsOperand(builtin));
2008 }
2009 
EntryFromBuiltinAsOperand(Builtin builtin)2010 MemOperand TurboAssembler::EntryFromBuiltinAsOperand(Builtin builtin) {
2011   ASM_CODE_COMMENT(this);
2012   DCHECK(root_array_available());
2013   return MemOperand(kRootRegister,
2014                     IsolateData::BuiltinEntrySlotOffset(builtin));
2015 }
2016 
CallBuiltinByIndex(Register builtin_index)2017 void TurboAssembler::CallBuiltinByIndex(Register builtin_index) {
2018   ASM_CODE_COMMENT(this);
2019   LoadEntryFromBuiltinIndex(builtin_index);
2020   Call(builtin_index);
2021 }
2022 
CallBuiltin(Builtin builtin)2023 void 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 
TailCallBuiltin(Builtin builtin)2039 void 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 
LoadCodeObjectEntry(Register destination,Register code_object)2064 void 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 
CallCodeObject(Register code_object)2119 void TurboAssembler::CallCodeObject(Register code_object) {
2120   ASM_CODE_COMMENT(this);
2121   LoadCodeObjectEntry(code_object, code_object);
2122   Call(code_object);
2123 }
2124 
JumpCodeObject(Register code_object,JumpMode jump_mode)2125 void 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 
LoadCodeDataContainerEntry(Register destination,Register code_data_container_object)2138 void 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 
LoadCodeDataContainerCodeNonBuiltin(Register destination,Register code_data_container_object)2150 void 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 
CallCodeDataContainerObject(Register code_data_container_object)2162 void 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 
JumpCodeDataContainerObject(Register code_data_container_object,JumpMode jump_mode)2170 void 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 
LoadCodeTEntry(Register destination,Register code)2184 void 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 
CallCodeTObject(Register code)2193 void TurboAssembler::CallCodeTObject(Register code) {
2194   if (V8_EXTERNAL_CODE_SPACE_BOOL) {
2195     CallCodeDataContainerObject(code);
2196   } else {
2197     CallCodeObject(code);
2198   }
2199 }
2200 
JumpCodeTObject(Register code,JumpMode jump_mode)2201 void 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 
StoreReturnAddressAndCall(Register target)2209 void 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 
IndirectCall(Address target,RelocInfo::Mode rmode)2243 void 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 
IsNearCallOffset(int64_t offset)2251 bool TurboAssembler::IsNearCallOffset(int64_t offset) {
2252   return is_int26(offset);
2253 }
2254 
CallForDeoptimization(Builtin target,int deopt_id,Label * exit,DeoptimizeKind kind,Label * ret,Label * jump_deoptimization_entry_label)2255 void 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 
LoadStackLimit(Register destination,StackLimitKind kind)2266 void 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 
StackOverflowCheck(Register num_args,Label * stack_overflow)2281 void 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 
InvokePrologue(Register formal_parameter_count,Register actual_argument_count,Label * done,InvokeType type)2300 void 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 
CallDebugOnFunctionCall(Register fun,Register new_target,Register expected_parameter_count,Register actual_parameter_count)2407 void 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 
InvokeFunctionCode(Register function,Register new_target,Register expected_parameter_count,Register actual_parameter_count,InvokeType type)2431 void 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 
ReceiverOperand(Register arg_count)2488 Operand MacroAssembler::ReceiverOperand(Register arg_count) {
2489   return Operand(0);
2490 }
2491 
InvokeFunctionWithNewTarget(Register function,Register new_target,Register actual_parameter_count,InvokeType type)2492 void 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 
InvokeFunction(Register function,Register expected_parameter_count,Register actual_parameter_count,InvokeType type)2521 void 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 
TryConvertDoubleToInt64(Register result,DoubleRegister double_input,Label * done)2541 void 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 
TruncateDoubleToI(Isolate * isolate,Zone * zone,Register result,DoubleRegister double_input,StubCallMode stub_mode,LinkRegisterStatus lr_status)2566 void 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 
2619 void 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 
2627 void 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 
2686 void 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 
2694 void MacroAssembler::ExitFramePreserveFPRegs() {
2695   ASM_CODE_COMMENT(this);
2696   DCHECK_EQ(kCallerSavedV.Count() % 2, 0);
2697   PushCPURegList(kCallerSavedV);
2698 }
2699 
2700 void 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 
2716 void 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.
2778 void 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 
2811 void MacroAssembler::LoadGlobalProxy(Register dst) {
2812   ASM_CODE_COMMENT(this);
2813   LoadNativeContextSlot(dst, Context::GLOBAL_PROXY_INDEX);
2814 }
2815 
2816 void 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 
2825 void 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 
2841 void 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.
2850 void 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 
2857 void 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.
2863 void 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.
2871 void 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 
2883 void 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 
2891 void 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 
2900 void MacroAssembler::JumpIfRoot(const Register& obj, RootIndex index,
2901                                 Label* if_equal) {
2902   CompareRoot(obj, index);
2903   B(eq, if_equal);
2904 }
2905 
2906 void MacroAssembler::JumpIfNotRoot(const Register& obj, RootIndex index,
2907                                    Label* if_not_equal) {
2908   CompareRoot(obj, index);
2909   B(ne, if_not_equal);
2910 }
2911 
2912 void 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 
2929 void 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 
2938 void 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 
2947 void 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 
2956 void TurboAssembler::SmiUntagField(Register dst, const MemOperand& src) {
2957   SmiUntag(dst, src);
2958 }
2959 
2960 void 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 
2969 void 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 
2981 void 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 
2992 void 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 
2999 void TurboAssembler::DecompressTaggedPointer(const Register& destination,
3000                                              const Register& source) {
3001   ASM_CODE_COMMENT(this);
3002   Add(destination, kPtrComprCageBaseRegister, Operand(source, UXTW));
3003 }
3004 
3005 void 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 
3012 void 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 
3026 void 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 
3036 void 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 
3046 void 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 
3061 void 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 
3101 void 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 
3111 void 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 
3121 void 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 
3128 void 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 
3138 void 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 
3169 void 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 
3179 void 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 
3189 void 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 
3205 void 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 
3223 void 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 
3250 void 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.
3288 void 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 
3346 void TurboAssembler::Assert(Condition cond, AbortReason reason) {
3347   if (FLAG_debug_code) {
3348     Check(cond, reason);
3349   }
3350 }
3351 
3352 void TurboAssembler::AssertUnreachable(AbortReason reason) {
3353   if (FLAG_debug_code) Abort(reason);
3354 }
3355 
3356 void 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 
3364 void TurboAssembler::Trap() { Brk(0); }
3365 void TurboAssembler::DebugBreak() { Debug("DebugBreak", 0, BREAK); }
3366 
3367 void 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 
3410 void 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.
3420 void 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 
3570 void 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 
3600 void 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 
3676 UseScratchRegisterScope::~UseScratchRegisterScope() {
3677   available_->set_bits(old_available_);
3678   availablefp_->set_bits(old_availablefp_);
3679 }
3680 
3681 Register UseScratchRegisterScope::AcquireSameSizeAs(const Register& reg) {
3682   int code = AcquireNextAvailable(available_).code();
3683   return Register::Create(code, reg.SizeInBits());
3684 }
3685 
3686 VRegister UseScratchRegisterScope::AcquireSameSizeAs(const VRegister& reg) {
3687   int code = AcquireNextAvailable(availablefp_).code();
3688   return VRegister::Create(code, reg.SizeInBits());
3689 }
3690 
3691 CPURegister 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 
3699 void TurboAssembler::ComputeCodeStartAddress(const Register& rd) {
3700   // We can use adr to load a pc relative location.
3701   adr(rd, -pc_offset());
3702 }
3703 
3704 void 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
3723 void 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 
3735 void 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 
3745 void 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 
3756 void 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