| Index: src/compiler/js-for-in-lowering.cc
|
| diff --git a/src/compiler/js-for-in-lowering.cc b/src/compiler/js-for-in-lowering.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2a64dfd3827c9332862097be7f66777460427f6b
|
| --- /dev/null
|
| +++ b/src/compiler/js-for-in-lowering.cc
|
| @@ -0,0 +1,305 @@
|
| +// Copyright 2017 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.
|
| +
|
| +#include "src/compiler/js-for-in-lowering.h"
|
| +
|
| +#include "src/code-factory.h"
|
| +#include "src/compiler/access-builder.h"
|
| +#include "src/compiler/common-operator.h"
|
| +#include "src/compiler/graph.h"
|
| +#include "src/compiler/js-graph.h"
|
| +#include "src/compiler/linkage.h"
|
| +#include "src/compiler/node-properties.h"
|
| +#include "src/compiler/simplified-operator.h"
|
| +
|
| +namespace v8 {
|
| +namespace internal {
|
| +namespace compiler {
|
| +
|
| +Reduction JSForInLowering::Reduce(Node* node) {
|
| + switch (node->opcode()) {
|
| + case IrOpcode::kJSForInHasOwnProperty:
|
| + return ReduceJSForInHasOwnProperty(node);
|
| + case IrOpcode::kJSForInLoadProperty:
|
| + return ReduceJSForInLoadProperty(node);
|
| + case IrOpcode::kJSForInNext:
|
| + return ReduceJSForInNext(node);
|
| + case IrOpcode::kJSForInPrepare:
|
| + return ReduceJSForInPrepare(node);
|
| + default:
|
| + break;
|
| + }
|
| + return NoChange();
|
| +}
|
| +
|
| +Reduction JSForInLowering::ReduceJSForInHasOwnProperty(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSForInHasOwnProperty, node->opcode());
|
| + Node* target = NodeProperties::GetValueInput(node, 0);
|
| + Node* receiver = NodeProperties::GetValueInput(node, 1);
|
| + Node* name = NodeProperties::GetValueInput(node, 2);
|
| + Node* enumerator = NodeProperties::GetValueInput(node, 3);
|
| + Node* frame_state = NodeProperties::GetFrameStateInput(node);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // Load the map of the {receiver}.
|
| + Node* receiver_map = effect =
|
| + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| + receiver, effect, control);
|
| +
|
| + // Check if the expected map still matches that of the {receiver}.
|
| + Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
|
| + enumerator);
|
| + Node* branch0 =
|
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
|
| +
|
| + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
| + Node* etrue0 = effect;
|
| + Node* vtrue0;
|
| + {
|
| + // Don't need filtering since {enumerator} still matches the {receiver} map.
|
| + vtrue0 = jsgraph()->TrueConstant();
|
| + }
|
| +
|
| + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
| + Node* efalse0 = effect;
|
| + Node* vfalse0;
|
| + {
|
| + // We need to call Object.prototype.hasOwnProperty here.
|
| + CallDescriptor const* const desc = Linkage::GetJSCallDescriptor(
|
| + graph()->zone(), false, 2, CallDescriptor::kNeedsFrameState);
|
| + Node* context = efalse0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
|
| + efalse0, if_false0);
|
| + vfalse0 = efalse0 = graph()->NewNode(common()->Call(desc), target, receiver,
|
| + name, jsgraph()->UndefinedConstant(),
|
| + jsgraph()->OneConstant(), context,
|
| + frame_state, efalse0, if_false0);
|
| + NodeProperties::SetType(vfalse0, Type::Boolean());
|
| +
|
| + // Update potential {IfException} uses of {node} to point to the above
|
| + // Object.prototype.hasOwnProperty call node instead.
|
| + Node* if_exception = nullptr;
|
| + if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
|
| + if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0);
|
| + NodeProperties::ReplaceControlInput(if_exception, vfalse0);
|
| + NodeProperties::ReplaceEffectInput(if_exception, efalse0);
|
| + Revisit(if_exception);
|
| + }
|
| + }
|
| +
|
| + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
|
| + effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
|
| + Node* value =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0,
|
| + vfalse0, control);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +Reduction JSForInLowering::ReduceJSForInLoadProperty(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSForInLoadProperty, node->opcode());
|
| + Node* receiver = NodeProperties::GetValueInput(node, 0);
|
| + Node* name = NodeProperties::GetValueInput(node, 1);
|
| + Node* enumerator = NodeProperties::GetValueInput(node, 2);
|
| + Node* index = NodeProperties::GetValueInput(node, 3);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* frame_state = NodeProperties::GetFrameStateInput(node);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // We know that the {index} is in Unsigned32 range here, otherwise executing
|
| + // the JSForInNext wouldn't be valid. Unfortunately due to OSR and generators
|
| + // this is not always reflected in the types, hence we might need to rename
|
| + // the {index} here.
|
| + if (!NodeProperties::GetType(index)->Is(Type::Unsigned32())) {
|
| + index = graph()->NewNode(common()->TypeGuard(Type::Unsigned32()), index,
|
| + control);
|
| + }
|
| +
|
| + // Load the map of the {receiver}.
|
| + Node* receiver_map = effect =
|
| + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| + receiver, effect, control);
|
| +
|
| + // Check if the expected map still matches that of the {receiver}.
|
| + Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
|
| + enumerator);
|
| + Node* branch0 =
|
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
|
| +
|
| + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
| + Node* etrue0 = effect;
|
| + Node* vtrue0;
|
| + {
|
| + // We are using the enumeration cache, so we can just load the value
|
| + // for the property {name} on {receiver} by loading the FieldIndex
|
| + // from the indices part of the enum cache.
|
| + Node* receiver_descriptors = etrue0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForMapDescriptors()),
|
| + receiver_map, etrue0, if_true0);
|
| + Node* receiver_enum_cache = etrue0 = graph()->NewNode(
|
| + simplified()->LoadField(AccessBuilder::ForDescriptorArrayEnumCache()),
|
| + receiver_descriptors, etrue0, if_true0);
|
| + Node* receiver_enum_indices = etrue0 = graph()->NewNode(
|
| + simplified()->LoadField(
|
| + AccessBuilder::ForDescriptorArrayEnumCacheBridgeIndicesCache()),
|
| + receiver_enum_cache, etrue0, if_true0);
|
| + // TODO(bmeurer): Weird hack!
|
| + receiver_enum_indices = etrue0 =
|
| + graph()->NewNode(simplified()->CheckHeapObject(), receiver_enum_indices,
|
| + etrue0, if_true0);
|
| + index = etrue0 = graph()->NewNode(
|
| + simplified()->LoadElement(
|
| + AccessBuilder::ForFixedArrayElement(FAST_SMI_ELEMENTS)),
|
| + receiver_enum_indices, index, etrue0, if_true0);
|
| + vtrue0 = etrue0 = graph()->NewNode(simplified()->LoadFieldByIndex(),
|
| + receiver, index, etrue0, if_true0);
|
| + }
|
| +
|
| + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
| + Node* efalse0 = effect;
|
| + Node* vfalse0;
|
| + {
|
| + // Load the property with the given {name} from the {receiver} using
|
| + // the GetProperty stub.
|
| + Callable const callable = CodeFactory::GetProperty(isolate());
|
| + CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
|
| + isolate(), graph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNeedsFrameState);
|
| + vfalse0 = efalse0 = graph()->NewNode(
|
| + common()->Call(desc), jsgraph()->HeapConstant(callable.code()),
|
| + receiver, name, context, frame_state, efalse0, if_false0);
|
| + NodeProperties::SetType(vfalse0, Type::NonInternal());
|
| +
|
| + // Update potential {IfException} uses of {node} to point to the ahove
|
| + // GetProperty stub call node instead.
|
| + Node* if_exception = nullptr;
|
| + if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
|
| + if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0);
|
| + NodeProperties::ReplaceControlInput(if_exception, vfalse0);
|
| + NodeProperties::ReplaceEffectInput(if_exception, efalse0);
|
| + Revisit(if_exception);
|
| + }
|
| + }
|
| +
|
| + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
|
| + effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
|
| + Node* value =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0,
|
| + vfalse0, control);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +Reduction JSForInLowering::ReduceJSForInNext(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSForInNext, node->opcode());
|
| + Node* receiver = NodeProperties::GetValueInput(node, 0);
|
| + Node* cache_array = NodeProperties::GetValueInput(node, 1);
|
| + Node* cache_type = NodeProperties::GetValueInput(node, 2);
|
| + Node* index = NodeProperties::GetValueInput(node, 3);
|
| + Node* context = NodeProperties::GetContextInput(node);
|
| + Node* frame_state = NodeProperties::GetFrameStateInput(node);
|
| + Node* effect = NodeProperties::GetEffectInput(node);
|
| + Node* control = NodeProperties::GetControlInput(node);
|
| +
|
| + // We know that the {index} is in Unsigned32 range here, otherwise executing
|
| + // the JSForInNext wouldn't be valid. Unfortunately due to OSR and generators
|
| + // this is not always reflected in the types, hence we might need to rename
|
| + // the {index} here.
|
| + if (!NodeProperties::GetType(index)->Is(Type::Unsigned32())) {
|
| + index = graph()->NewNode(common()->TypeGuard(Type::Unsigned32()), index,
|
| + control);
|
| + }
|
| +
|
| + // Load the next {key} from the {cache_array}.
|
| + Node* key = effect = graph()->NewNode(
|
| + simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
|
| + cache_array, index, effect, control);
|
| +
|
| + // Load the map of the {receiver}.
|
| + Node* receiver_map = effect =
|
| + graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
|
| + receiver, effect, control);
|
| +
|
| + // Check if the expected map still matches that of the {receiver}.
|
| + Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), receiver_map,
|
| + cache_type);
|
| + Node* branch0 =
|
| + graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
|
| +
|
| + Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
| + Node* etrue0;
|
| + Node* vtrue0;
|
| + {
|
| + // Don't need filtering since expected map still matches that of the
|
| + // {receiver}; in this case the {key} is definitely a String.
|
| + etrue0 = effect;
|
| + vtrue0 =
|
| + graph()->NewNode(common()->TypeGuard(Type::String()), key, if_true0);
|
| + }
|
| +
|
| + Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
| + Node* efalse0;
|
| + Node* vfalse0;
|
| + {
|
| + // Filter the {key} to check if it's still a valid property of the
|
| + // {receiver} (does the ToName conversion implicitly).
|
| + Callable const callable = CodeFactory::ForInFilter(isolate());
|
| + CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
|
| + isolate(), graph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNeedsFrameState);
|
| + vfalse0 = efalse0 = graph()->NewNode(
|
| + common()->Call(desc), jsgraph()->HeapConstant(callable.code()), key,
|
| + receiver, context, frame_state, effect, if_false0);
|
| + NodeProperties::SetType(vfalse0, Type::StringOrUndefined());
|
| +
|
| + // Update potential {IfException} uses of {node} to point to the ahove
|
| + // ForInFilter stub call node instead.
|
| + Node* if_exception = nullptr;
|
| + if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
|
| + if_false0 = graph()->NewNode(common()->IfSuccess(), vfalse0);
|
| + NodeProperties::ReplaceControlInput(if_exception, vfalse0);
|
| + NodeProperties::ReplaceEffectInput(if_exception, efalse0);
|
| + Revisit(if_exception);
|
| + }
|
| + }
|
| +
|
| + control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
|
| + effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
|
| + Node* value =
|
| + graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0,
|
| + vfalse0, control);
|
| + ReplaceWithValue(node, value, effect, control);
|
| + return Replace(value);
|
| +}
|
| +
|
| +Reduction JSForInLowering::ReduceJSForInPrepare(Node* node) {
|
| + DCHECK_EQ(IrOpcode::kJSForInPrepare, node->opcode());
|
| + Callable const callable = CodeFactory::ForInPrepare(isolate());
|
| + CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
|
| + isolate(), graph()->zone(), callable.descriptor(), 0,
|
| + CallDescriptor::kNeedsFrameState, node->op()->properties(),
|
| + MachineType::AnyTagged(), 3);
|
| + Node* stub = jsgraph()->HeapConstant(callable.code());
|
| + node->InsertInput(graph()->zone(), 0, stub);
|
| + NodeProperties::ChangeOp(node, common()->Call(desc));
|
| + return Changed(node);
|
| +}
|
| +
|
| +CommonOperatorBuilder* JSForInLowering::common() const {
|
| + return jsgraph()->common();
|
| +}
|
| +
|
| +Graph* JSForInLowering::graph() const { return jsgraph()->graph(); }
|
| +
|
| +Isolate* JSForInLowering::isolate() const { return jsgraph()->isolate(); }
|
| +
|
| +SimplifiedOperatorBuilder* JSForInLowering::simplified() const {
|
| + return jsgraph()->simplified();
|
| +}
|
| +
|
| +} // namespace compiler
|
| +} // namespace internal
|
| +} // namespace v8
|
|
|