1 // Copyright 2015 the V8 project authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "src/compiler/js-native-context-specialization.h" 6 7 #include "src/accessors.h" 8 #include "src/code-factory.h" 9 #include "src/compilation-dependencies.h" 10 #include "src/compiler/access-builder.h" 11 #include "src/compiler/access-info.h" 12 #include "src/compiler/js-graph.h" 13 #include "src/compiler/js-operator.h" 14 #include "src/compiler/linkage.h" 15 #include "src/compiler/node-matchers.h" 16 #include "src/compiler/type-cache.h" 17 #include "src/feedback-vector.h" 18 #include "src/field-index-inl.h" 19 #include "src/isolate-inl.h" 20 21 namespace v8 { 22 namespace internal { 23 namespace compiler { 24 25 namespace { 26 27 bool HasNumberMaps(MapList const& maps) { 28 for (auto map : maps) { 29 if (map->instance_type() == HEAP_NUMBER_TYPE) return true; 30 } 31 return false; 32 } 33 34 bool HasOnlyJSArrayMaps(MapList const& maps) { 35 for (auto map : maps) { 36 if (!map->IsJSArrayMap()) return false; 37 } 38 return true; 39 } 40 41 bool HasOnlyNumberMaps(MapList const& maps) { 42 for (auto map : maps) { 43 if (map->instance_type() != HEAP_NUMBER_TYPE) return false; 44 } 45 return true; 46 } 47 48 template <typename T> 49 bool HasOnlyStringMaps(T const& maps) { 50 for (auto map : maps) { 51 if (!map->IsStringMap()) return false; 52 } 53 return true; 54 } 55 56 } // namespace 57 58 struct JSNativeContextSpecialization::ScriptContextTableLookupResult { 59 Handle<Context> context; 60 bool immutable; 61 int index; 62 }; 63 64 JSNativeContextSpecialization::JSNativeContextSpecialization( 65 Editor* editor, JSGraph* jsgraph, Flags flags, 66 Handle<Context> native_context, CompilationDependencies* dependencies, 67 Zone* zone) 68 : AdvancedReducer(editor), 69 jsgraph_(jsgraph), 70 flags_(flags), 71 global_object_(native_context->global_object()), 72 global_proxy_(JSGlobalProxy::cast(native_context->global_proxy())), 73 native_context_(native_context), 74 dependencies_(dependencies), 75 zone_(zone), 76 type_cache_(TypeCache::Get()) {} 77 78 Reduction JSNativeContextSpecialization::Reduce(Node* node) { 79 switch (node->opcode()) { 80 case IrOpcode::kJSAdd: 81 return ReduceJSAdd(node); 82 case IrOpcode::kJSGetSuperConstructor: 83 return ReduceJSGetSuperConstructor(node); 84 case IrOpcode::kJSInstanceOf: 85 return ReduceJSInstanceOf(node); 86 case IrOpcode::kJSOrdinaryHasInstance: 87 return ReduceJSOrdinaryHasInstance(node); 88 case IrOpcode::kJSLoadContext: 89 return ReduceJSLoadContext(node); 90 case IrOpcode::kJSLoadGlobal: 91 return ReduceJSLoadGlobal(node); 92 case IrOpcode::kJSStoreGlobal: 93 return ReduceJSStoreGlobal(node); 94 case IrOpcode::kJSLoadNamed: 95 return ReduceJSLoadNamed(node); 96 case IrOpcode::kJSStoreNamed: 97 return ReduceJSStoreNamed(node); 98 case IrOpcode::kJSLoadProperty: 99 return ReduceJSLoadProperty(node); 100 case IrOpcode::kJSStoreProperty: 101 return ReduceJSStoreProperty(node); 102 case IrOpcode::kJSStoreNamedOwn: 103 return ReduceJSStoreNamedOwn(node); 104 case IrOpcode::kJSStoreDataPropertyInLiteral: 105 return ReduceJSStoreDataPropertyInLiteral(node); 106 default: 107 break; 108 } 109 return NoChange(); 110 } 111 112 Reduction JSNativeContextSpecialization::ReduceJSAdd(Node* node) { 113 // TODO(turbofan): This has to run together with the inlining and 114 // native context specialization to be able to leverage the string 115 // constant-folding for optimizing property access, but we should 116 // nevertheless find a better home for this at some point. 117 DCHECK_EQ(IrOpcode::kJSAdd, node->opcode()); 118 119 // Constant-fold string concatenation. 120 HeapObjectBinopMatcher m(node); 121 if (m.left().HasValue() && m.left().Value()->IsString() && 122 m.right().HasValue() && m.right().Value()->IsString()) { 123 Handle<String> left = Handle<String>::cast(m.left().Value()); 124 Handle<String> right = Handle<String>::cast(m.right().Value()); 125 if (left->length() + right->length() <= String::kMaxLength) { 126 Handle<String> result = 127 factory()->NewConsString(left, right).ToHandleChecked(); 128 Node* value = jsgraph()->HeapConstant(result); 129 ReplaceWithValue(node, value); 130 return Replace(value); 131 } 132 } 133 return NoChange(); 134 } 135 136 Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor( 137 Node* node) { 138 DCHECK_EQ(IrOpcode::kJSGetSuperConstructor, node->opcode()); 139 Node* constructor = NodeProperties::GetValueInput(node, 0); 140 141 // If deoptimization is disabled, we cannot optimize. 142 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 143 144 // Check if the input is a known JSFunction. 145 HeapObjectMatcher m(constructor); 146 if (!m.HasValue()) return NoChange(); 147 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 148 Handle<Map> function_map(function->map(), isolate()); 149 Handle<Object> function_prototype(function_map->prototype(), isolate()); 150 151 // We can constant-fold the super constructor access if the 152 // {function}s map is stable, i.e. we can use a code dependency 153 // to guard against [[Prototype]] changes of {function}. 154 if (function_map->is_stable()) { 155 Node* value = jsgraph()->Constant(function_prototype); 156 dependencies()->AssumeMapStable(function_map); 157 if (function_prototype->IsConstructor()) { 158 ReplaceWithValue(node, value); 159 return Replace(value); 160 } else { 161 node->InsertInput(graph()->zone(), 0, value); 162 NodeProperties::ChangeOp( 163 node, javascript()->CallRuntime(Runtime::kThrowNotSuperConstructor)); 164 return Changed(node); 165 } 166 } 167 168 return NoChange(); 169 } 170 171 Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) { 172 DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode()); 173 Node* object = NodeProperties::GetValueInput(node, 0); 174 Node* constructor = NodeProperties::GetValueInput(node, 1); 175 Node* context = NodeProperties::GetContextInput(node); 176 Node* effect = NodeProperties::GetEffectInput(node); 177 Node* control = NodeProperties::GetControlInput(node); 178 179 // If deoptimization is disabled, we cannot optimize. 180 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 181 182 // Check if the right hand side is a known {receiver}. 183 HeapObjectMatcher m(constructor); 184 if (!m.HasValue() || !m.Value()->IsJSObject()) return NoChange(); 185 Handle<JSObject> receiver = Handle<JSObject>::cast(m.Value()); 186 Handle<Map> receiver_map(receiver->map(), isolate()); 187 188 // Compute property access info for @@hasInstance on {receiver}. 189 PropertyAccessInfo access_info; 190 AccessInfoFactory access_info_factory(dependencies(), native_context(), 191 graph()->zone()); 192 if (!access_info_factory.ComputePropertyAccessInfo( 193 receiver_map, factory()->has_instance_symbol(), AccessMode::kLoad, 194 &access_info)) { 195 return NoChange(); 196 } 197 198 if (access_info.IsNotFound()) { 199 // If there's no @@hasInstance handler, the OrdinaryHasInstance operation 200 // takes over, but that requires the {receiver} to be callable. 201 if (receiver->IsCallable()) { 202 // Determine actual holder and perform prototype chain checks. 203 Handle<JSObject> holder; 204 if (access_info.holder().ToHandle(&holder)) { 205 AssumePrototypesStable(access_info.receiver_maps(), holder); 206 } 207 208 // Monomorphic property access. 209 effect = BuildCheckMaps(constructor, effect, control, 210 access_info.receiver_maps()); 211 212 // Lower to OrdinaryHasInstance(C, O). 213 NodeProperties::ReplaceValueInput(node, constructor, 0); 214 NodeProperties::ReplaceValueInput(node, object, 1); 215 NodeProperties::ReplaceEffectInput(node, effect); 216 NodeProperties::ChangeOp(node, javascript()->OrdinaryHasInstance()); 217 Reduction const reduction = ReduceJSOrdinaryHasInstance(node); 218 return reduction.Changed() ? reduction : Changed(node); 219 } 220 } else if (access_info.IsDataConstant()) { 221 DCHECK(access_info.constant()->IsCallable()); 222 223 // Determine actual holder and perform prototype chain checks. 224 Handle<JSObject> holder; 225 if (access_info.holder().ToHandle(&holder)) { 226 AssumePrototypesStable(access_info.receiver_maps(), holder); 227 } 228 229 // Monomorphic property access. 230 effect = BuildCheckMaps(constructor, effect, control, 231 access_info.receiver_maps()); 232 233 // Call the @@hasInstance handler. 234 Node* target = jsgraph()->Constant(access_info.constant()); 235 node->InsertInput(graph()->zone(), 0, target); 236 node->ReplaceInput(1, constructor); 237 node->ReplaceInput(2, object); 238 node->ReplaceInput(5, effect); 239 NodeProperties::ChangeOp( 240 node, 241 javascript()->Call(3, 0.0f, VectorSlotPair(), 242 ConvertReceiverMode::kNotNullOrUndefined)); 243 244 // Rewire the value uses of {node} to ToBoolean conversion of the result. 245 Node* value = graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny), 246 node, context); 247 for (Edge edge : node->use_edges()) { 248 if (NodeProperties::IsValueEdge(edge) && edge.from() != value) { 249 edge.UpdateTo(value); 250 Revisit(edge.from()); 251 } 252 } 253 return Changed(node); 254 } 255 256 return NoChange(); 257 } 258 259 Reduction JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance( 260 Node* node) { 261 DCHECK_EQ(IrOpcode::kJSOrdinaryHasInstance, node->opcode()); 262 Node* constructor = NodeProperties::GetValueInput(node, 0); 263 Node* object = NodeProperties::GetValueInput(node, 1); 264 265 // Check if the {constructor} is a JSBoundFunction. 266 HeapObjectMatcher m(constructor); 267 if (m.HasValue() && m.Value()->IsJSBoundFunction()) { 268 // OrdinaryHasInstance on bound functions turns into a recursive 269 // invocation of the instanceof operator again. 270 // ES6 section 7.3.19 OrdinaryHasInstance (C, O) step 2. 271 Handle<JSBoundFunction> function = Handle<JSBoundFunction>::cast(m.Value()); 272 Handle<JSReceiver> bound_target_function(function->bound_target_function()); 273 NodeProperties::ReplaceValueInput(node, object, 0); 274 NodeProperties::ReplaceValueInput( 275 node, jsgraph()->HeapConstant(bound_target_function), 1); 276 NodeProperties::ChangeOp(node, javascript()->InstanceOf()); 277 Reduction const reduction = ReduceJSInstanceOf(node); 278 return reduction.Changed() ? reduction : Changed(node); 279 } 280 281 return NoChange(); 282 } 283 284 Reduction JSNativeContextSpecialization::ReduceJSLoadContext(Node* node) { 285 DCHECK_EQ(IrOpcode::kJSLoadContext, node->opcode()); 286 ContextAccess const& access = ContextAccessOf(node->op()); 287 // Specialize JSLoadContext(NATIVE_CONTEXT_INDEX) to the known native 288 // context (if any), so we can constant-fold those fields, which is 289 // safe, since the NATIVE_CONTEXT_INDEX slot is always immutable. 290 if (access.index() == Context::NATIVE_CONTEXT_INDEX) { 291 Node* value = jsgraph()->HeapConstant(native_context()); 292 ReplaceWithValue(node, value); 293 return Replace(value); 294 } 295 return NoChange(); 296 } 297 298 namespace { 299 300 FieldAccess ForPropertyCellValue(MachineRepresentation representation, 301 Type* type, MaybeHandle<Map> map, 302 Handle<Name> name) { 303 WriteBarrierKind kind = kFullWriteBarrier; 304 if (representation == MachineRepresentation::kTaggedSigned) { 305 kind = kNoWriteBarrier; 306 } else if (representation == MachineRepresentation::kTaggedPointer) { 307 kind = kPointerWriteBarrier; 308 } 309 MachineType r = MachineType::TypeForRepresentation(representation); 310 FieldAccess access = { 311 kTaggedBase, PropertyCell::kValueOffset, name, map, type, r, kind}; 312 return access; 313 } 314 315 } // namespace 316 317 Reduction JSNativeContextSpecialization::ReduceGlobalAccess( 318 Node* node, Node* receiver, Node* value, Handle<Name> name, 319 AccessMode access_mode, Node* index) { 320 Node* effect = NodeProperties::GetEffectInput(node); 321 Node* control = NodeProperties::GetControlInput(node); 322 323 // Lookup on the global object. We only deal with own data properties 324 // of the global object here (represented as PropertyCell). 325 LookupIterator it(global_object(), name, LookupIterator::OWN); 326 it.TryLookupCachedProperty(); 327 if (it.state() != LookupIterator::DATA) return NoChange(); 328 if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange(); 329 Handle<PropertyCell> property_cell = it.GetPropertyCell(); 330 PropertyDetails property_details = property_cell->property_details(); 331 Handle<Object> property_cell_value(property_cell->value(), isolate()); 332 PropertyCellType property_cell_type = property_details.cell_type(); 333 334 // We have additional constraints for stores. 335 if (access_mode == AccessMode::kStore) { 336 if (property_details.IsReadOnly()) { 337 // Don't even bother trying to lower stores to read-only data properties. 338 return NoChange(); 339 } else if (property_cell_type == PropertyCellType::kUndefined) { 340 // There's no fast-path for dealing with undefined property cells. 341 return NoChange(); 342 } else if (property_cell_type == PropertyCellType::kConstantType) { 343 // There's also no fast-path to store to a global cell which pretended 344 // to be stable, but is no longer stable now. 345 if (property_cell_value->IsHeapObject() && 346 !Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) { 347 return NoChange(); 348 } 349 } 350 } 351 352 // Ensure that {index} matches the specified {name} (if {index} is given). 353 if (index != nullptr) { 354 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), index, 355 jsgraph()->HeapConstant(name)); 356 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); 357 } 358 359 // Check if we have a {receiver} to validate. If so, we need to check that 360 // the {receiver} is actually the JSGlobalProxy for the native context that 361 // we are specializing to. 362 if (receiver != nullptr) { 363 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), receiver, 364 jsgraph()->HeapConstant(global_proxy())); 365 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); 366 } 367 368 if (access_mode == AccessMode::kLoad) { 369 // Load from non-configurable, read-only data property on the global 370 // object can be constant-folded, even without deoptimization support. 371 if (!property_details.IsConfigurable() && property_details.IsReadOnly()) { 372 value = jsgraph()->Constant(property_cell_value); 373 } else { 374 // Record a code dependency on the cell if we can benefit from the 375 // additional feedback, or the global property is configurable (i.e. 376 // can be deleted or reconfigured to an accessor property). 377 if (property_details.cell_type() != PropertyCellType::kMutable || 378 property_details.IsConfigurable()) { 379 dependencies()->AssumePropertyCell(property_cell); 380 } 381 382 // Load from constant/undefined global property can be constant-folded. 383 if (property_details.cell_type() == PropertyCellType::kConstant || 384 property_details.cell_type() == PropertyCellType::kUndefined) { 385 value = jsgraph()->Constant(property_cell_value); 386 } else { 387 // Load from constant type cell can benefit from type feedback. 388 MaybeHandle<Map> map; 389 Type* property_cell_value_type = Type::NonInternal(); 390 MachineRepresentation representation = MachineRepresentation::kTagged; 391 if (property_details.cell_type() == PropertyCellType::kConstantType) { 392 // Compute proper type based on the current value in the cell. 393 if (property_cell_value->IsSmi()) { 394 property_cell_value_type = Type::SignedSmall(); 395 representation = MachineRepresentation::kTaggedSigned; 396 } else if (property_cell_value->IsNumber()) { 397 property_cell_value_type = Type::Number(); 398 representation = MachineRepresentation::kTaggedPointer; 399 } else { 400 Handle<Map> property_cell_value_map( 401 Handle<HeapObject>::cast(property_cell_value)->map(), 402 isolate()); 403 property_cell_value_type = Type::For(property_cell_value_map); 404 representation = MachineRepresentation::kTaggedPointer; 405 406 // We can only use the property cell value map for map check 407 // elimination if it's stable, i.e. the HeapObject wasn't 408 // mutated without the cell state being updated. 409 if (property_cell_value_map->is_stable()) { 410 dependencies()->AssumeMapStable(property_cell_value_map); 411 map = property_cell_value_map; 412 } 413 } 414 } 415 value = effect = graph()->NewNode( 416 simplified()->LoadField(ForPropertyCellValue( 417 representation, property_cell_value_type, map, name)), 418 jsgraph()->HeapConstant(property_cell), effect, control); 419 } 420 } 421 } else { 422 DCHECK_EQ(AccessMode::kStore, access_mode); 423 DCHECK(!property_details.IsReadOnly()); 424 switch (property_details.cell_type()) { 425 case PropertyCellType::kUndefined: { 426 UNREACHABLE(); 427 break; 428 } 429 case PropertyCellType::kConstant: { 430 // Record a code dependency on the cell, and just deoptimize if the new 431 // value doesn't match the previous value stored inside the cell. 432 dependencies()->AssumePropertyCell(property_cell); 433 Node* check = 434 graph()->NewNode(simplified()->ReferenceEqual(), value, 435 jsgraph()->Constant(property_cell_value)); 436 effect = 437 graph()->NewNode(simplified()->CheckIf(), check, effect, control); 438 break; 439 } 440 case PropertyCellType::kConstantType: { 441 // Record a code dependency on the cell, and just deoptimize if the new 442 // values' type doesn't match the type of the previous value in the 443 // cell. 444 dependencies()->AssumePropertyCell(property_cell); 445 Type* property_cell_value_type; 446 MachineRepresentation representation = MachineRepresentation::kTagged; 447 if (property_cell_value->IsHeapObject()) { 448 // We cannot do anything if the {property_cell_value}s map is no 449 // longer stable. 450 Handle<Map> property_cell_value_map( 451 Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); 452 DCHECK(property_cell_value_map->is_stable()); 453 dependencies()->AssumeMapStable(property_cell_value_map); 454 455 // Check that the {value} is a HeapObject. 456 value = effect = graph()->NewNode(simplified()->CheckHeapObject(), 457 value, effect, control); 458 459 // Check {value} map agains the {property_cell} map. 460 effect = 461 graph()->NewNode(simplified()->CheckMaps( 462 CheckMapsFlag::kNone, 463 ZoneHandleSet<Map>(property_cell_value_map)), 464 value, effect, control); 465 property_cell_value_type = Type::OtherInternal(); 466 representation = MachineRepresentation::kTaggedPointer; 467 } else { 468 // Check that the {value} is a Smi. 469 value = effect = graph()->NewNode(simplified()->CheckSmi(), value, 470 effect, control); 471 property_cell_value_type = Type::SignedSmall(); 472 representation = MachineRepresentation::kTaggedSigned; 473 } 474 effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue( 475 representation, property_cell_value_type, 476 MaybeHandle<Map>(), name)), 477 jsgraph()->HeapConstant(property_cell), value, 478 effect, control); 479 break; 480 } 481 case PropertyCellType::kMutable: { 482 // Record a code dependency on the cell, and just deoptimize if the 483 // property ever becomes read-only. 484 dependencies()->AssumePropertyCell(property_cell); 485 effect = graph()->NewNode( 486 simplified()->StoreField(ForPropertyCellValue( 487 MachineRepresentation::kTagged, Type::NonInternal(), 488 MaybeHandle<Map>(), name)), 489 jsgraph()->HeapConstant(property_cell), value, effect, control); 490 break; 491 } 492 } 493 } 494 495 ReplaceWithValue(node, value, effect, control); 496 return Replace(value); 497 } 498 499 Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { 500 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); 501 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); 502 Node* effect = NodeProperties::GetEffectInput(node); 503 504 // Try to lookup the name on the script context table first (lexical scoping). 505 ScriptContextTableLookupResult result; 506 if (LookupInScriptContextTable(name, &result)) { 507 if (result.context->is_the_hole(isolate(), result.index)) return NoChange(); 508 Node* context = jsgraph()->HeapConstant(result.context); 509 Node* value = effect = graph()->NewNode( 510 javascript()->LoadContext(0, result.index, result.immutable), context, 511 effect); 512 ReplaceWithValue(node, value, effect); 513 return Replace(value); 514 } 515 516 // Not much we can do if deoptimization support is disabled. 517 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 518 519 // Lookup the {name} on the global object instead. 520 return ReduceGlobalAccess(node, nullptr, nullptr, name, AccessMode::kLoad); 521 } 522 523 Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { 524 DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); 525 Handle<Name> name = StoreGlobalParametersOf(node->op()).name(); 526 Node* value = NodeProperties::GetValueInput(node, 0); 527 Node* effect = NodeProperties::GetEffectInput(node); 528 Node* control = NodeProperties::GetControlInput(node); 529 530 // Try to lookup the name on the script context table first (lexical scoping). 531 ScriptContextTableLookupResult result; 532 if (LookupInScriptContextTable(name, &result)) { 533 if (result.context->is_the_hole(isolate(), result.index)) return NoChange(); 534 if (result.immutable) return NoChange(); 535 Node* context = jsgraph()->HeapConstant(result.context); 536 effect = graph()->NewNode(javascript()->StoreContext(0, result.index), 537 value, context, effect, control); 538 ReplaceWithValue(node, value, effect, control); 539 return Replace(value); 540 } 541 542 // Not much we can do if deoptimization support is disabled. 543 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 544 545 // Lookup the {name} on the global object instead. 546 return ReduceGlobalAccess(node, nullptr, value, name, AccessMode::kStore); 547 } 548 549 Reduction JSNativeContextSpecialization::ReduceNamedAccess( 550 Node* node, Node* value, MapHandleList const& receiver_maps, 551 Handle<Name> name, AccessMode access_mode, LanguageMode language_mode, 552 Handle<FeedbackVector> vector, FeedbackSlot slot, Node* index) { 553 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || 554 node->opcode() == IrOpcode::kJSStoreNamed || 555 node->opcode() == IrOpcode::kJSLoadProperty || 556 node->opcode() == IrOpcode::kJSStoreProperty || 557 node->opcode() == IrOpcode::kJSStoreNamedOwn); 558 Node* receiver = NodeProperties::GetValueInput(node, 0); 559 Node* context = NodeProperties::GetContextInput(node); 560 Node* frame_state = NodeProperties::GetFrameStateInput(node); 561 Node* effect = NodeProperties::GetEffectInput(node); 562 Node* control = NodeProperties::GetControlInput(node); 563 564 // Not much we can do if deoptimization support is disabled. 565 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 566 567 // Check if we have an access o.x or o.x=v where o is the current 568 // native contexts' global proxy, and turn that into a direct access 569 // to the current native contexts' global object instead. 570 if (receiver_maps.length() == 1) { 571 Handle<Map> receiver_map = receiver_maps.first(); 572 if (receiver_map->IsJSGlobalProxyMap()) { 573 Object* maybe_constructor = receiver_map->GetConstructor(); 574 // Detached global proxies have |null| as their constructor. 575 if (maybe_constructor->IsJSFunction() && 576 JSFunction::cast(maybe_constructor)->native_context() == 577 *native_context()) { 578 return ReduceGlobalAccess(node, receiver, value, name, access_mode, 579 index); 580 } 581 } 582 } 583 584 // Compute property access infos for the receiver maps. 585 AccessInfoFactory access_info_factory(dependencies(), native_context(), 586 graph()->zone()); 587 ZoneVector<PropertyAccessInfo> access_infos(zone()); 588 if (!access_info_factory.ComputePropertyAccessInfos( 589 receiver_maps, name, access_mode, &access_infos)) { 590 return NoChange(); 591 } 592 593 // TODO(turbofan): Add support for inlining into try blocks. 594 bool is_exceptional = NodeProperties::IsExceptionalCall(node); 595 for (const auto& access_info : access_infos) { 596 if (access_info.IsAccessorConstant()) { 597 // Accessor in try-blocks are not supported yet. 598 if (is_exceptional || !(flags() & kAccessorInliningEnabled)) { 599 return NoChange(); 600 } 601 } else if (access_info.IsGeneric()) { 602 // We do not handle generic calls in try blocks. 603 if (is_exceptional) return NoChange(); 604 // We only handle the generic store IC case. 605 if (!vector->IsStoreIC(slot)) { 606 return NoChange(); 607 } 608 } 609 } 610 611 // Nothing to do if we have no non-deprecated maps. 612 if (access_infos.empty()) { 613 return ReduceSoftDeoptimize( 614 node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); 615 } 616 617 // Ensure that {index} matches the specified {name} (if {index} is given). 618 if (index != nullptr) { 619 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), index, 620 jsgraph()->HeapConstant(name)); 621 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); 622 } 623 624 // Check for the monomorphic cases. 625 if (access_infos.size() == 1) { 626 PropertyAccessInfo access_info = access_infos.front(); 627 if (HasOnlyStringMaps(access_info.receiver_maps())) { 628 // Monormorphic string access (ignoring the fact that there are multiple 629 // String maps). 630 receiver = effect = graph()->NewNode(simplified()->CheckString(), 631 receiver, effect, control); 632 } else if (HasOnlyNumberMaps(access_info.receiver_maps())) { 633 // Monomorphic number access (we also deal with Smis here). 634 receiver = effect = graph()->NewNode(simplified()->CheckNumber(), 635 receiver, effect, control); 636 } else { 637 // Monomorphic property access. 638 receiver = BuildCheckHeapObject(receiver, &effect, control); 639 effect = BuildCheckMaps(receiver, effect, control, 640 access_info.receiver_maps()); 641 } 642 643 // Generate the actual property access. 644 ValueEffectControl continuation = BuildPropertyAccess( 645 receiver, value, context, frame_state, effect, control, name, 646 access_info, access_mode, language_mode, vector, slot); 647 value = continuation.value(); 648 effect = continuation.effect(); 649 control = continuation.control(); 650 } else { 651 // The final states for every polymorphic branch. We join them with 652 // Merge+Phi+EffectPhi at the bottom. 653 ZoneVector<Node*> values(zone()); 654 ZoneVector<Node*> effects(zone()); 655 ZoneVector<Node*> controls(zone()); 656 657 // Check if {receiver} may be a number. 658 bool receiverissmi_possible = false; 659 for (PropertyAccessInfo const& access_info : access_infos) { 660 if (HasNumberMaps(access_info.receiver_maps())) { 661 receiverissmi_possible = true; 662 break; 663 } 664 } 665 666 // Ensure that {receiver} is a heap object. 667 Node* receiverissmi_control = nullptr; 668 Node* receiverissmi_effect = effect; 669 if (receiverissmi_possible) { 670 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); 671 Node* branch = graph()->NewNode(common()->Branch(), check, control); 672 control = graph()->NewNode(common()->IfFalse(), branch); 673 receiverissmi_control = graph()->NewNode(common()->IfTrue(), branch); 674 receiverissmi_effect = effect; 675 } else { 676 receiver = BuildCheckHeapObject(receiver, &effect, control); 677 } 678 679 // Load the {receiver} map. The resulting effect is the dominating effect 680 // for all (polymorphic) branches. 681 Node* receiver_map = effect = 682 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 683 receiver, effect, control); 684 685 // Generate code for the various different property access patterns. 686 Node* fallthrough_control = control; 687 for (size_t j = 0; j < access_infos.size(); ++j) { 688 PropertyAccessInfo const& access_info = access_infos[j]; 689 Node* this_value = value; 690 Node* this_receiver = receiver; 691 Node* this_effect = effect; 692 Node* this_control = fallthrough_control; 693 694 // Perform map check on {receiver}. 695 MapList const& receiver_maps = access_info.receiver_maps(); 696 { 697 // Emit a (sequence of) map checks for other {receiver}s. 698 ZoneVector<Node*> this_controls(zone()); 699 ZoneVector<Node*> this_effects(zone()); 700 if (j == access_infos.size() - 1) { 701 // Last map check on the fallthrough control path, do a 702 // conditional eager deoptimization exit here. 703 this_effect = BuildCheckMaps(receiver, this_effect, this_control, 704 receiver_maps); 705 this_effects.push_back(this_effect); 706 this_controls.push_back(fallthrough_control); 707 fallthrough_control = nullptr; 708 } else { 709 for (auto map : receiver_maps) { 710 Node* check = 711 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, 712 jsgraph()->Constant(map)); 713 Node* branch = graph()->NewNode(common()->Branch(), check, 714 fallthrough_control); 715 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); 716 this_controls.push_back( 717 graph()->NewNode(common()->IfTrue(), branch)); 718 this_effects.push_back(this_effect); 719 } 720 } 721 722 // The Number case requires special treatment to also deal with Smis. 723 if (HasNumberMaps(receiver_maps)) { 724 // Join this check with the "receiver is smi" check above. 725 DCHECK_NOT_NULL(receiverissmi_effect); 726 DCHECK_NOT_NULL(receiverissmi_control); 727 this_effects.push_back(receiverissmi_effect); 728 this_controls.push_back(receiverissmi_control); 729 receiverissmi_effect = receiverissmi_control = nullptr; 730 } 731 732 // Create single chokepoint for the control. 733 int const this_control_count = static_cast<int>(this_controls.size()); 734 if (this_control_count == 1) { 735 this_control = this_controls.front(); 736 this_effect = this_effects.front(); 737 } else { 738 this_control = 739 graph()->NewNode(common()->Merge(this_control_count), 740 this_control_count, &this_controls.front()); 741 this_effects.push_back(this_control); 742 this_effect = 743 graph()->NewNode(common()->EffectPhi(this_control_count), 744 this_control_count + 1, &this_effects.front()); 745 } 746 } 747 748 // Generate the actual property access. 749 ValueEffectControl continuation = 750 BuildPropertyAccess(this_receiver, this_value, context, frame_state, 751 this_effect, this_control, name, access_info, 752 access_mode, language_mode, vector, slot); 753 values.push_back(continuation.value()); 754 effects.push_back(continuation.effect()); 755 controls.push_back(continuation.control()); 756 } 757 758 DCHECK_NULL(fallthrough_control); 759 760 // Generate the final merge point for all (polymorphic) branches. 761 int const control_count = static_cast<int>(controls.size()); 762 if (control_count == 0) { 763 value = effect = control = jsgraph()->Dead(); 764 } else if (control_count == 1) { 765 value = values.front(); 766 effect = effects.front(); 767 control = controls.front(); 768 } else { 769 control = graph()->NewNode(common()->Merge(control_count), control_count, 770 &controls.front()); 771 values.push_back(control); 772 value = graph()->NewNode( 773 common()->Phi(MachineRepresentation::kTagged, control_count), 774 control_count + 1, &values.front()); 775 effects.push_back(control); 776 effect = graph()->NewNode(common()->EffectPhi(control_count), 777 control_count + 1, &effects.front()); 778 } 779 } 780 ReplaceWithValue(node, value, effect, control); 781 return Replace(value); 782 } 783 784 Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus( 785 Node* node, Node* value, FeedbackNexus const& nexus, Handle<Name> name, 786 AccessMode access_mode, LanguageMode language_mode) { 787 DCHECK(node->opcode() == IrOpcode::kJSLoadNamed || 788 node->opcode() == IrOpcode::kJSStoreNamed || 789 node->opcode() == IrOpcode::kJSStoreNamedOwn); 790 Node* const receiver = NodeProperties::GetValueInput(node, 0); 791 Node* const effect = NodeProperties::GetEffectInput(node); 792 793 if (flags() & kDeoptimizationEnabled) { 794 // Check if we are accessing the current native contexts' global proxy. 795 HeapObjectMatcher m(receiver); 796 if (m.HasValue() && m.Value().is_identical_to(global_proxy())) { 797 // Optimize accesses to the current native contexts' global proxy. 798 return ReduceGlobalAccess(node, nullptr, value, name, access_mode); 799 } 800 } 801 802 // Check if the {nexus} reports type feedback for the IC. 803 if (nexus.IsUninitialized()) { 804 if ((flags() & kDeoptimizationEnabled) && 805 (flags() & kBailoutOnUninitialized)) { 806 return ReduceSoftDeoptimize( 807 node, 808 DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); 809 } 810 return NoChange(); 811 } 812 813 // Extract receiver maps from the IC using the {nexus}. 814 MapHandleList receiver_maps; 815 if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { 816 return NoChange(); 817 } else if (receiver_maps.length() == 0) { 818 if ((flags() & kDeoptimizationEnabled) && 819 (flags() & kBailoutOnUninitialized)) { 820 return ReduceSoftDeoptimize( 821 node, 822 DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess); 823 } 824 return NoChange(); 825 } 826 827 // Try to lower the named access based on the {receiver_maps}. 828 return ReduceNamedAccess(node, value, receiver_maps, name, access_mode, 829 language_mode, nexus.vector_handle(), nexus.slot()); 830 } 831 832 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { 833 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); 834 NamedAccess const& p = NamedAccessOf(node->op()); 835 Node* const receiver = NodeProperties::GetValueInput(node, 0); 836 Node* const value = jsgraph()->Dead(); 837 838 // Check if we have a constant receiver. 839 HeapObjectMatcher m(receiver); 840 if (m.HasValue()) { 841 if (m.Value()->IsJSFunction() && 842 p.name().is_identical_to(factory()->prototype_string())) { 843 // Optimize "prototype" property of functions. 844 Handle<JSFunction> function = Handle<JSFunction>::cast(m.Value()); 845 if (function->has_initial_map()) { 846 // We need to add a code dependency on the initial map of the 847 // {function} in order to be notified about changes to the 848 // "prototype" of {function}, so it doesn't make sense to 849 // continue unless deoptimization is enabled. 850 if (flags() & kDeoptimizationEnabled) { 851 Handle<Map> initial_map(function->initial_map(), isolate()); 852 dependencies()->AssumeInitialMapCantChange(initial_map); 853 Handle<Object> prototype(initial_map->prototype(), isolate()); 854 Node* value = jsgraph()->Constant(prototype); 855 ReplaceWithValue(node, value); 856 return Replace(value); 857 } 858 } 859 } else if (m.Value()->IsString() && 860 p.name().is_identical_to(factory()->length_string())) { 861 // Constant-fold "length" property on constant strings. 862 Handle<String> string = Handle<String>::cast(m.Value()); 863 Node* value = jsgraph()->Constant(string->length()); 864 ReplaceWithValue(node, value); 865 return Replace(value); 866 } 867 } 868 869 // Extract receiver maps from the LOAD_IC using the LoadICNexus. 870 if (!p.feedback().IsValid()) return NoChange(); 871 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); 872 873 // Try to lower the named access based on the {receiver_maps}. 874 return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), 875 AccessMode::kLoad, p.language_mode()); 876 } 877 878 879 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { 880 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); 881 NamedAccess const& p = NamedAccessOf(node->op()); 882 Node* const value = NodeProperties::GetValueInput(node, 1); 883 884 // Extract receiver maps from the STORE_IC using the StoreICNexus. 885 if (!p.feedback().IsValid()) return NoChange(); 886 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); 887 888 // Try to lower the named access based on the {receiver_maps}. 889 return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), 890 AccessMode::kStore, p.language_mode()); 891 } 892 893 Reduction JSNativeContextSpecialization::ReduceJSStoreNamedOwn(Node* node) { 894 DCHECK_EQ(IrOpcode::kJSStoreNamedOwn, node->opcode()); 895 StoreNamedOwnParameters const& p = StoreNamedOwnParametersOf(node->op()); 896 Node* const value = NodeProperties::GetValueInput(node, 1); 897 898 // Extract receiver maps from the IC using the StoreOwnICNexus. 899 if (!p.feedback().IsValid()) return NoChange(); 900 StoreOwnICNexus nexus(p.feedback().vector(), p.feedback().slot()); 901 902 // Try to lower the creation of a named property based on the {receiver_maps}. 903 return ReduceNamedAccessFromNexus(node, value, nexus, p.name(), 904 AccessMode::kStoreInLiteral, STRICT); 905 } 906 907 Reduction JSNativeContextSpecialization::ReduceElementAccess( 908 Node* node, Node* index, Node* value, MapHandleList const& receiver_maps, 909 AccessMode access_mode, LanguageMode language_mode, 910 KeyedAccessStoreMode store_mode) { 911 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || 912 node->opcode() == IrOpcode::kJSStoreProperty); 913 Node* receiver = NodeProperties::GetValueInput(node, 0); 914 Node* effect = NodeProperties::GetEffectInput(node); 915 Node* control = NodeProperties::GetControlInput(node); 916 Node* frame_state = NodeProperties::FindFrameStateBefore(node); 917 918 // Not much we can do if deoptimization support is disabled. 919 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 920 921 // Check for keyed access to strings. 922 if (HasOnlyStringMaps(receiver_maps)) { 923 // Strings are immutable in JavaScript. 924 if (access_mode == AccessMode::kStore) return NoChange(); 925 926 // Ensure that the {receiver} is actually a String. 927 receiver = effect = graph()->NewNode(simplified()->CheckString(), receiver, 928 effect, control); 929 930 // Determine the {receiver} length. 931 Node* length = effect = graph()->NewNode( 932 simplified()->LoadField(AccessBuilder::ForStringLength()), receiver, 933 effect, control); 934 935 // Ensure that {index} is less than {receiver} length. 936 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, 937 length, effect, control); 938 939 // Return the character from the {receiver} as single character string. 940 value = graph()->NewNode(simplified()->StringCharAt(), receiver, index, 941 control); 942 } else { 943 // Retrieve the native context from the given {node}. 944 // Compute element access infos for the receiver maps. 945 AccessInfoFactory access_info_factory(dependencies(), native_context(), 946 graph()->zone()); 947 ZoneVector<ElementAccessInfo> access_infos(zone()); 948 if (!access_info_factory.ComputeElementAccessInfos( 949 receiver_maps, access_mode, &access_infos)) { 950 return NoChange(); 951 } 952 953 // Nothing to do if we have no non-deprecated maps. 954 if (access_infos.empty()) { 955 return ReduceSoftDeoptimize( 956 node, 957 DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); 958 } 959 960 // For holey stores or growing stores, we need to check that the prototype 961 // chain contains no setters for elements, and we need to guard those checks 962 // via code dependencies on the relevant prototype maps. 963 if (access_mode == AccessMode::kStore) { 964 // TODO(turbofan): We could have a fast path here, that checks for the 965 // common case of Array or Object prototype only and therefore avoids 966 // the zone allocation of this vector. 967 ZoneVector<Handle<Map>> prototype_maps(zone()); 968 for (ElementAccessInfo const& access_info : access_infos) { 969 for (Handle<Map> receiver_map : access_info.receiver_maps()) { 970 // If the {receiver_map} has a prototype and it's elements backing 971 // store is either holey, or we have a potentially growing store, 972 // then we need to check that all prototypes have stable maps with 973 // fast elements (and we need to guard against changes to that below). 974 if (IsHoleyElementsKind(receiver_map->elements_kind()) || 975 IsGrowStoreMode(store_mode)) { 976 // Make sure all prototypes are stable and have fast elements. 977 for (Handle<Map> map = receiver_map;;) { 978 Handle<Object> map_prototype(map->prototype(), isolate()); 979 if (map_prototype->IsNull(isolate())) break; 980 if (!map_prototype->IsJSObject()) return NoChange(); 981 map = handle(Handle<JSObject>::cast(map_prototype)->map(), 982 isolate()); 983 if (!map->is_stable()) return NoChange(); 984 if (!IsFastElementsKind(map->elements_kind())) return NoChange(); 985 prototype_maps.push_back(map); 986 } 987 } 988 } 989 } 990 991 // Install dependencies on the relevant prototype maps. 992 for (Handle<Map> prototype_map : prototype_maps) { 993 dependencies()->AssumeMapStable(prototype_map); 994 } 995 } 996 997 // Ensure that {receiver} is a heap object. 998 receiver = BuildCheckHeapObject(receiver, &effect, control); 999 1000 // Check for the monomorphic case. 1001 if (access_infos.size() == 1) { 1002 ElementAccessInfo access_info = access_infos.front(); 1003 1004 // Perform possible elements kind transitions. 1005 for (auto transition : access_info.transitions()) { 1006 Handle<Map> const transition_source = transition.first; 1007 Handle<Map> const transition_target = transition.second; 1008 effect = graph()->NewNode( 1009 simplified()->TransitionElementsKind(ElementsTransition( 1010 IsSimpleMapChangeTransition(transition_source->elements_kind(), 1011 transition_target->elements_kind()) 1012 ? ElementsTransition::kFastTransition 1013 : ElementsTransition::kSlowTransition, 1014 transition_source, transition_target)), 1015 receiver, effect, control); 1016 } 1017 1018 // TODO(turbofan): The effect/control linearization will not find a 1019 // FrameState after the StoreField or Call that is generated for the 1020 // elements kind transition above. This is because those operators 1021 // don't have the kNoWrite flag on it, even though they are not 1022 // observable by JavaScript. 1023 effect = graph()->NewNode(common()->Checkpoint(), frame_state, effect, 1024 control); 1025 1026 // Perform map check on the {receiver}. 1027 effect = BuildCheckMaps(receiver, effect, control, 1028 access_info.receiver_maps()); 1029 1030 // Access the actual element. 1031 ValueEffectControl continuation = 1032 BuildElementAccess(receiver, index, value, effect, control, 1033 access_info, access_mode, store_mode); 1034 value = continuation.value(); 1035 effect = continuation.effect(); 1036 control = continuation.control(); 1037 } else { 1038 // The final states for every polymorphic branch. We join them with 1039 // Merge+Phi+EffectPhi at the bottom. 1040 ZoneVector<Node*> values(zone()); 1041 ZoneVector<Node*> effects(zone()); 1042 ZoneVector<Node*> controls(zone()); 1043 1044 // Generate code for the various different element access patterns. 1045 Node* fallthrough_control = control; 1046 for (size_t j = 0; j < access_infos.size(); ++j) { 1047 ElementAccessInfo const& access_info = access_infos[j]; 1048 Node* this_receiver = receiver; 1049 Node* this_value = value; 1050 Node* this_index = index; 1051 Node* this_effect = effect; 1052 Node* this_control = fallthrough_control; 1053 1054 // Perform possible elements kind transitions. 1055 for (auto transition : access_info.transitions()) { 1056 Handle<Map> const transition_source = transition.first; 1057 Handle<Map> const transition_target = transition.second; 1058 this_effect = graph()->NewNode( 1059 simplified()->TransitionElementsKind( 1060 ElementsTransition(IsSimpleMapChangeTransition( 1061 transition_source->elements_kind(), 1062 transition_target->elements_kind()) 1063 ? ElementsTransition::kFastTransition 1064 : ElementsTransition::kSlowTransition, 1065 transition_source, transition_target)), 1066 receiver, this_effect, this_control); 1067 } 1068 1069 // Load the {receiver} map. 1070 Node* receiver_map = this_effect = 1071 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), 1072 receiver, this_effect, this_control); 1073 1074 // Perform map check(s) on {receiver}. 1075 MapList const& receiver_maps = access_info.receiver_maps(); 1076 if (j == access_infos.size() - 1) { 1077 // Last map check on the fallthrough control path, do a 1078 // conditional eager deoptimization exit here. 1079 this_effect = BuildCheckMaps(receiver, this_effect, this_control, 1080 receiver_maps); 1081 fallthrough_control = nullptr; 1082 } else { 1083 ZoneVector<Node*> this_controls(zone()); 1084 ZoneVector<Node*> this_effects(zone()); 1085 for (Handle<Map> map : receiver_maps) { 1086 Node* check = 1087 graph()->NewNode(simplified()->ReferenceEqual(), receiver_map, 1088 jsgraph()->Constant(map)); 1089 Node* branch = graph()->NewNode(common()->Branch(), check, 1090 fallthrough_control); 1091 this_controls.push_back( 1092 graph()->NewNode(common()->IfTrue(), branch)); 1093 this_effects.push_back(this_effect); 1094 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); 1095 } 1096 1097 // Create single chokepoint for the control. 1098 int const this_control_count = static_cast<int>(this_controls.size()); 1099 if (this_control_count == 1) { 1100 this_control = this_controls.front(); 1101 this_effect = this_effects.front(); 1102 } else { 1103 this_control = 1104 graph()->NewNode(common()->Merge(this_control_count), 1105 this_control_count, &this_controls.front()); 1106 this_effects.push_back(this_control); 1107 this_effect = 1108 graph()->NewNode(common()->EffectPhi(this_control_count), 1109 this_control_count + 1, &this_effects.front()); 1110 } 1111 } 1112 1113 // Access the actual element. 1114 ValueEffectControl continuation = BuildElementAccess( 1115 this_receiver, this_index, this_value, this_effect, this_control, 1116 access_info, access_mode, store_mode); 1117 values.push_back(continuation.value()); 1118 effects.push_back(continuation.effect()); 1119 controls.push_back(continuation.control()); 1120 } 1121 1122 DCHECK_NULL(fallthrough_control); 1123 1124 // Generate the final merge point for all (polymorphic) branches. 1125 int const control_count = static_cast<int>(controls.size()); 1126 if (control_count == 0) { 1127 value = effect = control = jsgraph()->Dead(); 1128 } else if (control_count == 1) { 1129 value = values.front(); 1130 effect = effects.front(); 1131 control = controls.front(); 1132 } else { 1133 control = graph()->NewNode(common()->Merge(control_count), 1134 control_count, &controls.front()); 1135 values.push_back(control); 1136 value = graph()->NewNode( 1137 common()->Phi(MachineRepresentation::kTagged, control_count), 1138 control_count + 1, &values.front()); 1139 effects.push_back(control); 1140 effect = graph()->NewNode(common()->EffectPhi(control_count), 1141 control_count + 1, &effects.front()); 1142 } 1143 } 1144 } 1145 1146 ReplaceWithValue(node, value, effect, control); 1147 return Replace(value); 1148 } 1149 1150 template <typename KeyedICNexus> 1151 Reduction JSNativeContextSpecialization::ReduceKeyedAccess( 1152 Node* node, Node* index, Node* value, KeyedICNexus const& nexus, 1153 AccessMode access_mode, LanguageMode language_mode, 1154 KeyedAccessStoreMode store_mode) { 1155 DCHECK(node->opcode() == IrOpcode::kJSLoadProperty || 1156 node->opcode() == IrOpcode::kJSStoreProperty); 1157 Node* receiver = NodeProperties::GetValueInput(node, 0); 1158 Node* effect = NodeProperties::GetEffectInput(node); 1159 Node* control = NodeProperties::GetControlInput(node); 1160 1161 // Optimize access for constant {receiver}. 1162 HeapObjectMatcher mreceiver(receiver); 1163 if (mreceiver.HasValue() && mreceiver.Value()->IsString()) { 1164 Handle<String> string = Handle<String>::cast(mreceiver.Value()); 1165 1166 // We can only assume that the {index} is a valid array index if the IC 1167 // is in element access mode and not MEGAMORPHIC, otherwise there's no 1168 // guard for the bounds check below. 1169 if (nexus.ic_state() != MEGAMORPHIC && nexus.GetKeyType() == ELEMENT) { 1170 // Strings are immutable in JavaScript. 1171 if (access_mode == AccessMode::kStore) return NoChange(); 1172 1173 // Properly deal with constant {index}. 1174 NumberMatcher mindex(index); 1175 if (mindex.IsInteger() && mindex.IsInRange(0.0, string->length() - 1)) { 1176 // Constant-fold the {index} access to {string}. 1177 Node* value = jsgraph()->HeapConstant( 1178 factory()->LookupSingleCharacterStringFromCode( 1179 string->Get(static_cast<int>(mindex.Value())))); 1180 ReplaceWithValue(node, value, effect, control); 1181 return Replace(value); 1182 } else if (flags() & kDeoptimizationEnabled) { 1183 // Ensure that {index} is less than {receiver} length. 1184 Node* length = jsgraph()->Constant(string->length()); 1185 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, 1186 length, effect, control); 1187 1188 // Return the character from the {receiver} as single character string. 1189 value = graph()->NewNode(simplified()->StringCharAt(), receiver, index, 1190 control); 1191 ReplaceWithValue(node, value, effect, control); 1192 return Replace(value); 1193 } 1194 } 1195 } 1196 1197 // Check if the {nexus} reports type feedback for the IC. 1198 if (nexus.IsUninitialized()) { 1199 if ((flags() & kDeoptimizationEnabled) && 1200 (flags() & kBailoutOnUninitialized)) { 1201 return ReduceSoftDeoptimize( 1202 node, 1203 DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); 1204 } 1205 return NoChange(); 1206 } 1207 1208 // Extract receiver maps from the {nexus}. 1209 MapHandleList receiver_maps; 1210 if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) { 1211 return NoChange(); 1212 } else if (receiver_maps.length() == 0) { 1213 if ((flags() & kDeoptimizationEnabled) && 1214 (flags() & kBailoutOnUninitialized)) { 1215 return ReduceSoftDeoptimize( 1216 node, 1217 DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess); 1218 } 1219 return NoChange(); 1220 } 1221 1222 // Optimize access for constant {index}. 1223 HeapObjectMatcher mindex(index); 1224 if (mindex.HasValue() && mindex.Value()->IsPrimitive()) { 1225 // Keyed access requires a ToPropertyKey on the {index} first before 1226 // looking up the property on the object (see ES6 section 12.3.2.1). 1227 // We can only do this for non-observable ToPropertyKey invocations, 1228 // so we limit the constant indices to primitives at this point. 1229 Handle<Name> name; 1230 if (Object::ToName(isolate(), mindex.Value()).ToHandle(&name)) { 1231 uint32_t array_index; 1232 if (name->AsArrayIndex(&array_index)) { 1233 // Use the constant array index. 1234 index = jsgraph()->Constant(static_cast<double>(array_index)); 1235 } else { 1236 name = factory()->InternalizeName(name); 1237 return ReduceNamedAccess(node, value, receiver_maps, name, access_mode, 1238 language_mode, nexus.vector_handle(), 1239 nexus.slot()); 1240 } 1241 } 1242 } 1243 1244 // Check if we have feedback for a named access. 1245 if (Name* name = nexus.FindFirstName()) { 1246 return ReduceNamedAccess( 1247 node, value, receiver_maps, handle(name, isolate()), access_mode, 1248 language_mode, nexus.vector_handle(), nexus.slot(), index); 1249 } else if (nexus.GetKeyType() != ELEMENT) { 1250 // The KeyedLoad/StoreIC has seen non-element accesses, so we cannot assume 1251 // that the {index} is a valid array index, thus we just let the IC continue 1252 // to deal with this load/store. 1253 return NoChange(); 1254 } else if (nexus.ic_state() == MEGAMORPHIC) { 1255 // The KeyedLoad/StoreIC uses the MEGAMORPHIC state to guard the assumption 1256 // that a numeric {index} is within the valid bounds for {receiver}, i.e. 1257 // it transitions to MEGAMORPHIC once it sees an out-of-bounds access. Thus 1258 // we cannot continue here if the IC state is MEGAMORPHIC. 1259 return NoChange(); 1260 } 1261 1262 // Try to lower the element access based on the {receiver_maps}. 1263 return ReduceElementAccess(node, index, value, receiver_maps, access_mode, 1264 language_mode, store_mode); 1265 } 1266 1267 Reduction JSNativeContextSpecialization::ReduceSoftDeoptimize( 1268 Node* node, DeoptimizeReason reason) { 1269 Node* effect = NodeProperties::GetEffectInput(node); 1270 Node* control = NodeProperties::GetControlInput(node); 1271 Node* frame_state = NodeProperties::FindFrameStateBefore(node); 1272 Node* deoptimize = 1273 graph()->NewNode(common()->Deoptimize(DeoptimizeKind::kSoft, reason), 1274 frame_state, effect, control); 1275 // TODO(bmeurer): This should be on the AdvancedReducer somehow. 1276 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); 1277 Revisit(graph()->end()); 1278 node->TrimInputCount(0); 1279 NodeProperties::ChangeOp(node, common()->Dead()); 1280 return Changed(node); 1281 } 1282 1283 1284 Reduction JSNativeContextSpecialization::ReduceJSLoadProperty(Node* node) { 1285 DCHECK_EQ(IrOpcode::kJSLoadProperty, node->opcode()); 1286 PropertyAccess const& p = PropertyAccessOf(node->op()); 1287 Node* const index = NodeProperties::GetValueInput(node, 1); 1288 Node* const value = jsgraph()->Dead(); 1289 1290 // Extract receiver maps from the KEYED_LOAD_IC using the KeyedLoadICNexus. 1291 if (!p.feedback().IsValid()) return NoChange(); 1292 KeyedLoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); 1293 1294 // Try to lower the keyed access based on the {nexus}. 1295 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kLoad, 1296 p.language_mode(), STANDARD_STORE); 1297 } 1298 1299 1300 Reduction JSNativeContextSpecialization::ReduceJSStoreProperty(Node* node) { 1301 DCHECK_EQ(IrOpcode::kJSStoreProperty, node->opcode()); 1302 PropertyAccess const& p = PropertyAccessOf(node->op()); 1303 Node* const index = NodeProperties::GetValueInput(node, 1); 1304 Node* const value = NodeProperties::GetValueInput(node, 2); 1305 1306 // Extract receiver maps from the KEYED_STORE_IC using the KeyedStoreICNexus. 1307 if (!p.feedback().IsValid()) return NoChange(); 1308 KeyedStoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); 1309 1310 // Extract the keyed access store mode from the KEYED_STORE_IC. 1311 KeyedAccessStoreMode store_mode = nexus.GetKeyedAccessStoreMode(); 1312 1313 // Try to lower the keyed access based on the {nexus}. 1314 return ReduceKeyedAccess(node, index, value, nexus, AccessMode::kStore, 1315 p.language_mode(), store_mode); 1316 } 1317 1318 JSNativeContextSpecialization::ValueEffectControl 1319 JSNativeContextSpecialization::BuildPropertyAccess( 1320 Node* receiver, Node* value, Node* context, Node* frame_state, Node* effect, 1321 Node* control, Handle<Name> name, PropertyAccessInfo const& access_info, 1322 AccessMode access_mode, LanguageMode language_mode, 1323 Handle<FeedbackVector> vector, FeedbackSlot slot) { 1324 // Determine actual holder and perform prototype chain checks. 1325 Handle<JSObject> holder; 1326 if (access_info.holder().ToHandle(&holder)) { 1327 DCHECK_NE(AccessMode::kStoreInLiteral, access_mode); 1328 AssumePrototypesStable(access_info.receiver_maps(), holder); 1329 } 1330 1331 // Generate the actual property access. 1332 if (access_info.IsNotFound()) { 1333 DCHECK_EQ(AccessMode::kLoad, access_mode); 1334 value = jsgraph()->UndefinedConstant(); 1335 } else if (access_info.IsDataConstant()) { 1336 Node* constant_value = jsgraph()->Constant(access_info.constant()); 1337 if (access_mode == AccessMode::kStore) { 1338 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), value, 1339 constant_value); 1340 effect = 1341 graph()->NewNode(simplified()->CheckIf(), check, effect, control); 1342 } 1343 value = constant_value; 1344 } else if (access_info.IsAccessorConstant()) { 1345 // TODO(bmeurer): Properly rewire the IfException edge here if there's any. 1346 Node* target = jsgraph()->Constant(access_info.constant()); 1347 FrameStateInfo const& frame_info = OpParameter<FrameStateInfo>(frame_state); 1348 Handle<SharedFunctionInfo> shared_info = 1349 frame_info.shared_info().ToHandleChecked(); 1350 switch (access_mode) { 1351 case AccessMode::kLoad: { 1352 // We need a FrameState for the getter stub to restore the correct 1353 // context before returning to fullcodegen. 1354 FrameStateFunctionInfo const* frame_info0 = 1355 common()->CreateFrameStateFunctionInfo(FrameStateType::kGetterStub, 1356 1, 0, shared_info); 1357 Node* frame_state0 = graph()->NewNode( 1358 common()->FrameState(BailoutId::None(), 1359 OutputFrameStateCombine::Ignore(), 1360 frame_info0), 1361 graph()->NewNode(common()->StateValues(1, SparseInputMask::Dense()), 1362 receiver), 1363 jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), 1364 context, target, frame_state); 1365 1366 // Introduce the call to the getter function. 1367 if (access_info.constant()->IsJSFunction()) { 1368 value = effect = graph()->NewNode( 1369 javascript()->Call(2, 0.0f, VectorSlotPair(), 1370 ConvertReceiverMode::kNotNullOrUndefined), 1371 target, receiver, context, frame_state0, effect, control); 1372 control = graph()->NewNode(common()->IfSuccess(), value); 1373 } else { 1374 DCHECK(access_info.constant()->IsFunctionTemplateInfo()); 1375 Handle<FunctionTemplateInfo> function_template_info( 1376 Handle<FunctionTemplateInfo>::cast(access_info.constant())); 1377 DCHECK(!function_template_info->call_code()->IsUndefined(isolate())); 1378 ValueEffectControl value_effect_control = InlineApiCall( 1379 receiver, context, target, frame_state0, nullptr, effect, control, 1380 shared_info, function_template_info); 1381 value = value_effect_control.value(); 1382 effect = value_effect_control.effect(); 1383 control = value_effect_control.control(); 1384 } 1385 break; 1386 } 1387 case AccessMode::kStoreInLiteral: 1388 case AccessMode::kStore: { 1389 // We need a FrameState for the setter stub to restore the correct 1390 // context and return the appropriate value to fullcodegen. 1391 FrameStateFunctionInfo const* frame_info0 = 1392 common()->CreateFrameStateFunctionInfo(FrameStateType::kSetterStub, 1393 2, 0, shared_info); 1394 Node* frame_state0 = graph()->NewNode( 1395 common()->FrameState(BailoutId::None(), 1396 OutputFrameStateCombine::Ignore(), 1397 frame_info0), 1398 graph()->NewNode(common()->StateValues(2, SparseInputMask::Dense()), 1399 receiver, value), 1400 jsgraph()->EmptyStateValues(), jsgraph()->EmptyStateValues(), 1401 context, target, frame_state); 1402 1403 // Introduce the call to the setter function. 1404 if (access_info.constant()->IsJSFunction()) { 1405 effect = graph()->NewNode( 1406 javascript()->Call(3, 0.0f, VectorSlotPair(), 1407 ConvertReceiverMode::kNotNullOrUndefined), 1408 target, receiver, value, context, frame_state0, effect, control); 1409 control = graph()->NewNode(common()->IfSuccess(), effect); 1410 } else { 1411 DCHECK(access_info.constant()->IsFunctionTemplateInfo()); 1412 Handle<FunctionTemplateInfo> function_template_info( 1413 Handle<FunctionTemplateInfo>::cast(access_info.constant())); 1414 DCHECK(!function_template_info->call_code()->IsUndefined(isolate())); 1415 ValueEffectControl value_effect_control = InlineApiCall( 1416 receiver, context, target, frame_state0, value, effect, control, 1417 shared_info, function_template_info); 1418 value = value_effect_control.value(); 1419 effect = value_effect_control.effect(); 1420 control = value_effect_control.control(); 1421 } 1422 break; 1423 } 1424 } 1425 } else if (access_info.IsDataField() || access_info.IsDataConstantField()) { 1426 FieldIndex const field_index = access_info.field_index(); 1427 Type* const field_type = access_info.field_type(); 1428 MachineRepresentation const field_representation = 1429 access_info.field_representation(); 1430 if (access_mode == AccessMode::kLoad) { 1431 if (access_info.holder().ToHandle(&holder)) { 1432 receiver = jsgraph()->Constant(holder); 1433 } 1434 // Optimize immutable property loads. 1435 HeapObjectMatcher m(receiver); 1436 if (m.HasValue() && m.Value()->IsJSObject()) { 1437 // TODO(ishell): Use something simpler like 1438 // 1439 // Handle<Object> value = 1440 // JSObject::FastPropertyAt(Handle<JSObject>::cast(m.Value()), 1441 // Representation::Tagged(), field_index); 1442 // 1443 // here, once we have the immutable bit in the access_info. 1444 1445 // TODO(turbofan): Given that we already have the field_index here, we 1446 // might be smarter in the future and not rely on the LookupIterator, 1447 // but for now let's just do what Crankshaft does. 1448 LookupIterator it(m.Value(), name, 1449 LookupIterator::OWN_SKIP_INTERCEPTOR); 1450 if (it.state() == LookupIterator::DATA) { 1451 bool is_reaonly_non_configurable = 1452 it.IsReadOnly() && !it.IsConfigurable(); 1453 if (is_reaonly_non_configurable || 1454 (FLAG_track_constant_fields && 1455 access_info.IsDataConstantField())) { 1456 Node* value = jsgraph()->Constant(JSReceiver::GetDataProperty(&it)); 1457 if (!is_reaonly_non_configurable) { 1458 // It's necessary to add dependency on the map that introduced 1459 // the field. 1460 DCHECK(access_info.IsDataConstantField()); 1461 DCHECK(!it.is_dictionary_holder()); 1462 Handle<Map> field_owner_map = it.GetFieldOwnerMap(); 1463 dependencies()->AssumeFieldOwner(field_owner_map); 1464 } 1465 return ValueEffectControl(value, effect, control); 1466 } 1467 } 1468 } 1469 } 1470 Node* storage = receiver; 1471 if (!field_index.is_inobject()) { 1472 storage = effect = graph()->NewNode( 1473 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), 1474 storage, effect, control); 1475 } 1476 FieldAccess field_access = { 1477 kTaggedBase, 1478 field_index.offset(), 1479 name, 1480 MaybeHandle<Map>(), 1481 field_type, 1482 MachineType::TypeForRepresentation(field_representation), 1483 kFullWriteBarrier}; 1484 if (access_mode == AccessMode::kLoad) { 1485 if (field_representation == MachineRepresentation::kFloat64) { 1486 if (!field_index.is_inobject() || field_index.is_hidden_field() || 1487 !FLAG_unbox_double_fields) { 1488 FieldAccess const storage_access = {kTaggedBase, 1489 field_index.offset(), 1490 name, 1491 MaybeHandle<Map>(), 1492 Type::OtherInternal(), 1493 MachineType::TaggedPointer(), 1494 kPointerWriteBarrier}; 1495 storage = effect = 1496 graph()->NewNode(simplified()->LoadField(storage_access), storage, 1497 effect, control); 1498 field_access.offset = HeapNumber::kValueOffset; 1499 field_access.name = MaybeHandle<Name>(); 1500 } 1501 } else if (field_representation == 1502 MachineRepresentation::kTaggedPointer) { 1503 // Remember the map of the field value, if its map is stable. This is 1504 // used by the LoadElimination to eliminate map checks on the result. 1505 Handle<Map> field_map; 1506 if (access_info.field_map().ToHandle(&field_map)) { 1507 if (field_map->is_stable()) { 1508 dependencies()->AssumeMapStable(field_map); 1509 field_access.map = field_map; 1510 } 1511 } 1512 } 1513 value = effect = graph()->NewNode(simplified()->LoadField(field_access), 1514 storage, effect, control); 1515 } else { 1516 bool store_to_constant_field = FLAG_track_constant_fields && 1517 (access_mode == AccessMode::kStore) && 1518 access_info.IsDataConstantField(); 1519 1520 DCHECK(access_mode == AccessMode::kStore || 1521 access_mode == AccessMode::kStoreInLiteral); 1522 switch (field_representation) { 1523 case MachineRepresentation::kFloat64: { 1524 value = effect = graph()->NewNode(simplified()->CheckNumber(), value, 1525 effect, control); 1526 if (!field_index.is_inobject() || field_index.is_hidden_field() || 1527 !FLAG_unbox_double_fields) { 1528 if (access_info.HasTransitionMap()) { 1529 // Allocate a MutableHeapNumber for the new property. 1530 effect = graph()->NewNode( 1531 common()->BeginRegion(RegionObservability::kNotObservable), 1532 effect); 1533 Node* box = effect = graph()->NewNode( 1534 simplified()->Allocate(NOT_TENURED), 1535 jsgraph()->Constant(HeapNumber::kSize), effect, control); 1536 effect = graph()->NewNode( 1537 simplified()->StoreField(AccessBuilder::ForMap()), box, 1538 jsgraph()->HeapConstant(factory()->mutable_heap_number_map()), 1539 effect, control); 1540 effect = graph()->NewNode( 1541 simplified()->StoreField(AccessBuilder::ForHeapNumberValue()), 1542 box, value, effect, control); 1543 value = effect = 1544 graph()->NewNode(common()->FinishRegion(), box, effect); 1545 1546 field_access.type = Type::Any(); 1547 field_access.machine_type = MachineType::TaggedPointer(); 1548 field_access.write_barrier_kind = kPointerWriteBarrier; 1549 } else { 1550 // We just store directly to the MutableHeapNumber. 1551 FieldAccess const storage_access = {kTaggedBase, 1552 field_index.offset(), 1553 name, 1554 MaybeHandle<Map>(), 1555 Type::OtherInternal(), 1556 MachineType::TaggedPointer(), 1557 kPointerWriteBarrier}; 1558 storage = effect = 1559 graph()->NewNode(simplified()->LoadField(storage_access), 1560 storage, effect, control); 1561 field_access.offset = HeapNumber::kValueOffset; 1562 field_access.name = MaybeHandle<Name>(); 1563 field_access.machine_type = MachineType::Float64(); 1564 } 1565 } 1566 if (store_to_constant_field) { 1567 DCHECK(!access_info.HasTransitionMap()); 1568 // If the field is constant check that the value we are going 1569 // to store matches current value. 1570 Node* current_value = effect = 1571 graph()->NewNode(simplified()->LoadField(field_access), storage, 1572 effect, control); 1573 1574 Node* check = graph()->NewNode(simplified()->NumberEqual(), 1575 current_value, value); 1576 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, 1577 control); 1578 return ValueEffectControl(value, effect, control); 1579 } 1580 break; 1581 } 1582 case MachineRepresentation::kTaggedSigned: 1583 case MachineRepresentation::kTaggedPointer: 1584 case MachineRepresentation::kTagged: 1585 if (store_to_constant_field) { 1586 DCHECK(!access_info.HasTransitionMap()); 1587 // If the field is constant check that the value we are going 1588 // to store matches current value. 1589 Node* current_value = effect = 1590 graph()->NewNode(simplified()->LoadField(field_access), storage, 1591 effect, control); 1592 1593 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), 1594 current_value, value); 1595 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, 1596 control); 1597 return ValueEffectControl(value, effect, control); 1598 } 1599 1600 if (field_representation == MachineRepresentation::kTaggedSigned) { 1601 value = effect = graph()->NewNode(simplified()->CheckSmi(), value, 1602 effect, control); 1603 field_access.write_barrier_kind = kNoWriteBarrier; 1604 1605 } else if (field_representation == 1606 MachineRepresentation::kTaggedPointer) { 1607 // Ensure that {value} is a HeapObject. 1608 value = BuildCheckHeapObject(value, &effect, control); 1609 Handle<Map> field_map; 1610 if (access_info.field_map().ToHandle(&field_map)) { 1611 // Emit a map check for the value. 1612 effect = graph()->NewNode( 1613 simplified()->CheckMaps(CheckMapsFlag::kNone, 1614 ZoneHandleSet<Map>(field_map)), 1615 value, effect, control); 1616 } 1617 field_access.write_barrier_kind = kPointerWriteBarrier; 1618 1619 } else { 1620 DCHECK_EQ(MachineRepresentation::kTagged, field_representation); 1621 } 1622 break; 1623 case MachineRepresentation::kNone: 1624 case MachineRepresentation::kBit: 1625 case MachineRepresentation::kWord8: 1626 case MachineRepresentation::kWord16: 1627 case MachineRepresentation::kWord32: 1628 case MachineRepresentation::kWord64: 1629 case MachineRepresentation::kFloat32: 1630 case MachineRepresentation::kSimd128: 1631 case MachineRepresentation::kSimd1x4: 1632 case MachineRepresentation::kSimd1x8: 1633 case MachineRepresentation::kSimd1x16: 1634 UNREACHABLE(); 1635 break; 1636 } 1637 Handle<Map> transition_map; 1638 if (access_info.transition_map().ToHandle(&transition_map)) { 1639 effect = graph()->NewNode( 1640 common()->BeginRegion(RegionObservability::kObservable), effect); 1641 effect = graph()->NewNode( 1642 simplified()->StoreField(AccessBuilder::ForMap()), receiver, 1643 jsgraph()->Constant(transition_map), effect, control); 1644 } 1645 effect = graph()->NewNode(simplified()->StoreField(field_access), storage, 1646 value, effect, control); 1647 if (access_info.HasTransitionMap()) { 1648 effect = graph()->NewNode(common()->FinishRegion(), 1649 jsgraph()->UndefinedConstant(), effect); 1650 } 1651 } 1652 } else { 1653 DCHECK(access_info.IsGeneric()); 1654 DCHECK_EQ(AccessMode::kStore, access_mode); 1655 DCHECK(vector->IsStoreIC(slot)); 1656 DCHECK_EQ(vector->GetLanguageMode(slot), language_mode); 1657 Callable callable = 1658 CodeFactory::StoreICInOptimizedCode(isolate(), language_mode); 1659 const CallInterfaceDescriptor& descriptor = callable.descriptor(); 1660 CallDescriptor* desc = Linkage::GetStubCallDescriptor( 1661 isolate(), graph()->zone(), descriptor, 1662 descriptor.GetStackParameterCount(), CallDescriptor::kNeedsFrameState, 1663 Operator::kNoProperties); 1664 Node* stub_code = jsgraph()->HeapConstant(callable.code()); 1665 Node* name_node = jsgraph()->HeapConstant(name); 1666 Node* slot_node = jsgraph()->Constant(vector->GetIndex(slot)); 1667 Node* vector_node = jsgraph()->HeapConstant(vector); 1668 1669 Node* inputs[] = {stub_code, receiver, name_node, value, slot_node, 1670 vector_node, context, frame_state, effect, control}; 1671 1672 value = effect = control = 1673 graph()->NewNode(common()->Call(desc), arraysize(inputs), inputs); 1674 control = graph()->NewNode(common()->IfSuccess(), control); 1675 } 1676 1677 return ValueEffectControl(value, effect, control); 1678 } 1679 1680 Reduction JSNativeContextSpecialization::ReduceJSStoreDataPropertyInLiteral( 1681 Node* node) { 1682 DCHECK_EQ(IrOpcode::kJSStoreDataPropertyInLiteral, node->opcode()); 1683 1684 // If deoptimization is disabled, we cannot optimize. 1685 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); 1686 1687 DataPropertyParameters const& p = DataPropertyParametersOf(node->op()); 1688 1689 if (!p.feedback().IsValid()) return NoChange(); 1690 1691 StoreDataPropertyInLiteralICNexus nexus(p.feedback().vector(), 1692 p.feedback().slot()); 1693 if (nexus.IsUninitialized()) { 1694 return NoChange(); 1695 } 1696 1697 if (nexus.ic_state() == MEGAMORPHIC) { 1698 return NoChange(); 1699 } 1700 1701 DCHECK_EQ(MONOMORPHIC, nexus.ic_state()); 1702 1703 Map* map = nexus.FindFirstMap(); 1704 if (map == nullptr) { 1705 // Maps are weakly held in the type feedback vector, we may not have one. 1706 return NoChange(); 1707 } 1708 1709 Handle<Map> receiver_map(map, isolate()); 1710 Handle<Name> cached_name = 1711 handle(Name::cast(nexus.GetFeedbackExtra()), isolate()); 1712 1713 PropertyAccessInfo access_info; 1714 AccessInfoFactory access_info_factory(dependencies(), native_context(), 1715 graph()->zone()); 1716 if (!access_info_factory.ComputePropertyAccessInfo( 1717 receiver_map, cached_name, AccessMode::kStoreInLiteral, 1718 &access_info)) { 1719 return NoChange(); 1720 } 1721 1722 if (access_info.IsGeneric()) { 1723 return NoChange(); 1724 } 1725 1726 Node* receiver = NodeProperties::GetValueInput(node, 0); 1727 Node* effect = NodeProperties::GetEffectInput(node); 1728 Node* control = NodeProperties::GetControlInput(node); 1729 1730 // Monomorphic property access. 1731 receiver = BuildCheckHeapObject(receiver, &effect, control); 1732 1733 effect = 1734 BuildCheckMaps(receiver, effect, control, access_info.receiver_maps()); 1735 1736 // Ensure that {name} matches the cached name. 1737 Node* name = NodeProperties::GetValueInput(node, 1); 1738 Node* check = graph()->NewNode(simplified()->ReferenceEqual(), name, 1739 jsgraph()->HeapConstant(cached_name)); 1740 effect = graph()->NewNode(simplified()->CheckIf(), check, effect, control); 1741 1742 Node* value = NodeProperties::GetValueInput(node, 2); 1743 Node* context = NodeProperties::GetContextInput(node); 1744 Node* frame_state_lazy = NodeProperties::GetFrameStateInput(node); 1745 1746 // Generate the actual property access. 1747 ValueEffectControl continuation = BuildPropertyAccess( 1748 receiver, value, context, frame_state_lazy, effect, control, cached_name, 1749 access_info, AccessMode::kStoreInLiteral, LanguageMode::SLOPPY, 1750 p.feedback().vector(), p.feedback().slot()); 1751 value = continuation.value(); 1752 effect = continuation.effect(); 1753 control = continuation.control(); 1754 1755 ReplaceWithValue(node, value, effect, control); 1756 return Replace(value); 1757 } 1758 1759 namespace { 1760 1761 ExternalArrayType GetArrayTypeFromElementsKind(ElementsKind kind) { 1762 switch (kind) { 1763 #define TYPED_ARRAY_CASE(Type, type, TYPE, ctype, size) \ 1764 case TYPE##_ELEMENTS: \ 1765 return kExternal##Type##Array; 1766 TYPED_ARRAYS(TYPED_ARRAY_CASE) 1767 #undef TYPED_ARRAY_CASE 1768 default: 1769 break; 1770 } 1771 UNREACHABLE(); 1772 return kExternalInt8Array; 1773 } 1774 1775 } // namespace 1776 1777 JSNativeContextSpecialization::ValueEffectControl 1778 JSNativeContextSpecialization::BuildElementAccess( 1779 Node* receiver, Node* index, Node* value, Node* effect, Node* control, 1780 ElementAccessInfo const& access_info, AccessMode access_mode, 1781 KeyedAccessStoreMode store_mode) { 1782 DCHECK_NE(AccessMode::kStoreInLiteral, access_mode); 1783 1784 // TODO(bmeurer): We currently specialize based on elements kind. We should 1785 // also be able to properly support strings and other JSObjects here. 1786 ElementsKind elements_kind = access_info.elements_kind(); 1787 MapList const& receiver_maps = access_info.receiver_maps(); 1788 1789 if (IsFixedTypedArrayElementsKind(elements_kind)) { 1790 Node* buffer; 1791 Node* length; 1792 Node* base_pointer; 1793 Node* external_pointer; 1794 1795 // Check if we can constant-fold information about the {receiver} (i.e. 1796 // for asm.js-like code patterns). 1797 HeapObjectMatcher m(receiver); 1798 if (m.HasValue() && m.Value()->IsJSTypedArray()) { 1799 Handle<JSTypedArray> typed_array = Handle<JSTypedArray>::cast(m.Value()); 1800 1801 // Determine the {receiver}s (known) length. 1802 length = jsgraph()->Constant(typed_array->length_value()); 1803 1804 // Check if the {receiver}s buffer was neutered. 1805 buffer = jsgraph()->HeapConstant(typed_array->GetBuffer()); 1806 1807 // Load the (known) base and external pointer for the {receiver}. The 1808 // {external_pointer} might be invalid if the {buffer} was neutered, so 1809 // we need to make sure that any access is properly guarded. 1810 base_pointer = jsgraph()->ZeroConstant(); 1811 external_pointer = jsgraph()->PointerConstant( 1812 FixedTypedArrayBase::cast(typed_array->elements()) 1813 ->external_pointer()); 1814 } else { 1815 // Load the {receiver}s length. 1816 length = effect = graph()->NewNode( 1817 simplified()->LoadField(AccessBuilder::ForJSTypedArrayLength()), 1818 receiver, effect, control); 1819 1820 // Load the buffer for the {receiver}. 1821 buffer = effect = graph()->NewNode( 1822 simplified()->LoadField(AccessBuilder::ForJSArrayBufferViewBuffer()), 1823 receiver, effect, control); 1824 1825 // Load the elements for the {receiver}. 1826 Node* elements = effect = graph()->NewNode( 1827 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), 1828 receiver, effect, control); 1829 1830 // Load the base and external pointer for the {receiver}s {elements}. 1831 base_pointer = effect = graph()->NewNode( 1832 simplified()->LoadField( 1833 AccessBuilder::ForFixedTypedArrayBaseBasePointer()), 1834 elements, effect, control); 1835 external_pointer = effect = graph()->NewNode( 1836 simplified()->LoadField( 1837 AccessBuilder::ForFixedTypedArrayBaseExternalPointer()), 1838 elements, effect, control); 1839 } 1840 1841 // See if we can skip the neutering check. 1842 if (isolate()->IsArrayBufferNeuteringIntact()) { 1843 // Add a code dependency so we are deoptimized in case an ArrayBuffer 1844 // gets neutered. 1845 dependencies()->AssumePropertyCell( 1846 factory()->array_buffer_neutering_protector()); 1847 } else { 1848 // Default to zero if the {receiver}s buffer was neutered. 1849 Node* check = effect = graph()->NewNode( 1850 simplified()->ArrayBufferWasNeutered(), buffer, effect, control); 1851 length = graph()->NewNode( 1852 common()->Select(MachineRepresentation::kTagged, BranchHint::kFalse), 1853 check, jsgraph()->ZeroConstant(), length); 1854 } 1855 1856 if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { 1857 // Check that the {index} is a valid array index, we do the actual 1858 // bounds check below and just skip the store below if it's out of 1859 // bounds for the {receiver}. 1860 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, 1861 jsgraph()->Constant(Smi::kMaxValue), 1862 effect, control); 1863 } else { 1864 // Check that the {index} is in the valid range for the {receiver}. 1865 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, 1866 length, effect, control); 1867 } 1868 1869 // Access the actual element. 1870 ExternalArrayType external_array_type = 1871 GetArrayTypeFromElementsKind(elements_kind); 1872 switch (access_mode) { 1873 case AccessMode::kLoad: { 1874 value = effect = graph()->NewNode( 1875 simplified()->LoadTypedElement(external_array_type), buffer, 1876 base_pointer, external_pointer, index, effect, control); 1877 break; 1878 } 1879 case AccessMode::kStoreInLiteral: 1880 UNREACHABLE(); 1881 break; 1882 case AccessMode::kStore: { 1883 // Ensure that the {value} is actually a Number. 1884 value = effect = graph()->NewNode(simplified()->CheckNumber(), value, 1885 effect, control); 1886 1887 // Introduce the appropriate truncation for {value}. Currently we 1888 // only need to do this for ClamedUint8Array {receiver}s, as the 1889 // other truncations are implicit in the StoreTypedElement, but we 1890 // might want to change that at some point. 1891 if (external_array_type == kExternalUint8ClampedArray) { 1892 value = graph()->NewNode(simplified()->NumberToUint8Clamped(), value); 1893 } 1894 1895 // Check if we can skip the out-of-bounds store. 1896 if (store_mode == STORE_NO_TRANSITION_IGNORE_OUT_OF_BOUNDS) { 1897 Node* check = 1898 graph()->NewNode(simplified()->NumberLessThan(), index, length); 1899 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), 1900 check, control); 1901 1902 Node* if_true = graph()->NewNode(common()->IfTrue(), branch); 1903 Node* etrue = effect; 1904 { 1905 // Perform the actual store. 1906 etrue = graph()->NewNode( 1907 simplified()->StoreTypedElement(external_array_type), buffer, 1908 base_pointer, external_pointer, index, value, etrue, if_true); 1909 } 1910 1911 Node* if_false = graph()->NewNode(common()->IfFalse(), branch); 1912 Node* efalse = effect; 1913 { 1914 // Just ignore the out-of-bounds write. 1915 } 1916 1917 control = graph()->NewNode(common()->Merge(2), if_true, if_false); 1918 effect = 1919 graph()->NewNode(common()->EffectPhi(2), etrue, efalse, control); 1920 } else { 1921 // Perform the actual store 1922 effect = graph()->NewNode( 1923 simplified()->StoreTypedElement(external_array_type), buffer, 1924 base_pointer, external_pointer, index, value, effect, control); 1925 } 1926 break; 1927 } 1928 } 1929 } else { 1930 // Load the elements for the {receiver}. 1931 Node* elements = effect = graph()->NewNode( 1932 simplified()->LoadField(AccessBuilder::ForJSObjectElements()), receiver, 1933 effect, control); 1934 1935 // Don't try to store to a copy-on-write backing store. 1936 if (access_mode == AccessMode::kStore && 1937 IsFastSmiOrObjectElementsKind(elements_kind) && 1938 store_mode != STORE_NO_TRANSITION_HANDLE_COW) { 1939 effect = graph()->NewNode( 1940 simplified()->CheckMaps( 1941 CheckMapsFlag::kNone, 1942 ZoneHandleSet<Map>(factory()->fixed_array_map())), 1943 elements, effect, control); 1944 } 1945 1946 // Check if the {receiver} is a JSArray. 1947 bool receiver_is_jsarray = HasOnlyJSArrayMaps(receiver_maps); 1948 1949 // Load the length of the {receiver}. 1950 Node* length = effect = 1951 receiver_is_jsarray 1952 ? graph()->NewNode( 1953 simplified()->LoadField( 1954 AccessBuilder::ForJSArrayLength(elements_kind)), 1955 receiver, effect, control) 1956 : graph()->NewNode( 1957 simplified()->LoadField(AccessBuilder::ForFixedArrayLength()), 1958 elements, effect, control); 1959 1960 // Check if we might need to grow the {elements} backing store. 1961 if (IsGrowStoreMode(store_mode)) { 1962 DCHECK_EQ(AccessMode::kStore, access_mode); 1963 1964 // Check that the {index} is a valid array index; the actual checking 1965 // happens below right before the element store. 1966 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, 1967 jsgraph()->Constant(Smi::kMaxValue), 1968 effect, control); 1969 } else { 1970 // Check that the {index} is in the valid range for the {receiver}. 1971 index = effect = graph()->NewNode(simplified()->CheckBounds(), index, 1972 length, effect, control); 1973 } 1974 1975 // Compute the element access. 1976 Type* element_type = Type::NonInternal(); 1977 MachineType element_machine_type = MachineType::AnyTagged(); 1978 if (IsFastDoubleElementsKind(elements_kind)) { 1979 element_type = Type::Number(); 1980 element_machine_type = MachineType::Float64(); 1981 } else if (IsFastSmiElementsKind(elements_kind)) { 1982 element_type = Type::SignedSmall(); 1983 element_machine_type = MachineType::TaggedSigned(); 1984 } 1985 ElementAccess element_access = {kTaggedBase, FixedArray::kHeaderSize, 1986 element_type, element_machine_type, 1987 kFullWriteBarrier}; 1988 1989 // Access the actual element. 1990 if (access_mode == AccessMode::kLoad) { 1991 // Compute the real element access type, which includes the hole in case 1992 // of holey backing stores. 1993 if (elements_kind == FAST_HOLEY_ELEMENTS || 1994 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { 1995 element_access.type = 1996 Type::Union(element_type, Type::Hole(), graph()->zone()); 1997 element_access.machine_type = MachineType::AnyTagged(); 1998 } 1999 // Perform the actual backing store access. 2000 value = effect = 2001 graph()->NewNode(simplified()->LoadElement(element_access), elements, 2002 index, effect, control); 2003 // Handle loading from holey backing stores correctly, by either mapping 2004 // the hole to undefined if possible, or deoptimizing otherwise. 2005 if (elements_kind == FAST_HOLEY_ELEMENTS || 2006 elements_kind == FAST_HOLEY_SMI_ELEMENTS) { 2007 // Check if we are allowed to turn the hole into undefined. 2008 if (CanTreatHoleAsUndefined(receiver_maps)) { 2009 // Turn the hole into undefined. 2010 value = graph()->NewNode(simplified()->ConvertTaggedHoleToUndefined(), 2011 value); 2012 } else { 2013 // Bailout if we see the hole. 2014 value = effect = graph()->NewNode(simplified()->CheckTaggedHole(), 2015 value, effect, control); 2016 } 2017 } else if (elements_kind == FAST_HOLEY_DOUBLE_ELEMENTS) { 2018 // Perform the hole check on the result. 2019 CheckFloat64HoleMode mode = CheckFloat64HoleMode::kNeverReturnHole; 2020 // Check if we are allowed to return the hole directly. 2021 if (CanTreatHoleAsUndefined(receiver_maps)) { 2022 // Return the signaling NaN hole directly if all uses are truncating. 2023 mode = CheckFloat64HoleMode::kAllowReturnHole; 2024 } 2025 value = effect = graph()->NewNode(simplified()->CheckFloat64Hole(mode), 2026 value, effect, control); 2027 } 2028 } else { 2029 DCHECK_EQ(AccessMode::kStore, access_mode); 2030 if (IsFastSmiElementsKind(elements_kind)) { 2031 value = effect = 2032 graph()->NewNode(simplified()->CheckSmi(), value, effect, control); 2033 } else if (IsFastDoubleElementsKind(elements_kind)) { 2034 value = effect = graph()->NewNode(simplified()->CheckNumber(), value, 2035 effect, control); 2036 // Make sure we do not store signalling NaNs into double arrays. 2037 value = graph()->NewNode(simplified()->NumberSilenceNaN(), value); 2038 } 2039 2040 // Ensure that copy-on-write backing store is writable. 2041 if (IsFastSmiOrObjectElementsKind(elements_kind) && 2042 store_mode == STORE_NO_TRANSITION_HANDLE_COW) { 2043 elements = effect = 2044 graph()->NewNode(simplified()->EnsureWritableFastElements(), 2045 receiver, elements, effect, control); 2046 } else if (IsGrowStoreMode(store_mode)) { 2047 // Grow {elements} backing store if necessary. Also updates the 2048 // "length" property for JSArray {receiver}s, hence there must 2049 // not be any other check after this operation, as the write 2050 // to the "length" property is observable. 2051 GrowFastElementsFlags flags = GrowFastElementsFlag::kNone; 2052 if (receiver_is_jsarray) { 2053 flags |= GrowFastElementsFlag::kArrayObject; 2054 } 2055 if (IsHoleyElementsKind(elements_kind)) { 2056 flags |= GrowFastElementsFlag::kHoleyElements; 2057 } 2058 if (IsFastDoubleElementsKind(elements_kind)) { 2059 flags |= GrowFastElementsFlag::kDoubleElements; 2060 } 2061 elements = effect = graph()->NewNode( 2062 simplified()->MaybeGrowFastElements(flags), receiver, elements, 2063 index, length, effect, control); 2064 } 2065 2066 // Perform the actual element access. 2067 effect = graph()->NewNode(simplified()->StoreElement(element_access), 2068 elements, index, value, effect, control); 2069 } 2070 } 2071 2072 return ValueEffectControl(value, effect, control); 2073 } 2074 2075 JSNativeContextSpecialization::ValueEffectControl 2076 JSNativeContextSpecialization::InlineApiCall( 2077 Node* receiver, Node* context, Node* target, Node* frame_state, Node* value, 2078 Node* effect, Node* control, Handle<SharedFunctionInfo> shared_info, 2079 Handle<FunctionTemplateInfo> function_template_info) { 2080 Handle<CallHandlerInfo> call_handler_info = handle( 2081 CallHandlerInfo::cast(function_template_info->call_code()), isolate()); 2082 Handle<Object> call_data_object(call_handler_info->data(), isolate()); 2083 2084 // Only setters have a value. 2085 int const argc = value == nullptr ? 0 : 1; 2086 // The stub always expects the receiver as the first param on the stack. 2087 CallApiCallbackStub stub( 2088 isolate(), argc, call_data_object->IsUndefined(isolate()), 2089 true /* FunctionTemplateInfo doesn't have an associated context. */); 2090 CallInterfaceDescriptor call_interface_descriptor = 2091 stub.GetCallInterfaceDescriptor(); 2092 CallDescriptor* call_descriptor = Linkage::GetStubCallDescriptor( 2093 isolate(), graph()->zone(), call_interface_descriptor, 2094 call_interface_descriptor.GetStackParameterCount() + argc + 2095 1 /* implicit receiver */, 2096 CallDescriptor::kNeedsFrameState, Operator::kNoProperties, 2097 MachineType::AnyTagged(), 1); 2098 2099 Node* data = jsgraph()->Constant(call_data_object); 2100 ApiFunction function(v8::ToCData<Address>(call_handler_info->callback())); 2101 Node* function_reference = 2102 graph()->NewNode(common()->ExternalConstant(ExternalReference( 2103 &function, ExternalReference::DIRECT_API_CALL, isolate()))); 2104 Node* code = jsgraph()->HeapConstant(stub.GetCode()); 2105 2106 // Add CallApiCallbackStub's register argument as well. 2107 Node* inputs[11] = { 2108 code, target, data, receiver /* holder */, function_reference, receiver}; 2109 int index = 6 + argc; 2110 inputs[index++] = context; 2111 inputs[index++] = frame_state; 2112 inputs[index++] = effect; 2113 inputs[index++] = control; 2114 // This needs to stay here because of the edge case described in 2115 // http://crbug.com/675648. 2116 if (value != nullptr) { 2117 inputs[6] = value; 2118 } 2119 2120 Node* effect0; 2121 Node* value0 = effect0 = 2122 graph()->NewNode(common()->Call(call_descriptor), index, inputs); 2123 Node* control0 = graph()->NewNode(common()->IfSuccess(), value0); 2124 return ValueEffectControl(value0, effect0, control0); 2125 } 2126 2127 Node* JSNativeContextSpecialization::BuildCheckHeapObject(Node* receiver, 2128 Node** effect, 2129 Node* control) { 2130 switch (receiver->opcode()) { 2131 case IrOpcode::kHeapConstant: 2132 case IrOpcode::kJSCreate: 2133 case IrOpcode::kJSCreateArguments: 2134 case IrOpcode::kJSCreateArray: 2135 case IrOpcode::kJSCreateClosure: 2136 case IrOpcode::kJSCreateIterResultObject: 2137 case IrOpcode::kJSCreateLiteralArray: 2138 case IrOpcode::kJSCreateLiteralObject: 2139 case IrOpcode::kJSCreateLiteralRegExp: 2140 case IrOpcode::kJSConvertReceiver: 2141 case IrOpcode::kJSToName: 2142 case IrOpcode::kJSToString: 2143 case IrOpcode::kJSToObject: 2144 case IrOpcode::kJSTypeOf: { 2145 return receiver; 2146 } 2147 default: { 2148 return *effect = graph()->NewNode(simplified()->CheckHeapObject(), 2149 receiver, *effect, control); 2150 } 2151 } 2152 } 2153 2154 Node* JSNativeContextSpecialization::BuildCheckMaps( 2155 Node* receiver, Node* effect, Node* control, 2156 std::vector<Handle<Map>> const& receiver_maps) { 2157 HeapObjectMatcher m(receiver); 2158 if (m.HasValue()) { 2159 Handle<Map> receiver_map(m.Value()->map(), isolate()); 2160 if (receiver_map->is_stable()) { 2161 for (Handle<Map> map : receiver_maps) { 2162 if (map.is_identical_to(receiver_map)) { 2163 dependencies()->AssumeMapStable(receiver_map); 2164 return effect; 2165 } 2166 } 2167 } 2168 } 2169 ZoneHandleSet<Map> maps; 2170 CheckMapsFlags flags = CheckMapsFlag::kNone; 2171 for (Handle<Map> map : receiver_maps) { 2172 maps.insert(map, graph()->zone()); 2173 if (map->is_migration_target()) { 2174 flags |= CheckMapsFlag::kTryMigrateInstance; 2175 } 2176 } 2177 return graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver, 2178 effect, control); 2179 } 2180 2181 void JSNativeContextSpecialization::AssumePrototypesStable( 2182 std::vector<Handle<Map>> const& receiver_maps, Handle<JSObject> holder) { 2183 // Determine actual holder and perform prototype chain checks. 2184 for (auto map : receiver_maps) { 2185 // Perform the implicit ToObject for primitives here. 2186 // Implemented according to ES6 section 7.3.2 GetV (V, P). 2187 Handle<JSFunction> constructor; 2188 if (Map::GetConstructorFunction(map, native_context()) 2189 .ToHandle(&constructor)) { 2190 map = handle(constructor->initial_map(), isolate()); 2191 } 2192 dependencies()->AssumePrototypeMapsStable(map, holder); 2193 } 2194 } 2195 2196 bool JSNativeContextSpecialization::CanTreatHoleAsUndefined( 2197 std::vector<Handle<Map>> const& receiver_maps) { 2198 // Check if the array prototype chain is intact. 2199 if (!isolate()->IsFastArrayConstructorPrototypeChainIntact()) return false; 2200 2201 // Make sure both the initial Array and Object prototypes are stable. 2202 Handle<JSObject> initial_array_prototype( 2203 native_context()->initial_array_prototype(), isolate()); 2204 Handle<JSObject> initial_object_prototype( 2205 native_context()->initial_object_prototype(), isolate()); 2206 if (!initial_array_prototype->map()->is_stable() || 2207 !initial_object_prototype->map()->is_stable()) { 2208 return false; 2209 } 2210 2211 // Check if all {receiver_maps} either have the initial Array.prototype 2212 // or the initial Object.prototype as their prototype, as those are 2213 // guarded by the array protector cell. 2214 for (Handle<Map> map : receiver_maps) { 2215 if (map->prototype() != *initial_array_prototype && 2216 map->prototype() != *initial_object_prototype) { 2217 return false; 2218 } 2219 } 2220 2221 // Install code dependencies on the prototype maps. 2222 for (Handle<Map> map : receiver_maps) { 2223 dependencies()->AssumePrototypeMapsStable(map, initial_object_prototype); 2224 } 2225 2226 // Install code dependency on the array protector cell. 2227 dependencies()->AssumePropertyCell(factory()->array_protector()); 2228 return true; 2229 } 2230 2231 bool JSNativeContextSpecialization::ExtractReceiverMaps( 2232 Node* receiver, Node* effect, FeedbackNexus const& nexus, 2233 MapHandleList* receiver_maps) { 2234 DCHECK_EQ(0, receiver_maps->length()); 2235 // See if we can infer a concrete type for the {receiver}. 2236 if (InferReceiverMaps(receiver, effect, receiver_maps)) { 2237 // We can assume that the {receiver} still has the infered {receiver_maps}. 2238 return true; 2239 } 2240 // Try to extract some maps from the {nexus}. 2241 if (nexus.ExtractMaps(receiver_maps) != 0) { 2242 // Try to filter impossible candidates based on infered root map. 2243 Handle<Map> receiver_map; 2244 if (InferReceiverRootMap(receiver).ToHandle(&receiver_map)) { 2245 for (int i = receiver_maps->length(); --i >= 0;) { 2246 if (receiver_maps->at(i)->FindRootMap() != *receiver_map) { 2247 receiver_maps->Remove(i); 2248 } 2249 } 2250 } 2251 return true; 2252 } 2253 return false; 2254 } 2255 2256 bool JSNativeContextSpecialization::InferReceiverMaps( 2257 Node* receiver, Node* effect, MapHandleList* receiver_maps) { 2258 ZoneHandleSet<Map> maps; 2259 NodeProperties::InferReceiverMapsResult result = 2260 NodeProperties::InferReceiverMaps(receiver, effect, &maps); 2261 if (result == NodeProperties::kReliableReceiverMaps) { 2262 for (size_t i = 0; i < maps.size(); ++i) { 2263 receiver_maps->Add(maps[i]); 2264 } 2265 return true; 2266 } else if (result == NodeProperties::kUnreliableReceiverMaps) { 2267 // For untrusted receiver maps, we can still use the information 2268 // if the maps are stable. 2269 for (size_t i = 0; i < maps.size(); ++i) { 2270 if (!maps[i]->is_stable()) return false; 2271 } 2272 for (size_t i = 0; i < maps.size(); ++i) { 2273 receiver_maps->Add(maps[i]); 2274 } 2275 return true; 2276 } 2277 return false; 2278 } 2279 2280 MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap( 2281 Node* receiver) { 2282 HeapObjectMatcher m(receiver); 2283 if (m.HasValue()) { 2284 return handle(m.Value()->map()->FindRootMap(), isolate()); 2285 } else if (m.IsJSCreate()) { 2286 HeapObjectMatcher mtarget(m.InputAt(0)); 2287 HeapObjectMatcher mnewtarget(m.InputAt(1)); 2288 if (mtarget.HasValue() && mnewtarget.HasValue()) { 2289 Handle<JSFunction> constructor = 2290 Handle<JSFunction>::cast(mtarget.Value()); 2291 if (constructor->has_initial_map()) { 2292 Handle<Map> initial_map(constructor->initial_map(), isolate()); 2293 if (initial_map->constructor_or_backpointer() == *mnewtarget.Value()) { 2294 DCHECK_EQ(*initial_map, initial_map->FindRootMap()); 2295 return initial_map; 2296 } 2297 } 2298 } 2299 } 2300 return MaybeHandle<Map>(); 2301 } 2302 2303 bool JSNativeContextSpecialization::LookupInScriptContextTable( 2304 Handle<Name> name, ScriptContextTableLookupResult* result) { 2305 if (!name->IsString()) return false; 2306 Handle<ScriptContextTable> script_context_table( 2307 global_object()->native_context()->script_context_table(), isolate()); 2308 ScriptContextTable::LookupResult lookup_result; 2309 if (!ScriptContextTable::Lookup(script_context_table, 2310 Handle<String>::cast(name), &lookup_result)) { 2311 return false; 2312 } 2313 Handle<Context> script_context = ScriptContextTable::GetContext( 2314 script_context_table, lookup_result.context_index); 2315 result->context = script_context; 2316 result->immutable = lookup_result.mode == CONST; 2317 result->index = lookup_result.slot_index; 2318 return true; 2319 } 2320 2321 Graph* JSNativeContextSpecialization::graph() const { 2322 return jsgraph()->graph(); 2323 } 2324 2325 Isolate* JSNativeContextSpecialization::isolate() const { 2326 return jsgraph()->isolate(); 2327 } 2328 2329 Factory* JSNativeContextSpecialization::factory() const { 2330 return isolate()->factory(); 2331 } 2332 2333 MachineOperatorBuilder* JSNativeContextSpecialization::machine() const { 2334 return jsgraph()->machine(); 2335 } 2336 2337 CommonOperatorBuilder* JSNativeContextSpecialization::common() const { 2338 return jsgraph()->common(); 2339 } 2340 2341 JSOperatorBuilder* JSNativeContextSpecialization::javascript() const { 2342 return jsgraph()->javascript(); 2343 } 2344 2345 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { 2346 return jsgraph()->simplified(); 2347 } 2348 2349 } // namespace compiler 2350 } // namespace internal 2351 } // namespace v8 2352