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,
JSObject::AddProperty(global_object,
factory->NewStringFromAsciiChecked("parseFloat"),
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 ---
......
......@@ -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 )
BUILTIN(NumberPrototypeToExponential) {
HandleScope scope(isolate);
......
......@@ -491,6 +491,8 @@ namespace internal {
TFJ(NumberIsSafeInteger, 2) \
/* ES6 section 20.1.2.12 Number.parseFloat ( string ) */ \
TFJ(NumberParseFloat, 2) \
/* ES6 section 20.1.2.13 Number.parseInt ( string, radix ) */ \
TFJ(NumberParseInt, 3) \
CPP(NumberPrototypeToExponential) \
CPP(NumberPrototypeToFixed) \
CPP(NumberPrototypeToLocaleString) \
......
......@@ -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.
var attributes = DONT_ENUM | DONT_DELETE | READ_ONLY;
......@@ -66,11 +30,6 @@ utils.InstallConstants(global, [
"undefined", UNDEFINED,
]);
// Set up non-enumerable function on the global object.
utils.InstallFunctions(global, DONT_ENUM, [
"parseInt", GlobalParseInt,
]);
// ----------------------------------------------------------------------------
// Object
......@@ -159,12 +118,6 @@ utils.InstallConstants(GlobalNumber, [
"EPSILON", 2.220446049250313e-16,
]);
// Harmony Number constructor additions
utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
"parseInt", GlobalParseInt,
]);
// ----------------------------------------------------------------------------
// Iterator related spec functions.
......
......@@ -33,28 +33,40 @@ RUNTIME_FUNCTION(Runtime_StringToNumber) {
// ES6 18.2.5 parseInt(string, radix) slow path
RUNTIME_FUNCTION(Runtime_StringParseInt) {
HandleScope handle_scope(isolate);
DCHECK(args.length() == 2);
CONVERT_ARG_HANDLE_CHECKED(String, subject, 0);
CONVERT_NUMBER_CHECKED(int, radix, Int32, args[1]);
// Step 8.a. is already handled in the JS function.
CHECK(radix == 0 || (2 <= radix && radix <= 36));
DCHECK_EQ(2, args.length());
CONVERT_ARG_HANDLE_CHECKED(Object, string, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, radix, 1);
// 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);
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;
String::FlatContent flat = subject->GetFlatContent();
if (flat.IsOneByte()) {
value =
StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(), radix);
result = StringToInt(isolate->unicode_cache(), flat.ToOneByteVector(),
radix32);
} 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) {
// 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.parseInt"));
CHECK(!IsCompiled("String.raw"));
CHECK(CompileRun("Math.random")->IsFunction());
}
isolate->Dispose();
......@@ -825,7 +825,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
DisableTurbofan();
const char* source =
"function f() { return Math.abs(1); }\n"
"function g() { return Number.parseInt(1); }\n"
"function g() { return String.raw(1); }\n"
"Object.valueOf(1);"
"var a = 5";
const char* warmup = "a = f()";
......@@ -850,7 +850,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
CHECK(IsCompiled("f"));
CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("g"));
CHECK(!IsCompiled("Number.parseInt"));
CHECK(!IsCompiled("String.raw"));
CHECK(!IsCompiled("Object.valueOf"));
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