[wasm] Detect unrepresentability in the float32-to-int32 conversion correctly on arm.
In the current implementation of wasm an unrepresentable input of the float32-to-int32 conversion is detected by first truncating the input, then converting the truncated input to int32 and back to float32, and then checking whether the result is the same as the truncated input. This input check does not work on arm and arm64 for an input of (INT32_MAX + 1) because on these platforms the float32-to-int32 conversion results in INT32_MAX if the input is greater than INT32_MAX. When INT32_MAX is converted back to float32, then the result is (INT32_MAX + 1) again because INT32_MAX cannot be represented precisely as float32, and rounding-to-nearest results in (INT32_MAX + 1). Since (INT32_MAX + 1) equals the truncated input value, the input appears to be representable. With the changes in this CL, the result of the float32-to-int32 conversion is incremented by 1 if the original result was INT32_MAX. Thereby the detection of unrepresenable inputs in wasm works. Note that since INT32_MAX cannot be represented precisely in float32, it can also never be a valid result of the float32-to-int32 conversion. @v8-mips-ports, can you do a similar implementation for mips? R=titzer@chromium.org, Rodolph.Perfetta@arm.com Review-Url: https://codereview.chromium.org/2105313002 Cr-Commit-Position: refs/heads/master@{#37448}
This commit is contained in:
parent
9b4f098943
commit
de369129d2
@ -1134,6 +1134,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
SwVfpRegister scratch = kScratchDoubleReg.low();
|
||||
__ vcvt_s32_f32(scratch, i.InputFloatRegister(0));
|
||||
__ vmov(i.OutputRegister(), scratch);
|
||||
// Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
|
||||
// because INT32_MIN allows easier out-of-bounds detection.
|
||||
__ cmn(i.OutputRegister(), Operand(1));
|
||||
__ mov(i.OutputRegister(), Operand(INT32_MIN), SBit::LeaveCC, vs);
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
@ -1141,6 +1145,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
SwVfpRegister scratch = kScratchDoubleReg.low();
|
||||
__ vcvt_u32_f32(scratch, i.InputFloatRegister(0));
|
||||
__ vmov(i.OutputRegister(), scratch);
|
||||
// Avoid UINT32_MAX as an overflow indicator and use 0 instead,
|
||||
// because 0 allows easier out-of-bounds detection.
|
||||
__ cmn(i.OutputRegister(), Operand(1));
|
||||
__ adc(i.OutputRegister(), i.OutputRegister(), Operand::Zero());
|
||||
DCHECK_EQ(LeaveCC, i.OutputSBit());
|
||||
break;
|
||||
}
|
||||
|
@ -1358,12 +1358,21 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleArchInstruction(
|
||||
break;
|
||||
case kArm64Float32ToInt32:
|
||||
__ Fcvtzs(i.OutputRegister32(), i.InputFloat32Register(0));
|
||||
// Avoid INT32_MAX as an overflow indicator and use INT32_MIN instead,
|
||||
// because INT32_MIN allows easier out-of-bounds detection.
|
||||
__ Cmn(i.OutputRegister32(), 1);
|
||||
__ Csinc(i.OutputRegister32(), i.OutputRegister32(), i.OutputRegister32(),
|
||||
vc);
|
||||
break;
|
||||
case kArm64Float64ToInt32:
|
||||
__ Fcvtzs(i.OutputRegister32(), i.InputDoubleRegister(0));
|
||||
break;
|
||||
case kArm64Float32ToUint32:
|
||||
__ Fcvtzu(i.OutputRegister32(), i.InputFloat32Register(0));
|
||||
// Avoid UINT32_MAX as an overflow indicator and use 0 instead,
|
||||
// because 0 allows easier out-of-bounds detection.
|
||||
__ Cmn(i.OutputRegister32(), 1);
|
||||
__ Adc(i.OutputRegister32(), i.OutputRegister32(), Operand(0));
|
||||
break;
|
||||
case kArm64Float64ToUint32:
|
||||
__ Fcvtzu(i.OutputRegister32(), i.InputDoubleRegister(0));
|
||||
|
@ -498,7 +498,14 @@ static inline double ExecuteF64Sqrt(double a, TrapReason* trap) {
|
||||
}
|
||||
|
||||
static int32_t ExecuteI32SConvertF32(float a, TrapReason* trap) {
|
||||
if (a < static_cast<float>(INT32_MAX) && a >= static_cast<float>(INT32_MIN)) {
|
||||
// The upper bound is (INT32_MAX + 1), which is the lowest float-representable
|
||||
// number above INT32_MAX which cannot be represented as int32.
|
||||
float upper_bound = 2147483648.0f;
|
||||
// We use INT32_MIN as a lower bound because (INT32_MIN - 1) is not
|
||||
// representable as float, and no number between (INT32_MIN - 1) and INT32_MIN
|
||||
// is.
|
||||
float lower_bound = static_cast<float>(INT32_MIN);
|
||||
if (a < upper_bound && a >= lower_bound) {
|
||||
return static_cast<int32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
@ -506,8 +513,13 @@ static int32_t ExecuteI32SConvertF32(float a, TrapReason* trap) {
|
||||
}
|
||||
|
||||
static int32_t ExecuteI32SConvertF64(double a, TrapReason* trap) {
|
||||
if (a < (static_cast<double>(INT32_MAX) + 1.0) &&
|
||||
a > (static_cast<double>(INT32_MIN) - 1.0)) {
|
||||
// The upper bound is (INT32_MAX + 1), which is the lowest double-
|
||||
// representable number above INT32_MAX which cannot be represented as int32.
|
||||
double upper_bound = 2147483648.0;
|
||||
// The lower bound is (INT32_MIN - 1), which is the greatest double-
|
||||
// representable number below INT32_MIN which cannot be represented as int32.
|
||||
double lower_bound = -2147483649.0;
|
||||
if (a < upper_bound && a > lower_bound) {
|
||||
return static_cast<int32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
@ -515,7 +527,12 @@ static int32_t ExecuteI32SConvertF64(double a, TrapReason* trap) {
|
||||
}
|
||||
|
||||
static uint32_t ExecuteI32UConvertF32(float a, TrapReason* trap) {
|
||||
if (a < (static_cast<float>(UINT32_MAX) + 1.0) && a > -1) {
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// float-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0f;
|
||||
double lower_bound = -1.0f;
|
||||
if (a < upper_bound && a > lower_bound) {
|
||||
return static_cast<uint32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
@ -523,7 +540,12 @@ static uint32_t ExecuteI32UConvertF32(float a, TrapReason* trap) {
|
||||
}
|
||||
|
||||
static uint32_t ExecuteI32UConvertF64(double a, TrapReason* trap) {
|
||||
if (a < (static_cast<float>(UINT32_MAX) + 1.0) && a > -1) {
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// double-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0;
|
||||
double lower_bound = -1.0;
|
||||
if (a < upper_bound && a > lower_bound) {
|
||||
return static_cast<uint32_t>(a);
|
||||
}
|
||||
*trap = kTrapFloatUnrepresentable;
|
||||
|
@ -4018,9 +4018,15 @@ TEST(RunChangeUint32ToFloat64) {
|
||||
TEST(RunTruncateFloat32ToInt32) {
|
||||
BufferedRawMachineAssemblerTester<int32_t> m(MachineType::Float32());
|
||||
m.Return(m.TruncateFloat32ToInt32(m.Parameter(0)));
|
||||
// The upper bound is (INT32_MAX + 1), which is the lowest float-representable
|
||||
// number above INT32_MAX which cannot be represented as int32.
|
||||
float upper_bound = 2147483648.0f;
|
||||
// We use INT32_MIN as a lower bound because (INT32_MIN - 1) is not
|
||||
// representable as float, and no number between (INT32_MIN - 1) and INT32_MIN
|
||||
// is.
|
||||
float lower_bound = static_cast<float>(INT32_MIN);
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
if (*i <= static_cast<float>(std::numeric_limits<int32_t>::max()) &&
|
||||
*i >= static_cast<float>(std::numeric_limits<int32_t>::min())) {
|
||||
if (*i < upper_bound && *i >= lower_bound) {
|
||||
CHECK_FLOAT_EQ(static_cast<int32_t>(*i), m.Call(*i));
|
||||
}
|
||||
}
|
||||
@ -4030,23 +4036,20 @@ TEST(RunTruncateFloat32ToInt32) {
|
||||
TEST(RunTruncateFloat32ToUint32) {
|
||||
BufferedRawMachineAssemblerTester<uint32_t> m(MachineType::Float32());
|
||||
m.Return(m.TruncateFloat32ToUint32(m.Parameter(0)));
|
||||
{
|
||||
FOR_UINT32_INPUTS(i) {
|
||||
volatile float input = static_cast<float>(*i);
|
||||
// This condition on 'input' is required because
|
||||
// static_cast<float>(std::numeric_limits<uint32_t>::max()) results in a
|
||||
// value outside uint32 range.
|
||||
if (input < static_cast<float>(std::numeric_limits<uint32_t>::max())) {
|
||||
CHECK_EQ(static_cast<uint32_t>(input), m.Call(input));
|
||||
}
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// float-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0f;
|
||||
double lower_bound = -1.0f;
|
||||
FOR_UINT32_INPUTS(i) {
|
||||
volatile float input = static_cast<float>(*i);
|
||||
if (input < upper_bound) {
|
||||
CHECK_EQ(static_cast<uint32_t>(input), m.Call(input));
|
||||
}
|
||||
}
|
||||
{
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
if (*i <= static_cast<float>(std::numeric_limits<uint32_t>::max()) &&
|
||||
*i >= static_cast<float>(std::numeric_limits<uint32_t>::min())) {
|
||||
CHECK_FLOAT_EQ(static_cast<uint32_t>(*i), m.Call(*i));
|
||||
}
|
||||
FOR_FLOAT32_INPUTS(j) {
|
||||
if ((*j < upper_bound) && (*j > lower_bound)) {
|
||||
CHECK_FLOAT_EQ(static_cast<uint32_t>(*j), m.Call(*j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,8 @@ class ValueHelper {
|
||||
-4.66622e+11f,
|
||||
-2.22581e+11f,
|
||||
-1.45381e+10f,
|
||||
-2147483649.0f, // INT32_MIN - 1
|
||||
-2147483648.0f, // INT32_MIN
|
||||
-1.3956e+09f,
|
||||
-1.32951e+09f,
|
||||
-1.30721e+09f,
|
||||
@ -151,6 +153,8 @@ class ValueHelper {
|
||||
20309.0f,
|
||||
797056.0f,
|
||||
1.77219e+09f,
|
||||
2147483648.0f, // INT32_MAX + 1
|
||||
4294967296.0f, // UINT32_MAX + 1
|
||||
1.51116e+11f,
|
||||
4.18193e+13f,
|
||||
3.59167e+16f,
|
||||
|
@ -2731,9 +2731,15 @@ WASM_EXEC_TEST(I32SConvertF32) {
|
||||
WasmRunner<int32_t> r(execution_mode, MachineType::Float32());
|
||||
BUILD(r, WASM_I32_SCONVERT_F32(WASM_GET_LOCAL(0)));
|
||||
|
||||
// The upper bound is (INT32_MAX + 1), which is the lowest float-representable
|
||||
// number above INT32_MAX which cannot be represented as int32.
|
||||
float upper_bound = 2147483648.0f;
|
||||
// We use INT32_MIN as a lower bound because (INT32_MIN - 1) is not
|
||||
// representable as float, and no number between (INT32_MIN - 1) and INT32_MIN
|
||||
// is.
|
||||
float lower_bound = static_cast<float>(INT32_MIN);
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
if (*i < static_cast<float>(INT32_MAX) &&
|
||||
*i >= static_cast<float>(INT32_MIN)) {
|
||||
if (*i < upper_bound && *i >= lower_bound) {
|
||||
CHECK_EQ(static_cast<int32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
@ -2745,10 +2751,15 @@ WASM_EXEC_TEST(I32SConvertF64) {
|
||||
WasmRunner<int32_t> r(execution_mode, MachineType::Float64());
|
||||
BUILD(r, WASM_I32_SCONVERT_F64(WASM_GET_LOCAL(0)));
|
||||
|
||||
// The upper bound is (INT32_MAX + 1), which is the lowest double-
|
||||
// representable number above INT32_MAX which cannot be represented as int32.
|
||||
double upper_bound = 2147483648.0;
|
||||
// The lower bound is (INT32_MIN - 1), which is the greatest double-
|
||||
// representable number below INT32_MIN which cannot be represented as int32.
|
||||
double lower_bound = -2147483649.0;
|
||||
FOR_FLOAT64_INPUTS(i) {
|
||||
if (*i < (static_cast<double>(INT32_MAX) + 1.0) &&
|
||||
*i > (static_cast<double>(INT32_MIN) - 1.0)) {
|
||||
CHECK_EQ(static_cast<int64_t>(*i), r.Call(*i));
|
||||
if (*i<upper_bound&& * i> lower_bound) {
|
||||
CHECK_EQ(static_cast<int32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
}
|
||||
@ -2758,9 +2769,13 @@ WASM_EXEC_TEST(I32SConvertF64) {
|
||||
WASM_EXEC_TEST(I32UConvertF32) {
|
||||
WasmRunner<uint32_t> r(execution_mode, MachineType::Float32());
|
||||
BUILD(r, WASM_I32_UCONVERT_F32(WASM_GET_LOCAL(0)));
|
||||
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// float-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0f;
|
||||
double lower_bound = -1.0f;
|
||||
FOR_FLOAT32_INPUTS(i) {
|
||||
if (*i < (static_cast<float>(UINT32_MAX) + 1.0) && *i > -1) {
|
||||
if (*i<upper_bound&& * i> lower_bound) {
|
||||
CHECK_EQ(static_cast<uint32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
@ -2771,9 +2786,13 @@ WASM_EXEC_TEST(I32UConvertF32) {
|
||||
WASM_EXEC_TEST(I32UConvertF64) {
|
||||
WasmRunner<uint32_t> r(execution_mode, MachineType::Float64());
|
||||
BUILD(r, WASM_I32_UCONVERT_F64(WASM_GET_LOCAL(0)));
|
||||
|
||||
// The upper bound is (UINT32_MAX + 1), which is the lowest
|
||||
// double-representable number above UINT32_MAX which cannot be represented as
|
||||
// uint32.
|
||||
double upper_bound = 4294967296.0;
|
||||
double lower_bound = -1.0;
|
||||
FOR_FLOAT64_INPUTS(i) {
|
||||
if (*i < (static_cast<float>(UINT32_MAX) + 1.0) && *i > -1) {
|
||||
if (*i<upper_bound&& * i> lower_bound) {
|
||||
CHECK_EQ(static_cast<uint32_t>(*i), r.Call(*i));
|
||||
} else {
|
||||
CHECK_TRAP32(r.Call(*i));
|
||||
|
Loading…
Reference in New Issue
Block a user