[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}
This commit is contained in:
bmeurer 2016-11-29 03:58:25 -08:00 committed by Commit bot
parent 95c0ecee66
commit 719d6c1d58
7 changed files with 79 additions and 33 deletions

View File

@ -964,34 +964,6 @@ Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) {
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 )
Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) {
JSCallReduction r(node);
@ -1875,9 +1847,6 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
return ReduceArrayPush(node);
case kDateGetTime:
return ReduceDateGetTime(node);
case kFunctionHasInstance:
return ReduceFunctionHasInstance(node);
break;
case kGlobalIsFinite:
reduction = ReduceGlobalIsFinite(node);
break;

View File

@ -58,7 +58,6 @@ class V8_EXPORT_PRIVATE JSBuiltinReducer final
Reduction ReduceArrayPop(Node* node);
Reduction ReduceArrayPush(Node* node);
Reduction ReduceDateGetTime(Node* node);
Reduction ReduceFunctionHasInstance(Node* node);
Reduction ReduceGlobalIsFinite(Node* node);
Reduction ReduceGlobalIsNaN(Node* node);
Reduction ReduceMathAbs(Node* node);

View File

@ -189,6 +189,35 @@ Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* 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 {
// TODO(turbofan): Shall we move this to the NodeProperties? Or some (untyped)
@ -280,6 +309,8 @@ Reduction JSCallReducer::ReduceJSCallFunction(Node* node) {
return ReduceFunctionPrototypeApply(node);
case Builtins::kFunctionPrototypeCall:
return ReduceFunctionPrototypeCall(node);
case Builtins::kFunctionPrototypeHasInstance:
return ReduceFunctionPrototypeHasInstance(node);
case Builtins::kNumberConstructor:
return ReduceNumberConstructor(node);
case Builtins::kObjectPrototypeGetProto:

View File

@ -44,6 +44,7 @@ class JSCallReducer final : public AdvancedReducer {
Reduction ReduceNumberConstructor(Node* node);
Reduction ReduceFunctionPrototypeApply(Node* node);
Reduction ReduceFunctionPrototypeCall(Node* node);
Reduction ReduceFunctionPrototypeHasInstance(Node* node);
Reduction ReduceObjectPrototypeGetProto(Node* node);
Reduction ReduceJSCallConstruct(Node* node);
Reduction ReduceJSCallFunction(Node* node);

View File

@ -71,6 +71,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
switch (node->opcode()) {
case IrOpcode::kJSInstanceOf:
return ReduceJSInstanceOf(node);
case IrOpcode::kJSOrdinaryHasInstance:
return ReduceJSOrdinaryHasInstance(node);
case IrOpcode::kJSLoadContext:
return ReduceJSLoadContext(node);
case IrOpcode::kJSLoadNamed:
@ -133,7 +135,8 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
NodeProperties::ReplaceValueInput(node, object, 1);
NodeProperties::ReplaceEffectInput(node, effect);
NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
return Changed(node);
Reduction const reduction = ReduceJSOrdinaryHasInstance(node);
return reduction.Changed() ? reduction : Changed(node);
}
} else if (access_info.IsDataConstant()) {
DCHECK(access_info.constant()->IsCallable());
@ -174,6 +177,31 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
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) {
DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode());
ContextAccess const& access = ContextAccessOf(node->op());

View File

@ -54,6 +54,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
private:
Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node);
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSStoreNamed(Node* node);

View File

@ -0,0 +1,17 @@
// 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());