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.h"
8 : #include "src/code-factory.h"
9 : #include "src/code-stubs.h"
10 : #include "src/compilation-dependencies.h"
11 : #include "src/compiler/access-builder.h"
12 : #include "src/compiler/js-graph.h"
13 : #include "src/compiler/linkage.h"
14 : #include "src/compiler/node-matchers.h"
15 : #include "src/compiler/simplified-operator.h"
16 : #include "src/feedback-vector-inl.h"
17 : #include "src/ic/call-optimization.h"
18 : #include "src/objects-inl.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 : namespace compiler {
23 :
24 : namespace {
25 :
26 776 : bool CanBePrimitive(Node* node) {
27 776 : switch (node->opcode()) {
28 : case IrOpcode::kJSCreate:
29 : case IrOpcode::kJSCreateArguments:
30 : case IrOpcode::kJSCreateArray:
31 : case IrOpcode::kJSCreateBoundFunction:
32 : case IrOpcode::kJSCreateClosure:
33 : case IrOpcode::kJSCreateEmptyLiteralArray:
34 : case IrOpcode::kJSCreateEmptyLiteralObject:
35 : case IrOpcode::kJSCreateIterResultObject:
36 : case IrOpcode::kJSCreateKeyValueArray:
37 : case IrOpcode::kJSCreateLiteralArray:
38 : case IrOpcode::kJSCreateLiteralObject:
39 : case IrOpcode::kJSCreateLiteralRegExp:
40 : case IrOpcode::kJSConstructForwardVarargs:
41 : case IrOpcode::kJSConstruct:
42 : case IrOpcode::kJSConstructWithArrayLike:
43 : case IrOpcode::kJSConstructWithSpread:
44 : case IrOpcode::kJSConvertReceiver:
45 : case IrOpcode::kJSGetSuperConstructor:
46 : case IrOpcode::kJSToObject:
47 : return false;
48 : case IrOpcode::kHeapConstant: {
49 : Handle<HeapObject> value = HeapObjectMatcher(node).Value();
50 : return value->IsPrimitive();
51 : }
52 : default:
53 208 : return true;
54 : }
55 : }
56 :
57 835 : bool CanBeNullOrUndefined(Node* node) {
58 634 : if (CanBePrimitive(node)) {
59 201 : switch (node->opcode()) {
60 : case IrOpcode::kToBoolean:
61 : case IrOpcode::kJSToInteger:
62 : case IrOpcode::kJSToLength:
63 : case IrOpcode::kJSToName:
64 : case IrOpcode::kJSToNumber:
65 : case IrOpcode::kJSToString:
66 : return false;
67 : case IrOpcode::kHeapConstant: {
68 : Handle<HeapObject> value = HeapObjectMatcher(node).Value();
69 : Isolate* const isolate = value->GetIsolate();
70 : return value->IsNullOrUndefined(isolate);
71 : }
72 : default:
73 123 : return true;
74 : }
75 : }
76 : return false;
77 : }
78 :
79 : } // namespace
80 :
81 30743550 : Reduction JSCallReducer::Reduce(Node* node) {
82 30743550 : switch (node->opcode()) {
83 : case IrOpcode::kJSConstruct:
84 40442 : return ReduceJSConstruct(node);
85 : case IrOpcode::kJSConstructWithArrayLike:
86 7 : return ReduceJSConstructWithArrayLike(node);
87 : case IrOpcode::kJSConstructWithSpread:
88 590 : return ReduceJSConstructWithSpread(node);
89 : case IrOpcode::kJSCall:
90 654697 : return ReduceJSCall(node);
91 : case IrOpcode::kJSCallWithArrayLike:
92 162 : return ReduceJSCallWithArrayLike(node);
93 : case IrOpcode::kJSCallWithSpread:
94 1110 : return ReduceJSCallWithSpread(node);
95 : default:
96 : break;
97 : }
98 : return NoChange();
99 : }
100 :
101 492665 : void JSCallReducer::Finalize() {
102 : // TODO(turbofan): This is not the best solution; ideally we would be able
103 : // to teach the GraphReducer about arbitrary dependencies between different
104 : // nodes, even if they don't show up in the use list of the other node.
105 : std::set<Node*> const waitlist = std::move(waitlist_);
106 985415 : for (Node* node : waitlist) {
107 85 : if (!node->IsDead()) {
108 85 : Reduction const reduction = Reduce(node);
109 85 : if (reduction.Changed()) {
110 : Node* replacement = reduction.replacement();
111 48 : if (replacement != node) {
112 0 : Replace(node, replacement);
113 : }
114 : }
115 : }
116 : }
117 492666 : }
118 :
119 : // ES6 section 22.1.1 The Array Constructor
120 452 : Reduction JSCallReducer::ReduceArrayConstructor(Node* node) {
121 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
122 226 : Node* target = NodeProperties::GetValueInput(node, 0);
123 452 : CallParameters const& p = CallParametersOf(node->op());
124 :
125 : // Turn the {node} into a {JSCreateArray} call.
126 : DCHECK_LE(2u, p.arity());
127 : Handle<AllocationSite> site;
128 226 : size_t const arity = p.arity() - 2;
129 226 : NodeProperties::ReplaceValueInput(node, target, 0);
130 226 : NodeProperties::ReplaceValueInput(node, target, 1);
131 226 : NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
132 226 : return Changed(node);
133 : }
134 :
135 : // ES6 section 19.3.1.1 Boolean ( value )
136 12 : Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) {
137 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
138 12 : CallParameters const& p = CallParametersOf(node->op());
139 :
140 : // Replace the {node} with a proper {ToBoolean} operator.
141 : DCHECK_LE(2u, p.arity());
142 : Node* value = (p.arity() == 2) ? jsgraph()->UndefinedConstant()
143 12 : : NodeProperties::GetValueInput(node, 2);
144 24 : value = graph()->NewNode(simplified()->ToBoolean(ToBooleanHint::kAny), value);
145 12 : ReplaceWithValue(node, value);
146 12 : return Replace(value);
147 : }
148 :
149 : // ES6 section 20.1.1 The Number Constructor
150 150 : Reduction JSCallReducer::ReduceNumberConstructor(Node* node) {
151 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
152 149 : CallParameters const& p = CallParametersOf(node->op());
153 :
154 : // Turn the {node} into a {JSToNumber} call.
155 : DCHECK_LE(2u, p.arity());
156 : Node* value = (p.arity() == 2) ? jsgraph()->ZeroConstant()
157 150 : : NodeProperties::GetValueInput(node, 2);
158 149 : NodeProperties::ReplaceValueInputs(node, value);
159 149 : NodeProperties::ChangeOp(node, javascript()->ToNumber());
160 149 : return Changed(node);
161 : }
162 :
163 : // ES section #sec-object-constructor
164 168 : Reduction JSCallReducer::ReduceObjectConstructor(Node* node) {
165 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
166 168 : CallParameters const& p = CallParametersOf(node->op());
167 168 : if (p.arity() < 3) return NoChange();
168 : Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2)
169 142 : : jsgraph()->UndefinedConstant();
170 :
171 : // We can fold away the Object(x) call if |x| is definitely not a primitive.
172 142 : if (CanBePrimitive(value)) {
173 135 : if (!CanBeNullOrUndefined(value)) {
174 : // Turn the {node} into a {JSToObject} call if we know that
175 : // the {value} cannot be null or undefined.
176 50 : NodeProperties::ReplaceValueInputs(node, value);
177 50 : NodeProperties::ChangeOp(node, javascript()->ToObject());
178 : return Changed(node);
179 : }
180 : } else {
181 7 : ReplaceWithValue(node, value);
182 : return Replace(node);
183 : }
184 : return NoChange();
185 : }
186 :
187 : // ES6 section 19.2.3.1 Function.prototype.apply ( thisArg, argArray )
188 624 : Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
189 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
190 506 : CallParameters const& p = CallParametersOf(node->op());
191 : size_t arity = p.arity();
192 : DCHECK_LE(2u, arity);
193 : ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
194 506 : if (arity == 2) {
195 : // Neither thisArg nor argArray was provided.
196 : convert_mode = ConvertReceiverMode::kNullOrUndefined;
197 14 : node->ReplaceInput(0, node->InputAt(1));
198 14 : node->ReplaceInput(1, jsgraph()->UndefinedConstant());
199 492 : } else if (arity == 3) {
200 : // The argArray was not provided, just remove the {target}.
201 7 : node->RemoveInput(0);
202 7 : --arity;
203 : } else {
204 485 : Node* target = NodeProperties::GetValueInput(node, 1);
205 485 : Node* this_argument = NodeProperties::GetValueInput(node, 2);
206 485 : Node* arguments_list = NodeProperties::GetValueInput(node, 3);
207 485 : Node* context = NodeProperties::GetContextInput(node);
208 485 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
209 485 : Node* effect = NodeProperties::GetEffectInput(node);
210 485 : Node* control = NodeProperties::GetControlInput(node);
211 :
212 : // If {arguments_list} cannot be null or undefined, we don't need
213 : // to expand this {node} to control-flow.
214 485 : if (!CanBeNullOrUndefined(arguments_list)) {
215 : // Massage the value inputs appropriately.
216 433 : node->ReplaceInput(0, target);
217 433 : node->ReplaceInput(1, this_argument);
218 433 : node->ReplaceInput(2, arguments_list);
219 433 : while (arity-- > 3) node->RemoveInput(3);
220 :
221 : // Morph the {node} to a {JSCallWithArrayLike}.
222 : NodeProperties::ChangeOp(node,
223 433 : javascript()->CallWithArrayLike(p.frequency()));
224 433 : Reduction const reduction = ReduceJSCallWithArrayLike(node);
225 433 : return reduction.Changed() ? reduction : Changed(node);
226 : } else {
227 : // Check whether {arguments_list} is null.
228 : Node* check_null =
229 : graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
230 104 : jsgraph()->NullConstant());
231 : control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
232 52 : check_null, control);
233 52 : Node* if_null = graph()->NewNode(common()->IfTrue(), control);
234 52 : control = graph()->NewNode(common()->IfFalse(), control);
235 :
236 : // Check whether {arguments_list} is undefined.
237 : Node* check_undefined =
238 : graph()->NewNode(simplified()->ReferenceEqual(), arguments_list,
239 104 : jsgraph()->UndefinedConstant());
240 : control = graph()->NewNode(common()->Branch(BranchHint::kFalse),
241 52 : check_undefined, control);
242 52 : Node* if_undefined = graph()->NewNode(common()->IfTrue(), control);
243 52 : control = graph()->NewNode(common()->IfFalse(), control);
244 :
245 : // Lower to {JSCallWithArrayLike} if {arguments_list} is neither null
246 : // nor undefined.
247 : Node* effect0 = effect;
248 : Node* control0 = control;
249 : Node* value0 = effect0 = control0 = graph()->NewNode(
250 : javascript()->CallWithArrayLike(p.frequency()), target, this_argument,
251 52 : arguments_list, context, frame_state, effect0, control0);
252 :
253 : // Lower to {JSCall} if {arguments_list} is either null or undefined.
254 : Node* effect1 = effect;
255 : Node* control1 =
256 52 : graph()->NewNode(common()->Merge(2), if_null, if_undefined);
257 : Node* value1 = effect1 = control1 =
258 : graph()->NewNode(javascript()->Call(2), target, this_argument,
259 156 : context, frame_state, effect1, control1);
260 :
261 : // Rewire potential exception edges.
262 52 : Node* if_exception = nullptr;
263 52 : if (NodeProperties::IsExceptionalCall(node, &if_exception)) {
264 : // Create appropriate {IfException} and {IfSuccess} nodes.
265 : Node* if_exception0 =
266 7 : graph()->NewNode(common()->IfException(), control0, effect0);
267 7 : control0 = graph()->NewNode(common()->IfSuccess(), control0);
268 : Node* if_exception1 =
269 7 : graph()->NewNode(common()->IfException(), control1, effect1);
270 7 : control1 = graph()->NewNode(common()->IfSuccess(), control1);
271 :
272 : // Join the exception edges.
273 : Node* merge =
274 7 : graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
275 : Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
276 7 : if_exception1, merge);
277 : Node* phi =
278 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
279 7 : if_exception0, if_exception1, merge);
280 59 : ReplaceWithValue(if_exception, phi, ephi, merge);
281 : }
282 :
283 : // Join control paths.
284 52 : control = graph()->NewNode(common()->Merge(2), control0, control1);
285 : effect =
286 52 : graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control);
287 : Node* value =
288 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
289 52 : value0, value1, control);
290 : ReplaceWithValue(node, value, effect, control);
291 : return Replace(value);
292 : }
293 : }
294 : // Change {node} to the new {JSCall} operator.
295 : NodeProperties::ChangeOp(
296 : node,
297 42 : javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
298 : // Try to further reduce the JSCall {node}.
299 21 : Reduction const reduction = ReduceJSCall(node);
300 21 : return reduction.Changed() ? reduction : Changed(node);
301 : }
302 :
303 : // ES section #sec-function.prototype.bind
304 282 : Reduction JSCallReducer::ReduceFunctionPrototypeBind(Node* node) {
305 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
306 : // Value inputs to the {node} are as follows:
307 : //
308 : // - target, which is Function.prototype.bind JSFunction
309 : // - receiver, which is the [[BoundTargetFunction]]
310 : // - bound_this (optional), which is the [[BoundThis]]
311 : // - and all the remaining value inouts are [[BoundArguments]]
312 94 : Node* receiver = NodeProperties::GetValueInput(node, 1);
313 94 : Node* bound_this = (node->op()->ValueInputCount() < 3)
314 : ? jsgraph()->UndefinedConstant()
315 94 : : NodeProperties::GetValueInput(node, 2);
316 94 : Node* context = NodeProperties::GetContextInput(node);
317 94 : Node* effect = NodeProperties::GetEffectInput(node);
318 94 : Node* control = NodeProperties::GetControlInput(node);
319 :
320 : // Ensure that the {receiver} is known to be a JSBoundFunction or
321 : // a JSFunction with the same [[Prototype]], and all maps we've
322 : // seen for the {receiver} so far indicate that {receiver} is
323 : // definitely a constructor or not a constructor.
324 : ZoneHandleSet<Map> receiver_maps;
325 : NodeProperties::InferReceiverMapsResult result =
326 94 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
327 94 : if (result == NodeProperties::kNoReceiverMaps) return NoChange();
328 : DCHECK_NE(0, receiver_maps.size());
329 : bool const is_constructor = receiver_maps[0]->is_constructor();
330 : Handle<Object> const prototype(receiver_maps[0]->prototype(), isolate());
331 188 : for (Handle<Map> const receiver_map : receiver_maps) {
332 : // Check for consistency among the {receiver_maps}.
333 : STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE);
334 94 : if (receiver_map->prototype() != *prototype) return NoChange();
335 94 : if (receiver_map->is_constructor() != is_constructor) return NoChange();
336 94 : if (receiver_map->instance_type() < FIRST_FUNCTION_TYPE) return NoChange();
337 :
338 : // Disallow binding of slow-mode functions. We need to figure out
339 : // whether the length and name property are in the original state.
340 94 : if (receiver_map->is_dictionary_map()) return NoChange();
341 :
342 : // Check whether the length and name properties are still present
343 : // as AccessorInfo objects. In that case, their values can be
344 : // recomputed even if the actual value of the object changes.
345 : // This mirrors the checks done in builtins-function-gen.cc at
346 : // runtime otherwise.
347 : Handle<DescriptorArray> descriptors(receiver_map->instance_descriptors(),
348 : isolate());
349 94 : if (descriptors->length() < 2) return NoChange();
350 94 : if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) !=
351 94 : isolate()->heap()->length_string()) {
352 : return NoChange();
353 : }
354 94 : if (!descriptors->GetValue(JSFunction::kLengthDescriptorIndex)
355 : ->IsAccessorInfo()) {
356 : return NoChange();
357 : }
358 94 : if (descriptors->GetKey(JSFunction::kNameDescriptorIndex) !=
359 94 : isolate()->heap()->name_string()) {
360 : return NoChange();
361 : }
362 94 : if (!descriptors->GetValue(JSFunction::kNameDescriptorIndex)
363 : ->IsAccessorInfo()) {
364 : return NoChange();
365 : }
366 : }
367 :
368 : // Setup the map for the resulting JSBoundFunction with the
369 : // correct instance {prototype}.
370 : Handle<Map> map(
371 : is_constructor
372 : ? native_context()->bound_function_with_constructor_map()
373 : : native_context()->bound_function_without_constructor_map(),
374 94 : isolate());
375 94 : if (map->prototype() != *prototype) {
376 0 : map = Map::TransitionToPrototype(map, prototype);
377 : }
378 :
379 : // Make sure we can rely on the {receiver_maps}.
380 94 : if (result == NodeProperties::kUnreliableReceiverMaps) {
381 : effect = graph()->NewNode(
382 : simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
383 0 : effect, control);
384 : }
385 :
386 : // Replace the {node} with a JSCreateBoundFunction.
387 282 : int const arity = std::max(0, node->op()->ValueInputCount() - 3);
388 94 : int const input_count = 2 + arity + 3;
389 188 : Node** inputs = graph()->zone()->NewArray<Node*>(input_count);
390 94 : inputs[0] = receiver;
391 94 : inputs[1] = bound_this;
392 161 : for (int i = 0; i < arity; ++i) {
393 67 : inputs[2 + i] = NodeProperties::GetValueInput(node, 3 + i);
394 : }
395 94 : inputs[2 + arity + 0] = context;
396 94 : inputs[2 + arity + 1] = effect;
397 94 : inputs[2 + arity + 2] = control;
398 : Node* value = effect = graph()->NewNode(
399 188 : javascript()->CreateBoundFunction(arity, map), input_count, inputs);
400 94 : ReplaceWithValue(node, value, effect, control);
401 : return Replace(value);
402 : }
403 :
404 : // ES6 section 19.2.3.3 Function.prototype.call (thisArg, ...args)
405 2277 : Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) {
406 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
407 2254 : CallParameters const& p = CallParametersOf(node->op());
408 : Handle<JSFunction> call = Handle<JSFunction>::cast(
409 1127 : HeapObjectMatcher(NodeProperties::GetValueInput(node, 0)).Value());
410 : // Change context of {node} to the Function.prototype.call context,
411 : // to ensure any exception is thrown in the correct context.
412 : NodeProperties::ReplaceContextInput(
413 1127 : node, jsgraph()->HeapConstant(handle(call->context(), isolate())));
414 : // Remove the target from {node} and use the receiver as target instead, and
415 : // the thisArg becomes the new target. If thisArg was not provided, insert
416 : // undefined instead.
417 : size_t arity = p.arity();
418 : DCHECK_LE(2u, arity);
419 : ConvertReceiverMode convert_mode;
420 1127 : if (arity == 2) {
421 : // The thisArg was not provided, use undefined as receiver.
422 : convert_mode = ConvertReceiverMode::kNullOrUndefined;
423 23 : node->ReplaceInput(0, node->InputAt(1));
424 23 : node->ReplaceInput(1, jsgraph()->UndefinedConstant());
425 : } else {
426 : // Just remove the target, which is the first value input.
427 : convert_mode = ConvertReceiverMode::kAny;
428 1104 : node->RemoveInput(0);
429 1104 : --arity;
430 : }
431 : NodeProperties::ChangeOp(
432 : node,
433 2254 : javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode));
434 : // Try to further reduce the JSCall {node}.
435 1127 : Reduction const reduction = ReduceJSCall(node);
436 1127 : return reduction.Changed() ? reduction : Changed(node);
437 : }
438 :
439 : // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V)
440 3316 : Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) {
441 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
442 1658 : Node* receiver = NodeProperties::GetValueInput(node, 1);
443 1658 : Node* object = (node->op()->ValueInputCount() >= 3)
444 : ? NodeProperties::GetValueInput(node, 2)
445 1658 : : jsgraph()->UndefinedConstant();
446 1658 : Node* context = NodeProperties::GetContextInput(node);
447 1658 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
448 1658 : Node* effect = NodeProperties::GetEffectInput(node);
449 1658 : Node* control = NodeProperties::GetControlInput(node);
450 :
451 : // TODO(turbofan): If JSOrdinaryToInstance raises an exception, the
452 : // stack trace doesn't contain the @@hasInstance call; we have the
453 : // corresponding bug in the baseline case. Some massaging of the frame
454 : // state would be necessary here.
455 :
456 : // Morph this {node} into a JSOrdinaryHasInstance node.
457 1658 : node->ReplaceInput(0, receiver);
458 1658 : node->ReplaceInput(1, object);
459 1658 : node->ReplaceInput(2, context);
460 1658 : node->ReplaceInput(3, frame_state);
461 1658 : node->ReplaceInput(4, effect);
462 1658 : node->ReplaceInput(5, control);
463 1658 : node->TrimInputCount(6);
464 1658 : NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance());
465 1658 : return Changed(node);
466 : }
467 :
468 303 : Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) {
469 185 : Node* effect = NodeProperties::GetEffectInput(node);
470 :
471 : // Try to determine the {object} map.
472 : ZoneHandleSet<Map> object_maps;
473 : NodeProperties::InferReceiverMapsResult result =
474 185 : NodeProperties::InferReceiverMaps(object, effect, &object_maps);
475 185 : if (result != NodeProperties::kNoReceiverMaps) {
476 : Handle<Map> candidate_map = object_maps[0];
477 : Handle<Object> candidate_prototype(candidate_map->prototype(), isolate());
478 :
479 : // Check if we can constant-fold the {candidate_prototype}.
480 420 : for (size_t i = 0; i < object_maps.size(); ++i) {
481 : Handle<Map> object_map = object_maps[i];
482 216 : if (object_map->IsSpecialReceiverMap() ||
483 216 : object_map->has_hidden_prototype() ||
484 : object_map->prototype() != *candidate_prototype) {
485 : // We exclude special receivers, like JSProxy or API objects that
486 : // might require access checks here; we also don't want to deal
487 : // with hidden prototypes at this point.
488 : return NoChange();
489 : }
490 : // The above check also excludes maps for primitive values, which is
491 : // important because we are not applying [[ToObject]] here as expected.
492 : DCHECK(!object_map->IsPrimitiveMap() && object_map->IsJSReceiverMap());
493 130 : if (result == NodeProperties::kUnreliableReceiverMaps &&
494 : !object_map->is_stable()) {
495 : return NoChange();
496 : }
497 : }
498 90 : if (result == NodeProperties::kUnreliableReceiverMaps) {
499 84 : for (size_t i = 0; i < object_maps.size(); ++i) {
500 28 : dependencies()->AssumeMapStable(object_maps[i]);
501 : }
502 : }
503 90 : Node* value = jsgraph()->Constant(candidate_prototype);
504 90 : ReplaceWithValue(node, value);
505 : return Replace(value);
506 : }
507 :
508 : return NoChange();
509 : }
510 :
511 : // ES6 section 19.1.2.11 Object.getPrototypeOf ( O )
512 78 : Reduction JSCallReducer::ReduceObjectGetPrototypeOf(Node* node) {
513 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
514 78 : Node* object = (node->op()->ValueInputCount() >= 3)
515 : ? NodeProperties::GetValueInput(node, 2)
516 78 : : jsgraph()->UndefinedConstant();
517 78 : return ReduceObjectGetPrototype(node, object);
518 : }
519 :
520 : // ES6 section B.2.2.1.1 get Object.prototype.__proto__
521 92 : Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) {
522 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
523 92 : Node* receiver = NodeProperties::GetValueInput(node, 1);
524 92 : return ReduceObjectGetPrototype(node, receiver);
525 : }
526 :
527 : // ES #sec-object.prototype.hasownproperty
528 609 : Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) {
529 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
530 465 : CallParameters const& params = CallParametersOf(node->op());
531 465 : int const argc = static_cast<int>(params.arity() - 2);
532 465 : Node* receiver = NodeProperties::GetValueInput(node, 1);
533 465 : Node* name = (argc >= 1) ? NodeProperties::GetValueInput(node, 2)
534 473 : : jsgraph()->UndefinedConstant();
535 465 : Node* effect = NodeProperties::GetEffectInput(node);
536 465 : Node* control = NodeProperties::GetControlInput(node);
537 :
538 : // We can optimize a call to Object.prototype.hasOwnProperty if it's being
539 : // used inside a fast-mode for..in, so for code like this:
540 : //
541 : // for (name in receiver) {
542 : // if (receiver.hasOwnProperty(name)) {
543 : // ...
544 : // }
545 : // }
546 : //
547 : // If the for..in is in fast-mode, we know that the {receiver} has {name}
548 : // as own property, otherwise the enumeration wouldn't include it. The graph
549 : // constructed by the BytecodeGraphBuilder in this case looks like this:
550 :
551 : // receiver
552 : // ^ ^
553 : // | |
554 : // | +-+
555 : // | |
556 : // | JSToObject
557 : // | ^
558 : // | |
559 : // | JSForInNext
560 : // | ^
561 : // +----+ |
562 : // | |
563 : // JSCall[hasOwnProperty]
564 :
565 : // We can constant-fold the {node} to True in this case, and insert
566 : // a (potentially redundant) map check to guard the fact that the
567 : // {receiver} map didn't change since the dominating JSForInNext. This
568 : // map check is only necessary when TurboFan cannot prove that there
569 : // is no observable side effect between the {JSForInNext} and the
570 : // {JSCall} to Object.prototype.hasOwnProperty.
571 : //
572 : // Also note that it's safe to look through the {JSToObject}, since the
573 : // Object.prototype.hasOwnProperty does an implicit ToObject anyway, and
574 : // these operations are not observable.
575 465 : if (name->opcode() == IrOpcode::kJSForInNext) {
576 214 : ForInMode const mode = ForInModeOf(name->op());
577 214 : if (mode != ForInMode::kGeneric) {
578 428 : Node* object = NodeProperties::GetValueInput(name, 0);
579 214 : Node* cache_type = NodeProperties::GetValueInput(name, 2);
580 214 : if (object->opcode() == IrOpcode::kJSToObject) {
581 200 : object = NodeProperties::GetValueInput(object, 0);
582 : }
583 214 : if (object == receiver) {
584 : // No need to repeat the map check if we can prove that there's no
585 : // observable side effect between {effect} and {name].
586 136 : if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) {
587 : Node* receiver_map = effect =
588 : graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
589 0 : receiver, effect, control);
590 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
591 0 : receiver_map, cache_type);
592 : effect = graph()->NewNode(
593 : simplified()->CheckIf(DeoptimizeReason::kNoReason), check, effect,
594 0 : control);
595 : }
596 136 : Node* value = jsgraph()->TrueConstant();
597 136 : ReplaceWithValue(node, value, effect, control);
598 : return Replace(value);
599 : }
600 : }
601 : }
602 :
603 : return NoChange();
604 : }
605 :
606 : // ES #sec-object.prototype.isprototypeof
607 175 : Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node) {
608 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
609 63 : Node* receiver = NodeProperties::GetValueInput(node, 1);
610 63 : Node* value = node->op()->ValueInputCount() > 2
611 : ? NodeProperties::GetValueInput(node, 2)
612 63 : : jsgraph()->UndefinedConstant();
613 63 : Node* effect = NodeProperties::GetEffectInput(node);
614 :
615 : // Ensure that the {receiver} is known to be a JSReceiver (so that
616 : // the ToObject step of Object.prototype.isPrototypeOf is a no-op).
617 : ZoneHandleSet<Map> receiver_maps;
618 : NodeProperties::InferReceiverMapsResult result =
619 63 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
620 63 : if (result == NodeProperties::kNoReceiverMaps) return NoChange();
621 147 : for (size_t i = 0; i < receiver_maps.size(); ++i) {
622 49 : if (!receiver_maps[i]->IsJSReceiverMap()) return NoChange();
623 : }
624 :
625 : // We don't check whether {value} is a proper JSReceiver here explicitly,
626 : // and don't explicitly rule out Primitive {value}s, since all of them
627 : // have null as their prototype, so the prototype chain walk inside the
628 : // JSHasInPrototypeChain operator immediately aborts and yields false.
629 49 : NodeProperties::ReplaceValueInput(node, value, 0);
630 49 : NodeProperties::ReplaceValueInput(node, receiver, 1);
631 196 : for (int i = node->op()->ValueInputCount(); i-- > 2;) {
632 49 : node->RemoveInput(i);
633 : }
634 49 : NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain());
635 : return Changed(node);
636 : }
637 :
638 : // ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList )
639 241 : Reduction JSCallReducer::ReduceReflectApply(Node* node) {
640 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
641 220 : CallParameters const& p = CallParametersOf(node->op());
642 220 : int arity = static_cast<int>(p.arity() - 2);
643 : DCHECK_LE(0, arity);
644 : // Massage value inputs appropriately.
645 220 : node->RemoveInput(0);
646 220 : node->RemoveInput(0);
647 461 : while (arity < 3) {
648 42 : node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
649 : }
650 227 : while (arity-- > 3) {
651 7 : node->RemoveInput(arity);
652 : }
653 : NodeProperties::ChangeOp(node,
654 220 : javascript()->CallWithArrayLike(p.frequency()));
655 220 : Reduction const reduction = ReduceJSCallWithArrayLike(node);
656 220 : return reduction.Changed() ? reduction : Changed(node);
657 : }
658 :
659 : // ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] )
660 218 : Reduction JSCallReducer::ReduceReflectConstruct(Node* node) {
661 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
662 211 : CallParameters const& p = CallParametersOf(node->op());
663 211 : int arity = static_cast<int>(p.arity() - 2);
664 : DCHECK_LE(0, arity);
665 : // Massage value inputs appropriately.
666 211 : node->RemoveInput(0);
667 211 : node->RemoveInput(0);
668 429 : while (arity < 2) {
669 14 : node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant());
670 : }
671 211 : if (arity < 3) {
672 72 : node->InsertInput(graph()->zone(), arity++, node->InputAt(0));
673 : }
674 218 : while (arity-- > 3) {
675 7 : node->RemoveInput(arity);
676 : }
677 : NodeProperties::ChangeOp(node,
678 211 : javascript()->ConstructWithArrayLike(p.frequency()));
679 211 : Reduction const reduction = ReduceJSConstructWithArrayLike(node);
680 211 : return reduction.Changed() ? reduction : Changed(node);
681 : }
682 :
683 : // ES6 section 26.1.7 Reflect.getPrototypeOf ( target )
684 15 : Reduction JSCallReducer::ReduceReflectGetPrototypeOf(Node* node) {
685 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
686 15 : Node* target = (node->op()->ValueInputCount() >= 3)
687 : ? NodeProperties::GetValueInput(node, 2)
688 15 : : jsgraph()->UndefinedConstant();
689 15 : return ReduceObjectGetPrototype(node, target);
690 : }
691 :
692 : // ES section #sec-reflect.has
693 147 : Reduction JSCallReducer::ReduceReflectHas(Node* node) {
694 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
695 35 : CallParameters const& p = CallParametersOf(node->op());
696 35 : int arity = static_cast<int>(p.arity() - 2);
697 : DCHECK_LE(0, arity);
698 : Node* target = (arity >= 1) ? NodeProperties::GetValueInput(node, 2)
699 49 : : jsgraph()->UndefinedConstant();
700 : Node* key = (arity >= 2) ? NodeProperties::GetValueInput(node, 3)
701 63 : : jsgraph()->UndefinedConstant();
702 35 : Node* context = NodeProperties::GetContextInput(node);
703 35 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
704 35 : Node* effect = NodeProperties::GetEffectInput(node);
705 35 : Node* control = NodeProperties::GetControlInput(node);
706 :
707 : // Check whether {target} is a JSReceiver.
708 35 : Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target);
709 : Node* branch =
710 35 : graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
711 :
712 : // Throw an appropriate TypeError if the {target} is not a JSReceiver.
713 35 : Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
714 : Node* efalse = effect;
715 : {
716 : if_false = efalse = graph()->NewNode(
717 : javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
718 : jsgraph()->Constant(MessageTemplate::kCalledOnNonObject),
719 : jsgraph()->HeapConstant(
720 : factory()->NewStringFromAsciiChecked("Reflect.has")),
721 175 : context, frame_state, efalse, if_false);
722 : }
723 :
724 : // Otherwise just use the existing {JSHasProperty} logic.
725 35 : Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
726 : Node* etrue = effect;
727 : Node* vtrue;
728 : {
729 : vtrue = etrue = if_true =
730 : graph()->NewNode(javascript()->HasProperty(), key, target, context,
731 35 : frame_state, etrue, if_true);
732 : }
733 :
734 : // Rewire potential exception edges.
735 35 : Node* on_exception = nullptr;
736 35 : if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
737 : // Create appropriate {IfException} and {IfSuccess} nodes.
738 14 : Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true);
739 14 : if_true = graph()->NewNode(common()->IfSuccess(), if_true);
740 14 : Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false);
741 14 : if_false = graph()->NewNode(common()->IfSuccess(), if_false);
742 :
743 : // Join the exception edges.
744 14 : Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse);
745 : Node* ephi =
746 14 : graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge);
747 : Node* phi =
748 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
749 14 : extrue, exfalse, merge);
750 49 : ReplaceWithValue(on_exception, phi, ephi, merge);
751 : }
752 :
753 : // Connect the throwing path to end.
754 35 : if_false = graph()->NewNode(common()->Throw(), efalse, if_false);
755 35 : NodeProperties::MergeControlToEnd(graph(), common(), if_false);
756 :
757 : // Continue on the regular path.
758 : ReplaceWithValue(node, vtrue, etrue, if_true);
759 35 : return Changed(vtrue);
760 : }
761 :
762 470 : bool CanInlineArrayIteratingBuiltin(Handle<Map> receiver_map) {
763 : Isolate* const isolate = receiver_map->GetIsolate();
764 470 : if (!receiver_map->prototype()->IsJSArray()) return false;
765 : Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
766 : isolate);
767 470 : return receiver_map->instance_type() == JS_ARRAY_TYPE &&
768 470 : IsFastElementsKind(receiver_map->elements_kind()) &&
769 470 : (!receiver_map->is_prototype_map() || receiver_map->is_stable()) &&
770 1404 : isolate->IsFastArrayConstructorPrototypeChainIntact() &&
771 934 : isolate->IsAnyInitialArrayPrototype(receiver_prototype);
772 : }
773 :
774 267 : Reduction JSCallReducer::ReduceArrayForEach(Handle<JSFunction> function,
775 3227 : Node* node) {
776 267 : if (!FLAG_turbo_inline_array_builtins) return NoChange();
777 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
778 267 : Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
779 267 : Node* effect = NodeProperties::GetEffectInput(node);
780 267 : Node* control = NodeProperties::GetControlInput(node);
781 267 : Node* context = NodeProperties::GetContextInput(node);
782 267 : CallParameters const& p = CallParametersOf(node->op());
783 :
784 : // Try to determine the {receiver} map.
785 267 : Node* receiver = NodeProperties::GetValueInput(node, 1);
786 267 : Node* fncallback = node->op()->ValueInputCount() > 2
787 : ? NodeProperties::GetValueInput(node, 2)
788 267 : : jsgraph()->UndefinedConstant();
789 267 : Node* this_arg = node->op()->ValueInputCount() > 3
790 : ? NodeProperties::GetValueInput(node, 3)
791 534 : : jsgraph()->UndefinedConstant();
792 : ZoneHandleSet<Map> receiver_maps;
793 : NodeProperties::InferReceiverMapsResult result =
794 267 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
795 267 : if (result != NodeProperties::kReliableReceiverMaps) {
796 : return NoChange();
797 : }
798 258 : if (receiver_maps.size() == 0) return NoChange();
799 :
800 : ElementsKind kind = IsDoubleElementsKind(receiver_maps[0]->elements_kind())
801 : ? PACKED_DOUBLE_ELEMENTS
802 258 : : PACKED_ELEMENTS;
803 533 : for (Handle<Map> receiver_map : receiver_maps) {
804 : ElementsKind next_kind = receiver_map->elements_kind();
805 297 : if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
806 : return NoChange();
807 : }
808 873 : if (!IsFastElementsKind(next_kind) ||
809 24 : (IsDoubleElementsKind(next_kind) && IsHoleyElementsKind(next_kind))) {
810 : return NoChange();
811 : }
812 283 : if (IsDoubleElementsKind(kind) != IsDoubleElementsKind(next_kind)) {
813 : return NoChange();
814 : }
815 275 : if (IsHoleyElementsKind(next_kind)) {
816 : kind = HOLEY_ELEMENTS;
817 : }
818 : }
819 :
820 : // Install code dependencies on the {receiver} prototype maps and the
821 : // global array protector cell.
822 : dependencies()->AssumePropertyCell(factory()->array_protector());
823 :
824 236 : Node* k = jsgraph()->ZeroConstant();
825 :
826 : Node* original_length = effect = graph()->NewNode(
827 : simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
828 708 : receiver, effect, control);
829 :
830 : std::vector<Node*> checkpoint_params(
831 472 : {receiver, fncallback, this_arg, k, original_length});
832 472 : const int stack_parameters = static_cast<int>(checkpoint_params.size());
833 :
834 : // Check whether the given callback function is callable. Note that this has
835 : // to happen outside the loop to make sure we also throw on empty arrays.
836 236 : Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
837 : Node* check_branch =
838 236 : graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
839 236 : Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
840 : Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
841 : jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
842 236 : node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
843 236 : outer_frame_state, ContinuationFrameStateMode::LAZY);
844 : Node* check_throw = check_fail = graph()->NewNode(
845 : javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
846 : jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback,
847 472 : context, check_frame_state, effect, check_fail);
848 236 : control = graph()->NewNode(common()->IfTrue(), check_branch);
849 :
850 : // Start the loop.
851 236 : Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
852 : Node* eloop = effect =
853 236 : graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
854 : Node* vloop = k = graph()->NewNode(
855 236 : common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
856 236 : checkpoint_params[3] = k;
857 :
858 : control = loop;
859 : effect = eloop;
860 :
861 : Node* continue_test =
862 236 : graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
863 : Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
864 236 : continue_test, control);
865 :
866 236 : Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
867 236 : Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
868 : control = if_true;
869 :
870 : Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
871 : jsgraph(), function, Builtins::kArrayForEachLoopEagerDeoptContinuation,
872 236 : node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
873 236 : outer_frame_state, ContinuationFrameStateMode::EAGER);
874 :
875 : effect =
876 236 : graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
877 :
878 : // Make sure the map hasn't changed during the iteration
879 : effect = graph()->NewNode(
880 : simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
881 472 : effect, control);
882 :
883 : // Make sure that the access is still in bounds, since the callback could have
884 : // changed the array's size.
885 : Node* length = effect = graph()->NewNode(
886 : simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)),
887 708 : receiver, effect, control);
888 : k = effect =
889 236 : graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control);
890 :
891 : // Reload the elements pointer before calling the callback, since the previous
892 : // callback might have resized the array causing the elements buffer to be
893 : // re-allocated.
894 : Node* elements = effect = graph()->NewNode(
895 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
896 708 : effect, control);
897 :
898 : Node* element = effect = graph()->NewNode(
899 : simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
900 708 : elements, k, effect, control);
901 :
902 : Node* next_k =
903 472 : graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->Constant(1));
904 236 : checkpoint_params[3] = next_k;
905 :
906 : Node* hole_true = nullptr;
907 : Node* hole_false = nullptr;
908 : Node* effect_true = effect;
909 :
910 236 : if (IsHoleyElementsKind(kind)) {
911 : // Holey elements kind require a hole check and skipping of the element in
912 : // the case of a hole.
913 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), element,
914 70 : jsgraph()->TheHoleConstant());
915 : Node* branch =
916 35 : graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
917 35 : hole_true = graph()->NewNode(common()->IfTrue(), branch);
918 35 : hole_false = graph()->NewNode(common()->IfFalse(), branch);
919 : control = hole_false;
920 :
921 : // The contract is that we don't leak "the hole" into "user JavaScript",
922 : // so we must rename the {element} here to explicitly exclude "the hole"
923 : // from the type of {element}.
924 : element = graph()->NewNode(common()->TypeGuard(Type::NonInternal()),
925 35 : element, control);
926 : }
927 :
928 : frame_state = CreateJavaScriptBuiltinContinuationFrameState(
929 : jsgraph(), function, Builtins::kArrayForEachLoopLazyDeoptContinuation,
930 236 : node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
931 236 : outer_frame_state, ContinuationFrameStateMode::LAZY);
932 :
933 : control = effect = graph()->NewNode(
934 : javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
935 708 : receiver, context, frame_state, effect, control);
936 :
937 : // Rewire potential exception edges.
938 236 : Node* on_exception = nullptr;
939 236 : if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
940 : // Create appropriate {IfException} and {IfSuccess} nodes.
941 : Node* if_exception0 =
942 33 : graph()->NewNode(common()->IfException(), check_throw, check_fail);
943 33 : check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
944 : Node* if_exception1 =
945 33 : graph()->NewNode(common()->IfException(), effect, control);
946 33 : control = graph()->NewNode(common()->IfSuccess(), control);
947 :
948 : // Join the exception edges.
949 : Node* merge =
950 33 : graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
951 : Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
952 33 : if_exception1, merge);
953 : Node* phi =
954 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
955 33 : if_exception0, if_exception1, merge);
956 269 : ReplaceWithValue(on_exception, phi, ephi, merge);
957 : }
958 :
959 236 : if (IsHoleyElementsKind(kind)) {
960 : Node* after_call_control = control;
961 : Node* after_call_effect = effect;
962 : control = hole_true;
963 : effect = effect_true;
964 :
965 35 : control = graph()->NewNode(common()->Merge(2), control, after_call_control);
966 : effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect,
967 35 : control);
968 : }
969 :
970 : k = next_k;
971 :
972 236 : loop->ReplaceInput(1, control);
973 236 : vloop->ReplaceInput(1, k);
974 236 : eloop->ReplaceInput(1, effect);
975 :
976 : control = if_false;
977 : effect = eloop;
978 :
979 : // The above %ThrowTypeError runtime call is an unconditional throw, making
980 : // it impossible to return a successful completion in this case. We simply
981 : // connect the successful completion to the graph end.
982 : Node* terminate =
983 236 : graph()->NewNode(common()->Throw(), check_throw, check_fail);
984 236 : NodeProperties::MergeControlToEnd(graph(), common(), terminate);
985 :
986 236 : ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control);
987 236 : return Replace(jsgraph()->UndefinedConstant());
988 : }
989 :
990 184 : Reduction JSCallReducer::ReduceArrayMap(Handle<JSFunction> function,
991 2120 : Node* node) {
992 184 : if (!FLAG_turbo_inline_array_builtins) return NoChange();
993 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
994 184 : Node* outer_frame_state = NodeProperties::GetFrameStateInput(node);
995 184 : Node* effect = NodeProperties::GetEffectInput(node);
996 184 : Node* control = NodeProperties::GetControlInput(node);
997 184 : Node* context = NodeProperties::GetContextInput(node);
998 184 : CallParameters const& p = CallParametersOf(node->op());
999 :
1000 : // Try to determine the {receiver} map.
1001 184 : Node* receiver = NodeProperties::GetValueInput(node, 1);
1002 184 : Node* fncallback = node->op()->ValueInputCount() > 2
1003 : ? NodeProperties::GetValueInput(node, 2)
1004 184 : : jsgraph()->UndefinedConstant();
1005 184 : Node* this_arg = node->op()->ValueInputCount() > 3
1006 : ? NodeProperties::GetValueInput(node, 3)
1007 368 : : jsgraph()->UndefinedConstant();
1008 : ZoneHandleSet<Map> receiver_maps;
1009 : NodeProperties::InferReceiverMapsResult result =
1010 184 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
1011 184 : if (result != NodeProperties::kReliableReceiverMaps) {
1012 : return NoChange();
1013 : }
1014 :
1015 : // Ensure that any changes to the Array species constructor cause deopt.
1016 177 : if (!isolate()->IsArraySpeciesLookupChainIntact()) return NoChange();
1017 :
1018 177 : if (receiver_maps.size() == 0) return NoChange();
1019 :
1020 : const ElementsKind kind = receiver_maps[0]->elements_kind();
1021 :
1022 : // TODO(danno): Handle holey elements kinds.
1023 177 : if (!IsFastPackedElementsKind(kind)) {
1024 : return NoChange();
1025 : }
1026 :
1027 346 : for (Handle<Map> receiver_map : receiver_maps) {
1028 173 : if (!CanInlineArrayIteratingBuiltin(receiver_map)) {
1029 : return NoChange();
1030 : }
1031 : // We can handle different maps, as long as their elements kind are the
1032 : // same.
1033 173 : if (receiver_map->elements_kind() != kind) {
1034 : return NoChange();
1035 : }
1036 : }
1037 :
1038 : dependencies()->AssumePropertyCell(factory()->species_protector());
1039 :
1040 : Handle<JSFunction> handle_constructor(
1041 : JSFunction::cast(
1042 : native_context()->GetInitialJSArrayMap(kind)->GetConstructor()),
1043 173 : isolate());
1044 173 : Node* array_constructor = jsgraph()->HeapConstant(handle_constructor);
1045 :
1046 :
1047 173 : Node* k = jsgraph()->ZeroConstant();
1048 :
1049 : // Make sure the map hasn't changed before we construct the output array.
1050 : effect = graph()->NewNode(
1051 : simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
1052 346 : effect, control);
1053 :
1054 : Node* original_length = effect = graph()->NewNode(
1055 : simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
1056 519 : effect, control);
1057 :
1058 : // This array should be HOLEY_SMI_ELEMENTS because of the non-zero length.
1059 : // Even though {JSCreateArray} is not marked as {kNoThrow}, we can elide the
1060 : // exceptional projections because it cannot throw with the given parameters.
1061 : Node* a = control = effect = graph()->NewNode(
1062 : javascript()->CreateArray(1, Handle<AllocationSite>::null()),
1063 : array_constructor, array_constructor, original_length, context,
1064 173 : outer_frame_state, effect, control);
1065 :
1066 : std::vector<Node*> checkpoint_params(
1067 346 : {receiver, fncallback, this_arg, a, k, original_length});
1068 346 : const int stack_parameters = static_cast<int>(checkpoint_params.size());
1069 :
1070 : // Check whether the given callback function is callable. Note that this has
1071 : // to happen outside the loop to make sure we also throw on empty arrays.
1072 173 : Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback);
1073 : Node* check_branch =
1074 173 : graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1075 173 : Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
1076 : Node* check_frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1077 : jsgraph(), function, Builtins::kArrayMapLoopLazyDeoptContinuation,
1078 173 : node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1079 173 : outer_frame_state, ContinuationFrameStateMode::LAZY);
1080 : Node* check_throw = check_fail = graph()->NewNode(
1081 : javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
1082 : jsgraph()->Constant(MessageTemplate::kCalledNonCallable), fncallback,
1083 346 : context, check_frame_state, effect, check_fail);
1084 173 : control = graph()->NewNode(common()->IfTrue(), check_branch);
1085 :
1086 : // Start the loop.
1087 173 : Node* loop = control = graph()->NewNode(common()->Loop(2), control, control);
1088 : Node* eloop = effect =
1089 173 : graph()->NewNode(common()->EffectPhi(2), effect, effect, loop);
1090 : Node* vloop = k = graph()->NewNode(
1091 173 : common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop);
1092 173 : checkpoint_params[4] = k;
1093 :
1094 : control = loop;
1095 : effect = eloop;
1096 :
1097 : Node* continue_test =
1098 173 : graph()->NewNode(simplified()->NumberLessThan(), k, original_length);
1099 : Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1100 173 : continue_test, control);
1101 :
1102 173 : Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch);
1103 173 : Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch);
1104 : control = if_true;
1105 :
1106 : Node* frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1107 : jsgraph(), function, Builtins::kArrayMapLoopEagerDeoptContinuation,
1108 173 : node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1109 173 : outer_frame_state, ContinuationFrameStateMode::EAGER);
1110 :
1111 : effect =
1112 173 : graph()->NewNode(common()->Checkpoint(), frame_state, effect, control);
1113 :
1114 : // Make sure the map hasn't changed during the iteration
1115 : effect = graph()->NewNode(
1116 : simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver,
1117 346 : effect, control);
1118 :
1119 : // Make sure that the access is still in bounds, since the callback could have
1120 : // changed the array's size.
1121 : Node* length = effect = graph()->NewNode(
1122 : simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver,
1123 519 : effect, control);
1124 : k = effect =
1125 173 : graph()->NewNode(simplified()->CheckBounds(), k, length, effect, control);
1126 :
1127 : // Reload the elements pointer before calling the callback, since the previous
1128 : // callback might have resized the array causing the elements buffer to be
1129 : // re-allocated.
1130 : Node* elements = effect = graph()->NewNode(
1131 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
1132 519 : effect, control);
1133 :
1134 : Node* element = effect = graph()->NewNode(
1135 : simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(kind)),
1136 519 : elements, k, effect, control);
1137 :
1138 : Node* next_k =
1139 346 : graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant());
1140 :
1141 : // This frame state is dealt with by hand in
1142 : // ArrayMapLoopLazyDeoptContinuation.
1143 : frame_state = CreateJavaScriptBuiltinContinuationFrameState(
1144 : jsgraph(), function, Builtins::kArrayMapLoopLazyDeoptContinuation,
1145 173 : node->InputAt(0), context, &checkpoint_params[0], stack_parameters,
1146 173 : outer_frame_state, ContinuationFrameStateMode::LAZY);
1147 :
1148 : Node* callback_value = control = effect = graph()->NewNode(
1149 : javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k,
1150 519 : receiver, context, frame_state, effect, control);
1151 :
1152 : // Rewire potential exception edges.
1153 173 : Node* on_exception = nullptr;
1154 173 : if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1155 : // Create appropriate {IfException} and {IfSuccess} nodes.
1156 : Node* if_exception0 =
1157 30 : graph()->NewNode(common()->IfException(), check_throw, check_fail);
1158 30 : check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
1159 : Node* if_exception1 =
1160 30 : graph()->NewNode(common()->IfException(), effect, control);
1161 30 : control = graph()->NewNode(common()->IfSuccess(), control);
1162 :
1163 : // Join the exception edges.
1164 : Node* merge =
1165 30 : graph()->NewNode(common()->Merge(2), if_exception0, if_exception1);
1166 : Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0,
1167 30 : if_exception1, merge);
1168 : Node* phi =
1169 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
1170 30 : if_exception0, if_exception1, merge);
1171 203 : ReplaceWithValue(on_exception, phi, ephi, merge);
1172 : }
1173 :
1174 : Handle<Map> double_map(Map::cast(
1175 : native_context()->get(Context::ArrayMapIndex(HOLEY_DOUBLE_ELEMENTS))));
1176 : Handle<Map> fast_map(
1177 : Map::cast(native_context()->get(Context::ArrayMapIndex(HOLEY_ELEMENTS))));
1178 : effect = graph()->NewNode(
1179 : simplified()->TransitionAndStoreElement(double_map, fast_map), a, k,
1180 173 : callback_value, effect, control);
1181 :
1182 : k = next_k;
1183 :
1184 173 : loop->ReplaceInput(1, control);
1185 173 : vloop->ReplaceInput(1, k);
1186 173 : eloop->ReplaceInput(1, effect);
1187 :
1188 : control = if_false;
1189 : effect = eloop;
1190 :
1191 : // The above %ThrowTypeError runtime call is an unconditional throw, making
1192 : // it impossible to return a successful completion in this case. We simply
1193 : // connect the successful completion to the graph end.
1194 : Node* terminate =
1195 173 : graph()->NewNode(common()->Throw(), check_throw, check_fail);
1196 173 : NodeProperties::MergeControlToEnd(graph(), common(), terminate);
1197 :
1198 : ReplaceWithValue(node, a, effect, control);
1199 : return Replace(a);
1200 : }
1201 :
1202 11196 : Reduction JSCallReducer::ReduceCallApiFunction(
1203 63563 : Node* node, Handle<FunctionTemplateInfo> function_template_info) {
1204 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1205 11196 : CallParameters const& p = CallParametersOf(node->op());
1206 11196 : int const argc = static_cast<int>(p.arity()) - 2;
1207 : Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined)
1208 29128 : ? jsgraph()->HeapConstant(global_proxy())
1209 22392 : : NodeProperties::GetValueInput(node, 1);
1210 11196 : Node* effect = NodeProperties::GetEffectInput(node);
1211 :
1212 : // CallApiCallbackStub expects the target in a register, so we count it out,
1213 : // and counts the receiver as an implicit argument, so we count the receiver
1214 : // out too.
1215 11196 : if (argc > CallApiCallbackStub::kArgMax) return NoChange();
1216 :
1217 : // Infer the {receiver} maps, and check if we can inline the API function
1218 : // callback based on those.
1219 : ZoneHandleSet<Map> receiver_maps;
1220 : NodeProperties::InferReceiverMapsResult result =
1221 11194 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
1222 11194 : if (result == NodeProperties::kNoReceiverMaps) return NoChange();
1223 33132 : for (size_t i = 0; i < receiver_maps.size(); ++i) {
1224 : Handle<Map> receiver_map = receiver_maps[i];
1225 33132 : if (!receiver_map->IsJSObjectMap() ||
1226 0 : (!function_template_info->accept_any_receiver() &&
1227 : receiver_map->is_access_check_needed())) {
1228 : return NoChange();
1229 : }
1230 : // In case of unreliable {receiver} information, the {receiver_maps}
1231 : // must all be stable in order to consume the information.
1232 11044 : if (result == NodeProperties::kUnreliableReceiverMaps) {
1233 10426 : if (!receiver_map->is_stable()) return NoChange();
1234 : }
1235 : }
1236 :
1237 : // See if we can constant-fold the compatible receiver checks.
1238 11044 : CallOptimization call_optimization(function_template_info);
1239 11044 : if (!call_optimization.is_simple_api_call()) return NoChange();
1240 : CallOptimization::HolderLookup lookup;
1241 : Handle<JSObject> api_holder =
1242 11042 : call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup);
1243 11042 : if (lookup == CallOptimization::kHolderNotFound) return NoChange();
1244 10991 : for (size_t i = 1; i < receiver_maps.size(); ++i) {
1245 : CallOptimization::HolderLookup lookupi;
1246 : Handle<JSObject> holder = call_optimization.LookupHolderOfExpectedType(
1247 0 : receiver_maps[i], &lookupi);
1248 0 : if (lookup != lookupi) return NoChange();
1249 0 : if (!api_holder.is_identical_to(holder)) return NoChange();
1250 : }
1251 :
1252 : // Install stability dependencies for unreliable {receiver_maps}.
1253 10991 : if (result == NodeProperties::kUnreliableReceiverMaps) {
1254 31263 : for (size_t i = 0; i < receiver_maps.size(); ++i) {
1255 10421 : dependencies()->AssumeMapStable(receiver_maps[i]);
1256 : }
1257 : }
1258 :
1259 : // CallApiCallbackStub's register arguments: code, target, call data, holder,
1260 : // function address.
1261 : // TODO(turbofan): Consider introducing a JSCallApiCallback operator for
1262 : // this and lower it during JSGenericLowering, and unify this with the
1263 : // JSNativeContextSpecialization::InlineApiCall method a bit.
1264 : Handle<CallHandlerInfo> call_handler_info(
1265 : CallHandlerInfo::cast(function_template_info->call_code()), isolate());
1266 : Handle<Object> data(call_handler_info->data(), isolate());
1267 : CallApiCallbackStub stub(isolate(), argc, false);
1268 : CallInterfaceDescriptor cid = stub.GetCallInterfaceDescriptor();
1269 : CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor(
1270 : isolate(), graph()->zone(), cid,
1271 10991 : cid.GetStackParameterCount() + argc +
1272 : 2 /* implicit receiver + accessor_holder */,
1273 : CallDescriptor::kNeedsFrameState, Operator::kNoProperties,
1274 32973 : MachineType::AnyTagged(), 1);
1275 : ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback()));
1276 10991 : Node* holder = lookup == CallOptimization::kHolderFound
1277 10998 : ? jsgraph()->HeapConstant(api_holder)
1278 21982 : : receiver;
1279 : ExternalReference function_reference(
1280 10991 : &api_function, ExternalReference::DIRECT_API_CALL, isolate());
1281 : node->InsertInput(graph()->zone(), 0,
1282 32973 : jsgraph()->HeapConstant(stub.GetCode()));
1283 21982 : node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(data));
1284 10991 : node->InsertInput(graph()->zone(), 3, holder);
1285 : node->InsertInput(graph()->zone(), 4,
1286 21982 : jsgraph()->ExternalConstant(function_reference));
1287 10991 : node->InsertInput(graph()->zone(), 5, holder /* as accessor_holder */);
1288 10991 : node->ReplaceInput(6, receiver);
1289 10991 : NodeProperties::ChangeOp(node, common()->Call(call_descriptor));
1290 : return Changed(node);
1291 : }
1292 :
1293 : namespace {
1294 :
1295 : // Check whether elements aren't mutated; we play it extremely safe here by
1296 : // explicitly checking that {node} is only used by {LoadField} or {LoadElement}.
1297 39 : bool IsSafeArgumentsElements(Node* node) {
1298 273 : for (Edge const edge : node->use_edges()) {
1299 117 : if (!NodeProperties::IsValueEdge(edge)) continue;
1300 195 : if (edge.from()->opcode() != IrOpcode::kLoadField &&
1301 39 : edge.from()->opcode() != IrOpcode::kLoadElement) {
1302 0 : return false;
1303 : }
1304 : }
1305 : return true;
1306 : }
1307 :
1308 : } // namespace
1309 :
1310 2733 : Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
1311 : Node* node, int arity, CallFrequency const& frequency,
1312 1595 : VectorSlotPair const& feedback) {
1313 : DCHECK(node->opcode() == IrOpcode::kJSCallWithArrayLike ||
1314 : node->opcode() == IrOpcode::kJSCallWithSpread ||
1315 : node->opcode() == IrOpcode::kJSConstructWithArrayLike ||
1316 : node->opcode() == IrOpcode::kJSConstructWithSpread);
1317 :
1318 : // In case of a call/construct with spread, we need to
1319 : // ensure that it's safe to avoid the actual iteration.
1320 11425 : if ((node->opcode() == IrOpcode::kJSCallWithSpread ||
1321 4433 : node->opcode() == IrOpcode::kJSConstructWithSpread) &&
1322 6133 : !isolate()->initial_array_iterator_prototype_map()->is_stable()) {
1323 : return NoChange();
1324 : }
1325 :
1326 : // Check if {arguments_list} is an arguments object, and {node} is the only
1327 : // value user of {arguments_list} (except for value uses in frame states).
1328 4062 : Node* arguments_list = NodeProperties::GetValueInput(node, arity);
1329 2725 : if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) {
1330 : return NoChange();
1331 : }
1332 20671 : for (Edge edge : arguments_list->use_edges()) {
1333 9667 : if (!NodeProperties::IsValueEdge(edge)) continue;
1334 8316 : Node* const user = edge.from();
1335 8277 : switch (user->opcode()) {
1336 : case IrOpcode::kCheckMaps:
1337 : case IrOpcode::kFrameState:
1338 : case IrOpcode::kStateValues:
1339 : case IrOpcode::kReferenceEqual:
1340 : case IrOpcode::kReturn:
1341 : // Ignore safe uses that definitely don't mess with the arguments.
1342 : continue;
1343 : case IrOpcode::kLoadField: {
1344 : DCHECK_EQ(arguments_list, user->InputAt(0));
1345 93 : FieldAccess const& access = FieldAccessOf(user->op());
1346 93 : if (access.offset == JSArray::kLengthOffset) {
1347 : // Ignore uses for arguments#length.
1348 : STATIC_ASSERT(JSArray::kLengthOffset ==
1349 : JSArgumentsObject::kLengthOffset);
1350 : continue;
1351 39 : } else if (access.offset == JSObject::kElementsOffset) {
1352 : // Ignore safe uses for arguments#elements.
1353 39 : if (IsSafeArgumentsElements(user)) continue;
1354 : }
1355 : break;
1356 : }
1357 : case IrOpcode::kJSCallWithArrayLike:
1358 : // Ignore uses as argumentsList input to calls with array like.
1359 533 : if (user->InputAt(2) == arguments_list) continue;
1360 : break;
1361 : case IrOpcode::kJSConstructWithArrayLike:
1362 : // Ignore uses as argumentsList input to calls with array like.
1363 157 : if (user->InputAt(1) == arguments_list) continue;
1364 : break;
1365 : case IrOpcode::kJSCallWithSpread: {
1366 : // Ignore uses as spread input to calls with spread.
1367 407 : CallParameters p = CallParametersOf(user->op());
1368 407 : int const arity = static_cast<int>(p.arity() - 1);
1369 407 : if (user->InputAt(arity) == arguments_list) continue;
1370 : break;
1371 : }
1372 : case IrOpcode::kJSConstructWithSpread: {
1373 : // Ignore uses as spread input to construct with spread.
1374 539 : ConstructParameters p = ConstructParametersOf(user->op());
1375 539 : int const arity = static_cast<int>(p.arity() - 2);
1376 539 : if (user->InputAt(arity) == arguments_list) continue;
1377 : break;
1378 : }
1379 : default:
1380 : break;
1381 : }
1382 : // We cannot currently reduce the {node} to something better than what
1383 : // it already is, but we might be able to do something about the {node}
1384 : // later, so put it on the waitlist and try again during finalization.
1385 : waitlist_.insert(node);
1386 122 : return NoChange();
1387 : }
1388 :
1389 : // Get to the actual frame state from which to extract the arguments;
1390 : // we can only optimize this in case the {node} was already inlined into
1391 : // some other function (and same for the {arguments_list}).
1392 1337 : CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op());
1393 1337 : Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list);
1394 1337 : FrameStateInfo state_info = OpParameter<FrameStateInfo>(frame_state);
1395 : int start_index = 0;
1396 : // Determine the formal parameter count;
1397 : Handle<SharedFunctionInfo> shared;
1398 1337 : if (!state_info.shared_info().ToHandle(&shared)) return NoChange();
1399 : int formal_parameter_count = shared->internal_formal_parameter_count();
1400 1337 : if (type == CreateArgumentsType::kMappedArguments) {
1401 : // Mapped arguments (sloppy mode) that are aliased can only be handled
1402 : // here if there's no side-effect between the {node} and the {arg_array}.
1403 : // TODO(turbofan): Further relax this constraint.
1404 391 : if (formal_parameter_count != 0) {
1405 66 : Node* effect = NodeProperties::GetEffectInput(node);
1406 66 : if (!NodeProperties::NoObservableSideEffectBetween(effect,
1407 66 : arguments_list)) {
1408 : return NoChange();
1409 : }
1410 : }
1411 946 : } else if (type == CreateArgumentsType::kRestParameter) {
1412 : start_index = formal_parameter_count;
1413 :
1414 : // For spread calls/constructs with rest parameters we need to ensure that
1415 : // the array iterator protector is intact, which guards that the rest
1416 : // parameter iteration is not observable.
1417 1406 : if (node->opcode() == IrOpcode::kJSCallWithSpread ||
1418 : node->opcode() == IrOpcode::kJSConstructWithSpread) {
1419 625 : if (!isolate()->IsArrayIteratorLookupChainIntact()) return NoChange();
1420 : dependencies()->AssumePropertyCell(factory()->array_iterator_protector());
1421 : }
1422 : }
1423 :
1424 : // For call/construct with spread, we need to also install a code
1425 : // dependency on the initial %ArrayIteratorPrototype% map here to
1426 : // ensure that no one messes with the next method.
1427 2600 : if (node->opcode() == IrOpcode::kJSCallWithSpread ||
1428 : node->opcode() == IrOpcode::kJSConstructWithSpread) {
1429 : dependencies()->AssumeMapStable(
1430 1486 : isolate()->initial_array_iterator_prototype_map());
1431 : }
1432 :
1433 : // Remove the {arguments_list} input from the {node}.
1434 1300 : node->RemoveInput(arity--);
1435 : // Check if are spreading to inlined arguments or to the arguments of
1436 : // the outermost function.
1437 1300 : Node* outer_state = frame_state->InputAt(kFrameStateOuterStateInput);
1438 1300 : if (outer_state->opcode() != IrOpcode::kFrameState) {
1439 : Operator const* op =
1440 1426 : (node->opcode() == IrOpcode::kJSCallWithArrayLike ||
1441 : node->opcode() == IrOpcode::kJSCallWithSpread)
1442 384 : ? javascript()->CallForwardVarargs(arity + 1, start_index)
1443 1674 : : javascript()->ConstructForwardVarargs(arity + 2, start_index);
1444 837 : NodeProperties::ChangeOp(node, op);
1445 837 : return Changed(node);
1446 : }
1447 : // Get to the actual frame state from which to extract the arguments;
1448 : // we can only optimize this in case the {node} was already inlined into
1449 : // some other function (and same for the {arg_array}).
1450 463 : FrameStateInfo outer_info = OpParameter<FrameStateInfo>(outer_state);
1451 463 : if (outer_info.type() == FrameStateType::kArgumentsAdaptor) {
1452 : // Need to take the parameters from the arguments adaptor.
1453 : frame_state = outer_state;
1454 : }
1455 : // Add the actual parameters to the {node}, skipping the receiver.
1456 : Node* const parameters = frame_state->InputAt(kFrameStateParametersInput);
1457 2056 : for (int i = start_index + 1; i < parameters->InputCount(); ++i) {
1458 : node->InsertInput(graph()->zone(), static_cast<int>(++arity),
1459 1130 : parameters->InputAt(i));
1460 : }
1461 :
1462 926 : if (node->opcode() == IrOpcode::kJSCallWithArrayLike ||
1463 : node->opcode() == IrOpcode::kJSCallWithSpread) {
1464 : NodeProperties::ChangeOp(
1465 452 : node, javascript()->Call(arity + 1, frequency, feedback));
1466 226 : Reduction const reduction = ReduceJSCall(node);
1467 226 : return reduction.Changed() ? reduction : Changed(node);
1468 : } else {
1469 : NodeProperties::ChangeOp(
1470 474 : node, javascript()->Construct(arity + 2, frequency, feedback));
1471 237 : Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
1472 237 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
1473 237 : Node* context = NodeProperties::GetContextInput(node);
1474 237 : Node* effect = NodeProperties::GetEffectInput(node);
1475 237 : Node* control = NodeProperties::GetControlInput(node);
1476 :
1477 : // Check whether the given new target value is a constructor function. The
1478 : // replacement {JSConstruct} operator only checks the passed target value
1479 : // but relies on the new target value to be implicitly valid.
1480 : Node* check =
1481 237 : graph()->NewNode(simplified()->ObjectIsConstructor(), new_target);
1482 : Node* check_branch =
1483 237 : graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control);
1484 237 : Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch);
1485 : Node* check_throw = check_fail =
1486 : graph()->NewNode(javascript()->CallRuntime(Runtime::kThrowTypeError, 2),
1487 : jsgraph()->Constant(MessageTemplate::kNotConstructor),
1488 474 : new_target, context, frame_state, effect, check_fail);
1489 237 : control = graph()->NewNode(common()->IfTrue(), check_branch);
1490 237 : NodeProperties::ReplaceControlInput(node, control);
1491 :
1492 : // Rewire potential exception edges.
1493 237 : Node* on_exception = nullptr;
1494 237 : if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
1495 : // Create appropriate {IfException} and {IfSuccess} nodes.
1496 : Node* if_exception =
1497 8 : graph()->NewNode(common()->IfException(), check_throw, check_fail);
1498 8 : check_fail = graph()->NewNode(common()->IfSuccess(), check_fail);
1499 :
1500 : // Join the exception edges.
1501 : Node* merge =
1502 16 : graph()->NewNode(common()->Merge(2), if_exception, on_exception);
1503 : Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception,
1504 16 : on_exception, merge);
1505 : Node* phi =
1506 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
1507 16 : if_exception, on_exception, merge);
1508 8 : ReplaceWithValue(on_exception, phi, ephi, merge);
1509 8 : merge->ReplaceInput(1, on_exception);
1510 8 : ephi->ReplaceInput(1, on_exception);
1511 8 : phi->ReplaceInput(1, on_exception);
1512 : }
1513 :
1514 : // The above %ThrowTypeError runtime call is an unconditional throw, making
1515 : // it impossible to return a successful completion in this case. We simply
1516 : // connect the successful completion to the graph end.
1517 : Node* terminate =
1518 237 : graph()->NewNode(common()->Throw(), check_throw, check_fail);
1519 237 : NodeProperties::MergeControlToEnd(graph(), common(), terminate);
1520 :
1521 237 : Reduction const reduction = ReduceJSConstruct(node);
1522 237 : return reduction.Changed() ? reduction : Changed(node);
1523 : }
1524 : }
1525 :
1526 : namespace {
1527 :
1528 26277 : bool ShouldUseCallICFeedback(Node* node) {
1529 : HeapObjectMatcher m(node);
1530 26277 : if (m.HasValue() || m.IsJSCreateClosure()) {
1531 : // Don't use CallIC feedback when we know the function
1532 : // being called, i.e. either know the closure itself or
1533 : // at least the SharedFunctionInfo.
1534 : return false;
1535 25376 : } else if (m.IsPhi()) {
1536 : // Protect against endless loops here.
1537 754 : Node* control = NodeProperties::GetControlInput(node);
1538 754 : if (control->opcode() == IrOpcode::kLoop) return false;
1539 : // Check if {node} is a Phi of nodes which shouldn't
1540 : // use CallIC feedback (not looking through loops).
1541 754 : int const value_input_count = m.node()->op()->ValueInputCount();
1542 1467 : for (int n = 0; n < value_input_count; ++n) {
1543 1464 : if (ShouldUseCallICFeedback(node->InputAt(n))) return true;
1544 : }
1545 : return false;
1546 : }
1547 : return true;
1548 : }
1549 :
1550 : } // namespace
1551 :
1552 705445 : Reduction JSCallReducer::ReduceJSCall(Node* node) {
1553 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1554 1361475 : CallParameters const& p = CallParametersOf(node->op());
1555 680753 : Node* target = NodeProperties::GetValueInput(node, 0);
1556 680739 : Node* control = NodeProperties::GetControlInput(node);
1557 680740 : Node* effect = NodeProperties::GetEffectInput(node);
1558 : size_t arity = p.arity();
1559 : DCHECK_LE(2u, arity);
1560 :
1561 : // Try to specialize JSCall {node}s with constant {target}s.
1562 : HeapObjectMatcher m(target);
1563 680738 : if (m.HasValue()) {
1564 355248 : if (m.Value()->IsJSFunction()) {
1565 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
1566 : Handle<SharedFunctionInfo> shared(function->shared(), isolate());
1567 :
1568 : // Raise a TypeError if the {target} is a "classConstructor".
1569 355143 : if (IsClassConstructor(shared->kind())) {
1570 32 : NodeProperties::ReplaceValueInputs(node, target);
1571 : NodeProperties::ChangeOp(
1572 : node, javascript()->CallRuntime(
1573 32 : Runtime::kThrowConstructorNonCallableError, 1));
1574 : return Changed(node);
1575 : }
1576 :
1577 : // Don't inline cross native context.
1578 355111 : if (function->native_context() != *native_context()) return NoChange();
1579 :
1580 : // Check for known builtin functions.
1581 355083 : switch (shared->code()->builtin_index()) {
1582 : case Builtins::kArrayConstructor:
1583 226 : return ReduceArrayConstructor(node);
1584 : case Builtins::kBooleanConstructor:
1585 12 : return ReduceBooleanConstructor(node);
1586 : case Builtins::kFunctionPrototypeApply:
1587 506 : return ReduceFunctionPrototypeApply(node);
1588 : case Builtins::kFastFunctionPrototypeBind:
1589 94 : return ReduceFunctionPrototypeBind(node);
1590 : case Builtins::kFunctionPrototypeCall:
1591 1127 : return ReduceFunctionPrototypeCall(node);
1592 : case Builtins::kFunctionPrototypeHasInstance:
1593 1658 : return ReduceFunctionPrototypeHasInstance(node);
1594 : case Builtins::kNumberConstructor:
1595 149 : return ReduceNumberConstructor(node);
1596 : case Builtins::kObjectConstructor:
1597 168 : return ReduceObjectConstructor(node);
1598 : case Builtins::kObjectGetPrototypeOf:
1599 78 : return ReduceObjectGetPrototypeOf(node);
1600 : case Builtins::kObjectPrototypeGetProto:
1601 92 : return ReduceObjectPrototypeGetProto(node);
1602 : case Builtins::kObjectPrototypeHasOwnProperty:
1603 465 : return ReduceObjectPrototypeHasOwnProperty(node);
1604 : case Builtins::kObjectPrototypeIsPrototypeOf:
1605 63 : return ReduceObjectPrototypeIsPrototypeOf(node);
1606 : case Builtins::kReflectApply:
1607 220 : return ReduceReflectApply(node);
1608 : case Builtins::kReflectConstruct:
1609 211 : return ReduceReflectConstruct(node);
1610 : case Builtins::kReflectGetPrototypeOf:
1611 15 : return ReduceReflectGetPrototypeOf(node);
1612 : case Builtins::kReflectHas:
1613 35 : return ReduceReflectHas(node);
1614 : case Builtins::kArrayForEach:
1615 267 : return ReduceArrayForEach(function, node);
1616 : case Builtins::kArrayMap:
1617 184 : return ReduceArrayMap(function, node);
1618 : case Builtins::kReturnReceiver:
1619 54 : return ReduceReturnReceiver(node);
1620 : default:
1621 : break;
1622 : }
1623 :
1624 349459 : if (!FLAG_runtime_stats && shared->IsApiFunction()) {
1625 : Handle<FunctionTemplateInfo> function_template_info(
1626 : FunctionTemplateInfo::cast(shared->function_data()), isolate());
1627 11196 : return ReduceCallApiFunction(node, function_template_info);
1628 : }
1629 105 : } else if (m.Value()->IsJSBoundFunction()) {
1630 : Handle<JSBoundFunction> function =
1631 : Handle<JSBoundFunction>::cast(m.Value());
1632 : Handle<JSReceiver> bound_target_function(
1633 : function->bound_target_function(), isolate());
1634 : Handle<Object> bound_this(function->bound_this(), isolate());
1635 : Handle<FixedArray> bound_arguments(function->bound_arguments(),
1636 : isolate());
1637 : ConvertReceiverMode const convert_mode =
1638 : (bound_this->IsNullOrUndefined(isolate()))
1639 : ? ConvertReceiverMode::kNullOrUndefined
1640 32 : : ConvertReceiverMode::kNotNullOrUndefined;
1641 : // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]].
1642 : NodeProperties::ReplaceValueInput(
1643 32 : node, jsgraph()->Constant(bound_target_function), 0);
1644 : NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this),
1645 32 : 1);
1646 : // Insert the [[BoundArguments]] for {node}.
1647 108 : for (int i = 0; i < bound_arguments->length(); ++i) {
1648 : node->InsertInput(
1649 : graph()->zone(), i + 2,
1650 44 : jsgraph()->Constant(handle(bound_arguments->get(i), isolate())));
1651 22 : arity++;
1652 : }
1653 : NodeProperties::ChangeOp(
1654 : node, javascript()->Call(arity, p.frequency(), VectorSlotPair(),
1655 64 : convert_mode));
1656 : // Try to further reduce the JSCall {node}.
1657 32 : Reduction const reduction = ReduceJSCall(node);
1658 32 : return reduction.Changed() ? reduction : Changed(node);
1659 : }
1660 :
1661 : // Don't mess with other {node}s that have a constant {target}.
1662 : // TODO(bmeurer): Also support proxies here.
1663 : return NoChange();
1664 : }
1665 :
1666 : // If {target} is the result of a JSCreateBoundFunction operation,
1667 : // we can just fold the construction and call the bound target
1668 : // function directly instead.
1669 325490 : if (target->opcode() == IrOpcode::kJSCreateBoundFunction) {
1670 14 : Node* bound_target_function = NodeProperties::GetValueInput(target, 0);
1671 14 : Node* bound_this = NodeProperties::GetValueInput(target, 1);
1672 : int const bound_arguments_length =
1673 14 : static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity());
1674 :
1675 : // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]].
1676 14 : NodeProperties::ReplaceValueInput(node, bound_target_function, 0);
1677 14 : NodeProperties::ReplaceValueInput(node, bound_this, 1);
1678 :
1679 : // Insert the [[BoundArguments]] for {node}.
1680 28 : for (int i = 0; i < bound_arguments_length; ++i) {
1681 14 : Node* value = NodeProperties::GetValueInput(target, 2 + i);
1682 14 : node->InsertInput(graph()->zone(), 2 + i, value);
1683 14 : arity++;
1684 : }
1685 :
1686 : // Update the JSCall operator on {node}.
1687 : ConvertReceiverMode const convert_mode =
1688 14 : CanBeNullOrUndefined(bound_this)
1689 : ? ConvertReceiverMode::kAny
1690 14 : : ConvertReceiverMode::kNotNullOrUndefined;
1691 : NodeProperties::ChangeOp(
1692 : node, javascript()->Call(arity, p.frequency(), VectorSlotPair(),
1693 28 : convert_mode));
1694 :
1695 : // Try to further reduce the JSCall {node}.
1696 14 : Reduction const reduction = ReduceJSCall(node);
1697 14 : return reduction.Changed() ? reduction : Changed(node);
1698 : }
1699 :
1700 : // Extract feedback from the {node} using the CallICNexus.
1701 325476 : if (!p.feedback().IsValid()) return NoChange();
1702 : CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
1703 315599 : if (nexus.IsUninitialized()) {
1704 284194 : if (flags() & kBailoutOnUninitialized) {
1705 : // Introduce a SOFT deopt if the call {node} wasn't executed so far.
1706 : return ReduceSoftDeoptimize(
1707 0 : node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
1708 : }
1709 : return NoChange();
1710 : }
1711 :
1712 31405 : Handle<Object> feedback(nexus.GetFeedback(), isolate());
1713 31405 : if (feedback->IsWeakCell()) {
1714 : // Check if we want to use CallIC feedback here.
1715 24813 : if (!ShouldUseCallICFeedback(target)) return NoChange();
1716 :
1717 : Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
1718 24622 : if (cell->value()->IsCallable()) {
1719 : Node* target_function =
1720 24622 : jsgraph()->Constant(handle(cell->value(), isolate()));
1721 :
1722 : // Check that the {target} is still the {target_function}.
1723 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
1724 24622 : target_function);
1725 : effect =
1726 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
1727 24622 : check, effect, control);
1728 :
1729 : // Specialize the JSCall node to the {target_function}.
1730 24622 : NodeProperties::ReplaceValueInput(node, target_function, 0);
1731 24622 : NodeProperties::ReplaceEffectInput(node, effect);
1732 :
1733 : // Try to further reduce the JSCall {node}.
1734 24622 : Reduction const reduction = ReduceJSCall(node);
1735 24622 : return reduction.Changed() ? reduction : Changed(node);
1736 : }
1737 : }
1738 : return NoChange();
1739 : }
1740 :
1741 815 : Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) {
1742 : DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode());
1743 815 : CallFrequency frequency = CallFrequencyOf(node->op());
1744 815 : VectorSlotPair feedback;
1745 : return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency,
1746 815 : feedback);
1747 : }
1748 :
1749 1110 : Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
1750 : DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode());
1751 1110 : CallParameters const& p = CallParametersOf(node->op());
1752 : DCHECK_LE(3u, p.arity());
1753 1110 : int arity = static_cast<int>(p.arity() - 1);
1754 1110 : CallFrequency frequency = p.frequency();
1755 1110 : VectorSlotPair feedback = p.feedback();
1756 : return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
1757 1110 : feedback);
1758 : }
1759 :
1760 47705 : Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
1761 : DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
1762 43934 : ConstructParameters const& p = ConstructParametersOf(node->op());
1763 : DCHECK_LE(2u, p.arity());
1764 43934 : int const arity = static_cast<int>(p.arity() - 2);
1765 43934 : Node* target = NodeProperties::GetValueInput(node, 0);
1766 43934 : Node* new_target = NodeProperties::GetValueInput(node, arity + 1);
1767 43934 : Node* effect = NodeProperties::GetEffectInput(node);
1768 43934 : Node* control = NodeProperties::GetControlInput(node);
1769 :
1770 : // Extract feedback from the {node} using the CallICNexus.
1771 43934 : if (p.feedback().IsValid()) {
1772 : CallICNexus nexus(p.feedback().vector(), p.feedback().slot());
1773 43902 : if (nexus.IsUninitialized()) {
1774 24707 : if (flags() & kBailoutOnUninitialized) {
1775 : // Introduce a SOFT deopt if the construct {node} wasn't executed so
1776 : // far.
1777 : return ReduceSoftDeoptimize(
1778 0 : node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
1779 : }
1780 : return NoChange();
1781 : }
1782 :
1783 19195 : Handle<Object> feedback(nexus.GetFeedback(), isolate());
1784 19195 : if (feedback->IsAllocationSite()) {
1785 : // The feedback is an AllocationSite, which means we have called the
1786 : // Array function and collected transition (and pretenuring) feedback
1787 : // for the resulting arrays. This has to be kept in sync with the
1788 : // implementation in Ignition.
1789 516 : Handle<AllocationSite> site = Handle<AllocationSite>::cast(feedback);
1790 :
1791 : // Retrieve the Array function from the {node}.
1792 : Node* array_function = jsgraph()->HeapConstant(
1793 516 : handle(native_context()->array_function(), isolate()));
1794 :
1795 : // Check that the {target} is still the {array_function}.
1796 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
1797 516 : array_function);
1798 : effect =
1799 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
1800 516 : check, effect, control);
1801 :
1802 : // Turn the {node} into a {JSCreateArray} call.
1803 516 : NodeProperties::ReplaceEffectInput(node, effect);
1804 1092 : for (int i = arity; i > 0; --i) {
1805 : NodeProperties::ReplaceValueInput(
1806 576 : node, NodeProperties::GetValueInput(node, i), i + 1);
1807 : }
1808 516 : NodeProperties::ReplaceValueInput(node, array_function, 1);
1809 1032 : NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
1810 : return Changed(node);
1811 32146 : } else if (feedback->IsWeakCell() &&
1812 : !HeapObjectMatcher(new_target).HasValue()) {
1813 : Handle<WeakCell> cell = Handle<WeakCell>::cast(feedback);
1814 3255 : if (cell->value()->IsConstructor()) {
1815 : Node* new_target_feedback =
1816 3255 : jsgraph()->Constant(handle(cell->value(), isolate()));
1817 :
1818 : // Check that the {new_target} is still the {new_target_feedback}.
1819 : Node* check = graph()->NewNode(simplified()->ReferenceEqual(),
1820 3255 : new_target, new_target_feedback);
1821 : effect =
1822 : graph()->NewNode(simplified()->CheckIf(DeoptimizeReason::kNoReason),
1823 3255 : check, effect, control);
1824 :
1825 : // Specialize the JSConstruct node to the {new_target_feedback}.
1826 3255 : NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1);
1827 3255 : NodeProperties::ReplaceEffectInput(node, effect);
1828 3255 : if (target == new_target) {
1829 3199 : NodeProperties::ReplaceValueInput(node, new_target_feedback, 0);
1830 : }
1831 :
1832 : // Try to further reduce the JSConstruct {node}.
1833 3255 : Reduction const reduction = ReduceJSConstruct(node);
1834 3255 : return reduction.Changed() ? reduction : Changed(node);
1835 : }
1836 : }
1837 : }
1838 :
1839 : // Try to specialize JSConstruct {node}s with constant {target}s.
1840 : HeapObjectMatcher m(target);
1841 15456 : if (m.HasValue()) {
1842 15247 : if (m.Value()->IsJSFunction()) {
1843 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
1844 :
1845 : // Raise a TypeError if the {target} is not a constructor.
1846 13943 : if (!function->IsConstructor()) {
1847 8 : NodeProperties::ReplaceValueInputs(node, target);
1848 : NodeProperties::ChangeOp(
1849 : node, javascript()->CallRuntime(
1850 8 : Runtime::kThrowConstructedNonConstructable));
1851 : return Changed(node);
1852 : }
1853 :
1854 : // Don't inline cross native context.
1855 13935 : if (function->native_context() != *native_context()) return NoChange();
1856 :
1857 : // Check for the ArrayConstructor.
1858 13935 : if (*function == function->native_context()->array_function()) {
1859 : // TODO(bmeurer): Deal with Array subclasses here.
1860 : Handle<AllocationSite> site;
1861 : // Turn the {node} into a {JSCreateArray} call.
1862 448 : for (int i = arity; i > 0; --i) {
1863 : NodeProperties::ReplaceValueInput(
1864 210 : node, NodeProperties::GetValueInput(node, i), i + 1);
1865 : }
1866 238 : NodeProperties::ReplaceValueInput(node, new_target, 1);
1867 476 : NodeProperties::ChangeOp(node, javascript()->CreateArray(arity, site));
1868 : return Changed(node);
1869 : }
1870 :
1871 : // Check for the ObjectConstructor.
1872 13697 : if (*function == function->native_context()->object_function()) {
1873 : // If no value is passed, we can immediately lower to a simple
1874 : // JSCreate and don't need to do any massaging of the {node}.
1875 144 : if (arity == 0) {
1876 129 : NodeProperties::ChangeOp(node, javascript()->Create());
1877 : return Changed(node);
1878 : }
1879 :
1880 : // Otherwise we can only lower to JSCreate if we know that
1881 : // the value parameter is ignored, which is only the case if
1882 : // the {new_target} and {target} are definitely not identical.
1883 : HeapObjectMatcher mnew_target(new_target);
1884 30 : if (mnew_target.HasValue() && *mnew_target.Value() != *function) {
1885 : // Drop the value inputs.
1886 21 : for (int i = arity; i > 0; --i) node->RemoveInput(i);
1887 7 : NodeProperties::ChangeOp(node, javascript()->Create());
1888 : return Changed(node);
1889 : }
1890 : }
1891 : }
1892 :
1893 : // TODO(bmeurer): Also support optimizing bound functions and proxies here.
1894 : }
1895 :
1896 : return NoChange();
1897 : }
1898 :
1899 218 : Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
1900 : DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode());
1901 218 : CallFrequency frequency = CallFrequencyOf(node->op());
1902 218 : VectorSlotPair feedback;
1903 : return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency,
1904 218 : feedback);
1905 : }
1906 :
1907 590 : Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
1908 : DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode());
1909 590 : ConstructParameters const& p = ConstructParametersOf(node->op());
1910 : DCHECK_LE(3u, p.arity());
1911 590 : int arity = static_cast<int>(p.arity() - 2);
1912 590 : CallFrequency frequency = p.frequency();
1913 590 : VectorSlotPair feedback = p.feedback();
1914 : return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency,
1915 590 : feedback);
1916 : }
1917 :
1918 54 : Reduction JSCallReducer::ReduceReturnReceiver(Node* node) {
1919 : DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
1920 54 : Node* receiver = NodeProperties::GetValueInput(node, 1);
1921 54 : ReplaceWithValue(node, receiver);
1922 54 : return Replace(receiver);
1923 : }
1924 :
1925 0 : Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node,
1926 : DeoptimizeReason reason) {
1927 0 : Node* effect = NodeProperties::GetEffectInput(node);
1928 0 : Node* control = NodeProperties::GetControlInput(node);
1929 0 : Node* frame_state = NodeProperties::FindFrameStateBefore(node);
1930 : Node* deoptimize =
1931 : graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft, reason),
1932 0 : frame_state, effect, control);
1933 : // TODO(bmeurer): This should be on the AdvancedReducer somehow.
1934 0 : NodeProperties::MergeControlToEnd(graph(), common(), deoptimize);
1935 0 : Revisit(graph()->end());
1936 0 : node->TrimInputCount(0);
1937 0 : NodeProperties::ChangeOp(node, common()->Dead());
1938 0 : return Changed(node);
1939 : }
1940 :
1941 137063 : Graph* JSCallReducer::graph() const { return jsgraph()->graph(); }
1942 :
1943 514982 : Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); }
1944 :
1945 0 : Factory* JSCallReducer::factory() const { return isolate()->factory(); }
1946 :
1947 8966 : Handle<JSGlobalProxy> JSCallReducer::global_proxy() const {
1948 8966 : return handle(JSGlobalProxy::cast(native_context()->global_proxy()),
1949 8966 : isolate());
1950 : }
1951 :
1952 18617 : CommonOperatorBuilder* JSCallReducer::common() const {
1953 18617 : return jsgraph()->common();
1954 : }
1955 :
1956 7916 : JSOperatorBuilder* JSCallReducer::javascript() const {
1957 7916 : return jsgraph()->javascript();
1958 : }
1959 :
1960 61236 : SimplifiedOperatorBuilder* JSCallReducer::simplified() const {
1961 61236 : return jsgraph()->simplified();
1962 : }
1963 :
1964 : } // namespace compiler
1965 : } // namespace internal
1966 : } // namespace v8
|