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