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