| // Copyright 2017 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_WASM_BASELINE_MIPS64_LIFTOFF_ASSEMBLER_MIPS64_H_ |
| #define V8_WASM_BASELINE_MIPS64_LIFTOFF_ASSEMBLER_MIPS64_H_ |
| |
| #include "src/heap/memory-chunk.h" |
| #include "src/wasm/baseline/liftoff-assembler.h" |
| |
| namespace v8 { |
| namespace internal { |
| namespace wasm { |
| |
| namespace liftoff { |
| |
| // Liftoff Frames. |
| // |
| // slot Frame |
| // +--------------------+--------------------------- |
| // n+4 | optional padding slot to keep the stack 16 byte aligned. |
| // n+3 | parameter n | |
| // ... | ... | |
| // 4 | parameter 1 | or parameter 2 |
| // 3 | parameter 0 | or parameter 1 |
| // 2 | (result address) | or parameter 0 |
| // -----+--------------------+--------------------------- |
| // 1 | return addr (ra) | |
| // 0 | previous frame (fp)| |
| // -----+--------------------+ <-- frame ptr (fp) |
| // -1 | 0xa: WASM | |
| // -2 | instance | |
| // -----+--------------------+--------------------------- |
| // -3 | slot 0 | ^ |
| // -4 | slot 1 | | |
| // | | Frame slots |
| // | | | |
| // | | v |
| // | optional padding slot to keep the stack 16 byte aligned. |
| // -----+--------------------+ <-- stack ptr (sp) |
| // |
| |
| // fp-8 holds the stack marker, fp-16 is the instance parameter. |
| constexpr int kInstanceOffset = 16; |
| |
| inline MemOperand GetStackSlot(int offset) { return MemOperand(fp, -offset); } |
| |
| inline MemOperand GetInstanceOperand() { return GetStackSlot(kInstanceOffset); } |
| |
| inline MemOperand GetMemOp(LiftoffAssembler* assm, Register addr, |
| Register offset, uint32_t offset_imm) { |
| if (is_uint31(offset_imm)) { |
| if (offset == no_reg) return MemOperand(addr, offset_imm); |
| assm->daddu(kScratchReg, addr, offset); |
| return MemOperand(kScratchReg, offset_imm); |
| } |
| // Offset immediate does not fit in 31 bits. |
| assm->li(kScratchReg, offset_imm); |
| assm->daddu(kScratchReg, kScratchReg, addr); |
| if (offset != no_reg) { |
| assm->daddu(kScratchReg, kScratchReg, offset); |
| } |
| return MemOperand(kScratchReg, 0); |
| } |
| |
| inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, MemOperand src, |
| ValueType type) { |
| switch (type.kind()) { |
| case ValueType::kI32: |
| assm->Lw(dst.gp(), src); |
| break; |
| case ValueType::kI64: |
| case ValueType::kRef: |
| case ValueType::kOptRef: |
| assm->Ld(dst.gp(), src); |
| break; |
| case ValueType::kF32: |
| assm->Lwc1(dst.fp(), src); |
| break; |
| case ValueType::kF64: |
| assm->Ldc1(dst.fp(), src); |
| break; |
| case ValueType::kS128: |
| assm->ld_b(dst.fp().toW(), src); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| inline void Store(LiftoffAssembler* assm, Register base, int32_t offset, |
| LiftoffRegister src, ValueType type) { |
| MemOperand dst(base, offset); |
| switch (type.kind()) { |
| case ValueType::kI32: |
| assm->Usw(src.gp(), dst); |
| break; |
| case ValueType::kI64: |
| assm->Usd(src.gp(), dst); |
| break; |
| case ValueType::kF32: |
| assm->Uswc1(src.fp(), dst, t8); |
| break; |
| case ValueType::kF64: |
| assm->Usdc1(src.fp(), dst, t8); |
| break; |
| case ValueType::kS128: |
| assm->st_b(src.fp().toW(), dst); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) { |
| switch (type.kind()) { |
| case ValueType::kI32: |
| assm->daddiu(sp, sp, -kSystemPointerSize); |
| assm->sw(reg.gp(), MemOperand(sp, 0)); |
| break; |
| case ValueType::kI64: |
| assm->push(reg.gp()); |
| break; |
| case ValueType::kF32: |
| assm->daddiu(sp, sp, -kSystemPointerSize); |
| assm->swc1(reg.fp(), MemOperand(sp, 0)); |
| break; |
| case ValueType::kF64: |
| assm->daddiu(sp, sp, -kSystemPointerSize); |
| assm->Sdc1(reg.fp(), MemOperand(sp, 0)); |
| break; |
| case ValueType::kS128: |
| assm->daddiu(sp, sp, -kSystemPointerSize * 2); |
| assm->st_b(reg.fp().toW(), MemOperand(sp, 0)); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| #if defined(V8_TARGET_BIG_ENDIAN) |
| inline void ChangeEndiannessLoad(LiftoffAssembler* assm, LiftoffRegister dst, |
| LoadType type, LiftoffRegList pinned) { |
| bool is_float = false; |
| LiftoffRegister tmp = dst; |
| switch (type.value()) { |
| case LoadType::kI64Load8U: |
| case LoadType::kI64Load8S: |
| case LoadType::kI32Load8U: |
| case LoadType::kI32Load8S: |
| // No need to change endianness for byte size. |
| return; |
| case LoadType::kF32Load: |
| is_float = true; |
| tmp = assm->GetUnusedRegister(kGpReg, pinned); |
| assm->emit_type_conversion(kExprI32ReinterpretF32, tmp, dst); |
| V8_FALLTHROUGH; |
| case LoadType::kI64Load32U: |
| assm->TurboAssembler::ByteSwapUnsigned(tmp.gp(), tmp.gp(), 4); |
| break; |
| case LoadType::kI32Load: |
| case LoadType::kI64Load32S: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 4); |
| break; |
| case LoadType::kI32Load16S: |
| case LoadType::kI64Load16S: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 2); |
| break; |
| case LoadType::kI32Load16U: |
| case LoadType::kI64Load16U: |
| assm->TurboAssembler::ByteSwapUnsigned(tmp.gp(), tmp.gp(), 2); |
| break; |
| case LoadType::kF64Load: |
| is_float = true; |
| tmp = assm->GetUnusedRegister(kGpReg, pinned); |
| assm->emit_type_conversion(kExprI64ReinterpretF64, tmp, dst); |
| V8_FALLTHROUGH; |
| case LoadType::kI64Load: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 8); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| if (is_float) { |
| switch (type.value()) { |
| case LoadType::kF32Load: |
| assm->emit_type_conversion(kExprF32ReinterpretI32, dst, tmp); |
| break; |
| case LoadType::kF64Load: |
| assm->emit_type_conversion(kExprF64ReinterpretI64, dst, tmp); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| } |
| |
| inline void ChangeEndiannessStore(LiftoffAssembler* assm, LiftoffRegister src, |
| StoreType type, LiftoffRegList pinned) { |
| bool is_float = false; |
| LiftoffRegister tmp = src; |
| switch (type.value()) { |
| case StoreType::kI64Store8: |
| case StoreType::kI32Store8: |
| // No need to change endianness for byte size. |
| return; |
| case StoreType::kF32Store: |
| is_float = true; |
| tmp = assm->GetUnusedRegister(kGpReg, pinned); |
| assm->emit_type_conversion(kExprI32ReinterpretF32, tmp, src); |
| V8_FALLTHROUGH; |
| case StoreType::kI32Store: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 4); |
| break; |
| case StoreType::kI32Store16: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 2); |
| break; |
| case StoreType::kF64Store: |
| is_float = true; |
| tmp = assm->GetUnusedRegister(kGpReg, pinned); |
| assm->emit_type_conversion(kExprI64ReinterpretF64, tmp, src); |
| V8_FALLTHROUGH; |
| case StoreType::kI64Store: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 8); |
| break; |
| case StoreType::kI64Store32: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 4); |
| break; |
| case StoreType::kI64Store16: |
| assm->TurboAssembler::ByteSwapSigned(tmp.gp(), tmp.gp(), 2); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| if (is_float) { |
| switch (type.value()) { |
| case StoreType::kF32Store: |
| assm->emit_type_conversion(kExprF32ReinterpretI32, src, tmp); |
| break; |
| case StoreType::kF64Store: |
| assm->emit_type_conversion(kExprF64ReinterpretI64, src, tmp); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| } |
| #endif // V8_TARGET_BIG_ENDIAN |
| |
| } // namespace liftoff |
| |
| int LiftoffAssembler::PrepareStackFrame() { |
| int offset = pc_offset(); |
| // When constant that represents size of stack frame can't be represented |
| // as 16bit we need three instructions to add it to sp, so we reserve space |
| // for this case. |
| daddiu(sp, sp, 0); |
| nop(); |
| nop(); |
| return offset; |
| } |
| |
| void LiftoffAssembler::PrepareTailCall(int num_callee_stack_params, |
| int stack_param_delta) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| |
| // Push the return address and frame pointer to complete the stack frame. |
| Ld(scratch, MemOperand(fp, 8)); |
| Push(scratch); |
| Ld(scratch, MemOperand(fp, 0)); |
| Push(scratch); |
| |
| // Shift the whole frame upwards. |
| int slot_count = num_callee_stack_params + 2; |
| for (int i = slot_count - 1; i >= 0; --i) { |
| Ld(scratch, MemOperand(sp, i * 8)); |
| Sd(scratch, MemOperand(fp, (i - stack_param_delta) * 8)); |
| } |
| |
| // Set the new stack and frame pointer. |
| daddiu(sp, fp, -stack_param_delta * 8); |
| Pop(ra, fp); |
| } |
| |
| void LiftoffAssembler::PatchPrepareStackFrame(int offset, int frame_size) { |
| // We can't run out of space, just pass anything big enough to not cause the |
| // assembler to try to grow the buffer. |
| constexpr int kAvailableSpace = 256; |
| TurboAssembler patching_assembler( |
| nullptr, AssemblerOptions{}, CodeObjectRequired::kNo, |
| ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace)); |
| // If bytes can be represented as 16bit, daddiu will be generated and two |
| // nops will stay untouched. Otherwise, lui-ori sequence will load it to |
| // register and, as third instruction, daddu will be generated. |
| patching_assembler.Daddu(sp, sp, Operand(-frame_size)); |
| } |
| |
| void LiftoffAssembler::FinishCode() {} |
| |
| void LiftoffAssembler::AbortCompilation() {} |
| |
| // static |
| constexpr int LiftoffAssembler::StaticStackFrameSize() { |
| return liftoff::kInstanceOffset; |
| } |
| |
| int LiftoffAssembler::SlotSizeForType(ValueType type) { |
| switch (type.kind()) { |
| case ValueType::kS128: |
| return type.element_size_bytes(); |
| default: |
| return kStackSlotSize; |
| } |
| } |
| |
| bool LiftoffAssembler::NeedsAlignment(ValueType type) { |
| return type.kind() == ValueType::kS128 || type.is_reference_type(); |
| } |
| |
| void LiftoffAssembler::LoadConstant(LiftoffRegister reg, WasmValue value, |
| RelocInfo::Mode rmode) { |
| switch (value.type().kind()) { |
| case ValueType::kI32: |
| TurboAssembler::li(reg.gp(), Operand(value.to_i32(), rmode)); |
| break; |
| case ValueType::kI64: |
| TurboAssembler::li(reg.gp(), Operand(value.to_i64(), rmode)); |
| break; |
| case ValueType::kF32: |
| TurboAssembler::Move(reg.fp(), value.to_f32_boxed().get_bits()); |
| break; |
| case ValueType::kF64: |
| TurboAssembler::Move(reg.fp(), value.to_f64_boxed().get_bits()); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| void LiftoffAssembler::LoadFromInstance(Register dst, int32_t offset, |
| int size) { |
| DCHECK_LE(0, offset); |
| Ld(dst, liftoff::GetInstanceOperand()); |
| DCHECK(size == 4 || size == 8); |
| if (size == 4) { |
| Lw(dst, MemOperand(dst, offset)); |
| } else { |
| Ld(dst, MemOperand(dst, offset)); |
| } |
| } |
| |
| void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst, |
| int32_t offset) { |
| LoadFromInstance(dst, offset, kTaggedSize); |
| } |
| |
| void LiftoffAssembler::SpillInstance(Register instance) { |
| Sd(instance, liftoff::GetInstanceOperand()); |
| } |
| |
| void LiftoffAssembler::FillInstanceInto(Register dst) { |
| Ld(dst, liftoff::GetInstanceOperand()); |
| } |
| |
| void LiftoffAssembler::LoadTaggedPointer(Register dst, Register src_addr, |
| Register offset_reg, |
| int32_t offset_imm, |
| LiftoffRegList pinned) { |
| DCHECK_GE(offset_imm, 0); |
| STATIC_ASSERT(kTaggedSize == kInt64Size); |
| Load(LiftoffRegister(dst), src_addr, offset_reg, |
| static_cast<uint32_t>(offset_imm), LoadType::kI64Load, pinned); |
| } |
| |
| void LiftoffAssembler::StoreTaggedPointer(Register dst_addr, |
| int32_t offset_imm, |
| LiftoffRegister src, |
| LiftoffRegList pinned) { |
| DCHECK_GE(offset_imm, 0); |
| DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max()); |
| STATIC_ASSERT(kTaggedSize == kInt64Size); |
| Register scratch = pinned.set(GetUnusedRegister(kGpReg, pinned)).gp(); |
| Sd(src.gp(), MemOperand(dst_addr, offset_imm)); |
| |
| Label write_barrier; |
| Label exit; |
| CheckPageFlag(dst_addr, scratch, |
| MemoryChunk::kPointersFromHereAreInterestingMask, ne, |
| &write_barrier); |
| b(&exit); |
| bind(&write_barrier); |
| JumpIfSmi(src.gp(), &exit); |
| CheckPageFlag(src.gp(), scratch, |
| MemoryChunk::kPointersToHereAreInterestingMask, eq, |
| &exit); |
| Daddu(scratch, dst_addr, offset_imm); |
| CallRecordWriteStub(dst_addr, scratch, EMIT_REMEMBERED_SET, kSaveFPRegs, |
| wasm::WasmCode::kRecordWrite); |
| bind(&exit); |
| } |
| |
| void LiftoffAssembler::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) { |
| MemOperand src_op = liftoff::GetMemOp(this, src_addr, offset_reg, offset_imm); |
| |
| if (protected_load_pc) *protected_load_pc = pc_offset(); |
| switch (type.value()) { |
| case LoadType::kI32Load8U: |
| case LoadType::kI64Load8U: |
| Lbu(dst.gp(), src_op); |
| break; |
| case LoadType::kI32Load8S: |
| case LoadType::kI64Load8S: |
| Lb(dst.gp(), src_op); |
| break; |
| case LoadType::kI32Load16U: |
| case LoadType::kI64Load16U: |
| TurboAssembler::Ulhu(dst.gp(), src_op); |
| break; |
| case LoadType::kI32Load16S: |
| case LoadType::kI64Load16S: |
| TurboAssembler::Ulh(dst.gp(), src_op); |
| break; |
| case LoadType::kI64Load32U: |
| TurboAssembler::Ulwu(dst.gp(), src_op); |
| break; |
| case LoadType::kI32Load: |
| case LoadType::kI64Load32S: |
| TurboAssembler::Ulw(dst.gp(), src_op); |
| break; |
| case LoadType::kI64Load: |
| TurboAssembler::Uld(dst.gp(), src_op); |
| break; |
| case LoadType::kF32Load: |
| TurboAssembler::Ulwc1(dst.fp(), src_op, t8); |
| break; |
| case LoadType::kF64Load: |
| TurboAssembler::Uldc1(dst.fp(), src_op, t8); |
| break; |
| case LoadType::kS128Load: |
| TurboAssembler::ld_b(dst.fp().toW(), src_op); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| |
| #if defined(V8_TARGET_BIG_ENDIAN) |
| if (is_load_mem) { |
| pinned.set(src_op.rm()); |
| liftoff::ChangeEndiannessLoad(this, dst, type, pinned); |
| } |
| #endif |
| } |
| |
| void LiftoffAssembler::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) { |
| MemOperand dst_op = liftoff::GetMemOp(this, dst_addr, offset_reg, offset_imm); |
| |
| #if defined(V8_TARGET_BIG_ENDIAN) |
| if (is_store_mem) { |
| pinned.set(dst_op.rm()); |
| LiftoffRegister tmp = GetUnusedRegister(src.reg_class(), pinned); |
| // Save original value. |
| Move(tmp, src, type.value_type()); |
| |
| src = tmp; |
| pinned.set(tmp); |
| liftoff::ChangeEndiannessStore(this, src, type, pinned); |
| } |
| #endif |
| |
| if (protected_store_pc) *protected_store_pc = pc_offset(); |
| switch (type.value()) { |
| case StoreType::kI32Store8: |
| case StoreType::kI64Store8: |
| Sb(src.gp(), dst_op); |
| break; |
| case StoreType::kI32Store16: |
| case StoreType::kI64Store16: |
| TurboAssembler::Ush(src.gp(), dst_op, t8); |
| break; |
| case StoreType::kI32Store: |
| case StoreType::kI64Store32: |
| TurboAssembler::Usw(src.gp(), dst_op); |
| break; |
| case StoreType::kI64Store: |
| TurboAssembler::Usd(src.gp(), dst_op); |
| break; |
| case StoreType::kF32Store: |
| TurboAssembler::Uswc1(src.fp(), dst_op, t8); |
| break; |
| case StoreType::kF64Store: |
| TurboAssembler::Usdc1(src.fp(), dst_op, t8); |
| break; |
| case StoreType::kS128Store: |
| TurboAssembler::st_b(src.fp().toW(), dst_op); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| void LiftoffAssembler::AtomicLoad(LiftoffRegister dst, Register src_addr, |
| Register offset_reg, uint32_t offset_imm, |
| LoadType type, LiftoffRegList pinned) { |
| bailout(kAtomics, "AtomicLoad"); |
| } |
| |
| void LiftoffAssembler::AtomicStore(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, LiftoffRegister src, |
| StoreType type, LiftoffRegList pinned) { |
| bailout(kAtomics, "AtomicStore"); |
| } |
| |
| void LiftoffAssembler::AtomicAdd(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, LiftoffRegister value, |
| LiftoffRegister result, StoreType type) { |
| bailout(kAtomics, "AtomicAdd"); |
| } |
| |
| void LiftoffAssembler::AtomicSub(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, LiftoffRegister value, |
| LiftoffRegister result, StoreType type) { |
| bailout(kAtomics, "AtomicSub"); |
| } |
| |
| void LiftoffAssembler::AtomicAnd(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, LiftoffRegister value, |
| LiftoffRegister result, StoreType type) { |
| bailout(kAtomics, "AtomicAnd"); |
| } |
| |
| void LiftoffAssembler::AtomicOr(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, LiftoffRegister value, |
| LiftoffRegister result, StoreType type) { |
| bailout(kAtomics, "AtomicOr"); |
| } |
| |
| void LiftoffAssembler::AtomicXor(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, LiftoffRegister value, |
| LiftoffRegister result, StoreType type) { |
| bailout(kAtomics, "AtomicXor"); |
| } |
| |
| void LiftoffAssembler::AtomicExchange(Register dst_addr, Register offset_reg, |
| uint32_t offset_imm, |
| LiftoffRegister value, |
| LiftoffRegister result, StoreType type) { |
| bailout(kAtomics, "AtomicExchange"); |
| } |
| |
| void LiftoffAssembler::AtomicCompareExchange( |
| Register dst_addr, Register offset_reg, uint32_t offset_imm, |
| LiftoffRegister expected, LiftoffRegister new_value, LiftoffRegister result, |
| StoreType type) { |
| bailout(kAtomics, "AtomicCompareExchange"); |
| } |
| |
| void LiftoffAssembler::AtomicFence() { sync(); } |
| |
| void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst, |
| uint32_t caller_slot_idx, |
| ValueType type) { |
| MemOperand src(fp, kSystemPointerSize * (caller_slot_idx + 1)); |
| liftoff::Load(this, dst, src, type); |
| } |
| |
| void LiftoffAssembler::StoreCallerFrameSlot(LiftoffRegister src, |
| uint32_t caller_slot_idx, |
| ValueType type) { |
| int32_t offset = kSystemPointerSize * (caller_slot_idx + 1); |
| liftoff::Store(this, fp, offset, src, type); |
| } |
| |
| void LiftoffAssembler::LoadReturnStackSlot(LiftoffRegister dst, int offset, |
| ValueType type) { |
| liftoff::Load(this, dst, MemOperand(sp, offset), type); |
| } |
| |
| void LiftoffAssembler::MoveStackValue(uint32_t dst_offset, uint32_t src_offset, |
| ValueType type) { |
| DCHECK_NE(dst_offset, src_offset); |
| LiftoffRegister reg = GetUnusedRegister(reg_class_for(type), {}); |
| Fill(reg, src_offset, type); |
| Spill(dst_offset, reg, type); |
| } |
| |
| void LiftoffAssembler::Move(Register dst, Register src, ValueType type) { |
| DCHECK_NE(dst, src); |
| // TODO(ksreten): Handle different sizes here. |
| TurboAssembler::Move(dst, src); |
| } |
| |
| void LiftoffAssembler::Move(DoubleRegister dst, DoubleRegister src, |
| ValueType type) { |
| DCHECK_NE(dst, src); |
| if (type != kWasmS128) { |
| TurboAssembler::Move(dst, src); |
| } else { |
| TurboAssembler::move_v(dst.toW(), src.toW()); |
| } |
| } |
| |
| void LiftoffAssembler::Spill(int offset, LiftoffRegister reg, ValueType type) { |
| RecordUsedSpillOffset(offset); |
| MemOperand dst = liftoff::GetStackSlot(offset); |
| switch (type.kind()) { |
| case ValueType::kI32: |
| Sw(reg.gp(), dst); |
| break; |
| case ValueType::kI64: |
| case ValueType::kRef: |
| case ValueType::kOptRef: |
| Sd(reg.gp(), dst); |
| break; |
| case ValueType::kF32: |
| Swc1(reg.fp(), dst); |
| break; |
| case ValueType::kF64: |
| TurboAssembler::Sdc1(reg.fp(), dst); |
| break; |
| case ValueType::kS128: |
| TurboAssembler::st_b(reg.fp().toW(), dst); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| void LiftoffAssembler::Spill(int offset, WasmValue value) { |
| RecordUsedSpillOffset(offset); |
| MemOperand dst = liftoff::GetStackSlot(offset); |
| switch (value.type().kind()) { |
| case ValueType::kI32: { |
| LiftoffRegister tmp = GetUnusedRegister(kGpReg, {}); |
| TurboAssembler::li(tmp.gp(), Operand(value.to_i32())); |
| Sw(tmp.gp(), dst); |
| break; |
| } |
| case ValueType::kI64: |
| case ValueType::kRef: |
| case ValueType::kOptRef: { |
| LiftoffRegister tmp = GetUnusedRegister(kGpReg, {}); |
| TurboAssembler::li(tmp.gp(), value.to_i64()); |
| Sd(tmp.gp(), dst); |
| break; |
| } |
| default: |
| // kWasmF32 and kWasmF64 are unreachable, since those |
| // constants are not tracked. |
| UNREACHABLE(); |
| } |
| } |
| |
| void LiftoffAssembler::Fill(LiftoffRegister reg, int offset, ValueType type) { |
| MemOperand src = liftoff::GetStackSlot(offset); |
| switch (type.kind()) { |
| case ValueType::kI32: |
| Lw(reg.gp(), src); |
| break; |
| case ValueType::kI64: |
| case ValueType::kRef: |
| case ValueType::kOptRef: |
| Ld(reg.gp(), src); |
| break; |
| case ValueType::kF32: |
| Lwc1(reg.fp(), src); |
| break; |
| case ValueType::kF64: |
| TurboAssembler::Ldc1(reg.fp(), src); |
| break; |
| case ValueType::kS128: |
| TurboAssembler::ld_b(reg.fp().toW(), src); |
| break; |
| default: |
| UNREACHABLE(); |
| } |
| } |
| |
| void LiftoffAssembler::FillI64Half(Register, int offset, RegPairHalf) { |
| UNREACHABLE(); |
| } |
| |
| void LiftoffAssembler::FillStackSlotsWithZero(int start, int size) { |
| DCHECK_LT(0, size); |
| RecordUsedSpillOffset(start + size); |
| |
| if (size <= 12 * kStackSlotSize) { |
| // Special straight-line code for up to 12 slots. Generates one |
| // instruction per slot (<= 12 instructions total). |
| uint32_t remainder = size; |
| for (; remainder >= kStackSlotSize; remainder -= kStackSlotSize) { |
| Sd(zero_reg, liftoff::GetStackSlot(start + remainder)); |
| } |
| DCHECK(remainder == 4 || remainder == 0); |
| if (remainder) { |
| Sw(zero_reg, liftoff::GetStackSlot(start + remainder)); |
| } |
| } else { |
| // General case for bigger counts (12 instructions). |
| // Use a0 for start address (inclusive), a1 for end address (exclusive). |
| Push(a1, a0); |
| Daddu(a0, fp, Operand(-start - size)); |
| Daddu(a1, fp, Operand(-start)); |
| |
| Label loop; |
| bind(&loop); |
| Sd(zero_reg, MemOperand(a0)); |
| daddiu(a0, a0, kSystemPointerSize); |
| BranchShort(&loop, ne, a0, Operand(a1)); |
| |
| Pop(a1, a0); |
| } |
| } |
| |
| void LiftoffAssembler::emit_i64_clz(LiftoffRegister dst, LiftoffRegister src) { |
| TurboAssembler::Dclz(dst.gp(), src.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i64_ctz(LiftoffRegister dst, LiftoffRegister src) { |
| TurboAssembler::Dctz(dst.gp(), src.gp()); |
| } |
| |
| bool LiftoffAssembler::emit_i64_popcnt(LiftoffRegister dst, |
| LiftoffRegister src) { |
| TurboAssembler::Dpopcnt(dst.gp(), src.gp()); |
| return true; |
| } |
| |
| void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) { |
| TurboAssembler::Mul(dst, lhs, rhs); |
| } |
| |
| void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs, |
| Label* trap_div_by_zero, |
| Label* trap_div_unrepresentable) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); |
| |
| // Check if lhs == kMinInt and rhs == -1, since this case is unrepresentable. |
| TurboAssembler::li(kScratchReg, 1); |
| TurboAssembler::li(kScratchReg2, 1); |
| TurboAssembler::LoadZeroOnCondition(kScratchReg, lhs, Operand(kMinInt), eq); |
| TurboAssembler::LoadZeroOnCondition(kScratchReg2, rhs, Operand(-1), eq); |
| daddu(kScratchReg, kScratchReg, kScratchReg2); |
| TurboAssembler::Branch(trap_div_unrepresentable, eq, kScratchReg, |
| Operand(zero_reg)); |
| |
| TurboAssembler::Div(dst, lhs, rhs); |
| } |
| |
| void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs, |
| Label* trap_div_by_zero) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); |
| TurboAssembler::Divu(dst, lhs, rhs); |
| } |
| |
| void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs, |
| Label* trap_div_by_zero) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); |
| TurboAssembler::Mod(dst, lhs, rhs); |
| } |
| |
| void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs, |
| Label* trap_div_by_zero) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs, Operand(zero_reg)); |
| TurboAssembler::Modu(dst, lhs, rhs); |
| } |
| |
| #define I32_BINOP(name, instruction) \ |
| void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \ |
| Register rhs) { \ |
| instruction(dst, lhs, rhs); \ |
| } |
| |
| // clang-format off |
| I32_BINOP(add, addu) |
| I32_BINOP(sub, subu) |
| I32_BINOP(and, and_) |
| I32_BINOP(or, or_) |
| I32_BINOP(xor, xor_) |
| // clang-format on |
| |
| #undef I32_BINOP |
| |
| #define I32_BINOP_I(name, instruction) \ |
| void LiftoffAssembler::emit_i32_##name##i(Register dst, Register lhs, \ |
| int32_t imm) { \ |
| instruction(dst, lhs, Operand(imm)); \ |
| } |
| |
| // clang-format off |
| I32_BINOP_I(add, Addu) |
| I32_BINOP_I(and, And) |
| I32_BINOP_I(or, Or) |
| I32_BINOP_I(xor, Xor) |
| // clang-format on |
| |
| #undef I32_BINOP_I |
| |
| void LiftoffAssembler::emit_i32_clz(Register dst, Register src) { |
| TurboAssembler::Clz(dst, src); |
| } |
| |
| void LiftoffAssembler::emit_i32_ctz(Register dst, Register src) { |
| TurboAssembler::Ctz(dst, src); |
| } |
| |
| bool LiftoffAssembler::emit_i32_popcnt(Register dst, Register src) { |
| TurboAssembler::Popcnt(dst, src); |
| return true; |
| } |
| |
| #define I32_SHIFTOP(name, instruction) \ |
| void LiftoffAssembler::emit_i32_##name(Register dst, Register src, \ |
| Register amount) { \ |
| instruction(dst, src, amount); \ |
| } |
| #define I32_SHIFTOP_I(name, instruction) \ |
| I32_SHIFTOP(name, instruction##v) \ |
| void LiftoffAssembler::emit_i32_##name##i(Register dst, Register src, \ |
| int amount) { \ |
| instruction(dst, src, amount & 31); \ |
| } |
| |
| I32_SHIFTOP_I(shl, sll) |
| I32_SHIFTOP_I(sar, sra) |
| I32_SHIFTOP_I(shr, srl) |
| |
| #undef I32_SHIFTOP |
| #undef I32_SHIFTOP_I |
| |
| void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| TurboAssembler::Dmul(dst.gp(), lhs.gp(), rhs.gp()); |
| } |
| |
| bool LiftoffAssembler::emit_i64_divs(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs, |
| Label* trap_div_by_zero, |
| Label* trap_div_unrepresentable) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); |
| |
| // Check if lhs == MinInt64 and rhs == -1, since this case is unrepresentable. |
| TurboAssembler::li(kScratchReg, 1); |
| TurboAssembler::li(kScratchReg2, 1); |
| TurboAssembler::LoadZeroOnCondition( |
| kScratchReg, lhs.gp(), Operand(std::numeric_limits<int64_t>::min()), eq); |
| TurboAssembler::LoadZeroOnCondition(kScratchReg2, rhs.gp(), Operand(-1), eq); |
| daddu(kScratchReg, kScratchReg, kScratchReg2); |
| TurboAssembler::Branch(trap_div_unrepresentable, eq, kScratchReg, |
| Operand(zero_reg)); |
| |
| TurboAssembler::Ddiv(dst.gp(), lhs.gp(), rhs.gp()); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_i64_divu(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs, |
| Label* trap_div_by_zero) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); |
| TurboAssembler::Ddivu(dst.gp(), lhs.gp(), rhs.gp()); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_i64_rems(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs, |
| Label* trap_div_by_zero) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); |
| TurboAssembler::Dmod(dst.gp(), lhs.gp(), rhs.gp()); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_i64_remu(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs, |
| Label* trap_div_by_zero) { |
| TurboAssembler::Branch(trap_div_by_zero, eq, rhs.gp(), Operand(zero_reg)); |
| TurboAssembler::Dmodu(dst.gp(), lhs.gp(), rhs.gp()); |
| return true; |
| } |
| |
| #define I64_BINOP(name, instruction) \ |
| void LiftoffAssembler::emit_i64_##name( \ |
| LiftoffRegister dst, LiftoffRegister lhs, LiftoffRegister rhs) { \ |
| instruction(dst.gp(), lhs.gp(), rhs.gp()); \ |
| } |
| |
| // clang-format off |
| I64_BINOP(add, daddu) |
| I64_BINOP(sub, dsubu) |
| I64_BINOP(and, and_) |
| I64_BINOP(or, or_) |
| I64_BINOP(xor, xor_) |
| // clang-format on |
| |
| #undef I64_BINOP |
| |
| #define I64_BINOP_I(name, instruction) \ |
| void LiftoffAssembler::emit_i64_##name##i( \ |
| LiftoffRegister dst, LiftoffRegister lhs, int32_t imm) { \ |
| instruction(dst.gp(), lhs.gp(), Operand(imm)); \ |
| } |
| |
| // clang-format off |
| I64_BINOP_I(add, Daddu) |
| I64_BINOP_I(and, And) |
| I64_BINOP_I(or, Or) |
| I64_BINOP_I(xor, Xor) |
| // clang-format on |
| |
| #undef I64_BINOP_I |
| |
| #define I64_SHIFTOP(name, instruction) \ |
| void LiftoffAssembler::emit_i64_##name( \ |
| LiftoffRegister dst, LiftoffRegister src, Register amount) { \ |
| instruction(dst.gp(), src.gp(), amount); \ |
| } |
| #define I64_SHIFTOP_I(name, instruction) \ |
| I64_SHIFTOP(name, instruction##v) \ |
| void LiftoffAssembler::emit_i64_##name##i(LiftoffRegister dst, \ |
| LiftoffRegister src, int amount) { \ |
| amount &= 63; \ |
| if (amount < 32) \ |
| instruction(dst.gp(), src.gp(), amount); \ |
| else \ |
| instruction##32(dst.gp(), src.gp(), amount - 32); \ |
| } |
| |
| I64_SHIFTOP_I(shl, dsll) |
| I64_SHIFTOP_I(sar, dsra) |
| I64_SHIFTOP_I(shr, dsrl) |
| |
| #undef I64_SHIFTOP |
| #undef I64_SHIFTOP_I |
| |
| void LiftoffAssembler::emit_u32_to_intptr(Register dst, Register src) { |
| Dext(dst, src, 0, 32); |
| } |
| |
| void LiftoffAssembler::emit_f32_neg(DoubleRegister dst, DoubleRegister src) { |
| TurboAssembler::Neg_s(dst, src); |
| } |
| |
| void LiftoffAssembler::emit_f64_neg(DoubleRegister dst, DoubleRegister src) { |
| TurboAssembler::Neg_d(dst, src); |
| } |
| |
| void LiftoffAssembler::emit_f32_min(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs) { |
| Label ool, done; |
| TurboAssembler::Float32Min(dst, lhs, rhs, &ool); |
| Branch(&done); |
| |
| bind(&ool); |
| TurboAssembler::Float32MinOutOfLine(dst, lhs, rhs); |
| bind(&done); |
| } |
| |
| void LiftoffAssembler::emit_f32_max(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs) { |
| Label ool, done; |
| TurboAssembler::Float32Max(dst, lhs, rhs, &ool); |
| Branch(&done); |
| |
| bind(&ool); |
| TurboAssembler::Float32MaxOutOfLine(dst, lhs, rhs); |
| bind(&done); |
| } |
| |
| void LiftoffAssembler::emit_f32_copysign(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs) { |
| bailout(kComplexOperation, "f32_copysign"); |
| } |
| |
| void LiftoffAssembler::emit_f64_min(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs) { |
| Label ool, done; |
| TurboAssembler::Float64Min(dst, lhs, rhs, &ool); |
| Branch(&done); |
| |
| bind(&ool); |
| TurboAssembler::Float64MinOutOfLine(dst, lhs, rhs); |
| bind(&done); |
| } |
| |
| void LiftoffAssembler::emit_f64_max(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs) { |
| Label ool, done; |
| TurboAssembler::Float64Max(dst, lhs, rhs, &ool); |
| Branch(&done); |
| |
| bind(&ool); |
| TurboAssembler::Float64MaxOutOfLine(dst, lhs, rhs); |
| bind(&done); |
| } |
| |
| void LiftoffAssembler::emit_f64_copysign(DoubleRegister dst, DoubleRegister lhs, |
| DoubleRegister rhs) { |
| bailout(kComplexOperation, "f64_copysign"); |
| } |
| |
| #define FP_BINOP(name, instruction) \ |
| void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister lhs, \ |
| DoubleRegister rhs) { \ |
| instruction(dst, lhs, rhs); \ |
| } |
| #define FP_UNOP(name, instruction) \ |
| void LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ |
| instruction(dst, src); \ |
| } |
| #define FP_UNOP_RETURN_TRUE(name, instruction) \ |
| bool LiftoffAssembler::emit_##name(DoubleRegister dst, DoubleRegister src) { \ |
| instruction(dst, src); \ |
| return true; \ |
| } |
| |
| FP_BINOP(f32_add, add_s) |
| FP_BINOP(f32_sub, sub_s) |
| FP_BINOP(f32_mul, mul_s) |
| FP_BINOP(f32_div, div_s) |
| FP_UNOP(f32_abs, abs_s) |
| FP_UNOP_RETURN_TRUE(f32_ceil, Ceil_s_s) |
| FP_UNOP_RETURN_TRUE(f32_floor, Floor_s_s) |
| FP_UNOP_RETURN_TRUE(f32_trunc, Trunc_s_s) |
| FP_UNOP_RETURN_TRUE(f32_nearest_int, Round_s_s) |
| FP_UNOP(f32_sqrt, sqrt_s) |
| FP_BINOP(f64_add, add_d) |
| FP_BINOP(f64_sub, sub_d) |
| FP_BINOP(f64_mul, mul_d) |
| FP_BINOP(f64_div, div_d) |
| FP_UNOP(f64_abs, abs_d) |
| FP_UNOP_RETURN_TRUE(f64_ceil, Ceil_d_d) |
| FP_UNOP_RETURN_TRUE(f64_floor, Floor_d_d) |
| FP_UNOP_RETURN_TRUE(f64_trunc, Trunc_d_d) |
| FP_UNOP_RETURN_TRUE(f64_nearest_int, Round_d_d) |
| FP_UNOP(f64_sqrt, sqrt_d) |
| |
| #undef FP_BINOP |
| #undef FP_UNOP |
| #undef FP_UNOP_RETURN_TRUE |
| |
| bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode, |
| LiftoffRegister dst, |
| LiftoffRegister src, Label* trap) { |
| switch (opcode) { |
| case kExprI32ConvertI64: |
| TurboAssembler::Ext(dst.gp(), src.gp(), 0, 32); |
| return true; |
| case kExprI32SConvertF32: { |
| LiftoffRegister rounded = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src)); |
| LiftoffRegister converted_back = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src, rounded)); |
| |
| // Real conversion. |
| TurboAssembler::Trunc_s_s(rounded.fp(), src.fp()); |
| trunc_w_s(kScratchDoubleReg, rounded.fp()); |
| mfc1(dst.gp(), kScratchDoubleReg); |
| // Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead, |
| // because INT32_MIN allows easier out-of-bounds detection. |
| TurboAssembler::Addu(kScratchReg, dst.gp(), 1); |
| TurboAssembler::Slt(kScratchReg2, kScratchReg, dst.gp()); |
| TurboAssembler::Movn(dst.gp(), kScratchReg, kScratchReg2); |
| |
| // Checking if trap. |
| mtc1(dst.gp(), kScratchDoubleReg); |
| cvt_s_w(converted_back.fp(), kScratchDoubleReg); |
| TurboAssembler::CompareF32(EQ, rounded.fp(), converted_back.fp()); |
| TurboAssembler::BranchFalseF(trap); |
| return true; |
| } |
| case kExprI32UConvertF32: { |
| LiftoffRegister rounded = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src)); |
| LiftoffRegister converted_back = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src, rounded)); |
| |
| // Real conversion. |
| TurboAssembler::Trunc_s_s(rounded.fp(), src.fp()); |
| TurboAssembler::Trunc_uw_s(dst.gp(), rounded.fp(), kScratchDoubleReg); |
| // Avoid UINT32_MAX as an overflow indicator and use 0 instead, |
| // because 0 allows easier out-of-bounds detection. |
| TurboAssembler::Addu(kScratchReg, dst.gp(), 1); |
| TurboAssembler::Movz(dst.gp(), zero_reg, kScratchReg); |
| |
| // Checking if trap. |
| TurboAssembler::Cvt_d_uw(converted_back.fp(), dst.gp()); |
| cvt_s_d(converted_back.fp(), converted_back.fp()); |
| TurboAssembler::CompareF32(EQ, rounded.fp(), converted_back.fp()); |
| TurboAssembler::BranchFalseF(trap); |
| return true; |
| } |
| case kExprI32SConvertF64: { |
| LiftoffRegister rounded = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src)); |
| LiftoffRegister converted_back = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src, rounded)); |
| |
| // Real conversion. |
| TurboAssembler::Trunc_d_d(rounded.fp(), src.fp()); |
| trunc_w_d(kScratchDoubleReg, rounded.fp()); |
| mfc1(dst.gp(), kScratchDoubleReg); |
| |
| // Checking if trap. |
| cvt_d_w(converted_back.fp(), kScratchDoubleReg); |
| TurboAssembler::CompareF64(EQ, rounded.fp(), converted_back.fp()); |
| TurboAssembler::BranchFalseF(trap); |
| return true; |
| } |
| case kExprI32UConvertF64: { |
| LiftoffRegister rounded = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src)); |
| LiftoffRegister converted_back = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src, rounded)); |
| |
| // Real conversion. |
| TurboAssembler::Trunc_d_d(rounded.fp(), src.fp()); |
| TurboAssembler::Trunc_uw_d(dst.gp(), rounded.fp(), kScratchDoubleReg); |
| |
| // Checking if trap. |
| TurboAssembler::Cvt_d_uw(converted_back.fp(), dst.gp()); |
| TurboAssembler::CompareF64(EQ, rounded.fp(), converted_back.fp()); |
| TurboAssembler::BranchFalseF(trap); |
| return true; |
| } |
| case kExprI32ReinterpretF32: |
| TurboAssembler::FmoveLow(dst.gp(), src.fp()); |
| return true; |
| case kExprI64SConvertI32: |
| sll(dst.gp(), src.gp(), 0); |
| return true; |
| case kExprI64UConvertI32: |
| TurboAssembler::Dext(dst.gp(), src.gp(), 0, 32); |
| return true; |
| case kExprI64SConvertF32: { |
| LiftoffRegister rounded = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src)); |
| LiftoffRegister converted_back = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src, rounded)); |
| |
| // Real conversion. |
| TurboAssembler::Trunc_s_s(rounded.fp(), src.fp()); |
| trunc_l_s(kScratchDoubleReg, rounded.fp()); |
| dmfc1(dst.gp(), kScratchDoubleReg); |
| // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead, |
| // because INT64_MIN allows easier out-of-bounds detection. |
| TurboAssembler::Daddu(kScratchReg, dst.gp(), 1); |
| TurboAssembler::Slt(kScratchReg2, kScratchReg, dst.gp()); |
| TurboAssembler::Movn(dst.gp(), kScratchReg, kScratchReg2); |
| |
| // Checking if trap. |
| dmtc1(dst.gp(), kScratchDoubleReg); |
| cvt_s_l(converted_back.fp(), kScratchDoubleReg); |
| TurboAssembler::CompareF32(EQ, rounded.fp(), converted_back.fp()); |
| TurboAssembler::BranchFalseF(trap); |
| return true; |
| } |
| case kExprI64UConvertF32: { |
| // Real conversion. |
| TurboAssembler::Trunc_ul_s(dst.gp(), src.fp(), kScratchDoubleReg, |
| kScratchReg); |
| |
| // Checking if trap. |
| TurboAssembler::Branch(trap, eq, kScratchReg, Operand(zero_reg)); |
| return true; |
| } |
| case kExprI64SConvertF64: { |
| LiftoffRegister rounded = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src)); |
| LiftoffRegister converted_back = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(src, rounded)); |
| |
| // Real conversion. |
| TurboAssembler::Trunc_d_d(rounded.fp(), src.fp()); |
| trunc_l_d(kScratchDoubleReg, rounded.fp()); |
| dmfc1(dst.gp(), kScratchDoubleReg); |
| // Avoid INT64_MAX as an overflow indicator and use INT64_MIN instead, |
| // because INT64_MIN allows easier out-of-bounds detection. |
| TurboAssembler::Daddu(kScratchReg, dst.gp(), 1); |
| TurboAssembler::Slt(kScratchReg2, kScratchReg, dst.gp()); |
| TurboAssembler::Movn(dst.gp(), kScratchReg, kScratchReg2); |
| |
| // Checking if trap. |
| dmtc1(dst.gp(), kScratchDoubleReg); |
| cvt_d_l(converted_back.fp(), kScratchDoubleReg); |
| TurboAssembler::CompareF64(EQ, rounded.fp(), converted_back.fp()); |
| TurboAssembler::BranchFalseF(trap); |
| return true; |
| } |
| case kExprI64UConvertF64: { |
| // Real conversion. |
| TurboAssembler::Trunc_ul_d(dst.gp(), src.fp(), kScratchDoubleReg, |
| kScratchReg); |
| |
| // Checking if trap. |
| TurboAssembler::Branch(trap, eq, kScratchReg, Operand(zero_reg)); |
| return true; |
| } |
| case kExprI64ReinterpretF64: |
| dmfc1(dst.gp(), src.fp()); |
| return true; |
| case kExprF32SConvertI32: { |
| LiftoffRegister scratch = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(dst)); |
| mtc1(src.gp(), scratch.fp()); |
| cvt_s_w(dst.fp(), scratch.fp()); |
| return true; |
| } |
| case kExprF32UConvertI32: |
| TurboAssembler::Cvt_s_uw(dst.fp(), src.gp()); |
| return true; |
| case kExprF32ConvertF64: |
| cvt_s_d(dst.fp(), src.fp()); |
| return true; |
| case kExprF32ReinterpretI32: |
| TurboAssembler::FmoveLow(dst.fp(), src.gp()); |
| return true; |
| case kExprF64SConvertI32: { |
| LiftoffRegister scratch = |
| GetUnusedRegister(kFpReg, LiftoffRegList::ForRegs(dst)); |
| mtc1(src.gp(), scratch.fp()); |
| cvt_d_w(dst.fp(), scratch.fp()); |
| return true; |
| } |
| case kExprF64UConvertI32: |
| TurboAssembler::Cvt_d_uw(dst.fp(), src.gp()); |
| return true; |
| case kExprF64ConvertF32: |
| cvt_d_s(dst.fp(), src.fp()); |
| return true; |
| case kExprF64ReinterpretI64: |
| dmtc1(src.gp(), dst.fp()); |
| return true; |
| case kExprI32SConvertSatF32: |
| bailout(kNonTrappingFloatToInt, "kExprI32SConvertSatF32"); |
| return true; |
| case kExprI32UConvertSatF32: |
| bailout(kNonTrappingFloatToInt, "kExprI32UConvertSatF32"); |
| return true; |
| case kExprI32SConvertSatF64: |
| bailout(kNonTrappingFloatToInt, "kExprI32SConvertSatF64"); |
| return true; |
| case kExprI32UConvertSatF64: |
| bailout(kNonTrappingFloatToInt, "kExprI32UConvertSatF64"); |
| return true; |
| case kExprI64SConvertSatF32: |
| bailout(kNonTrappingFloatToInt, "kExprI64SConvertSatF32"); |
| return true; |
| case kExprI64UConvertSatF32: |
| bailout(kNonTrappingFloatToInt, "kExprI64UConvertSatF32"); |
| return true; |
| case kExprI64SConvertSatF64: |
| bailout(kNonTrappingFloatToInt, "kExprI64SConvertSatF64"); |
| return true; |
| case kExprI64UConvertSatF64: |
| bailout(kNonTrappingFloatToInt, "kExprI64UConvertSatF64"); |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) { |
| bailout(kComplexOperation, "i32_signextend_i8"); |
| } |
| |
| void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) { |
| bailout(kComplexOperation, "i32_signextend_i16"); |
| } |
| |
| void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bailout(kComplexOperation, "i64_signextend_i8"); |
| } |
| |
| void LiftoffAssembler::emit_i64_signextend_i16(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bailout(kComplexOperation, "i64_signextend_i16"); |
| } |
| |
| void LiftoffAssembler::emit_i64_signextend_i32(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bailout(kComplexOperation, "i64_signextend_i32"); |
| } |
| |
| void LiftoffAssembler::emit_jump(Label* label) { |
| TurboAssembler::Branch(label); |
| } |
| |
| void LiftoffAssembler::emit_jump(Register target) { |
| TurboAssembler::Jump(target); |
| } |
| |
| void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label, |
| ValueType type, Register lhs, |
| Register rhs) { |
| if (rhs != no_reg) { |
| TurboAssembler::Branch(label, cond, lhs, Operand(rhs)); |
| } else { |
| TurboAssembler::Branch(label, cond, lhs, Operand(zero_reg)); |
| } |
| } |
| |
| void LiftoffAssembler::emit_i32_eqz(Register dst, Register src) { |
| sltiu(dst, src, 1); |
| } |
| |
| void LiftoffAssembler::emit_i32_set_cond(Condition cond, Register dst, |
| Register lhs, Register rhs) { |
| Register tmp = dst; |
| if (dst == lhs || dst == rhs) { |
| tmp = GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(lhs, rhs)).gp(); |
| } |
| // Write 1 as result. |
| TurboAssembler::li(tmp, 1); |
| |
| // If negative condition is true, write 0 as result. |
| Condition neg_cond = NegateCondition(cond); |
| TurboAssembler::LoadZeroOnCondition(tmp, lhs, Operand(rhs), neg_cond); |
| |
| // If tmp != dst, result will be moved. |
| TurboAssembler::Move(dst, tmp); |
| } |
| |
| void LiftoffAssembler::emit_i64_eqz(Register dst, LiftoffRegister src) { |
| sltiu(dst, src.gp(), 1); |
| } |
| |
| void LiftoffAssembler::emit_i64_set_cond(Condition cond, Register dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| Register tmp = dst; |
| if (dst == lhs.gp() || dst == rhs.gp()) { |
| tmp = GetUnusedRegister(kGpReg, LiftoffRegList::ForRegs(lhs, rhs)).gp(); |
| } |
| // Write 1 as result. |
| TurboAssembler::li(tmp, 1); |
| |
| // If negative condition is true, write 0 as result. |
| Condition neg_cond = NegateCondition(cond); |
| TurboAssembler::LoadZeroOnCondition(tmp, lhs.gp(), Operand(rhs.gp()), |
| neg_cond); |
| |
| // If tmp != dst, result will be moved. |
| TurboAssembler::Move(dst, tmp); |
| } |
| |
| namespace liftoff { |
| |
| inline FPUCondition ConditionToConditionCmpFPU(Condition condition, |
| bool* predicate) { |
| switch (condition) { |
| case kEqual: |
| *predicate = true; |
| return EQ; |
| case kUnequal: |
| *predicate = false; |
| return EQ; |
| case kUnsignedLessThan: |
| *predicate = true; |
| return OLT; |
| case kUnsignedGreaterEqual: |
| *predicate = false; |
| return OLT; |
| case kUnsignedLessEqual: |
| *predicate = true; |
| return OLE; |
| case kUnsignedGreaterThan: |
| *predicate = false; |
| return OLE; |
| default: |
| *predicate = true; |
| break; |
| } |
| UNREACHABLE(); |
| } |
| |
| inline void EmitAnyTrue(LiftoffAssembler* assm, LiftoffRegister dst, |
| LiftoffRegister src) { |
| Label all_false; |
| assm->BranchMSA(&all_false, MSA_BRANCH_V, all_zero, src.fp().toW(), |
| USE_DELAY_SLOT); |
| assm->li(dst.gp(), 0l); |
| assm->li(dst.gp(), 1); |
| assm->bind(&all_false); |
| } |
| |
| inline void EmitAllTrue(LiftoffAssembler* assm, LiftoffRegister dst, |
| LiftoffRegister src, MSABranchDF msa_branch_df) { |
| Label all_true; |
| assm->BranchMSA(&all_true, msa_branch_df, all_not_zero, src.fp().toW(), |
| USE_DELAY_SLOT); |
| assm->li(dst.gp(), 1); |
| assm->li(dst.gp(), 0l); |
| assm->bind(&all_true); |
| } |
| |
| } // namespace liftoff |
| |
| void LiftoffAssembler::emit_f32_set_cond(Condition cond, Register dst, |
| DoubleRegister lhs, |
| DoubleRegister rhs) { |
| Label not_nan, cont; |
| TurboAssembler::CompareIsNanF32(lhs, rhs); |
| TurboAssembler::BranchFalseF(¬_nan); |
| // If one of the operands is NaN, return 1 for f32.ne, else 0. |
| if (cond == ne) { |
| TurboAssembler::li(dst, 1); |
| } else { |
| TurboAssembler::Move(dst, zero_reg); |
| } |
| TurboAssembler::Branch(&cont); |
| |
| bind(¬_nan); |
| |
| TurboAssembler::li(dst, 1); |
| bool predicate; |
| FPUCondition fcond = liftoff::ConditionToConditionCmpFPU(cond, &predicate); |
| TurboAssembler::CompareF32(fcond, lhs, rhs); |
| if (predicate) { |
| TurboAssembler::LoadZeroIfNotFPUCondition(dst); |
| } else { |
| TurboAssembler::LoadZeroIfFPUCondition(dst); |
| } |
| |
| bind(&cont); |
| } |
| |
| void LiftoffAssembler::emit_f64_set_cond(Condition cond, Register dst, |
| DoubleRegister lhs, |
| DoubleRegister rhs) { |
| Label not_nan, cont; |
| TurboAssembler::CompareIsNanF64(lhs, rhs); |
| TurboAssembler::BranchFalseF(¬_nan); |
| // If one of the operands is NaN, return 1 for f64.ne, else 0. |
| if (cond == ne) { |
| TurboAssembler::li(dst, 1); |
| } else { |
| TurboAssembler::Move(dst, zero_reg); |
| } |
| TurboAssembler::Branch(&cont); |
| |
| bind(¬_nan); |
| |
| TurboAssembler::li(dst, 1); |
| bool predicate; |
| FPUCondition fcond = liftoff::ConditionToConditionCmpFPU(cond, &predicate); |
| TurboAssembler::CompareF64(fcond, lhs, rhs); |
| if (predicate) { |
| TurboAssembler::LoadZeroIfNotFPUCondition(dst); |
| } else { |
| TurboAssembler::LoadZeroIfFPUCondition(dst); |
| } |
| |
| bind(&cont); |
| } |
| |
| bool LiftoffAssembler::emit_select(LiftoffRegister dst, Register condition, |
| LiftoffRegister true_value, |
| LiftoffRegister false_value, |
| ValueType type) { |
| return false; |
| } |
| |
| void LiftoffAssembler::LoadTransform(LiftoffRegister dst, Register src_addr, |
| Register offset_reg, uint32_t offset_imm, |
| LoadType type, |
| LoadTransformationKind transform, |
| uint32_t* protected_load_pc) { |
| UseScratchRegisterScope temps(this); |
| Register scratch = temps.Acquire(); |
| Daddu(scratch, src_addr, offset_reg); |
| MemOperand src_op = MemOperand(scratch, offset_imm); |
| MSARegister dst_msa = dst.fp().toW(); |
| *protected_load_pc = pc_offset(); |
| MachineType memtype = type.mem_type(); |
| |
| if (transform == LoadTransformationKind::kExtend) { |
| Ld(scratch, src_op); |
| if (memtype == MachineType::Int8()) { |
| fill_d(dst_msa, scratch); |
| clti_s_b(kSimd128ScratchReg, dst_msa, 0); |
| ilvr_b(dst_msa, kSimd128ScratchReg, dst_msa); |
| } else if (memtype == MachineType::Uint8()) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| fill_d(dst_msa, scratch); |
| ilvr_b(dst_msa, kSimd128RegZero, dst_msa); |
| } else if (memtype == MachineType::Int16()) { |
| fill_d(dst_msa, scratch); |
| clti_s_h(kSimd128ScratchReg, dst_msa, 0); |
| ilvr_h(dst_msa, kSimd128ScratchReg, dst_msa); |
| } else if (memtype == MachineType::Uint16()) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| fill_d(dst_msa, scratch); |
| ilvr_h(dst_msa, kSimd128RegZero, dst_msa); |
| } else if (memtype == MachineType::Int32()) { |
| fill_d(dst_msa, scratch); |
| clti_s_w(kSimd128ScratchReg, dst_msa, 0); |
| ilvr_w(dst_msa, kSimd128ScratchReg, dst_msa); |
| } else if (memtype == MachineType::Uint32()) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| fill_d(dst_msa, scratch); |
| ilvr_w(dst_msa, kSimd128RegZero, dst_msa); |
| } |
| } else if (transform == LoadTransformationKind::kZeroExtend) { |
| xor_v(dst_msa, dst_msa, dst_msa); |
| if (memtype == MachineType::Int32()) { |
| Lwu(scratch, src_op); |
| insert_w(dst_msa, 0, scratch); |
| } else { |
| DCHECK_EQ(MachineType::Int64(), memtype); |
| Ld(scratch, src_op); |
| insert_d(dst_msa, 0, scratch); |
| } |
| } else { |
| DCHECK_EQ(LoadTransformationKind::kSplat, transform); |
| if (memtype == MachineType::Int8()) { |
| Lb(scratch, src_op); |
| fill_b(dst_msa, scratch); |
| } else if (memtype == MachineType::Int16()) { |
| Lh(scratch, src_op); |
| fill_h(dst_msa, scratch); |
| } else if (memtype == MachineType::Int32()) { |
| Lw(scratch, src_op); |
| fill_w(dst_msa, scratch); |
| } else if (memtype == MachineType::Int64()) { |
| Ld(scratch, src_op); |
| fill_d(dst_msa, scratch); |
| } |
| } |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shuffle(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs, |
| const uint8_t shuffle[16], |
| bool is_swizzle) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| |
| uint64_t control_hi = 0; |
| uint64_t control_low = 0; |
| for (int i = 7; i >= 0; i--) { |
| control_hi <<= 8; |
| control_hi |= shuffle[i + 8]; |
| control_low <<= 8; |
| control_low |= shuffle[i]; |
| } |
| |
| if (dst_msa == lhs_msa) { |
| move_v(kSimd128ScratchReg, lhs_msa); |
| lhs_msa = kSimd128ScratchReg; |
| } else if (dst_msa == rhs_msa) { |
| move_v(kSimd128ScratchReg, rhs_msa); |
| rhs_msa = kSimd128ScratchReg; |
| } |
| |
| li(kScratchReg, control_low); |
| insert_d(dst_msa, 0, kScratchReg); |
| li(kScratchReg, control_hi); |
| insert_d(dst_msa, 1, kScratchReg); |
| vshf_b(dst_msa, rhs_msa, lhs_msa); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_swizzle(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| |
| if (dst == lhs) { |
| move_v(kSimd128ScratchReg, lhs_msa); |
| lhs_msa = kSimd128ScratchReg; |
| } |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| move_v(dst_msa, rhs_msa); |
| vshf_b(dst_msa, kSimd128RegZero, lhs_msa); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_splat(LiftoffRegister dst, |
| LiftoffRegister src) { |
| fill_b(dst.fp().toW(), src.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_splat(LiftoffRegister dst, |
| LiftoffRegister src) { |
| fill_h(dst.fp().toW(), src.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_splat(LiftoffRegister dst, |
| LiftoffRegister src) { |
| fill_w(dst.fp().toW(), src.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_splat(LiftoffRegister dst, |
| LiftoffRegister src) { |
| fill_d(dst.fp().toW(), src.gp()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_splat(LiftoffRegister dst, |
| LiftoffRegister src) { |
| TurboAssembler::FmoveLow(kScratchReg, src.fp()); |
| fill_w(dst.fp().toW(), kScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_splat(LiftoffRegister dst, |
| LiftoffRegister src) { |
| TurboAssembler::Move(kScratchReg, src.fp()); |
| fill_d(dst.fp().toW(), kScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_eq(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| ceq_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_ne(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| ceq_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_gt_s(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| clt_s_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_gt_u(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| clt_u_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_ge_s(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| cle_s_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_ge_u(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| cle_u_b(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_eq(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| ceq_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_ne(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| ceq_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_gt_s(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| clt_s_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_gt_u(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| clt_u_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_ge_s(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| cle_s_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_ge_u(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| cle_u_h(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| ceq_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| ceq_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| nor_v(dst.fp().toW(), dst.fp().toW(), dst.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_gt_s(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| clt_s_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_gt_u(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| clt_u_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_ge_s(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| cle_s_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_ge_u(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| cle_u_w(dst.fp().toW(), rhs.fp().toW(), lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_eq(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fceq_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_ne(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fcune_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_lt(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fclt_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_le(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fcle_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_eq(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fceq_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_ne(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fcune_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_lt(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fclt_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_le(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fcle_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_s128_const(LiftoffRegister dst, |
| const uint8_t imms[16]) { |
| MSARegister dst_msa = dst.fp().toW(); |
| uint64_t vals[2]; |
| memcpy(vals, imms, sizeof(vals)); |
| li(kScratchReg, vals[0]); |
| insert_d(dst_msa, 0, kScratchReg); |
| li(kScratchReg, vals[1]); |
| insert_d(dst_msa, 1, kScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_s128_not(LiftoffRegister dst, LiftoffRegister src) { |
| nor_v(dst.fp().toW(), src.fp().toW(), src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_s128_and(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| and_v(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_s128_or(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| or_v(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_s128_xor(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| xor_v(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_s128_and_not(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| nor_v(kSimd128ScratchReg, rhs.fp().toW(), rhs.fp().toW()); |
| and_v(dst.fp().toW(), kSimd128ScratchReg, lhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_s128_select(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| LiftoffRegister mask) { |
| if (dst == mask) { |
| bsel_v(dst.fp().toW(), src2.fp().toW(), src1.fp().toW()); |
| } else { |
| xor_v(kSimd128ScratchReg, src1.fp().toW(), src2.fp().toW()); |
| and_v(kSimd128ScratchReg, kSimd128ScratchReg, mask.fp().toW()); |
| xor_v(dst.fp().toW(), kSimd128ScratchReg, src2.fp().toW()); |
| } |
| } |
| |
| void LiftoffAssembler::emit_i8x16_neg(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| subv_b(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_v8x16_anytrue(LiftoffRegister dst, |
| LiftoffRegister src) { |
| liftoff::EmitAnyTrue(this, dst, src); |
| } |
| |
| void LiftoffAssembler::emit_v8x16_alltrue(LiftoffRegister dst, |
| LiftoffRegister src) { |
| liftoff::EmitAllTrue(this, dst, src, MSA_BRANCH_B); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_bitmask(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| srli_b(scratch0, src.fp().toW(), 7); |
| srli_h(scratch1, scratch0, 7); |
| or_v(scratch0, scratch0, scratch1); |
| srli_w(scratch1, scratch0, 14); |
| or_v(scratch0, scratch0, scratch1); |
| srli_d(scratch1, scratch0, 28); |
| or_v(scratch0, scratch0, scratch1); |
| shf_w(scratch1, scratch0, 0x0E); |
| ilvev_b(scratch0, scratch1, scratch0); |
| copy_u_h(dst.gp(), scratch0, 0); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shl(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_b(kSimd128ScratchReg, rhs.gp()); |
| sll_b(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shli(LiftoffRegister dst, LiftoffRegister lhs, |
| int32_t rhs) { |
| slli_b(dst.fp().toW(), lhs.fp().toW(), rhs & 7); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shr_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_b(kSimd128ScratchReg, rhs.gp()); |
| sra_b(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shri_s(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srai_b(dst.fp().toW(), lhs.fp().toW(), rhs & 7); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shr_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_b(kSimd128ScratchReg, rhs.gp()); |
| srl_b(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_shri_u(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srli_b(dst.fp().toW(), lhs.fp().toW(), rhs & 7); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_add(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| addv_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_add_sat_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| adds_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_add_sat_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| adds_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_sub(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subv_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_sub_sat_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subs_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_sub_sat_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subs_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| mulv_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_min_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| min_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_min_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| min_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_max_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| max_s_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_max_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| max_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_neg(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| subv_h(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_v16x8_anytrue(LiftoffRegister dst, |
| LiftoffRegister src) { |
| liftoff::EmitAnyTrue(this, dst, src); |
| } |
| |
| void LiftoffAssembler::emit_v16x8_alltrue(LiftoffRegister dst, |
| LiftoffRegister src) { |
| liftoff::EmitAllTrue(this, dst, src, MSA_BRANCH_H); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_bitmask(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| srli_h(scratch0, src.fp().toW(), 15); |
| srli_w(scratch1, scratch0, 15); |
| or_v(scratch0, scratch0, scratch1); |
| srli_d(scratch1, scratch0, 30); |
| or_v(scratch0, scratch0, scratch1); |
| shf_w(scratch1, scratch0, 0x0E); |
| slli_d(scratch1, scratch1, 4); |
| or_v(scratch0, scratch0, scratch1); |
| copy_u_b(dst.gp(), scratch0, 0); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_shl(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_h(kSimd128ScratchReg, rhs.gp()); |
| sll_h(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_shli(LiftoffRegister dst, LiftoffRegister lhs, |
| int32_t rhs) { |
| slli_h(dst.fp().toW(), lhs.fp().toW(), rhs & 15); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_shr_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_h(kSimd128ScratchReg, rhs.gp()); |
| sra_h(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_shri_s(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srai_h(dst.fp().toW(), lhs.fp().toW(), rhs & 15); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_shr_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_h(kSimd128ScratchReg, rhs.gp()); |
| srl_h(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_shri_u(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srli_h(dst.fp().toW(), lhs.fp().toW(), rhs & 15); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_add(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| addv_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_add_sat_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| adds_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_add_sat_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| adds_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_sub(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subv_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_sub_sat_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subs_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_sub_sat_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subs_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| mulv_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_min_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| min_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_min_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| min_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_max_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| max_s_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_max_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| max_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_neg(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| subv_w(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_v32x4_anytrue(LiftoffRegister dst, |
| LiftoffRegister src) { |
| liftoff::EmitAnyTrue(this, dst, src); |
| } |
| |
| void LiftoffAssembler::emit_v32x4_alltrue(LiftoffRegister dst, |
| LiftoffRegister src) { |
| liftoff::EmitAllTrue(this, dst, src, MSA_BRANCH_W); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_bitmask(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| srli_w(scratch0, src.fp().toW(), 31); |
| srli_d(scratch1, scratch0, 31); |
| or_v(scratch0, scratch0, scratch1); |
| shf_w(scratch1, scratch0, 0x0E); |
| slli_d(scratch1, scratch1, 2); |
| or_v(scratch0, scratch0, scratch1); |
| copy_u_b(dst.gp(), scratch0, 0); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_shl(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_w(kSimd128ScratchReg, rhs.gp()); |
| sll_w(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_shli(LiftoffRegister dst, LiftoffRegister lhs, |
| int32_t rhs) { |
| slli_w(dst.fp().toW(), lhs.fp().toW(), rhs & 31); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_shr_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_w(kSimd128ScratchReg, rhs.gp()); |
| sra_w(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_shri_s(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srai_w(dst.fp().toW(), lhs.fp().toW(), rhs & 31); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_shr_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_w(kSimd128ScratchReg, rhs.gp()); |
| srl_w(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_shri_u(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srli_w(dst.fp().toW(), lhs.fp().toW(), rhs & 31); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_add(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| addv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| mulv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_min_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| min_s_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_min_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| min_u_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_max_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| max_s_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_max_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| max_u_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_dot_i16x8_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| dotp_s_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_neg(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| subv_d(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_shl(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_d(kSimd128ScratchReg, rhs.gp()); |
| sll_d(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_shli(LiftoffRegister dst, LiftoffRegister lhs, |
| int32_t rhs) { |
| slli_d(dst.fp().toW(), lhs.fp().toW(), rhs & 63); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_shr_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_d(kSimd128ScratchReg, rhs.gp()); |
| sra_d(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_shri_s(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srai_d(dst.fp().toW(), lhs.fp().toW(), rhs & 63); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_shr_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fill_d(kSimd128ScratchReg, rhs.gp()); |
| srl_d(dst.fp().toW(), lhs.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_shri_u(LiftoffRegister dst, |
| LiftoffRegister lhs, int32_t rhs) { |
| srli_d(dst.fp().toW(), lhs.fp().toW(), rhs & 63); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_add(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| addv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| subv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| mulv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_abs(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bclri_w(dst.fp().toW(), src.fp().toW(), 31); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_neg(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bnegi_w(dst.fp().toW(), src.fp().toW(), 31); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_sqrt(LiftoffRegister dst, |
| LiftoffRegister src) { |
| fsqrt_w(dst.fp().toW(), src.fp().toW()); |
| } |
| |
| bool LiftoffAssembler::emit_f32x4_ceil(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundW(dst.fp().toW(), src.fp().toW(), kRoundToPlusInf); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_f32x4_floor(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundW(dst.fp().toW(), src.fp().toW(), kRoundToMinusInf); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_f32x4_trunc(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundW(dst.fp().toW(), src.fp().toW(), kRoundToZero); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_f32x4_nearest_int(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundW(dst.fp().toW(), src.fp().toW(), kRoundToNearest); |
| return true; |
| } |
| |
| void LiftoffAssembler::emit_f32x4_add(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fadd_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_sub(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fsub_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fmul_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_div(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fdiv_w(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_min(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| // If inputs are -0.0. and +0.0, then write -0.0 to scratch1. |
| // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). |
| fseq_w(scratch0, lhs_msa, rhs_msa); |
| bsel_v(scratch0, rhs_msa, lhs_msa); |
| or_v(scratch1, scratch0, rhs_msa); |
| // scratch0 = isNaN(scratch1) ? scratch1: lhs. |
| fseq_w(scratch0, scratch1, scratch1); |
| bsel_v(scratch0, scratch1, lhs_msa); |
| // dst = (scratch1 <= scratch0) ? scratch1 : scratch0. |
| fsle_w(dst_msa, scratch1, scratch0); |
| bsel_v(dst_msa, scratch0, scratch1); |
| // Canonicalize the result. |
| fmin_w(dst_msa, dst_msa, dst_msa); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_max(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| // If inputs are -0.0. and +0.0, then write +0.0 to scratch1. |
| // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). |
| fseq_w(scratch0, lhs_msa, rhs_msa); |
| bsel_v(scratch0, rhs_msa, lhs_msa); |
| and_v(scratch1, scratch0, rhs_msa); |
| // scratch0 = isNaN(scratch1) ? scratch1: lhs. |
| fseq_w(scratch0, scratch1, scratch1); |
| bsel_v(scratch0, scratch1, lhs_msa); |
| // dst = (scratch0 <= scratch1) ? scratch1 : scratch0. |
| fsle_w(dst_msa, scratch0, scratch1); |
| bsel_v(dst_msa, scratch0, scratch1); |
| // Canonicalize the result. |
| fmax_w(dst_msa, dst_msa, dst_msa); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_pmin(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| // dst = rhs < lhs ? rhs : lhs |
| fclt_w(dst_msa, rhs_msa, lhs_msa); |
| bsel_v(dst_msa, lhs_msa, rhs_msa); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_pmax(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| // dst = lhs < rhs ? rhs : lhs |
| fclt_w(dst_msa, lhs_msa, rhs_msa); |
| bsel_v(dst_msa, lhs_msa, rhs_msa); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_abs(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bclri_d(dst.fp().toW(), src.fp().toW(), 63); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_neg(LiftoffRegister dst, |
| LiftoffRegister src) { |
| bnegi_d(dst.fp().toW(), src.fp().toW(), 63); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_sqrt(LiftoffRegister dst, |
| LiftoffRegister src) { |
| fsqrt_d(dst.fp().toW(), src.fp().toW()); |
| } |
| |
| bool LiftoffAssembler::emit_f64x2_ceil(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundD(dst.fp().toW(), src.fp().toW(), kRoundToPlusInf); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_f64x2_floor(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundD(dst.fp().toW(), src.fp().toW(), kRoundToMinusInf); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_f64x2_trunc(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundD(dst.fp().toW(), src.fp().toW(), kRoundToZero); |
| return true; |
| } |
| |
| bool LiftoffAssembler::emit_f64x2_nearest_int(LiftoffRegister dst, |
| LiftoffRegister src) { |
| MSARoundD(dst.fp().toW(), src.fp().toW(), kRoundToNearest); |
| return true; |
| } |
| |
| void LiftoffAssembler::emit_f64x2_add(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fadd_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_sub(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fsub_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_mul(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fmul_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_div(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| fdiv_d(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_min(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| // If inputs are -0.0. and +0.0, then write -0.0 to scratch1. |
| // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). |
| fseq_d(scratch0, lhs_msa, rhs_msa); |
| bsel_v(scratch0, rhs_msa, lhs_msa); |
| or_v(scratch1, scratch0, rhs_msa); |
| // scratch0 = isNaN(scratch1) ? scratch1: lhs. |
| fseq_d(scratch0, scratch1, scratch1); |
| bsel_v(scratch0, scratch1, lhs_msa); |
| // dst = (scratch1 <= scratch0) ? scratch1 : scratch0. |
| fsle_d(dst_msa, scratch1, scratch0); |
| bsel_v(dst_msa, scratch0, scratch1); |
| // Canonicalize the result. |
| fmin_d(dst_msa, dst_msa, dst_msa); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_max(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| MSARegister scratch0 = kSimd128RegZero; |
| MSARegister scratch1 = kSimd128ScratchReg; |
| // If inputs are -0.0. and +0.0, then write +0.0 to scratch1. |
| // scratch1 = (lhs == rhs) ? (lhs | rhs) : (rhs | rhs). |
| fseq_d(scratch0, lhs_msa, rhs_msa); |
| bsel_v(scratch0, rhs_msa, lhs_msa); |
| and_v(scratch1, scratch0, rhs_msa); |
| // scratch0 = isNaN(scratch1) ? scratch1: lhs. |
| fseq_d(scratch0, scratch1, scratch1); |
| bsel_v(scratch0, scratch1, lhs_msa); |
| // dst = (scratch0 <= scratch1) ? scratch1 : scratch0. |
| fsle_d(dst_msa, scratch0, scratch1); |
| bsel_v(dst_msa, scratch0, scratch1); |
| // Canonicalize the result. |
| fmax_d(dst_msa, dst_msa, dst_msa); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_pmin(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| // dst = rhs < lhs ? rhs : lhs |
| fclt_d(dst_msa, rhs_msa, lhs_msa); |
| bsel_v(dst_msa, lhs_msa, rhs_msa); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_pmax(LiftoffRegister dst, LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| MSARegister dst_msa = dst.fp().toW(); |
| MSARegister lhs_msa = lhs.fp().toW(); |
| MSARegister rhs_msa = rhs.fp().toW(); |
| // dst = lhs < rhs ? rhs : lhs |
| fclt_d(dst_msa, lhs_msa, rhs_msa); |
| bsel_v(dst_msa, lhs_msa, rhs_msa); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_sconvert_f32x4(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ftrunc_s_w(dst.fp().toW(), src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_uconvert_f32x4(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ftrunc_u_w(dst.fp().toW(), src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_sconvert_i32x4(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ffint_s_w(dst.fp().toW(), src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_uconvert_i32x4(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ffint_u_w(dst.fp().toW(), src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_sconvert_i16x8(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| sat_s_h(kSimd128ScratchReg, lhs.fp().toW(), 7); |
| sat_s_h(dst.fp().toW(), lhs.fp().toW(), 7); |
| pckev_b(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_uconvert_i16x8(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| max_s_h(kSimd128ScratchReg, kSimd128RegZero, lhs.fp().toW()); |
| sat_u_h(kSimd128ScratchReg, kSimd128ScratchReg, 7); |
| max_s_h(dst.fp().toW(), kSimd128RegZero, rhs.fp().toW()); |
| sat_u_h(dst.fp().toW(), dst.fp().toW(), 7); |
| pckev_b(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_sconvert_i32x4(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| sat_s_w(kSimd128ScratchReg, lhs.fp().toW(), 15); |
| sat_s_w(dst.fp().toW(), lhs.fp().toW(), 15); |
| pckev_h(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_uconvert_i32x4(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| max_s_w(kSimd128ScratchReg, kSimd128RegZero, lhs.fp().toW()); |
| sat_u_w(kSimd128ScratchReg, kSimd128ScratchReg, 15); |
| max_s_w(dst.fp().toW(), kSimd128RegZero, rhs.fp().toW()); |
| sat_u_w(dst.fp().toW(), dst.fp().toW(), 15); |
| pckev_h(dst.fp().toW(), dst.fp().toW(), kSimd128ScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_sconvert_i8x16_low(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ilvr_b(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); |
| slli_h(dst.fp().toW(), kSimd128ScratchReg, 8); |
| srai_h(dst.fp().toW(), dst.fp().toW(), 8); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_sconvert_i8x16_high(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ilvl_b(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); |
| slli_h(dst.fp().toW(), kSimd128ScratchReg, 8); |
| srai_h(dst.fp().toW(), dst.fp().toW(), 8); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_uconvert_i8x16_low(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| ilvr_b(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_uconvert_i8x16_high(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| ilvl_b(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_sconvert_i16x8_low(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ilvr_h(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); |
| slli_w(dst.fp().toW(), kSimd128ScratchReg, 16); |
| srai_w(dst.fp().toW(), dst.fp().toW(), 16); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_sconvert_i16x8_high(LiftoffRegister dst, |
| LiftoffRegister src) { |
| ilvl_h(kSimd128ScratchReg, src.fp().toW(), src.fp().toW()); |
| slli_w(dst.fp().toW(), kSimd128ScratchReg, 16); |
| srai_w(dst.fp().toW(), dst.fp().toW(), 16); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_uconvert_i16x8_low(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| ilvr_h(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_uconvert_i16x8_high(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| ilvl_h(dst.fp().toW(), kSimd128RegZero, src.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_rounding_average_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| aver_u_b(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_rounding_average_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| LiftoffRegister rhs) { |
| aver_u_h(dst.fp().toW(), lhs.fp().toW(), rhs.fp().toW()); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_abs(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| asub_s_b(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_abs(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| asub_s_h(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_abs(LiftoffRegister dst, |
| LiftoffRegister src) { |
| xor_v(kSimd128RegZero, kSimd128RegZero, kSimd128RegZero); |
| asub_s_w(dst.fp().toW(), src.fp().toW(), kSimd128RegZero); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_extract_lane_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_s_b(dst.gp(), lhs.fp().toW(), imm_lane_idx); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_extract_lane_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_u_b(dst.gp(), lhs.fp().toW(), imm_lane_idx); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_extract_lane_s(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_s_h(dst.gp(), lhs.fp().toW(), imm_lane_idx); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_extract_lane_u(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_u_h(dst.gp(), lhs.fp().toW(), imm_lane_idx); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_extract_lane(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_s_w(dst.gp(), lhs.fp().toW(), imm_lane_idx); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_extract_lane(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_s_d(dst.gp(), lhs.fp().toW(), imm_lane_idx); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_extract_lane(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_u_w(kScratchReg, lhs.fp().toW(), imm_lane_idx); |
| TurboAssembler::FmoveLow(dst.fp(), kScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_extract_lane(LiftoffRegister dst, |
| LiftoffRegister lhs, |
| uint8_t imm_lane_idx) { |
| copy_s_d(kScratchReg, lhs.fp().toW(), imm_lane_idx); |
| TurboAssembler::Move(dst.fp(), kScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_i8x16_replace_lane(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| uint8_t imm_lane_idx) { |
| if (dst != src1) { |
| move_v(dst.fp().toW(), src1.fp().toW()); |
| } |
| insert_b(dst.fp().toW(), imm_lane_idx, src2.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i16x8_replace_lane(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| uint8_t imm_lane_idx) { |
| if (dst != src1) { |
| move_v(dst.fp().toW(), src1.fp().toW()); |
| } |
| insert_h(dst.fp().toW(), imm_lane_idx, src2.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i32x4_replace_lane(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| uint8_t imm_lane_idx) { |
| if (dst != src1) { |
| move_v(dst.fp().toW(), src1.fp().toW()); |
| } |
| insert_w(dst.fp().toW(), imm_lane_idx, src2.gp()); |
| } |
| |
| void LiftoffAssembler::emit_i64x2_replace_lane(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| uint8_t imm_lane_idx) { |
| if (dst != src1) { |
| move_v(dst.fp().toW(), src1.fp().toW()); |
| } |
| insert_d(dst.fp().toW(), imm_lane_idx, src2.gp()); |
| } |
| |
| void LiftoffAssembler::emit_f32x4_replace_lane(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| uint8_t imm_lane_idx) { |
| TurboAssembler::FmoveLow(kScratchReg, src2.fp()); |
| if (dst != src1) { |
| move_v(dst.fp().toW(), src1.fp().toW()); |
| } |
| insert_w(dst.fp().toW(), imm_lane_idx, kScratchReg); |
| } |
| |
| void LiftoffAssembler::emit_f64x2_replace_lane(LiftoffRegister dst, |
| LiftoffRegister src1, |
| LiftoffRegister src2, |
| uint8_t imm_lane_idx) { |
| TurboAssembler::Move(kScratchReg, src2.fp()); |
| if (dst != src1) { |
| move_v(dst.fp().toW(), src1.fp().toW()); |
| } |
| insert_d(dst.fp().toW(), imm_lane_idx, kScratchReg); |
| } |
| |
| void LiftoffAssembler::StackCheck(Label* ool_code, Register limit_address) { |
| TurboAssembler::Uld(limit_address, MemOperand(limit_address)); |
| TurboAssembler::Branch(ool_code, ule, sp, Operand(limit_address)); |
| } |
| |
| void LiftoffAssembler::CallTrapCallbackForTesting() { |
| PrepareCallCFunction(0, GetUnusedRegister(kGpReg, {}).gp()); |
| CallCFunction(ExternalReference::wasm_call_trap_callback_for_testing(), 0); |
| } |
| |
| void LiftoffAssembler::AssertUnreachable(AbortReason reason) { |
| if (emit_debug_code()) Abort(reason); |
| } |
| |
| void LiftoffAssembler::PushRegisters(LiftoffRegList regs) { |
| LiftoffRegList gp_regs = regs & kGpCacheRegList; |
| unsigned num_gp_regs = gp_regs.GetNumRegsSet(); |
| if (num_gp_regs) { |
| unsigned offset = num_gp_regs * kSystemPointerSize; |
| daddiu(sp, sp, -offset); |
| while (!gp_regs.is_empty()) { |
| LiftoffRegister reg = gp_regs.GetFirstRegSet(); |
| offset -= kSystemPointerSize; |
| sd(reg.gp(), MemOperand(sp, offset)); |
| gp_regs.clear(reg); |
| } |
| DCHECK_EQ(offset, 0); |
| } |
| LiftoffRegList fp_regs = regs & kFpCacheRegList; |
| unsigned num_fp_regs = fp_regs.GetNumRegsSet(); |
| if (num_fp_regs) { |
| unsigned slot_size = IsEnabled(MIPS_SIMD) ? 16 : 8; |
| daddiu(sp, sp, -(num_fp_regs * slot_size)); |
| unsigned offset = 0; |
| while (!fp_regs.is_empty()) { |
| LiftoffRegister reg = fp_regs.GetFirstRegSet(); |
| if (IsEnabled(MIPS_SIMD)) { |
| TurboAssembler::st_d(reg.fp().toW(), MemOperand(sp, offset)); |
| } else { |
| TurboAssembler::Sdc1(reg.fp(), MemOperand(sp, offset)); |
| } |
| fp_regs.clear(reg); |
| offset += slot_size; |
| } |
| DCHECK_EQ(offset, num_fp_regs * slot_size); |
| } |
| } |
| |
| void LiftoffAssembler::PopRegisters(LiftoffRegList regs) { |
| LiftoffRegList fp_regs = regs & kFpCacheRegList; |
| unsigned fp_offset = 0; |
| while (!fp_regs.is_empty()) { |
| LiftoffRegister reg = fp_regs.GetFirstRegSet(); |
| if (IsEnabled(MIPS_SIMD)) { |
| TurboAssembler::ld_d(reg.fp().toW(), MemOperand(sp, fp_offset)); |
| } else { |
| TurboAssembler::Ldc1(reg.fp(), MemOperand(sp, fp_offset)); |
| } |
| fp_regs.clear(reg); |
| fp_offset += (IsEnabled(MIPS_SIMD) ? 16 : 8); |
| } |
| if (fp_offset) daddiu(sp, sp, fp_offset); |
| LiftoffRegList gp_regs = regs & kGpCacheRegList; |
| unsigned gp_offset = 0; |
| while (!gp_regs.is_empty()) { |
| LiftoffRegister reg = gp_regs.GetLastRegSet(); |
| ld(reg.gp(), MemOperand(sp, gp_offset)); |
| gp_regs.clear(reg); |
| gp_offset += kSystemPointerSize; |
| } |
| daddiu(sp, sp, gp_offset); |
| } |
| |
| void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) { |
| DCHECK_LT(num_stack_slots, |
| (1 << 16) / kSystemPointerSize); // 16 bit immediate |
| TurboAssembler::DropAndRet(static_cast<int>(num_stack_slots)); |
| } |
| |
| void LiftoffAssembler::CallC(const wasm::FunctionSig* sig, |
| const LiftoffRegister* args, |
| const LiftoffRegister* rets, |
| ValueType out_argument_type, int stack_bytes, |
| ExternalReference ext_ref) { |
| Daddu(sp, sp, -stack_bytes); |
| |
| int arg_bytes = 0; |
| for (ValueType param_type : sig->parameters()) { |
| liftoff::Store(this, sp, arg_bytes, *args++, param_type); |
| arg_bytes += param_type.element_size_bytes(); |
| } |
| DCHECK_LE(arg_bytes, stack_bytes); |
| |
| // Pass a pointer to the buffer with the arguments to the C function. |
| // On mips, the first argument is passed in {a0}. |
| constexpr Register kFirstArgReg = a0; |
| mov(kFirstArgReg, sp); |
| |
| // Now call the C function. |
| constexpr int kNumCCallArgs = 1; |
| PrepareCallCFunction(kNumCCallArgs, kScratchReg); |
| CallCFunction(ext_ref, kNumCCallArgs); |
| |
| // Move return value to the right register. |
| const LiftoffRegister* next_result_reg = rets; |
| if (sig->return_count() > 0) { |
| DCHECK_EQ(1, sig->return_count()); |
| constexpr Register kReturnReg = v0; |
| if (kReturnReg != next_result_reg->gp()) { |
| Move(*next_result_reg, LiftoffRegister(kReturnReg), sig->GetReturn(0)); |
| } |
| ++next_result_reg; |
| } |
| |
| // Load potential output value from the buffer on the stack. |
| if (out_argument_type != kWasmStmt) { |
| liftoff::Load(this, *next_result_reg, MemOperand(sp, 0), out_argument_type); |
| } |
| |
| Daddu(sp, sp, stack_bytes); |
| } |
| |
| void LiftoffAssembler::CallNativeWasmCode(Address addr) { |
| Call(addr, RelocInfo::WASM_CALL); |
| } |
| |
| void LiftoffAssembler::TailCallNativeWasmCode(Address addr) { |
| Jump(addr, RelocInfo::WASM_CALL); |
| } |
| |
| void LiftoffAssembler::CallIndirect(const wasm::FunctionSig* sig, |
| compiler::CallDescriptor* call_descriptor, |
| Register target) { |
| if (target == no_reg) { |
| pop(kScratchReg); |
| Call(kScratchReg); |
| } else { |
| Call(target); |
| } |
| } |
| |
| void LiftoffAssembler::TailCallIndirect(Register target) { |
| if (target == no_reg) { |
| Pop(kScratchReg); |
| Jump(kScratchReg); |
| } else { |
| Jump(target); |
| } |
| } |
| |
| void LiftoffAssembler::CallRuntimeStub(WasmCode::RuntimeStubId sid) { |
| // A direct call to a wasm runtime stub defined in this module. |
| // Just encode the stub index. This will be patched at relocation. |
| Call(static_cast<Address>(sid), RelocInfo::WASM_STUB_CALL); |
| } |
| |
| void LiftoffAssembler::AllocateStackSlot(Register addr, uint32_t size) { |
| Daddu(sp, sp, -size); |
| TurboAssembler::Move(addr, sp); |
| } |
| |
| void LiftoffAssembler::DeallocateStackSlot(uint32_t size) { |
| Daddu(sp, sp, size); |
| } |
| |
| void LiftoffStackSlots::Construct() { |
| for (auto& slot : slots_) { |
| const LiftoffAssembler::VarState& src = slot.src_; |
| switch (src.loc()) { |
| case LiftoffAssembler::VarState::kStack: |
| if (src.type() != kWasmS128) { |
| asm_->Ld(kScratchReg, liftoff::GetStackSlot(slot.src_offset_)); |
| asm_->push(kScratchReg); |
| } else { |
| asm_->Ld(kScratchReg, liftoff::GetStackSlot(slot.src_offset_ - 8)); |
| asm_->push(kScratchReg); |
| asm_->Ld(kScratchReg, liftoff::GetStackSlot(slot.src_offset_)); |
| asm_->push(kScratchReg); |
| } |
| break; |
| case LiftoffAssembler::VarState::kRegister: |
| liftoff::push(asm_, src.reg(), src.type()); |
| break; |
| case LiftoffAssembler::VarState::kIntConst: { |
| asm_->li(kScratchReg, Operand(src.i32_const())); |
| asm_->push(kScratchReg); |
| break; |
| } |
| } |
| } |
| } |
| |
| } // namespace wasm |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_WASM_BASELINE_MIPS64_LIFTOFF_ASSEMBLER_MIPS64_H_ |