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