Commit 719d6c1d authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Also optimize instanceof with bound functions.

For bound functions on the right-hand side of instanceof we can
constant-fold to the actual [[BoundTargetFunction]], actually
instance OrdinaryHasInstance. Move the Function.prototype[@@hasInstance]
reduction up to the JSCallReducer to allow this optimization to become
effective (and also enable other optimizations).

BUG=v8:5267
R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2537763002
Cr-Commit-Position: refs/heads/master@{#41352}
parent 95c0ecee
...@@ -964,34 +964,6 @@ Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) { ...@@ -964,34 +964,6 @@ Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) {
return NoChange(); return NoChange();
} }
// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] ( V )
Reduction JSBuiltinReducer::ReduceFunctionHasInstance(Node* node) {
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* object = (node->op()->ValueInputCount() >= 3)
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
// stack trace doesn't contain the @@hasInstance call; we have the
// corresponding bug in the baseline case. Some massaging of the frame
// state would be necessary here.
// Morph this {node} into a JSOrdinaryHasInstance node.
node->ReplaceInput(0, receiver);
node->ReplaceInput(1, object);
node->ReplaceInput(2, context);
node->ReplaceInput(3, frame_state);
node->ReplaceInput(4, effect);
node->ReplaceInput(5, control);
node->TrimInputCount(6);
NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
return Changed(node);
}
// ES6 section 18.2.2 isFinite ( number ) // ES6 section 18.2.2 isFinite ( number )
Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) { Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) {
JSCallReduction r(node); JSCallReduction r(node);
...@@ -1875,9 +1847,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) { ...@@ -1875,9 +1847,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceArrayPush(node); return ReduceArrayPush(node);
case kDateGetTime: case kDateGetTime:
return ReduceDateGetTime(node); return ReduceDateGetTime(node);
case kFunctionHasInstance:
return ReduceFunctionHasInstance(node);
break;
case kGlobalIsFinite: case kGlobalIsFinite:
reduction = ReduceGlobalIsFinite(node); reduction = ReduceGlobalIsFinite(node);
break; break;
......
...@@ -58,7 +58,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final ...@@ -58,7 +58,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceArrayPop(Node* node); Reduction ReduceArrayPop(Node* node);
Reduction ReduceArrayPush(Node* node); Reduction ReduceArrayPush(Node* node);
Reduction ReduceDateGetTime(Node* node); Reduction ReduceDateGetTime(Node* node);
Reduction ReduceFunctionHasInstance(Node* node);
Reduction ReduceGlobalIsFinite(Node* node); Reduction ReduceGlobalIsFinite(Node* node);
Reduction ReduceGlobalIsNaN(Node* node); Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceMathAbs(Node* node); Reduction ReduceMathAbs(Node* node);
......
...@@ -189,6 +189,35 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { ...@@ -189,6 +189,35 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
return reduction.Changed() ? reduction : Changed(node); return reduction.Changed() ? reduction : Changed(node);
} }
// ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) {
DCHECK_EQ(IrOpcode::kJSCallFunction, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 1);
Node* object = (node->op()->ValueInputCount() >= 3)
? NodeProperties::GetValueInput(node, 2)
: jsgraph()->UndefinedConstant();
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
// TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
// stack trace doesn't contain the @@hasInstance call; we have the
// corresponding bug in the baseline case. Some massaging of the frame
// state would be necessary here.
// Morph this {node} into a JSOrdinaryHasInstance node.
node->ReplaceInput(0, receiver);
node->ReplaceInput(1, object);
node->ReplaceInput(2, context);
node->ReplaceInput(3, frame_state);
node->ReplaceInput(4, effect);
node->ReplaceInput(5, control);
node->TrimInputCount(6);
NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
return Changed(node);
}
namespace { namespace {
// TODO(turbofan): Shall we move this to the NodeProperties? Or some (untyped) // TODO(turbofan): Shall we move this to the NodeProperties? Or some (untyped)
...@@ -280,6 +309,8 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) { ...@@ -280,6 +309,8 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
return ReduceFunctionPrototypeApply(node); return ReduceFunctionPrototypeApply(node);
case Builtins::kFunctionPrototypeCall: case Builtins::kFunctionPrototypeCall:
return ReduceFunctionPrototypeCall(node); return ReduceFunctionPrototypeCall(node);
case Builtins::kFunctionPrototypeHasInstance:
return ReduceFunctionPrototypeHasInstance(node);
case Builtins::kNumberConstructor: case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node); return ReduceNumberConstructor(node);
case Builtins::kObjectPrototypeGetProto: case Builtins::kObjectPrototypeGetProto:
......
...@@ -44,6 +44,7 @@ class JSCallReducer final : public AdvancedReducer { ...@@ -44,6 +44,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceNumberConstructor(Node* node); Reduction ReduceNumberConstructor(Node* node);
Reduction ReduceFunctionPrototypeApply(Node* node); Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node); Reduction ReduceFunctionPrototypeCall(Node* node);
Reduction ReduceFunctionPrototypeHasInstance(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node); Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceJSCallConstruct(Node* node); Reduction ReduceJSCallConstruct(Node* node);
Reduction ReduceJSCallFunction(Node* node); Reduction ReduceJSCallFunction(Node* node);
......
...@@ -71,6 +71,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) { ...@@ -71,6 +71,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
switch (node->opcode()) { switch (node->opcode()) {
case IrOpcode::kJSInstanceOf: case IrOpcode::kJSInstanceOf:
return ReduceJSInstanceOf(node); return ReduceJSInstanceOf(node);
case IrOpcode::kJSOrdinaryHasInstance:
return ReduceJSOrdinaryHasInstance(node);
case IrOpcode::kJSLoadContext: case IrOpcode::kJSLoadContext:
return ReduceJSLoadContext(node); return ReduceJSLoadContext(node);
case IrOpcode::kJSLoadNamed: case IrOpcode::kJSLoadNamed:
...@@ -133,7 +135,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -133,7 +135,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
NodeProperties::ReplaceValueInput(node, object, 1); NodeProperties::ReplaceValueInput(node, object, 1);
NodeProperties::ReplaceEffectInput(node, effect); NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
return Changed(node); Reduction const reduction = ReduceJSOrdinaryHasInstance(node);
return reduction.Changed() ? reduction : Changed(node);
} }
} else if (access_info.IsDataConstant()) { } else if (access_info.IsDataConstant()) {
DCHECK(access_info.constant()->IsCallable()); DCHECK(access_info.constant()->IsCallable());
...@@ -174,6 +177,31 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { ...@@ -174,6 +177,31 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
return NoChange(); return NoChange();
} }
Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
Node* node) {
DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
Node* constructor = NodeProperties::GetValueInput(node, 0);
Node* object = NodeProperties::GetValueInput(node, 1);
// Check if the {constructor} is a JSBoundFunction.
HeapObjectMatcher m(constructor);
if (m.HasValue() && m.Value()->IsJSBoundFunction()) {
// OrdinaryHasInstance on bound functions turns into a recursive
// invocation of the instanceof operator again.
// ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2.
Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value());
Handle<JSReceiver> bound_target_function(function->bound_target_function());
NodeProperties::ReplaceValueInput(node, object, 0);
NodeProperties::ReplaceValueInput(
node, jsgraph()->HeapConstant(bound_target_function), 1);
NodeProperties::ChangeOp(node, javascript()->InstanceOf());
Reduction const reduction = ReduceJSInstanceOf(node);
return reduction.Changed() ? reduction : Changed(node);
}
return NoChange();
}
Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
ContextAccess const& access = ContextAccessOf(node->op()); ContextAccess const& access = ContextAccessOf(node->op());
......
...@@ -54,6 +54,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer { ...@@ -54,6 +54,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
private: private:
Reduction ReduceJSInstanceOf(Node* node); Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node);
Reduction ReduceJSLoadContext(Node* node); Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSLoadNamed(Node* node); Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSStoreNamed(Node* node); Reduction ReduceJSStoreNamed(Node* node);
......
// Copyright 2016 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
function Bar() {}
var Foo = Bar.bind(null);
// TurboFan will optimize this to false via constant-folding the
// OrdinaryHasInstance call inside Function.prototype[@@hasInstance].
function foo() { return 1 instanceof Foo; }
assertEquals(false, foo());
assertEquals(false, foo());
%OptimizeFunctionOnNextCall(foo);
assertEquals(false, 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