Commit f50721d5 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Recognize fast path for Number.parseInt.

The Number.parseInt (and therefore the parseInt function on the global
object) are often used instead of Math.floor or just plain int32
truncation, and we can easily recognize those cases and provide a fast
path in TurboFan.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2125583002
Cr-Commit-Position: refs/heads/master@{#37518}
parent 277fac44
......@@ -521,6 +521,23 @@ Reduction JSBuiltinReducer::ReduceMathTrunc(Node* node) {
return NoChange();
}
// ES6 section 20.1.2.13 Number.parseInt ( string, radix )
Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) {
JSCallReduction r(node);
if (r.InputsMatchOne(type_cache_.kSafeInteger) ||
r.InputsMatchTwo(type_cache_.kSafeInteger,
type_cache_.kZeroOrUndefined) ||
r.InputsMatchTwo(type_cache_.kSafeInteger, type_cache_.kTenOrUndefined)) {
// Number.parseInt(a:safe-integer) -> NumberToInt32(a)
// Number.parseInt(a:safe-integer,b:#0\/undefined) -> NumberToInt32(a)
// Number.parseInt(a:safe-integer,b:#10\/undefined) -> NumberToInt32(a)
Node* input = r.GetJSCallInput(0);
Node* value = graph()->NewNode(simplified()->NumberToInt32(), input);
return Replace(value);
}
return NoChange();
}
// ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
Reduction JSBuiltinReducer::ReduceStringFromCharCode(Node* node) {
JSCallReduction r(node);
......@@ -639,6 +656,9 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
case kMathTrunc:
reduction = ReduceMathTrunc(node);
break;
case kNumberParseInt:
reduction = ReduceNumberParseInt(node);
break;
case kStringFromCharCode:
reduction = ReduceStringFromCharCode(node);
break;
......
......@@ -62,6 +62,7 @@ class JSBuiltinReducer final : public AdvancedReducer {
Reduction ReduceMathTan(Node* node);
Reduction ReduceMathTanh(Node* node);
Reduction ReduceMathTrunc(Node* node);
Reduction ReduceNumberParseInt(Node* node);
Reduction ReduceStringFromCharCode(Node* node);
Node* ToNumber(Node* value);
......
......@@ -6756,7 +6756,8 @@ class Script: public Struct {
V(Math, imul, MathImul) \
V(Math, clz32, MathClz32) \
V(Math, fround, MathFround) \
V(Math, trunc, MathTrunc)
V(Math, trunc, MathTrunc) \
V(Number, parseInt, NumberParseInt)
#define ATOMIC_FUNCTIONS_WITH_ID_LIST(V) \
V(Atomics, load, AtomicsLoad) \
......
......@@ -42,7 +42,12 @@ class TypeCache final {
Type* const kSingletonZero = CreateRange(0.0, 0.0);
Type* const kSingletonOne = CreateRange(1.0, 1.0);
Type* const kSingletonTen = CreateRange(10.0, 10.0);
Type* const kSingletonMinusOne = CreateRange(-1.0, -1.0);
Type* const kZeroOrUndefined =
Type::Union(kSingletonZero, Type::Undefined(), zone());
Type* const kTenOrUndefined =
Type::Union(kSingletonTen, Type::Undefined(), zone());
Type* const kMinusOneOrZero = CreateRange(-1.0, 0.0);
Type* const kZeroOrOne = CreateRange(0.0, 1.0);
Type* const kZeroToThirtyOne = CreateRange(0.0, 31.0);
......
......@@ -49,6 +49,19 @@ class JSBuiltinReducerTest : public TypedGraphTest {
return HeapConstant(f);
}
Node* NumberFunction(const char* name) {
Handle<Object> m =
JSObject::GetProperty(
isolate()->global_object(),
isolate()->factory()->NewStringFromAsciiChecked("Number"))
.ToHandleChecked();
Handle<JSFunction> f = Handle<JSFunction>::cast(
Object::GetProperty(
m, isolate()->factory()->NewStringFromAsciiChecked(name))
.ToHandleChecked());
return HeapConstant(f);
}
Node* StringFunction(const char* name) {
Handle<Object> m =
JSObject::GetProperty(
......@@ -1342,6 +1355,48 @@ TEST_F(JSBuiltinReducerTest, MathTruncWithPlainPrimitive) {
EXPECT_THAT(r.replacement(), IsNumberTrunc(IsPlainPrimitiveToNumber(p0)));
}
// -----------------------------------------------------------------------------
// Number.parseInt
TEST_F(JSBuiltinReducerTest, NumberParseIntWithIntegral32) {
Node* function = NumberFunction("parseInt");
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(), IsNumberToInt32(p0));
}
}
TEST_F(JSBuiltinReducerTest, NumberParseIntWithIntegral32AndUndefined) {
Node* function = NumberFunction("parseInt");
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* p1 = Parameter(Type::Undefined(), 1);
Node* call = graph()->NewNode(javascript()->CallFunction(4), function,
UndefinedConstant(), p0, p1, context,
frame_state, effect, control);
Reduction r = Reduce(call);
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsNumberToInt32(p0));
}
}
// -----------------------------------------------------------------------------
// String.fromCharCode
......
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