[turbofan] Do constant-folding of JSHasInPrototypeChain early.

We need to constant-fold JSHasInPrototypeChain nodes early during
inlining, otherwise we already miss a couple of optimization
opportunities if we wait until after typing. This moves the
constant-folding part of the JSHasInPrototypeChain lowering back to
JSNativeContextSpecialization, where it was before the changes in
https://codereview.chromium.org/2934893002 (part of
JSOrdinaryHasInstance lowering back then).

BUG=v8:5269,v8:5989,v8:6483,chromium:733158
R=jgruber@chromium.org

Review-Url: https://codereview.chromium.org/2943293002
Cr-Commit-Position: refs/heads/master@{#45989}
This commit is contained in:
bmeurer 2017-06-19 01:00:07 -07:00 committed by Commit Bot
parent a9b9c7ab8c
commit 53b6f27674
4 changed files with 90 additions and 76 deletions

View File

@ -76,6 +76,8 @@ Reduction JSNativeContextSpecialization::Reduce(Node* node) {
return ReduceJSGetSuperConstructor(node);
case IrOpcode::kJSInstanceOf:
return ReduceJSInstanceOf(node);
case IrOpcode::kJSHasInPrototypeChain:
return ReduceJSHasInPrototypeChain(node);
case IrOpcode::kJSOrdinaryHasInstance:
return ReduceJSOrdinaryHasInstance(node);
case IrOpcode::kJSLoadContext:
@ -257,6 +259,80 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
return NoChange();
}
JSNativeContextSpecialization::InferHasInPrototypeChainResult
JSNativeContextSpecialization::InferHasInPrototypeChain(
Node* receiver, Node* effect, Handle<HeapObject> prototype) {
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain;
// Check if either all or none of the {receiver_maps} have the given
// {prototype} in their prototype chain.
bool all = true;
bool none = true;
for (size_t i = 0; i < receiver_maps.size(); ++i) {
Handle<Map> receiver_map = receiver_maps[i];
if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
return kMayBeInPrototypeChain;
}
if (result == NodeProperties::kUnreliableReceiverMaps) {
// In case of an unreliable {result} we need to ensure that all
// {receiver_maps} are stable, because otherwise we cannot trust
// the {receiver_maps} information, since arbitrary side-effects
// may have happened.
if (!receiver_map->is_stable()) {
return kMayBeInPrototypeChain;
}
}
for (PrototypeIterator j(receiver_map);; j.Advance()) {
if (j.IsAtEnd()) {
all = false;
break;
}
Handle<HeapObject> const current =
PrototypeIterator::GetCurrent<HeapObject>(j);
if (current.is_identical_to(prototype)) {
none = false;
break;
}
if (!current->map()->is_stable() ||
current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
return kMayBeInPrototypeChain;
}
}
}
DCHECK_IMPLIES(all, !none);
DCHECK_IMPLIES(none, !all);
if (all) return kIsInPrototypeChain;
if (none) return kIsNotInPrototypeChain;
return kMayBeInPrototypeChain;
}
Reduction JSNativeContextSpecialization::ReduceJSHasInPrototypeChain(
Node* node) {
DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
Node* value = NodeProperties::GetValueInput(node, 0);
Node* prototype = NodeProperties::GetValueInput(node, 1);
Node* effect = NodeProperties::GetEffectInput(node);
// Check if we can constant-fold the prototype chain walk
// for the given {value} and the {prototype}.
HeapObjectMatcher m(prototype);
if (m.HasValue()) {
InferHasInPrototypeChainResult result =
InferHasInPrototypeChain(value, effect, m.Value());
if (result != kMayBeInPrototypeChain) {
Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain);
ReplaceWithValue(node, value);
return Replace(value);
}
}
return NoChange();
}
Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
Node* node) {
DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode());
@ -302,7 +378,8 @@ Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance(
NodeProperties::ReplaceValueInput(node, object, 0);
NodeProperties::ReplaceValueInput(node, prototype, 1);
NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
return Changed(node);
Reduction const reduction = ReduceJSHasInPrototypeChain(node);
return reduction.Changed() ? reduction : Changed(node);
}
}

View File

