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