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