Commit 340d2c0f authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[Liftoff] Implement i32.div_u and i32.div_s

This adds support for i32.div_u and i32.div_s, implemented on ia32 and
x64.

R=ahaas@chromium.org

Bug: v8:6600
Change-Id: I920fb0613ecba0021dab0936690415be88d666e9
Reviewed-on: https://chromium-review.googlesource.com/1021890
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52737}
parent 75e28234
......@@ -195,6 +195,17 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
#undef UNIMPLEMENTED_I32_SHIFTOP
#undef UNIMPLEMENTED_I64_SHIFTOP
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
BAILOUT("i32_divs");
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_divu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -312,6 +312,17 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
#undef UNIMPLEMENTED_I32_SHIFTOP
#undef UNIMPLEMENTED_I64_SHIFTOP
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
BAILOUT("i32_divs");
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_divu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -111,6 +111,13 @@ inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) {
}
}
template <typename... Regs>
inline void SpillRegisters(LiftoffAssembler* assm, Regs... regs) {
for (LiftoffRegister r : {LiftoffRegister(regs)...}) {
if (assm->cache_state()->is_used(r)) assm->SpillRegister(r);
}
}
} // namespace liftoff
static constexpr DoubleRegister kScratchDoubleReg = xmm7;
......@@ -481,6 +488,66 @@ void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) {
liftoff::EmitCommutativeBinOp<&Assembler::imul>(this, dst, lhs, rhs);
}
namespace liftoff {
template <bool is_signed>
void EmitInt32Div(LiftoffAssembler* assm, Register dst, Register lhs,
Register rhs, Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
DCHECK_EQ(is_signed, trap_div_unrepresentable != nullptr);
// Check for division by zero.
assm->test(rhs, rhs);
assm->j(zero, trap_div_by_zero);
if (is_signed) {
// Check for kMinInt / -1. This is unrepresentable.
Label do_div;
assm->cmp(rhs, -1);
assm->j(not_equal, &do_div);
assm->cmp(lhs, kMinInt);
assm->j(equal, trap_div_unrepresentable);
assm->bind(&do_div);
}
// For division, the lhs is always taken from {edx:eax}. Thus, make sure that
// these registers are unused. If {rhs} is stored in one of them, move it to
// another temporary register.
liftoff::SpillRegisters(assm, eax, edx);
if (rhs == eax || rhs == edx) {
LiftoffRegList unavailable = LiftoffRegList::ForRegs(eax, edx, lhs);
Register tmp = assm->GetUnusedRegister(kGpReg, unavailable).gp();
assm->mov(tmp, rhs);
rhs = tmp;
}
// Now move {lhs} into {eax}, then zero-extend or sign-extend into {edx}, then
// do the division.
if (lhs != eax) assm->mov(eax, lhs);
if (is_signed) {
assm->cdq();
assm->idiv(rhs);
} else {
assm->xor_(edx, edx);
assm->div(rhs);
}
// Move back the result (in {eax}) into the dst register.
if (dst != eax) assm->mov(dst, eax);
}
} // namespace liftoff
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
liftoff::EmitInt32Div<true>(this, dst, lhs, rhs, trap_div_by_zero,
trap_div_unrepresentable);
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
liftoff::EmitInt32Div<false>(this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register rhs) {
liftoff::EmitCommutativeBinOp<&Assembler::and_>(this, dst, lhs, rhs);
}
......@@ -650,10 +717,7 @@ void LiftoffAssembler::emit_i64_mul(LiftoffRegister dst, LiftoffRegister lhs,
Register rhs_lo = ebx;
// Spill all these registers if they are still holding other values.
for (Register r : {dst_hi, dst_lo, lhs_hi, rhs_lo}) {
if (!cache_state_.is_used(LiftoffRegister(r))) continue;
SpillRegister(LiftoffRegister(r));
}
liftoff::SpillRegisters(this, dst_hi, dst_lo, lhs_hi, rhs_lo);
// Move lhs and rhs into the respective registers.
ParallelRegisterMove(
......
......@@ -388,6 +388,11 @@ class LiftoffAssembler : public TurboAssembler {
inline void emit_i32_add(Register dst, Register lhs, Register rhs);
inline void emit_i32_sub(Register dst, Register lhs, Register rhs);
inline void emit_i32_mul(Register dst, Register lhs, Register rhs);
inline void emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable);
inline void emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero);
inline void emit_i32_and(Register dst, Register lhs, Register rhs);
inline void emit_i32_or(Register dst, Register lhs, Register rhs);
inline void emit_i32_xor(Register dst, Register lhs, Register rhs);
......
......@@ -882,6 +882,33 @@ class LiftoffCompiler {
CASE_FLOAT_BINOP(F64Sub, F64, f64_sub)
CASE_FLOAT_BINOP(F64Mul, F64, f64_mul)
CASE_FLOAT_BINOP(F64Div, F64, f64_div)
case WasmOpcode::kExprI32DivS: {
LiftoffRegister rhs = __ PopToRegister();
LiftoffRegister lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs));
LiftoffRegister dst = __ GetUnusedRegister(kGpReg, {lhs, rhs});
WasmCodePosition position = decoder->position();
// Add two traps. Adding the second one might invalidate the pointer
// returned for the first one, thus get both pointers afterwards.
AddOutOfLineTrap(position, Builtins::kThrowWasmTrapDivByZero);
AddOutOfLineTrap(position, Builtins::kThrowWasmTrapDivUnrepresentable);
Label* div_by_zero = out_of_line_code_.end()[-2].label.get();
Label* div_unrepresentable = out_of_line_code_.end()[-1].label.get();
__ emit_i32_divs(dst.gp(), lhs.gp(), rhs.gp(), div_by_zero,
div_unrepresentable);
__ PushRegister(kWasmI32, dst);
break;
}
case WasmOpcode::kExprI32DivU: {
LiftoffRegister rhs = __ PopToRegister();
LiftoffRegister lhs = __ PopToRegister(LiftoffRegList::ForRegs(rhs));
LiftoffRegister dst = __ GetUnusedRegister(kGpReg, {lhs, rhs});
WasmCodePosition position = decoder->position();
Label* div_by_zero =
AddOutOfLineTrap(position, Builtins::kThrowWasmTrapDivByZero);
__ emit_i32_divu(dst.gp(), lhs.gp(), rhs.gp(), div_by_zero);
__ PushRegister(kWasmI32, dst);
break;
}
default:
return unsupported(decoder, WasmOpcodes::OpcodeName(opcode));
}
......
......@@ -289,10 +289,8 @@ class LiftoffRegList {
template <typename... Regs>
static LiftoffRegList ForRegs(Regs... regs) {
std::array<LiftoffRegister, sizeof...(regs)> regs_arr{
{LiftoffRegister(regs)...}};
LiftoffRegList list;
for (LiftoffRegister reg : regs_arr) list.set(reg);
for (LiftoffRegister reg : {LiftoffRegister(regs)...}) list.set(reg);
return list;
}
......
......@@ -556,6 +556,17 @@ 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) {
BAILOUT("i32_divs");
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_divu");
}
#define I32_BINOP(name, instruction) \
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
Register rhs) { \
......
......@@ -483,6 +483,17 @@ 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) {
BAILOUT("i32_divs");
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_divu");
}
#define I32_BINOP(name, instruction) \
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
Register rhs) { \
......
......@@ -200,6 +200,17 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
#undef UNIMPLEMENTED_I32_SHIFTOP
#undef UNIMPLEMENTED_I64_SHIFTOP
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
BAILOUT("i32_divs");
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_divu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -200,6 +200,17 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
#undef UNIMPLEMENTED_I32_SHIFTOP
#undef UNIMPLEMENTED_I64_SHIFTOP
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
BAILOUT("i32_divs");
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_divu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -106,6 +106,13 @@ inline void push(LiftoffAssembler* assm, LiftoffRegister reg, ValueType type) {
}
}
template <typename... Regs>
inline void SpillRegisters(LiftoffAssembler* assm, Regs... regs) {
for (LiftoffRegister r : {LiftoffRegister(regs)...}) {
if (assm->cache_state()->is_used(r)) assm->SpillRegister(r);
}
}
} // namespace liftoff
uint32_t LiftoffAssembler::PrepareStackFrame() {
......@@ -436,6 +443,66 @@ void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register rhs) {
lhs, rhs);
}
namespace liftoff {
template <bool is_signed>
void EmitInt32Div(LiftoffAssembler* assm, Register dst, Register lhs,
Register rhs, Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
DCHECK_EQ(is_signed, trap_div_unrepresentable != nullptr);
// Check for division by zero.
assm->testl(rhs, rhs);
assm->j(zero, trap_div_by_zero);
if (is_signed) {
// Check for kMinInt / -1. This is unrepresentable.
Label do_div;
assm->cmpl(rhs, Immediate(-1));
assm->j(not_equal, &do_div);
assm->cmpl(lhs, Immediate(kMinInt));
assm->j(equal, trap_div_unrepresentable);
assm->bind(&do_div);
}
// For division, the lhs is always taken from {edx:eax}. Thus, make sure that
// these registers are unused. If {rhs} is stored in one of them, move it to
// another temporary register.
liftoff::SpillRegisters(assm, rdx, rax);
if (rhs == rax || rhs == rdx) {
LiftoffRegList unavailable = LiftoffRegList::ForRegs(rax, rdx, lhs);
Register tmp = assm->GetUnusedRegister(kGpReg, unavailable).gp();
assm->movl(tmp, rhs);
rhs = tmp;
}
// Now move {lhs} into {eax}, then zero-extend or sign-extend into {edx}, then
// do the division.
if (lhs != rax) assm->movl(rax, lhs);
if (is_signed) {
assm->cdq();
assm->idivl(rhs);
} else {
assm->xorl(rdx, rdx);
assm->divl(rhs);
}
// Move back the result (in {eax}) into the dst register.
if (dst != rax) assm->movl(dst, rax);
}
} // namespace liftoff
void LiftoffAssembler::emit_i32_divs(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
liftoff::EmitInt32Div<true>(this, dst, lhs, rhs, trap_div_by_zero,
trap_div_unrepresentable);
}
void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
liftoff::EmitInt32Div<false>(this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register rhs) {
liftoff::EmitCommutativeBinOp<&Assembler::andl, &Assembler::movl>(this, dst,
lhs, rhs);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment