Commit 226e63fc authored by Benedikt Meurer's avatar Benedikt Meurer Committed by Commit Bot

[turbofan] Fold Object constructor calls with certain values.

When calling

  Object(value)

where the value is known to be a JSReceiver, we can just replace it with
value, as the Object constructor call is a no-op in that case. Otherwise
when value is known to be not null or undefined then we can replace the
Object constructor call with an invocation of ToObject.

This covers the common pattern found in bundles generated by Webpack,
where the Object constructor is used to call imported functions, i.e.

  Object(module.foo)(1, 2, 3)

There's a lot of detail in https://github.com/webpack/webpack/issues/5600
on this matter and why this pattern was chosen.

Bug: v8:6772
Change-Id: I2b4f0b4542b68b97b337ce571d6d79946c73d8bb
Reviewed-on: https://chromium-review.googlesource.com/643868Reviewed-by: 's avatarYang Guo <yangguo@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#47728}
parent 025ea28b
......@@ -20,6 +20,62 @@ namespace v8 {
namespace internal {
namespace compiler {
namespace {
bool CanBePrimitive(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
case IrOpcode::kJSCreateClosure:
case IrOpcode::kJSCreateEmptyLiteralArray:
case IrOpcode::kJSCreateEmptyLiteralObject:
case IrOpcode::kJSCreateIterResultObject:
case IrOpcode::kJSCreateKeyValueArray:
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructWithArrayLike:
case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSGetSuperConstructor:
case IrOpcode::kJSToObject:
return false;
case IrOpcode::kHeapConstant: {
Handle<HeapObject> value = HeapObjectMatcher(node).Value();
return value->IsPrimitive();
}
default:
return true;
}
}
bool CanBeNullOrUndefined(Node* node) {
if (CanBePrimitive(node)) {
switch (node->opcode()) {
case IrOpcode::kJSToBoolean:
case IrOpcode::kJSToInteger:
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToString:
return false;
case IrOpcode::kHeapConstant: {
Handle<HeapObject> value = HeapObjectMatcher(node).Value();
Isolate* const isolate = value->GetIsolate();
return value->IsNullOrUndefined(isolate);
}
default:
return true;
}
}
return false;
}
} // namespace
Reduction JSCallReducer::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSConstruct:
......@@ -104,43 +160,30 @@ Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
return Changed(node);
}
namespace {
bool CanBeNullOrUndefined(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSCreate:
case IrOpcode::kJSCreateArguments:
case IrOpcode::kJSCreateArray:
case IrOpcode::kJSCreateClosure:
case IrOpcode::kJSCreateIterResultObject:
case IrOpcode::kJSCreateKeyValueArray:
case IrOpcode::kJSCreateLiteralArray:
case IrOpcode::kJSCreateLiteralObject:
case IrOpcode::kJSCreateLiteralRegExp:
case IrOpcode::kJSConstruct:
case IrOpcode::kJSConstructForwardVarargs:
case IrOpcode::kJSConstructWithSpread:
case IrOpcode::kJSConvertReceiver:
case IrOpcode::kJSToBoolean:
case IrOpcode::kJSToInteger:
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToObject:
case IrOpcode::kJSToString:
return false;
case IrOpcode::kHeapConstant: {
Handle<HeapObject> value = HeapObjectMatcher(node).Value();
Isolate* const isolate = value->GetIsolate();
return value->IsNull(isolate) || value->IsUndefined(isolate);
// ES section #sec-object-constructor
Reduction JSCallReducer::ReduceObjectConstructor(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
if (p.arity() < 3) return NoChange();
Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
// We can fold away the Object(x) call if |x| is definitely not a primitive.
if (CanBePrimitive(value)) {
if (!CanBeNullOrUndefined(value)) {
// Turn the {node} into a {JSToObject} call if we know that
// the {value} cannot be null or undefined.
NodeProperties::ReplaceValueInputs(node, value);
NodeProperties::ChangeOp(node, javascript()->ToObject());
return Changed(node);
}
default:
return true;
} else {
ReplaceWithValue(node, value);
return Replace(node);
}
return NoChange();
}
} // namespace
// ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
......@@ -1312,6 +1355,12 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
// Don't inline cross native context.
if (function->native_context() != *native_context()) return NoChange();
// TODO(turbofan): Merge this into the switch below once the
// Object constructor is a proper TFJ builtin.
if (*function == native_context()->object_function()) {
return ReduceObjectConstructor(node);
}
// Check for known builtin functions.
switch (shared->code()->builtin_index()) {
case Builtins::kArrayConstructor:
......
......@@ -60,6 +60,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node);
Reduction ReduceFunctionPrototypeHasInstance(Node* node);
Reduction ReduceObjectConstructor(Node* node);
Reduction ReduceObjectGetPrototype(Node* node, Node* object);
Reduction ReduceObjectGetPrototypeOf(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node);
......
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
// Common pattern in Webpack 3 generated bundles, see
// https://github.com/webpack/webpack/issues/5600 for details.
(function ObjectConstructorWithKnownFunction() {
"use strict";
class A {
bar() { return this; }
};
function foo(a) {
return Object(a.bar)();
}
assertEquals(undefined, foo(new A));
assertEquals(undefined, foo(new A));
%OptimizeFunctionOnNextCall(foo);
assertEquals(undefined, foo(new A));
})();
(function ObjectConstructorWithString() {
"use strict";
function foo() {
return Object("a");
}
assertEquals('object', typeof foo());
assertEquals('object', typeof foo());
%OptimizeFunctionOnNextCall(foo);
assertEquals('object', typeof foo());
})();
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