Commit 7ac19fe5 authored by bmeurer's avatar bmeurer Committed by Commit bot

[builtins] Migrate Number predicates and make them optimizable.

Migrate the isNaN, isFinite, Number.isFinite, Number.isInteger,
Number.isSafeInteger and Number.isNaN predicates to TurboFan
builtins and make them optimizable (for certain input types) in
JavaScript callees being optimized by TurboFan. That means both
the baseline and the optimized version is now always at maximum,
consistent performance. Especially TurboFan suffered from poor
baseline (and optimized) performance because it cannot play the
same weird tricks that Crankshaft plays for %_IsSmi.

This also adds a bunch of new tests to properly cover the use
of the Harmony predicates in optimized code.

R=franzih@chromium.org
BUG=v8:5049,v8:5267

Review-Url: https://codereview.chromium.org/2313073002
Cr-Commit-Position: refs/heads/master@{#39242}
parent d06495b1
......@@ -1305,6 +1305,15 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
// Install i18n fallback functions.
SimpleInstallFunction(prototype, "toLocaleString",
Builtins::kNumberPrototypeToLocaleString, 0, false);
// Install the Number functions.
SimpleInstallFunction(number_fun, "isFinite", Builtins::kNumberIsFinite, 1,
true);
SimpleInstallFunction(number_fun, "isInteger", Builtins::kNumberIsInteger,
1, true);
SimpleInstallFunction(number_fun, "isNaN", Builtins::kNumberIsNaN, 1, true);
SimpleInstallFunction(number_fun, "isSafeInteger",
Builtins::kNumberIsSafeInteger, 1, true);
}
{ // --- B o o l e a n ---
......@@ -3145,6 +3154,14 @@ bool Genesis::InstallNatives(GlobalContextType context_type) {
native_context()->set_global_eval_fun(*eval);
}
// Install Global.isFinite
SimpleInstallFunction(global_object, "isFinite", Builtins::kGlobalIsFinite, 1,
true, kGlobalIsFinite);
// Install Global.isNaN
SimpleInstallFunction(global_object, "isNaN", Builtins::kGlobalIsNaN, 1, true,
kGlobalIsNaN);
// Install Array.prototype.concat
{
Handle<JSFunction> array_constructor(native_context()->array_function());
......
......@@ -5,6 +5,7 @@
#include "src/builtins/builtins.h"
#include "src/builtins/builtins-utils.h"
#include "src/code-factory.h"
#include "src/compiler.h"
#include "src/uri.h"
......@@ -99,5 +100,113 @@ BUILTIN(GlobalEval) {
Execution::Call(isolate, function, target_global_proxy, 0, nullptr));
}
// ES6 section 18.2.2 isFinite ( number )
void Builtins::Generate_GlobalIsFinite(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(4);
Label return_true(assembler), return_false(assembler);
// We might need to loop once for ToNumber conversion.
Variable var_num(assembler, MachineRepresentation::kTagged);
Label loop(assembler, &var_num);
var_num.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {num} value.
Node* num = var_num.value();
// Check if {num} is a Smi or a HeapObject.
assembler->GotoIf(assembler->WordIsSmi(num), &return_true);
// Check if {num} is a HeapNumber.
Label if_numisheapnumber(assembler),
if_numisnotheapnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(assembler->LoadMap(num),
assembler->HeapNumberMapConstant()),
&if_numisheapnumber, &if_numisnotheapnumber);
assembler->Bind(&if_numisheapnumber);
{
// Check if {num} contains a finite, non-NaN value.
Node* num_value = assembler->LoadHeapNumberValue(num);
assembler->BranchIfFloat64IsNaN(
assembler->Float64Sub(num_value, num_value), &return_false,
&return_true);
}
assembler->Bind(&if_numisnotheapnumber);
{
// Need to convert {num} to a Number first.
Callable callable = CodeFactory::NonNumberToNumber(assembler->isolate());
var_num.Bind(assembler->CallStub(callable, context, num));
assembler->Goto(&loop);
}
}
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
// ES6 section 18.2.3 isNaN ( number )
void Builtins::Generate_GlobalIsNaN(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(4);
Label return_true(assembler), return_false(assembler);
// We might need to loop once for ToNumber conversion.
Variable var_num(assembler, MachineRepresentation::kTagged);
Label loop(assembler, &var_num);
var_num.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {num} value.
Node* num = var_num.value();
// Check if {num} is a Smi or a HeapObject.
assembler->GotoIf(assembler->WordIsSmi(num), &return_false);
// Check if {num} is a HeapNumber.
Label if_numisheapnumber(assembler),
if_numisnotheapnumber(assembler, Label::kDeferred);
assembler->Branch(assembler->WordEqual(assembler->LoadMap(num),
assembler->HeapNumberMapConstant()),
&if_numisheapnumber, &if_numisnotheapnumber);
assembler->Bind(&if_numisheapnumber);
{
// Check if {num} contains a NaN.
Node* num_value = assembler->LoadHeapNumberValue(num);
assembler->BranchIfFloat64IsNaN(num_value, &return_true, &return_false);
}
assembler->Bind(&if_numisnotheapnumber);
{
// Need to convert {num} to a Number first.
Callable callable = CodeFactory::NonNumberToNumber(assembler->isolate());
var_num.Bind(assembler->CallStub(callable, context, num));
assembler->Goto(&loop);
}
}
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
} // namespace internal
} // namespace v8
......@@ -11,6 +11,144 @@ namespace internal {
// -----------------------------------------------------------------------------
// ES6 section 20.1 Number Objects
// ES6 section 20.1.2.2 Number.isFinite ( number )
void Builtins::Generate_NumberIsFinite(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* number = assembler->Parameter(1);
Label return_true(assembler), return_false(assembler);
// Check if {number} is a Smi.
assembler->GotoIf(assembler->WordIsSmi(number), &return_true);
// Check if {number} is a HeapNumber.
assembler->GotoUnless(
assembler->WordEqual(assembler->LoadMap(number),
assembler->HeapNumberMapConstant()),
&return_false);
// Check if {number} contains a finite, non-NaN value.
Node* number_value = assembler->LoadHeapNumberValue(number);
assembler->BranchIfFloat64IsNaN(
assembler->Float64Sub(number_value, number_value), &return_false,
&return_true);
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
// ES6 section 20.1.2.3 Number.isInteger ( number )
void Builtins::Generate_NumberIsInteger(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* number = assembler->Parameter(1);
Label return_true(assembler), return_false(assembler);
// Check if {number} is a Smi.
assembler->GotoIf(assembler->WordIsSmi(number), &return_true);
// Check if {number} is a HeapNumber.
assembler->GotoUnless(
assembler->WordEqual(assembler->LoadMap(number),
assembler->HeapNumberMapConstant()),
&return_false);
// Load the actual value of {number}.
Node* number_value = assembler->LoadHeapNumberValue(number);
// Truncate the value of {number} to an integer (or an infinity).
Node* integer = assembler->Float64Trunc(number_value);
// Check if {number}s value matches the integer (ruling out the infinities).
assembler->BranchIfFloat64Equal(assembler->Float64Sub(number_value, integer),
assembler->Float64Constant(0.0), &return_true,
&return_false);
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
// ES6 section 20.1.2.4 Number.isNaN ( number )
void Builtins::Generate_NumberIsNaN(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* number = assembler->Parameter(1);
Label return_true(assembler), return_false(assembler);
// Check if {number} is a Smi.
assembler->GotoIf(assembler->WordIsSmi(number), &return_false);
// Check if {number} is a HeapNumber.
assembler->GotoUnless(
assembler->WordEqual(assembler->LoadMap(number),
assembler->HeapNumberMapConstant()),
&return_false);
// Check if {number} contains a NaN value.
Node* number_value = assembler->LoadHeapNumberValue(number);
assembler->BranchIfFloat64IsNaN(number_value, &return_true, &return_false);
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
// ES6 section 20.1.2.5 Number.isSafeInteger ( number )
void Builtins::Generate_NumberIsSafeInteger(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* number = assembler->Parameter(1);
Label return_true(assembler), return_false(assembler);
// Check if {number} is a Smi.
assembler->GotoIf(assembler->WordIsSmi(number), &return_true);
// Check if {number} is a HeapNumber.
assembler->GotoUnless(
assembler->WordEqual(assembler->LoadMap(number),
assembler->HeapNumberMapConstant()),
&return_false);
// Load the actual value of {number}.
Node* number_value = assembler->LoadHeapNumberValue(number);
// Truncate the value of {number} to an integer (or an infinity).
Node* integer = assembler->Float64Trunc(number_value);
// Check if {number}s value matches the integer (ruling out the infinities).
assembler->GotoUnless(
assembler->Float64Equal(assembler->Float64Sub(number_value, integer),
assembler->Float64Constant(0.0)),
&return_false);
// Check if the {integer} value is in safe integer range.
assembler->BranchIfFloat64LessThanOrEqual(
assembler->Float64Abs(integer),
assembler->Float64Constant(kMaxSafeInteger), &return_true, &return_false);
assembler->Bind(&return_true);
assembler->Return(assembler->BooleanConstant(true));
assembler->Bind(&return_false);
assembler->Return(assembler->BooleanConstant(false));
}
// ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
BUILTIN(NumberPrototypeToExponential) {
HandleScope scope(isolate);
......
......@@ -361,16 +361,18 @@ namespace internal {
TFJ(GeneratorPrototypeThrow, 2) \
CPP(AsyncFunctionConstructor) \
\
/* Encode and decode */ \
/* Global object */ \
CPP(GlobalDecodeURI) \
CPP(GlobalDecodeURIComponent) \
CPP(GlobalEncodeURI) \
CPP(GlobalEncodeURIComponent) \
CPP(GlobalEscape) \
CPP(GlobalUnescape) \
\
/* Eval */ \
CPP(GlobalEval) \
/* ES6 section 18.2.2 isFinite ( number ) */ \
TFJ(GlobalIsFinite, 2) \
/* ES6 section 18.2.3 isNaN ( number ) */ \
TFJ(GlobalIsNaN, 2) \
\
/* JSON */ \
CPP(JsonParse) \
......@@ -451,6 +453,14 @@ namespace internal {
ASM(NumberConstructor) \
/* ES6 section 20.1.1.1 Number ( [ value ] ) for the [[Construct]] case */ \
ASM(NumberConstructor_ConstructStub) \
/* ES6 section 20.1.2.2 Number.isFinite ( number ) */ \
TFJ(NumberIsFinite, 2) \
/* ES6 section 20.1.2.3 Number.isInteger ( number ) */ \
TFJ(NumberIsInteger, 2) \
/* ES6 section 20.1.2.4 Number.isNaN ( number ) */ \
TFJ(NumberIsNaN, 2) \
/* ES6 section 20.1.2.5 Number.isSafeInteger ( number ) */ \
TFJ(NumberIsSafeInteger, 2) \
CPP(NumberPrototypeToExponential) \
CPP(NumberPrototypeToFixed) \
CPP(NumberPrototypeToLocaleString) \
......
......@@ -375,6 +375,34 @@ Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) {
return NoChange();
}
// ES6 section 18.2.2 isFinite ( number )
Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::PlainPrimitive())) {
// isFinite(a:plain-primitive) -> NumberEqual(a', a')
// where a' = NumberSubtract(ToNumber(a), ToNumber(a))
Node* input = ToNumber(r.GetJSCallInput(0));
Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input);
Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff);
return Replace(value);
}
return NoChange();
}
// ES6 section 18.2.3 isNaN ( number )
Reduction JSBuiltinReducer::ReduceGlobalIsNaN(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::PlainPrimitive())) {
// isNaN(a:plain-primitive) -> BooleanNot(NumberEqual(a', a'))
// where a' = ToNumber(a)
Node* input = ToNumber(r.GetJSCallInput(0));
Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input);
Node* value = graph()->NewNode(simplified()->BooleanNot(), check);
return Replace(value);
}
return NoChange();
}
// ES6 section 20.2.2.1 Math.abs ( x )
Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
JSCallReduction r(node);
......@@ -789,6 +817,60 @@ Reduction JSBuiltinReducer::ReduceMathTrunc(Node* node) {
return NoChange();
}
// ES6 section 20.1.2.2 Number.isFinite ( number )
Reduction JSBuiltinReducer::ReduceNumberIsFinite(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number())) {
// Number.isFinite(a:number) -> NumberEqual(a', a')
// where a' = NumberSubtract(a, a)
Node* input = r.GetJSCallInput(0);
Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input);
Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff);
return Replace(value);
}
return NoChange();
}
// ES6 section 20.1.2.3 Number.isInteger ( number )
Reduction JSBuiltinReducer::ReduceNumberIsInteger(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number())) {
// Number.isInteger(x:number) -> NumberEqual(NumberSubtract(x, x'), #0)
// where x' = NumberTrunc(x)
Node* input = r.GetJSCallInput(0);
Node* trunc = graph()->NewNode(simplified()->NumberTrunc(), input);
Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, trunc);
Node* value = graph()->NewNode(simplified()->NumberEqual(), diff,
jsgraph()->ZeroConstant());
return Replace(value);
}
return NoChange();
}
// ES6 section 20.1.2.4 Number.isNaN ( number )
Reduction JSBuiltinReducer::ReduceNumberIsNaN(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(Type::Number())) {
// Number.isNaN(a:number) -> BooleanNot(NumberEqual(a, a))
Node* input = r.GetJSCallInput(0);
Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input);
Node* value = graph()->NewNode(simplified()->BooleanNot(), check);
return Replace(value);
}
return NoChange();
}
// ES6 section 20.1.2.5 Number.isSafeInteger ( number )
Reduction JSBuiltinReducer::ReduceNumberIsSafeInteger(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(type_cache_.kSafeInteger)) {
// Number.isInteger(x:safe-integer) -> #true
Node* value = jsgraph()->TrueConstant();
return Replace(value);
}
return NoChange();
}
// ES6 section 20.1.2.13 Number.parseInt ( string, radix )
Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) {
JSCallReduction r(node);
......@@ -981,6 +1063,12 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceArrayPush(node);
case kDateGetTime:
return ReduceDateGetTime(node);
case kGlobalIsFinite:
reduction = ReduceGlobalIsFinite(node);
break;
case kGlobalIsNaN:
reduction = ReduceGlobalIsNaN(node);
break;
case kMathAbs:
reduction = ReduceMathAbs(node);
break;
......@@ -1080,6 +1168,18 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kMathTrunc:
reduction = ReduceMathTrunc(node);
break;
case kNumberIsFinite:
reduction = ReduceNumberIsFinite(node);
break;
case kNumberIsInteger:
reduction = ReduceNumberIsInteger(node);
break;
case kNumberIsNaN:
reduction = ReduceNumberIsNaN(node);
break;
case kNumberIsSafeInteger:
reduction = ReduceNumberIsSafeInteger(node);
break;
case kNumberParseInt:
reduction = ReduceNumberParseInt(node);
break;
......
......@@ -43,6 +43,8 @@ class JSBuiltinReducer final : public AdvancedReducer {
Reduction ReduceArrayPop(Node* node);
Reduction ReduceArrayPush(Node* node);
Reduction ReduceDateGetTime(Node* node);
Reduction ReduceGlobalIsFinite(Node* node);
Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceMathAbs(Node* node);
Reduction ReduceMathAcos(Node* node);
Reduction ReduceMathAcosh(Node* node);
......@@ -76,6 +78,10 @@ class JSBuiltinReducer final : public AdvancedReducer {
Reduction ReduceMathTan(Node* node);
Reduction ReduceMathTanh(Node* node);
Reduction ReduceMathTrunc(Node* node);
Reduction ReduceNumberIsFinite(Node* node);
Reduction ReduceNumberIsInteger(Node* node);
Reduction ReduceNumberIsNaN(Node* node);
Reduction ReduceNumberIsSafeInteger(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceStringCharAt(Node* node);
Reduction ReduceStringCharCodeAt(Node* node);
......
......@@ -1340,6 +1340,11 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
case kDateGetTime:
return t->cache_.kJSDateValueType;
// Number functions.
case kNumberIsFinite:
case kNumberIsInteger:
case kNumberIsNaN:
case kNumberIsSafeInteger:
return Type::Boolean();
case kNumberParseInt:
return t->cache_.kIntegerOrMinusZeroOrNaN;
case kNumberToString:
......@@ -1370,6 +1375,9 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
case kGlobalEscape:
case kGlobalUnescape:
return Type::String();
case kGlobalIsFinite:
case kGlobalIsNaN:
return Type::Boolean();
default:
break;
}
......
......@@ -16,7 +16,6 @@ var GlobalSet = global.Set;
var hashCodeSymbol = utils.ImportNow("hash_code_symbol");
var MathRandom;
var MapIterator;
var NumberIsNaN;
var SetIterator;
var speciesSymbol = utils.ImportNow("species_symbol");
var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
......@@ -24,7 +23,6 @@ var toStringTagSymbol = utils.ImportNow("to_string_tag_symbol");
utils.Import(function(from) {
MathRandom = from.MathRandom;
MapIterator = from.MapIterator;
NumberIsNaN = from.NumberIsNaN;
SetIterator = from.SetIterator;
});
......@@ -42,9 +40,9 @@ function SetFindEntry(table, numBuckets, key, hash) {
if (entry === NOT_FOUND) return entry;
var candidate = ORDERED_HASH_SET_KEY_AT(table, entry, numBuckets);
if (key === candidate) return entry;
var keyIsNaN = NumberIsNaN(key);
var keyIsNaN = NUMBER_IS_NAN(key);
while (true) {
if (keyIsNaN && NumberIsNaN(candidate)) {
if (keyIsNaN && NUMBER_IS_NAN(candidate)) {
return entry;
}
entry = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets);
......@@ -62,9 +60,9 @@ function MapFindEntry(table, numBuckets, key, hash) {
if (entry === NOT_FOUND) return entry;
var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
if (key === candidate) return entry;
var keyIsNaN = NumberIsNaN(key);
var keyIsNaN = NUMBER_IS_NAN(key);
while (true) {
if (keyIsNaN && NumberIsNaN(candidate)) {
if (keyIsNaN && NUMBER_IS_NAN(candidate)) {
return entry;
}
entry = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets);
......
......@@ -29,7 +29,6 @@ var InstallGetter = utils.InstallGetter;
var InternalArray = utils.InternalArray;
var InternalRegExpMatch;
var InternalRegExpReplace
var IsNaN;
var ObjectHasOwnProperty = utils.ImportNow("ObjectHasOwnProperty");
var OverrideFunction = utils.OverrideFunction;
var patternSymbol = utils.ImportNow("intl_pattern_symbol");
......@@ -43,7 +42,6 @@ var StringSubstring;
utils.Import(function(from) {
ArrayJoin = from.ArrayJoin;
ArrayPush = from.ArrayPush;
IsNaN = from.IsNaN;
InternalRegExpMatch = from.InternalRegExpMatch;
InternalRegExpReplace = from.InternalRegExpReplace;
StringIndexOf = from.StringIndexOf;
......@@ -2250,7 +2248,8 @@ function toLocaleDateTime(date, locales, options, required, defaults, service) {
throw %make_type_error(kMethodInvokedOnWrongType, "Date");
}
if (IsNaN(date)) return 'Invalid Date';
var dateValue = TO_NUMBER(date);
if (NUMBER_IS_NAN(dateValue)) return 'Invalid Date';
var internalOptions = toDateTimeOptions(options, required, defaults);
......
......@@ -191,14 +191,12 @@ function PostNatives(utils) {
"GlobalPromise",
"IntlParseDate",
"IntlParseNumber",
"IsNaN",
"MapEntries",
"MapIterator",
"MapIteratorNext",
"MaxSimple",
"MinSimple",
"NewPromiseCapability",
"NumberIsInteger",
"PerformPromiseThen",
"PromiseCastResolved",
"PromiseThen",
......
......@@ -34,7 +34,6 @@ var InnerArraySome;
var InnerArraySort;
var InnerArrayToLocaleString;
var InternalArray = utils.InternalArray;
var IsNaN;
var MaxSimple;
var MinSimple;
var PackedArrayReverse;
......@@ -83,7 +82,6 @@ utils.Import(function(from) {
InnerArraySome = from.InnerArraySome;
InnerArraySort = from.InnerArraySort;
InnerArrayToLocaleString = from.InnerArrayToLocaleString;
IsNaN = from.IsNaN;
MaxSimple = from.MaxSimple;
MinSimple = from.MinSimple;
PackedArrayReverse = from.PackedArrayReverse;
......@@ -544,9 +542,9 @@ function TypedArrayComparefn(x, y) {
return -1;
} else if (x > y) {
return 1;
} else if (IsNaN(x) && IsNaN(y)) {
return IsNaN(y) ? 0 : 1;
} else if (IsNaN(x)) {
} else if (NUMBER_IS_NAN(x) && NUMBER_IS_NAN(y)) {
return NUMBER_IS_NAN(y) ? 0 : 1;
} else if (NUMBER_IS_NAN(x)) {
return 1;
}
return 0;
......
......@@ -18,20 +18,6 @@ var ObjectToString = utils.ImportNow("object_to_string");
// ----------------------------------------------------------------------------
// ES6 18.2.3 isNaN(number)
function GlobalIsNaN(number) {
number = TO_NUMBER(number);
return NUMBER_IS_NAN(number);
}
// ES6 18.2.2 isFinite(number)
function GlobalIsFinite(number) {
number = TO_NUMBER(number);
return NUMBER_IS_FINITE(number);
}
// ES6 18.2.5 parseInt(string, radix)
function GlobalParseInt(string, radix) {
if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
......@@ -91,8 +77,6 @@ utils.InstallConstants(global, [
// Set up non-enumerable function on the global object.
utils.InstallFunctions(global, DONT_ENUM, [
"isNaN", GlobalIsNaN,
"isFinite", GlobalIsFinite,
"parseInt", GlobalParseInt,
"parseFloat", GlobalParseFloat,
]);
......@@ -207,38 +191,6 @@ utils.InstallFunctions(GlobalObject, DONT_ENUM, [
// ----------------------------------------------------------------------------
// Number
// Harmony isFinite.
function NumberIsFinite(number) {
return IS_NUMBER(number) && NUMBER_IS_FINITE(number);
}
// Harmony isInteger
function NumberIsInteger(number) {
return NumberIsFinite(number) && TO_INTEGER(number) == number;
}
// Harmony isNaN.
function NumberIsNaN(number) {
return IS_NUMBER(number) && NUMBER_IS_NAN(number);
}
// Harmony isSafeInteger
function NumberIsSafeInteger(number) {
if (NumberIsFinite(number)) {
var integral = TO_INTEGER(number);
if (integral == number) {
return -kMaxSafeInteger <= integral && integral <= kMaxSafeInteger;
}
}
return false;
}
// ----------------------------------------------------------------------------
utils.InstallConstants(GlobalNumber, [
// ECMA-262 section 15.7.3.1.
"MAX_VALUE", 1.7976931348623157e+308,
......@@ -260,15 +212,10 @@ utils.InstallConstants(GlobalNumber, [
// Harmony Number constructor additions
utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
"isFinite", NumberIsFinite,
"isInteger", NumberIsInteger,
"isNaN", NumberIsNaN,
"isSafeInteger", NumberIsSafeInteger,
"parseInt", GlobalParseInt,
"parseFloat", GlobalParseFloat
]);
%SetForceInlineFlag(NumberIsNaN);
// ----------------------------------------------------------------------------
......@@ -295,9 +242,6 @@ function GetIterator(obj, method) {
utils.Export(function(to) {
to.GetIterator = GetIterator;
to.GetMethod = GetMethod;
to.IsNaN = GlobalIsNaN;
to.NumberIsNaN = NumberIsNaN;
to.NumberIsInteger = NumberIsInteger;
to.ObjectHasOwnProperty = GlobalObject.prototype.hasOwnProperty;
});
......
......@@ -6951,6 +6951,10 @@ class Script: public Struct {
V(Math, clz32, MathClz32) \
V(Math, fround, MathFround) \
V(Math, trunc, MathTrunc) \
V(Number, isFinite, NumberIsFinite) \
V(Number, isInteger, NumberIsInteger) \
V(Number, isNaN, NumberIsNaN) \
V(Number, isSafeInteger, NumberIsSafeInteger) \
V(Number, parseInt, NumberParseInt) \
V(Number.prototype, toString, NumberToString)
......@@ -6979,6 +6983,8 @@ enum BuiltinFunctionId {
kGlobalEncodeURIComponent,
kGlobalEscape,
kGlobalUnescape,
kGlobalIsFinite,
kGlobalIsNaN,
kTypedArrayByteLength,
kTypedArrayByteOffset,
kTypedArrayLength,
......
......@@ -815,7 +815,7 @@ TEST(SnapshotDataBlobWithWarmup) {
// Running the warmup script has effect on whether functions are
// pre-compiled, but does not pollute the context.
CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("Number.isFinite"));
CHECK(!IsCompiled("Number.parseInt"));
CHECK(CompileRun("Math.random")->IsFunction());
}
isolate->Dispose();
......@@ -825,8 +825,8 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
DisableTurbofan();
const char* source =
"function f() { return Math.abs(1); }\n"
"function g() { return Number.isFinite(1); }\n"
"Number.isNaN(1);"
"function g() { return Number.parseInt(1); }\n"
"Number.parseFloat(1);"
"var a = 5";
const char* warmup = "a = f()";
......@@ -850,8 +850,8 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
CHECK(IsCompiled("f"));
CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("g"));
CHECK(!IsCompiled("Number.isFinite"));
CHECK(!IsCompiled("Number.isNaN"));
CHECK(!IsCompiled("Number.parseInt"));
CHECK(!IsCompiled("Number.parseFloat"));
CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
}
isolate->Dispose();
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function test(f) {
assertTrue(f(0));
assertTrue(f(Number.MIN_VALUE));
assertTrue(f(Number.MAX_VALUE));
assertTrue(f(Number.MIN_SAFE_INTEGER));
assertTrue(f(Number.MIN_SAFE_INTEGER - 13));
assertTrue(f(Number.MAX_SAFE_INTEGER));
assertTrue(f(Number.MAX_SAFE_INTEGER + 23));
assertFalse(f(Number.NaN));
assertFalse(f(Number.POSITIVE_INFINITY));
assertFalse(f(Number.NEGATIVE_INFINITY));
assertFalse(f(1 / 0));
assertFalse(f(-1 / 0));
}
function f(x) {
return Number.isFinite(+x);
}
test(f);
test(f);
%OptimizeFunctionOnNextCall(f);
test(f);
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function test(f) {
assertTrue(f(0));
assertFalse(f(Number.MIN_VALUE));
assertTrue(f(Number.MAX_VALUE));
assertTrue(f(Number.MIN_SAFE_INTEGER));
assertTrue(f(Number.MIN_SAFE_INTEGER - 13));
assertTrue(f(Number.MAX_SAFE_INTEGER));
assertTrue(f(Number.MAX_SAFE_INTEGER + 23));
assertFalse(f(Number.NaN));
assertFalse(f(Number.POSITIVE_INFINITY));
assertFalse(f(Number.NEGATIVE_INFINITY));
assertFalse(f(1 / 0));
assertFalse(f(-1 / 0));
assertFalse(f(Number.EPSILON));
}
function f(x) {
return Number.isInteger(+x);
}
test(f);
test(f);
%OptimizeFunctionOnNextCall(f);
test(f);
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function test(f) {
assertFalse(f(0));
assertFalse(f(Number.MIN_VALUE));
assertFalse(f(Number.MAX_VALUE));
assertFalse(f(Number.MIN_SAFE_INTEGER - 13));
assertFalse(f(Number.MAX_SAFE_INTEGER + 23));
assertTrue(f(Number.NaN));
assertFalse(f(Number.POSITIVE_INFINITY));
assertFalse(f(Number.NEGATIVE_INFINITY));
assertFalse(f(Number.EPSILON));
assertFalse(f(1 / 0));
assertFalse(f(-1 / 0));
}
function f(x) {
return Number.isNaN(+x);
}
test(f);
test(f);
%OptimizeFunctionOnNextCall(f);
test(f);
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function test(f) {
assertTrue(f(0));
assertTrue(f(Number.MIN_SAFE_INTEGER));
assertFalse(f(Number.MIN_SAFE_INTEGER - 13));
assertTrue(f(Number.MIN_SAFE_INTEGER + 13));
assertTrue(f(Number.MAX_SAFE_INTEGER));
assertFalse(f(Number.MAX_SAFE_INTEGER + 23));
assertTrue(f(Number.MAX_SAFE_INTEGER - 23));
assertFalse(f(Number.MIN_VALUE));
assertFalse(f(Number.MAX_VALUE));
assertFalse(f(Number.NaN));
assertFalse(f(Number.POSITIVE_INFINITY));
assertFalse(f(Number.NEGATIVE_INFINITY));
assertFalse(f(1 / 0));
assertFalse(f(-1 / 0));
assertFalse(f(Number.EPSILON));
var near_upper = Math.pow(2, 52);
assertTrue(f(near_upper));
assertFalse(f(2 * near_upper));
assertTrue(f(2 * near_upper - 1));
assertTrue(f(2 * near_upper - 2));
assertFalse(f(2 * near_upper + 1));
assertFalse(f(2 * near_upper + 2));
assertFalse(f(2 * near_upper + 7));
var near_lower = -near_upper;
assertTrue(f(near_lower));
assertFalse(f(2 * near_lower));
assertTrue(f(2 * near_lower + 1));
assertTrue(f(2 * near_lower + 2));
assertFalse(f(2 * near_lower - 1));
assertFalse(f(2 * near_lower - 2));
assertFalse(f(2 * near_lower - 7));
}
function f(x) {
return Number.isSafeInteger(+x);
}
test(f);
test(f);
%OptimizeFunctionOnNextCall(f);
test(f);
......@@ -38,6 +38,15 @@ class JSBuiltinReducerTest : public TypedGraphTest {
return reducer.Reduce(node);
}
Node* GlobalFunction(const char* name) {
Handle<JSFunction> f = Handle<JSFunction>::cast(
Object::GetProperty(
isolate()->global_object(),
isolate()->factory()->NewStringFromAsciiChecked(name))
.ToHandleChecked());
return HeapConstant(f);
}
Node* MathFunction(const char* name) {
Handle<Object> m =
JSObject::GetProperty(isolate()->global_object(),
......@@ -100,6 +109,91 @@ Type* const kNumberTypes[] = {
} // namespace
// -----------------------------------------------------------------------------
// isFinite
TEST_F(JSBuiltinReducerTest, GlobalIsFiniteWithNumber) {
Node* function = GlobalFunction("isFinite");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberEqual(IsNumberSubtract(p0, p0),
IsNumberSubtract(p0, p0)));
}
}
TEST_F(JSBuiltinReducerTest, GlobalIsFiniteWithPlainPrimitive) {
Node* function = GlobalFunction("isFinite");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
Node* p0 = Parameter(Type::PlainPrimitive(), 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsNumberEqual(IsNumberSubtract(IsPlainPrimitiveToNumber(p0),
IsPlainPrimitiveToNumber(p0)),
IsNumberSubtract(IsPlainPrimitiveToNumber(p0),
IsPlainPrimitiveToNumber(p0))));
}
// -----------------------------------------------------------------------------
// isNaN
TEST_F(JSBuiltinReducerTest, GlobalIsNaNWithNumber) {
Node* function = GlobalFunction("isNaN");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsNumberEqual(p0, p0)));
}
}
TEST_F(JSBuiltinReducerTest, GlobalIsNaNWithPlainPrimitive) {
Node* function = GlobalFunction("isNaN");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
Node* p0 = Parameter(Type::PlainPrimitive(), 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsNumberEqual(IsPlainPrimitiveToNumber(p0),
IsPlainPrimitiveToNumber(p0))));
}
// -----------------------------------------------------------------------------
// Math.abs
......@@ -1314,6 +1408,97 @@ TEST_F(JSBuiltinReducerTest, MathTruncWithPlainPrimitive) {
EXPECT_THAT(r.replacement(), IsNumberTrunc(IsPlainPrimitiveToNumber(p0)));
}
// -----------------------------------------------------------------------------
// Number.isFinite
TEST_F(JSBuiltinReducerTest, NumberIsFiniteWithNumber) {
Node* function = NumberFunction("isFinite");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberEqual(IsNumberSubtract(p0, p0),
IsNumberSubtract(p0, p0)));
}
}
// -----------------------------------------------------------------------------
// Number.isInteger
TEST_F(JSBuiltinReducerTest, NumberIsIntegerWithNumber) {
Node* function = NumberFunction("isInteger");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsNumberEqual(IsNumberSubtract(p0, IsNumberTrunc(p0)),
IsNumberConstant(0.0)));
}
}
// -----------------------------------------------------------------------------
// Number.isNaN
TEST_F(JSBuiltinReducerTest, NumberIsNaNWithNumber) {
Node* function = NumberFunction("isNaN");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kNumberTypes) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsBooleanNot(IsNumberEqual(p0, p0)));
}
}
// -----------------------------------------------------------------------------
// Number.isSafeInteger
TEST_F(JSBuiltinReducerTest, NumberIsSafeIntegerWithIntegral32) {
Node* function = NumberFunction("isSafeInteger");
Node* effect = graph()->start();
Node* control = graph()->start();
Node* context = UndefinedConstant();
Node* frame_state = graph()->start();
TRACED_FOREACH(Type*, t0, kIntegral32Types) {
Node* p0 = Parameter(t0, 0);
Node* call = graph()->NewNode(javascript()->CallFunction(3), function,
UndefinedConstant(), p0, context, frame_state,
effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
}
// -----------------------------------------------------------------------------
// Number.parseInt
......
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