[builtins] Migrate Number.parseFloat to a TurboFan builtin.

This implicitly convers parseFloat on the global object as well, since
it's the same function. This is mostly straight-forward, but adds
another fast case for HeapNumbers as well.

R=ishell@chromium.org

Review-Url: https://codereview.chromium.org/2395373002
Cr-Commit-Position: refs/heads/master@{#40072}
This commit is contained in:
bmeurer 2016-10-07 01:58:43 -07:00 committed by Commit bot
parent e5aade74fe
commit 0f7f6e33ba
7 changed files with 120 additions and 13 deletions

View File

@ -1349,6 +1349,13 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
SimpleInstallFunction(number_fun, "isNaN", Builtins::kNumberIsNaN, 1, true);
SimpleInstallFunction(number_fun, "isSafeInteger",
Builtins::kNumberIsSafeInteger, 1, true);
// Install Number.parseFloat and Global.parseFloat.
Handle<JSFunction> parse_float_fun = SimpleInstallFunction(
number_fun, "parseFloat", Builtins::kNumberParseFloat, 1, true);
JSObject::AddProperty(global_object,
factory->NewStringFromAsciiChecked("parseFloat"),
parse_float_fun, DONT_ENUM);
}
{ // --- B o o l e a n ---

View File

@ -150,6 +150,112 @@ void Builtins::Generate_NumberIsSafeInteger(CodeStubAssembler* assembler) {
assembler->Return(assembler->BooleanConstant(false));
}
// ES6 section 20.1.2.12 Number.parseFloat ( string )
void Builtins::Generate_NumberParseFloat(CodeStubAssembler* assembler) {
typedef CodeStubAssembler::Label Label;
typedef compiler::Node Node;
typedef CodeStubAssembler::Variable Variable;
Node* context = assembler->Parameter(4);
// We might need to loop once for ToString conversion.
Variable var_input(assembler, MachineRepresentation::kTagged);
Label loop(assembler, &var_input);
var_input.Bind(assembler->Parameter(1));
assembler->Goto(&loop);
assembler->Bind(&loop);
{
// Load the current {input} value.
Node* input = var_input.value();
// Check if the {input} is a HeapObject or a Smi.
Label if_inputissmi(assembler), if_inputisnotsmi(assembler);
assembler->Branch(assembler->WordIsSmi(input), &if_inputissmi,
&if_inputisnotsmi);
assembler->Bind(&if_inputissmi);
{
// The {input} is already a Number, no need to do anything.
assembler->Return(input);
}
assembler->Bind(&if_inputisnotsmi);
{
// The {input} is a HeapObject, check if it's already a String.
Label if_inputisstring(assembler), if_inputisnotstring(assembler);
Node* input_map = assembler->LoadMap(input);
Node* input_instance_type = assembler->LoadMapInstanceType(input_map);
assembler->Branch(assembler->IsStringInstanceType(input_instance_type),
&if_inputisstring, &if_inputisnotstring);
assembler->Bind(&if_inputisstring);
{
// The {input} is already a String, check if {input} contains
// a cached array index.
Label if_inputcached(assembler), if_inputnotcached(assembler);
Node* input_hash = assembler->LoadNameHashField(input);
Node* input_bit = assembler->Word32And(
input_hash,
assembler->Int32Constant(String::kContainsCachedArrayIndexMask));
assembler->Branch(
assembler->Word32Equal(input_bit, assembler->Int32Constant(0)),
&if_inputcached, &if_inputnotcached);
assembler->Bind(&if_inputcached);
{
// Just return the {input}s cached array index.
Node* input_array_index =
assembler->BitFieldDecodeWord<String::ArrayIndexValueBits>(
input_hash);
assembler->Return(assembler->SmiTag(input_array_index));
}
assembler->Bind(&if_inputnotcached);
{
// Need to fall back to the runtime to convert {input} to double.
assembler->Return(assembler->CallRuntime(Runtime::kStringParseFloat,
context, input));
}
}
assembler->Bind(&if_inputisnotstring);
{
// The {input} is neither a String nor a Smi, check for HeapNumber.
Label if_inputisnumber(assembler),
if_inputisnotnumber(assembler, Label::kDeferred);
assembler->Branch(
assembler->WordEqual(input_map, assembler->HeapNumberMapConstant()),
&if_inputisnumber, &if_inputisnotnumber);
assembler->Bind(&if_inputisnumber);
{
// The {input} is already a Number, take care of -0.
Label if_inputiszero(assembler), if_inputisnotzero(assembler);
Node* input_value = assembler->LoadHeapNumberValue(input);
assembler->Branch(assembler->Float64Equal(
input_value, assembler->Float64Constant(0.0)),
&if_inputiszero, &if_inputisnotzero);
assembler->Bind(&if_inputiszero);
assembler->Return(assembler->SmiConstant(0));
assembler->Bind(&if_inputisnotzero);
assembler->Return(input);
}
assembler->Bind(&if_inputisnotnumber);
{
// Need to convert the {input} to String first.
// TODO(bmeurer): This could be more efficient if necessary.
Callable callable = CodeFactory::ToString(assembler->isolate());
var_input.Bind(assembler->CallStub(callable, context, input));
assembler->Goto(&loop);
}
}
}
}
}
// ES6 section 20.1.3.2 Number.prototype.toExponential ( fractionDigits )
BUILTIN(NumberPrototypeToExponential) {
HandleScope scope(isolate);

View File

@ -480,6 +480,8 @@ namespace internal {
TFJ(NumberIsNaN, 2) \
/* ES6 section 20.1.2.5 Number.isSafeInteger ( number ) */ \
TFJ(NumberIsSafeInteger, 2) \
/* ES6 section 20.1.2.12 Number.parseFloat ( string ) */ \
TFJ(NumberParseFloat, 2) \
CPP(NumberPrototypeToExponential) \
CPP(NumberPrototypeToFixed) \
CPP(NumberPrototypeToLocaleString) \

View File

@ -1362,6 +1362,8 @@ Type* Typer::Visitor::JSCallFunctionTyper(Type* fun, Typer* t) {
case kNumberIsNaN:
case kNumberIsSafeInteger:
return Type::Boolean();
case kNumberParseFloat:
return Type::Number();
case kNumberParseInt:
return t->cache_.kIntegerOrMinusZeroOrNaN;
case kNumberToString:

View File

@ -52,15 +52,6 @@ function GlobalParseInt(string, radix) {
}
// ES6 18.2.4 parseFloat(string)
function GlobalParseFloat(string) {
// 1. Let inputString be ? ToString(string).
string = TO_STRING(string);
if (%_HasCachedArrayIndex(string)) return %_GetCachedArrayIndex(string);
return %StringParseFloat(string);
}
// ----------------------------------------------------------------------------
// Set up global object.
@ -78,7 +69,6 @@ utils.InstallConstants(global, [
// Set up non-enumerable function on the global object.
utils.InstallFunctions(global, DONT_ENUM, [
"parseInt", GlobalParseInt,
"parseFloat", GlobalParseFloat,
]);
@ -213,7 +203,6 @@ utils.InstallConstants(GlobalNumber, [
// Harmony Number constructor additions
utils.InstallFunctions(GlobalNumber, DONT_ENUM, [
"parseInt", GlobalParseInt,
"parseFloat", GlobalParseFloat
]);

View File

@ -7071,6 +7071,7 @@ class Script: public Struct {
V(Number, isInteger, NumberIsInteger) \
V(Number, isNaN, NumberIsNaN) \
V(Number, isSafeInteger, NumberIsSafeInteger) \
V(Number, parseFloat, NumberParseFloat) \
V(Number, parseInt, NumberParseInt) \
V(Number.prototype, toString, NumberToString)

View File

@ -826,7 +826,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
const char* source =
"function f() { return Math.abs(1); }\n"
"function g() { return Number.parseInt(1); }\n"
"Number.parseFloat(1);"
"Object.valueOf(1);"
"var a = 5";
const char* warmup = "a = f()";
@ -851,7 +851,7 @@ TEST(CustomSnapshotDataBlobWithWarmup) {
CHECK(IsCompiled("Math.abs"));
CHECK(!IsCompiled("g"));
CHECK(!IsCompiled("Number.parseInt"));
CHECK(!IsCompiled("Number.parseFloat"));
CHECK(!IsCompiled("Object.valueOf"));
CHECK_EQ(5, CompileRun("a")->Int32Value(context).FromJust());
}
isolate->Dispose();