| Index: src/compiler/js-call-reducer.cc
|
| diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc
|
| index 959a43bf0e6cb74cdf9c7b8490b647dd096eeb9c..f6a818e54c37d7671b213f688ad986acac6ef414 100644
|
| --- a/src/compiler/js-call-reducer.cc
|
| +++ b/src/compiler/js-call-reducer.cc
|
| @@ -6,7 +6,6 @@
|
|
|
| #include "src/compiler/js-graph.h"
|
| #include "src/compiler/node-matchers.h"
|
| -#include "src/compiler/simplified-operator.h"
|
| #include "src/objects-inl.h"
|
| #include "src/type-feedback-vector-inl.h"
|
|
|
| @@ -40,6 +39,8 @@ VectorSlotPair CallCountFeedback(VectorSlotPair p) {
|
|
|
| Reduction JSCallReducer::Reduce(Node* node) {
|
| switch (node->opcode()) {
|
| + case IrOpcode::kJSCallConstruct:
|
| + return ReduceJSCallConstruct(node);
|
| case IrOpcode::kJSCallFunction:
|
| return ReduceJSCallFunction(node);
|
| default:
|
| @@ -49,6 +50,36 @@ Reduction JSCallReducer::Reduce(Node* node) {
|
| }
|
|
|
|
|
| +// ES6 section 22.1.1 The Array Constructor
|
| +Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
|
| + Node* target = NodeProperties::GetValueInput(node, 0);
|
| + DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
|
| + CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
|
| +
|
| + // Check if we have an allocation site from the CallIC.
|
| + Handle<AllocationSite> site;
|
| + if (p.feedback().IsValid()) {
|
| + CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
|
| + Handle<Object> feedback(nexus.GetFeedback(), isolate());
|
| + if (feedback->IsAllocationSite()) {
|
| + site = Handle<AllocationSite>::cast(feedback);
|
| + }
|
| + }
|
| +
|
| + // Turn the {node} into a {JSCreateArray} call.
|
| + DCHECK_LE(2u, p.arity());
|
| + size_t const arity = p.arity() - 2;
|
| + NodeProperties::ReplaceValueInput(node, target, 0);
|
| + NodeProperties::ReplaceValueInput(node, target, 1);
|
| + NodeProperties::RemoveFrameStateInput(node, 1);
|
| + // TODO(bmeurer): We might need to propagate the tail call mode to
|
| + // the JSCreateArray operator, because an Array call in tail call
|
| + // position must always properly consume the parent stack frame.
|
| + NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
|
| + return Changed(node);
|
| +}
|
| +
|
| +
|
| // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
|
| Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
|
| DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
|
| @@ -170,6 +201,7 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
|
| DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
|
| CallFunctionParameters const& p = CallFunctionParametersOf(node->op());
|
| Node* target = NodeProperties::GetValueInput(node, 0);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| Node* frame_state = NodeProperties::GetFrameStateInput(node, 1);
|
| Node* control = NodeProperties::GetControlInput(node);
|
| Node* effect = NodeProperties::GetEffectInput(node);
|
| @@ -178,8 +210,8 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
|
| HeapObjectMatcher m(target);
|
| if (m.HasValue()) {
|
| if (m.Value()->IsJSFunction()) {
|
| - Handle<SharedFunctionInfo> shared(
|
| - Handle<JSFunction>::cast(m.Value())->shared(), isolate());
|
| + Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
|
| + Handle<SharedFunctionInfo> shared(function->shared(), isolate());
|
|
|
| // Raise a TypeError if the {target} is a "classConstructor".
|
| if (IsClassConstructor(shared->kind())) {
|
| @@ -202,7 +234,13 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
|
| break;
|
| }
|
| }
|
| +
|
| + // Check for the ArrayConstructor.
|
| + if (*function == function->native_context()->array_function()) {
|
| + return ReduceArrayConstructor(node);
|
| + }
|
| }
|
| +
|
| // Don't mess with other {node}s that have a constant {target}.
|
| // TODO(bmeurer): Also support optimizing bound functions and proxies here.
|
| return NoChange();
|
| @@ -215,14 +253,52 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
|
| if (!p.feedback().IsValid()) return NoChange();
|
| CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
|
| Handle<Object> feedback(nexus.GetFeedback(), isolate());
|
| - if (feedback->IsWeakCell()) {
|
| + if (feedback->IsAllocationSite()) {
|
| + // Retrieve the Array function from the {node}.
|
| + Node* array_function;
|
| + Handle<Context> native_context;
|
| + if (GetNativeContext(node).ToHandle(&native_context)) {
|
| + array_function = jsgraph()->HeapConstant(
|
| + handle(native_context->array_function(), isolate()));
|
| + } else {
|
| + Node* global_object = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::GLOBAL_OBJECT_INDEX, true),
|
| + context, context, effect);
|
| + Node* native_context = effect = graph()->NewNode(
|
| + javascript()->LoadNativeContext(), global_object, context, effect);
|
| + array_function = effect = graph()->NewNode(
|
| + javascript()->LoadContext(0, Context::ARRAY_FUNCTION_INDEX, true),
|
| + native_context, native_context, effect);
|
| + }
|
| +
|
| + // Check that the {target} is still the {array_function}.
|
| + Node* check = effect =
|
| + graph()->NewNode(javascript()->StrictEqual(), target, array_function,
|
| + context, effect, control);
|
| + Node* branch =
|
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
| + Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| + Node* deoptimize =
|
| + graph()->NewNode(common()->Deoptimize(), frame_state, effect, if_false);
|
| + // TODO(bmeurer): This should be on the AdvancedReducer somehow.
|
| + NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
|
| + control = graph()->NewNode(common()->IfTrue(), branch);
|
| +
|
| + // Turn the {node} into a {JSCreateArray} call.
|
| + NodeProperties::ReplaceValueInput(node, array_function, 0);
|
| + NodeProperties::ReplaceEffectInput(node, effect);
|
| + NodeProperties::ReplaceControlInput(node, control);
|
| + return ReduceArrayConstructor(node);
|
| + } else if (feedback->IsWeakCell()) {
|
| Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
|
| if (cell->value()->IsJSFunction()) {
|
| + Node* target_function =
|
| + jsgraph()->Constant(handle(cell->value(), isolate()));
|
| +
|
| // Check that the {target} is still the {target_function}.
|
| - Node* target_function = jsgraph()->HeapConstant(
|
| - handle(JSFunction::cast(cell->value()), isolate()));
|
| - Node* check = graph()->NewNode(simplified()->ReferenceEqual(Type::Any()),
|
| - target, target_function);
|
| + Node* check = effect =
|
| + graph()->NewNode(javascript()->StrictEqual(), target, target_function,
|
| + context, effect, control);
|
| Node* branch =
|
| graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
|
| Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
|
| @@ -234,6 +310,7 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
|
|
|
| // Specialize the JSCallFunction node to the {target_function}.
|
| NodeProperties::ReplaceValueInput(node, target_function, 0);
|
| + NodeProperties::ReplaceEffectInput(node, effect);
|
| NodeProperties::ReplaceControlInput(node, control);
|
|
|
| // Try to further reduce the JSCallFunction {node}.
|
| @@ -245,6 +322,68 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
|
| }
|
|
|
|
|
| +Reduction JSCallReducer::ReduceJSCallConstruct(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSCallConstruct, node->opcode());
|
| + CallConstructParameters const& p = CallConstructParametersOf(node->op());
|
| + DCHECK_LE(2u, p.arity());
|
| + int const arity = static_cast<int>(p.arity() - 2);
|
| + Node* target = NodeProperties::GetValueInput(node, 0);
|
| + Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
|
| +
|
| + // Try to specialize JSCallConstruct {node}s with constant {target}s.
|
| + HeapObjectMatcher m(target);
|
| + if (m.HasValue()) {
|
| + if (m.Value()->IsJSFunction()) {
|
| + Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
|
| +
|
| + // Raise a TypeError if the {target} is not a constructor.
|
| + if (!function->IsConstructor()) {
|
| + NodeProperties::ReplaceValueInputs(node, target);
|
| + NodeProperties::ChangeOp(
|
| + node,
|
| + javascript()->CallRuntime(Runtime::kThrowCalledNonCallable, 1));
|
| + return Changed(node);
|
| + }
|
| +
|
| + // Check for the ArrayConstructor.
|
| + if (*function == function->native_context()->array_function()) {
|
| + // Check if we have an allocation site.
|
| + Handle<AllocationSite> site;
|
| + if (p.feedback().IsValid()) {
|
| + Handle<Object> feedback(
|
| + p.feedback().vector()->Get(p.feedback().slot()), isolate());
|
| + if (feedback->IsAllocationSite()) {
|
| + site = Handle<AllocationSite>::cast(feedback);
|
| + }
|
| + }
|
| +
|
| + // Turn the {node} into a {JSCreateArray} call.
|
| + for (int i = arity; i > 0; --i) {
|
| + NodeProperties::ReplaceValueInput(
|
| + node, NodeProperties::GetValueInput(node, i), i + 1);
|
| + }
|
| + NodeProperties::ReplaceValueInput(node, new_target, 1);
|
| + NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
|
| + return Changed(node);
|
| + }
|
| + }
|
| +
|
| + // Don't mess with other {node}s that have a constant {target}.
|
| + // TODO(bmeurer): Also support optimizing bound functions and proxies here.
|
| + return NoChange();
|
| + }
|
| +
|
| + return NoChange();
|
| +}
|
| +
|
| +
|
| +MaybeHandle<Context> JSCallReducer::GetNativeContext(Node* node) {
|
| + Node* const context = NodeProperties::GetContextInput(node);
|
| + return NodeProperties::GetSpecializationNativeContext(context,
|
| + native_context());
|
| +}
|
| +
|
| +
|
| Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
|
|
|
|
|
| @@ -260,11 +399,6 @@ JSOperatorBuilder* JSCallReducer::javascript() const {
|
| return jsgraph()->javascript();
|
| }
|
|
|
| -
|
| -SimplifiedOperatorBuilder* JSCallReducer::simplified() const {
|
| - return jsgraph()->simplified();
|
| -}
|
| -
|
| } // namespace compiler
|
| } // namespace internal
|
| } // namespace v8
|
|
|