Index: src/compiler/js-call-reducer.cc |
diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc |
index fb54ab56bc82b2cb5681f703e8dffc8199c663f2..959a43bf0e6cb74cdf9c7b8490b647dd096eeb9c 100644 |
--- a/src/compiler/js-call-reducer.cc |
+++ b/src/compiler/js-call-reducer.cc |
@@ -6,6 +6,7 @@ |
#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" |
@@ -38,22 +39,11 @@ VectorSlotPair CallCountFeedback(VectorSlotPair p) { |
Reduction JSCallReducer::Reduce(Node* node) { |
- if (node->opcode() == IrOpcode::kJSCallFunction) { |
- HeapObjectMatcher m(node->InputAt(0)); |
- if (m.HasValue() && m.Value()->IsJSFunction()) { |
- Handle<SharedFunctionInfo> shared( |
- Handle<JSFunction>::cast(m.Value())->shared(), isolate()); |
- if (shared->HasBuiltinFunctionId()) { |
- switch (shared->builtin_function_id()) { |
- case kFunctionApply: |
- return ReduceFunctionPrototypeApply(node); |
- case kFunctionCall: |
- return ReduceFunctionPrototypeCall(node); |
- default: |
- break; |
- } |
- } |
- } |
+ switch (node->opcode()) { |
+ case IrOpcode::kJSCallFunction: |
+ return ReduceJSCallFunction(node); |
+ default: |
+ break; |
} |
return NoChange(); |
} |
@@ -133,7 +123,9 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { |
// to ensure any exception is thrown in the correct context. |
NodeProperties::ReplaceContextInput( |
node, jsgraph()->HeapConstant(handle(apply->context(), isolate()))); |
- return Changed(node); |
+ // Try to further reduce the JSCallFunction {node}. |
+ Reduction const reduction = ReduceJSCallFunction(node); |
+ return reduction.Changed() ? reduction : Changed(node); |
} |
@@ -168,7 +160,88 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { |
node, javascript()->CallFunction(arity, p.language_mode(), |
CallCountFeedback(p.feedback()), |
convert_mode, p.tail_call_mode())); |
- return Changed(node); |
+ // Try to further reduce the JSCallFunction {node}. |
+ Reduction const reduction = ReduceJSCallFunction(node); |
+ return reduction.Changed() ? reduction : Changed(node); |
+} |
+ |
+ |
+Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { |
+ DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode()); |
+ CallFunctionParameters const& p = CallFunctionParametersOf(node->op()); |
+ Node* target = NodeProperties::GetValueInput(node, 0); |
+ Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
+ Node* control = NodeProperties::GetControlInput(node); |
+ Node* effect = NodeProperties::GetEffectInput(node); |
+ |
+ // Try to specialize JSCallFunction {node}s with constant {target}s. |
+ HeapObjectMatcher m(target); |
+ if (m.HasValue()) { |
+ if (m.Value()->IsJSFunction()) { |
+ Handle<SharedFunctionInfo> shared( |
+ Handle<JSFunction>::cast(m.Value())->shared(), isolate()); |
+ |
+ // Raise a TypeError if the {target} is a "classConstructor". |
+ if (IsClassConstructor(shared->kind())) { |
+ NodeProperties::RemoveFrameStateInput(node, 0); |
+ NodeProperties::RemoveValueInputs(node); |
+ NodeProperties::ChangeOp( |
+ node, javascript()->CallRuntime( |
+ Runtime::kThrowConstructorNonCallableError, 0)); |
+ return Changed(node); |
+ } |
+ |
+ // Check for known builtin functions. |
+ if (shared->HasBuiltinFunctionId()) { |
+ switch (shared->builtin_function_id()) { |
+ case kFunctionApply: |
+ return ReduceFunctionPrototypeApply(node); |
+ case kFunctionCall: |
+ return ReduceFunctionPrototypeCall(node); |
+ default: |
+ break; |
+ } |
+ } |
+ } |
+ // Don't mess with other {node}s that have a constant {target}. |
+ // TODO(bmeurer): Also support optimizing bound functions and proxies here. |
+ return NoChange(); |
+ } |
+ |
+ // Not much we can do if deoptimization support is disabled. |
+ if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
+ |
+ // Extract feedback from the {node} using the CallICNexus. |
+ if (!p.feedback().IsValid()) return NoChange(); |
+ CallICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
+ Handle<Object> feedback(nexus.GetFeedback(), isolate()); |
+ if (feedback->IsWeakCell()) { |
+ Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback); |
+ if (cell->value()->IsJSFunction()) { |
+ // 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* 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); |
+ |
+ // Specialize the JSCallFunction node to the {target_function}. |
+ NodeProperties::ReplaceValueInput(node, target_function, 0); |
+ NodeProperties::ReplaceControlInput(node, control); |
+ |
+ // Try to further reduce the JSCallFunction {node}. |
+ Reduction const reduction = ReduceJSCallFunction(node); |
+ return reduction.Changed() ? reduction : Changed(node); |
+ } |
+ } |
+ return NoChange(); |
} |
@@ -178,10 +251,20 @@ Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
+CommonOperatorBuilder* JSCallReducer::common() const { |
+ return jsgraph()->common(); |
+} |
+ |
+ |
JSOperatorBuilder* JSCallReducer::javascript() const { |
return jsgraph()->javascript(); |
} |
+ |
+SimplifiedOperatorBuilder* JSCallReducer::simplified() const { |
+ return jsgraph()->simplified(); |
+} |
+ |
} // namespace compiler |
} // namespace internal |
} // namespace v8 |