Commit 9718d079 authored by Camillo Bruni's avatar Camillo Bruni Committed by Commit Bot

[CSA] Fix Array.prototype.reduceRight CAS_ASSERT

- Add typed IsHeapNumberPositive, IsNumberNonNegativeSafeInteger, IsInteger,
  IsSafeInteger and IsHeapNumberUint32 helpers on CodeStubAssembler
- Type NumberIsInteger and NumberIsSafeInteger builtin

Bug: chromium:847204, v8:6949
Change-Id: I27d3ab79bd17312c223209ed0b221c174024126e
Reviewed-on: https://chromium-review.googlesource.com/1087961
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#53707}
parent 2aa166a6
......@@ -426,28 +426,34 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
GotoIf(DoesntHaveInstanceType(o(), JS_ARRAY_TYPE), &not_js_array);
merged_length = LoadJSArrayLength(CAST(o()));
Goto(&has_length);
BIND(&not_js_array);
Node* len_property =
GetProperty(context(), o(), isolate()->factory()->length_string());
merged_length = ToLength_Inline(context(), len_property);
Goto(&has_length);
{
Node* len_property =
GetProperty(context(), o(), isolate()->factory()->length_string());
merged_length = ToLength_Inline(context(), len_property);
Goto(&has_length);
}
BIND(&has_length);
len_ = merged_length.value();
{
len_ = merged_length.value();
// 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
Label type_exception(this, Label::kDeferred);
Label done(this);
GotoIf(TaggedIsSmi(callbackfn()), &type_exception);
Branch(IsCallableMap(LoadMap(callbackfn())), &done, &type_exception);
// 5. If IsCallable(callbackfn) is false, throw a TypeError exception.
Label type_exception(this, Label::kDeferred);
Label done(this);
GotoIf(TaggedIsSmi(callbackfn()), &type_exception);
Branch(IsCallableMap(LoadMap(callbackfn())), &done, &type_exception);
BIND(&throw_null_undefined_exception);
ThrowTypeError(context(), MessageTemplate::kCalledOnNullOrUndefined, name);
BIND(&throw_null_undefined_exception);
ThrowTypeError(context(), MessageTemplate::kCalledOnNullOrUndefined,
name);
BIND(&type_exception);
ThrowTypeError(context(), MessageTemplate::kCalledNonCallable,
callbackfn());
BIND(&type_exception);
ThrowTypeError(context(), MessageTemplate::kCalledNonCallable,
callbackfn());
BIND(&done);
BIND(&done);
}
// 6. If thisArg was supplied, let T be thisArg; else let T be undefined.
// [Already done by the arguments adapter]
......@@ -552,6 +558,7 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
} else {
k_.Bind(NumberDec(len()));
}
CSA_ASSERT(this, IsSafeInteger(k()));
Node* instance_type = LoadInstanceType(LoadElements(typed_array));
Switch(instance_type, &unexpected_instance_type, instance_types.data(),
label_ptrs.data(), labels.size());
......@@ -593,10 +600,9 @@ Node* ArrayBuiltinsAssembler::FindProcessor(Node* k_value, Node* k) {
Label done_element(this, &to_);
// a. Let Pk be ToString(k).
// We never have to perform a ToString conversion as the above guards
// guarantee that we have a positive {k} which also is a valid array
// index in the range [0, 2^32-1).
CSA_ASSERT(this, IsNumberArrayIndex(k()));
// k() is guaranteed to be a positive integer, hence ToString is
// side-effect free and HasProperty/GetProperty do the conversion inline.
CSA_ASSERT(this, IsSafeInteger(k()));
if (missing_property_mode == MissingPropertyMode::kSkip) {
// b. Let kPresent be HasProperty(O, Pk).
......
......@@ -85,7 +85,7 @@ class ArrayBuiltinsAssembler : public BaseBuiltinsFromDSLAssembler {
TNode<Number> len() { return len_; }
Node* callbackfn() { return callbackfn_; }
Node* this_arg() { return this_arg_; }
Node* k() { return k_.value(); }
TNode<Number> k() { return CAST(k_.value()); }
Node* a() { return a_.value(); }
void ReturnFromBuiltin(Node* value);
......
......@@ -101,31 +101,8 @@ TF_BUILTIN(AllocateHeapNumber, CodeStubAssembler) {
// ES6 #sec-number.isinteger
TF_BUILTIN(NumberIsInteger, CodeStubAssembler) {
Node* number = Parameter(Descriptor::kNumber);
Label return_true(this), return_false(this);
// Check if {number} is a Smi.
GotoIf(TaggedIsSmi(number), &return_true);
// Check if {number} is a HeapNumber.
GotoIfNot(IsHeapNumber(number), &return_false);
// Load the actual value of {number}.
Node* number_value = LoadHeapNumberValue(number);
// Truncate the value of {number} to an integer (or an infinity).
Node* integer = Float64Trunc(number_value);
// Check if {number}s value matches the integer (ruling out the infinities).
Branch(Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)),
&return_true, &return_false);
BIND(&return_true);
Return(TrueConstant());
BIND(&return_false);
Return(FalseConstant());
TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
Return(SelectBooleanConstant(IsInteger(number)));
}
// ES6 #sec-number.isnan
......@@ -153,37 +130,8 @@ TF_BUILTIN(NumberIsNaN, CodeStubAssembler) {
// ES6 #sec-number.issafeinteger
TF_BUILTIN(NumberIsSafeInteger, CodeStubAssembler) {
Node* number = Parameter(Descriptor::kNumber);
Label return_true(this), return_false(this);
// Check if {number} is a Smi.
GotoIf(TaggedIsSmi(number), &return_true);
// Check if {number} is a HeapNumber.
GotoIfNot(IsHeapNumber(number), &return_false);
// Load the actual value of {number}.
Node* number_value = LoadHeapNumberValue(number);
// Truncate the value of {number} to an integer (or an infinity).
Node* integer = Float64Trunc(number_value);
// Check if {number}s value matches the integer (ruling out the infinities).
GotoIfNot(
Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)),
&return_false);
// Check if the {integer} value is in safe integer range.
Branch(Float64LessThanOrEqual(Float64Abs(integer),
Float64Constant(kMaxSafeInteger)),
&return_true, &return_false);
BIND(&return_true);
Return(TrueConstant());
BIND(&return_false);
Return(FalseConstant());
TNode<Object> number = CAST(Parameter(Descriptor::kNumber));
Return(SelectBooleanConstant(IsSafeInteger(number)));
}
// ES6 #sec-number.parsefloat
......
......@@ -5415,43 +5415,95 @@ TNode<BoolT> CodeStubAssembler::IsNumberNormalized(SloppyTNode<Number> number) {
}
TNode<BoolT> CodeStubAssembler::IsNumberPositive(SloppyTNode<Number> number) {
TNode<Float64T> float_zero = Float64Constant(0.);
return Select<BoolT>(TaggedIsSmi(number),
[=] { return TaggedIsPositiveSmi(number); },
[=] {
TNode<Float64T> v = LoadHeapNumberValue(CAST(number));
return Float64GreaterThanOrEqual(v, float_zero);
});
[=] { return IsHeapNumberPositive(CAST(number)); });
}
TNode<BoolT> CodeStubAssembler::IsNumberArrayIndex(SloppyTNode<Number> number) {
TVARIABLE(BoolT, var_result, Int32TrueConstant());
// TODO(cbruni): Use TNode<HeapNumber> instead of custom name.
TNode<BoolT> CodeStubAssembler::IsHeapNumberPositive(TNode<HeapNumber> number) {
TNode<Float64T> value = LoadHeapNumberValue(number);
TNode<Float64T> float_zero = Float64Constant(0.);
return Float64GreaterThanOrEqual(value, float_zero);
}
Label check_upper_bound(this), check_is_integer(this), out(this),
return_false(this);
TNode<BoolT> CodeStubAssembler::IsNumberNonNegativeSafeInteger(
TNode<Number> number) {
return Select<BoolT>(
// TODO(cbruni): Introduce TaggedIsNonNegateSmi to avoid confusion.
TaggedIsSmi(number), [=] { return TaggedIsPositiveSmi(number); },
[=] {
TNode<HeapNumber> heap_number = CAST(number);
return Select<BoolT>(IsInteger(heap_number),
[=] { return IsHeapNumberPositive(heap_number); },
[=] { return Int32FalseConstant(); });
});
}
GotoIfNumberGreaterThanOrEqual(number, NumberConstant(0), &check_upper_bound);
Goto(&return_false);
TNode<BoolT> CodeStubAssembler::IsSafeInteger(TNode<Object> number) {
return Select<BoolT>(
TaggedIsSmi(number), [=] { return Int32TrueConstant(); },
[=] {
return Select<BoolT>(
IsHeapNumber(CAST(number)),
[=] { return IsSafeInteger(UncheckedCast<HeapNumber>(number)); },
[=] { return Int32FalseConstant(); });
});
}
BIND(&check_upper_bound);
GotoIfNumberGreaterThanOrEqual(number, NumberConstant(kMaxUInt32),
&return_false);
Goto(&check_is_integer);
TNode<BoolT> CodeStubAssembler::IsSafeInteger(TNode<HeapNumber> number) {
// Load the actual value of {number}.
TNode<Float64T> number_value = LoadHeapNumberValue(number);
// Truncate the value of {number} to an integer (or an infinity).
TNode<Float64T> integer = Float64Trunc(number_value);
BIND(&check_is_integer);
GotoIf(TaggedIsSmi(number), &out);
// Check that the HeapNumber is a valid uint32
TNode<Float64T> value = LoadHeapNumberValue(CAST(number));
TNode<Uint32T> int_value = ChangeFloat64ToUint32(value);
GotoIf(Float64Equal(value, ChangeUint32ToFloat64(int_value)), &out);
Goto(&return_false);
return Select<BoolT>(
// Check if {number}s value matches the integer (ruling out the
// infinities).
Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0)),
[=] {
// Check if the {integer} value is in safe integer range.
return Float64LessThanOrEqual(Float64Abs(integer),
Float64Constant(kMaxSafeInteger));
},
[=] { return Int32FalseConstant(); });
}
BIND(&return_false);
var_result = Int32FalseConstant();
Goto(&out);
TNode<BoolT> CodeStubAssembler::IsInteger(TNode<Object> number) {
return Select<BoolT>(
TaggedIsSmi(number), [=] { return Int32TrueConstant(); },
[=] {
return Select<BoolT>(
IsHeapNumber(CAST(number)),
[=] { return IsInteger(UncheckedCast<HeapNumber>(number)); },
[=] { return Int32FalseConstant(); });
});
}
BIND(&out);
return var_result.value();
TNode<BoolT> CodeStubAssembler::IsInteger(TNode<HeapNumber> number) {
TNode<Float64T> number_value = LoadHeapNumberValue(number);
// Truncate the value of {number} to an integer (or an infinity).
TNode<Float64T> integer = Float64Trunc(number_value);
// Check if {number}s value matches the integer (ruling out the infinities).
return Float64Equal(Float64Sub(number_value, integer), Float64Constant(0.0));
}
TNode<BoolT> CodeStubAssembler::IsHeapNumberUint32(TNode<HeapNumber> number) {
// Check that the HeapNumber is a valid uint32
return Select<BoolT>(
IsHeapNumberPositive(number),
[=] {
TNode<Float64T> value = LoadHeapNumberValue(number);
TNode<Uint32T> int_value = ChangeFloat64ToUint32(value);
return Float64Equal(value, ChangeUint32ToFloat64(int_value));
},
[=] { return Int32FalseConstant(); });
}
TNode<BoolT> CodeStubAssembler::IsNumberArrayIndex(TNode<Number> number) {
return Select<BoolT>(TaggedIsSmi(number),
[=] { return TaggedIsPositiveSmi(number); },
[=] { return IsHeapNumberUint32(CAST(number)); });
}
Node* CodeStubAssembler::FixedArraySizeDoesntFitInNewSpace(Node* element_count,
......
......@@ -1693,9 +1693,25 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
// within Smi range.
TNode<BoolT> IsNumberNormalized(SloppyTNode<Number> number);
TNode<BoolT> IsNumberPositive(SloppyTNode<Number> number);
TNode<BoolT> IsHeapNumberPositive(TNode<HeapNumber> number);
// True iff {number} is non-negative and less or equal than 2**53-1.
TNode<BoolT> IsNumberNonNegativeSafeInteger(TNode<Number> number);
// True iff {number} represents an integer value.
TNode<BoolT> IsInteger(TNode<Object> number);
TNode<BoolT> IsInteger(TNode<HeapNumber> number);
// True iff abs({number}) <= 2**53 -1
TNode<BoolT> IsSafeInteger(TNode<Object> number);
TNode<BoolT> IsSafeInteger(TNode<HeapNumber> number);
// True iff {number} represents a valid uint32t value.
TNode<BoolT> IsHeapNumberUint32(TNode<HeapNumber> number);
// True iff {number} is a positive number and a valid array index in the range
// [0, 2^32-1).
TNode<BoolT> IsNumberArrayIndex(SloppyTNode<Number> number);
TNode<BoolT> IsNumberArrayIndex(TNode<Number> number);
Node* FixedArraySizeDoesntFitInNewSpace(
Node* element_count, int base_size = FixedArray::kHeaderSize,
......
......@@ -2922,8 +2922,9 @@ TEST(IsNumberArrayIndex) {
CodeAssemblerTester asm_tester(isolate, kNumParams);
{
CodeStubAssembler m(asm_tester.state());
m.Return(m.SmiFromInt32(
m.UncheckedCast<Int32T>(m.IsNumberArrayIndex(m.Parameter(0)))));
TNode<Number> number = m.CAST(m.Parameter(0));
m.Return(
m.SmiFromInt32(m.UncheckedCast<Int32T>(m.IsNumberArrayIndex(number))));
}
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
......
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