[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}
This commit is contained in:
bmeurer 2016-07-05 02:49:52 -07:00 committed by Commit bot
parent 277fac44ff
commit f50721d56d
5 changed files with 83 additions and 1 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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) \

View File

@ -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);

View File

@ -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