1 // Copyright 2017 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 #ifndef V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_H_
6 #define V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_H_
7 
8 #include "src/wasm/baseline/liftoff-assembler.h"
9 
10 namespace v8 {
11 namespace internal {
12 namespace wasm {
13 
14 namespace liftoff {
15 
16 //  half
17 //  slot        Frame
18 //  -----+--------------------+---------------------------
19 //  n+3  |   parameter n      |
20 //  ...  |       ...          |
21 //   4   |   parameter 1      | or parameter 2
22 //   3   |   parameter 0      | or parameter 1
23 //   2   |  (result address)  | or parameter 0
24 //  -----+--------------------+---------------------------
25 //   1   | return addr (lr)   |
26 //   0   | previous frame (fp)|
27 //  -----+--------------------+  <-- frame ptr (fp)
28 //  -1   | 0xa: WASM_COMPILED |
29 //  -2   |     instance       |
30 //  -----+--------------------+---------------------------
31 //  -3   |    slot 0 (high)   |   ^
32 //  -4   |    slot 0 (low)    |   |
33 //  -5   |    slot 1 (high)   | Frame slots
34 //  -6   |    slot 1 (low)    |   |
35 //       |                    |   v
36 //  -----+--------------------+  <-- stack ptr (sp)
37 //
38 static_assert(2 * kSystemPointerSize == LiftoffAssembler::kStackSlotSize,
39               "Slot size should be twice the size of the 32 bit pointer.");
40 constexpr int kInstanceOffset = 2 * kSystemPointerSize;
41 // kPatchInstructionsRequired sets a maximum limit of how many instructions that
42 // PatchPrepareStackFrame will use in order to increase the stack appropriately.
43 // Three instructions are required to sub a large constant, movw + movt + sub.
44 constexpr int32_t kPatchInstructionsRequired = 3;
45 
GetStackSlot(int offset)46 inline MemOperand GetStackSlot(int offset) { return MemOperand(fp, -offset); }
47 
GetHalfStackSlot(int offset,RegPairHalf half)48 inline MemOperand GetHalfStackSlot(int offset, RegPairHalf half) {
49   int32_t half_offset =
50       half == kLowWord ? 0 : LiftoffAssembler::kStackSlotSize / 2;
51   return MemOperand(fp, -offset + half_offset);
52 }
53 
GetInstanceOperand()54 inline MemOperand GetInstanceOperand() { return GetStackSlot(kInstanceOffset); }
55 
GetMemOp(LiftoffAssembler * assm,UseScratchRegisterScope * temps,Register addr,Register offset,int32_t offset_imm)56 inline MemOperand GetMemOp(LiftoffAssembler* assm,
57                            UseScratchRegisterScope* temps, Register addr,
58                            Register offset, int32_t offset_imm) {
59   if (offset != no_reg) {
60     if (offset_imm == 0) return MemOperand(addr, offset);
61     Register tmp = temps->Acquire();
62     assm->add(tmp, offset, Operand(offset_imm));
63     return MemOperand(addr, tmp);
64   }
65   return MemOperand(addr, offset_imm);
66 }
67 
CalculateActualAddress(LiftoffAssembler * assm,UseScratchRegisterScope * temps,Register addr_reg,Register offset_reg,int32_t offset_imm)68 inline Register CalculateActualAddress(LiftoffAssembler* assm,
69                                        UseScratchRegisterScope* temps,
70                                        Register addr_reg, Register offset_reg,
71                                        int32_t offset_imm) {
72   if (offset_reg == no_reg && offset_imm == 0) {
73     return addr_reg;
74   }
75   Register actual_addr_reg = temps->Acquire();
76   if (offset_reg == no_reg) {
77     assm->add(actual_addr_reg, addr_reg, Operand(offset_imm));
78   } else {
79     assm->add(actual_addr_reg, addr_reg, Operand(offset_reg));
80     if (offset_imm != 0) {
81       assm->add(actual_addr_reg, actual_addr_reg, Operand(offset_imm));
82     }
83   }
84   return actual_addr_reg;
85 }
86 
MakeUnsigned(Condition cond)87 inline Condition MakeUnsigned(Condition cond) {
88   switch (cond) {
89     case kSignedLessThan:
90       return kUnsignedLessThan;
91     case kSignedLessEqual:
92       return kUnsignedLessEqual;
93     case kSignedGreaterThan:
94       return kUnsignedGreaterThan;
95     case kSignedGreaterEqual:
96       return kUnsignedGreaterEqual;
97     case kEqual:
98     case kUnequal:
99     case kUnsignedLessThan:
100     case kUnsignedLessEqual:
101     case kUnsignedGreaterThan:
102     case kUnsignedGreaterEqual:
103       return cond;
104     default:
105       UNREACHABLE();
106   }
107 }
108 
109 template <void (Assembler::*op)(Register, Register, Register, SBit, Condition),
110           void (Assembler::*op_with_carry)(Register, Register, const Operand&,
111                                            SBit, Condition)>
I64Binop(LiftoffAssembler * assm,LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)112 inline void I64Binop(LiftoffAssembler* assm, LiftoffRegister dst,
113                      LiftoffRegister lhs, LiftoffRegister rhs) {
114   UseScratchRegisterScope temps(assm);
115   Register scratch = dst.low_gp();
116   bool can_use_dst =
117       dst.low_gp() != lhs.high_gp() && dst.low_gp() != rhs.high_gp();
118   if (!can_use_dst) {
119     scratch = temps.Acquire();
120   }
121   (assm->*op)(scratch, lhs.low_gp(), rhs.low_gp(), SetCC, al);
122   (assm->*op_with_carry)(dst.high_gp(), lhs.high_gp(), Operand(rhs.high_gp()),
123                          LeaveCC, al);
124   if (!can_use_dst) {
125     assm->mov(dst.low_gp(), scratch);
126   }
127 }
128 
129 template <void (Assembler::*op)(Register, Register, const Operand&, SBit,
130                                 Condition),
131           void (Assembler::*op_with_carry)(Register, Register, const Operand&,
132                                            SBit, Condition)>
I64BinopI(LiftoffAssembler * assm,LiftoffRegister dst,LiftoffRegister lhs,int32_t imm)133 inline void I64BinopI(LiftoffAssembler* assm, LiftoffRegister dst,
134                       LiftoffRegister lhs, int32_t imm) {
135   UseScratchRegisterScope temps(assm);
136   Register scratch = dst.low_gp();
137   bool can_use_dst = dst.low_gp() != lhs.high_gp();
138   if (!can_use_dst) {
139     scratch = temps.Acquire();
140   }
141   (assm->*op)(scratch, lhs.low_gp(), Operand(imm), SetCC, al);
142   // Top half of the immediate sign extended, either 0 or -1.
143   int32_t sign_extend = imm < 0 ? -1 : 0;
144   (assm->*op_with_carry)(dst.high_gp(), lhs.high_gp(), Operand(sign_extend),
145                          LeaveCC, al);
146   if (!can_use_dst) {
147     assm->mov(dst.low_gp(), scratch);
148   }
149 }
150 
151 template <void (TurboAssembler::*op)(Register, Register, Register, Register,
152                                      Register),
153           bool is_left_shift>
I64Shiftop(LiftoffAssembler * assm,LiftoffRegister dst,LiftoffRegister src,Register amount)154 inline void I64Shiftop(LiftoffAssembler* assm, LiftoffRegister dst,
155                        LiftoffRegister src, Register amount) {
156   Register src_low = src.low_gp();
157   Register src_high = src.high_gp();
158   Register dst_low = dst.low_gp();
159   Register dst_high = dst.high_gp();
160   // Left shift writes {dst_high} then {dst_low}, right shifts write {dst_low}
161   // then {dst_high}.
162   Register clobbered_dst_reg = is_left_shift ? dst_high : dst_low;
163   LiftoffRegList pinned = LiftoffRegList::ForRegs(clobbered_dst_reg, src);
164   Register amount_capped =
165       pinned.set(assm->GetUnusedRegister(kGpReg, pinned)).gp();
166   assm->and_(amount_capped, amount, Operand(0x3F));
167 
168   // Ensure that writing the first half of {dst} does not overwrite the still
169   // needed half of {src}.
170   Register* later_src_reg = is_left_shift ? &src_low : &src_high;
171   if (*later_src_reg == clobbered_dst_reg) {
172     *later_src_reg = assm->GetUnusedRegister(kGpReg, pinned).gp();
173     assm->TurboAssembler::Move(*later_src_reg, clobbered_dst_reg);
174   }
175 
176   (assm->*op)(dst_low, dst_high, src_low, src_high, amount_capped);
177 }
178 
GetFloatRegister(DoubleRegister reg)179 inline FloatRegister GetFloatRegister(DoubleRegister reg) {
180   DCHECK_LT(reg.code(), kDoubleCode_d16);
181   return LowDwVfpRegister::from_code(reg.code()).low();
182 }
183 
GetSimd128Register(DoubleRegister reg)184 inline Simd128Register GetSimd128Register(DoubleRegister reg) {
185   return QwNeonRegister::from_code(reg.code() / 2);
186 }
187 
188 enum class MinOrMax : uint8_t { kMin, kMax };
189 template <typename RegisterType>
EmitFloatMinOrMax(LiftoffAssembler * assm,RegisterType dst,RegisterType lhs,RegisterType rhs,MinOrMax min_or_max)190 inline void EmitFloatMinOrMax(LiftoffAssembler* assm, RegisterType dst,
191                               RegisterType lhs, RegisterType rhs,
192                               MinOrMax min_or_max) {
193   DCHECK(RegisterType::kSizeInBytes == 4 || RegisterType::kSizeInBytes == 8);
194   if (lhs == rhs) {
195     assm->TurboAssembler::Move(dst, lhs);
196     return;
197   }
198   Label done, is_nan;
199   if (min_or_max == MinOrMax::kMin) {
200     assm->TurboAssembler::FloatMin(dst, lhs, rhs, &is_nan);
201   } else {
202     assm->TurboAssembler::FloatMax(dst, lhs, rhs, &is_nan);
203   }
204   assm->b(&done);
205   assm->bind(&is_nan);
206   // Create a NaN output.
207   assm->vadd(dst, lhs, rhs);
208   assm->bind(&done);
209 }
210 
EnsureNoAlias(Assembler * assm,Register reg,Register must_not_alias,UseScratchRegisterScope * temps)211 inline Register EnsureNoAlias(Assembler* assm, Register reg,
212                               Register must_not_alias,
213                               UseScratchRegisterScope* temps) {
214   if (reg != must_not_alias) return reg;
215   Register tmp = temps->Acquire();
216   DCHECK_NE(reg, tmp);
217   assm->mov(tmp, reg);
218   return tmp;
219 }
220 
221 }  // namespace liftoff
222 
PrepareStackFrame()223 int LiftoffAssembler::PrepareStackFrame() {
224   if (!CpuFeatures::IsSupported(ARMv7)) {
225     bailout(kUnsupportedArchitecture, "Armv6 not supported");
226     return 0;
227   }
228   uint32_t offset = static_cast<uint32_t>(pc_offset());
229   // PatchPrepareStackFrame will patch this in order to increase the stack
230   // appropriately. Additional nops are required as the bytes operand might
231   // require extra moves to encode.
232   for (int i = 0; i < liftoff::kPatchInstructionsRequired; i++) {
233     nop();
234   }
235   DCHECK_EQ(offset + liftoff::kPatchInstructionsRequired * kInstrSize,
236             pc_offset());
237   return offset;
238 }
239 
PatchPrepareStackFrame(int offset,int frame_size)240 void LiftoffAssembler::PatchPrepareStackFrame(int offset, int frame_size) {
241 #ifdef USE_SIMULATOR
242   // When using the simulator, deal with Liftoff which allocates the stack
243   // before checking it.
244   // TODO(arm): Remove this when the stack check mechanism will be updated.
245   if (frame_size > KB / 2) {
246     bailout(kOtherReason,
247             "Stack limited to 512 bytes to avoid a bug in StackCheck");
248     return;
249   }
250 #endif
251   PatchingAssembler patching_assembler(AssemblerOptions{},
252                                        buffer_start_ + offset,
253                                        liftoff::kPatchInstructionsRequired);
254 #if V8_OS_WIN
255   if (frame_size > kStackPageSize) {
256     // Generate OOL code (at the end of the function, where the current
257     // assembler is pointing) to do the explicit stack limit check (see
258     // https://docs.microsoft.com/en-us/previous-versions/visualstudio/
259     // visual-studio-6.0/aa227153(v=vs.60)).
260     // At the function start, emit a jump to that OOL code (from {offset} to
261     // {pc_offset()}).
262     int ool_offset = pc_offset() - offset;
263     patching_assembler.b(ool_offset - Instruction::kPcLoadDelta);
264     patching_assembler.PadWithNops();
265 
266     // Now generate the OOL code.
267     AllocateStackSpace(frame_size);
268     // Jump back to the start of the function (from {pc_offset()} to {offset +
269     // liftoff::kPatchInstructionsRequired * kInstrSize}).
270     int func_start_offset =
271         offset + liftoff::kPatchInstructionsRequired * kInstrSize - pc_offset();
272     b(func_start_offset - Instruction::kPcLoadDelta);
273     return;
274   }
275 #endif
276   patching_assembler.sub(sp, sp, Operand(frame_size));
277   patching_assembler.PadWithNops();
278 }
279 
FinishCode()280 void LiftoffAssembler::FinishCode() { CheckConstPool(true, false); }
281 
AbortCompilation()282 void LiftoffAssembler::AbortCompilation() { AbortedCodeGeneration(); }
283 
284 // static
StaticStackFrameSize()285 constexpr int LiftoffAssembler::StaticStackFrameSize() {
286   return liftoff::kInstanceOffset;
287 }
288 
SlotSizeForType(ValueType type)289 int LiftoffAssembler::SlotSizeForType(ValueType type) {
290   switch (type.kind()) {
291     case ValueType::kS128:
292       return type.element_size_bytes();
293     default:
294       return kStackSlotSize;
295   }
296 }
297 
NeedsAlignment(ValueType type)298 bool LiftoffAssembler::NeedsAlignment(ValueType type) {
299   switch (type.kind()) {
300     case ValueType::kS128:
301       return true;
302     default:
303       // No alignment because all other types are kStackSlotSize.
304       return false;
305   }
306 }
307 
LoadConstant(LiftoffRegister reg,WasmValue value,RelocInfo::Mode rmode)308 void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
309                                     RelocInfo::Mode rmode) {
310   switch (value.type().kind()) {
311     case ValueType::kI32:
312       TurboAssembler::Move(reg.gp(), Operand(value.to_i32(), rmode));
313       break;
314     case ValueType::kI64: {
315       DCHECK(RelocInfo::IsNone(rmode));
316       int32_t low_word = value.to_i64();
317       int32_t high_word = value.to_i64() >> 32;
318       TurboAssembler::Move(reg.low_gp(), Operand(low_word));
319       TurboAssembler::Move(reg.high_gp(), Operand(high_word));
320       break;
321     }
322     case ValueType::kF32:
323       vmov(liftoff::GetFloatRegister(reg.fp()), value.to_f32_boxed());
324       break;
325     case ValueType::kF64: {
326       Register extra_scratch = GetUnusedRegister(kGpReg).gp();
327       vmov(reg.fp(), Double(value.to_f64_boxed().get_scalar()), extra_scratch);
328       break;
329     }
330     default:
331       UNREACHABLE();
332   }
333 }
334 
LoadFromInstance(Register dst,uint32_t offset,int size)335 void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset,
336                                         int size) {
337   DCHECK_LE(offset, kMaxInt);
338   DCHECK_EQ(4, size);
339   ldr(dst, liftoff::GetInstanceOperand());
340   ldr(dst, MemOperand(dst, offset));
341 }
342 
LoadTaggedPointerFromInstance(Register dst,uint32_t offset)343 void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst,
344                                                      uint32_t offset) {
345   LoadFromInstance(dst, offset, kTaggedSize);
346 }
347 
SpillInstance(Register instance)348 void LiftoffAssembler::SpillInstance(Register instance) {
349   str(instance, liftoff::GetInstanceOperand());
350 }
351 
FillInstanceInto(Register dst)352 void LiftoffAssembler::FillInstanceInto(Register dst) {
353   ldr(dst, liftoff::GetInstanceOperand());
354 }
355 
LoadTaggedPointer(Register dst,Register src_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegList pinned)356 void LiftoffAssembler::LoadTaggedPointer(Register dst, Register src_addr,
357                                          Register offset_reg,
358                                          uint32_t offset_imm,
359                                          LiftoffRegList pinned) {
360   STATIC_ASSERT(kTaggedSize == kInt32Size);
361   Load(LiftoffRegister(dst), src_addr, offset_reg, offset_imm,
362        LoadType::kI32Load, pinned);
363 }
364 
Load(LiftoffRegister dst,Register src_addr,Register offset_reg,uint32_t offset_imm,LoadType type,LiftoffRegList pinned,uint32_t * protected_load_pc,bool is_load_mem)365 void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
366                             Register offset_reg, uint32_t offset_imm,
367                             LoadType type, LiftoffRegList pinned,
368                             uint32_t* protected_load_pc, bool is_load_mem) {
369   DCHECK_IMPLIES(type.value_type() == kWasmI64, dst.is_gp_pair());
370   // If offset_imm cannot be converted to int32 safely, we abort as a separate
371   // check should cause this code to never be executed.
372   // TODO(7881): Support when >2GB is required.
373   if (!is_uint31(offset_imm)) {
374     TurboAssembler::Abort(AbortReason::kOffsetOutOfRange);
375     return;
376   }
377   UseScratchRegisterScope temps(this);
378   if (type.value() == LoadType::kF64Load ||
379       type.value() == LoadType::kF32Load ||
380       type.value() == LoadType::kS128Load) {
381     Register actual_src_addr = liftoff::CalculateActualAddress(
382         this, &temps, src_addr, offset_reg, offset_imm);
383     if (type.value() == LoadType::kF64Load) {
384       // Armv6 is not supported so Neon can be used to avoid alignment issues.
385       CpuFeatureScope scope(this, NEON);
386       vld1(Neon64, NeonListOperand(dst.fp()), NeonMemOperand(actual_src_addr));
387     } else if (type.value() == LoadType::kF32Load) {
388       // TODO(arm): Use vld1 for f32 when implemented in simulator as used for
389       // f64. It supports unaligned access.
390       Register scratch =
391           (actual_src_addr == src_addr) ? temps.Acquire() : actual_src_addr;
392       ldr(scratch, MemOperand(actual_src_addr));
393       vmov(liftoff::GetFloatRegister(dst.fp()), scratch);
394     } else {
395       // Armv6 is not supported so Neon can be used to avoid alignment issues.
396       CpuFeatureScope scope(this, NEON);
397       vld1(Neon8, NeonListOperand(dst.low_fp(), 2),
398            NeonMemOperand(actual_src_addr));
399     }
400   } else {
401     MemOperand src_op =
402         liftoff::GetMemOp(this, &temps, src_addr, offset_reg, offset_imm);
403     if (protected_load_pc) *protected_load_pc = pc_offset();
404     switch (type.value()) {
405       case LoadType::kI32Load8U:
406         ldrb(dst.gp(), src_op);
407         break;
408       case LoadType::kI64Load8U:
409         ldrb(dst.low_gp(), src_op);
410         mov(dst.high_gp(), Operand(0));
411         break;
412       case LoadType::kI32Load8S:
413         ldrsb(dst.gp(), src_op);
414         break;
415       case LoadType::kI64Load8S:
416         ldrsb(dst.low_gp(), src_op);
417         asr(dst.high_gp(), dst.low_gp(), Operand(31));
418         break;
419       case LoadType::kI32Load16U:
420         ldrh(dst.gp(), src_op);
421         break;
422       case LoadType::kI64Load16U:
423         ldrh(dst.low_gp(), src_op);
424         mov(dst.high_gp(), Operand(0));
425         break;
426       case LoadType::kI32Load16S:
427         ldrsh(dst.gp(), src_op);
428         break;
429       case LoadType::kI32Load:
430         ldr(dst.gp(), src_op);
431         break;
432       case LoadType::kI64Load16S:
433         ldrsh(dst.low_gp(), src_op);
434         asr(dst.high_gp(), dst.low_gp(), Operand(31));
435         break;
436       case LoadType::kI64Load32U:
437         ldr(dst.low_gp(), src_op);
438         mov(dst.high_gp(), Operand(0));
439         break;
440       case LoadType::kI64Load32S:
441         ldr(dst.low_gp(), src_op);
442         asr(dst.high_gp(), dst.low_gp(), Operand(31));
443         break;
444       case LoadType::kI64Load:
445         ldr(dst.low_gp(), src_op);
446         // GetMemOp may use a scratch register as the offset register, in which
447         // case, calling GetMemOp again will fail due to the assembler having
448         // ran out of scratch registers.
449         if (temps.CanAcquire()) {
450           src_op = liftoff::GetMemOp(this, &temps, src_addr, offset_reg,
451                                      offset_imm + kSystemPointerSize);
452         } else {
453           add(src_op.rm(), src_op.rm(), Operand(kSystemPointerSize));
454         }
455         ldr(dst.high_gp(), src_op);
456         break;
457       default:
458         UNREACHABLE();
459     }
460   }
461 }
462 
Store(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister src,StoreType type,LiftoffRegList pinned,uint32_t * protected_store_pc,bool is_store_mem)463 void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
464                              uint32_t offset_imm, LiftoffRegister src,
465                              StoreType type, LiftoffRegList pinned,
466                              uint32_t* protected_store_pc, bool is_store_mem) {
467   // If offset_imm cannot be converted to int32 safely, we abort as a separate
468   // check should cause this code to never be executed.
469   // TODO(7881): Support when >2GB is required.
470   if (!is_uint31(offset_imm)) {
471     TurboAssembler::Abort(AbortReason::kOffsetOutOfRange);
472     return;
473   }
474   UseScratchRegisterScope temps(this);
475   if (type.value() == StoreType::kF64Store) {
476     Register actual_dst_addr = liftoff::CalculateActualAddress(
477         this, &temps, dst_addr, offset_reg, offset_imm);
478     // Armv6 is not supported so Neon can be used to avoid alignment issues.
479     CpuFeatureScope scope(this, NEON);
480     vst1(Neon64, NeonListOperand(src.fp()), NeonMemOperand(actual_dst_addr));
481   } else if (type.value() == StoreType::kS128Store) {
482     Register actual_dst_addr = liftoff::CalculateActualAddress(
483         this, &temps, dst_addr, offset_reg, offset_imm);
484     // Armv6 is not supported so Neon can be used to avoid alignment issues.
485     CpuFeatureScope scope(this, NEON);
486     vst1(Neon8, NeonListOperand(src.low_fp(), 2),
487          NeonMemOperand(actual_dst_addr));
488   } else if (type.value() == StoreType::kF32Store) {
489     // TODO(arm): Use vst1 for f32 when implemented in simulator as used for
490     // f64. It supports unaligned access.
491     // CalculateActualAddress will only not use a scratch register if the
492     // following condition holds, otherwise another register must be
493     // retrieved.
494     Register scratch = (offset_reg == no_reg && offset_imm == 0)
495                            ? temps.Acquire()
496                            : GetUnusedRegister(kGpReg, pinned).gp();
497     Register actual_dst_addr = liftoff::CalculateActualAddress(
498         this, &temps, dst_addr, offset_reg, offset_imm);
499     vmov(scratch, liftoff::GetFloatRegister(src.fp()));
500     str(scratch, MemOperand(actual_dst_addr));
501   } else {
502     MemOperand dst_op =
503         liftoff::GetMemOp(this, &temps, dst_addr, offset_reg, offset_imm);
504     if (protected_store_pc) *protected_store_pc = pc_offset();
505     switch (type.value()) {
506       case StoreType::kI64Store8:
507         src = src.low();
508         V8_FALLTHROUGH;
509       case StoreType::kI32Store8:
510         strb(src.gp(), dst_op);
511         break;
512       case StoreType::kI64Store16:
513         src = src.low();
514         V8_FALLTHROUGH;
515       case StoreType::kI32Store16:
516         strh(src.gp(), dst_op);
517         break;
518       case StoreType::kI64Store32:
519         src = src.low();
520         V8_FALLTHROUGH;
521       case StoreType::kI32Store:
522         str(src.gp(), dst_op);
523         break;
524       case StoreType::kI64Store:
525         str(src.low_gp(), dst_op);
526         // GetMemOp may use a scratch register as the offset register, in which
527         // case, calling GetMemOp again will fail due to the assembler having
528         // ran out of scratch registers.
529         if (temps.CanAcquire()) {
530           dst_op = liftoff::GetMemOp(this, &temps, dst_addr, offset_reg,
531                                      offset_imm + kSystemPointerSize);
532         } else {
533           add(dst_op.rm(), dst_op.rm(), Operand(kSystemPointerSize));
534         }
535         str(src.high_gp(), dst_op);
536         break;
537       default:
538         UNREACHABLE();
539     }
540   }
541 }
542 
AtomicLoad(LiftoffRegister dst,Register src_addr,Register offset_reg,uint32_t offset_imm,LoadType type,LiftoffRegList pinned)543 void LiftoffAssembler::AtomicLoad(LiftoffRegister dst, Register src_addr,
544                                   Register offset_reg, uint32_t offset_imm,
545                                   LoadType type, LiftoffRegList pinned) {
546   bailout(kAtomics, "AtomicLoad");
547 }
548 
AtomicStore(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister src,StoreType type,LiftoffRegList pinned)549 void LiftoffAssembler::AtomicStore(Register dst_addr, Register offset_reg,
550                                    uint32_t offset_imm, LiftoffRegister src,
551                                    StoreType type, LiftoffRegList pinned) {
552   bailout(kAtomics, "AtomicStore");
553 }
554 
AtomicAdd(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister value,StoreType type)555 void LiftoffAssembler::AtomicAdd(Register dst_addr, Register offset_reg,
556                                  uint32_t offset_imm, LiftoffRegister value,
557                                  StoreType type) {
558   bailout(kAtomics, "AtomicAdd");
559 }
560 
AtomicSub(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister value,StoreType type)561 void LiftoffAssembler::AtomicSub(Register dst_addr, Register offset_reg,
562                                  uint32_t offset_imm, LiftoffRegister value,
563                                  StoreType type) {
564   bailout(kAtomics, "AtomicSub");
565 }
566 
AtomicAnd(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister value,StoreType type)567 void LiftoffAssembler::AtomicAnd(Register dst_addr, Register offset_reg,
568                                  uint32_t offset_imm, LiftoffRegister value,
569                                  StoreType type) {
570   bailout(kAtomics, "AtomicAnd");
571 }
572 
AtomicOr(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister value,StoreType type)573 void LiftoffAssembler::AtomicOr(Register dst_addr, Register offset_reg,
574                                 uint32_t offset_imm, LiftoffRegister value,
575                                 StoreType type) {
576   bailout(kAtomics, "AtomicOr");
577 }
578 
AtomicXor(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister value,StoreType type)579 void LiftoffAssembler::AtomicXor(Register dst_addr, Register offset_reg,
580                                  uint32_t offset_imm, LiftoffRegister value,
581                                  StoreType type) {
582   bailout(kAtomics, "AtomicXor");
583 }
584 
AtomicExchange(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister value,StoreType type)585 void LiftoffAssembler::AtomicExchange(Register dst_addr, Register offset_reg,
586                                       uint32_t offset_imm,
587                                       LiftoffRegister value, StoreType type) {
588   bailout(kAtomics, "AtomicExchange");
589 }
590 
AtomicCompareExchange(Register dst_addr,Register offset_reg,uint32_t offset_imm,LiftoffRegister expected,LiftoffRegister new_value,LiftoffRegister result,StoreType type)591 void LiftoffAssembler::AtomicCompareExchange(
592     Register dst_addr, Register offset_reg, uint32_t offset_imm,
593     LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister result,
594     StoreType type) {
595   bailout(kAtomics, "AtomicCompareExchange");
596 }
597 
AtomicFence()598 void LiftoffAssembler::AtomicFence() { dmb(ISH); }
599 
LoadCallerFrameSlot(LiftoffRegister dst,uint32_t caller_slot_idx,ValueType type)600 void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst,
601                                            uint32_t caller_slot_idx,
602                                            ValueType type) {
603   int32_t offset = (caller_slot_idx + 1) * kSystemPointerSize;
604   MemOperand src(fp, offset);
605   switch (type.kind()) {
606     case ValueType::kI32:
607       ldr(dst.gp(), src);
608       break;
609     case ValueType::kI64:
610       ldr(dst.low_gp(), src);
611       ldr(dst.high_gp(), MemOperand(fp, offset + kSystemPointerSize));
612       break;
613     case ValueType::kF32:
614       vldr(liftoff::GetFloatRegister(dst.fp()), src);
615       break;
616     case ValueType::kF64:
617       vldr(dst.fp(), src);
618       break;
619     case ValueType::kS128: {
620       UseScratchRegisterScope temps(this);
621       Register addr = liftoff::CalculateActualAddress(this, &temps, src.rn(),
622                                                       no_reg, src.offset());
623       vld1(Neon8, NeonListOperand(dst.low_fp(), 2), NeonMemOperand(addr));
624       break;
625     }
626     default:
627       UNREACHABLE();
628   }
629 }
630 
MoveStackValue(uint32_t dst_offset,uint32_t src_offset,ValueType type)631 void LiftoffAssembler::MoveStackValue(uint32_t dst_offset, uint32_t src_offset,
632                                       ValueType type) {
633   DCHECK_NE(dst_offset, src_offset);
634   LiftoffRegister reg = GetUnusedRegister(reg_class_for(type));
635   Fill(reg, src_offset, type);
636   Spill(dst_offset, reg, type);
637 }
638 
Move(Register dst,Register src,ValueType type)639 void LiftoffAssembler::Move(Register dst, Register src, ValueType type) {
640   DCHECK_NE(dst, src);
641   DCHECK_EQ(type, kWasmI32);
642   TurboAssembler::Move(dst, src);
643 }
644 
Move(DoubleRegister dst,DoubleRegister src,ValueType type)645 void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src,
646                             ValueType type) {
647   DCHECK_NE(dst, src);
648   if (type == kWasmF32) {
649     vmov(liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(src));
650   } else if (type == kWasmF64) {
651     vmov(dst, src);
652   } else {
653     DCHECK_EQ(kWasmS128, type);
654     vmov(liftoff::GetSimd128Register(dst), liftoff::GetSimd128Register(src));
655   }
656 }
657 
Spill(int offset,LiftoffRegister reg,ValueType type)658 void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) {
659   RecordUsedSpillOffset(offset);
660   MemOperand dst = liftoff::GetStackSlot(offset);
661   switch (type.kind()) {
662     case ValueType::kI32:
663       str(reg.gp(), dst);
664       break;
665     case ValueType::kI64:
666       str(reg.low_gp(), liftoff::GetHalfStackSlot(offset, kLowWord));
667       str(reg.high_gp(), liftoff::GetHalfStackSlot(offset, kHighWord));
668       break;
669     case ValueType::kF32:
670       vstr(liftoff::GetFloatRegister(reg.fp()), dst);
671       break;
672     case ValueType::kF64:
673       vstr(reg.fp(), dst);
674       break;
675     case ValueType::kS128: {
676       UseScratchRegisterScope temps(this);
677       Register addr = liftoff::CalculateActualAddress(this, &temps, dst.rn(),
678                                                       no_reg, dst.offset());
679       vst1(Neon8, NeonListOperand(reg.low_fp(), 2), NeonMemOperand(addr));
680       break;
681     }
682     default:
683       UNREACHABLE();
684   }
685 }
686 
Spill(int offset,WasmValue value)687 void LiftoffAssembler::Spill(int offset, WasmValue value) {
688   RecordUsedSpillOffset(offset);
689   MemOperand dst = liftoff::GetStackSlot(offset);
690   UseScratchRegisterScope temps(this);
691   Register src = no_reg;
692   // The scratch register will be required by str if multiple instructions
693   // are required to encode the offset, and so we cannot use it in that case.
694   if (!ImmediateFitsAddrMode2Instruction(dst.offset())) {
695     src = GetUnusedRegister(kGpReg).gp();
696   } else {
697     src = temps.Acquire();
698   }
699   switch (value.type().kind()) {
700     case ValueType::kI32:
701       mov(src, Operand(value.to_i32()));
702       str(src, dst);
703       break;
704     case ValueType::kI64: {
705       int32_t low_word = value.to_i64();
706       mov(src, Operand(low_word));
707       str(src, liftoff::GetHalfStackSlot(offset, kLowWord));
708       int32_t high_word = value.to_i64() >> 32;
709       mov(src, Operand(high_word));
710       str(src, liftoff::GetHalfStackSlot(offset, kHighWord));
711       break;
712     }
713     default:
714       // We do not track f32 and f64 constants, hence they are unreachable.
715       UNREACHABLE();
716   }
717 }
718 
Fill(LiftoffRegister reg,int offset,ValueType type)719 void LiftoffAssembler::Fill(LiftoffRegister reg, int offset, ValueType type) {
720   switch (type.kind()) {
721     case ValueType::kI32:
722       ldr(reg.gp(), liftoff::GetStackSlot(offset));
723       break;
724     case ValueType::kI64:
725       ldr(reg.low_gp(), liftoff::GetHalfStackSlot(offset, kLowWord));
726       ldr(reg.high_gp(), liftoff::GetHalfStackSlot(offset, kHighWord));
727       break;
728     case ValueType::kF32:
729       vldr(liftoff::GetFloatRegister(reg.fp()), liftoff::GetStackSlot(offset));
730       break;
731     case ValueType::kF64:
732       vldr(reg.fp(), liftoff::GetStackSlot(offset));
733       break;
734     case ValueType::kS128: {
735       // Get memory address of slot to fill from.
736       MemOperand slot = liftoff::GetStackSlot(offset);
737       UseScratchRegisterScope temps(this);
738       Register addr = liftoff::CalculateActualAddress(this, &temps, slot.rn(),
739                                                       no_reg, slot.offset());
740       vld1(Neon8, NeonListOperand(reg.low_fp(), 2), NeonMemOperand(addr));
741       break;
742     }
743     default:
744       UNREACHABLE();
745   }
746 }
747 
FillI64Half(Register reg,int offset,RegPairHalf half)748 void LiftoffAssembler::FillI64Half(Register reg, int offset, RegPairHalf half) {
749   ldr(reg, liftoff::GetHalfStackSlot(offset, half));
750 }
751 
FillStackSlotsWithZero(int start,int size)752 void LiftoffAssembler::FillStackSlotsWithZero(int start, int size) {
753   DCHECK_LT(0, size);
754   DCHECK_EQ(0, size % 4);
755   RecordUsedSpillOffset(start + size);
756 
757   // We need a zero reg. Always use r0 for that, and push it before to restore
758   // its value afterwards.
759   push(r0);
760   mov(r0, Operand(0));
761 
762   if (size <= 36) {
763     // Special straight-line code for up to 9 words. Generates one
764     // instruction per word.
765     for (int offset = 4; offset <= size; offset += 4) {
766       str(r0, liftoff::GetHalfStackSlot(start + offset, kLowWord));
767     }
768   } else {
769     // General case for bigger counts (9 instructions).
770     // Use r1 for start address (inclusive), r2 for end address (exclusive).
771     push(r1);
772     push(r2);
773     sub(r1, fp, Operand(start + size));
774     sub(r2, fp, Operand(start));
775 
776     Label loop;
777     bind(&loop);
778     str(r0, MemOperand(r1, /* offset */ kSystemPointerSize, PostIndex));
779     cmp(r1, r2);
780     b(&loop, ne);
781 
782     pop(r2);
783     pop(r1);
784   }
785 
786   pop(r0);
787 }
788 
789 #define I32_BINOP(name, instruction)                             \
790   void LiftoffAssembler::emit_##name(Register dst, Register lhs, \
791                                      Register rhs) {             \
792     instruction(dst, lhs, rhs);                                  \
793   }
794 #define I32_BINOP_I(name, instruction)                           \
795   I32_BINOP(name, instruction)                                   \
796   void LiftoffAssembler::emit_##name(Register dst, Register lhs, \
797                                      int32_t imm) {              \
798     instruction(dst, lhs, Operand(imm));                         \
799   }
800 #define I32_SHIFTOP(name, instruction)                           \
801   void LiftoffAssembler::emit_##name(Register dst, Register src, \
802                                      Register amount) {          \
803     UseScratchRegisterScope temps(this);                         \
804     Register scratch = temps.Acquire();                          \
805     and_(scratch, amount, Operand(0x1f));                        \
806     instruction(dst, src, Operand(scratch));                     \
807   }                                                              \
808   void LiftoffAssembler::emit_##name(Register dst, Register src, \
809                                      int32_t amount) {           \
810     if (V8_LIKELY((amount & 31) != 0)) {                         \
811       instruction(dst, src, Operand(amount & 31));               \
812     } else if (dst != src) {                                     \
813       mov(dst, src);                                             \
814     }                                                            \
815   }
816 #define FP32_UNOP(name, instruction)                                           \
817   void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \
818     instruction(liftoff::GetFloatRegister(dst),                                \
819                 liftoff::GetFloatRegister(src));                               \
820   }
821 #define FP32_BINOP(name, instruction)                                        \
822   void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \
823                                      DoubleRegister rhs) {                   \
824     instruction(liftoff::GetFloatRegister(dst),                              \
825                 liftoff::GetFloatRegister(lhs),                              \
826                 liftoff::GetFloatRegister(rhs));                             \
827   }
828 #define FP64_UNOP(name, instruction)                                           \
829   void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \
830     instruction(dst, src);                                                     \
831   }
832 #define FP64_BINOP(name, instruction)                                        \
833   void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \
834                                      DoubleRegister rhs) {                   \
835     instruction(dst, lhs, rhs);                                              \
836   }
837 
I32_BINOP_I(i32_add,add)838 I32_BINOP_I(i32_add, add)
839 I32_BINOP(i32_sub, sub)
840 I32_BINOP(i32_mul, mul)
841 I32_BINOP_I(i32_and, and_)
842 I32_BINOP_I(i32_or, orr)
843 I32_BINOP_I(i32_xor, eor)
844 I32_SHIFTOP(i32_shl, lsl)
845 I32_SHIFTOP(i32_sar, asr)
846 I32_SHIFTOP(i32_shr, lsr)
847 FP32_BINOP(f32_add, vadd)
848 FP32_BINOP(f32_sub, vsub)
849 FP32_BINOP(f32_mul, vmul)
850 FP32_BINOP(f32_div, vdiv)
851 FP32_UNOP(f32_abs, vabs)
852 FP32_UNOP(f32_neg, vneg)
853 FP32_UNOP(f32_sqrt, vsqrt)
854 FP64_BINOP(f64_add, vadd)
855 FP64_BINOP(f64_sub, vsub)
856 FP64_BINOP(f64_mul, vmul)
857 FP64_BINOP(f64_div, vdiv)
858 FP64_UNOP(f64_abs, vabs)
859 FP64_UNOP(f64_neg, vneg)
860 FP64_UNOP(f64_sqrt, vsqrt)
861 
862 #undef I32_BINOP
863 #undef I32_SHIFTOP
864 #undef FP32_UNOP
865 #undef FP32_BINOP
866 #undef FP64_UNOP
867 #undef FP64_BINOP
868 
869 void LiftoffAssembler::emit_i32_clz(Register dst, Register src) {
870   clz(dst, src);
871 }
872 
emit_i32_ctz(Register dst,Register src)873 void LiftoffAssembler::emit_i32_ctz(Register dst, Register src) {
874   rbit(dst, src);
875   clz(dst, dst);
876 }
877 
878 namespace liftoff {
GeneratePopCnt(Assembler * assm,Register dst,Register src,Register scratch1,Register scratch2)879 inline void GeneratePopCnt(Assembler* assm, Register dst, Register src,
880                            Register scratch1, Register scratch2) {
881   DCHECK(!AreAliased(dst, scratch1, scratch2));
882   if (src == scratch1) std::swap(scratch1, scratch2);
883   // x = x - ((x & (0x55555555 << 1)) >> 1)
884   assm->and_(scratch1, src, Operand(0xaaaaaaaa));
885   assm->sub(dst, src, Operand(scratch1, LSR, 1));
886   // x = (x & 0x33333333) + ((x & (0x33333333 << 2)) >> 2)
887   assm->mov(scratch1, Operand(0x33333333));
888   assm->and_(scratch2, dst, Operand(scratch1, LSL, 2));
889   assm->and_(scratch1, dst, scratch1);
890   assm->add(dst, scratch1, Operand(scratch2, LSR, 2));
891   // x = (x + (x >> 4)) & 0x0F0F0F0F
892   assm->add(dst, dst, Operand(dst, LSR, 4));
893   assm->and_(dst, dst, Operand(0x0f0f0f0f));
894   // x = x + (x >> 8)
895   assm->add(dst, dst, Operand(dst, LSR, 8));
896   // x = x + (x >> 16)
897   assm->add(dst, dst, Operand(dst, LSR, 16));
898   // x = x & 0x3F
899   assm->and_(dst, dst, Operand(0x3f));
900 }
901 }  // namespace liftoff
902 
emit_i32_popcnt(Register dst,Register src)903 bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) {
904   LiftoffRegList pinned = LiftoffRegList::ForRegs(dst);
905   Register scratch1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp();
906   Register scratch2 = GetUnusedRegister(kGpReg, pinned).gp();
907   liftoff::GeneratePopCnt(this, dst, src, scratch1, scratch2);
908   return true;
909 }
910 
emit_i32_divs(Register dst,Register lhs,Register rhs,Label * trap_div_by_zero,Label * trap_div_unrepresentable)911 void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
912                                      Label* trap_div_by_zero,
913                                      Label* trap_div_unrepresentable) {
914   if (!CpuFeatures::IsSupported(SUDIV)) {
915     bailout(kMissingCPUFeature, "i32_divs");
916     return;
917   }
918   CpuFeatureScope scope(this, SUDIV);
919   // Issue division early so we can perform the trapping checks whilst it
920   // completes.
921   bool speculative_sdiv = dst != lhs && dst != rhs;
922   if (speculative_sdiv) {
923     sdiv(dst, lhs, rhs);
924   }
925   Label noTrap;
926   // Check for division by zero.
927   cmp(rhs, Operand(0));
928   b(trap_div_by_zero, eq);
929   // Check for kMinInt / -1. This is unrepresentable.
930   cmp(rhs, Operand(-1));
931   b(&noTrap, ne);
932   cmp(lhs, Operand(kMinInt));
933   b(trap_div_unrepresentable, eq);
934   bind(&noTrap);
935   if (!speculative_sdiv) {
936     sdiv(dst, lhs, rhs);
937   }
938 }
939 
emit_i32_divu(Register dst,Register lhs,Register rhs,Label * trap_div_by_zero)940 void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
941                                      Label* trap_div_by_zero) {
942   if (!CpuFeatures::IsSupported(SUDIV)) {
943     bailout(kMissingCPUFeature, "i32_divu");
944     return;
945   }
946   CpuFeatureScope scope(this, SUDIV);
947   // Check for division by zero.
948   cmp(rhs, Operand(0));
949   b(trap_div_by_zero, eq);
950   udiv(dst, lhs, rhs);
951 }
952 
emit_i32_rems(Register dst,Register lhs,Register rhs,Label * trap_div_by_zero)953 void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
954                                      Label* trap_div_by_zero) {
955   if (!CpuFeatures::IsSupported(SUDIV)) {
956     // When this case is handled, a check for ARMv7 is required to use mls.
957     // Mls support is implied with SUDIV support.
958     bailout(kMissingCPUFeature, "i32_rems");
959     return;
960   }
961   CpuFeatureScope scope(this, SUDIV);
962   // No need to check kMinInt / -1 because the result is kMinInt and then
963   // kMinInt * -1 -> kMinInt. In this case, the Msub result is therefore 0.
964   UseScratchRegisterScope temps(this);
965   Register scratch = temps.Acquire();
966   sdiv(scratch, lhs, rhs);
967   // Check for division by zero.
968   cmp(rhs, Operand(0));
969   b(trap_div_by_zero, eq);
970   // Compute remainder.
971   mls(dst, scratch, rhs, lhs);
972 }
973 
emit_i32_remu(Register dst,Register lhs,Register rhs,Label * trap_div_by_zero)974 void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
975                                      Label* trap_div_by_zero) {
976   if (!CpuFeatures::IsSupported(SUDIV)) {
977     // When this case is handled, a check for ARMv7 is required to use mls.
978     // Mls support is implied with SUDIV support.
979     bailout(kMissingCPUFeature, "i32_remu");
980     return;
981   }
982   CpuFeatureScope scope(this, SUDIV);
983   // No need to check kMinInt / -1 because the result is kMinInt and then
984   // kMinInt * -1 -> kMinInt. In this case, the Msub result is therefore 0.
985   UseScratchRegisterScope temps(this);
986   Register scratch = temps.Acquire();
987   udiv(scratch, lhs, rhs);
988   // Check for division by zero.
989   cmp(rhs, Operand(0));
990   b(trap_div_by_zero, eq);
991   // Compute remainder.
992   mls(dst, scratch, rhs, lhs);
993 }
994 
emit_i64_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)995 void LiftoffAssembler::emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs,
996                                     LiftoffRegister rhs) {
997   liftoff::I64Binop<&Assembler::add, &Assembler::adc>(this, dst, lhs, rhs);
998 }
999 
emit_i64_add(LiftoffRegister dst,LiftoffRegister lhs,int32_t imm)1000 void LiftoffAssembler::emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs,
1001                                     int32_t imm) {
1002   liftoff::I64BinopI<&Assembler::add, &Assembler::adc>(this, dst, lhs, imm);
1003 }
1004 
emit_i64_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1005 void LiftoffAssembler::emit_i64_sub(LiftoffRegister dst, LiftoffRegister lhs,
1006                                     LiftoffRegister rhs) {
1007   liftoff::I64Binop<&Assembler::sub, &Assembler::sbc>(this, dst, lhs, rhs);
1008 }
1009 
emit_i64_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1010 void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs,
1011                                     LiftoffRegister rhs) {
1012   // Idea:
1013   //        [           lhs_hi  |           lhs_lo  ] * [  rhs_hi  |  rhs_lo  ]
1014   //    =   [  lhs_hi * rhs_lo  |                   ]  (32 bit mul, shift 32)
1015   //      + [  lhs_lo * rhs_hi  |                   ]  (32 bit mul, shift 32)
1016   //      + [             lhs_lo * rhs_lo           ]  (32x32->64 mul, shift 0)
1017   UseScratchRegisterScope temps(this);
1018   Register scratch = temps.Acquire();
1019   // scratch = lhs_hi * rhs_lo
1020   mul(scratch, lhs.high_gp(), rhs.low_gp());
1021   // scratch += lhs_lo * rhs_hi
1022   mla(scratch, lhs.low_gp(), rhs.high_gp(), scratch);
1023   // TODO(arm): use umlal once implemented correctly in the simulator.
1024   // [dst_hi|dst_lo] = lhs_lo * rhs_lo
1025   umull(dst.low_gp(), dst.high_gp(), lhs.low_gp(), rhs.low_gp());
1026   // dst_hi += scratch
1027   add(dst.high_gp(), dst.high_gp(), scratch);
1028 }
1029 
emit_i64_divs(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs,Label * trap_div_by_zero,Label * trap_div_unrepresentable)1030 bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs,
1031                                      LiftoffRegister rhs,
1032                                      Label* trap_div_by_zero,
1033                                      Label* trap_div_unrepresentable) {
1034   return false;
1035 }
1036 
emit_i64_divu(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs,Label * trap_div_by_zero)1037 bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs,
1038                                      LiftoffRegister rhs,
1039                                      Label* trap_div_by_zero) {
1040   return false;
1041 }
1042 
emit_i64_rems(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs,Label * trap_div_by_zero)1043 bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs,
1044                                      LiftoffRegister rhs,
1045                                      Label* trap_div_by_zero) {
1046   return false;
1047 }
1048 
emit_i64_remu(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs,Label * trap_div_by_zero)1049 bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs,
1050                                      LiftoffRegister rhs,
1051                                      Label* trap_div_by_zero) {
1052   return false;
1053 }
1054 
emit_i64_shl(LiftoffRegister dst,LiftoffRegister src,Register amount)1055 void LiftoffAssembler::emit_i64_shl(LiftoffRegister dst, LiftoffRegister src,
1056                                     Register amount) {
1057   liftoff::I64Shiftop<&TurboAssembler::LslPair, true>(this, dst, src, amount);
1058 }
1059 
emit_i64_shl(LiftoffRegister dst,LiftoffRegister src,int32_t amount)1060 void LiftoffAssembler::emit_i64_shl(LiftoffRegister dst, LiftoffRegister src,
1061                                     int32_t amount) {
1062   UseScratchRegisterScope temps(this);
1063   // {src.low_gp()} will still be needed after writing {dst.high_gp()}.
1064   Register src_low =
1065       liftoff::EnsureNoAlias(this, src.low_gp(), dst.high_gp(), &temps);
1066 
1067   LslPair(dst.low_gp(), dst.high_gp(), src_low, src.high_gp(), amount & 63);
1068 }
1069 
emit_i64_sar(LiftoffRegister dst,LiftoffRegister src,Register amount)1070 void LiftoffAssembler::emit_i64_sar(LiftoffRegister dst, LiftoffRegister src,
1071                                     Register amount) {
1072   liftoff::I64Shiftop<&TurboAssembler::AsrPair, false>(this, dst, src, amount);
1073 }
1074 
emit_i64_sar(LiftoffRegister dst,LiftoffRegister src,int32_t amount)1075 void LiftoffAssembler::emit_i64_sar(LiftoffRegister dst, LiftoffRegister src,
1076                                     int32_t amount) {
1077   UseScratchRegisterScope temps(this);
1078   // {src.high_gp()} will still be needed after writing {dst.low_gp()}.
1079   Register src_high =
1080       liftoff::EnsureNoAlias(this, src.high_gp(), dst.low_gp(), &temps);
1081 
1082   AsrPair(dst.low_gp(), dst.high_gp(), src.low_gp(), src_high, amount & 63);
1083 }
1084 
emit_i64_shr(LiftoffRegister dst,LiftoffRegister src,Register amount)1085 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
1086                                     Register amount) {
1087   liftoff::I64Shiftop<&TurboAssembler::LsrPair, false>(this, dst, src, amount);
1088 }
1089 
emit_i64_shr(LiftoffRegister dst,LiftoffRegister src,int32_t amount)1090 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
1091                                     int32_t amount) {
1092   UseScratchRegisterScope temps(this);
1093   // {src.high_gp()} will still be needed after writing {dst.low_gp()}.
1094   Register src_high =
1095       liftoff::EnsureNoAlias(this, src.high_gp(), dst.low_gp(), &temps);
1096 
1097   LsrPair(dst.low_gp(), dst.high_gp(), src.low_gp(), src_high, amount & 63);
1098 }
1099 
emit_i64_clz(LiftoffRegister dst,LiftoffRegister src)1100 void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) {
1101   // return high == 0 ? 32 + CLZ32(low) : CLZ32(high);
1102   Label done;
1103   Label high_is_zero;
1104   cmp(src.high_gp(), Operand(0));
1105   b(&high_is_zero, eq);
1106 
1107   clz(dst.low_gp(), src.high_gp());
1108   jmp(&done);
1109 
1110   bind(&high_is_zero);
1111   clz(dst.low_gp(), src.low_gp());
1112   add(dst.low_gp(), dst.low_gp(), Operand(32));
1113 
1114   bind(&done);
1115   mov(dst.high_gp(), Operand(0));  // High word of result is always 0.
1116 }
1117 
emit_i64_ctz(LiftoffRegister dst,LiftoffRegister src)1118 void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) {
1119   // return low == 0 ? 32 + CTZ32(high) : CTZ32(low);
1120   // CTZ32(x) = CLZ(RBIT(x))
1121   Label done;
1122   Label low_is_zero;
1123   cmp(src.low_gp(), Operand(0));
1124   b(&low_is_zero, eq);
1125 
1126   rbit(dst.low_gp(), src.low_gp());
1127   clz(dst.low_gp(), dst.low_gp());
1128   jmp(&done);
1129 
1130   bind(&low_is_zero);
1131   rbit(dst.low_gp(), src.high_gp());
1132   clz(dst.low_gp(), dst.low_gp());
1133   add(dst.low_gp(), dst.low_gp(), Operand(32));
1134 
1135   bind(&done);
1136   mov(dst.high_gp(), Operand(0));  // High word of result is always 0.
1137 }
1138 
emit_i64_popcnt(LiftoffRegister dst,LiftoffRegister src)1139 bool LiftoffAssembler::emit_i64_popcnt(LiftoffRegister dst,
1140                                        LiftoffRegister src) {
1141   // Produce partial popcnts in the two dst registers, making sure not to
1142   // overwrite the second src register before using it.
1143   Register src1 = src.high_gp() == dst.low_gp() ? src.high_gp() : src.low_gp();
1144   Register src2 = src.high_gp() == dst.low_gp() ? src.low_gp() : src.high_gp();
1145   LiftoffRegList pinned = LiftoffRegList::ForRegs(dst, src2);
1146   Register scratch1 = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp();
1147   Register scratch2 = GetUnusedRegister(kGpReg, pinned).gp();
1148   liftoff::GeneratePopCnt(this, dst.low_gp(), src1, scratch1, scratch2);
1149   liftoff::GeneratePopCnt(this, dst.high_gp(), src2, scratch1, scratch2);
1150   // Now add the two into the lower dst reg and clear the higher dst reg.
1151   add(dst.low_gp(), dst.low_gp(), dst.high_gp());
1152   mov(dst.high_gp(), Operand(0));
1153   return true;
1154 }
1155 
emit_f32_ceil(DoubleRegister dst,DoubleRegister src)1156 bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) {
1157   if (CpuFeatures::IsSupported(ARMv8)) {
1158     CpuFeatureScope scope(this, ARMv8);
1159     vrintp(liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(src));
1160     return true;
1161   }
1162   return false;
1163 }
1164 
emit_f32_floor(DoubleRegister dst,DoubleRegister src)1165 bool LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) {
1166   if (CpuFeatures::IsSupported(ARMv8)) {
1167     CpuFeatureScope scope(this, ARMv8);
1168     vrintm(liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(src));
1169     return true;
1170   }
1171   return false;
1172 }
1173 
emit_f32_trunc(DoubleRegister dst,DoubleRegister src)1174 bool LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) {
1175   if (CpuFeatures::IsSupported(ARMv8)) {
1176     CpuFeatureScope scope(this, ARMv8);
1177     vrintz(liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(src));
1178     return true;
1179   }
1180   return false;
1181 }
1182 
emit_f32_nearest_int(DoubleRegister dst,DoubleRegister src)1183 bool LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst,
1184                                             DoubleRegister src) {
1185   if (CpuFeatures::IsSupported(ARMv8)) {
1186     CpuFeatureScope scope(this, ARMv8);
1187     vrintn(liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(src));
1188     return true;
1189   }
1190   return false;
1191 }
1192 
emit_f32_min(DoubleRegister dst,DoubleRegister lhs,DoubleRegister rhs)1193 void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs,
1194                                     DoubleRegister rhs) {
1195   liftoff::EmitFloatMinOrMax(
1196       this, liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(lhs),
1197       liftoff::GetFloatRegister(rhs), liftoff::MinOrMax::kMin);
1198 }
1199 
emit_f32_max(DoubleRegister dst,DoubleRegister lhs,DoubleRegister rhs)1200 void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
1201                                     DoubleRegister rhs) {
1202   liftoff::EmitFloatMinOrMax(
1203       this, liftoff::GetFloatRegister(dst), liftoff::GetFloatRegister(lhs),
1204       liftoff::GetFloatRegister(rhs), liftoff::MinOrMax::kMax);
1205 }
1206 
emit_f64_ceil(DoubleRegister dst,DoubleRegister src)1207 bool LiftoffAssembler::emit_f64_ceil(DoubleRegister dst, DoubleRegister src) {
1208   if (CpuFeatures::IsSupported(ARMv8)) {
1209     CpuFeatureScope scope(this, ARMv8);
1210     vrintp(dst, src);
1211     return true;
1212   }
1213   return false;
1214 }
1215 
emit_f64_floor(DoubleRegister dst,DoubleRegister src)1216 bool LiftoffAssembler::emit_f64_floor(DoubleRegister dst, DoubleRegister src) {
1217   if (CpuFeatures::IsSupported(ARMv8)) {
1218     CpuFeatureScope scope(this, ARMv8);
1219     vrintm(dst, src);
1220     return true;
1221   }
1222   return false;
1223 }
1224 
emit_f64_trunc(DoubleRegister dst,DoubleRegister src)1225 bool LiftoffAssembler::emit_f64_trunc(DoubleRegister dst, DoubleRegister src) {
1226   if (CpuFeatures::IsSupported(ARMv8)) {
1227     CpuFeatureScope scope(this, ARMv8);
1228     vrintz(dst, src);
1229     return true;
1230   }
1231   return false;
1232 }
1233 
emit_f64_nearest_int(DoubleRegister dst,DoubleRegister src)1234 bool LiftoffAssembler::emit_f64_nearest_int(DoubleRegister dst,
1235                                             DoubleRegister src) {
1236   if (CpuFeatures::IsSupported(ARMv8)) {
1237     CpuFeatureScope scope(this, ARMv8);
1238     vrintn(dst, src);
1239     return true;
1240   }
1241   return false;
1242 }
1243 
emit_f64_min(DoubleRegister dst,DoubleRegister lhs,DoubleRegister rhs)1244 void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
1245                                     DoubleRegister rhs) {
1246   liftoff::EmitFloatMinOrMax(this, dst, lhs, rhs, liftoff::MinOrMax::kMin);
1247 }
1248 
emit_f64_max(DoubleRegister dst,DoubleRegister lhs,DoubleRegister rhs)1249 void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
1250                                     DoubleRegister rhs) {
1251   liftoff::EmitFloatMinOrMax(this, dst, lhs, rhs, liftoff::MinOrMax::kMax);
1252 }
1253 
emit_u32_to_intptr(Register dst,Register src)1254 void LiftoffAssembler::emit_u32_to_intptr(Register dst, Register src) {
1255   // This is a nop on arm.
1256 }
1257 
emit_f32_copysign(DoubleRegister dst,DoubleRegister lhs,DoubleRegister rhs)1258 void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
1259                                          DoubleRegister rhs) {
1260   constexpr uint32_t kF32SignBit = uint32_t{1} << 31;
1261   UseScratchRegisterScope temps(this);
1262   Register scratch = GetUnusedRegister(kGpReg).gp();
1263   Register scratch2 = temps.Acquire();
1264   VmovLow(scratch, lhs);
1265   // Clear sign bit in {scratch}.
1266   bic(scratch, scratch, Operand(kF32SignBit));
1267   VmovLow(scratch2, rhs);
1268   // Isolate sign bit in {scratch2}.
1269   and_(scratch2, scratch2, Operand(kF32SignBit));
1270   // Combine {scratch2} into {scratch}.
1271   orr(scratch, scratch, scratch2);
1272   VmovLow(dst, scratch);
1273 }
1274 
emit_f64_copysign(DoubleRegister dst,DoubleRegister lhs,DoubleRegister rhs)1275 void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
1276                                          DoubleRegister rhs) {
1277   constexpr uint32_t kF64SignBitHighWord = uint32_t{1} << 31;
1278   // On arm, we cannot hold the whole f64 value in a gp register, so we just
1279   // operate on the upper half (UH).
1280   UseScratchRegisterScope temps(this);
1281   Register scratch = GetUnusedRegister(kGpReg).gp();
1282   Register scratch2 = temps.Acquire();
1283   VmovHigh(scratch, lhs);
1284   // Clear sign bit in {scratch}.
1285   bic(scratch, scratch, Operand(kF64SignBitHighWord));
1286   VmovHigh(scratch2, rhs);
1287   // Isolate sign bit in {scratch2}.
1288   and_(scratch2, scratch2, Operand(kF64SignBitHighWord));
1289   // Combine {scratch2} into {scratch}.
1290   orr(scratch, scratch, scratch2);
1291   vmov(dst, lhs);
1292   VmovHigh(dst, scratch);
1293 }
1294 
emit_type_conversion(WasmOpcode opcode,LiftoffRegister dst,LiftoffRegister src,Label * trap)1295 bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
1296                                             LiftoffRegister dst,
1297                                             LiftoffRegister src, Label* trap) {
1298   switch (opcode) {
1299     case kExprI32ConvertI64:
1300       TurboAssembler::Move(dst.gp(), src.low_gp());
1301       return true;
1302     case kExprI32SConvertF32: {
1303       UseScratchRegisterScope temps(this);
1304       SwVfpRegister scratch_f = temps.AcquireS();
1305       vcvt_s32_f32(
1306           scratch_f,
1307           liftoff::GetFloatRegister(src.fp()));  // f32 -> i32 round to zero.
1308       vmov(dst.gp(), scratch_f);
1309       // Check underflow and NaN.
1310       vmov(scratch_f, Float32(static_cast<float>(INT32_MIN)));
1311       VFPCompareAndSetFlags(liftoff::GetFloatRegister(src.fp()), scratch_f);
1312       b(trap, lt);
1313       // Check overflow.
1314       cmp(dst.gp(), Operand(-1));
1315       b(trap, vs);
1316       return true;
1317     }
1318     case kExprI32UConvertF32: {
1319       UseScratchRegisterScope temps(this);
1320       SwVfpRegister scratch_f = temps.AcquireS();
1321       vcvt_u32_f32(
1322           scratch_f,
1323           liftoff::GetFloatRegister(src.fp()));  // f32 -> i32 round to zero.
1324       vmov(dst.gp(), scratch_f);
1325       // Check underflow and NaN.
1326       vmov(scratch_f, Float32(-1.0f));
1327       VFPCompareAndSetFlags(liftoff::GetFloatRegister(src.fp()), scratch_f);
1328       b(trap, le);
1329       // Check overflow.
1330       cmp(dst.gp(), Operand(-1));
1331       b(trap, eq);
1332       return true;
1333     }
1334     case kExprI32SConvertF64: {
1335       UseScratchRegisterScope temps(this);
1336       SwVfpRegister scratch_f = temps.AcquireS();
1337       vcvt_s32_f64(scratch_f, src.fp());  // f64 -> i32 round to zero.
1338       vmov(dst.gp(), scratch_f);
1339       // Check underflow and NaN.
1340       DwVfpRegister scratch_d = temps.AcquireD();
1341       vmov(scratch_d, Double(static_cast<double>(INT32_MIN - 1.0)));
1342       VFPCompareAndSetFlags(src.fp(), scratch_d);
1343       b(trap, le);
1344       // Check overflow.
1345       vmov(scratch_d, Double(static_cast<double>(INT32_MAX + 1.0)));
1346       VFPCompareAndSetFlags(src.fp(), scratch_d);
1347       b(trap, ge);
1348       return true;
1349     }
1350     case kExprI32UConvertF64: {
1351       UseScratchRegisterScope temps(this);
1352       SwVfpRegister scratch_f = temps.AcquireS();
1353       vcvt_u32_f64(scratch_f, src.fp());  // f64 -> i32 round to zero.
1354       vmov(dst.gp(), scratch_f);
1355       // Check underflow and NaN.
1356       DwVfpRegister scratch_d = temps.AcquireD();
1357       vmov(scratch_d, Double(static_cast<double>(-1.0)));
1358       VFPCompareAndSetFlags(src.fp(), scratch_d);
1359       b(trap, le);
1360       // Check overflow.
1361       vmov(scratch_d, Double(static_cast<double>(UINT32_MAX + 1.0)));
1362       VFPCompareAndSetFlags(src.fp(), scratch_d);
1363       b(trap, ge);
1364       return true;
1365     }
1366     case kExprI32ReinterpretF32:
1367       vmov(dst.gp(), liftoff::GetFloatRegister(src.fp()));
1368       return true;
1369     case kExprI64SConvertI32:
1370       if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
1371       mov(dst.high_gp(), Operand(src.gp(), ASR, 31));
1372       return true;
1373     case kExprI64UConvertI32:
1374       if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
1375       mov(dst.high_gp(), Operand(0));
1376       return true;
1377     case kExprI64ReinterpretF64:
1378       vmov(dst.low_gp(), dst.high_gp(), src.fp());
1379       return true;
1380     case kExprF32SConvertI32: {
1381       SwVfpRegister dst_float = liftoff::GetFloatRegister(dst.fp());
1382       vmov(dst_float, src.gp());
1383       vcvt_f32_s32(dst_float, dst_float);
1384       return true;
1385     }
1386     case kExprF32UConvertI32: {
1387       SwVfpRegister dst_float = liftoff::GetFloatRegister(dst.fp());
1388       vmov(dst_float, src.gp());
1389       vcvt_f32_u32(dst_float, dst_float);
1390       return true;
1391     }
1392     case kExprF32ConvertF64:
1393       vcvt_f32_f64(liftoff::GetFloatRegister(dst.fp()), src.fp());
1394       return true;
1395     case kExprF32ReinterpretI32:
1396       vmov(liftoff::GetFloatRegister(dst.fp()), src.gp());
1397       return true;
1398     case kExprF64SConvertI32: {
1399       vmov(liftoff::GetFloatRegister(dst.fp()), src.gp());
1400       vcvt_f64_s32(dst.fp(), liftoff::GetFloatRegister(dst.fp()));
1401       return true;
1402     }
1403     case kExprF64UConvertI32: {
1404       vmov(liftoff::GetFloatRegister(dst.fp()), src.gp());
1405       vcvt_f64_u32(dst.fp(), liftoff::GetFloatRegister(dst.fp()));
1406       return true;
1407     }
1408     case kExprF64ConvertF32:
1409       vcvt_f64_f32(dst.fp(), liftoff::GetFloatRegister(src.fp()));
1410       return true;
1411     case kExprF64ReinterpretI64:
1412       vmov(dst.fp(), src.low_gp(), src.high_gp());
1413       return true;
1414     case kExprF64SConvertI64:
1415     case kExprF64UConvertI64:
1416     case kExprI64SConvertF32:
1417     case kExprI64UConvertF32:
1418     case kExprF32SConvertI64:
1419     case kExprF32UConvertI64:
1420     case kExprI64SConvertF64:
1421     case kExprI64UConvertF64:
1422       // These cases can be handled by the C fallback function.
1423       return false;
1424     default:
1425       UNREACHABLE();
1426   }
1427 }
1428 
emit_i32_signextend_i8(Register dst,Register src)1429 void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) {
1430   sxtb(dst, src);
1431 }
1432 
emit_i32_signextend_i16(Register dst,Register src)1433 void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) {
1434   sxth(dst, src);
1435 }
1436 
emit_i64_signextend_i8(LiftoffRegister dst,LiftoffRegister src)1437 void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst,
1438                                               LiftoffRegister src) {
1439   emit_i32_signextend_i8(dst.low_gp(), src.low_gp());
1440   mov(dst.high_gp(), Operand(dst.low_gp(), ASR, 31));
1441 }
1442 
emit_i64_signextend_i16(LiftoffRegister dst,LiftoffRegister src)1443 void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst,
1444                                                LiftoffRegister src) {
1445   emit_i32_signextend_i16(dst.low_gp(), src.low_gp());
1446   mov(dst.high_gp(), Operand(dst.low_gp(), ASR, 31));
1447 }
1448 
emit_i64_signextend_i32(LiftoffRegister dst,LiftoffRegister src)1449 void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst,
1450                                                LiftoffRegister src) {
1451   TurboAssembler::Move(dst.low_gp(), src.low_gp());
1452   mov(dst.high_gp(), Operand(src.low_gp(), ASR, 31));
1453 }
1454 
emit_jump(Label * label)1455 void LiftoffAssembler::emit_jump(Label* label) { b(label); }
1456 
emit_jump(Register target)1457 void LiftoffAssembler::emit_jump(Register target) { bx(target); }
1458 
emit_cond_jump(Condition cond,Label * label,ValueType type,Register lhs,Register rhs)1459 void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label,
1460                                       ValueType type, Register lhs,
1461                                       Register rhs) {
1462   DCHECK_EQ(type, kWasmI32);
1463   if (rhs == no_reg) {
1464     cmp(lhs, Operand(0));
1465   } else {
1466     cmp(lhs, rhs);
1467   }
1468   b(label, cond);
1469 }
1470 
emit_i32_eqz(Register dst,Register src)1471 void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
1472   clz(dst, src);
1473   mov(dst, Operand(dst, LSR, kRegSizeInBitsLog2));
1474 }
1475 
emit_i32_set_cond(Condition cond,Register dst,Register lhs,Register rhs)1476 void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst,
1477                                          Register lhs, Register rhs) {
1478   cmp(lhs, rhs);
1479   mov(dst, Operand(0), LeaveCC);
1480   mov(dst, Operand(1), LeaveCC, cond);
1481 }
1482 
emit_i64_eqz(Register dst,LiftoffRegister src)1483 void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) {
1484   orr(dst, src.low_gp(), src.high_gp());
1485   clz(dst, dst);
1486   mov(dst, Operand(dst, LSR, 5));
1487 }
1488 
emit_i64_set_cond(Condition cond,Register dst,LiftoffRegister lhs,LiftoffRegister rhs)1489 void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst,
1490                                          LiftoffRegister lhs,
1491                                          LiftoffRegister rhs) {
1492   // For signed i64 comparisons, we still need to use unsigned comparison for
1493   // the low word (the only bit carrying signedness information is the MSB in
1494   // the high word).
1495   Condition unsigned_cond = liftoff::MakeUnsigned(cond);
1496   Label set_cond;
1497   Label cont;
1498   LiftoffRegister dest = LiftoffRegister(dst);
1499   bool speculative_move = !dest.overlaps(lhs) && !dest.overlaps(rhs);
1500   if (speculative_move) {
1501     mov(dst, Operand(0));
1502   }
1503   // Compare high word first. If it differs, use it for the set_cond. If it's
1504   // equal, compare the low word and use that for set_cond.
1505   cmp(lhs.high_gp(), rhs.high_gp());
1506   if (unsigned_cond == cond) {
1507     cmp(lhs.low_gp(), rhs.low_gp(), kEqual);
1508     if (!speculative_move) {
1509       mov(dst, Operand(0));
1510     }
1511     mov(dst, Operand(1), LeaveCC, cond);
1512   } else {
1513     // If the condition predicate for the low differs from that for the high
1514     // word, the conditional move instructions must be separated.
1515     b(ne, &set_cond);
1516     cmp(lhs.low_gp(), rhs.low_gp());
1517     if (!speculative_move) {
1518       mov(dst, Operand(0));
1519     }
1520     mov(dst, Operand(1), LeaveCC, unsigned_cond);
1521     b(&cont);
1522     bind(&set_cond);
1523     if (!speculative_move) {
1524       mov(dst, Operand(0));
1525     }
1526     mov(dst, Operand(1), LeaveCC, cond);
1527     bind(&cont);
1528   }
1529 }
1530 
emit_f32_set_cond(Condition cond,Register dst,DoubleRegister lhs,DoubleRegister rhs)1531 void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst,
1532                                          DoubleRegister lhs,
1533                                          DoubleRegister rhs) {
1534   VFPCompareAndSetFlags(liftoff::GetFloatRegister(lhs),
1535                         liftoff::GetFloatRegister(rhs));
1536   mov(dst, Operand(0), LeaveCC);
1537   mov(dst, Operand(1), LeaveCC, cond);
1538   if (cond != ne) {
1539     // If V flag set, at least one of the arguments was a Nan -> false.
1540     mov(dst, Operand(0), LeaveCC, vs);
1541   }
1542 }
1543 
emit_f64_set_cond(Condition cond,Register dst,DoubleRegister lhs,DoubleRegister rhs)1544 void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst,
1545                                          DoubleRegister lhs,
1546                                          DoubleRegister rhs) {
1547   VFPCompareAndSetFlags(lhs, rhs);
1548   mov(dst, Operand(0), LeaveCC);
1549   mov(dst, Operand(1), LeaveCC, cond);
1550   if (cond != ne) {
1551     // If V flag set, at least one of the arguments was a Nan -> false.
1552     mov(dst, Operand(0), LeaveCC, vs);
1553   }
1554 }
1555 
emit_f64x2_splat(LiftoffRegister dst,LiftoffRegister src)1556 void LiftoffAssembler::emit_f64x2_splat(LiftoffRegister dst,
1557                                         LiftoffRegister src) {
1558   TurboAssembler::Move(dst.low_fp(), src.fp());
1559   TurboAssembler::Move(dst.high_fp(), src.fp());
1560 }
1561 
emit_f64x2_extract_lane(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1562 void LiftoffAssembler::emit_f64x2_extract_lane(LiftoffRegister dst,
1563                                                LiftoffRegister lhs,
1564                                                uint8_t imm_lane_idx) {
1565   ExtractLane(dst.fp(), liftoff::GetSimd128Register(lhs.low_fp()),
1566               imm_lane_idx);
1567 }
1568 
emit_f64x2_replace_lane(LiftoffRegister dst,LiftoffRegister src1,LiftoffRegister src2,uint8_t imm_lane_idx)1569 void LiftoffAssembler::emit_f64x2_replace_lane(LiftoffRegister dst,
1570                                                LiftoffRegister src1,
1571                                                LiftoffRegister src2,
1572                                                uint8_t imm_lane_idx) {
1573   ReplaceLane(liftoff::GetSimd128Register(dst.low_fp()),
1574               liftoff::GetSimd128Register(src1.low_fp()), src2.fp(),
1575               imm_lane_idx);
1576 }
1577 
emit_f64x2_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1578 void LiftoffAssembler::emit_f64x2_add(LiftoffRegister dst, LiftoffRegister lhs,
1579                                       LiftoffRegister rhs) {
1580   vadd(dst.low_fp(), lhs.low_fp(), rhs.low_fp());
1581   vadd(dst.high_fp(), lhs.high_fp(), rhs.high_fp());
1582 }
1583 
emit_f64x2_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1584 void LiftoffAssembler::emit_f64x2_sub(LiftoffRegister dst, LiftoffRegister lhs,
1585                                       LiftoffRegister rhs) {
1586   vsub(dst.low_fp(), lhs.low_fp(), rhs.low_fp());
1587   vsub(dst.high_fp(), lhs.high_fp(), rhs.high_fp());
1588 }
1589 
emit_f64x2_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1590 void LiftoffAssembler::emit_f64x2_mul(LiftoffRegister dst, LiftoffRegister lhs,
1591                                       LiftoffRegister rhs) {
1592   bailout(kSimd, "f64x2mul");
1593 }
1594 
emit_f32x4_splat(LiftoffRegister dst,LiftoffRegister src)1595 void LiftoffAssembler::emit_f32x4_splat(LiftoffRegister dst,
1596                                         LiftoffRegister src) {
1597   vdup(Neon32, liftoff::GetSimd128Register(dst.low_fp()), src.fp(), 0);
1598 }
1599 
emit_f32x4_extract_lane(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1600 void LiftoffAssembler::emit_f32x4_extract_lane(LiftoffRegister dst,
1601                                                LiftoffRegister lhs,
1602                                                uint8_t imm_lane_idx) {
1603   ExtractLane(liftoff::GetFloatRegister(dst.fp()),
1604               liftoff::GetSimd128Register(lhs.low_fp()), imm_lane_idx);
1605 }
1606 
emit_f32x4_replace_lane(LiftoffRegister dst,LiftoffRegister src1,LiftoffRegister src2,uint8_t imm_lane_idx)1607 void LiftoffAssembler::emit_f32x4_replace_lane(LiftoffRegister dst,
1608                                                LiftoffRegister src1,
1609                                                LiftoffRegister src2,
1610                                                uint8_t imm_lane_idx) {
1611   ReplaceLane(liftoff::GetSimd128Register(dst.low_fp()),
1612               liftoff::GetSimd128Register(src1.low_fp()),
1613               liftoff::GetFloatRegister(src2.fp()), imm_lane_idx);
1614 }
1615 
emit_f32x4_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1616 void LiftoffAssembler::emit_f32x4_add(LiftoffRegister dst, LiftoffRegister lhs,
1617                                       LiftoffRegister rhs) {
1618   vadd(liftoff::GetSimd128Register(dst.low_fp()),
1619        liftoff::GetSimd128Register(lhs.low_fp()),
1620        liftoff::GetSimd128Register(rhs.low_fp()));
1621 }
1622 
emit_f32x4_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1623 void LiftoffAssembler::emit_f32x4_sub(LiftoffRegister dst, LiftoffRegister lhs,
1624                                       LiftoffRegister rhs) {
1625   vsub(liftoff::GetSimd128Register(dst.low_fp()),
1626        liftoff::GetSimd128Register(lhs.low_fp()),
1627        liftoff::GetSimd128Register(rhs.low_fp()));
1628 }
1629 
emit_f32x4_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1630 void LiftoffAssembler::emit_f32x4_mul(LiftoffRegister dst, LiftoffRegister lhs,
1631                                       LiftoffRegister rhs) {
1632   bailout(kSimd, "f32x4mul");
1633 }
1634 
emit_i64x2_splat(LiftoffRegister dst,LiftoffRegister src)1635 void LiftoffAssembler::emit_i64x2_splat(LiftoffRegister dst,
1636                                         LiftoffRegister src) {
1637   Simd128Register dst_simd = liftoff::GetSimd128Register(dst.low_fp());
1638   vdup(Neon32, dst_simd, src.low_gp());
1639   ReplaceLane(dst_simd, dst_simd, src.high_gp(), NeonS32, 1);
1640   ReplaceLane(dst_simd, dst_simd, src.high_gp(), NeonS32, 3);
1641 }
1642 
emit_i64x2_extract_lane(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1643 void LiftoffAssembler::emit_i64x2_extract_lane(LiftoffRegister dst,
1644                                                LiftoffRegister lhs,
1645                                                uint8_t imm_lane_idx) {
1646   ExtractLane(dst.low_gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonS32,
1647               imm_lane_idx * 2);
1648   ExtractLane(dst.high_gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonS32,
1649               imm_lane_idx * 2 + 1);
1650 }
1651 
emit_i64x2_replace_lane(LiftoffRegister dst,LiftoffRegister src1,LiftoffRegister src2,uint8_t imm_lane_idx)1652 void LiftoffAssembler::emit_i64x2_replace_lane(LiftoffRegister dst,
1653                                                LiftoffRegister src1,
1654                                                LiftoffRegister src2,
1655                                                uint8_t imm_lane_idx) {
1656   Simd128Register dst_simd = liftoff::GetSimd128Register(dst.low_fp());
1657   Simd128Register src1_simd = liftoff::GetSimd128Register(src1.low_fp());
1658   ReplaceLane(dst_simd, src1_simd, src2.low_gp(), NeonS32, imm_lane_idx * 2);
1659   ReplaceLane(dst_simd, dst_simd, src2.high_gp(), NeonS32,
1660               imm_lane_idx * 2 + 1);
1661 }
1662 
emit_i64x2_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1663 void LiftoffAssembler::emit_i64x2_add(LiftoffRegister dst, LiftoffRegister lhs,
1664                                       LiftoffRegister rhs) {
1665   vadd(Neon64, liftoff::GetSimd128Register(dst.low_fp()),
1666        liftoff::GetSimd128Register(lhs.low_fp()),
1667        liftoff::GetSimd128Register(rhs.low_fp()));
1668 }
1669 
emit_i64x2_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1670 void LiftoffAssembler::emit_i64x2_sub(LiftoffRegister dst, LiftoffRegister lhs,
1671                                       LiftoffRegister rhs) {
1672   vsub(Neon64, liftoff::GetSimd128Register(dst.low_fp()),
1673        liftoff::GetSimd128Register(lhs.low_fp()),
1674        liftoff::GetSimd128Register(rhs.low_fp()));
1675 }
1676 
emit_i64x2_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1677 void LiftoffAssembler::emit_i64x2_mul(LiftoffRegister dst, LiftoffRegister lhs,
1678                                       LiftoffRegister rhs) {
1679   bailout(kSimd, "i64x2mul");
1680 }
1681 
emit_i32x4_splat(LiftoffRegister dst,LiftoffRegister src)1682 void LiftoffAssembler::emit_i32x4_splat(LiftoffRegister dst,
1683                                         LiftoffRegister src) {
1684   vdup(Neon32, liftoff::GetSimd128Register(dst.low_fp()), src.gp());
1685 }
1686 
emit_i32x4_extract_lane(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1687 void LiftoffAssembler::emit_i32x4_extract_lane(LiftoffRegister dst,
1688                                                LiftoffRegister lhs,
1689                                                uint8_t imm_lane_idx) {
1690   ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonS32,
1691               imm_lane_idx);
1692 }
1693 
emit_i32x4_replace_lane(LiftoffRegister dst,LiftoffRegister src1,LiftoffRegister src2,uint8_t imm_lane_idx)1694 void LiftoffAssembler::emit_i32x4_replace_lane(LiftoffRegister dst,
1695                                                LiftoffRegister src1,
1696                                                LiftoffRegister src2,
1697                                                uint8_t imm_lane_idx) {
1698   ReplaceLane(liftoff::GetSimd128Register(dst.low_fp()),
1699               liftoff::GetSimd128Register(src1.low_fp()), src2.gp(), NeonS32,
1700               imm_lane_idx);
1701 }
1702 
emit_i32x4_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1703 void LiftoffAssembler::emit_i32x4_add(LiftoffRegister dst, LiftoffRegister lhs,
1704                                       LiftoffRegister rhs) {
1705   vadd(Neon32, liftoff::GetSimd128Register(dst.low_fp()),
1706        liftoff::GetSimd128Register(lhs.low_fp()),
1707        liftoff::GetSimd128Register(rhs.low_fp()));
1708 }
1709 
emit_i32x4_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1710 void LiftoffAssembler::emit_i32x4_sub(LiftoffRegister dst, LiftoffRegister lhs,
1711                                       LiftoffRegister rhs) {
1712   vsub(Neon32, liftoff::GetSimd128Register(dst.low_fp()),
1713        liftoff::GetSimd128Register(lhs.low_fp()),
1714        liftoff::GetSimd128Register(rhs.low_fp()));
1715 }
1716 
emit_i32x4_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1717 void LiftoffAssembler::emit_i32x4_mul(LiftoffRegister dst, LiftoffRegister lhs,
1718                                       LiftoffRegister rhs) {
1719   bailout(kSimd, "i32x4mul");
1720 }
1721 
emit_i16x8_splat(LiftoffRegister dst,LiftoffRegister src)1722 void LiftoffAssembler::emit_i16x8_splat(LiftoffRegister dst,
1723                                         LiftoffRegister src) {
1724   vdup(Neon16, liftoff::GetSimd128Register(dst.low_fp()), src.gp());
1725 }
1726 
emit_i16x8_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1727 void LiftoffAssembler::emit_i16x8_add(LiftoffRegister dst, LiftoffRegister lhs,
1728                                       LiftoffRegister rhs) {
1729   vadd(Neon16, liftoff::GetSimd128Register(dst.low_fp()),
1730        liftoff::GetSimd128Register(lhs.low_fp()),
1731        liftoff::GetSimd128Register(rhs.low_fp()));
1732 }
1733 
emit_i16x8_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1734 void LiftoffAssembler::emit_i16x8_sub(LiftoffRegister dst, LiftoffRegister lhs,
1735                                       LiftoffRegister rhs) {
1736   vsub(Neon16, liftoff::GetSimd128Register(dst.low_fp()),
1737        liftoff::GetSimd128Register(lhs.low_fp()),
1738        liftoff::GetSimd128Register(rhs.low_fp()));
1739 }
1740 
emit_i16x8_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1741 void LiftoffAssembler::emit_i16x8_mul(LiftoffRegister dst, LiftoffRegister lhs,
1742                                       LiftoffRegister rhs) {
1743   bailout(kSimd, "i16x8mul");
1744 }
1745 
emit_i16x8_extract_lane_u(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1746 void LiftoffAssembler::emit_i16x8_extract_lane_u(LiftoffRegister dst,
1747                                                  LiftoffRegister lhs,
1748                                                  uint8_t imm_lane_idx) {
1749   ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonU16,
1750               imm_lane_idx);
1751 }
1752 
emit_i16x8_extract_lane_s(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1753 void LiftoffAssembler::emit_i16x8_extract_lane_s(LiftoffRegister dst,
1754                                                  LiftoffRegister lhs,
1755                                                  uint8_t imm_lane_idx) {
1756   ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonS16,
1757               imm_lane_idx);
1758 }
1759 
emit_i16x8_replace_lane(LiftoffRegister dst,LiftoffRegister src1,LiftoffRegister src2,uint8_t imm_lane_idx)1760 void LiftoffAssembler::emit_i16x8_replace_lane(LiftoffRegister dst,
1761                                                LiftoffRegister src1,
1762                                                LiftoffRegister src2,
1763                                                uint8_t imm_lane_idx) {
1764   ReplaceLane(liftoff::GetSimd128Register(dst.low_fp()),
1765               liftoff::GetSimd128Register(src1.low_fp()), src2.gp(), NeonS16,
1766               imm_lane_idx);
1767 }
1768 
emit_i8x16_splat(LiftoffRegister dst,LiftoffRegister src)1769 void LiftoffAssembler::emit_i8x16_splat(LiftoffRegister dst,
1770                                         LiftoffRegister src) {
1771   vdup(Neon8, liftoff::GetSimd128Register(dst.low_fp()), src.gp());
1772 }
1773 
emit_i8x16_extract_lane_u(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1774 void LiftoffAssembler::emit_i8x16_extract_lane_u(LiftoffRegister dst,
1775                                                  LiftoffRegister lhs,
1776                                                  uint8_t imm_lane_idx) {
1777   ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonU8,
1778               imm_lane_idx);
1779 }
1780 
emit_i8x16_extract_lane_s(LiftoffRegister dst,LiftoffRegister lhs,uint8_t imm_lane_idx)1781 void LiftoffAssembler::emit_i8x16_extract_lane_s(LiftoffRegister dst,
1782                                                  LiftoffRegister lhs,
1783                                                  uint8_t imm_lane_idx) {
1784   ExtractLane(dst.gp(), liftoff::GetSimd128Register(lhs.low_fp()), NeonS8,
1785               imm_lane_idx);
1786 }
1787 
emit_i8x16_replace_lane(LiftoffRegister dst,LiftoffRegister src1,LiftoffRegister src2,uint8_t imm_lane_idx)1788 void LiftoffAssembler::emit_i8x16_replace_lane(LiftoffRegister dst,
1789                                                LiftoffRegister src1,
1790                                                LiftoffRegister src2,
1791                                                uint8_t imm_lane_idx) {
1792   ReplaceLane(liftoff::GetSimd128Register(dst.low_fp()),
1793               liftoff::GetSimd128Register(src1.low_fp()), src2.gp(), NeonS8,
1794               imm_lane_idx);
1795 }
1796 
emit_i8x16_add(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1797 void LiftoffAssembler::emit_i8x16_add(LiftoffRegister dst, LiftoffRegister lhs,
1798                                       LiftoffRegister rhs) {
1799   vadd(Neon8, liftoff::GetSimd128Register(dst.low_fp()),
1800        liftoff::GetSimd128Register(lhs.low_fp()),
1801        liftoff::GetSimd128Register(rhs.low_fp()));
1802 }
1803 
emit_i8x16_sub(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1804 void LiftoffAssembler::emit_i8x16_sub(LiftoffRegister dst, LiftoffRegister lhs,
1805                                       LiftoffRegister rhs) {
1806   vsub(Neon8, liftoff::GetSimd128Register(dst.low_fp()),
1807        liftoff::GetSimd128Register(lhs.low_fp()),
1808        liftoff::GetSimd128Register(rhs.low_fp()));
1809 }
1810 
emit_i8x16_mul(LiftoffRegister dst,LiftoffRegister lhs,LiftoffRegister rhs)1811 void LiftoffAssembler::emit_i8x16_mul(LiftoffRegister dst, LiftoffRegister lhs,
1812                                       LiftoffRegister rhs) {
1813   bailout(kSimd, "i8x16mul");
1814 }
1815 
StackCheck(Label * ool_code,Register limit_address)1816 void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) {
1817   ldr(limit_address, MemOperand(limit_address));
1818   cmp(sp, limit_address);
1819   b(ool_code, ls);
1820 }
1821 
CallTrapCallbackForTesting()1822 void LiftoffAssembler::CallTrapCallbackForTesting() {
1823   PrepareCallCFunction(0, 0);
1824   CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0);
1825 }
1826 
AssertUnreachable(AbortReason reason)1827 void LiftoffAssembler::AssertUnreachable(AbortReason reason) {
1828   // Asserts unreachable within the wasm code.
1829   TurboAssembler::AssertUnreachable(reason);
1830 }
1831 
PushRegisters(LiftoffRegList regs)1832 void LiftoffAssembler::PushRegisters(LiftoffRegList regs) {
1833   RegList core_regs = regs.GetGpList();
1834   if (core_regs != 0) {
1835     stm(db_w, sp, core_regs);
1836   }
1837   LiftoffRegList fp_regs = regs & kFpCacheRegList;
1838   while (!fp_regs.is_empty()) {
1839     LiftoffRegister reg = fp_regs.GetFirstRegSet();
1840     DoubleRegister first = reg.fp();
1841     DoubleRegister last = first;
1842     fp_regs.clear(reg);
1843     while (!fp_regs.is_empty()) {
1844       LiftoffRegister reg = fp_regs.GetFirstRegSet();
1845       int code = reg.fp().code();
1846       // vstm can not push more than 16 registers. We have to make sure the
1847       // condition is met.
1848       if ((code != last.code() + 1) || ((code - first.code() + 1) > 16)) break;
1849       last = reg.fp();
1850       fp_regs.clear(reg);
1851     }
1852     vstm(db_w, sp, first, last);
1853   }
1854 }
1855 
PopRegisters(LiftoffRegList regs)1856 void LiftoffAssembler::PopRegisters(LiftoffRegList regs) {
1857   LiftoffRegList fp_regs = regs & kFpCacheRegList;
1858   while (!fp_regs.is_empty()) {
1859     LiftoffRegister reg = fp_regs.GetLastRegSet();
1860     DoubleRegister last = reg.fp();
1861     DoubleRegister first = last;
1862     fp_regs.clear(reg);
1863     while (!fp_regs.is_empty()) {
1864       LiftoffRegister reg = fp_regs.GetLastRegSet();
1865       int code = reg.fp().code();
1866       if ((code != first.code() - 1) || ((last.code() - code + 1) > 16)) break;
1867       first = reg.fp();
1868       fp_regs.clear(reg);
1869     }
1870     vldm(ia_w, sp, first, last);
1871   }
1872   RegList core_regs = regs.GetGpList();
1873   if (core_regs != 0) {
1874     ldm(ia_w, sp, core_regs);
1875   }
1876 }
1877 
DropStackSlotsAndRet(uint32_t num_stack_slots)1878 void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) {
1879   Drop(num_stack_slots);
1880   Ret();
1881 }
1882 
CallC(const wasm::FunctionSig * sig,const LiftoffRegister * args,const LiftoffRegister * rets,ValueType out_argument_type,int stack_bytes,ExternalReference ext_ref)1883 void LiftoffAssembler::CallC(const wasm::FunctionSig* sig,
1884                              const LiftoffRegister* args,
1885                              const LiftoffRegister* rets,
1886                              ValueType out_argument_type, int stack_bytes,
1887                              ExternalReference ext_ref) {
1888   // Arguments are passed by pushing them all to the stack and then passing
1889   // a pointer to them.
1890   DCHECK(IsAligned(stack_bytes, kSystemPointerSize));
1891   // Reserve space in the stack.
1892   AllocateStackSpace(stack_bytes);
1893 
1894   int arg_bytes = 0;
1895   for (ValueType param_type : sig->parameters()) {
1896     switch (param_type.kind()) {
1897       case ValueType::kI32:
1898         str(args->gp(), MemOperand(sp, arg_bytes));
1899         break;
1900       case ValueType::kI64:
1901         str(args->low_gp(), MemOperand(sp, arg_bytes));
1902         str(args->high_gp(), MemOperand(sp, arg_bytes + kSystemPointerSize));
1903         break;
1904       case ValueType::kF32:
1905         vstr(liftoff::GetFloatRegister(args->fp()), MemOperand(sp, arg_bytes));
1906         break;
1907       case ValueType::kF64:
1908         vstr(args->fp(), MemOperand(sp, arg_bytes));
1909         break;
1910       default:
1911         UNREACHABLE();
1912     }
1913     args++;
1914     arg_bytes += param_type.element_size_bytes();
1915   }
1916   DCHECK_LE(arg_bytes, stack_bytes);
1917 
1918   // Pass a pointer to the buffer with the arguments to the C function.
1919   mov(r0, sp);
1920 
1921   // Now call the C function.
1922   constexpr int kNumCCallArgs = 1;
1923   PrepareCallCFunction(kNumCCallArgs);
1924   CallCFunction(ext_ref, kNumCCallArgs);
1925 
1926   // Move return value to the right register.
1927   const LiftoffRegister* result_reg = rets;
1928   if (sig->return_count() > 0) {
1929     DCHECK_EQ(1, sig->return_count());
1930     constexpr Register kReturnReg = r0;
1931     if (kReturnReg != rets->gp()) {
1932       Move(*rets, LiftoffRegister(kReturnReg), sig->GetReturn(0));
1933     }
1934     result_reg++;
1935   }
1936 
1937   // Load potential output value from the buffer on the stack.
1938   if (out_argument_type != kWasmStmt) {
1939     switch (out_argument_type.kind()) {
1940       case ValueType::kI32:
1941         ldr(result_reg->gp(), MemOperand(sp));
1942         break;
1943       case ValueType::kI64:
1944         ldr(result_reg->low_gp(), MemOperand(sp));
1945         ldr(result_reg->high_gp(), MemOperand(sp, kSystemPointerSize));
1946         break;
1947       case ValueType::kF32:
1948         vldr(liftoff::GetFloatRegister(result_reg->fp()), MemOperand(sp));
1949         break;
1950       case ValueType::kF64:
1951         vldr(result_reg->fp(), MemOperand(sp));
1952         break;
1953       default:
1954         UNREACHABLE();
1955     }
1956   }
1957   add(sp, sp, Operand(stack_bytes));
1958 }
1959 
CallNativeWasmCode(Address addr)1960 void LiftoffAssembler::CallNativeWasmCode(Address addr) {
1961   Call(addr, RelocInfo::WASM_CALL);
1962 }
1963 
CallIndirect(const wasm::FunctionSig * sig,compiler::CallDescriptor * call_descriptor,Register target)1964 void LiftoffAssembler::CallIndirect(const wasm::FunctionSig* sig,
1965                                     compiler::CallDescriptor* call_descriptor,
1966                                     Register target) {
1967   DCHECK(target != no_reg);
1968   Call(target);
1969 }
1970 
CallRuntimeStub(WasmCode::RuntimeStubId sid)1971 void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) {
1972   // A direct call to a wasm runtime stub defined in this module.
1973   // Just encode the stub index. This will be patched at relocation.
1974   Call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL);
1975 }
1976 
AllocateStackSlot(Register addr,uint32_t size)1977 void LiftoffAssembler::AllocateStackSlot(Register addr, uint32_t size) {
1978   AllocateStackSpace(size);
1979   mov(addr, sp);
1980 }
1981 
DeallocateStackSlot(uint32_t size)1982 void LiftoffAssembler::DeallocateStackSlot(uint32_t size) {
1983   add(sp, sp, Operand(size));
1984 }
1985 
Construct()1986 void LiftoffStackSlots::Construct() {
1987   for (auto& slot : slots_) {
1988     const LiftoffAssembler::VarState& src = slot.src_;
1989     switch (src.loc()) {
1990       case LiftoffAssembler::VarState::kStack: {
1991         switch (src.type().kind()) {
1992           // i32 and i64 can be treated as similar cases, i64 being previously
1993           // split into two i32 registers
1994           case ValueType::kI32:
1995           case ValueType::kI64:
1996           case ValueType::kF32: {
1997             UseScratchRegisterScope temps(asm_);
1998             Register scratch = temps.Acquire();
1999             asm_->ldr(scratch,
2000                       liftoff::GetHalfStackSlot(slot.src_offset_, slot.half_));
2001             asm_->Push(scratch);
2002           } break;
2003           case ValueType::kF64: {
2004             UseScratchRegisterScope temps(asm_);
2005             DwVfpRegister scratch = temps.AcquireD();
2006             asm_->vldr(scratch, liftoff::GetStackSlot(slot.src_offset_));
2007             asm_->vpush(scratch);
2008           } break;
2009           case ValueType::kS128: {
2010             MemOperand mem_op = liftoff::GetStackSlot(slot.src_offset_);
2011             UseScratchRegisterScope temps(asm_);
2012             Register addr = liftoff::CalculateActualAddress(
2013                 asm_, &temps, mem_op.rn(), no_reg, mem_op.offset());
2014             QwNeonRegister scratch = temps.AcquireQ();
2015             asm_->vld1(Neon8, NeonListOperand(scratch), NeonMemOperand(addr));
2016             asm_->vpush(scratch);
2017             break;
2018           }
2019           default:
2020             UNREACHABLE();
2021         }
2022         break;
2023       }
2024       case LiftoffAssembler::VarState::kRegister:
2025         switch (src.type().kind()) {
2026           case ValueType::kI64: {
2027             LiftoffRegister reg =
2028                 slot.half_ == kLowWord ? src.reg().low() : src.reg().high();
2029             asm_->push(reg.gp());
2030           } break;
2031           case ValueType::kI32:
2032             asm_->push(src.reg().gp());
2033             break;
2034           case ValueType::kF32:
2035             asm_->vpush(liftoff::GetFloatRegister(src.reg().fp()));
2036             break;
2037           case ValueType::kF64:
2038             asm_->vpush(src.reg().fp());
2039             break;
2040           case ValueType::kS128:
2041             asm_->vpush(liftoff::GetSimd128Register(src.reg().low_fp()));
2042             break;
2043           default:
2044             UNREACHABLE();
2045         }
2046         break;
2047       case LiftoffAssembler::VarState::kIntConst: {
2048         DCHECK(src.type() == kWasmI32 || src.type() == kWasmI64);
2049         UseScratchRegisterScope temps(asm_);
2050         Register scratch = temps.Acquire();
2051         // The high word is the sign extension of the low word.
2052         asm_->mov(scratch,
2053                   Operand(slot.half_ == kLowWord ? src.i32_const()
2054                                                  : src.i32_const() >> 31));
2055         asm_->push(scratch);
2056         break;
2057       }
2058     }
2059   }
2060 }
2061 
2062 }  // namespace wasm
2063 }  // namespace internal
2064 }  // namespace v8
2065 
2066 #endif  // V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_H_
2067