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