| Index: src/compiler/js-call-reducer.cc | 
| diff --git a/src/compiler/js-call-reducer.cc b/src/compiler/js-call-reducer.cc | 
| index edb3d7149f7fcab2410ae1d815f7ce66c5ebc8a6..a7121c2b8d440b53ecaa524d1b9ab2f8422bdfbf 100644 | 
| --- a/src/compiler/js-call-reducer.cc | 
| +++ b/src/compiler/js-call-reducer.cc | 
| @@ -7,6 +7,7 @@ | 
| #include "src/code-factory.h" | 
| #include "src/code-stubs.h" | 
| #include "src/compilation-dependencies.h" | 
| +#include "src/compiler/access-builder.h" | 
| #include "src/compiler/js-graph.h" | 
| #include "src/compiler/linkage.h" | 
| #include "src/compiler/node-matchers.h" | 
| @@ -343,6 +344,196 @@ Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) { | 
| return ReduceObjectGetPrototype(node, target); | 
| } | 
|  | 
| +std::pair<Node*, Node*> | 
| +JSCallReducer::CreateJavaScriptBuiltinContinuationFrameState( | 
| +    Handle<SharedFunctionInfo> shared, Builtins::Name name, Node* target, | 
| +    Node* context, Node** stack_parameters, int stack_parameter_count, | 
| +    Node* effect, Node* control, Node* outer_frame_state, CheckpointMode mode) { | 
| +  BailoutId bailout_id = Builtins::GetContinuationBailoutId(name); | 
| +  Callable callable = Builtins::CallableFor(isolate(), name); | 
| + | 
| +  // Lazy deopt points where the frame state is assocated with a call get an | 
| +  // additional parameter for the return result from the call that's added by | 
| +  // the deoptimizer and not explicitly specified in the frame state. Check that | 
| +  // there is not a mismatch between the number of frame state parameters and | 
| +  // the stack parameters required by the builtin taking this into account. | 
| +  DCHECK_EQ( | 
| +      Builtins::GetStackParameterCount(isolate(), name) + 1,  // add receiver | 
| +      stack_parameter_count + (mode == CREATE_CHECKPOINT ? 0 : 1)); | 
| + | 
| +  NodeVector params(local_zone_); | 
| +  // target | 
| +  params.push_back(target); | 
| +  // new target | 
| +  params.push_back(jsgraph()->UndefinedConstant()); | 
| +  // argc, remove receiver and add in return value for lazy deopts from calls | 
| +  params.push_back(jsgraph()->Constant(stack_parameter_count - | 
| +                                       (mode == CREATE_CHECKPOINT ? 1 : 0))); | 
| +  // context | 
| +  params.push_back(context); | 
| + | 
| +  for (int i = 0; i < stack_parameter_count; ++i) { | 
| +    params.push_back(stack_parameters[i]); | 
| +  } | 
| + | 
| +  const Operator* op_param = common()->StateValues( | 
| +      static_cast<int>(params.size()), SparseInputMask::Dense()); | 
| +  Node* params_node = graph()->NewNode( | 
| +      op_param, static_cast<int>(params.size()), ¶ms.front()); | 
| + | 
| +  // Add the context to the total parameter count, it is implicit in the | 
| +  // descriptor. | 
| +  const int total_parameter_count = | 
| +      stack_parameter_count + | 
| +      callable.descriptor().GetRegisterParameterCount() + 1; | 
| +  const FrameStateFunctionInfo* state_info = | 
| +      common()->CreateFrameStateFunctionInfo( | 
| +          FrameStateType::kBuiltinContinuation, total_parameter_count, 0, | 
| +          shared); | 
| +  const Operator* op = common()->FrameState( | 
| +      bailout_id, OutputFrameStateCombine::Ignore(), state_info); | 
| +  const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense()); | 
| +  Node* node0 = graph()->NewNode(op0); | 
| + | 
| +  Node* frame_state = graph()->NewNode(op, params_node, node0, node0, | 
| +                                       jsgraph()->UndefinedConstant(), target, | 
| +                                       outer_frame_state); | 
| + | 
| +  return std::make_pair( | 
| +      frame_state, mode == CREATE_CHECKPOINT | 
| +                       ? graph()->NewNode(common()->Checkpoint(), frame_state, | 
| +                                          effect, control) | 
| +                       : effect); | 
| +} | 
| + | 
| +Reduction JSCallReducer::ReduceArrayForEach(Handle<SharedFunctionInfo> shared, | 
| +                                            Node* node) { | 
| +  DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 
| +  Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); | 
| +  Node* effect = NodeProperties::GetEffectInput(node); | 
| +  Node* control = NodeProperties::GetControlInput(node); | 
| +  Node* context = NodeProperties::GetContextInput(node); | 
| + | 
| +  // Try to determine the {receiver} map. | 
| +  Node* receiver = NodeProperties::GetValueInput(node, 1); | 
| +  Node* fncallback = node->op()->ValueInputCount() > 2 | 
| +                         ? NodeProperties::GetValueInput(node, 2) | 
| +                         : jsgraph()->UndefinedConstant(); | 
| +  Node* this_arg = node->op()->ValueInputCount() > 3 | 
| +                       ? NodeProperties::GetValueInput(node, 3) | 
| +                       : jsgraph()->UndefinedConstant(); | 
| +  ZoneHandleSet<Map> receiver_maps; | 
| +  if (!NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps)) { | 
| +    return NoChange(); | 
| +  } | 
| +  if (receiver_maps.size() != 1) return NoChange(); | 
| +  Handle<Map> receiver_map(receiver_maps[0]); | 
| +  ElementsKind kind = receiver_map->elements_kind(); | 
| +  // TODO(danno): Handle holey Smi and Object fast elements kinds and double | 
| +  // packed. | 
| +  if (!IsFastPackedElementsKind(kind) || IsFastDoubleElementsKind(kind)) { | 
| +    return NoChange(); | 
| +  } | 
| + | 
| +  Node* k = jsgraph()->Constant(0); | 
| + | 
| +  Node* original_length = graph()->NewNode( | 
| +      simplified()->LoadField(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)), | 
| +      receiver, effect, control); | 
| + | 
| +  Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); | 
| +  Node* eloop = effect = | 
| +      graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); | 
| +  Node* vloop = k = graph()->NewNode( | 
| +      common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop); | 
| + | 
| +  control = loop; | 
| +  effect = eloop; | 
| + | 
| +  Node* continue_test = | 
| +      graph()->NewNode(simplified()->NumberLessThan(), k, original_length); | 
| +  Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | 
| +                                           continue_test, control); | 
| + | 
| +  Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); | 
| +  Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); | 
| +  control = if_true; | 
| + | 
| +  NodeVector checkpoint_params(local_zone_); | 
| +  checkpoint_params.push_back(receiver); | 
| +  checkpoint_params.push_back(fncallback); | 
| +  checkpoint_params.push_back(this_arg); | 
| +  checkpoint_params.push_back(k); | 
| +  checkpoint_params.push_back(original_length); | 
| +  const int stack_parameters = static_cast<int>(checkpoint_params.size()); | 
| + | 
| +  Node* frame_state; | 
| +  std::tie(frame_state, effect) = CreateJavaScriptBuiltinContinuationFrameState( | 
| +      shared, Builtins::kArrayForEachLoopEagerDeoptContinuation, | 
| +      node->InputAt(0), context, &checkpoint_params[0], stack_parameters, | 
| +      effect, control, outer_frame_state, CREATE_CHECKPOINT); | 
| + | 
| +  // Make sure the map hasn't changed during the iteration | 
| +  Node* orig_map = jsgraph()->HeapConstant(receiver_map); | 
| +  Node* array_map = effect = | 
| +      graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | 
| +                       receiver, effect, control); | 
| +  Node* check_map = | 
| +      graph()->NewNode(simplified()->ReferenceEqual(), array_map, orig_map); | 
| +  effect = | 
| +      graph()->NewNode(simplified()->CheckIf(), check_map, effect, control); | 
| + | 
| +  // Make sure that the access is still in bounds, since the callback could have | 
| +  // changed the array's size. | 
| +  Node* length = graph()->NewNode( | 
| +      simplified()->LoadField(AccessBuilder::ForJSArrayLength(FAST_ELEMENTS)), | 
| +      receiver, effect, control); | 
| +  k = effect = | 
| +      graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control); | 
| +  USE(length); | 
| + | 
| +  // Reload the elements pointer before calling the callback, since the previous | 
| +  // callback might have resized the array causing the elements buffer to be | 
| +  // re-allocated. | 
| +  Node* elements = graph()->NewNode( | 
| +      simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, | 
| +      effect, control); | 
| + | 
| +  Node* element = graph()->NewNode( | 
| +      simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), | 
| +      elements, k, effect, control); | 
| + | 
| +  Node* next_k = | 
| +      graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->Constant(1)); | 
| +  checkpoint_params[3] = next_k; | 
| +  std::tie(frame_state, effect) = CreateJavaScriptBuiltinContinuationFrameState( | 
| +      shared, Builtins::kArrayForEachLoopLazyDeoptContinuation, | 
| +      node->InputAt(0), context, &checkpoint_params[0], stack_parameters, | 
| +      effect, control, outer_frame_state, DONT_CREATE_CHECKPOINT); | 
| + | 
| +  Node* call_back = control = effect = | 
| +      graph()->NewNode(javascript()->Call(5, 1), fncallback, this_arg, element, | 
| +                       k, receiver, context, frame_state, effect, control); | 
| +  USE(call_back); | 
| +  USE(this_arg); | 
| + | 
| +  k = next_k; | 
| + | 
| +  loop->ReplaceInput(1, control); | 
| +  vloop->ReplaceInput(1, k); | 
| +  eloop->ReplaceInput(1, effect); | 
| + | 
| +  control = if_false; | 
| +  effect = eloop; | 
| + | 
| +  NodeProperties::ReplaceUses(node, jsgraph()->UndefinedConstant(), effect, | 
| +                              control); | 
| + | 
| +  node->TrimInputCount(0); | 
| +  NodeProperties::ChangeOp(node, common()->Dead()); | 
| +  return Changed(node); | 
| +} | 
| + | 
| Reduction JSCallReducer::ReduceCallApiFunction( | 
| Node* node, Handle<FunctionTemplateInfo> function_template_info) { | 
| DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); | 
| @@ -575,6 +766,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { | 
| return ReduceObjectPrototypeGetProto(node); | 
| case Builtins::kReflectGetPrototypeOf: | 
| return ReduceReflectGetPrototypeOf(node); | 
| +        case Builtins::kArrayForEach: | 
| +          return ReduceArrayForEach(shared, node); | 
| default: | 
| break; | 
| } | 
|  |