[csa] Fix CSA::ToUint32 rounding for negative HeapNumbers
The spec requires truncation while ToUint32 originally rounded down. This also adds a bunch of test cases to check edge case behavior. BUG=v8:6212 Review-Url: https://codereview.chromium.org/2805783003 Cr-Commit-Position: refs/heads/master@{#44487}
This commit is contained in:
parent
1be5279bcc
commit
52a53da5a4
@ -4097,6 +4097,7 @@ Node* CodeStubAssembler::ToNumber(Node* context, Node* input) {
|
||||
return var_result.value();
|
||||
}
|
||||
|
||||
// ES#sec-touint32
|
||||
Node* CodeStubAssembler::ToUint32(Node* context, Node* input) {
|
||||
Node* const float_zero = Float64Constant(0.0);
|
||||
Node* const float_two_32 = Float64Constant(static_cast<double>(1ULL << 32));
|
||||
@ -4174,10 +4175,12 @@ Node* CodeStubAssembler::ToUint32(Node* context, Node* input) {
|
||||
BIND(&next);
|
||||
}
|
||||
|
||||
// Return floor({input}) mod 2^32 (assuming mod semantics that always return
|
||||
// positive results).
|
||||
// * Let int be the mathematical value that is the same sign as number and
|
||||
// whose magnitude is floor(abs(number)).
|
||||
// * Let int32bit be int modulo 2^32.
|
||||
// * Return int32bit.
|
||||
{
|
||||
Node* x = Float64Floor(value);
|
||||
Node* x = Float64Trunc(value);
|
||||
x = Float64Mod(x, float_two_32);
|
||||
x = Float64Add(x, float_two_32);
|
||||
x = Float64Mod(x, float_two_32);
|
||||
|
@ -74,6 +74,15 @@ MaybeHandle<Object> FunctionTester::Call(Handle<Object> a, Handle<Object> b,
|
||||
return Execution::Call(isolate, function, undefined(), 4, args);
|
||||
}
|
||||
|
||||
void FunctionTester::CheckThrows(Handle<Object> a) {
|
||||
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
|
||||
MaybeHandle<Object> no_result = Call(a);
|
||||
CHECK(isolate->has_pending_exception());
|
||||
CHECK(try_catch.HasCaught());
|
||||
CHECK(no_result.is_null());
|
||||
isolate->OptionalRescheduleException(true);
|
||||
}
|
||||
|
||||
void FunctionTester::CheckThrows(Handle<Object> a, Handle<Object> b) {
|
||||
TryCatch try_catch(reinterpret_cast<v8::Isolate*>(isolate));
|
||||
MaybeHandle<Object> no_result = Call(a, b);
|
||||
|
@ -40,6 +40,7 @@ class FunctionTester : public InitializedHandleScope {
|
||||
MaybeHandle<Object> Call(Handle<Object> a, Handle<Object> b, Handle<Object> c,
|
||||
Handle<Object> d);
|
||||
|
||||
void CheckThrows(Handle<Object> a);
|
||||
void CheckThrows(Handle<Object> a, Handle<Object> b);
|
||||
v8::Local<v8::Message> CheckThrowsReturnMessage(Handle<Object> a,
|
||||
Handle<Object> b);
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <cmath>
|
||||
|
||||
#include "src/api.h"
|
||||
#include "src/base/utils/random-number-generator.h"
|
||||
#include "src/builtins/builtins-promise-gen.h"
|
||||
@ -24,6 +26,109 @@ using compiler::CodeAssemblerLabel;
|
||||
using compiler::CodeAssemblerVariable;
|
||||
using compiler::CodeAssemblerVariableList;
|
||||
|
||||
namespace {
|
||||
|
||||
void CheckToUint32Result(uint32_t expected, Handle<Object> result) {
|
||||
const int64_t result_int64 = NumberToInt64(*result);
|
||||
const uint32_t result_uint32 = NumberToUint32(*result);
|
||||
|
||||
CHECK_EQ(static_cast<int64_t>(result_uint32), result_int64);
|
||||
CHECK_EQ(expected, result_uint32);
|
||||
|
||||
// Ensure that the result is normalized to a Smi, i.e. a HeapNumber is only
|
||||
// returned if the result is not within Smi range.
|
||||
const bool expected_fits_into_intptr =
|
||||
static_cast<int64_t>(expected) <=
|
||||
static_cast<int64_t>(std::numeric_limits<intptr_t>::max());
|
||||
if (expected_fits_into_intptr &&
|
||||
Smi::IsValid(static_cast<intptr_t>(expected))) {
|
||||
CHECK(result->IsSmi());
|
||||
} else {
|
||||
CHECK(result->IsHeapNumber());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TEST(ToUint32) {
|
||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||
Factory* factory = isolate->factory();
|
||||
|
||||
const int kNumParams = 1;
|
||||
CodeAssemblerTester data(isolate, kNumParams);
|
||||
CodeStubAssembler m(data.state());
|
||||
|
||||
const int kContextOffset = 2;
|
||||
Node* const context = m.Parameter(kNumParams + kContextOffset);
|
||||
Node* const input = m.Parameter(0);
|
||||
m.Return(m.ToUint32(context, input));
|
||||
|
||||
Handle<Code> code = data.GenerateCode();
|
||||
FunctionTester ft(code, kNumParams);
|
||||
|
||||
// clang-format off
|
||||
double inputs[] = {
|
||||
std::nan("-1"), std::nan("1"), std::nan("2"),
|
||||
-std::numeric_limits<double>::infinity(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-0.0, -0.001, -0.5, -0.999, -1.0,
|
||||
0.0, 0.001, 0.5, 0.999, 1.0,
|
||||
-2147483647.9, -2147483648.0, -2147483648.5, -2147483648.9, // SmiMin.
|
||||
2147483646.9, 2147483647.0, 2147483647.5, 2147483647.9, // SmiMax.
|
||||
-4294967295.9, -4294967296.0, -4294967296.5, -4294967297.0, // - 2^32.
|
||||
4294967295.9, 4294967296.0, 4294967296.5, 4294967297.0, // 2^32.
|
||||
};
|
||||
|
||||
uint32_t expectations[] = {
|
||||
0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0, 0, 0, 0, 4294967295,
|
||||
0, 0, 0, 0, 1,
|
||||
2147483649, 2147483648, 2147483648, 2147483648,
|
||||
2147483646, 2147483647, 2147483647, 2147483647,
|
||||
1, 0, 0, 4294967295,
|
||||
4294967295, 0, 0, 1,
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
STATIC_ASSERT(arraysize(inputs) == arraysize(expectations));
|
||||
|
||||
const int test_count = arraysize(inputs);
|
||||
for (int i = 0; i < test_count; i++) {
|
||||
Handle<Object> input_obj = factory->NewNumber(inputs[i]);
|
||||
Handle<HeapNumber> input_num;
|
||||
|
||||
// Check with Smi input.
|
||||
if (input_obj->IsSmi()) {
|
||||
Handle<Smi> input_smi = Handle<Smi>::cast(input_obj);
|
||||
Handle<Object> result = ft.Call(input_smi).ToHandleChecked();
|
||||
CheckToUint32Result(expectations[i], result);
|
||||
input_num = factory->NewHeapNumber(inputs[i]);
|
||||
} else {
|
||||
input_num = Handle<HeapNumber>::cast(input_obj);
|
||||
}
|
||||
|
||||
// Check with HeapNumber input.
|
||||
{
|
||||
CHECK(input_num->IsHeapNumber());
|
||||
Handle<Object> result = ft.Call(input_num).ToHandleChecked();
|
||||
CheckToUint32Result(expectations[i], result);
|
||||
}
|
||||
}
|
||||
|
||||
// A couple of final cases for ToNumber conversions.
|
||||
CheckToUint32Result(0, ft.Call(factory->undefined_value()).ToHandleChecked());
|
||||
CheckToUint32Result(0, ft.Call(factory->null_value()).ToHandleChecked());
|
||||
CheckToUint32Result(0, ft.Call(factory->false_value()).ToHandleChecked());
|
||||
CheckToUint32Result(1, ft.Call(factory->true_value()).ToHandleChecked());
|
||||
CheckToUint32Result(
|
||||
42,
|
||||
ft.Call(factory->NewStringFromAsciiChecked("0x2A")).ToHandleChecked());
|
||||
|
||||
ft.CheckThrows(factory->match_symbol());
|
||||
}
|
||||
|
||||
TEST(FixedArrayAccessSmiIndex) {
|
||||
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||
CodeAssemblerTester data(isolate);
|
||||
|
@ -167,7 +167,9 @@ assertEquals(2, counter);
|
||||
|
||||
// Check ToUint32 conversion of limit.
|
||||
assertArrayEquals(["a"], "a,b,c,d,e,f".split(/,/, -4294967295));
|
||||
assertArrayEquals(["a"], "a,b,c,d,e,f".split(/,/, -4294967294.5));
|
||||
assertArrayEquals(["a", "b"], "a,b,c,d,e,f".split(/,/, -4294967294.001));
|
||||
assertArrayEquals(["a", "b"], "a,b,c,d,e,f".split(/,/, -4294967294.5));
|
||||
assertArrayEquals(["a", "b"], "a,b,c,d,e,f".split(/,/, -4294967294.999));
|
||||
assertArrayEquals(["a", "b"], "a,b,c,d,e,f".split(/,/, -4294967294));
|
||||
assertArrayEquals(["a", "b", "c"], "a,b,c,d,e,f".split(/,/, -4294967293));
|
||||
assertArrayEquals(["a", "b", "c", "d"], "a,b,c,d,e,f".split(/,/, -4294967292));
|
||||
|
Loading…
Reference in New Issue
Block a user