Commit b4a43097 authored by Clemens Hammacher's avatar Clemens Hammacher Committed by Commit Bot

[Liftoff] Implement f32/f64 to i32/u32 conversions

This implement float to i32/u32 conversions on ia32 and x64.

R=ahaas@chromium.org

Bug: v8:6600
Change-Id: I4eb6a74d86fafce3245bf3f37d32ac6ca156550a
Reviewed-on: https://chromium-review.googlesource.com/1014123Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52662}
parent 0ec7e505
......@@ -197,7 +197,7 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
BAILOUT("emit_type_conversion");
return true;
}
......
......@@ -314,7 +314,7 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
BAILOUT("emit_type_conversion");
return true;
}
......
......@@ -920,13 +920,89 @@ void LiftoffAssembler::emit_f64_sqrt(DoubleRegister dst, DoubleRegister src) {
Sqrtsd(dst, src);
}
namespace liftoff {
// Used for float to int conversions. If the value in {converted_back} equals
// {src} afterwards, the conversion succeeded.
template <typename dst_type, typename src_type>
inline void ConvertFloatToIntAndBack(LiftoffAssembler* assm, Register dst,
DoubleRegister src,
DoubleRegister converted_back,
LiftoffRegList pinned) {
if (std::is_same<double, src_type>::value) { // f64
if (std::is_signed<dst_type>::value) { // f64 -> i32
assm->cvttsd2si(dst, src);
assm->Cvtsi2sd(converted_back, dst);
} else { // f64 -> u32
assm->Cvttsd2ui(dst, src, kScratchDoubleReg);
assm->Cvtui2sd(converted_back, dst);
}
} else { // f32
if (std::is_signed<dst_type>::value) { // f32 -> i32
assm->cvttss2si(dst, src);
assm->Cvtsi2ss(converted_back, dst);
} else { // f32 -> u32
assm->Cvttss2ui(dst, src, kScratchDoubleReg);
assm->Cvtui2ss(converted_back, dst,
assm->GetUnusedRegister(kGpReg, pinned).gp());
}
}
}
template <typename dst_type, typename src_type>
inline bool EmitTruncateFloatToInt(LiftoffAssembler* assm, Register dst,
DoubleRegister src, Label* trap) {
if (!CpuFeatures::IsSupported(SSE4_1)) {
assm->bailout("no SSE4.1");
return true;
}
CpuFeatureScope feature(assm, SSE4_1);
LiftoffRegList pinned = LiftoffRegList::ForRegs(src, dst);
DoubleRegister rounded =
pinned.set(assm->GetUnusedRegister(kFpReg, pinned)).fp();
DoubleRegister converted_back =
pinned.set(assm->GetUnusedRegister(kFpReg, pinned)).fp();
if (std::is_same<double, src_type>::value) { // f64
assm->roundsd(rounded, src, kRoundToZero);
} else { // f32
assm->roundss(rounded, src, kRoundToZero);
}
ConvertFloatToIntAndBack<dst_type, src_type>(assm, dst, rounded,
converted_back, pinned);
if (std::is_same<double, src_type>::value) { // f64
assm->ucomisd(converted_back, rounded);
} else { // f32
assm->ucomiss(converted_back, rounded);
}
// Jump to trap if PF is 0 (one of the operands was NaN) or they are not
// equal.
assm->j(parity_even, trap);
assm->j(not_equal, trap);
return true;
}
} // namespace liftoff
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
switch (opcode) {
case kExprI32ConvertI64:
if (dst.gp() != src.low_gp()) mov(dst.gp(), src.low_gp());
return true;
case kExprI32SConvertF32:
return liftoff::EmitTruncateFloatToInt<int32_t, float>(this, dst.gp(),
src.fp(), trap);
case kExprI32UConvertF32:
return liftoff::EmitTruncateFloatToInt<uint32_t, float>(this, dst.gp(),
src.fp(), trap);
case kExprI32SConvertF64:
return liftoff::EmitTruncateFloatToInt<int32_t, double>(this, dst.gp(),
src.fp(), trap);
case kExprI32UConvertF64:
return liftoff::EmitTruncateFloatToInt<uint32_t, double>(this, dst.gp(),
src.fp(), trap);
case kExprI32ReinterpretF32:
Movd(dst.gp(), src.fp());
return true;
......
......@@ -471,7 +471,7 @@ class LiftoffAssembler : public TurboAssembler {
// type conversions.
inline bool emit_type_conversion(WasmOpcode opcode, LiftoffRegister dst,
LiftoffRegister src);
LiftoffRegister src, Label* trap = nullptr);
inline void emit_jump(Label*);
inline void emit_jump(Register);
......@@ -565,6 +565,10 @@ class LiftoffAssembler : public TurboAssembler {
bool did_bailout() { return bailout_reason_ != nullptr; }
const char* bailout_reason() const { return bailout_reason_; }
void bailout(const char* reason) {
if (bailout_reason_ == nullptr) bailout_reason_ = reason;
}
private:
uint32_t num_locals_ = 0;
static constexpr uint32_t kInlineLocalTypes = 8;
......@@ -580,10 +584,6 @@ class LiftoffAssembler : public TurboAssembler {
LiftoffRegister SpillOneRegister(LiftoffRegList candidates,
LiftoffRegList pinned);
void bailout(const char* reason) {
if (bailout_reason_ == nullptr) bailout_reason_ = reason;
}
};
std::ostream& operator<<(std::ostream& os, LiftoffAssembler::VarState);
......
......@@ -112,6 +112,7 @@ class LiftoffCompiler {
// Named constructors:
static OutOfLineCode Trap(Builtins::Name b, wasm::WasmCodePosition pos,
uint32_t pc) {
DCHECK_LT(0, pos);
return {{}, {}, b, pos, {}, pc};
}
static OutOfLineCode StackCheck(wasm::WasmCodePosition pos,
......@@ -684,17 +685,24 @@ class LiftoffCompiler {
EmitUnOp<kWasmI32, kWasmI32>(emit_with_c_fallback);
}
void EmitTypeConversion(WasmOpcode opcode, ValueType dst_type,
ValueType src_type,
ExternalReference (*fallback_fn)(Isolate*)) {
RegClass src_rc = reg_class_for(src_type);
RegClass dst_rc = reg_class_for(dst_type);
enum TypeConversionTrapping : bool { kCanTrap = true, kNoTrap = false };
template <ValueType dst_type, ValueType src_type,
TypeConversionTrapping can_trap>
void EmitTypeConversion(WasmOpcode opcode,
ExternalReference (*fallback_fn)(Isolate*),
wasm::WasmCodePosition trap_position) {
static constexpr RegClass src_rc = reg_class_for(src_type);
static constexpr RegClass dst_rc = reg_class_for(dst_type);
LiftoffRegList pinned;
LiftoffRegister src = pinned.set(__ PopToRegister());
LiftoffRegister dst = src_rc == dst_rc
? __ GetUnusedRegister(dst_rc, {src}, pinned)
: __ GetUnusedRegister(dst_rc, pinned);
if (!__ emit_type_conversion(opcode, dst, src)) {
Label* trap = can_trap ? AddOutOfLineTrap(
trap_position,
Builtins::kThrowWasmTrapFloatUnrepresentable)
: nullptr;
if (!__ emit_type_conversion(opcode, dst, src, trap)) {
DCHECK_NOT_NULL(fallback_fn);
ExternalReference ext_ref = fallback_fn(asm_->isolate());
ValueType sig_reps[] = {src_type};
......@@ -720,10 +728,10 @@ class LiftoffCompiler {
__ emit_##fn(dst.fp(), src.fp()); \
}); \
break;
#define CASE_TYPE_CONVERSION(opcode, dst_type, src_type, ext_ref) \
case WasmOpcode::kExpr##opcode: \
EmitTypeConversion(kExpr##opcode, kWasm##dst_type, kWasm##src_type, \
ext_ref); \
#define CASE_TYPE_CONVERSION(opcode, dst_type, src_type, ext_ref, can_trap) \
case WasmOpcode::kExpr##opcode: \
EmitTypeConversion<kWasm##dst_type, kWasm##src_type, can_trap>( \
kExpr##opcode, ext_ref, can_trap ? decoder->position() : 0); \
break;
switch (opcode) {
CASE_I32_UNOP(I32Eqz, i32_eqz)
......@@ -743,27 +751,31 @@ class LiftoffCompiler {
CASE_FLOAT_UNOP(F64Trunc, F64, f64_trunc)
CASE_FLOAT_UNOP(F64NearestInt, F64, f64_nearest_int)
CASE_FLOAT_UNOP(F64Sqrt, F64, f64_sqrt)
CASE_TYPE_CONVERSION(I32ConvertI64, I32, I64, nullptr)
CASE_TYPE_CONVERSION(I32ReinterpretF32, I32, F32, nullptr)
CASE_TYPE_CONVERSION(I64SConvertI32, I64, I32, nullptr)
CASE_TYPE_CONVERSION(I64UConvertI32, I64, I32, nullptr)
CASE_TYPE_CONVERSION(I64ReinterpretF64, I64, F64, nullptr)
CASE_TYPE_CONVERSION(F32SConvertI32, F32, I32, nullptr)
CASE_TYPE_CONVERSION(F32UConvertI32, F32, I32, nullptr)
CASE_TYPE_CONVERSION(I32ConvertI64, I32, I64, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(I32SConvertF32, I32, F32, nullptr, kCanTrap)
CASE_TYPE_CONVERSION(I32UConvertF32, I32, F32, nullptr, kCanTrap)
CASE_TYPE_CONVERSION(I32SConvertF64, I32, F64, nullptr, kCanTrap)
CASE_TYPE_CONVERSION(I32UConvertF64, I32, F64, nullptr, kCanTrap)
CASE_TYPE_CONVERSION(I32ReinterpretF32, I32, F32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(I64SConvertI32, I64, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(I64UConvertI32, I64, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(I64ReinterpretF64, I64, F64, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F32SConvertI32, F32, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F32UConvertI32, F32, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F32SConvertI64, F32, I64,
&ExternalReference::wasm_int64_to_float32)
&ExternalReference::wasm_int64_to_float32, kNoTrap)
CASE_TYPE_CONVERSION(F32UConvertI64, F32, I64,
&ExternalReference::wasm_uint64_to_float32)
CASE_TYPE_CONVERSION(F32ConvertF64, F32, F64, nullptr)
CASE_TYPE_CONVERSION(F32ReinterpretI32, F32, I32, nullptr)
CASE_TYPE_CONVERSION(F64SConvertI32, F64, I32, nullptr)
CASE_TYPE_CONVERSION(F64UConvertI32, F64, I32, nullptr)
&ExternalReference::wasm_uint64_to_float32, kNoTrap)
CASE_TYPE_CONVERSION(F32ConvertF64, F32, F64, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F32ReinterpretI32, F32, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F64SConvertI32, F64, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F64UConvertI32, F64, I32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F64SConvertI64, F64, I64,
&ExternalReference::wasm_int64_to_float64)
&ExternalReference::wasm_int64_to_float64, kNoTrap)
CASE_TYPE_CONVERSION(F64UConvertI64, F64, I64,
&ExternalReference::wasm_uint64_to_float64)
CASE_TYPE_CONVERSION(F64ConvertF32, F64, F32, nullptr)
CASE_TYPE_CONVERSION(F64ReinterpretI64, F64, I64, nullptr)
&ExternalReference::wasm_uint64_to_float64, kNoTrap)
CASE_TYPE_CONVERSION(F64ConvertF32, F64, F32, nullptr, kNoTrap)
CASE_TYPE_CONVERSION(F64ReinterpretI64, F64, I64, nullptr, kNoTrap)
case kExprI32Popcnt:
EmitI32UnOpWithCFallback(&LiftoffAssembler::emit_i32_popcnt,
&ExternalReference::wasm_word32_popcnt);
......
......@@ -746,7 +746,7 @@ void LiftoffAssembler::emit_f64_nearest_int(DoubleRegister dst,
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
switch (opcode) {
case kExprI32ConvertI64:
TurboAssembler::Move(dst.gp(), src.low_gp());
......
......@@ -588,7 +588,7 @@ FP_UNOP(f64_sqrt, sqrt_d)
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
switch (opcode) {
case kExprI32ConvertI64:
TurboAssembler::Ext(dst.gp(), src.gp(), 0, 32);
......
......@@ -202,7 +202,7 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
BAILOUT("emit_type_conversion");
return true;
}
......
......@@ -202,7 +202,7 @@ UNIMPLEMENTED_FP_UNOP(f64_sqrt)
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
BAILOUT("emit_type_conversion");
return true;
}
......
......@@ -796,13 +796,88 @@ void LiftoffAssembler::emit_f64_sqrt(DoubleRegister dst, DoubleRegister src) {
Sqrtsd(dst, src);
}
namespace liftoff {
// Used for float to int conversions. If the value in {converted_back} equals
// {src} afterwards, the conversion succeeded.
template <typename dst_type, typename src_type>
inline void ConvertFloatToIntAndBack(LiftoffAssembler* assm, Register dst,
DoubleRegister src,
DoubleRegister converted_back) {
if (std::is_same<double, src_type>::value) { // f64
if (std::is_signed<dst_type>::value) { // f64 -> i32
assm->Cvttsd2si(dst, src);
assm->Cvtlsi2sd(converted_back, dst);
} else { // f64 -> u32
assm->Cvttsd2siq(dst, src);
assm->movl(dst, dst);
assm->Cvtqsi2sd(converted_back, dst);
}
} else { // f32
if (std::is_signed<dst_type>::value) { // f32 -> i32
assm->Cvttss2si(dst, src);
assm->Cvtlsi2ss(converted_back, dst);
} else { // f32 -> u32
assm->Cvttss2siq(dst, src);
assm->movl(dst, dst);
assm->Cvtqsi2ss(converted_back, dst);
}
}
}
template <typename dst_type, typename src_type>
inline bool EmitTruncateFloatToInt(LiftoffAssembler* assm, Register dst,
DoubleRegister src, Label* trap) {
if (!CpuFeatures::IsSupported(SSE4_1)) {
assm->bailout("no SSE4.1");
return true;
}
CpuFeatureScope feature(assm, SSE4_1);
LiftoffRegList pinned = LiftoffRegList::ForRegs(src, dst);
DoubleRegister rounded =
pinned.set(assm->GetUnusedRegister(kFpReg, pinned)).fp();
DoubleRegister converted_back = assm->GetUnusedRegister(kFpReg, pinned).fp();
if (std::is_same<double, src_type>::value) { // f64
assm->Roundsd(rounded, src, kRoundToZero);
} else { // f32
assm->Roundss(rounded, src, kRoundToZero);
}
ConvertFloatToIntAndBack<dst_type, src_type>(assm, dst, rounded,
converted_back);
if (std::is_same<double, src_type>::value) { // f64
assm->Ucomisd(converted_back, rounded);
} else { // f32
assm->Ucomiss(converted_back, rounded);
}
// Jump to trap if PF is 0 (one of the operands was NaN) or they are not
// equal.
assm->j(parity_even, trap);
assm->j(not_equal, trap);
return true;
}
} // namespace liftoff
bool LiftoffAssembler::emit_type_conversion(WasmOpcode opcode,
LiftoffRegister dst,
LiftoffRegister src) {
LiftoffRegister src, Label* trap) {
switch (opcode) {
case kExprI32ConvertI64:
movl(dst.gp(), src.gp());
return true;
case kExprI32SConvertF32:
return liftoff::EmitTruncateFloatToInt<int32_t, float>(this, dst.gp(),
src.fp(), trap);
case kExprI32UConvertF32:
return liftoff::EmitTruncateFloatToInt<uint32_t, float>(this, dst.gp(),
src.fp(), trap);
case kExprI32SConvertF64:
return liftoff::EmitTruncateFloatToInt<int32_t, double>(this, dst.gp(),
src.fp(), trap);
case kExprI32UConvertF64:
return liftoff::EmitTruncateFloatToInt<uint32_t, double>(this, dst.gp(),
src.fp(), trap);
case kExprI32ReinterpretF32:
Movd(dst.gp(), src.fp());
return true;
......@@ -920,7 +995,7 @@ void EmitFloatSetCond(LiftoffAssembler* assm, Condition cond, Register dst,
Label not_nan;
(assm->*cmp_op)(lhs, rhs);
// IF PF is one, one of the operands was Nan. This needs special handling.
// If PF is one, one of the operands was NaN. This needs special handling.
assm->j(parity_odd, &not_nan, Label::kNear);
// Return 1 for f32.ne, 0 for all other cases.
if (cond == not_equal) {
......
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