Commit 75922ead authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[Liftoff] Implement i32.rem_u and i32.rem_s

This adds support for i32.rem_u and i32.rem_s, implemented on ia32 and
x64.

R=ahaas@chromium.org

Bug: v8:6600
Change-Id: Id08a51f7a0dcb7a1ed43c5a97be7a7dafff85397
Reviewed-on: https://chromium-review.googlesource.com/1023932Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52764}
parent 75288a03
......@@ -206,6 +206,16 @@ void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
BAILOUT("i32_divu");
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_rems");
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_remu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -339,6 +339,16 @@ void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
BAILOUT("i32_divu");
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_rems");
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_remu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -489,24 +489,39 @@ void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register 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);
enum class DivOrRem : uint8_t { kDiv, kRem };
template <bool is_signed, DivOrRem div_or_rem>
void EmitInt32DivOrRem(LiftoffAssembler* assm, Register dst, Register lhs,
Register rhs, Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
constexpr bool needs_unrepresentable_check =
is_signed && div_or_rem == DivOrRem::kDiv;
constexpr bool special_case_minus_1 =
is_signed && div_or_rem == DivOrRem::kRem;
DCHECK_EQ(needs_unrepresentable_check, 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 done;
if (needs_unrepresentable_check) {
// 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);
} else if (special_case_minus_1) {
// {lhs % -1} is always 0 (needs to be special cased because {kMinInt / -1}
// cannot be computed).
Label do_rem;
assm->cmp(rhs, -1);
assm->j(not_equal, &do_rem);
assm->xor_(dst, dst);
assm->jmp(&done);
assm->bind(&do_rem);
}
// For division, the lhs is always taken from {edx:eax}. Thus, make sure that
......@@ -531,21 +546,36 @@ void EmitInt32Div(LiftoffAssembler* assm, Register dst, Register lhs,
assm->div(rhs);
}
// Move back the result (in {eax}) into the dst register.
if (dst != eax) assm->mov(dst, eax);
// Move back the result (in {eax} or {edx}) into the {dst} register.
constexpr Register kResultReg = div_or_rem == DivOrRem::kDiv ? eax : edx;
if (dst != kResultReg) assm->mov(dst, kResultReg);
if (special_case_minus_1) assm->bind(&done);
}
} // 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);
liftoff::EmitInt32DivOrRem<true, liftoff::DivOrRem::kDiv>(
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);
liftoff::EmitInt32DivOrRem<false, liftoff::DivOrRem::kDiv>(
this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
liftoff::EmitInt32DivOrRem<true, liftoff::DivOrRem::kRem>(
this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
liftoff::EmitInt32DivOrRem<false, liftoff::DivOrRem::kRem>(
this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register rhs) {
......
......@@ -393,6 +393,10 @@ class LiftoffAssembler : public TurboAssembler {
Label* trap_div_unrepresentable);
inline void emit_i32_divu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero);
inline void emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_rem_by_zero);
inline void emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_rem_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);
......
......@@ -106,18 +106,17 @@ class LiftoffCompiler {
MovableLabel label;
MovableLabel continuation;
Builtins::Name builtin;
wasm::WasmCodePosition position;
WasmCodePosition position;
LiftoffRegList regs_to_save;
uint32_t pc; // for trap handler.
// Named constructors:
static OutOfLineCode Trap(Builtins::Name b, wasm::WasmCodePosition pos,
static OutOfLineCode Trap(Builtins::Name b, WasmCodePosition pos,
uint32_t pc) {
DCHECK_LT(0, pos);
return {{}, {}, b, pos, {}, pc};
}
static OutOfLineCode StackCheck(wasm::WasmCodePosition pos,
LiftoffRegList regs) {
static OutOfLineCode StackCheck(WasmCodePosition pos, LiftoffRegList regs) {
return {{}, {}, Builtins::kWasmStackGuard, pos, regs, 0};
}
};
......@@ -279,7 +278,7 @@ class LiftoffCompiler {
return num_lowered_params;
}
void StackCheck(wasm::WasmCodePosition position) {
void StackCheck(WasmCodePosition position) {
if (FLAG_wasm_no_stack_checks || !env_->runtime_exception_support) return;
out_of_line_code_.push_back(
OutOfLineCode::StackCheck(position, __ cache_state()->used_registers));
......@@ -618,7 +617,7 @@ class LiftoffCompiler {
TypeConversionTrapping can_trap>
void EmitTypeConversion(WasmOpcode opcode,
ExternalReference (*fallback_fn)(Isolate*),
wasm::WasmCodePosition trap_position) {
WasmCodePosition trap_position) {
static constexpr RegClass src_rc = reg_class_for(src_type);
static constexpr RegClass dst_rc = reg_class_for(dst_type);
LiftoffRegister src = __ PopToRegister();
......@@ -882,33 +881,49 @@ 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);
case WasmOpcode::kExprI32DivS:
EmitBinOp<kWasmI32, kWasmI32>([this, decoder](LiftoffRegister dst,
LiftoffRegister lhs,
LiftoffRegister rhs) {
WasmCodePosition position = decoder->position();
AddOutOfLineTrap(position, Builtins::kThrowWasmTrapDivByZero);
// Adding the second trap might invalidate the pointer returned for
// the first one, thus get both pointers afterwards.
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);
});
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);
case WasmOpcode::kExprI32DivU:
EmitBinOp<kWasmI32, kWasmI32>([this, decoder](LiftoffRegister dst,
LiftoffRegister lhs,
LiftoffRegister rhs) {
Label* div_by_zero = AddOutOfLineTrap(
decoder->position(), Builtins::kThrowWasmTrapDivByZero);
__ emit_i32_divu(dst.gp(), lhs.gp(), rhs.gp(), div_by_zero);
});
break;
case WasmOpcode::kExprI32RemS:
EmitBinOp<kWasmI32, kWasmI32>([this, decoder](LiftoffRegister dst,
LiftoffRegister lhs,
LiftoffRegister rhs) {
Label* rem_by_zero = AddOutOfLineTrap(
decoder->position(), Builtins::kThrowWasmTrapRemByZero);
__ emit_i32_rems(dst.gp(), lhs.gp(), rhs.gp(), rem_by_zero);
});
break;
case WasmOpcode::kExprI32RemU:
EmitBinOp<kWasmI32, kWasmI32>([this, decoder](LiftoffRegister dst,
LiftoffRegister lhs,
LiftoffRegister rhs) {
Label* rem_by_zero = AddOutOfLineTrap(
decoder->position(), Builtins::kThrowWasmTrapRemByZero);
__ emit_i32_remu(dst.gp(), lhs.gp(), rhs.gp(), rem_by_zero);
});
break;
}
default:
return unsupported(decoder, WasmOpcodes::OpcodeName(opcode));
}
......@@ -1183,8 +1198,8 @@ class LiftoffCompiler {
__ cache_state()->Steal(if_block->else_state->state);
}
Label* AddOutOfLineTrap(wasm::WasmCodePosition position,
Builtins::Name builtin, uint32_t pc = 0) {
Label* AddOutOfLineTrap(WasmCodePosition position, Builtins::Name builtin,
uint32_t pc = 0) {
DCHECK(!FLAG_wasm_no_bounds_checks);
// The pc is needed for memory OOB trap with trap handler enabled. Other
// callers should not even compute it.
......
......@@ -567,6 +567,16 @@ void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
BAILOUT("i32_divu");
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_rems");
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_remu");
}
#define I32_BINOP(name, instruction) \
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
Register rhs) { \
......
......@@ -494,6 +494,16 @@ void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
BAILOUT("i32_divu");
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_rems");
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_remu");
}
#define I32_BINOP(name, instruction) \
void LiftoffAssembler::emit_i32_##name(Register dst, Register lhs, \
Register rhs) { \
......
......@@ -211,6 +211,16 @@ void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
BAILOUT("i32_divu");
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_rems");
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_remu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -211,6 +211,16 @@ void LiftoffAssembler::emit_i32_divu(Register dst, Register lhs, Register rhs,
BAILOUT("i32_divu");
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_rems");
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
BAILOUT("i32_remu");
}
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src, Label* trap) {
......
......@@ -444,24 +444,39 @@ void LiftoffAssembler::emit_i32_mul(Register dst, Register lhs, Register 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);
enum class DivOrRem : uint8_t { kDiv, kRem };
template <bool is_signed, DivOrRem div_or_rem>
void EmitInt32DivOrRem(LiftoffAssembler* assm, Register dst, Register lhs,
Register rhs, Label* trap_div_by_zero,
Label* trap_div_unrepresentable) {
constexpr bool needs_unrepresentable_check =
is_signed && div_or_rem == DivOrRem::kDiv;
constexpr bool special_case_minus_1 =
is_signed && div_or_rem == DivOrRem::kRem;
DCHECK_EQ(needs_unrepresentable_check, 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 done;
if (needs_unrepresentable_check) {
// 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);
} else if (special_case_minus_1) {
// {lhs % -1} is always 0 (needs to be special cased because {kMinInt / -1}
// cannot be computed).
Label do_rem;
assm->cmpl(rhs, Immediate(-1));
assm->j(not_equal, &do_rem);
assm->xorl(dst, dst);
assm->jmp(&done);
assm->bind(&do_rem);
}
// For division, the lhs is always taken from {edx:eax}. Thus, make sure that
......@@ -486,21 +501,36 @@ void EmitInt32Div(LiftoffAssembler* assm, Register dst, Register lhs,
assm->divl(rhs);
}
// Move back the result (in {eax}) into the dst register.
if (dst != rax) assm->movl(dst, rax);
// Move back the result (in {eax} or {edx}) into the {dst} register.
constexpr Register kResultReg = div_or_rem == DivOrRem::kDiv ? rax : rdx;
if (dst != kResultReg) assm->movl(dst, kResultReg);
if (special_case_minus_1) assm->bind(&done);
}
} // 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);
liftoff::EmitInt32DivOrRem<true, liftoff::DivOrRem::kDiv>(
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);
liftoff::EmitInt32DivOrRem<false, liftoff::DivOrRem::kDiv>(
this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_rems(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
liftoff::EmitInt32DivOrRem<true, liftoff::DivOrRem::kRem>(
this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_remu(Register dst, Register lhs, Register rhs,
Label* trap_div_by_zero) {
liftoff::EmitInt32DivOrRem<false, liftoff::DivOrRem::kRem>(
this, dst, lhs, rhs, trap_div_by_zero, nullptr);
}
void LiftoffAssembler::emit_i32_and(Register dst, Register lhs, Register 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