Commit 1b4e0fca authored by bmeurer's avatar bmeurer Committed by Commit bot

[builtins] Migrate Number.parseInt to TurboFan builtin.

R=epertoso@chromium.org
BUG=v8:5049

Review-Url: https://codereview.chromium.org/2424403002
Cr-Commit-Position: refs/heads/master@{#40399}
parent 7499d92d
...@@ -1447,6 +1447,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1447,6 +1447,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
JSObject::AddProperty(global_object, JSObject::AddProperty(global_object,
factory->NewStringFromAsciiChecked("parseFloat"), factory->NewStringFromAsciiChecked("parseFloat"),
parse_float_fun, DONT_ENUM); parse_float_fun, DONT_ENUM);
// Install Number.parseInt and Global.parseInt.
Handle<JSFunction> parse_int_fun = SimpleInstallFunction(
number_fun, "parseInt", Builtins::kNumberParseInt, 2, true);
JSObject::AddProperty(global_object,
factory->NewStringFromAsciiChecked("parseInt"),
parse_int_fun, DONT_ENUM);
} }
{ // --- B o o l e a n --- { // --- B o o l e a n ---
......
...@@ -256,6 +256,93 @@ void Builtins::Generate_NumberParseFloat(CodeStubAssembler* assembler) { ...@@ -256,6 +256,93 @@ void Builtins::Generate_NumberParseFloat(CodeStubAssembler* assembler) {
} }
} }
// ES6 section 20.1.2.13 Number.parseInt ( string, radix )
void Builtins::Generate_NumberParseInt(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
Node* input = assembler->Parameter(1);
Node* radix = assembler->Parameter(2);
Node* context = assembler->Parameter(5);
// Check if {radix} is treated as 10 (i.e. undefined, 0 or 10).
Label if_radix10(assembler), if_generic(assembler, Label::kDeferred);
assembler->GotoIf(assembler->WordEqual(radix, assembler->UndefinedConstant()),
&if_radix10);
assembler->GotoIf(
assembler->WordEqual(radix, assembler->SmiConstant(Smi::FromInt(10))),
&if_radix10);
assembler->GotoIf(
assembler->WordEqual(radix, assembler->SmiConstant(Smi::FromInt(0))),
&if_radix10);
assembler->Goto(&if_generic);
assembler->Bind(&if_radix10);
{
// Check if we can avoid the ToString conversion on {input}.
Label if_inputissmi(assembler), if_inputisheapnumber(assembler),
if_inputisstring(assembler);
assembler->GotoIf(assembler->TaggedIsSmi(input), &if_inputissmi);
Node* input_map = assembler->LoadMap(input);
assembler->GotoIf(
assembler->WordEqual(input_map, assembler->UndefinedConstant()),
&if_inputisheapnumber);
Node* input_instance_type = assembler->LoadMapInstanceType(input_map);
assembler->Branch(assembler->IsStringInstanceType(input_instance_type),
&if_inputisstring, &if_generic);
assembler->Bind(&if_inputissmi);
{
// Just return the {input}.
assembler->Return(input);
}
assembler->Bind(&if_inputisheapnumber);
{
// Check if the absolute {input} value is in the ]0.01,1e9[ range.
Node* input_value = assembler->LoadHeapNumberValue(input);
Node* input_value_abs = assembler->Float64Abs(input_value);
assembler->GotoIf(assembler->Float64LessThan(
input_value_abs, assembler->Float64Constant(0.01)),
&if_generic);
assembler->GotoIf(assembler->Float64LessThan(
assembler->Float64Constant(1e9), input_value_abs),
&if_generic);
// Return the truncated int32 value, and return the tagged result.
Node* input_value32 = assembler->TruncateFloat64ToWord32(input_value);
Node* result = assembler->SmiFromWord32(input_value32);
assembler->Return(result);
}
assembler->Bind(&if_inputisstring);
{
// Check if the String {input} has a cached array index.
Node* input_hash = assembler->LoadNameHashField(input);
Node* input_bit = assembler->Word32And(
input_hash,
assembler->Int32Constant(String::kContainsCachedArrayIndexMask));
assembler->GotoIf(
assembler->Word32NotEqual(input_bit, assembler->Int32Constant(0)),
&if_generic);
// Return the cached array index as result.
Node* input_index =
assembler->BitFieldDecode<String::ArrayIndexValueBits>(input_hash);
Node* result = assembler->SmiTag(input_index);
assembler->Return(result);
}
}
assembler->Bind(&if_generic);
{
Node* result =
assembler->CallRuntime(Runtime::kStringParseInt, context, input, radix);
assembler->Return(result);
}
}
// ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits ) // ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
BUILTIN(NumberPrototypeToExponential) { BUILTIN(NumberPrototypeToExponential) {
HandleScope scope(isolate); HandleScope scope(isolate);
......
...@@ -491,6 +491,8 @@ namespace internal { ...@@ -491,6 +491,8 @@ namespace internal {
TFJ(NumberIsSafeInteger, 2) \ TFJ(NumberIsSafeInteger, 2) \
/* ES6 section 20.1.2.12 Number.parseFloat ( string ) */ \ /* ES6 section 20.1.2.12 Number.parseFloat ( string ) */ \
TFJ(NumberParseFloat, 2) \ TFJ(NumberParseFloat, 2) \
/* ES6 section 20.1.2.13 Number.parseInt ( string, radix ) */ \
TFJ(NumberParseInt, 3) \
CPP(NumberPrototypeToExponential) \ CPP(NumberPrototypeToExponential) \
CPP(NumberPrototypeToFixed) \ CPP(NumberPrototypeToFixed) \
CPP(NumberPrototypeToLocaleString) \ CPP(NumberPrototypeToLocaleString) \
......
...@@ -18,42 +18,6 @@ var ObjectToString = utils.ImportNow("object_to_string"); ...@@ -18,42 +18,6 @@ var ObjectToString = utils.ImportNow("object_to_string");
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// ES6 18.2.5 parseInt(string, radix)
function GlobalParseInt(string, radix) {
if (IS_UNDEFINED(radix) || radix === 10 || radix === 0) {
// Some people use parseInt instead of Math.floor. This
// optimization makes parseInt on a Smi 12 times faster (60ns
// vs 800ns). The following optimization makes parseInt on a
// non-Smi number 9 times faster (230ns vs 2070ns). Together
// they make parseInt on a string 1.4% slower (274ns vs 270ns).
if (%_IsSmi(string)) return string;
if (IS_NUMBER(string) &&
((0.01 < string && string < 1e9) ||
(-1e9 < string && string < -0.01))) {
// Truncate number.
return string | 0;
}
string = TO_STRING(string);
radix = radix | 0;
} else {
// The spec says ToString should be evaluated before ToInt32.
string = TO_STRING(string);
radix = TO_INT32(radix);
if (!(radix == 0 || (2 <= radix && radix <= 36))) {
return NaN;
}
}
if (%_HasCachedArrayIndex(string) &&
(radix == 0 || radix == 10)) {
return %_GetCachedArrayIndex(string);
}
return %StringParseInt(string, radix);
}
// ----------------------------------------------------------------------------
// Set up global object. // Set up global object.
var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY; var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
...@@ -66,11 +30,6 @@ utils.InstallConstants(global, [ ...@@ -66,11 +30,6 @@ utils.InstallConstants(global, [
"undefined", UNDEFINED, "undefined", UNDEFINED,
]); ]);
// Set up non-enumerable function on the global object.
utils.InstallFunctions(global, DONT_ENUM, [
"parseInt", GlobalParseInt,
]);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Object // Object
...@@ -159,12 +118,6 @@ utils.InstallConstants(GlobalNumber, [ ...@@ -159,12 +118,6 @@ utils.InstallConstants(GlobalNumber, [
"EPSILON", 2.220446049250313e-16, "EPSILON", 2.220446049250313e-16,
]); ]);
// Harmony Number constructor additions
utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
"parseInt", GlobalParseInt,
]);
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// Iterator related spec functions. // Iterator related spec functions.
......
...@@ -33,28 +33,40 @@ RUNTIME_FUNCTION(Runtime_StringToNumber) { ...@@ -33,28 +33,40 @@ RUNTIME_FUNCTION(Runtime_StringToNumber) {
// ES6 18.2.5 parseInt(string, radix) slow path // ES6 18.2.5 parseInt(string, radix) slow path
RUNTIME_FUNCTION(Runtime_StringParseInt) { RUNTIME_FUNCTION(Runtime_StringParseInt) {
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
DCHECK(args.length() == 2); DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(String, subject, 0); CONVERT_ARG_HANDLE_CHECKED(Object, string, 0);
CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]); CONVERT_ARG_HANDLE_CHECKED(Object, radix, 1);
// Step 8.a. is already handled in the JS function.
CHECK(radix == 0 || (2 <= radix && radix <= 36)); // Convert {string} to a String first, and flatten it.
Handle<String> subject;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, subject,
Object::ToString(isolate, string));
subject = String::Flatten(subject); subject = String::Flatten(subject);
double value;
// Convert {radix} to Int32.
if (!radix->IsNumber()) {
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, radix, Object::ToNumber(radix));
}
int radix32 = DoubleToInt32(radix->Number());
if (radix32 != 0 && (radix32 < 2 || radix32 > 36)) {
return isolate->heap()->nan_value();
}
double result;
{ {
DisallowHeapAllocation no_gc; DisallowHeapAllocation no_gc;
String::FlatContent flat = subject->GetFlatContent(); String::FlatContent flat = subject->GetFlatContent();
if (flat.IsOneByte()) { if (flat.IsOneByte()) {
value = result = StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(),
StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(), radix); radix32);
} else { } else {
value = StringToInt(isolate->unicode_cache(), flat.ToUC16Vector(), radix); result =
StringToInt(isolate->unicode_cache(), flat.ToUC16Vector(), radix32);
} }
} }
return *isolate->factory()->NewNumber(value); return *isolate->factory()->NewNumber(result);
} }
......
...@@ -815,7 +815,7 @@ TEST(SnapshotDataBlobWithWarmup) { ...@@ -815,7 +815,7 @@ TEST(SnapshotDataBlobWithWarmup) {
// Running the warmup script has effect on whether functions are // Running the warmup script has effect on whether functions are
// pre-compiled, but does not pollute the context. // pre-compiled, but does not pollute the context.
CHECK(IsCompiled("Math.abs")); CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("Number.parseInt")); CHECK(!IsCompiled("String.raw"));
CHECK(CompileRun("Math.random")->IsFunction()); CHECK(CompileRun("Math.random")->IsFunction());
} }
isolate->Dispose(); isolate->Dispose();
...@@ -825,7 +825,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) { ...@@ -825,7 +825,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
DisableTurbofan(); DisableTurbofan();
const char* source = const char* source =
"function f() { return Math.abs(1); }\n" "function f() { return Math.abs(1); }\n"
"function g() { return Number.parseInt(1); }\n" "function g() { return String.raw(1); }\n"
"Object.valueOf(1);" "Object.valueOf(1);"
"var a = 5"; "var a = 5";
const char* warmup = "a = f()"; const char* warmup = "a = f()";
...@@ -850,7 +850,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) { ...@@ -850,7 +850,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
CHECK(IsCompiled("f")); CHECK(IsCompiled("f"));
CHECK(IsCompiled("Math.abs")); CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("g")); CHECK(!IsCompiled("g"));
CHECK(!IsCompiled("Number.parseInt")); CHECK(!IsCompiled("String.raw"));
CHECK(!IsCompiled("Object.valueOf")); CHECK(!IsCompiled("Object.valueOf"));
CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust()); CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
} }
......
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