5 #ifndef V8_WASM_BASELINE_X64_LIFTOFF_ASSEMBLER_X64_H_ 6 #define V8_WASM_BASELINE_X64_LIFTOFF_ASSEMBLER_X64_H_ 8 #include "src/wasm/baseline/liftoff-assembler.h" 10 #include "src/assembler.h" 11 #include "src/wasm/value-type.h" 17 #define REQUIRE_CPU_FEATURE(name, ...) \ 18 if (!CpuFeatures::IsSupported(name)) { \ 19 bailout("no " #name); \ 22 CpuFeatureScope feature(this, name); 26 constexpr Register kScratchRegister2 = r11;
27 static_assert(kScratchRegister != kScratchRegister2,
"collision");
28 static_assert((kLiftoffAssemblerGpCacheRegs &
29 Register::ListOf<kScratchRegister, kScratchRegister2>()) == 0,
30 "scratch registers must not be used as cache registers");
32 constexpr DoubleRegister kScratchDoubleReg2 = xmm14;
33 static_assert(kScratchDoubleReg != kScratchDoubleReg2,
"collision");
35 (kLiftoffAssemblerFpCacheRegs &
36 DoubleRegister::ListOf<kScratchDoubleReg, kScratchDoubleReg2>()) == 0,
37 "scratch registers must not be used as cache registers");
41 constexpr int32_t kConstantStackSpace = 16;
42 constexpr int32_t kFirstStackSlotOffset =
43 kConstantStackSpace + LiftoffAssembler::kStackSlotSize;
45 inline Operand GetStackSlot(
uint32_t index) {
46 int32_t offset = index * LiftoffAssembler::kStackSlotSize;
47 return Operand(rbp, -kFirstStackSlotOffset - offset);
51 inline Operand GetInstanceOperand() {
return Operand(rbp, -16); }
53 inline Operand GetMemOp(LiftoffAssembler* assm, Register addr, Register offset,
55 if (is_uint31(offset_imm)) {
56 if (offset == no_reg)
return Operand(addr, offset_imm);
57 return Operand(addr, offset, times_1, offset_imm);
60 Register scratch = kScratchRegister;
61 assm->movl(scratch, Immediate(offset_imm));
62 if (offset != no_reg) {
63 assm->addq(scratch, offset);
65 return Operand(addr, scratch, times_1, 0);
68 inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Operand src,
72 assm->movl(dst.gp(), src);
75 assm->movq(dst.gp(), src);
78 assm->Movss(dst.fp(), src);
81 assm->Movsd(dst.fp(), src);
88 inline void Store(LiftoffAssembler* assm, Operand dst, LiftoffRegister src,
92 assm->movl(dst, src.gp());
95 assm->movq(dst, src.gp());
98 assm->Movss(dst, src.fp());
101 assm->Movsd(dst, src.fp());
108 inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) {
112 assm->pushq(reg.gp());
115 assm->subp(rsp, Immediate(kPointerSize));
116 assm->Movss(Operand(rsp, 0), reg.fp());
119 assm->subp(rsp, Immediate(kPointerSize));
120 assm->Movsd(Operand(rsp, 0), reg.fp());
127 template <
typename... Regs>
128 inline void SpillRegisters(LiftoffAssembler* assm, Regs... regs) {
129 for (LiftoffRegister r : {LiftoffRegister(regs)...}) {
130 if (assm->cache_state()->is_used(r)) assm->SpillRegister(r);
136 int LiftoffAssembler::PrepareStackFrame() {
137 int offset = pc_offset();
142 void LiftoffAssembler::PatchPrepareStackFrame(
int offset,
144 uint32_t bytes = liftoff::kConstantStackSpace + kStackSlotSize * stack_slots;
145 DCHECK_LE(bytes, kMaxInt);
148 constexpr
int kAvailableSpace = 64;
149 Assembler patching_assembler(AssemblerOptions{}, buffer_ + offset,
151 patching_assembler.sub_sp_32(bytes);
154 void LiftoffAssembler::FinishCode() {}
156 void LiftoffAssembler::AbortCompilation() {}
158 void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value,
159 RelocInfo::Mode rmode) {
160 switch (value.type()) {
162 if (value.to_i32() == 0 && RelocInfo::IsNone(rmode)) {
163 xorl(reg.gp(), reg.gp());
165 movl(reg.gp(), Immediate(value.to_i32(), rmode));
169 if (RelocInfo::IsNone(rmode)) {
170 TurboAssembler::Set(reg.gp(), value.to_i64());
172 movq(reg.gp(), value.to_i64(), rmode);
176 TurboAssembler::Move(reg.fp(), value.to_f32_boxed().get_bits());
179 TurboAssembler::Move(reg.fp(), value.to_f64_boxed().get_bits());
186 void LiftoffAssembler::LoadFromInstance(Register dst,
uint32_t offset,
188 DCHECK_LE(offset, kMaxInt);
189 movp(dst, liftoff::GetInstanceOperand());
190 DCHECK(size == 4 || size == 8);
192 movl(dst, Operand(dst, offset));
194 movq(dst, Operand(dst, offset));
198 void LiftoffAssembler::SpillInstance(Register instance) {
199 movp(liftoff::GetInstanceOperand(), instance);
202 void LiftoffAssembler::FillInstanceInto(Register dst) {
203 movp(dst, liftoff::GetInstanceOperand());
206 void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
207 Register offset_reg,
uint32_t offset_imm,
208 LoadType type, LiftoffRegList pinned,
209 uint32_t* protected_load_pc,
bool is_load_mem) {
210 if (emit_debug_code() && offset_reg != no_reg) {
211 AssertZeroExtended(offset_reg);
213 Operand src_op = liftoff::GetMemOp(
this, src_addr, offset_reg, offset_imm);
214 if (protected_load_pc) *protected_load_pc = pc_offset();
215 switch (type.value()) {
216 case LoadType::kI32Load8U:
217 case LoadType::kI64Load8U:
218 movzxbl(dst.gp(), src_op);
220 case LoadType::kI32Load8S:
221 movsxbl(dst.gp(), src_op);
223 case LoadType::kI64Load8S:
224 movsxbq(dst.gp(), src_op);
226 case LoadType::kI32Load16U:
227 case LoadType::kI64Load16U:
228 movzxwl(dst.gp(), src_op);
230 case LoadType::kI32Load16S:
231 movsxwl(dst.gp(), src_op);
233 case LoadType::kI64Load16S:
234 movsxwq(dst.gp(), src_op);
236 case LoadType::kI32Load:
237 case LoadType::kI64Load32U:
238 movl(dst.gp(), src_op);
240 case LoadType::kI64Load32S:
241 movsxlq(dst.gp(), src_op);
243 case LoadType::kI64Load:
244 movq(dst.gp(), src_op);
246 case LoadType::kF32Load:
247 Movss(dst.fp(), src_op);
249 case LoadType::kF64Load:
250 Movsd(dst.fp(), src_op);
257 void LiftoffAssembler::Store(Register dst_addr, Register offset_reg,
258 uint32_t offset_imm, LiftoffRegister src,
259 StoreType type, LiftoffRegList ,
260 uint32_t* protected_store_pc,
bool is_store_mem) {
261 if (emit_debug_code() && offset_reg != no_reg) {
262 AssertZeroExtended(offset_reg);
264 Operand dst_op = liftoff::GetMemOp(
this, dst_addr, offset_reg, offset_imm);
265 if (protected_store_pc) *protected_store_pc = pc_offset();
266 switch (type.value()) {
267 case StoreType::kI32Store8:
268 case StoreType::kI64Store8:
269 movb(dst_op, src.gp());
271 case StoreType::kI32Store16:
272 case StoreType::kI64Store16:
273 movw(dst_op, src.gp());
275 case StoreType::kI32Store:
276 case StoreType::kI64Store32:
277 movl(dst_op, src.gp());
279 case StoreType::kI64Store:
280 movq(dst_op, src.gp());
282 case StoreType::kF32Store:
283 Movss(dst_op, src.fp());
285 case StoreType::kF64Store:
286 Movsd(dst_op, src.fp());
293 void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst,
296 Operand src(rbp, kPointerSize * (caller_slot_idx + 1));
297 liftoff::Load(
this, dst, src, type);
300 void LiftoffAssembler::MoveStackValue(
uint32_t dst_index,
uint32_t src_index,
302 DCHECK_NE(dst_index, src_index);
303 if (cache_state_.has_unused_register(kGpReg)) {
304 Fill(LiftoffRegister{kScratchRegister}, src_index, type);
305 Spill(dst_index, LiftoffRegister{kScratchRegister}, type);
307 pushq(liftoff::GetStackSlot(src_index));
308 popq(liftoff::GetStackSlot(dst_index));
312 void LiftoffAssembler::Move(Register dst, Register src, ValueType type) {
314 if (type == kWasmI32) {
317 DCHECK_EQ(kWasmI64, type);
322 void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src,
325 if (type == kWasmF32) {
328 DCHECK_EQ(kWasmF64, type);
333 void LiftoffAssembler::Spill(
uint32_t index, LiftoffRegister reg,
335 RecordUsedSpillSlot(index);
336 Operand dst = liftoff::GetStackSlot(index);
345 Movss(dst, reg.fp());
348 Movsd(dst, reg.fp());
355 void LiftoffAssembler::Spill(
uint32_t index, WasmValue value) {
356 RecordUsedSpillSlot(index);
357 Operand dst = liftoff::GetStackSlot(index);
358 switch (value.type()) {
360 movl(dst, Immediate(value.to_i32()));
363 if (is_int32(value.to_i64())) {
365 movq(dst, Immediate(static_cast<int32_t>(value.to_i64())));
366 }
else if (is_uint32(value.to_i64())) {
368 movl(kScratchRegister, Immediate(static_cast<int32_t>(value.to_i64())));
369 movq(dst, kScratchRegister);
371 movq(kScratchRegister, value.to_i64());
372 movq(dst, kScratchRegister);
382 void LiftoffAssembler::Fill(LiftoffRegister reg,
uint32_t index,
384 Operand src = liftoff::GetStackSlot(index);
393 Movss(reg.fp(), src);
396 Movsd(reg.fp(), src);
403 void LiftoffAssembler::FillI64Half(Register,
uint32_t half_index) {
407 void LiftoffAssembler::emit_i32_add(Register dst, Register lhs, Register rhs) {
409 leal(dst, Operand(lhs, rhs, times_1, 0));
415 void LiftoffAssembler::emit_i32_sub(Register dst, Register lhs, Register rhs) {
420 if (dst != lhs) movl(dst, lhs);
426 template <void (Assembler::*op)(Register, Register),
427 void (Assembler::*mov)(Register, Register)>
428 void EmitCommutativeBinOp(LiftoffAssembler* assm, Register dst, Register lhs,
431 (assm->*op)(dst, lhs);
433 if (dst != lhs) (assm->*mov)(dst, lhs);
434 (assm->*op)(dst, rhs);
439 void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) {
440 liftoff::EmitCommutativeBinOp<&Assembler::imull, &Assembler::movl>(
this, dst,
445 enum class DivOrRem : uint8_t { kDiv, kRem };
446 template <
typename type, DivOrRem div_or_rem>
447 void EmitIntDivOrRem(LiftoffAssembler* assm, Register dst, Register lhs,
448 Register rhs, Label* trap_div_by_zero,
449 Label* trap_div_unrepresentable) {
450 constexpr
bool needs_unrepresentable_check =
451 std::is_signed<type>::value && div_or_rem == DivOrRem::kDiv;
452 constexpr
bool special_case_minus_1 =
453 std::is_signed<type>::value && div_or_rem == DivOrRem::kRem;
454 DCHECK_EQ(needs_unrepresentable_check, trap_div_unrepresentable !=
nullptr);
456 #define iop(name, ...) \ 458 if (sizeof(type) == 4) { \ 459 assm->name##l(__VA_ARGS__); \ 461 assm->name##q(__VA_ARGS__); \ 470 liftoff::SpillRegisters(assm, rdx, rax);
471 if (rhs == rax || rhs == rdx) {
472 iop(mov, kScratchRegister, rhs);
473 rhs = kScratchRegister;
478 assm->j(zero, trap_div_by_zero);
481 if (needs_unrepresentable_check) {
484 iop(cmp, rhs, Immediate(-1));
485 assm->j(not_equal, &do_div);
487 iop(cmp, lhs, Immediate(1));
488 assm->j(overflow, trap_div_unrepresentable);
490 }
else if (special_case_minus_1) {
494 iop(cmp, rhs, Immediate(-1));
495 assm->j(not_equal, &do_rem);
506 if (lhs != rax) iop(mov, rax, lhs);
507 if (std::is_same<int32_t, type>::value) {
510 }
else if (std::is_same<uint32_t, type>::value) {
511 assm->xorl(rdx, rdx);
513 }
else if (std::is_same<int64_t, type>::value) {
517 assm->xorq(rdx, rdx);
522 constexpr Register kResultReg = div_or_rem == DivOrRem::kDiv ? rax : rdx;
523 if (dst != kResultReg) {
524 iop(mov, dst, kResultReg);
526 if (special_case_minus_1) assm->bind(&done);
530 void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
531 Label* trap_div_by_zero,
532 Label* trap_div_unrepresentable) {
533 liftoff::EmitIntDivOrRem<int32_t, liftoff::DivOrRem::kDiv>(
534 this, dst, lhs, rhs, trap_div_by_zero, trap_div_unrepresentable);
537 void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
538 Label* trap_div_by_zero) {
539 liftoff::EmitIntDivOrRem<uint32_t, liftoff::DivOrRem::kDiv>(
540 this, dst, lhs, rhs, trap_div_by_zero,
nullptr);
543 void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
544 Label* trap_div_by_zero) {
545 liftoff::EmitIntDivOrRem<int32_t, liftoff::DivOrRem::kRem>(
546 this, dst, lhs, rhs, trap_div_by_zero,
nullptr);
549 void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
550 Label* trap_div_by_zero) {
551 liftoff::EmitIntDivOrRem<uint32_t, liftoff::DivOrRem::kRem>(
552 this, dst, lhs, rhs, trap_div_by_zero,
nullptr);
555 void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register rhs) {
556 liftoff::EmitCommutativeBinOp<&Assembler::andl, &Assembler::movl>(
this, dst,
560 void LiftoffAssembler::emit_i32_or(Register dst, Register lhs, Register rhs) {
561 liftoff::EmitCommutativeBinOp<&Assembler::orl, &Assembler::movl>(
this, dst,
565 void LiftoffAssembler::emit_i32_xor(Register dst, Register lhs, Register rhs) {
566 liftoff::EmitCommutativeBinOp<&Assembler::xorl, &Assembler::movl>(
this, dst,
571 template <ValueType type>
572 inline void EmitShiftOperation(LiftoffAssembler* assm, Register dst,
573 Register src, Register amount,
574 void (Assembler::*emit_shift)(Register),
575 LiftoffRegList pinned) {
578 assm->Move(kScratchRegister, src, type);
579 if (amount != rcx) assm->Move(rcx, amount, type);
580 (assm->*emit_shift)(kScratchRegister);
581 assm->Move(rcx, kScratchRegister, type);
587 bool use_scratch =
false;
589 use_scratch = src == rcx ||
590 assm->cache_state()->is_used(LiftoffRegister(rcx)) ||
591 pinned.has(LiftoffRegister(rcx));
592 if (use_scratch) assm->movq(kScratchRegister, rcx);
593 if (src == rcx) src = kScratchRegister;
594 assm->Move(rcx, amount, type);
598 if (dst != src) assm->Move(dst, src, type);
599 (assm->*emit_shift)(dst);
602 if (use_scratch) assm->movq(rcx, kScratchRegister);
606 void LiftoffAssembler::emit_i32_shl(Register dst, Register src, Register amount,
607 LiftoffRegList pinned) {
608 liftoff::EmitShiftOperation<kWasmI32>(
this, dst, src, amount,
609 &Assembler::shll_cl, pinned);
612 void LiftoffAssembler::emit_i32_sar(Register dst, Register src, Register amount,
613 LiftoffRegList pinned) {
614 liftoff::EmitShiftOperation<kWasmI32>(
this, dst, src, amount,
615 &Assembler::sarl_cl, pinned);
618 void LiftoffAssembler::emit_i32_shr(Register dst, Register src, Register amount,
619 LiftoffRegList pinned) {
620 liftoff::EmitShiftOperation<kWasmI32>(
this, dst, src, amount,
621 &Assembler::shrl_cl, pinned);
624 void LiftoffAssembler::emit_i32_shr(Register dst, Register src,
int amount) {
625 if (dst != src) movl(dst, src);
626 DCHECK(is_uint5(amount));
627 shrl(dst, Immediate(amount));
630 bool LiftoffAssembler::emit_i32_clz(Register dst, Register src) {
634 j(not_zero, &nonzero_input, Label::kNear);
635 movl(dst, Immediate(32));
636 jmp(&continuation, Label::kNear);
638 bind(&nonzero_input);
642 xorl(dst, Immediate(31));
648 bool LiftoffAssembler::emit_i32_ctz(Register dst, Register src) {
652 j(not_zero, &nonzero_input, Label::kNear);
653 movl(dst, Immediate(32));
654 jmp(&continuation, Label::kNear);
656 bind(&nonzero_input);
664 bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) {
665 if (!CpuFeatures::IsSupported(POPCNT))
return false;
666 CpuFeatureScope scope(
this, POPCNT);
671 void LiftoffAssembler::emit_i64_add(LiftoffRegister dst, LiftoffRegister lhs,
672 LiftoffRegister rhs) {
673 if (lhs.gp() != dst.gp()) {
674 leap(dst.gp(), Operand(lhs.gp(), rhs.gp(), times_1, 0));
676 addp(dst.gp(), rhs.gp());
680 void LiftoffAssembler::emit_i64_sub(LiftoffRegister dst, LiftoffRegister lhs,
681 LiftoffRegister rhs) {
682 if (dst.gp() == rhs.gp()) {
684 addq(dst.gp(), lhs.gp());
686 if (dst.gp() != lhs.gp()) movq(dst.gp(), lhs.gp());
687 subq(dst.gp(), rhs.gp());
691 void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs,
692 LiftoffRegister rhs) {
693 liftoff::EmitCommutativeBinOp<&Assembler::imulq, &Assembler::movq>(
694 this, dst.gp(), lhs.gp(), rhs.gp());
697 bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs,
699 Label* trap_div_by_zero,
700 Label* trap_div_unrepresentable) {
701 liftoff::EmitIntDivOrRem<int64_t, liftoff::DivOrRem::kDiv>(
702 this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero,
703 trap_div_unrepresentable);
707 bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs,
709 Label* trap_div_by_zero) {
710 liftoff::EmitIntDivOrRem<uint64_t, liftoff::DivOrRem::kDiv>(
711 this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero,
nullptr);
715 bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs,
717 Label* trap_div_by_zero) {
718 liftoff::EmitIntDivOrRem<int64_t, liftoff::DivOrRem::kRem>(
719 this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero,
nullptr);
723 bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs,
725 Label* trap_div_by_zero) {
726 liftoff::EmitIntDivOrRem<uint64_t, liftoff::DivOrRem::kRem>(
727 this, dst.gp(), lhs.gp(), rhs.gp(), trap_div_by_zero,
nullptr);
731 void LiftoffAssembler::emit_i64_and(LiftoffRegister dst, LiftoffRegister lhs,
732 LiftoffRegister rhs) {
733 liftoff::EmitCommutativeBinOp<&Assembler::andq, &Assembler::movq>(
734 this, dst.gp(), lhs.gp(), rhs.gp());
737 void LiftoffAssembler::emit_i64_or(LiftoffRegister dst, LiftoffRegister lhs,
738 LiftoffRegister rhs) {
739 liftoff::EmitCommutativeBinOp<&Assembler::orq, &Assembler::movq>(
740 this, dst.gp(), lhs.gp(), rhs.gp());
743 void LiftoffAssembler::emit_i64_xor(LiftoffRegister dst, LiftoffRegister lhs,
744 LiftoffRegister rhs) {
745 liftoff::EmitCommutativeBinOp<&Assembler::xorq, &Assembler::movq>(
746 this, dst.gp(), lhs.gp(), rhs.gp());
749 void LiftoffAssembler::emit_i64_shl(LiftoffRegister dst, LiftoffRegister src,
750 Register amount, LiftoffRegList pinned) {
751 liftoff::EmitShiftOperation<kWasmI64>(
this, dst.gp(), src.gp(), amount,
752 &Assembler::shlq_cl, pinned);
755 void LiftoffAssembler::emit_i64_sar(LiftoffRegister dst, LiftoffRegister src,
756 Register amount, LiftoffRegList pinned) {
757 liftoff::EmitShiftOperation<kWasmI64>(
this, dst.gp(), src.gp(), amount,
758 &Assembler::sarq_cl, pinned);
761 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
762 Register amount, LiftoffRegList pinned) {
763 liftoff::EmitShiftOperation<kWasmI64>(
this, dst.gp(), src.gp(), amount,
764 &Assembler::shrq_cl, pinned);
767 void LiftoffAssembler::emit_i64_shr(LiftoffRegister dst, LiftoffRegister src,
769 if (dst.gp() != src.gp()) movl(dst.gp(), src.gp());
770 DCHECK(is_uint6(amount));
771 shrq(dst.gp(), Immediate(amount));
774 void LiftoffAssembler::emit_i32_to_intptr(Register dst, Register src) {
778 void LiftoffAssembler::emit_f32_add(DoubleRegister dst, DoubleRegister lhs,
779 DoubleRegister rhs) {
780 if (CpuFeatures::IsSupported(AVX)) {
781 CpuFeatureScope scope(
this, AVX);
782 vaddss(dst, lhs, rhs);
783 }
else if (dst == rhs) {
786 if (dst != lhs) movss(dst, lhs);
791 void LiftoffAssembler::emit_f32_sub(DoubleRegister dst, DoubleRegister lhs,
792 DoubleRegister rhs) {
793 if (CpuFeatures::IsSupported(AVX)) {
794 CpuFeatureScope scope(
this, AVX);
795 vsubss(dst, lhs, rhs);
796 }
else if (dst == rhs) {
797 movss(kScratchDoubleReg, rhs);
799 subss(dst, kScratchDoubleReg);
801 if (dst != lhs) movss(dst, lhs);
806 void LiftoffAssembler::emit_f32_mul(DoubleRegister dst, DoubleRegister lhs,
807 DoubleRegister rhs) {
808 if (CpuFeatures::IsSupported(AVX)) {
809 CpuFeatureScope scope(
this, AVX);
810 vmulss(dst, lhs, rhs);
811 }
else if (dst == rhs) {
814 if (dst != lhs) movss(dst, lhs);
819 void LiftoffAssembler::emit_f32_div(DoubleRegister dst, DoubleRegister lhs,
820 DoubleRegister rhs) {
821 if (CpuFeatures::IsSupported(AVX)) {
822 CpuFeatureScope scope(
this, AVX);
823 vdivss(dst, lhs, rhs);
824 }
else if (dst == rhs) {
825 movss(kScratchDoubleReg, rhs);
827 divss(dst, kScratchDoubleReg);
829 if (dst != lhs) movss(dst, lhs);
835 enum class MinOrMax : uint8_t { kMin, kMax };
836 template <
typename type>
837 inline void EmitFloatMinOrMax(LiftoffAssembler* assm, DoubleRegister dst,
838 DoubleRegister lhs, DoubleRegister rhs,
839 MinOrMax min_or_max) {
845 #define dop(name, ...) \ 847 if (sizeof(type) == 4) { \ 848 assm->name##s(__VA_ARGS__); \ 850 assm->name##d(__VA_ARGS__); \ 856 dop(Ucomis, lhs, rhs);
857 assm->j(parity_even, &is_nan, Label::kNear);
858 assm->j(below, &lhs_below_rhs, Label::kNear);
859 assm->j(above, &lhs_above_rhs, Label::kNear);
867 dop(Movmskp, kScratchRegister, rhs);
868 assm->testl(kScratchRegister, Immediate(1));
869 assm->j(zero, &lhs_below_rhs, Label::kNear);
870 assm->jmp(&lhs_above_rhs, Label::kNear);
876 assm->jmp(&done, Label::kNear);
878 assm->bind(&lhs_below_rhs);
879 DoubleRegister lhs_below_rhs_src = min_or_max == MinOrMax::kMin ? lhs : rhs;
880 if (dst != lhs_below_rhs_src) dop(Movs, dst, lhs_below_rhs_src);
881 assm->jmp(&done, Label::kNear);
883 assm->bind(&lhs_above_rhs);
884 DoubleRegister lhs_above_rhs_src = min_or_max == MinOrMax::kMin ? rhs : lhs;
885 if (dst != lhs_above_rhs_src) dop(Movs, dst, lhs_above_rhs_src);
891 void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs,
892 DoubleRegister rhs) {
893 liftoff::EmitFloatMinOrMax<float>(
this, dst, lhs, rhs,
894 liftoff::MinOrMax::kMin);
897 void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs,
898 DoubleRegister rhs) {
899 liftoff::EmitFloatMinOrMax<float>(
this, dst, lhs, rhs,
900 liftoff::MinOrMax::kMax);
903 void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs,
904 DoubleRegister rhs) {
905 static constexpr
int kF32SignBit = 1 << 31;
906 Movd(kScratchRegister, lhs);
907 andl(kScratchRegister, Immediate(~kF32SignBit));
908 Movd(liftoff::kScratchRegister2, rhs);
909 andl(liftoff::kScratchRegister2, Immediate(kF32SignBit));
910 orl(kScratchRegister, liftoff::kScratchRegister2);
911 Movd(dst, kScratchRegister);
914 void LiftoffAssembler::emit_f32_abs(DoubleRegister dst, DoubleRegister src) {
917 TurboAssembler::Move(kScratchDoubleReg, kSignBit - 1);
918 Andps(dst, kScratchDoubleReg);
920 TurboAssembler::Move(dst, kSignBit - 1);
925 void LiftoffAssembler::emit_f32_neg(DoubleRegister dst, DoubleRegister src) {
928 TurboAssembler::Move(kScratchDoubleReg, kSignBit);
929 Xorps(dst, kScratchDoubleReg);
931 TurboAssembler::Move(dst, kSignBit);
936 bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) {
937 if (CpuFeatures::IsSupported(SSE4_1)) {
938 CpuFeatureScope feature(
this, SSE4_1);
939 Roundss(dst, src, kRoundUp);
945 bool LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) {
946 if (CpuFeatures::IsSupported(SSE4_1)) {
947 CpuFeatureScope feature(
this, SSE4_1);
948 Roundss(dst, src, kRoundDown);
954 bool LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) {
955 if (CpuFeatures::IsSupported(SSE4_1)) {
956 CpuFeatureScope feature(
this, SSE4_1);
957 Roundss(dst, src, kRoundToZero);
963 bool LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst,
964 DoubleRegister src) {
965 if (CpuFeatures::IsSupported(SSE4_1)) {
966 CpuFeatureScope feature(
this, SSE4_1);
967 Roundss(dst, src, kRoundToNearest);
973 void LiftoffAssembler::emit_f32_sqrt(DoubleRegister dst, DoubleRegister src) {
977 void LiftoffAssembler::emit_f64_add(DoubleRegister dst, DoubleRegister lhs,
978 DoubleRegister rhs) {
979 if (CpuFeatures::IsSupported(AVX)) {
980 CpuFeatureScope scope(
this, AVX);
981 vaddsd(dst, lhs, rhs);
982 }
else if (dst == rhs) {
985 if (dst != lhs) movsd(dst, lhs);
990 void LiftoffAssembler::emit_f64_sub(DoubleRegister dst, DoubleRegister lhs,
991 DoubleRegister rhs) {
992 if (CpuFeatures::IsSupported(AVX)) {
993 CpuFeatureScope scope(
this, AVX);
994 vsubsd(dst, lhs, rhs);
995 }
else if (dst == rhs) {
996 movsd(kScratchDoubleReg, rhs);
998 subsd(dst, kScratchDoubleReg);
1000 if (dst != lhs) movsd(dst, lhs);
1005 void LiftoffAssembler::emit_f64_mul(DoubleRegister dst, DoubleRegister lhs,
1006 DoubleRegister rhs) {
1007 if (CpuFeatures::IsSupported(AVX)) {
1008 CpuFeatureScope scope(
this, AVX);
1009 vmulsd(dst, lhs, rhs);
1010 }
else if (dst == rhs) {
1013 if (dst != lhs) movsd(dst, lhs);
1018 void LiftoffAssembler::emit_f64_div(DoubleRegister dst, DoubleRegister lhs,
1019 DoubleRegister rhs) {
1020 if (CpuFeatures::IsSupported(AVX)) {
1021 CpuFeatureScope scope(
this, AVX);
1022 vdivsd(dst, lhs, rhs);
1023 }
else if (dst == rhs) {
1024 movsd(kScratchDoubleReg, rhs);
1026 divsd(dst, kScratchDoubleReg);
1028 if (dst != lhs) movsd(dst, lhs);
1033 void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs,
1034 DoubleRegister rhs) {
1035 liftoff::EmitFloatMinOrMax<double>(
this, dst, lhs, rhs,
1036 liftoff::MinOrMax::kMin);
1039 void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs,
1040 DoubleRegister rhs) {
1042 Movq(liftoff::kScratchRegister2, rhs);
1043 shrq(liftoff::kScratchRegister2, Immediate(63));
1044 shlq(liftoff::kScratchRegister2, Immediate(63));
1046 Movq(kScratchRegister, lhs);
1047 btrq(kScratchRegister, Immediate(63));
1049 orq(kScratchRegister, liftoff::kScratchRegister2);
1050 Movq(dst, kScratchRegister);
1053 void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs,
1054 DoubleRegister rhs) {
1055 liftoff::EmitFloatMinOrMax<double>(
this, dst, lhs, rhs,
1056 liftoff::MinOrMax::kMax);
1059 void LiftoffAssembler::emit_f64_abs(DoubleRegister dst, DoubleRegister src) {
1060 static constexpr uint64_t kSignBit = uint64_t{1} << 63;
1062 TurboAssembler::Move(kScratchDoubleReg, kSignBit - 1);
1063 Andpd(dst, kScratchDoubleReg);
1065 TurboAssembler::Move(dst, kSignBit - 1);
1070 void LiftoffAssembler::emit_f64_neg(DoubleRegister dst, DoubleRegister src) {
1071 static constexpr uint64_t kSignBit = uint64_t{1} << 63;
1073 TurboAssembler::Move(kScratchDoubleReg, kSignBit);
1074 Xorpd(dst, kScratchDoubleReg);
1076 TurboAssembler::Move(dst, kSignBit);
1081 bool LiftoffAssembler::emit_f64_ceil(DoubleRegister dst, DoubleRegister src) {
1082 REQUIRE_CPU_FEATURE(SSE4_1,
true);
1083 Roundsd(dst, src, kRoundUp);
1087 bool LiftoffAssembler::emit_f64_floor(DoubleRegister dst, DoubleRegister src) {
1088 REQUIRE_CPU_FEATURE(SSE4_1,
true);
1089 Roundsd(dst, src, kRoundDown);
1093 bool LiftoffAssembler::emit_f64_trunc(DoubleRegister dst, DoubleRegister src) {
1094 REQUIRE_CPU_FEATURE(SSE4_1,
true);
1095 Roundsd(dst, src, kRoundToZero);
1099 bool LiftoffAssembler::emit_f64_nearest_int(DoubleRegister dst,
1100 DoubleRegister src) {
1101 REQUIRE_CPU_FEATURE(SSE4_1,
true);
1102 Roundsd(dst, src, kRoundToNearest);
1106 void LiftoffAssembler::emit_f64_sqrt(DoubleRegister dst, DoubleRegister src) {
1113 template <
typename dst_type,
typename src_type>
1114 inline void ConvertFloatToIntAndBack(LiftoffAssembler* assm, Register dst,
1116 DoubleRegister converted_back) {
1117 if (std::is_same<double, src_type>::value) {
1118 if (std::is_same<int32_t, dst_type>::value) {
1119 assm->Cvttsd2si(dst, src);
1120 assm->Cvtlsi2sd(converted_back, dst);
1121 }
else if (std::is_same<uint32_t, dst_type>::value) {
1122 assm->Cvttsd2siq(dst, src);
1123 assm->movl(dst, dst);
1124 assm->Cvtqsi2sd(converted_back, dst);
1125 }
else if (std::is_same<int64_t, dst_type>::value) {
1126 assm->Cvttsd2siq(dst, src);
1127 assm->Cvtqsi2sd(converted_back, dst);
1132 if (std::is_same<int32_t, dst_type>::value) {
1133 assm->Cvttss2si(dst, src);
1134 assm->Cvtlsi2ss(converted_back, dst);
1135 }
else if (std::is_same<uint32_t, dst_type>::value) {
1136 assm->Cvttss2siq(dst, src);
1137 assm->movl(dst, dst);
1138 assm->Cvtqsi2ss(converted_back, dst);
1139 }
else if (std::is_same<int64_t, dst_type>::value) {
1140 assm->Cvttss2siq(dst, src);
1141 assm->Cvtqsi2ss(converted_back, dst);
1148 template <
typename dst_type,
typename src_type>
1149 inline bool EmitTruncateFloatToInt(LiftoffAssembler* assm, Register dst,
1150 DoubleRegister src, Label* trap) {
1151 if (!CpuFeatures::IsSupported(SSE4_1)) {
1152 assm->bailout(
"no SSE4.1");
1155 CpuFeatureScope feature(assm, SSE4_1);
1157 DoubleRegister rounded = kScratchDoubleReg;
1158 DoubleRegister converted_back = kScratchDoubleReg2;
1160 if (std::is_same<double, src_type>::value) {
1161 assm->Roundsd(rounded, src, kRoundToZero);
1163 assm->Roundss(rounded, src, kRoundToZero);
1165 ConvertFloatToIntAndBack<dst_type, src_type>(assm, dst, rounded,
1167 if (std::is_same<double, src_type>::value) {
1168 assm->Ucomisd(converted_back, rounded);
1170 assm->Ucomiss(converted_back, rounded);
1175 assm->j(parity_even, trap);
1176 assm->j(not_equal, trap);
1181 bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
1182 LiftoffRegister dst,
1183 LiftoffRegister src, Label* trap) {
1185 case kExprI32ConvertI64:
1186 movl(dst.gp(), src.gp());
1188 case kExprI32SConvertF32:
1189 return liftoff::EmitTruncateFloatToInt<int32_t, float>(
this, dst.gp(),
1191 case kExprI32UConvertF32:
1192 return liftoff::EmitTruncateFloatToInt<uint32_t, float>(
this, dst.gp(),
1194 case kExprI32SConvertF64:
1195 return liftoff::EmitTruncateFloatToInt<int32_t, double>(
this, dst.gp(),
1197 case kExprI32UConvertF64:
1198 return liftoff::EmitTruncateFloatToInt<uint32_t, double>(
this, dst.gp(),
1200 case kExprI32ReinterpretF32:
1201 Movd(dst.gp(), src.fp());
1203 case kExprI64SConvertI32:
1204 movsxlq(dst.gp(), src.gp());
1206 case kExprI64SConvertF32:
1207 return liftoff::EmitTruncateFloatToInt<int64_t, float>(
this, dst.gp(),
1209 case kExprI64UConvertF32: {
1210 REQUIRE_CPU_FEATURE(SSE4_1,
true);
1211 Cvttss2uiq(dst.gp(), src.fp(), trap);
1214 case kExprI64SConvertF64:
1215 return liftoff::EmitTruncateFloatToInt<int64_t, double>(
this, dst.gp(),
1217 case kExprI64UConvertF64: {
1218 REQUIRE_CPU_FEATURE(SSE4_1,
true);
1219 Cvttsd2uiq(dst.gp(), src.fp(), trap);
1222 case kExprI64UConvertI32:
1223 AssertZeroExtended(src.gp());
1224 if (dst.gp() != src.gp()) movl(dst.gp(), src.gp());
1226 case kExprI64ReinterpretF64:
1227 Movq(dst.gp(), src.fp());
1229 case kExprF32SConvertI32:
1230 Cvtlsi2ss(dst.fp(), src.gp());
1232 case kExprF32UConvertI32:
1233 movl(kScratchRegister, src.gp());
1234 Cvtqsi2ss(dst.fp(), kScratchRegister);
1236 case kExprF32SConvertI64:
1237 Cvtqsi2ss(dst.fp(), src.gp());
1239 case kExprF32UConvertI64:
1240 Cvtqui2ss(dst.fp(), src.gp());
1242 case kExprF32ConvertF64:
1243 Cvtsd2ss(dst.fp(), src.fp());
1245 case kExprF32ReinterpretI32:
1246 Movd(dst.fp(), src.gp());
1248 case kExprF64SConvertI32:
1249 Cvtlsi2sd(dst.fp(), src.gp());
1251 case kExprF64UConvertI32:
1252 movl(kScratchRegister, src.gp());
1253 Cvtqsi2sd(dst.fp(), kScratchRegister);
1255 case kExprF64SConvertI64:
1256 Cvtqsi2sd(dst.fp(), src.gp());
1258 case kExprF64UConvertI64:
1259 Cvtqui2sd(dst.fp(), src.gp());
1261 case kExprF64ConvertF32:
1262 Cvtss2sd(dst.fp(), src.fp());
1264 case kExprF64ReinterpretI64:
1265 Movq(dst.fp(), src.gp());
1272 void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) {
1276 void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) {
1280 void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst,
1281 LiftoffRegister src) {
1282 movsxbq(dst.gp(), src.gp());
1285 void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst,
1286 LiftoffRegister src) {
1287 movsxwq(dst.gp(), src.gp());
1290 void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst,
1291 LiftoffRegister src) {
1292 movsxlq(dst.gp(), src.gp());
1295 void LiftoffAssembler::emit_jump(Label* label) { jmp(label); }
1297 void LiftoffAssembler::emit_jump(Register target) { jmp(target); }
1299 void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label,
1300 ValueType type, Register lhs,
1302 if (rhs != no_reg) {
1314 DCHECK_EQ(type, kWasmI32);
1321 void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) {
1327 void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst,
1328 Register lhs, Register rhs) {
1334 void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) {
1335 testq(src.gp(), src.gp());
1340 void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst,
1341 LiftoffRegister lhs,
1342 LiftoffRegister rhs) {
1343 cmpq(lhs.gp(), rhs.gp());
1349 template <
void (TurboAssembler::*cmp_op)(DoubleRegister, DoubleRegister)>
1350 void EmitFloatSetCond(LiftoffAssembler* assm, Condition cond, Register dst,
1351 DoubleRegister lhs, DoubleRegister rhs) {
1355 (assm->*cmp_op)(lhs, rhs);
1357 assm->j(parity_odd, ¬_nan, Label::kNear);
1359 if (cond == not_equal) {
1360 assm->movl(dst, Immediate(1));
1362 assm->xorl(dst, dst);
1364 assm->jmp(&cont, Label::kNear);
1365 assm->bind(¬_nan);
1367 assm->setcc(cond, dst);
1368 assm->movzxbl(dst, dst);
1373 void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst,
1375 DoubleRegister rhs) {
1376 liftoff::EmitFloatSetCond<&TurboAssembler::Ucomiss>(
this, cond, dst, lhs,
1380 void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst,
1382 DoubleRegister rhs) {
1383 liftoff::EmitFloatSetCond<&TurboAssembler::Ucomisd>(
this, cond, dst, lhs,
1387 void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) {
1388 cmpp(rsp, Operand(limit_address, 0));
1389 j(below_equal, ool_code);
1392 void LiftoffAssembler::CallTrapCallbackForTesting() {
1393 PrepareCallCFunction(0);
1394 CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0);
1397 void LiftoffAssembler::AssertUnreachable(AbortReason reason) {
1398 TurboAssembler::AssertUnreachable(reason);
1401 void LiftoffAssembler::PushRegisters(LiftoffRegList regs) {
1402 LiftoffRegList gp_regs = regs & kGpCacheRegList;
1403 while (!gp_regs.is_empty()) {
1404 LiftoffRegister reg = gp_regs.GetFirstRegSet();
1408 LiftoffRegList fp_regs = regs & kFpCacheRegList;
1409 unsigned num_fp_regs = fp_regs.GetNumRegsSet();
1411 subp(rsp, Immediate(num_fp_regs * kStackSlotSize));
1412 unsigned offset = 0;
1413 while (!fp_regs.is_empty()) {
1414 LiftoffRegister reg = fp_regs.GetFirstRegSet();
1415 Movsd(Operand(rsp, offset), reg.fp());
1417 offset +=
sizeof(double);
1419 DCHECK_EQ(offset, num_fp_regs *
sizeof(
double));
1423 void LiftoffAssembler::PopRegisters(LiftoffRegList regs) {
1424 LiftoffRegList fp_regs = regs & kFpCacheRegList;
1425 unsigned fp_offset = 0;
1426 while (!fp_regs.is_empty()) {
1427 LiftoffRegister reg = fp_regs.GetFirstRegSet();
1428 Movsd(reg.fp(), Operand(rsp, fp_offset));
1430 fp_offset +=
sizeof(double);
1432 if (fp_offset) addp(rsp, Immediate(fp_offset));
1433 LiftoffRegList gp_regs = regs & kGpCacheRegList;
1434 while (!gp_regs.is_empty()) {
1435 LiftoffRegister reg = gp_regs.GetLastRegSet();
1441 void LiftoffAssembler::DropStackSlotsAndRet(
uint32_t num_stack_slots) {
1442 DCHECK_LT(num_stack_slots, (1 << 16) / kPointerSize);
1443 ret(static_cast<int>(num_stack_slots * kPointerSize));
1446 void LiftoffAssembler::CallC(wasm::FunctionSig* sig,
1447 const LiftoffRegister* args,
1448 const LiftoffRegister* rets,
1449 ValueType out_argument_type,
int stack_bytes,
1450 ExternalReference ext_ref) {
1451 subp(rsp, Immediate(stack_bytes));
1454 for (ValueType param_type : sig->parameters()) {
1455 liftoff::Store(
this, Operand(rsp, arg_bytes), *args++, param_type);
1456 arg_bytes += ValueTypes::MemSize(param_type);
1458 DCHECK_LE(arg_bytes, stack_bytes);
1461 movp(arg_reg_1, rsp);
1463 constexpr
int kNumCCallArgs = 1;
1466 PrepareCallCFunction(kNumCCallArgs);
1467 CallCFunction(ext_ref, kNumCCallArgs);
1470 const LiftoffRegister* next_result_reg = rets;
1471 if (sig->return_count() > 0) {
1472 DCHECK_EQ(1, sig->return_count());
1473 constexpr Register kReturnReg = rax;
1474 if (kReturnReg != next_result_reg->gp()) {
1475 Move(*next_result_reg, LiftoffRegister(kReturnReg), sig->GetReturn(0));
1481 if (out_argument_type != kWasmStmt) {
1482 liftoff::Load(
this, *next_result_reg, Operand(rsp, 0), out_argument_type);
1485 addp(rsp, Immediate(stack_bytes));
1488 void LiftoffAssembler::CallNativeWasmCode(Address addr) {
1489 near_call(addr, RelocInfo::WASM_CALL);
1492 void LiftoffAssembler::CallIndirect(wasm::FunctionSig* sig,
1493 compiler::CallDescriptor* call_descriptor,
1495 if (target == no_reg) {
1496 popq(kScratchRegister);
1497 target = kScratchRegister;
1499 if (FLAG_untrusted_code_mitigations) {
1500 RetpolineCall(target);
1506 void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) {
1509 near_call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL);
1512 void LiftoffAssembler::AllocateStackSlot(Register addr,
uint32_t size) {
1513 subp(rsp, Immediate(size));
1517 void LiftoffAssembler::DeallocateStackSlot(
uint32_t size) {
1518 addp(rsp, Immediate(size));
1521 void LiftoffStackSlots::Construct() {
1522 for (
auto& slot : slots_) {
1523 const LiftoffAssembler::VarState& src = slot.src_;
1524 switch (src.loc()) {
1525 case LiftoffAssembler::VarState::kStack:
1526 if (src.type() == kWasmI32) {
1529 asm_->movl(kScratchRegister, liftoff::GetStackSlot(slot.src_index_));
1530 asm_->pushq(kScratchRegister);
1536 asm_->pushq(liftoff::GetStackSlot(slot.src_index_));
1539 case LiftoffAssembler::VarState::kRegister:
1540 liftoff::push(asm_, src.reg(), src.type());
1542 case LiftoffAssembler::VarState::KIntConst:
1543 asm_->pushq(Immediate(src.i32_const()));
1549 #undef REQUIRE_CPU_FEATURE 1555 #endif // V8_WASM_BASELINE_X64_LIFTOFF_ASSEMBLER_X64_H_