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