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 | Reduction JSCallReducer::ReduceMathUnary(Node* node, const Operator* op) { |
37 | CallParameters const& p = CallParametersOf(node->op()); |
38 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
39 | return NoChange(); |
40 | } |
41 | if (node->op()->ValueInputCount() < 3) { |
42 | Node* value = jsgraph()->NaNConstant(); |
43 | ReplaceWithValue(node, value); |
44 | return Replace(value); |
45 | } |
46 | |
47 | Node* effect = NodeProperties::GetEffectInput(node); |
48 | Node* control = NodeProperties::GetControlInput(node); |
49 | Node* input = NodeProperties::GetValueInput(node, 2); |
50 | |
51 | input = effect = |
52 | 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 | Reduction JSCallReducer::ReduceMathBinary(Node* node, const Operator* op) { |
61 | CallParameters const& p = CallParametersOf(node->op()); |
62 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
63 | return NoChange(); |
64 | } |
65 | if (node->op()->ValueInputCount() < 3) { |
66 | Node* value = jsgraph()->NaNConstant(); |
67 | ReplaceWithValue(node, value); |
68 | return Replace(value); |
69 | } |
70 | Node* effect = NodeProperties::GetEffectInput(node); |
71 | Node* control = NodeProperties::GetControlInput(node); |
72 | |
73 | Node* left = NodeProperties::GetValueInput(node, 2); |
74 | Node* right = node->op()->ValueInputCount() > 3 |
75 | ? NodeProperties::GetValueInput(node, 3) |
76 | : jsgraph()->NaNConstant(); |
77 | left = effect = |
78 | graph()->NewNode(simplified()->SpeculativeToNumber( |
79 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
80 | left, effect, control); |
81 | right = effect = |
82 | 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 | Reduction JSCallReducer::ReduceMathImul(Node* node) { |
92 | CallParameters const& p = CallParametersOf(node->op()); |
93 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
94 | return NoChange(); |
95 | } |
96 | if (node->op()->ValueInputCount() < 3) { |
97 | Node* value = jsgraph()->ZeroConstant(); |
98 | ReplaceWithValue(node, value); |
99 | return Replace(value); |
100 | } |
101 | Node* left = NodeProperties::GetValueInput(node, 2); |
102 | Node* right = node->op()->ValueInputCount() > 3 |
103 | ? NodeProperties::GetValueInput(node, 3) |
104 | : jsgraph()->ZeroConstant(); |
105 | Node* effect = NodeProperties::GetEffectInput(node); |
106 | Node* control = NodeProperties::GetControlInput(node); |
107 | |
108 | left = effect = |
109 | graph()->NewNode(simplified()->SpeculativeToNumber( |
110 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
111 | left, effect, control); |
112 | right = effect = |
113 | graph()->NewNode(simplified()->SpeculativeToNumber( |
114 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
115 | right, effect, control); |
116 | left = graph()->NewNode(simplified()->NumberToUint32(), left); |
117 | right = graph()->NewNode(simplified()->NumberToUint32(), right); |
118 | 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 | Reduction JSCallReducer::ReduceMathClz32(Node* node) { |
125 | CallParameters const& p = CallParametersOf(node->op()); |
126 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
127 | return NoChange(); |
128 | } |
129 | if (node->op()->ValueInputCount() < 3) { |
130 | Node* value = jsgraph()->Constant(32); |
131 | ReplaceWithValue(node, value); |
132 | return Replace(value); |
133 | } |
134 | Node* input = NodeProperties::GetValueInput(node, 2); |
135 | Node* effect = NodeProperties::GetEffectInput(node); |
136 | Node* control = NodeProperties::GetControlInput(node); |
137 | |
138 | input = effect = |
139 | graph()->NewNode(simplified()->SpeculativeToNumber( |
140 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
141 | input, effect, control); |
142 | input = graph()->NewNode(simplified()->NumberToUint32(), input); |
143 | 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 | Reduction JSCallReducer::ReduceMathMinMax(Node* node, const Operator* op, |
151 | Node* empty_value) { |
152 | CallParameters const& p = CallParametersOf(node->op()); |
153 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
154 | return NoChange(); |
155 | } |
156 | if (node->op()->ValueInputCount() <= 2) { |
157 | ReplaceWithValue(node, empty_value); |
158 | return Replace(empty_value); |
159 | } |
160 | Node* effect = NodeProperties::GetEffectInput(node); |
161 | Node* control = NodeProperties::GetControlInput(node); |
162 | |
163 | Node* value = effect = |
164 | graph()->NewNode(simplified()->SpeculativeToNumber( |
165 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
166 | NodeProperties::GetValueInput(node, 2), effect, control); |
167 | for (int i = 3; i < node->op()->ValueInputCount(); i++) { |
168 | 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 | Reduction JSCallReducer::Reduce(Node* node) { |
180 | switch (node->opcode()) { |
181 | case IrOpcode::kJSConstruct: |
182 | return ReduceJSConstruct(node); |
183 | case IrOpcode::kJSConstructWithArrayLike: |
184 | return ReduceJSConstructWithArrayLike(node); |
185 | case IrOpcode::kJSConstructWithSpread: |
186 | return ReduceJSConstructWithSpread(node); |
187 | case IrOpcode::kJSCall: |
188 | return ReduceJSCall(node); |
189 | case IrOpcode::kJSCallWithArrayLike: |
190 | return ReduceJSCallWithArrayLike(node); |
191 | case IrOpcode::kJSCallWithSpread: |
192 | return ReduceJSCallWithSpread(node); |
193 | default: |
194 | break; |
195 | } |
196 | return NoChange(); |
197 | } |
198 | |
199 | 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 | for (Node* node : waitlist) { |
205 | if (!node->IsDead()) { |
206 | Reduction const reduction = Reduce(node); |
207 | if (reduction.Changed()) { |
208 | Node* replacement = reduction.replacement(); |
209 | if (replacement != node) { |
210 | Replace(node, replacement); |
211 | } |
212 | } |
213 | } |
214 | } |
215 | } |
216 | |
217 | // ES6 section 22.1.1 The Array Constructor |
218 | Reduction JSCallReducer::ReduceArrayConstructor(Node* node) { |
219 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
220 | Node* target = NodeProperties::GetValueInput(node, 0); |
221 | CallParameters const& p = CallParametersOf(node->op()); |
222 | |
223 | // Turn the {node} into a {JSCreateArray} call. |
224 | DCHECK_LE(2u, p.arity()); |
225 | size_t const arity = p.arity() - 2; |
226 | NodeProperties::ReplaceValueInput(node, target, 0); |
227 | NodeProperties::ReplaceValueInput(node, target, 1); |
228 | NodeProperties::ChangeOp( |
229 | node, javascript()->CreateArray(arity, MaybeHandle<AllocationSite>())); |
230 | return Changed(node); |
231 | } |
232 | |
233 | // ES6 section 19.3.1.1 Boolean ( value ) |
234 | Reduction JSCallReducer::ReduceBooleanConstructor(Node* node) { |
235 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
236 | 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 | : NodeProperties::GetValueInput(node, 2); |
242 | value = graph()->NewNode(simplified()->ToBoolean(), value); |
243 | ReplaceWithValue(node, value); |
244 | return Replace(value); |
245 | } |
246 | |
247 | // ES section #sec-object-constructor |
248 | Reduction JSCallReducer::ReduceObjectConstructor(Node* node) { |
249 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
250 | CallParameters const& p = CallParametersOf(node->op()); |
251 | if (p.arity() < 3) return NoChange(); |
252 | Node* value = (p.arity() >= 3) ? NodeProperties::GetValueInput(node, 2) |
253 | : jsgraph()->UndefinedConstant(); |
254 | Node* effect = NodeProperties::GetEffectInput(node); |
255 | |
256 | // We can fold away the Object(x) call if |x| is definitely not a primitive. |
257 | if (NodeProperties::CanBePrimitive(broker(), value, effect)) { |
258 | 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 | NodeProperties::ReplaceValueInputs(node, value); |
262 | 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 | Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { |
274 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
275 | CallParameters const& p = CallParametersOf(node->op()); |
276 | size_t arity = p.arity(); |
277 | DCHECK_LE(2u, arity); |
278 | ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; |
279 | if (arity == 2) { |
280 | // Neither thisArg nor argArray was provided. |
281 | convert_mode = ConvertReceiverMode::kNullOrUndefined; |
282 | node->ReplaceInput(0, node->InputAt(1)); |
283 | node->ReplaceInput(1, jsgraph()->UndefinedConstant()); |
284 | } else if (arity == 3) { |
285 | // The argArray was not provided, just remove the {target}. |
286 | node->RemoveInput(0); |
287 | --arity; |
288 | } else { |
289 | Node* target = NodeProperties::GetValueInput(node, 1); |
290 | Node* this_argument = NodeProperties::GetValueInput(node, 2); |
291 | Node* arguments_list = NodeProperties::GetValueInput(node, 3); |
292 | Node* context = NodeProperties::GetContextInput(node); |
293 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
294 | Node* effect = NodeProperties::GetEffectInput(node); |
295 | 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 | if (!NodeProperties::CanBeNullOrUndefined(broker(), arguments_list, |
300 | effect)) { |
301 | // Massage the value inputs appropriately. |
302 | node->ReplaceInput(0, target); |
303 | node->ReplaceInput(1, this_argument); |
304 | node->ReplaceInput(2, arguments_list); |
305 | while (arity-- > 3) node->RemoveInput(3); |
306 | |
307 | // Morph the {node} to a {JSCallWithArrayLike}. |
308 | NodeProperties::ChangeOp(node, |
309 | javascript()->CallWithArrayLike(p.frequency())); |
310 | Reduction const reduction = ReduceJSCallWithArrayLike(node); |
311 | return reduction.Changed() ? reduction : Changed(node); |
312 | } else { |
313 | // Check whether {arguments_list} is null. |
314 | Node* check_null = |
315 | graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, |
316 | jsgraph()->NullConstant()); |
317 | control = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
318 | check_null, control); |
319 | Node* if_null = graph()->NewNode(common()->IfTrue(), control); |
320 | control = graph()->NewNode(common()->IfFalse(), control); |
321 | |
322 | // Check whether {arguments_list} is undefined. |
323 | Node* check_undefined = |
324 | graph()->NewNode(simplified()->ReferenceEqual(), arguments_list, |
325 | jsgraph()->UndefinedConstant()); |
326 | control = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
327 | check_undefined, control); |
328 | Node* if_undefined = graph()->NewNode(common()->IfTrue(), control); |
329 | 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 | 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 | graph()->NewNode(common()->Merge(2), if_null, if_undefined); |
343 | Node* value1 = effect1 = control1 = |
344 | graph()->NewNode(javascript()->Call(2), target, this_argument, |
345 | context, frame_state, effect1, control1); |
346 | |
347 | // Rewire potential exception edges. |
348 | Node* if_exception = nullptr; |
349 | if (NodeProperties::IsExceptionalCall(node, &if_exception)) { |
350 | // Create appropriate {IfException} and {IfSuccess} nodes. |
351 | Node* if_exception0 = |
352 | graph()->NewNode(common()->IfException(), control0, effect0); |
353 | control0 = graph()->NewNode(common()->IfSuccess(), control0); |
354 | Node* if_exception1 = |
355 | graph()->NewNode(common()->IfException(), control1, effect1); |
356 | control1 = graph()->NewNode(common()->IfSuccess(), control1); |
357 | |
358 | // Join the exception edges. |
359 | Node* merge = |
360 | graph()->NewNode(common()->Merge(2), if_exception0, if_exception1); |
361 | Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0, |
362 | if_exception1, merge); |
363 | Node* phi = |
364 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
365 | if_exception0, if_exception1, merge); |
366 | ReplaceWithValue(if_exception, phi, ephi, merge); |
367 | } |
368 | |
369 | // Join control paths. |
370 | control = graph()->NewNode(common()->Merge(2), control0, control1); |
371 | effect = |
372 | graph()->NewNode(common()->EffectPhi(2), effect0, effect1, control); |
373 | Node* value = |
374 | 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 | NodeProperties::ChangeOp( |
382 | node, |
383 | javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode)); |
384 | // Try to further reduce the JSCall {node}. |
385 | Reduction const reduction = ReduceJSCall(node); |
386 | return reduction.Changed() ? reduction : Changed(node); |
387 | } |
388 | |
389 | // ES section #sec-function.prototype.bind |
390 | 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 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
399 | Node* bound_this = (node->op()->ValueInputCount() < 3) |
400 | ? jsgraph()->UndefinedConstant() |
401 | : NodeProperties::GetValueInput(node, 2); |
402 | Node* context = NodeProperties::GetContextInput(node); |
403 | Node* effect = NodeProperties::GetEffectInput(node); |
404 | 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 | &receiver_maps); |
414 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
415 | DCHECK_NE(0, receiver_maps.size()); |
416 | MapRef first_receiver_map(broker(), receiver_maps[0]); |
417 | bool const is_constructor = first_receiver_map.is_constructor(); |
418 | first_receiver_map.SerializePrototype(); |
419 | ObjectRef const prototype = first_receiver_map.prototype(); |
420 | 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 | receiver_map.SerializePrototype(); |
426 | if (!receiver_map.prototype().equals(prototype) || |
427 | receiver_map.is_constructor() != is_constructor || |
428 | receiver_map.instance_type() < FIRST_FUNCTION_TYPE) { |
429 | 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 | 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 | receiver_map.object()->instance_descriptors(), isolate()); |
443 | if (descriptors->number_of_descriptors() < 2) return NoChange(); |
444 | if (descriptors->GetKey(JSFunction::kLengthDescriptorIndex) != |
445 | ReadOnlyRoots(isolate()).length_string()) { |
446 | return NoChange(); |
447 | } |
448 | if (!descriptors->GetStrongValue(JSFunction::kLengthDescriptorIndex) |
449 | ->IsAccessorInfo()) { |
450 | return NoChange(); |
451 | } |
452 | if (descriptors->GetKey(JSFunction::kNameDescriptorIndex) != |
453 | ReadOnlyRoots(isolate()).name_string()) { |
454 | return NoChange(); |
455 | } |
456 | 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 | ? native_context().bound_function_with_constructor_map() |
466 | : native_context().bound_function_without_constructor_map(); |
467 | if (!map.prototype().equals(prototype)) return NoChange(); |
468 | |
469 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
470 | result, receiver_maps, VectorSlotPair(), receiver, effect, control); |
471 | |
472 | // Replace the {node} with a JSCreateBoundFunction. |
473 | int const arity = std::max(0, node->op()->ValueInputCount() - 3); |
474 | int const input_count = 2 + arity + 3; |
475 | Node** inputs = graph()->zone()->NewArray<Node*>(input_count); |
476 | inputs[0] = receiver; |
477 | inputs[1] = bound_this; |
478 | for (int i = 0; i < arity; ++i) { |
479 | inputs[2 + i] = NodeProperties::GetValueInput(node, 3 + i); |
480 | } |
481 | inputs[2 + arity + 0] = context; |
482 | inputs[2 + arity + 1] = effect; |
483 | inputs[2 + arity + 2] = control; |
484 | Node* value = effect = |
485 | graph()->NewNode(javascript()->CreateBoundFunction(arity, map.object()), |
486 | 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 | Reduction JSCallReducer::ReduceFunctionPrototypeCall(Node* node) { |
493 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
494 | CallParameters const& p = CallParametersOf(node->op()); |
495 | Node* target = NodeProperties::GetValueInput(node, 0); |
496 | Node* effect = NodeProperties::GetEffectInput(node); |
497 | 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 | if (m.HasValue()) { |
504 | JSFunctionRef function = m.Ref(broker()).AsJSFunction(); |
505 | context = jsgraph()->Constant(function.context()); |
506 | } else { |
507 | context = effect = graph()->NewNode( |
508 | simplified()->LoadField(AccessBuilder::ForJSFunctionContext()), target, |
509 | effect, control); |
510 | } |
511 | NodeProperties::ReplaceContextInput(node, context); |
512 | 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 | if (arity == 2) { |
521 | // The thisArg was not provided, use undefined as receiver. |
522 | convert_mode = ConvertReceiverMode::kNullOrUndefined; |
523 | node->ReplaceInput(0, node->InputAt(1)); |
524 | node->ReplaceInput(1, jsgraph()->UndefinedConstant()); |
525 | } else { |
526 | // Just remove the target, which is the first value input. |
527 | convert_mode = ConvertReceiverMode::kAny; |
528 | node->RemoveInput(0); |
529 | --arity; |
530 | } |
531 | NodeProperties::ChangeOp( |
532 | node, |
533 | javascript()->Call(arity, p.frequency(), VectorSlotPair(), convert_mode)); |
534 | // Try to further reduce the JSCall {node}. |
535 | Reduction const reduction = ReduceJSCall(node); |
536 | return reduction.Changed() ? reduction : Changed(node); |
537 | } |
538 | |
539 | // ES6 section 19.2.3.6 Function.prototype [ @@hasInstance ] (V) |
540 | Reduction JSCallReducer::ReduceFunctionPrototypeHasInstance(Node* node) { |
541 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
542 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
543 | Node* object = (node->op()->ValueInputCount() >= 3) |
544 | ? NodeProperties::GetValueInput(node, 2) |
545 | : jsgraph()->UndefinedConstant(); |
546 | Node* context = NodeProperties::GetContextInput(node); |
547 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
548 | Node* effect = NodeProperties::GetEffectInput(node); |
549 | 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 | node->ReplaceInput(0, receiver); |
558 | node->ReplaceInput(1, object); |
559 | node->ReplaceInput(2, context); |
560 | node->ReplaceInput(3, frame_state); |
561 | node->ReplaceInput(4, effect); |
562 | node->ReplaceInput(5, control); |
563 | node->TrimInputCount(6); |
564 | NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); |
565 | return Changed(node); |
566 | } |
567 | |
568 | Reduction JSCallReducer::ReduceObjectGetPrototype(Node* node, Node* object) { |
569 | Node* effect = NodeProperties::GetEffectInput(node); |
570 | |
571 | // Try to determine the {object} map. |
572 | ZoneHandleSet<Map> object_maps; |
573 | NodeProperties::InferReceiverMapsResult result = |
574 | NodeProperties::InferReceiverMaps(broker(), object, effect, &object_maps); |
575 | if (result != NodeProperties::kNoReceiverMaps) { |
576 | MapRef candidate_map(broker(), object_maps[0]); |
577 | candidate_map.SerializePrototype(); |
578 | ObjectRef candidate_prototype = candidate_map.prototype(); |
579 | |
580 | // Check if we can constant-fold the {candidate_prototype}. |
581 | for (size_t i = 0; i < object_maps.size(); ++i) { |
582 | MapRef object_map(broker(), object_maps[i]); |
583 | object_map.SerializePrototype(); |
584 | if (IsSpecialReceiverInstanceType(object_map.instance_type()) || |
585 | object_map.has_hidden_prototype() || |
586 | !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 | 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 | if (result == NodeProperties::kUnreliableReceiverMaps && |
596 | !object_map.is_stable()) { |
597 | return NoChange(); |
598 | } |
599 | } |
600 | if (result == NodeProperties::kUnreliableReceiverMaps) { |
601 | for (size_t i = 0; i < object_maps.size(); ++i) { |
602 | dependencies()->DependOnStableMap(MapRef(broker(), object_maps[i])); |
603 | } |
604 | } |
605 | 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 | 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 | : jsgraph()->UndefinedConstant(); |
619 | return ReduceObjectGetPrototype(node, object); |
620 | } |
621 | |
622 | // ES section #sec-object.is |
623 | Reduction JSCallReducer::ReduceObjectIs(Node* node) { |
624 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
625 | CallParameters const& params = CallParametersOf(node->op()); |
626 | int const argc = static_cast<int>(params.arity() - 2); |
627 | Node* lhs = (argc >= 1) ? NodeProperties::GetValueInput(node, 2) |
628 | : jsgraph()->UndefinedConstant(); |
629 | Node* rhs = (argc >= 2) ? NodeProperties::GetValueInput(node, 3) |
630 | : jsgraph()->UndefinedConstant(); |
631 | Node* value = graph()->NewNode(simplified()->SameValue(), lhs, rhs); |
632 | ReplaceWithValue(node, value); |
633 | return Replace(value); |
634 | } |
635 | |
636 | // ES6 section B.2.2.1.1 get Object.prototype.__proto__ |
637 | Reduction JSCallReducer::ReduceObjectPrototypeGetProto(Node* node) { |
638 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
639 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
640 | return ReduceObjectGetPrototype(node, receiver); |
641 | } |
642 | |
643 | // ES #sec-object.prototype.hasownproperty |
644 | Reduction JSCallReducer::ReduceObjectPrototypeHasOwnProperty(Node* node) { |
645 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
646 | CallParameters const& params = CallParametersOf(node->op()); |
647 | int const argc = static_cast<int>(params.arity() - 2); |
648 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
649 | Node* name = (argc >= 1) ? NodeProperties::GetValueInput(node, 2) |
650 | : jsgraph()->UndefinedConstant(); |
651 | Node* effect = NodeProperties::GetEffectInput(node); |
652 | 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 | if (name->opcode() == IrOpcode::kJSForInNext) { |
692 | ForInMode const mode = ForInModeOf(name->op()); |
693 | if (mode != ForInMode::kGeneric) { |
694 | Node* object = NodeProperties::GetValueInput(name, 0); |
695 | Node* cache_type = NodeProperties::GetValueInput(name, 2); |
696 | if (object->opcode() == IrOpcode::kJSToObject) { |
697 | object = NodeProperties::GetValueInput(object, 0); |
698 | } |
699 | 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 | if (!NodeProperties::NoObservableSideEffectBetween(effect, name)) { |
703 | Node* receiver_map = effect = |
704 | graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
705 | receiver, effect, control); |
706 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), |
707 | receiver_map, cache_type); |
708 | effect = graph()->NewNode( |
709 | simplified()->CheckIf(DeoptimizeReason::kWrongMap), check, effect, |
710 | control); |
711 | } |
712 | 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 | Reduction JSCallReducer::ReduceObjectPrototypeIsPrototypeOf(Node* node) { |
724 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
725 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
726 | Node* value = node->op()->ValueInputCount() > 2 |
727 | ? NodeProperties::GetValueInput(node, 2) |
728 | : jsgraph()->UndefinedConstant(); |
729 | 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 | &receiver_maps); |
737 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
738 | for (Handle<Map> map : receiver_maps) { |
739 | MapRef receiver_map(broker(), map); |
740 | 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 | NodeProperties::ReplaceValueInput(node, value, 0); |
748 | NodeProperties::ReplaceValueInput(node, receiver, 1); |
749 | for (int i = node->op()->ValueInputCount(); i-- > 2;) { |
750 | node->RemoveInput(i); |
751 | } |
752 | NodeProperties::ChangeOp(node, javascript()->HasInPrototypeChain()); |
753 | return Changed(node); |
754 | } |
755 | |
756 | // ES6 section 26.1.1 Reflect.apply ( target, thisArgument, argumentsList ) |
757 | Reduction JSCallReducer::ReduceReflectApply(Node* node) { |
758 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
759 | CallParameters const& p = CallParametersOf(node->op()); |
760 | int arity = static_cast<int>(p.arity() - 2); |
761 | DCHECK_LE(0, arity); |
762 | // Massage value inputs appropriately. |
763 | node->RemoveInput(0); |
764 | node->RemoveInput(0); |
765 | while (arity < 3) { |
766 | node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); |
767 | } |
768 | while (arity-- > 3) { |
769 | node->RemoveInput(arity); |
770 | } |
771 | NodeProperties::ChangeOp(node, |
772 | javascript()->CallWithArrayLike(p.frequency())); |
773 | Reduction const reduction = ReduceJSCallWithArrayLike(node); |
774 | return reduction.Changed() ? reduction : Changed(node); |
775 | } |
776 | |
777 | // ES6 section 26.1.2 Reflect.construct ( target, argumentsList [, newTarget] ) |
778 | Reduction JSCallReducer::ReduceReflectConstruct(Node* node) { |
779 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
780 | CallParameters const& p = CallParametersOf(node->op()); |
781 | int arity = static_cast<int>(p.arity() - 2); |
782 | DCHECK_LE(0, arity); |
783 | // Massage value inputs appropriately. |
784 | node->RemoveInput(0); |
785 | node->RemoveInput(0); |
786 | while (arity < 2) { |
787 | node->InsertInput(graph()->zone(), arity++, jsgraph()->UndefinedConstant()); |
788 | } |
789 | if (arity < 3) { |
790 | node->InsertInput(graph()->zone(), arity++, node->InputAt(0)); |
791 | } |
792 | while (arity-- > 3) { |
793 | node->RemoveInput(arity); |
794 | } |
795 | NodeProperties::ChangeOp(node, |
796 | javascript()->ConstructWithArrayLike(p.frequency())); |
797 | Reduction const reduction = ReduceJSConstructWithArrayLike(node); |
798 | return reduction.Changed() ? reduction : Changed(node); |
799 | } |
800 | |
801 | // ES6 section 26.1.7 Reflect.getPrototypeOf ( target ) |
802 | 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 | : jsgraph()->UndefinedConstant(); |
807 | return ReduceObjectGetPrototype(node, target); |
808 | } |
809 | |
810 | // ES6 section #sec-object.create Object.create(proto, properties) |
811 | Reduction JSCallReducer::ReduceObjectCreate(Node* node) { |
812 | int arg_count = node->op()->ValueInputCount(); |
813 | Node* properties = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3) |
814 | : jsgraph()->UndefinedConstant(); |
815 | if (properties != jsgraph()->UndefinedConstant()) return NoChange(); |
816 | |
817 | Node* effect = NodeProperties::GetEffectInput(node); |
818 | Node* control = NodeProperties::GetControlInput(node); |
819 | Node* context = NodeProperties::GetContextInput(node); |
820 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
821 | Node* prototype = arg_count >= 3 ? NodeProperties::GetValueInput(node, 2) |
822 | : jsgraph()->UndefinedConstant(); |
823 | node->ReplaceInput(0, prototype); |
824 | node->ReplaceInput(1, context); |
825 | node->ReplaceInput(2, frame_state); |
826 | node->ReplaceInput(3, effect); |
827 | node->ReplaceInput(4, control); |
828 | node->TrimInputCount(5); |
829 | NodeProperties::ChangeOp(node, javascript()->CreateObject()); |
830 | return Changed(node); |
831 | } |
832 | |
833 | // ES section #sec-reflect.get |
834 | Reduction JSCallReducer::ReduceReflectGet(Node* node) { |
835 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
836 | CallParameters const& p = CallParametersOf(node->op()); |
837 | int arity = static_cast<int>(p.arity() - 2); |
838 | if (arity != 2) return NoChange(); |
839 | Node* target = NodeProperties::GetValueInput(node, 2); |
840 | Node* key = NodeProperties::GetValueInput(node, 3); |
841 | Node* context = NodeProperties::GetContextInput(node); |
842 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
843 | Node* effect = NodeProperties::GetEffectInput(node); |
844 | Node* control = NodeProperties::GetControlInput(node); |
845 | |
846 | // Check whether {target} is a JSReceiver. |
847 | Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target); |
848 | Node* branch = |
849 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
850 | |
851 | // Throw an appropriate TypeError if the {target} is not a JSReceiver. |
852 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
853 | Node* efalse = effect; |
854 | { |
855 | 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 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
865 | Node* etrue = effect; |
866 | Node* vtrue; |
867 | { |
868 | Callable callable = |
869 | Builtins::CallableFor(isolate(), Builtins::kGetProperty); |
870 | auto call_descriptor = Linkage::GetStubCallDescriptor( |
871 | graph()->zone(), callable.descriptor(), |
872 | callable.descriptor().GetStackParameterCount(), |
873 | CallDescriptor::kNeedsFrameState, Operator::kNoProperties); |
874 | Node* stub_code = jsgraph()->HeapConstant(callable.code()); |
875 | vtrue = etrue = if_true = |
876 | 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 | Node* on_exception = nullptr; |
882 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
883 | // Create appropriate {IfException} and {IfSuccess} nodes. |
884 | Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true); |
885 | if_true = graph()->NewNode(common()->IfSuccess(), if_true); |
886 | Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false); |
887 | if_false = graph()->NewNode(common()->IfSuccess(), if_false); |
888 | |
889 | // Join the exception edges. |
890 | Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse); |
891 | Node* ephi = |
892 | graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge); |
893 | Node* phi = |
894 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
895 | extrue, exfalse, merge); |
896 | ReplaceWithValue(on_exception, phi, ephi, merge); |
897 | } |
898 | |
899 | // Connect the throwing path to end. |
900 | if_false = graph()->NewNode(common()->Throw(), efalse, if_false); |
901 | 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 | Reduction JSCallReducer::ReduceReflectHas(Node* node) { |
910 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
911 | CallParameters const& p = CallParametersOf(node->op()); |
912 | int arity = static_cast<int>(p.arity() - 2); |
913 | DCHECK_LE(0, arity); |
914 | Node* target = (arity >= 1) ? NodeProperties::GetValueInput(node, 2) |
915 | : jsgraph()->UndefinedConstant(); |
916 | Node* key = (arity >= 2) ? NodeProperties::GetValueInput(node, 3) |
917 | : jsgraph()->UndefinedConstant(); |
918 | Node* context = NodeProperties::GetContextInput(node); |
919 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
920 | Node* effect = NodeProperties::GetEffectInput(node); |
921 | Node* control = NodeProperties::GetControlInput(node); |
922 | |
923 | // Check whether {target} is a JSReceiver. |
924 | Node* check = graph()->NewNode(simplified()->ObjectIsReceiver(), target); |
925 | Node* branch = |
926 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
927 | |
928 | // Throw an appropriate TypeError if the {target} is not a JSReceiver. |
929 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
930 | Node* efalse = effect; |
931 | { |
932 | 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 | 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 | graph()->NewNode(javascript()->HasProperty(VectorSlotPair()), target, |
948 | key, context, frame_state, etrue, if_true); |
949 | } |
950 | |
951 | // Rewire potential exception edges. |
952 | Node* on_exception = nullptr; |
953 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
954 | // Create appropriate {IfException} and {IfSuccess} nodes. |
955 | Node* extrue = graph()->NewNode(common()->IfException(), etrue, if_true); |
956 | if_true = graph()->NewNode(common()->IfSuccess(), if_true); |
957 | Node* exfalse = graph()->NewNode(common()->IfException(), efalse, if_false); |
958 | if_false = graph()->NewNode(common()->IfSuccess(), if_false); |
959 | |
960 | // Join the exception edges. |
961 | Node* merge = graph()->NewNode(common()->Merge(2), extrue, exfalse); |
962 | Node* ephi = |
963 | graph()->NewNode(common()->EffectPhi(2), extrue, exfalse, merge); |
964 | Node* phi = |
965 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
966 | extrue, exfalse, merge); |
967 | ReplaceWithValue(on_exception, phi, ephi, merge); |
968 | } |
969 | |
970 | // Connect the throwing path to end. |
971 | if_false = graph()->NewNode(common()->Throw(), efalse, if_false); |
972 | NodeProperties::MergeControlToEnd(graph(), common(), if_false); |
973 | |
974 | // Continue on the regular path. |
975 | ReplaceWithValue(node, vtrue, etrue, if_true); |
976 | return Changed(vtrue); |
977 | } |
978 | |
979 | Node* JSCallReducer::WireInLoopStart(Node* k, Node** control, Node** effect) { |
980 | Node* loop = *control = |
981 | graph()->NewNode(common()->Loop(2), *control, *control); |
982 | Node* eloop = *effect = |
983 | graph()->NewNode(common()->EffectPhi(2), *effect, *effect, loop); |
984 | Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); |
985 | NodeProperties::MergeControlToEnd(graph(), common(), terminate); |
986 | return graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), k, |
987 | k, loop); |
988 | } |
989 | |
990 | void JSCallReducer::WireInLoopEnd(Node* loop, Node* eloop, Node* vloop, Node* k, |
991 | Node* control, Node* effect) { |
992 | loop->ReplaceInput(1, control); |
993 | vloop->ReplaceInput(1, k); |
994 | eloop->ReplaceInput(1, effect); |
995 | } |
996 | |
997 | namespace { |
998 | bool CanInlineArrayIteratingBuiltin(JSHeapBroker* broker, |
999 | ZoneHandleSet<Map> receiver_maps, |
1000 | ElementsKind* kind_return) { |
1001 | DCHECK_NE(0, receiver_maps.size()); |
1002 | *kind_return = MapRef(broker, receiver_maps[0]).elements_kind(); |
1003 | for (auto receiver_map : receiver_maps) { |
1004 | MapRef map(broker, receiver_map); |
1005 | if (!map.supports_fast_array_iteration() || |
1006 | !UnionElementsKindUptoSize(kind_return, map.elements_kind())) { |
1007 | return false; |
1008 | } |
1009 | } |
1010 | return true; |
1011 | } |
1012 | |
1013 | 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 | *kind_return = MapRef(broker, receiver_maps[0]).elements_kind(); |
1019 | for (auto receiver_map : receiver_maps) { |
1020 | MapRef map(broker, receiver_map); |
1021 | if (!map.supports_fast_array_resize()) return false; |
1022 | if (builtin_is_push) { |
1023 | 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 | if (map.elements_kind() == HOLEY_DOUBLE_ELEMENTS || |
1030 | !UnionElementsKindUptoSize(kind_return, map.elements_kind())) { |
1031 | return false; |
1032 | } |
1033 | } |
1034 | } |
1035 | return true; |
1036 | } |
1037 | } // namespace |
1038 | |
1039 | Reduction JSCallReducer::ReduceArrayForEach( |
1040 | Node* node, const SharedFunctionInfoRef& shared) { |
1041 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
1042 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
1043 | CallParameters const& p = CallParametersOf(node->op()); |
1044 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
1045 | return NoChange(); |
1046 | } |
1047 | |
1048 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
1049 | Node* effect = NodeProperties::GetEffectInput(node); |
1050 | Node* control = NodeProperties::GetControlInput(node); |
1051 | Node* context = NodeProperties::GetContextInput(node); |
1052 | |
1053 | // Try to determine the {receiver} map. |
1054 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
1055 | Node* fncallback = node->op()->ValueInputCount() > 2 |
1056 | ? NodeProperties::GetValueInput(node, 2) |
1057 | : jsgraph()->UndefinedConstant(); |
1058 | Node* this_arg = node->op()->ValueInputCount() > 3 |
1059 | ? NodeProperties::GetValueInput(node, 3) |
1060 | : jsgraph()->UndefinedConstant(); |
1061 | ZoneHandleSet<Map> receiver_maps; |
1062 | NodeProperties::InferReceiverMapsResult result = |
1063 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
1064 | &receiver_maps); |
1065 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
1066 | |
1067 | ElementsKind kind; |
1068 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
1069 | return NoChange(); |
1070 | } |
1071 | |
1072 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
1073 | |
1074 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
1075 | result, receiver_maps, p.feedback(), receiver, effect, control); |
1076 | |
1077 | Node* k = jsgraph()->ZeroConstant(); |
1078 | |
1079 | Node* original_length = effect = graph()->NewNode( |
1080 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
1081 | effect, control); |
1082 | |
1083 | Node* checkpoint_params[] = {receiver, fncallback, this_arg, k, |
1084 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
1093 | Node* check_fail = nullptr; |
1094 | Node* check_throw = nullptr; |
1095 | WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect, |
1096 | &control, &check_fail, &check_throw); |
1097 | |
1098 | // Start the loop. |
1099 | Node* vloop = k = WireInLoopStart(k, &control, &effect); |
1100 | Node *loop = control, *eloop = effect; |
1101 | checkpoint_params[3] = k; |
1102 | |
1103 | Node* continue_test = |
1104 | graph()->NewNode(simplified()->NumberLessThan(), k, original_length); |
1105 | Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone), |
1106 | continue_test, control); |
1107 | |
1108 | Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); |
1109 | Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); |
1110 | 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 | outer_frame_state, ContinuationFrameStateMode::EAGER); |
1116 | |
1117 | effect = |
1118 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
1119 | |
1120 | // Make sure the map hasn't changed during the iteration |
1121 | effect = |
1122 | graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, |
1123 | receiver_maps, p.feedback()), |
1124 | receiver, effect, control); |
1125 | |
1126 | Node* element = |
1127 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
1128 | |
1129 | Node* next_k = |
1130 | graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); |
1131 | checkpoint_params[3] = next_k; |
1132 | |
1133 | Node* hole_true = nullptr; |
1134 | Node* hole_false = nullptr; |
1135 | Node* effect_true = effect; |
1136 | |
1137 | 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 | if (IsDoubleElementsKind(kind)) { |
1142 | check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); |
1143 | } else { |
1144 | check = graph()->NewNode(simplified()->ReferenceEqual(), element, |
1145 | jsgraph()->TheHoleConstant()); |
1146 | } |
1147 | Node* branch = |
1148 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
1149 | hole_true = graph()->NewNode(common()->IfTrue(), branch); |
1150 | hole_false = graph()->NewNode(common()->IfFalse(), branch); |
1151 | 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 | element = effect = graph()->NewNode( |
1157 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
1164 | |
1165 | control = effect = graph()->NewNode( |
1166 | javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, |
1167 | receiver, context, frame_state, effect, control); |
1168 | |
1169 | // Rewire potential exception edges. |
1170 | Node* on_exception = nullptr; |
1171 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
1172 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
1173 | &check_fail, &control); |
1174 | } |
1175 | |
1176 | if (IsHoleyElementsKind(kind)) { |
1177 | Node* after_call_control = control; |
1178 | Node* after_call_effect = effect; |
1179 | control = hole_true; |
1180 | effect = effect_true; |
1181 | |
1182 | control = graph()->NewNode(common()->Merge(2), control, after_call_control); |
1183 | effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, |
1184 | control); |
1185 | } |
1186 | |
1187 | WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); |
1188 | |
1189 | control = if_false; |
1190 | 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 | control = graph()->NewNode(common()->LoopExit(), control, loop); |
1195 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
1203 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
1204 | |
1205 | ReplaceWithValue(node, jsgraph()->UndefinedConstant(), effect, control); |
1206 | return Replace(jsgraph()->UndefinedConstant()); |
1207 | } |
1208 | |
1209 | Node* JSCallReducer::InsertMapChecksIfUnreliableReceiverMaps( |
1210 | NodeProperties::InferReceiverMapsResult result, |
1211 | ZoneHandleSet<Map> const& receiver_maps, VectorSlotPair const& feedback, |
1212 | Node* receiver, Node* effect, Node* control) { |
1213 | if (result == NodeProperties::kUnreliableReceiverMaps) { |
1214 | effect = graph()->NewNode( |
1215 | simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps, feedback), |
1216 | receiver, effect, control); |
1217 | } |
1218 | return effect; |
1219 | } |
1220 | |
1221 | Reduction JSCallReducer::ReduceArrayReduce( |
1222 | Node* node, ArrayReduceDirection direction, |
1223 | const SharedFunctionInfoRef& shared) { |
1224 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
1225 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
1226 | CallParameters const& p = CallParametersOf(node->op()); |
1227 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
1228 | return NoChange(); |
1229 | } |
1230 | bool left = direction == ArrayReduceDirection::kLeft; |
1231 | |
1232 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
1233 | Node* effect = NodeProperties::GetEffectInput(node); |
1234 | Node* control = NodeProperties::GetControlInput(node); |
1235 | Node* context = NodeProperties::GetContextInput(node); |
1236 | |
1237 | // Try to determine the {receiver} map. |
1238 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
1239 | Node* fncallback = node->op()->ValueInputCount() > 2 |
1240 | ? NodeProperties::GetValueInput(node, 2) |
1241 | : jsgraph()->UndefinedConstant(); |
1242 | |
1243 | ZoneHandleSet<Map> receiver_maps; |
1244 | NodeProperties::InferReceiverMapsResult result = |
1245 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
1246 | &receiver_maps); |
1247 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
1248 | |
1249 | ElementsKind kind; |
1250 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
1251 | return NoChange(); |
1252 | } |
1253 | |
1254 | std::function<Node*(Node*)> hole_check = [this, kind](Node* element) { |
1255 | if (IsDoubleElementsKind(kind)) { |
1256 | return graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); |
1257 | } else { |
1258 | return graph()->NewNode(simplified()->ReferenceEqual(), element, |
1259 | jsgraph()->TheHoleConstant()); |
1260 | } |
1261 | }; |
1262 | |
1263 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
1264 | |
1265 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
1266 | result, receiver_maps, p.feedback(), receiver, effect, control); |
1267 | |
1268 | Node* original_length = effect = graph()->NewNode( |
1269 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(PACKED_ELEMENTS)), |
1270 | receiver, effect, control); |
1271 | |
1272 | Node* initial_index = |
1273 | left ? jsgraph()->ZeroConstant() |
1274 | : graph()->NewNode(simplified()->NumberSubtract(), original_length, |
1275 | jsgraph()->OneConstant()); |
1276 | const Operator* next_op = |
1277 | left ? simplified()->NumberAdd() : simplified()->NumberSubtract(); |
1278 | Node* k = initial_index; |
1279 | |
1280 | Node* check_frame_state; |
1281 | { |
1282 | Builtins::Name builtin_lazy = |
1283 | left ? Builtins::kArrayReduceLoopLazyDeoptContinuation |
1284 | : Builtins::kArrayReduceRightLoopLazyDeoptContinuation; |
1285 | Node* checkpoint_params[] = {receiver, fncallback, k, original_length, |
1286 | 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 | ContinuationFrameStateMode::LAZY); |
1292 | } |
1293 | Node* check_fail = nullptr; |
1294 | 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 | &control, &check_fail, &check_throw); |
1300 | |
1301 | // Set initial accumulator value |
1302 | Node* cur = jsgraph()->TheHoleConstant(); |
1303 | |
1304 | if (node->op()->ValueInputCount() > 3) { |
1305 | 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 | : Builtins::kArrayReduceRightPreLoopEagerDeoptContinuation; |
1312 | 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 | ContinuationFrameStateMode::EAGER); |
1319 | |
1320 | Node* vloop = k = WireInLoopStart(k, &control, &effect); |
1321 | Node* loop = control; |
1322 | Node* eloop = effect; |
1323 | effect = graph()->NewNode(common()->Checkpoint(), |
1324 | find_first_element_frame_state, effect, control); |
1325 | Node* continue_test = |
1326 | left ? graph()->NewNode(simplified()->NumberLessThan(), k, |
1327 | original_length) |
1328 | : graph()->NewNode(simplified()->NumberLessThanOrEqual(), |
1329 | jsgraph()->ZeroConstant(), k); |
1330 | effect = graph()->NewNode( |
1331 | simplified()->CheckIf(DeoptimizeReason::kNoInitialElement), |
1332 | continue_test, effect, control); |
1333 | |
1334 | cur = SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
1335 | Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant()); |
1336 | |
1337 | Node* hole_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
1338 | hole_check(cur), control); |
1339 | Node* found_el = graph()->NewNode(common()->IfFalse(), hole_branch); |
1340 | control = found_el; |
1341 | Node* is_hole = graph()->NewNode(common()->IfTrue(), hole_branch); |
1342 | |
1343 | WireInLoopEnd(loop, eloop, vloop, next_k, is_hole, effect); |
1344 | // We did the hole-check, so exclude hole from the type. |
1345 | cur = effect = graph()->NewNode(common()->TypeGuard(Type::NonInternal()), |
1346 | cur, effect, control); |
1347 | k = next_k; |
1348 | } |
1349 | |
1350 | // Start the loop. |
1351 | Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); |
1352 | Node* eloop = effect = |
1353 | graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
1354 | Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); |
1355 | NodeProperties::MergeControlToEnd(graph(), common(), terminate); |
1356 | Node* kloop = k = graph()->NewNode( |
1357 | common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop); |
1358 | Node* curloop = cur = graph()->NewNode( |
1359 | common()->Phi(MachineRepresentation::kTagged, 2), cur, cur, loop); |
1360 | |
1361 | control = loop; |
1362 | effect = eloop; |
1363 | |
1364 | Node* continue_test = |
1365 | left |
1366 | ? graph()->NewNode(simplified()->NumberLessThan(), k, original_length) |
1367 | : graph()->NewNode(simplified()->NumberLessThanOrEqual(), |
1368 | jsgraph()->ZeroConstant(), k); |
1369 | |
1370 | Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone), |
1371 | continue_test, control); |
1372 | |
1373 | Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); |
1374 | Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); |
1375 | control = if_true; |
1376 | |
1377 | { |
1378 | Builtins::Name builtin_eager = |
1379 | left ? Builtins::kArrayReduceLoopEagerDeoptContinuation |
1380 | : Builtins::kArrayReduceRightLoopEagerDeoptContinuation; |
1381 | Node* checkpoint_params[] = {receiver, fncallback, k, original_length, |
1382 | 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 | ContinuationFrameStateMode::EAGER); |
1388 | effect = |
1389 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
1390 | } |
1391 | |
1392 | // Make sure the map hasn't changed during the iteration |
1393 | effect = graph()->NewNode( |
1394 | simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps), receiver, |
1395 | effect, control); |
1396 | |
1397 | Node* element = |
1398 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
1399 | |
1400 | Node* next_k = graph()->NewNode(next_op, k, jsgraph()->OneConstant()); |
1401 | |
1402 | Node* hole_true = nullptr; |
1403 | Node* hole_false = nullptr; |
1404 | Node* effect_true = effect; |
1405 | |
1406 | if (IsHoleyElementsKind(kind)) { |
1407 | // Holey elements kind require a hole check and skipping of the element in |
1408 | // the case of a hole. |
1409 | Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
1410 | hole_check(element), control); |
1411 | hole_true = graph()->NewNode(common()->IfTrue(), branch); |
1412 | hole_false = graph()->NewNode(common()->IfFalse(), branch); |
1413 | 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 | element = effect = graph()->NewNode( |
1419 | common()->TypeGuard(Type::NonInternal()), element, effect, control); |
1420 | } |
1421 | |
1422 | Node* next_cur; |
1423 | { |
1424 | Builtins::Name builtin_lazy = |
1425 | left ? Builtins::kArrayReduceLoopLazyDeoptContinuation |
1426 | : Builtins::kArrayReduceRightLoopLazyDeoptContinuation; |
1427 | Node* checkpoint_params[] = {receiver, fncallback, next_k, original_length, |
1428 | 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 | ContinuationFrameStateMode::LAZY); |
1434 | |
1435 | next_cur = control = effect = |
1436 | graph()->NewNode(javascript()->Call(6, p.frequency()), fncallback, |
1437 | jsgraph()->UndefinedConstant(), cur, element, k, |
1438 | receiver, context, frame_state, effect, control); |
1439 | } |
1440 | |
1441 | // Rewire potential exception edges. |
1442 | Node* on_exception = nullptr; |
1443 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
1444 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
1445 | &check_fail, &control); |
1446 | } |
1447 | |
1448 | if (IsHoleyElementsKind(kind)) { |
1449 | Node* after_call_control = control; |
1450 | Node* after_call_effect = effect; |
1451 | control = hole_true; |
1452 | effect = effect_true; |
1453 | |
1454 | control = graph()->NewNode(common()->Merge(2), control, after_call_control); |
1455 | effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, |
1456 | control); |
1457 | next_cur = |
1458 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), cur, |
1459 | next_cur, control); |
1460 | } |
1461 | |
1462 | k = next_k; |
1463 | cur = next_cur; |
1464 | |
1465 | loop->ReplaceInput(1, control); |
1466 | kloop->ReplaceInput(1, k); |
1467 | curloop->ReplaceInput(1, cur); |
1468 | eloop->ReplaceInput(1, effect); |
1469 | |
1470 | control = if_false; |
1471 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
1479 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
1480 | |
1481 | ReplaceWithValue(node, curloop, effect, control); |
1482 | return Replace(curloop); |
1483 | } |
1484 | |
1485 | Reduction JSCallReducer::ReduceArrayMap(Node* node, |
1486 | const SharedFunctionInfoRef& shared) { |
1487 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
1488 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
1489 | CallParameters const& p = CallParametersOf(node->op()); |
1490 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
1491 | return NoChange(); |
1492 | } |
1493 | |
1494 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
1495 | Node* effect = NodeProperties::GetEffectInput(node); |
1496 | Node* control = NodeProperties::GetControlInput(node); |
1497 | Node* context = NodeProperties::GetContextInput(node); |
1498 | |
1499 | // Try to determine the {receiver} map. |
1500 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
1501 | Node* fncallback = node->op()->ValueInputCount() > 2 |
1502 | ? NodeProperties::GetValueInput(node, 2) |
1503 | : jsgraph()->UndefinedConstant(); |
1504 | Node* this_arg = node->op()->ValueInputCount() > 3 |
1505 | ? NodeProperties::GetValueInput(node, 3) |
1506 | : jsgraph()->UndefinedConstant(); |
1507 | ZoneHandleSet<Map> receiver_maps; |
1508 | NodeProperties::InferReceiverMapsResult result = |
1509 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
1510 | &receiver_maps); |
1511 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
1512 | |
1513 | ElementsKind kind; |
1514 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
1515 | return NoChange(); |
1516 | } |
1517 | |
1518 | if (!dependencies()->DependOnArraySpeciesProtector()) return NoChange(); |
1519 | if (IsHoleyElementsKind(kind)) { |
1520 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
1521 | } |
1522 | |
1523 | Node* array_constructor = jsgraph()->Constant( |
1524 | native_context().GetInitialJSArrayMap(kind).GetConstructor()); |
1525 | |
1526 | Node* k = jsgraph()->ZeroConstant(); |
1527 | |
1528 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
1529 | result, receiver_maps, p.feedback(), receiver, effect, control); |
1530 | |
1531 | Node* original_length = effect = graph()->NewNode( |
1532 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
1533 | 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 | original_length = effect = graph()->NewNode( |
1539 | simplified()->CheckBounds(p.feedback()), original_length, |
1540 | 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 | Node* a = control = effect = graph()->NewNode( |
1546 | javascript()->CreateArray(1, MaybeHandle<AllocationSite>()), |
1547 | array_constructor, array_constructor, original_length, context, |
1548 | outer_frame_state, effect, control); |
1549 | |
1550 | Node* checkpoint_params[] = {receiver, fncallback, this_arg, |
1551 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
1560 | Node* check_fail = nullptr; |
1561 | Node* check_throw = nullptr; |
1562 | WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, effect, |
1563 | &control, &check_fail, &check_throw); |
1564 | |
1565 | // Start the loop. |
1566 | Node* vloop = k = WireInLoopStart(k, &control, &effect); |
1567 | Node *loop = control, *eloop = effect; |
1568 | checkpoint_params[4] = k; |
1569 | |
1570 | Node* continue_test = |
1571 | graph()->NewNode(simplified()->NumberLessThan(), k, original_length); |
1572 | Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone), |
1573 | continue_test, control); |
1574 | |
1575 | Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); |
1576 | Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); |
1577 | 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 | outer_frame_state, ContinuationFrameStateMode::EAGER); |
1583 | |
1584 | effect = |
1585 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
1586 | |
1587 | // Make sure the map hasn't changed during the iteration |
1588 | effect = |
1589 | graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, |
1590 | receiver_maps, p.feedback()), |
1591 | receiver, effect, control); |
1592 | |
1593 | Node* element = |
1594 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
1595 | |
1596 | Node* next_k = |
1597 | graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); |
1598 | |
1599 | Node* hole_true = nullptr; |
1600 | Node* hole_false = nullptr; |
1601 | Node* effect_true = effect; |
1602 | |
1603 | 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 | if (IsDoubleElementsKind(kind)) { |
1608 | check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); |
1609 | } else { |
1610 | check = graph()->NewNode(simplified()->ReferenceEqual(), element, |
1611 | jsgraph()->TheHoleConstant()); |
1612 | } |
1613 | Node* branch = |
1614 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
1615 | hole_true = graph()->NewNode(common()->IfTrue(), branch); |
1616 | hole_false = graph()->NewNode(common()->IfFalse(), branch); |
1617 | 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 | element = effect = graph()->NewNode( |
1623 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
1632 | |
1633 | Node* callback_value = control = effect = graph()->NewNode( |
1634 | javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, |
1635 | receiver, context, frame_state, effect, control); |
1636 | |
1637 | // Rewire potential exception edges. |
1638 | Node* on_exception = nullptr; |
1639 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
1640 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
1641 | &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 | native_context().GetInitialJSArrayMap(HOLEY_DOUBLE_ELEMENTS); |
1649 | MapRef holey_map = native_context().GetInitialJSArrayMap(HOLEY_ELEMENTS); |
1650 | effect = graph()->NewNode(simplified()->TransitionAndStoreElement( |
1651 | holey_double_map.object(), holey_map.object()), |
1652 | a, k, callback_value, effect, control); |
1653 | |
1654 | if (IsHoleyElementsKind(kind)) { |
1655 | Node* after_call_and_store_control = control; |
1656 | Node* after_call_and_store_effect = effect; |
1657 | control = hole_true; |
1658 | effect = effect_true; |
1659 | |
1660 | control = graph()->NewNode(common()->Merge(2), control, |
1661 | after_call_and_store_control); |
1662 | effect = graph()->NewNode(common()->EffectPhi(2), effect, |
1663 | after_call_and_store_effect, control); |
1664 | } |
1665 | |
1666 | WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); |
1667 | |
1668 | control = if_false; |
1669 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
1677 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
1678 | |
1679 | ReplaceWithValue(node, a, effect, control); |
1680 | return Replace(a); |
1681 | } |
1682 | |
1683 | Reduction JSCallReducer::ReduceArrayFilter( |
1684 | Node* node, const SharedFunctionInfoRef& shared) { |
1685 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
1686 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
1687 | CallParameters const& p = CallParametersOf(node->op()); |
1688 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
1689 | return NoChange(); |
1690 | } |
1691 | |
1692 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
1693 | Node* effect = NodeProperties::GetEffectInput(node); |
1694 | Node* control = NodeProperties::GetControlInput(node); |
1695 | Node* context = NodeProperties::GetContextInput(node); |
1696 | // Try to determine the {receiver} map. |
1697 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
1698 | Node* fncallback = node->op()->ValueInputCount() > 2 |
1699 | ? NodeProperties::GetValueInput(node, 2) |
1700 | : jsgraph()->UndefinedConstant(); |
1701 | Node* this_arg = node->op()->ValueInputCount() > 3 |
1702 | ? NodeProperties::GetValueInput(node, 3) |
1703 | : jsgraph()->UndefinedConstant(); |
1704 | ZoneHandleSet<Map> receiver_maps; |
1705 | NodeProperties::InferReceiverMapsResult result = |
1706 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
1707 | &receiver_maps); |
1708 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
1709 | |
1710 | ElementsKind kind; |
1711 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
1712 | return NoChange(); |
1713 | } |
1714 | |
1715 | // The output array is packed (filter doesn't visit holes). |
1716 | const ElementsKind packed_kind = GetPackedElementsKind(kind); |
1717 | |
1718 | if (!dependencies()->DependOnArraySpeciesProtector()) return NoChange(); |
1719 | if (IsHoleyElementsKind(kind)) { |
1720 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
1721 | } |
1722 | |
1723 | MapRef initial_map = native_context().GetInitialJSArrayMap(packed_kind); |
1724 | |
1725 | Node* k = jsgraph()->ZeroConstant(); |
1726 | Node* to = jsgraph()->ZeroConstant(); |
1727 | |
1728 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
1729 | result, receiver_maps, p.feedback(), receiver, effect, control); |
1730 | |
1731 | Node* a; // Construct the output array. |
1732 | { |
1733 | AllocationBuilder ab(jsgraph(), effect, control); |
1734 | ab.Allocate(initial_map.instance_size(), AllocationType::kYoung, |
1735 | Type::Array()); |
1736 | ab.Store(AccessBuilder::ForMap(), initial_map); |
1737 | Node* empty_fixed_array = jsgraph()->EmptyFixedArrayConstant(); |
1738 | ab.Store(AccessBuilder::ForJSObjectPropertiesOrHash(), empty_fixed_array); |
1739 | ab.Store(AccessBuilder::ForJSObjectElements(), empty_fixed_array); |
1740 | ab.Store(AccessBuilder::ForJSArrayLength(packed_kind), |
1741 | jsgraph()->ZeroConstant()); |
1742 | for (int i = 0; i < initial_map.GetInObjectProperties(); ++i) { |
1743 | ab.Store(AccessBuilder::ForJSObjectInObjectProperty(initial_map, i), |
1744 | jsgraph()->UndefinedConstant()); |
1745 | } |
1746 | a = effect = ab.Finish(); |
1747 | } |
1748 | |
1749 | Node* original_length = effect = graph()->NewNode( |
1750 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
1751 | 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 | Node* check_fail = nullptr; |
1756 | 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 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
1772 | WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, |
1773 | effect, &control, &check_fail, &check_throw); |
1774 | } |
1775 | |
1776 | // Start the loop. |
1777 | Node* vloop = k = WireInLoopStart(k, &control, &effect); |
1778 | Node *loop = control, *eloop = effect; |
1779 | Node* v_to_loop = to = graph()->NewNode( |
1780 | common()->Phi(MachineRepresentation::kTaggedSigned, 2), to, to, loop); |
1781 | |
1782 | Node* continue_test = |
1783 | graph()->NewNode(simplified()->NumberLessThan(), k, original_length); |
1784 | Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone), |
1785 | continue_test, control); |
1786 | |
1787 | Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); |
1788 | Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); |
1789 | control = if_true; |
1790 | |
1791 | { |
1792 | Node* checkpoint_params[] = {receiver, fncallback, this_arg, a, |
1793 | 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 | outer_frame_state, ContinuationFrameStateMode::EAGER); |
1800 | |
1801 | effect = |
1802 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
1803 | } |
1804 | |
1805 | // Make sure the map hasn't changed during the iteration. |
1806 | effect = |
1807 | graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, |
1808 | receiver_maps, p.feedback()), |
1809 | receiver, effect, control); |
1810 | |
1811 | Node* element = |
1812 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
1813 | |
1814 | Node* next_k = |
1815 | graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); |
1816 | |
1817 | Node* hole_true = nullptr; |
1818 | Node* hole_false = nullptr; |
1819 | Node* effect_true = effect; |
1820 | Node* hole_true_vto = to; |
1821 | |
1822 | 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 | if (IsDoubleElementsKind(kind)) { |
1827 | check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); |
1828 | } else { |
1829 | check = graph()->NewNode(simplified()->ReferenceEqual(), element, |
1830 | jsgraph()->TheHoleConstant()); |
1831 | } |
1832 | Node* branch = |
1833 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
1834 | hole_true = graph()->NewNode(common()->IfTrue(), branch); |
1835 | hole_false = graph()->NewNode(common()->IfFalse(), branch); |
1836 | 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 | element = effect = graph()->NewNode( |
1842 | 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 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
1857 | |
1858 | callback_value = control = effect = graph()->NewNode( |
1859 | javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, |
1860 | receiver, context, frame_state, effect, control); |
1861 | } |
1862 | |
1863 | // Rewire potential exception edges. |
1864 | Node* on_exception = nullptr; |
1865 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
1866 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
1867 | &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 | 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 | outer_frame_state, ContinuationFrameStateMode::EAGER); |
1886 | |
1887 | effect = |
1888 | 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 | callback_value); |
1896 | |
1897 | if (IsHoleyElementsKind(kind)) { |
1898 | Node* after_call_control = control; |
1899 | Node* after_call_effect = effect; |
1900 | control = hole_true; |
1901 | effect = effect_true; |
1902 | |
1903 | control = graph()->NewNode(common()->Merge(2), control, after_call_control); |
1904 | effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, |
1905 | control); |
1906 | to = |
1907 | graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2), |
1908 | hole_true_vto, to, control); |
1909 | } |
1910 | |
1911 | WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); |
1912 | v_to_loop->ReplaceInput(1, to); |
1913 | |
1914 | control = if_false; |
1915 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
1923 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
1924 | |
1925 | ReplaceWithValue(node, a, effect, control); |
1926 | return Replace(a); |
1927 | } |
1928 | |
1929 | Reduction JSCallReducer::ReduceArrayFind(Node* node, ArrayFindVariant variant, |
1930 | const SharedFunctionInfoRef& shared) { |
1931 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
1932 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
1933 | CallParameters const& p = CallParametersOf(node->op()); |
1934 | 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 | 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 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
1957 | Node* effect = NodeProperties::GetEffectInput(node); |
1958 | Node* control = NodeProperties::GetControlInput(node); |
1959 | Node* context = NodeProperties::GetContextInput(node); |
1960 | |
1961 | // Try to determine the {receiver} map. |
1962 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
1963 | Node* fncallback = node->op()->ValueInputCount() > 2 |
1964 | ? NodeProperties::GetValueInput(node, 2) |
1965 | : jsgraph()->UndefinedConstant(); |
1966 | Node* this_arg = node->op()->ValueInputCount() > 3 |
1967 | ? NodeProperties::GetValueInput(node, 3) |
1968 | : jsgraph()->UndefinedConstant(); |
1969 | ZoneHandleSet<Map> receiver_maps; |
1970 | NodeProperties::InferReceiverMapsResult result = |
1971 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
1972 | &receiver_maps); |
1973 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
1974 | |
1975 | ElementsKind kind; |
1976 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
1977 | return NoChange(); |
1978 | } |
1979 | |
1980 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
1981 | |
1982 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
1983 | result, receiver_maps, p.feedback(), receiver, effect, control); |
1984 | |
1985 | Node* k = jsgraph()->ZeroConstant(); |
1986 | |
1987 | Node* original_length = effect = graph()->NewNode( |
1988 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
1989 | effect, control); |
1990 | |
1991 | Node* checkpoint_params[] = {receiver, fncallback, this_arg, k, |
1992 | 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 | Node* check_fail = nullptr; |
1998 | 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 | ContinuationFrameStateMode::LAZY); |
2004 | WireInCallbackIsCallableCheck(fncallback, context, frame_state, effect, |
2005 | &control, &check_fail, &check_throw); |
2006 | } |
2007 | |
2008 | // Start the loop. |
2009 | Node* vloop = k = WireInLoopStart(k, &control, &effect); |
2010 | Node *loop = control, *eloop = effect; |
2011 | 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 | graph()->NewNode(simplified()->NumberLessThan(), k, original_length); |
2018 | Node* continue_branch = graph()->NewNode( |
2019 | common()->Branch(BranchHint::kNone), continue_test, control); |
2020 | control = graph()->NewNode(common()->IfTrue(), continue_branch); |
2021 | 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 | ContinuationFrameStateMode::EAGER); |
2030 | |
2031 | effect = |
2032 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
2033 | |
2034 | effect = |
2035 | graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, |
2036 | receiver_maps, p.feedback()), |
2037 | receiver, effect, control); |
2038 | } |
2039 | |
2040 | // Load k-th element from receiver. |
2041 | Node* element = |
2042 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
2043 | |
2044 | // Increment k for the next iteration. |
2045 | Node* next_k = checkpoint_params[3] = |
2046 | graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); |
2047 | |
2048 | // Replace holes with undefined. |
2049 | 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 | graph()->NewNode(simplified()->CheckFloat64Hole(mode, p.feedback()), |
2054 | element, effect, control); |
2055 | } else if (IsHoleyElementsKind(kind)) { |
2056 | element = |
2057 | graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), element); |
2058 | } |
2059 | |
2060 | Node* if_found_return_value = |
2061 | (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 | 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 | ContinuationFrameStateMode::LAZY); |
2076 | |
2077 | callback_value = control = effect = graph()->NewNode( |
2078 | javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, |
2079 | receiver, context, frame_state, effect, control); |
2080 | } |
2081 | |
2082 | // Rewire potential exception edges. |
2083 | Node* on_exception = nullptr; |
2084 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
2085 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
2086 | &check_fail, &control); |
2087 | } |
2088 | |
2089 | // Check whether the given callback function returned a truthy value. |
2090 | Node* boolean_result = |
2091 | graph()->NewNode(simplified()->ToBoolean(), callback_value); |
2092 | Node* efound_branch = effect; |
2093 | Node* found_branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
2094 | boolean_result, control); |
2095 | Node* if_found = graph()->NewNode(common()->IfTrue(), found_branch); |
2096 | Node* if_notfound = graph()->NewNode(common()->IfFalse(), found_branch); |
2097 | control = if_notfound; |
2098 | |
2099 | // Close the loop. |
2100 | WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); |
2101 | |
2102 | control = graph()->NewNode(common()->Merge(2), if_found, if_false); |
2103 | effect = |
2104 | graph()->NewNode(common()->EffectPhi(2), efound_branch, eloop, control); |
2105 | |
2106 | Node* if_not_found_value = (variant == ArrayFindVariant::kFind) |
2107 | ? jsgraph()->UndefinedConstant() |
2108 | : jsgraph()->MinusOneConstant(); |
2109 | Node* value = |
2110 | 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 | control = graph()->NewNode(common()->LoopExit(), control, loop); |
2116 | effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); |
2117 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
2125 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
2126 | |
2127 | ReplaceWithValue(node, value, effect, control); |
2128 | return Replace(value); |
2129 | } |
2130 | |
2131 | 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 | graph()->NewNode(simplified()->ToBoolean(), callback_value); |
2137 | Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
2138 | boolean_result, *control); |
2139 | |
2140 | Node* if_true = graph()->NewNode(common()->IfTrue(), boolean_branch); |
2141 | Node* etrue = *effect; |
2142 | Node* vtrue; |
2143 | { |
2144 | // Load the elements backing store of the {receiver}. |
2145 | Node* elements = etrue = graph()->NewNode( |
2146 | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), a, etrue, |
2147 | if_true); |
2148 | |
2149 | DCHECK(TypeCache::Get()->kFixedDoubleArrayLengthType.Is( |
2150 | TypeCache::Get()->kFixedArrayLengthType)); |
2151 | Node* checked_to = etrue = graph()->NewNode( |
2152 | common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), to, etrue, |
2153 | if_true); |
2154 | Node* elements_length = etrue = graph()->NewNode( |
2155 | simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements, |
2156 | etrue, if_true); |
2157 | |
2158 | GrowFastElementsMode mode = |
2159 | IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements |
2160 | : GrowFastElementsMode::kSmiOrObjectElements; |
2161 | 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 | Node* new_length_a = graph()->NewNode(simplified()->NumberAdd(), checked_to, |
2167 | jsgraph()->OneConstant()); |
2168 | |
2169 | etrue = graph()->NewNode( |
2170 | simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), a, |
2171 | new_length_a, etrue, if_true); |
2172 | |
2173 | // Append the value to the {elements}. |
2174 | etrue = graph()->NewNode( |
2175 | simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(kind)), |
2176 | elements, checked_to, element, etrue, if_true); |
2177 | |
2178 | vtrue = new_length_a; |
2179 | } |
2180 | |
2181 | Node* if_false = graph()->NewNode(common()->IfFalse(), boolean_branch); |
2182 | Node* efalse = *effect; |
2183 | Node* vfalse = to; |
2184 | |
2185 | *control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
2186 | *effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, *control); |
2187 | to = graph()->NewNode(common()->Phi(MachineRepresentation::kTaggedSigned, 2), |
2188 | vtrue, vfalse, *control); |
2189 | return to; |
2190 | } |
2191 | |
2192 | void JSCallReducer::WireInCallbackIsCallableCheck( |
2193 | Node* fncallback, Node* context, Node* check_frame_state, Node* effect, |
2194 | Node** control, Node** check_fail, Node** check_throw) { |
2195 | Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), fncallback); |
2196 | Node* check_branch = |
2197 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, *control); |
2198 | *check_fail = graph()->NewNode(common()->IfFalse(), check_branch); |
2199 | *check_throw = *check_fail = graph()->NewNode( |
2200 | javascript()->CallRuntime(Runtime::kThrowTypeError, 2), |
2201 | jsgraph()->Constant( |
2202 | static_cast<int>(MessageTemplate::kCalledNonCallable)), |
2203 | fncallback, context, check_frame_state, effect, *check_fail); |
2204 | *control = graph()->NewNode(common()->IfTrue(), check_branch); |
2205 | } |
2206 | |
2207 | 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 | graph()->NewNode(common()->IfException(), check_throw, *check_fail); |
2215 | *check_fail = graph()->NewNode(common()->IfSuccess(), *check_fail); |
2216 | Node* if_exception1 = |
2217 | graph()->NewNode(common()->IfException(), effect, *control); |
2218 | *control = graph()->NewNode(common()->IfSuccess(), *control); |
2219 | |
2220 | // Join the exception edges. |
2221 | Node* merge = |
2222 | graph()->NewNode(common()->Merge(2), if_exception0, if_exception1); |
2223 | Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception0, |
2224 | if_exception1, merge); |
2225 | Node* phi = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
2226 | if_exception0, if_exception1, merge); |
2227 | ReplaceWithValue(on_exception, phi, ephi, merge); |
2228 | } |
2229 | |
2230 | 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 | Node* length = *effect = graph()->NewNode( |
2236 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
2237 | *effect, control); |
2238 | *k = *effect = graph()->NewNode(simplified()->CheckBounds(feedback), *k, |
2239 | 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 | Node* elements = *effect = graph()->NewNode( |
2245 | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, |
2246 | *effect, control); |
2247 | |
2248 | Node* element = *effect = graph()->NewNode( |
2249 | simplified()->LoadElement(AccessBuilder::ForFixedArrayElement( |
2250 | kind, LoadSensitivity::kCritical)), |
2251 | elements, *k, *effect, control); |
2252 | return element; |
2253 | } |
2254 | |
2255 | Reduction JSCallReducer::ReduceArrayEvery(Node* node, |
2256 | const SharedFunctionInfoRef& shared) { |
2257 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
2258 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
2259 | CallParameters const& p = CallParametersOf(node->op()); |
2260 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
2261 | return NoChange(); |
2262 | } |
2263 | |
2264 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
2265 | Node* effect = NodeProperties::GetEffectInput(node); |
2266 | Node* control = NodeProperties::GetControlInput(node); |
2267 | Node* context = NodeProperties::GetContextInput(node); |
2268 | // Try to determine the {receiver} map. |
2269 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
2270 | Node* fncallback = node->op()->ValueInputCount() > 2 |
2271 | ? NodeProperties::GetValueInput(node, 2) |
2272 | : jsgraph()->UndefinedConstant(); |
2273 | Node* this_arg = node->op()->ValueInputCount() > 3 |
2274 | ? NodeProperties::GetValueInput(node, 3) |
2275 | : jsgraph()->UndefinedConstant(); |
2276 | ZoneHandleSet<Map> receiver_maps; |
2277 | NodeProperties::InferReceiverMapsResult result = |
2278 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
2279 | &receiver_maps); |
2280 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
2281 | |
2282 | ElementsKind kind; |
2283 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
2284 | return NoChange(); |
2285 | } |
2286 | |
2287 | if (!dependencies()->DependOnArraySpeciesProtector()) return NoChange(); |
2288 | if (IsHoleyElementsKind(kind)) { |
2289 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
2290 | } |
2291 | |
2292 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
2293 | result, receiver_maps, p.feedback(), receiver, effect, control); |
2294 | |
2295 | Node* k = jsgraph()->ZeroConstant(); |
2296 | |
2297 | Node* original_length = effect = graph()->NewNode( |
2298 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
2299 | 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 | Node* check_fail = nullptr; |
2304 | 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 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
2317 | WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, |
2318 | effect, &control, &check_fail, &check_throw); |
2319 | } |
2320 | |
2321 | // Start the loop. |
2322 | Node* vloop = k = WireInLoopStart(k, &control, &effect); |
2323 | Node *loop = control, *eloop = effect; |
2324 | |
2325 | Node* continue_test = |
2326 | graph()->NewNode(simplified()->NumberLessThan(), k, original_length); |
2327 | Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone), |
2328 | continue_test, control); |
2329 | |
2330 | Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); |
2331 | Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); |
2332 | control = if_true; |
2333 | |
2334 | { |
2335 | Node* checkpoint_params[] = {receiver, fncallback, this_arg, k, |
2336 | 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 | outer_frame_state, ContinuationFrameStateMode::EAGER); |
2343 | |
2344 | effect = |
2345 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
2346 | } |
2347 | |
2348 | // Make sure the map hasn't changed during the iteration. |
2349 | effect = |
2350 | graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, |
2351 | receiver_maps, p.feedback()), |
2352 | receiver, effect, control); |
2353 | |
2354 | Node* element = |
2355 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
2356 | |
2357 | Node* next_k = |
2358 | graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); |
2359 | |
2360 | Node* hole_true = nullptr; |
2361 | Node* hole_false = nullptr; |
2362 | Node* effect_true = effect; |
2363 | |
2364 | 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 | if (IsDoubleElementsKind(kind)) { |
2369 | check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); |
2370 | } else { |
2371 | check = graph()->NewNode(simplified()->ReferenceEqual(), element, |
2372 | jsgraph()->TheHoleConstant()); |
2373 | } |
2374 | Node* branch = |
2375 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
2376 | hole_true = graph()->NewNode(common()->IfTrue(), branch); |
2377 | hole_false = graph()->NewNode(common()->IfFalse(), branch); |
2378 | 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 | element = effect = graph()->NewNode( |
2384 | 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 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
2399 | |
2400 | callback_value = control = effect = graph()->NewNode( |
2401 | javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, |
2402 | receiver, context, frame_state, effect, control); |
2403 | } |
2404 | |
2405 | // Rewire potential exception edges. |
2406 | Node* on_exception = nullptr; |
2407 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
2408 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
2409 | &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 | graph()->NewNode(simplified()->ToBoolean(), callback_value); |
2418 | Node* boolean_branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
2419 | boolean_result, control); |
2420 | if_false_callback = graph()->NewNode(common()->IfFalse(), boolean_branch); |
2421 | efalse_callback = effect; |
2422 | |
2423 | // Nothing to do in the true case. |
2424 | control = graph()->NewNode(common()->IfTrue(), boolean_branch); |
2425 | } |
2426 | |
2427 | if (IsHoleyElementsKind(kind)) { |
2428 | Node* after_call_control = control; |
2429 | Node* after_call_effect = effect; |
2430 | control = hole_true; |
2431 | effect = effect_true; |
2432 | |
2433 | control = graph()->NewNode(common()->Merge(2), control, after_call_control); |
2434 | effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, |
2435 | control); |
2436 | } |
2437 | |
2438 | WireInLoopEnd(loop, eloop, vloop, next_k, control, effect); |
2439 | |
2440 | control = graph()->NewNode(common()->Merge(2), if_false, if_false_callback); |
2441 | effect = |
2442 | graph()->NewNode(common()->EffectPhi(2), eloop, efalse_callback, control); |
2443 | 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 | control = graph()->NewNode(common()->LoopExit(), control, loop); |
2450 | effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); |
2451 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
2459 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
2460 | |
2461 | 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 | Callable GetCallableForArrayIndexOf(ElementsKind elements_kind, |
2470 | Isolate* isolate) { |
2471 | switch (elements_kind) { |
2472 | case PACKED_SMI_ELEMENTS: |
2473 | case HOLEY_SMI_ELEMENTS: |
2474 | case PACKED_ELEMENTS: |
2475 | case HOLEY_ELEMENTS: |
2476 | return Builtins::CallableFor(isolate, Builtins::kArrayIndexOfSmiOrObject); |
2477 | case PACKED_DOUBLE_ELEMENTS: |
2478 | return Builtins::CallableFor(isolate, |
2479 | Builtins::kArrayIndexOfPackedDoubles); |
2480 | default: |
2481 | DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); |
2482 | return Builtins::CallableFor(isolate, |
2483 | 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 | Callable GetCallableForArrayIncludes(ElementsKind elements_kind, |
2490 | Isolate* isolate) { |
2491 | 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 | Builtins::kArrayIncludesSmiOrObject); |
2498 | case PACKED_DOUBLE_ELEMENTS: |
2499 | return Builtins::CallableFor(isolate, |
2500 | Builtins::kArrayIncludesPackedDoubles); |
2501 | default: |
2502 | DCHECK_EQ(HOLEY_DOUBLE_ELEMENTS, elements_kind); |
2503 | return Builtins::CallableFor(isolate, |
2504 | 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 | Reduction JSCallReducer::ReduceArrayIndexOfIncludes( |
2517 | SearchVariant search_variant, Node* node) { |
2518 | CallParameters const& p = CallParametersOf(node->op()); |
2519 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
2520 | return NoChange(); |
2521 | } |
2522 | |
2523 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
2524 | Node* effect = NodeProperties::GetEffectInput(node); |
2525 | Node* control = NodeProperties::GetControlInput(node); |
2526 | |
2527 | ZoneHandleSet<Map> receiver_maps; |
2528 | NodeProperties::InferReceiverMapsResult result = |
2529 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
2530 | &receiver_maps); |
2531 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
2532 | |
2533 | ElementsKind kind; |
2534 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
2535 | return NoChange(); |
2536 | } |
2537 | |
2538 | if (IsHoleyElementsKind(kind)) { |
2539 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
2540 | } |
2541 | |
2542 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
2543 | result, receiver_maps, p.feedback(), receiver, effect, control); |
2544 | |
2545 | Callable const callable = search_variant == SearchVariant::kIndexOf |
2546 | ? GetCallableForArrayIndexOf(kind, isolate()) |
2547 | : GetCallableForArrayIncludes(kind, isolate()); |
2548 | CallDescriptor const* const desc = Linkage::GetStubCallDescriptor( |
2549 | graph()->zone(), callable.descriptor(), |
2550 | callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags, |
2551 | 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 | Node* elements = effect = graph()->NewNode( |
2556 | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, |
2557 | effect, control); |
2558 | Node* search_element = (node->op()->ValueInputCount() >= 3) |
2559 | ? NodeProperties::GetValueInput(node, 2) |
2560 | : jsgraph()->UndefinedConstant(); |
2561 | Node* length = effect = graph()->NewNode( |
2562 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
2563 | effect, control); |
2564 | Node* new_from_index = jsgraph()->ZeroConstant(); |
2565 | if (node->op()->ValueInputCount() >= 4) { |
2566 | Node* from_index = NodeProperties::GetValueInput(node, 3); |
2567 | 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 | 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 | Node* context = NodeProperties::GetContextInput(node); |
2584 | 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 | Reduction JSCallReducer::ReduceArraySome(Node* node, |
2592 | const SharedFunctionInfoRef& shared) { |
2593 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
2594 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
2595 | CallParameters const& p = CallParametersOf(node->op()); |
2596 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
2597 | return NoChange(); |
2598 | } |
2599 | |
2600 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
2601 | Node* effect = NodeProperties::GetEffectInput(node); |
2602 | Node* control = NodeProperties::GetControlInput(node); |
2603 | Node* context = NodeProperties::GetContextInput(node); |
2604 | // Try to determine the {receiver} map. |
2605 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
2606 | Node* fncallback = node->op()->ValueInputCount() > 2 |
2607 | ? NodeProperties::GetValueInput(node, 2) |
2608 | : jsgraph()->UndefinedConstant(); |
2609 | Node* this_arg = node->op()->ValueInputCount() > 3 |
2610 | ? NodeProperties::GetValueInput(node, 3) |
2611 | : jsgraph()->UndefinedConstant(); |
2612 | ZoneHandleSet<Map> receiver_maps; |
2613 | NodeProperties::InferReceiverMapsResult result = |
2614 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
2615 | &receiver_maps); |
2616 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
2617 | |
2618 | ElementsKind kind; |
2619 | if (!CanInlineArrayIteratingBuiltin(broker(), receiver_maps, &kind)) { |
2620 | return NoChange(); |
2621 | } |
2622 | |
2623 | if (!dependencies()->DependOnArraySpeciesProtector()) return NoChange(); |
2624 | if (IsHoleyElementsKind(kind)) { |
2625 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
2626 | } |
2627 | |
2628 | Node* k = jsgraph()->ZeroConstant(); |
2629 | |
2630 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
2631 | result, receiver_maps, p.feedback(), receiver, effect, control); |
2632 | |
2633 | Node* original_length = effect = graph()->NewNode( |
2634 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
2635 | 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 | Node* check_fail = nullptr; |
2640 | 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 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
2653 | WireInCallbackIsCallableCheck(fncallback, context, check_frame_state, |
2654 | effect, &control, &check_fail, &check_throw); |
2655 | } |
2656 | |
2657 | // Start the loop. |
2658 | Node* loop = control = graph()->NewNode(common()->Loop(2), control, control); |
2659 | Node* eloop = effect = |
2660 | graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
2661 | Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); |
2662 | NodeProperties::MergeControlToEnd(graph(), common(), terminate); |
2663 | Node* vloop = k = graph()->NewNode( |
2664 | common()->Phi(MachineRepresentation::kTagged, 2), k, k, loop); |
2665 | |
2666 | Node* continue_test = |
2667 | graph()->NewNode(simplified()->NumberLessThan(), k, original_length); |
2668 | Node* continue_branch = graph()->NewNode(common()->Branch(BranchHint::kNone), |
2669 | continue_test, control); |
2670 | |
2671 | Node* if_true = graph()->NewNode(common()->IfTrue(), continue_branch); |
2672 | Node* if_false = graph()->NewNode(common()->IfFalse(), continue_branch); |
2673 | control = if_true; |
2674 | |
2675 | { |
2676 | Node* checkpoint_params[] = {receiver, fncallback, this_arg, k, |
2677 | 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 | outer_frame_state, ContinuationFrameStateMode::EAGER); |
2684 | |
2685 | effect = |
2686 | graph()->NewNode(common()->Checkpoint(), frame_state, effect, control); |
2687 | } |
2688 | |
2689 | // Make sure the map hasn't changed during the iteration. |
2690 | effect = |
2691 | graph()->NewNode(simplified()->CheckMaps(CheckMapsFlag::kNone, |
2692 | receiver_maps, p.feedback()), |
2693 | receiver, effect, control); |
2694 | |
2695 | Node* element = |
2696 | SafeLoadElement(kind, receiver, control, &effect, &k, p.feedback()); |
2697 | |
2698 | Node* next_k = |
2699 | graph()->NewNode(simplified()->NumberAdd(), k, jsgraph()->OneConstant()); |
2700 | |
2701 | Node* hole_true = nullptr; |
2702 | Node* hole_false = nullptr; |
2703 | Node* effect_true = effect; |
2704 | |
2705 | 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 | if (IsDoubleElementsKind(kind)) { |
2710 | check = graph()->NewNode(simplified()->NumberIsFloat64Hole(), element); |
2711 | } else { |
2712 | check = graph()->NewNode(simplified()->ReferenceEqual(), element, |
2713 | jsgraph()->TheHoleConstant()); |
2714 | } |
2715 | Node* branch = |
2716 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
2717 | hole_true = graph()->NewNode(common()->IfTrue(), branch); |
2718 | hole_false = graph()->NewNode(common()->IfFalse(), branch); |
2719 | 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 | element = effect = graph()->NewNode( |
2725 | 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 | 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 | outer_frame_state, ContinuationFrameStateMode::LAZY); |
2740 | |
2741 | callback_value = control = effect = graph()->NewNode( |
2742 | javascript()->Call(5, p.frequency()), fncallback, this_arg, element, k, |
2743 | receiver, context, frame_state, effect, control); |
2744 | } |
2745 | |
2746 | // Rewire potential exception edges. |
2747 | Node* on_exception = nullptr; |
2748 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
2749 | RewirePostCallbackExceptionEdges(check_throw, on_exception, effect, |
2750 | &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 | graph()->NewNode(simplified()->ToBoolean(), callback_value); |
2759 | Node* boolean_branch = graph()->NewNode( |
2760 | common()->Branch(BranchHint::kFalse), boolean_result, control); |
2761 | if_true_callback = graph()->NewNode(common()->IfTrue(), boolean_branch); |
2762 | etrue_callback = effect; |
2763 | |
2764 | // Nothing to do in the false case. |
2765 | control = graph()->NewNode(common()->IfFalse(), boolean_branch); |
2766 | } |
2767 | |
2768 | if (IsHoleyElementsKind(kind)) { |
2769 | Node* after_call_control = control; |
2770 | Node* after_call_effect = effect; |
2771 | control = hole_true; |
2772 | effect = effect_true; |
2773 | |
2774 | control = graph()->NewNode(common()->Merge(2), control, after_call_control); |
2775 | effect = graph()->NewNode(common()->EffectPhi(2), effect, after_call_effect, |
2776 | control); |
2777 | } |
2778 | |
2779 | loop->ReplaceInput(1, control); |
2780 | vloop->ReplaceInput(1, next_k); |
2781 | eloop->ReplaceInput(1, effect); |
2782 | |
2783 | control = graph()->NewNode(common()->Merge(2), if_false, if_true_callback); |
2784 | effect = |
2785 | graph()->NewNode(common()->EffectPhi(2), eloop, etrue_callback, control); |
2786 | 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 | control = graph()->NewNode(common()->LoopExit(), control, loop); |
2793 | effect = graph()->NewNode(common()->LoopExitEffect(), effect, control); |
2794 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
2802 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
2803 | |
2804 | ReplaceWithValue(node, value, effect, control); |
2805 | return Replace(value); |
2806 | } |
2807 | |
2808 | Reduction JSCallReducer::ReduceCallApiFunction( |
2809 | Node* node, const SharedFunctionInfoRef& shared) { |
2810 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
2811 | CallParameters const& p = CallParametersOf(node->op()); |
2812 | int const argc = static_cast<int>(p.arity()) - 2; |
2813 | Node* global_proxy = |
2814 | jsgraph()->Constant(native_context().global_proxy_object()); |
2815 | Node* receiver = (p.convert_mode() == ConvertReceiverMode::kNullOrUndefined) |
2816 | ? global_proxy |
2817 | : NodeProperties::GetValueInput(node, 1); |
2818 | Node* holder; |
2819 | Node* effect = NodeProperties::GetEffectInput(node); |
2820 | Node* control = NodeProperties::GetControlInput(node); |
2821 | |
2822 | // See if we can optimize this API call to {shared}. |
2823 | Handle<FunctionTemplateInfo> function_template_info( |
2824 | FunctionTemplateInfo::cast(shared.object()->function_data()), isolate()); |
2825 | CallOptimization call_optimization(isolate(), function_template_info); |
2826 | 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 | &receiver_maps); |
2833 | 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 | for (Handle<Map> map : receiver_maps) { |
2854 | MapRef receiver_map(broker(), map); |
2855 | if (!receiver_map.IsJSReceiverMap() || |
2856 | (receiver_map.is_access_check_needed() && |
2857 | !function_template_info->accept_any_receiver())) { |
2858 | 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 | call_optimization.LookupHolderOfExpectedType(receiver_maps[0], &lookup); |
2866 | if (lookup == CallOptimization::kHolderNotFound) return NoChange(); |
2867 | for (size_t i = 1; i < receiver_maps.size(); ++i) { |
2868 | CallOptimization::HolderLookup lookupi; |
2869 | Handle<JSObject> holderi = call_optimization.LookupHolderOfExpectedType( |
2870 | receiver_maps[i], &lookupi); |
2871 | if (lookup != lookupi) return NoChange(); |
2872 | if (!api_holder.is_identical_to(holderi)) return NoChange(); |
2873 | } |
2874 | |
2875 | // Determine the appropriate holder for the {lookup}. |
2876 | holder = lookup == CallOptimization::kHolderFound |
2877 | ? jsgraph()->HeapConstant(api_holder) |
2878 | : receiver; |
2879 | } 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 | 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 | : Builtins::kCallFunctionTemplate_CheckCompatibleReceiver; |
2911 | Callable callable = Builtins::CallableFor(isolate(), builtin_name); |
2912 | auto call_descriptor = Linkage::GetStubCallDescriptor( |
2913 | graph()->zone(), callable.descriptor(), |
2914 | argc + 1 /* implicit receiver */, CallDescriptor::kNeedsFrameState); |
2915 | node->InsertInput(graph()->zone(), 0, |
2916 | jsgraph()->HeapConstant(callable.code())); |
2917 | node->ReplaceInput(1, jsgraph()->HeapConstant(function_template_info)); |
2918 | node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc)); |
2919 | 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 | Callable call_api_callback = CodeFactory::CallApiCallback(isolate()); |
2930 | CallInterfaceDescriptor cid = call_api_callback.descriptor(); |
2931 | auto call_descriptor = Linkage::GetStubCallDescriptor( |
2932 | graph()->zone(), cid, argc + 1 /* implicit receiver */, |
2933 | CallDescriptor::kNeedsFrameState); |
2934 | ApiFunction api_function(v8::ToCData<Address>(call_handler_info->callback())); |
2935 | ExternalReference function_reference = ExternalReference::Create( |
2936 | &api_function, ExternalReference::DIRECT_API_CALL); |
2937 | node->InsertInput(graph()->zone(), 0, |
2938 | jsgraph()->HeapConstant(call_api_callback.code())); |
2939 | node->ReplaceInput(1, jsgraph()->ExternalConstant(function_reference)); |
2940 | node->InsertInput(graph()->zone(), 2, jsgraph()->Constant(argc)); |
2941 | node->InsertInput(graph()->zone(), 3, jsgraph()->Constant(data)); |
2942 | node->InsertInput(graph()->zone(), 4, holder); |
2943 | node->ReplaceInput(5, receiver); // Update receiver input. |
2944 | node->ReplaceInput(8 + argc, effect); // Update effect input. |
2945 | 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 | bool IsSafeArgumentsElements(Node* node) { |
2955 | for (Edge const edge : node->use_edges()) { |
2956 | if (!NodeProperties::IsValueEdge(edge)) continue; |
2957 | if (edge.from()->opcode() != IrOpcode::kLoadField && |
2958 | edge.from()->opcode() != IrOpcode::kLoadElement) { |
2959 | return false; |
2960 | } |
2961 | } |
2962 | return true; |
2963 | } |
2964 | |
2965 | } // namespace |
2966 | |
2967 | 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 | Node* arguments_list = NodeProperties::GetValueInput(node, arity); |
2978 | if (arguments_list->opcode() != IrOpcode::kJSCreateArguments) { |
2979 | return NoChange(); |
2980 | } |
2981 | for (Edge edge : arguments_list->use_edges()) { |
2982 | if (!NodeProperties::IsValueEdge(edge)) continue; |
2983 | Node* const user = edge.from(); |
2984 | 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 | FieldAccess const& access = FieldAccessOf(user->op()); |
2995 | 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 | } else if (access.offset == JSObject::kElementsOffset) { |
3002 | // Ignore safe uses for arguments#elements. |
3003 | if (IsSafeArgumentsElements(user)) continue; |
3004 | } |
3005 | break; |
3006 | } |
3007 | case IrOpcode::kJSCallWithArrayLike: |
3008 | // Ignore uses as argumentsList input to calls with array like. |
3009 | 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 | if (user->InputAt(1) == arguments_list) continue; |
3014 | break; |
3015 | case IrOpcode::kJSCallWithSpread: { |
3016 | // Ignore uses as spread input to calls with spread. |
3017 | CallParameters p = CallParametersOf(user->op()); |
3018 | int const arity = static_cast<int>(p.arity() - 1); |
3019 | 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 | ConstructParameters p = ConstructParametersOf(user->op()); |
3025 | int const arity = static_cast<int>(p.arity() - 2); |
3026 | 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 | 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 | CreateArgumentsType const type = CreateArgumentsTypeOf(arguments_list->op()); |
3043 | Node* frame_state = NodeProperties::GetFrameStateInput(arguments_list); |
3044 | FrameStateInfo state_info = FrameStateInfoOf(frame_state->op()); |
3045 | int start_index = 0; |
3046 | |
3047 | int formal_parameter_count; |
3048 | { |
3049 | Handle<SharedFunctionInfo> shared; |
3050 | if (!state_info.shared_info().ToHandle(&shared)) return NoChange(); |
3051 | formal_parameter_count = SharedFunctionInfoRef(broker(), shared) |
3052 | .internal_formal_parameter_count(); |
3053 | } |
3054 | |
3055 | 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 | if (formal_parameter_count != 0) { |
3060 | Node* effect = NodeProperties::GetEffectInput(node); |
3061 | if (!NodeProperties::NoObservableSideEffectBetween(effect, |
3062 | arguments_list)) { |
3063 | return NoChange(); |
3064 | } |
3065 | } |
3066 | } 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 | if (node->opcode() == IrOpcode::kJSCallWithSpread || |
3074 | node->opcode() == IrOpcode::kJSConstructWithSpread) { |
3075 | if (!dependencies()->DependOnArrayIteratorProtector()) return NoChange(); |
3076 | } |
3077 | |
3078 | // Remove the {arguments_list} input from the {node}. |
3079 | 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 | if (outer_state->opcode() != IrOpcode::kFrameState) { |
3084 | Operator const* op = |
3085 | (node->opcode() == IrOpcode::kJSCallWithArrayLike || |
3086 | node->opcode() == IrOpcode::kJSCallWithSpread) |
3087 | ? javascript()->CallForwardVarargs(arity + 1, start_index) |
3088 | : javascript()->ConstructForwardVarargs(arity + 2, start_index); |
3089 | NodeProperties::ChangeOp(node, op); |
3090 | 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 | FrameStateInfo outer_info = FrameStateInfoOf(outer_state->op()); |
3096 | 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 | for (int i = start_index + 1; i < parameters->InputCount(); ++i) { |
3103 | node->InsertInput(graph()->zone(), static_cast<int>(++arity), |
3104 | parameters->InputAt(i)); |
3105 | } |
3106 | |
3107 | if (node->opcode() == IrOpcode::kJSCallWithArrayLike || |
3108 | node->opcode() == IrOpcode::kJSCallWithSpread) { |
3109 | NodeProperties::ChangeOp( |
3110 | node, javascript()->Call(arity + 1, frequency, feedback)); |
3111 | Reduction const reduction = ReduceJSCall(node); |
3112 | return reduction.Changed() ? reduction : Changed(node); |
3113 | } else { |
3114 | NodeProperties::ChangeOp( |
3115 | node, javascript()->Construct(arity + 2, frequency, feedback)); |
3116 | Node* new_target = NodeProperties::GetValueInput(node, arity + 1); |
3117 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
3118 | Node* context = NodeProperties::GetContextInput(node); |
3119 | Node* effect = NodeProperties::GetEffectInput(node); |
3120 | 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 | graph()->NewNode(simplified()->ObjectIsConstructor(), new_target); |
3127 | Node* check_branch = |
3128 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
3129 | Node* check_fail = graph()->NewNode(common()->IfFalse(), check_branch); |
3130 | 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 | control = graph()->NewNode(common()->IfTrue(), check_branch); |
3135 | NodeProperties::ReplaceControlInput(node, control); |
3136 | |
3137 | // Rewire potential exception edges. |
3138 | Node* on_exception = nullptr; |
3139 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
3140 | // Create appropriate {IfException} and {IfSuccess} nodes. |
3141 | Node* if_exception = |
3142 | graph()->NewNode(common()->IfException(), check_throw, check_fail); |
3143 | check_fail = graph()->NewNode(common()->IfSuccess(), check_fail); |
3144 | |
3145 | // Join the exception edges. |
3146 | Node* merge = |
3147 | graph()->NewNode(common()->Merge(2), if_exception, on_exception); |
3148 | Node* ephi = graph()->NewNode(common()->EffectPhi(2), if_exception, |
3149 | on_exception, merge); |
3150 | Node* phi = |
3151 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
3152 | if_exception, on_exception, merge); |
3153 | ReplaceWithValue(on_exception, phi, ephi, merge); |
3154 | merge->ReplaceInput(1, on_exception); |
3155 | ephi->ReplaceInput(1, on_exception); |
3156 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
3164 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
3165 | |
3166 | Reduction const reduction = ReduceJSConstruct(node); |
3167 | return reduction.Changed() ? reduction : Changed(node); |
3168 | } |
3169 | } |
3170 | |
3171 | namespace { |
3172 | |
3173 | bool ShouldUseCallICFeedback(Node* node) { |
3174 | HeapObjectMatcher m(node); |
3175 | 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 | } else if (m.IsPhi()) { |
3181 | // Protect against endless loops here. |
3182 | Node* control = NodeProperties::GetControlInput(node); |
3183 | 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 | for (int n = 0; n < value_input_count; ++n) { |
3188 | if (ShouldUseCallICFeedback(node->InputAt(n))) return true; |
3189 | } |
3190 | return false; |
3191 | } |
3192 | return true; |
3193 | } |
3194 | |
3195 | base::Optional<HeapObjectRef> GetHeapObjectFeedback( |
3196 | JSHeapBroker* broker, const FeedbackNexus& nexus) { |
3197 | HeapObject object; |
3198 | if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt; |
3199 | return HeapObjectRef(broker, handle(object, broker->isolate())); |
3200 | } |
3201 | |
3202 | } // namespace |
3203 | |
3204 | Reduction JSCallReducer::ReduceJSCall(Node* node) { |
3205 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
3206 | CallParameters const& p = CallParametersOf(node->op()); |
3207 | Node* target = NodeProperties::GetValueInput(node, 0); |
3208 | Node* control = NodeProperties::GetControlInput(node); |
3209 | 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 | if (m.HasValue()) { |
3216 | ObjectRef target_ref = m.Ref(broker()); |
3217 | if (target_ref.IsJSFunction()) { |
3218 | JSFunctionRef function = target_ref.AsJSFunction(); |
3219 | function.Serialize(); |
3220 | |
3221 | // Don't inline cross native context. |
3222 | if (!function.native_context().equals(native_context())) { |
3223 | return NoChange(); |
3224 | } |
3225 | |
3226 | return ReduceJSCall(node, function.shared()); |
3227 | } else if (target_ref.IsJSBoundFunction()) { |
3228 | JSBoundFunctionRef function = target_ref.AsJSBoundFunction(); |
3229 | function.Serialize(); |
3230 | |
3231 | ObjectRef bound_this = function.bound_this(); |
3232 | ConvertReceiverMode const convert_mode = |
3233 | bound_this.IsNullOrUndefined() |
3234 | ? ConvertReceiverMode::kNullOrUndefined |
3235 | : ConvertReceiverMode::kNotNullOrUndefined; |
3236 | |
3237 | // Patch {node} to use [[BoundTargetFunction]] and [[BoundThis]]. |
3238 | NodeProperties::ReplaceValueInput( |
3239 | node, jsgraph()->Constant(function.bound_target_function()), 0); |
3240 | NodeProperties::ReplaceValueInput(node, jsgraph()->Constant(bound_this), |
3241 | 1); |
3242 | |
3243 | // Insert the [[BoundArguments]] for {node}. |
3244 | FixedArrayRef bound_arguments = function.bound_arguments(); |
3245 | for (int i = 0; i < bound_arguments.length(); ++i) { |
3246 | node->InsertInput(graph()->zone(), i + 2, |
3247 | jsgraph()->Constant(bound_arguments.get(i))); |
3248 | arity++; |
3249 | } |
3250 | |
3251 | NodeProperties::ChangeOp( |
3252 | node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), |
3253 | convert_mode)); |
3254 | |
3255 | // Try to further reduce the JSCall {node}. |
3256 | Reduction const reduction = ReduceJSCall(node); |
3257 | 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 | if (target->opcode() == IrOpcode::kJSCreateClosure) { |
3270 | CreateClosureParameters const& p = CreateClosureParametersOf(target->op()); |
3271 | 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 | if (target->opcode() == IrOpcode::kJSCreateBoundFunction) { |
3278 | Node* bound_target_function = NodeProperties::GetValueInput(target, 0); |
3279 | Node* bound_this = NodeProperties::GetValueInput(target, 1); |
3280 | int const bound_arguments_length = |
3281 | static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity()); |
3282 | |
3283 | // Patch the {node} to use [[BoundTargetFunction]] and [[BoundThis]]. |
3284 | NodeProperties::ReplaceValueInput(node, bound_target_function, 0); |
3285 | NodeProperties::ReplaceValueInput(node, bound_this, 1); |
3286 | |
3287 | // Insert the [[BoundArguments]] for {node}. |
3288 | for (int i = 0; i < bound_arguments_length; ++i) { |
3289 | Node* value = NodeProperties::GetValueInput(target, 2 + i); |
3290 | node->InsertInput(graph()->zone(), 2 + i, value); |
3291 | arity++; |
3292 | } |
3293 | |
3294 | // Update the JSCall operator on {node}. |
3295 | ConvertReceiverMode const convert_mode = |
3296 | NodeProperties::CanBeNullOrUndefined(broker(), bound_this, effect) |
3297 | ? ConvertReceiverMode::kAny |
3298 | : ConvertReceiverMode::kNotNullOrUndefined; |
3299 | NodeProperties::ChangeOp( |
3300 | node, javascript()->Call(arity, p.frequency(), VectorSlotPair(), |
3301 | convert_mode)); |
3302 | |
3303 | // Try to further reduce the JSCall {node}. |
3304 | Reduction const reduction = ReduceJSCall(node); |
3305 | return reduction.Changed() ? reduction : Changed(node); |
3306 | } |
3307 | |
3308 | // Extract feedback from the {node} using the FeedbackNexus. |
3309 | if (!p.feedback().IsValid()) return NoChange(); |
3310 | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); |
3311 | if (nexus.IsUninitialized()) { |
3312 | return ReduceSoftDeoptimize( |
3313 | node, DeoptimizeReason::kInsufficientTypeFeedbackForCall); |
3314 | } |
3315 | |
3316 | base::Optional<HeapObjectRef> feedback = |
3317 | GetHeapObjectFeedback(broker(), nexus); |
3318 | if (feedback.has_value() && ShouldUseCallICFeedback(target) && |
3319 | feedback->map().is_callable()) { |
3320 | Node* target_function = jsgraph()->Constant(*feedback); |
3321 | |
3322 | // Check that the {target} is still the {target_function}. |
3323 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, |
3324 | target_function); |
3325 | effect = graph()->NewNode( |
3326 | simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, |
3327 | effect, control); |
3328 | |
3329 | // Specialize the JSCall node to the {target_function}. |
3330 | NodeProperties::ReplaceValueInput(node, target_function, 0); |
3331 | NodeProperties::ReplaceEffectInput(node, effect); |
3332 | |
3333 | // Try to further reduce the JSCall {node}. |
3334 | Reduction const reduction = ReduceJSCall(node); |
3335 | return reduction.Changed() ? reduction : Changed(node); |
3336 | } |
3337 | |
3338 | return NoChange(); |
3339 | } |
3340 | |
3341 | Reduction JSCallReducer::ReduceJSCall(Node* node, |
3342 | const SharedFunctionInfoRef& shared) { |
3343 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
3344 | Node* target = NodeProperties::GetValueInput(node, 0); |
3345 | |
3346 | // Do not reduce calls to functions with break points. |
3347 | if (shared.HasBreakInfo()) return NoChange(); |
3348 | |
3349 | // Raise a TypeError if the {target} is a "classConstructor". |
3350 | if (IsClassConstructor(shared.kind())) { |
3351 | NodeProperties::ReplaceValueInputs(node, target); |
3352 | NodeProperties::ChangeOp( |
3353 | node, javascript()->CallRuntime( |
3354 | Runtime::kThrowConstructorNonCallableError, 1)); |
3355 | return Changed(node); |
3356 | } |
3357 | |
3358 | // Check for known builtin functions. |
3359 | |
3360 | int builtin_id = |
3361 | shared.HasBuiltinId() ? shared.builtin_id() : Builtins::kNoBuiltinId; |
3362 | switch (builtin_id) { |
3363 | case Builtins::kArrayConstructor: |
3364 | return ReduceArrayConstructor(node); |
3365 | case Builtins::kBooleanConstructor: |
3366 | return ReduceBooleanConstructor(node); |
3367 | case Builtins::kFunctionPrototypeApply: |
3368 | return ReduceFunctionPrototypeApply(node); |
3369 | case Builtins::kFastFunctionPrototypeBind: |
3370 | return ReduceFunctionPrototypeBind(node); |
3371 | case Builtins::kFunctionPrototypeCall: |
3372 | return ReduceFunctionPrototypeCall(node); |
3373 | case Builtins::kFunctionPrototypeHasInstance: |
3374 | return ReduceFunctionPrototypeHasInstance(node); |
3375 | case Builtins::kObjectConstructor: |
3376 | return ReduceObjectConstructor(node); |
3377 | case Builtins::kObjectCreate: |
3378 | return ReduceObjectCreate(node); |
3379 | case Builtins::kObjectGetPrototypeOf: |
3380 | return ReduceObjectGetPrototypeOf(node); |
3381 | case Builtins::kObjectIs: |
3382 | return ReduceObjectIs(node); |
3383 | case Builtins::kObjectPrototypeGetProto: |
3384 | return ReduceObjectPrototypeGetProto(node); |
3385 | case Builtins::kObjectPrototypeHasOwnProperty: |
3386 | return ReduceObjectPrototypeHasOwnProperty(node); |
3387 | case Builtins::kObjectPrototypeIsPrototypeOf: |
3388 | return ReduceObjectPrototypeIsPrototypeOf(node); |
3389 | case Builtins::kReflectApply: |
3390 | return ReduceReflectApply(node); |
3391 | case Builtins::kReflectConstruct: |
3392 | return ReduceReflectConstruct(node); |
3393 | case Builtins::kReflectGet: |
3394 | return ReduceReflectGet(node); |
3395 | case Builtins::kReflectGetPrototypeOf: |
3396 | return ReduceReflectGetPrototypeOf(node); |
3397 | case Builtins::kReflectHas: |
3398 | return ReduceReflectHas(node); |
3399 | case Builtins::kArrayForEach: |
3400 | return ReduceArrayForEach(node, shared); |
3401 | case Builtins::kArrayMap: |
3402 | return ReduceArrayMap(node, shared); |
3403 | case Builtins::kArrayFilter: |
3404 | return ReduceArrayFilter(node, shared); |
3405 | case Builtins::kArrayReduce: |
3406 | return ReduceArrayReduce(node, ArrayReduceDirection::kLeft, shared); |
3407 | case Builtins::kArrayReduceRight: |
3408 | return ReduceArrayReduce(node, ArrayReduceDirection::kRight, shared); |
3409 | case Builtins::kArrayPrototypeFind: |
3410 | return ReduceArrayFind(node, ArrayFindVariant::kFind, shared); |
3411 | case Builtins::kArrayPrototypeFindIndex: |
3412 | return ReduceArrayFind(node, ArrayFindVariant::kFindIndex, shared); |
3413 | case Builtins::kArrayEvery: |
3414 | return ReduceArrayEvery(node, shared); |
3415 | case Builtins::kArrayIndexOf: |
3416 | return ReduceArrayIndexOfIncludes(SearchVariant::kIndexOf, node); |
3417 | case Builtins::kArrayIncludes: |
3418 | return ReduceArrayIndexOfIncludes(SearchVariant::kIncludes, node); |
3419 | case Builtins::kArraySome: |
3420 | return ReduceArraySome(node, shared); |
3421 | case Builtins::kArrayPrototypePush: |
3422 | return ReduceArrayPrototypePush(node); |
3423 | case Builtins::kArrayPrototypePop: |
3424 | return ReduceArrayPrototypePop(node); |
3425 | case Builtins::kArrayPrototypeShift: |
3426 | return ReduceArrayPrototypeShift(node); |
3427 | case Builtins::kArrayPrototypeSlice: |
3428 | return ReduceArrayPrototypeSlice(node); |
3429 | case Builtins::kArrayPrototypeEntries: |
3430 | return ReduceArrayIterator(node, IterationKind::kEntries); |
3431 | case Builtins::kArrayPrototypeKeys: |
3432 | return ReduceArrayIterator(node, IterationKind::kKeys); |
3433 | case Builtins::kArrayPrototypeValues: |
3434 | return ReduceArrayIterator(node, IterationKind::kValues); |
3435 | case Builtins::kArrayIteratorPrototypeNext: |
3436 | return ReduceArrayIteratorPrototypeNext(node); |
3437 | case Builtins::kArrayIsArray: |
3438 | return ReduceArrayIsArray(node); |
3439 | case Builtins::kArrayBufferIsView: |
3440 | return ReduceArrayBufferIsView(node); |
3441 | case Builtins::kDataViewPrototypeGetByteLength: |
3442 | return ReduceArrayBufferViewAccessor( |
3443 | node, JS_DATA_VIEW_TYPE, |
3444 | AccessBuilder::ForJSArrayBufferViewByteLength()); |
3445 | case Builtins::kDataViewPrototypeGetByteOffset: |
3446 | return ReduceArrayBufferViewAccessor( |
3447 | node, JS_DATA_VIEW_TYPE, |
3448 | AccessBuilder::ForJSArrayBufferViewByteOffset()); |
3449 | case Builtins::kDataViewPrototypeGetUint8: |
3450 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3451 | ExternalArrayType::kExternalUint8Array); |
3452 | case Builtins::kDataViewPrototypeGetInt8: |
3453 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3454 | ExternalArrayType::kExternalInt8Array); |
3455 | case Builtins::kDataViewPrototypeGetUint16: |
3456 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3457 | ExternalArrayType::kExternalUint16Array); |
3458 | case Builtins::kDataViewPrototypeGetInt16: |
3459 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3460 | ExternalArrayType::kExternalInt16Array); |
3461 | case Builtins::kDataViewPrototypeGetUint32: |
3462 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3463 | ExternalArrayType::kExternalUint32Array); |
3464 | case Builtins::kDataViewPrototypeGetInt32: |
3465 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3466 | ExternalArrayType::kExternalInt32Array); |
3467 | case Builtins::kDataViewPrototypeGetFloat32: |
3468 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3469 | ExternalArrayType::kExternalFloat32Array); |
3470 | case Builtins::kDataViewPrototypeGetFloat64: |
3471 | return ReduceDataViewAccess(node, DataViewAccess::kGet, |
3472 | ExternalArrayType::kExternalFloat64Array); |
3473 | case Builtins::kDataViewPrototypeSetUint8: |
3474 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3475 | ExternalArrayType::kExternalUint8Array); |
3476 | case Builtins::kDataViewPrototypeSetInt8: |
3477 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3478 | ExternalArrayType::kExternalInt8Array); |
3479 | case Builtins::kDataViewPrototypeSetUint16: |
3480 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3481 | ExternalArrayType::kExternalUint16Array); |
3482 | case Builtins::kDataViewPrototypeSetInt16: |
3483 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3484 | ExternalArrayType::kExternalInt16Array); |
3485 | case Builtins::kDataViewPrototypeSetUint32: |
3486 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3487 | ExternalArrayType::kExternalUint32Array); |
3488 | case Builtins::kDataViewPrototypeSetInt32: |
3489 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3490 | ExternalArrayType::kExternalInt32Array); |
3491 | case Builtins::kDataViewPrototypeSetFloat32: |
3492 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3493 | ExternalArrayType::kExternalFloat32Array); |
3494 | case Builtins::kDataViewPrototypeSetFloat64: |
3495 | return ReduceDataViewAccess(node, DataViewAccess::kSet, |
3496 | ExternalArrayType::kExternalFloat64Array); |
3497 | case Builtins::kTypedArrayPrototypeByteLength: |
3498 | return ReduceArrayBufferViewAccessor( |
3499 | node, JS_TYPED_ARRAY_TYPE, |
3500 | AccessBuilder::ForJSArrayBufferViewByteLength()); |
3501 | case Builtins::kTypedArrayPrototypeByteOffset: |
3502 | return ReduceArrayBufferViewAccessor( |
3503 | node, JS_TYPED_ARRAY_TYPE, |
3504 | AccessBuilder::ForJSArrayBufferViewByteOffset()); |
3505 | case Builtins::kTypedArrayPrototypeLength: |
3506 | return ReduceArrayBufferViewAccessor( |
3507 | node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength()); |
3508 | case Builtins::kTypedArrayPrototypeToStringTag: |
3509 | return ReduceTypedArrayPrototypeToStringTag(node); |
3510 | case Builtins::kMathAbs: |
3511 | return ReduceMathUnary(node, simplified()->NumberAbs()); |
3512 | case Builtins::kMathAcos: |
3513 | return ReduceMathUnary(node, simplified()->NumberAcos()); |
3514 | case Builtins::kMathAcosh: |
3515 | return ReduceMathUnary(node, simplified()->NumberAcosh()); |
3516 | case Builtins::kMathAsin: |
3517 | return ReduceMathUnary(node, simplified()->NumberAsin()); |
3518 | case Builtins::kMathAsinh: |
3519 | return ReduceMathUnary(node, simplified()->NumberAsinh()); |
3520 | case Builtins::kMathAtan: |
3521 | return ReduceMathUnary(node, simplified()->NumberAtan()); |
3522 | case Builtins::kMathAtanh: |
3523 | return ReduceMathUnary(node, simplified()->NumberAtanh()); |
3524 | case Builtins::kMathCbrt: |
3525 | return ReduceMathUnary(node, simplified()->NumberCbrt()); |
3526 | case Builtins::kMathCeil: |
3527 | return ReduceMathUnary(node, simplified()->NumberCeil()); |
3528 | case Builtins::kMathCos: |
3529 | return ReduceMathUnary(node, simplified()->NumberCos()); |
3530 | case Builtins::kMathCosh: |
3531 | return ReduceMathUnary(node, simplified()->NumberCosh()); |
3532 | case Builtins::kMathExp: |
3533 | return ReduceMathUnary(node, simplified()->NumberExp()); |
3534 | case Builtins::kMathExpm1: |
3535 | return ReduceMathUnary(node, simplified()->NumberExpm1()); |
3536 | case Builtins::kMathFloor: |
3537 | return ReduceMathUnary(node, simplified()->NumberFloor()); |
3538 | case Builtins::kMathFround: |
3539 | return ReduceMathUnary(node, simplified()->NumberFround()); |
3540 | case Builtins::kMathLog: |
3541 | return ReduceMathUnary(node, simplified()->NumberLog()); |
3542 | case Builtins::kMathLog1p: |
3543 | return ReduceMathUnary(node, simplified()->NumberLog1p()); |
3544 | case Builtins::kMathLog10: |
3545 | return ReduceMathUnary(node, simplified()->NumberLog10()); |
3546 | case Builtins::kMathLog2: |
3547 | return ReduceMathUnary(node, simplified()->NumberLog2()); |
3548 | case Builtins::kMathRound: |
3549 | return ReduceMathUnary(node, simplified()->NumberRound()); |
3550 | case Builtins::kMathSign: |
3551 | return ReduceMathUnary(node, simplified()->NumberSign()); |
3552 | case Builtins::kMathSin: |
3553 | return ReduceMathUnary(node, simplified()->NumberSin()); |
3554 | case Builtins::kMathSinh: |
3555 | return ReduceMathUnary(node, simplified()->NumberSinh()); |
3556 | case Builtins::kMathSqrt: |
3557 | return ReduceMathUnary(node, simplified()->NumberSqrt()); |
3558 | case Builtins::kMathTan: |
3559 | return ReduceMathUnary(node, simplified()->NumberTan()); |
3560 | case Builtins::kMathTanh: |
3561 | return ReduceMathUnary(node, simplified()->NumberTanh()); |
3562 | case Builtins::kMathTrunc: |
3563 | return ReduceMathUnary(node, simplified()->NumberTrunc()); |
3564 | case Builtins::kMathAtan2: |
3565 | return ReduceMathBinary(node, simplified()->NumberAtan2()); |
3566 | case Builtins::kMathPow: |
3567 | return ReduceMathBinary(node, simplified()->NumberPow()); |
3568 | case Builtins::kMathClz32: |
3569 | return ReduceMathClz32(node); |
3570 | case Builtins::kMathImul: |
3571 | return ReduceMathImul(node); |
3572 | case Builtins::kMathMax: |
3573 | return ReduceMathMinMax(node, simplified()->NumberMax(), |
3574 | jsgraph()->Constant(-V8_INFINITY)); |
3575 | case Builtins::kMathMin: |
3576 | return ReduceMathMinMax(node, simplified()->NumberMin(), |
3577 | jsgraph()->Constant(V8_INFINITY)); |
3578 | case Builtins::kNumberIsFinite: |
3579 | return ReduceNumberIsFinite(node); |
3580 | case Builtins::kNumberIsInteger: |
3581 | return ReduceNumberIsInteger(node); |
3582 | case Builtins::kNumberIsSafeInteger: |
3583 | return ReduceNumberIsSafeInteger(node); |
3584 | case Builtins::kNumberIsNaN: |
3585 | return ReduceNumberIsNaN(node); |
3586 | case Builtins::kNumberParseInt: |
3587 | return ReduceNumberParseInt(node); |
3588 | case Builtins::kGlobalIsFinite: |
3589 | return ReduceGlobalIsFinite(node); |
3590 | case Builtins::kGlobalIsNaN: |
3591 | return ReduceGlobalIsNaN(node); |
3592 | case Builtins::kMapPrototypeGet: |
3593 | return ReduceMapPrototypeGet(node); |
3594 | case Builtins::kMapPrototypeHas: |
3595 | return ReduceMapPrototypeHas(node); |
3596 | case Builtins::kRegExpPrototypeTest: |
3597 | return ReduceRegExpPrototypeTest(node); |
3598 | case Builtins::kReturnReceiver: |
3599 | return ReduceReturnReceiver(node); |
3600 | case Builtins::kStringPrototypeIndexOf: |
3601 | return ReduceStringPrototypeIndexOf(node); |
3602 | case Builtins::kStringPrototypeCharAt: |
3603 | return ReduceStringPrototypeCharAt(node); |
3604 | case Builtins::kStringPrototypeCharCodeAt: |
3605 | return ReduceStringPrototypeStringAt(simplified()->StringCharCodeAt(), |
3606 | node); |
3607 | case Builtins::kStringPrototypeCodePointAt: |
3608 | return ReduceStringPrototypeStringAt( |
3609 | simplified()->StringCodePointAt(UnicodeEncoding::UTF32), node); |
3610 | case Builtins::kStringPrototypeSubstring: |
3611 | return ReduceStringPrototypeSubstring(node); |
3612 | case Builtins::kStringPrototypeSlice: |
3613 | return ReduceStringPrototypeSlice(node); |
3614 | case Builtins::kStringPrototypeSubstr: |
3615 | return ReduceStringPrototypeSubstr(node); |
3616 | #ifdef V8_INTL_SUPPORT |
3617 | case Builtins::kStringPrototypeToLowerCaseIntl: |
3618 | return ReduceStringPrototypeToLowerCaseIntl(node); |
3619 | case Builtins::kStringPrototypeToUpperCaseIntl: |
3620 | return ReduceStringPrototypeToUpperCaseIntl(node); |
3621 | #endif // V8_INTL_SUPPORT |
3622 | case Builtins::kStringFromCharCode: |
3623 | return ReduceStringFromCharCode(node); |
3624 | case Builtins::kStringFromCodePoint: |
3625 | return ReduceStringFromCodePoint(node); |
3626 | case Builtins::kStringPrototypeIterator: |
3627 | return ReduceStringPrototypeIterator(node); |
3628 | case Builtins::kStringIteratorPrototypeNext: |
3629 | return ReduceStringIteratorPrototypeNext(node); |
3630 | case Builtins::kStringPrototypeConcat: |
3631 | return ReduceStringPrototypeConcat(node); |
3632 | case Builtins::kTypedArrayPrototypeEntries: |
3633 | return ReduceArrayIterator(node, IterationKind::kEntries); |
3634 | case Builtins::kTypedArrayPrototypeKeys: |
3635 | return ReduceArrayIterator(node, IterationKind::kKeys); |
3636 | case Builtins::kTypedArrayPrototypeValues: |
3637 | return ReduceArrayIterator(node, IterationKind::kValues); |
3638 | case Builtins::kPromiseInternalConstructor: |
3639 | return ReducePromiseInternalConstructor(node); |
3640 | case Builtins::kPromiseInternalReject: |
3641 | return ReducePromiseInternalReject(node); |
3642 | case Builtins::kPromiseInternalResolve: |
3643 | return ReducePromiseInternalResolve(node); |
3644 | case Builtins::kPromisePrototypeCatch: |
3645 | return ReducePromisePrototypeCatch(node); |
3646 | case Builtins::kPromisePrototypeFinally: |
3647 | return ReducePromisePrototypeFinally(node); |
3648 | case Builtins::kPromisePrototypeThen: |
3649 | return ReducePromisePrototypeThen(node); |
3650 | case Builtins::kPromiseResolveTrampoline: |
3651 | return ReducePromiseResolveTrampoline(node); |
3652 | case Builtins::kMapPrototypeEntries: |
3653 | return ReduceCollectionIteration(node, CollectionKind::kMap, |
3654 | IterationKind::kEntries); |
3655 | case Builtins::kMapPrototypeKeys: |
3656 | return ReduceCollectionIteration(node, CollectionKind::kMap, |
3657 | IterationKind::kKeys); |
3658 | case Builtins::kMapPrototypeGetSize: |
3659 | return ReduceCollectionPrototypeSize(node, CollectionKind::kMap); |
3660 | case Builtins::kMapPrototypeValues: |
3661 | return ReduceCollectionIteration(node, CollectionKind::kMap, |
3662 | IterationKind::kValues); |
3663 | case Builtins::kMapIteratorPrototypeNext: |
3664 | return ReduceCollectionIteratorPrototypeNext( |
3665 | node, OrderedHashMap::kEntrySize, factory()->empty_ordered_hash_map(), |
3666 | FIRST_MAP_ITERATOR_TYPE, LAST_MAP_ITERATOR_TYPE); |
3667 | case Builtins::kSetPrototypeEntries: |
3668 | return ReduceCollectionIteration(node, CollectionKind::kSet, |
3669 | IterationKind::kEntries); |
3670 | case Builtins::kSetPrototypeGetSize: |
3671 | return ReduceCollectionPrototypeSize(node, CollectionKind::kSet); |
3672 | case Builtins::kSetPrototypeValues: |
3673 | return ReduceCollectionIteration(node, CollectionKind::kSet, |
3674 | IterationKind::kValues); |
3675 | case Builtins::kSetIteratorPrototypeNext: |
3676 | return ReduceCollectionIteratorPrototypeNext( |
3677 | node, OrderedHashSet::kEntrySize, factory()->empty_ordered_hash_set(), |
3678 | FIRST_SET_ITERATOR_TYPE, LAST_SET_ITERATOR_TYPE); |
3679 | case Builtins::kDatePrototypeGetTime: |
3680 | return ReduceDatePrototypeGetTime(node); |
3681 | case Builtins::kDateNow: |
3682 | return ReduceDateNow(node); |
3683 | case Builtins::kNumberConstructor: |
3684 | return ReduceNumberConstructor(node); |
3685 | default: |
3686 | break; |
3687 | } |
3688 | |
3689 | if (!TracingFlags::is_runtime_stats_enabled() && |
3690 | shared.object()->IsApiFunction()) { |
3691 | return ReduceCallApiFunction(node, shared); |
3692 | } |
3693 | return NoChange(); |
3694 | } |
3695 | |
3696 | Reduction JSCallReducer::ReduceJSCallWithArrayLike(Node* node) { |
3697 | DCHECK_EQ(IrOpcode::kJSCallWithArrayLike, node->opcode()); |
3698 | CallFrequency frequency = CallFrequencyOf(node->op()); |
3699 | VectorSlotPair feedback; |
3700 | return ReduceCallOrConstructWithArrayLikeOrSpread(node, 2, frequency, |
3701 | feedback); |
3702 | } |
3703 | |
3704 | Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) { |
3705 | DCHECK_EQ(IrOpcode::kJSCallWithSpread, node->opcode()); |
3706 | CallParameters const& p = CallParametersOf(node->op()); |
3707 | DCHECK_LE(3u, p.arity()); |
3708 | int arity = static_cast<int>(p.arity() - 1); |
3709 | CallFrequency frequency = p.frequency(); |
3710 | VectorSlotPair feedback = p.feedback(); |
3711 | return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency, |
3712 | feedback); |
3713 | } |
3714 | |
3715 | Reduction JSCallReducer::ReduceJSConstruct(Node* node) { |
3716 | DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); |
3717 | ConstructParameters const& p = ConstructParametersOf(node->op()); |
3718 | DCHECK_LE(2u, p.arity()); |
3719 | int arity = static_cast<int>(p.arity() - 2); |
3720 | Node* target = NodeProperties::GetValueInput(node, 0); |
3721 | Node* new_target = NodeProperties::GetValueInput(node, arity + 1); |
3722 | Node* effect = NodeProperties::GetEffectInput(node); |
3723 | Node* control = NodeProperties::GetControlInput(node); |
3724 | |
3725 | // Extract feedback from the {node} using the FeedbackNexus. |
3726 | if (p.feedback().IsValid()) { |
3727 | FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); |
3728 | if (nexus.IsUninitialized()) { |
3729 | return ReduceSoftDeoptimize( |
3730 | node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct); |
3731 | } |
3732 | |
3733 | base::Optional<HeapObjectRef> feedback = |
3734 | GetHeapObjectFeedback(broker(), nexus); |
3735 | 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 | jsgraph()->Constant(native_context().array_function()); |
3743 | |
3744 | // Check that the {target} is still the {array_function}. |
3745 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target, |
3746 | array_function); |
3747 | effect = graph()->NewNode( |
3748 | simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, |
3749 | effect, control); |
3750 | |
3751 | // Turn the {node} into a {JSCreateArray} call. |
3752 | NodeProperties::ReplaceEffectInput(node, effect); |
3753 | for (int i = arity; i > 0; --i) { |
3754 | NodeProperties::ReplaceValueInput( |
3755 | node, NodeProperties::GetValueInput(node, i), i + 1); |
3756 | } |
3757 | NodeProperties::ReplaceValueInput(node, array_function, 1); |
3758 | NodeProperties::ChangeOp( |
3759 | node, javascript()->CreateArray( |
3760 | arity, feedback->AsAllocationSite().object())); |
3761 | return Changed(node); |
3762 | } else if (feedback.has_value() && |
3763 | !HeapObjectMatcher(new_target).HasValue() && |
3764 | feedback->map().is_constructor()) { |
3765 | Node* new_target_feedback = jsgraph()->Constant(*feedback); |
3766 | |
3767 | // Check that the {new_target} is still the {new_target_feedback}. |
3768 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target, |
3769 | new_target_feedback); |
3770 | effect = graph()->NewNode( |
3771 | simplified()->CheckIf(DeoptimizeReason::kWrongCallTarget), check, |
3772 | effect, control); |
3773 | |
3774 | // Specialize the JSConstruct node to the {new_target_feedback}. |
3775 | NodeProperties::ReplaceValueInput(node, new_target_feedback, arity + 1); |
3776 | NodeProperties::ReplaceEffectInput(node, effect); |
3777 | if (target == new_target) { |
3778 | NodeProperties::ReplaceValueInput(node, new_target_feedback, 0); |
3779 | } |
3780 | |
3781 | // Try to further reduce the JSConstruct {node}. |
3782 | Reduction const reduction = ReduceJSConstruct(node); |
3783 | 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 | if (m.HasValue()) { |
3790 | HeapObjectRef target_ref = m.Ref(broker()); |
3791 | |
3792 | // Raise a TypeError if the {target} is not a constructor. |
3793 | if (!target_ref.map().is_constructor()) { |
3794 | NodeProperties::ReplaceValueInputs(node, target); |
3795 | NodeProperties::ChangeOp(node, |
3796 | javascript()->CallRuntime( |
3797 | Runtime::kThrowConstructedNonConstructable)); |
3798 | return Changed(node); |
3799 | } |
3800 | |
3801 | if (target_ref.IsJSFunction()) { |
3802 | JSFunctionRef function = target_ref.AsJSFunction(); |
3803 | function.Serialize(); |
3804 | |
3805 | // Do not reduce constructors with break points. |
3806 | if (function.shared().HasBreakInfo()) return NoChange(); |
3807 | |
3808 | // Don't inline cross native context. |
3809 | if (!function.native_context().equals(native_context())) { |
3810 | return NoChange(); |
3811 | } |
3812 | |
3813 | // Check for known builtin functions. |
3814 | int builtin_id = function.shared().HasBuiltinId() |
3815 | ? function.shared().builtin_id() |
3816 | : Builtins::kNoBuiltinId; |
3817 | switch (builtin_id) { |
3818 | case Builtins::kArrayConstructor: { |
3819 | // TODO(bmeurer): Deal with Array subclasses here. |
3820 | // Turn the {node} into a {JSCreateArray} call. |
3821 | for (int i = arity; i > 0; --i) { |
3822 | NodeProperties::ReplaceValueInput( |
3823 | node, NodeProperties::GetValueInput(node, i), i + 1); |
3824 | } |
3825 | NodeProperties::ReplaceValueInput(node, new_target, 1); |
3826 | NodeProperties::ChangeOp( |
3827 | 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 | if (arity == 0) { |
3834 | 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 | if (mnew_target.HasValue() && |
3843 | !mnew_target.Ref(broker()).equals(function)) { |
3844 | // Drop the value inputs. |
3845 | for (int i = arity; i > 0; --i) node->RemoveInput(i); |
3846 | NodeProperties::ChangeOp(node, javascript()->Create()); |
3847 | return Changed(node); |
3848 | } |
3849 | break; |
3850 | } |
3851 | case Builtins::kPromiseConstructor: |
3852 | return ReducePromiseConstructor(node); |
3853 | case Builtins::kTypedArrayConstructor: |
3854 | return ReduceTypedArrayConstructor(node, function.shared()); |
3855 | default: |
3856 | break; |
3857 | } |
3858 | } else if (target_ref.IsJSBoundFunction()) { |
3859 | JSBoundFunctionRef function = target_ref.AsJSBoundFunction(); |
3860 | function.Serialize(); |
3861 | |
3862 | ObjectRef bound_target_function = function.bound_target_function(); |
3863 | FixedArrayRef bound_arguments = function.bound_arguments(); |
3864 | |
3865 | // Patch {node} to use [[BoundTargetFunction]]. |
3866 | NodeProperties::ReplaceValueInput( |
3867 | node, jsgraph()->Constant(bound_target_function), 0); |
3868 | |
3869 | // Patch {node} to use [[BoundTargetFunction]] |
3870 | // as new.target if {new_target} equals {target}. |
3871 | 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 | arity + 1); |
3879 | |
3880 | // Insert the [[BoundArguments]] for {node}. |
3881 | for (int i = 0; i < bound_arguments.length(); ++i) { |
3882 | node->InsertInput(graph()->zone(), i + 1, |
3883 | jsgraph()->Constant(bound_arguments.get(i))); |
3884 | arity++; |
3885 | } |
3886 | |
3887 | // Update the JSConstruct operator on {node}. |
3888 | NodeProperties::ChangeOp( |
3889 | node, |
3890 | javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair())); |
3891 | |
3892 | // Try to further reduce the JSConstruct {node}. |
3893 | Reduction const reduction = ReduceJSConstruct(node); |
3894 | 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 | if (target->opcode() == IrOpcode::kJSCreateBoundFunction) { |
3904 | Node* bound_target_function = NodeProperties::GetValueInput(target, 0); |
3905 | int const bound_arguments_length = |
3906 | static_cast<int>(CreateBoundFunctionParametersOf(target->op()).arity()); |
3907 | |
3908 | // Patch the {node} to use [[BoundTargetFunction]]. |
3909 | NodeProperties::ReplaceValueInput(node, bound_target_function, 0); |
3910 | |
3911 | // Patch {node} to use [[BoundTargetFunction]] |
3912 | // as new.target if {new_target} equals {target}. |
3913 | 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 | arity + 1); |
3920 | |
3921 | // Insert the [[BoundArguments]] for {node}. |
3922 | for (int i = 0; i < bound_arguments_length; ++i) { |
3923 | Node* value = NodeProperties::GetValueInput(target, 2 + i); |
3924 | node->InsertInput(graph()->zone(), 1 + i, value); |
3925 | arity++; |
3926 | } |
3927 | |
3928 | // Update the JSConstruct operator on {node}. |
3929 | NodeProperties::ChangeOp( |
3930 | node, |
3931 | javascript()->Construct(arity + 2, p.frequency(), VectorSlotPair())); |
3932 | |
3933 | // Try to further reduce the JSConstruct {node}. |
3934 | Reduction const reduction = ReduceJSConstruct(node); |
3935 | return reduction.Changed() ? reduction : Changed(node); |
3936 | } |
3937 | |
3938 | return NoChange(); |
3939 | } |
3940 | |
3941 | // ES #sec-string.prototype.indexof |
3942 | Reduction JSCallReducer::ReduceStringPrototypeIndexOf(Node* node) { |
3943 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
3944 | CallParameters const& p = CallParametersOf(node->op()); |
3945 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
3946 | return NoChange(); |
3947 | } |
3948 | |
3949 | Node* effect = NodeProperties::GetEffectInput(node); |
3950 | Node* control = NodeProperties::GetControlInput(node); |
3951 | if (node->op()->ValueInputCount() >= 3) { |
3952 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
3953 | Node* new_receiver = effect = graph()->NewNode( |
3954 | simplified()->CheckString(p.feedback()), receiver, effect, control); |
3955 | |
3956 | Node* search_string = NodeProperties::GetValueInput(node, 2); |
3957 | Node* new_search_string = effect = |
3958 | graph()->NewNode(simplified()->CheckString(p.feedback()), search_string, |
3959 | effect, control); |
3960 | |
3961 | Node* new_position = jsgraph()->ZeroConstant(); |
3962 | if (node->op()->ValueInputCount() >= 4) { |
3963 | Node* position = NodeProperties::GetValueInput(node, 3); |
3964 | new_position = effect = graph()->NewNode( |
3965 | simplified()->CheckSmi(p.feedback()), position, effect, control); |
3966 | } |
3967 | |
3968 | NodeProperties::ReplaceEffectInput(node, effect); |
3969 | RelaxEffectsAndControls(node); |
3970 | node->ReplaceInput(0, new_receiver); |
3971 | node->ReplaceInput(1, new_search_string); |
3972 | node->ReplaceInput(2, new_position); |
3973 | node->TrimInputCount(3); |
3974 | NodeProperties::ChangeOp(node, simplified()->StringIndexOf()); |
3975 | return Changed(node); |
3976 | } |
3977 | return NoChange(); |
3978 | } |
3979 | |
3980 | // ES #sec-string.prototype.substring |
3981 | Reduction JSCallReducer::ReduceStringPrototypeSubstring(Node* node) { |
3982 | if (node->op()->ValueInputCount() < 3) return NoChange(); |
3983 | CallParameters const& p = CallParametersOf(node->op()); |
3984 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
3985 | return NoChange(); |
3986 | } |
3987 | |
3988 | Node* effect = NodeProperties::GetEffectInput(node); |
3989 | Node* control = NodeProperties::GetControlInput(node); |
3990 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
3991 | Node* start = NodeProperties::GetValueInput(node, 2); |
3992 | Node* end = node->op()->ValueInputCount() > 3 |
3993 | ? NodeProperties::GetValueInput(node, 3) |
3994 | : jsgraph()->UndefinedConstant(); |
3995 | |
3996 | receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), |
3997 | receiver, effect, control); |
3998 | |
3999 | start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, |
4000 | effect, control); |
4001 | |
4002 | Node* length = graph()->NewNode(simplified()->StringLength(), receiver); |
4003 | |
4004 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, |
4005 | jsgraph()->UndefinedConstant()); |
4006 | Node* branch = |
4007 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
4008 | |
4009 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4010 | Node* etrue = effect; |
4011 | Node* vtrue = length; |
4012 | |
4013 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
4014 | Node* efalse = effect; |
4015 | Node* vfalse = efalse = graph()->NewNode(simplified()->CheckSmi(p.feedback()), |
4016 | end, efalse, if_false); |
4017 | |
4018 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
4019 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
4020 | end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
4021 | vtrue, vfalse, control); |
4022 | Node* finalStart = |
4023 | graph()->NewNode(simplified()->NumberMin(), |
4024 | graph()->NewNode(simplified()->NumberMax(), start, |
4025 | jsgraph()->ZeroConstant()), |
4026 | length); |
4027 | Node* finalEnd = |
4028 | graph()->NewNode(simplified()->NumberMin(), |
4029 | graph()->NewNode(simplified()->NumberMax(), end, |
4030 | jsgraph()->ZeroConstant()), |
4031 | length); |
4032 | |
4033 | Node* from = |
4034 | graph()->NewNode(simplified()->NumberMin(), finalStart, finalEnd); |
4035 | Node* to = graph()->NewNode(simplified()->NumberMax(), finalStart, finalEnd); |
4036 | |
4037 | 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 | Reduction JSCallReducer::ReduceStringPrototypeSlice(Node* node) { |
4045 | if (node->op()->ValueInputCount() < 3) return NoChange(); |
4046 | CallParameters const& p = CallParametersOf(node->op()); |
4047 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4048 | return NoChange(); |
4049 | } |
4050 | |
4051 | Node* effect = NodeProperties::GetEffectInput(node); |
4052 | Node* control = NodeProperties::GetControlInput(node); |
4053 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4054 | Node* start = NodeProperties::GetValueInput(node, 2); |
4055 | Node* end = node->op()->ValueInputCount() > 3 |
4056 | ? NodeProperties::GetValueInput(node, 3) |
4057 | : jsgraph()->UndefinedConstant(); |
4058 | |
4059 | receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), |
4060 | receiver, effect, control); |
4061 | |
4062 | start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, |
4063 | effect, control); |
4064 | |
4065 | Node* length = graph()->NewNode(simplified()->StringLength(), receiver); |
4066 | |
4067 | // Replace {end} argument with {length} if it is undefined. |
4068 | { |
4069 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, |
4070 | jsgraph()->UndefinedConstant()); |
4071 | |
4072 | Node* branch = |
4073 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
4074 | |
4075 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4076 | Node* etrue = effect; |
4077 | Node* vtrue = length; |
4078 | |
4079 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
4080 | Node* efalse = effect; |
4081 | Node* vfalse = efalse = graph()->NewNode( |
4082 | simplified()->CheckSmi(p.feedback()), end, efalse, if_false); |
4083 | |
4084 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
4085 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
4086 | end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
4087 | vtrue, vfalse, control); |
4088 | } |
4089 | |
4090 | 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 | from = effect = graph()->NewNode(common()->TypeGuard(Type::UnsignedSmall()), |
4102 | from, effect, control); |
4103 | |
4104 | 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 | 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 | Node* check = graph()->NewNode(simplified()->NumberLessThan(), from, to); |
4121 | |
4122 | Node* branch = |
4123 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
4124 | |
4125 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4126 | Node* etrue = effect; |
4127 | Node* vtrue = etrue = graph()->NewNode(simplified()->StringSubstring(), |
4128 | receiver, from, to, etrue, if_true); |
4129 | |
4130 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
4131 | Node* efalse = effect; |
4132 | Node* vfalse = jsgraph()->EmptyStringConstant(); |
4133 | |
4134 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
4135 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
4136 | result_string = |
4137 | 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 | Reduction JSCallReducer::ReduceStringPrototypeSubstr(Node* node) { |
4147 | if (node->op()->ValueInputCount() < 3) return NoChange(); |
4148 | CallParameters const& p = CallParametersOf(node->op()); |
4149 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4150 | return NoChange(); |
4151 | } |
4152 | |
4153 | Node* effect = NodeProperties::GetEffectInput(node); |
4154 | Node* control = NodeProperties::GetControlInput(node); |
4155 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4156 | Node* start = NodeProperties::GetValueInput(node, 2); |
4157 | Node* end = node->op()->ValueInputCount() > 3 |
4158 | ? NodeProperties::GetValueInput(node, 3) |
4159 | : jsgraph()->UndefinedConstant(); |
4160 | |
4161 | receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), |
4162 | receiver, effect, control); |
4163 | |
4164 | start = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), start, |
4165 | effect, control); |
4166 | |
4167 | Node* length = graph()->NewNode(simplified()->StringLength(), receiver); |
4168 | |
4169 | // Replace {end} argument with {length} if it is undefined. |
4170 | { |
4171 | Node* check = graph()->NewNode(simplified()->ReferenceEqual(), end, |
4172 | jsgraph()->UndefinedConstant()); |
4173 | Node* branch = |
4174 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
4175 | |
4176 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4177 | Node* etrue = effect; |
4178 | Node* vtrue = length; |
4179 | |
4180 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
4181 | Node* efalse = effect; |
4182 | Node* vfalse = efalse = graph()->NewNode( |
4183 | simplified()->CheckSmi(p.feedback()), end, efalse, if_false); |
4184 | |
4185 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
4186 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
4187 | end = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
4188 | vtrue, vfalse, control); |
4189 | } |
4190 | |
4191 | 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 | initStart = effect = graph()->NewNode( |
4203 | common()->TypeGuard(Type::UnsignedSmall()), initStart, effect, control); |
4204 | |
4205 | 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 | 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 | Node* check = graph()->NewNode(simplified()->NumberLessThan(), |
4222 | jsgraph()->ZeroConstant(), resultLength); |
4223 | |
4224 | Node* branch = |
4225 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
4226 | |
4227 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4228 | Node* etrue = effect; |
4229 | Node* vtrue = etrue = |
4230 | graph()->NewNode(simplified()->StringSubstring(), receiver, initStart, |
4231 | to, etrue, if_true); |
4232 | |
4233 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
4234 | Node* efalse = effect; |
4235 | Node* vfalse = jsgraph()->EmptyStringConstant(); |
4236 | |
4237 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
4238 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
4239 | result_string = |
4240 | 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 | Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) { |
4249 | DCHECK_EQ(IrOpcode::kJSConstructWithArrayLike, node->opcode()); |
4250 | CallFrequency frequency = CallFrequencyOf(node->op()); |
4251 | VectorSlotPair feedback; |
4252 | return ReduceCallOrConstructWithArrayLikeOrSpread(node, 1, frequency, |
4253 | feedback); |
4254 | } |
4255 | |
4256 | Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { |
4257 | DCHECK_EQ(IrOpcode::kJSConstructWithSpread, node->opcode()); |
4258 | ConstructParameters const& p = ConstructParametersOf(node->op()); |
4259 | DCHECK_LE(3u, p.arity()); |
4260 | int arity = static_cast<int>(p.arity() - 2); |
4261 | CallFrequency frequency = p.frequency(); |
4262 | VectorSlotPair feedback = p.feedback(); |
4263 | return ReduceCallOrConstructWithArrayLikeOrSpread(node, arity, frequency, |
4264 | feedback); |
4265 | } |
4266 | |
4267 | Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { |
4268 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4269 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4270 | ReplaceWithValue(node, receiver); |
4271 | return Replace(receiver); |
4272 | } |
4273 | |
4274 | Reduction JSCallReducer::ReduceSoftDeoptimize(Node* node, |
4275 | DeoptimizeReason reason) { |
4276 | if (flags() & kBailoutOnUninitialized) { |
4277 | Node* effect = NodeProperties::GetEffectInput(node); |
4278 | Node* control = NodeProperties::GetControlInput(node); |
4279 | Node* frame_state = NodeProperties::FindFrameStateBefore(node); |
4280 | 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 | NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
4285 | Revisit(graph()->end()); |
4286 | node->TrimInputCount(0); |
4287 | 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 | Reduction JSCallReducer::ReduceArrayPrototypePush(Node* node) { |
4295 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4296 | CallParameters const& p = CallParametersOf(node->op()); |
4297 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4298 | return NoChange(); |
4299 | } |
4300 | |
4301 | int const num_values = node->op()->ValueInputCount() - 2; |
4302 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4303 | Node* effect = NodeProperties::GetEffectInput(node); |
4304 | Node* control = NodeProperties::GetControlInput(node); |
4305 | |
4306 | ZoneHandleSet<Map> receiver_maps; |
4307 | NodeProperties::InferReceiverMapsResult result = |
4308 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
4309 | &receiver_maps); |
4310 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
4311 | DCHECK_NE(0, receiver_maps.size()); |
4312 | |
4313 | ElementsKind kind; |
4314 | if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind, true)) { |
4315 | return NoChange(); |
4316 | } |
4317 | |
4318 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
4319 | |
4320 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
4321 | result, receiver_maps, p.feedback(), receiver, effect, control); |
4322 | |
4323 | // Collect the value inputs to push. |
4324 | std::vector<Node*> values(num_values); |
4325 | for (int i = 0; i < num_values; ++i) { |
4326 | values[i] = NodeProperties::GetValueInput(node, 2 + i); |
4327 | } |
4328 | |
4329 | for (auto& value : values) { |
4330 | if (IsSmiElementsKind(kind)) { |
4331 | value = effect = graph()->NewNode(simplified()->CheckSmi(p.feedback()), |
4332 | value, effect, control); |
4333 | } else if (IsDoubleElementsKind(kind)) { |
4334 | value = effect = graph()->NewNode(simplified()->CheckNumber(p.feedback()), |
4335 | value, effect, control); |
4336 | // Make sure we do not store signaling NaNs into double arrays. |
4337 | value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); |
4338 | } |
4339 | } |
4340 | |
4341 | // Load the "length" property of the {receiver}. |
4342 | Node* length = effect = graph()->NewNode( |
4343 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
4344 | effect, control); |
4345 | Node* value = length; |
4346 | |
4347 | // Check if we have any {values} to push. |
4348 | if (num_values > 0) { |
4349 | // Compute the resulting "length" of the {receiver}. |
4350 | 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 | Node* elements = effect = graph()->NewNode( |
4355 | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, |
4356 | effect, control); |
4357 | Node* elements_length = effect = graph()->NewNode( |
4358 | simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), elements, |
4359 | effect, control); |
4360 | |
4361 | GrowFastElementsMode mode = |
4362 | IsDoubleElementsKind(kind) ? GrowFastElementsMode::kDoubleElements |
4363 | : GrowFastElementsMode::kSmiOrObjectElements; |
4364 | elements = effect = graph()->NewNode( |
4365 | simplified()->MaybeGrowFastElements(mode, p.feedback()), receiver, |
4366 | elements, |
4367 | graph()->NewNode(simplified()->NumberAdd(), length, |
4368 | 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 | effect = graph()->NewNode( |
4374 | simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), |
4375 | receiver, new_length, effect, control); |
4376 | |
4377 | // Append the {values} to the {elements}. |
4378 | for (int i = 0; i < num_values; ++i) { |
4379 | Node* value = values[i]; |
4380 | Node* index = graph()->NewNode(simplified()->NumberAdd(), length, |
4381 | jsgraph()->Constant(i)); |
4382 | effect = graph()->NewNode( |
4383 | 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 | Reduction JSCallReducer::ReduceArrayPrototypePop(Node* node) { |
4394 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4395 | CallParameters const& p = CallParametersOf(node->op()); |
4396 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4397 | return NoChange(); |
4398 | } |
4399 | |
4400 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4401 | Node* effect = NodeProperties::GetEffectInput(node); |
4402 | Node* control = NodeProperties::GetControlInput(node); |
4403 | |
4404 | ZoneHandleSet<Map> receiver_maps; |
4405 | NodeProperties::InferReceiverMapsResult result = |
4406 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
4407 | &receiver_maps); |
4408 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
4409 | DCHECK_NE(0, receiver_maps.size()); |
4410 | |
4411 | ElementsKind kind; |
4412 | if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind)) { |
4413 | return NoChange(); |
4414 | } |
4415 | |
4416 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
4417 | |
4418 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
4419 | result, receiver_maps, p.feedback(), receiver, effect, control); |
4420 | |
4421 | // Load the "length" property of the {receiver}. |
4422 | Node* length = effect = graph()->NewNode( |
4423 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
4424 | effect, control); |
4425 | |
4426 | // Check if the {receiver} has any elements. |
4427 | Node* check = graph()->NewNode(simplified()->NumberEqual(), length, |
4428 | jsgraph()->ZeroConstant()); |
4429 | Node* branch = |
4430 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
4431 | |
4432 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4433 | Node* etrue = effect; |
4434 | Node* vtrue = jsgraph()->UndefinedConstant(); |
4435 | |
4436 | 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 | Node* elements = efalse = graph()->NewNode( |
4445 | 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 | if (IsSmiOrObjectElementsKind(kind)) { |
4450 | elements = efalse = |
4451 | graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver, |
4452 | elements, efalse, if_false); |
4453 | } |
4454 | |
4455 | // Compute the new {length}. |
4456 | length = graph()->NewNode(simplified()->NumberSubtract(), length, |
4457 | jsgraph()->OneConstant()); |
4458 | |
4459 | // Store the new {length} to the {receiver}. |
4460 | efalse = graph()->NewNode( |
4461 | simplified()->StoreField(AccessBuilder::ForJSArrayLength(kind)), |
4462 | receiver, length, efalse, if_false); |
4463 | |
4464 | // Load the last entry from the {elements}. |
4465 | vfalse = efalse = graph()->NewNode( |
4466 | 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 | efalse = graph()->NewNode( |
4471 | simplified()->StoreElement( |
4472 | AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))), |
4473 | elements, length, jsgraph()->TheHoleConstant(), efalse, if_false); |
4474 | } |
4475 | |
4476 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
4477 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
4478 | 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 | if (IsHoleyElementsKind(kind)) { |
4484 | value = |
4485 | 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 | Reduction JSCallReducer::ReduceArrayPrototypeShift(Node* node) { |
4494 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4495 | CallParameters const& p = CallParametersOf(node->op()); |
4496 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4497 | return NoChange(); |
4498 | } |
4499 | |
4500 | Node* target = NodeProperties::GetValueInput(node, 0); |
4501 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4502 | Node* context = NodeProperties::GetContextInput(node); |
4503 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
4504 | Node* effect = NodeProperties::GetEffectInput(node); |
4505 | Node* control = NodeProperties::GetControlInput(node); |
4506 | |
4507 | ZoneHandleSet<Map> receiver_maps; |
4508 | NodeProperties::InferReceiverMapsResult result = |
4509 | NodeProperties::InferReceiverMaps(broker(), receiver, effect, |
4510 | &receiver_maps); |
4511 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
4512 | DCHECK_NE(0, receiver_maps.size()); |
4513 | |
4514 | ElementsKind kind; |
4515 | if (!CanInlineArrayResizingBuiltin(broker(), receiver_maps, &kind)) { |
4516 | return NoChange(); |
4517 | } |
4518 | |
4519 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
4520 | |
4521 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
4522 | result, receiver_maps, p.feedback(), receiver, effect, control); |
4523 | |
4524 | // Load length of the {receiver}. |
4525 | Node* length = effect = graph()->NewNode( |
4526 | simplified()->LoadField(AccessBuilder::ForJSArrayLength(kind)), receiver, |
4527 | effect, control); |
4528 | |
4529 | // Return undefined if {receiver} has no elements. |
4530 | Node* check0 = graph()->NewNode(simplified()->NumberEqual(), length, |
4531 | jsgraph()->ZeroConstant()); |
4532 | Node* branch0 = |
4533 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control); |
4534 | |
4535 | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
4536 | Node* etrue0 = effect; |
4537 | Node* vtrue0 = jsgraph()->UndefinedConstant(); |
4538 | |
4539 | 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 | graph()->NewNode(simplified()->NumberLessThanOrEqual(), length, |
4546 | jsgraph()->Constant(JSArray::kMaxCopyElements)); |
4547 | Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
4548 | check1, if_false0); |
4549 | |
4550 | Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1); |
4551 | Node* etrue1 = efalse0; |
4552 | Node* vtrue1; |
4553 | { |
4554 | Node* elements = etrue1 = graph()->NewNode( |
4555 | simplified()->LoadField(AccessBuilder::ForJSObjectElements()), |
4556 | receiver, etrue1, if_true1); |
4557 | |
4558 | // Load the first element here, which we return below. |
4559 | vtrue1 = etrue1 = graph()->NewNode( |
4560 | 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 | if (IsSmiOrObjectElementsKind(kind)) { |
4565 | elements = etrue1 = |
4566 | graph()->NewNode(simplified()->EnsureWritableFastElements(), |
4567 | receiver, elements, etrue1, if_true1); |
4568 | } |
4569 | |
4570 | // Shift the remaining {elements} by one towards the start. |
4571 | Node* loop = graph()->NewNode(common()->Loop(2), if_true1, if_true1); |
4572 | Node* eloop = |
4573 | graph()->NewNode(common()->EffectPhi(2), etrue1, etrue1, loop); |
4574 | Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); |
4575 | NodeProperties::MergeControlToEnd(graph(), common(), terminate); |
4576 | 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 | graph()->NewNode(simplified()->NumberLessThan(), index, length); |
4584 | Node* branch2 = graph()->NewNode(common()->Branch(), check2, loop); |
4585 | |
4586 | if_true1 = graph()->NewNode(common()->IfFalse(), branch2); |
4587 | etrue1 = eloop; |
4588 | |
4589 | Node* control = graph()->NewNode(common()->IfTrue(), branch2); |
4590 | Node* effect = etrue1; |
4591 | |
4592 | ElementAccess const access = AccessBuilder::ForFixedArrayElement(kind); |
4593 | Node* value = effect = |
4594 | graph()->NewNode(simplified()->LoadElement(access), elements, index, |
4595 | effect, control); |
4596 | effect = |
4597 | graph()->NewNode(simplified()->StoreElement(access), elements, |
4598 | graph()->NewNode(simplified()->NumberSubtract(), |
4599 | index, jsgraph()->OneConstant()), |
4600 | value, effect, control); |
4601 | |
4602 | loop->ReplaceInput(1, control); |
4603 | eloop->ReplaceInput(1, effect); |
4604 | index->ReplaceInput(1, |
4605 | graph()->NewNode(simplified()->NumberAdd(), index, |
4606 | jsgraph()->OneConstant())); |
4607 | } |
4608 | |
4609 | // Compute the new {length}. |
4610 | length = graph()->NewNode(simplified()->NumberSubtract(), length, |
4611 | jsgraph()->OneConstant()); |
4612 | |
4613 | // Store the new {length} to the {receiver}. |
4614 | etrue1 = graph()->NewNode( |
4615 | 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 | etrue1 = graph()->NewNode( |
4620 | simplified()->StoreElement( |
4621 | AccessBuilder::ForFixedArrayElement(GetHoleyElementsKind(kind))), |
4622 | elements, length, jsgraph()->TheHoleConstant(), etrue1, if_true1); |
4623 | } |
4624 | |
4625 | 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 | auto call_descriptor = Linkage::GetCEntryStubCallDescriptor( |
4632 | graph()->zone(), 1, BuiltinArguments::kNumExtraArgsWithReceiver, |
4633 | Builtins::name(builtin_index), node->op()->properties(), |
4634 | CallDescriptor::kNeedsFrameState); |
4635 | Node* stub_code = |
4636 | jsgraph()->CEntryStubConstant(1, kDontSaveFPRegs, kArgvOnStack, true); |
4637 | Address builtin_entry = Builtins::CppEntryOf(builtin_index); |
4638 | Node* entry = |
4639 | jsgraph()->ExternalConstant(ExternalReference::Create(builtin_entry)); |
4640 | Node* argc = |
4641 | jsgraph()->Constant(BuiltinArguments::kNumExtraArgsWithReceiver); |
4642 | if_false1 = efalse1 = vfalse1 = |
4643 | 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 | if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1); |
4650 | efalse0 = |
4651 | graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0); |
4652 | vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
4653 | vtrue1, vfalse1, if_false0); |
4654 | } |
4655 | |
4656 | control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
4657 | effect = graph()->NewNode(common()->EffectPhi(2), etrue0, efalse0, control); |
4658 | Node* value = |
4659 | 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 | if (IsHoleyElementsKind(kind)) { |
4665 | value = |
4666 | 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 | Reduction JSCallReducer::ReduceArrayPrototypeSlice(Node* node) { |
4675 | if (!FLAG_turbo_inline_array_builtins) return NoChange(); |
4676 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4677 | CallParameters const& p = CallParametersOf(node->op()); |
4678 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4679 | return NoChange(); |
4680 | } |
4681 | |
4682 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4683 | Node* start = node->op()->ValueInputCount() > 2 |
4684 | ? NodeProperties::GetValueInput(node, 2) |
4685 | : jsgraph()->ZeroConstant(); |
4686 | Node* end = node->op()->ValueInputCount() > 3 |
4687 | ? NodeProperties::GetValueInput(node, 3) |
4688 | : jsgraph()->UndefinedConstant(); |
4689 | Node* context = NodeProperties::GetContextInput(node); |
4690 | Node* effect = NodeProperties::GetEffectInput(node); |
4691 | 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 | 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 | &receiver_maps); |
4706 | 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 | for (Handle<Map> map : receiver_maps) { |
4713 | MapRef receiver_map(broker(), map); |
4714 | if (!receiver_map.supports_fast_array_iteration()) return NoChange(); |
4715 | |
4716 | if (IsHoleyElementsKind(receiver_map.elements_kind())) { |
4717 | can_be_holey = true; |
4718 | } |
4719 | } |
4720 | |
4721 | if (!dependencies()->DependOnArraySpeciesProtector()) return NoChange(); |
4722 | if (can_be_holey) { |
4723 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
4724 | } |
4725 | |
4726 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
4727 | 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 | Builtins::CallableFor(isolate(), Builtins::kCloneFastJSArray); |
4737 | auto call_descriptor = Linkage::GetStubCallDescriptor( |
4738 | graph()->zone(), callable.descriptor(), |
4739 | callable.descriptor().GetStackParameterCount(), CallDescriptor::kNoFlags, |
4740 | Operator::kNoThrow | Operator::kNoDeopt); |
4741 | |
4742 | // Calls to Builtins::kCloneFastJSArray produce COW arrays |
4743 | // if the original array is COW |
4744 | 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 | Reduction JSCallReducer::ReduceArrayIsArray(Node* node) { |
4754 | // We certainly know that undefined is not an array. |
4755 | if (node->op()->ValueInputCount() < 3) { |
4756 | Node* value = jsgraph()->FalseConstant(); |
4757 | ReplaceWithValue(node, value); |
4758 | return Replace(value); |
4759 | } |
4760 | |
4761 | Node* effect = NodeProperties::GetEffectInput(node); |
4762 | Node* control = NodeProperties::GetControlInput(node); |
4763 | Node* context = NodeProperties::GetContextInput(node); |
4764 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
4765 | Node* object = NodeProperties::GetValueInput(node, 2); |
4766 | node->ReplaceInput(0, object); |
4767 | node->ReplaceInput(1, context); |
4768 | node->ReplaceInput(2, frame_state); |
4769 | node->ReplaceInput(3, effect); |
4770 | node->ReplaceInput(4, control); |
4771 | node->TrimInputCount(5); |
4772 | NodeProperties::ChangeOp(node, javascript()->ObjectIsArray()); |
4773 | return Changed(node); |
4774 | } |
4775 | |
4776 | Reduction JSCallReducer::ReduceArrayIterator(Node* node, IterationKind kind) { |
4777 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4778 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
4779 | Node* context = NodeProperties::GetContextInput(node); |
4780 | Node* effect = NodeProperties::GetEffectInput(node); |
4781 | 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 | &receiver_maps); |
4788 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
4789 | DCHECK_NE(0, receiver_maps.size()); |
4790 | for (Handle<Map> map : receiver_maps) { |
4791 | MapRef receiver_map(broker(), map); |
4792 | if (!receiver_map.IsJSReceiverMap()) return NoChange(); |
4793 | } |
4794 | |
4795 | // Morph the {node} into a JSCreateArrayIterator with the given {kind}. |
4796 | RelaxControls(node); |
4797 | node->ReplaceInput(0, receiver); |
4798 | node->ReplaceInput(1, context); |
4799 | node->ReplaceInput(2, effect); |
4800 | node->ReplaceInput(3, control); |
4801 | node->TrimInputCount(4); |
4802 | NodeProperties::ChangeOp(node, javascript()->CreateArrayIterator(kind)); |
4803 | return Changed(node); |
4804 | } |
4805 | |
4806 | namespace { |
4807 | |
4808 | bool InferIteratedObjectMaps(JSHeapBroker* broker, Node* iterator, |
4809 | ZoneHandleSet<Map>* iterated_object_maps) { |
4810 | DCHECK_EQ(IrOpcode::kJSCreateArrayIterator, iterator->opcode()); |
4811 | Node* iterated_object = NodeProperties::GetValueInput(iterator, 0); |
4812 | Node* effect = NodeProperties::GetEffectInput(iterator); |
4813 | |
4814 | NodeProperties::InferReceiverMapsResult result = |
4815 | NodeProperties::InferReceiverMaps(broker, iterated_object, effect, |
4816 | iterated_object_maps); |
4817 | return result != NodeProperties::kNoReceiverMaps; |
4818 | } |
4819 | |
4820 | } // namespace |
4821 | |
4822 | // ES #sec-%arrayiteratorprototype%.next |
4823 | Reduction JSCallReducer::ReduceArrayIteratorPrototypeNext(Node* node) { |
4824 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
4825 | CallParameters const& p = CallParametersOf(node->op()); |
4826 | Node* iterator = NodeProperties::GetValueInput(node, 1); |
4827 | Node* context = NodeProperties::GetContextInput(node); |
4828 | Node* effect = NodeProperties::GetEffectInput(node); |
4829 | Node* control = NodeProperties::GetControlInput(node); |
4830 | |
4831 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
4832 | return NoChange(); |
4833 | } |
4834 | |
4835 | // Check if the {iterator} is a JSCreateArrayIterator. |
4836 | if (iterator->opcode() != IrOpcode::kJSCreateArrayIterator) return NoChange(); |
4837 | IterationKind const iteration_kind = |
4838 | CreateArrayIteratorParametersOf(iterator->op()).kind(); |
4839 | |
4840 | // Try to infer the [[IteratedObject]] maps from the {iterator}. |
4841 | ZoneHandleSet<Map> iterated_object_maps; |
4842 | 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 | MapRef(broker(), iterated_object_maps[0]).elements_kind(); |
4850 | if (IsFixedTypedArrayElementsKind(elements_kind)) { |
4851 | // TurboFan doesn't support loading from BigInt typed arrays yet. |
4852 | if (elements_kind == BIGUINT64_ELEMENTS || |
4853 | elements_kind == BIGINT64_ELEMENTS) { |
4854 | return NoChange(); |
4855 | } |
4856 | for (Handle<Map> map : iterated_object_maps) { |
4857 | MapRef iterated_object_map(broker(), map); |
4858 | if (iterated_object_map.elements_kind() != elements_kind) { |
4859 | return NoChange(); |
4860 | } |
4861 | } |
4862 | } else { |
4863 | 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 | if (IsHoleyElementsKind(elements_kind)) { |
4871 | if (!dependencies()->DependOnNoElementsProtector()) UNREACHABLE(); |
4872 | } |
4873 | |
4874 | // Load the (current) {iterated_object} from the {iterator}. |
4875 | Node* iterated_object = effect = |
4876 | graph()->NewNode(simplified()->LoadField( |
4877 | AccessBuilder::ForJSArrayIteratorIteratedObject()), |
4878 | iterator, effect, control); |
4879 | |
4880 | // Ensure that the {iterated_object} map didn't change. |
4881 | effect = graph()->NewNode( |
4882 | simplified()->CheckMaps(CheckMapsFlag::kNone, iterated_object_maps, |
4883 | p.feedback()), |
4884 | iterated_object, effect, control); |
4885 | |
4886 | if (IsFixedTypedArrayElementsKind(elements_kind)) { |
4887 | // See if we can skip the detaching check. |
4888 | if (!dependencies()->DependOnArrayBufferDetachingProtector()) { |
4889 | // Bail out if the {iterated_object}s JSArrayBuffer was detached. |
4890 | Node* buffer = effect = graph()->NewNode( |
4891 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), |
4892 | iterated_object, effect, control); |
4893 | Node* buffer_bit_field = effect = graph()->NewNode( |
4894 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), |
4895 | buffer, effect, control); |
4896 | 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 | 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 | FieldAccess index_access = AccessBuilder::ForJSArrayIteratorNextIndex(); |
4914 | if (IsFixedTypedArrayElementsKind(elements_kind)) { |
4915 | index_access.type = TypeCache::Get()->kJSTypedArrayLengthType; |
4916 | index_access.machine_type = MachineType::TaggedSigned(); |
4917 | index_access.write_barrier_kind = kNoWriteBarrier; |
4918 | } else { |
4919 | index_access.type = TypeCache::Get()->kJSArrayLengthType; |
4920 | } |
4921 | 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 | Node* elements = effect = graph()->NewNode( |
4931 | 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 | IsFixedTypedArrayElementsKind(elements_kind) |
4939 | ? AccessBuilder::ForJSTypedArrayLength() |
4940 | : AccessBuilder::ForJSArrayLength(elements_kind); |
4941 | 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 | Node* check = graph()->NewNode(simplified()->NumberLessThan(), index, length); |
4946 | Node* branch = |
4947 | graph()->NewNode(common()->Branch(BranchHint::kNone), check, control); |
4948 | |
4949 | Node* done_true; |
4950 | Node* value_true; |
4951 | Node* etrue = effect; |
4952 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
4953 | { |
4954 | // We know that the {index} is range of the {length} now. |
4955 | index = etrue = graph()->NewNode( |
4956 | common()->TypeGuard( |
4957 | Type::Range(0.0, length_access.type.Max() - 1.0, graph()->zone())), |
4958 | index, etrue, if_true); |
4959 | |
4960 | done_true = jsgraph()->FalseConstant(); |
4961 | 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 | if (IsFixedTypedArrayElementsKind(elements_kind)) { |
4969 | Node* base_ptr = etrue = graph()->NewNode( |
4970 | simplified()->LoadField( |
4971 | AccessBuilder::ForFixedTypedArrayBaseBasePointer()), |
4972 | elements, etrue, if_true); |
4973 | Node* external_ptr = etrue = graph()->NewNode( |
4974 | simplified()->LoadField( |
4975 | AccessBuilder::ForFixedTypedArrayBaseExternalPointer()), |
4976 | elements, etrue, if_true); |
4977 | |
4978 | ExternalArrayType array_type = kExternalInt8Array; |
4979 | 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 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
4985 | default: |
4986 | UNREACHABLE(); |
4987 | #undef TYPED_ARRAY_CASE |
4988 | } |
4989 | |
4990 | Node* buffer = etrue = |
4991 | graph()->NewNode(simplified()->LoadField( |
4992 | AccessBuilder::ForJSArrayBufferViewBuffer()), |
4993 | iterated_object, etrue, if_true); |
4994 | |
4995 | value_true = etrue = |
4996 | graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer, |
4997 | base_ptr, external_ptr, index, etrue, if_true); |
4998 | } else { |
4999 | value_true = etrue = graph()->NewNode( |
5000 | simplified()->LoadElement( |
5001 | AccessBuilder::ForFixedArrayElement(elements_kind)), |
5002 | elements, index, etrue, if_true); |
5003 | |
5004 | // Convert hole to undefined if needed. |
5005 | if (elements_kind == HOLEY_ELEMENTS || |
5006 | elements_kind == HOLEY_SMI_ELEMENTS) { |
5007 | value_true = graph()->NewNode( |
5008 | simplified()->ConvertTaggedHoleToUndefined(), value_true); |
5009 | } 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 | value_true = etrue = graph()->NewNode( |
5013 | simplified()->CheckFloat64Hole(mode, p.feedback()), value_true, |
5014 | etrue, if_true); |
5015 | } |
5016 | } |
5017 | |
5018 | if (iteration_kind == IterationKind::kEntries) { |
5019 | // Allocate elements for key/value pair |
5020 | value_true = etrue = |
5021 | 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 | Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index, |
5031 | jsgraph()->OneConstant()); |
5032 | 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 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
5040 | { |
5041 | // iterator.[[NextIndex]] >= array.length, stop iterating. |
5042 | done_false = jsgraph()->TrueConstant(); |
5043 | value_false = jsgraph()->UndefinedConstant(); |
5044 | |
5045 | 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 | Node* end_index = jsgraph()->Constant(index_access.type.Max()); |
5057 | efalse = graph()->NewNode(simplified()->StoreField(index_access), |
5058 | iterator, end_index, efalse, if_false); |
5059 | } |
5060 | } |
5061 | |
5062 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
5063 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
5064 | Node* value = |
5065 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
5066 | value_true, value_false, control); |
5067 | Node* done = |
5068 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
5069 | done_true, done_false, control); |
5070 | |
5071 | // Create IteratorResult object. |
5072 | 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 | 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 | CallParameters const& p = CallParametersOf(node->op()); |
5086 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5087 | return NoChange(); |
5088 | } |
5089 | |
5090 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5091 | Node* index = node->op()->ValueInputCount() >= 3 |
5092 | ? NodeProperties::GetValueInput(node, 2) |
5093 | : jsgraph()->ZeroConstant(); |
5094 | Node* effect = NodeProperties::GetEffectInput(node); |
5095 | Node* control = NodeProperties::GetControlInput(node); |
5096 | |
5097 | // Ensure that the {receiver} is actually a String. |
5098 | receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), |
5099 | receiver, effect, control); |
5100 | |
5101 | // Determine the {receiver} length. |
5102 | Node* receiver_length = |
5103 | graph()->NewNode(simplified()->StringLength(), receiver); |
5104 | |
5105 | // Check that the {index} is within range. |
5106 | 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 | 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 | Reduction JSCallReducer::ReduceStringPrototypeCharAt(Node* node) { |
5120 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5121 | CallParameters const& p = CallParametersOf(node->op()); |
5122 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5123 | return NoChange(); |
5124 | } |
5125 | |
5126 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5127 | Node* index = node->op()->ValueInputCount() >= 3 |
5128 | ? NodeProperties::GetValueInput(node, 2) |
5129 | : jsgraph()->ZeroConstant(); |
5130 | Node* effect = NodeProperties::GetEffectInput(node); |
5131 | Node* control = NodeProperties::GetControlInput(node); |
5132 | |
5133 | // Ensure that the {receiver} is actually a String. |
5134 | receiver = effect = graph()->NewNode(simplified()->CheckString(p.feedback()), |
5135 | receiver, effect, control); |
5136 | |
5137 | // Determine the {receiver} length. |
5138 | Node* receiver_length = |
5139 | graph()->NewNode(simplified()->StringLength(), receiver); |
5140 | |
5141 | // Check that the {index} is within range. |
5142 | 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 | Node* masked_index = graph()->NewNode(simplified()->PoisonIndex(), index); |
5147 | Node* value = effect = |
5148 | graph()->NewNode(simplified()->StringCharCodeAt(), receiver, masked_index, |
5149 | effect, control); |
5150 | 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 | Reduction JSCallReducer::ReduceStringPrototypeToLowerCaseIntl(Node* node) { |
5159 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5160 | CallParameters const& p = CallParametersOf(node->op()); |
5161 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5162 | return NoChange(); |
5163 | } |
5164 | Node* effect = NodeProperties::GetEffectInput(node); |
5165 | Node* control = NodeProperties::GetControlInput(node); |
5166 | |
5167 | Node* receiver = effect = |
5168 | graph()->NewNode(simplified()->CheckString(p.feedback()), |
5169 | NodeProperties::GetValueInput(node, 1), effect, control); |
5170 | |
5171 | NodeProperties::ReplaceEffectInput(node, effect); |
5172 | RelaxEffectsAndControls(node); |
5173 | node->ReplaceInput(0, receiver); |
5174 | node->TrimInputCount(1); |
5175 | NodeProperties::ChangeOp(node, simplified()->StringToLowerCaseIntl()); |
5176 | NodeProperties::SetType(node, Type::String()); |
5177 | return Changed(node); |
5178 | } |
5179 | |
5180 | Reduction JSCallReducer::ReduceStringPrototypeToUpperCaseIntl(Node* node) { |
5181 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5182 | CallParameters const& p = CallParametersOf(node->op()); |
5183 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5184 | return NoChange(); |
5185 | } |
5186 | Node* effect = NodeProperties::GetEffectInput(node); |
5187 | Node* control = NodeProperties::GetControlInput(node); |
5188 | |
5189 | Node* receiver = effect = |
5190 | graph()->NewNode(simplified()->CheckString(p.feedback()), |
5191 | NodeProperties::GetValueInput(node, 1), effect, control); |
5192 | |
5193 | NodeProperties::ReplaceEffectInput(node, effect); |
5194 | RelaxEffectsAndControls(node); |
5195 | node->ReplaceInput(0, receiver); |
5196 | node->TrimInputCount(1); |
5197 | 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 | Reduction JSCallReducer::ReduceStringFromCharCode(Node* node) { |
5206 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5207 | CallParameters const& p = CallParametersOf(node->op()); |
5208 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5209 | return NoChange(); |
5210 | } |
5211 | if (node->op()->ValueInputCount() == 3) { |
5212 | Node* effect = NodeProperties::GetEffectInput(node); |
5213 | Node* control = NodeProperties::GetControlInput(node); |
5214 | Node* input = NodeProperties::GetValueInput(node, 2); |
5215 | |
5216 | input = effect = graph()->NewNode( |
5217 | simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball, |
5218 | p.feedback()), |
5219 | input, effect, control); |
5220 | |
5221 | Node* value = |
5222 | 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 | Reduction JSCallReducer::ReduceStringFromCodePoint(Node* node) { |
5231 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5232 | CallParameters const& p = CallParametersOf(node->op()); |
5233 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5234 | return NoChange(); |
5235 | } |
5236 | if (node->op()->ValueInputCount() == 3) { |
5237 | Node* effect = NodeProperties::GetEffectInput(node); |
5238 | Node* control = NodeProperties::GetControlInput(node); |
5239 | Node* input = NodeProperties::GetValueInput(node, 2); |
5240 | |
5241 | input = effect = |
5242 | graph()->NewNode(simplified()->CheckBounds(p.feedback()), input, |
5243 | jsgraph()->Constant(0x10FFFF + 1), effect, control); |
5244 | |
5245 | 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 | Reduction JSCallReducer::ReduceStringPrototypeIterator(Node* node) { |
5254 | CallParameters const& p = CallParametersOf(node->op()); |
5255 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5256 | return NoChange(); |
5257 | } |
5258 | Node* effect = NodeProperties::GetEffectInput(node); |
5259 | Node* control = NodeProperties::GetControlInput(node); |
5260 | Node* receiver = effect = |
5261 | graph()->NewNode(simplified()->CheckString(p.feedback()), |
5262 | NodeProperties::GetValueInput(node, 1), effect, control); |
5263 | Node* iterator = effect = |
5264 | graph()->NewNode(javascript()->CreateStringIterator(), receiver, |
5265 | jsgraph()->NoContextConstant(), effect); |
5266 | ReplaceWithValue(node, iterator, effect, control); |
5267 | return Replace(iterator); |
5268 | } |
5269 | |
5270 | Reduction JSCallReducer::ReduceStringIteratorPrototypeNext(Node* node) { |
5271 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5272 | Node* effect = NodeProperties::GetEffectInput(node); |
5273 | Node* control = NodeProperties::GetControlInput(node); |
5274 | Node* context = NodeProperties::GetContextInput(node); |
5275 | if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, |
5276 | JS_STRING_ITERATOR_TYPE)) { |
5277 | Node* string = effect = graph()->NewNode( |
5278 | simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()), |
5279 | receiver, effect, control); |
5280 | Node* index = effect = graph()->NewNode( |
5281 | simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()), |
5282 | receiver, effect, control); |
5283 | Node* length = graph()->NewNode(simplified()->StringLength(), string); |
5284 | |
5285 | // branch0: if (index < length) |
5286 | Node* check0 = |
5287 | graph()->NewNode(simplified()->NumberLessThan(), index, length); |
5288 | Node* branch0 = |
5289 | graph()->NewNode(common()->Branch(BranchHint::kNone), check0, control); |
5290 | |
5291 | Node* etrue0 = effect; |
5292 | Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0); |
5293 | Node* done_true; |
5294 | Node* vtrue0; |
5295 | { |
5296 | done_true = jsgraph()->FalseConstant(); |
5297 | Node* codepoint = etrue0 = graph()->NewNode( |
5298 | simplified()->StringCodePointAt(UnicodeEncoding::UTF16), string, |
5299 | index, etrue0, if_true0); |
5300 | vtrue0 = graph()->NewNode( |
5301 | simplified()->StringFromSingleCodePoint(UnicodeEncoding::UTF16), |
5302 | codepoint); |
5303 | |
5304 | // Update iterator.[[NextIndex]] |
5305 | Node* char_length = |
5306 | graph()->NewNode(simplified()->StringLength(), vtrue0); |
5307 | index = graph()->NewNode(simplified()->NumberAdd(), index, char_length); |
5308 | etrue0 = graph()->NewNode( |
5309 | simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()), |
5310 | receiver, index, etrue0, if_true0); |
5311 | } |
5312 | |
5313 | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
5314 | Node* done_false; |
5315 | Node* vfalse0; |
5316 | { |
5317 | vfalse0 = jsgraph()->UndefinedConstant(); |
5318 | done_false = jsgraph()->TrueConstant(); |
5319 | } |
5320 | |
5321 | control = graph()->NewNode(common()->Merge(2), if_true0, if_false0); |
5322 | effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control); |
5323 | Node* value = |
5324 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
5325 | vtrue0, vfalse0, control); |
5326 | Node* done = |
5327 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
5328 | done_true, done_false, control); |
5329 | |
5330 | 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 | Reduction JSCallReducer::ReduceStringPrototypeConcat(Node* node) { |
5341 | if (node->op()->ValueInputCount() < 2 || node->op()->ValueInputCount() > 3) { |
5342 | return NoChange(); |
5343 | } |
5344 | CallParameters const& p = CallParametersOf(node->op()); |
5345 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5346 | return NoChange(); |
5347 | } |
5348 | |
5349 | Node* effect = NodeProperties::GetEffectInput(node); |
5350 | Node* control = NodeProperties::GetControlInput(node); |
5351 | Node* receiver = effect = |
5352 | graph()->NewNode(simplified()->CheckString(p.feedback()), |
5353 | NodeProperties::GetValueInput(node, 1), effect, control); |
5354 | |
5355 | if (node->op()->ValueInputCount() < 3) { |
5356 | ReplaceWithValue(node, receiver, effect, control); |
5357 | return Replace(receiver); |
5358 | } |
5359 | |
5360 | Node* argument = effect = |
5361 | graph()->NewNode(simplified()->CheckString(p.feedback()), |
5362 | NodeProperties::GetValueInput(node, 2), effect, control); |
5363 | Node* receiver_length = |
5364 | graph()->NewNode(simplified()->StringLength(), receiver); |
5365 | Node* argument_length = |
5366 | graph()->NewNode(simplified()->StringLength(), argument); |
5367 | Node* length = graph()->NewNode(simplified()->NumberAdd(), receiver_length, |
5368 | argument_length); |
5369 | length = effect = graph()->NewNode( |
5370 | simplified()->CheckBounds(p.feedback()), length, |
5371 | jsgraph()->Constant(String::kMaxLength + 1), effect, control); |
5372 | |
5373 | Node* value = graph()->NewNode(simplified()->StringConcat(), length, receiver, |
5374 | argument); |
5375 | |
5376 | ReplaceWithValue(node, value, effect, control); |
5377 | return Replace(value); |
5378 | } |
5379 | |
5380 | 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 | common()->CreateFrameStateFunctionInfo( |
5386 | frame_state_type, parameter_count + 1, 0, shared.object()); |
5387 | |
5388 | const Operator* op = common()->FrameState( |
5389 | bailout_id, OutputFrameStateCombine::Ignore(), state_info); |
5390 | const Operator* op0 = common()->StateValues(0, SparseInputMask::Dense()); |
5391 | Node* node0 = graph()->NewNode(op0); |
5392 | std::vector<Node*> params; |
5393 | params.reserve(parameter_count + 1); |
5394 | for (int parameter = 0; parameter < parameter_count + 1; ++parameter) { |
5395 | params.push_back(node->InputAt(1 + parameter)); |
5396 | } |
5397 | const Operator* op_param = common()->StateValues( |
5398 | static_cast<int>(params.size()), SparseInputMask::Dense()); |
5399 | Node* params_node = graph()->NewNode( |
5400 | op_param, static_cast<int>(params.size()), ¶ms.front()); |
5401 | if (!context) { |
5402 | context = jsgraph()->UndefinedConstant(); |
5403 | } |
5404 | return graph()->NewNode(op, params_node, node0, node0, context, |
5405 | node->InputAt(0), outer_frame_state); |
5406 | } |
5407 | |
5408 | Reduction JSCallReducer::ReducePromiseConstructor(Node* node) { |
5409 | DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); |
5410 | ConstructParameters const& p = ConstructParametersOf(node->op()); |
5411 | int arity = static_cast<int>(p.arity() - 2); |
5412 | // We only inline when we have the executor. |
5413 | if (arity < 1) return NoChange(); |
5414 | Node* target = NodeProperties::GetValueInput(node, 0); |
5415 | Node* executor = NodeProperties::GetValueInput(node, 1); |
5416 | Node* new_target = NodeProperties::GetValueInput(node, arity + 1); |
5417 | |
5418 | Node* context = NodeProperties::GetContextInput(node); |
5419 | Node* outer_frame_state = NodeProperties::GetFrameStateInput(node); |
5420 | Node* effect = NodeProperties::GetEffectInput(node); |
5421 | Node* control = NodeProperties::GetControlInput(node); |
5422 | |
5423 | if (!FLAG_experimental_inline_promise_constructor) return NoChange(); |
5424 | |
5425 | // Only handle builtins Promises, not subclasses. |
5426 | if (target != new_target) return NoChange(); |
5427 | |
5428 | if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); |
5429 | |
5430 | SharedFunctionInfoRef promise_shared = |
5431 | 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 | 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 | jsgraph()->UndefinedConstant(), /* receiver */ |
5446 | jsgraph()->UndefinedConstant(), /* promise */ |
5447 | jsgraph()->UndefinedConstant(), /* reject function */ |
5448 | jsgraph()->TheHoleConstant() /* exception */ |
5449 | }); |
5450 | int checkpoint_parameters_size = |
5451 | 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 | constructor_frame_state, ContinuationFrameStateMode::LAZY); |
5457 | |
5458 | // Check if executor is callable |
5459 | Node* check_fail = nullptr; |
5460 | Node* check_throw = nullptr; |
5461 | WireInCallbackIsCallableCheck(executor, context, frame_state, effect, |
5462 | &control, &check_fail, &check_throw); |
5463 | |
5464 | // Create the resulting JSPromise. |
5465 | Node* promise = effect = |
5466 | graph()->NewNode(javascript()->CreatePromise(), context, effect); |
5467 | |
5468 | // 8. CreatePromiseResolvingFunctions |
5469 | // Allocate a promise context for the closures below. |
5470 | Node* promise_context = effect = graph()->NewNode( |
5471 | javascript()->CreateFunctionContext( |
5472 | handle(native_context().object()->scope_info(), isolate()), |
5473 | PromiseBuiltins::kPromiseContextLength - Context::MIN_CONTEXT_SLOTS, |
5474 | FUNCTION_SCOPE), |
5475 | context, effect, control); |
5476 | effect = graph()->NewNode( |
5477 | simplified()->StoreField( |
5478 | AccessBuilder::ForContextSlot(PromiseBuiltins::kPromiseSlot)), |
5479 | promise_context, promise, effect, control); |
5480 | effect = graph()->NewNode( |
5481 | simplified()->StoreField( |
5482 | AccessBuilder::ForContextSlot(PromiseBuiltins::kAlreadyResolvedSlot)), |
5483 | promise_context, jsgraph()->FalseConstant(), effect, control); |
5484 | effect = graph()->NewNode( |
5485 | simplified()->StoreField( |
5486 | 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 | native_context().promise_capability_default_resolve_shared_fun(); |
5492 | Node* resolve = effect = graph()->NewNode( |
5493 | javascript()->CreateClosure( |
5494 | resolve_shared.object(), factory()->many_closures_cell(), |
5495 | 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 | native_context().promise_capability_default_reject_shared_fun(); |
5501 | Node* reject = effect = graph()->NewNode( |
5502 | javascript()->CreateClosure( |
5503 | reject_shared.object(), factory()->many_closures_cell(), |
5504 | handle(reject_shared.object()->GetCode(), isolate())), |
5505 | promise_context, effect, control); |
5506 | |
5507 | const std::vector<Node*> checkpoint_parameters_continuation( |
5508 | {jsgraph()->UndefinedConstant() /* receiver */, promise, reject}); |
5509 | int checkpoint_parameters_continuation_size = |
5510 | 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 | ContinuationFrameStateMode::LAZY_WITH_CATCH); |
5519 | |
5520 | // 9. Call executor with both resolving functions |
5521 | effect = control = graph()->NewNode( |
5522 | javascript()->Call(4, p.frequency(), VectorSlotPair(), |
5523 | ConvertReceiverMode::kNullOrUndefined, |
5524 | SpeculationMode::kDisallowSpeculation), |
5525 | executor, jsgraph()->UndefinedConstant(), resolve, reject, context, |
5526 | frame_state, effect, control); |
5527 | |
5528 | Node* exception_effect = effect; |
5529 | Node* exception_control = control; |
5530 | { |
5531 | Node* reason = exception_effect = exception_control = graph()->NewNode( |
5532 | common()->IfException(), exception_control, exception_effect); |
5533 | // 10a. Call reject if the call to executor threw. |
5534 | exception_effect = exception_control = graph()->NewNode( |
5535 | javascript()->Call(3, p.frequency(), VectorSlotPair(), |
5536 | ConvertReceiverMode::kNullOrUndefined, |
5537 | SpeculationMode::kDisallowSpeculation), |
5538 | reject, jsgraph()->UndefinedConstant(), reason, context, frame_state, |
5539 | exception_effect, exception_control); |
5540 | |
5541 | // Rewire potential exception edges. |
5542 | Node* on_exception = nullptr; |
5543 | if (NodeProperties::IsExceptionalCall(node, &on_exception)) { |
5544 | RewirePostCallbackExceptionEdges(check_throw, on_exception, |
5545 | exception_effect, &check_fail, |
5546 | &exception_control); |
5547 | } |
5548 | } |
5549 | |
5550 | Node* success_effect = effect; |
5551 | Node* success_control = control; |
5552 | { |
5553 | success_control = graph()->NewNode(common()->IfSuccess(), success_control); |
5554 | } |
5555 | |
5556 | control = |
5557 | graph()->NewNode(common()->Merge(2), success_control, exception_control); |
5558 | 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 | graph()->NewNode(common()->Throw(), check_throw, check_fail); |
5567 | NodeProperties::MergeControlToEnd(graph(), common(), throw_node); |
5568 | |
5569 | ReplaceWithValue(node, promise, effect, control); |
5570 | return Replace(promise); |
5571 | } |
5572 | |
5573 | // V8 Extras: v8.createPromise(parent) |
5574 | Reduction JSCallReducer::ReducePromiseInternalConstructor(Node* node) { |
5575 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5576 | Node* context = NodeProperties::GetContextInput(node); |
5577 | Node* effect = NodeProperties::GetEffectInput(node); |
5578 | |
5579 | // Check that promises aren't being observed through (debug) hooks. |
5580 | if (!dependencies()->DependOnPromiseHookProtector()) return NoChange(); |
5581 | |
5582 | // Create a new pending promise. |
5583 | Node* value = effect = |
5584 | 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 | 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 | : jsgraph()->UndefinedConstant(); |
5596 | Node* reason = node->op()->ValueInputCount() >= 3 |
5597 | ? NodeProperties::GetValueInput(node, 3) |
5598 | : jsgraph()->UndefinedConstant(); |
5599 | Node* debug_event = jsgraph()->TrueConstant(); |
5600 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
5601 | Node* context = NodeProperties::GetContextInput(node); |
5602 | Node* effect = NodeProperties::GetEffectInput(node); |
5603 | Node* control = NodeProperties::GetControlInput(node); |
5604 | |
5605 | // Reject the {promise} using the given {reason}, and trigger debug logic. |
5606 | Node* value = effect = |
5607 | graph()->NewNode(javascript()->RejectPromise(), promise, reason, |
5608 | debug_event, context, frame_state, effect, control); |
5609 | |
5610 | ReplaceWithValue(node, value, effect, control); |
5611 | return Replace(value); |
5612 | } |
5613 | |
5614 | // V8 Extras: v8.resolvePromise(promise, resolution) |
5615 | 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 | : jsgraph()->UndefinedConstant(); |
5620 | Node* resolution = node->op()->ValueInputCount() >= 3 |
5621 | ? NodeProperties::GetValueInput(node, 3) |
5622 | : jsgraph()->UndefinedConstant(); |
5623 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
5624 | Node* context = NodeProperties::GetContextInput(node); |
5625 | Node* effect = NodeProperties::GetEffectInput(node); |
5626 | Node* control = NodeProperties::GetControlInput(node); |
5627 | |
5628 | // Resolve the {promise} using the given {resolution}. |
5629 | Node* value = effect = |
5630 | graph()->NewNode(javascript()->ResolvePromise(), promise, resolution, |
5631 | context, frame_state, effect, control); |
5632 | |
5633 | ReplaceWithValue(node, value, effect, control); |
5634 | return Replace(value); |
5635 | } |
5636 | |
5637 | // ES section #sec-promise.prototype.catch |
5638 | Reduction JSCallReducer::ReducePromisePrototypeCatch(Node* node) { |
5639 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5640 | CallParameters const& p = CallParametersOf(node->op()); |
5641 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5642 | return NoChange(); |
5643 | } |
5644 | int arity = static_cast<int>(p.arity() - 2); |
5645 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5646 | Node* effect = NodeProperties::GetEffectInput(node); |
5647 | 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 | &receiver_maps); |
5654 | 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 | for (Handle<Map> map : receiver_maps) { |
5660 | MapRef receiver_map(broker(), map); |
5661 | if (!receiver_map.IsJSPromiseMap()) return NoChange(); |
5662 | receiver_map.SerializePrototype(); |
5663 | if (!receiver_map.prototype().equals( |
5664 | 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 | if (!dependencies()->DependOnPromiseThenProtector()) return NoChange(); |
5674 | |
5675 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
5676 | 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 | Node* target = jsgraph()->Constant(native_context().promise_then()); |
5682 | NodeProperties::ReplaceValueInput(node, target, 0); |
5683 | NodeProperties::ReplaceEffectInput(node, effect); |
5684 | for (; arity > 1; --arity) node->RemoveInput(3); |
5685 | for (; arity < 2; ++arity) { |
5686 | node->InsertInput(graph()->zone(), 2, jsgraph()->UndefinedConstant()); |
5687 | } |
5688 | NodeProperties::ChangeOp( |
5689 | node, javascript()->Call(2 + arity, p.frequency(), p.feedback(), |
5690 | ConvertReceiverMode::kNotNullOrUndefined, |
5691 | p.speculation_mode())); |
5692 | Reduction const reduction = ReducePromisePrototypeThen(node); |
5693 | return reduction.Changed() ? reduction : Changed(node); |
5694 | } |
5695 | |
5696 | // ES section #sec-promise.prototype.finally |
5697 | Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) { |
5698 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5699 | CallParameters const& p = CallParametersOf(node->op()); |
5700 | int arity = static_cast<int>(p.arity() - 2); |
5701 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5702 | Node* on_finally = arity >= 1 ? NodeProperties::GetValueInput(node, 2) |
5703 | : jsgraph()->UndefinedConstant(); |
5704 | Node* effect = NodeProperties::GetEffectInput(node); |
5705 | Node* control = NodeProperties::GetControlInput(node); |
5706 | 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 | &receiver_maps); |
5715 | 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 | for (Handle<Map> map : receiver_maps) { |
5721 | MapRef receiver_map(broker(), map); |
5722 | if (!receiver_map.IsJSPromiseMap()) return NoChange(); |
5723 | receiver_map.SerializePrototype(); |
5724 | if (!receiver_map.prototype().equals( |
5725 | native_context().promise_prototype())) { |
5726 | return NoChange(); |
5727 | } |
5728 | } |
5729 | |
5730 | // Check that promises aren't being observed through (debug) hooks. |
5731 | 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 | 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 | if (!dependencies()->DependOnPromiseSpeciesProtector()) return NoChange(); |
5744 | |
5745 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
5746 | 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 | Node* check = graph()->NewNode(simplified()->ObjectIsCallable(), on_finally); |
5751 | Node* branch = |
5752 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
5753 | |
5754 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
5755 | Node* etrue = effect; |
5756 | Node* catch_true; |
5757 | Node* then_true; |
5758 | { |
5759 | Node* context = jsgraph()->Constant(native_context()); |
5760 | Node* constructor = |
5761 | jsgraph()->Constant(native_context().promise_function()); |
5762 | |
5763 | // Allocate shared context for the closures below. |
5764 | context = etrue = graph()->NewNode( |
5765 | javascript()->CreateFunctionContext( |
5766 | handle(native_context().object()->scope_info(), isolate()), |
5767 | PromiseBuiltins::kPromiseFinallyContextLength - |
5768 | Context::MIN_CONTEXT_SLOTS, |
5769 | FUNCTION_SCOPE), |
5770 | context, etrue, if_true); |
5771 | etrue = graph()->NewNode( |
5772 | simplified()->StoreField( |
5773 | AccessBuilder::ForContextSlot(PromiseBuiltins::kOnFinallySlot)), |
5774 | context, on_finally, etrue, if_true); |
5775 | etrue = graph()->NewNode( |
5776 | simplified()->StoreField( |
5777 | AccessBuilder::ForContextSlot(PromiseBuiltins::kConstructorSlot)), |
5778 | context, constructor, etrue, if_true); |
5779 | |
5780 | // Allocate the closure for the reject case. |
5781 | SharedFunctionInfoRef catch_finally = |
5782 | native_context().promise_catch_finally_shared_fun(); |
5783 | catch_true = etrue = graph()->NewNode( |
5784 | javascript()->CreateClosure( |
5785 | catch_finally.object(), factory()->many_closures_cell(), |
5786 | 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 | native_context().promise_then_finally_shared_fun(); |
5792 | then_true = etrue = graph()->NewNode( |
5793 | javascript()->CreateClosure( |
5794 | then_finally.object(), factory()->many_closures_cell(), |
5795 | handle(then_finally.object()->GetCode(), isolate())), |
5796 | context, etrue, if_true); |
5797 | } |
5798 | |
5799 | 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 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
5805 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
5806 | Node* catch_finally = |
5807 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), |
5808 | catch_true, catch_false, control); |
5809 | Node* then_finally = |
5810 | 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 | 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 | Node* target = jsgraph()->Constant(native_context().promise_then()); |
5823 | NodeProperties::ReplaceValueInput(node, target, 0); |
5824 | NodeProperties::ReplaceEffectInput(node, effect); |
5825 | NodeProperties::ReplaceControlInput(node, control); |
5826 | for (; arity > 2; --arity) node->RemoveInput(2); |
5827 | for (; arity < 2; ++arity) |
5828 | node->InsertInput(graph()->zone(), 2, then_finally); |
5829 | node->ReplaceInput(2, then_finally); |
5830 | node->ReplaceInput(3, catch_finally); |
5831 | NodeProperties::ChangeOp( |
5832 | node, javascript()->Call(2 + arity, p.frequency(), p.feedback(), |
5833 | ConvertReceiverMode::kNotNullOrUndefined, |
5834 | p.speculation_mode())); |
5835 | Reduction const reduction = ReducePromisePrototypeThen(node); |
5836 | return reduction.Changed() ? reduction : Changed(node); |
5837 | } |
5838 | |
5839 | Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) { |
5840 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5841 | CallParameters const& p = CallParametersOf(node->op()); |
5842 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
5843 | return NoChange(); |
5844 | } |
5845 | |
5846 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5847 | Node* on_fulfilled = node->op()->ValueInputCount() > 2 |
5848 | ? NodeProperties::GetValueInput(node, 2) |
5849 | : jsgraph()->UndefinedConstant(); |
5850 | Node* on_rejected = node->op()->ValueInputCount() > 3 |
5851 | ? NodeProperties::GetValueInput(node, 3) |
5852 | : jsgraph()->UndefinedConstant(); |
5853 | Node* context = NodeProperties::GetContextInput(node); |
5854 | Node* effect = NodeProperties::GetEffectInput(node); |
5855 | Node* control = NodeProperties::GetControlInput(node); |
5856 | 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 | &receiver_maps); |
5863 | 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 | for (Handle<Map> map : receiver_maps) { |
5869 | MapRef receiver_map(broker(), map); |
5870 | if (!receiver_map.IsJSPromiseMap()) return NoChange(); |
5871 | receiver_map.SerializePrototype(); |
5872 | if (!receiver_map.prototype().equals( |
5873 | native_context().promise_prototype())) { |
5874 | return NoChange(); |
5875 | } |
5876 | } |
5877 | |
5878 | // Check that promises aren't being observed through (debug) hooks. |
5879 | 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 | if (!dependencies()->DependOnPromiseSpeciesProtector()) return NoChange(); |
5886 | |
5887 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
5888 | result, receiver_maps, p.feedback(), receiver, effect, control); |
5889 | |
5890 | // Check that {on_fulfilled} is callable. |
5891 | 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 | 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 | graph()->NewNode(javascript()->CreatePromise(), context, effect); |
5905 | |
5906 | // Chain {result} onto {receiver}. |
5907 | 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 | MapRef promise_map = native_context().promise_function().initial_map(); |
5918 | 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 | Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) { |
5928 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
5929 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
5930 | Node* value = node->op()->ValueInputCount() > 2 |
5931 | ? NodeProperties::GetValueInput(node, 2) |
5932 | : jsgraph()->UndefinedConstant(); |
5933 | Node* context = NodeProperties::GetContextInput(node); |
5934 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
5935 | Node* effect = NodeProperties::GetEffectInput(node); |
5936 | 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 | &receiver_maps); |
5943 | 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 | for (Handle<Map> map : receiver_maps) { |
5950 | MapRef receiver_map(broker(), map); |
5951 | if (!receiver_map.IsJSReceiverMap()) return NoChange(); |
5952 | } |
5953 | |
5954 | // Morph the {node} into a JSPromiseResolve operation. |
5955 | node->ReplaceInput(0, receiver); |
5956 | node->ReplaceInput(1, value); |
5957 | node->ReplaceInput(2, context); |
5958 | node->ReplaceInput(3, frame_state); |
5959 | node->ReplaceInput(4, effect); |
5960 | node->ReplaceInput(5, control); |
5961 | node->TrimInputCount(6); |
5962 | NodeProperties::ChangeOp(node, javascript()->PromiseResolve()); |
5963 | return Changed(node); |
5964 | } |
5965 | |
5966 | // ES #sec-typedarray-constructors |
5967 | Reduction JSCallReducer::ReduceTypedArrayConstructor( |
5968 | Node* node, const SharedFunctionInfoRef& shared) { |
5969 | DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode()); |
5970 | ConstructParameters const& p = ConstructParametersOf(node->op()); |
5971 | int arity = static_cast<int>(p.arity() - 2); |
5972 | Node* target = NodeProperties::GetValueInput(node, 0); |
5973 | Node* arg1 = (arity >= 1) ? NodeProperties::GetValueInput(node, 1) |
5974 | : jsgraph()->UndefinedConstant(); |
5975 | Node* arg2 = (arity >= 2) ? NodeProperties::GetValueInput(node, 2) |
5976 | : jsgraph()->UndefinedConstant(); |
5977 | Node* arg3 = (arity >= 3) ? NodeProperties::GetValueInput(node, 3) |
5978 | : jsgraph()->UndefinedConstant(); |
5979 | Node* new_target = NodeProperties::GetValueInput(node, arity + 1); |
5980 | Node* context = NodeProperties::GetContextInput(node); |
5981 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
5982 | Node* effect = NodeProperties::GetEffectInput(node); |
5983 | 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 | 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 | 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 | ContinuationFrameStateMode::LAZY); |
6000 | |
6001 | Node* result = |
6002 | graph()->NewNode(javascript()->CreateTypedArray(), target, new_target, |
6003 | arg1, arg2, arg3, context, frame_state, effect, control); |
6004 | return Replace(result); |
6005 | } |
6006 | |
6007 | // ES #sec-get-%typedarray%.prototype-@@tostringtag |
6008 | Reduction JSCallReducer::ReduceTypedArrayPrototypeToStringTag(Node* node) { |
6009 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6010 | Node* effect = NodeProperties::GetEffectInput(node); |
6011 | Node* control = NodeProperties::GetControlInput(node); |
6012 | |
6013 | NodeVector values(graph()->zone()); |
6014 | NodeVector effects(graph()->zone()); |
6015 | NodeVector controls(graph()->zone()); |
6016 | |
6017 | Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); |
6018 | control = |
6019 | graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); |
6020 | |
6021 | values.push_back(jsgraph()->UndefinedConstant()); |
6022 | effects.push_back(effect); |
6023 | controls.push_back(graph()->NewNode(common()->IfTrue(), control)); |
6024 | |
6025 | control = graph()->NewNode(common()->IfFalse(), control); |
6026 | Node* receiver_map = effect = |
6027 | graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), |
6028 | receiver, effect, control); |
6029 | Node* receiver_bit_field2 = effect = graph()->NewNode( |
6030 | simplified()->LoadField(AccessBuilder::ForMapBitField2()), receiver_map, |
6031 | effect, control); |
6032 | 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 | 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 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
6059 | #undef TYPED_ARRAY_CASE |
6060 | |
6061 | values.push_back(jsgraph()->UndefinedConstant()); |
6062 | effects.push_back(effect); |
6063 | controls.push_back(control); |
6064 | |
6065 | int const count = static_cast<int>(controls.size()); |
6066 | control = graph()->NewNode(common()->Merge(count), count, &controls.front()); |
6067 | effects.push_back(control); |
6068 | effect = |
6069 | graph()->NewNode(common()->EffectPhi(count), count + 1, &effects.front()); |
6070 | values.push_back(control); |
6071 | Node* value = |
6072 | graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count), |
6073 | count + 1, &values.front()); |
6074 | ReplaceWithValue(node, value, effect, control); |
6075 | return Replace(value); |
6076 | } |
6077 | |
6078 | // ES #sec-number.isfinite |
6079 | Reduction JSCallReducer::ReduceNumberIsFinite(Node* node) { |
6080 | if (node->op()->ValueInputCount() < 3) { |
6081 | Node* value = jsgraph()->FalseConstant(); |
6082 | ReplaceWithValue(node, value); |
6083 | return Replace(value); |
6084 | } |
6085 | Node* input = NodeProperties::GetValueInput(node, 2); |
6086 | Node* value = graph()->NewNode(simplified()->ObjectIsFiniteNumber(), input); |
6087 | ReplaceWithValue(node, value); |
6088 | return Replace(value); |
6089 | } |
6090 | |
6091 | // ES #sec-number.isfinite |
6092 | Reduction JSCallReducer::ReduceNumberIsInteger(Node* node) { |
6093 | if (node->op()->ValueInputCount() < 3) { |
6094 | Node* value = jsgraph()->FalseConstant(); |
6095 | ReplaceWithValue(node, value); |
6096 | return Replace(value); |
6097 | } |
6098 | Node* input = NodeProperties::GetValueInput(node, 2); |
6099 | Node* value = graph()->NewNode(simplified()->ObjectIsInteger(), input); |
6100 | ReplaceWithValue(node, value); |
6101 | return Replace(value); |
6102 | } |
6103 | |
6104 | // ES #sec-number.issafeinteger |
6105 | Reduction JSCallReducer::ReduceNumberIsSafeInteger(Node* node) { |
6106 | if (node->op()->ValueInputCount() < 3) { |
6107 | Node* value = jsgraph()->FalseConstant(); |
6108 | ReplaceWithValue(node, value); |
6109 | return Replace(value); |
6110 | } |
6111 | Node* input = NodeProperties::GetValueInput(node, 2); |
6112 | Node* value = graph()->NewNode(simplified()->ObjectIsSafeInteger(), input); |
6113 | ReplaceWithValue(node, value); |
6114 | return Replace(value); |
6115 | } |
6116 | |
6117 | // ES #sec-number.isnan |
6118 | Reduction JSCallReducer::ReduceNumberIsNaN(Node* node) { |
6119 | if (node->op()->ValueInputCount() < 3) { |
6120 | Node* value = jsgraph()->FalseConstant(); |
6121 | ReplaceWithValue(node, value); |
6122 | return Replace(value); |
6123 | } |
6124 | Node* input = NodeProperties::GetValueInput(node, 2); |
6125 | Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input); |
6126 | ReplaceWithValue(node, value); |
6127 | return Replace(value); |
6128 | } |
6129 | |
6130 | Reduction JSCallReducer::ReduceMapPrototypeGet(Node* node) { |
6131 | // We only optimize if we have target, receiver and key parameters. |
6132 | if (node->op()->ValueInputCount() != 3) return NoChange(); |
6133 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6134 | Node* effect = NodeProperties::GetEffectInput(node); |
6135 | Node* control = NodeProperties::GetControlInput(node); |
6136 | Node* key = NodeProperties::GetValueInput(node, 2); |
6137 | |
6138 | if (!NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, |
6139 | JS_MAP_TYPE)) |
6140 | return NoChange(); |
6141 | |
6142 | Node* table = effect = graph()->NewNode( |
6143 | simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, |
6144 | effect, control); |
6145 | |
6146 | Node* entry = effect = graph()->NewNode( |
6147 | simplified()->FindOrderedHashMapEntry(), table, key, effect, control); |
6148 | |
6149 | Node* check = graph()->NewNode(simplified()->NumberEqual(), entry, |
6150 | jsgraph()->MinusOneConstant()); |
6151 | |
6152 | Node* branch = graph()->NewNode(common()->Branch(), check, control); |
6153 | |
6154 | // Key not found. |
6155 | Node* if_true = graph()->NewNode(common()->IfTrue(), branch); |
6156 | Node* etrue = effect; |
6157 | Node* vtrue = jsgraph()->UndefinedConstant(); |
6158 | |
6159 | // Key found. |
6160 | Node* if_false = graph()->NewNode(common()->IfFalse(), branch); |
6161 | Node* efalse = effect; |
6162 | Node* vfalse = efalse = graph()->NewNode( |
6163 | simplified()->LoadElement(AccessBuilder::ForOrderedHashMapEntryValue()), |
6164 | table, entry, efalse, if_false); |
6165 | |
6166 | control = graph()->NewNode(common()->Merge(2), if_true, if_false); |
6167 | Node* value = graph()->NewNode( |
6168 | common()->Phi(MachineRepresentation::kTagged, 2), vtrue, vfalse, control); |
6169 | effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); |
6170 | |
6171 | ReplaceWithValue(node, value, effect, control); |
6172 | return Replace(value); |
6173 | } |
6174 | |
6175 | Reduction JSCallReducer::ReduceMapPrototypeHas(Node* node) { |
6176 | // We only optimize if we have target, receiver and key parameters. |
6177 | if (node->op()->ValueInputCount() != 3) return NoChange(); |
6178 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6179 | Node* effect = NodeProperties::GetEffectInput(node); |
6180 | Node* control = NodeProperties::GetControlInput(node); |
6181 | Node* key = NodeProperties::GetValueInput(node, 2); |
6182 | |
6183 | if (!NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, |
6184 | JS_MAP_TYPE)) |
6185 | return NoChange(); |
6186 | |
6187 | Node* table = effect = graph()->NewNode( |
6188 | simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), receiver, |
6189 | effect, control); |
6190 | |
6191 | Node* index = effect = graph()->NewNode( |
6192 | simplified()->FindOrderedHashMapEntry(), table, key, effect, control); |
6193 | |
6194 | Node* value = graph()->NewNode(simplified()->NumberEqual(), index, |
6195 | jsgraph()->MinusOneConstant()); |
6196 | 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 | switch (kind) { |
6206 | case CollectionKind::kMap: |
6207 | return JS_MAP_TYPE; |
6208 | case CollectionKind::kSet: |
6209 | return JS_SET_TYPE; |
6210 | } |
6211 | UNREACHABLE(); |
6212 | } |
6213 | |
6214 | } // namespace |
6215 | |
6216 | Reduction JSCallReducer::ReduceCollectionIteration( |
6217 | Node* node, CollectionKind collection_kind, IterationKind iteration_kind) { |
6218 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
6219 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6220 | Node* context = NodeProperties::GetContextInput(node); |
6221 | Node* effect = NodeProperties::GetEffectInput(node); |
6222 | Node* control = NodeProperties::GetControlInput(node); |
6223 | if (NodeProperties::HasInstanceTypeWitness( |
6224 | broker(), receiver, effect, |
6225 | InstanceTypeForCollectionKind(collection_kind))) { |
6226 | 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 | Reduction JSCallReducer::ReduceCollectionPrototypeSize( |
6236 | Node* node, CollectionKind collection_kind) { |
6237 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
6238 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6239 | Node* effect = NodeProperties::GetEffectInput(node); |
6240 | Node* control = NodeProperties::GetControlInput(node); |
6241 | if (NodeProperties::HasInstanceTypeWitness( |
6242 | broker(), receiver, effect, |
6243 | InstanceTypeForCollectionKind(collection_kind))) { |
6244 | Node* table = effect = graph()->NewNode( |
6245 | simplified()->LoadField(AccessBuilder::ForJSCollectionTable()), |
6246 | receiver, effect, control); |
6247 | Node* value = effect = graph()->NewNode( |
6248 | simplified()->LoadField( |
6249 | AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()), |
6250 | table, effect, control); |
6251 | ReplaceWithValue(node, value, effect, control); |
6252 | return Replace(value); |
6253 | } |
6254 | return NoChange(); |
6255 | } |
6256 | |
6257 | 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 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6263 | Node* context = NodeProperties::GetContextInput(node); |
6264 | Node* effect = NodeProperties::GetEffectInput(node); |
6265 | 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 | &receiver_maps); |
6285 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
6286 | DCHECK_NE(0, receiver_maps.size()); |
6287 | receiver_instance_type = receiver_maps[0]->instance_type(); |
6288 | for (size_t i = 1; i < receiver_maps.size(); ++i) { |
6289 | if (receiver_maps[i]->instance_type() != receiver_instance_type) { |
6290 | return NoChange(); |
6291 | } |
6292 | } |
6293 | if (receiver_instance_type < collection_iterator_instance_type_first || |
6294 | 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 | graph()->NewNode(common()->Loop(2), control, control); |
6305 | Node* eloop = effect = |
6306 | graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
6307 | Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); |
6308 | NodeProperties::MergeControlToEnd(graph(), common(), terminate); |
6309 | |
6310 | // Check if reached the final table of the {receiver}. |
6311 | Node* table = effect = graph()->NewNode( |
6312 | simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorTable()), |
6313 | receiver, effect, control); |
6314 | Node* next_table = effect = |
6315 | graph()->NewNode(simplified()->LoadField( |
6316 | AccessBuilder::ForOrderedHashMapOrSetNextTable()), |
6317 | table, effect, control); |
6318 | Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), next_table); |
6319 | control = |
6320 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check, control); |
6321 | |
6322 | // Abort the {loop} when we reach the final table. |
6323 | done_loop = graph()->NewNode(common()->IfTrue(), control); |
6324 | done_eloop = effect; |
6325 | |
6326 | // Migrate to the {next_table} otherwise. |
6327 | control = graph()->NewNode(common()->IfFalse(), control); |
6328 | |
6329 | // Self-heal the {receiver}s index. |
6330 | Node* index = effect = graph()->NewNode( |
6331 | simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), |
6332 | receiver, effect, control); |
6333 | Callable const callable = |
6334 | Builtins::CallableFor(isolate(), Builtins::kOrderedHashTableHealIndex); |
6335 | auto call_descriptor = Linkage::GetStubCallDescriptor( |
6336 | graph()->zone(), callable.descriptor(), |
6337 | callable.descriptor().GetStackParameterCount(), |
6338 | CallDescriptor::kNoFlags, Operator::kEliminatable); |
6339 | index = effect = |
6340 | graph()->NewNode(common()->Call(call_descriptor), |
6341 | jsgraph()->HeapConstant(callable.code()), table, index, |
6342 | jsgraph()->NoContextConstant(), effect); |
6343 | |
6344 | index = effect = graph()->NewNode( |
6345 | common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), index, |
6346 | effect, control); |
6347 | |
6348 | // Update the {index} and {table} on the {receiver}. |
6349 | effect = graph()->NewNode( |
6350 | simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorIndex()), |
6351 | receiver, index, effect, control); |
6352 | effect = graph()->NewNode( |
6353 | simplified()->StoreField(AccessBuilder::ForJSCollectionIteratorTable()), |
6354 | receiver, next_table, effect, control); |
6355 | |
6356 | // Tie the knot. |
6357 | loop->ReplaceInput(1, control); |
6358 | 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 | Node* index = effect = graph()->NewNode( |
6366 | simplified()->LoadField(AccessBuilder::ForJSCollectionIteratorIndex()), |
6367 | receiver, effect, control); |
6368 | Node* table = effect = graph()->NewNode( |
6369 | 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 | 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 | Node* number_of_buckets = effect = graph()->NewNode( |
6384 | simplified()->LoadField( |
6385 | AccessBuilder::ForOrderedHashMapOrSetNumberOfBuckets()), |
6386 | table, effect, control); |
6387 | Node* number_of_elements = effect = graph()->NewNode( |
6388 | simplified()->LoadField( |
6389 | AccessBuilder::ForOrderedHashMapOrSetNumberOfElements()), |
6390 | table, effect, control); |
6391 | Node* number_of_deleted_elements = effect = graph()->NewNode( |
6392 | simplified()->LoadField( |
6393 | AccessBuilder::ForOrderedHashMapOrSetNumberOfDeletedElements()), |
6394 | table, effect, control); |
6395 | Node* used_capacity = |
6396 | graph()->NewNode(simplified()->NumberAdd(), number_of_elements, |
6397 | number_of_deleted_elements); |
6398 | |
6399 | // Skip holes and update the {index}. |
6400 | Node* loop = graph()->NewNode(common()->Loop(2), control, control); |
6401 | Node* eloop = |
6402 | graph()->NewNode(common()->EffectPhi(2), effect, effect, loop); |
6403 | Node* terminate = graph()->NewNode(common()->Terminate(), eloop, loop); |
6404 | NodeProperties::MergeControlToEnd(graph(), common(), terminate); |
6405 | Node* iloop = graph()->NewNode( |
6406 | common()->Phi(MachineRepresentation::kTagged, 2), index, index, loop); |
6407 | |
6408 | Node* index = effect = graph()->NewNode( |
6409 | common()->TypeGuard(TypeCache::Get()->kFixedArrayLengthType), iloop, |
6410 | eloop, control); |
6411 | { |
6412 | Node* check0 = graph()->NewNode(simplified()->NumberLessThan(), index, |
6413 | used_capacity); |
6414 | Node* branch0 = |
6415 | graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, loop); |
6416 | |
6417 | Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0); |
6418 | Node* efalse0 = effect; |
6419 | { |
6420 | // Mark the {receiver} as exhausted. |
6421 | efalse0 = graph()->NewNode( |
6422 | simplified()->StoreField( |
6423 | AccessBuilder::ForJSCollectionIteratorTable()), |
6424 | receiver, jsgraph()->HeapConstant(empty_collection), efalse0, |
6425 | if_false0); |
6426 | |
6427 | controls[0] = if_false0; |
6428 | effects[0] = efalse0; |
6429 | } |
6430 | |
6431 | 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 | 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 | Node* entry_key = etrue0 = graph()->NewNode( |
6446 | simplified()->LoadElement(AccessBuilder::ForFixedArrayElement()), |
6447 | table, entry_start_position, etrue0, if_true0); |
6448 | |
6449 | // Advance the index. |
6450 | index = graph()->NewNode(simplified()->NumberAdd(), index, |
6451 | jsgraph()->OneConstant()); |
6452 | |
6453 | Node* check1 = |
6454 | graph()->NewNode(simplified()->ReferenceEqual(), entry_key, |
6455 | jsgraph()->TheHoleConstant()); |
6456 | Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse), |
6457 | check1, if_true0); |
6458 | |
6459 | { |
6460 | // Abort loop with resulting value. |
6461 | Node* control = graph()->NewNode(common()->IfFalse(), branch1); |
6462 | Node* effect = etrue0; |
6463 | Node* value = effect = |
6464 | graph()->NewNode(common()->TypeGuard(Type::NonInternal()), |
6465 | entry_key, effect, control); |
6466 | Node* done = jsgraph()->FalseConstant(); |
6467 | |
6468 | // Advance the index on the {receiver}. |
6469 | effect = graph()->NewNode( |
6470 | simplified()->StoreField( |
6471 | AccessBuilder::ForJSCollectionIteratorIndex()), |
6472 | receiver, index, effect, control); |
6473 | |
6474 | // The actual {value} depends on the {receiver} iteration type. |
6475 | 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 | graph()->NewNode(javascript()->CreateKeyValueArray(), value, |
6483 | value, context, effect); |
6484 | break; |
6485 | |
6486 | case JS_MAP_VALUE_ITERATOR_TYPE: |
6487 | value = effect = graph()->NewNode( |
6488 | simplified()->LoadElement( |
6489 | AccessBuilder::ForFixedArrayElement()), |
6490 | table, |
6491 | graph()->NewNode( |
6492 | simplified()->NumberAdd(), entry_start_position, |
6493 | jsgraph()->Constant(OrderedHashMap::kValueOffset)), |
6494 | effect, control); |
6495 | break; |
6496 | |
6497 | case JS_MAP_KEY_VALUE_ITERATOR_TYPE: |
6498 | value = effect = graph()->NewNode( |
6499 | simplified()->LoadElement( |
6500 | 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 | graph()->NewNode(javascript()->CreateKeyValueArray(), |
6508 | entry_key, value, context, effect); |
6509 | break; |
6510 | |
6511 | default: |
6512 | UNREACHABLE(); |
6513 | break; |
6514 | } |
6515 | |
6516 | // Store final {value} and {done} into the {iterator_result}. |
6517 | effect = |
6518 | graph()->NewNode(simplified()->StoreField( |
6519 | AccessBuilder::ForJSIteratorResultValue()), |
6520 | iterator_result, value, effect, control); |
6521 | effect = |
6522 | graph()->NewNode(simplified()->StoreField( |
6523 | AccessBuilder::ForJSIteratorResultDone()), |
6524 | iterator_result, done, effect, control); |
6525 | |
6526 | controls[1] = control; |
6527 | effects[1] = effect; |
6528 | } |
6529 | |
6530 | // Continue with next loop index. |
6531 | loop->ReplaceInput(1, graph()->NewNode(common()->IfTrue(), branch1)); |
6532 | eloop->ReplaceInput(1, etrue0); |
6533 | iloop->ReplaceInput(1, index); |
6534 | } |
6535 | } |
6536 | |
6537 | control = effects[2] = graph()->NewNode(common()->Merge(2), 2, controls); |
6538 | 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 | Reduction JSCallReducer::ReduceArrayBufferIsView(Node* node) { |
6547 | Node* value = node->op()->ValueInputCount() >= 3 |
6548 | ? NodeProperties::GetValueInput(node, 2) |
6549 | : jsgraph()->UndefinedConstant(); |
6550 | RelaxEffectsAndControls(node); |
6551 | node->ReplaceInput(0, value); |
6552 | node->TrimInputCount(1); |
6553 | NodeProperties::ChangeOp(node, simplified()->ObjectIsArrayBufferView()); |
6554 | return Changed(node); |
6555 | } |
6556 | |
6557 | Reduction JSCallReducer::ReduceArrayBufferViewAccessor( |
6558 | Node* node, InstanceType instance_type, FieldAccess const& access) { |
6559 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6560 | Node* effect = NodeProperties::GetEffectInput(node); |
6561 | Node* control = NodeProperties::GetControlInput(node); |
6562 | |
6563 | if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, |
6564 | instance_type)) { |
6565 | // Load the {receiver}s field. |
6566 | Node* value = effect = graph()->NewNode(simplified()->LoadField(access), |
6567 | receiver, effect, control); |
6568 | |
6569 | // See if we can skip the detaching check. |
6570 | if (!dependencies()->DependOnArrayBufferDetachingProtector()) { |
6571 | // Check whether {receiver}s JSArrayBuffer was detached. |
6572 | Node* buffer = effect = graph()->NewNode( |
6573 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), |
6574 | receiver, effect, control); |
6575 | Node* buffer_bit_field = effect = graph()->NewNode( |
6576 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), |
6577 | buffer, effect, control); |
6578 | 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 | 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 | uint32_t ExternalArrayElementSize(const ExternalArrayType element_type) { |
6603 | 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 | TYPED_ARRAYS(TYPED_ARRAY_CASE) |
6609 | default: |
6610 | UNREACHABLE(); |
6611 | #undef TYPED_ARRAY_CASE |
6612 | } |
6613 | } |
6614 | } // namespace |
6615 | |
6616 | Reduction JSCallReducer::ReduceDataViewAccess(Node* node, DataViewAccess access, |
6617 | ExternalArrayType element_type) { |
6618 | size_t const element_size = ExternalArrayElementSize(element_type); |
6619 | CallParameters const& p = CallParametersOf(node->op()); |
6620 | Node* effect = NodeProperties::GetEffectInput(node); |
6621 | Node* control = NodeProperties::GetControlInput(node); |
6622 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6623 | Node* offset = node->op()->ValueInputCount() > 2 |
6624 | ? NodeProperties::GetValueInput(node, 2) |
6625 | : jsgraph()->ZeroConstant(); |
6626 | Node* value = (access == DataViewAccess::kGet) |
6627 | ? nullptr |
6628 | : (node->op()->ValueInputCount() > 3 |
6629 | ? NodeProperties::GetValueInput(node, 3) |
6630 | : 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 | : jsgraph()->FalseConstant()); |
6638 | |
6639 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
6640 | return NoChange(); |
6641 | } |
6642 | |
6643 | // Only do stuff if the {receiver} is really a DataView. |
6644 | 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 | 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 | JSDataViewRef dataview = m.Ref(broker()).AsJSDataView(); |
6654 | 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 | jsgraph()->Constant(dataview.byte_length() - (element_size - 1)); |
6659 | offset = effect = |
6660 | graph()->NewNode(simplified()->CheckBounds(p.feedback()), offset, |
6661 | byte_length, effect, control); |
6662 | |
6663 | // Load the [[ByteOffset]] from the {dataview}. |
6664 | 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 | graph()->NewNode(simplified()->LoadField( |
6669 | AccessBuilder::ForJSArrayBufferViewByteLength()), |
6670 | receiver, effect, control); |
6671 | |
6672 | 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 | byte_length = graph()->NewNode( |
6680 | simplified()->NumberMax(), jsgraph()->ZeroConstant(), |
6681 | graph()->NewNode(simplified()->NumberSubtract(), byte_length, |
6682 | jsgraph()->Constant(element_size - 1))); |
6683 | } |
6684 | |
6685 | // Check that the {offset} is within range of the {byte_length}. |
6686 | offset = effect = |
6687 | 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 | graph()->NewNode(simplified()->LoadField( |
6693 | AccessBuilder::ForJSArrayBufferViewByteOffset()), |
6694 | receiver, effect, control); |
6695 | } |
6696 | |
6697 | // Coerce {is_little_endian} to boolean. |
6698 | is_little_endian = |
6699 | graph()->NewNode(simplified()->ToBoolean(), is_little_endian); |
6700 | |
6701 | // Coerce {value} to Number. |
6702 | if (access == DataViewAccess::kSet) { |
6703 | 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 | Node* buffer = effect = graph()->NewNode( |
6711 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), |
6712 | receiver, effect, control); |
6713 | |
6714 | if (!dependencies()->DependOnArrayBufferDetachingProtector()) { |
6715 | // Bail out if the {buffer} was detached. |
6716 | Node* buffer_bit_field = effect = graph()->NewNode( |
6717 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferBitField()), |
6718 | buffer, effect, control); |
6719 | 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 | 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 | Node* backing_store = effect = graph()->NewNode( |
6733 | simplified()->LoadField(AccessBuilder::ForJSArrayBufferBackingStore()), |
6734 | buffer, effect, control); |
6735 | |
6736 | switch (access) { |
6737 | case DataViewAccess::kGet: |
6738 | // Perform the load. |
6739 | value = effect = |
6740 | graph()->NewNode(simplified()->LoadDataViewElement(element_type), |
6741 | buffer, backing_store, byte_offset, offset, |
6742 | is_little_endian, effect, control); |
6743 | break; |
6744 | case DataViewAccess::kSet: |
6745 | // Perform the store. |
6746 | effect = |
6747 | graph()->NewNode(simplified()->StoreDataViewElement(element_type), |
6748 | buffer, backing_store, byte_offset, offset, value, |
6749 | is_little_endian, effect, control); |
6750 | value = jsgraph()->UndefinedConstant(); |
6751 | 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 | Reduction JSCallReducer::ReduceGlobalIsFinite(Node* node) { |
6764 | CallParameters const& p = CallParametersOf(node->op()); |
6765 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
6766 | return NoChange(); |
6767 | } |
6768 | if (node->op()->ValueInputCount() < 3) { |
6769 | Node* value = jsgraph()->FalseConstant(); |
6770 | ReplaceWithValue(node, value); |
6771 | return Replace(value); |
6772 | } |
6773 | |
6774 | Node* effect = NodeProperties::GetEffectInput(node); |
6775 | Node* control = NodeProperties::GetControlInput(node); |
6776 | Node* input = NodeProperties::GetValueInput(node, 2); |
6777 | |
6778 | input = effect = |
6779 | graph()->NewNode(simplified()->SpeculativeToNumber( |
6780 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
6781 | input, effect, control); |
6782 | 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 | Reduction JSCallReducer::ReduceGlobalIsNaN(Node* node) { |
6789 | CallParameters const& p = CallParametersOf(node->op()); |
6790 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
6791 | return NoChange(); |
6792 | } |
6793 | if (node->op()->ValueInputCount() < 3) { |
6794 | Node* value = jsgraph()->TrueConstant(); |
6795 | ReplaceWithValue(node, value); |
6796 | return Replace(value); |
6797 | } |
6798 | |
6799 | Node* effect = NodeProperties::GetEffectInput(node); |
6800 | Node* control = NodeProperties::GetControlInput(node); |
6801 | Node* input = NodeProperties::GetValueInput(node, 2); |
6802 | |
6803 | input = effect = |
6804 | graph()->NewNode(simplified()->SpeculativeToNumber( |
6805 | NumberOperationHint::kNumberOrOddball, p.feedback()), |
6806 | input, effect, control); |
6807 | 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 | Reduction JSCallReducer::ReduceDatePrototypeGetTime(Node* node) { |
6814 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6815 | Node* effect = NodeProperties::GetEffectInput(node); |
6816 | Node* control = NodeProperties::GetControlInput(node); |
6817 | if (NodeProperties::HasInstanceTypeWitness(broker(), receiver, effect, |
6818 | JS_DATE_TYPE)) { |
6819 | Node* value = effect = graph()->NewNode( |
6820 | 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 | Reduction JSCallReducer::ReduceDateNow(Node* node) { |
6830 | Node* effect = NodeProperties::GetEffectInput(node); |
6831 | Node* control = NodeProperties::GetControlInput(node); |
6832 | Node* value = effect = |
6833 | graph()->NewNode(simplified()->DateNow(), effect, control); |
6834 | ReplaceWithValue(node, value, effect, control); |
6835 | return Replace(value); |
6836 | } |
6837 | |
6838 | // ES6 section 20.1.2.13 Number.parseInt ( string, radix ) |
6839 | Reduction JSCallReducer::ReduceNumberParseInt(Node* node) { |
6840 | // We certainly know that undefined is not an array. |
6841 | if (node->op()->ValueInputCount() < 3) { |
6842 | Node* value = jsgraph()->NaNConstant(); |
6843 | ReplaceWithValue(node, value); |
6844 | return Replace(value); |
6845 | } |
6846 | |
6847 | int arg_count = node->op()->ValueInputCount(); |
6848 | Node* effect = NodeProperties::GetEffectInput(node); |
6849 | Node* control = NodeProperties::GetControlInput(node); |
6850 | Node* context = NodeProperties::GetContextInput(node); |
6851 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
6852 | Node* object = NodeProperties::GetValueInput(node, 2); |
6853 | Node* radix = arg_count >= 4 ? NodeProperties::GetValueInput(node, 3) |
6854 | : jsgraph()->UndefinedConstant(); |
6855 | node->ReplaceInput(0, object); |
6856 | node->ReplaceInput(1, radix); |
6857 | node->ReplaceInput(2, context); |
6858 | node->ReplaceInput(3, frame_state); |
6859 | node->ReplaceInput(4, effect); |
6860 | node->ReplaceInput(5, control); |
6861 | node->TrimInputCount(6); |
6862 | NodeProperties::ChangeOp(node, javascript()->ParseInt()); |
6863 | return Changed(node); |
6864 | } |
6865 | |
6866 | Reduction JSCallReducer::ReduceRegExpPrototypeTest(Node* node) { |
6867 | if (FLAG_force_slow_path) return NoChange(); |
6868 | if (node->op()->ValueInputCount() < 3) return NoChange(); |
6869 | CallParameters const& p = CallParametersOf(node->op()); |
6870 | if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) { |
6871 | return NoChange(); |
6872 | } |
6873 | |
6874 | Node* effect = NodeProperties::GetEffectInput(node); |
6875 | Node* control = NodeProperties::GetControlInput(node); |
6876 | 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 | NodeProperties::InferReceiverMaps(broker(), regexp, effect, ®exp_maps); |
6882 | if (result == NodeProperties::kNoReceiverMaps) return NoChange(); |
6883 | |
6884 | for (auto map : regexp_maps) { |
6885 | MapRef receiver_map(broker(), map); |
6886 | if (receiver_map.instance_type() != JS_REGEXP_TYPE) return NoChange(); |
6887 | } |
6888 | |
6889 | // Compute property access info for "exec" on {resolution}. |
6890 | PropertyAccessInfo ai_exec; |
6891 | AccessInfoFactory access_info_factory(broker(), dependencies(), |
6892 | graph()->zone()); |
6893 | if (!access_info_factory.ComputePropertyAccessInfo( |
6894 | MapHandles(regexp_maps.begin(), regexp_maps.end()), |
6895 | factory()->exec_string(), AccessMode::kLoad, &ai_exec)) { |
6896 | return NoChange(); |
6897 | } |
6898 | // If "exec" has been modified on {regexp}, we can't do anything. |
6899 | if (ai_exec.IsDataConstant()) { |
6900 | if (!ai_exec.constant().is_identical_to( |
6901 | isolate()->regexp_exec_function())) { |
6902 | return NoChange(); |
6903 | } |
6904 | } else if (ai_exec.IsDataConstantField()) { |
6905 | Handle<JSObject> holder; |
6906 | // Do not reduce if the exec method is not on the prototype chain. |
6907 | if (!ai_exec.holder().ToHandle(&holder)) return NoChange(); |
6908 | |
6909 | // Bail out if the exec method is not the original one. |
6910 | Handle<Object> constant = JSObject::FastPropertyAt( |
6911 | holder, Representation::Tagged(), ai_exec.field_index()); |
6912 | if (!constant.is_identical_to(isolate()->regexp_exec_function())) { |
6913 | return NoChange(); |
6914 | } |
6915 | |
6916 | // Protect the exec method change in the holder. |
6917 | Handle<Object> exec_on_proto; |
6918 | MapRef holder_map(broker(), handle(holder->map(), isolate())); |
6919 | Handle<DescriptorArray> descriptors( |
6920 | holder_map.object()->instance_descriptors(), isolate()); |
6921 | int descriptor_index = |
6922 | descriptors->Search(*(factory()->exec_string()), *holder_map.object()); |
6923 | CHECK_NE(descriptor_index, DescriptorArray::kNotFound); |
6924 | holder_map.SerializeOwnDescriptors(); |
6925 | dependencies()->DependOnFieldType(holder_map, descriptor_index); |
6926 | } else { |
6927 | return NoChange(); |
6928 | } |
6929 | |
6930 | PropertyAccessBuilder access_builder(jsgraph(), broker(), dependencies()); |
6931 | |
6932 | // Add proper dependencies on the {regexp}s [[Prototype]]s. |
6933 | Handle<JSObject> holder; |
6934 | if (ai_exec.holder().ToHandle(&holder)) { |
6935 | dependencies()->DependOnStablePrototypeChains( |
6936 | ai_exec.receiver_maps(), kStartAtPrototype, |
6937 | JSObjectRef(broker(), holder)); |
6938 | } |
6939 | |
6940 | effect = InsertMapChecksIfUnreliableReceiverMaps( |
6941 | result, regexp_maps, p.feedback(), regexp, effect, control); |
6942 | |
6943 | Node* context = NodeProperties::GetContextInput(node); |
6944 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
6945 | Node* search = NodeProperties::GetValueInput(node, 2); |
6946 | Node* search_string = effect = graph()->NewNode( |
6947 | simplified()->CheckString(p.feedback()), search, effect, control); |
6948 | |
6949 | Node* lastIndex = effect = graph()->NewNode( |
6950 | simplified()->LoadField(AccessBuilder::ForJSRegExpLastIndex()), regexp, |
6951 | effect, control); |
6952 | |
6953 | Node* lastIndexSmi = effect = graph()->NewNode( |
6954 | simplified()->CheckSmi(p.feedback()), lastIndex, effect, control); |
6955 | |
6956 | Node* is_positive = graph()->NewNode(simplified()->NumberLessThanOrEqual(), |
6957 | jsgraph()->ZeroConstant(), lastIndexSmi); |
6958 | |
6959 | effect = graph()->NewNode( |
6960 | simplified()->CheckIf(DeoptimizeReason::kNotASmi, p.feedback()), |
6961 | is_positive, effect, control); |
6962 | |
6963 | node->ReplaceInput(0, regexp); |
6964 | node->ReplaceInput(1, search_string); |
6965 | node->ReplaceInput(2, context); |
6966 | node->ReplaceInput(3, frame_state); |
6967 | node->ReplaceInput(4, effect); |
6968 | node->ReplaceInput(5, control); |
6969 | node->TrimInputCount(6); |
6970 | NodeProperties::ChangeOp(node, javascript()->RegExpTest()); |
6971 | return Changed(node); |
6972 | } |
6973 | |
6974 | // ES section #sec-number-constructor |
6975 | Reduction JSCallReducer::ReduceNumberConstructor(Node* node) { |
6976 | DCHECK_EQ(IrOpcode::kJSCall, node->opcode()); |
6977 | CallParameters const& p = CallParametersOf(node->op()); |
6978 | Node* target = NodeProperties::GetValueInput(node, 0); |
6979 | Node* receiver = NodeProperties::GetValueInput(node, 1); |
6980 | Node* value = p.arity() < 3 ? jsgraph()->ZeroConstant() |
6981 | : NodeProperties::GetValueInput(node, 2); |
6982 | Node* context = NodeProperties::GetContextInput(node); |
6983 | Node* frame_state = NodeProperties::GetFrameStateInput(node); |
6984 | |
6985 | // Create the artificial frame state in the middle of the Number constructor. |
6986 | SharedFunctionInfoRef shared_info = |
6987 | native_context().number_function().shared(); |
6988 | Node* stack_parameters[] = {receiver}; |
6989 | int stack_parameter_count = arraysize(stack_parameters); |
6990 | Node* continuation_frame_state = |
6991 | CreateJavaScriptBuiltinContinuationFrameState( |
6992 | jsgraph(), shared_info, |
6993 | Builtins::kGenericConstructorLazyDeoptContinuation, target, context, |
6994 | stack_parameters, stack_parameter_count, frame_state, |
6995 | ContinuationFrameStateMode::LAZY); |
6996 | |
6997 | // Convert the {value} to a Number. |
6998 | NodeProperties::ReplaceValueInputs(node, value); |
6999 | NodeProperties::ChangeOp(node, javascript()->ToNumberConvertBigInt()); |
7000 | NodeProperties::ReplaceFrameStateInput(node, continuation_frame_state); |
7001 | return Changed(node); |
7002 | } |
7003 | |
7004 | Graph* JSCallReducer::graph() const { return jsgraph()->graph(); } |
7005 | |
7006 | Isolate* JSCallReducer::isolate() const { return jsgraph()->isolate(); } |
7007 | |
7008 | Factory* JSCallReducer::factory() const { return isolate()->factory(); } |
7009 | |
7010 | CommonOperatorBuilder* JSCallReducer::common() const { |
7011 | return jsgraph()->common(); |
7012 | } |
7013 | |
7014 | JSOperatorBuilder* JSCallReducer::javascript() const { |
7015 | return jsgraph()->javascript(); |
7016 | } |
7017 | |
7018 | SimplifiedOperatorBuilder* JSCallReducer::simplified() const { |
7019 | return jsgraph()->simplified(); |
7020 | } |
7021 | |
7022 | } // namespace compiler |
7023 | } // namespace internal |
7024 | } // namespace v8 |
7025 | |