| 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 | 
|  |