Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(115)

Unified Diff: src/compiler/js-for-in-lowering.cc

Issue 2763533002: [WIP] JSForInLowering and JSForInHasOwnProperty.
Patch Set: Hack around the issue with indices not being available always. Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/compiler/js-for-in-lowering.h ('k') | src/compiler/js-generic-lowering.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/compiler/js-for-in-lowering.h ('k') | src/compiler/js-generic-lowering.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698