[wasm] Add remaining saturating conversions

Adds the remaining saturating float to int conversion opcodes.

Bug: v8:7226
Change-Id: If84e564a7816eb4aedbc336f5c2e614da22bb10a
Reviewed-on: https://chromium-review.googlesource.com/905472
Commit-Queue: Karl Schimpf <kschimpf@chromium.org>
Reviewed-by: Clemens Hammacher <clemensh@chromium.org>
Cr-Commit-Position: refs/heads/master@{#51157}
This commit is contained in:
Karl Schimpf 2018-02-07 11:10:16 -08:00 committed by Commit Bot
parent b7cf8ef868
commit 7f981c6c21
7 changed files with 162 additions and 106 deletions

View File

@ -785,16 +785,16 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input,
op = m->SignExtendWord32ToInt64();
break;
case wasm::kExprI64SConvertF32:
case wasm::kExprI64UConvertF32:
case wasm::kExprI64SConvertF64:
case wasm::kExprI64UConvertF64:
case wasm::kExprI64SConvertSatF32:
case wasm::kExprI64UConvertSatF32:
case wasm::kExprI64SConvertSatF64:
case wasm::kExprI64UConvertSatF64:
return jsgraph()->machine()->Is32()
? BuildCcallConvertFloat(input, position, opcode)
: BuildIntConvertFloat(input, position, opcode);
case wasm::kExprI64SConvertF64:
return BuildI64SConvertF64(input, position);
case wasm::kExprI64UConvertF32:
return BuildI64UConvertF32(input, position);
case wasm::kExprI64UConvertF64:
return BuildI64UConvertF64(input, position);
case wasm::kExprI32AsmjsLoadMem8S:
return BuildAsmjsLoadMem(MachineType::Int8(), input);
case wasm::kExprI32AsmjsLoadMem8U:
@ -1395,8 +1395,15 @@ MachineType IntConvertType(wasm::WasmOpcode opcode) {
case wasm::kExprI32UConvertSatF64:
return MachineType::Uint32();
case wasm::kExprI64SConvertF32:
case wasm::kExprI64SConvertF64:
case wasm::kExprI64SConvertSatF32:
case wasm::kExprI64SConvertSatF64:
return MachineType::Int64();
case wasm::kExprI64UConvertF32:
case wasm::kExprI64UConvertF64:
case wasm::kExprI64UConvertSatF32:
case wasm::kExprI64UConvertSatF64:
return MachineType::Uint64();
default:
UNREACHABLE();
}
@ -1407,14 +1414,20 @@ MachineType FloatConvertType(wasm::WasmOpcode opcode) {
case wasm::kExprI32SConvertF32:
case wasm::kExprI32UConvertF32:
case wasm::kExprI32SConvertSatF32:
case wasm::kExprI32UConvertSatF32:
case wasm::kExprI64SConvertF32:
case wasm::kExprI64UConvertF32:
case wasm::kExprI32UConvertSatF32:
case wasm::kExprI64SConvertSatF32:
case wasm::kExprI64UConvertSatF32:
return MachineType::Float32();
case wasm::kExprI32SConvertF64:
case wasm::kExprI32UConvertF64:
case wasm::kExprI64SConvertF64:
case wasm::kExprI64UConvertF64:
case wasm::kExprI32SConvertSatF64:
case wasm::kExprI32UConvertSatF64:
case wasm::kExprI64SConvertSatF64:
case wasm::kExprI64UConvertSatF64:
return MachineType::Float64();
default:
UNREACHABLE();
@ -1438,6 +1451,15 @@ const Operator* ConvertOp(WasmGraphBuilder* builder, wasm::WasmOpcode opcode) {
case wasm::kExprI64SConvertF32:
case wasm::kExprI64SConvertSatF32:
return builder->jsgraph()->machine()->TryTruncateFloat32ToInt64();
case wasm::kExprI64UConvertF32:
case wasm::kExprI64UConvertSatF32:
return builder->jsgraph()->machine()->TryTruncateFloat32ToUint64();
case wasm::kExprI64SConvertF64:
case wasm::kExprI64SConvertSatF64:
return builder->jsgraph()->machine()->TryTruncateFloat64ToInt64();
case wasm::kExprI64UConvertF64:
case wasm::kExprI64UConvertSatF64:
return builder->jsgraph()->machine()->TryTruncateFloat64ToUint64();
default:
UNREACHABLE();
}
@ -1469,12 +1491,18 @@ bool IsTrappingConvertOp(wasm::WasmOpcode opcode) {
case wasm::kExprI32SConvertF64:
case wasm::kExprI32UConvertF64:
case wasm::kExprI64SConvertF32:
case wasm::kExprI64UConvertF32:
case wasm::kExprI64SConvertF64:
case wasm::kExprI64UConvertF64:
return true;
case wasm::kExprI32SConvertSatF64:
case wasm::kExprI32UConvertSatF64:
case wasm::kExprI32SConvertSatF32:
case wasm::kExprI32UConvertSatF32:
case wasm::kExprI64SConvertSatF32:
case wasm::kExprI64UConvertSatF32:
case wasm::kExprI64SConvertSatF64:
case wasm::kExprI64UConvertSatF64:
return false;
default:
UNREACHABLE();
@ -1890,93 +1918,6 @@ Node* WasmGraphBuilder::BuildIntToFloatConversionInstruction(
return load;
}
Node* WasmGraphBuilder::BuildI64UConvertF32(Node* input,
wasm::WasmCodePosition position) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float32_to_uint64(jsgraph()->isolate()),
MachineRepresentation::kFloat32, MachineType::Int64(), position);
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat32ToUint64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc,
graph()->start());
Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc,
graph()->start());
ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position);
return result;
}
}
Node* WasmGraphBuilder::BuildI64SConvertF64(Node* input,
wasm::WasmCodePosition position) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float64_to_int64(jsgraph()->isolate()),
MachineRepresentation::kFloat64, MachineType::Int64(), position);
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat64ToInt64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc,
graph()->start());
Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc,
graph()->start());
ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position);
return result;
}
}
Node* WasmGraphBuilder::BuildI64UConvertF64(Node* input,
wasm::WasmCodePosition position) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float64_to_uint64(jsgraph()->isolate()),
MachineRepresentation::kFloat64, MachineType::Int64(), position);
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat64ToUint64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc,
graph()->start());
Node* overflow = graph()->NewNode(jsgraph()->common()->Projection(1), trunc,
graph()->start());
ZeroCheck64(wasm::kTrapFloatUnrepresentable, overflow, position);
return result;
}
}
// TODO(kschimpf) Remove this method once BuildI64UConvertF32,
// BuildI64SConvertF64, and BuildI64UConvertF64 have been moved over to use
// method BuildCcallConvertFloat() below.
Node* WasmGraphBuilder::BuildFloatToIntConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type, wasm::WasmCodePosition position) {
Node* stack_slot_param = graph()->NewNode(
jsgraph()->machine()->StackSlot(parameter_representation));
Node* stack_slot_result = graph()->NewNode(
jsgraph()->machine()->StackSlot(result_type.representation()));
const Operator* store_op = jsgraph()->machine()->Store(
StoreRepresentation(parameter_representation, kNoWriteBarrier));
*effect_ =
graph()->NewNode(store_op, stack_slot_param, jsgraph()->Int32Constant(0),
input, *effect_, *control_);
MachineSignature::Builder sig_builder(jsgraph()->zone(), 1, 2);
sig_builder.AddReturn(MachineType::Int32());
sig_builder.AddParam(MachineType::Pointer());
sig_builder.AddParam(MachineType::Pointer());
Node* function = graph()->NewNode(jsgraph()->common()->ExternalConstant(ref));
ZeroCheck32(wasm::kTrapFloatUnrepresentable,
BuildCCall(sig_builder.Build(), function, stack_slot_param,
stack_slot_result),
position);
const Operator* load_op = jsgraph()->machine()->Load(result_type);
Node* load =
graph()->NewNode(load_op, stack_slot_result, jsgraph()->Int32Constant(0),
*effect_, *control_);
*effect_ = load;
return load;
}
namespace {
ExternalReference convert_ccall_ref(WasmGraphBuilder* builder,
@ -1986,6 +1927,18 @@ ExternalReference convert_ccall_ref(WasmGraphBuilder* builder,
case wasm::kExprI64SConvertSatF32:
return ExternalReference::wasm_float32_to_int64(
builder->jsgraph()->isolate());
case wasm::kExprI64UConvertF32:
case wasm::kExprI64UConvertSatF32:
return ExternalReference::wasm_float32_to_uint64(
builder->jsgraph()->isolate());
case wasm::kExprI64SConvertF64:
case wasm::kExprI64SConvertSatF64:
return ExternalReference::wasm_float64_to_int64(
builder->jsgraph()->isolate());
case wasm::kExprI64UConvertF64:
case wasm::kExprI64UConvertSatF64:
return ExternalReference::wasm_float64_to_uint64(
builder->jsgraph()->isolate());
default:
UNREACHABLE();
}

View File

@ -547,15 +547,8 @@ class WasmGraphBuilder {
Node* BuildF64SConvertI64(Node* input);
Node* BuildF64UConvertI64(Node* input);
Node* BuildFloatToIntConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type, wasm::WasmCodePosition position);
Node* BuildCcallConvertFloat(Node* input, wasm::WasmCodePosition position,
wasm::WasmOpcode opcode);
Node* BuildI64UConvertF32(Node* input, wasm::WasmCodePosition position);
Node* BuildI64SConvertF64(Node* input, wasm::WasmCodePosition position);
Node* BuildI64UConvertF64(Node* input, wasm::WasmCodePosition position);
Node* BuildI32DivS(Node* left, Node* right, wasm::WasmCodePosition position);
Node* BuildI32RemS(Node* left, Node* right, wasm::WasmCodePosition position);

View File

@ -496,6 +496,17 @@ int64_t ExecuteI64SConvertF64(double a, TrapReason* trap) {
return output;
}
int64_t ExecuteI64SConvertSatF64(double a) {
TrapReason base_trap = kTrapCount;
int64_t val = ExecuteI64SConvertF64(a, &base_trap);
if (base_trap == kTrapCount) {
return val;
}
return std::isnan(a) ? 0
: (a < 0.0 ? std::numeric_limits<int64_t>::min()
: std::numeric_limits<int64_t>::max());
}
uint64_t ExecuteI64UConvertF32(float a, TrapReason* trap) {
uint64_t output;
if (!float32_to_uint64_wrapper(&a, &output)) {
@ -504,6 +515,17 @@ uint64_t ExecuteI64UConvertF32(float a, TrapReason* trap) {
return output;
}
uint64_t ExecuteI64UConvertSatF32(float a) {
TrapReason base_trap = kTrapCount;
uint64_t val = ExecuteI64UConvertF32(a, &base_trap);
if (base_trap == kTrapCount) {
return val;
}
return std::isnan(a) ? 0
: (a < 0.0 ? std::numeric_limits<uint64_t>::min()
: std::numeric_limits<uint64_t>::max());
}
uint64_t ExecuteI64UConvertF64(double a, TrapReason* trap) {
uint64_t output;
if (!float64_to_uint64_wrapper(&a, &output)) {
@ -512,6 +534,17 @@ uint64_t ExecuteI64UConvertF64(double a, TrapReason* trap) {
return output;
}
uint64_t ExecuteI64UConvertSatF64(double a) {
TrapReason base_trap = kTrapCount;
int64_t val = ExecuteI64UConvertF64(a, &base_trap);
if (base_trap == kTrapCount) {
return val;
}
return std::isnan(a) ? 0
: (a < 0.0 ? std::numeric_limits<uint64_t>::min()
: std::numeric_limits<uint64_t>::max());
}
inline int64_t ExecuteI64SConvertI32(int32_t a, TrapReason* trap) {
return static_cast<int64_t>(a);
}
@ -1576,10 +1609,18 @@ class ThreadImpl {
case kExprI32UConvertSatF64:
Push(WasmValue(ExecuteConvertSaturate<uint32_t>(Pop().to<double>())));
return true;
case kExprI64SConvertSatF32: {
case kExprI64SConvertSatF32:
Push(WasmValue(ExecuteI64SConvertSatF32(Pop().to<float>())));
return true;
}
case kExprI64UConvertSatF32:
Push(WasmValue(ExecuteI64UConvertSatF32(Pop().to<float>())));
return true;
case kExprI64SConvertSatF64:
Push(WasmValue(ExecuteI64SConvertSatF64(Pop().to<double>())));
return true;
case kExprI64UConvertSatF64:
Push(WasmValue(ExecuteI64UConvertSatF64(Pop().to<double>())));
return true;
default:
V8_Fatal(__FILE__, __LINE__, "Unknown or unimplemented opcode #%d:%s",
code->start[pc], OpcodeName(code->start[pc]));

View File

@ -104,7 +104,8 @@ const char* WasmOpcodes::OpcodeName(WasmOpcode opcode) {
// TODO(kschimpf): Simplify after filling in other saturating operations.
CASE_CONVERT_SAT_OP(Convert, I32, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I32, F64, "f64", "trunc")
CASE_I64_OP(SConvertSatF32, "trunc_s::sat/f32")
CASE_CONVERT_SAT_OP(Convert, I64, F32, "f32", "trunc")
CASE_CONVERT_SAT_OP(Convert, I64, F64, "f64", "trunc")
CASE_CONVERT_OP(Convert, I64, I32, "i32", "extend")
CASE_CONVERT_OP(Convert, F32, I32, "i32", "convert")

View File

@ -409,8 +409,10 @@ using WasmName = Vector<const char>;
V(I32UConvertSatF32, 0xfc01, i_f) \
V(I32SConvertSatF64, 0xfc02, i_d) \
V(I32UConvertSatF64, 0xfc03, i_d) \
V(I64SConvertSatF32, 0xfc04, l_f)
// TODO(kschimpf): Add remaining numeric opcodes.
V(I64SConvertSatF32, 0xfc04, l_f) \
V(I64UConvertSatF32, 0xfc05, l_f) \
V(I64SConvertSatF64, 0xfc06, l_d) \
V(I64UConvertSatF64, 0xfc07, l_d)
#define FOREACH_ATOMIC_OPCODE(V) \
V(I32AtomicLoad, 0xfe10, i_i) \

View File

@ -797,6 +797,27 @@ WASM_EXEC_TEST(I64SConvertF64) {
}
}
WASM_EXEC_TEST(I64SConvertSatF64) {
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
WasmRunner<int64_t, double> r(execution_mode);
BUILD(r, WASM_I64_SCONVERT_SAT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
int64_t expected;
if (*i < static_cast<double>(std::numeric_limits<int64_t>::max()) &&
*i >= static_cast<double>(std::numeric_limits<int64_t>::min())) {
expected = static_cast<int64_t>(*i);
} else if (std::isnan(*i)) {
expected = static_cast<int64_t>(0);
} else if (*i < 0.0) {
expected = std::numeric_limits<int64_t>::min();
} else {
expected = std::numeric_limits<int64_t>::max();
}
int64_t found = r.Call(*i);
CHECK_EQ(expected, found);
}
}
WASM_EXEC_TEST(I64UConvertF32) {
WasmRunner<uint64_t, float> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_F32(WASM_GET_LOCAL(0)));
@ -811,6 +832,27 @@ WASM_EXEC_TEST(I64UConvertF32) {
}
}
WASM_EXEC_TEST(I64UConvertSatF32) {
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
WasmRunner<int64_t, float> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_SAT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
uint64_t expected;
if (*i < static_cast<float>(std::numeric_limits<uint64_t>::max()) &&
*i > -1) {
expected = static_cast<uint64_t>(*i);
} else if (std::isnan(*i)) {
expected = static_cast<uint64_t>(0);
} else if (*i < 0.0) {
expected = std::numeric_limits<uint64_t>::min();
} else {
expected = std::numeric_limits<uint64_t>::max();
}
uint64_t found = r.Call(*i);
CHECK_EQ(expected, found);
}
}
WASM_EXEC_TEST(I64UConvertF64) {
WasmRunner<uint64_t, double> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_F64(WASM_GET_LOCAL(0)));
@ -825,6 +867,27 @@ WASM_EXEC_TEST(I64UConvertF64) {
}
}
WASM_EXEC_TEST(I64UConvertSatF64) {
EXPERIMENTAL_FLAG_SCOPE(sat_f2i_conversions);
WasmRunner<int64_t, double> r(execution_mode);
BUILD(r, WASM_I64_UCONVERT_SAT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
int64_t expected;
if (*i < static_cast<float>(std::numeric_limits<uint64_t>::max()) &&
*i > -1) {
expected = static_cast<uint64_t>(*i);
} else if (std::isnan(*i)) {
expected = static_cast<uint64_t>(0);
} else if (*i < 0.0) {
expected = std::numeric_limits<uint64_t>::min();
} else {
expected = std::numeric_limits<uint64_t>::max();
}
int64_t found = r.Call(*i);
CHECK_EQ(expected, found);
}
}
WASM_EXEC_TEST(CallI64Parameter) {
ValueType param_types[20];
for (int i = 0; i < 20; i++) param_types[i] = kWasmI64;

View File

@ -565,6 +565,9 @@ inline WasmOpcode LoadStoreOpcodeOf(MachineType type, bool store) {
#define WASM_I32_SCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI32SConvertSatF64)
#define WASM_I32_UCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI32UConvertSatF64)
#define WASM_I64_SCONVERT_SAT_F32(x) x, WASM_NUMERIC_OP(kExprI64SConvertSatF32)
#define WASM_I64_UCONVERT_SAT_F32(x) x, WASM_NUMERIC_OP(kExprI64UConvertSatF32)
#define WASM_I64_SCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI64SConvertSatF64)
#define WASM_I64_UCONVERT_SAT_F64(x) x, WASM_NUMERIC_OP(kExprI64UConvertSatF64)
//------------------------------------------------------------------------------
// Memory Operations.