5 #ifndef V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_H_ 6 #define V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_H_ 8 #include "src/wasm/baseline/liftoff-assembler.h" 10 #define BAILOUT(reason) bailout("arm " reason) 40 static_assert(2 * kPointerSize == LiftoffAssembler::kStackSlotSize,
41 "Slot size should be twice the size of the 32 bit pointer.");
42 constexpr int32_t kInstanceOffset = 2 * kPointerSize;
43 constexpr int32_t kFirstStackSlotOffset = kInstanceOffset + 2 * kPointerSize;
44 constexpr int32_t kConstantStackSpace = kPointerSize;
48 constexpr int32_t kPatchInstructionsRequired = 3;
50 inline MemOperand GetStackSlot(
uint32_t index) {
52 kFirstStackSlotOffset + index * LiftoffAssembler::kStackSlotSize;
53 return MemOperand(fp, -offset);
56 inline MemOperand GetHalfStackSlot(
uint32_t half_index) {
57 int32_t offset = kFirstStackSlotOffset +
58 half_index * (LiftoffAssembler::kStackSlotSize / 2);
59 return MemOperand(fp, -offset);
62 inline MemOperand GetHalfStackSlot(
uint32_t index, RegPairHalf half) {
63 if (half == kLowWord) {
64 return GetHalfStackSlot(2 * index);
66 return GetHalfStackSlot(2 * index - 1);
70 inline MemOperand GetInstanceOperand() {
71 return MemOperand(fp, -kInstanceOffset);
74 inline MemOperand GetMemOp(LiftoffAssembler* assm,
75 UseScratchRegisterScope* temps, Register addr,
76 Register offset, int32_t offset_imm) {
77 if (offset != no_reg) {
78 if (offset_imm == 0)
return MemOperand(addr, offset);
79 Register tmp = temps->Acquire();
80 assm->add(tmp, offset, Operand(offset_imm));
81 return MemOperand(addr, tmp);
83 return MemOperand(addr, offset_imm);
86 inline Register CalculateActualAddress(LiftoffAssembler* assm,
87 UseScratchRegisterScope* temps,
88 Register addr_reg, Register offset_reg,
90 if (offset_reg == no_reg && offset_imm == 0) {
93 Register actual_addr_reg = temps->Acquire();
94 if (offset_reg == no_reg) {
95 assm->add(actual_addr_reg, addr_reg, Operand(offset_imm));
97 assm->add(actual_addr_reg, addr_reg, Operand(offset_reg));
98 if (offset_imm != 0) {
99 assm->add(actual_addr_reg, actual_addr_reg, Operand(offset_imm));
102 return actual_addr_reg;
105 inline Condition MakeUnsigned(Condition cond) {
107 case kSignedLessThan:
108 return kUnsignedLessThan;
109 case kSignedLessEqual:
110 return kUnsignedLessEqual;
111 case kSignedGreaterThan:
112 return kUnsignedGreaterThan;
113 case kSignedGreaterEqual:
114 return kUnsignedGreaterEqual;
117 case kUnsignedLessThan:
118 case kUnsignedLessEqual:
119 case kUnsignedGreaterThan:
120 case kUnsignedGreaterEqual:
127 template <void (Assembler::*op)(Register, Register, Register, SBit, Condition),
128 void (Assembler::*op_with_carry)(Register, Register,
const Operand&,
130 inline void I64Binop(LiftoffAssembler* assm, LiftoffRegister dst,
131 LiftoffRegister lhs, LiftoffRegister rhs) {
132 UseScratchRegisterScope temps(assm);
133 Register scratch = dst.low_gp();
135 dst.low_gp() != lhs.high_gp() && dst.low_gp() != rhs.high_gp();
137 scratch = temps.Acquire();
139 (assm->*op)(scratch, lhs.low_gp(), rhs.low_gp(), SetCC, al);
140 (assm->*op_with_carry)(dst.high_gp(), lhs.high_gp(), Operand(rhs.high_gp()),
143 assm->mov(dst.low_gp(), scratch);
150 template <void (TurboAssembler::*op)(Register, Register, Register, Register,
152 inline void I64Shiftop(LiftoffAssembler* assm, LiftoffRegister dst,
153 LiftoffRegister src, Register amount,
154 Register safe_amount_reg, LiftoffRegList pinned) {
155 DCHECK(safe_amount_reg == dst.low_gp() || safe_amount_reg == dst.high_gp());
157 (safe_amount_reg == dst.low_gp()) ? dst.high_gp() : dst.low_gp();
158 pinned.set(other_reg);
159 pinned.set(src.low_gp());
160 pinned.set(src.high_gp());
161 Register scratch = assm->GetUnusedRegister(kGpReg, pinned).gp();
162 assm->and_(scratch, amount, Operand(0x3F));
163 (assm->*op)(dst.low_gp(), dst.high_gp(), src.low_gp(), src.high_gp(),
167 enum class MinOrMax : uint8_t { kMin, kMax };
168 inline void EmitFloatMinOrMax(LiftoffAssembler* assm, DoubleRegister dst,
169 DoubleRegister lhs, DoubleRegister rhs,
170 MinOrMax min_or_max) {
176 assm->VFPCompareAndSetFlags(lhs, rhs);
177 assm->b(&is_nan, vs);
179 if (CpuFeatures::IsSupported(ARMv8)) {
180 CpuFeatureScope scope(assm, ARMv8);
181 if (min_or_max == MinOrMax::kMin) {
182 assm->vminnm(dst, lhs, rhs);
184 assm->vmaxnm(dst, lhs, rhs);
189 assm->vadd(dst, lhs, rhs);
191 assm->b(&lhs_below_rhs, lt);
192 assm->b(&lhs_above_rhs, gt);
194 UseScratchRegisterScope temps(assm);
195 Register scratch = temps.Acquire();
203 assm->VmovHigh(scratch, lhs);
204 assm->cmp(scratch, Operand(0));
205 assm->b(&lhs_below_rhs, mi);
206 assm->b(&lhs_above_rhs);
209 assm->vadd(dst, lhs, rhs);
212 assm->bind(&lhs_below_rhs);
213 DoubleRegister lhs_below_rhs_src =
214 (min_or_max == MinOrMax::kMin) ? lhs : rhs;
215 if (dst != lhs_below_rhs_src) {
216 assm->vmov(dst, lhs_below_rhs_src);
220 assm->bind(&lhs_above_rhs);
221 DoubleRegister lhs_above_rhs_src =
222 (min_or_max == MinOrMax::kMin) ? rhs : lhs;
223 if (dst != lhs_above_rhs_src) {
224 assm->vmov(dst, lhs_above_rhs_src);
231 int LiftoffAssembler::PrepareStackFrame() {
232 if (!CpuFeatures::IsSupported(ARMv7)) {
233 BAILOUT(
"Armv6 not supported");
240 for (
int i = 0;
i < liftoff::kPatchInstructionsRequired;
i++) {
243 DCHECK_EQ(offset + liftoff::kPatchInstructionsRequired * kInstrSize,
248 void LiftoffAssembler::PatchPrepareStackFrame(
int offset,
251 uint32_t bytes = liftoff::kConstantStackSpace + kStackSlotSize * stack_slots;
256 if (bytes > KB / 2) {
257 BAILOUT(
"Stack limited to 512 bytes to avoid a bug in StackCheck");
261 PatchingAssembler patching_assembler(AssemblerOptions{}, buffer_ + offset,
262 liftoff::kPatchInstructionsRequired);
263 patching_assembler.sub(sp, sp, Operand(bytes));
264 patching_assembler.PadWithNops();
267 void LiftoffAssembler::FinishCode() { CheckConstPool(
true,
false); }
269 void LiftoffAssembler::AbortCompilation() { AbortedCodeGeneration(); }
271 void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
272 RelocInfo::Mode rmode) {
273 switch (value.type()) {
275 TurboAssembler::Move(reg.gp(), Operand(value.to_i32(), rmode));
278 DCHECK(RelocInfo::IsNone(rmode));
279 int32_t low_word = value.to_i64();
280 int32_t high_word = value.to_i64() >> 32;
281 TurboAssembler::Move(reg.low_gp(), Operand(low_word));
282 TurboAssembler::Move(reg.high_gp(), Operand(high_word));
286 BAILOUT(
"Load f32 Constant");
289 Register extra_scratch = GetUnusedRegister(kGpReg).gp();
290 vmov(reg.fp(), Double(value.to_f64_boxed().get_scalar()), extra_scratch);
298 void LiftoffAssembler::LoadFromInstance(Register dst,
uint32_t offset,
300 DCHECK_LE(offset, kMaxInt);
302 ldr(dst, liftoff::GetInstanceOperand());
303 ldr(dst, MemOperand(dst, offset));
306 void LiftoffAssembler::SpillInstance(Register instance) {
307 str(instance, liftoff::GetInstanceOperand());
310 void LiftoffAssembler::FillInstanceInto(Register dst) {
311 ldr(dst, liftoff::GetInstanceOperand());
314 void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
315 Register offset_reg,
uint32_t offset_imm,
316 LoadType type, LiftoffRegList pinned,
317 uint32_t* protected_load_pc,
bool is_load_mem) {
318 DCHECK_IMPLIES(type.value_type() == kWasmI64, dst.is_pair());
322 if (!is_uint31(offset_imm)) {
323 TurboAssembler::Abort(AbortReason::kOffsetOutOfRange);
326 UseScratchRegisterScope temps(
this);
327 if (type.value() == LoadType::kF64Load) {
329 CpuFeatureScope scope(
this, NEON);
330 Register actual_src_addr = liftoff::CalculateActualAddress(
331 this, &temps, src_addr, offset_reg, offset_imm);
332 vld1(Neon64, NeonListOperand(dst.fp()), NeonMemOperand(actual_src_addr));
335 liftoff::GetMemOp(
this, &temps, src_addr, offset_reg, offset_imm);
336 if (protected_load_pc) *protected_load_pc = pc_offset();
337 switch (type.value()) {
338 case LoadType::kI32Load8U:
339 ldrb(dst.gp(), src_op);
341 case LoadType::kI64Load8U:
342 ldrb(dst.low_gp(), src_op);
343 mov(dst.high_gp(), Operand(0));
345 case LoadType::kI32Load8S:
346 ldrsb(dst.gp(), src_op);
348 case LoadType::kI64Load8S:
349 ldrsb(dst.low_gp(), src_op);
350 asr(dst.high_gp(), dst.low_gp(), Operand(31));
352 case LoadType::kI32Load16U:
353 ldrh(dst.gp(), src_op);
355 case LoadType::kI64Load16U:
356 ldrh(dst.low_gp(), src_op);
357 mov(dst.high_gp(), Operand(0));
359 case LoadType::kI32Load16S:
360 ldrsh(dst.gp(), src_op);
362 case LoadType::kI32Load:
363 ldr(dst.gp(), src_op);
365 case LoadType::kI64Load16S:
366 ldrsh(dst.low_gp(), src_op);
367 asr(dst.high_gp(), dst.low_gp(), Operand(31));
369 case LoadType::kI64Load32U:
370 ldr(dst.low_gp(), src_op);
371 mov(dst.high_gp(), Operand(0));
373 case LoadType::kI64Load32S:
374 ldr(dst.low_gp(), src_op);
375 asr(dst.high_gp(), dst.low_gp(), Operand(31));
377 case LoadType::kI64Load:
378 ldr(dst.low_gp(), src_op);
382 if (temps.CanAcquire()) {
383 src_op = liftoff::GetMemOp(
this, &temps, src_addr, offset_reg,
384 offset_imm + kRegisterSize);
386 add(src_op.rm(), src_op.rm(), Operand(kRegisterSize));
388 ldr(dst.high_gp(), src_op);
390 case LoadType::kF32Load:
399 void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
400 uint32_t offset_imm, LiftoffRegister src,
401 StoreType type, LiftoffRegList pinned,
402 uint32_t* protected_store_pc,
bool is_store_mem) {
406 if (!is_uint31(offset_imm)) {
407 TurboAssembler::Abort(AbortReason::kOffsetOutOfRange);
410 UseScratchRegisterScope temps(
this);
411 if (type.value() == StoreType::kF64Store) {
413 CpuFeatureScope scope(
this, NEON);
414 Register actual_dst_addr = liftoff::CalculateActualAddress(
415 this, &temps, dst_addr, offset_reg, offset_imm);
416 vst1(Neon64, NeonListOperand(src.fp()), NeonMemOperand(actual_dst_addr));
419 liftoff::GetMemOp(
this, &temps, dst_addr, offset_reg, offset_imm);
420 if (protected_store_pc) *protected_store_pc = pc_offset();
421 switch (type.value()) {
422 case StoreType::kI64Store8:
425 case StoreType::kI32Store8:
426 strb(src.gp(), dst_op);
428 case StoreType::kI64Store16:
431 case StoreType::kI32Store16:
432 strh(src.gp(), dst_op);
434 case StoreType::kI64Store32:
437 case StoreType::kI32Store:
438 str(src.gp(), dst_op);
440 case StoreType::kI64Store:
441 str(src.low_gp(), dst_op);
445 if (temps.CanAcquire()) {
446 dst_op = liftoff::GetMemOp(
this, &temps, dst_addr, offset_reg,
447 offset_imm + kRegisterSize);
449 add(dst_op.rm(), dst_op.rm(), Operand(kRegisterSize));
451 str(src.high_gp(), dst_op);
453 case StoreType::kF32Store:
454 BAILOUT(
"Store f32");
462 void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst,
465 int32_t offset = (caller_slot_idx + 1) * kRegisterSize;
466 MemOperand src(fp, offset);
472 ldr(dst.low_gp(), src);
473 ldr(dst.high_gp(), MemOperand(fp, offset + kRegisterSize));
476 BAILOUT(
"Load Caller Frame Slot for f32");
486 void LiftoffAssembler::MoveStackValue(
uint32_t dst_index,
uint32_t src_index,
488 DCHECK_NE(dst_index, src_index);
489 LiftoffRegister reg = GetUnusedRegister(kGpReg);
490 Fill(reg, src_index, type);
491 Spill(dst_index, reg, type);
494 void LiftoffAssembler::Move(Register dst, Register src, ValueType type) {
496 DCHECK_EQ(type, kWasmI32);
497 TurboAssembler::Move(dst, src);
500 void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src,
503 if (type == kWasmF32) {
504 BAILOUT(
"Move DoubleRegister");
506 DCHECK_EQ(kWasmF64, type);
511 void LiftoffAssembler::Spill(
uint32_t index, LiftoffRegister reg,
513 RecordUsedSpillSlot(index);
514 MemOperand dst = liftoff::GetStackSlot(index);
520 str(reg.low_gp(), liftoff::GetHalfStackSlot(index, kLowWord));
521 str(reg.high_gp(), liftoff::GetHalfStackSlot(index, kHighWord));
524 BAILOUT(
"Spill Register f32");
534 void LiftoffAssembler::Spill(
uint32_t index, WasmValue value) {
535 RecordUsedSpillSlot(index);
536 MemOperand dst = liftoff::GetStackSlot(index);
537 UseScratchRegisterScope temps(
this);
538 Register src = no_reg;
541 if (!ImmediateFitsAddrMode2Instruction(dst.offset())) {
542 src = GetUnusedRegister(kGpReg).gp();
544 src = temps.Acquire();
546 switch (value.type()) {
548 mov(src, Operand(value.to_i32()));
552 int32_t low_word = value.to_i64();
553 mov(src, Operand(low_word));
554 str(src, liftoff::GetHalfStackSlot(index, kLowWord));
555 int32_t high_word = value.to_i64() >> 32;
556 mov(src, Operand(high_word));
557 str(src, liftoff::GetHalfStackSlot(index, kHighWord));
566 void LiftoffAssembler::Fill(LiftoffRegister reg,
uint32_t index,
570 ldr(reg.gp(), liftoff::GetStackSlot(index));
573 ldr(reg.low_gp(), liftoff::GetHalfStackSlot(index, kLowWord));
574 ldr(reg.high_gp(), liftoff::GetHalfStackSlot(index, kHighWord));
577 BAILOUT(
"Fill Register");
580 vldr(reg.fp(), liftoff::GetStackSlot(index));
587 void LiftoffAssembler::FillI64Half(Register reg,
uint32_t half_index) {
588 ldr(reg, liftoff::GetHalfStackSlot(half_index));
591 #define I32_BINOP(name, instruction) \ 592 void LiftoffAssembler::emit_##name(Register dst, Register lhs, \ 594 instruction(dst, lhs, rhs); \ 596 #define I32_SHIFTOP(name, instruction) \ 597 void LiftoffAssembler::emit_##name(Register dst, Register src, \ 598 Register amount, LiftoffRegList pinned) { \ 599 UseScratchRegisterScope temps(this); \ 600 Register scratch = temps.Acquire(); \ 601 and_(scratch, amount, Operand(0x1f)); \ 602 instruction(dst, src, Operand(scratch)); \ 604 #define FP64_UNOP(name, instruction) \ 605 void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ 606 instruction(dst, src); \ 608 #define FP64_BINOP(name, instruction) \ 609 void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \ 610 DoubleRegister rhs) { \ 611 instruction(dst, lhs, rhs); \ 613 #define UNIMPLEMENTED_FP_BINOP(name) \ 614 void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \ 615 DoubleRegister rhs) { \ 616 BAILOUT("fp binop: " #name); \ 618 #define UNIMPLEMENTED_FP_UNOP(name) \ 619 void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ 620 BAILOUT("fp unop: " #name); \ 623 I32_BINOP(i32_add, add)
624 I32_BINOP(i32_sub, sub)
625 I32_BINOP(i32_mul, mul)
626 I32_BINOP(i32_and, and_)
627 I32_BINOP(i32_or, orr)
628 I32_BINOP(i32_xor, eor)
629 I32_SHIFTOP(i32_shl, lsl)
630 I32_SHIFTOP(i32_sar, asr)
631 I32_SHIFTOP(i32_shr, lsr)
632 UNIMPLEMENTED_FP_BINOP(f32_add)
633 UNIMPLEMENTED_FP_BINOP(f32_sub)
634 UNIMPLEMENTED_FP_BINOP(f32_mul)
635 UNIMPLEMENTED_FP_BINOP(f32_div)
636 UNIMPLEMENTED_FP_BINOP(f32_min)
637 UNIMPLEMENTED_FP_BINOP(f32_max)
638 UNIMPLEMENTED_FP_BINOP(f32_copysign)
639 UNIMPLEMENTED_FP_UNOP(f32_abs)
640 UNIMPLEMENTED_FP_UNOP(f32_neg)
641 UNIMPLEMENTED_FP_UNOP(f32_sqrt)
642 FP64_BINOP(f64_add, vadd)
643 FP64_BINOP(f64_sub, vsub)
644 FP64_BINOP(f64_mul, vmul)
645 FP64_BINOP(f64_div, vdiv)
646 FP64_UNOP(f64_abs, vabs)
647 FP64_UNOP(f64_neg, vneg)
648 FP64_UNOP(f64_sqrt, vsqrt)
652 #undef UNIMPLEMENTED_FP_BINOP 653 #undef UNIMPLEMENTED_FP_UNOP 655 bool LiftoffAssembler::emit_i32_clz(Register dst, Register src) {
660 bool LiftoffAssembler::emit_i32_ctz(Register dst, Register src) {
666 bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) {
668 UseScratchRegisterScope temps(
this);
669 Register scratch = temps.Acquire();
671 and_(scratch, src, Operand(0xaaaaaaaa));
672 sub(dst, src, Operand(scratch, LSR, 1));
673 LiftoffRegList pinned;
675 Register scratch_2 = GetUnusedRegister(kGpReg, pinned).gp();
677 mov(scratch, Operand(0x33333333));
678 and_(scratch_2, dst, Operand(scratch, LSL, 2));
679 and_(scratch, dst, scratch);
680 add(dst, scratch, Operand(scratch_2, LSR, 2));
683 add(dst, dst, Operand(dst, LSR, 4));
684 and_(dst, dst, Operand(0x0f0f0f0f));
686 add(dst, dst, Operand(dst, LSR, 8));
688 add(dst, dst, Operand(dst, LSR, 16));
690 and_(dst, dst, Operand(0x3f));
694 void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
695 Label* trap_div_by_zero,
696 Label* trap_div_unrepresentable) {
697 if (!CpuFeatures::IsSupported(SUDIV)) {
701 CpuFeatureScope scope(
this, SUDIV);
704 bool speculative_sdiv = dst != lhs && dst != rhs;
705 if (speculative_sdiv) {
710 cmp(rhs, Operand(0));
711 b(trap_div_by_zero, eq);
713 cmp(rhs, Operand(-1));
715 cmp(lhs, Operand(kMinInt));
716 b(trap_div_unrepresentable, eq);
718 if (!speculative_sdiv) {
723 void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
724 Label* trap_div_by_zero) {
725 if (!CpuFeatures::IsSupported(SUDIV)) {
729 CpuFeatureScope scope(
this, SUDIV);
731 cmp(rhs, Operand(0));
732 b(trap_div_by_zero, eq);
736 void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
737 Label* trap_div_by_zero) {
738 if (!CpuFeatures::IsSupported(SUDIV)) {
744 CpuFeatureScope scope(
this, SUDIV);
747 UseScratchRegisterScope temps(
this);
748 Register scratch = temps.Acquire();
749 sdiv(scratch, lhs, rhs);
751 cmp(rhs, Operand(0));
752 b(trap_div_by_zero, eq);
754 mls(dst, scratch, rhs, lhs);
757 void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
758 Label* trap_div_by_zero) {
759 if (!CpuFeatures::IsSupported(SUDIV)) {
765 CpuFeatureScope scope(
this, SUDIV);
768 UseScratchRegisterScope temps(
this);
769 Register scratch = temps.Acquire();
770 udiv(scratch, lhs, rhs);
772 cmp(rhs, Operand(0));
773 b(trap_div_by_zero, eq);
775 mls(dst, scratch, rhs, lhs);
778 void LiftoffAssembler::emit_i32_shr(Register dst, Register src,
int amount) {
779 DCHECK(is_uint5(amount));
780 lsr(dst, src, Operand(amount));
783 void LiftoffAssembler::emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs,
784 LiftoffRegister rhs) {
785 liftoff::I64Binop<&Assembler::add, &Assembler::adc>(
this, dst, lhs, rhs);
788 void LiftoffAssembler::emit_i64_sub(LiftoffRegister dst, LiftoffRegister lhs,
789 LiftoffRegister rhs) {
790 liftoff::I64Binop<&Assembler::sub, &Assembler::sbc>(
this, dst, lhs, rhs);
793 void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs,
794 LiftoffRegister rhs) {
800 UseScratchRegisterScope temps(
this);
801 Register scratch = temps.Acquire();
803 mul(scratch, lhs.high_gp(), rhs.low_gp());
805 mla(scratch, lhs.low_gp(), rhs.high_gp(), scratch);
808 umull(dst.low_gp(), dst.high_gp(), lhs.low_gp(), rhs.low_gp());
810 add(dst.high_gp(), dst.high_gp(), scratch);
813 bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs,
815 Label* trap_div_by_zero,
816 Label* trap_div_unrepresentable) {
820 bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs,
822 Label* trap_div_by_zero) {
826 bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs,
828 Label* trap_div_by_zero) {
832 bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs,
834 Label* trap_div_by_zero) {
838 void LiftoffAssembler::emit_i64_shl(LiftoffRegister dst, LiftoffRegister src,
839 Register amount, LiftoffRegList pinned) {
840 liftoff::I64Shiftop<&TurboAssembler::LslPair>(
this, dst, src, amount,
841 dst.low_gp(), pinned);
844 void LiftoffAssembler::emit_i64_sar(LiftoffRegister dst, LiftoffRegister src,
845 Register amount, LiftoffRegList pinned) {
846 liftoff::I64Shiftop<&TurboAssembler::AsrPair>(
this, dst, src, amount,
847 dst.high_gp(), pinned);
850 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
851 Register amount, LiftoffRegList pinned) {
852 liftoff::I64Shiftop<&TurboAssembler::LsrPair>(
this, dst, src, amount,
853 dst.high_gp(), pinned);
856 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
858 DCHECK(is_uint6(amount));
859 LsrPair(dst.low_gp(), dst.high_gp(), src.low_gp(), src.high_gp(), amount);
862 bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) {
866 bool LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) {
870 bool LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) {
874 bool LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst,
875 DoubleRegister src) {
879 bool LiftoffAssembler::emit_f64_ceil(DoubleRegister dst, DoubleRegister src) {
880 if (CpuFeatures::IsSupported(ARMv8)) {
881 CpuFeatureScope scope(
this, ARMv8);
888 bool LiftoffAssembler::emit_f64_floor(DoubleRegister dst, DoubleRegister src) {
889 if (CpuFeatures::IsSupported(ARMv8)) {
890 CpuFeatureScope scope(
this, ARMv8);
897 bool LiftoffAssembler::emit_f64_trunc(DoubleRegister dst, DoubleRegister src) {
898 if (CpuFeatures::IsSupported(ARMv8)) {
899 CpuFeatureScope scope(
this, ARMv8);
906 bool LiftoffAssembler::emit_f64_nearest_int(DoubleRegister dst,
907 DoubleRegister src) {
908 if (CpuFeatures::IsSupported(ARMv8)) {
909 CpuFeatureScope scope(
this, ARMv8);
916 void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
917 DoubleRegister rhs) {
918 liftoff::EmitFloatMinOrMax(
this, dst, lhs, rhs, liftoff::MinOrMax::kMin);
921 void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
922 DoubleRegister rhs) {
923 liftoff::EmitFloatMinOrMax(
this, dst, lhs, rhs, liftoff::MinOrMax::kMax);
926 void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
930 void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
931 DoubleRegister rhs) {
935 UseScratchRegisterScope temps(
this);
936 Register scratch = temps.Acquire();
937 Register scratch2 = GetUnusedRegister(kGpReg).gp();
938 VmovHigh(scratch, lhs);
940 bic(scratch, scratch, Operand(kF64SignBitHighWord));
941 VmovHigh(scratch2, rhs);
943 and_(scratch2, scratch2, Operand(kF64SignBitHighWord));
945 orr(scratch, scratch, scratch2);
947 VmovHigh(dst, scratch);
950 bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
952 LiftoffRegister src, Label* trap) {
954 case kExprI32ConvertI64:
955 TurboAssembler::Move(dst.gp(), src.low_gp());
957 case kExprI32SConvertF32: {
958 BAILOUT(
"kExprI32SConvertF32");
961 case kExprI32UConvertF32: {
962 BAILOUT(
"kExprI32UConvertF32");
965 case kExprI32SConvertF64: {
966 UseScratchRegisterScope temps(
this);
967 SwVfpRegister scratch_f = temps.AcquireS();
968 vcvt_s32_f64(scratch_f, src.fp());
969 vmov(dst.gp(), scratch_f);
971 DwVfpRegister scratch_d = temps.AcquireD();
972 vmov(scratch_d, Double(static_cast<double>(INT32_MIN - 1.0)));
973 VFPCompareAndSetFlags(src.fp(), scratch_d);
976 vmov(scratch_d, Double(static_cast<double>(INT32_MAX + 1.0)));
977 VFPCompareAndSetFlags(src.fp(), scratch_d);
981 case kExprI32UConvertF64: {
982 UseScratchRegisterScope temps(
this);
983 SwVfpRegister scratch_f = temps.AcquireS();
984 vcvt_u32_f64(scratch_f, src.fp());
985 vmov(dst.gp(), scratch_f);
987 DwVfpRegister scratch_d = temps.AcquireD();
988 vmov(scratch_d, Double(static_cast<double>(-1.0)));
989 VFPCompareAndSetFlags(src.fp(), scratch_d);
992 vmov(scratch_d, Double(static_cast<double>(UINT32_MAX + 1.0)));
993 VFPCompareAndSetFlags(src.fp(), scratch_d);
997 case kExprI32ReinterpretF32:
998 BAILOUT(
"kExprI32ReinterpretF32");
1000 case kExprI64SConvertI32:
1001 if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
1002 mov(dst.high_gp(), Operand(src.gp(), ASR, 31));
1004 case kExprI64UConvertI32:
1005 if (dst.low_gp() != src.gp()) mov(dst.low_gp(), src.gp());
1006 mov(dst.high_gp(), Operand(0));
1008 case kExprI64ReinterpretF64:
1009 vmov(dst.low_gp(), dst.high_gp(), src.fp());
1011 case kExprF32SConvertI32:
1012 BAILOUT(
"kExprF32SConvertI32");
1014 case kExprF32UConvertI32:
1015 BAILOUT(
"kExprF32UConvertI32");
1017 case kExprF32ConvertF64:
1018 BAILOUT(
"kExprF32ConvertF64");
1020 case kExprF32ReinterpretI32:
1021 BAILOUT(
"kExprF32ReinterpretI32");
1023 case kExprF64SConvertI32: {
1024 UseScratchRegisterScope temps(
this);
1025 SwVfpRegister scratch = temps.AcquireS();
1026 vmov(scratch, src.gp());
1027 vcvt_f64_s32(dst.fp(), scratch);
1030 case kExprF64UConvertI32: {
1031 UseScratchRegisterScope temps(
this);
1032 SwVfpRegister scratch = temps.AcquireS();
1033 vmov(scratch, src.gp());
1034 vcvt_f64_u32(dst.fp(), scratch);
1037 case kExprF64ConvertF32:
1038 BAILOUT(
"kExprF64ConvertF32");
1040 case kExprF64ReinterpretI64:
1041 vmov(dst.fp(), src.low_gp(), src.high_gp());
1043 case kExprF64SConvertI64:
1044 case kExprF64UConvertI64:
1045 case kExprI64SConvertF32:
1046 case kExprI64UConvertF32:
1047 case kExprF32SConvertI64:
1048 case kExprF32UConvertI64:
1049 case kExprI64SConvertF64:
1050 case kExprI64UConvertF64:
1058 void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) {
1062 void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) {
1066 void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst,
1067 LiftoffRegister src) {
1068 emit_i32_signextend_i8(dst.low_gp(), src.low_gp());
1069 mov(dst.high_gp(), Operand(dst.low_gp(), ASR, 31));
1072 void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst,
1073 LiftoffRegister src) {
1074 emit_i32_signextend_i16(dst.low_gp(), src.low_gp());
1075 mov(dst.high_gp(), Operand(dst.low_gp(), ASR, 31));
1078 void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst,
1079 LiftoffRegister src) {
1080 TurboAssembler::Move(dst.low_gp(), src.low_gp());
1081 mov(dst.high_gp(), Operand(src.low_gp(), ASR, 31));
1084 void LiftoffAssembler::emit_jump(Label* label) { b(label); }
1086 void LiftoffAssembler::emit_jump(Register target) { bx(target); }
1088 void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label,
1089 ValueType type, Register lhs,
1091 DCHECK_EQ(type, kWasmI32);
1092 if (rhs == no_reg) {
1093 cmp(lhs, Operand(0));
1100 void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
1102 mov(dst, Operand(dst, LSR, kRegSizeInBitsLog2));
1105 void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst,
1106 Register lhs, Register rhs) {
1108 mov(dst, Operand(0), LeaveCC);
1109 mov(dst, Operand(1), LeaveCC, cond);
1112 void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) {
1113 orr(dst, src.low_gp(), src.high_gp());
1115 mov(dst, Operand(dst, LSR, 5));
1118 void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst,
1119 LiftoffRegister lhs,
1120 LiftoffRegister rhs) {
1124 Condition unsigned_cond = liftoff::MakeUnsigned(cond);
1127 LiftoffRegister dest = LiftoffRegister(dst);
1128 bool speculative_move = !dest.overlaps(lhs) && !dest.overlaps(rhs);
1129 if (speculative_move) {
1130 mov(dst, Operand(0));
1134 cmp(lhs.high_gp(), rhs.high_gp());
1135 if (unsigned_cond == cond) {
1136 cmp(lhs.low_gp(), rhs.low_gp(), kEqual);
1137 if (!speculative_move) {
1138 mov(dst, Operand(0));
1140 mov(dst, Operand(1), LeaveCC, cond);
1145 cmp(lhs.low_gp(), rhs.low_gp());
1146 if (!speculative_move) {
1147 mov(dst, Operand(0));
1149 mov(dst, Operand(1), LeaveCC, unsigned_cond);
1152 if (!speculative_move) {
1153 mov(dst, Operand(0));
1155 mov(dst, Operand(1), LeaveCC, cond);
1160 void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst,
1162 DoubleRegister rhs) {
1163 BAILOUT(
"emit_f32_set_cond");
1166 void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst,
1168 DoubleRegister rhs) {
1169 VFPCompareAndSetFlags(lhs, rhs);
1170 mov(dst, Operand(0), LeaveCC);
1171 mov(dst, Operand(1), LeaveCC, cond);
1174 mov(dst, Operand(0), LeaveCC, vs);
1178 void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) {
1179 ldr(limit_address, MemOperand(limit_address));
1180 cmp(sp, limit_address);
1184 void LiftoffAssembler::CallTrapCallbackForTesting() {
1185 PrepareCallCFunction(0, 0);
1186 CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0);
1189 void LiftoffAssembler::AssertUnreachable(AbortReason reason) {
1191 TurboAssembler::AssertUnreachable(reason);
1194 void LiftoffAssembler::PushRegisters(LiftoffRegList regs) {
1195 RegList core_regs = regs.GetGpList();
1196 if (core_regs != 0) {
1197 stm(db_w, sp, core_regs);
1199 LiftoffRegList fp_regs = regs & kFpCacheRegList;
1200 while (!fp_regs.is_empty()) {
1201 LiftoffRegister reg = fp_regs.GetFirstRegSet();
1202 DoubleRegister first = reg.fp();
1203 DoubleRegister last = first;
1205 while (!fp_regs.is_empty()) {
1206 LiftoffRegister reg = fp_regs.GetFirstRegSet();
1207 int code = reg.fp().code();
1210 if ((code != last.code() + 1) || ((code - first.code() + 1) > 16))
break;
1214 vstm(db_w, sp, first, last);
1218 void LiftoffAssembler::PopRegisters(LiftoffRegList regs) {
1219 LiftoffRegList fp_regs = regs & kFpCacheRegList;
1220 while (!fp_regs.is_empty()) {
1221 LiftoffRegister reg = fp_regs.GetLastRegSet();
1222 DoubleRegister last = reg.fp();
1223 DoubleRegister first = last;
1225 while (!fp_regs.is_empty()) {
1226 LiftoffRegister reg = fp_regs.GetLastRegSet();
1227 int code = reg.fp().code();
1228 if ((code != first.code() - 1) || ((last.code() - code + 1) > 16))
break;
1232 vldm(ia_w, sp, first, last);
1234 RegList core_regs = regs.GetGpList();
1235 if (core_regs != 0) {
1236 ldm(ia_w, sp, core_regs);
1240 void LiftoffAssembler::DropStackSlotsAndRet(
uint32_t num_stack_slots) {
1241 Drop(num_stack_slots);
1245 void LiftoffAssembler::CallC(wasm::FunctionSig* sig,
1246 const LiftoffRegister* args,
1247 const LiftoffRegister* rets,
1248 ValueType out_argument_type,
int stack_bytes,
1249 ExternalReference ext_ref) {
1252 DCHECK_EQ(stack_bytes % kPointerSize, 0);
1254 sub(sp, sp, Operand(stack_bytes));
1257 for (ValueType param_type : sig->parameters()) {
1258 switch (param_type) {
1260 str(args->gp(), MemOperand(sp, arg_bytes));
1263 str(args->low_gp(), MemOperand(sp, arg_bytes));
1264 str(args->high_gp(), MemOperand(sp, arg_bytes + kRegisterSize));
1267 BAILOUT(
"Call C for f32 parameter");
1270 vstr(args->fp(), MemOperand(sp, arg_bytes));
1276 arg_bytes += ValueTypes::MemSize(param_type);
1278 DCHECK_LE(arg_bytes, stack_bytes);
1284 constexpr
int kNumCCallArgs = 1;
1285 PrepareCallCFunction(kNumCCallArgs);
1286 CallCFunction(ext_ref, kNumCCallArgs);
1289 const LiftoffRegister* result_reg = rets;
1290 if (sig->return_count() > 0) {
1291 DCHECK_EQ(1, sig->return_count());
1292 constexpr Register kReturnReg = r0;
1293 if (kReturnReg != rets->gp()) {
1294 Move(*rets, LiftoffRegister(kReturnReg), sig->GetReturn(0));
1300 if (out_argument_type != kWasmStmt) {
1301 switch (out_argument_type) {
1303 ldr(result_reg->gp(), MemOperand(sp));
1306 ldr(result_reg->low_gp(), MemOperand(sp));
1307 ldr(result_reg->high_gp(), MemOperand(sp, kPointerSize));
1310 BAILOUT(
"Call C for f32 parameter");
1313 vldr(result_reg->fp(), MemOperand(sp));
1319 add(sp, sp, Operand(stack_bytes));
1322 void LiftoffAssembler::CallNativeWasmCode(Address addr) {
1323 Call(addr, RelocInfo::WASM_CALL);
1326 void LiftoffAssembler::CallIndirect(wasm::FunctionSig* sig,
1327 compiler::CallDescriptor* call_descriptor,
1329 DCHECK(target != no_reg);
1333 void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) {
1336 Call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL);
1339 void LiftoffAssembler::AllocateStackSlot(Register addr,
uint32_t size) {
1340 sub(sp, sp, Operand(size));
1344 void LiftoffAssembler::DeallocateStackSlot(
uint32_t size) {
1345 add(sp, sp, Operand(size));
1348 void LiftoffStackSlots::Construct() {
1349 for (
auto& slot : slots_) {
1350 const LiftoffAssembler::VarState& src = slot.src_;
1351 switch (src.loc()) {
1352 case LiftoffAssembler::VarState::kStack: {
1353 switch (src.type()) {
1358 UseScratchRegisterScope temps(asm_);
1359 Register scratch = temps.Acquire();
1361 liftoff::GetHalfStackSlot(slot.src_index_, slot.half_));
1362 asm_->Push(scratch);
1365 asm_->BAILOUT(
"Construct f32 from kStack");
1368 UseScratchRegisterScope temps(asm_);
1369 DwVfpRegister scratch = temps.AcquireD();
1370 asm_->vldr(scratch, liftoff::GetStackSlot(slot.src_index_));
1371 asm_->vpush(scratch);
1378 case LiftoffAssembler::VarState::kRegister:
1379 switch (src.type()) {
1381 LiftoffRegister reg =
1382 slot.half_ == kLowWord ? src.reg().low() : src.reg().high();
1383 asm_->push(reg.gp());
1386 asm_->push(src.reg().gp());
1389 asm_->BAILOUT(
"Construct f32 from kRegister");
1392 asm_->vpush(src.reg().fp());
1398 case LiftoffAssembler::VarState::KIntConst: {
1399 DCHECK(src.type() == kWasmI32 || src.type() == kWasmI64);
1400 UseScratchRegisterScope temps(asm_);
1401 Register scratch = temps.Acquire();
1404 Operand(slot.half_ == kLowWord ? src.i32_const()
1405 : src.i32_const() >> 31));
1406 asm_->push(scratch);
1419 #endif // V8_WASM_BASELINE_ARM_LIFTOFF_ASSEMBLER_ARM_H_