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: 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. 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. 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 46 bool ReceiverMatches(Type* type) { 47 return NodeProperties::GetType(receiver())->Is(type); 48 } 49 50 // Determines whether the call takes zero inputs. 51 bool InputsMatchZero() { return GetJSCallArity() == 0; } 52 53 // Determines whether the call takes one input of the given type. 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. 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. 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 76 Node* receiver() { return NodeProperties::GetValueInput(node_, 1); } 77 Node* left() { return GetJSCallInput(0); } 78 Node* right() { return GetJSCallInput(1); } 79 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 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 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? 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 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. 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. 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 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 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 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 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 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 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 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 ( ) 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 ( ) 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 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 ( ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 ) 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 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 ) 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 ) 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 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 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 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 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 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 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 2034 Graph* JSBuiltinReducer::graph() const { return jsgraph()->graph(); } 2035 2036 Factory* JSBuiltinReducer::factory() const { return isolate()->factory(); } 2037 2038 Isolate* JSBuiltinReducer::isolate() const { return jsgraph()->isolate(); } 2039 2040 2041 CommonOperatorBuilder* JSBuiltinReducer::common() const { 2042 return jsgraph()->common(); 2043 } 2044 2045 2046 SimplifiedOperatorBuilder* JSBuiltinReducer::simplified() const { 2047 return jsgraph()->simplified(); 2048 } 2049 2050 JSOperatorBuilder* JSBuiltinReducer::javascript() const { 2051 return jsgraph()->javascript(); 2052 } 2053 2054 } // namespace compiler 2055 } // namespace internal 2056 } // namespace v8 2057