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 |