[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}
This commit is contained in:
ahaas 2016-03-14 03:13:49 -07:00 committed by Commit bot
parent 5fceb67049
commit d57d14b978
9 changed files with 351 additions and 105 deletions

View File

@ -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(

View File

@ -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);

View File

@ -893,48 +893,24 @@ Node* WasmGraphBuilder::Unop(wasm::WasmOpcode opcode, Node* input) {
op = m->RoundUint64ToFloat64();
break;
// kExprI64SConvertF32:
// kExprI64SConvertF64:
// kExprI64UConvertF32:
// kExprI64UConvertF64:
case wasm::kExprI64SConvertF32: {
return BuildI64SConvertF32(input);
}
// kExprI64SConvertF64:
case wasm::kExprI64SConvertF64: {
return BuildI64SConvertF64(input);
}
// kExprI64UConvertF32:
case wasm::kExprI64UConvertF32: {
return BuildI64UConvertF32(input);
}
// kExprI64UConvertF64:
case wasm::kExprI64UConvertF64: {
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::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;
}
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;
}
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;
}
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;
}
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.

View File

@ -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*));

View File

@ -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(),

View File

@ -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

View File

@ -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

View File

@ -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.

View File

@ -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) {