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 |