diff options
Diffstat (limited to 'deps/v8/src/wasm/baseline/ia32/liftoff-assembler-ia32.h')
-rw-r--r-- | deps/v8/src/wasm/baseline/ia32/liftoff-assembler-ia32.h | 219 |
1 files changed, 145 insertions, 74 deletions
diff --git a/deps/v8/src/wasm/baseline/ia32/liftoff-assembler-ia32.h b/deps/v8/src/wasm/baseline/ia32/liftoff-assembler-ia32.h index 3a0ace0d62..067c79be32 100644 --- a/deps/v8/src/wasm/baseline/ia32/liftoff-assembler-ia32.h +++ b/deps/v8/src/wasm/baseline/ia32/liftoff-assembler-ia32.h @@ -34,8 +34,10 @@ inline Operand GetStackSlot(uint32_t index) { return Operand(ebp, -kFirstStackSlotOffset - offset); } -inline Operand GetHalfStackSlot(uint32_t half_index) { - int32_t offset = half_index * (LiftoffAssembler::kStackSlotSize / 2); +inline MemOperand GetHalfStackSlot(uint32_t index, RegPairHalf half) { + int32_t half_offset = + half == kLowWord ? 0 : LiftoffAssembler::kStackSlotSize / 2; + int32_t offset = index * LiftoffAssembler::kStackSlotSize - half_offset; return Operand(ebp, -kFirstStackSlotOffset - offset); } @@ -43,10 +45,7 @@ inline Operand GetHalfStackSlot(uint32_t half_index) { inline Operand GetInstanceOperand() { return Operand(ebp, -8); } static constexpr LiftoffRegList kByteRegs = - LiftoffRegList::FromBits<Register::ListOf<eax, ecx, edx, ebx>()>(); -static_assert(kByteRegs.GetNumRegsSet() == 4, "should have four byte regs"); -static_assert((kByteRegs & kGpCacheRegList) == kByteRegs, - "kByteRegs only contains gp cache registers"); + LiftoffRegList::FromBits<Register::ListOf<eax, ecx, edx>()>(); inline void Load(LiftoffAssembler* assm, LiftoffRegister dst, Register base, int32_t offset, ValueType type) { @@ -126,6 +125,28 @@ inline void SignExtendI32ToI64(Assembler* assm, LiftoffRegister reg) { assm->sar(reg.high_gp(), 31); } +// Get a temporary byte register, using {candidate} if possible. +// Might spill, but always keeps status flags intact. +inline Register GetTmpByteRegister(LiftoffAssembler* assm, Register candidate) { + if (candidate.is_byte_register()) return candidate; + // {GetUnusedRegister()} may insert move instructions to spill registers to + // the stack. This is OK because {mov} does not change the status flags. + return assm->GetUnusedRegister(liftoff::kByteRegs).gp(); +} + +inline void MoveStackValue(LiftoffAssembler* assm, const Operand& src, + const Operand& dst) { + if (assm->cache_state()->has_unused_register(kGpReg)) { + Register tmp = assm->cache_state()->unused_register(kGpReg).gp(); + assm->mov(tmp, src); + assm->mov(dst, tmp); + } else { + // No free register, move via the stack. + assm->push(src); + assm->pop(dst); + } +} + constexpr DoubleRegister kScratchDoubleReg = xmm7; constexpr int kSubSpSize = 6; // 6 bytes for "sub esp, <imm32>" @@ -146,8 +167,9 @@ void LiftoffAssembler::PatchPrepareStackFrame(int offset, // 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 = 64; - Assembler patching_assembler(AssemblerOptions{}, buffer_ + offset, - kAvailableSpace); + Assembler patching_assembler( + AssemblerOptions{}, + ExternalAssemblerBuffer(buffer_start_ + offset, kAvailableSpace)); #if V8_OS_WIN constexpr int kPageSize = 4 * 1024; if (bytes > kPageSize) { @@ -216,6 +238,11 @@ void LiftoffAssembler::LoadFromInstance(Register dst, uint32_t offset, mov(dst, Operand(dst, offset)); } +void LiftoffAssembler::LoadTaggedPointerFromInstance(Register dst, + uint32_t offset) { + LoadFromInstance(dst, offset, kTaggedSize); +} + void LiftoffAssembler::SpillInstance(Register instance) { mov(liftoff::GetInstanceOperand(), instance); } @@ -224,6 +251,15 @@ void LiftoffAssembler::FillInstanceInto(Register dst) { mov(dst, liftoff::GetInstanceOperand()); } +void LiftoffAssembler::LoadTaggedPointer(Register dst, Register src_addr, + Register offset_reg, + uint32_t offset_imm, + LiftoffRegList pinned) { + STATIC_ASSERT(kTaggedSize == kInt32Size); + Load(LiftoffRegister(dst), src_addr, offset_reg, offset_imm, + LoadType::kI32Load, pinned); +} + void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr, Register offset_reg, uint32_t offset_imm, LoadType type, LiftoffRegList pinned, @@ -324,7 +360,13 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, if (src.gp().is_byte_register()) { mov_b(dst_op, src.gp()); } else { - Register byte_src = GetUnusedRegister(liftoff::kByteRegs, pinned).gp(); + // We know that {src} is not a byte register, so the only pinned byte + // registers (beside the outer {pinned}) are {dst_addr} and potentially + // {offset_reg}. + LiftoffRegList pinned_byte = pinned | LiftoffRegList::ForRegs(dst_addr); + if (offset_reg != no_reg) pinned_byte.set(offset_reg); + Register byte_src = + GetUnusedRegister(liftoff::kByteRegs, pinned_byte).gp(); mov(byte_src, src.gp()); mov_b(dst_op, byte_src); } @@ -367,19 +409,22 @@ void LiftoffAssembler::Store(Register dst_addr, Register offset_reg, void LiftoffAssembler::LoadCallerFrameSlot(LiftoffRegister dst, uint32_t caller_slot_idx, ValueType type) { - liftoff::Load(this, dst, ebp, kPointerSize * (caller_slot_idx + 1), type); + liftoff::Load(this, dst, ebp, kSystemPointerSize * (caller_slot_idx + 1), + type); } void LiftoffAssembler::MoveStackValue(uint32_t dst_index, uint32_t src_index, ValueType type) { - DCHECK_NE(dst_index, src_index); - if (cache_state_.has_unused_register(kGpReg)) { - LiftoffRegister reg = GetUnusedRegister(kGpReg); - Fill(reg, src_index, type); - Spill(dst_index, reg, type); + if (needs_reg_pair(type)) { + liftoff::MoveStackValue(this, + liftoff::GetHalfStackSlot(src_index, kLowWord), + liftoff::GetHalfStackSlot(dst_index, kLowWord)); + liftoff::MoveStackValue(this, + liftoff::GetHalfStackSlot(src_index, kHighWord), + liftoff::GetHalfStackSlot(dst_index, kHighWord)); } else { - push(liftoff::GetStackSlot(src_index)); - pop(liftoff::GetStackSlot(dst_index)); + liftoff::MoveStackValue(this, liftoff::GetStackSlot(src_index), + liftoff::GetStackSlot(dst_index)); } } @@ -409,8 +454,8 @@ void LiftoffAssembler::Spill(uint32_t index, LiftoffRegister reg, mov(dst, reg.gp()); break; case kWasmI64: - mov(dst, reg.low_gp()); - mov(liftoff::GetHalfStackSlot(2 * index - 1), reg.high_gp()); + mov(liftoff::GetHalfStackSlot(index, kLowWord), reg.low_gp()); + mov(liftoff::GetHalfStackSlot(index, kHighWord), reg.high_gp()); break; case kWasmF32: movss(dst, reg.fp()); @@ -433,8 +478,8 @@ void LiftoffAssembler::Spill(uint32_t index, WasmValue value) { case kWasmI64: { int32_t low_word = value.to_i64(); int32_t high_word = value.to_i64() >> 32; - mov(dst, Immediate(low_word)); - mov(liftoff::GetHalfStackSlot(2 * index - 1), Immediate(high_word)); + mov(liftoff::GetHalfStackSlot(index, kLowWord), Immediate(low_word)); + mov(liftoff::GetHalfStackSlot(index, kHighWord), Immediate(high_word)); break; } default: @@ -451,8 +496,8 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index, mov(reg.gp(), src); break; case kWasmI64: - mov(reg.low_gp(), src); - mov(reg.high_gp(), liftoff::GetHalfStackSlot(2 * index - 1)); + mov(reg.low_gp(), liftoff::GetHalfStackSlot(index, kLowWord)); + mov(reg.high_gp(), liftoff::GetHalfStackSlot(index, kHighWord)); break; case kWasmF32: movss(reg.fp(), src); @@ -465,8 +510,9 @@ void LiftoffAssembler::Fill(LiftoffRegister reg, uint32_t index, } } -void LiftoffAssembler::FillI64Half(Register reg, uint32_t half_index) { - mov(reg, liftoff::GetHalfStackSlot(half_index)); +void LiftoffAssembler::FillI64Half(Register reg, uint32_t index, + RegPairHalf half) { + mov(reg, liftoff::GetHalfStackSlot(index, half)); } void LiftoffAssembler::emit_i32_add(Register dst, Register lhs, Register rhs) { @@ -478,12 +524,17 @@ void LiftoffAssembler::emit_i32_add(Register dst, Register lhs, Register rhs) { } void LiftoffAssembler::emit_i32_sub(Register dst, Register lhs, Register rhs) { - if (dst == rhs) { - neg(dst); - add(dst, lhs); - } else { + if (dst != rhs) { + // Default path. if (dst != lhs) mov(dst, lhs); sub(dst, rhs); + } else if (lhs == rhs) { + // Degenerate case. + xor_(dst, dst); + } else { + // Emit {dst = lhs + -rhs} if dst == rhs. + neg(dst); + add(dst, lhs); } } @@ -768,15 +819,16 @@ void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs, Register lhs_hi = ecx; Register lhs_lo = dst_lo; Register rhs_hi = dst_hi; - Register rhs_lo = ebx; + Register rhs_lo = esi; // Spill all these registers if they are still holding other values. liftoff::SpillRegisters(this, dst_hi, dst_lo, lhs_hi, rhs_lo); // Move lhs and rhs into the respective registers. - ParallelRegisterMove( - {{LiftoffRegister::ForPair(lhs_lo, lhs_hi), lhs, kWasmI64}, - {LiftoffRegister::ForPair(rhs_lo, rhs_hi), rhs, kWasmI64}}); + ParallelRegisterMoveTuple reg_moves[]{ + {LiftoffRegister::ForPair(lhs_lo, lhs_hi), lhs, kWasmI64}, + {LiftoffRegister::ForPair(rhs_lo, rhs_hi), rhs, kWasmI64}}; + ParallelRegisterMove(ArrayVector(reg_moves)); // First mul: lhs_hi' = lhs_hi * rhs_lo. imul(lhs_hi, rhs_lo); @@ -784,7 +836,7 @@ void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs, imul(rhs_hi, lhs_lo); // Add them: lhs_hi'' = lhs_hi' + rhs_hi' = lhs_hi * rhs_lo + rhs_hi * lhs_lo. add(lhs_hi, rhs_hi); - // Third mul: edx:eax (dst_hi:dst_lo) = eax * ebx (lhs_lo * rhs_lo). + // Third mul: edx:eax (dst_hi:dst_lo) = eax * esi (lhs_lo * rhs_lo). mul(rhs_lo); // Add lhs_hi'' to dst_hi. add(dst_hi, lhs_hi); @@ -839,27 +891,32 @@ inline void Emit64BitShiftOperation( LiftoffAssembler* assm, LiftoffRegister dst, LiftoffRegister src, Register amount, void (TurboAssembler::*emit_shift)(Register, Register), LiftoffRegList pinned) { + // Temporary registers cannot overlap with {dst}. pinned.set(dst); - pinned.set(src); - pinned.set(amount); + + constexpr size_t kMaxRegMoves = 3; + base::SmallVector<LiftoffAssembler::ParallelRegisterMoveTuple, kMaxRegMoves> + reg_moves; + // If {dst} contains {ecx}, replace it by an unused register, which is then // moved to {ecx} in the end. Register ecx_replace = no_reg; if (PairContains(dst, ecx)) { - ecx_replace = pinned.set(assm->GetUnusedRegister(kGpReg, pinned)).gp(); + ecx_replace = assm->GetUnusedRegister(kGpReg, pinned).gp(); dst = ReplaceInPair(dst, ecx, ecx_replace); // If {amount} needs to be moved to {ecx}, but {ecx} is in use (and not part // of {dst}, hence overwritten anyway), move {ecx} to a tmp register and // restore it at the end. } else if (amount != ecx && - assm->cache_state()->is_used(LiftoffRegister(ecx))) { + (assm->cache_state()->is_used(LiftoffRegister(ecx)) || + pinned.has(LiftoffRegister(ecx)))) { ecx_replace = assm->GetUnusedRegister(kGpReg, pinned).gp(); - assm->mov(ecx_replace, ecx); + reg_moves.emplace_back(ecx_replace, ecx, kWasmI32); } - assm->ParallelRegisterMove( - {{dst, src, kWasmI64}, - {LiftoffRegister{ecx}, LiftoffRegister{amount}, kWasmI32}}); + reg_moves.emplace_back(dst, src, kWasmI64); + reg_moves.emplace_back(ecx, amount, kWasmI32); + assm->ParallelRegisterMove(VectorOf(reg_moves)); // Do the actual shift. (assm->*emit_shift)(dst.high_gp(), dst.low_gp()); @@ -1063,25 +1120,41 @@ void LiftoffAssembler::emit_f32_neg(DoubleRegister dst, DoubleRegister src) { } } -void LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) { - REQUIRE_CPU_FEATURE(SSE4_1); - roundss(dst, src, kRoundUp); +bool LiftoffAssembler::emit_f32_ceil(DoubleRegister dst, DoubleRegister src) { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope feature(this, SSE4_1); + roundss(dst, src, kRoundUp); + return true; + } + return false; } -void LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) { - REQUIRE_CPU_FEATURE(SSE4_1); - roundss(dst, src, kRoundDown); +bool LiftoffAssembler::emit_f32_floor(DoubleRegister dst, DoubleRegister src) { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope feature(this, SSE4_1); + roundss(dst, src, kRoundDown); + return true; + } + return false; } -void LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) { - REQUIRE_CPU_FEATURE(SSE4_1); - roundss(dst, src, kRoundToZero); +bool LiftoffAssembler::emit_f32_trunc(DoubleRegister dst, DoubleRegister src) { + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope feature(this, SSE4_1); + roundss(dst, src, kRoundToZero); + return true; + } + return false; } -void LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst, +bool LiftoffAssembler::emit_f32_nearest_int(DoubleRegister dst, DoubleRegister src) { - REQUIRE_CPU_FEATURE(SSE4_1); - roundss(dst, src, kRoundToNearest); + if (CpuFeatures::IsSupported(SSE4_1)) { + CpuFeatureScope feature(this, SSE4_1); + roundss(dst, src, kRoundToNearest); + return true; + } + return false; } void LiftoffAssembler::emit_f32_sqrt(DoubleRegister dst, DoubleRegister src) { @@ -1239,7 +1312,8 @@ inline void ConvertFloatToIntAndBack(LiftoffAssembler* assm, Register dst, assm->Cvtsi2sd(converted_back, dst); } else { // f64 -> u32 assm->Cvttsd2ui(dst, src, liftoff::kScratchDoubleReg); - assm->Cvtui2sd(converted_back, dst); + assm->Cvtui2sd(converted_back, dst, + assm->GetUnusedRegister(kGpReg, pinned).gp()); } } else { // f32 if (std::is_signed<dst_type>::value) { // f32 -> i32 @@ -1346,9 +1420,12 @@ bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode, case kExprF64SConvertI32: Cvtsi2sd(dst.fp(), src.gp()); return true; - case kExprF64UConvertI32: - Cvtui2sd(dst.fp(), src.gp()); + case kExprF64UConvertI32: { + LiftoffRegList pinned = LiftoffRegList::ForRegs(dst, src); + Register scratch = GetUnusedRegister(kGpReg, pinned).gp(); + Cvtui2sd(dst.fp(), src.gp(), scratch); return true; + } case kExprF64ConvertF32: cvtss2sd(dst.fp(), src.fp()); return true; @@ -1366,7 +1443,9 @@ bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode, } void LiftoffAssembler::emit_i32_signextend_i8(Register dst, Register src) { - movsx_b(dst, src); + Register byte_reg = liftoff::GetTmpByteRegister(this, src); + if (byte_reg != src) mov(byte_reg, src); + movsx_b(dst, byte_reg); } void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) { @@ -1375,7 +1454,9 @@ void LiftoffAssembler::emit_i32_signextend_i16(Register dst, Register src) { void LiftoffAssembler::emit_i64_signextend_i8(LiftoffRegister dst, LiftoffRegister src) { - movsx_b(dst.low_gp(), src.low_gp()); + Register byte_reg = liftoff::GetTmpByteRegister(this, src.low_gp()); + if (byte_reg != src.low_gp()) mov(byte_reg, src.low_gp()); + movsx_b(dst.low_gp(), byte_reg); liftoff::SignExtendI32ToI64(this, dst); } @@ -1416,16 +1497,6 @@ void LiftoffAssembler::emit_cond_jump(Condition cond, Label* label, namespace liftoff { -// Get a temporary byte register, using {candidate} if possible. -// Might spill, but always keeps status flags intact. -inline Register GetTmpByteRegister(LiftoffAssembler* assm, Register candidate) { - if (candidate.is_byte_register()) return candidate; - LiftoffRegList pinned = LiftoffRegList::ForRegs(candidate); - // {GetUnusedRegister()} may insert move instructions to spill registers to - // the stack. This is OK because {mov} does not change the status flags. - return assm->GetUnusedRegister(liftoff::kByteRegs, pinned).gp(); -} - // Setcc into dst register, given a scratch byte register (might be the same as // dst). Never spills. inline void setcc_32_no_spill(LiftoffAssembler* assm, Condition cond, @@ -1606,8 +1677,9 @@ void LiftoffAssembler::PopRegisters(LiftoffRegList regs) { } void LiftoffAssembler::DropStackSlotsAndRet(uint32_t num_stack_slots) { - DCHECK_LT(num_stack_slots, (1 << 16) / kPointerSize); // 16 bit immediate - ret(static_cast<int>(num_stack_slots * kPointerSize)); + DCHECK_LT(num_stack_slots, + (1 << 16) / kSystemPointerSize); // 16 bit immediate + ret(static_cast<int>(num_stack_slots * kSystemPointerSize)); } void LiftoffAssembler::CallC(wasm::FunctionSig* sig, @@ -1695,10 +1767,9 @@ void LiftoffStackSlots::Construct() { case LiftoffAssembler::VarState::kStack: if (src.type() == kWasmF64) { DCHECK_EQ(kLowWord, slot.half_); - asm_->push(liftoff::GetHalfStackSlot(2 * slot.src_index_ - 1)); + asm_->push(liftoff::GetHalfStackSlot(slot.src_index_, kHighWord)); } - asm_->push(liftoff::GetHalfStackSlot(2 * slot.src_index_ - - (slot.half_ == kLowWord ? 0 : 1))); + asm_->push(liftoff::GetHalfStackSlot(slot.src_index_, slot.half_)); break; case LiftoffAssembler::VarState::kRegister: if (src.type() == kWasmI64) { |