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