@ -56,6 +56,7 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
Reduction ReduceJSAdd(Node* node);
Reduction ReduceJSGetSuperConstructor(Node* node);
Reduction ReduceJSInstanceOf(Node* node);
Reduction ReduceJSHasInPrototypeChain(Node* node);
Reduction ReduceJSOrdinaryHasInstance(Node* node);
Reduction ReduceJSLoadContext(Node* node);
Reduction ReduceJSLoadGlobal(Node* node);
@ -179,6 +180,17 @@ class JSNativeContextSpecialization final : public AdvancedReducer {
// program location.
MaybeHandle<Map> InferReceiverRootMap(Node* receiver);
// Checks if we know at compile time that the {receiver} either definitely
// has the {prototype} in it's prototype chain, or the {receiver} definitely
// doesn't have the {prototype} in it's prototype chain.
enum InferHasInPrototypeChainResult {
kIsInPrototypeChain,
kIsNotInPrototypeChain,
kMayBeInPrototypeChain
};
InferHasInPrototypeChainResult InferHasInPrototypeChain(
Node* receiver, Node* effect, Handle<HeapObject> prototype);
// Script context lookup logic.
struct ScriptContextTableLookupResult;
bool LookupInScriptContextTable(Handle<Name> name,

View File

@ -1390,63 +1390,11 @@ Reduction JSTypedLowering::ReduceJSStoreProperty(Node* node) {
return NoChange();
}
JSTypedLowering::InferHasInPrototypeChainResult
JSTypedLowering::InferHasInPrototypeChain(Node* receiver, Node* effect,
Handle<HeapObject> prototype) {
ZoneHandleSet<Map> receiver_maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
if (result == NodeProperties::kNoReceiverMaps) return kMayBeInPrototypeChain;
// Check if either all or none of the {receiver_maps} have the given
// {prototype} in their prototype chain.
bool all = true;
bool none = true;
for (size_t i = 0; i < receiver_maps.size(); ++i) {
Handle<Map> receiver_map = receiver_maps[i];
if (receiver_map->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
return kMayBeInPrototypeChain;
}
if (result == NodeProperties::kUnreliableReceiverMaps) {
// In case of an unreliable {result} we need to ensure that all
// {receiver_maps} are stable, because otherwise we cannot trust
// the {receiver_maps} information, since arbitrary side-effects
// may have happened.
if (!receiver_map->is_stable()) {
return kMayBeInPrototypeChain;
}
}
for (PrototypeIterator j(receiver_map);; j.Advance()) {
if (j.IsAtEnd()) {
all = false;
break;
}
Handle<HeapObject> const current =
PrototypeIterator::GetCurrent<HeapObject>(j);
if (current.is_identical_to(prototype)) {
none = false;
break;
}
if (!current->map()->is_stable() ||
current->map()->instance_type() <= LAST_SPECIAL_RECEIVER_TYPE) {
return kMayBeInPrototypeChain;
}
}
}
DCHECK_IMPLIES(all, !none);
DCHECK_IMPLIES(none, !all);
if (all) return kIsInPrototypeChain;
if (none) return kIsNotInPrototypeChain;
return kMayBeInPrototypeChain;
}
Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node) {
DCHECK_EQ(IrOpcode::kJSHasInPrototypeChain, node->opcode());
Node* value = NodeProperties::GetValueInput(node, 0);
Type* value_type = NodeProperties::GetType(value);
Node* prototype = NodeProperties::GetValueInput(node, 1);
Type* prototype_type = NodeProperties::GetType(prototype);
Node* context = NodeProperties::GetContextInput(node);
Node* frame_state = NodeProperties::GetFrameStateInput(node);
Node* effect = NodeProperties::GetEffectInput(node);
@ -1460,18 +1408,6 @@ Reduction JSTypedLowering::ReduceJSHasInPrototypeChain(Node* node) {
return Replace(value);
}
// Check if we can constant-fold the prototype chain walk
// for the given {value} and the {prototype}.
if (prototype_type->IsHeapConstant()) {
InferHasInPrototypeChainResult result = InferHasInPrototypeChain(
value, effect, prototype_type->AsHeapConstant()->Value());
if (result != kMayBeInPrototypeChain) {
Node* value = jsgraph()->BooleanConstant(result == kIsInPrototypeChain);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
}
Node* check0 = graph()->NewNode(simplified()->ObjectIsSmi(), value);
Node* branch0 =
graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);

View File

@ -94,17 +94,6 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
// Helper for ReduceJSLoadModule and ReduceJSStoreModule.
Node* BuildGetModuleCell(Node* node);
// Checks if we know at compile time that the {receiver} either definitely
// has the {prototype} in it's prototype chain, or the {receiver} definitely
// doesn't have the {prototype} in it's prototype chain.
enum InferHasInPrototypeChainResult {
kIsInPrototypeChain,
kIsNotInPrototypeChain,
kMayBeInPrototypeChain
};
InferHasInPrototypeChainResult InferHasInPrototypeChain(
Node* receiver, Node* effect, Handle<HeapObject> prototype);
Factory* factory() const;
Graph* graph() const;
JSGraph* jsgraph() const { return jsgraph_; }