[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:
parent
e5aade74fe
commit
0f7f6e33ba
@ -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 ---
|
||||
|
@ -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);
|
||||
|
@ -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) \
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
]);
|
||||
|
||||
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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();
|
||||
|
Loading…
Reference in New Issue
Block a user