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

[wasm] Int64Lowering of I64XConvertFXX instructions.

On 32-bit systems I64XConvertFXX instructions are compiled to calls to
C functions. The TF node for the function call is already generated in
the wasm compiler, the lowering of the I64 parameter is done in the
Int64Lowering. We use the return value of the C function to determine
whether the conversion should trap or not.

R=titzer@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#34738}
parent 5fceb670
......@@ -1209,6 +1209,26 @@ ExternalReference ExternalReference::wasm_uint64_to_float64(Isolate* isolate) {
Redirect(isolate, FUNCTION_ADDR(wasm::uint64_to_float64_wrapper)));
}
ExternalReference ExternalReference::wasm_float32_to_int64(Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(wasm::float32_to_int64_wrapper)));
}
ExternalReference ExternalReference::wasm_float32_to_uint64(Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(wasm::float32_to_uint64_wrapper)));
}
ExternalReference ExternalReference::wasm_float64_to_int64(Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(wasm::float64_to_int64_wrapper)));
}
ExternalReference ExternalReference::wasm_float64_to_uint64(Isolate* isolate) {
return ExternalReference(
Redirect(isolate, FUNCTION_ADDR(wasm::float64_to_uint64_wrapper)));
}
static void f64_acos_wrapper(double* param) { *param = std::acos(*param); }
ExternalReference ExternalReference::f64_acos_wrapper_function(
......
......@@ -926,6 +926,10 @@ class ExternalReference BASE_EMBEDDED {
static ExternalReference wasm_uint64_to_float32(Isolate* isolate);
static ExternalReference wasm_int64_to_float64(Isolate* isolate);
static ExternalReference wasm_uint64_to_float64(Isolate* isolate);
static ExternalReference wasm_float32_to_int64(Isolate* isolate);
static ExternalReference wasm_float32_to_uint64(Isolate* isolate);
static ExternalReference wasm_float64_to_int64(Isolate* isolate);
static ExternalReference wasm_float64_to_uint64(Isolate* isolate);
static ExternalReference f64_acos_wrapper_function(Isolate* isolate);
static ExternalReference f64_asin_wrapper_function(Isolate* isolate);
......
......@@ -893,48 +893,24 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) {
op = m->RoundUint64ToFloat64();
break;
// kExprI64SConvertF32:
// kExprI64SConvertF64:
// kExprI64UConvertF32:
// kExprI64UConvertF64:
#if WASM_64
// Opcodes only supported on 64-bit platforms.
// TODO(titzer): query the machine operator builder here instead of #ifdef.
case wasm::kExprI64SConvertF32: {
Node* trunc = graph()->NewNode(m->TryTruncateFloat32ToInt64(), input);
Node* result =
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
return BuildI64SConvertF32(input);
}
// kExprI64SConvertF64:
case wasm::kExprI64SConvertF64: {
Node* trunc = graph()->NewNode(m->TryTruncateFloat64ToInt64(), input);
Node* result =
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
return BuildI64SConvertF64(input);
}
// kExprI64UConvertF32:
case wasm::kExprI64UConvertF32: {
Node* trunc = graph()->NewNode(m->TryTruncateFloat32ToUint64(), input);
Node* result =
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
return BuildI64UConvertF32(input);
}
// kExprI64UConvertF64:
case wasm::kExprI64UConvertF64: {
Node* trunc = graph()->NewNode(m->TryTruncateFloat64ToUint64(), input);
Node* result =
graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
return BuildI64UConvertF64(input);
}
#if WASM_64
// Opcodes only supported on 64-bit platforms.
// TODO(titzer): query the machine operator builder here instead of #ifdef.
case wasm::kExprF64ReinterpretI64:
op = m->BitcastInt64ToFloat64();
break;
......@@ -1655,26 +1631,27 @@ Node* WasmGraphBuilder::BuildCFuncInstruction(ExternalReference ref,
}
Node* WasmGraphBuilder::BuildF32SConvertI64(Node* input) {
return BuildConversionInstruction(
return BuildIntToFloatConversionInstruction(
input, ExternalReference::wasm_int64_to_float32(jsgraph()->isolate()),
MachineRepresentation::kWord64, MachineType::Float32());
}
Node* WasmGraphBuilder::BuildF32UConvertI64(Node* input) {
return BuildConversionInstruction(
return BuildIntToFloatConversionInstruction(
input, ExternalReference::wasm_uint64_to_float32(jsgraph()->isolate()),
MachineRepresentation::kWord64, MachineType::Float32());
}
Node* WasmGraphBuilder::BuildF64SConvertI64(Node* input) {
return BuildConversionInstruction(
return BuildIntToFloatConversionInstruction(
input, ExternalReference::wasm_int64_to_float64(jsgraph()->isolate()),
MachineRepresentation::kWord64, MachineType::Float64());
}
Node* WasmGraphBuilder::BuildF64UConvertI64(Node* input) {
return BuildConversionInstruction(
return BuildIntToFloatConversionInstruction(
input, ExternalReference::wasm_uint64_to_float64(jsgraph()->isolate()),
MachineRepresentation::kWord64, MachineType::Float64());
}
Node* WasmGraphBuilder::BuildConversionInstruction(
Node* WasmGraphBuilder::BuildIntToFloatConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type) {
......@@ -1701,6 +1678,99 @@ Node* WasmGraphBuilder::BuildConversionInstruction(
return load;
}
Node* WasmGraphBuilder::BuildI64SConvertF32(Node* input) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float32_to_int64(jsgraph()->isolate()),
MachineRepresentation::kFloat32, MachineType::Int64());
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat32ToInt64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
}
}
Node* WasmGraphBuilder::BuildI64UConvertF32(Node* input) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float32_to_uint64(jsgraph()->isolate()),
MachineRepresentation::kFloat32, MachineType::Int64());
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat32ToUint64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
}
}
Node* WasmGraphBuilder::BuildI64SConvertF64(Node* input) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float64_to_int64(jsgraph()->isolate()),
MachineRepresentation::kFloat64, MachineType::Int64());
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat64ToInt64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
}
}
Node* WasmGraphBuilder::BuildI64UConvertF64(Node* input) {
if (jsgraph()->machine()->Is32()) {
return BuildFloatToIntConversionInstruction(
input, ExternalReference::wasm_float64_to_uint64(jsgraph()->isolate()),
MachineRepresentation::kFloat64, MachineType::Int64());
} else {
Node* trunc = graph()->NewNode(
jsgraph()->machine()->TryTruncateFloat64ToUint64(), input);
Node* result = graph()->NewNode(jsgraph()->common()->Projection(0), trunc);
Node* overflow =
graph()->NewNode(jsgraph()->common()->Projection(1), trunc);
trap_->ZeroCheck64(kTrapFloatUnrepresentable, overflow);
return result;
}
}
Node* WasmGraphBuilder::BuildFloatToIntConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type) {
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));
Node* args[] = {function, stack_slot_param, stack_slot_result};
trap_->ZeroCheck32(kTrapFloatUnrepresentable,
BuildCCall(sig_builder.Build(), args));
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;
}
Node* WasmGraphBuilder::BuildCCall(MachineSignature* sig, Node** args) {
const size_t params = sig->parameter_count();
const size_t extra = 2; // effect and control inputs.
......
......@@ -207,7 +207,7 @@ class WasmGraphBuilder {
Node* BuildF64Atan2(Node* left, Node* right);
Node* BuildF64Mod(Node* left, Node* right);
Node* BuildConversionInstruction(
Node* BuildIntToFloatConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type);
......@@ -216,6 +216,15 @@ class WasmGraphBuilder {
Node* BuildF64SConvertI64(Node* input);
Node* BuildF64UConvertI64(Node* input);
Node* BuildFloatToIntConversionInstruction(
Node* input, ExternalReference ref,
MachineRepresentation parameter_representation,
const MachineType result_type);
Node* BuildI64SConvertF32(Node* input);
Node* BuildI64UConvertF32(Node* input);
Node* BuildI64SConvertF64(Node* input);
Node* BuildI64UConvertF64(Node* input);
Node** Realloc(Node** buffer, size_t count) {
Node** buf = Buffer(count);
if (buf != buffer) memcpy(buf, buffer, count * sizeof(Node*));
......
......@@ -123,6 +123,14 @@ ExternalReferenceTable::ExternalReferenceTable(Isolate* isolate) {
"wasm::int64_to_float64_wrapper");
Add(ExternalReference::wasm_uint64_to_float64(isolate).address(),
"wasm::uint64_to_float64_wrapper");
Add(ExternalReference::wasm_float32_to_int64(isolate).address(),
"wasm::float32_to_int64_wrapper");
Add(ExternalReference::wasm_float32_to_uint64(isolate).address(),
"wasm::float32_to_uint64_wrapper");
Add(ExternalReference::wasm_float64_to_int64(isolate).address(),
"wasm::float64_to_int64_wrapper");
Add(ExternalReference::wasm_float64_to_uint64(isolate).address(),
"wasm::float64_to_uint64_wrapper");
Add(ExternalReference::f64_acos_wrapper_function(isolate).address(),
"f64_acos_wrapper");
Add(ExternalReference::f64_asin_wrapper_function(isolate).address(),
......
......@@ -92,6 +92,53 @@ static void uint64_to_float64_wrapper(uint64_t* input, double* output) {
#endif
}
static int32_t float32_to_int64_wrapper(float* input, int64_t* output) {
// We use "<" here to check the upper bound because of rounding problems: With
// "<=" some inputs would be considered within int64 range which are actually
// not within int64 range.
if (*input >= static_cast<float>(std::numeric_limits<int64_t>::min()) &&
*input < static_cast<float>(std::numeric_limits<int64_t>::max())) {
*output = static_cast<int64_t>(*input);
return 1;
}
return 0;
}
static int32_t float32_to_uint64_wrapper(float* input, uint64_t* output) {
// We use "<" here to check the upper bound because of rounding problems: With
// "<=" some inputs would be considered within uint64 range which are actually
// not within uint64 range.
if (*input > -1.0 &&
*input < static_cast<float>(std::numeric_limits<uint64_t>::max())) {
*output = static_cast<uint64_t>(*input);
return 1;
}
return 0;
}
static int32_t float64_to_int64_wrapper(double* input, int64_t* output) {
// We use "<" here to check the upper bound because of rounding problems: With
// "<=" some inputs would be considered within int64 range which are actually
// not within int64 range.
if (*input >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
*input < static_cast<double>(std::numeric_limits<int64_t>::max())) {
*output = static_cast<int64_t>(*input);
return 1;
}
return 0;
}
static int32_t float64_to_uint64_wrapper(double* input, uint64_t* output) {
// We use "<" here to check the upper bound because of rounding problems: With
// "<=" some inputs would be considered within uint64 range which are actually
// not within uint64 range.
if (*input > -1.0 &&
*input < static_cast<double>(std::numeric_limits<uint64_t>::max())) {
*output = static_cast<uint64_t>(*input);
return 1;
}
return 0;
}
} // namespace wasm
} // namespace internal
} // namespace v8
......
......@@ -331,6 +331,99 @@ TEST(RunCallUint64ToFloat64) {
}
}
TEST(RunCallFloat32ToInt64) {
BufferedRawMachineAssemblerTester<int32_t> m;
ExternalReference ref = ExternalReference::wasm_float32_to_int64(m.isolate());
float input;
int64_t output;
Node* function = m.ExternalConstant(ref);
m.Return(m.CallCFunction2(
MachineType::Int32(), MachineType::Pointer(), MachineType::Pointer(),
function, m.PointerConstant(&input), m.PointerConstant(&output)));
FOR_FLOAT32_INPUTS(i) {
input = *i;
if (*i >= static_cast<float>(std::numeric_limits<int64_t>::min()) &&
*i < static_cast<float>(std::numeric_limits<int64_t>::max())) {
CHECK_EQ(1, m.Call());
CHECK_EQ(static_cast<int64_t>(*i), output);
} else {
CHECK_EQ(0, m.Call());
}
}
}
TEST(RunCallFloat32ToUint64) {
BufferedRawMachineAssemblerTester<int32_t> m;
ExternalReference ref =
ExternalReference::wasm_float32_to_uint64(m.isolate());
float input;
uint64_t output;
Node* function = m.ExternalConstant(ref);
m.Return(m.CallCFunction2(
MachineType::Int32(), MachineType::Pointer(), MachineType::Pointer(),
function, m.PointerConstant(&input), m.PointerConstant(&output)));
FOR_FLOAT32_INPUTS(i) {
input = *i;
if (*i > -1.0 &&
*i < static_cast<float>(std::numeric_limits<uint64_t>::max())) {
CHECK_EQ(1, m.Call());
CHECK_EQ(static_cast<uint64_t>(*i), output);
} else {
CHECK_EQ(0, m.Call());
}
}
}
TEST(RunCallFloat64ToInt64) {
BufferedRawMachineAssemblerTester<int32_t> m;
ExternalReference ref = ExternalReference::wasm_float64_to_int64(m.isolate());
double input;
int64_t output;
Node* function = m.ExternalConstant(ref);
m.Return(m.CallCFunction2(
MachineType::Int32(), MachineType::Pointer(), MachineType::Pointer(),
function, m.PointerConstant(&input), m.PointerConstant(&output)));
FOR_FLOAT64_INPUTS(i) {
input = *i;
if (*i >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
*i < static_cast<double>(std::numeric_limits<int64_t>::max())) {
CHECK_EQ(1, m.Call());
CHECK_EQ(static_cast<int64_t>(*i), output);
} else {
CHECK_EQ(0, m.Call());
}
}
}
TEST(RunCallFloat64ToUint64) {
BufferedRawMachineAssemblerTester<int32_t> m;
ExternalReference ref =
ExternalReference::wasm_float64_to_uint64(m.isolate());
double input;
uint64_t output;
Node* function = m.ExternalConstant(ref);
m.Return(m.CallCFunction2(
MachineType::Int32(), MachineType::Pointer(), MachineType::Pointer(),
function, m.PointerConstant(&input), m.PointerConstant(&output)));
FOR_FLOAT64_INPUTS(i) {
input = *i;
if (*i > -1.0 &&
*i < static_cast<double>(std::numeric_limits<uint64_t>::max())) {
CHECK_EQ(1, m.Call());
CHECK_EQ(static_cast<uint64_t>(*i), output);
} else {
CHECK_EQ(0, m.Call());
}
}
}
} // namespace compiler
} // namespace internal
} // namespace v8
......@@ -442,9 +442,64 @@ TEST(Run_Wasm_F64UConvertI64) {
}
}
// kExprI64SConvertF32:
TEST(Run_Wasm_I64SConvertF32) {
WasmRunner<int64_t> r(MachineType::Float32());
BUILD(r, WASM_I64_SCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(std::numeric_limits<int64_t>::max()) &&
*i >= static_cast<float>(std::numeric_limits<int64_t>::min())) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
// kExprI64SConvertF64:
TEST(Run_Wasm_I64SConvertF64) {
WasmRunner<int64_t> r(MachineType::Float64());
BUILD(r, WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<double>(std::numeric_limits<int64_t>::max()) &&
*i >= static_cast<double>(std::numeric_limits<int64_t>::min())) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
// kExprI64UConvertF32:
TEST(Run_Wasm_I64UConvertF32) {
WasmRunner<uint64_t> r(MachineType::Float32());
BUILD(r, WASM_I64_UCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(std::numeric_limits<uint64_t>::max()) &&
*i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
// kExprI64UConvertF64:
TEST(Run_Wasm_I64UConvertF64) {
WasmRunner<uint64_t> r(MachineType::Float64());
BUILD(r, WASM_I64_UCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<float>(std::numeric_limits<uint64_t>::max()) &&
*i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
TEST(Run_WasmCallI64Parameter) {
// Build the target function.
......
......@@ -3129,66 +3129,6 @@ TEST(Run_Wasm_F64Max_Snan) {
#endif
#if WASM_64
TEST(Run_Wasm_I64SConvertF32) {
WasmRunner<int64_t> r(MachineType::Float32());
BUILD(r, WASM_I64_SCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(INT64_MAX) &&
*i >= static_cast<float>(INT64_MIN)) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
TEST(Run_Wasm_I64SConvertF64) {
WasmRunner<int64_t> r(MachineType::Float64());
BUILD(r, WASM_I64_SCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<double>(INT64_MAX) &&
*i >= static_cast<double>(INT64_MIN)) {
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
TEST(Run_Wasm_I64UConvertF32) {
WasmRunner<uint64_t> r(MachineType::Float32());
BUILD(r, WASM_I64_UCONVERT_F32(WASM_GET_LOCAL(0)));
FOR_FLOAT32_INPUTS(i) {
if (*i < static_cast<float>(UINT64_MAX) && *i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
TEST(Run_Wasm_I64UConvertF64) {
WasmRunner<uint64_t> r(MachineType::Float64());
BUILD(r, WASM_I64_UCONVERT_F64(WASM_GET_LOCAL(0)));
FOR_FLOAT64_INPUTS(i) {
if (*i < static_cast<float>(UINT64_MAX) && *i > -1) {
CHECK_EQ(static_cast<uint64_t>(*i), r.Call(*i));
} else {
CHECK_TRAP64(r.Call(*i));
}
}
}
#endif
// TODO(titzer): Fix and re-enable.
#if 0
TEST(Run_Wasm_I32SConvertF32) {
......
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