Commit dcac1f14 authored by ahaas's avatar ahaas Committed by Commit bot

[wasm] Fixed float-to-int32 conversion to match the spec.

The new implementation detects if the input value is outside i32 range
and traps it that case.

The range check is done as follows:
The input value is converted to int32 and then back to float. If the
result is the same as the truncated input value, then the input value
is within int32 range.

R=bmeurer@chromium.org

Review URL: https://codereview.chromium.org/1537393003

Cr-Commit-Position: refs/heads/master@{#32984}
parent 108d5264
......@@ -683,11 +683,9 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) {
op = m->Float64Sqrt();
break;
case wasm::kExprI32SConvertF64:
op = m->ChangeFloat64ToInt32();
break;
return BuildI32SConvertF64(input);
case wasm::kExprI32UConvertF64:
op = m->ChangeFloat64ToUint32();
break;
return BuildI32UConvertF64(input);
case wasm::kExprF32ConvertF64:
op = m->TruncateFloat64ToFloat32();
break;
......@@ -703,20 +701,14 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) {
op = m->TruncateFloat64ToFloat32();
break;
case wasm::kExprF32UConvertI32:
op = m->ChangeUint32ToFloat64(); // TODO(titzer): two conversions
op = m->ChangeUint32ToFloat64();
input = graph()->NewNode(op, input);
op = m->TruncateFloat64ToFloat32();
break;
case wasm::kExprI32SConvertF32:
op = m->ChangeFloat32ToFloat64(); // TODO(titzer): two conversions
input = graph()->NewNode(op, input);
op = m->ChangeFloat64ToInt32();
break;
return BuildI32SConvertF32(input);
case wasm::kExprI32UConvertF32:
op = m->ChangeFloat32ToFloat64(); // TODO(titzer): two conversions
input = graph()->NewNode(op, input);
op = m->ChangeFloat64ToUint32();
break;
return BuildI32UConvertF32(input);
case wasm::kExprF64ConvertF32:
op = m->ChangeFloat32ToFloat64();
break;
......@@ -1125,6 +1117,74 @@ Node* WasmGraphBuilder::BuildF64Max(Node* left, Node* right) {
}
Node* WasmGraphBuilder::BuildI32SConvertF32(Node* input) {
MachineOperatorBuilder* m = jsgraph()->machine();
// Truncation of the input value is needed for the overflow check later.
Node* trunc = Unop(wasm::kExprF32Trunc, input);
// TODO(titzer): two conversions
Node* f64_trunc = graph()->NewNode(m->ChangeFloat32ToFloat64(), trunc);
Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), f64_trunc);
// Convert the result back to f64. If we end up at a different value than the
// truncated input value, then there has been an overflow and we trap.
Node* check = Unop(wasm::kExprF64SConvertI32, result);
Node* overflow = Binop(wasm::kExprF64Ne, f64_trunc, check);
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
return result;
}
Node* WasmGraphBuilder::BuildI32SConvertF64(Node* input) {
MachineOperatorBuilder* m = jsgraph()->machine();
// Truncation of the input value is needed for the overflow check later.
Node* trunc = Unop(wasm::kExprF64Trunc, input);
Node* result = graph()->NewNode(m->ChangeFloat64ToInt32(), trunc);
// Convert the result back to f64. If we end up at a different value than the
// truncated input value, then there has been an overflow and we trap.
Node* check = Unop(wasm::kExprF64SConvertI32, result);
Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
return result;
}
Node* WasmGraphBuilder::BuildI32UConvertF32(Node* input) {
MachineOperatorBuilder* m = jsgraph()->machine();
// Truncation of the input value is needed for the overflow check later.
Node* trunc = Unop(wasm::kExprF32Trunc, input);
// TODO(titzer): two conversions
Node* f64_trunc = graph()->NewNode(m->ChangeFloat32ToFloat64(), trunc);
Node* result = graph()->NewNode(m->ChangeFloat64ToUint32(), f64_trunc);
// Convert the result back to f64. If we end up at a different value than the
// truncated input value, then there has been an overflow and we trap.
Node* check = Unop(wasm::kExprF64UConvertI32, result);
Node* overflow = Binop(wasm::kExprF64Ne, f64_trunc, check);
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
return result;
}
Node* WasmGraphBuilder::BuildI32UConvertF64(Node* input) {
MachineOperatorBuilder* m = jsgraph()->machine();
// Truncation of the input value is needed for the overflow check later.
Node* trunc = Unop(wasm::kExprF64Trunc, input);
Node* result = graph()->NewNode(m->ChangeFloat64ToUint32(), trunc);
// Convert the result back to f64. If we end up at a different value than the
// truncated input value, then there has been an overflow and we trap.
Node* check = Unop(wasm::kExprF64UConvertI32, result);
Node* overflow = Binop(wasm::kExprF64Ne, trunc, check);
trap_->AddTrapIfTrue(kTrapFloatUnrepresentable, overflow);
return result;
}
Node* WasmGraphBuilder::BuildI32Ctz(Node* input) {
//// Implement the following code as TF graph.
// value = value | (value << 1);
......
......@@ -168,6 +168,10 @@ class WasmGraphBuilder {
Node* BuildF32Max(Node* left, Node* right);
Node* BuildF64Min(Node* left, Node* right);
Node* BuildF64Max(Node* left, Node* right);
Node* BuildI32SConvertF32(Node* input);
Node* BuildI32SConvertF64(Node* input);
Node* BuildI32UConvertF32(Node* input);
Node* BuildI32UConvertF64(Node* input);
Node* BuildI32Ctz(Node* input);
Node* BuildI32Popcnt(Node* input);
Node* BuildI64Ctz(Node* input);
......
......@@ -3441,6 +3441,64 @@ TEST(Run_Wasm_I64UConvertF64) {
#endif
TEST(Run_Wasm_I32SConvertF32) {
WasmRunner<int32_t> r(MachineType::Float32());
BUILD(r, WASM_I32_SCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(INT32_MAX) &&
*i >= static_cast<float>(INT32_MIN)) {
CHECK_EQ(static_cast<int32_t>(*i), r.Call(*i));
} else {
CHECK_TRAP32(r.Call(*i));
}
}
}
TEST(Run_Wasm_I32SConvertF64) {
WasmRunner<int32_t> r(MachineType::Float64());
BUILD(r, WASM_I32_SCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<double>(INT32_MAX) &&
*i >= static_cast<double>(INT32_MIN)) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP32(r.Call(*i));
}
}
}
TEST(Run_Wasm_I32UConvertF32) {
WasmRunner<uint32_t> r(MachineType::Float32());
BUILD(r, WASM_I32_UCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(UINT32_MAX) && *i > -1) {
CHECK_EQ(static_cast<uint32_t>(*i), r.Call(*i));
} else {
CHECK_TRAP32(r.Call(*i));
}
}
}
TEST(Run_Wasm_I32UConvertF64) {
WasmRunner<uint32_t> r(MachineType::Float64());
BUILD(r, WASM_I32_UCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<float>(UINT32_MAX) && *i > -1) {
CHECK_EQ(static_cast<uint32_t>(*i), r.Call(*i));
} else {
CHECK_TRAP32(r.Call(*i));
}
}
}
TEST(Run_Wasm_F64CopySign) {
WasmRunner<double> r(MachineType::Float64(), MachineType::Float64());
BUILD(r, WASM_F64_COPYSIGN(WASM_GET_LOCAL(0), WASM_GET_LOCAL(1)));
......
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