1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/compiler/js-call-reducer.h"
6
7 #include "src/api-inl.h"
8 #include "src/builtins/builtins-promise-gen.h"
9 #include "src/builtins/builtins-utils.h"
10 #include "src/code-factory.h"
11 #include "src/code-stubs.h"
12 #include "src/compiler/access-builder.h"
13 #include "src/compiler/access-info.h"
14 #include "src/compiler/allocation-builder.h"
15 #include "src/compiler/compilation-dependencies.h"
16 #include "src/compiler/js-graph.h"
17 #include "src/compiler/linkage.h"
18 #include "src/compiler/node-matchers.h"
19 #include "src/compiler/property-access-builder.h"
20 #include "src/compiler/simplified-operator.h"
21 #include "src/compiler/type-cache.h"
22 #include "src/feedback-vector-inl.h"
23 #include "src/ic/call-optimization.h"
24 #include "src/objects-inl.h"
25 #include "src/objects/arguments-inl.h"
26 #include "src/objects/js-array-buffer-inl.h"
27 #include "src/objects/js-array-inl.h"
28 #include "src/vector-slot-pair.h"
29
30 namespace v8 {
31 namespace internal {
32 namespace compiler {
33
ReduceMathUnary(Node * node,const Operator * op)34 Reduction JSCallReducer::ReduceMathUnary(Node* node, const Operator* op) {
35 CallParameters const& p = CallParametersOf(node->op());
36 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
37 return NoChange();
38 }
39 if (node->op()->ValueInputCount() < 3) {
40 Node* value = jsgraph()->NaNConstant();
41 ReplaceWithValue(node, value);
42 return Replace(value);
43 }
44
45 Node* effect = NodeProperties::GetEffectInput(node);
46 Node* control = NodeProperties::GetControlInput(node);
47 Node* input = NodeProperties::GetValueInput(node, 2);
48
49 input = effect =
50 graph()->NewNode(simplified()->SpeculativeToNumber(
51 NumberOperationHint::kNumberOrOddball, p.feedback()),
52 input, effect, control);
53 Node* value = graph()->NewNode(op, input);
54 ReplaceWithValue(node, value, effect);
55 return Replace(value);
56 }
57
ReduceMathBinary(Node * node,const Operator * op)58 Reduction JSCallReducer::ReduceMathBinary(Node* node, const Operator* op) {
59 CallParameters const& p = CallParametersOf(node->op());
60 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
61 return NoChange();
62 }
63 if (node->op()->ValueInputCount() < 3) {
64 Node* value = jsgraph()->NaNConstant();
65 ReplaceWithValue(node, value);
66 return Replace(value);
67 }
68 Node* effect = NodeProperties::GetEffectInput(node);
69 Node* control = NodeProperties::GetControlInput(node);
70
71 Node* left = NodeProperties::GetValueInput(node, 2);
72 Node* right = node->op()->ValueInputCount() > 3
73 ? NodeProperties::GetValueInput(node, 3)
74 : jsgraph()->NaNConstant();
75 left = effect =
76 graph()->NewNode(simplified()->SpeculativeToNumber(
77 NumberOperationHint::kNumberOrOddball, p.feedback()),
78 left, effect, control);
79 right = effect =
80 graph()->NewNode(simplified()->SpeculativeToNumber(
81 NumberOperationHint::kNumberOrOddball, p.feedback()),
82 right, effect, control);
83 Node* value = graph()->NewNode(op, left, right);
84 ReplaceWithValue(node, value, effect);
85 return Replace(value);
86 }
87
88 // ES6 section 20.2.2.19 Math.imul ( x, y )
ReduceMathImul(Node * node)89 Reduction JSCallReducer::ReduceMathImul(Node* node) {
90 CallParameters const& p = CallParametersOf(node->op());
91 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
92 return NoChange();
93 }
94 if (node->op()->ValueInputCount() < 3) {
95 Node* value = jsgraph()->ZeroConstant();
96 ReplaceWithValue(node, value);
97 return Replace(value);
98 }
99 Node* left = NodeProperties::GetValueInput(node, 2);
100 Node* right = node->op()->ValueInputCount() > 3
101 ? NodeProperties::GetValueInput(node, 3)
102 : jsgraph()->ZeroConstant();
103 Node* effect = NodeProperties::GetEffectInput(node);
104 Node* control = NodeProperties::GetControlInput(node);
105
106 left = effect =
107 graph()->NewNode(simplified()->SpeculativeToNumber(
108 NumberOperationHint::kNumberOrOddball, p.feedback()),
109 left, effect, control);
110 right = effect =
111 graph()->NewNode(simplified()->SpeculativeToNumber(
112 NumberOperationHint::kNumberOrOddball, p.feedback()),
113 right, effect, control);
114 left = graph()->NewNode(simplified()->NumberToUint32(), left);
115 right = graph()->NewNode(simplified()->NumberToUint32(), right);
116 Node* value = graph()->NewNode(simplified()->NumberImul(), left, right);
117 ReplaceWithValue(node, value, effect);
118 return Replace(value);
119 }
120
121 // ES6 section 20.2.2.11 Math.clz32 ( x )
ReduceMathClz32(Node * node)122 Reduction JSCallReducer::ReduceMathClz32(Node* node) {
123 CallParameters const& p = CallParametersOf(node->op());
124 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
125 return NoChange();
126 }
127 if (node->op()->ValueInputCount() < 3) {
128 Node* value = jsgraph()->Constant(32);
129 ReplaceWithValue(node, value);
130 return Replace(value);
131 }
132 Node* input = NodeProperties::GetValueInput(node, 2);
133 Node* effect = NodeProperties::GetEffectInput(node);
134 Node* control = NodeProperties::GetControlInput(node);
135
136 input = effect =
137 graph()->NewNode(simplified()->SpeculativeToNumber(
138 NumberOperationHint::kNumberOrOddball, p.feedback()),
139 input, effect, control);
140 input = graph()->NewNode(simplified()->NumberToUint32(), input);
141 Node* value = graph()->NewNode(simplified()->NumberClz32(), input);
142 ReplaceWithValue(node, value, effect);
143 return Replace(value);
144 }
145
146 // ES6 section 20.2.2.24 Math.max ( value1, value2, ...values )
147 // ES6 section 20.2.2.25 Math.min ( value1, value2, ...values )
ReduceMathMinMax(Node * node,const Operator * op,Node * empty_value)148 Reduction JSCallReducer::ReduceMathMinMax(Node* node, const Operator* op,
149 Node* empty_value) {
150 CallParameters const& p = CallParametersOf(node->op());
151 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
152 return NoChange();
153 }
154 if (node->op()->ValueInputCount() <= 2) {
155 ReplaceWithValue(node, empty_value);
156 return Replace(empty_value);
157 }
158 Node* effect = NodeProperties::GetEffectInput(node);
159 Node* control = NodeProperties::GetControlInput(node);
160
161 Node* value = effect =
162 graph()->NewNode(simplified()->SpeculativeToNumber(
163 NumberOperationHint::kNumberOrOddball, p.feedback()),
164 NodeProperties::GetValueInput(node, 2), effect, control);
165 for (int i = 3; i < node->op()->ValueInputCount(); i++) {
166 Node* input = effect = graph()->NewNode(
167 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
168 p.feedback()),
169 NodeProperties::GetValueInput(node, i), effect, control);
170 value = graph()->NewNode(op, value, input);
171 }
172
173 ReplaceWithValue(node, value, effect);
174 return Replace(value);
175 }
176
Reduce(Node * node)177 Reduction JSCallReducer::Reduce(Node* node) {
178 switch (node->opcode()) {
179 case IrOpcode::kJSConstruct:
180 return ReduceJSConstruct(node);
181 case IrOpcode::kJSConstructWithArrayLike:
182 return ReduceJSConstructWithArrayLike(node);
183 case IrOpcode::kJSConstructWithSpread:
184 return ReduceJSConstructWithSpread(node);
185 case IrOpcode::kJSCall:
186 return ReduceJSCall(node);
187 case IrOpcode::kJSCallWithArrayLike:
188 return ReduceJSCallWithArrayLike(node);
189 case IrOpcode::kJSCallWithSpread:
190 return ReduceJSCallWithSpread(node);
191 default:
192 break;
193 }
194 return NoChange();
195 }
196
Finalize()197 void JSCallReducer::Finalize() {
198 // TODO(turbofan): This is not the best solution; ideally we would be able
199 // to teach the GraphReducer about arbitrary dependencies between different
200 // nodes, even if they don't show up in the use list of the other node.
201 std::set<Node*> const waitlist = std::move(waitlist_);
202 for (Node* node : waitlist) {
203 if (!node->IsDead()) {
204 Reduction const reduction = Reduce(node);
205 if (reduction.Changed()) {
206 Node* replacement = reduction.replacement();
207 if (replacement != node) {
208 Replace(node, replacement);
209 }
210 }
211 }
212 }
213 }
214
215 // ES6 section 22.1.1 The Array Constructor
ReduceArrayConstructor(Node * node)216 Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
217 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
218 Node* target = NodeProperties::GetValueInput(node, 0);
219 CallParameters const& p = CallParametersOf(node->op());
220
221 // Turn the {node} into a {JSCreateArray} call.
222 DCHECK_LE(2u, p.arity());
223 size_t const arity = p.arity() - 2;
224 NodeProperties::ReplaceValueInput(node, target, 0);
225 NodeProperties::ReplaceValueInput(node, target, 1);
226 NodeProperties::ChangeOp(
227 node, javascript()->CreateArray(arity, MaybeHandle<AllocationSite>()));
228 return Changed(node);
229 }
230
231 // ES6 section 19.3.1.1 Boolean ( value )
ReduceBooleanConstructor(Node * node)232 Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
233 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
234 CallParameters const& p = CallParametersOf(node->op());
235
236 // Replace the {node} with a proper {ToBoolean} operator.
237 DCHECK_LE(2u, p.arity());
238 Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
239 : NodeProperties::GetValueInput(node, 2);
240 value = graph()->NewNode(simplified()->ToBoolean(), value);
241 ReplaceWithValue(node, value);
242 return Replace(value);
243 }
244
245 // ES section #sec-object-constructor
ReduceObjectConstructor(Node * node)246 Reduction JSCallReducer::ReduceObjectConstructor(Node* node) {
247 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
248 CallParameters const& p = CallParametersOf(node->op());
249 if (p.arity() < 3) return NoChange();
250 Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2)
251 : jsgraph()->UndefinedConstant();
252 Node* effect = NodeProperties::GetEffectInput(node);
253
254 // We can fold away the Object(x) call if |x| is definitely not a primitive.
255 if (NodeProperties::CanBePrimitive(isolate(), value, effect)) {
256 if (!NodeProperties::CanBeNullOrUndefined(isolate(), value, effect)) {
257 // Turn the {node} into a {JSToObject} call if we know that
258 // the {value} cannot be null or undefined.
259 NodeProperties::ReplaceValueInputs(node, value);
260 NodeProperties::ChangeOp(node, javascript()->ToObject());
261 return Changed(node);
262 }
263 } else {
264 ReplaceWithValue(node, value);
265 return Replace(value);
266 }
267 return NoChange();
268 }
269
270 // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
ReduceFunctionPrototypeApply(Node * node)271 Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
272 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
273 CallParameters const& p = CallParametersOf(node->op());
274 size_t arity = p.arity();
275 DCHECK_LE(2u, arity);
276 ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
277 if (arity == 2) {
278 // Neither thisArg nor argArray was provided.
279 convert_mode = ConvertReceiverMode::kNullOrUndefined;
280 node->ReplaceInput(0, node->InputAt(1));
281 node->ReplaceInput(1, jsgraph()->UndefinedConstant());
282 } else if (arity == 3) {
283 // The argArray was not provided, just remove the {target}.
284 node->RemoveInput(0);
285 --arity;
286 } else {
287 Node* target = NodeProperties::GetValueInput(node, 1);
288 Node* this_argument = NodeProperties::GetValueInput(node, 2);
289 Node* arguments_list = NodeProperties::GetValueInput(node, 3);
290 Node* context = NodeProperties::GetContextInput(node);
291 Node* frame_state = NodeProperties::GetFrameStateInput(node);
292 Node* effect = NodeProperties::GetEffectInput(node);
293 Node* control = NodeProperties::GetControlInput(node);
294
295 // If {arguments_list} cannot be null or undefined, we don't need
296 // to expand this {node} to control-flow.
297 if (!NodeProperties::CanBeNullOrUndefined(isolate(), arguments_list,
298 effect)) {
299 // Massage the value inputs appropriately.
300 node->ReplaceInput(0, target);
301 node->ReplaceInput(1, this_argument);
302 node->ReplaceInput(2, arguments_list);
303 while (arity-- > 3) node->RemoveInput(3);
304
305 // Morph the {node} to a {JSCallWithArrayLike}.
306 NodeProperties::ChangeOp(node,
307 javascript()->CallWithArrayLike(p.frequency()));
308 Reduction const reduction = ReduceJSCallWithArrayLike(node);
309 return reduction.Changed() ? reduction : Changed(node);
310 } else {
311 // Check whether {arguments_list} is null.
312 Node* check_null =
313 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
314 jsgraph()->NullConstant());
315 control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
316 check_null, control);
317 Node* if_null = graph()->NewNode(common()->IfTrue(), control);
318 control = graph()->NewNode(common()->IfFalse(), control);
319
320 // Check whether {arguments_list} is undefined.
321 Node* check_undefined =
322 graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
323 jsgraph()->UndefinedConstant());
324 control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
325 check_undefined, control);
326 Node* if_undefined = graph()->NewNode(common()->IfTrue(), control);
327 control = graph()->NewNode(common()->IfFalse(), control);
328
329 // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null
330 // nor undefined.
331 Node* effect0 = effect;
332 Node* control0 = control;
333 Node* value0 = effect0 = control0 = graph()->NewNode(
334 javascript()->CallWithArrayLike(p.frequency()), target, this_argument,
335 arguments_list, context, frame_state, effect0, control0);
336
337 // Lower to {JSCall} if {arguments_list} is either null or undefined.
338 Node* effect1 = effect;
339 Node* control1 =
340 graph()->NewNode(common()->Merge(2), if_null, if_undefined);
341 Node* value1 = effect1 = control1 =
342 graph()->NewNode(javascript()->Call(2), target, this_argument,
343 context, frame_state, effect1, control1);
344
345 // Rewire potential exception edges.
346 Node* if_exception = nullptr;
347 if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
348 // Create appropriate {IfException} and {IfSuccess} nodes.
349 Node* if_exception0 =
350 graph()->NewNode(common()->IfException(), control0, effect0);
351 control0 = graph()->NewNode(common()->IfSuccess(), control0);
352 Node* if_exception1 =
353 graph()->NewNode(common()->IfException(), control1, effect1);
354 control1 = graph()->NewNode(common()->IfSuccess(), control1);
355
356 // Join the exception edges.
357 Node* merge =
358 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
359 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
360 if_exception1, merge);
361 Node* phi =
362 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
363 if_exception0, if_exception1, merge);
364 ReplaceWithValue(if_exception, phi, ephi, merge);
365 }
366
367 // Join control paths.
368 control = graph()->NewNode(common()->Merge(2), control0, control1);
369 effect =
370 graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control);
371 Node* value =
372 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
373 value0, value1, control);
374 ReplaceWithValue(node, value, effect, control);
375 return Replace(value);
376 }
377 }
378 // Change {node} to the new {JSCall} operator.
379 NodeProperties::ChangeOp(
380 node,
381 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
382 // Try to further reduce the JSCall {node}.
383 Reduction const reduction = ReduceJSCall(node);
384 return reduction.Changed() ? reduction : Changed(node);
385 }
386
387 // ES section #sec-function.prototype.bind
ReduceFunctionPrototypeBind(Node * node)388 Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) {
389 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
390 // Value inputs to the {node} are as follows:
391 //
392 // - target, which is Function.prototype.bind JSFunction
393 // - receiver, which is the [[BoundTargetFunction]]
394 // - bound_this (optional), which is the [[BoundThis]]
395 // - and all the remaining value inouts are [[BoundArguments]]
396 Node* receiver = NodeProperties::GetValueInput(node, 1);
397 Node* bound_this = (node->op()->ValueInputCount() < 3)
398 ? jsgraph()->UndefinedConstant()
399 : NodeProperties::GetValueInput(node, 2);
400 Node* context = NodeProperties::GetContextInput(node);
401 Node* effect = NodeProperties::GetEffectInput(node);
402 Node* control = NodeProperties::GetControlInput(node);
403
404 // Ensure that the {receiver} is known to be a JSBoundFunction or
405 // a JSFunction with the same [[Prototype]], and all maps we've
406 // seen for the {receiver} so far indicate that {receiver} is
407 // definitely a constructor or not a constructor.
408 ZoneHandleSet<Map> receiver_maps;
409 NodeProperties::InferReceiverMapsResult result =
410 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
411 &receiver_maps);
412 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
413 DCHECK_NE(0, receiver_maps.size());
414 bool const is_constructor = receiver_maps[0]->is_constructor();
415 Handle<Object> const prototype(receiver_maps[0]->prototype(), isolate());
416 for (Handle<Map> const receiver_map : receiver_maps) {
417 // Check for consistency among the {receiver_maps}.
418 STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE);
419 if (receiver_map->prototype() != *prototype) return NoChange();
420 if (receiver_map->is_constructor() != is_constructor) return NoChange();
421 if (receiver_map->instance_type() < FIRST_FUNCTION_TYPE) return NoChange();
422
423 // Disallow binding of slow-mode functions. We need to figure out
424 // whether the length and name property are in the original state.
425 if (receiver_map->is_dictionary_map()) return NoChange();
426
427 // Check whether the length and name properties are still present
428 // as AccessorInfo objects. In that case, their values can be
429 // recomputed even if the actual value of the object changes.
430 // This mirrors the checks done in builtins-function-gen.cc at
431 // runtime otherwise.
432 Handle<DescriptorArray> descriptors(receiver_map->instance_descriptors(),
433 isolate());
434 if (descriptors->number_of_descriptors() < 2) return NoChange();
435 if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) !=
436 ReadOnlyRoots(isolate()).length_string()) {
437 return NoChange();
438 }
439 if (!descriptors->GetStrongValue(JSFunction::kLengthDescriptorIndex)
440 ->IsAccessorInfo()) {
441 return NoChange();
442 }
443 if (descriptors->GetKey(JSFunction::kNameDescriptorIndex) !=
444 ReadOnlyRoots(isolate()).name_string()) {
445 return NoChange();
446 }
447 if (!descriptors->GetStrongValue(JSFunction::kNameDescriptorIndex)
448 ->IsAccessorInfo()) {
449 return NoChange();
450 }
451 }
452
453 // Setup the map for the resulting JSBoundFunction with the
454 // correct instance {prototype}.
455 Handle<Map> map(
456 is_constructor
457 ? native_context()->bound_function_with_constructor_map()
458 : native_context()->bound_function_without_constructor_map(),
459 isolate());
460 if (map->prototype() != *prototype) {
461 map = Map::TransitionToPrototype(isolate(), map, prototype);
462 }
463
464 // Make sure we can rely on the {receiver_maps}.
465 if (result == NodeProperties::kUnreliableReceiverMaps) {
466 effect = graph()->NewNode(
467 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
468 effect, control);
469 }
470
471 // Replace the {node} with a JSCreateBoundFunction.
472 int const arity = std::max(0, node->op()->ValueInputCount() - 3);
473 int const input_count = 2 + arity + 3;
474 Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
475 inputs[0] = receiver;
476 inputs[1] = bound_this;
477 for (int i = 0; i < arity; ++i) {
478 inputs[2 + i] = NodeProperties::GetValueInput(node, 3 + i);
479 }
480 inputs[2 + arity + 0] = context;
481 inputs[2 + arity + 1] = effect;
482 inputs[2 + arity + 2] = control;
483 Node* value = effect = graph()->NewNode(
484 javascript()->CreateBoundFunction(arity, map), input_count, inputs);
485 ReplaceWithValue(node, value, effect, control);
486 return Replace(value);
487 }
488
489 // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
ReduceFunctionPrototypeCall(Node * node)490 Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
491 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
492 CallParameters const& p = CallParametersOf(node->op());
493 Node* target = NodeProperties::GetValueInput(node, 0);
494 Node* effect = NodeProperties::GetEffectInput(node);
495 Node* control = NodeProperties::GetControlInput(node);
496
497 // Change context of {node} to the Function.prototype.call context,
498 // to ensure any exception is thrown in the correct context.
499 Node* context;
500 HeapObjectMatcher m(target);
501 if (m.HasValue()) {
502 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
503 context = jsgraph()->HeapConstant(handle(function->context(), isolate()));
504 } else {
505 context = effect = graph()->NewNode(
506 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
507 effect, control);
508 }
509 NodeProperties::ReplaceContextInput(node, context);
510 NodeProperties::ReplaceEffectInput(node, effect);
511
512 // Remove the target from {node} and use the receiver as target instead, and
513 // the thisArg becomes the new target. If thisArg was not provided, insert
514 // undefined instead.
515 size_t arity = p.arity();
516 DCHECK_LE(2u, arity);
517 ConvertReceiverMode convert_mode;
518 if (arity == 2) {
519 // The thisArg was not provided, use undefined as receiver.
520 convert_mode = ConvertReceiverMode::kNullOrUndefined;
521 node->ReplaceInput(0, node->InputAt(1));
522 node->ReplaceInput(1, jsgraph()->UndefinedConstant());
523 } else {
524 // Just remove the target, which is the first value input.
525 convert_mode = ConvertReceiverMode::kAny;
526 node->RemoveInput(0);
527 --arity;
528 }
529 NodeProperties::ChangeOp(
530 node,
531 javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
532 // Try to further reduce the JSCall {node}.
533 Reduction const reduction = ReduceJSCall(node);
534 return reduction.Changed() ? reduction : Changed(node);
535 }
536
537 // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
ReduceFunctionPrototypeHasInstance(Node * node)538 Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) {
539 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
540 Node* receiver = NodeProperties::GetValueInput(node, 1);
541 Node* object = (node->op()->ValueInputCount() >= 3)
542 ? NodeProperties::GetValueInput(node, 2)
543 : jsgraph()->UndefinedConstant();
544 Node* context = NodeProperties::GetContextInput(node);
545 Node* frame_state = NodeProperties::GetFrameStateInput(node);
546 Node* effect = NodeProperties::GetEffectInput(node);
547 Node* control = NodeProperties::GetControlInput(node);
548
549 // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
550 // stack trace doesn't contain the @@hasInstance call; we have the
551 // corresponding bug in the baseline case. Some massaging of the frame
552 // state would be necessary here.
553
554 // Morph this {node} into a JSOrdinaryHasInstance node.
555 node->ReplaceInput(0, receiver);
556 node->ReplaceInput(1, object);
557 node->ReplaceInput(2, context);
558 node->ReplaceInput(3, frame_state);
559 node->ReplaceInput(4, effect);
560 node->ReplaceInput(5, control);
561 node->TrimInputCount(6);
562 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
563 return Changed(node);
564 }
565
ReduceObjectGetPrototype(Node * node,Node * object)566 Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) {
567 Node* effect = NodeProperties::GetEffectInput(node);
568
569 // Try to determine the {object} map.
570 ZoneHandleSet<Map> object_maps;
571 NodeProperties::InferReceiverMapsResult result =
572 NodeProperties::InferReceiverMaps(isolate(), object, effect,
573 &object_maps);
574 if (result != NodeProperties::kNoReceiverMaps) {
575 Handle<Map> candidate_map = object_maps[0];
576 Handle<Object> candidate_prototype(candidate_map->prototype(), isolate());
577
578 // Check if we can constant-fold the {candidate_prototype}.
579 for (size_t i = 0; i < object_maps.size(); ++i) {
580 Handle<Map> object_map = object_maps[i];
581 if (object_map->IsSpecialReceiverMap() ||
582 object_map->has_hidden_prototype() ||
583 object_map->prototype() != *candidate_prototype) {
584 // We exclude special receivers, like JSProxy or API objects that
585 // might require access checks here; we also don't want to deal
586 // with hidden prototypes at this point.
587 return NoChange();
588 }
589 // The above check also excludes maps for primitive values, which is
590 // important because we are not applying [[ToObject]] here as expected.
591 DCHECK(!object_map->IsPrimitiveMap() && object_map->IsJSReceiverMap());
592 if (result == NodeProperties::kUnreliableReceiverMaps &&
593 !object_map->is_stable()) {
594 return NoChange();
595 }
596 }
597 if (result == NodeProperties::kUnreliableReceiverMaps) {
598 for (size_t i = 0; i < object_maps.size(); ++i) {
599 dependencies()->DependOnStableMap(
600 MapRef(js_heap_broker(), object_maps[i]));
601 }
602 }
603 Node* value = jsgraph()->Constant(candidate_prototype);
604 ReplaceWithValue(node, value);
605 return Replace(value);
606 }
607
608 return NoChange();
609 }
610
611 // ES6 section 19.1.2.11 Object.getPrototypeOf ( O )
ReduceObjectGetPrototypeOf(Node * node)612 Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) {
613 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
614 Node* object = (node->op()->ValueInputCount() >= 3)
615 ? NodeProperties::GetValueInput(node, 2)
616 : jsgraph()->UndefinedConstant();
617 return ReduceObjectGetPrototype(node, object);
618 }
619
620 // ES section #sec-object.is
ReduceObjectIs(Node * node)621 Reduction JSCallReducer::ReduceObjectIs(Node* node) {
622 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
623 CallParameters const& params = CallParametersOf(node->op());
624 int const argc = static_cast<int>(params.arity() - 2);
625 Node* lhs = (argc >= 1) ? NodeProperties::GetValueInput(node, 2)
626 : jsgraph()->UndefinedConstant();
627 Node* rhs = (argc >= 2) ? NodeProperties::GetValueInput(node, 3)
628 : jsgraph()->UndefinedConstant();
629 Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs);
630 ReplaceWithValue(node, value);
631 return Replace(value);
632 }
633
634 // ES6 section B.2.2.1.1 get Object.prototype.__proto__
ReduceObjectPrototypeGetProto(Node * node)635 Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
636 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
637 Node* receiver = NodeProperties::GetValueInput(node, 1);
638 return ReduceObjectGetPrototype(node, receiver);
639 }
640
641 // ES #sec-object.prototype.hasownproperty
ReduceObjectPrototypeHasOwnProperty(Node * node)642 Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) {
643 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
644 CallParameters const& params = CallParametersOf(node->op());
645 int const argc = static_cast<int>(params.arity() - 2);
646 Node* receiver = NodeProperties::GetValueInput(node, 1);
647 Node* name = (argc >= 1) ? NodeProperties::GetValueInput(node, 2)
648 : jsgraph()->UndefinedConstant();
649 Node* effect = NodeProperties::GetEffectInput(node);
650 Node* control = NodeProperties::GetControlInput(node);
651
652 // We can optimize a call to Object.prototype.hasOwnProperty if it's being
653 // used inside a fast-mode for..in, so for code like this:
654 //
655 // for (name in receiver) {
656 // if (receiver.hasOwnProperty(name)) {
657 // ...
658 // }
659 // }
660 //
661 // If the for..in is in fast-mode, we know that the {receiver} has {name}
662 // as own property, otherwise the enumeration wouldn't include it. The graph
663 // constructed by the BytecodeGraphBuilder in this case looks like this:
664
665 // receiver
666 // ^ ^
667 // | |
668 // | +-+
669 // | |
670 // | JSToObject
671 // | ^
672 // | |
673 // | JSForInNext
674 // | ^
675 // +----+ |
676 // | |
677 // JSCall[hasOwnProperty]
678
679 // We can constant-fold the {node} to True in this case, and insert
680 // a (potentially redundant) map check to guard the fact that the
681 // {receiver} map didn't change since the dominating JSForInNext. This
682 // map check is only necessary when TurboFan cannot prove that there
683 // is no observable side effect between the {JSForInNext} and the
684 // {JSCall} to Object.prototype.hasOwnProperty.
685 //
686 // Also note that it's safe to look through the {JSToObject}, since the
687 // Object.prototype.hasOwnProperty does an implicit ToObject anyway, and
688 // these operations are not observable.
689 if (name->opcode() == IrOpcode::kJSForInNext) {
690 ForInMode const mode = ForInModeOf(name->op());
691 if (mode != ForInMode::kGeneric) {
692 Node* object = NodeProperties::GetValueInput(name, 0);
693 Node* cache_type = NodeProperties::GetValueInput(name, 2);
694 if (object->opcode() == IrOpcode::kJSToObject) {
695 object = NodeProperties::GetValueInput(object, 0);
696 }
697 if (object == receiver) {
698 // No need to repeat the map check if we can prove that there's no
699 // observable side effect between {effect} and {name].
700 if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) {
701 Node* receiver_map = effect =
702 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
703 receiver, effect, control);
704 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
705 receiver_map, cache_type);
706 effect = graph()->NewNode(
707 simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect,
708 control);
709 }
710 Node* value = jsgraph()->TrueConstant();
711 ReplaceWithValue(node, value, effect, control);
712 return Replace(value);
713 }
714 }
715 }
716
717 return NoChange();
718 }
719
720 // ES #sec-object.prototype.isprototypeof
ReduceObjectPrototypeIsPrototypeOf(Node * node)721 Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node) {
722 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
723 Node* receiver = NodeProperties::GetValueInput(node, 1);
724 Node* value = node->op()->ValueInputCount() > 2
725 ? NodeProperties::GetValueInput(node, 2)
726 : jsgraph()->UndefinedConstant();
727 Node* effect = NodeProperties::GetEffectInput(node);
728
729 // Ensure that the {receiver} is known to be a JSReceiver (so that
730 // the ToObject step of Object.prototype.isPrototypeOf is a no-op).
731 ZoneHandleSet<Map> receiver_maps;
732 NodeProperties::InferReceiverMapsResult result =
733 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
734 &receiver_maps);
735 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
736 for (size_t i = 0; i < receiver_maps.size(); ++i) {
737 if (!receiver_maps[i]->IsJSReceiverMap()) return NoChange();
738 }
739
740 // We don't check whether {value} is a proper JSReceiver here explicitly,
741 // and don't explicitly rule out Primitive {value}s, since all of them
742 // have null as their prototype, so the prototype chain walk inside the
743 // JSHasInPrototypeChain operator immediately aborts and yields false.
744 NodeProperties::ReplaceValueInput(node, value, 0);
745 NodeProperties::ReplaceValueInput(node, receiver, 1);
746 for (int i = node->op()->ValueInputCount(); i-- > 2;) {
747 node->RemoveInput(i);
748 }
749 NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
750 return Changed(node);
751 }
752
753 // ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList )
ReduceReflectApply(Node * node)754 Reduction JSCallReducer::ReduceReflectApply(Node* node) {
755 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
756 CallParameters const& p = CallParametersOf(node->op());
757 int arity = static_cast<int>(p.arity() - 2);
758 DCHECK_LE(0, arity);
759 // Massage value inputs appropriately.
760 node->RemoveInput(0);
761 node->RemoveInput(0);
762 while (arity < 3) {
763 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
764 }
765 while (arity-- > 3) {
766 node->RemoveInput(arity);
767 }
768 NodeProperties::ChangeOp(node,
769 javascript()->CallWithArrayLike(p.frequency()));
770 Reduction const reduction = ReduceJSCallWithArrayLike(node);
771 return reduction.Changed() ? reduction : Changed(node);
772 }
773
774 // ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] )
ReduceReflectConstruct(Node * node)775 Reduction JSCallReducer::ReduceReflectConstruct(Node* node) {
776 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
777 CallParameters const& p = CallParametersOf(node->op());
778 int arity = static_cast<int>(p.arity() - 2);
779 DCHECK_LE(0, arity);
780 // Massage value inputs appropriately.
781 node->RemoveInput(0);
782 node->RemoveInput(0);
783 while (arity < 2) {
784 node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
785 }
786 if (arity < 3) {
787 node->InsertInput(graph()->zone(), arity++, node->InputAt(0));
788 }
789 while (arity-- > 3) {
790 node->RemoveInput(arity);
791 }
792 NodeProperties::ChangeOp(node,
793 javascript()->ConstructWithArrayLike(p.frequency()));
794 Reduction const reduction = ReduceJSConstructWithArrayLike(node);
795 return reduction.Changed() ? reduction : Changed(node);
796 }
797
798 // ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
ReduceReflectGetPrototypeOf(Node * node)799 Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
800 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
801 Node* target = (node->op()->ValueInputCount() >= 3)
802 ? NodeProperties::GetValueInput(node, 2)
803 : jsgraph()->UndefinedConstant();
804 return ReduceObjectGetPrototype(node, target);
805 }
806
807 // ES6 section #sec-object.create Object.create(proto, properties)
ReduceObjectCreate(Node * node)808 Reduction JSCallReducer::ReduceObjectCreate(Node* node) {
809 int arg_count = node->op()->ValueInputCount();
810 Node* properties = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3)
811 : jsgraph()->UndefinedConstant();
812 if (properties != jsgraph()->UndefinedConstant()) return NoChange();
813
814 Node* effect = NodeProperties::GetEffectInput(node);
815 Node* control = NodeProperties::GetControlInput(node);
816 Node* context = NodeProperties::GetContextInput(node);
817 Node* frame_state = NodeProperties::GetFrameStateInput(node);
818 Node* prototype = arg_count >= 3 ? NodeProperties::GetValueInput(node, 2)
819 : jsgraph()->UndefinedConstant();
820 node->ReplaceInput(0, prototype);
821 node->ReplaceInput(1, context);
822 node->ReplaceInput(2, frame_state);
823 node->ReplaceInput(3, effect);
824 node->ReplaceInput(4, control);
825 node->TrimInputCount(5);
826 NodeProperties::ChangeOp(node, javascript()->CreateObject());
827 return Changed(node);
828 }
829
830 // ES section #sec-reflect.get
ReduceReflectGet(Node * node)831 Reduction JSCallReducer::ReduceReflectGet(Node* node) {
832 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
833 CallParameters const& p = CallParametersOf(node->op());
834 int arity = static_cast<int>(p.arity() - 2);
835 if (arity != 2) return NoChange();
836 Node* target = NodeProperties::GetValueInput(node, 2);
837 Node* key = NodeProperties::GetValueInput(node, 3);
838 Node* context = NodeProperties::GetContextInput(node);
839 Node* frame_state = NodeProperties::GetFrameStateInput(node);
840 Node* effect = NodeProperties::GetEffectInput(node);
841 Node* control = NodeProperties::GetControlInput(node);
842
843 // Check whether {target} is a JSReceiver.
844 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
845 Node* branch =
846 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
847
848 // Throw an appropriate TypeError if the {target} is not a JSReceiver.
849 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
850 Node* efalse = effect;
851 {
852 if_false = efalse = graph()->NewNode(
853 javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
854 jsgraph()->Constant(MessageTemplate::kCalledOnNonObject),
855 jsgraph()->HeapConstant(
856 factory()->NewStringFromAsciiChecked("Reflect.get")),
857 context, frame_state, efalse, if_false);
858 }
859
860 // Otherwise just use the existing GetPropertyStub.
861 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
862 Node* etrue = effect;
863 Node* vtrue;
864 {
865 Callable callable =
866 Builtins::CallableFor(isolate(), Builtins::kGetProperty);
867 auto call_descriptor = Linkage::GetStubCallDescriptor(
868 graph()->zone(), callable.descriptor(), 0,
869 CallDescriptor::kNeedsFrameState, Operator::kNoProperties);
870 Node* stub_code = jsgraph()->HeapConstant(callable.code());
871 vtrue = etrue = if_true =
872 graph()->NewNode(common()->Call(call_descriptor), stub_code, target,
873 key, context, frame_state, etrue, if_true);
874 }
875
876 // Rewire potential exception edges.
877 Node* on_exception = nullptr;
878 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
879 // Create appropriate {IfException} and {IfSuccess} nodes.
880 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
881 if_true = graph()->NewNode(common()->IfSuccess(), if_true);
882 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
883 if_false = graph()->NewNode(common()->IfSuccess(), if_false);
884
885 // Join the exception edges.
886 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
887 Node* ephi =
888 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
889 Node* phi =
890 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
891 extrue, exfalse, merge);
892 ReplaceWithValue(on_exception, phi, ephi, merge);
893 }
894
895 // Connect the throwing path to end.
896 if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
897 NodeProperties::MergeControlToEnd(graph(), common(), if_false);
898
899 // Continue on the regular path.
900 ReplaceWithValue(node, vtrue, etrue, if_true);
901 return Changed(vtrue);
902 }
903
904 // ES section #sec-reflect.has
ReduceReflectHas(Node * node)905 Reduction JSCallReducer::ReduceReflectHas(Node* node) {
906 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
907 CallParameters const& p = CallParametersOf(node->op());
908 int arity = static_cast<int>(p.arity() - 2);
909 DCHECK_LE(0, arity);
910 Node* target = (arity >= 1) ? NodeProperties::GetValueInput(node, 2)
911 : jsgraph()->UndefinedConstant();
912 Node* key = (arity >= 2) ? NodeProperties::GetValueInput(node, 3)
913 : jsgraph()->UndefinedConstant();
914 Node* context = NodeProperties::GetContextInput(node);
915 Node* frame_state = NodeProperties::GetFrameStateInput(node);
916 Node* effect = NodeProperties::GetEffectInput(node);
917 Node* control = NodeProperties::GetControlInput(node);
918
919 // Check whether {target} is a JSReceiver.
920 Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
921 Node* branch =
922 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
923
924 // Throw an appropriate TypeError if the {target} is not a JSReceiver.
925 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
926 Node* efalse = effect;
927 {
928 if_false = efalse = graph()->NewNode(
929 javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
930 jsgraph()->Constant(MessageTemplate::kCalledOnNonObject),
931 jsgraph()->HeapConstant(
932 factory()->NewStringFromAsciiChecked("Reflect.has")),
933 context, frame_state, efalse, if_false);
934 }
935
936 // Otherwise just use the existing {JSHasProperty} logic.
937 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
938 Node* etrue = effect;
939 Node* vtrue;
940 {
941 vtrue = etrue = if_true =
942 graph()->NewNode(javascript()->HasProperty(), target, key, context,
943 frame_state, etrue, if_true);
944 }
945
946 // Rewire potential exception edges.
947 Node* on_exception = nullptr;
948 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
949 // Create appropriate {IfException} and {IfSuccess} nodes.
950 Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
951 if_true = graph()->NewNode(common()->IfSuccess(), if_true);
952 Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
953 if_false = graph()->NewNode(common()->IfSuccess(), if_false);
954
955 // Join the exception edges.
956 Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
957 Node* ephi =
958 graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
959 Node* phi =
960 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
961 extrue, exfalse, merge);
962 ReplaceWithValue(on_exception, phi, ephi, merge);
963 }
964
965 // Connect the throwing path to end.
966 if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
967 NodeProperties::MergeControlToEnd(graph(), common(), if_false);
968
969 // Continue on the regular path.
970 ReplaceWithValue(node, vtrue, etrue, if_true);
971 return Changed(vtrue);
972 }
973
CanInlineArrayIteratingBuiltin(Isolate * isolate,Handle<Map> receiver_map)974 bool CanInlineArrayIteratingBuiltin(Isolate* isolate,
975 Handle<Map> receiver_map) {
976 if (!receiver_map->prototype()->IsJSArray()) return false;
977 Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
978 isolate);
979 return receiver_map->instance_type() == JS_ARRAY_TYPE &&
980 IsFastElementsKind(receiver_map->elements_kind()) &&
981 isolate->IsNoElementsProtectorIntact() &&
982 isolate->IsAnyInitialArrayPrototype(receiver_prototype);
983 }
984
WireInLoopStart(Node * k,Node ** control,Node ** effect)985 Node* JSCallReducer::WireInLoopStart(Node* k, Node** control, Node** effect) {
986 Node* loop = *control =
987 graph()->NewNode(common()->Loop(2), *control, *control);
988 Node* eloop = *effect =
989 graph()->NewNode(common()->EffectPhi(2), *effect, *effect, loop);
990 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
991 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
992 return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), k,
993 k, loop);
994 }
995
WireInLoopEnd(Node * loop,Node * eloop,Node * vloop,Node * k,Node * control,Node * effect)996 void JSCallReducer::WireInLoopEnd(Node* loop, Node* eloop, Node* vloop, Node* k,
997 Node* control, Node* effect) {
998 loop->ReplaceInput(1, control);
999 vloop->ReplaceInput(1, k);
1000 eloop->ReplaceInput(1, effect);
1001 }
1002
ReduceArrayForEach(Node * node,Handle<SharedFunctionInfo> shared)1003 Reduction JSCallReducer::ReduceArrayForEach(Node* node,
1004 Handle<SharedFunctionInfo> shared) {
1005 if (!FLAG_turbo_inline_array_builtins) return NoChange();
1006 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1007 CallParameters const& p = CallParametersOf(node->op());
1008 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
1009 return NoChange();
1010 }
1011
1012 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
1013 Node* effect = NodeProperties::GetEffectInput(node);
1014 Node* control = NodeProperties::GetControlInput(node);
1015 Node* context = NodeProperties::GetContextInput(node);
1016
1017 // Try to determine the {receiver} map.
1018 Node* receiver = NodeProperties::GetValueInput(node, 1);
1019 Node* fncallback = node->op()->ValueInputCount() > 2
1020 ? NodeProperties::GetValueInput(node, 2)
1021 : jsgraph()->UndefinedConstant();
1022 Node* this_arg = node->op()->ValueInputCount() > 3
1023 ? NodeProperties::GetValueInput(node, 3)
1024 : jsgraph()->UndefinedConstant();
1025 ZoneHandleSet<Map> receiver_maps;
1026 NodeProperties::InferReceiverMapsResult result =
1027 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
1028 &receiver_maps);
1029 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
1030
1031 // By ensuring that {kind} is object or double, we can be polymorphic
1032 // on different elements kinds.
1033 ElementsKind kind = receiver_maps[0]->elements_kind();
1034 if (IsSmiElementsKind(kind)) {
1035 kind = FastSmiToObjectElementsKind(kind);
1036 }
1037 for (Handle<Map> receiver_map : receiver_maps) {
1038 ElementsKind next_kind = receiver_map->elements_kind();
1039 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) {
1040 return NoChange();
1041 }
1042 if (!IsFastElementsKind(next_kind)) {
1043 return NoChange();
1044 }
1045 if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) {
1046 return NoChange();
1047 }
1048 if (IsHoleyElementsKind(next_kind)) {
1049 kind = GetHoleyElementsKind(kind);
1050 }
1051 }
1052
1053 // Install code dependencies on the {receiver} prototype maps and the
1054 // global array protector cell.
1055 dependencies()->DependOnProtector(
1056 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
1057
1058 // If we have unreliable maps, we need a map check.
1059 if (result == NodeProperties::kUnreliableReceiverMaps) {
1060 effect =
1061 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1062 receiver_maps, p.feedback()),
1063 receiver, effect, control);
1064 }
1065
1066 Node* k = jsgraph()->ZeroConstant();
1067
1068 Node* original_length = effect = graph()->NewNode(
1069 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
1070 effect, control);
1071
1072 std::vector<Node*> checkpoint_params(
1073 {receiver, fncallback, this_arg, k, original_length});
1074 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1075
1076 // Check whether the given callback function is callable. Note that this has
1077 // to happen outside the loop to make sure we also throw on empty arrays.
1078 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1079 jsgraph(), shared, Builtins::kArrayForEachLoopLazyDeoptContinuation,
1080 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1081 outer_frame_state, ContinuationFrameStateMode::LAZY);
1082 Node* check_fail = nullptr;
1083 Node* check_throw = nullptr;
1084 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
1085 &control, &check_fail, &check_throw);
1086
1087 // Start the loop.
1088 Node* vloop = k = WireInLoopStart(k, &control, &effect);
1089 Node *loop = control, *eloop = effect;
1090 checkpoint_params[3] = k;
1091
1092 Node* continue_test =
1093 graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
1094 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1095 continue_test, control);
1096
1097 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
1098 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
1099 control = if_true;
1100
1101 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1102 jsgraph(), shared, Builtins::kArrayForEachLoopEagerDeoptContinuation,
1103 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1104 outer_frame_state, ContinuationFrameStateMode::EAGER);
1105
1106 effect =
1107 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1108
1109 // Make sure the map hasn't changed during the iteration
1110 effect =
1111 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1112 receiver_maps, p.feedback()),
1113 receiver, effect, control);
1114
1115 Node* element =
1116 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
1117
1118 Node* next_k =
1119 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
1120 checkpoint_params[3] = next_k;
1121
1122 Node* hole_true = nullptr;
1123 Node* hole_false = nullptr;
1124 Node* effect_true = effect;
1125
1126 if (IsHoleyElementsKind(kind)) {
1127 // Holey elements kind require a hole check and skipping of the element in
1128 // the case of a hole.
1129 Node* check;
1130 if (IsDoubleElementsKind(kind)) {
1131 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
1132 } else {
1133 check = graph()->NewNode(simplified()->ReferenceEqual(), element,
1134 jsgraph()->TheHoleConstant());
1135 }
1136 Node* branch =
1137 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
1138 hole_true = graph()->NewNode(common()->IfTrue(), branch);
1139 hole_false = graph()->NewNode(common()->IfFalse(), branch);
1140 control = hole_false;
1141
1142 // The contract is that we don't leak "the hole" into "user JavaScript",
1143 // so we must rename the {element} here to explicitly exclude "the hole"
1144 // from the type of {element}.
1145 element = effect = graph()->NewNode(
1146 common()->TypeGuard(Type::NonInternal()), element, effect, control);
1147 }
1148
1149 frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1150 jsgraph(), shared, Builtins::kArrayForEachLoopLazyDeoptContinuation,
1151 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1152 outer_frame_state, ContinuationFrameStateMode::LAZY);
1153
1154 control = effect = graph()->NewNode(
1155 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
1156 receiver, context, frame_state, effect, control);
1157
1158 // Rewire potential exception edges.
1159 Node* on_exception = nullptr;
1160 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1161 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
1162 &check_fail, &control);
1163 }
1164
1165 if (IsHoleyElementsKind(kind)) {
1166 Node* after_call_control = control;
1167 Node* after_call_effect = effect;
1168 control = hole_true;
1169 effect = effect_true;
1170
1171 control = graph()->NewNode(common()->Merge(2), control, after_call_control);
1172 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
1173 control);
1174 }
1175
1176 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
1177
1178 control = if_false;
1179 effect = eloop;
1180
1181 // Wire up the branch for the case when IsCallable fails for the callback.
1182 // Since {check_throw} is an unconditional throw, it's impossible to
1183 // return a successful completion. Therefore, we simply connect the successful
1184 // completion to the graph end.
1185 Node* throw_node =
1186 graph()->NewNode(common()->Throw(), check_throw, check_fail);
1187 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
1188
1189 ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
1190 return Replace(jsgraph()->UndefinedConstant());
1191 }
1192
ReduceArrayReduce(Node * node,ArrayReduceDirection direction,Handle<SharedFunctionInfo> shared)1193 Reduction JSCallReducer::ReduceArrayReduce(Node* node,
1194 ArrayReduceDirection direction,
1195 Handle<SharedFunctionInfo> shared) {
1196 if (!FLAG_turbo_inline_array_builtins) return NoChange();
1197 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1198 CallParameters const& p = CallParametersOf(node->op());
1199 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
1200 return NoChange();
1201 }
1202 bool left = direction == ArrayReduceDirection::kLeft;
1203
1204 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
1205 Node* effect = NodeProperties::GetEffectInput(node);
1206 Node* control = NodeProperties::GetControlInput(node);
1207 Node* context = NodeProperties::GetContextInput(node);
1208
1209 // Try to determine the {receiver} map.
1210 Node* receiver = NodeProperties::GetValueInput(node, 1);
1211 Node* fncallback = node->op()->ValueInputCount() > 2
1212 ? NodeProperties::GetValueInput(node, 2)
1213 : jsgraph()->UndefinedConstant();
1214
1215 ZoneHandleSet<Map> receiver_maps;
1216 NodeProperties::InferReceiverMapsResult result =
1217 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
1218 &receiver_maps);
1219 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
1220
1221 ElementsKind kind = receiver_maps[0]->elements_kind();
1222 for (Handle<Map> receiver_map : receiver_maps) {
1223 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
1224 return NoChange();
1225 if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind()))
1226 return NoChange();
1227 }
1228
1229 std::function<Node*(Node*)> hole_check = [this, kind](Node* element) {
1230 if (IsDoubleElementsKind(kind)) {
1231 return graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
1232 } else {
1233 return graph()->NewNode(simplified()->ReferenceEqual(), element,
1234 jsgraph()->TheHoleConstant());
1235 }
1236 };
1237
1238 // Install code dependencies on the {receiver} prototype maps and the
1239 // global array protector cell.
1240 dependencies()->DependOnProtector(
1241 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
1242
1243 // If we have unreliable maps, we need a map check.
1244 if (result == NodeProperties::kUnreliableReceiverMaps) {
1245 effect =
1246 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1247 receiver_maps, p.feedback()),
1248 receiver, effect, control);
1249 }
1250
1251 Node* original_length = effect = graph()->NewNode(
1252 simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
1253 receiver, effect, control);
1254
1255 Node* initial_index =
1256 left ? jsgraph()->ZeroConstant()
1257 : graph()->NewNode(simplified()->NumberSubtract(), original_length,
1258 jsgraph()->OneConstant());
1259 const Operator* next_op =
1260 left ? simplified()->NumberAdd() : simplified()->NumberSubtract();
1261 Node* k = initial_index;
1262
1263 Node* check_frame_state;
1264 {
1265 Builtins::Name builtin_lazy =
1266 left ? Builtins::kArrayReduceLoopLazyDeoptContinuation
1267 : Builtins::kArrayReduceRightLoopLazyDeoptContinuation;
1268 const std::vector<Node*> checkpoint_params(
1269 {receiver, fncallback, k, original_length,
1270 jsgraph()->UndefinedConstant()});
1271 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1272 check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1273 jsgraph(), shared, builtin_lazy, node->InputAt(0), context,
1274 checkpoint_params.data(), stack_parameters - 1, outer_frame_state,
1275 ContinuationFrameStateMode::LAZY);
1276 }
1277 Node* check_fail = nullptr;
1278 Node* check_throw = nullptr;
1279 // Check whether the given callback function is callable. Note that
1280 // this has to happen outside the loop to make sure we also throw on
1281 // empty arrays.
1282 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
1283 &control, &check_fail, &check_throw);
1284
1285 // Set initial accumulator value
1286 Node* cur = jsgraph()->TheHoleConstant();
1287
1288 if (node->op()->ValueInputCount() > 3) {
1289 cur = NodeProperties::GetValueInput(node, 3);
1290 } else {
1291 // Find first/last non holey element. In case the search fails, we need a
1292 // deopt continuation.
1293 Builtins::Name builtin_eager =
1294 left ? Builtins::kArrayReducePreLoopEagerDeoptContinuation
1295 : Builtins::kArrayReduceRightPreLoopEagerDeoptContinuation;
1296 const std::vector<Node*> checkpoint_params(
1297 {receiver, fncallback, original_length});
1298 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1299 Node* find_first_element_frame_state =
1300 CreateJavaScriptBuiltinContinuationFrameState(
1301 jsgraph(), shared, builtin_eager, node->InputAt(0), context,
1302 checkpoint_params.data(), stack_parameters, outer_frame_state,
1303 ContinuationFrameStateMode::EAGER);
1304
1305 Node* vloop = k = WireInLoopStart(k, &control, &effect);
1306 Node* loop = control;
1307 Node* eloop = effect;
1308 effect = graph()->NewNode(common()->Checkpoint(),
1309 find_first_element_frame_state, effect, control);
1310 Node* continue_test =
1311 left ? graph()->NewNode(simplified()->NumberLessThan(), k,
1312 original_length)
1313 : graph()->NewNode(simplified()->NumberLessThanOrEqual(),
1314 jsgraph()->ZeroConstant(), k);
1315 effect = graph()->NewNode(
1316 simplified()->CheckIf(DeoptimizeReason::kNoInitialElement),
1317 continue_test, effect, control);
1318
1319 cur = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
1320 Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant());
1321
1322 Node* hole_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1323 hole_check(cur), control);
1324 Node* found_el = graph()->NewNode(common()->IfFalse(), hole_branch);
1325 control = found_el;
1326 Node* is_hole = graph()->NewNode(common()->IfTrue(), hole_branch);
1327
1328 WireInLoopEnd(loop, eloop, vloop, next_k, is_hole, effect);
1329 // We did the hole-check, so exclude hole from the type.
1330 cur = effect = graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
1331 cur, effect, control);
1332 k = next_k;
1333 }
1334
1335 // Start the loop.
1336 Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
1337 Node* eloop = effect =
1338 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
1339 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
1340 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
1341 Node* kloop = k = graph()->NewNode(
1342 common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
1343 Node* curloop = cur = graph()->NewNode(
1344 common()->Phi(MachineRepresentation::kTagged, 2), cur, cur, loop);
1345
1346 control = loop;
1347 effect = eloop;
1348
1349 Node* continue_test =
1350 left
1351 ? graph()->NewNode(simplified()->NumberLessThan(), k, original_length)
1352 : graph()->NewNode(simplified()->NumberLessThanOrEqual(),
1353 jsgraph()->ZeroConstant(), k);
1354
1355 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1356 continue_test, control);
1357
1358 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
1359 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
1360 control = if_true;
1361
1362 {
1363 Builtins::Name builtin_eager =
1364 left ? Builtins::kArrayReduceLoopEagerDeoptContinuation
1365 : Builtins::kArrayReduceRightLoopEagerDeoptContinuation;
1366 const std::vector<Node*> checkpoint_params(
1367 {receiver, fncallback, k, original_length, curloop});
1368 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1369 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1370 jsgraph(), shared, builtin_eager, node->InputAt(0), context,
1371 checkpoint_params.data(), stack_parameters, outer_frame_state,
1372 ContinuationFrameStateMode::EAGER);
1373 effect =
1374 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1375 }
1376
1377 // Make sure the map hasn't changed during the iteration
1378 effect = graph()->NewNode(
1379 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
1380 effect, control);
1381
1382 Node* element =
1383 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
1384
1385 Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant());
1386
1387 Node* hole_true = nullptr;
1388 Node* hole_false = nullptr;
1389 Node* effect_true = effect;
1390
1391 if (IsHoleyElementsKind(kind)) {
1392 // Holey elements kind require a hole check and skipping of the element in
1393 // the case of a hole.
1394 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
1395 hole_check(element), control);
1396 hole_true = graph()->NewNode(common()->IfTrue(), branch);
1397 hole_false = graph()->NewNode(common()->IfFalse(), branch);
1398 control = hole_false;
1399
1400 // The contract is that we don't leak "the hole" into "user JavaScript",
1401 // so we must rename the {element} here to explicitly exclude "the hole"
1402 // from the type of {element}.
1403 element = effect = graph()->NewNode(
1404 common()->TypeGuard(Type::NonInternal()), element, effect, control);
1405 }
1406
1407 Node* next_cur;
1408 {
1409 Builtins::Name builtin_lazy =
1410 left ? Builtins::kArrayReduceLoopLazyDeoptContinuation
1411 : Builtins::kArrayReduceRightLoopLazyDeoptContinuation;
1412 const std::vector<Node*> checkpoint_params(
1413 {receiver, fncallback, next_k, original_length, curloop});
1414 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1415 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1416 jsgraph(), shared, builtin_lazy, node->InputAt(0), context,
1417 checkpoint_params.data(), stack_parameters - 1, outer_frame_state,
1418 ContinuationFrameStateMode::LAZY);
1419
1420 next_cur = control = effect =
1421 graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback,
1422 jsgraph()->UndefinedConstant(), cur, element, k,
1423 receiver, context, frame_state, effect, control);
1424 }
1425
1426 // Rewire potential exception edges.
1427 Node* on_exception = nullptr;
1428 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1429 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
1430 &check_fail, &control);
1431 }
1432
1433 if (IsHoleyElementsKind(kind)) {
1434 Node* after_call_control = control;
1435 Node* after_call_effect = effect;
1436 control = hole_true;
1437 effect = effect_true;
1438
1439 control = graph()->NewNode(common()->Merge(2), control, after_call_control);
1440 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
1441 control);
1442 next_cur =
1443 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), cur,
1444 next_cur, control);
1445 }
1446
1447 k = next_k;
1448 cur = next_cur;
1449
1450 loop->ReplaceInput(1, control);
1451 kloop->ReplaceInput(1, k);
1452 curloop->ReplaceInput(1, cur);
1453 eloop->ReplaceInput(1, effect);
1454
1455 control = if_false;
1456 effect = eloop;
1457
1458 // Wire up the branch for the case when IsCallable fails for the callback.
1459 // Since {check_throw} is an unconditional throw, it's impossible to
1460 // return a successful completion. Therefore, we simply connect the successful
1461 // completion to the graph end.
1462 Node* throw_node =
1463 graph()->NewNode(common()->Throw(), check_throw, check_fail);
1464 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
1465
1466 ReplaceWithValue(node, curloop, effect, control);
1467 return Replace(curloop);
1468 }
1469
ReduceArrayMap(Node * node,Handle<SharedFunctionInfo> shared)1470 Reduction JSCallReducer::ReduceArrayMap(Node* node,
1471 Handle<SharedFunctionInfo> shared) {
1472 if (!FLAG_turbo_inline_array_builtins) return NoChange();
1473 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1474 CallParameters const& p = CallParametersOf(node->op());
1475 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
1476 return NoChange();
1477 }
1478
1479 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
1480 Node* effect = NodeProperties::GetEffectInput(node);
1481 Node* control = NodeProperties::GetControlInput(node);
1482 Node* context = NodeProperties::GetContextInput(node);
1483
1484 // Try to determine the {receiver} map.
1485 Node* receiver = NodeProperties::GetValueInput(node, 1);
1486 Node* fncallback = node->op()->ValueInputCount() > 2
1487 ? NodeProperties::GetValueInput(node, 2)
1488 : jsgraph()->UndefinedConstant();
1489 Node* this_arg = node->op()->ValueInputCount() > 3
1490 ? NodeProperties::GetValueInput(node, 3)
1491 : jsgraph()->UndefinedConstant();
1492 ZoneHandleSet<Map> receiver_maps;
1493 NodeProperties::InferReceiverMapsResult result =
1494 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
1495 &receiver_maps);
1496 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
1497
1498 // Ensure that any changes to the Array species constructor cause deopt.
1499 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
1500
1501 const ElementsKind kind = receiver_maps[0]->elements_kind();
1502
1503 for (Handle<Map> receiver_map : receiver_maps) {
1504 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
1505 return NoChange();
1506 // We can handle different maps, as long as their elements kind are the
1507 // same.
1508 if (receiver_map->elements_kind() != kind) return NoChange();
1509 }
1510
1511 if (IsHoleyElementsKind(kind)) {
1512 dependencies()->DependOnProtector(
1513 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
1514 }
1515
1516 dependencies()->DependOnProtector(
1517 PropertyCellRef(js_heap_broker(), factory()->array_species_protector()));
1518
1519 Handle<JSFunction> handle_constructor(
1520 JSFunction::cast(
1521 native_context()->GetInitialJSArrayMap(kind)->GetConstructor()),
1522 isolate());
1523 Node* array_constructor = jsgraph()->HeapConstant(handle_constructor);
1524
1525
1526 Node* k = jsgraph()->ZeroConstant();
1527
1528 // If we have unreliable maps, we need a map check.
1529 if (result == NodeProperties::kUnreliableReceiverMaps) {
1530 effect =
1531 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1532 receiver_maps, p.feedback()),
1533 receiver, effect, control);
1534 }
1535
1536 Node* original_length = effect = graph()->NewNode(
1537 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
1538 effect, control);
1539
1540 // Even though {JSCreateArray} is not marked as {kNoThrow}, we can elide the
1541 // exceptional projections because it cannot throw with the given parameters.
1542 Node* a = control = effect = graph()->NewNode(
1543 javascript()->CreateArray(1, MaybeHandle<AllocationSite>()),
1544 array_constructor, array_constructor, original_length, context,
1545 outer_frame_state, effect, control);
1546
1547 std::vector<Node*> checkpoint_params(
1548 {receiver, fncallback, this_arg, a, k, original_length});
1549 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1550
1551 // Check whether the given callback function is callable. Note that this has
1552 // to happen outside the loop to make sure we also throw on empty arrays.
1553 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1554 jsgraph(), shared, Builtins::kArrayMapLoopLazyDeoptContinuation,
1555 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1556 outer_frame_state, ContinuationFrameStateMode::LAZY);
1557 Node* check_fail = nullptr;
1558 Node* check_throw = nullptr;
1559 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect,
1560 &control, &check_fail, &check_throw);
1561
1562 // Start the loop.
1563 Node* vloop = k = WireInLoopStart(k, &control, &effect);
1564 Node *loop = control, *eloop = effect;
1565 checkpoint_params[4] = k;
1566
1567 Node* continue_test =
1568 graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
1569 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1570 continue_test, control);
1571
1572 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
1573 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
1574 control = if_true;
1575
1576 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1577 jsgraph(), shared, Builtins::kArrayMapLoopEagerDeoptContinuation,
1578 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1579 outer_frame_state, ContinuationFrameStateMode::EAGER);
1580
1581 effect =
1582 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1583
1584 // Make sure the map hasn't changed during the iteration
1585 effect =
1586 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1587 receiver_maps, p.feedback()),
1588 receiver, effect, control);
1589
1590 Node* element =
1591 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
1592
1593 Node* next_k =
1594 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
1595
1596 Node* hole_true = nullptr;
1597 Node* hole_false = nullptr;
1598 Node* effect_true = effect;
1599
1600 if (IsHoleyElementsKind(kind)) {
1601 // Holey elements kind require a hole check and skipping of the element in
1602 // the case of a hole.
1603 Node* check;
1604 if (IsDoubleElementsKind(kind)) {
1605 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
1606 } else {
1607 check = graph()->NewNode(simplified()->ReferenceEqual(), element,
1608 jsgraph()->TheHoleConstant());
1609 }
1610 Node* branch =
1611 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
1612 hole_true = graph()->NewNode(common()->IfTrue(), branch);
1613 hole_false = graph()->NewNode(common()->IfFalse(), branch);
1614 control = hole_false;
1615
1616 // The contract is that we don't leak "the hole" into "user JavaScript",
1617 // so we must rename the {element} here to explicitly exclude "the hole"
1618 // from the type of {element}.
1619 element = effect = graph()->NewNode(
1620 common()->TypeGuard(Type::NonInternal()), element, effect, control);
1621 }
1622
1623 // This frame state is dealt with by hand in
1624 // ArrayMapLoopLazyDeoptContinuation.
1625 frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1626 jsgraph(), shared, Builtins::kArrayMapLoopLazyDeoptContinuation,
1627 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1628 outer_frame_state, ContinuationFrameStateMode::LAZY);
1629
1630 Node* callback_value = control = effect = graph()->NewNode(
1631 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
1632 receiver, context, frame_state, effect, control);
1633
1634 // Rewire potential exception edges.
1635 Node* on_exception = nullptr;
1636 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1637 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
1638 &check_fail, &control);
1639 }
1640
1641 // The array {a} should be HOLEY_SMI_ELEMENTS because we'd only come into this
1642 // loop if the input array length is non-zero, and "new Array({x > 0})" always
1643 // produces a HOLEY array.
1644 Handle<Map> double_map(Map::cast(native_context()->get(
1645 Context::ArrayMapIndex(HOLEY_DOUBLE_ELEMENTS))),
1646 isolate());
1647 Handle<Map> fast_map(
1648 Map::cast(native_context()->get(Context::ArrayMapIndex(HOLEY_ELEMENTS))),
1649 isolate());
1650 effect = graph()->NewNode(
1651 simplified()->TransitionAndStoreElement(double_map, fast_map), a, k,
1652 callback_value, effect, control);
1653
1654 if (IsHoleyElementsKind(kind)) {
1655 Node* after_call_and_store_control = control;
1656 Node* after_call_and_store_effect = effect;
1657 control = hole_true;
1658 effect = effect_true;
1659
1660 control = graph()->NewNode(common()->Merge(2), control,
1661 after_call_and_store_control);
1662 effect = graph()->NewNode(common()->EffectPhi(2), effect,
1663 after_call_and_store_effect, control);
1664 }
1665
1666 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
1667
1668 control = if_false;
1669 effect = eloop;
1670
1671 // Wire up the branch for the case when IsCallable fails for the callback.
1672 // Since {check_throw} is an unconditional throw, it's impossible to
1673 // return a successful completion. Therefore, we simply connect the successful
1674 // completion to the graph end.
1675 Node* throw_node =
1676 graph()->NewNode(common()->Throw(), check_throw, check_fail);
1677 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
1678
1679 ReplaceWithValue(node, a, effect, control);
1680 return Replace(a);
1681 }
1682
ReduceArrayFilter(Node * node,Handle<SharedFunctionInfo> shared)1683 Reduction JSCallReducer::ReduceArrayFilter(Node* node,
1684 Handle<SharedFunctionInfo> shared) {
1685 if (!FLAG_turbo_inline_array_builtins) return NoChange();
1686 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1687 CallParameters const& p = CallParametersOf(node->op());
1688 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
1689 return NoChange();
1690 }
1691
1692 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
1693 Node* effect = NodeProperties::GetEffectInput(node);
1694 Node* control = NodeProperties::GetControlInput(node);
1695 Node* context = NodeProperties::GetContextInput(node);
1696 // Try to determine the {receiver} map.
1697 Node* receiver = NodeProperties::GetValueInput(node, 1);
1698 Node* fncallback = node->op()->ValueInputCount() > 2
1699 ? NodeProperties::GetValueInput(node, 2)
1700 : jsgraph()->UndefinedConstant();
1701 Node* this_arg = node->op()->ValueInputCount() > 3
1702 ? NodeProperties::GetValueInput(node, 3)
1703 : jsgraph()->UndefinedConstant();
1704 ZoneHandleSet<Map> receiver_maps;
1705 NodeProperties::InferReceiverMapsResult result =
1706 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
1707 &receiver_maps);
1708 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
1709
1710 // And ensure that any changes to the Array species constructor cause deopt.
1711 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
1712
1713 const ElementsKind kind = receiver_maps[0]->elements_kind();
1714 // The output array is packed (filter doesn't visit holes).
1715 const ElementsKind packed_kind = GetPackedElementsKind(kind);
1716
1717 for (Handle<Map> receiver_map : receiver_maps) {
1718 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map)) {
1719 return NoChange();
1720 }
1721 // We can handle different maps, as long as their elements kind are the
1722 // same.
1723 if (receiver_map->elements_kind() != kind) return NoChange();
1724 }
1725
1726 if (IsHoleyElementsKind(kind)) {
1727 dependencies()->DependOnProtector(
1728 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
1729 }
1730
1731 dependencies()->DependOnProtector(
1732 PropertyCellRef(js_heap_broker(), factory()->array_species_protector()));
1733
1734 Handle<Map> initial_map(
1735 Map::cast(native_context()->GetInitialJSArrayMap(packed_kind)),
1736 isolate());
1737
1738 Node* k = jsgraph()->ZeroConstant();
1739 Node* to = jsgraph()->ZeroConstant();
1740
1741 // If we have unreliable maps, we need a map check.
1742 if (result == NodeProperties::kUnreliableReceiverMaps) {
1743 effect =
1744 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1745 receiver_maps, p.feedback()),
1746 receiver, effect, control);
1747 }
1748
1749 Node* a; // Construct the output array.
1750 {
1751 AllocationBuilder ab(jsgraph(), effect, control);
1752 ab.Allocate(initial_map->instance_size(), NOT_TENURED, Type::Array());
1753 ab.Store(AccessBuilder::ForMap(), initial_map);
1754 Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant();
1755 ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array);
1756 ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array);
1757 ab.Store(AccessBuilder::ForJSArrayLength(packed_kind),
1758 jsgraph()->ZeroConstant());
1759 for (int i = 0; i < initial_map->GetInObjectProperties(); ++i) {
1760 ab.Store(AccessBuilder::ForJSObjectInObjectProperty(
1761 MapRef(js_heap_broker(), initial_map), i),
1762 jsgraph()->UndefinedConstant());
1763 }
1764 a = effect = ab.Finish();
1765 }
1766
1767 Node* original_length = effect = graph()->NewNode(
1768 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
1769 effect, control);
1770
1771 // Check whether the given callback function is callable. Note that this has
1772 // to happen outside the loop to make sure we also throw on empty arrays.
1773 Node* check_fail = nullptr;
1774 Node* check_throw = nullptr;
1775 {
1776 // This frame state doesn't ever call the deopt continuation, it's only
1777 // necessary to specifiy a continuation in order to handle the exceptional
1778 // case. We don't have all the values available to completely fill out
1779 // checkpoint_params yet, but that's okay because it'll never be called.
1780 // Therefore, "to" is mentioned twice, once standing in for the k_value
1781 // value.
1782 std::vector<Node*> checkpoint_params(
1783 {receiver, fncallback, this_arg, a, k, original_length, to, to});
1784 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1785
1786 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1787 jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation,
1788 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1789 outer_frame_state, ContinuationFrameStateMode::LAZY);
1790 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
1791 effect, &control, &check_fail, &check_throw);
1792 }
1793
1794 // Start the loop.
1795 Node* vloop = k = WireInLoopStart(k, &control, &effect);
1796 Node *loop = control, *eloop = effect;
1797 Node* v_to_loop = to = graph()->NewNode(
1798 common()->Phi(MachineRepresentation::kTaggedSigned, 2), to, to, loop);
1799
1800 Node* continue_test =
1801 graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
1802 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1803 continue_test, control);
1804
1805 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
1806 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
1807 control = if_true;
1808
1809 {
1810 std::vector<Node*> checkpoint_params(
1811 {receiver, fncallback, this_arg, a, k, original_length, to});
1812 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1813
1814 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1815 jsgraph(), shared, Builtins::kArrayFilterLoopEagerDeoptContinuation,
1816 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1817 outer_frame_state, ContinuationFrameStateMode::EAGER);
1818
1819 effect =
1820 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1821 }
1822
1823 // Make sure the map hasn't changed during the iteration.
1824 effect =
1825 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
1826 receiver_maps, p.feedback()),
1827 receiver, effect, control);
1828
1829 Node* element =
1830 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
1831
1832 Node* next_k =
1833 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
1834
1835 Node* hole_true = nullptr;
1836 Node* hole_false = nullptr;
1837 Node* effect_true = effect;
1838 Node* hole_true_vto = to;
1839
1840 if (IsHoleyElementsKind(kind)) {
1841 // Holey elements kind require a hole check and skipping of the element in
1842 // the case of a hole.
1843 Node* check;
1844 if (IsDoubleElementsKind(kind)) {
1845 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
1846 } else {
1847 check = graph()->NewNode(simplified()->ReferenceEqual(), element,
1848 jsgraph()->TheHoleConstant());
1849 }
1850 Node* branch =
1851 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
1852 hole_true = graph()->NewNode(common()->IfTrue(), branch);
1853 hole_false = graph()->NewNode(common()->IfFalse(), branch);
1854 control = hole_false;
1855
1856 // The contract is that we don't leak "the hole" into "user JavaScript",
1857 // so we must rename the {element} here to explicitly exclude "the hole"
1858 // from the type of {element}.
1859 element = effect = graph()->NewNode(
1860 common()->TypeGuard(Type::NonInternal()), element, effect, control);
1861 }
1862
1863 Node* callback_value = nullptr;
1864 {
1865 // This frame state is dealt with by hand in
1866 // Builtins::kArrayFilterLoopLazyDeoptContinuation.
1867 std::vector<Node*> checkpoint_params(
1868 {receiver, fncallback, this_arg, a, k, original_length, element, to});
1869 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1870
1871 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1872 jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation,
1873 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1874 outer_frame_state, ContinuationFrameStateMode::LAZY);
1875
1876 callback_value = control = effect = graph()->NewNode(
1877 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
1878 receiver, context, frame_state, effect, control);
1879 }
1880
1881 // Rewire potential exception edges.
1882 Node* on_exception = nullptr;
1883 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1884 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
1885 &check_fail, &control);
1886 }
1887
1888 // We need an eager frame state for right after the callback function
1889 // returned, just in case an attempt to grow the output array fails.
1890 //
1891 // Note that we are intentionally reusing the
1892 // Builtins::kArrayFilterLoopLazyDeoptContinuation as an *eager* entry
1893 // point in this case. This is safe, because re-evaluating a [ToBoolean]
1894 // coercion is safe.
1895 {
1896 std::vector<Node*> checkpoint_params({receiver, fncallback, this_arg, a, k,
1897 original_length, element, to,
1898 callback_value});
1899 const int stack_parameters = static_cast<int>(checkpoint_params.size());
1900 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1901 jsgraph(), shared, Builtins::kArrayFilterLoopLazyDeoptContinuation,
1902 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1903 outer_frame_state, ContinuationFrameStateMode::EAGER);
1904
1905 effect =
1906 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1907 }
1908
1909 // We have to coerce callback_value to boolean, and only store the element in
1910 // a if it's true. The checkpoint above protects against the case that
1911 // growing {a} fails.
1912 to = DoFilterPostCallbackWork(packed_kind, &control, &effect, a, to, element,
1913 callback_value);
1914
1915 if (IsHoleyElementsKind(kind)) {
1916 Node* after_call_control = control;
1917 Node* after_call_effect = effect;
1918 control = hole_true;
1919 effect = effect_true;
1920
1921 control = graph()->NewNode(common()->Merge(2), control, after_call_control);
1922 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
1923 control);
1924 to =
1925 graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2),
1926 hole_true_vto, to, control);
1927 }
1928
1929 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
1930 v_to_loop->ReplaceInput(1, to);
1931
1932 control = if_false;
1933 effect = eloop;
1934
1935 // Wire up the branch for the case when IsCallable fails for the callback.
1936 // Since {check_throw} is an unconditional throw, it's impossible to
1937 // return a successful completion. Therefore, we simply connect the successful
1938 // completion to the graph end.
1939 Node* throw_node =
1940 graph()->NewNode(common()->Throw(), check_throw, check_fail);
1941 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
1942
1943 ReplaceWithValue(node, a, effect, control);
1944 return Replace(a);
1945 }
1946
ReduceArrayFind(Node * node,ArrayFindVariant variant,Handle<SharedFunctionInfo> shared)1947 Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant,
1948 Handle<SharedFunctionInfo> shared) {
1949 if (!FLAG_turbo_inline_array_builtins) return NoChange();
1950 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1951 CallParameters const& p = CallParametersOf(node->op());
1952 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
1953 return NoChange();
1954 }
1955
1956 Builtins::Name eager_continuation_builtin;
1957 Builtins::Name lazy_continuation_builtin;
1958 Builtins::Name after_callback_lazy_continuation_builtin;
1959 if (variant == ArrayFindVariant::kFind) {
1960 eager_continuation_builtin = Builtins::kArrayFindLoopEagerDeoptContinuation;
1961 lazy_continuation_builtin = Builtins::kArrayFindLoopLazyDeoptContinuation;
1962 after_callback_lazy_continuation_builtin =
1963 Builtins::kArrayFindLoopAfterCallbackLazyDeoptContinuation;
1964 } else {
1965 DCHECK_EQ(ArrayFindVariant::kFindIndex, variant);
1966 eager_continuation_builtin =
1967 Builtins::kArrayFindIndexLoopEagerDeoptContinuation;
1968 lazy_continuation_builtin =
1969 Builtins::kArrayFindIndexLoopLazyDeoptContinuation;
1970 after_callback_lazy_continuation_builtin =
1971 Builtins::kArrayFindIndexLoopAfterCallbackLazyDeoptContinuation;
1972 }
1973
1974 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
1975 Node* effect = NodeProperties::GetEffectInput(node);
1976 Node* control = NodeProperties::GetControlInput(node);
1977 Node* context = NodeProperties::GetContextInput(node);
1978
1979 // Try to determine the {receiver} map.
1980 Node* receiver = NodeProperties::GetValueInput(node, 1);
1981 Node* fncallback = node->op()->ValueInputCount() > 2
1982 ? NodeProperties::GetValueInput(node, 2)
1983 : jsgraph()->UndefinedConstant();
1984 Node* this_arg = node->op()->ValueInputCount() > 3
1985 ? NodeProperties::GetValueInput(node, 3)
1986 : jsgraph()->UndefinedConstant();
1987 ZoneHandleSet<Map> receiver_maps;
1988 NodeProperties::InferReceiverMapsResult result =
1989 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
1990 &receiver_maps);
1991 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
1992
1993 const ElementsKind kind = receiver_maps[0]->elements_kind();
1994
1995 for (Handle<Map> receiver_map : receiver_maps) {
1996 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
1997 return NoChange();
1998 // We can handle different maps, as long as their elements kind are the
1999 // same.
2000 if (receiver_map->elements_kind() != kind) return NoChange();
2001 }
2002
2003 // Install code dependencies on the {receiver} prototype maps and the
2004 // global array protector cell.
2005 dependencies()->DependOnProtector(
2006 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
2007
2008 // If we have unreliable maps, we need a map check.
2009 if (result == NodeProperties::kUnreliableReceiverMaps) {
2010 effect =
2011 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
2012 receiver_maps, p.feedback()),
2013 receiver, effect, control);
2014 }
2015
2016 Node* k = jsgraph()->ZeroConstant();
2017
2018 Node* original_length = effect = graph()->NewNode(
2019 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
2020 effect, control);
2021
2022 std::vector<Node*> checkpoint_params(
2023 {receiver, fncallback, this_arg, k, original_length});
2024 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2025
2026 // Check whether the given callback function is callable. Note that this has
2027 // to happen outside the loop to make sure we also throw on empty arrays.
2028 Node* check_fail = nullptr;
2029 Node* check_throw = nullptr;
2030 {
2031 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2032 jsgraph(), shared, lazy_continuation_builtin, node->InputAt(0), context,
2033 &checkpoint_params[0], stack_parameters, outer_frame_state,
2034 ContinuationFrameStateMode::LAZY);
2035 WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect,
2036 &control, &check_fail, &check_throw);
2037 }
2038
2039 // Start the loop.
2040 Node* vloop = k = WireInLoopStart(k, &control, &effect);
2041 Node *loop = control, *eloop = effect;
2042 checkpoint_params[3] = k;
2043
2044 // Check if we've iterated past the last element of the array.
2045 Node* if_false = nullptr;
2046 {
2047 Node* continue_test =
2048 graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
2049 Node* continue_branch = graph()->NewNode(
2050 common()->Branch(BranchHint::kTrue), continue_test, control);
2051 control = graph()->NewNode(common()->IfTrue(), continue_branch);
2052 if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
2053 }
2054
2055 // Check the map hasn't changed during the iteration.
2056 {
2057 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2058 jsgraph(), shared, eager_continuation_builtin, node->InputAt(0),
2059 context, &checkpoint_params[0], stack_parameters, outer_frame_state,
2060 ContinuationFrameStateMode::EAGER);
2061
2062 effect =
2063 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
2064
2065 effect =
2066 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
2067 receiver_maps, p.feedback()),
2068 receiver, effect, control);
2069 }
2070
2071 // Load k-th element from receiver.
2072 Node* element =
2073 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
2074
2075 // Increment k for the next iteration.
2076 Node* next_k = checkpoint_params[3] =
2077 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
2078
2079 // Replace holes with undefined.
2080 if (kind == HOLEY_DOUBLE_ELEMENTS) {
2081 // TODO(7409): avoid deopt if not all uses of value are truncated.
2082 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
2083 element = effect =
2084 graph()->NewNode(simplified()->CheckFloat64Hole(mode, p.feedback()),
2085 element, effect, control);
2086 } else if (IsHoleyElementsKind(kind)) {
2087 element =
2088 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), element);
2089 }
2090
2091 Node* if_found_return_value =
2092 (variant == ArrayFindVariant::kFind) ? element : k;
2093
2094 // Call the callback.
2095 Node* callback_value = nullptr;
2096 {
2097 std::vector<Node*> call_checkpoint_params({receiver, fncallback, this_arg,
2098 next_k, original_length,
2099 if_found_return_value});
2100 const int call_stack_parameters =
2101 static_cast<int>(call_checkpoint_params.size());
2102
2103 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2104 jsgraph(), shared, after_callback_lazy_continuation_builtin,
2105 node->InputAt(0), context, &call_checkpoint_params[0],
2106 call_stack_parameters, outer_frame_state,
2107 ContinuationFrameStateMode::LAZY);
2108
2109 callback_value = control = effect = graph()->NewNode(
2110 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
2111 receiver, context, frame_state, effect, control);
2112 }
2113
2114 // Rewire potential exception edges.
2115 Node* on_exception = nullptr;
2116 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
2117 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
2118 &check_fail, &control);
2119 }
2120
2121 // Check whether the given callback function returned a truthy value.
2122 Node* boolean_result =
2123 graph()->NewNode(simplified()->ToBoolean(), callback_value);
2124 Node* efound_branch = effect;
2125 Node* found_branch = graph()->NewNode(common()->Branch(BranchHint::kFalse),
2126 boolean_result, control);
2127 Node* if_found = graph()->NewNode(common()->IfTrue(), found_branch);
2128 Node* if_notfound = graph()->NewNode(common()->IfFalse(), found_branch);
2129 control = if_notfound;
2130
2131 // Close the loop.
2132 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
2133
2134 control = graph()->NewNode(common()->Merge(2), if_found, if_false);
2135 effect =
2136 graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control);
2137
2138 Node* if_not_found_value = (variant == ArrayFindVariant::kFind)
2139 ? jsgraph()->UndefinedConstant()
2140 : jsgraph()->MinusOneConstant();
2141 Node* return_value =
2142 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2143 if_found_return_value, if_not_found_value, control);
2144
2145 // Wire up the branch for the case when IsCallable fails for the callback.
2146 // Since {check_throw} is an unconditional throw, it's impossible to
2147 // return a successful completion. Therefore, we simply connect the successful
2148 // completion to the graph end.
2149 Node* throw_node =
2150 graph()->NewNode(common()->Throw(), check_throw, check_fail);
2151 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
2152
2153 ReplaceWithValue(node, return_value, effect, control);
2154 return Replace(return_value);
2155 }
2156
DoFilterPostCallbackWork(ElementsKind kind,Node ** control,Node ** effect,Node * a,Node * to,Node * element,Node * callback_value)2157 Node* JSCallReducer::DoFilterPostCallbackWork(ElementsKind kind, Node** control,
2158 Node** effect, Node* a, Node* to,
2159 Node* element,
2160 Node* callback_value) {
2161 Node* boolean_result =
2162 graph()->NewNode(simplified()->ToBoolean(), callback_value);
2163
2164 Node* check_boolean_result =
2165 graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
2166 jsgraph()->TrueConstant());
2167 Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2168 check_boolean_result, *control);
2169
2170 Node* if_true = graph()->NewNode(common()->IfTrue(), boolean_branch);
2171 Node* etrue = *effect;
2172 Node* vtrue;
2173 {
2174 // Load the elements backing store of the {receiver}.
2175 Node* elements = etrue = graph()->NewNode(
2176 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), a, etrue,
2177 if_true);
2178
2179 DCHECK(TypeCache::Get().kFixedDoubleArrayLengthType.Is(
2180 TypeCache::Get().kFixedArrayLengthType));
2181 Node* checked_to = etrue = graph()->NewNode(
2182 common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), to, etrue,
2183 if_true);
2184 Node* elements_length = etrue = graph()->NewNode(
2185 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
2186 etrue, if_true);
2187
2188 GrowFastElementsMode mode =
2189 IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
2190 : GrowFastElementsMode::kSmiOrObjectElements;
2191 elements = etrue = graph()->NewNode(
2192 simplified()->MaybeGrowFastElements(mode, VectorSlotPair()), a,
2193 elements, checked_to, elements_length, etrue, if_true);
2194
2195 // Update the length of {a}.
2196 Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to,
2197 jsgraph()->OneConstant());
2198
2199 etrue = graph()->NewNode(
2200 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), a,
2201 new_length_a, etrue, if_true);
2202
2203 // Append the value to the {elements}.
2204 etrue = graph()->NewNode(
2205 simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
2206 elements, checked_to, element, etrue, if_true);
2207
2208 vtrue = new_length_a;
2209 }
2210
2211 Node* if_false = graph()->NewNode(common()->IfFalse(), boolean_branch);
2212 Node* efalse = *effect;
2213 Node* vfalse = to;
2214
2215 *control = graph()->NewNode(common()->Merge(2), if_true, if_false);
2216 *effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, *control);
2217 to = graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2),
2218 vtrue, vfalse, *control);
2219 return to;
2220 }
2221
WireInCallbackIsCallableCheck(Node * fncallback,Node * context,Node * check_frame_state,Node * effect,Node ** control,Node ** check_fail,Node ** check_throw)2222 void JSCallReducer::WireInCallbackIsCallableCheck(
2223 Node* fncallback, Node* context, Node* check_frame_state, Node* effect,
2224 Node** control, Node** check_fail, Node** check_throw) {
2225 Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
2226 Node* check_branch =
2227 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, *control);
2228 *check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
2229 *check_throw = *check_fail = graph()->NewNode(
2230 javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
2231 jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback,
2232 context, check_frame_state, effect, *check_fail);
2233 *control = graph()->NewNode(common()->IfTrue(), check_branch);
2234 }
2235
RewirePostCallbackExceptionEdges(Node * check_throw,Node * on_exception,Node * effect,Node ** check_fail,Node ** control)2236 void JSCallReducer::RewirePostCallbackExceptionEdges(Node* check_throw,
2237 Node* on_exception,
2238 Node* effect,
2239 Node** check_fail,
2240 Node** control) {
2241 // Create appropriate {IfException} and {IfSuccess} nodes.
2242 Node* if_exception0 =
2243 graph()->NewNode(common()->IfException(), check_throw, *check_fail);
2244 *check_fail = graph()->NewNode(common()->IfSuccess(), *check_fail);
2245 Node* if_exception1 =
2246 graph()->NewNode(common()->IfException(), effect, *control);
2247 *control = graph()->NewNode(common()->IfSuccess(), *control);
2248
2249 // Join the exception edges.
2250 Node* merge =
2251 graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
2252 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
2253 if_exception1, merge);
2254 Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2255 if_exception0, if_exception1, merge);
2256 ReplaceWithValue(on_exception, phi, ephi, merge);
2257 }
2258
SafeLoadElement(ElementsKind kind,Node * receiver,Node * control,Node ** effect,Node ** k,const VectorSlotPair & feedback)2259 Node* JSCallReducer::SafeLoadElement(ElementsKind kind, Node* receiver,
2260 Node* control, Node** effect, Node** k,
2261 const VectorSlotPair& feedback) {
2262 // Make sure that the access is still in bounds, since the callback could have
2263 // changed the array's size.
2264 Node* length = *effect = graph()->NewNode(
2265 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
2266 *effect, control);
2267 *k = *effect = graph()->NewNode(simplified()->CheckBounds(feedback), *k,
2268 length, *effect, control);
2269
2270 // Reload the elements pointer before calling the callback, since the previous
2271 // callback might have resized the array causing the elements buffer to be
2272 // re-allocated.
2273 Node* elements = *effect = graph()->NewNode(
2274 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
2275 *effect, control);
2276
2277 Node* element = *effect = graph()->NewNode(
2278 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(
2279 kind, LoadSensitivity::kCritical)),
2280 elements, *k, *effect, control);
2281 return element;
2282 }
2283
ReduceArrayEvery(Node * node,Handle<SharedFunctionInfo> shared)2284 Reduction JSCallReducer::ReduceArrayEvery(Node* node,
2285 Handle<SharedFunctionInfo> shared) {
2286 if (!FLAG_turbo_inline_array_builtins) return NoChange();
2287 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
2288 CallParameters const& p = CallParametersOf(node->op());
2289 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
2290 return NoChange();
2291 }
2292
2293 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
2294 Node* effect = NodeProperties::GetEffectInput(node);
2295 Node* control = NodeProperties::GetControlInput(node);
2296 Node* context = NodeProperties::GetContextInput(node);
2297 // Try to determine the {receiver} map.
2298 Node* receiver = NodeProperties::GetValueInput(node, 1);
2299 Node* fncallback = node->op()->ValueInputCount() > 2
2300 ? NodeProperties::GetValueInput(node, 2)
2301 : jsgraph()->UndefinedConstant();
2302 Node* this_arg = node->op()->ValueInputCount() > 3
2303 ? NodeProperties::GetValueInput(node, 3)
2304 : jsgraph()->UndefinedConstant();
2305 ZoneHandleSet<Map> receiver_maps;
2306 NodeProperties::InferReceiverMapsResult result =
2307 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
2308 &receiver_maps);
2309 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
2310
2311 // And ensure that any changes to the Array species constructor cause deopt.
2312 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
2313
2314 const ElementsKind kind = receiver_maps[0]->elements_kind();
2315
2316 for (Handle<Map> receiver_map : receiver_maps) {
2317 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
2318 return NoChange();
2319 // We can handle different maps, as long as their elements kind are the
2320 // same.
2321 if (receiver_map->elements_kind() != kind) return NoChange();
2322 }
2323
2324 if (IsHoleyElementsKind(kind)) {
2325 dependencies()->DependOnProtector(
2326 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
2327 }
2328
2329 dependencies()->DependOnProtector(
2330 PropertyCellRef(js_heap_broker(), factory()->array_species_protector()));
2331
2332 // If we have unreliable maps, we need a map check.
2333 if (result == NodeProperties::kUnreliableReceiverMaps) {
2334 effect =
2335 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
2336 receiver_maps, p.feedback()),
2337 receiver, effect, control);
2338 }
2339
2340 Node* k = jsgraph()->ZeroConstant();
2341
2342 // Make sure the map hasn't changed before we construct the output array.
2343 effect = graph()->NewNode(
2344 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
2345 effect, control);
2346
2347 Node* original_length = effect = graph()->NewNode(
2348 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
2349 effect, control);
2350
2351 // Check whether the given callback function is callable. Note that this has
2352 // to happen outside the loop to make sure we also throw on empty arrays.
2353 Node* check_fail = nullptr;
2354 Node* check_throw = nullptr;
2355 {
2356 // This frame state doesn't ever call the deopt continuation, it's only
2357 // necessary to specifiy a continuation in order to handle the exceptional
2358 // case.
2359 std::vector<Node*> checkpoint_params(
2360 {receiver, fncallback, this_arg, k, original_length});
2361 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2362
2363 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2364 jsgraph(), shared, Builtins::kArrayEveryLoopLazyDeoptContinuation,
2365 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
2366 outer_frame_state, ContinuationFrameStateMode::LAZY);
2367 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
2368 effect, &control, &check_fail, &check_throw);
2369 }
2370
2371 // Start the loop.
2372 Node* vloop = k = WireInLoopStart(k, &control, &effect);
2373 Node *loop = control, *eloop = effect;
2374
2375 Node* continue_test =
2376 graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
2377 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2378 continue_test, control);
2379
2380 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
2381 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
2382 control = if_true;
2383
2384 {
2385 std::vector<Node*> checkpoint_params(
2386 {receiver, fncallback, this_arg, k, original_length});
2387 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2388
2389 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2390 jsgraph(), shared, Builtins::kArrayEveryLoopEagerDeoptContinuation,
2391 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
2392 outer_frame_state, ContinuationFrameStateMode::EAGER);
2393
2394 effect =
2395 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
2396 }
2397
2398 // Make sure the map hasn't changed during the iteration.
2399 effect =
2400 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
2401 receiver_maps, p.feedback()),
2402 receiver, effect, control);
2403
2404 Node* element =
2405 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
2406
2407 Node* next_k =
2408 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
2409
2410 Node* hole_true = nullptr;
2411 Node* hole_false = nullptr;
2412 Node* effect_true = effect;
2413
2414 if (IsHoleyElementsKind(kind)) {
2415 // Holey elements kind require a hole check and skipping of the element in
2416 // the case of a hole.
2417 Node* check;
2418 if (IsDoubleElementsKind(kind)) {
2419 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
2420 } else {
2421 check = graph()->NewNode(simplified()->ReferenceEqual(), element,
2422 jsgraph()->TheHoleConstant());
2423 }
2424 Node* branch =
2425 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
2426 hole_true = graph()->NewNode(common()->IfTrue(), branch);
2427 hole_false = graph()->NewNode(common()->IfFalse(), branch);
2428 control = hole_false;
2429
2430 // The contract is that we don't leak "the hole" into "user JavaScript",
2431 // so we must rename the {element} here to explicitly exclude "the hole"
2432 // from the type of {element}.
2433 element = effect = graph()->NewNode(
2434 common()->TypeGuard(Type::NonInternal()), element, effect, control);
2435 }
2436
2437 Node* callback_value = nullptr;
2438 {
2439 // This frame state is dealt with by hand in
2440 // Builtins::kArrayEveryLoopLazyDeoptContinuation.
2441 std::vector<Node*> checkpoint_params(
2442 {receiver, fncallback, this_arg, k, original_length});
2443 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2444
2445 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2446 jsgraph(), shared, Builtins::kArrayEveryLoopLazyDeoptContinuation,
2447 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
2448 outer_frame_state, ContinuationFrameStateMode::LAZY);
2449
2450 callback_value = control = effect = graph()->NewNode(
2451 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
2452 receiver, context, frame_state, effect, control);
2453 }
2454
2455 // Rewire potential exception edges.
2456 Node* on_exception = nullptr;
2457 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
2458 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
2459 &check_fail, &control);
2460 }
2461
2462 // We have to coerce callback_value to boolean.
2463 Node* if_false_callback;
2464 Node* efalse_callback;
2465 {
2466 Node* boolean_result =
2467 graph()->NewNode(simplified()->ToBoolean(), callback_value);
2468 Node* check_boolean_result =
2469 graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
2470 jsgraph()->TrueConstant());
2471 Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2472 check_boolean_result, control);
2473 if_false_callback = graph()->NewNode(common()->IfFalse(), boolean_branch);
2474 efalse_callback = effect;
2475
2476 // Nothing to do in the true case.
2477 control = graph()->NewNode(common()->IfTrue(), boolean_branch);
2478 }
2479
2480 if (IsHoleyElementsKind(kind)) {
2481 Node* after_call_control = control;
2482 Node* after_call_effect = effect;
2483 control = hole_true;
2484 effect = effect_true;
2485
2486 control = graph()->NewNode(common()->Merge(2), control, after_call_control);
2487 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
2488 control);
2489 }
2490
2491 WireInLoopEnd(loop, eloop, vloop, next_k, control, effect);
2492
2493 control = graph()->NewNode(common()->Merge(2), if_false, if_false_callback);
2494 effect =
2495 graph()->NewNode(common()->EffectPhi(2), eloop, efalse_callback, control);
2496 Node* return_value = graph()->NewNode(
2497 common()->Phi(MachineRepresentation::kTagged, 2),
2498 jsgraph()->TrueConstant(), jsgraph()->FalseConstant(), control);
2499
2500 // Wire up the branch for the case when IsCallable fails for the callback.
2501 // Since {check_throw} is an unconditional throw, it's impossible to
2502 // return a successful completion. Therefore, we simply connect the successful
2503 // completion to the graph end.
2504 Node* throw_node =
2505 graph()->NewNode(common()->Throw(), check_throw, check_fail);
2506 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
2507
2508 ReplaceWithValue(node, return_value, effect, control);
2509 return Replace(return_value);
2510 }
2511
2512 namespace {
2513
2514 // Returns the correct Callable for Array's indexOf based on the receiver's
2515 // |elements_kind| and |isolate|. Assumes that |elements_kind| is a fast one.
GetCallableForArrayIndexOf(ElementsKind elements_kind,Isolate * isolate)2516 Callable GetCallableForArrayIndexOf(ElementsKind elements_kind,
2517 Isolate* isolate) {
2518 switch (elements_kind) {
2519 case PACKED_SMI_ELEMENTS:
2520 case HOLEY_SMI_ELEMENTS:
2521 case PACKED_ELEMENTS:
2522 case HOLEY_ELEMENTS:
2523 return Builtins::CallableFor(isolate, Builtins::kArrayIndexOfSmiOrObject);
2524 case PACKED_DOUBLE_ELEMENTS:
2525 return Builtins::CallableFor(isolate,
2526 Builtins::kArrayIndexOfPackedDoubles);
2527 default:
2528 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind);
2529 return Builtins::CallableFor(isolate,
2530 Builtins::kArrayIndexOfHoleyDoubles);
2531 }
2532 }
2533
2534 // Returns the correct Callable for Array's includes based on the receiver's
2535 // |elements_kind| and |isolate|. Assumes that |elements_kind| is a fast one.
GetCallableForArrayIncludes(ElementsKind elements_kind,Isolate * isolate)2536 Callable GetCallableForArrayIncludes(ElementsKind elements_kind,
2537 Isolate* isolate) {
2538 switch (elements_kind) {
2539 case PACKED_SMI_ELEMENTS:
2540 case HOLEY_SMI_ELEMENTS:
2541 case PACKED_ELEMENTS:
2542 case HOLEY_ELEMENTS:
2543 return Builtins::CallableFor(isolate,
2544 Builtins::kArrayIncludesSmiOrObject);
2545 case PACKED_DOUBLE_ELEMENTS:
2546 return Builtins::CallableFor(isolate,
2547 Builtins::kArrayIncludesPackedDoubles);
2548 default:
2549 DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind);
2550 return Builtins::CallableFor(isolate,
2551 Builtins::kArrayIncludesHoleyDoubles);
2552 }
2553 }
2554
2555 } // namespace
2556
2557 // For search_variant == kIndexOf:
2558 // ES6 Array.prototype.indexOf(searchElement[, fromIndex])
2559 // #sec-array.prototype.indexof
2560 // For search_variant == kIncludes:
2561 // ES7 Array.prototype.inludes(searchElement[, fromIndex])
2562 // #sec-array.prototype.includes
ReduceArrayIndexOfIncludes(SearchVariant search_variant,Node * node)2563 Reduction JSCallReducer::ReduceArrayIndexOfIncludes(
2564 SearchVariant search_variant, Node* node) {
2565 CallParameters const& p = CallParametersOf(node->op());
2566 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
2567 return NoChange();
2568 }
2569
2570 Handle<Map> receiver_map;
2571 if (!NodeProperties::GetMapWitness(isolate(), node).ToHandle(&receiver_map))
2572 return NoChange();
2573
2574 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
2575 return NoChange();
2576
2577 if (IsHoleyElementsKind(receiver_map->elements_kind())) {
2578 dependencies()->DependOnProtector(
2579 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
2580 }
2581
2582 Callable const callable =
2583 search_variant == SearchVariant::kIndexOf
2584 ? GetCallableForArrayIndexOf(receiver_map->elements_kind(), isolate())
2585 : GetCallableForArrayIncludes(receiver_map->elements_kind(),
2586 isolate());
2587 CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
2588 graph()->zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags,
2589 Operator::kEliminatable);
2590 // The stub expects the following arguments: the receiver array, its elements,
2591 // the search_element, the array length, and the index to start searching
2592 // from.
2593 Node* receiver = NodeProperties::GetValueInput(node, 1);
2594 Node* effect = NodeProperties::GetEffectInput(node);
2595 Node* control = NodeProperties::GetControlInput(node);
2596 Node* elements = effect = graph()->NewNode(
2597 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
2598 effect, control);
2599 Node* search_element = (node->op()->ValueInputCount() >= 3)
2600 ? NodeProperties::GetValueInput(node, 2)
2601 : jsgraph()->UndefinedConstant();
2602 Node* length = effect = graph()->NewNode(
2603 simplified()->LoadField(
2604 AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
2605 receiver, effect, control);
2606 Node* new_from_index = jsgraph()->ZeroConstant();
2607 if (node->op()->ValueInputCount() >= 4) {
2608 Node* from_index = NodeProperties::GetValueInput(node, 3);
2609 from_index = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
2610 from_index, effect, control);
2611 // If the index is negative, it means the offset from the end and therefore
2612 // needs to be added to the length. If the result is still negative, it
2613 // needs to be clamped to 0.
2614 new_from_index = graph()->NewNode(
2615 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
2616 graph()->NewNode(simplified()->NumberLessThan(), from_index,
2617 jsgraph()->ZeroConstant()),
2618 graph()->NewNode(
2619 simplified()->NumberMax(),
2620 graph()->NewNode(simplified()->NumberAdd(), length, from_index),
2621 jsgraph()->ZeroConstant()),
2622 from_index);
2623 }
2624
2625 Node* context = NodeProperties::GetContextInput(node);
2626 Node* replacement_node = effect = graph()->NewNode(
2627 common()->Call(desc), jsgraph()->HeapConstant(callable.code()), elements,
2628 search_element, length, new_from_index, context, effect);
2629 ReplaceWithValue(node, replacement_node, effect);
2630 return Replace(replacement_node);
2631 }
2632
ReduceArraySome(Node * node,Handle<SharedFunctionInfo> shared)2633 Reduction JSCallReducer::ReduceArraySome(Node* node,
2634 Handle<SharedFunctionInfo> shared) {
2635 if (!FLAG_turbo_inline_array_builtins) return NoChange();
2636 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
2637 CallParameters const& p = CallParametersOf(node->op());
2638 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
2639 return NoChange();
2640 }
2641
2642 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
2643 Node* effect = NodeProperties::GetEffectInput(node);
2644 Node* control = NodeProperties::GetControlInput(node);
2645 Node* context = NodeProperties::GetContextInput(node);
2646 // Try to determine the {receiver} map.
2647 Node* receiver = NodeProperties::GetValueInput(node, 1);
2648 Node* fncallback = node->op()->ValueInputCount() > 2
2649 ? NodeProperties::GetValueInput(node, 2)
2650 : jsgraph()->UndefinedConstant();
2651 Node* this_arg = node->op()->ValueInputCount() > 3
2652 ? NodeProperties::GetValueInput(node, 3)
2653 : jsgraph()->UndefinedConstant();
2654 ZoneHandleSet<Map> receiver_maps;
2655 NodeProperties::InferReceiverMapsResult result =
2656 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
2657 &receiver_maps);
2658 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
2659
2660 // And ensure that any changes to the Array species constructor cause deopt.
2661 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
2662
2663 if (receiver_maps.size() == 0) return NoChange();
2664
2665 const ElementsKind kind = receiver_maps[0]->elements_kind();
2666
2667 for (Handle<Map> receiver_map : receiver_maps) {
2668 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
2669 return NoChange();
2670 // We can handle different maps, as long as their elements kind are the
2671 // same.
2672 if (receiver_map->elements_kind() != kind) return NoChange();
2673 }
2674
2675 if (IsHoleyElementsKind(kind)) {
2676 dependencies()->DependOnProtector(
2677 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
2678 }
2679
2680 dependencies()->DependOnProtector(
2681 PropertyCellRef(js_heap_broker(), factory()->array_species_protector()));
2682
2683 Node* k = jsgraph()->ZeroConstant();
2684
2685 // If we have unreliable maps, we need a map check.
2686 if (result == NodeProperties::kUnreliableReceiverMaps) {
2687 effect =
2688 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
2689 receiver_maps, p.feedback()),
2690 receiver, effect, control);
2691 }
2692
2693 // Make sure the map hasn't changed before we construct the output array.
2694 effect = graph()->NewNode(
2695 simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
2696 effect, control);
2697
2698 Node* original_length = effect = graph()->NewNode(
2699 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
2700 effect, control);
2701
2702 // Check whether the given callback function is callable. Note that this has
2703 // to happen outside the loop to make sure we also throw on empty arrays.
2704 Node* check_fail = nullptr;
2705 Node* check_throw = nullptr;
2706 {
2707 // This frame state doesn't ever call the deopt continuation, it's only
2708 // necessary to specifiy a continuation in order to handle the exceptional
2709 // case.
2710 std::vector<Node*> checkpoint_params(
2711 {receiver, fncallback, this_arg, k, original_length});
2712 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2713
2714 Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2715 jsgraph(), shared, Builtins::kArraySomeLoopLazyDeoptContinuation,
2716 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
2717 outer_frame_state, ContinuationFrameStateMode::LAZY);
2718 WireInCallbackIsCallableCheck(fncallback, context, check_frame_state,
2719 effect, &control, &check_fail, &check_throw);
2720 }
2721
2722 // Start the loop.
2723 Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
2724 Node* eloop = effect =
2725 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
2726 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
2727 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
2728 Node* vloop = k = graph()->NewNode(
2729 common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
2730
2731 Node* continue_test =
2732 graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
2733 Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2734 continue_test, control);
2735
2736 Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
2737 Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
2738 control = if_true;
2739
2740 {
2741 std::vector<Node*> checkpoint_params(
2742 {receiver, fncallback, this_arg, k, original_length});
2743 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2744
2745 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2746 jsgraph(), shared, Builtins::kArraySomeLoopEagerDeoptContinuation,
2747 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
2748 outer_frame_state, ContinuationFrameStateMode::EAGER);
2749
2750 effect =
2751 graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
2752 }
2753
2754 // Make sure the map hasn't changed during the iteration.
2755 effect =
2756 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
2757 receiver_maps, p.feedback()),
2758 receiver, effect, control);
2759
2760 Node* element =
2761 SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback());
2762
2763 Node* next_k =
2764 graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
2765
2766 Node* hole_true = nullptr;
2767 Node* hole_false = nullptr;
2768 Node* effect_true = effect;
2769
2770 if (IsHoleyElementsKind(kind)) {
2771 // Holey elements kind require a hole check and skipping of the element in
2772 // the case of a hole.
2773 Node* check;
2774 if (IsDoubleElementsKind(kind)) {
2775 check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element);
2776 } else {
2777 check = graph()->NewNode(simplified()->ReferenceEqual(), element,
2778 jsgraph()->TheHoleConstant());
2779 }
2780 Node* branch =
2781 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
2782 hole_true = graph()->NewNode(common()->IfTrue(), branch);
2783 hole_false = graph()->NewNode(common()->IfFalse(), branch);
2784 control = hole_false;
2785
2786 // The contract is that we don't leak "the hole" into "user JavaScript",
2787 // so we must rename the {element} here to explicitly exclude "the hole"
2788 // from the type of {element}.
2789 element = effect = graph()->NewNode(
2790 common()->TypeGuard(Type::NonInternal()), element, effect, control);
2791 }
2792
2793 Node* callback_value = nullptr;
2794 {
2795 // This frame state is dealt with by hand in
2796 // Builtins::kArrayEveryLoopLazyDeoptContinuation.
2797 std::vector<Node*> checkpoint_params(
2798 {receiver, fncallback, this_arg, k, original_length});
2799 const int stack_parameters = static_cast<int>(checkpoint_params.size());
2800
2801 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
2802 jsgraph(), shared, Builtins::kArraySomeLoopLazyDeoptContinuation,
2803 node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
2804 outer_frame_state, ContinuationFrameStateMode::LAZY);
2805
2806 callback_value = control = effect = graph()->NewNode(
2807 javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
2808 receiver, context, frame_state, effect, control);
2809 }
2810
2811 // Rewire potential exception edges.
2812 Node* on_exception = nullptr;
2813 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
2814 RewirePostCallbackExceptionEdges(check_throw, on_exception, effect,
2815 &check_fail, &control);
2816 }
2817
2818 // We have to coerce callback_value to boolean.
2819 Node* if_true_callback;
2820 Node* etrue_callback;
2821 {
2822 Node* boolean_result =
2823 graph()->NewNode(simplified()->ToBoolean(), callback_value);
2824 Node* check_boolean_result =
2825 graph()->NewNode(simplified()->ReferenceEqual(), boolean_result,
2826 jsgraph()->TrueConstant());
2827 Node* boolean_branch = graph()->NewNode(
2828 common()->Branch(BranchHint::kFalse), check_boolean_result, control);
2829 if_true_callback = graph()->NewNode(common()->IfTrue(), boolean_branch);
2830 etrue_callback = effect;
2831
2832 // Nothing to do in the false case.
2833 control = graph()->NewNode(common()->IfFalse(), boolean_branch);
2834 }
2835
2836 if (IsHoleyElementsKind(kind)) {
2837 Node* after_call_control = control;
2838 Node* after_call_effect = effect;
2839 control = hole_true;
2840 effect = effect_true;
2841
2842 control = graph()->NewNode(common()->Merge(2), control, after_call_control);
2843 effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
2844 control);
2845 }
2846
2847 loop->ReplaceInput(1, control);
2848 vloop->ReplaceInput(1, next_k);
2849 eloop->ReplaceInput(1, effect);
2850
2851 control = graph()->NewNode(common()->Merge(2), if_false, if_true_callback);
2852 effect =
2853 graph()->NewNode(common()->EffectPhi(2), eloop, etrue_callback, control);
2854 Node* return_value = graph()->NewNode(
2855 common()->Phi(MachineRepresentation::kTagged, 2),
2856 jsgraph()->FalseConstant(), jsgraph()->TrueConstant(), control);
2857
2858 // Wire up the branch for the case when IsCallable fails for the callback.
2859 // Since {check_throw} is an unconditional throw, it's impossible to
2860 // return a successful completion. Therefore, we simply connect the successful
2861 // completion to the graph end.
2862 Node* throw_node =
2863 graph()->NewNode(common()->Throw(), check_throw, check_fail);
2864 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
2865
2866 ReplaceWithValue(node, return_value, effect, control);
2867 return Replace(return_value);
2868 }
2869
ReduceCallApiFunction(Node * node,Handle<SharedFunctionInfo> shared)2870 Reduction JSCallReducer::ReduceCallApiFunction(
2871 Node* node, Handle<SharedFunctionInfo> shared) {
2872 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
2873 CallParameters const& p = CallParametersOf(node->op());
2874 int const argc = static_cast<int>(p.arity()) - 2;
2875 Node* target = NodeProperties::GetValueInput(node, 0);
2876 Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined)
2877 ? jsgraph()->HeapConstant(global_proxy())
2878 : NodeProperties::GetValueInput(node, 1);
2879 Node* effect = NodeProperties::GetEffectInput(node);
2880 Node* control = NodeProperties::GetControlInput(node);
2881
2882 Handle<FunctionTemplateInfo> function_template_info(
2883 FunctionTemplateInfo::cast(shared->function_data()), isolate());
2884
2885 // CallApiCallbackStub expects the target in a register, so we count it out,
2886 // and counts the receiver as an implicit argument, so we count the receiver
2887 // out too.
2888 if (argc > CallApiCallbackStub::kArgMax) return NoChange();
2889
2890 // Infer the {receiver} maps, and check if we can inline the API function
2891 // callback based on those.
2892 ZoneHandleSet<Map> receiver_maps;
2893 NodeProperties::InferReceiverMapsResult result =
2894 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
2895 &receiver_maps);
2896 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
2897 for (size_t i = 0; i < receiver_maps.size(); ++i) {
2898 Handle<Map> receiver_map = receiver_maps[i];
2899 if (!receiver_map->IsJSObjectMap() ||
2900 (!function_template_info->accept_any_receiver() &&
2901 receiver_map->is_access_check_needed())) {
2902 return NoChange();
2903 }
2904 // In case of unreliable {receiver} information, the {receiver_maps}
2905 // must all be stable in order to consume the information.
2906 if (result == NodeProperties::kUnreliableReceiverMaps) {
2907 if (!receiver_map->is_stable()) return NoChange();
2908 }
2909 }
2910
2911 // See if we can constant-fold the compatible receiver checks.
2912 CallOptimization call_optimization(isolate(), function_template_info);
2913 if (!call_optimization.is_simple_api_call()) return NoChange();
2914 CallOptimization::HolderLookup lookup;
2915 Handle<JSObject> api_holder =
2916 call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup);
2917 if (lookup == CallOptimization::kHolderNotFound) return NoChange();
2918 for (size_t i = 1; i < receiver_maps.size(); ++i) {
2919 CallOptimization::HolderLookup lookupi;
2920 Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType(
2921 receiver_maps[i], &lookupi);
2922 if (lookup != lookupi) return NoChange();
2923 if (!api_holder.is_identical_to(holder)) return NoChange();
2924 }
2925
2926 // Install stability dependencies for unreliable {receiver_maps}.
2927 if (result == NodeProperties::kUnreliableReceiverMaps) {
2928 for (size_t i = 0; i < receiver_maps.size(); ++i) {
2929 dependencies()->DependOnStableMap(
2930 MapRef(js_heap_broker(), receiver_maps[i]));
2931 }
2932 }
2933
2934 // Load the {target}s context.
2935 Node* context = effect = graph()->NewNode(
2936 simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target,
2937 effect, control);
2938
2939 // CallApiCallbackStub's register arguments: code, target, call data, holder,
2940 // function address.
2941 // TODO(turbofan): Consider introducing a JSCallApiCallback operator for
2942 // this and lower it during JSGenericLowering, and unify this with the
2943 // JSNativeContextSpecialization::InlineApiCall method a bit.
2944 Handle<CallHandlerInfo> call_handler_info(
2945 CallHandlerInfo::cast(function_template_info->call_code()), isolate());
2946 Handle<Object> data(call_handler_info->data(), isolate());
2947 Callable call_api_callback = CodeFactory::CallApiCallback(isolate(), argc);
2948 CallInterfaceDescriptor cid = call_api_callback.descriptor();
2949 auto call_descriptor = Linkage::GetStubCallDescriptor(
2950 graph()->zone(), cid,
2951 cid.GetStackParameterCount() + argc + 1 /* implicit receiver */,
2952 CallDescriptor::kNeedsFrameState);
2953 ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
2954 Node* holder = lookup == CallOptimization::kHolderFound
2955 ? jsgraph()->HeapConstant(api_holder)
2956 : receiver;
2957 ExternalReference function_reference = ExternalReference::Create(
2958 &api_function, ExternalReference::DIRECT_API_CALL);
2959 node->InsertInput(graph()->zone(), 0,
2960 jsgraph()->HeapConstant(call_api_callback.code()));
2961 node->ReplaceInput(1, context);
2962 node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data));
2963 node->InsertInput(graph()->zone(), 3, holder);
2964 node->InsertInput(graph()->zone(), 4,
2965 jsgraph()->ExternalConstant(function_reference));
2966 node->ReplaceInput(5, receiver);
2967 node->RemoveInput(6 + argc); // Remove context input.
2968 node->ReplaceInput(7 + argc, effect); // Update effect input.
2969 NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
2970 return Changed(node);
2971 }
2972
2973 namespace {
2974
2975 // Check whether elements aren't mutated; we play it extremely safe here by
2976 // explicitly checking that {node} is only used by {LoadField} or {LoadElement}.
IsSafeArgumentsElements(Node * node)2977 bool IsSafeArgumentsElements(Node* node) {
2978 for (Edge const edge : node->use_edges()) {
2979 if (!NodeProperties::IsValueEdge(edge)) continue;
2980 if (edge.from()->opcode() != IrOpcode::kLoadField &&
2981 edge.from()->opcode() != IrOpcode::kLoadElement) {
2982 return false;
2983 }
2984 }
2985 return true;
2986 }
2987
2988 } // namespace
2989
ReduceCallOrConstructWithArrayLikeOrSpread(Node * node,int arity,CallFrequency const & frequency,VectorSlotPair const & feedback)2990 Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
2991 Node* node, int arity, CallFrequency const& frequency,
2992 VectorSlotPair const& feedback) {
2993 DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike ||
2994 node->opcode() == IrOpcode::kJSCallWithSpread ||
2995 node->opcode() == IrOpcode::kJSConstructWithArrayLike ||
2996 node->opcode() == IrOpcode::kJSConstructWithSpread);
2997
2998 // In case of a call/construct with spread, we need to
2999 // ensure that it's safe to avoid the actual iteration.
3000 if ((node->opcode() == IrOpcode::kJSCallWithSpread ||
3001 node->opcode() == IrOpcode::kJSConstructWithSpread) &&
3002 !isolate()->IsArrayIteratorLookupChainIntact()) {
3003 return NoChange();
3004 }
3005
3006 // Check if {arguments_list} is an arguments object, and {node} is the only
3007 // value user of {arguments_list} (except for value uses in frame states).
3008 Node* arguments_list = NodeProperties::GetValueInput(node, arity);
3009 if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) {
3010 return NoChange();
3011 }
3012 for (Edge edge : arguments_list->use_edges()) {
3013 if (!NodeProperties::IsValueEdge(edge)) continue;
3014 Node* const user = edge.from();
3015 switch (user->opcode()) {
3016 case IrOpcode::kCheckMaps:
3017 case IrOpcode::kFrameState:
3018 case IrOpcode::kStateValues:
3019 case IrOpcode::kReferenceEqual:
3020 case IrOpcode::kReturn:
3021 // Ignore safe uses that definitely don't mess with the arguments.
3022 continue;
3023 case IrOpcode::kLoadField: {
3024 DCHECK_EQ(arguments_list, user->InputAt(0));
3025 FieldAccess const& access = FieldAccessOf(user->op());
3026 if (access.offset == JSArray::kLengthOffset) {
3027 // Ignore uses for arguments#length.
3028 STATIC_ASSERT(JSArray::kLengthOffset ==
3029 JSArgumentsObject::kLengthOffset);
3030 continue;
3031 } else if (access.offset == JSObject::kElementsOffset) {
3032 // Ignore safe uses for arguments#elements.
3033 if (IsSafeArgumentsElements(user)) continue;
3034 }
3035 break;
3036 }
3037 case IrOpcode::kJSCallWithArrayLike:
3038 // Ignore uses as argumentsList input to calls with array like.
3039 if (user->InputAt(2) == arguments_list) continue;
3040 break;
3041 case IrOpcode::kJSConstructWithArrayLike:
3042 // Ignore uses as argumentsList input to calls with array like.
3043 if (user->InputAt(1) == arguments_list) continue;
3044 break;
3045 case IrOpcode::kJSCallWithSpread: {
3046 // Ignore uses as spread input to calls with spread.
3047 CallParameters p = CallParametersOf(user->op());
3048 int const arity = static_cast<int>(p.arity() - 1);
3049 if (user->InputAt(arity) == arguments_list) continue;
3050 break;
3051 }
3052 case IrOpcode::kJSConstructWithSpread: {
3053 // Ignore uses as spread input to construct with spread.
3054 ConstructParameters p = ConstructParametersOf(user->op());
3055 int const arity = static_cast<int>(p.arity() - 2);
3056 if (user->InputAt(arity) == arguments_list) continue;
3057 break;
3058 }
3059 default:
3060 break;
3061 }
3062 // We cannot currently reduce the {node} to something better than what
3063 // it already is, but we might be able to do something about the {node}
3064 // later, so put it on the waitlist and try again during finalization.
3065 waitlist_.insert(node);
3066 return NoChange();
3067 }
3068
3069 // Get to the actual frame state from which to extract the arguments;
3070 // we can only optimize this in case the {node} was already inlined into
3071 // some other function (and same for the {arguments_list}).
3072 CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op());
3073 Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list);
3074 FrameStateInfo state_info = FrameStateInfoOf(frame_state->op());
3075 int start_index = 0;
3076 // Determine the formal parameter count;
3077 Handle<SharedFunctionInfo> shared;
3078 if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
3079 int formal_parameter_count = shared->internal_formal_parameter_count();
3080 if (type == CreateArgumentsType::kMappedArguments) {
3081 // Mapped arguments (sloppy mode) that are aliased can only be handled
3082 // here if there's no side-effect between the {node} and the {arg_array}.
3083 // TODO(turbofan): Further relax this constraint.
3084 if (formal_parameter_count != 0) {
3085 Node* effect = NodeProperties::GetEffectInput(node);
3086 if (!NodeProperties::NoObservableSideEffectBetween(effect,
3087 arguments_list)) {
3088 return NoChange();
3089 }
3090 }
3091 } else if (type == CreateArgumentsType::kRestParameter) {
3092 start_index = formal_parameter_count;
3093 }
3094
3095 // For call/construct with spread, we need to also install a code
3096 // dependency on the array iterator lookup protector cell to ensure
3097 // that no one messed with the %ArrayIteratorPrototype%.next method.
3098 if (node->opcode() == IrOpcode::kJSCallWithSpread ||
3099 node->opcode() == IrOpcode::kJSConstructWithSpread) {
3100 dependencies()->DependOnProtector(PropertyCellRef(
3101 js_heap_broker(), factory()->array_iterator_protector()));
3102 }
3103
3104 // Remove the {arguments_list} input from the {node}.
3105 node->RemoveInput(arity--);
3106 // Check if are spreading to inlined arguments or to the arguments of
3107 // the outermost function.
3108 Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
3109 if (outer_state->opcode() != IrOpcode::kFrameState) {
3110 Operator const* op =
3111 (node->opcode() == IrOpcode::kJSCallWithArrayLike ||
3112 node->opcode() == IrOpcode::kJSCallWithSpread)
3113 ? javascript()->CallForwardVarargs(arity + 1, start_index)
3114 : javascript()->ConstructForwardVarargs(arity + 2, start_index);
3115 NodeProperties::ChangeOp(node, op);
3116 return Changed(node);
3117 }
3118 // Get to the actual frame state from which to extract the arguments;
3119 // we can only optimize this in case the {node} was already inlined into
3120 // some other function (and same for the {arg_array}).
3121 FrameStateInfo outer_info = FrameStateInfoOf(outer_state->op());
3122 if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
3123 // Need to take the parameters from the arguments adaptor.
3124 frame_state = outer_state;
3125 }
3126 // Add the actual parameters to the {node}, skipping the receiver.
3127 Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
3128 for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
3129 node->InsertInput(graph()->zone(), static_cast<int>(++arity),
3130 parameters->InputAt(i));
3131 }
3132
3133 if (node->opcode() == IrOpcode::kJSCallWithArrayLike ||
3134 node->opcode() == IrOpcode::kJSCallWithSpread) {
3135 NodeProperties::ChangeOp(
3136 node, javascript()->Call(arity + 1, frequency, feedback));
3137 Reduction const reduction = ReduceJSCall(node);
3138 return reduction.Changed() ? reduction : Changed(node);
3139 } else {
3140 NodeProperties::ChangeOp(
3141 node, javascript()->Construct(arity + 2, frequency, feedback));
3142 Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
3143 Node* frame_state = NodeProperties::GetFrameStateInput(node);
3144 Node* context = NodeProperties::GetContextInput(node);
3145 Node* effect = NodeProperties::GetEffectInput(node);
3146 Node* control = NodeProperties::GetControlInput(node);
3147
3148 // Check whether the given new target value is a constructor function. The
3149 // replacement {JSConstruct} operator only checks the passed target value
3150 // but relies on the new target value to be implicitly valid.
3151 Node* check =
3152 graph()->NewNode(simplified()->ObjectIsConstructor(), new_target);
3153 Node* check_branch =
3154 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
3155 Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
3156 Node* check_throw = check_fail =
3157 graph()->NewNode(javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
3158 jsgraph()->Constant(MessageTemplate::kNotConstructor),
3159 new_target, context, frame_state, effect, check_fail);
3160 control = graph()->NewNode(common()->IfTrue(), check_branch);
3161 NodeProperties::ReplaceControlInput(node, control);
3162
3163 // Rewire potential exception edges.
3164 Node* on_exception = nullptr;
3165 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
3166 // Create appropriate {IfException} and {IfSuccess} nodes.
3167 Node* if_exception =
3168 graph()->NewNode(common()->IfException(), check_throw, check_fail);
3169 check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
3170
3171 // Join the exception edges.
3172 Node* merge =
3173 graph()->NewNode(common()->Merge(2), if_exception, on_exception);
3174 Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception,
3175 on_exception, merge);
3176 Node* phi =
3177 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
3178 if_exception, on_exception, merge);
3179 ReplaceWithValue(on_exception, phi, ephi, merge);
3180 merge->ReplaceInput(1, on_exception);
3181 ephi->ReplaceInput(1, on_exception);
3182 phi->ReplaceInput(1, on_exception);
3183 }
3184
3185 // The above %ThrowTypeError runtime call is an unconditional throw, making
3186 // it impossible to return a successful completion in this case. We simply
3187 // connect the successful completion to the graph end.
3188 Node* throw_node =
3189 graph()->NewNode(common()->Throw(), check_throw, check_fail);
3190 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
3191
3192 Reduction const reduction = ReduceJSConstruct(node);
3193 return reduction.Changed() ? reduction : Changed(node);
3194 }
3195 }
3196
3197 namespace {
3198
ShouldUseCallICFeedback(Node * node)3199 bool ShouldUseCallICFeedback(Node* node) {
3200 HeapObjectMatcher m(node);
3201 if (m.HasValue() || m.IsJSCreateClosure()) {
3202 // Don't use CallIC feedback when we know the function
3203 // being called, i.e. either know the closure itself or
3204 // at least the SharedFunctionInfo.
3205 return false;
3206 } else if (m.IsPhi()) {
3207 // Protect against endless loops here.
3208 Node* control = NodeProperties::GetControlInput(node);
3209 if (control->opcode() == IrOpcode::kLoop) return false;
3210 // Check if {node} is a Phi of nodes which shouldn't
3211 // use CallIC feedback (not looking through loops).
3212 int const value_input_count = m.node()->op()->ValueInputCount();
3213 for (int n = 0; n < value_input_count; ++n) {
3214 if (ShouldUseCallICFeedback(node->InputAt(n))) return true;
3215 }
3216 return false;
3217 }
3218 return true;
3219 }
3220
3221 } // namespace
3222
ReduceJSCall(Node * node)3223 Reduction JSCallReducer::ReduceJSCall(Node* node) {
3224 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
3225 CallParameters const& p = CallParametersOf(node->op());
3226 Node* target = NodeProperties::GetValueInput(node, 0);
3227 Node* control = NodeProperties::GetControlInput(node);
3228 Node* effect = NodeProperties::GetEffectInput(node);
3229 size_t arity = p.arity();
3230 DCHECK_LE(2u, arity);
3231
3232 // Try to specialize JSCall {node}s with constant {target}s.
3233 HeapObjectMatcher m(target);
3234 if (m.HasValue()) {
3235 if (m.Value()->IsJSFunction()) {
3236 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
3237
3238 // Don't inline cross native context.
3239 if (function->native_context() != *native_context()) return NoChange();
3240
3241 return ReduceJSCall(node, handle(function->shared(), isolate()));
3242 } else if (m.Value()->IsJSBoundFunction()) {
3243 Handle<JSBoundFunction> function =
3244 Handle<JSBoundFunction>::cast(m.Value());
3245 Handle<JSReceiver> bound_target_function(
3246 function->bound_target_function(), isolate());
3247 Handle<Object> bound_this(function->bound_this(), isolate());
3248 Handle<FixedArray> bound_arguments(function->bound_arguments(),
3249 isolate());
3250 ConvertReceiverMode const convert_mode =
3251 (bound_this->IsNullOrUndefined(isolate()))
3252 ? ConvertReceiverMode::kNullOrUndefined
3253 : ConvertReceiverMode::kNotNullOrUndefined;
3254 // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
3255 NodeProperties::ReplaceValueInput(
3256 node, jsgraph()->Constant(bound_target_function), 0);
3257 NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this),
3258 1);
3259 // Insert the [[BoundArguments]] for {node}.
3260 for (int i = 0; i < bound_arguments->length(); ++i) {
3261 node->InsertInput(
3262 graph()->zone(), i + 2,
3263 jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
3264 arity++;
3265 }
3266 NodeProperties::ChangeOp(
3267 node, javascript()->Call(arity, p.frequency(), VectorSlotPair(),
3268 convert_mode));
3269 // Try to further reduce the JSCall {node}.
3270 Reduction const reduction = ReduceJSCall(node);
3271 return reduction.Changed() ? reduction : Changed(node);
3272 }
3273
3274 // Don't mess with other {node}s that have a constant {target}.
3275 // TODO(bmeurer): Also support proxies here.
3276 return NoChange();
3277 }
3278
3279 // If {target} is the result of a JSCreateClosure operation, we can
3280 // just immediately try to inline based on the SharedFunctionInfo,
3281 // since TurboFan generally doesn't inline cross-context, and hence
3282 // the {target} must have the same native context as the call site.
3283 if (target->opcode() == IrOpcode::kJSCreateClosure) {
3284 CreateClosureParameters const& p = CreateClosureParametersOf(target->op());
3285 return ReduceJSCall(node, p.shared_info());
3286 }
3287
3288 // If {target} is the result of a JSCreateBoundFunction operation,
3289 // we can just fold the construction and call the bound target
3290 // function directly instead.
3291 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
3292 Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
3293 Node* bound_this = NodeProperties::GetValueInput(target, 1);
3294 int const bound_arguments_length =
3295 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
3296
3297 // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]].
3298 NodeProperties::ReplaceValueInput(node, bound_target_function, 0);
3299 NodeProperties::ReplaceValueInput(node, bound_this, 1);
3300
3301 // Insert the [[BoundArguments]] for {node}.
3302 for (int i = 0; i < bound_arguments_length; ++i) {
3303 Node* value = NodeProperties::GetValueInput(target, 2 + i);
3304 node->InsertInput(graph()->zone(), 2 + i, value);
3305 arity++;
3306 }
3307
3308 // Update the JSCall operator on {node}.
3309 ConvertReceiverMode const convert_mode =
3310 NodeProperties::CanBeNullOrUndefined(isolate(), bound_this, effect)
3311 ? ConvertReceiverMode::kAny
3312 : ConvertReceiverMode::kNotNullOrUndefined;
3313 NodeProperties::ChangeOp(
3314 node, javascript()->Call(arity, p.frequency(), VectorSlotPair(),
3315 convert_mode));
3316
3317 // Try to further reduce the JSCall {node}.
3318 Reduction const reduction = ReduceJSCall(node);
3319 return reduction.Changed() ? reduction : Changed(node);
3320 }
3321
3322 // Extract feedback from the {node} using the FeedbackNexus.
3323 if (!p.feedback().IsValid()) return NoChange();
3324 FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
3325 if (nexus.IsUninitialized()) {
3326 if (flags() & kBailoutOnUninitialized) {
3327 // Introduce a SOFT deopt if the call {node} wasn't executed so far.
3328 return ReduceSoftDeoptimize(
3329 node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
3330 }
3331 return NoChange();
3332 }
3333
3334 HeapObject* heap_object;
3335 if (nexus.GetFeedback()->ToWeakHeapObject(&heap_object)) {
3336 Handle<HeapObject> feedback(heap_object, isolate());
3337 // Check if we want to use CallIC feedback here.
3338 if (!ShouldUseCallICFeedback(target)) return NoChange();
3339
3340 if (feedback->IsCallable()) {
3341 Node* target_function = jsgraph()->Constant(feedback);
3342
3343 // Check that the {target} is still the {target_function}.
3344 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
3345 target_function);
3346 effect = graph()->NewNode(
3347 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
3348 effect, control);
3349
3350 // Specialize the JSCall node to the {target_function}.
3351 NodeProperties::ReplaceValueInput(node, target_function, 0);
3352 NodeProperties::ReplaceEffectInput(node, effect);
3353
3354 // Try to further reduce the JSCall {node}.
3355 Reduction const reduction = ReduceJSCall(node);
3356 return reduction.Changed() ? reduction : Changed(node);
3357 }
3358 }
3359 return NoChange();
3360 }
3361
ReduceJSCall(Node * node,Handle<SharedFunctionInfo> shared)3362 Reduction JSCallReducer::ReduceJSCall(Node* node,
3363 Handle<SharedFunctionInfo> shared) {
3364 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
3365 Node* target = NodeProperties::GetValueInput(node, 0);
3366
3367 // Do not reduce calls to functions with break points.
3368 if (shared->HasBreakInfo()) return NoChange();
3369
3370 // Raise a TypeError if the {target} is a "classConstructor".
3371 if (IsClassConstructor(shared->kind())) {
3372 NodeProperties::ReplaceValueInputs(node, target);
3373 NodeProperties::ChangeOp(
3374 node, javascript()->CallRuntime(
3375 Runtime::kThrowConstructorNonCallableError, 1));
3376 return Changed(node);
3377 }
3378
3379 // Check for known builtin functions.
3380
3381 int builtin_id =
3382 shared->HasBuiltinId() ? shared->builtin_id() : Builtins::kNoBuiltinId;
3383 switch (builtin_id) {
3384 case Builtins::kArrayConstructor:
3385 return ReduceArrayConstructor(node);
3386 case Builtins::kBooleanConstructor:
3387 return ReduceBooleanConstructor(node);
3388 case Builtins::kFunctionPrototypeApply:
3389 return ReduceFunctionPrototypeApply(node);
3390 case Builtins::kFastFunctionPrototypeBind:
3391 return ReduceFunctionPrototypeBind(node);
3392 case Builtins::kFunctionPrototypeCall:
3393 return ReduceFunctionPrototypeCall(node);
3394 case Builtins::kFunctionPrototypeHasInstance:
3395 return ReduceFunctionPrototypeHasInstance(node);
3396 case Builtins::kObjectConstructor:
3397 return ReduceObjectConstructor(node);
3398 case Builtins::kObjectCreate:
3399 return ReduceObjectCreate(node);
3400 case Builtins::kObjectGetPrototypeOf:
3401 return ReduceObjectGetPrototypeOf(node);
3402 case Builtins::kObjectIs:
3403 return ReduceObjectIs(node);
3404 case Builtins::kObjectPrototypeGetProto:
3405 return ReduceObjectPrototypeGetProto(node);
3406 case Builtins::kObjectPrototypeHasOwnProperty:
3407 return ReduceObjectPrototypeHasOwnProperty(node);
3408 case Builtins::kObjectPrototypeIsPrototypeOf:
3409 return ReduceObjectPrototypeIsPrototypeOf(node);
3410 case Builtins::kReflectApply:
3411 return ReduceReflectApply(node);
3412 case Builtins::kReflectConstruct:
3413 return ReduceReflectConstruct(node);
3414 case Builtins::kReflectGet:
3415 return ReduceReflectGet(node);
3416 case Builtins::kReflectGetPrototypeOf:
3417 return ReduceReflectGetPrototypeOf(node);
3418 case Builtins::kReflectHas:
3419 return ReduceReflectHas(node);
3420 case Builtins::kArrayForEach:
3421 return ReduceArrayForEach(node, shared);
3422 case Builtins::kArrayMap:
3423 return ReduceArrayMap(node, shared);
3424 case Builtins::kArrayFilter:
3425 return ReduceArrayFilter(node, shared);
3426 case Builtins::kArrayReduce:
3427 return ReduceArrayReduce(node, ArrayReduceDirection::kLeft, shared);
3428 case Builtins::kArrayReduceRight:
3429 return ReduceArrayReduce(node, ArrayReduceDirection::kRight, shared);
3430 case Builtins::kArrayPrototypeFind:
3431 return ReduceArrayFind(node, ArrayFindVariant::kFind, shared);
3432 case Builtins::kArrayPrototypeFindIndex:
3433 return ReduceArrayFind(node, ArrayFindVariant::kFindIndex, shared);
3434 case Builtins::kArrayEvery:
3435 return ReduceArrayEvery(node, shared);
3436 case Builtins::kArrayIndexOf:
3437 return ReduceArrayIndexOfIncludes(SearchVariant::kIndexOf, node);
3438 case Builtins::kArrayIncludes:
3439 return ReduceArrayIndexOfIncludes(SearchVariant::kIncludes, node);
3440 case Builtins::kArraySome:
3441 return ReduceArraySome(node, shared);
3442 case Builtins::kArrayPrototypePush:
3443 return ReduceArrayPrototypePush(node);
3444 case Builtins::kArrayPrototypePop:
3445 return ReduceArrayPrototypePop(node);
3446 case Builtins::kArrayPrototypeShift:
3447 return ReduceArrayPrototypeShift(node);
3448 case Builtins::kArrayPrototypeSlice:
3449 return ReduceArrayPrototypeSlice(node);
3450 case Builtins::kArrayPrototypeEntries:
3451 return ReduceArrayIterator(node, IterationKind::kEntries);
3452 case Builtins::kArrayPrototypeKeys:
3453 return ReduceArrayIterator(node, IterationKind::kKeys);
3454 case Builtins::kArrayPrototypeValues:
3455 return ReduceArrayIterator(node, IterationKind::kValues);
3456 case Builtins::kArrayIteratorPrototypeNext:
3457 return ReduceArrayIteratorPrototypeNext(node);
3458 case Builtins::kArrayIsArray:
3459 return ReduceArrayIsArray(node);
3460 case Builtins::kArrayBufferIsView:
3461 return ReduceArrayBufferIsView(node);
3462 case Builtins::kDataViewPrototypeGetByteLength:
3463 return ReduceArrayBufferViewAccessor(
3464 node, JS_DATA_VIEW_TYPE,
3465 AccessBuilder::ForJSArrayBufferViewByteLength());
3466 case Builtins::kDataViewPrototypeGetByteOffset:
3467 return ReduceArrayBufferViewAccessor(
3468 node, JS_DATA_VIEW_TYPE,
3469 AccessBuilder::ForJSArrayBufferViewByteOffset());
3470 case Builtins::kDataViewPrototypeGetUint8:
3471 return ReduceDataViewPrototypeGet(node,
3472 ExternalArrayType::kExternalUint8Array);
3473 case Builtins::kDataViewPrototypeGetInt8:
3474 return ReduceDataViewPrototypeGet(node,
3475 ExternalArrayType::kExternalInt8Array);
3476 case Builtins::kDataViewPrototypeGetUint16:
3477 return ReduceDataViewPrototypeGet(
3478 node, ExternalArrayType::kExternalUint16Array);
3479 case Builtins::kDataViewPrototypeGetInt16:
3480 return ReduceDataViewPrototypeGet(node,
3481 ExternalArrayType::kExternalInt16Array);
3482 case Builtins::kDataViewPrototypeGetUint32:
3483 return ReduceDataViewPrototypeGet(
3484 node, ExternalArrayType::kExternalUint32Array);
3485 case Builtins::kDataViewPrototypeGetInt32:
3486 return ReduceDataViewPrototypeGet(node,
3487 ExternalArrayType::kExternalInt32Array);
3488 case Builtins::kDataViewPrototypeGetFloat32:
3489 return ReduceDataViewPrototypeGet(
3490 node, ExternalArrayType::kExternalFloat32Array);
3491 case Builtins::kDataViewPrototypeGetFloat64:
3492 return ReduceDataViewPrototypeGet(
3493 node, ExternalArrayType::kExternalFloat64Array);
3494 case Builtins::kDataViewPrototypeSetUint8:
3495 return ReduceDataViewPrototypeSet(node,
3496 ExternalArrayType::kExternalUint8Array);
3497 case Builtins::kDataViewPrototypeSetInt8:
3498 return ReduceDataViewPrototypeSet(node,
3499 ExternalArrayType::kExternalInt8Array);
3500 case Builtins::kDataViewPrototypeSetUint16:
3501 return ReduceDataViewPrototypeSet(
3502 node, ExternalArrayType::kExternalUint16Array);
3503 case Builtins::kDataViewPrototypeSetInt16:
3504 return ReduceDataViewPrototypeSet(node,
3505 ExternalArrayType::kExternalInt16Array);
3506 case Builtins::kDataViewPrototypeSetUint32:
3507 return ReduceDataViewPrototypeSet(
3508 node, ExternalArrayType::kExternalUint32Array);
3509 case Builtins::kDataViewPrototypeSetInt32:
3510 return ReduceDataViewPrototypeSet(node,
3511 ExternalArrayType::kExternalInt32Array);
3512 case Builtins::kDataViewPrototypeSetFloat32:
3513 return ReduceDataViewPrototypeSet(
3514 node, ExternalArrayType::kExternalFloat32Array);
3515 case Builtins::kDataViewPrototypeSetFloat64:
3516 return ReduceDataViewPrototypeSet(
3517 node, ExternalArrayType::kExternalFloat64Array);
3518 case Builtins::kTypedArrayPrototypeByteLength:
3519 return ReduceArrayBufferViewAccessor(
3520 node, JS_TYPED_ARRAY_TYPE,
3521 AccessBuilder::ForJSArrayBufferViewByteLength());
3522 case Builtins::kTypedArrayPrototypeByteOffset:
3523 return ReduceArrayBufferViewAccessor(
3524 node, JS_TYPED_ARRAY_TYPE,
3525 AccessBuilder::ForJSArrayBufferViewByteOffset());
3526 case Builtins::kTypedArrayPrototypeLength:
3527 return ReduceArrayBufferViewAccessor(
3528 node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength());
3529 case Builtins::kTypedArrayPrototypeToStringTag:
3530 return ReduceTypedArrayPrototypeToStringTag(node);
3531 case Builtins::kMathAbs:
3532 return ReduceMathUnary(node, simplified()->NumberAbs());
3533 case Builtins::kMathAcos:
3534 return ReduceMathUnary(node, simplified()->NumberAcos());
3535 case Builtins::kMathAcosh:
3536 return ReduceMathUnary(node, simplified()->NumberAcosh());
3537 case Builtins::kMathAsin:
3538 return ReduceMathUnary(node, simplified()->NumberAsin());
3539 case Builtins::kMathAsinh:
3540 return ReduceMathUnary(node, simplified()->NumberAsinh());
3541 case Builtins::kMathAtan:
3542 return ReduceMathUnary(node, simplified()->NumberAtan());
3543 case Builtins::kMathAtanh:
3544 return ReduceMathUnary(node, simplified()->NumberAtanh());
3545 case Builtins::kMathCbrt:
3546 return ReduceMathUnary(node, simplified()->NumberCbrt());
3547 case Builtins::kMathCeil:
3548 return ReduceMathUnary(node, simplified()->NumberCeil());
3549 case Builtins::kMathCos:
3550 return ReduceMathUnary(node, simplified()->NumberCos());
3551 case Builtins::kMathCosh:
3552 return ReduceMathUnary(node, simplified()->NumberCosh());
3553 case Builtins::kMathExp:
3554 return ReduceMathUnary(node, simplified()->NumberExp());
3555 case Builtins::kMathExpm1:
3556 return ReduceMathUnary(node, simplified()->NumberExpm1());
3557 case Builtins::kMathFloor:
3558 return ReduceMathUnary(node, simplified()->NumberFloor());
3559 case Builtins::kMathFround:
3560 return ReduceMathUnary(node, simplified()->NumberFround());
3561 case Builtins::kMathLog:
3562 return ReduceMathUnary(node, simplified()->NumberLog());
3563 case Builtins::kMathLog1p:
3564 return ReduceMathUnary(node, simplified()->NumberLog1p());
3565 case Builtins::kMathLog10:
3566 return ReduceMathUnary(node, simplified()->NumberLog10());
3567 case Builtins::kMathLog2:
3568 return ReduceMathUnary(node, simplified()->NumberLog2());
3569 case Builtins::kMathRound:
3570 return ReduceMathUnary(node, simplified()->NumberRound());
3571 case Builtins::kMathSign:
3572 return ReduceMathUnary(node, simplified()->NumberSign());
3573 case Builtins::kMathSin:
3574 return ReduceMathUnary(node, simplified()->NumberSin());
3575 case Builtins::kMathSinh:
3576 return ReduceMathUnary(node, simplified()->NumberSinh());
3577 case Builtins::kMathSqrt:
3578 return ReduceMathUnary(node, simplified()->NumberSqrt());
3579 case Builtins::kMathTan:
3580 return ReduceMathUnary(node, simplified()->NumberTan());
3581 case Builtins::kMathTanh:
3582 return ReduceMathUnary(node, simplified()->NumberTanh());
3583 case Builtins::kMathTrunc:
3584 return ReduceMathUnary(node, simplified()->NumberTrunc());
3585 case Builtins::kMathAtan2:
3586 return ReduceMathBinary(node, simplified()->NumberAtan2());
3587 case Builtins::kMathPow:
3588 return ReduceMathBinary(node, simplified()->NumberPow());
3589 case Builtins::kMathClz32:
3590 return ReduceMathClz32(node);
3591 case Builtins::kMathImul:
3592 return ReduceMathImul(node);
3593 case Builtins::kMathMax:
3594 return ReduceMathMinMax(node, simplified()->NumberMax(),
3595 jsgraph()->Constant(-V8_INFINITY));
3596 case Builtins::kMathMin:
3597 return ReduceMathMinMax(node, simplified()->NumberMin(),
3598 jsgraph()->Constant(V8_INFINITY));
3599 case Builtins::kNumberIsFinite:
3600 return ReduceNumberIsFinite(node);
3601 case Builtins::kNumberIsInteger:
3602 return ReduceNumberIsInteger(node);
3603 case Builtins::kNumberIsSafeInteger:
3604 return ReduceNumberIsSafeInteger(node);
3605 case Builtins::kNumberIsNaN:
3606 return ReduceNumberIsNaN(node);
3607 case Builtins::kNumberParseInt:
3608 return ReduceNumberParseInt(node);
3609 case Builtins::kGlobalIsFinite:
3610 return ReduceGlobalIsFinite(node);
3611 case Builtins::kGlobalIsNaN:
3612 return ReduceGlobalIsNaN(node);
3613 case Builtins::kMapPrototypeGet:
3614 return ReduceMapPrototypeGet(node);
3615 case Builtins::kMapPrototypeHas:
3616 return ReduceMapPrototypeHas(node);
3617 case Builtins::kRegExpPrototypeTest:
3618 return ReduceRegExpPrototypeTest(node);
3619 case Builtins::kReturnReceiver:
3620 return ReduceReturnReceiver(node);
3621 case Builtins::kStringPrototypeIndexOf:
3622 return ReduceStringPrototypeIndexOf(node);
3623 case Builtins::kStringPrototypeCharAt:
3624 return ReduceStringPrototypeCharAt(node);
3625 case Builtins::kStringPrototypeCharCodeAt:
3626 return ReduceStringPrototypeStringAt(simplified()->StringCharCodeAt(),
3627 node);
3628 case Builtins::kStringPrototypeCodePointAt:
3629 return ReduceStringPrototypeStringAt(
3630 simplified()->StringCodePointAt(UnicodeEncoding::UTF32), node);
3631 case Builtins::kStringPrototypeSubstring:
3632 return ReduceStringPrototypeSubstring(node);
3633 case Builtins::kStringPrototypeSlice:
3634 return ReduceStringPrototypeSlice(node);
3635 case Builtins::kStringPrototypeSubstr:
3636 return ReduceStringPrototypeSubstr(node);
3637 #ifdef V8_INTL_SUPPORT
3638 case Builtins::kStringPrototypeToLowerCaseIntl:
3639 return ReduceStringPrototypeToLowerCaseIntl(node);
3640 case Builtins::kStringPrototypeToUpperCaseIntl:
3641 return ReduceStringPrototypeToUpperCaseIntl(node);
3642 #endif // V8_INTL_SUPPORT
3643 case Builtins::kStringFromCharCode:
3644 return ReduceStringFromCharCode(node);
3645 case Builtins::kStringFromCodePoint:
3646 return ReduceStringFromCodePoint(node);
3647 case Builtins::kStringPrototypeIterator:
3648 return ReduceStringPrototypeIterator(node);
3649 case Builtins::kStringIteratorPrototypeNext:
3650 return ReduceStringIteratorPrototypeNext(node);
3651 case Builtins::kStringPrototypeConcat:
3652 return ReduceStringPrototypeConcat(node, shared);
3653 case Builtins::kTypedArrayPrototypeEntries:
3654 return ReduceArrayIterator(node, IterationKind::kEntries);
3655 case Builtins::kTypedArrayPrototypeKeys:
3656 return ReduceArrayIterator(node, IterationKind::kKeys);
3657 case Builtins::kTypedArrayPrototypeValues:
3658 return ReduceArrayIterator(node, IterationKind::kValues);
3659 case Builtins::kAsyncFunctionPromiseCreate:
3660 return ReduceAsyncFunctionPromiseCreate(node);
3661 case Builtins::kAsyncFunctionPromiseRelease:
3662 return ReduceAsyncFunctionPromiseRelease(node);
3663 case Builtins::kPromiseInternalConstructor:
3664 return ReducePromiseInternalConstructor(node);
3665 case Builtins::kPromiseInternalReject:
3666 return ReducePromiseInternalReject(node);
3667 case Builtins::kPromiseInternalResolve:
3668 return ReducePromiseInternalResolve(node);
3669 case Builtins::kPromisePrototypeCatch:
3670 return ReducePromisePrototypeCatch(node);
3671 case Builtins::kPromisePrototypeFinally:
3672 return ReducePromisePrototypeFinally(node);
3673 case Builtins::kPromisePrototypeThen:
3674 return ReducePromisePrototypeThen(node);
3675 case Builtins::kMapPrototypeEntries:
3676 return ReduceCollectionIteration(node, CollectionKind::kMap,
3677 IterationKind::kEntries);
3678 case Builtins::kMapPrototypeKeys:
3679 return ReduceCollectionIteration(node, CollectionKind::kMap,
3680 IterationKind::kKeys);
3681 case Builtins::kMapPrototypeGetSize:
3682 return ReduceCollectionPrototypeSize(node, CollectionKind::kMap);
3683 case Builtins::kMapPrototypeValues:
3684 return ReduceCollectionIteration(node, CollectionKind::kMap,
3685 IterationKind::kValues);
3686 case Builtins::kMapIteratorPrototypeNext:
3687 return ReduceCollectionIteratorPrototypeNext(
3688 node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(),
3689 FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE);
3690 case Builtins::kSetPrototypeEntries:
3691 return ReduceCollectionIteration(node, CollectionKind::kSet,
3692 IterationKind::kEntries);
3693 case Builtins::kSetPrototypeGetSize:
3694 return ReduceCollectionPrototypeSize(node, CollectionKind::kSet);
3695 case Builtins::kSetPrototypeValues:
3696 return ReduceCollectionIteration(node, CollectionKind::kSet,
3697 IterationKind::kValues);
3698 case Builtins::kSetIteratorPrototypeNext:
3699 return ReduceCollectionIteratorPrototypeNext(
3700 node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(),
3701 FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE);
3702 case Builtins::kDatePrototypeGetTime:
3703 return ReduceDatePrototypeGetTime(node);
3704 case Builtins::kDateNow:
3705 return ReduceDateNow(node);
3706 case Builtins::kNumberConstructor:
3707 return ReduceNumberConstructor(node);
3708 default:
3709 break;
3710 }
3711
3712 if (!FLAG_runtime_stats && shared->IsApiFunction()) {
3713 return ReduceCallApiFunction(node, shared);
3714 }
3715 return NoChange();
3716 }
3717
ReduceJSCallWithArrayLike(Node * node)3718 Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) {
3719 DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode());
3720 CallFrequency frequency = CallFrequencyOf(node->op());
3721 VectorSlotPair feedback;
3722 return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency,
3723 feedback);
3724 }
3725
ReduceJSCallWithSpread(Node * node)3726 Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
3727 DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode());
3728 CallParameters const& p = CallParametersOf(node->op());
3729 DCHECK_LE(3u, p.arity());
3730 int arity = static_cast<int>(p.arity() - 1);
3731 CallFrequency frequency = p.frequency();
3732 VectorSlotPair feedback = p.feedback();
3733 return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
3734 feedback);
3735 }
3736
ReduceJSConstruct(Node * node)3737 Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
3738 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
3739 ConstructParameters const& p = ConstructParametersOf(node->op());
3740 DCHECK_LE(2u, p.arity());
3741 int arity = static_cast<int>(p.arity() - 2);
3742 Node* target = NodeProperties::GetValueInput(node, 0);
3743 Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
3744 Node* effect = NodeProperties::GetEffectInput(node);
3745 Node* control = NodeProperties::GetControlInput(node);
3746
3747 // Extract feedback from the {node} using the FeedbackNexus.
3748 if (p.feedback().IsValid()) {
3749 FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
3750 if (nexus.IsUninitialized()) {
3751 if (flags() & kBailoutOnUninitialized) {
3752 // Introduce a SOFT deopt if the construct {node} wasn't executed so
3753 // far.
3754 return ReduceSoftDeoptimize(
3755 node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
3756 }
3757 return NoChange();
3758 }
3759
3760 HeapObject* feedback_object;
3761 if (nexus.GetFeedback()->ToStrongHeapObject(&feedback_object) &&
3762 feedback_object->IsAllocationSite()) {
3763 // The feedback is an AllocationSite, which means we have called the
3764 // Array function and collected transition (and pretenuring) feedback
3765 // for the resulting arrays. This has to be kept in sync with the
3766 // implementation in Ignition.
3767 Handle<AllocationSite> site(AllocationSite::cast(feedback_object),
3768 isolate());
3769
3770 // Retrieve the Array function from the {node}.
3771 Node* array_function = jsgraph()->HeapConstant(
3772 handle(native_context()->array_function(), isolate()));
3773
3774 // Check that the {target} is still the {array_function}.
3775 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
3776 array_function);
3777 effect = graph()->NewNode(
3778 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
3779 effect, control);
3780
3781 // Turn the {node} into a {JSCreateArray} call.
3782 NodeProperties::ReplaceEffectInput(node, effect);
3783 for (int i = arity; i > 0; --i) {
3784 NodeProperties::ReplaceValueInput(
3785 node, NodeProperties::GetValueInput(node, i), i + 1);
3786 }
3787 NodeProperties::ReplaceValueInput(node, array_function, 1);
3788 NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
3789 return Changed(node);
3790 } else if (nexus.GetFeedback()->ToWeakHeapObject(&feedback_object) &&
3791 !HeapObjectMatcher(new_target).HasValue()) {
3792 Handle<HeapObject> object(feedback_object, isolate());
3793 if (object->IsConstructor()) {
3794 Node* new_target_feedback = jsgraph()->Constant(object);
3795
3796 // Check that the {new_target} is still the {new_target_feedback}.
3797 Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
3798 new_target, new_target_feedback);
3799 effect = graph()->NewNode(
3800 simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check,
3801 effect, control);
3802
3803 // Specialize the JSConstruct node to the {new_target_feedback}.
3804 NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1);
3805 NodeProperties::ReplaceEffectInput(node, effect);
3806 if (target == new_target) {
3807 NodeProperties::ReplaceValueInput(node, new_target_feedback, 0);
3808 }
3809
3810 // Try to further reduce the JSConstruct {node}.
3811 Reduction const reduction = ReduceJSConstruct(node);
3812 return reduction.Changed() ? reduction : Changed(node);
3813 }
3814 }
3815 }
3816
3817 // Try to specialize JSConstruct {node}s with constant {target}s.
3818 HeapObjectMatcher m(target);
3819 if (m.HasValue()) {
3820 // Raise a TypeError if the {target} is not a constructor.
3821 if (!m.Value()->IsConstructor()) {
3822 NodeProperties::ReplaceValueInputs(node, target);
3823 NodeProperties::ChangeOp(node,
3824 javascript()->CallRuntime(
3825 Runtime::kThrowConstructedNonConstructable));
3826 return Changed(node);
3827 }
3828
3829 if (m.Value()->IsJSFunction()) {
3830 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
3831
3832 // Do not reduce constructors with break points.
3833 if (function->shared()->HasBreakInfo()) return NoChange();
3834
3835 // Don't inline cross native context.
3836 if (function->native_context() != *native_context()) return NoChange();
3837
3838 // Check for known builtin functions.
3839 int builtin_id = function->shared()->HasBuiltinId()
3840 ? function->shared()->builtin_id()
3841 : Builtins::kNoBuiltinId;
3842 switch (builtin_id) {
3843 case Builtins::kArrayConstructor: {
3844 // TODO(bmeurer): Deal with Array subclasses here.
3845 Handle<AllocationSite> site;
3846 // Turn the {node} into a {JSCreateArray} call.
3847 for (int i = arity; i > 0; --i) {
3848 NodeProperties::ReplaceValueInput(
3849 node, NodeProperties::GetValueInput(node, i), i + 1);
3850 }
3851 NodeProperties::ReplaceValueInput(node, new_target, 1);
3852 NodeProperties::ChangeOp(node,
3853 javascript()->CreateArray(arity, site));
3854 return Changed(node);
3855 }
3856 case Builtins::kObjectConstructor: {
3857 // If no value is passed, we can immediately lower to a simple
3858 // JSCreate and don't need to do any massaging of the {node}.
3859 if (arity == 0) {
3860 NodeProperties::ChangeOp(node, javascript()->Create());
3861 return Changed(node);
3862 }
3863
3864 // Otherwise we can only lower to JSCreate if we know that
3865 // the value parameter is ignored, which is only the case if
3866 // the {new_target} and {target} are definitely not identical.
3867 HeapObjectMatcher mnew_target(new_target);
3868 if (mnew_target.HasValue() && *mnew_target.Value() != *function) {
3869 // Drop the value inputs.
3870 for (int i = arity; i > 0; --i) node->RemoveInput(i);
3871 NodeProperties::ChangeOp(node, javascript()->Create());
3872 return Changed(node);
3873 }
3874 break;
3875 }
3876 case Builtins::kPromiseConstructor:
3877 return ReducePromiseConstructor(node);
3878 case Builtins::kTypedArrayConstructor:
3879 return ReduceTypedArrayConstructor(
3880 node, handle(function->shared(), isolate()));
3881 default:
3882 break;
3883 }
3884 } else if (m.Value()->IsJSBoundFunction()) {
3885 Handle<JSBoundFunction> function =
3886 Handle<JSBoundFunction>::cast(m.Value());
3887 Handle<JSReceiver> bound_target_function(
3888 function->bound_target_function(), isolate());
3889 Handle<FixedArray> bound_arguments(function->bound_arguments(),
3890 isolate());
3891
3892 // Patch {node} to use [[BoundTargetFunction]].
3893 NodeProperties::ReplaceValueInput(
3894 node, jsgraph()->Constant(bound_target_function), 0);
3895
3896 // Patch {node} to use [[BoundTargetFunction]]
3897 // as new.target if {new_target} equals {target}.
3898 NodeProperties::ReplaceValueInput(
3899 node,
3900 graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
3901 graph()->NewNode(simplified()->ReferenceEqual(),
3902 target, new_target),
3903 jsgraph()->Constant(bound_target_function),
3904 new_target),
3905 arity + 1);
3906
3907 // Insert the [[BoundArguments]] for {node}.
3908 for (int i = 0; i < bound_arguments->length(); ++i) {
3909 node->InsertInput(
3910 graph()->zone(), i + 1,
3911 jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
3912 arity++;
3913 }
3914
3915 // Update the JSConstruct operator on {node}.
3916 NodeProperties::ChangeOp(
3917 node,
3918 javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair()));
3919
3920 // Try to further reduce the JSConstruct {node}.
3921 Reduction const reduction = ReduceJSConstruct(node);
3922 return reduction.Changed() ? reduction : Changed(node);
3923 }
3924
3925 // TODO(bmeurer): Also support optimizing proxies here.
3926 }
3927
3928 // If {target} is the result of a JSCreateBoundFunction operation,
3929 // we can just fold the construction and construct the bound target
3930 // function directly instead.
3931 if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
3932 Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
3933 int const bound_arguments_length =
3934 static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
3935
3936 // Patch the {node} to use [[BoundTargetFunction]].
3937 NodeProperties::ReplaceValueInput(node, bound_target_function, 0);
3938
3939 // Patch {node} to use [[BoundTargetFunction]]
3940 // as new.target if {new_target} equals {target}.
3941 NodeProperties::ReplaceValueInput(
3942 node,
3943 graph()->NewNode(common()->Select(MachineRepresentation::kTagged),
3944 graph()->NewNode(simplified()->ReferenceEqual(),
3945 target, new_target),
3946 bound_target_function, new_target),
3947 arity + 1);
3948
3949 // Insert the [[BoundArguments]] for {node}.
3950 for (int i = 0; i < bound_arguments_length; ++i) {
3951 Node* value = NodeProperties::GetValueInput(target, 2 + i);
3952 node->InsertInput(graph()->zone(), 1 + i, value);
3953 arity++;
3954 }
3955
3956 // Update the JSConstruct operator on {node}.
3957 NodeProperties::ChangeOp(
3958 node,
3959 javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair()));
3960
3961 // Try to further reduce the JSConstruct {node}.
3962 Reduction const reduction = ReduceJSConstruct(node);
3963 return reduction.Changed() ? reduction : Changed(node);
3964 }
3965
3966 return NoChange();
3967 }
3968
3969 // ES #sec-string.prototype.indexof
ReduceStringPrototypeIndexOf(Node * node)3970 Reduction JSCallReducer::ReduceStringPrototypeIndexOf(Node* node) {
3971 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
3972 CallParameters const& p = CallParametersOf(node->op());
3973 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
3974 return NoChange();
3975 }
3976
3977 Node* effect = NodeProperties::GetEffectInput(node);
3978 Node* control = NodeProperties::GetControlInput(node);
3979 if (node->op()->ValueInputCount() >= 3) {
3980 Node* receiver = NodeProperties::GetValueInput(node, 1);
3981 Node* new_receiver = effect = graph()->NewNode(
3982 simplified()->CheckString(p.feedback()), receiver, effect, control);
3983
3984 Node* search_string = NodeProperties::GetValueInput(node, 2);
3985 Node* new_search_string = effect =
3986 graph()->NewNode(simplified()->CheckString(p.feedback()), search_string,
3987 effect, control);
3988
3989 Node* new_position = jsgraph()->ZeroConstant();
3990 if (node->op()->ValueInputCount() >= 4) {
3991 Node* position = NodeProperties::GetValueInput(node, 3);
3992 new_position = effect = graph()->NewNode(
3993 simplified()->CheckSmi(p.feedback()), position, effect, control);
3994 }
3995
3996 NodeProperties::ReplaceEffectInput(node, effect);
3997 RelaxEffectsAndControls(node);
3998 node->ReplaceInput(0, new_receiver);
3999 node->ReplaceInput(1, new_search_string);
4000 node->ReplaceInput(2, new_position);
4001 node->TrimInputCount(3);
4002 NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
4003 return Changed(node);
4004 }
4005 return NoChange();
4006 }
4007
4008 // ES #sec-string.prototype.substring
ReduceStringPrototypeSubstring(Node * node)4009 Reduction JSCallReducer::ReduceStringPrototypeSubstring(Node* node) {
4010 if (node->op()->ValueInputCount() < 3) return NoChange();
4011 CallParameters const& p = CallParametersOf(node->op());
4012 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4013 return NoChange();
4014 }
4015
4016 Node* effect = NodeProperties::GetEffectInput(node);
4017 Node* control = NodeProperties::GetControlInput(node);
4018 Node* receiver = NodeProperties::GetValueInput(node, 1);
4019 Node* start = NodeProperties::GetValueInput(node, 2);
4020 Node* end = node->op()->ValueInputCount() > 3
4021 ? NodeProperties::GetValueInput(node, 3)
4022 : jsgraph()->UndefinedConstant();
4023
4024 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
4025 receiver, effect, control);
4026
4027 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
4028 effect, control);
4029
4030 Node* length = graph()->NewNode(simplified()->StringLength(), receiver);
4031
4032 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
4033 jsgraph()->UndefinedConstant());
4034 Node* branch =
4035 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
4036
4037 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
4038 Node* etrue = effect;
4039 Node* vtrue = length;
4040
4041 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
4042 Node* efalse = effect;
4043 Node* vfalse = efalse = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
4044 end, efalse, if_false);
4045
4046 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
4047 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
4048 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4049 vtrue, vfalse, control);
4050 Node* finalStart =
4051 graph()->NewNode(simplified()->NumberMin(),
4052 graph()->NewNode(simplified()->NumberMax(), start,
4053 jsgraph()->ZeroConstant()),
4054 length);
4055 Node* finalEnd =
4056 graph()->NewNode(simplified()->NumberMin(),
4057 graph()->NewNode(simplified()->NumberMax(), end,
4058 jsgraph()->ZeroConstant()),
4059 length);
4060
4061 Node* from =
4062 graph()->NewNode(simplified()->NumberMin(), finalStart, finalEnd);
4063 Node* to = graph()->NewNode(simplified()->NumberMax(), finalStart, finalEnd);
4064
4065 Node* value = effect = graph()->NewNode(simplified()->StringSubstring(),
4066 receiver, from, to, effect, control);
4067 ReplaceWithValue(node, value, effect, control);
4068 return Replace(value);
4069 }
4070
4071 // ES #sec-string.prototype.slice
ReduceStringPrototypeSlice(Node * node)4072 Reduction JSCallReducer::ReduceStringPrototypeSlice(Node* node) {
4073 if (node->op()->ValueInputCount() < 3) return NoChange();
4074 CallParameters const& p = CallParametersOf(node->op());
4075 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4076 return NoChange();
4077 }
4078
4079 Node* effect = NodeProperties::GetEffectInput(node);
4080 Node* control = NodeProperties::GetControlInput(node);
4081 Node* receiver = NodeProperties::GetValueInput(node, 1);
4082 Node* start = NodeProperties::GetValueInput(node, 2);
4083 Node* end = node->op()->ValueInputCount() > 3
4084 ? NodeProperties::GetValueInput(node, 3)
4085 : jsgraph()->UndefinedConstant();
4086
4087 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
4088 receiver, effect, control);
4089
4090 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
4091 effect, control);
4092
4093 Node* length = graph()->NewNode(simplified()->StringLength(), receiver);
4094
4095 // Replace {end} argument with {length} if it is undefined.
4096 {
4097 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
4098 jsgraph()->UndefinedConstant());
4099
4100 Node* branch =
4101 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
4102
4103 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
4104 Node* etrue = effect;
4105 Node* vtrue = length;
4106
4107 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
4108 Node* efalse = effect;
4109 Node* vfalse = efalse = graph()->NewNode(
4110 simplified()->CheckSmi(p.feedback()), end, efalse, if_false);
4111
4112 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
4113 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
4114 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4115 vtrue, vfalse, control);
4116 }
4117
4118 Node* from = graph()->NewNode(
4119 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
4120 graph()->NewNode(simplified()->NumberLessThan(), start,
4121 jsgraph()->ZeroConstant()),
4122 graph()->NewNode(
4123 simplified()->NumberMax(),
4124 graph()->NewNode(simplified()->NumberAdd(), length, start),
4125 jsgraph()->ZeroConstant()),
4126 graph()->NewNode(simplified()->NumberMin(), start, length));
4127 // {from} is always in non-negative Smi range, but our typer cannot
4128 // figure that out yet.
4129 from = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()),
4130 from, effect, control);
4131
4132 Node* to = graph()->NewNode(
4133 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
4134 graph()->NewNode(simplified()->NumberLessThan(), end,
4135 jsgraph()->ZeroConstant()),
4136 graph()->NewNode(simplified()->NumberMax(),
4137 graph()->NewNode(simplified()->NumberAdd(), length, end),
4138 jsgraph()->ZeroConstant()),
4139 graph()->NewNode(simplified()->NumberMin(), end, length));
4140 // {to} is always in non-negative Smi range, but our typer cannot
4141 // figure that out yet.
4142 to = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), to,
4143 effect, control);
4144
4145 Node* result_string = nullptr;
4146 // Return empty string if {from} is smaller than {to}.
4147 {
4148 Node* check = graph()->NewNode(simplified()->NumberLessThan(), from, to);
4149
4150 Node* branch =
4151 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
4152
4153 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
4154 Node* etrue = effect;
4155 Node* vtrue = etrue = graph()->NewNode(simplified()->StringSubstring(),
4156 receiver, from, to, etrue, if_true);
4157
4158 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
4159 Node* efalse = effect;
4160 Node* vfalse = jsgraph()->EmptyStringConstant();
4161
4162 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
4163 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
4164 result_string =
4165 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4166 vtrue, vfalse, control);
4167 }
4168
4169 ReplaceWithValue(node, result_string, effect, control);
4170 return Replace(result_string);
4171 }
4172
4173 // ES #sec-string.prototype.substr
ReduceStringPrototypeSubstr(Node * node)4174 Reduction JSCallReducer::ReduceStringPrototypeSubstr(Node* node) {
4175 if (node->op()->ValueInputCount() < 3) return NoChange();
4176 CallParameters const& p = CallParametersOf(node->op());
4177 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4178 return NoChange();
4179 }
4180
4181 Node* effect = NodeProperties::GetEffectInput(node);
4182 Node* control = NodeProperties::GetControlInput(node);
4183 Node* receiver = NodeProperties::GetValueInput(node, 1);
4184 Node* start = NodeProperties::GetValueInput(node, 2);
4185 Node* end = node->op()->ValueInputCount() > 3
4186 ? NodeProperties::GetValueInput(node, 3)
4187 : jsgraph()->UndefinedConstant();
4188
4189 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
4190 receiver, effect, control);
4191
4192 start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start,
4193 effect, control);
4194
4195 Node* length = graph()->NewNode(simplified()->StringLength(), receiver);
4196
4197 // Replace {end} argument with {length} if it is undefined.
4198 {
4199 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end,
4200 jsgraph()->UndefinedConstant());
4201 Node* branch =
4202 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
4203
4204 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
4205 Node* etrue = effect;
4206 Node* vtrue = length;
4207
4208 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
4209 Node* efalse = effect;
4210 Node* vfalse = efalse = graph()->NewNode(
4211 simplified()->CheckSmi(p.feedback()), end, efalse, if_false);
4212
4213 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
4214 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
4215 end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4216 vtrue, vfalse, control);
4217 }
4218
4219 Node* initStart = graph()->NewNode(
4220 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
4221 graph()->NewNode(simplified()->NumberLessThan(), start,
4222 jsgraph()->ZeroConstant()),
4223 graph()->NewNode(
4224 simplified()->NumberMax(),
4225 graph()->NewNode(simplified()->NumberAdd(), length, start),
4226 jsgraph()->ZeroConstant()),
4227 start);
4228 // The select above guarantees that initStart is non-negative, but
4229 // our typer can't figure that out yet.
4230 initStart = effect = graph()->NewNode(
4231 common()->TypeGuard(Type::UnsignedSmall()), initStart, effect, control);
4232
4233 Node* resultLength = graph()->NewNode(
4234 simplified()->NumberMin(),
4235 graph()->NewNode(simplified()->NumberMax(), end,
4236 jsgraph()->ZeroConstant()),
4237 graph()->NewNode(simplified()->NumberSubtract(), length, initStart));
4238
4239 // The the select below uses {resultLength} only if {resultLength > 0},
4240 // but our typer can't figure that out yet.
4241 Node* to = effect = graph()->NewNode(
4242 common()->TypeGuard(Type::UnsignedSmall()),
4243 graph()->NewNode(simplified()->NumberAdd(), initStart, resultLength),
4244 effect, control);
4245
4246 Node* result_string = nullptr;
4247 // Return empty string if {from} is smaller than {to}.
4248 {
4249 Node* check = graph()->NewNode(simplified()->NumberLessThan(),
4250 jsgraph()->ZeroConstant(), resultLength);
4251
4252 Node* branch =
4253 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
4254
4255 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
4256 Node* etrue = effect;
4257 Node* vtrue = etrue =
4258 graph()->NewNode(simplified()->StringSubstring(), receiver, initStart,
4259 to, etrue, if_true);
4260
4261 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
4262 Node* efalse = effect;
4263 Node* vfalse = jsgraph()->EmptyStringConstant();
4264
4265 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
4266 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
4267 result_string =
4268 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4269 vtrue, vfalse, control);
4270 }
4271
4272 ReplaceWithValue(node, result_string, effect, control);
4273 return Replace(result_string);
4274 }
4275
ReduceJSConstructWithArrayLike(Node * node)4276 Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
4277 DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
4278 CallFrequency frequency = CallFrequencyOf(node->op());
4279 VectorSlotPair feedback;
4280 return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency,
4281 feedback);
4282 }
4283
ReduceJSConstructWithSpread(Node * node)4284 Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
4285 DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
4286 ConstructParameters const& p = ConstructParametersOf(node->op());
4287 DCHECK_LE(3u, p.arity());
4288 int arity = static_cast<int>(p.arity() - 2);
4289 CallFrequency frequency = p.frequency();
4290 VectorSlotPair feedback = p.feedback();
4291 return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
4292 feedback);
4293 }
4294
ReduceReturnReceiver(Node * node)4295 Reduction JSCallReducer::ReduceReturnReceiver(Node* node) {
4296 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4297 Node* receiver = NodeProperties::GetValueInput(node, 1);
4298 ReplaceWithValue(node, receiver);
4299 return Replace(receiver);
4300 }
4301
ReduceSoftDeoptimize(Node * node,DeoptimizeReason reason)4302 Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node,
4303 DeoptimizeReason reason) {
4304 Node* effect = NodeProperties::GetEffectInput(node);
4305 Node* control = NodeProperties::GetControlInput(node);
4306 Node* frame_state = NodeProperties::FindFrameStateBefore(node);
4307 Node* deoptimize = graph()->NewNode(
4308 common()->Deoptimize(DeoptimizeKind::kSoft, reason, VectorSlotPair()),
4309 frame_state, effect, control);
4310 // TODO(bmeurer): This should be on the AdvancedReducer somehow.
4311 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
4312 Revisit(graph()->end());
4313 node->TrimInputCount(0);
4314 NodeProperties::ChangeOp(node, common()->Dead());
4315 return Changed(node);
4316 }
4317
4318 namespace {
4319
4320 // TODO(turbofan): This was copied from old compiler, might be too restrictive.
IsReadOnlyLengthDescriptor(Isolate * isolate,Handle<Map> jsarray_map)4321 bool IsReadOnlyLengthDescriptor(Isolate* isolate, Handle<Map> jsarray_map) {
4322 DCHECK(!jsarray_map->is_dictionary_map());
4323 Handle<Name> length_string = isolate->factory()->length_string();
4324 DescriptorArray* descriptors = jsarray_map->instance_descriptors();
4325 int number = descriptors->Search(*length_string, *jsarray_map);
4326 DCHECK_NE(DescriptorArray::kNotFound, number);
4327 return descriptors->GetDetails(number).IsReadOnly();
4328 }
4329
4330 // TODO(turbofan): This was copied from old compiler, might be too restrictive.
CanInlineArrayResizeOperation(Isolate * isolate,Handle<Map> receiver_map)4331 bool CanInlineArrayResizeOperation(Isolate* isolate, Handle<Map> receiver_map) {
4332 if (!receiver_map->prototype()->IsJSArray()) return false;
4333 Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
4334 isolate);
4335 return receiver_map->instance_type() == JS_ARRAY_TYPE &&
4336 IsFastElementsKind(receiver_map->elements_kind()) &&
4337 !receiver_map->is_dictionary_map() && receiver_map->is_extensible() &&
4338 isolate->IsAnyInitialArrayPrototype(receiver_prototype) &&
4339 !IsReadOnlyLengthDescriptor(isolate, receiver_map);
4340 }
4341
4342 } // namespace
4343
4344 // ES6 section 22.1.3.18 Array.prototype.push ( )
ReduceArrayPrototypePush(Node * node)4345 Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) {
4346 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4347 CallParameters const& p = CallParametersOf(node->op());
4348 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4349 return NoChange();
4350 }
4351
4352 if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
4353
4354 int const num_values = node->op()->ValueInputCount() - 2;
4355 Node* receiver = NodeProperties::GetValueInput(node, 1);
4356 Node* effect = NodeProperties::GetEffectInput(node);
4357 Node* control = NodeProperties::GetControlInput(node);
4358
4359 // Try to determine the {receiver} map(s).
4360 ZoneHandleSet<Map> receiver_maps;
4361 NodeProperties::InferReceiverMapsResult result =
4362 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
4363 &receiver_maps);
4364 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
4365 DCHECK_NE(0, receiver_maps.size());
4366
4367 ElementsKind kind = receiver_maps[0]->elements_kind();
4368
4369 for (Handle<Map> receiver_map : receiver_maps) {
4370 if (!CanInlineArrayResizeOperation(isolate(), receiver_map))
4371 return NoChange();
4372 if (!UnionElementsKindUptoPackedness(&kind, receiver_map->elements_kind()))
4373 return NoChange();
4374 }
4375
4376 // Install code dependencies on the {receiver} global array protector cell.
4377 dependencies()->DependOnProtector(
4378 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
4379
4380 // If the {receiver_maps} information is not reliable, we need
4381 // to check that the {receiver} still has one of these maps.
4382 if (result == NodeProperties::kUnreliableReceiverMaps) {
4383 effect =
4384 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
4385 receiver_maps, p.feedback()),
4386 receiver, effect, control);
4387 }
4388
4389 // Collect the value inputs to push.
4390 std::vector<Node*> values(num_values);
4391 for (int i = 0; i < num_values; ++i) {
4392 values[i] = NodeProperties::GetValueInput(node, 2 + i);
4393 }
4394
4395 for (auto& value : values) {
4396 if (IsSmiElementsKind(kind)) {
4397 value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
4398 value, effect, control);
4399 } else if (IsDoubleElementsKind(kind)) {
4400 value = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()),
4401 value, effect, control);
4402 // Make sure we do not store signaling NaNs into double arrays.
4403 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
4404 }
4405 }
4406
4407 // Load the "length" property of the {receiver}.
4408 Node* length = effect = graph()->NewNode(
4409 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
4410 effect, control);
4411 Node* value = length;
4412
4413 // Check if we have any {values} to push.
4414 if (num_values > 0) {
4415 // Compute the resulting "length" of the {receiver}.
4416 Node* new_length = value = graph()->NewNode(
4417 simplified()->NumberAdd(), length, jsgraph()->Constant(num_values));
4418
4419 // Load the elements backing store of the {receiver}.
4420 Node* elements = effect = graph()->NewNode(
4421 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
4422 effect, control);
4423 Node* elements_length = effect = graph()->NewNode(
4424 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements,
4425 effect, control);
4426
4427 GrowFastElementsMode mode =
4428 IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements
4429 : GrowFastElementsMode::kSmiOrObjectElements;
4430 elements = effect = graph()->NewNode(
4431 simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver,
4432 elements,
4433 graph()->NewNode(simplified()->NumberAdd(), length,
4434 jsgraph()->Constant(num_values - 1)),
4435 elements_length, effect, control);
4436
4437 // Update the JSArray::length field. Since this is observable,
4438 // there must be no other check after this.
4439 effect = graph()->NewNode(
4440 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
4441 receiver, new_length, effect, control);
4442
4443 // Append the {values} to the {elements}.
4444 for (int i = 0; i < num_values; ++i) {
4445 Node* value = values[i];
4446 Node* index = graph()->NewNode(simplified()->NumberAdd(), length,
4447 jsgraph()->Constant(i));
4448 effect = graph()->NewNode(
4449 simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)),
4450 elements, index, value, effect, control);
4451 }
4452 }
4453
4454 ReplaceWithValue(node, value, effect, control);
4455 return Replace(value);
4456 }
4457
4458 // ES6 section 22.1.3.17 Array.prototype.pop ( )
ReduceArrayPrototypePop(Node * node)4459 Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) {
4460 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4461 CallParameters const& p = CallParametersOf(node->op());
4462 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4463 return NoChange();
4464 }
4465
4466 if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
4467
4468 Node* receiver = NodeProperties::GetValueInput(node, 1);
4469 Node* effect = NodeProperties::GetEffectInput(node);
4470 Node* control = NodeProperties::GetControlInput(node);
4471
4472 ZoneHandleSet<Map> receiver_maps;
4473 NodeProperties::InferReceiverMapsResult result =
4474 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
4475 &receiver_maps);
4476 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
4477 DCHECK_NE(0, receiver_maps.size());
4478
4479 ElementsKind kind = receiver_maps[0]->elements_kind();
4480 for (Handle<Map> receiver_map : receiver_maps) {
4481 if (!CanInlineArrayResizeOperation(isolate(), receiver_map))
4482 return NoChange();
4483 // TODO(turbofan): Extend this to also handle fast holey double elements
4484 // once we got the hole NaN mess sorted out in TurboFan/V8.
4485 if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS)
4486 return NoChange();
4487 if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind()))
4488 return NoChange();
4489 }
4490
4491 // Install code dependencies on the {receiver} global array protector cell.
4492 dependencies()->DependOnProtector(
4493 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
4494
4495 // If the {receiver_maps} information is not reliable, we need
4496 // to check that the {receiver} still has one of these maps.
4497 if (result == NodeProperties::kUnreliableReceiverMaps) {
4498 effect =
4499 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
4500 receiver_maps, p.feedback()),
4501 receiver, effect, control);
4502 }
4503
4504 // Load the "length" property of the {receiver}.
4505 Node* length = effect = graph()->NewNode(
4506 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
4507 effect, control);
4508
4509 // Check if the {receiver} has any elements.
4510 Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
4511 jsgraph()->ZeroConstant());
4512 Node* branch =
4513 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
4514
4515 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
4516 Node* etrue = effect;
4517 Node* vtrue = jsgraph()->UndefinedConstant();
4518
4519 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
4520 Node* efalse = effect;
4521 Node* vfalse;
4522 {
4523 // TODO(tebbi): We should trim the backing store if the capacity is too
4524 // big, as implemented in elements.cc:ElementsAccessorBase::SetLengthImpl.
4525
4526 // Load the elements backing store from the {receiver}.
4527 Node* elements = efalse = graph()->NewNode(
4528 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
4529 efalse, if_false);
4530
4531 // Ensure that we aren't popping from a copy-on-write backing store.
4532 if (IsSmiOrObjectElementsKind(kind)) {
4533 elements = efalse =
4534 graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver,
4535 elements, efalse, if_false);
4536 }
4537
4538 // Compute the new {length}.
4539 length = graph()->NewNode(simplified()->NumberSubtract(), length,
4540 jsgraph()->OneConstant());
4541
4542 // Store the new {length} to the {receiver}.
4543 efalse = graph()->NewNode(
4544 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
4545 receiver, length, efalse, if_false);
4546
4547 // Load the last entry from the {elements}.
4548 vfalse = efalse = graph()->NewNode(
4549 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
4550 elements, length, efalse, if_false);
4551
4552 // Store a hole to the element we just removed from the {receiver}.
4553 efalse = graph()->NewNode(
4554 simplified()->StoreElement(
4555 AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))),
4556 elements, length, jsgraph()->TheHoleConstant(), efalse, if_false);
4557 }
4558
4559 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
4560 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
4561 Node* value = graph()->NewNode(
4562 common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
4563
4564 // Convert the hole to undefined. Do this last, so that we can optimize
4565 // conversion operator via some smart strength reduction in many cases.
4566 if (IsHoleyElementsKind(kind)) {
4567 value =
4568 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
4569 }
4570
4571 ReplaceWithValue(node, value, effect, control);
4572 return Replace(value);
4573 }
4574
4575 // ES6 section 22.1.3.22 Array.prototype.shift ( )
ReduceArrayPrototypeShift(Node * node)4576 Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) {
4577 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4578 CallParameters const& p = CallParametersOf(node->op());
4579 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4580 return NoChange();
4581 }
4582
4583 if (!isolate()->IsNoElementsProtectorIntact()) return NoChange();
4584 Node* target = NodeProperties::GetValueInput(node, 0);
4585 Node* receiver = NodeProperties::GetValueInput(node, 1);
4586 Node* context = NodeProperties::GetContextInput(node);
4587 Node* frame_state = NodeProperties::GetFrameStateInput(node);
4588 Node* effect = NodeProperties::GetEffectInput(node);
4589 Node* control = NodeProperties::GetControlInput(node);
4590
4591 ZoneHandleSet<Map> receiver_maps;
4592 NodeProperties::InferReceiverMapsResult result =
4593 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
4594 &receiver_maps);
4595 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
4596 DCHECK_NE(0, receiver_maps.size());
4597
4598 ElementsKind kind = receiver_maps[0]->elements_kind();
4599 for (Handle<Map> receiver_map : receiver_maps) {
4600 if (!CanInlineArrayResizeOperation(isolate(), receiver_map))
4601 return NoChange();
4602 // TODO(turbofan): Extend this to also handle fast holey double elements
4603 // once we got the hole NaN mess sorted out in TurboFan/V8.
4604 if (receiver_map->elements_kind() == HOLEY_DOUBLE_ELEMENTS)
4605 return NoChange();
4606 if (!UnionElementsKindUptoSize(&kind, receiver_map->elements_kind()))
4607 return NoChange();
4608 }
4609
4610 // Install code dependencies on the {receiver} global array protector cell.
4611 dependencies()->DependOnProtector(
4612 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
4613
4614 // If the {receiver_maps} information is not reliable, we need
4615 // to check that the {receiver} still has one of these maps.
4616 if (result == NodeProperties::kUnreliableReceiverMaps) {
4617 effect =
4618 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
4619 receiver_maps, p.feedback()),
4620 receiver, effect, control);
4621 }
4622
4623 // Load length of the {receiver}.
4624 Node* length = effect = graph()->NewNode(
4625 simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
4626 effect, control);
4627
4628 // Return undefined if {receiver} has no elements.
4629 Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length,
4630 jsgraph()->ZeroConstant());
4631 Node* branch0 =
4632 graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
4633
4634 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
4635 Node* etrue0 = effect;
4636 Node* vtrue0 = jsgraph()->UndefinedConstant();
4637
4638 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
4639 Node* efalse0 = effect;
4640 Node* vfalse0;
4641 {
4642 // Check if we should take the fast-path.
4643 Node* check1 =
4644 graph()->NewNode(simplified()->NumberLessThanOrEqual(), length,
4645 jsgraph()->Constant(JSArray::kMaxCopyElements));
4646 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
4647 check1, if_false0);
4648
4649 Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
4650 Node* etrue1 = efalse0;
4651 Node* vtrue1;
4652 {
4653 Node* elements = etrue1 = graph()->NewNode(
4654 simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
4655 receiver, etrue1, if_true1);
4656
4657 // Load the first element here, which we return below.
4658 vtrue1 = etrue1 = graph()->NewNode(
4659 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
4660 elements, jsgraph()->ZeroConstant(), etrue1, if_true1);
4661
4662 // Ensure that we aren't shifting a copy-on-write backing store.
4663 if (IsSmiOrObjectElementsKind(kind)) {
4664 elements = etrue1 =
4665 graph()->NewNode(simplified()->EnsureWritableFastElements(),
4666 receiver, elements, etrue1, if_true1);
4667 }
4668
4669 // Shift the remaining {elements} by one towards the start.
4670 Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1);
4671 Node* eloop =
4672 graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop);
4673 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
4674 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
4675 Node* index = graph()->NewNode(
4676 common()->Phi(MachineRepresentation::kTagged, 2),
4677 jsgraph()->OneConstant(),
4678 jsgraph()->Constant(JSArray::kMaxCopyElements - 1), loop);
4679
4680 {
4681 Node* check2 =
4682 graph()->NewNode(simplified()->NumberLessThan(), index, length);
4683 Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop);
4684
4685 if_true1 = graph()->NewNode(common()->IfFalse(), branch2);
4686 etrue1 = eloop;
4687
4688 Node* control = graph()->NewNode(common()->IfTrue(), branch2);
4689 Node* effect = etrue1;
4690
4691 ElementAccess const access = AccessBuilder::ForFixedArrayElement(kind);
4692 Node* value = effect =
4693 graph()->NewNode(simplified()->LoadElement(access), elements, index,
4694 effect, control);
4695 effect =
4696 graph()->NewNode(simplified()->StoreElement(access), elements,
4697 graph()->NewNode(simplified()->NumberSubtract(),
4698 index, jsgraph()->OneConstant()),
4699 value, effect, control);
4700
4701 loop->ReplaceInput(1, control);
4702 eloop->ReplaceInput(1, effect);
4703 index->ReplaceInput(1,
4704 graph()->NewNode(simplified()->NumberAdd(), index,
4705 jsgraph()->OneConstant()));
4706 }
4707
4708 // Compute the new {length}.
4709 length = graph()->NewNode(simplified()->NumberSubtract(), length,
4710 jsgraph()->OneConstant());
4711
4712 // Store the new {length} to the {receiver}.
4713 etrue1 = graph()->NewNode(
4714 simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)),
4715 receiver, length, etrue1, if_true1);
4716
4717 // Store a hole to the element we just removed from the {receiver}.
4718 etrue1 = graph()->NewNode(
4719 simplified()->StoreElement(
4720 AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))),
4721 elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1);
4722 }
4723
4724 Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
4725 Node* efalse1 = efalse0;
4726 Node* vfalse1;
4727 {
4728 // Call the generic C++ implementation.
4729 const int builtin_index = Builtins::kArrayShift;
4730 auto call_descriptor = Linkage::GetCEntryStubCallDescriptor(
4731 graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver,
4732 Builtins::name(builtin_index), node->op()->properties(),
4733 CallDescriptor::kNeedsFrameState);
4734 Node* stub_code =
4735 jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, kArgvOnStack, true);
4736 Address builtin_entry = Builtins::CppEntryOf(builtin_index);
4737 Node* entry =
4738 jsgraph()->ExternalConstant(ExternalReference::Create(builtin_entry));
4739 Node* argc =
4740 jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver);
4741 if_false1 = efalse1 = vfalse1 =
4742 graph()->NewNode(common()->Call(call_descriptor), stub_code, receiver,
4743 jsgraph()->PaddingConstant(), argc, target,
4744 jsgraph()->UndefinedConstant(), entry, argc, context,
4745 frame_state, efalse1, if_false1);
4746 }
4747
4748 if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
4749 efalse0 =
4750 graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
4751 vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4752 vtrue1, vfalse1, if_false0);
4753 }
4754
4755 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
4756 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control);
4757 Node* value =
4758 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
4759 vtrue0, vfalse0, control);
4760
4761 // Convert the hole to undefined. Do this last, so that we can optimize
4762 // conversion operator via some smart strength reduction in many cases.
4763 if (IsHoleyElementsKind(kind)) {
4764 value =
4765 graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
4766 }
4767
4768 ReplaceWithValue(node, value, effect, control);
4769 return Replace(value);
4770 }
4771
4772 // ES6 section 22.1.3.23 Array.prototype.slice ( )
ReduceArrayPrototypeSlice(Node * node)4773 Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) {
4774 if (!FLAG_turbo_inline_array_builtins) return NoChange();
4775 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4776 CallParameters const& p = CallParametersOf(node->op());
4777 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4778 return NoChange();
4779 }
4780
4781 int arity = static_cast<int>(p.arity() - 2);
4782 // Here we only optimize for cloning, that is when slice is called
4783 // without arguments, or with a single argument that is the constant 0.
4784 if (arity >= 2) return NoChange();
4785 if (arity == 1) {
4786 NumberMatcher m(NodeProperties::GetValueInput(node, 2));
4787 if (!m.HasValue()) return NoChange();
4788 if (m.Value() != 0) return NoChange();
4789 }
4790
4791 Node* receiver = NodeProperties::GetValueInput(node, 1);
4792 Node* effect = NodeProperties::GetEffectInput(node);
4793 Node* control = NodeProperties::GetControlInput(node);
4794
4795 // Try to determine the {receiver} map.
4796 ZoneHandleSet<Map> receiver_maps;
4797 NodeProperties::InferReceiverMapsResult result =
4798 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
4799 &receiver_maps);
4800 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
4801
4802 // Ensure that any changes to the Array species constructor cause deopt.
4803 if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
4804 dependencies()->DependOnProtector(
4805 PropertyCellRef(js_heap_broker(), factory()->array_species_protector()));
4806
4807 bool can_be_holey = false;
4808 // Check that the maps are of JSArray (and more)
4809 for (Handle<Map> receiver_map : receiver_maps) {
4810 if (!CanInlineArrayIteratingBuiltin(isolate(), receiver_map))
4811 return NoChange();
4812
4813 if (IsHoleyElementsKind(receiver_map->elements_kind())) can_be_holey = true;
4814 }
4815
4816 // Install code dependency on the array protector for holey arrays.
4817 if (can_be_holey) {
4818 dependencies()->DependOnProtector(
4819 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
4820 }
4821
4822 // If we have unreliable maps, we need a map check.
4823 // This is actually redundant due to how JSNativeContextSpecialization
4824 // reduces the load of slice, but we do it here nevertheless for consistency
4825 // and robustness.
4826 if (result == NodeProperties::kUnreliableReceiverMaps) {
4827 effect =
4828 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
4829 receiver_maps, p.feedback()),
4830 receiver, effect, control);
4831 }
4832
4833 Node* context = NodeProperties::GetContextInput(node);
4834
4835 Callable callable =
4836 Builtins::CallableFor(isolate(), Builtins::kCloneFastJSArray);
4837 auto call_descriptor = Linkage::GetStubCallDescriptor(
4838 graph()->zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags,
4839 Operator::kNoThrow | Operator::kNoDeopt);
4840
4841 // Calls to Builtins::kCloneFastJSArray produce COW arrays
4842 // if the original array is COW
4843 Node* clone = effect = graph()->NewNode(
4844 common()->Call(call_descriptor), jsgraph()->HeapConstant(callable.code()),
4845 receiver, context, effect, control);
4846
4847 ReplaceWithValue(node, clone, effect, control);
4848 return Replace(clone);
4849 }
4850
4851 // ES6 section 22.1.2.2 Array.isArray ( arg )
ReduceArrayIsArray(Node * node)4852 Reduction JSCallReducer::ReduceArrayIsArray(Node* node) {
4853 // We certainly know that undefined is not an array.
4854 if (node->op()->ValueInputCount() < 3) {
4855 Node* value = jsgraph()->FalseConstant();
4856 ReplaceWithValue(node, value);
4857 return Replace(value);
4858 }
4859
4860 Node* effect = NodeProperties::GetEffectInput(node);
4861 Node* control = NodeProperties::GetControlInput(node);
4862 Node* context = NodeProperties::GetContextInput(node);
4863 Node* frame_state = NodeProperties::GetFrameStateInput(node);
4864 Node* object = NodeProperties::GetValueInput(node, 2);
4865 node->ReplaceInput(0, object);
4866 node->ReplaceInput(1, context);
4867 node->ReplaceInput(2, frame_state);
4868 node->ReplaceInput(3, effect);
4869 node->ReplaceInput(4, control);
4870 node->TrimInputCount(5);
4871 NodeProperties::ChangeOp(node, javascript()->ObjectIsArray());
4872 return Changed(node);
4873 }
4874
ReduceArrayIterator(Node * node,IterationKind kind)4875 Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind) {
4876 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4877 Node* receiver = NodeProperties::GetValueInput(node, 1);
4878 Node* context = NodeProperties::GetContextInput(node);
4879 Node* effect = NodeProperties::GetEffectInput(node);
4880 Node* control = NodeProperties::GetControlInput(node);
4881
4882 // Check if we know that {receiver} is a valid JSReceiver.
4883 ZoneHandleSet<Map> receiver_maps;
4884 NodeProperties::InferReceiverMapsResult result =
4885 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
4886 &receiver_maps);
4887 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
4888 DCHECK_NE(0, receiver_maps.size());
4889 for (Handle<Map> receiver_map : receiver_maps) {
4890 if (!receiver_map->IsJSReceiverMap()) return NoChange();
4891 }
4892
4893 // Morph the {node} into a JSCreateArrayIterator with the given {kind}.
4894 RelaxControls(node);
4895 node->ReplaceInput(0, receiver);
4896 node->ReplaceInput(1, context);
4897 node->ReplaceInput(2, effect);
4898 node->ReplaceInput(3, control);
4899 node->TrimInputCount(4);
4900 NodeProperties::ChangeOp(node, javascript()->CreateArrayIterator(kind));
4901 return Changed(node);
4902 }
4903
4904 namespace {
4905
InferIteratedObjectMaps(Isolate * isolate,Node * iterator,ZoneHandleSet<Map> * iterated_object_maps)4906 bool InferIteratedObjectMaps(Isolate* isolate, Node* iterator,
4907 ZoneHandleSet<Map>* iterated_object_maps) {
4908 DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, iterator->opcode());
4909 Node* iterated_object = NodeProperties::GetValueInput(iterator, 0);
4910 Node* effect = NodeProperties::GetEffectInput(iterator);
4911
4912 NodeProperties::InferReceiverMapsResult result =
4913 NodeProperties::InferReceiverMaps(isolate, iterated_object, effect,
4914 iterated_object_maps);
4915 return result != NodeProperties::kNoReceiverMaps;
4916 }
4917
4918 } // namespace
4919
4920 // ES #sec-%arrayiteratorprototype%.next
ReduceArrayIteratorPrototypeNext(Node * node)4921 Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) {
4922 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
4923 CallParameters const& p = CallParametersOf(node->op());
4924 Node* iterator = NodeProperties::GetValueInput(node, 1);
4925 Node* context = NodeProperties::GetContextInput(node);
4926 Node* effect = NodeProperties::GetEffectInput(node);
4927 Node* control = NodeProperties::GetControlInput(node);
4928
4929 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
4930 return NoChange();
4931 }
4932
4933 // Check if the {iterator} is a JSCreateArrayIterator.
4934 if (iterator->opcode() != IrOpcode::kJSCreateArrayIterator) return NoChange();
4935 IterationKind const iteration_kind =
4936 CreateArrayIteratorParametersOf(iterator->op()).kind();
4937
4938 // Try to infer the [[IteratedObject]] maps from the {iterator}.
4939 ZoneHandleSet<Map> iterated_object_maps;
4940 if (!InferIteratedObjectMaps(isolate(), iterator, &iterated_object_maps)) {
4941 return NoChange();
4942 }
4943 DCHECK_NE(0, iterated_object_maps.size());
4944
4945 // Check that various {iterated_object_maps} have compatible elements kinds.
4946 ElementsKind elements_kind = iterated_object_maps[0]->elements_kind();
4947 if (IsFixedTypedArrayElementsKind(elements_kind)) {
4948 // TurboFan doesn't support loading from BigInt typed arrays yet.
4949 if (elements_kind == BIGUINT64_ELEMENTS ||
4950 elements_kind == BIGINT64_ELEMENTS) {
4951 return NoChange();
4952 }
4953 for (Handle<Map> iterated_object_map : iterated_object_maps) {
4954 if (iterated_object_map->elements_kind() != elements_kind) {
4955 return NoChange();
4956 }
4957 }
4958 } else {
4959 for (Handle<Map> iterated_object_map : iterated_object_maps) {
4960 if (!CanInlineArrayIteratingBuiltin(isolate(), iterated_object_map)) {
4961 return NoChange();
4962 }
4963 if (!UnionElementsKindUptoSize(&elements_kind,
4964 iterated_object_map->elements_kind())) {
4965 return NoChange();
4966 }
4967 }
4968 }
4969
4970 // Install code dependency on the array protector for holey arrays.
4971 if (IsHoleyElementsKind(elements_kind)) {
4972 dependencies()->DependOnProtector(
4973 PropertyCellRef(js_heap_broker(), factory()->no_elements_protector()));
4974 }
4975
4976 // Load the (current) {iterated_object} from the {iterator}.
4977 Node* iterated_object = effect =
4978 graph()->NewNode(simplified()->LoadField(
4979 AccessBuilder::ForJSArrayIteratorIteratedObject()),
4980 iterator, effect, control);
4981
4982 // Ensure that the {iterated_object} map didn't change.
4983 effect = graph()->NewNode(
4984 simplified()->CheckMaps(CheckMapsFlag::kNone, iterated_object_maps,
4985 p.feedback()),
4986 iterated_object, effect, control);
4987
4988 if (IsFixedTypedArrayElementsKind(elements_kind)) {
4989 // See if we can skip the neutering check.
4990 if (isolate()->IsArrayBufferNeuteringIntact()) {
4991 // Add a code dependency so we are deoptimized in case an ArrayBuffer
4992 // gets neutered.
4993 dependencies()->DependOnProtector(PropertyCellRef(
4994 js_heap_broker(), factory()->array_buffer_neutering_protector()));
4995 } else {
4996 // Deoptimize if the array buffer was neutered.
4997 Node* buffer = effect = graph()->NewNode(
4998 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
4999 iterated_object, effect, control);
5000
5001 Node* check = effect = graph()->NewNode(
5002 simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
5003 check = graph()->NewNode(simplified()->BooleanNot(), check);
5004 // TODO(bmeurer): Pass p.feedback(), or better introduce
5005 // CheckArrayBufferNotNeutered?
5006 effect = graph()->NewNode(
5007 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered),
5008 check, effect, control);
5009 }
5010 }
5011
5012 // Load the [[NextIndex]] from the {iterator} and leverage the fact
5013 // that we definitely know that it's in Unsigned32 range since the
5014 // {iterated_object} is either a JSArray or a JSTypedArray. For the
5015 // latter case we even know that it's a Smi in UnsignedSmall range.
5016 FieldAccess index_access = AccessBuilder::ForJSArrayIteratorNextIndex();
5017 if (IsFixedTypedArrayElementsKind(elements_kind)) {
5018 index_access.type = TypeCache::Get().kJSTypedArrayLengthType;
5019 index_access.machine_type = MachineType::TaggedSigned();
5020 index_access.write_barrier_kind = kNoWriteBarrier;
5021 } else {
5022 index_access.type = TypeCache::Get().kJSArrayLengthType;
5023 }
5024 Node* index = effect = graph()->NewNode(simplified()->LoadField(index_access),
5025 iterator, effect, control);
5026
5027 // Load the elements of the {iterated_object}. While it feels
5028 // counter-intuitive to place the elements pointer load before
5029 // the condition below, as it might not be needed (if the {index}
5030 // is out of bounds for the {iterated_object}), it's better this
5031 // way as it allows the LoadElimination to eliminate redundant
5032 // reloads of the elements pointer.
5033 Node* elements = effect = graph()->NewNode(
5034 simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
5035 iterated_object, effect, control);
5036
5037 // Load the length of the {iterated_object}. Due to the map checks we
5038 // already know something about the length here, which we can leverage
5039 // to generate Word32 operations below without additional checking.
5040 FieldAccess length_access =
5041 IsFixedTypedArrayElementsKind(elements_kind)
5042 ? AccessBuilder::ForJSTypedArrayLength()
5043 : AccessBuilder::ForJSArrayLength(elements_kind);
5044 Node* length = effect = graph()->NewNode(
5045 simplified()->LoadField(length_access), iterated_object, effect, control);
5046
5047 // Check whether {index} is within the valid range for the {iterated_object}.
5048 Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length);
5049 Node* branch =
5050 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
5051
5052 Node* done_true;
5053 Node* value_true;
5054 Node* etrue = effect;
5055 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
5056 {
5057 // We know that the {index} is range of the {length} now.
5058 index = etrue = graph()->NewNode(
5059 common()->TypeGuard(
5060 Type::Range(0.0, length_access.type.Max() - 1.0, graph()->zone())),
5061 index, etrue, if_true);
5062
5063 done_true = jsgraph()->FalseConstant();
5064 if (iteration_kind == IterationKind::kKeys) {
5065 // Just return the {index}.
5066 value_true = index;
5067 } else {
5068 DCHECK(iteration_kind == IterationKind::kEntries ||
5069 iteration_kind == IterationKind::kValues);
5070
5071 if (IsFixedTypedArrayElementsKind(elements_kind)) {
5072 Node* base_ptr = etrue = graph()->NewNode(
5073 simplified()->LoadField(
5074 AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
5075 elements, etrue, if_true);
5076 Node* external_ptr = etrue = graph()->NewNode(
5077 simplified()->LoadField(
5078 AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
5079 elements, etrue, if_true);
5080
5081 ExternalArrayType array_type = kExternalInt8Array;
5082 switch (elements_kind) {
5083 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
5084 case TYPE##_ELEMENTS: \
5085 array_type = kExternal##Type##Array; \
5086 break;
5087 TYPED_ARRAYS(TYPED_ARRAY_CASE)
5088 default:
5089 UNREACHABLE();
5090 #undef TYPED_ARRAY_CASE
5091 }
5092
5093 Node* buffer = etrue =
5094 graph()->NewNode(simplified()->LoadField(
5095 AccessBuilder::ForJSArrayBufferViewBuffer()),
5096 iterated_object, etrue, if_true);
5097
5098 value_true = etrue =
5099 graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer,
5100 base_ptr, external_ptr, index, etrue, if_true);
5101 } else {
5102 value_true = etrue = graph()->NewNode(
5103 simplified()->LoadElement(
5104 AccessBuilder::ForFixedArrayElement(elements_kind)),
5105 elements, index, etrue, if_true);
5106
5107 // Convert hole to undefined if needed.
5108 if (elements_kind == HOLEY_ELEMENTS ||
5109 elements_kind == HOLEY_SMI_ELEMENTS) {
5110 value_true = graph()->NewNode(
5111 simplified()->ConvertTaggedHoleToUndefined(), value_true);
5112 } else if (elements_kind == HOLEY_DOUBLE_ELEMENTS) {
5113 // TODO(6587): avoid deopt if not all uses of value are truncated.
5114 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
5115 value_true = etrue = graph()->NewNode(
5116 simplified()->CheckFloat64Hole(mode, p.feedback()), value_true,
5117 etrue, if_true);
5118 }
5119 }
5120
5121 if (iteration_kind == IterationKind::kEntries) {
5122 // Allocate elements for key/value pair
5123 value_true = etrue =
5124 graph()->NewNode(javascript()->CreateKeyValueArray(), index,
5125 value_true, context, etrue);
5126 } else {
5127 DCHECK_EQ(IterationKind::kValues, iteration_kind);
5128 }
5129 }
5130
5131 // Increment the [[NextIndex]] field in the {iterator}. The TypeGuards
5132 // above guarantee that the {next_index} is in the UnsignedSmall range.
5133 Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
5134 jsgraph()->OneConstant());
5135 etrue = graph()->NewNode(simplified()->StoreField(index_access), iterator,
5136 next_index, etrue, if_true);
5137 }
5138
5139 Node* done_false;
5140 Node* value_false;
5141 Node* efalse = effect;
5142 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
5143 {
5144 // iterator.[[NextIndex]] >= array.length, stop iterating.
5145 done_false = jsgraph()->TrueConstant();
5146 value_false = jsgraph()->UndefinedConstant();
5147
5148 if (!IsFixedTypedArrayElementsKind(elements_kind)) {
5149 // Mark the {iterator} as exhausted by setting the [[NextIndex]] to a
5150 // value that will never pass the length check again (aka the maximum
5151 // value possible for the specific iterated object). Note that this is
5152 // different from what the specification says, which is changing the
5153 // [[IteratedObject]] field to undefined, but that makes it difficult
5154 // to eliminate the map checks and "length" accesses in for..of loops.
5155 //
5156 // This is not necessary for JSTypedArray's, since the length of those
5157 // cannot change later and so if we were ever out of bounds for them
5158 // we will stay out-of-bounds forever.
5159 Node* end_index = jsgraph()->Constant(index_access.type.Max());
5160 efalse = graph()->NewNode(simplified()->StoreField(index_access),
5161 iterator, end_index, efalse, if_false);
5162 }
5163 }
5164
5165 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
5166 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
5167 Node* value =
5168 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
5169 value_true, value_false, control);
5170 Node* done =
5171 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
5172 done_true, done_false, control);
5173
5174 // Create IteratorResult object.
5175 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
5176 value, done, context, effect);
5177 ReplaceWithValue(node, value, effect, control);
5178 return Replace(value);
5179 }
5180
5181 // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
5182 // ES6 section 21.1.3.3 String.prototype.codePointAt ( pos )
ReduceStringPrototypeStringAt(const Operator * string_access_operator,Node * node)5183 Reduction JSCallReducer::ReduceStringPrototypeStringAt(
5184 const Operator* string_access_operator, Node* node) {
5185 DCHECK(string_access_operator->opcode() == IrOpcode::kStringCharCodeAt ||
5186 string_access_operator->opcode() == IrOpcode::kStringCodePointAt);
5187 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5188 CallParameters const& p = CallParametersOf(node->op());
5189 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5190 return NoChange();
5191 }
5192
5193 Node* receiver = NodeProperties::GetValueInput(node, 1);
5194 Node* index = node->op()->ValueInputCount() >= 3
5195 ? NodeProperties::GetValueInput(node, 2)
5196 : jsgraph()->ZeroConstant();
5197 Node* effect = NodeProperties::GetEffectInput(node);
5198 Node* control = NodeProperties::GetControlInput(node);
5199
5200 // Ensure that the {receiver} is actually a String.
5201 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
5202 receiver, effect, control);
5203
5204 // Determine the {receiver} length.
5205 Node* receiver_length =
5206 graph()->NewNode(simplified()->StringLength(), receiver);
5207
5208 // Check that the {index} is within range.
5209 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
5210 index, receiver_length, effect, control);
5211
5212 // Return the character from the {receiver} as single character string.
5213 Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
5214 Node* value = effect = graph()->NewNode(string_access_operator, receiver,
5215 masked_index, effect, control);
5216
5217 ReplaceWithValue(node, value, effect, control);
5218 return Replace(value);
5219 }
5220
5221 // ES section 21.1.3.1 String.prototype.charAt ( pos )
ReduceStringPrototypeCharAt(Node * node)5222 Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) {
5223 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5224 CallParameters const& p = CallParametersOf(node->op());
5225 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5226 return NoChange();
5227 }
5228
5229 Node* receiver = NodeProperties::GetValueInput(node, 1);
5230 Node* index = node->op()->ValueInputCount() >= 3
5231 ? NodeProperties::GetValueInput(node, 2)
5232 : jsgraph()->ZeroConstant();
5233 Node* effect = NodeProperties::GetEffectInput(node);
5234 Node* control = NodeProperties::GetControlInput(node);
5235
5236 // Ensure that the {receiver} is actually a String.
5237 receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()),
5238 receiver, effect, control);
5239
5240 // Determine the {receiver} length.
5241 Node* receiver_length =
5242 graph()->NewNode(simplified()->StringLength(), receiver);
5243
5244 // Check that the {index} is within range.
5245 index = effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
5246 index, receiver_length, effect, control);
5247
5248 // Return the character from the {receiver} as single character string.
5249 Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index);
5250 Node* value = effect =
5251 graph()->NewNode(simplified()->StringCharCodeAt(), receiver, masked_index,
5252 effect, control);
5253 value = graph()->NewNode(simplified()->StringFromSingleCharCode(), value);
5254
5255 ReplaceWithValue(node, value, effect, control);
5256 return Replace(value);
5257 }
5258
5259 #ifdef V8_INTL_SUPPORT
5260
ReduceStringPrototypeToLowerCaseIntl(Node * node)5261 Reduction JSCallReducer::ReduceStringPrototypeToLowerCaseIntl(Node* node) {
5262 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5263 CallParameters const& p = CallParametersOf(node->op());
5264 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5265 return NoChange();
5266 }
5267 Node* effect = NodeProperties::GetEffectInput(node);
5268 Node* control = NodeProperties::GetControlInput(node);
5269
5270 Node* receiver = effect =
5271 graph()->NewNode(simplified()->CheckString(p.feedback()),
5272 NodeProperties::GetValueInput(node, 1), effect, control);
5273
5274 NodeProperties::ReplaceEffectInput(node, effect);
5275 RelaxEffectsAndControls(node);
5276 node->ReplaceInput(0, receiver);
5277 node->TrimInputCount(1);
5278 NodeProperties::ChangeOp(node, simplified()->StringToLowerCaseIntl());
5279 NodeProperties::SetType(node, Type::String());
5280 return Changed(node);
5281 }
5282
ReduceStringPrototypeToUpperCaseIntl(Node * node)5283 Reduction JSCallReducer::ReduceStringPrototypeToUpperCaseIntl(Node* node) {
5284 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5285 CallParameters const& p = CallParametersOf(node->op());
5286 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5287 return NoChange();
5288 }
5289 Node* effect = NodeProperties::GetEffectInput(node);
5290 Node* control = NodeProperties::GetControlInput(node);
5291
5292 Node* receiver = effect =
5293 graph()->NewNode(simplified()->CheckString(p.feedback()),
5294 NodeProperties::GetValueInput(node, 1), effect, control);
5295
5296 NodeProperties::ReplaceEffectInput(node, effect);
5297 RelaxEffectsAndControls(node);
5298 node->ReplaceInput(0, receiver);
5299 node->TrimInputCount(1);
5300 NodeProperties::ChangeOp(node, simplified()->StringToUpperCaseIntl());
5301 NodeProperties::SetType(node, Type::String());
5302 return Changed(node);
5303 }
5304
5305 #endif // V8_INTL_SUPPORT
5306
5307 // ES #sec-string.fromcharcode
ReduceStringFromCharCode(Node * node)5308 Reduction JSCallReducer::ReduceStringFromCharCode(Node* node) {
5309 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5310 CallParameters const& p = CallParametersOf(node->op());
5311 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5312 return NoChange();
5313 }
5314 if (node->op()->ValueInputCount() == 3) {
5315 Node* effect = NodeProperties::GetEffectInput(node);
5316 Node* control = NodeProperties::GetControlInput(node);
5317 Node* input = NodeProperties::GetValueInput(node, 2);
5318
5319 input = effect = graph()->NewNode(
5320 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
5321 p.feedback()),
5322 input, effect, control);
5323
5324 Node* value =
5325 graph()->NewNode(simplified()->StringFromSingleCharCode(), input);
5326 ReplaceWithValue(node, value, effect);
5327 return Replace(value);
5328 }
5329 return NoChange();
5330 }
5331
5332 // ES #sec-string.fromcodepoint
ReduceStringFromCodePoint(Node * node)5333 Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) {
5334 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5335 CallParameters const& p = CallParametersOf(node->op());
5336 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5337 return NoChange();
5338 }
5339 if (node->op()->ValueInputCount() == 3) {
5340 Node* effect = NodeProperties::GetEffectInput(node);
5341 Node* control = NodeProperties::GetControlInput(node);
5342 Node* input = NodeProperties::GetValueInput(node, 2);
5343
5344 input = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()),
5345 input, effect, control);
5346
5347 input = effect =
5348 graph()->NewNode(simplified()->CheckBounds(p.feedback()), input,
5349 jsgraph()->Constant(0x10FFFF + 1), effect, control);
5350
5351 Node* value = graph()->NewNode(
5352 simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF32), input);
5353 ReplaceWithValue(node, value, effect);
5354 return Replace(value);
5355 }
5356 return NoChange();
5357 }
5358
ReduceStringPrototypeIterator(Node * node)5359 Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node) {
5360 CallParameters const& p = CallParametersOf(node->op());
5361 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5362 return NoChange();
5363 }
5364 Node* effect = NodeProperties::GetEffectInput(node);
5365 Node* control = NodeProperties::GetControlInput(node);
5366 Node* receiver = effect =
5367 graph()->NewNode(simplified()->CheckString(p.feedback()),
5368 NodeProperties::GetValueInput(node, 1), effect, control);
5369 Node* iterator = effect =
5370 graph()->NewNode(javascript()->CreateStringIterator(), receiver,
5371 jsgraph()->NoContextConstant(), effect);
5372 ReplaceWithValue(node, iterator, effect, control);
5373 return Replace(iterator);
5374 }
5375
ReduceStringIteratorPrototypeNext(Node * node)5376 Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) {
5377 Node* receiver = NodeProperties::GetValueInput(node, 1);
5378 Node* effect = NodeProperties::GetEffectInput(node);
5379 Node* control = NodeProperties::GetControlInput(node);
5380 Node* context = NodeProperties::GetContextInput(node);
5381 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
5382 JS_STRING_ITERATOR_TYPE)) {
5383 Node* string = effect = graph()->NewNode(
5384 simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()),
5385 receiver, effect, control);
5386 Node* index = effect = graph()->NewNode(
5387 simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()),
5388 receiver, effect, control);
5389 Node* length = graph()->NewNode(simplified()->StringLength(), string);
5390
5391 // branch0: if (index < length)
5392 Node* check0 =
5393 graph()->NewNode(simplified()->NumberLessThan(), index, length);
5394 Node* branch0 =
5395 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
5396
5397 Node* etrue0 = effect;
5398 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
5399 Node* done_true;
5400 Node* vtrue0;
5401 {
5402 done_true = jsgraph()->FalseConstant();
5403 Node* codepoint = etrue0 = graph()->NewNode(
5404 simplified()->StringCodePointAt(UnicodeEncoding::UTF16), string,
5405 index, etrue0, if_true0);
5406 vtrue0 = graph()->NewNode(
5407 simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF16),
5408 codepoint);
5409
5410 // Update iterator.[[NextIndex]]
5411 Node* char_length =
5412 graph()->NewNode(simplified()->StringLength(), vtrue0);
5413 index = graph()->NewNode(simplified()->NumberAdd(), index, char_length);
5414 etrue0 = graph()->NewNode(
5415 simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()),
5416 receiver, index, etrue0, if_true0);
5417 }
5418
5419 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
5420 Node* done_false;
5421 Node* vfalse0;
5422 {
5423 vfalse0 = jsgraph()->UndefinedConstant();
5424 done_false = jsgraph()->TrueConstant();
5425 }
5426
5427 control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
5428 effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control);
5429 Node* value =
5430 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
5431 vtrue0, vfalse0, control);
5432 Node* done =
5433 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
5434 done_true, done_false, control);
5435
5436 value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
5437 value, done, context, effect);
5438
5439 ReplaceWithValue(node, value, effect, control);
5440 return Replace(value);
5441 }
5442 return NoChange();
5443 }
5444
5445 // ES #sec-string.prototype.concat
ReduceStringPrototypeConcat(Node * node,Handle<SharedFunctionInfo> shared)5446 Reduction JSCallReducer::ReduceStringPrototypeConcat(
5447 Node* node, Handle<SharedFunctionInfo> shared) {
5448 if (node->op()->ValueInputCount() < 2 || node->op()->ValueInputCount() > 3) {
5449 return NoChange();
5450 }
5451 CallParameters const& p = CallParametersOf(node->op());
5452 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5453 return NoChange();
5454 }
5455 Node* effect = NodeProperties::GetEffectInput(node);
5456 Node* control = NodeProperties::GetControlInput(node);
5457 Node* context = NodeProperties::GetContextInput(node);
5458 Node* receiver = effect =
5459 graph()->NewNode(simplified()->CheckString(p.feedback()),
5460 NodeProperties::GetValueInput(node, 1), effect, control);
5461
5462 if (node->op()->ValueInputCount() < 3) {
5463 ReplaceWithValue(node, receiver, effect, control);
5464 return Replace(receiver);
5465 }
5466 Node* argument = effect =
5467 graph()->NewNode(simplified()->CheckString(p.feedback()),
5468 NodeProperties::GetValueInput(node, 2), effect, control);
5469
5470 Callable const callable =
5471 CodeFactory::StringAdd(isolate(), STRING_ADD_CHECK_NONE, NOT_TENURED);
5472 auto call_descriptor =
5473 Linkage::GetStubCallDescriptor(graph()->zone(), callable.descriptor(), 0,
5474 CallDescriptor::kNeedsFrameState,
5475 Operator::kNoDeopt | Operator::kNoWrite);
5476
5477 // TODO(turbofan): Massage the FrameState of the {node} here once we
5478 // have an artificial builtin frame type, so that it looks like the
5479 // exception from StringAdd overflow came from String.prototype.concat
5480 // builtin instead of the calling function.
5481 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
5482
5483 Node* value = effect = control = graph()->NewNode(
5484 common()->Call(call_descriptor), jsgraph()->HeapConstant(callable.code()),
5485 receiver, argument, context, outer_frame_state, effect, control);
5486
5487 ReplaceWithValue(node, value, effect, control);
5488 return Replace(value);
5489 }
5490
ReduceAsyncFunctionPromiseCreate(Node * node)5491 Reduction JSCallReducer::ReduceAsyncFunctionPromiseCreate(Node* node) {
5492 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5493 Node* context = NodeProperties::GetContextInput(node);
5494 Node* effect = NodeProperties::GetEffectInput(node);
5495 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
5496
5497 // Install a code dependency on the promise hook protector cell.
5498 dependencies()->DependOnProtector(
5499 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector()));
5500
5501 // Morph this {node} into a JSCreatePromise node.
5502 RelaxControls(node);
5503 node->ReplaceInput(0, context);
5504 node->ReplaceInput(1, effect);
5505 node->TrimInputCount(2);
5506 NodeProperties::ChangeOp(node, javascript()->CreatePromise());
5507 return Changed(node);
5508 }
5509
ReduceAsyncFunctionPromiseRelease(Node * node)5510 Reduction JSCallReducer::ReduceAsyncFunctionPromiseRelease(Node* node) {
5511 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5512 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
5513
5514 dependencies()->DependOnProtector(
5515 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector()));
5516
5517 // The AsyncFunctionPromiseRelease builtin is a no-op as long as neither
5518 // the debugger is active nor any promise hook has been installed (ever).
5519 Node* value = jsgraph()->UndefinedConstant();
5520 ReplaceWithValue(node, value);
5521 return Replace(value);
5522 }
5523
CreateArtificialFrameState(Node * node,Node * outer_frame_state,int parameter_count,BailoutId bailout_id,FrameStateType frame_state_type,Handle<SharedFunctionInfo> shared)5524 Node* JSCallReducer::CreateArtificialFrameState(
5525 Node* node, Node* outer_frame_state, int parameter_count,
5526 BailoutId bailout_id, FrameStateType frame_state_type,
5527 Handle<SharedFunctionInfo> shared) {
5528 const FrameStateFunctionInfo* state_info =
5529 common()->CreateFrameStateFunctionInfo(frame_state_type,
5530 parameter_count + 1, 0, shared);
5531
5532 const Operator* op = common()->FrameState(
5533 bailout_id, OutputFrameStateCombine::Ignore(), state_info);
5534 const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense());
5535 Node* node0 = graph()->NewNode(op0);
5536 std::vector<Node*> params;
5537 for (int parameter = 0; parameter < parameter_count + 1; ++parameter) {
5538 params.push_back(node->InputAt(1 + parameter));
5539 }
5540 const Operator* op_param = common()->StateValues(
5541 static_cast<int>(params.size()), SparseInputMask::Dense());
5542 Node* params_node = graph()->NewNode(
5543 op_param, static_cast<int>(params.size()), ¶ms.front());
5544 return graph()->NewNode(op, params_node, node0, node0,
5545 jsgraph()->UndefinedConstant(), node->InputAt(0),
5546 outer_frame_state);
5547 }
5548
ReducePromiseConstructor(Node * node)5549 Reduction JSCallReducer::ReducePromiseConstructor(Node* node) {
5550 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
5551 ConstructParameters const& p = ConstructParametersOf(node->op());
5552 int arity = static_cast<int>(p.arity() - 2);
5553 // We only inline when we have the executor.
5554 if (arity < 1) return NoChange();
5555 Node* target = NodeProperties::GetValueInput(node, 0);
5556 Node* executor = NodeProperties::GetValueInput(node, 1);
5557 Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
5558
5559 Node* context = NodeProperties::GetContextInput(node);
5560 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
5561 Node* effect = NodeProperties::GetEffectInput(node);
5562 Node* control = NodeProperties::GetControlInput(node);
5563
5564 if (!FLAG_experimental_inline_promise_constructor) return NoChange();
5565 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
5566
5567 // Only handle builtins Promises, not subclasses.
5568 if (target != new_target) return NoChange();
5569
5570 dependencies()->DependOnProtector(
5571 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector()));
5572
5573 Handle<SharedFunctionInfo> promise_shared(
5574 handle(native_context()->promise_function()->shared(), isolate()));
5575
5576 // Insert a construct stub frame into the chain of frame states. This will
5577 // reconstruct the proper frame when deoptimizing within the constructor.
5578 // For the frame state, we only provide the executor parameter, even if more
5579 // arugments were passed. This is not observable from JS.
5580 DCHECK_EQ(1, promise_shared->internal_formal_parameter_count());
5581 Node* constructor_frame_state = CreateArtificialFrameState(
5582 node, outer_frame_state, 1, BailoutId::ConstructStubInvoke(),
5583 FrameStateType::kConstructStub, promise_shared);
5584
5585 // The deopt continuation of this frame state is never called; the frame state
5586 // is only necessary to obtain the right stack trace.
5587 const std::vector<Node*> checkpoint_parameters({
5588 jsgraph()->UndefinedConstant(), /* receiver */
5589 jsgraph()->UndefinedConstant(), /* promise */
5590 jsgraph()->UndefinedConstant(), /* reject function */
5591 jsgraph()->TheHoleConstant() /* exception */
5592 });
5593 int checkpoint_parameters_size =
5594 static_cast<int>(checkpoint_parameters.size());
5595 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
5596 jsgraph(), promise_shared,
5597 Builtins::kPromiseConstructorLazyDeoptContinuation, target, context,
5598 checkpoint_parameters.data(), checkpoint_parameters_size,
5599 constructor_frame_state, ContinuationFrameStateMode::LAZY);
5600
5601 // Check if executor is callable
5602 Node* check_fail = nullptr;
5603 Node* check_throw = nullptr;
5604 WireInCallbackIsCallableCheck(executor, context, frame_state, effect,
5605 &control, &check_fail, &check_throw);
5606
5607 // Create the resulting JSPromise.
5608 Node* promise = effect =
5609 graph()->NewNode(javascript()->CreatePromise(), context, effect);
5610
5611 // 8. CreatePromiseResolvingFunctions
5612 // Allocate a promise context for the closures below.
5613 Node* promise_context = effect =
5614 graph()->NewNode(javascript()->CreateFunctionContext(
5615 handle(native_context()->scope_info(), isolate()),
5616 PromiseBuiltinsAssembler::kPromiseContextLength -
5617 Context::MIN_CONTEXT_SLOTS,
5618 FUNCTION_SCOPE),
5619 context, effect, control);
5620 effect =
5621 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
5622 PromiseBuiltinsAssembler::kPromiseSlot)),
5623 promise_context, promise, effect, control);
5624 effect = graph()->NewNode(
5625 simplified()->StoreField(AccessBuilder::ForContextSlot(
5626 PromiseBuiltinsAssembler::kAlreadyResolvedSlot)),
5627 promise_context, jsgraph()->FalseConstant(), effect, control);
5628 effect = graph()->NewNode(
5629 simplified()->StoreField(AccessBuilder::ForContextSlot(
5630 PromiseBuiltinsAssembler::kDebugEventSlot)),
5631 promise_context, jsgraph()->TrueConstant(), effect, control);
5632
5633 // Allocate the closure for the resolve case.
5634 Handle<SharedFunctionInfo> resolve_shared(
5635 native_context()->promise_capability_default_resolve_shared_fun(),
5636 isolate());
5637 Node* resolve = effect =
5638 graph()->NewNode(javascript()->CreateClosure(
5639 resolve_shared, factory()->many_closures_cell(),
5640 handle(resolve_shared->GetCode(), isolate())),
5641 promise_context, effect, control);
5642
5643 // Allocate the closure for the reject case.
5644 Handle<SharedFunctionInfo> reject_shared(
5645 native_context()->promise_capability_default_reject_shared_fun(),
5646 isolate());
5647 Node* reject = effect =
5648 graph()->NewNode(javascript()->CreateClosure(
5649 reject_shared, factory()->many_closures_cell(),
5650 handle(reject_shared->GetCode(), isolate())),
5651 promise_context, effect, control);
5652
5653 const std::vector<Node*> checkpoint_parameters_continuation(
5654 {jsgraph()->UndefinedConstant() /* receiver */, promise, reject});
5655 int checkpoint_parameters_continuation_size =
5656 static_cast<int>(checkpoint_parameters_continuation.size());
5657 // This continuation just returns the created promise and takes care of
5658 // exceptions thrown by the executor.
5659 frame_state = CreateJavaScriptBuiltinContinuationFrameState(
5660 jsgraph(), promise_shared,
5661 Builtins::kPromiseConstructorLazyDeoptContinuation, target, context,
5662 checkpoint_parameters_continuation.data(),
5663 checkpoint_parameters_continuation_size, constructor_frame_state,
5664 ContinuationFrameStateMode::LAZY_WITH_CATCH);
5665
5666 // 9. Call executor with both resolving functions
5667 effect = control = graph()->NewNode(
5668 javascript()->Call(4, p.frequency(), VectorSlotPair(),
5669 ConvertReceiverMode::kNullOrUndefined,
5670 SpeculationMode::kDisallowSpeculation),
5671 executor, jsgraph()->UndefinedConstant(), resolve, reject, context,
5672 frame_state, effect, control);
5673
5674 Node* exception_effect = effect;
5675 Node* exception_control = control;
5676 {
5677 Node* reason = exception_effect = exception_control = graph()->NewNode(
5678 common()->IfException(), exception_control, exception_effect);
5679 // 10a. Call reject if the call to executor threw.
5680 exception_effect = exception_control = graph()->NewNode(
5681 javascript()->Call(3, p.frequency(), VectorSlotPair(),
5682 ConvertReceiverMode::kNullOrUndefined,
5683 SpeculationMode::kDisallowSpeculation),
5684 reject, jsgraph()->UndefinedConstant(), reason, context, frame_state,
5685 exception_effect, exception_control);
5686
5687 // Rewire potential exception edges.
5688 Node* on_exception = nullptr;
5689 if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
5690 RewirePostCallbackExceptionEdges(check_throw, on_exception,
5691 exception_effect, &check_fail,
5692 &exception_control);
5693 }
5694 }
5695
5696 Node* success_effect = effect;
5697 Node* success_control = control;
5698 {
5699 success_control = graph()->NewNode(common()->IfSuccess(), success_control);
5700 }
5701
5702 control =
5703 graph()->NewNode(common()->Merge(2), success_control, exception_control);
5704 effect = graph()->NewNode(common()->EffectPhi(2), success_effect,
5705 exception_effect, control);
5706
5707 // Wire up the branch for the case when IsCallable fails for the executor.
5708 // Since {check_throw} is an unconditional throw, it's impossible to
5709 // return a successful completion. Therefore, we simply connect the successful
5710 // completion to the graph end.
5711 Node* throw_node =
5712 graph()->NewNode(common()->Throw(), check_throw, check_fail);
5713 NodeProperties::MergeControlToEnd(graph(), common(), throw_node);
5714
5715 ReplaceWithValue(node, promise, effect, control);
5716 return Replace(promise);
5717 }
5718
5719 // V8 Extras: v8.createPromise(parent)
ReducePromiseInternalConstructor(Node * node)5720 Reduction JSCallReducer::ReducePromiseInternalConstructor(Node* node) {
5721 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5722 Node* context = NodeProperties::GetContextInput(node);
5723 Node* effect = NodeProperties::GetEffectInput(node);
5724
5725 // Check that promises aren't being observed through (debug) hooks.
5726 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
5727
5728 dependencies()->DependOnProtector(
5729 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector()));
5730
5731 // Create a new pending promise.
5732 Node* value = effect =
5733 graph()->NewNode(javascript()->CreatePromise(), context, effect);
5734
5735 ReplaceWithValue(node, value, effect);
5736 return Replace(value);
5737 }
5738
5739 // V8 Extras: v8.rejectPromise(promise, reason)
ReducePromiseInternalReject(Node * node)5740 Reduction JSCallReducer::ReducePromiseInternalReject(Node* node) {
5741 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5742 Node* promise = node->op()->ValueInputCount() >= 2
5743 ? NodeProperties::GetValueInput(node, 2)
5744 : jsgraph()->UndefinedConstant();
5745 Node* reason = node->op()->ValueInputCount() >= 3
5746 ? NodeProperties::GetValueInput(node, 3)
5747 : jsgraph()->UndefinedConstant();
5748 Node* debug_event = jsgraph()->TrueConstant();
5749 Node* frame_state = NodeProperties::GetFrameStateInput(node);
5750 Node* context = NodeProperties::GetContextInput(node);
5751 Node* effect = NodeProperties::GetEffectInput(node);
5752 Node* control = NodeProperties::GetControlInput(node);
5753
5754 // Reject the {promise} using the given {reason}, and trigger debug logic.
5755 Node* value = effect =
5756 graph()->NewNode(javascript()->RejectPromise(), promise, reason,
5757 debug_event, context, frame_state, effect, control);
5758
5759 ReplaceWithValue(node, value, effect, control);
5760 return Replace(value);
5761 }
5762
5763 // V8 Extras: v8.resolvePromise(promise, resolution)
ReducePromiseInternalResolve(Node * node)5764 Reduction JSCallReducer::ReducePromiseInternalResolve(Node* node) {
5765 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5766 Node* promise = node->op()->ValueInputCount() >= 2
5767 ? NodeProperties::GetValueInput(node, 2)
5768 : jsgraph()->UndefinedConstant();
5769 Node* resolution = node->op()->ValueInputCount() >= 3
5770 ? NodeProperties::GetValueInput(node, 3)
5771 : jsgraph()->UndefinedConstant();
5772 Node* frame_state = NodeProperties::GetFrameStateInput(node);
5773 Node* context = NodeProperties::GetContextInput(node);
5774 Node* effect = NodeProperties::GetEffectInput(node);
5775 Node* control = NodeProperties::GetControlInput(node);
5776
5777 // Resolve the {promise} using the given {resolution}.
5778 Node* value = effect =
5779 graph()->NewNode(javascript()->ResolvePromise(), promise, resolution,
5780 context, frame_state, effect, control);
5781
5782 ReplaceWithValue(node, value, effect, control);
5783 return Replace(value);
5784 }
5785
5786 // ES section #sec-promise.prototype.catch
ReducePromisePrototypeCatch(Node * node)5787 Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) {
5788 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5789 CallParameters const& p = CallParametersOf(node->op());
5790 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5791 return NoChange();
5792 }
5793 int arity = static_cast<int>(p.arity() - 2);
5794 Node* receiver = NodeProperties::GetValueInput(node, 1);
5795 Node* effect = NodeProperties::GetEffectInput(node);
5796 Node* control = NodeProperties::GetControlInput(node);
5797
5798 // Check that the Promise.then protector is intact. This protector guards
5799 // that all JSPromise instances whose [[Prototype]] is the initial
5800 // %PromisePrototype% yield the initial %PromisePrototype%.then method
5801 // when looking up "then".
5802 if (!isolate()->IsPromiseThenLookupChainIntact()) return NoChange();
5803
5804 // Check if we know something about {receiver} already.
5805 ZoneHandleSet<Map> receiver_maps;
5806 NodeProperties::InferReceiverMapsResult result =
5807 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
5808 &receiver_maps);
5809 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
5810 DCHECK_NE(0, receiver_maps.size());
5811
5812 // Check whether all {receiver_maps} are JSPromise maps and
5813 // have the initial Promise.prototype as their [[Prototype]].
5814 for (Handle<Map> receiver_map : receiver_maps) {
5815 if (!receiver_map->IsJSPromiseMap()) return NoChange();
5816 if (receiver_map->prototype() != native_context()->promise_prototype()) {
5817 return NoChange();
5818 }
5819 }
5820
5821 dependencies()->DependOnProtector(
5822 PropertyCellRef(js_heap_broker(), factory()->promise_then_protector()));
5823
5824 // If the {receiver_maps} aren't reliable, we need to repeat the
5825 // map check here, guarded by the CALL_IC.
5826 if (result == NodeProperties::kUnreliableReceiverMaps) {
5827 effect =
5828 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
5829 receiver_maps, p.feedback()),
5830 receiver, effect, control);
5831 }
5832
5833 // Massage the {node} to call "then" instead by first removing all inputs
5834 // following the onRejected parameter, and then filling up the parameters
5835 // to two inputs from the left with undefined.
5836 Node* target =
5837 jsgraph()->Constant(handle(native_context()->promise_then(), isolate()));
5838 NodeProperties::ReplaceValueInput(node, target, 0);
5839 NodeProperties::ReplaceEffectInput(node, effect);
5840 for (; arity > 1; --arity) node->RemoveInput(3);
5841 for (; arity < 2; ++arity) {
5842 node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant());
5843 }
5844 NodeProperties::ChangeOp(
5845 node, javascript()->Call(2 + arity, p.frequency(), p.feedback(),
5846 ConvertReceiverMode::kNotNullOrUndefined,
5847 p.speculation_mode()));
5848 Reduction const reduction = ReducePromisePrototypeThen(node);
5849 return reduction.Changed() ? reduction : Changed(node);
5850 }
5851
5852 // ES section #sec-promise.prototype.finally
ReducePromisePrototypeFinally(Node * node)5853 Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
5854 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
5855 CallParameters const& p = CallParametersOf(node->op());
5856 int arity = static_cast<int>(p.arity() - 2);
5857 Node* receiver = NodeProperties::GetValueInput(node, 1);
5858 Node* on_finally = arity >= 1 ? NodeProperties::GetValueInput(node, 2)
5859 : jsgraph()->UndefinedConstant();
5860 Node* effect = NodeProperties::GetEffectInput(node);
5861 Node* control = NodeProperties::GetControlInput(node);
5862 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
5863 return NoChange();
5864 }
5865
5866 // Check that promises aren't being observed through (debug) hooks.
5867 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
5868
5869 // Check that the Promise#then protector is intact. This protector guards
5870 // that all JSPromise instances whose [[Prototype]] is the initial
5871 // %PromisePrototype% yield the initial %PromisePrototype%.then method
5872 // when looking up "then".
5873 if (!isolate()->IsPromiseThenLookupChainIntact()) return NoChange();
5874
5875 // Also check that the @@species protector is intact, which guards the
5876 // lookup of "constructor" on JSPromise instances, whoch [[Prototype]] is
5877 // the initial %PromisePrototype%, and the Symbol.species lookup on the
5878 // %PromisePrototype%.
5879 if (!isolate()->IsPromiseSpeciesLookupChainIntact()) return NoChange();
5880
5881 // Check if we know something about {receiver} already.
5882 ZoneHandleSet<Map> receiver_maps;
5883 NodeProperties::InferReceiverMapsResult result =
5884 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
5885 &receiver_maps);
5886 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
5887 DCHECK_NE(0, receiver_maps.size());
5888
5889 // Check whether all {receiver_maps} are JSPromise maps and
5890 // have the initial Promise.prototype as their [[Prototype]].
5891 for (Handle<Map> receiver_map : receiver_maps) {
5892 if (!receiver_map->IsJSPromiseMap()) return NoChange();
5893 if (receiver_map->prototype() != native_context()->promise_prototype()) {
5894 return NoChange();
5895 }
5896 }
5897
5898 dependencies()->DependOnProtector(
5899 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector()));
5900 dependencies()->DependOnProtector(
5901 PropertyCellRef(js_heap_broker(), factory()->promise_then_protector()));
5902 dependencies()->DependOnProtector(PropertyCellRef(
5903 js_heap_broker(), factory()->promise_species_protector()));
5904
5905 // If the {receiver_maps} aren't reliable, we need to repeat the
5906 // map check here, guarded by the CALL_IC.
5907 if (result == NodeProperties::kUnreliableReceiverMaps) {
5908 effect =
5909 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
5910 receiver_maps, p.feedback()),
5911 receiver, effect, control);
5912 }
5913
5914 // Check if {on_finally} is callable, and if so wrap it into appropriate
5915 // closures that perform the finalization.
5916 Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally);
5917 Node* branch =
5918 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
5919
5920 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
5921 Node* etrue = effect;
5922 Node* catch_true;
5923 Node* then_true;
5924 {
5925 Node* context = jsgraph()->HeapConstant(native_context());
5926 Node* constructor = jsgraph()->HeapConstant(
5927 handle(native_context()->promise_function(), isolate()));
5928
5929 // Allocate shared context for the closures below.
5930 context = etrue = graph()->NewNode(
5931 javascript()->CreateFunctionContext(
5932 handle(native_context()->scope_info(), isolate()),
5933 PromiseBuiltinsAssembler::kPromiseFinallyContextLength -
5934 Context::MIN_CONTEXT_SLOTS,
5935 FUNCTION_SCOPE),
5936 context, etrue, if_true);
5937 etrue =
5938 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
5939 PromiseBuiltinsAssembler::kOnFinallySlot)),
5940 context, on_finally, etrue, if_true);
5941 etrue =
5942 graph()->NewNode(simplified()->StoreField(AccessBuilder::ForContextSlot(
5943 PromiseBuiltinsAssembler::kConstructorSlot)),
5944 context, constructor, etrue, if_true);
5945
5946 // Allocate the closure for the reject case.
5947 Handle<SharedFunctionInfo> catch_finally(
5948 native_context()->promise_catch_finally_shared_fun(), isolate());
5949 catch_true = etrue =
5950 graph()->NewNode(javascript()->CreateClosure(
5951 catch_finally, factory()->many_closures_cell(),
5952 handle(catch_finally->GetCode(), isolate())),
5953 context, etrue, if_true);
5954
5955 // Allocate the closure for the fulfill case.
5956 Handle<SharedFunctionInfo> then_finally(
5957 native_context()->promise_then_finally_shared_fun(), isolate());
5958 then_true = etrue =
5959 graph()->NewNode(javascript()->CreateClosure(
5960 then_finally, factory()->many_closures_cell(),
5961 handle(then_finally->GetCode(), isolate())),
5962 context, etrue, if_true);
5963 }
5964
5965 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
5966 Node* efalse = effect;
5967 Node* catch_false = on_finally;
5968 Node* then_false = on_finally;
5969
5970 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
5971 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
5972 Node* catch_finally =
5973 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
5974 catch_true, catch_false, control);
5975 Node* then_finally =
5976 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
5977 then_true, then_false, control);
5978
5979 // At this point we definitely know that {receiver} has one of the
5980 // {receiver_maps}, so insert a MapGuard as a hint for the lowering
5981 // of the call to "then" below.
5982 effect = graph()->NewNode(simplified()->MapGuard(receiver_maps), receiver,
5983 effect, control);
5984
5985 // Massage the {node} to call "then" instead by first removing all inputs
5986 // following the onFinally parameter, and then replacing the only parameter
5987 // input with the {on_finally} value.
5988 Node* target =
5989 jsgraph()->Constant(handle(native_context()->promise_then(), isolate()));
5990 NodeProperties::ReplaceValueInput(node, target, 0);
5991 NodeProperties::ReplaceEffectInput(node, effect);
5992 NodeProperties::ReplaceControlInput(node, control);
5993 for (; arity > 2; --arity) node->RemoveInput(2);
5994 for (; arity < 2; ++arity)
5995 node->InsertInput(graph()->zone(), 2, then_finally);
5996 node->ReplaceInput(2, then_finally);
5997 node->ReplaceInput(3, catch_finally);
5998 NodeProperties::ChangeOp(
5999 node, javascript()->Call(2 + arity, p.frequency(), p.feedback(),
6000 ConvertReceiverMode::kNotNullOrUndefined,
6001 p.speculation_mode()));
6002 Reduction const reduction = ReducePromisePrototypeThen(node);
6003 return reduction.Changed() ? reduction : Changed(node);
6004 }
6005
ReducePromisePrototypeThen(Node * node)6006 Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
6007 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
6008 CallParameters const& p = CallParametersOf(node->op());
6009 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
6010 return NoChange();
6011 }
6012
6013 Node* receiver = NodeProperties::GetValueInput(node, 1);
6014 Node* on_fulfilled = node->op()->ValueInputCount() > 2
6015 ? NodeProperties::GetValueInput(node, 2)
6016 : jsgraph()->UndefinedConstant();
6017 Node* on_rejected = node->op()->ValueInputCount() > 3
6018 ? NodeProperties::GetValueInput(node, 3)
6019 : jsgraph()->UndefinedConstant();
6020 Node* context = NodeProperties::GetContextInput(node);
6021 Node* effect = NodeProperties::GetEffectInput(node);
6022 Node* control = NodeProperties::GetControlInput(node);
6023 Node* frame_state = NodeProperties::GetFrameStateInput(node);
6024
6025 // Check that promises aren't being observed through (debug) hooks.
6026 if (!isolate()->IsPromiseHookProtectorIntact()) return NoChange();
6027
6028 // Check if the @@species protector is intact. The @@species protector
6029 // guards the "constructor" lookup on all JSPromise instances and the
6030 // initial Promise.prototype, as well as the Symbol.species lookup on
6031 // the Promise constructor.
6032 if (!isolate()->IsPromiseSpeciesLookupChainIntact()) return NoChange();
6033
6034 // Check if we know something about {receiver} already.
6035 ZoneHandleSet<Map> receiver_maps;
6036 NodeProperties::InferReceiverMapsResult infer_receiver_maps_result =
6037 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
6038 &receiver_maps);
6039 if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) {
6040 return NoChange();
6041 }
6042 DCHECK_NE(0, receiver_maps.size());
6043
6044 // Check whether all {receiver_maps} are JSPromise maps and
6045 // have the initial Promise.prototype as their [[Prototype]].
6046 for (Handle<Map> receiver_map : receiver_maps) {
6047 if (!receiver_map->IsJSPromiseMap()) return NoChange();
6048 if (receiver_map->prototype() != native_context()->promise_prototype()) {
6049 return NoChange();
6050 }
6051 }
6052
6053 dependencies()->DependOnProtector(
6054 PropertyCellRef(js_heap_broker(), factory()->promise_hook_protector()));
6055 dependencies()->DependOnProtector(PropertyCellRef(
6056 js_heap_broker(), factory()->promise_species_protector()));
6057
6058 // If the {receiver_maps} aren't reliable, we need to repeat the
6059 // map check here, guarded by the CALL_IC.
6060 if (infer_receiver_maps_result == NodeProperties::kUnreliableReceiverMaps) {
6061 effect =
6062 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
6063 receiver_maps, p.feedback()),
6064 receiver, effect, control);
6065 }
6066
6067 // Check that {on_fulfilled} is callable.
6068 on_fulfilled = graph()->NewNode(
6069 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
6070 graph()->NewNode(simplified()->ObjectIsCallable(), on_fulfilled),
6071 on_fulfilled, jsgraph()->UndefinedConstant());
6072
6073 // Check that {on_rejected} is callable.
6074 on_rejected = graph()->NewNode(
6075 common()->Select(MachineRepresentation::kTagged, BranchHint::kTrue),
6076 graph()->NewNode(simplified()->ObjectIsCallable(), on_rejected),
6077 on_rejected, jsgraph()->UndefinedConstant());
6078
6079 // Create the resulting JSPromise.
6080 Node* result = effect =
6081 graph()->NewNode(javascript()->CreatePromise(), context, effect);
6082
6083 // Chain {result} onto {receiver}.
6084 result = effect = graph()->NewNode(
6085 javascript()->PerformPromiseThen(), receiver, on_fulfilled, on_rejected,
6086 result, context, frame_state, effect, control);
6087 ReplaceWithValue(node, result, effect, control);
6088 return Replace(result);
6089 }
6090
6091 // ES section #sec-promise.resolve
ReducePromiseResolveTrampoline(Node * node)6092 Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) {
6093 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
6094 Node* receiver = NodeProperties::GetValueInput(node, 1);
6095 Node* value = node->op()->ValueInputCount() > 2
6096 ? NodeProperties::GetValueInput(node, 2)
6097 : jsgraph()->UndefinedConstant();
6098 Node* context = NodeProperties::GetContextInput(node);
6099 Node* frame_state = NodeProperties::GetFrameStateInput(node);
6100 Node* effect = NodeProperties::GetEffectInput(node);
6101 Node* control = NodeProperties::GetControlInput(node);
6102
6103 // Check if we know something about {receiver} already.
6104 ZoneHandleSet<Map> receiver_maps;
6105 NodeProperties::InferReceiverMapsResult infer_receiver_maps_result =
6106 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
6107 &receiver_maps);
6108 if (infer_receiver_maps_result == NodeProperties::kNoReceiverMaps) {
6109 return NoChange();
6110 }
6111 DCHECK_NE(0, receiver_maps.size());
6112
6113 // Only reduce when all {receiver_maps} are JSReceiver maps.
6114 for (Handle<Map> receiver_map : receiver_maps) {
6115 if (!receiver_map->IsJSReceiverMap()) return NoChange();
6116 }
6117
6118 // Morph the {node} into a JSPromiseResolve operation.
6119 node->ReplaceInput(0, receiver);
6120 node->ReplaceInput(1, value);
6121 node->ReplaceInput(2, context);
6122 node->ReplaceInput(3, frame_state);
6123 node->ReplaceInput(4, effect);
6124 node->ReplaceInput(5, control);
6125 node->TrimInputCount(6);
6126 NodeProperties::ChangeOp(node, javascript()->PromiseResolve());
6127 return Changed(node);
6128 }
6129
6130 // ES #sec-typedarray-constructors
ReduceTypedArrayConstructor(Node * node,Handle<SharedFunctionInfo> shared)6131 Reduction JSCallReducer::ReduceTypedArrayConstructor(
6132 Node* node, Handle<SharedFunctionInfo> shared) {
6133 DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
6134 ConstructParameters const& p = ConstructParametersOf(node->op());
6135 int arity = static_cast<int>(p.arity() - 2);
6136 Node* target = NodeProperties::GetValueInput(node, 0);
6137 Node* arg1 = (arity >= 1) ? NodeProperties::GetValueInput(node, 1)
6138 : jsgraph()->UndefinedConstant();
6139 Node* arg2 = (arity >= 2) ? NodeProperties::GetValueInput(node, 2)
6140 : jsgraph()->UndefinedConstant();
6141 Node* arg3 = (arity >= 3) ? NodeProperties::GetValueInput(node, 3)
6142 : jsgraph()->UndefinedConstant();
6143 Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
6144 Node* context = NodeProperties::GetContextInput(node);
6145 Node* frame_state = NodeProperties::GetFrameStateInput(node);
6146 Node* effect = NodeProperties::GetEffectInput(node);
6147 Node* control = NodeProperties::GetControlInput(node);
6148
6149 // Insert a construct stub frame into the chain of frame states. This will
6150 // reconstruct the proper frame when deoptimizing within the constructor.
6151 frame_state = CreateArtificialFrameState(
6152 node, frame_state, arity, BailoutId::ConstructStubInvoke(),
6153 FrameStateType::kConstructStub, shared);
6154
6155 // This continuation just returns the newly created JSTypedArray. We
6156 // pass the_hole as the receiver, just like the builtin construct stub
6157 // does in this case.
6158 Node* const parameters[] = {jsgraph()->TheHoleConstant()};
6159 int const num_parameters = static_cast<int>(arraysize(parameters));
6160 frame_state = CreateJavaScriptBuiltinContinuationFrameState(
6161 jsgraph(), shared, Builtins::kGenericConstructorLazyDeoptContinuation,
6162 target, context, parameters, num_parameters, frame_state,
6163 ContinuationFrameStateMode::LAZY);
6164
6165 Node* result =
6166 graph()->NewNode(javascript()->CreateTypedArray(), target, new_target,
6167 arg1, arg2, arg3, context, frame_state, effect, control);
6168 return Replace(result);
6169 }
6170
6171 // ES #sec-get-%typedarray%.prototype-@@tostringtag
ReduceTypedArrayPrototypeToStringTag(Node * node)6172 Reduction JSCallReducer::ReduceTypedArrayPrototypeToStringTag(Node* node) {
6173 Node* receiver = NodeProperties::GetValueInput(node, 1);
6174 Node* effect = NodeProperties::GetEffectInput(node);
6175 Node* control = NodeProperties::GetControlInput(node);
6176
6177 NodeVector values(graph()->zone());
6178 NodeVector effects(graph()->zone());
6179 NodeVector controls(graph()->zone());
6180
6181 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver);
6182 control =
6183 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
6184
6185 values.push_back(jsgraph()->UndefinedConstant());
6186 effects.push_back(effect);
6187 controls.push_back(graph()->NewNode(common()->IfTrue(), control));
6188
6189 control = graph()->NewNode(common()->IfFalse(), control);
6190 Node* receiver_map = effect =
6191 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
6192 receiver, effect, control);
6193 Node* receiver_bit_field2 = effect = graph()->NewNode(
6194 simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map,
6195 effect, control);
6196 Node* receiver_elements_kind = graph()->NewNode(
6197 simplified()->NumberShiftRightLogical(),
6198 graph()->NewNode(simplified()->NumberBitwiseAnd(), receiver_bit_field2,
6199 jsgraph()->Constant(Map::ElementsKindBits::kMask)),
6200 jsgraph()->Constant(Map::ElementsKindBits::kShift));
6201
6202 // Offset the elements kind by FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND,
6203 // so that the branch cascade below is turned into a simple table
6204 // switch by the ControlFlowOptimizer later.
6205 receiver_elements_kind = graph()->NewNode(
6206 simplified()->NumberSubtract(), receiver_elements_kind,
6207 jsgraph()->Constant(FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND));
6208
6209 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
6210 do { \
6211 Node* check = graph()->NewNode( \
6212 simplified()->NumberEqual(), receiver_elements_kind, \
6213 jsgraph()->Constant(TYPE##_ELEMENTS - \
6214 FIRST_FIXED_TYPED_ARRAY_ELEMENTS_KIND)); \
6215 control = graph()->NewNode(common()->Branch(), check, control); \
6216 values.push_back(jsgraph()->HeapConstant( \
6217 factory()->InternalizeUtf8String(#Type "Array"))); \
6218 effects.push_back(effect); \
6219 controls.push_back(graph()->NewNode(common()->IfTrue(), control)); \
6220 control = graph()->NewNode(common()->IfFalse(), control); \
6221 } while (false);
6222 TYPED_ARRAYS(TYPED_ARRAY_CASE)
6223 #undef TYPED_ARRAY_CASE
6224
6225 values.push_back(jsgraph()->UndefinedConstant());
6226 effects.push_back(effect);
6227 controls.push_back(control);
6228
6229 int const count = static_cast<int>(controls.size());
6230 control = graph()->NewNode(common()->Merge(count), count, &controls.front());
6231 effects.push_back(control);
6232 effect =
6233 graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front());
6234 values.push_back(control);
6235 Node* value =
6236 graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
6237 count + 1, &values.front());
6238 ReplaceWithValue(node, value, effect, control);
6239 return Replace(value);
6240 }
6241
6242 // ES #sec-number.isfinite
ReduceNumberIsFinite(Node * node)6243 Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) {
6244 if (node->op()->ValueInputCount() < 3) {
6245 Node* value = jsgraph()->FalseConstant();
6246 ReplaceWithValue(node, value);
6247 return Replace(value);
6248 }
6249 Node* input = NodeProperties::GetValueInput(node, 2);
6250 Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input);
6251 ReplaceWithValue(node, value);
6252 return Replace(value);
6253 }
6254
6255 // ES #sec-number.isfinite
ReduceNumberIsInteger(Node * node)6256 Reduction JSCallReducer::ReduceNumberIsInteger(Node* node) {
6257 if (node->op()->ValueInputCount() < 3) {
6258 Node* value = jsgraph()->FalseConstant();
6259 ReplaceWithValue(node, value);
6260 return Replace(value);
6261 }
6262 Node* input = NodeProperties::GetValueInput(node, 2);
6263 Node* value = graph()->NewNode(simplified()->ObjectIsInteger(), input);
6264 ReplaceWithValue(node, value);
6265 return Replace(value);
6266 }
6267
6268 // ES #sec-number.issafeinteger
ReduceNumberIsSafeInteger(Node * node)6269 Reduction JSCallReducer::ReduceNumberIsSafeInteger(Node* node) {
6270 if (node->op()->ValueInputCount() < 3) {
6271 Node* value = jsgraph()->FalseConstant();
6272 ReplaceWithValue(node, value);
6273 return Replace(value);
6274 }
6275 Node* input = NodeProperties::GetValueInput(node, 2);
6276 Node* value = graph()->NewNode(simplified()->ObjectIsSafeInteger(), input);
6277 ReplaceWithValue(node, value);
6278 return Replace(value);
6279 }
6280
6281 // ES #sec-number.isnan
ReduceNumberIsNaN(Node * node)6282 Reduction JSCallReducer::ReduceNumberIsNaN(Node* node) {
6283 if (node->op()->ValueInputCount() < 3) {
6284 Node* value = jsgraph()->FalseConstant();
6285 ReplaceWithValue(node, value);
6286 return Replace(value);
6287 }
6288 Node* input = NodeProperties::GetValueInput(node, 2);
6289 Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
6290 ReplaceWithValue(node, value);
6291 return Replace(value);
6292 }
6293
ReduceMapPrototypeGet(Node * node)6294 Reduction JSCallReducer::ReduceMapPrototypeGet(Node* node) {
6295 // We only optimize if we have target, receiver and key parameters.
6296 if (node->op()->ValueInputCount() != 3) return NoChange();
6297 Node* receiver = NodeProperties::GetValueInput(node, 1);
6298 Node* effect = NodeProperties::GetEffectInput(node);
6299 Node* control = NodeProperties::GetControlInput(node);
6300 Node* key = NodeProperties::GetValueInput(node, 2);
6301
6302 if (!NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
6303 JS_MAP_TYPE))
6304 return NoChange();
6305
6306 Node* table = effect = graph()->NewNode(
6307 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver,
6308 effect, control);
6309
6310 Node* entry = effect = graph()->NewNode(
6311 simplified()->FindOrderedHashMapEntry(), table, key, effect, control);
6312
6313 Node* check = graph()->NewNode(simplified()->NumberEqual(), entry,
6314 jsgraph()->MinusOneConstant());
6315
6316 Node* branch = graph()->NewNode(common()->Branch(), check, control);
6317
6318 // Key not found.
6319 Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
6320 Node* etrue = effect;
6321 Node* vtrue = jsgraph()->UndefinedConstant();
6322
6323 // Key found.
6324 Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
6325 Node* efalse = effect;
6326 Node* vfalse = efalse = graph()->NewNode(
6327 simplified()->LoadElement(AccessBuilder::ForOrderedHashMapEntryValue()),
6328 table, entry, efalse, if_false);
6329
6330 control = graph()->NewNode(common()->Merge(2), if_true, if_false);
6331 Node* value = graph()->NewNode(
6332 common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control);
6333 effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
6334
6335 ReplaceWithValue(node, value, effect, control);
6336 return Replace(value);
6337 }
6338
ReduceMapPrototypeHas(Node * node)6339 Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node) {
6340 // We only optimize if we have target, receiver and key parameters.
6341 if (node->op()->ValueInputCount() != 3) return NoChange();
6342 Node* receiver = NodeProperties::GetValueInput(node, 1);
6343 Node* effect = NodeProperties::GetEffectInput(node);
6344 Node* control = NodeProperties::GetControlInput(node);
6345 Node* key = NodeProperties::GetValueInput(node, 2);
6346
6347 if (!NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
6348 JS_MAP_TYPE))
6349 return NoChange();
6350
6351 Node* table = effect = graph()->NewNode(
6352 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver,
6353 effect, control);
6354
6355 Node* index = effect = graph()->NewNode(
6356 simplified()->FindOrderedHashMapEntry(), table, key, effect, control);
6357
6358 Node* value = graph()->NewNode(simplified()->NumberEqual(), index,
6359 jsgraph()->MinusOneConstant());
6360 value = graph()->NewNode(simplified()->BooleanNot(), value);
6361
6362 ReplaceWithValue(node, value, effect, control);
6363 return Replace(value);
6364 }
6365
6366 namespace {
6367
InstanceTypeForCollectionKind(CollectionKind kind)6368 InstanceType InstanceTypeForCollectionKind(CollectionKind kind) {
6369 switch (kind) {
6370 case CollectionKind::kMap:
6371 return JS_MAP_TYPE;
6372 case CollectionKind::kSet:
6373 return JS_SET_TYPE;
6374 }
6375 UNREACHABLE();
6376 }
6377
6378 } // namespace
6379
ReduceCollectionIteration(Node * node,CollectionKind collection_kind,IterationKind iteration_kind)6380 Reduction JSCallReducer::ReduceCollectionIteration(
6381 Node* node, CollectionKind collection_kind, IterationKind iteration_kind) {
6382 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
6383 Node* receiver = NodeProperties::GetValueInput(node, 1);
6384 Node* context = NodeProperties::GetContextInput(node);
6385 Node* effect = NodeProperties::GetEffectInput(node);
6386 Node* control = NodeProperties::GetControlInput(node);
6387 if (NodeProperties::HasInstanceTypeWitness(
6388 isolate(), receiver, effect,
6389 InstanceTypeForCollectionKind(collection_kind))) {
6390 Node* js_create_iterator = effect = graph()->NewNode(
6391 javascript()->CreateCollectionIterator(collection_kind, iteration_kind),
6392 receiver, context, effect, control);
6393 ReplaceWithValue(node, js_create_iterator, effect);
6394 return Replace(js_create_iterator);
6395 }
6396 return NoChange();
6397 }
6398
ReduceCollectionPrototypeSize(Node * node,CollectionKind collection_kind)6399 Reduction JSCallReducer::ReduceCollectionPrototypeSize(
6400 Node* node, CollectionKind collection_kind) {
6401 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
6402 Node* receiver = NodeProperties::GetValueInput(node, 1);
6403 Node* effect = NodeProperties::GetEffectInput(node);
6404 Node* control = NodeProperties::GetControlInput(node);
6405 if (NodeProperties::HasInstanceTypeWitness(
6406 isolate(), receiver, effect,
6407 InstanceTypeForCollectionKind(collection_kind))) {
6408 Node* table = effect = graph()->NewNode(
6409 simplified()->LoadField(AccessBuilder::ForJSCollectionTable()),
6410 receiver, effect, control);
6411 Node* value = effect = graph()->NewNode(
6412 simplified()->LoadField(
6413 AccessBuilder::ForOrderedHashTableBaseNumberOfElements()),
6414 table, effect, control);
6415 ReplaceWithValue(node, value, effect, control);
6416 return Replace(value);
6417 }
6418 return NoChange();
6419 }
6420
ReduceCollectionIteratorPrototypeNext(Node * node,int entry_size,Handle<HeapObject> empty_collection,InstanceType collection_iterator_instance_type_first,InstanceType collection_iterator_instance_type_last)6421 Reduction JSCallReducer::ReduceCollectionIteratorPrototypeNext(
6422 Node* node, int entry_size, Handle<HeapObject> empty_collection,
6423 InstanceType collection_iterator_instance_type_first,
6424 InstanceType collection_iterator_instance_type_last) {
6425 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
6426 Node* receiver = NodeProperties::GetValueInput(node, 1);
6427 Node* context = NodeProperties::GetContextInput(node);
6428 Node* effect = NodeProperties::GetEffectInput(node);
6429 Node* control = NodeProperties::GetControlInput(node);
6430
6431 // A word of warning to begin with: This whole method might look a bit
6432 // strange at times, but that's mostly because it was carefully handcrafted
6433 // to allow for full escape analysis and scalar replacement of both the
6434 // collection iterator object and the iterator results, including the
6435 // key-value arrays in case of Set/Map entry iteration.
6436 //
6437 // TODO(turbofan): Currently the escape analysis (and the store-load
6438 // forwarding) is unable to eliminate the allocations for the key-value
6439 // arrays in case of Set/Map entry iteration, and we should investigate
6440 // how to update the escape analysis / arrange the graph in a way that
6441 // this becomes possible.
6442
6443 // Infer the {receiver} instance type.
6444 InstanceType receiver_instance_type;
6445 ZoneHandleSet<Map> receiver_maps;
6446 NodeProperties::InferReceiverMapsResult result =
6447 NodeProperties::InferReceiverMaps(isolate(), receiver, effect,
6448 &receiver_maps);
6449 if (result == NodeProperties::kNoReceiverMaps) return NoChange();
6450 DCHECK_NE(0, receiver_maps.size());
6451 receiver_instance_type = receiver_maps[0]->instance_type();
6452 for (size_t i = 1; i < receiver_maps.size(); ++i) {
6453 if (receiver_maps[i]->instance_type() != receiver_instance_type) {
6454 return NoChange();
6455 }
6456 }
6457 if (receiver_instance_type < collection_iterator_instance_type_first ||
6458 receiver_instance_type > collection_iterator_instance_type_last) {
6459 return NoChange();
6460 }
6461
6462 // Transition the JSCollectionIterator {receiver} if necessary
6463 // (i.e. there were certain mutations while we're iterating).
6464 {
6465 Node* done_loop;
6466 Node* done_eloop;
6467 Node* loop = control =
6468 graph()->NewNode(common()->Loop(2), control, control);
6469 Node* eloop = effect =
6470 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
6471 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
6472 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
6473
6474 // Check if reached the final table of the {receiver}.
6475 Node* table = effect = graph()->NewNode(
6476 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
6477 receiver, effect, control);
6478 Node* next_table = effect =
6479 graph()->NewNode(simplified()->LoadField(
6480 AccessBuilder::ForOrderedHashTableBaseNextTable()),
6481 table, effect, control);
6482 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table);
6483 control =
6484 graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
6485
6486 // Abort the {loop} when we reach the final table.
6487 done_loop = graph()->NewNode(common()->IfTrue(), control);
6488 done_eloop = effect;
6489
6490 // Migrate to the {next_table} otherwise.
6491 control = graph()->NewNode(common()->IfFalse(), control);
6492
6493 // Self-heal the {receiver}s index.
6494 Node* index = effect = graph()->NewNode(
6495 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
6496 receiver, effect, control);
6497 Callable const callable =
6498 Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex);
6499 auto call_descriptor = Linkage::GetStubCallDescriptor(
6500 graph()->zone(), callable.descriptor(), 0, CallDescriptor::kNoFlags,
6501 Operator::kEliminatable);
6502 index = effect =
6503 graph()->NewNode(common()->Call(call_descriptor),
6504 jsgraph()->HeapConstant(callable.code()), table, index,
6505 jsgraph()->NoContextConstant(), effect);
6506
6507 index = effect = graph()->NewNode(
6508 common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), index,
6509 effect, control);
6510
6511 // Update the {index} and {table} on the {receiver}.
6512 effect = graph()->NewNode(
6513 simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()),
6514 receiver, index, effect, control);
6515 effect = graph()->NewNode(
6516 simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()),
6517 receiver, next_table, effect, control);
6518
6519 // Tie the knot.
6520 loop->ReplaceInput(1, control);
6521 eloop->ReplaceInput(1, effect);
6522
6523 control = done_loop;
6524 effect = done_eloop;
6525 }
6526
6527 // Get current index and table from the JSCollectionIterator {receiver}.
6528 Node* index = effect = graph()->NewNode(
6529 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()),
6530 receiver, effect, control);
6531 Node* table = effect = graph()->NewNode(
6532 simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()),
6533 receiver, effect, control);
6534
6535 // Create the {JSIteratorResult} first to ensure that we always have
6536 // a dominating Allocate node for the allocation folding phase.
6537 Node* iterator_result = effect = graph()->NewNode(
6538 javascript()->CreateIterResultObject(), jsgraph()->UndefinedConstant(),
6539 jsgraph()->TrueConstant(), context, effect);
6540
6541 // Look for the next non-holey key, starting from {index} in the {table}.
6542 Node* controls[2];
6543 Node* effects[3];
6544 {
6545 // Compute the currently used capacity.
6546 Node* number_of_buckets = effect = graph()->NewNode(
6547 simplified()->LoadField(
6548 AccessBuilder::ForOrderedHashTableBaseNumberOfBuckets()),
6549 table, effect, control);
6550 Node* number_of_elements = effect = graph()->NewNode(
6551 simplified()->LoadField(
6552 AccessBuilder::ForOrderedHashTableBaseNumberOfElements()),
6553 table, effect, control);
6554 Node* number_of_deleted_elements = effect = graph()->NewNode(
6555 simplified()->LoadField(
6556 AccessBuilder::ForOrderedHashTableBaseNumberOfDeletedElements()),
6557 table, effect, control);
6558 Node* used_capacity =
6559 graph()->NewNode(simplified()->NumberAdd(), number_of_elements,
6560 number_of_deleted_elements);
6561
6562 // Skip holes and update the {index}.
6563 Node* loop = graph()->NewNode(common()->Loop(2), control, control);
6564 Node* eloop =
6565 graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
6566 Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop);
6567 NodeProperties::MergeControlToEnd(graph(), common(), terminate);
6568 Node* iloop = graph()->NewNode(
6569 common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop);
6570
6571 Node* index = effect = graph()->NewNode(
6572 common()->TypeGuard(TypeCache::Get().kFixedArrayLengthType), iloop,
6573 eloop, control);
6574 {
6575 Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index,
6576 used_capacity);
6577 Node* branch0 =
6578 graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop);
6579
6580 Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
6581 Node* efalse0 = effect;
6582 {
6583 // Mark the {receiver} as exhausted.
6584 efalse0 = graph()->NewNode(
6585 simplified()->StoreField(
6586 AccessBuilder::ForJSCollectionIteratorTable()),
6587 receiver, jsgraph()->HeapConstant(empty_collection), efalse0,
6588 if_false0);
6589
6590 controls[0] = if_false0;
6591 effects[0] = efalse0;
6592 }
6593
6594 Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
6595 Node* etrue0 = effect;
6596 {
6597 // Load the key of the entry.
6598 Node* entry_start_position = graph()->NewNode(
6599 simplified()->NumberAdd(),
6600 graph()->NewNode(
6601 simplified()->NumberAdd(),
6602 graph()->NewNode(simplified()->NumberMultiply(), index,
6603 jsgraph()->Constant(entry_size)),
6604 number_of_buckets),
6605 jsgraph()->Constant(OrderedHashTableBase::kHashTableStartIndex));
6606 Node* entry_key = etrue0 = graph()->NewNode(
6607 simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()),
6608 table, entry_start_position, etrue0, if_true0);
6609
6610 // Advance the index.
6611 index = graph()->NewNode(simplified()->NumberAdd(), index,
6612 jsgraph()->OneConstant());
6613
6614 Node* check1 =
6615 graph()->NewNode(simplified()->ReferenceEqual(), entry_key,
6616 jsgraph()->TheHoleConstant());
6617 Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
6618 check1, if_true0);
6619
6620 {
6621 // Abort loop with resulting value.
6622 Node* control = graph()->NewNode(common()->IfFalse(), branch1);
6623 Node* effect = etrue0;
6624 Node* value = effect =
6625 graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
6626 entry_key, effect, control);
6627 Node* done = jsgraph()->FalseConstant();
6628
6629 // Advance the index on the {receiver}.
6630 effect = graph()->NewNode(
6631 simplified()->StoreField(
6632 AccessBuilder::ForJSCollectionIteratorIndex()),
6633 receiver, index, effect, control);
6634
6635 // The actual {value} depends on the {receiver} iteration type.
6636 switch (receiver_instance_type) {
6637 case JS_MAP_KEY_ITERATOR_TYPE:
6638 case JS_SET_VALUE_ITERATOR_TYPE:
6639 break;
6640
6641 case JS_SET_KEY_VALUE_ITERATOR_TYPE:
6642 value = effect =
6643 graph()->NewNode(javascript()->CreateKeyValueArray(), value,
6644 value, context, effect);
6645 break;
6646
6647 case JS_MAP_VALUE_ITERATOR_TYPE:
6648 value = effect = graph()->NewNode(
6649 simplified()->LoadElement(
6650 AccessBuilder::ForFixedArrayElement()),
6651 table,
6652 graph()->NewNode(
6653 simplified()->NumberAdd(), entry_start_position,
6654 jsgraph()->Constant(OrderedHashMap::kValueOffset)),
6655 effect, control);
6656 break;
6657
6658 case JS_MAP_KEY_VALUE_ITERATOR_TYPE:
6659 value = effect = graph()->NewNode(
6660 simplified()->LoadElement(
6661 AccessBuilder::ForFixedArrayElement()),
6662 table,
6663 graph()->NewNode(
6664 simplified()->NumberAdd(), entry_start_position,
6665 jsgraph()->Constant(OrderedHashMap::kValueOffset)),
6666 effect, control);
6667 value = effect =
6668 graph()->NewNode(javascript()->CreateKeyValueArray(),
6669 entry_key, value, context, effect);
6670 break;
6671
6672 default:
6673 UNREACHABLE();
6674 break;
6675 }
6676
6677 // Store final {value} and {done} into the {iterator_result}.
6678 effect =
6679 graph()->NewNode(simplified()->StoreField(
6680 AccessBuilder::ForJSIteratorResultValue()),
6681 iterator_result, value, effect, control);
6682 effect =
6683 graph()->NewNode(simplified()->StoreField(
6684 AccessBuilder::ForJSIteratorResultDone()),
6685 iterator_result, done, effect, control);
6686
6687 controls[1] = control;
6688 effects[1] = effect;
6689 }
6690
6691 // Continue with next loop index.
6692 loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1));
6693 eloop->ReplaceInput(1, etrue0);
6694 iloop->ReplaceInput(1, index);
6695 }
6696 }
6697
6698 control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls);
6699 effect = graph()->NewNode(common()->EffectPhi(2), 3, effects);
6700 }
6701
6702 // Yield the final {iterator_result}.
6703 ReplaceWithValue(node, iterator_result, effect, control);
6704 return Replace(iterator_result);
6705 }
6706
ReduceArrayBufferIsView(Node * node)6707 Reduction JSCallReducer::ReduceArrayBufferIsView(Node* node) {
6708 Node* value = node->op()->ValueInputCount() >= 3
6709 ? NodeProperties::GetValueInput(node, 2)
6710 : jsgraph()->UndefinedConstant();
6711 RelaxEffectsAndControls(node);
6712 node->ReplaceInput(0, value);
6713 node->TrimInputCount(1);
6714 NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView());
6715 return Changed(node);
6716 }
6717
ReduceArrayBufferViewAccessor(Node * node,InstanceType instance_type,FieldAccess const & access)6718 Reduction JSCallReducer::ReduceArrayBufferViewAccessor(
6719 Node* node, InstanceType instance_type, FieldAccess const& access) {
6720 Node* receiver = NodeProperties::GetValueInput(node, 1);
6721 Node* effect = NodeProperties::GetEffectInput(node);
6722 Node* control = NodeProperties::GetControlInput(node);
6723 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
6724 instance_type)) {
6725 // Load the {receiver}s field.
6726 Node* value = effect = graph()->NewNode(simplified()->LoadField(access),
6727 receiver, effect, control);
6728
6729 // See if we can skip the neutering check.
6730 if (isolate()->IsArrayBufferNeuteringIntact()) {
6731 // Add a code dependency so we are deoptimized in case an ArrayBuffer
6732 // gets neutered.
6733 dependencies()->DependOnProtector(PropertyCellRef(
6734 js_heap_broker(), factory()->array_buffer_neutering_protector()));
6735 } else {
6736 // Check if the {receiver}s buffer was neutered.
6737 Node* buffer = effect = graph()->NewNode(
6738 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
6739 receiver, effect, control);
6740 Node* check = effect = graph()->NewNode(
6741 simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
6742
6743 // Default to zero if the {receiver}s buffer was neutered.
6744 value = graph()->NewNode(
6745 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
6746 check, jsgraph()->ZeroConstant(), value);
6747 }
6748
6749 ReplaceWithValue(node, value, effect, control);
6750 return Replace(value);
6751 }
6752 return NoChange();
6753 }
6754
6755 namespace {
ExternalArrayElementSize(const ExternalArrayType element_type)6756 uint32_t ExternalArrayElementSize(const ExternalArrayType element_type) {
6757 switch (element_type) {
6758 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype) \
6759 case kExternal##Type##Array: \
6760 DCHECK_LE(sizeof(ctype), 8); \
6761 return sizeof(ctype);
6762 TYPED_ARRAYS(TYPED_ARRAY_CASE)
6763 default:
6764 UNREACHABLE();
6765 #undef TYPED_ARRAY_CASE
6766 }
6767 }
6768 } // namespace
6769
ReduceDataViewPrototypeGet(Node * node,ExternalArrayType element_type)6770 Reduction JSCallReducer::ReduceDataViewPrototypeGet(
6771 Node* node, ExternalArrayType element_type) {
6772 uint32_t const element_size = ExternalArrayElementSize(element_type);
6773 CallParameters const& p = CallParametersOf(node->op());
6774 Node* effect = NodeProperties::GetEffectInput(node);
6775 Node* control = NodeProperties::GetControlInput(node);
6776 Node* receiver = NodeProperties::GetValueInput(node, 1);
6777
6778 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
6779 return NoChange();
6780 }
6781
6782 Node* offset = node->op()->ValueInputCount() > 2
6783 ? NodeProperties::GetValueInput(node, 2)
6784 : jsgraph()->ZeroConstant();
6785
6786 Node* is_little_endian = node->op()->ValueInputCount() > 3
6787 ? NodeProperties::GetValueInput(node, 3)
6788 : jsgraph()->FalseConstant();
6789
6790 // Only do stuff if the {receiver} is really a DataView.
6791 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
6792 JS_DATA_VIEW_TYPE)) {
6793 // Check that the {offset} is within range for the {receiver}.
6794 HeapObjectMatcher m(receiver);
6795 if (m.HasValue()) {
6796 // We only deal with DataViews here whose [[ByteLength]] is at least
6797 // {element_size} and less than 2^31-{element_size}.
6798 Handle<JSDataView> dataview = Handle<JSDataView>::cast(m.Value());
6799 if (dataview->byte_length()->Number() < element_size ||
6800 dataview->byte_length()->Number() - element_size > kMaxInt) {
6801 return NoChange();
6802 }
6803
6804 // The {receiver}s [[ByteOffset]] must be within Unsigned31 range.
6805 if (dataview->byte_offset()->Number() > kMaxInt) {
6806 return NoChange();
6807 }
6808
6809 // Check that the {offset} is within range of the {byte_length}.
6810 Node* byte_length = jsgraph()->Constant(
6811 dataview->byte_length()->Number() - (element_size - 1));
6812 offset = effect =
6813 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
6814 byte_length, effect, control);
6815
6816 // Add the [[ByteOffset]] to compute the effective offset.
6817 Node* byte_offset =
6818 jsgraph()->Constant(dataview->byte_offset()->Number());
6819 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
6820 } else {
6821 // We only deal with DataViews here that have Smi [[ByteLength]]s.
6822 Node* byte_length = effect =
6823 graph()->NewNode(simplified()->LoadField(
6824 AccessBuilder::ForJSArrayBufferViewByteLength()),
6825 receiver, effect, control);
6826 byte_length = effect = graph()->NewNode(
6827 simplified()->CheckSmi(p.feedback()), byte_length, effect, control);
6828
6829 // Check that the {offset} is within range of the {byte_length}.
6830 offset = effect =
6831 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
6832 byte_length, effect, control);
6833
6834 if (element_size > 0) {
6835 // For non-byte accesses we also need to check that the {offset}
6836 // plus the {element_size}-1 fits within the given {byte_length}.
6837 Node* end_offset =
6838 graph()->NewNode(simplified()->NumberAdd(), offset,
6839 jsgraph()->Constant(element_size - 1));
6840 effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
6841 end_offset, byte_length, effect, control);
6842 }
6843
6844 // The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi.
6845 Node* byte_offset = effect =
6846 graph()->NewNode(simplified()->LoadField(
6847 AccessBuilder::ForJSArrayBufferViewByteOffset()),
6848 receiver, effect, control);
6849 byte_offset = effect = graph()->NewNode(
6850 simplified()->CheckSmi(p.feedback()), byte_offset, effect, control);
6851
6852 // Compute the buffer index at which we'll read.
6853 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
6854 }
6855
6856 // Coerce {is_little_endian} to boolean.
6857 is_little_endian =
6858 graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
6859
6860 // Get the underlying buffer and check that it has not been neutered.
6861 Node* buffer = effect = graph()->NewNode(
6862 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
6863 receiver, effect, control);
6864
6865 if (isolate()->IsArrayBufferNeuteringIntact()) {
6866 // Add a code dependency so we are deoptimized in case an ArrayBuffer
6867 // gets neutered.
6868 dependencies()->DependOnProtector(PropertyCellRef(
6869 js_heap_broker(), factory()->array_buffer_neutering_protector()));
6870 } else {
6871 // If the buffer was neutered, deopt and let the unoptimized code throw.
6872 Node* check_neutered = effect = graph()->NewNode(
6873 simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
6874 check_neutered =
6875 graph()->NewNode(simplified()->BooleanNot(), check_neutered);
6876 effect = graph()->NewNode(
6877 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered,
6878 p.feedback()),
6879 check_neutered, effect, control);
6880 }
6881
6882 // Get the buffer's backing store.
6883 Node* backing_store = effect = graph()->NewNode(
6884 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()),
6885 buffer, effect, control);
6886
6887 // Perform the load.
6888 Node* value = effect = graph()->NewNode(
6889 simplified()->LoadDataViewElement(element_type), buffer, backing_store,
6890 offset, is_little_endian, effect, control);
6891
6892 // Continue on the regular path.
6893 ReplaceWithValue(node, value, effect, control);
6894 return Changed(value);
6895 }
6896
6897 return NoChange();
6898 }
6899
ReduceDataViewPrototypeSet(Node * node,ExternalArrayType element_type)6900 Reduction JSCallReducer::ReduceDataViewPrototypeSet(
6901 Node* node, ExternalArrayType element_type) {
6902 uint32_t const element_size = ExternalArrayElementSize(element_type);
6903 CallParameters const& p = CallParametersOf(node->op());
6904 Node* effect = NodeProperties::GetEffectInput(node);
6905 Node* control = NodeProperties::GetControlInput(node);
6906 Node* receiver = NodeProperties::GetValueInput(node, 1);
6907
6908 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
6909 return NoChange();
6910 }
6911
6912 Node* offset = node->op()->ValueInputCount() > 2
6913 ? NodeProperties::GetValueInput(node, 2)
6914 : jsgraph()->ZeroConstant();
6915
6916 Node* value = node->op()->ValueInputCount() > 3
6917 ? NodeProperties::GetValueInput(node, 3)
6918 : jsgraph()->ZeroConstant();
6919
6920 Node* is_little_endian = node->op()->ValueInputCount() > 4
6921 ? NodeProperties::GetValueInput(node, 4)
6922 : jsgraph()->FalseConstant();
6923
6924 // Only do stuff if the {receiver} is really a DataView.
6925 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
6926 JS_DATA_VIEW_TYPE)) {
6927 // Check that the {offset} is within range for the {receiver}.
6928 HeapObjectMatcher m(receiver);
6929 if (m.HasValue()) {
6930 // We only deal with DataViews here whose [[ByteLength]] is at least
6931 // {element_size} and less than 2^31-{element_size}.
6932 Handle<JSDataView> dataview = Handle<JSDataView>::cast(m.Value());
6933 if (dataview->byte_length()->Number() < element_size ||
6934 dataview->byte_length()->Number() - element_size > kMaxInt) {
6935 return NoChange();
6936 }
6937
6938 // The {receiver}s [[ByteOffset]] must be within Unsigned31 range.
6939 if (dataview->byte_offset()->Number() > kMaxInt) {
6940 return NoChange();
6941 }
6942
6943 // Check that the {offset} is within range of the {byte_length}.
6944 Node* byte_length = jsgraph()->Constant(
6945 dataview->byte_length()->Number() - (element_size - 1));
6946 offset = effect =
6947 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
6948 byte_length, effect, control);
6949
6950 // Add the [[ByteOffset]] to compute the effective offset.
6951 Node* byte_offset =
6952 jsgraph()->Constant(dataview->byte_offset()->Number());
6953 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
6954 } else {
6955 // We only deal with DataViews here that have Smi [[ByteLength]]s.
6956 Node* byte_length = effect =
6957 graph()->NewNode(simplified()->LoadField(
6958 AccessBuilder::ForJSArrayBufferViewByteLength()),
6959 receiver, effect, control);
6960 byte_length = effect = graph()->NewNode(
6961 simplified()->CheckSmi(p.feedback()), byte_length, effect, control);
6962
6963 // Check that the {offset} is within range of the {byte_length}.
6964 offset = effect =
6965 graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset,
6966 byte_length, effect, control);
6967
6968 if (element_size > 0) {
6969 // For non-byte accesses we also need to check that the {offset}
6970 // plus the {element_size}-1 fits within the given {byte_length}.
6971 Node* end_offset =
6972 graph()->NewNode(simplified()->NumberAdd(), offset,
6973 jsgraph()->Constant(element_size - 1));
6974 effect = graph()->NewNode(simplified()->CheckBounds(p.feedback()),
6975 end_offset, byte_length, effect, control);
6976 }
6977
6978 // The {receiver}s [[ByteOffset]] also needs to be a (positive) Smi.
6979 Node* byte_offset = effect =
6980 graph()->NewNode(simplified()->LoadField(
6981 AccessBuilder::ForJSArrayBufferViewByteOffset()),
6982 receiver, effect, control);
6983 byte_offset = effect = graph()->NewNode(
6984 simplified()->CheckSmi(p.feedback()), byte_offset, effect, control);
6985
6986 // Compute the buffer index at which we'll read.
6987 offset = graph()->NewNode(simplified()->NumberAdd(), offset, byte_offset);
6988 }
6989
6990 // Coerce {is_little_endian} to boolean.
6991 is_little_endian =
6992 graph()->NewNode(simplified()->ToBoolean(), is_little_endian);
6993
6994 // Coerce {value} to Number.
6995 value = effect = graph()->NewNode(
6996 simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
6997 p.feedback()),
6998 value, effect, control);
6999
7000 // Get the underlying buffer and check that it has not been neutered.
7001 Node* buffer = effect = graph()->NewNode(
7002 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
7003 receiver, effect, control);
7004
7005 if (isolate()->IsArrayBufferNeuteringIntact()) {
7006 // Add a code dependency so we are deoptimized in case an ArrayBuffer
7007 // gets neutered.
7008 dependencies()->DependOnProtector(PropertyCellRef(
7009 js_heap_broker(), factory()->array_buffer_neutering_protector()));
7010 } else {
7011 // If the buffer was neutered, deopt and let the unoptimized code throw.
7012 Node* check_neutered = effect = graph()->NewNode(
7013 simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
7014 check_neutered =
7015 graph()->NewNode(simplified()->BooleanNot(), check_neutered);
7016 effect = graph()->NewNode(
7017 simplified()->CheckIf(DeoptimizeReason::kArrayBufferWasNeutered,
7018 p.feedback()),
7019 check_neutered, effect, control);
7020 }
7021
7022 // Get the buffer's backing store.
7023 Node* backing_store = effect = graph()->NewNode(
7024 simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()),
7025 buffer, effect, control);
7026
7027 // Perform the store.
7028 effect = graph()->NewNode(simplified()->StoreDataViewElement(element_type),
7029 buffer, backing_store, offset, value,
7030 is_little_endian, effect, control);
7031
7032 Node* value = jsgraph()->UndefinedConstant();
7033
7034 // Continue on the regular path.
7035 ReplaceWithValue(node, value, effect, control);
7036 return Changed(value);
7037 }
7038
7039 return NoChange();
7040 }
7041
7042 // ES6 section 18.2.2 isFinite ( number )
ReduceGlobalIsFinite(Node * node)7043 Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) {
7044 CallParameters const& p = CallParametersOf(node->op());
7045 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
7046 return NoChange();
7047 }
7048 if (node->op()->ValueInputCount() < 3) {
7049 Node* value = jsgraph()->FalseConstant();
7050 ReplaceWithValue(node, value);
7051 return Replace(value);
7052 }
7053
7054 Node* effect = NodeProperties::GetEffectInput(node);
7055 Node* control = NodeProperties::GetControlInput(node);
7056 Node* input = NodeProperties::GetValueInput(node, 2);
7057
7058 input = effect =
7059 graph()->NewNode(simplified()->SpeculativeToNumber(
7060 NumberOperationHint::kNumberOrOddball, p.feedback()),
7061 input, effect, control);
7062 Node* value = graph()->NewNode(simplified()->NumberIsFinite(), input);
7063 ReplaceWithValue(node, value, effect);
7064 return Replace(value);
7065 }
7066
7067 // ES6 section 18.2.3 isNaN ( number )
ReduceGlobalIsNaN(Node * node)7068 Reduction JSCallReducer::ReduceGlobalIsNaN(Node* node) {
7069 CallParameters const& p = CallParametersOf(node->op());
7070 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
7071 return NoChange();
7072 }
7073 if (node->op()->ValueInputCount() < 3) {
7074 Node* value = jsgraph()->TrueConstant();
7075 ReplaceWithValue(node, value);
7076 return Replace(value);
7077 }
7078
7079 Node* effect = NodeProperties::GetEffectInput(node);
7080 Node* control = NodeProperties::GetControlInput(node);
7081 Node* input = NodeProperties::GetValueInput(node, 2);
7082
7083 input = effect =
7084 graph()->NewNode(simplified()->SpeculativeToNumber(
7085 NumberOperationHint::kNumberOrOddball, p.feedback()),
7086 input, effect, control);
7087 Node* value = graph()->NewNode(simplified()->NumberIsNaN(), input);
7088 ReplaceWithValue(node, value, effect);
7089 return Replace(value);
7090 }
7091
7092 // ES6 section 20.3.4.10 Date.prototype.getTime ( )
ReduceDatePrototypeGetTime(Node * node)7093 Reduction JSCallReducer::ReduceDatePrototypeGetTime(Node* node) {
7094 Node* receiver = NodeProperties::GetValueInput(node, 1);
7095 Node* effect = NodeProperties::GetEffectInput(node);
7096 Node* control = NodeProperties::GetControlInput(node);
7097 if (NodeProperties::HasInstanceTypeWitness(isolate(), receiver, effect,
7098 JS_DATE_TYPE)) {
7099 Node* value = effect = graph()->NewNode(
7100 simplified()->LoadField(AccessBuilder::ForJSDateValue()), receiver,
7101 effect, control);
7102 ReplaceWithValue(node, value, effect, control);
7103 return Replace(value);
7104 }
7105 return NoChange();
7106 }
7107
7108 // ES6 section 20.3.3.1 Date.now ( )
ReduceDateNow(Node * node)7109 Reduction JSCallReducer::ReduceDateNow(Node* node) {
7110 Node* effect = NodeProperties::GetEffectInput(node);
7111 Node* control = NodeProperties::GetControlInput(node);
7112 Node* value = effect =
7113 graph()->NewNode(simplified()->DateNow(), effect, control);
7114 ReplaceWithValue(node, value, effect, control);
7115 return Replace(value);
7116 }
7117
7118 // ES6 section 20.1.2.13 Number.parseInt ( string, radix )
ReduceNumberParseInt(Node * node)7119 Reduction JSCallReducer::ReduceNumberParseInt(Node* node) {
7120 // We certainly know that undefined is not an array.
7121 if (node->op()->ValueInputCount() < 3) {
7122 Node* value = jsgraph()->NaNConstant();
7123 ReplaceWithValue(node, value);
7124 return Replace(value);
7125 }
7126
7127 int arg_count = node->op()->ValueInputCount();
7128 Node* effect = NodeProperties::GetEffectInput(node);
7129 Node* control = NodeProperties::GetControlInput(node);
7130 Node* context = NodeProperties::GetContextInput(node);
7131 Node* frame_state = NodeProperties::GetFrameStateInput(node);
7132 Node* object = NodeProperties::GetValueInput(node, 2);
7133 Node* radix = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3)
7134 : jsgraph()->UndefinedConstant();
7135 node->ReplaceInput(0, object);
7136 node->ReplaceInput(1, radix);
7137 node->ReplaceInput(2, context);
7138 node->ReplaceInput(3, frame_state);
7139 node->ReplaceInput(4, effect);
7140 node->ReplaceInput(5, control);
7141 node->TrimInputCount(6);
7142 NodeProperties::ChangeOp(node, javascript()->ParseInt());
7143 return Changed(node);
7144 }
7145
ReduceRegExpPrototypeTest(Node * node)7146 Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) {
7147 if (FLAG_force_slow_path) return NoChange();
7148 if (node->op()->ValueInputCount() < 3) return NoChange();
7149 CallParameters const& p = CallParametersOf(node->op());
7150 if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
7151 return NoChange();
7152 }
7153
7154 Node* effect = NodeProperties::GetEffectInput(node);
7155 Node* control = NodeProperties::GetControlInput(node);
7156 Node* regexp = NodeProperties::GetValueInput(node, 1);
7157
7158 // Check if we know something about the {regexp}.
7159 ZoneHandleSet<Map> regexp_maps;
7160 NodeProperties::InferReceiverMapsResult result =
7161 NodeProperties::InferReceiverMaps(isolate(), regexp, effect,
7162 ®exp_maps);
7163
7164 bool need_map_check = false;
7165 switch (result) {
7166 case NodeProperties::kNoReceiverMaps:
7167 return NoChange();
7168 case NodeProperties::kUnreliableReceiverMaps:
7169 need_map_check = true;
7170 break;
7171 case NodeProperties::kReliableReceiverMaps:
7172 break;
7173 }
7174
7175 for (auto map : regexp_maps) {
7176 if (map->instance_type() != JS_REGEXP_TYPE) return NoChange();
7177 }
7178
7179 // Compute property access info for "exec" on {resolution}.
7180 PropertyAccessInfo ai_exec;
7181 AccessInfoFactory access_info_factory(js_heap_broker(), dependencies(),
7182 native_context(), graph()->zone());
7183 if (!access_info_factory.ComputePropertyAccessInfo(
7184 MapHandles(regexp_maps.begin(), regexp_maps.end()),
7185 factory()->exec_string(), AccessMode::kLoad, &ai_exec)) {
7186 return NoChange();
7187 }
7188 // If "exec" has been modified on {regexp}, we can't do anything.
7189 if (!ai_exec.IsDataConstant()) return NoChange();
7190 Handle<Object> exec_on_proto = ai_exec.constant();
7191 if (*exec_on_proto != *isolate()->regexp_exec_function()) return NoChange();
7192
7193 PropertyAccessBuilder access_builder(jsgraph(), js_heap_broker(),
7194 dependencies());
7195
7196 // Add proper dependencies on the {regexp}s [[Prototype]]s.
7197 Handle<JSObject> holder;
7198 if (ai_exec.holder().ToHandle(&holder)) {
7199 dependencies()->DependOnStablePrototypeChains(
7200 js_heap_broker(), native_context(), ai_exec.receiver_maps(), holder);
7201 }
7202
7203 if (need_map_check) {
7204 effect =
7205 graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone,
7206 regexp_maps, p.feedback()),
7207 regexp, effect, control);
7208 }
7209
7210 Node* context = NodeProperties::GetContextInput(node);
7211 Node* frame_state = NodeProperties::GetFrameStateInput(node);
7212 Node* search = NodeProperties::GetValueInput(node, 2);
7213 Node* search_string = effect = graph()->NewNode(
7214 simplified()->CheckString(p.feedback()), search, effect, control);
7215
7216 Node* lastIndex = effect = graph()->NewNode(
7217 simplified()->LoadField(AccessBuilder::ForJSRegExpLastIndex()), regexp,
7218 effect, control);
7219
7220 Node* lastIndexSmi = effect = graph()->NewNode(
7221 simplified()->CheckSmi(p.feedback()), lastIndex, effect, control);
7222
7223 Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(),
7224 jsgraph()->ZeroConstant(), lastIndexSmi);
7225
7226 effect = graph()->NewNode(
7227 simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()),
7228 is_positive, effect, control);
7229
7230 node->ReplaceInput(0, regexp);
7231 node->ReplaceInput(1, search_string);
7232 node->ReplaceInput(2, context);
7233 node->ReplaceInput(3, frame_state);
7234 node->ReplaceInput(4, effect);
7235 node->ReplaceInput(5, control);
7236 node->TrimInputCount(6);
7237 NodeProperties::ChangeOp(node, javascript()->RegExpTest());
7238 return Changed(node);
7239 }
7240
7241 // ES section #sec-number-constructor
ReduceNumberConstructor(Node * node)7242 Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
7243 DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
7244 CallParameters const& p = CallParametersOf(node->op());
7245
7246 if (p.arity() <= 2) {
7247 ReplaceWithValue(node, jsgraph()->ZeroConstant());
7248 }
7249
7250 // We don't have a new.target argument, so we can convert to number,
7251 // but must also convert BigInts.
7252 if (p.arity() == 3) {
7253 Node* target = NodeProperties::GetValueInput(node, 0);
7254 Node* context = NodeProperties::GetContextInput(node);
7255 Node* value = NodeProperties::GetValueInput(node, 2);
7256 Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
7257 Handle<SharedFunctionInfo> number_constructor(
7258 handle(native_context()->number_function()->shared(), isolate()));
7259
7260 const std::vector<Node*> checkpoint_parameters({
7261 jsgraph()->UndefinedConstant(), /* receiver */
7262 });
7263 int checkpoint_parameters_size =
7264 static_cast<int>(checkpoint_parameters.size());
7265
7266 Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
7267 jsgraph(), number_constructor,
7268 Builtins::kGenericConstructorLazyDeoptContinuation, target, context,
7269 checkpoint_parameters.data(), checkpoint_parameters_size,
7270 outer_frame_state, ContinuationFrameStateMode::LAZY);
7271
7272 NodeProperties::ReplaceValueInputs(node, value);
7273 NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt());
7274 NodeProperties::ReplaceFrameStateInput(node, frame_state);
7275 return Changed(node);
7276 }
7277 return NoChange();
7278 }
7279
graph() const7280 Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
7281
isolate() const7282 Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
7283
factory() const7284 Factory* JSCallReducer::factory() const { return isolate()->factory(); }
7285
global_proxy() const7286 Handle<JSGlobalProxy> JSCallReducer::global_proxy() const {
7287 return handle(JSGlobalProxy::cast(native_context()->global_proxy()),
7288 isolate());
7289 }
7290
common() const7291 CommonOperatorBuilder* JSCallReducer::common() const {
7292 return jsgraph()->common();
7293 }
7294
javascript() const7295 JSOperatorBuilder* JSCallReducer::javascript() const {
7296 return jsgraph()->javascript();
7297 }
7298
simplified() const7299 SimplifiedOperatorBuilder* JSCallReducer::simplified() const {
7300 return jsgraph()->simplified();
7301 }
7302
7303 } // namespace compiler
7304 } // namespace internal
7305 } // namespace v8
7306