Line data Source code
1 : // Copyright 2014 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-builtin-reducer.h"
6 :
7 : #include "src/base/bits.h"
8 : #include "src/code-factory.h"
9 : #include "src/compilation-dependencies.h"
10 : #include "src/compiler/access-builder.h"
11 : #include "src/compiler/js-graph.h"
12 : #include "src/compiler/linkage.h"
13 : #include "src/compiler/node-matchers.h"
14 : #include "src/compiler/node-properties.h"
15 : #include "src/compiler/simplified-operator.h"
16 : #include "src/compiler/type-cache.h"
17 : #include "src/compiler/types.h"
18 : #include "src/objects-inl.h"
19 :
20 : namespace v8 {
21 : namespace internal {
22 : namespace compiler {
23 :
24 : // Helper class to access JSCall nodes that are potential candidates
25 : // for reduction when they have a BuiltinFunctionId associated with them.
26 : class JSCallReduction {
27 : public:
28 32024535 : explicit JSCallReduction(Node* node) : node_(node) {}
29 :
30 : // Determines whether the node is a JSCall operation that targets a
31 : // constant callee being a well-known builtin with a BuiltinFunctionId.
32 31952291 : bool HasBuiltinFunctionId() {
33 63904582 : if (node_->opcode() != IrOpcode::kJSCall) return false;
34 223299 : HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
35 427841 : if (!m.HasValue() || !m.Value()->IsJSFunction()) return false;
36 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
37 204342 : return function->shared()->HasBuiltinFunctionId();
38 : }
39 :
40 : // Retrieves the BuiltinFunctionId as described above.
41 81877 : BuiltinFunctionId GetBuiltinFunctionId() {
42 : DCHECK_EQ(IrOpcode::kJSCall, node_->opcode());
43 81877 : HeapObjectMatcher m(NodeProperties::GetValueInput(node_, 0));
44 : Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value());
45 81877 : return function->shared()->builtin_function_id();
46 : }
47 :
48 : bool ReceiverMatches(Type* type) {
49 : return NodeProperties::GetType(receiver())->Is(type);
50 : }
51 :
52 : // Determines whether the call takes zero inputs.
53 : bool InputsMatchZero() { return GetJSCallArity() == 0; }
54 :
55 : // Determines whether the call takes one input of the given type.
56 70111 : bool InputsMatchOne(Type* t1) {
57 140119 : return GetJSCallArity() == 1 &&
58 70111 : NodeProperties::GetType(GetJSCallInput(0))->Is(t1);
59 : }
60 :
61 : // Determines whether the call takes two inputs of the given types.
62 2362 : bool InputsMatchTwo(Type* t1, Type* t2) {
63 1603 : return GetJSCallArity() == 2 &&
64 3767 : NodeProperties::GetType(GetJSCallInput(0))->Is(t1) &&
65 2362 : NodeProperties::GetType(GetJSCallInput(1))->Is(t2);
66 : }
67 :
68 : // Determines whether the call takes inputs all of the given type.
69 641 : bool InputsMatchAll(Type* t) {
70 3344 : for (int i = 0; i < GetJSCallArity(); i++) {
71 1171 : if (!NodeProperties::GetType(GetJSCallInput(i))->Is(t)) {
72 : return false;
73 : }
74 : }
75 : return true;
76 : }
77 :
78 : Node* receiver() { return NodeProperties::GetValueInput(node_, 1); }
79 : Node* left() { return GetJSCallInput(0); }
80 : Node* right() { return GetJSCallInput(1); }
81 :
82 : int GetJSCallArity() {
83 : DCHECK_EQ(IrOpcode::kJSCall, node_->opcode());
84 : // Skip first (i.e. callee) and second (i.e. receiver) operand.
85 78483 : return node_->op()->ValueInputCount() - 2;
86 : }
87 :
88 : Node* GetJSCallInput(int index) {
89 : DCHECK_EQ(IrOpcode::kJSCall, node_->opcode());
90 : DCHECK_LT(index, GetJSCallArity());
91 : // Skip first (i.e. callee) and second (i.e. receiver) operand.
92 105612 : return NodeProperties::GetValueInput(node_, index + 2);
93 : }
94 :
95 : private:
96 : Node* node_;
97 : };
98 :
99 396062 : JSBuiltinReducer::JSBuiltinReducer(Editor* editor, JSGraph* jsgraph,
100 : Flags flags,
101 : CompilationDependencies* dependencies,
102 : Handle<Context> native_context)
103 : : AdvancedReducer(editor),
104 : dependencies_(dependencies),
105 : flags_(flags),
106 : jsgraph_(jsgraph),
107 : native_context_(native_context),
108 396062 : type_cache_(TypeCache::Get()) {}
109 :
110 : namespace {
111 :
112 3241 : MaybeHandle<Map> GetMapWitness(Node* node) {
113 : ZoneHandleSet<Map> maps;
114 3241 : Node* receiver = NodeProperties::GetValueInput(node, 1);
115 3241 : Node* effect = NodeProperties::GetEffectInput(node);
116 : NodeProperties::InferReceiverMapsResult result =
117 3241 : NodeProperties::InferReceiverMaps(receiver, effect, &maps);
118 5703 : if (result == NodeProperties::kReliableReceiverMaps && maps.size() == 1) {
119 : return maps[0];
120 : }
121 : return MaybeHandle<Map>();
122 : }
123 :
124 : // TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
125 1039 : bool IsReadOnlyLengthDescriptor(Handle<Map> jsarray_map) {
126 : DCHECK(!jsarray_map->is_dictionary_map());
127 : Isolate* isolate = jsarray_map->GetIsolate();
128 : Handle<Name> length_string = isolate->factory()->length_string();
129 : DescriptorArray* descriptors = jsarray_map->instance_descriptors();
130 : int number =
131 : descriptors->SearchWithCache(isolate, *length_string, *jsarray_map);
132 : DCHECK_NE(DescriptorArray::kNotFound, number);
133 2078 : return descriptors->GetDetails(number).IsReadOnly();
134 : }
135 :
136 : // TODO(turbofan): This was copied from Crankshaft, might be too restrictive.
137 1639 : bool CanInlineArrayResizeOperation(Handle<Map> receiver_map) {
138 : Isolate* const isolate = receiver_map->GetIsolate();
139 1639 : if (!receiver_map->prototype()->IsJSArray()) return false;
140 : Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
141 : isolate);
142 : // Ensure that all prototypes of the {receiver} are stable.
143 4520 : for (PrototypeIterator it(isolate, receiver_prototype, kStartAtReceiver);
144 2252 : !it.IsAtEnd(); it.Advance()) {
145 : Handle<JSReceiver> current = PrototypeIterator::GetCurrent<JSReceiver>(it);
146 2268 : if (!current->map()->is_stable()) return false;
147 : }
148 1110 : return receiver_map->instance_type() == JS_ARRAY_TYPE &&
149 1078 : IsFastElementsKind(receiver_map->elements_kind()) &&
150 2156 : !receiver_map->is_dictionary_map() && receiver_map->is_extensible() &&
151 1078 : (!receiver_map->is_prototype_map() || receiver_map->is_stable()) &&
152 2109 : isolate->IsFastArrayConstructorPrototypeChainIntact() &&
153 3196 : isolate->IsAnyInitialArrayPrototype(receiver_prototype) &&
154 2157 : !IsReadOnlyLengthDescriptor(receiver_map);
155 : }
156 :
157 827 : bool CanInlineJSArrayIteration(Handle<Map> receiver_map) {
158 : Isolate* const isolate = receiver_map->GetIsolate();
159 : // Ensure that the [[Prototype]] is actually an exotic Array
160 827 : if (!receiver_map->prototype()->IsJSArray()) return false;
161 :
162 : // Don't inline JSArrays with slow elements of any kind
163 777 : if (!IsFastElementsKind(receiver_map->elements_kind())) return false;
164 :
165 : // If the receiver map has packed elements, no need to check the prototype.
166 : // This requires a MapCheck where this is used.
167 777 : if (!IsFastHoleyElementsKind(receiver_map->elements_kind())) return true;
168 :
169 : Handle<JSArray> receiver_prototype(JSArray::cast(receiver_map->prototype()),
170 : isolate);
171 : // Ensure all prototypes of the {receiver} are stable.
172 0 : for (PrototypeIterator it(isolate, receiver_prototype, kStartAtReceiver);
173 0 : !it.IsAtEnd(); it.Advance()) {
174 : Handle<JSReceiver> current = PrototypeIterator::GetCurrent<JSReceiver>(it);
175 0 : if (!current->map()->is_stable()) return false;
176 : }
177 :
178 : // For holey Arrays, ensure that the array_protector cell is valid (must be
179 : // a CompilationDependency), and the JSArray prototype has not been altered.
180 0 : return receiver_map->instance_type() == JS_ARRAY_TYPE &&
181 0 : (!receiver_map->is_dictionary_map() || receiver_map->is_stable()) &&
182 0 : isolate->IsFastArrayConstructorPrototypeChainIntact() &&
183 0 : isolate->IsAnyInitialArrayPrototype(receiver_prototype);
184 : }
185 :
186 : } // namespace
187 :
188 1512 : Reduction JSBuiltinReducer::ReduceArrayIterator(Node* node,
189 : IterationKind kind) {
190 : Handle<Map> receiver_map;
191 3024 : if (GetMapWitness(node).ToHandle(&receiver_map)) {
192 : return ReduceArrayIterator(receiver_map, node, kind,
193 840 : ArrayIteratorKind::kArray);
194 : }
195 : return NoChange();
196 : }
197 :
198 14 : Reduction JSBuiltinReducer::ReduceTypedArrayIterator(Node* node,
199 : IterationKind kind) {
200 : Handle<Map> receiver_map;
201 42 : if (GetMapWitness(node).ToHandle(&receiver_map) &&
202 : receiver_map->instance_type() == JS_TYPED_ARRAY_TYPE) {
203 : return ReduceArrayIterator(receiver_map, node, kind,
204 14 : ArrayIteratorKind::kTypedArray);
205 : }
206 : return NoChange();
207 : }
208 :
209 854 : Reduction JSBuiltinReducer::ReduceArrayIterator(Handle<Map> receiver_map,
210 : Node* node, IterationKind kind,
211 5908 : ArrayIteratorKind iter_kind) {
212 854 : Node* receiver = NodeProperties::GetValueInput(node, 1);
213 854 : Node* effect = NodeProperties::GetEffectInput(node);
214 854 : Node* control = NodeProperties::GetControlInput(node);
215 :
216 854 : if (iter_kind == ArrayIteratorKind::kTypedArray) {
217 : // See if we can skip the neutering check.
218 14 : if (isolate()->IsArrayBufferNeuteringIntact()) {
219 : // Add a code dependency so we are deoptimized in case an ArrayBuffer
220 : // gets neutered.
221 : dependencies()->AssumePropertyCell(
222 : factory()->array_buffer_neutering_protector());
223 : } else {
224 : // For JSTypedArray iterator methods, deopt if the buffer is neutered.
225 : // This is potentially a deopt loop, but should be extremely unlikely.
226 : DCHECK_EQ(JS_TYPED_ARRAY_TYPE, receiver_map->instance_type());
227 : Node* buffer = effect = graph()->NewNode(
228 : simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
229 21 : receiver, effect, control);
230 :
231 : // Deoptimize if the {buffer} has been neutered.
232 : Node* check = effect = graph()->NewNode(
233 7 : simplified()->ArrayBufferWasNeutered(), buffer, effect, control);
234 7 : check = graph()->NewNode(simplified()->BooleanNot(), check);
235 : effect =
236 7 : graph()->NewNode(simplified()->CheckIf(), check, effect, control);
237 : }
238 : }
239 :
240 : int map_index = -1;
241 854 : Node* object_map = jsgraph()->UndefinedConstant();
242 854 : switch (receiver_map->instance_type()) {
243 : case JS_ARRAY_TYPE:
244 840 : if (kind == IterationKind::kKeys) {
245 : map_index = Context::FAST_ARRAY_KEY_ITERATOR_MAP_INDEX;
246 : } else {
247 : map_index = kind == IterationKind::kValues
248 : ? Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX
249 827 : : Context::FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX;
250 :
251 827 : if (CanInlineJSArrayIteration(receiver_map)) {
252 : // Use `generic` elements for holey arrays if there may be elements
253 : // on the prototype chain.
254 777 : map_index += static_cast<int>(receiver_map->elements_kind());
255 777 : object_map = jsgraph()->Constant(receiver_map);
256 777 : if (IsFastHoleyElementsKind(receiver_map->elements_kind())) {
257 : Handle<JSObject> initial_array_prototype(
258 : native_context()->initial_array_prototype(), isolate());
259 : dependencies()->AssumePrototypeMapsStable(receiver_map,
260 0 : initial_array_prototype);
261 : }
262 : } else {
263 : map_index += (Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX -
264 50 : Context::FAST_SMI_ARRAY_VALUE_ITERATOR_MAP_INDEX);
265 : }
266 : }
267 : break;
268 : case JS_TYPED_ARRAY_TYPE:
269 14 : if (kind == IterationKind::kKeys) {
270 : map_index = Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX;
271 : } else {
272 : DCHECK_GE(receiver_map->elements_kind(), UINT8_ELEMENTS);
273 : DCHECK_LE(receiver_map->elements_kind(), UINT8_CLAMPED_ELEMENTS);
274 : map_index = (kind == IterationKind::kValues
275 : ? Context::UINT8_ARRAY_VALUE_ITERATOR_MAP_INDEX
276 14 : : Context::UINT8_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX) +
277 14 : (receiver_map->elements_kind() - UINT8_ELEMENTS);
278 : }
279 : break;
280 : default:
281 0 : if (kind == IterationKind::kKeys) {
282 : map_index = Context::GENERIC_ARRAY_KEY_ITERATOR_MAP_INDEX;
283 0 : } else if (kind == IterationKind::kValues) {
284 : map_index = Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX;
285 : } else {
286 : map_index = Context::GENERIC_ARRAY_KEY_VALUE_ITERATOR_MAP_INDEX;
287 : }
288 : break;
289 : }
290 :
291 : DCHECK_GE(map_index, Context::TYPED_ARRAY_KEY_ITERATOR_MAP_INDEX);
292 : DCHECK_LE(map_index, Context::GENERIC_ARRAY_VALUE_ITERATOR_MAP_INDEX);
293 :
294 : Handle<Map> map(Map::cast(native_context()->get(map_index)), isolate());
295 :
296 : // allocate new iterator
297 : effect = graph()->NewNode(
298 854 : common()->BeginRegion(RegionObservability::kNotObservable), effect);
299 : Node* value = effect = graph()->NewNode(
300 : simplified()->Allocate(Type::OtherObject(), NOT_TENURED),
301 1708 : jsgraph()->Constant(JSArrayIterator::kSize), effect, control);
302 : effect = graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()),
303 2562 : value, jsgraph()->Constant(map), effect, control);
304 : effect = graph()->NewNode(
305 : simplified()->StoreField(AccessBuilder::ForJSObjectProperties()), value,
306 2562 : jsgraph()->EmptyFixedArrayConstant(), effect, control);
307 : effect = graph()->NewNode(
308 : simplified()->StoreField(AccessBuilder::ForJSObjectElements()), value,
309 2562 : jsgraph()->EmptyFixedArrayConstant(), effect, control);
310 :
311 : // attach the iterator to this object
312 : effect = graph()->NewNode(
313 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()),
314 2562 : value, receiver, effect, control);
315 : effect = graph()->NewNode(
316 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex()), value,
317 2562 : jsgraph()->ZeroConstant(), effect, control);
318 : effect = graph()->NewNode(
319 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObjectMap()),
320 2562 : value, object_map, effect, control);
321 :
322 854 : value = effect = graph()->NewNode(common()->FinishRegion(), value, effect);
323 :
324 : // replace it
325 854 : ReplaceWithValue(node, value, effect, control);
326 854 : return Replace(value);
327 : }
328 :
329 1425 : Reduction JSBuiltinReducer::ReduceFastArrayIteratorNext(
330 9681 : Handle<Map> iterator_map, Node* node, IterationKind kind) {
331 1425 : Node* iterator = NodeProperties::GetValueInput(node, 1);
332 1425 : Node* effect = NodeProperties::GetEffectInput(node);
333 1425 : Node* control = NodeProperties::GetControlInput(node);
334 1425 : Node* context = NodeProperties::GetContextInput(node);
335 :
336 2837 : if (kind != IterationKind::kKeys &&
337 : !isolate()->IsFastArrayIterationIntact()) {
338 : // Avoid deopt loops for non-key iteration if the
339 : // fast_array_iteration_protector cell has been invalidated.
340 : return NoChange();
341 : }
342 :
343 : ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType(
344 1383 : iterator_map->instance_type());
345 :
346 1383 : if (IsFastHoleyElementsKind(elements_kind)) {
347 0 : if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) {
348 : return NoChange();
349 : } else {
350 : Handle<JSObject> initial_array_prototype(
351 : native_context()->initial_array_prototype(), isolate());
352 : dependencies()->AssumePropertyCell(factory()->array_protector());
353 : }
354 : }
355 :
356 : Node* array = effect = graph()->NewNode(
357 : simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()),
358 4149 : iterator, effect, control);
359 : Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), array,
360 2766 : jsgraph()->UndefinedConstant());
361 : Node* branch0 =
362 1383 : graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
363 :
364 : Node* vdone_false0;
365 : Node* vfalse0;
366 : Node* efalse0 = effect;
367 1383 : Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
368 : {
369 : // iterator.[[IteratedObject]] !== undefined, continue iterating.
370 : Node* index = efalse0 = graph()->NewNode(
371 : simplified()->LoadField(AccessBuilder::ForJSArrayIteratorIndex(
372 : JS_ARRAY_TYPE, elements_kind)),
373 4149 : iterator, efalse0, if_false0);
374 :
375 : Node* length = efalse0 = graph()->NewNode(
376 : simplified()->LoadField(AccessBuilder::ForJSArrayLength(elements_kind)),
377 4149 : array, efalse0, if_false0);
378 : Node* check1 =
379 1383 : graph()->NewNode(simplified()->NumberLessThan(), index, length);
380 : Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
381 1383 : check1, if_false0);
382 :
383 : Node* vdone_true1;
384 : Node* vtrue1;
385 : Node* etrue1 = efalse0;
386 1383 : Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
387 : {
388 : // iterator.[[NextIndex]] < array.length, continue iterating
389 1383 : vdone_true1 = jsgraph()->FalseConstant();
390 1383 : if (kind == IterationKind::kKeys) {
391 : vtrue1 = index;
392 : } else {
393 : // For value/entry iteration, first step is a mapcheck to ensure
394 : // inlining is still valid.
395 : Node* array_map = etrue1 =
396 : graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()),
397 4110 : array, etrue1, if_true1);
398 : Node* orig_map = etrue1 =
399 : graph()->NewNode(simplified()->LoadField(
400 : AccessBuilder::ForJSArrayIteratorObjectMap()),
401 4110 : iterator, etrue1, if_true1);
402 : Node* check_map = graph()->NewNode(simplified()->ReferenceEqual(),
403 1370 : array_map, orig_map);
404 : etrue1 = graph()->NewNode(simplified()->CheckIf(), check_map, etrue1,
405 1370 : if_true1);
406 : }
407 :
408 1383 : if (kind != IterationKind::kKeys) {
409 : Node* elements = etrue1 = graph()->NewNode(
410 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
411 4110 : array, etrue1, if_true1);
412 : Node* value = etrue1 = graph()->NewNode(
413 : simplified()->LoadElement(
414 : AccessBuilder::ForFixedArrayElement(elements_kind)),
415 4110 : elements, index, etrue1, if_true1);
416 :
417 : // Convert hole to undefined if needed.
418 1370 : if (elements_kind == FAST_HOLEY_ELEMENTS ||
419 : elements_kind == FAST_HOLEY_SMI_ELEMENTS) {
420 : value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(),
421 0 : value);
422 1370 : } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) {
423 : // TODO(bmeurer): avoid deopt if not all uses of value are truncated.
424 : CheckFloat64HoleMode mode = CheckFloat64HoleMode::kAllowReturnHole;
425 : value = etrue1 = graph()->NewNode(
426 0 : simplified()->CheckFloat64Hole(mode), value, etrue1, if_true1);
427 : }
428 :
429 1370 : if (kind == IterationKind::kEntries) {
430 : // Allocate elements for key/value pair
431 : vtrue1 = etrue1 =
432 : graph()->NewNode(javascript()->CreateKeyValueArray(), index,
433 7 : value, context, etrue1);
434 : } else {
435 : DCHECK_EQ(kind, IterationKind::kValues);
436 : vtrue1 = value;
437 : }
438 : }
439 :
440 : Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
441 2766 : jsgraph()->OneConstant());
442 1383 : next_index = graph()->NewNode(simplified()->NumberToUint32(), next_index);
443 :
444 : etrue1 = graph()->NewNode(
445 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex(
446 : JS_ARRAY_TYPE, elements_kind)),
447 4149 : iterator, next_index, etrue1, if_true1);
448 : }
449 :
450 : Node* vdone_false1;
451 : Node* vfalse1;
452 : Node* efalse1 = efalse0;
453 1383 : Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
454 : {
455 : // iterator.[[NextIndex]] >= array.length, stop iterating.
456 1383 : vdone_false1 = jsgraph()->TrueConstant();
457 1383 : vfalse1 = jsgraph()->UndefinedConstant();
458 : efalse1 = graph()->NewNode(
459 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()),
460 4149 : iterator, vfalse1, efalse1, if_false1);
461 : }
462 :
463 1383 : if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
464 : efalse0 =
465 1383 : graph()->NewNode(common()->EffectPhi(2), etrue1, efalse1, if_false0);
466 : vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
467 1383 : vtrue1, vfalse1, if_false0);
468 : vdone_false0 =
469 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
470 1383 : vdone_true1, vdone_false1, if_false0);
471 : }
472 :
473 : Node* vdone_true0;
474 : Node* vtrue0;
475 : Node* etrue0 = effect;
476 1383 : Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
477 : {
478 : // iterator.[[IteratedObject]] === undefined, the iterator is done.
479 1383 : vdone_true0 = jsgraph()->TrueConstant();
480 1383 : vtrue0 = jsgraph()->UndefinedConstant();
481 : }
482 :
483 1383 : control = graph()->NewNode(common()->Merge(2), if_false0, if_true0);
484 1383 : effect = graph()->NewNode(common()->EffectPhi(2), efalse0, etrue0, control);
485 : Node* value =
486 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
487 1383 : vfalse0, vtrue0, control);
488 : Node* done =
489 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
490 1383 : vdone_false0, vdone_true0, control);
491 :
492 : // Create IteratorResult object.
493 : value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
494 1383 : value, done, context, effect);
495 1383 : ReplaceWithValue(node, value, effect, control);
496 : return Replace(value);
497 : }
498 :
499 14 : Reduction JSBuiltinReducer::ReduceTypedArrayIteratorNext(
500 105 : Handle<Map> iterator_map, Node* node, IterationKind kind) {
501 14 : Node* iterator = NodeProperties::GetValueInput(node, 1);
502 14 : Node* effect = NodeProperties::GetEffectInput(node);
503 14 : Node* control = NodeProperties::GetControlInput(node);
504 14 : Node* context = NodeProperties::GetContextInput(node);
505 :
506 : ElementsKind elements_kind = JSArrayIterator::ElementsKindForInstanceType(
507 14 : iterator_map->instance_type());
508 :
509 : Node* array = effect = graph()->NewNode(
510 : simplified()->LoadField(AccessBuilder::ForJSArrayIteratorObject()),
511 42 : iterator, effect, control);
512 : Node* check0 = graph()->NewNode(simplified()->ReferenceEqual(), array,
513 28 : jsgraph()->UndefinedConstant());
514 : Node* branch0 =
515 14 : graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
516 :
517 : Node* vdone_false0;
518 : Node* vfalse0;
519 : Node* efalse0 = effect;
520 14 : Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
521 : {
522 : // iterator.[[IteratedObject]] !== undefined, continue iterating.
523 : Node* index = efalse0 = graph()->NewNode(
524 : simplified()->LoadField(AccessBuilder::ForJSArrayIteratorIndex(
525 : JS_TYPED_ARRAY_TYPE, elements_kind)),
526 42 : iterator, efalse0, if_false0);
527 :
528 : // typedarray.[[ViewedArrayBuffer]]
529 : Node* buffer = efalse0 = graph()->NewNode(
530 : simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
531 42 : array, efalse0, if_false0);
532 :
533 : // See if we can skip the neutering check.
534 14 : if (isolate()->IsArrayBufferNeuteringIntact()) {
535 : // Add a code dependency so we are deoptimized in case an ArrayBuffer
536 : // gets neutered.
537 : dependencies()->AssumePropertyCell(
538 : factory()->array_buffer_neutering_protector());
539 : } else {
540 : // Deoptimize if the array buffer was neutered.
541 : Node* check1 = efalse0 = graph()->NewNode(
542 7 : simplified()->ArrayBufferWasNeutered(), buffer, efalse0, if_false0);
543 7 : check1 = graph()->NewNode(simplified()->BooleanNot(), check1);
544 : efalse0 =
545 7 : graph()->NewNode(simplified()->CheckIf(), check1, efalse0, if_false0);
546 : }
547 :
548 : Node* length = efalse0 = graph()->NewNode(
549 : simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), array,
550 42 : efalse0, if_false0);
551 :
552 : Node* check2 =
553 14 : graph()->NewNode(simplified()->NumberLessThan(), index, length);
554 : Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
555 14 : check2, if_false0);
556 :
557 : Node* vdone_true2;
558 : Node* vtrue2;
559 : Node* etrue2 = efalse0;
560 14 : Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
561 : {
562 : // iterator.[[NextIndex]] < array.length, continue iterating
563 14 : vdone_true2 = jsgraph()->FalseConstant();
564 14 : if (kind == IterationKind::kKeys) {
565 : vtrue2 = index;
566 : }
567 :
568 : Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
569 28 : jsgraph()->OneConstant());
570 14 : next_index = graph()->NewNode(simplified()->NumberToUint32(), next_index);
571 :
572 : etrue2 = graph()->NewNode(
573 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorIndex(
574 : JS_TYPED_ARRAY_TYPE, elements_kind)),
575 42 : iterator, next_index, etrue2, if_true2);
576 :
577 14 : if (kind != IterationKind::kKeys) {
578 : Node* elements = etrue2 = graph()->NewNode(
579 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
580 42 : array, etrue2, if_true2);
581 : Node* base_ptr = etrue2 = graph()->NewNode(
582 : simplified()->LoadField(
583 : AccessBuilder::ForFixedTypedArrayBaseBasePointer()),
584 42 : elements, etrue2, if_true2);
585 : Node* external_ptr = etrue2 = graph()->NewNode(
586 : simplified()->LoadField(
587 : AccessBuilder::ForFixedTypedArrayBaseExternalPointer()),
588 42 : elements, etrue2, if_true2);
589 :
590 14 : ExternalArrayType array_type = kExternalInt8Array;
591 14 : switch (elements_kind) {
592 : #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \
593 : case TYPE##_ELEMENTS: \
594 : array_type = kExternal##Type##Array; \
595 : break;
596 14 : TYPED_ARRAYS(TYPED_ARRAY_CASE)
597 : default:
598 0 : UNREACHABLE();
599 : #undef TYPED_ARRAY_CASE
600 : }
601 :
602 : Node* value = etrue2 =
603 : graph()->NewNode(simplified()->LoadTypedElement(array_type), buffer,
604 14 : base_ptr, external_ptr, index, etrue2, if_true2);
605 :
606 14 : if (kind == IterationKind::kEntries) {
607 : // Allocate elements for key/value pair
608 : vtrue2 = etrue2 =
609 : graph()->NewNode(javascript()->CreateKeyValueArray(), index,
610 7 : value, context, etrue2);
611 : } else {
612 : DCHECK(kind == IterationKind::kValues);
613 : vtrue2 = value;
614 : }
615 : }
616 : }
617 :
618 : Node* vdone_false2;
619 : Node* vfalse2;
620 : Node* efalse2 = efalse0;
621 14 : Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
622 : {
623 : // iterator.[[NextIndex]] >= array.length, stop iterating.
624 14 : vdone_false2 = jsgraph()->TrueConstant();
625 14 : vfalse2 = jsgraph()->UndefinedConstant();
626 : efalse2 = graph()->NewNode(
627 : simplified()->StoreField(AccessBuilder::ForJSArrayIteratorObject()),
628 42 : iterator, vfalse2, efalse2, if_false2);
629 : }
630 :
631 14 : if_false0 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
632 : efalse0 =
633 14 : graph()->NewNode(common()->EffectPhi(2), etrue2, efalse2, if_false0);
634 : vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
635 14 : vtrue2, vfalse2, if_false0);
636 : vdone_false0 =
637 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
638 14 : vdone_true2, vdone_false2, if_false0);
639 : }
640 :
641 : Node* vdone_true0;
642 : Node* vtrue0;
643 : Node* etrue0 = effect;
644 14 : Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
645 : {
646 : // iterator.[[IteratedObject]] === undefined, the iterator is done.
647 14 : vdone_true0 = jsgraph()->TrueConstant();
648 14 : vtrue0 = jsgraph()->UndefinedConstant();
649 : }
650 :
651 14 : control = graph()->NewNode(common()->Merge(2), if_false0, if_true0);
652 14 : effect = graph()->NewNode(common()->EffectPhi(2), efalse0, etrue0, control);
653 : Node* value =
654 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
655 14 : vfalse0, vtrue0, control);
656 : Node* done =
657 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
658 14 : vdone_false0, vdone_true0, control);
659 :
660 : // Create IteratorResult object.
661 : value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
662 14 : value, done, context, effect);
663 14 : ReplaceWithValue(node, value, effect, control);
664 14 : return Replace(value);
665 : }
666 :
667 1562 : Reduction JSBuiltinReducer::ReduceArrayIteratorNext(Node* node) {
668 : Handle<Map> receiver_map;
669 3124 : if (GetMapWitness(node).ToHandle(&receiver_map)) {
670 1440 : switch (receiver_map->instance_type()) {
671 : case JS_TYPED_ARRAY_KEY_ITERATOR_TYPE:
672 : return ReduceTypedArrayIteratorNext(receiver_map, node,
673 0 : IterationKind::kKeys);
674 :
675 : case JS_FAST_ARRAY_KEY_ITERATOR_TYPE:
676 : return ReduceFastArrayIteratorNext(receiver_map, node,
677 13 : IterationKind::kKeys);
678 :
679 : case JS_INT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
680 : case JS_UINT8_ARRAY_KEY_VALUE_ITERATOR_TYPE:
681 : case JS_INT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
682 : case JS_UINT16_ARRAY_KEY_VALUE_ITERATOR_TYPE:
683 : case JS_INT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
684 : case JS_UINT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
685 : case JS_FLOAT32_ARRAY_KEY_VALUE_ITERATOR_TYPE:
686 : case JS_FLOAT64_ARRAY_KEY_VALUE_ITERATOR_TYPE:
687 : case JS_UINT8_CLAMPED_ARRAY_KEY_VALUE_ITERATOR_TYPE:
688 : return ReduceTypedArrayIteratorNext(receiver_map, node,
689 7 : IterationKind::kEntries);
690 :
691 : case JS_FAST_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
692 : case JS_FAST_HOLEY_SMI_ARRAY_KEY_VALUE_ITERATOR_TYPE:
693 : case JS_FAST_ARRAY_KEY_VALUE_ITERATOR_TYPE:
694 : case JS_FAST_HOLEY_ARRAY_KEY_VALUE_ITERATOR_TYPE:
695 : case JS_FAST_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
696 : case JS_FAST_HOLEY_DOUBLE_ARRAY_KEY_VALUE_ITERATOR_TYPE:
697 : return ReduceFastArrayIteratorNext(receiver_map, node,
698 7 : IterationKind::kEntries);
699 :
700 : case JS_INT8_ARRAY_VALUE_ITERATOR_TYPE:
701 : case JS_UINT8_ARRAY_VALUE_ITERATOR_TYPE:
702 : case JS_INT16_ARRAY_VALUE_ITERATOR_TYPE:
703 : case JS_UINT16_ARRAY_VALUE_ITERATOR_TYPE:
704 : case JS_INT32_ARRAY_VALUE_ITERATOR_TYPE:
705 : case JS_UINT32_ARRAY_VALUE_ITERATOR_TYPE:
706 : case JS_FLOAT32_ARRAY_VALUE_ITERATOR_TYPE:
707 : case JS_FLOAT64_ARRAY_VALUE_ITERATOR_TYPE:
708 : case JS_UINT8_CLAMPED_ARRAY_VALUE_ITERATOR_TYPE:
709 : return ReduceTypedArrayIteratorNext(receiver_map, node,
710 7 : IterationKind::kValues);
711 :
712 : case JS_FAST_SMI_ARRAY_VALUE_ITERATOR_TYPE:
713 : case JS_FAST_HOLEY_SMI_ARRAY_VALUE_ITERATOR_TYPE:
714 : case JS_FAST_ARRAY_VALUE_ITERATOR_TYPE:
715 : case JS_FAST_HOLEY_ARRAY_VALUE_ITERATOR_TYPE:
716 : case JS_FAST_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
717 : case JS_FAST_HOLEY_DOUBLE_ARRAY_VALUE_ITERATOR_TYPE:
718 : return ReduceFastArrayIteratorNext(receiver_map, node,
719 1405 : IterationKind::kValues);
720 :
721 : default:
722 : // Slow array iterators are not reduced
723 : return NoChange();
724 : }
725 : }
726 : return NoChange();
727 : }
728 :
729 : // ES6 section 22.1.2.2 Array.isArray ( arg )
730 74 : Reduction JSBuiltinReducer::ReduceArrayIsArray(Node* node) {
731 : // We certainly know that undefined is not an array.
732 42 : if (node->op()->ValueInputCount() < 3) {
733 0 : Node* value = jsgraph()->FalseConstant();
734 21 : ReplaceWithValue(node, value);
735 : return Replace(value);
736 : }
737 21 : Node* value = NodeProperties::GetValueInput(node, 2);
738 : Type* value_type = NodeProperties::GetType(value);
739 21 : Node* context = NodeProperties::GetContextInput(node);
740 21 : Node* frame_state = NodeProperties::GetFrameStateInput(node);
741 21 : Node* effect = NodeProperties::GetEffectInput(node);
742 21 : Node* control = NodeProperties::GetControlInput(node);
743 :
744 : // Constant-fold based on {value} type.
745 21 : if (value_type->Is(Type::Array())) {
746 13 : Node* value = jsgraph()->TrueConstant();
747 : ReplaceWithValue(node, value);
748 : return Replace(value);
749 8 : } else if (!value_type->Maybe(Type::ArrayOrProxy())) {
750 0 : Node* value = jsgraph()->FalseConstant();
751 : ReplaceWithValue(node, value);
752 : return Replace(value);
753 : }
754 :
755 : int count = 0;
756 : Node* values[5];
757 : Node* effects[5];
758 : Node* controls[4];
759 :
760 : // Check if the {value} is a Smi.
761 8 : Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value);
762 : control =
763 8 : graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
764 :
765 : // The {value} is a Smi.
766 16 : controls[count] = graph()->NewNode(common()->IfTrue(), control);
767 8 : effects[count] = effect;
768 8 : values[count] = jsgraph()->FalseConstant();
769 : count++;
770 :
771 8 : control = graph()->NewNode(common()->IfFalse(), control);
772 :
773 : // Load the {value}s instance type.
774 : Node* value_map = effect = graph()->NewNode(
775 24 : simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
776 : Node* value_instance_type = effect = graph()->NewNode(
777 : simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
778 24 : effect, control);
779 :
780 : // Check if the {value} is a JSArray.
781 : check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
782 16 : jsgraph()->Constant(JS_ARRAY_TYPE));
783 8 : control = graph()->NewNode(common()->Branch(), check, control);
784 :
785 : // The {value} is a JSArray.
786 16 : controls[count] = graph()->NewNode(common()->IfTrue(), control);
787 8 : effects[count] = effect;
788 8 : values[count] = jsgraph()->TrueConstant();
789 : count++;
790 :
791 8 : control = graph()->NewNode(common()->IfFalse(), control);
792 :
793 : // Check if the {value} is a JSProxy.
794 : check = graph()->NewNode(simplified()->NumberEqual(), value_instance_type,
795 16 : jsgraph()->Constant(JS_PROXY_TYPE));
796 : control =
797 8 : graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
798 :
799 : // The {value} is neither a JSArray nor a JSProxy.
800 16 : controls[count] = graph()->NewNode(common()->IfFalse(), control);
801 8 : effects[count] = effect;
802 8 : values[count] = jsgraph()->FalseConstant();
803 : count++;
804 :
805 8 : control = graph()->NewNode(common()->IfTrue(), control);
806 :
807 : // Let the %ArrayIsArray runtime function deal with the JSProxy {value}.
808 : value = effect = control =
809 : graph()->NewNode(javascript()->CallRuntime(Runtime::kArrayIsArray), value,
810 8 : context, frame_state, effect, control);
811 : NodeProperties::SetType(value, Type::Boolean());
812 :
813 : // Update potential {IfException} uses of {node} to point to the above
814 : // %ArrayIsArray runtime call node instead.
815 8 : Node* on_exception = nullptr;
816 8 : if (NodeProperties::IsExceptionalCall(node, &on_exception)) {
817 0 : NodeProperties::ReplaceControlInput(on_exception, control);
818 0 : NodeProperties::ReplaceEffectInput(on_exception, effect);
819 0 : control = graph()->NewNode(common()->IfSuccess(), control);
820 0 : Revisit(on_exception);
821 : }
822 :
823 : // The {value} is neither a JSArray nor a JSProxy.
824 8 : controls[count] = control;
825 8 : effects[count] = effect;
826 8 : values[count] = value;
827 : count++;
828 :
829 16 : control = graph()->NewNode(common()->Merge(count), count, controls);
830 8 : effects[count] = control;
831 8 : values[count] = control;
832 16 : effect = graph()->NewNode(common()->EffectPhi(count), count + 1, effects);
833 : value = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, count),
834 16 : count + 1, values);
835 : ReplaceWithValue(node, value, effect, control);
836 : return Replace(value);
837 : }
838 :
839 : // ES6 section 22.1.3.17 Array.prototype.pop ( )
840 549 : Reduction JSBuiltinReducer::ReduceArrayPop(Node* node) {
841 : Handle<Map> receiver_map;
842 153 : Node* receiver = NodeProperties::GetValueInput(node, 1);
843 153 : Node* effect = NodeProperties::GetEffectInput(node);
844 153 : Node* control = NodeProperties::GetControlInput(node);
845 : // TODO(turbofan): Extend this to also handle fast (holey) double elements
846 : // once we got the hole NaN mess sorted out in TurboFan/V8.
847 455 : if (GetMapWitness(node).ToHandle(&receiver_map) &&
848 234 : CanInlineArrayResizeOperation(receiver_map) &&
849 : IsFastSmiOrObjectElementsKind(receiver_map->elements_kind())) {
850 : // Install code dependencies on the {receiver} prototype maps and the
851 : // global array protector cell.
852 : dependencies()->AssumePropertyCell(factory()->array_protector());
853 66 : dependencies()->AssumePrototypeMapsStable(receiver_map);
854 :
855 : // Load the "length" property of the {receiver}.
856 : Node* length = effect = graph()->NewNode(
857 : simplified()->LoadField(
858 : AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
859 198 : receiver, effect, control);
860 :
861 : // Check if the {receiver} has any elements.
862 : Node* check = graph()->NewNode(simplified()->NumberEqual(), length,
863 132 : jsgraph()->ZeroConstant());
864 : Node* branch =
865 66 : graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control);
866 :
867 66 : Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
868 : Node* etrue = effect;
869 66 : Node* vtrue = jsgraph()->UndefinedConstant();
870 :
871 66 : Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
872 : Node* efalse = effect;
873 : Node* vfalse;
874 : {
875 : // Load the elements backing store from the {receiver}.
876 : Node* elements = efalse = graph()->NewNode(
877 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()),
878 198 : receiver, efalse, if_false);
879 :
880 : // Ensure that we aren't popping from a copy-on-write backing store.
881 : elements = efalse =
882 : graph()->NewNode(simplified()->EnsureWritableFastElements(), receiver,
883 66 : elements, efalse, if_false);
884 :
885 : // Compute the new {length}.
886 : length = graph()->NewNode(simplified()->NumberSubtract(), length,
887 132 : jsgraph()->OneConstant());
888 :
889 : // Store the new {length} to the {receiver}.
890 : efalse = graph()->NewNode(
891 : simplified()->StoreField(
892 : AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
893 198 : receiver, length, efalse, if_false);
894 :
895 : // Load the last entry from the {elements}.
896 : vfalse = efalse = graph()->NewNode(
897 : simplified()->LoadElement(AccessBuilder::ForFixedArrayElement(
898 : receiver_map->elements_kind())),
899 198 : elements, length, efalse, if_false);
900 :
901 : // Store a hole to the element we just removed from the {receiver}.
902 : efalse = graph()->NewNode(
903 : simplified()->StoreElement(AccessBuilder::ForFixedArrayElement(
904 : GetHoleyElementsKind(receiver_map->elements_kind()))),
905 264 : elements, length, jsgraph()->TheHoleConstant(), efalse, if_false);
906 : }
907 :
908 66 : control = graph()->NewNode(common()->Merge(2), if_true, if_false);
909 66 : effect = graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control);
910 : Node* value =
911 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
912 66 : vtrue, vfalse, control);
913 :
914 : // Convert the hole to undefined. Do this last, so that we can optimize
915 : // conversion operator via some smart strength reduction in many cases.
916 66 : if (IsFastHoleyElementsKind(receiver_map->elements_kind())) {
917 : value =
918 15 : graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), value);
919 : }
920 :
921 66 : ReplaceWithValue(node, value, effect, control);
922 : return Replace(value);
923 : }
924 : return NoChange();
925 : }
926 :
927 : // ES6 section 22.1.3.18 Array.prototype.push ( )
928 4447 : Reduction JSBuiltinReducer::ReduceArrayPush(Node* node) {
929 : // We need exactly target, receiver and value parameters.
930 1642 : if (node->op()->ValueInputCount() != 3) return NoChange();
931 1617 : Node* receiver = NodeProperties::GetValueInput(node, 1);
932 1617 : Node* effect = NodeProperties::GetEffectInput(node);
933 1617 : Node* control = NodeProperties::GetControlInput(node);
934 1617 : Node* value = NodeProperties::GetValueInput(node, 2);
935 : ZoneHandleSet<Map> receiver_maps;
936 : NodeProperties::InferReceiverMapsResult result =
937 1617 : NodeProperties::InferReceiverMaps(receiver, effect, &receiver_maps);
938 1617 : if (receiver_maps.size() != 1) return NoChange();
939 : DCHECK_NE(NodeProperties::kNoReceiverMaps, result);
940 :
941 : // TODO(turbofan): Relax this to deal with multiple {receiver} maps.
942 : Handle<Map> receiver_map = receiver_maps[0];
943 1490 : if (CanInlineArrayResizeOperation(receiver_map)) {
944 : // Install code dependencies on the {receiver} prototype maps and the
945 : // global array protector cell.
946 : dependencies()->AssumePropertyCell(factory()->array_protector());
947 935 : dependencies()->AssumePrototypeMapsStable(receiver_map);
948 :
949 : // If the {receiver_maps} information is not reliable, we need
950 : // to check that the {receiver} still has one of these maps.
951 935 : if (result == NodeProperties::kUnreliableReceiverMaps) {
952 250 : if (receiver_map->is_stable()) {
953 0 : dependencies()->AssumeMapStable(receiver_map);
954 : } else {
955 : // TODO(turbofan): This is a potential - yet unlikely - deoptimization
956 : // loop, since we might not learn from this deoptimization in baseline
957 : // code. We need a way to learn from deoptimizations in optimized to
958 : // address these problems.
959 : effect = graph()->NewNode(
960 : simplified()->CheckMaps(CheckMapsFlag::kNone, receiver_maps),
961 500 : receiver, effect, control);
962 : }
963 : }
964 :
965 : // TODO(turbofan): Perform type checks on the {value}. We are not guaranteed
966 : // to learn from these checks in case they fail, as the witness (i.e. the
967 : // map check from the LoadIC for a.push) might not be executed in baseline
968 : // code (after we stored the value in the builtin and thereby changed the
969 : // elements kind of a) before be decide to optimize this function again. We
970 : // currently don't have a proper way to deal with this; the proper solution
971 : // here is to learn on deopt, i.e. disable Array.prototype.push inlining
972 : // for this function.
973 935 : if (IsFastSmiElementsKind(receiver_map->elements_kind())) {
974 : value = effect =
975 504 : graph()->NewNode(simplified()->CheckSmi(), value, effect, control);
976 431 : } else if (IsFastDoubleElementsKind(receiver_map->elements_kind())) {
977 : value = effect =
978 41 : graph()->NewNode(simplified()->CheckNumber(), value, effect, control);
979 : // Make sure we do not store signaling NaNs into double arrays.
980 41 : value = graph()->NewNode(simplified()->NumberSilenceNaN(), value);
981 : }
982 :
983 : // Load the "length" property of the {receiver}.
984 : Node* length = effect = graph()->NewNode(
985 : simplified()->LoadField(
986 : AccessBuilder::ForJSArrayLength(receiver_map->elements_kind())),
987 2805 : receiver, effect, control);
988 :
989 : // Load the elements backing store of the {receiver}.
990 : Node* elements = effect = graph()->NewNode(
991 : simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver,
992 2805 : effect, control);
993 :
994 : // TODO(turbofan): Check if we need to grow the {elements} backing store.
995 : // This will deopt if we cannot grow the array further, and we currently
996 : // don't necessarily learn from it. See the comment on the value type check
997 : // above.
998 : GrowFastElementsFlags flags = GrowFastElementsFlag::kArrayObject;
999 935 : if (IsFastDoubleElementsKind(receiver_map->elements_kind())) {
1000 : flags |= GrowFastElementsFlag::kDoubleElements;
1001 : }
1002 : elements = effect =
1003 : graph()->NewNode(simplified()->MaybeGrowFastElements(flags), receiver,
1004 935 : elements, length, length, effect, control);
1005 :
1006 : // Append the value to the {elements}.
1007 : effect = graph()->NewNode(
1008 : simplified()->StoreElement(
1009 : AccessBuilder::ForFixedArrayElement(receiver_map->elements_kind())),
1010 2805 : elements, length, value, effect, control);
1011 :
1012 : // Return the new length of the {receiver}.
1013 : value = graph()->NewNode(simplified()->NumberAdd(), length,
1014 1870 : jsgraph()->OneConstant());
1015 :
1016 935 : ReplaceWithValue(node, value, effect, control);
1017 : return Replace(value);
1018 : }
1019 : return NoChange();
1020 : }
1021 :
1022 : namespace {
1023 :
1024 135 : bool HasInstanceTypeWitness(Node* receiver, Node* effect,
1025 : InstanceType instance_type) {
1026 310 : for (Node* dominator = effect;;) {
1027 286 : if (dominator->opcode() == IrOpcode::kCheckMaps &&
1028 131 : NodeProperties::IsSame(dominator->InputAt(0), receiver)) {
1029 : ZoneHandleSet<Map> const& maps =
1030 131 : CheckMapsParametersOf(dominator->op()).maps();
1031 : // Check if all maps have the given {instance_type}.
1032 604 : for (size_t i = 0; i < maps.size(); ++i) {
1033 171 : if (maps[i]->instance_type() != instance_type) return false;
1034 : }
1035 : return true;
1036 : }
1037 : // The instance type doesn't change for JSReceiver values, so we
1038 : // don't need to pay attention to potentially side-effecting nodes
1039 : // here. Strings and internal structures like FixedArray and
1040 : // FixedDoubleArray are weird here, but we don't use this function then.
1041 : DCHECK_LE(FIRST_JS_RECEIVER_TYPE, instance_type);
1042 : DCHECK_EQ(1, dominator->op()->EffectOutputCount());
1043 24 : if (dominator->op()->EffectInputCount() != 1) {
1044 : // Didn't find any appropriate CheckMaps node.
1045 : return false;
1046 : }
1047 20 : dominator = NodeProperties::GetEffectInput(dominator);
1048 20 : }
1049 : }
1050 :
1051 : } // namespace
1052 :
1053 : // ES6 section 20.3.3.1 Date.now ( )
1054 8 : Reduction JSBuiltinReducer::ReduceDateNow(Node* node) {
1055 8 : NodeProperties::RemoveValueInputs(node);
1056 : NodeProperties::ChangeOp(
1057 8 : node, javascript()->CallRuntime(Runtime::kDateCurrentTime));
1058 8 : return Changed(node);
1059 : }
1060 :
1061 : // ES6 section 20.3.4.10 Date.prototype.getTime ( )
1062 4 : Reduction JSBuiltinReducer::ReduceDateGetTime(Node* node) {
1063 4 : Node* receiver = NodeProperties::GetValueInput(node, 1);
1064 4 : Node* effect = NodeProperties::GetEffectInput(node);
1065 4 : Node* control = NodeProperties::GetControlInput(node);
1066 4 : if (HasInstanceTypeWitness(receiver, effect, JS_DATE_TYPE)) {
1067 : Node* value = effect = graph()->NewNode(
1068 : simplified()->LoadField(AccessBuilder::ForJSDateValue()), receiver,
1069 0 : effect, control);
1070 0 : ReplaceWithValue(node, value, effect, control);
1071 : return Replace(value);
1072 : }
1073 : return NoChange();
1074 : }
1075 :
1076 : // ES6 section 18.2.2 isFinite ( number )
1077 51 : Reduction JSBuiltinReducer::ReduceGlobalIsFinite(Node* node) {
1078 : JSCallReduction r(node);
1079 51 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1080 : // isFinite(a:plain-primitive) -> NumberEqual(a', a')
1081 : // where a' = NumberSubtract(ToNumber(a), ToNumber(a))
1082 36 : Node* input = ToNumber(r.GetJSCallInput(0));
1083 36 : Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input);
1084 36 : Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff);
1085 : return Replace(value);
1086 : }
1087 : return NoChange();
1088 : }
1089 :
1090 : // ES6 section 18.2.3 isNaN ( number )
1091 21097 : Reduction JSBuiltinReducer::ReduceGlobalIsNaN(Node* node) {
1092 : JSCallReduction r(node);
1093 21097 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1094 : // isNaN(a:plain-primitive) -> BooleanNot(NumberEqual(a', a'))
1095 : // where a' = ToNumber(a)
1096 8617 : Node* input = ToNumber(r.GetJSCallInput(0));
1097 8617 : Node* check = graph()->NewNode(simplified()->NumberEqual(), input, input);
1098 8617 : Node* value = graph()->NewNode(simplified()->BooleanNot(), check);
1099 : return Replace(value);
1100 : }
1101 : return NoChange();
1102 : }
1103 :
1104 : // ES6 section 20.2.2.1 Math.abs ( x )
1105 401 : Reduction JSBuiltinReducer::ReduceMathAbs(Node* node) {
1106 : JSCallReduction r(node);
1107 401 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1108 : // Math.abs(a:plain-primitive) -> NumberAbs(ToNumber(a))
1109 320 : Node* input = ToNumber(r.GetJSCallInput(0));
1110 320 : Node* value = graph()->NewNode(simplified()->NumberAbs(), input);
1111 : return Replace(value);
1112 : }
1113 : return NoChange();
1114 : }
1115 :
1116 : // ES6 section 20.2.2.2 Math.acos ( x )
1117 14 : Reduction JSBuiltinReducer::ReduceMathAcos(Node* node) {
1118 : JSCallReduction r(node);
1119 14 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1120 : // Math.acos(a:plain-primitive) -> NumberAcos(ToNumber(a))
1121 14 : Node* input = ToNumber(r.GetJSCallInput(0));
1122 14 : Node* value = graph()->NewNode(simplified()->NumberAcos(), input);
1123 : return Replace(value);
1124 : }
1125 : return NoChange();
1126 : }
1127 :
1128 : // ES6 section 20.2.2.3 Math.acosh ( x )
1129 13 : Reduction JSBuiltinReducer::ReduceMathAcosh(Node* node) {
1130 : JSCallReduction r(node);
1131 13 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1132 : // Math.acosh(a:plain-primitive) -> NumberAcosh(ToNumber(a))
1133 13 : Node* input = ToNumber(r.GetJSCallInput(0));
1134 13 : Node* value = graph()->NewNode(simplified()->NumberAcosh(), input);
1135 : return Replace(value);
1136 : }
1137 : return NoChange();
1138 : }
1139 :
1140 : // ES6 section 20.2.2.4 Math.asin ( x )
1141 14 : Reduction JSBuiltinReducer::ReduceMathAsin(Node* node) {
1142 : JSCallReduction r(node);
1143 14 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1144 : // Math.asin(a:plain-primitive) -> NumberAsin(ToNumber(a))
1145 14 : Node* input = ToNumber(r.GetJSCallInput(0));
1146 14 : Node* value = graph()->NewNode(simplified()->NumberAsin(), input);
1147 : return Replace(value);
1148 : }
1149 : return NoChange();
1150 : }
1151 :
1152 : // ES6 section 20.2.2.5 Math.asinh ( x )
1153 13 : Reduction JSBuiltinReducer::ReduceMathAsinh(Node* node) {
1154 : JSCallReduction r(node);
1155 13 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1156 : // Math.asinh(a:plain-primitive) -> NumberAsinh(ToNumber(a))
1157 13 : Node* input = ToNumber(r.GetJSCallInput(0));
1158 13 : Node* value = graph()->NewNode(simplified()->NumberAsinh(), input);
1159 : return Replace(value);
1160 : }
1161 : return NoChange();
1162 : }
1163 :
1164 : // ES6 section 20.2.2.6 Math.atan ( x )
1165 14 : Reduction JSBuiltinReducer::ReduceMathAtan(Node* node) {
1166 : JSCallReduction r(node);
1167 14 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1168 : // Math.atan(a:plain-primitive) -> NumberAtan(ToNumber(a))
1169 14 : Node* input = ToNumber(r.GetJSCallInput(0));
1170 14 : Node* value = graph()->NewNode(simplified()->NumberAtan(), input);
1171 : return Replace(value);
1172 : }
1173 : return NoChange();
1174 : }
1175 :
1176 : // ES6 section 20.2.2.7 Math.atanh ( x )
1177 13 : Reduction JSBuiltinReducer::ReduceMathAtanh(Node* node) {
1178 : JSCallReduction r(node);
1179 13 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1180 : // Math.atanh(a:plain-primitive) -> NumberAtanh(ToNumber(a))
1181 13 : Node* input = ToNumber(r.GetJSCallInput(0));
1182 13 : Node* value = graph()->NewNode(simplified()->NumberAtanh(), input);
1183 : return Replace(value);
1184 : }
1185 : return NoChange();
1186 : }
1187 :
1188 : // ES6 section 20.2.2.8 Math.atan2 ( y, x )
1189 160 : Reduction JSBuiltinReducer::ReduceMathAtan2(Node* node) {
1190 : JSCallReduction r(node);
1191 160 : if (r.InputsMatchTwo(Type::PlainPrimitive(), Type::PlainPrimitive())) {
1192 : // Math.atan2(a:plain-primitive,
1193 : // b:plain-primitive) -> NumberAtan2(ToNumber(a),
1194 : // ToNumber(b))
1195 160 : Node* left = ToNumber(r.left());
1196 160 : Node* right = ToNumber(r.right());
1197 160 : Node* value = graph()->NewNode(simplified()->NumberAtan2(), left, right);
1198 : return Replace(value);
1199 : }
1200 : return NoChange();
1201 : }
1202 :
1203 : // ES6 section 20.2.2.10 Math.ceil ( x )
1204 7636 : Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) {
1205 : JSCallReduction r(node);
1206 7636 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1207 : // Math.ceil(a:plain-primitive) -> NumberCeil(ToNumber(a))
1208 5736 : Node* input = ToNumber(r.GetJSCallInput(0));
1209 5736 : Node* value = graph()->NewNode(simplified()->NumberCeil(), input);
1210 : return Replace(value);
1211 : }
1212 : return NoChange();
1213 : }
1214 :
1215 : // ES6 section 20.2.2.11 Math.clz32 ( x )
1216 59 : Reduction JSBuiltinReducer::ReduceMathClz32(Node* node) {
1217 : JSCallReduction r(node);
1218 59 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1219 : // Math.clz32(a:plain-primitive) -> NumberClz32(ToUint32(a))
1220 30 : Node* input = ToUint32(r.GetJSCallInput(0));
1221 30 : Node* value = graph()->NewNode(simplified()->NumberClz32(), input);
1222 : return Replace(value);
1223 : }
1224 : return NoChange();
1225 : }
1226 :
1227 : // ES6 section 20.2.2.12 Math.cos ( x )
1228 78 : Reduction JSBuiltinReducer::ReduceMathCos(Node* node) {
1229 : JSCallReduction r(node);
1230 78 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1231 : // Math.cos(a:plain-primitive) -> NumberCos(ToNumber(a))
1232 57 : Node* input = ToNumber(r.GetJSCallInput(0));
1233 57 : Node* value = graph()->NewNode(simplified()->NumberCos(), input);
1234 : return Replace(value);
1235 : }
1236 : return NoChange();
1237 : }
1238 :
1239 : // ES6 section 20.2.2.13 Math.cosh ( x )
1240 22 : Reduction JSBuiltinReducer::ReduceMathCosh(Node* node) {
1241 : JSCallReduction r(node);
1242 22 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1243 : // Math.cosh(a:plain-primitive) -> NumberCosh(ToNumber(a))
1244 22 : Node* input = ToNumber(r.GetJSCallInput(0));
1245 22 : Node* value = graph()->NewNode(simplified()->NumberCosh(), input);
1246 : return Replace(value);
1247 : }
1248 : return NoChange();
1249 : }
1250 :
1251 : // ES6 section 20.2.2.14 Math.exp ( x )
1252 57 : Reduction JSBuiltinReducer::ReduceMathExp(Node* node) {
1253 : JSCallReduction r(node);
1254 57 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1255 : // Math.exp(a:plain-primitive) -> NumberExp(ToNumber(a))
1256 42 : Node* input = ToNumber(r.GetJSCallInput(0));
1257 42 : Node* value = graph()->NewNode(simplified()->NumberExp(), input);
1258 : return Replace(value);
1259 : }
1260 : return NoChange();
1261 : }
1262 :
1263 : // ES6 section 20.2.2.15 Math.expm1 ( x )
1264 0 : Reduction JSBuiltinReducer::ReduceMathExpm1(Node* node) {
1265 : JSCallReduction r(node);
1266 0 : if (r.InputsMatchOne(Type::Number())) {
1267 : // Math.expm1(a:number) -> NumberExpm1(a)
1268 0 : Node* value = graph()->NewNode(simplified()->NumberExpm1(), r.left());
1269 : return Replace(value);
1270 : }
1271 : return NoChange();
1272 : }
1273 :
1274 : // ES6 section 20.2.2.16 Math.floor ( x )
1275 28920 : Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) {
1276 : JSCallReduction r(node);
1277 28920 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1278 : // Math.floor(a:plain-primitive) -> NumberFloor(ToNumber(a))
1279 9355 : Node* input = ToNumber(r.GetJSCallInput(0));
1280 9355 : Node* value = graph()->NewNode(simplified()->NumberFloor(), input);
1281 : return Replace(value);
1282 : }
1283 : return NoChange();
1284 : }
1285 :
1286 : // ES6 section 20.2.2.17 Math.fround ( x )
1287 1113 : Reduction JSBuiltinReducer::ReduceMathFround(Node* node) {
1288 : JSCallReduction r(node);
1289 1113 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1290 : // Math.fround(a:plain-primitive) -> NumberFround(ToNumber(a))
1291 672 : Node* input = ToNumber(r.GetJSCallInput(0));
1292 672 : Node* value = graph()->NewNode(simplified()->NumberFround(), input);
1293 : return Replace(value);
1294 : }
1295 : return NoChange();
1296 : }
1297 :
1298 : // ES6 section 20.2.2.19 Math.imul ( x, y )
1299 152 : Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
1300 : JSCallReduction r(node);
1301 152 : if (r.InputsMatchTwo(Type::PlainPrimitive(), Type::PlainPrimitive())) {
1302 : // Math.imul(a:plain-primitive,
1303 : // b:plain-primitive) -> NumberImul(ToUint32(a),
1304 : // ToUint32(b))
1305 57 : Node* left = ToUint32(r.left());
1306 57 : Node* right = ToUint32(r.right());
1307 57 : Node* value = graph()->NewNode(simplified()->NumberImul(), left, right);
1308 : return Replace(value);
1309 : }
1310 : return NoChange();
1311 : }
1312 :
1313 : // ES6 section 20.2.2.20 Math.log ( x )
1314 151 : Reduction JSBuiltinReducer::ReduceMathLog(Node* node) {
1315 : JSCallReduction r(node);
1316 151 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1317 : // Math.log(a:plain-primitive) -> NumberLog(ToNumber(a))
1318 144 : Node* input = ToNumber(r.GetJSCallInput(0));
1319 144 : Node* value = graph()->NewNode(simplified()->NumberLog(), input);
1320 : return Replace(value);
1321 : }
1322 : return NoChange();
1323 : }
1324 :
1325 : // ES6 section 20.2.2.21 Math.log1p ( x )
1326 13 : Reduction JSBuiltinReducer::ReduceMathLog1p(Node* node) {
1327 : JSCallReduction r(node);
1328 13 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1329 : // Math.log1p(a:plain-primitive) -> NumberLog1p(ToNumber(a))
1330 13 : Node* input = ToNumber(r.GetJSCallInput(0));
1331 13 : Node* value = graph()->NewNode(simplified()->NumberLog1p(), input);
1332 : return Replace(value);
1333 : }
1334 : return NoChange();
1335 : }
1336 :
1337 : // ES6 section 20.2.2.22 Math.log10 ( x )
1338 0 : Reduction JSBuiltinReducer::ReduceMathLog10(Node* node) {
1339 : JSCallReduction r(node);
1340 0 : if (r.InputsMatchOne(Type::Number())) {
1341 : // Math.log10(a:number) -> NumberLog10(a)
1342 0 : Node* value = graph()->NewNode(simplified()->NumberLog10(), r.left());
1343 : return Replace(value);
1344 : }
1345 : return NoChange();
1346 : }
1347 :
1348 : // ES6 section 20.2.2.23 Math.log2 ( x )
1349 0 : Reduction JSBuiltinReducer::ReduceMathLog2(Node* node) {
1350 : JSCallReduction r(node);
1351 0 : if (r.InputsMatchOne(Type::Number())) {
1352 : // Math.log2(a:number) -> NumberLog(a)
1353 0 : Node* value = graph()->NewNode(simplified()->NumberLog2(), r.left());
1354 : return Replace(value);
1355 : }
1356 : return NoChange();
1357 : }
1358 :
1359 : // ES6 section 20.2.2.24 Math.max ( value1, value2, ...values )
1360 338 : Reduction JSBuiltinReducer::ReduceMathMax(Node* node) {
1361 : JSCallReduction r(node);
1362 337 : if (r.InputsMatchZero()) {
1363 : // Math.max() -> -Infinity
1364 1 : return Replace(jsgraph()->Constant(-V8_INFINITY));
1365 : }
1366 336 : if (r.InputsMatchAll(Type::PlainPrimitive())) {
1367 : // Math.max(a:plain-primitive, b:plain-primitive, ...)
1368 233 : Node* value = ToNumber(r.GetJSCallInput(0));
1369 908 : for (int i = 1; i < r.GetJSCallArity(); i++) {
1370 221 : Node* input = ToNumber(r.GetJSCallInput(i));
1371 221 : value = graph()->NewNode(simplified()->NumberMax(), value, input);
1372 : }
1373 : return Replace(value);
1374 : }
1375 : return NoChange();
1376 : }
1377 :
1378 : // ES6 section 20.2.2.25 Math.min ( value1, value2, ...values )
1379 307 : Reduction JSBuiltinReducer::ReduceMathMin(Node* node) {
1380 : JSCallReduction r(node);
1381 306 : if (r.InputsMatchZero()) {
1382 : // Math.min() -> Infinity
1383 1 : return Replace(jsgraph()->Constant(V8_INFINITY));
1384 : }
1385 305 : if (r.InputsMatchAll(Type::PlainPrimitive())) {
1386 : // Math.min(a:plain-primitive, b:plain-primitive, ...)
1387 268 : Node* value = ToNumber(r.GetJSCallInput(0));
1388 1048 : for (int i = 1; i < r.GetJSCallArity(); i++) {
1389 256 : Node* input = ToNumber(r.GetJSCallInput(i));
1390 256 : value = graph()->NewNode(simplified()->NumberMin(), value, input);
1391 : }
1392 : return Replace(value);
1393 : }
1394 : return NoChange();
1395 : }
1396 :
1397 : // ES6 section 20.2.2.26 Math.pow ( x, y )
1398 1181 : Reduction JSBuiltinReducer::ReduceMathPow(Node* node) {
1399 : JSCallReduction r(node);
1400 1181 : if (r.InputsMatchTwo(Type::PlainPrimitive(), Type::PlainPrimitive())) {
1401 : // Math.pow(a:plain-primitive,
1402 : // b:plain-primitive) -> NumberPow(ToNumber(a), ToNumber(b))
1403 1139 : Node* left = ToNumber(r.left());
1404 1139 : Node* right = ToNumber(r.right());
1405 1139 : Node* value = graph()->NewNode(simplified()->NumberPow(), left, right);
1406 : return Replace(value);
1407 : }
1408 : return NoChange();
1409 : }
1410 :
1411 : // ES6 section 20.2.2.28 Math.round ( x )
1412 1587 : Reduction JSBuiltinReducer::ReduceMathRound(Node* node) {
1413 : JSCallReduction r(node);
1414 1587 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1415 : // Math.round(a:plain-primitive) -> NumberRound(ToNumber(a))
1416 106 : Node* input = ToNumber(r.GetJSCallInput(0));
1417 106 : Node* value = graph()->NewNode(simplified()->NumberRound(), input);
1418 : return Replace(value);
1419 : }
1420 : return NoChange();
1421 : }
1422 :
1423 : // ES6 section 20.2.2.9 Math.cbrt ( x )
1424 0 : Reduction JSBuiltinReducer::ReduceMathCbrt(Node* node) {
1425 : JSCallReduction r(node);
1426 0 : if (r.InputsMatchOne(Type::Number())) {
1427 : // Math.cbrt(a:number) -> NumberCbrt(a)
1428 0 : Node* value = graph()->NewNode(simplified()->NumberCbrt(), r.left());
1429 : return Replace(value);
1430 : }
1431 : return NoChange();
1432 : }
1433 :
1434 : // ES6 section 20.2.2.29 Math.sign ( x )
1435 34 : Reduction JSBuiltinReducer::ReduceMathSign(Node* node) {
1436 : JSCallReduction r(node);
1437 34 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1438 : // Math.sign(a:plain-primitive) -> NumberSign(ToNumber(a))
1439 34 : Node* input = ToNumber(r.GetJSCallInput(0));
1440 34 : Node* value = graph()->NewNode(simplified()->NumberSign(), input);
1441 : return Replace(value);
1442 : }
1443 : return NoChange();
1444 : }
1445 :
1446 : // ES6 section 20.2.2.30 Math.sin ( x )
1447 102 : Reduction JSBuiltinReducer::ReduceMathSin(Node* node) {
1448 : JSCallReduction r(node);
1449 102 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1450 : // Math.sin(a:plain-primitive) -> NumberSin(ToNumber(a))
1451 92 : Node* input = ToNumber(r.GetJSCallInput(0));
1452 92 : Node* value = graph()->NewNode(simplified()->NumberSin(), input);
1453 : return Replace(value);
1454 : }
1455 : return NoChange();
1456 : }
1457 :
1458 : // ES6 section 20.2.2.31 Math.sinh ( x )
1459 22 : Reduction JSBuiltinReducer::ReduceMathSinh(Node* node) {
1460 : JSCallReduction r(node);
1461 22 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1462 : // Math.sinh(a:plain-primitive) -> NumberSinh(ToNumber(a))
1463 22 : Node* input = ToNumber(r.GetJSCallInput(0));
1464 22 : Node* value = graph()->NewNode(simplified()->NumberSinh(), input);
1465 : return Replace(value);
1466 : }
1467 : return NoChange();
1468 : }
1469 :
1470 : // ES6 section 20.2.2.32 Math.sqrt ( x )
1471 103 : Reduction JSBuiltinReducer::ReduceMathSqrt(Node* node) {
1472 : JSCallReduction r(node);
1473 103 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1474 : // Math.sqrt(a:plain-primitive) -> NumberSqrt(ToNumber(a))
1475 72 : Node* input = ToNumber(r.GetJSCallInput(0));
1476 72 : Node* value = graph()->NewNode(simplified()->NumberSqrt(), input);
1477 : return Replace(value);
1478 : }
1479 : return NoChange();
1480 : }
1481 :
1482 : // ES6 section 20.2.2.33 Math.tan ( x )
1483 57 : Reduction JSBuiltinReducer::ReduceMathTan(Node* node) {
1484 : JSCallReduction r(node);
1485 57 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1486 : // Math.tan(a:plain-primitive) -> NumberTan(ToNumber(a))
1487 21 : Node* input = ToNumber(r.GetJSCallInput(0));
1488 21 : Node* value = graph()->NewNode(simplified()->NumberTan(), input);
1489 : return Replace(value);
1490 : }
1491 : return NoChange();
1492 : }
1493 :
1494 : // ES6 section 20.2.2.34 Math.tanh ( x )
1495 22 : Reduction JSBuiltinReducer::ReduceMathTanh(Node* node) {
1496 : JSCallReduction r(node);
1497 22 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1498 : // Math.tanh(a:plain-primitive) -> NumberTanh(ToNumber(a))
1499 22 : Node* input = ToNumber(r.GetJSCallInput(0));
1500 22 : Node* value = graph()->NewNode(simplified()->NumberTanh(), input);
1501 : return Replace(value);
1502 : }
1503 : return NoChange();
1504 : }
1505 :
1506 : // ES6 section 20.2.2.35 Math.trunc ( x )
1507 7260 : Reduction JSBuiltinReducer::ReduceMathTrunc(Node* node) {
1508 : JSCallReduction r(node);
1509 7260 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1510 : // Math.trunc(a:plain-primitive) -> NumberTrunc(ToNumber(a))
1511 1395 : Node* input = ToNumber(r.GetJSCallInput(0));
1512 1395 : Node* value = graph()->NewNode(simplified()->NumberTrunc(), input);
1513 : return Replace(value);
1514 : }
1515 : return NoChange();
1516 : }
1517 :
1518 : // ES6 section 20.1.2.2 Number.isFinite ( number )
1519 19 : Reduction JSBuiltinReducer::ReduceNumberIsFinite(Node* node) {
1520 : JSCallReduction r(node);
1521 19 : if (r.InputsMatchOne(Type::Number())) {
1522 : // Number.isFinite(a:number) -> NumberEqual(a', a')
1523 : // where a' = NumberSubtract(a, a)
1524 : Node* input = r.GetJSCallInput(0);
1525 19 : Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, input);
1526 19 : Node* value = graph()->NewNode(simplified()->NumberEqual(), diff, diff);
1527 : return Replace(value);
1528 : }
1529 : return NoChange();
1530 : }
1531 :
1532 : // ES6 section 20.1.2.3 Number.isInteger ( number )
1533 38 : Reduction JSBuiltinReducer::ReduceNumberIsInteger(Node* node) {
1534 : JSCallReduction r(node);
1535 19 : if (r.InputsMatchOne(Type::Number())) {
1536 : // Number.isInteger(x:number) -> NumberEqual(NumberSubtract(x, x'), #0)
1537 : // where x' = NumberTrunc(x)
1538 : Node* input = r.GetJSCallInput(0);
1539 19 : Node* trunc = graph()->NewNode(simplified()->NumberTrunc(), input);
1540 19 : Node* diff = graph()->NewNode(simplified()->NumberSubtract(), input, trunc);
1541 : Node* value = graph()->NewNode(simplified()->NumberEqual(), diff,
1542 38 : jsgraph()->ZeroConstant());
1543 : return Replace(value);
1544 : }
1545 : return NoChange();
1546 : }
1547 :
1548 : // ES6 section 20.1.2.4 Number.isNaN ( number )
1549 75 : Reduction JSBuiltinReducer::ReduceNumberIsNaN(Node* node) {
1550 : JSCallReduction r(node);
1551 67 : if (r.InputsMatchZero()) {
1552 : // Number.isNaN() -> #false
1553 8 : Node* value = jsgraph()->FalseConstant();
1554 : return Replace(value);
1555 : }
1556 : // Number.isNaN(a:number) -> ObjectIsNaN(a)
1557 : Node* input = r.GetJSCallInput(0);
1558 59 : Node* value = graph()->NewNode(simplified()->ObjectIsNaN(), input);
1559 : return Replace(value);
1560 : }
1561 :
1562 : // ES6 section 20.1.2.5 Number.isSafeInteger ( number )
1563 21 : Reduction JSBuiltinReducer::ReduceNumberIsSafeInteger(Node* node) {
1564 : JSCallReduction r(node);
1565 14 : if (r.InputsMatchOne(type_cache_.kSafeInteger)) {
1566 : // Number.isInteger(x:safe-integer) -> #true
1567 7 : Node* value = jsgraph()->TrueConstant();
1568 : return Replace(value);
1569 : }
1570 : return NoChange();
1571 : }
1572 :
1573 : // ES6 section 20.1.2.13 Number.parseInt ( string, radix )
1574 460 : Reduction JSBuiltinReducer::ReduceNumberParseInt(Node* node) {
1575 : JSCallReduction r(node);
1576 1365 : if (r.InputsMatchOne(type_cache_.kSafeInteger) ||
1577 : r.InputsMatchTwo(type_cache_.kSafeInteger,
1578 884 : type_cache_.kZeroOrUndefined) ||
1579 424 : r.InputsMatchTwo(type_cache_.kSafeInteger, type_cache_.kTenOrUndefined)) {
1580 : // Number.parseInt(a:safe-integer) -> a
1581 : // Number.parseInt(a:safe-integer,b:#0\/undefined) -> a
1582 : // Number.parseInt(a:safe-integer,b:#10\/undefined) -> a
1583 : Node* value = r.GetJSCallInput(0);
1584 : return Replace(value);
1585 : }
1586 : return NoChange();
1587 : }
1588 :
1589 : // ES6 section #sec-object.create Object.create(proto, properties)
1590 21 : Reduction JSBuiltinReducer::ReduceObjectCreate(Node* node) {
1591 : // We need exactly target, receiver and value parameters.
1592 21 : int arg_count = node->op()->ValueInputCount();
1593 21 : if (arg_count != 3) return NoChange();
1594 21 : Node* effect = NodeProperties::GetEffectInput(node);
1595 21 : Node* control = NodeProperties::GetControlInput(node);
1596 21 : Node* prototype = NodeProperties::GetValueInput(node, 2);
1597 : Type* prototype_type = NodeProperties::GetType(prototype);
1598 : Handle<Map> instance_map;
1599 21 : if (!prototype_type->IsHeapConstant()) return NoChange();
1600 : Handle<HeapObject> prototype_const =
1601 : prototype_type->AsHeapConstant()->Value();
1602 0 : if (!prototype_const->IsNull(isolate()) && !prototype_const->IsJSReceiver()) {
1603 : return NoChange();
1604 : }
1605 0 : instance_map = Map::GetObjectCreateMap(prototype_const);
1606 0 : Node* properties = jsgraph()->EmptyFixedArrayConstant();
1607 0 : if (instance_map->is_dictionary_map()) {
1608 : // Allocated an empty NameDictionary as backing store for the properties.
1609 0 : Handle<Map> map(isolate()->heap()->hash_table_map(), isolate());
1610 : int capacity =
1611 : NameDictionary::ComputeCapacity(NameDictionary::kInitialCapacity);
1612 : DCHECK(base::bits::IsPowerOfTwo32(capacity));
1613 : int length = NameDictionary::EntryToIndex(capacity);
1614 : int size = NameDictionary::SizeFor(length);
1615 :
1616 : effect = graph()->NewNode(
1617 0 : common()->BeginRegion(RegionObservability::kNotObservable), effect);
1618 :
1619 : Node* value = effect =
1620 : graph()->NewNode(simplified()->Allocate(Type::Any(), NOT_TENURED),
1621 0 : jsgraph()->Constant(size), effect, control);
1622 : effect =
1623 : graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()),
1624 0 : value, jsgraph()->HeapConstant(map), effect, control);
1625 :
1626 : // Initialize FixedArray fields.
1627 : effect = graph()->NewNode(
1628 : simplified()->StoreField(AccessBuilder::ForFixedArrayLength()), value,
1629 0 : jsgraph()->SmiConstant(length), effect, control);
1630 : // Initialize HashTable fields.
1631 : effect =
1632 : graph()->NewNode(simplified()->StoreField(
1633 : AccessBuilder::ForHashTableBaseNumberOfElements()),
1634 0 : value, jsgraph()->SmiConstant(0), effect, control);
1635 : effect = graph()->NewNode(
1636 : simplified()->StoreField(
1637 : AccessBuilder::ForHashTableBaseNumberOfDeletedElement()),
1638 0 : value, jsgraph()->SmiConstant(0), effect, control);
1639 : effect = graph()->NewNode(
1640 : simplified()->StoreField(AccessBuilder::ForHashTableBaseCapacity()),
1641 0 : value, jsgraph()->SmiConstant(capacity), effect, control);
1642 : // Initialize Dictionary fields.
1643 0 : Node* undefined = jsgraph()->UndefinedConstant();
1644 : effect = graph()->NewNode(
1645 : simplified()->StoreField(AccessBuilder::ForDictionaryMaxNumberKey()),
1646 0 : value, undefined, effect, control);
1647 : effect = graph()->NewNode(
1648 : simplified()->StoreField(
1649 : AccessBuilder::ForDictionaryNextEnumerationIndex()),
1650 : value, jsgraph()->SmiConstant(PropertyDetails::kInitialIndex), effect,
1651 0 : control);
1652 : // Initialize hte Properties fields.
1653 0 : for (int index = NameDictionary::kNextEnumerationIndexIndex + 1;
1654 : index < length; index++) {
1655 : effect = graph()->NewNode(
1656 : simplified()->StoreField(
1657 : AccessBuilder::ForFixedArraySlot(index, kNoWriteBarrier)),
1658 0 : value, undefined, effect, control);
1659 : }
1660 : properties = effect =
1661 0 : graph()->NewNode(common()->FinishRegion(), value, effect);
1662 : }
1663 :
1664 : int const instance_size = instance_map->instance_size();
1665 0 : if (instance_size > kMaxRegularHeapObjectSize) return NoChange();
1666 : dependencies()->AssumeInitialMapCantChange(instance_map);
1667 :
1668 : // Emit code to allocate the JSObject instance for the given
1669 : // {instance_map}.
1670 : effect = graph()->NewNode(
1671 0 : common()->BeginRegion(RegionObservability::kNotObservable), effect);
1672 : Node* value = effect =
1673 : graph()->NewNode(simplified()->Allocate(Type::Any(), NOT_TENURED),
1674 0 : jsgraph()->Constant(instance_size), effect, control);
1675 : effect =
1676 : graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()), value,
1677 0 : jsgraph()->HeapConstant(instance_map), effect, control);
1678 : effect = graph()->NewNode(
1679 : simplified()->StoreField(AccessBuilder::ForJSObjectProperties()), value,
1680 0 : properties, effect, control);
1681 : effect = graph()->NewNode(
1682 : simplified()->StoreField(AccessBuilder::ForJSObjectElements()), value,
1683 0 : jsgraph()->EmptyFixedArrayConstant(), effect, control);
1684 : // Initialize Object fields.
1685 0 : Node* undefined = jsgraph()->UndefinedConstant();
1686 0 : for (int offset = JSObject::kHeaderSize; offset < instance_size;
1687 : offset += kPointerSize) {
1688 : effect = graph()->NewNode(
1689 : simplified()->StoreField(
1690 : AccessBuilder::ForJSObjectOffset(offset, kNoWriteBarrier)),
1691 0 : value, undefined, effect, control);
1692 : }
1693 0 : value = effect = graph()->NewNode(common()->FinishRegion(), value, effect);
1694 :
1695 : // replace it
1696 0 : ReplaceWithValue(node, value, effect, control);
1697 : return Replace(value);
1698 : }
1699 :
1700 : // ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
1701 732 : Reduction JSBuiltinReducer::ReduceStringFromCharCode(Node* node) {
1702 : JSCallReduction r(node);
1703 732 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1704 : // String.fromCharCode(a:plain-primitive) -> StringFromCharCode(a)
1705 706 : Node* input = ToNumber(r.GetJSCallInput(0));
1706 706 : Node* value = graph()->NewNode(simplified()->StringFromCharCode(), input);
1707 : return Replace(value);
1708 : }
1709 : return NoChange();
1710 : }
1711 :
1712 : namespace {
1713 :
1714 554 : Node* GetStringWitness(Node* node) {
1715 554 : Node* receiver = NodeProperties::GetValueInput(node, 1);
1716 : Type* receiver_type = NodeProperties::GetType(receiver);
1717 554 : Node* effect = NodeProperties::GetEffectInput(node);
1718 554 : if (receiver_type->Is(Type::String())) return receiver;
1719 : // Check if the {node} is dominated by a CheckString renaming for
1720 : // it's {receiver}, and if so use that renaming as {receiver} for
1721 : // the lowering below.
1722 341 : for (Node* dominator = effect;;) {
1723 424 : if (dominator->opcode() == IrOpcode::kCheckString &&
1724 169 : NodeProperties::IsSame(dominator->InputAt(0), receiver)) {
1725 : return dominator;
1726 : }
1727 86 : if (dominator->op()->EffectInputCount() != 1) {
1728 : // Didn't find any appropriate CheckString node.
1729 : return nullptr;
1730 : }
1731 78 : dominator = NodeProperties::GetEffectInput(dominator);
1732 78 : }
1733 : }
1734 :
1735 : } // namespace
1736 :
1737 : // ES6 section 21.1.3.1 String.prototype.charAt ( pos )
1738 188 : Reduction JSBuiltinReducer::ReduceStringCharAt(Node* node) {
1739 : // We need at least target, receiver and index parameters.
1740 214 : if (node->op()->ValueInputCount() >= 3) {
1741 107 : Node* index = NodeProperties::GetValueInput(node, 2);
1742 : Type* index_type = NodeProperties::GetType(index);
1743 107 : Node* effect = NodeProperties::GetEffectInput(node);
1744 107 : Node* control = NodeProperties::GetControlInput(node);
1745 :
1746 107 : if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) {
1747 81 : if (Node* receiver = GetStringWitness(node)) {
1748 81 : if (!index_type->Is(Type::Unsigned32())) {
1749 : // Map -0 and NaN to 0 (as per ToInteger), and the values in
1750 : // the [-2^31,-1] range to the [2^31,2^32-1] range, which will
1751 : // be considered out-of-bounds as well, because of the maximal
1752 : // String length limit in V8.
1753 : STATIC_ASSERT(String::kMaxLength <= kMaxInt);
1754 4 : index = graph()->NewNode(simplified()->NumberToUint32(), index);
1755 : }
1756 :
1757 : // Determine the {receiver} length.
1758 : Node* receiver_length = effect = graph()->NewNode(
1759 : simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
1760 243 : effect, control);
1761 :
1762 : // Check if {index} is less than {receiver} length.
1763 : Node* check = graph()->NewNode(simplified()->NumberLessThan(), index,
1764 81 : receiver_length);
1765 : Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1766 81 : check, control);
1767 :
1768 : // Return the character from the {receiver} as single character string.
1769 81 : Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1770 : Node* vtrue = graph()->NewNode(simplified()->StringCharAt(), receiver,
1771 81 : index, if_true);
1772 :
1773 : // Return the empty string otherwise.
1774 81 : Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1775 81 : Node* vfalse = jsgraph()->EmptyStringConstant();
1776 :
1777 81 : control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1778 : Node* value =
1779 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
1780 81 : vtrue, vfalse, control);
1781 :
1782 81 : ReplaceWithValue(node, value, effect, control);
1783 : return Replace(value);
1784 : }
1785 : }
1786 : }
1787 :
1788 : return NoChange();
1789 : }
1790 :
1791 : // ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
1792 558 : Reduction JSBuiltinReducer::ReduceStringCharCodeAt(Node* node) {
1793 : // We need at least target, receiver and index parameters.
1794 618 : if (node->op()->ValueInputCount() >= 3) {
1795 309 : Node* index = NodeProperties::GetValueInput(node, 2);
1796 : Type* index_type = NodeProperties::GetType(index);
1797 309 : Node* effect = NodeProperties::GetEffectInput(node);
1798 309 : Node* control = NodeProperties::GetControlInput(node);
1799 :
1800 309 : if (index_type->Is(Type::Integral32OrMinusZeroOrNaN())) {
1801 252 : if (Node* receiver = GetStringWitness(node)) {
1802 249 : if (!index_type->Is(Type::Unsigned32())) {
1803 : // Map -0 and NaN to 0 (as per ToInteger), and the values in
1804 : // the [-2^31,-1] range to the [2^31,2^32-1] range, which will
1805 : // be considered out-of-bounds as well, because of the maximal
1806 : // String length limit in V8.
1807 : STATIC_ASSERT(String::kMaxLength <= kMaxInt);
1808 4 : index = graph()->NewNode(simplified()->NumberToUint32(), index);
1809 : }
1810 :
1811 : // Determine the {receiver} length.
1812 : Node* receiver_length = effect = graph()->NewNode(
1813 : simplified()->LoadField(AccessBuilder::ForStringLength()), receiver,
1814 747 : effect, control);
1815 :
1816 : // Check if {index} is less than {receiver} length.
1817 : Node* check = graph()->NewNode(simplified()->NumberLessThan(), index,
1818 249 : receiver_length);
1819 : Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1820 249 : check, control);
1821 :
1822 : // Load the character from the {receiver}.
1823 249 : Node* if_true = graph()->NewNode(common()->IfTrue(), branch);
1824 : Node* vtrue = graph()->NewNode(simplified()->StringCharCodeAt(),
1825 249 : receiver, index, if_true);
1826 :
1827 : // Return NaN otherwise.
1828 249 : Node* if_false = graph()->NewNode(common()->IfFalse(), branch);
1829 249 : Node* vfalse = jsgraph()->NaNConstant();
1830 :
1831 249 : control = graph()->NewNode(common()->Merge(2), if_true, if_false);
1832 : Node* value =
1833 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
1834 249 : vtrue, vfalse, control);
1835 :
1836 249 : ReplaceWithValue(node, value, effect, control);
1837 : return Replace(value);
1838 : }
1839 : }
1840 : }
1841 :
1842 : return NoChange();
1843 : }
1844 :
1845 : // ES6 String.prototype.concat(...args)
1846 : // #sec-string.prototype.concat
1847 1 : Reduction JSBuiltinReducer::ReduceStringConcat(Node* node) {
1848 1 : if (Node* receiver = GetStringWitness(node)) {
1849 : JSCallReduction r(node);
1850 1 : if (r.InputsMatchOne(Type::PlainPrimitive())) {
1851 : // String.prototype.concat(lhs:string, rhs:plain-primitive)
1852 : // -> Call[StringAddStub](lhs, rhs)
1853 0 : StringAddFlags flags = r.InputsMatchOne(Type::String())
1854 : ? STRING_ADD_CHECK_NONE
1855 0 : : STRING_ADD_CONVERT_RIGHT;
1856 : // TODO(turbofan): Massage the FrameState of the {node} here once we
1857 : // have an artificial builtin frame type, so that it looks like the
1858 : // exception from StringAdd overflow came from String.prototype.concat
1859 : // builtin instead of the calling function.
1860 : Callable const callable =
1861 0 : CodeFactory::StringAdd(isolate(), flags, NOT_TENURED);
1862 : CallDescriptor const* const desc = Linkage::GetStubCallDescriptor(
1863 : isolate(), graph()->zone(), callable.descriptor(), 0,
1864 : CallDescriptor::kNeedsFrameState,
1865 0 : Operator::kNoDeopt | Operator::kNoWrite);
1866 0 : node->ReplaceInput(0, jsgraph()->HeapConstant(callable.code()));
1867 0 : node->ReplaceInput(1, receiver);
1868 0 : NodeProperties::ChangeOp(node, common()->Call(desc));
1869 : return Changed(node);
1870 : }
1871 : }
1872 :
1873 : return NoChange();
1874 : }
1875 :
1876 : // ES6 String.prototype.indexOf(searchString [, position])
1877 : // #sec-string.prototype.indexof
1878 458 : Reduction JSBuiltinReducer::ReduceStringIndexOf(Node* node) {
1879 : // We need at least target, receiver and search_string parameters.
1880 346 : if (node->op()->ValueInputCount() >= 3) {
1881 173 : Node* search_string = NodeProperties::GetValueInput(node, 2);
1882 : Type* search_string_type = NodeProperties::GetType(search_string);
1883 173 : Node* position = (node->op()->ValueInputCount() >= 4)
1884 : ? NodeProperties::GetValueInput(node, 3)
1885 285 : : jsgraph()->ZeroConstant();
1886 : Type* position_type = NodeProperties::GetType(position);
1887 :
1888 339 : if (search_string_type->Is(Type::String()) &&
1889 : position_type->Is(Type::SignedSmall())) {
1890 166 : if (Node* receiver = GetStringWitness(node)) {
1891 : RelaxEffectsAndControls(node);
1892 161 : node->ReplaceInput(0, receiver);
1893 161 : node->ReplaceInput(1, search_string);
1894 161 : node->ReplaceInput(2, position);
1895 161 : node->TrimInputCount(3);
1896 161 : NodeProperties::ChangeOp(node, simplified()->StringIndexOf());
1897 : return Changed(node);
1898 : }
1899 : }
1900 : }
1901 : return NoChange();
1902 : }
1903 :
1904 324 : Reduction JSBuiltinReducer::ReduceStringIterator(Node* node) {
1905 54 : if (Node* receiver = GetStringWitness(node)) {
1906 54 : Node* effect = NodeProperties::GetEffectInput(node);
1907 54 : Node* control = NodeProperties::GetControlInput(node);
1908 :
1909 : Node* map = jsgraph()->HeapConstant(
1910 54 : handle(native_context()->string_iterator_map(), isolate()));
1911 :
1912 : // allocate new iterator
1913 : effect = graph()->NewNode(
1914 54 : common()->BeginRegion(RegionObservability::kNotObservable), effect);
1915 : Node* value = effect = graph()->NewNode(
1916 : simplified()->Allocate(Type::OtherObject(), NOT_TENURED),
1917 108 : jsgraph()->Constant(JSStringIterator::kSize), effect, control);
1918 : effect = graph()->NewNode(simplified()->StoreField(AccessBuilder::ForMap()),
1919 162 : value, map, effect, control);
1920 : effect = graph()->NewNode(
1921 : simplified()->StoreField(AccessBuilder::ForJSObjectProperties()), value,
1922 162 : jsgraph()->EmptyFixedArrayConstant(), effect, control);
1923 : effect = graph()->NewNode(
1924 : simplified()->StoreField(AccessBuilder::ForJSObjectElements()), value,
1925 162 : jsgraph()->EmptyFixedArrayConstant(), effect, control);
1926 :
1927 : // attach the iterator to this string
1928 : effect = graph()->NewNode(
1929 : simplified()->StoreField(AccessBuilder::ForJSStringIteratorString()),
1930 162 : value, receiver, effect, control);
1931 : effect = graph()->NewNode(
1932 : simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()),
1933 162 : value, jsgraph()->SmiConstant(0), effect, control);
1934 :
1935 54 : value = effect = graph()->NewNode(common()->FinishRegion(), value, effect);
1936 :
1937 : // replace it
1938 54 : ReplaceWithValue(node, value, effect, control);
1939 : return Replace(value);
1940 : }
1941 : return NoChange();
1942 : }
1943 :
1944 450 : Reduction JSBuiltinReducer::ReduceStringIteratorNext(Node* node) {
1945 45 : Node* receiver = NodeProperties::GetValueInput(node, 1);
1946 45 : Node* effect = NodeProperties::GetEffectInput(node);
1947 45 : Node* control = NodeProperties::GetControlInput(node);
1948 45 : Node* context = NodeProperties::GetContextInput(node);
1949 45 : if (HasInstanceTypeWitness(receiver, effect, JS_STRING_ITERATOR_TYPE)) {
1950 : Node* string = effect = graph()->NewNode(
1951 : simplified()->LoadField(AccessBuilder::ForJSStringIteratorString()),
1952 135 : receiver, effect, control);
1953 : Node* index = effect = graph()->NewNode(
1954 : simplified()->LoadField(AccessBuilder::ForJSStringIteratorIndex()),
1955 135 : receiver, effect, control);
1956 : Node* length = effect = graph()->NewNode(
1957 : simplified()->LoadField(AccessBuilder::ForStringLength()), string,
1958 135 : effect, control);
1959 :
1960 : // branch0: if (index < length)
1961 : Node* check0 =
1962 45 : graph()->NewNode(simplified()->NumberLessThan(), index, length);
1963 : Node* branch0 =
1964 45 : graph()->NewNode(common()->Branch(BranchHint::kTrue), check0, control);
1965 :
1966 : Node* etrue0 = effect;
1967 45 : Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
1968 : Node* done_true;
1969 : Node* vtrue0;
1970 : {
1971 45 : done_true = jsgraph()->FalseConstant();
1972 : Node* lead = graph()->NewNode(simplified()->StringCharCodeAt(), string,
1973 45 : index, if_true0);
1974 :
1975 : // branch1: if ((lead & 0xFC00) === 0xD800)
1976 : Node* check1 =
1977 : graph()->NewNode(simplified()->NumberEqual(),
1978 : graph()->NewNode(simplified()->NumberBitwiseAnd(),
1979 : lead, jsgraph()->Constant(0xFC00)),
1980 180 : jsgraph()->Constant(0xD800));
1981 : Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
1982 45 : check1, if_true0);
1983 45 : Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
1984 : Node* vtrue1;
1985 : {
1986 : Node* next_index = graph()->NewNode(simplified()->NumberAdd(), index,
1987 90 : jsgraph()->OneConstant());
1988 : // branch2: if ((index + 1) < length)
1989 : Node* check2 = graph()->NewNode(simplified()->NumberLessThan(),
1990 45 : next_index, length);
1991 : Node* branch2 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
1992 45 : check2, if_true1);
1993 45 : Node* if_true2 = graph()->NewNode(common()->IfTrue(), branch2);
1994 : Node* vtrue2;
1995 : {
1996 : Node* trail = graph()->NewNode(simplified()->StringCharCodeAt(),
1997 45 : string, next_index, if_true2);
1998 : // branch3: if ((trail & 0xFC00) === 0xDC00)
1999 : Node* check3 = graph()->NewNode(
2000 : simplified()->NumberEqual(),
2001 : graph()->NewNode(simplified()->NumberBitwiseAnd(), trail,
2002 : jsgraph()->Constant(0xFC00)),
2003 180 : jsgraph()->Constant(0xDC00));
2004 : Node* branch3 = graph()->NewNode(common()->Branch(BranchHint::kTrue),
2005 45 : check3, if_true2);
2006 45 : Node* if_true3 = graph()->NewNode(common()->IfTrue(), branch3);
2007 : Node* vtrue3;
2008 : {
2009 : vtrue3 = graph()->NewNode(
2010 : simplified()->NumberBitwiseOr(),
2011 : // Need to swap the order for big-endian platforms
2012 : #if V8_TARGET_BIG_ENDIAN
2013 : graph()->NewNode(simplified()->NumberShiftLeft(), lead,
2014 : jsgraph()->Constant(16)),
2015 : trail);
2016 : #else
2017 : graph()->NewNode(simplified()->NumberShiftLeft(), trail,
2018 : jsgraph()->Constant(16)),
2019 135 : lead);
2020 : #endif
2021 : }
2022 :
2023 45 : Node* if_false3 = graph()->NewNode(common()->IfFalse(), branch3);
2024 : Node* vfalse3 = lead;
2025 45 : if_true2 = graph()->NewNode(common()->Merge(2), if_true3, if_false3);
2026 : vtrue2 =
2027 : graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
2028 45 : vtrue3, vfalse3, if_true2);
2029 : }
2030 :
2031 45 : Node* if_false2 = graph()->NewNode(common()->IfFalse(), branch2);
2032 : Node* vfalse2 = lead;
2033 45 : if_true1 = graph()->NewNode(common()->Merge(2), if_true2, if_false2);
2034 : vtrue1 =
2035 : graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
2036 45 : vtrue2, vfalse2, if_true1);
2037 : }
2038 :
2039 45 : Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
2040 : Node* vfalse1 = lead;
2041 45 : if_true0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
2042 : vtrue0 =
2043 : graph()->NewNode(common()->Phi(MachineRepresentation::kWord32, 2),
2044 45 : vtrue1, vfalse1, if_true0);
2045 : vtrue0 = graph()->NewNode(
2046 45 : simplified()->StringFromCodePoint(UnicodeEncoding::UTF16), vtrue0);
2047 :
2048 : // Update iterator.[[NextIndex]]
2049 : Node* char_length = etrue0 = graph()->NewNode(
2050 : simplified()->LoadField(AccessBuilder::ForStringLength()), vtrue0,
2051 135 : etrue0, if_true0);
2052 45 : index = graph()->NewNode(simplified()->NumberAdd(), index, char_length);
2053 : etrue0 = graph()->NewNode(
2054 : simplified()->StoreField(AccessBuilder::ForJSStringIteratorIndex()),
2055 135 : receiver, index, etrue0, if_true0);
2056 : }
2057 :
2058 45 : Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
2059 : Node* done_false;
2060 : Node* vfalse0;
2061 : {
2062 45 : vfalse0 = jsgraph()->UndefinedConstant();
2063 45 : done_false = jsgraph()->TrueConstant();
2064 : }
2065 :
2066 45 : control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
2067 45 : effect = graph()->NewNode(common()->EffectPhi(2), etrue0, effect, control);
2068 : Node* value =
2069 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2070 45 : vtrue0, vfalse0, control);
2071 : Node* done =
2072 : graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
2073 45 : done_true, done_false, control);
2074 :
2075 : value = effect = graph()->NewNode(javascript()->CreateIterResultObject(),
2076 45 : value, done, context, effect);
2077 :
2078 45 : ReplaceWithValue(node, value, effect, control);
2079 : return Replace(value);
2080 : }
2081 : return NoChange();
2082 : }
2083 :
2084 86 : Reduction JSBuiltinReducer::ReduceArrayBufferViewAccessor(
2085 86 : Node* node, InstanceType instance_type, FieldAccess const& access) {
2086 86 : Node* receiver = NodeProperties::GetValueInput(node, 1);
2087 86 : Node* effect = NodeProperties::GetEffectInput(node);
2088 86 : Node* control = NodeProperties::GetControlInput(node);
2089 86 : if (HasInstanceTypeWitness(receiver, effect, instance_type)) {
2090 : // Load the {receiver}s field.
2091 : Node* value = effect = graph()->NewNode(simplified()->LoadField(access),
2092 86 : receiver, effect, control);
2093 :
2094 : // See if we can skip the neutering check.
2095 86 : if (isolate()->IsArrayBufferNeuteringIntact()) {
2096 : // Add a code dependency so we are deoptimized in case an ArrayBuffer
2097 : // gets neutered.
2098 : dependencies()->AssumePropertyCell(
2099 : factory()->array_buffer_neutering_protector());
2100 : } else {
2101 : // Check if the {receiver}s buffer was neutered.
2102 : Node* receiver_buffer = effect = graph()->NewNode(
2103 : simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()),
2104 15 : receiver, effect, control);
2105 : Node* check = effect =
2106 : graph()->NewNode(simplified()->ArrayBufferWasNeutered(),
2107 5 : receiver_buffer, effect, control);
2108 :
2109 : // Default to zero if the {receiver}s buffer was neutered.
2110 : value = graph()->NewNode(
2111 : common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse),
2112 10 : check, jsgraph()->ZeroConstant(), value);
2113 : }
2114 :
2115 86 : ReplaceWithValue(node, value, effect, control);
2116 : return Replace(value);
2117 : }
2118 : return NoChange();
2119 : }
2120 :
2121 31952288 : Reduction JSBuiltinReducer::Reduce(Node* node) {
2122 : Reduction reduction = NoChange();
2123 : JSCallReduction r(node);
2124 :
2125 : // Dispatch according to the BuiltinFunctionId if present.
2126 31952288 : if (!r.HasBuiltinFunctionId()) return NoChange();
2127 81877 : switch (r.GetBuiltinFunctionId()) {
2128 : case kArrayEntries:
2129 7 : return ReduceArrayIterator(node, IterationKind::kEntries);
2130 : case kArrayKeys:
2131 13 : return ReduceArrayIterator(node, IterationKind::kKeys);
2132 : case kArrayValues:
2133 1492 : return ReduceArrayIterator(node, IterationKind::kValues);
2134 : case kArrayIteratorNext:
2135 1562 : return ReduceArrayIteratorNext(node);
2136 : case kArrayIsArray:
2137 21 : return ReduceArrayIsArray(node);
2138 : case kArrayPop:
2139 153 : return ReduceArrayPop(node);
2140 : case kArrayPush:
2141 1642 : return ReduceArrayPush(node);
2142 : case kDateNow:
2143 8 : return ReduceDateNow(node);
2144 : case kDateGetTime:
2145 4 : return ReduceDateGetTime(node);
2146 : case kGlobalIsFinite:
2147 51 : reduction = ReduceGlobalIsFinite(node);
2148 51 : break;
2149 : case kGlobalIsNaN:
2150 21097 : reduction = ReduceGlobalIsNaN(node);
2151 21097 : break;
2152 : case kMathAbs:
2153 401 : reduction = ReduceMathAbs(node);
2154 401 : break;
2155 : case kMathAcos:
2156 14 : reduction = ReduceMathAcos(node);
2157 14 : break;
2158 : case kMathAcosh:
2159 13 : reduction = ReduceMathAcosh(node);
2160 13 : break;
2161 : case kMathAsin:
2162 14 : reduction = ReduceMathAsin(node);
2163 14 : break;
2164 : case kMathAsinh:
2165 13 : reduction = ReduceMathAsinh(node);
2166 13 : break;
2167 : case kMathAtan:
2168 14 : reduction = ReduceMathAtan(node);
2169 14 : break;
2170 : case kMathAtanh:
2171 13 : reduction = ReduceMathAtanh(node);
2172 13 : break;
2173 : case kMathAtan2:
2174 160 : reduction = ReduceMathAtan2(node);
2175 160 : break;
2176 : case kMathCbrt:
2177 0 : reduction = ReduceMathCbrt(node);
2178 0 : break;
2179 : case kMathCeil:
2180 7636 : reduction = ReduceMathCeil(node);
2181 7636 : break;
2182 : case kMathClz32:
2183 59 : reduction = ReduceMathClz32(node);
2184 59 : break;
2185 : case kMathCos:
2186 78 : reduction = ReduceMathCos(node);
2187 78 : break;
2188 : case kMathCosh:
2189 22 : reduction = ReduceMathCosh(node);
2190 22 : break;
2191 : case kMathExp:
2192 57 : reduction = ReduceMathExp(node);
2193 57 : break;
2194 : case kMathExpm1:
2195 0 : reduction = ReduceMathExpm1(node);
2196 0 : break;
2197 : case kMathFloor:
2198 28920 : reduction = ReduceMathFloor(node);
2199 28920 : break;
2200 : case kMathFround:
2201 1113 : reduction = ReduceMathFround(node);
2202 1113 : break;
2203 : case kMathImul:
2204 152 : reduction = ReduceMathImul(node);
2205 152 : break;
2206 : case kMathLog:
2207 151 : reduction = ReduceMathLog(node);
2208 151 : break;
2209 : case kMathLog1p:
2210 13 : reduction = ReduceMathLog1p(node);
2211 13 : break;
2212 : case kMathLog10:
2213 0 : reduction = ReduceMathLog10(node);
2214 0 : break;
2215 : case kMathLog2:
2216 0 : reduction = ReduceMathLog2(node);
2217 0 : break;
2218 : case kMathMax:
2219 337 : reduction = ReduceMathMax(node);
2220 337 : break;
2221 : case kMathMin:
2222 306 : reduction = ReduceMathMin(node);
2223 306 : break;
2224 : case kMathPow:
2225 1181 : reduction = ReduceMathPow(node);
2226 1181 : break;
2227 : case kMathRound:
2228 1587 : reduction = ReduceMathRound(node);
2229 1587 : break;
2230 : case kMathSign:
2231 34 : reduction = ReduceMathSign(node);
2232 34 : break;
2233 : case kMathSin:
2234 102 : reduction = ReduceMathSin(node);
2235 102 : break;
2236 : case kMathSinh:
2237 22 : reduction = ReduceMathSinh(node);
2238 22 : break;
2239 : case kMathSqrt:
2240 103 : reduction = ReduceMathSqrt(node);
2241 103 : break;
2242 : case kMathTan:
2243 57 : reduction = ReduceMathTan(node);
2244 57 : break;
2245 : case kMathTanh:
2246 22 : reduction = ReduceMathTanh(node);
2247 22 : break;
2248 : case kMathTrunc:
2249 7260 : reduction = ReduceMathTrunc(node);
2250 7260 : break;
2251 : case kNumberIsFinite:
2252 19 : reduction = ReduceNumberIsFinite(node);
2253 19 : break;
2254 : case kNumberIsInteger:
2255 19 : reduction = ReduceNumberIsInteger(node);
2256 19 : break;
2257 : case kNumberIsNaN:
2258 67 : reduction = ReduceNumberIsNaN(node);
2259 67 : break;
2260 : case kNumberIsSafeInteger:
2261 14 : reduction = ReduceNumberIsSafeInteger(node);
2262 14 : break;
2263 : case kNumberParseInt:
2264 460 : reduction = ReduceNumberParseInt(node);
2265 460 : break;
2266 : case kObjectCreate:
2267 21 : reduction = ReduceObjectCreate(node);
2268 21 : break;
2269 : case kStringFromCharCode:
2270 732 : reduction = ReduceStringFromCharCode(node);
2271 732 : break;
2272 : case kStringCharAt:
2273 107 : return ReduceStringCharAt(node);
2274 : case kStringCharCodeAt:
2275 309 : return ReduceStringCharCodeAt(node);
2276 : case kStringConcat:
2277 1 : return ReduceStringConcat(node);
2278 : case kStringIndexOf:
2279 173 : return ReduceStringIndexOf(node);
2280 : case kStringIterator:
2281 54 : return ReduceStringIterator(node);
2282 : case kStringIteratorNext:
2283 45 : return ReduceStringIteratorNext(node);
2284 : case kDataViewByteLength:
2285 : return ReduceArrayBufferViewAccessor(
2286 : node, JS_DATA_VIEW_TYPE,
2287 0 : AccessBuilder::ForJSArrayBufferViewByteLength());
2288 : case kDataViewByteOffset:
2289 : return ReduceArrayBufferViewAccessor(
2290 : node, JS_DATA_VIEW_TYPE,
2291 0 : AccessBuilder::ForJSArrayBufferViewByteOffset());
2292 : case kTypedArrayByteLength:
2293 : return ReduceArrayBufferViewAccessor(
2294 : node, JS_TYPED_ARRAY_TYPE,
2295 0 : AccessBuilder::ForJSArrayBufferViewByteLength());
2296 : case kTypedArrayByteOffset:
2297 : return ReduceArrayBufferViewAccessor(
2298 : node, JS_TYPED_ARRAY_TYPE,
2299 0 : AccessBuilder::ForJSArrayBufferViewByteOffset());
2300 : case kTypedArrayLength:
2301 : return ReduceArrayBufferViewAccessor(
2302 86 : node, JS_TYPED_ARRAY_TYPE, AccessBuilder::ForJSTypedArrayLength());
2303 : case kTypedArrayEntries:
2304 7 : return ReduceTypedArrayIterator(node, IterationKind::kEntries);
2305 : case kTypedArrayKeys:
2306 0 : return ReduceTypedArrayIterator(node, IterationKind::kKeys);
2307 : case kTypedArrayValues:
2308 7 : return ReduceTypedArrayIterator(node, IterationKind::kValues);
2309 : default:
2310 : break;
2311 : }
2312 :
2313 : // Replace builtin call assuming replacement nodes are pure values that don't
2314 : // produce an effect. Replaces {node} with {reduction} and relaxes effects.
2315 76186 : if (reduction.Changed()) ReplaceWithValue(node, reduction.replacement());
2316 :
2317 76186 : return reduction;
2318 : }
2319 :
2320 31285 : Node* JSBuiltinReducer::ToNumber(Node* input) {
2321 : Type* input_type = NodeProperties::GetType(input);
2322 31285 : if (input_type->Is(Type::Number())) return input;
2323 2950 : return graph()->NewNode(simplified()->PlainPrimitiveToNumber(), input);
2324 : }
2325 :
2326 144 : Node* JSBuiltinReducer::ToUint32(Node* input) {
2327 144 : input = ToNumber(input);
2328 : Type* input_type = NodeProperties::GetType(input);
2329 144 : if (input_type->Is(Type::Unsigned32())) return input;
2330 212 : return graph()->NewNode(simplified()->NumberToUint32(), input);
2331 : }
2332 :
2333 101003 : Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); }
2334 :
2335 0 : Factory* JSBuiltinReducer::factory() const { return isolate()->factory(); }
2336 :
2337 3435 : Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); }
2338 :
2339 :
2340 24511 : CommonOperatorBuilder* JSBuiltinReducer::common() const {
2341 24511 : return jsgraph()->common();
2342 : }
2343 :
2344 :
2345 75182 : SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const {
2346 75189 : return jsgraph()->simplified();
2347 : }
2348 :
2349 1472 : JSOperatorBuilder* JSBuiltinReducer::javascript() const {
2350 1472 : return jsgraph()->javascript();
2351 : }
2352 :
2353 : } // namespace compiler
2354 : } // namespace internal
2355 : } // namespace v8
|