Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 the V8 project authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "src/compiler/js-native-context-specialization.h" | 5 #include "src/compiler/js-native-context-specialization.h" |
| 6 | 6 |
| 7 #include "src/accessors.h" | 7 #include "src/accessors.h" |
| 8 #include "src/compilation-dependencies.h" | 8 #include "src/compilation-dependencies.h" |
| 9 #include "src/compiler/access-builder.h" | 9 #include "src/compiler/access-builder.h" |
| 10 #include "src/compiler/js-graph.h" | 10 #include "src/compiler/js-graph.h" |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 39 | 39 |
| 40 | 40 |
| 41 Reduction JSNativeContextSpecialization::Reduce(Node* node) { | 41 Reduction JSNativeContextSpecialization::Reduce(Node* node) { |
| 42 switch (node->opcode()) { | 42 switch (node->opcode()) { |
| 43 case IrOpcode::kJSLoadGlobal: | 43 case IrOpcode::kJSLoadGlobal: |
| 44 return ReduceJSLoadGlobal(node); | 44 return ReduceJSLoadGlobal(node); |
| 45 case IrOpcode::kJSStoreGlobal: | 45 case IrOpcode::kJSStoreGlobal: |
| 46 return ReduceJSStoreGlobal(node); | 46 return ReduceJSStoreGlobal(node); |
| 47 case IrOpcode::kJSLoadNamed: | 47 case IrOpcode::kJSLoadNamed: |
| 48 return ReduceJSLoadNamed(node); | 48 return ReduceJSLoadNamed(node); |
| 49 case IrOpcode::kJSStoreNamed: | |
| 50 return ReduceJSStoreNamed(node); | |
| 49 default: | 51 default: |
| 50 break; | 52 break; |
| 51 } | 53 } |
| 52 return NoChange(); | 54 return NoChange(); |
| 53 } | 55 } |
| 54 | 56 |
| 55 | 57 |
| 56 Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { | 58 Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { |
| 57 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); | 59 DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); |
| 58 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); | 60 Handle<Name> name = LoadGlobalParametersOf(node->op()).name(); |
| (...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 236 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), | 238 simplified()->StoreField(AccessBuilder::ForPropertyCellValue()), |
| 237 jsgraph()->Constant(property_cell), value, effect, control); | 239 jsgraph()->Constant(property_cell), value, effect, control); |
| 238 return Replace(node, value, effect, control); | 240 return Replace(node, value, effect, control); |
| 239 } | 241 } |
| 240 | 242 |
| 241 | 243 |
| 242 // This class encapsulates all information required to access a certain | 244 // This class encapsulates all information required to access a certain |
| 243 // object property, either on the object itself or on the prototype chain. | 245 // object property, either on the object itself or on the prototype chain. |
| 244 class JSNativeContextSpecialization::PropertyAccessInfo final { | 246 class JSNativeContextSpecialization::PropertyAccessInfo final { |
| 245 public: | 247 public: |
| 246 enum Kind { kInvalid, kData, kDataConstant }; | 248 enum Kind { kInvalid, kDataConstant, kDataField }; |
| 247 | 249 |
| 248 static PropertyAccessInfo DataConstant(Type* receiver_type, | 250 static PropertyAccessInfo DataConstant(Type* receiver_type, |
| 249 Handle<Object> constant, | 251 Handle<Object> constant, |
| 250 MaybeHandle<JSObject> holder) { | 252 MaybeHandle<JSObject> holder) { |
| 251 return PropertyAccessInfo(holder, constant, receiver_type); | 253 return PropertyAccessInfo(holder, constant, receiver_type); |
| 252 } | 254 } |
| 253 static PropertyAccessInfo Data(Type* receiver_type, FieldIndex field_index, | 255 static PropertyAccessInfo DataField(Type* receiver_type, |
| 254 Representation field_representation, | 256 FieldIndex field_index, Type* field_type, |
| 255 Type* field_type, | 257 MaybeHandle<JSObject> holder) { |
| 256 MaybeHandle<JSObject> holder) { | 258 return PropertyAccessInfo(holder, field_index, field_type, receiver_type); |
| 257 return PropertyAccessInfo(holder, field_index, field_representation, | |
| 258 field_type, receiver_type); | |
| 259 } | 259 } |
| 260 | 260 |
| 261 PropertyAccessInfo() : kind_(kInvalid) {} | 261 PropertyAccessInfo() : kind_(kInvalid) {} |
| 262 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, | 262 PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, |
| 263 Type* receiver_type) | 263 Type* receiver_type) |
| 264 : kind_(kDataConstant), | 264 : kind_(kDataConstant), |
| 265 receiver_type_(receiver_type), | 265 receiver_type_(receiver_type), |
| 266 constant_(constant), | 266 constant_(constant), |
| 267 holder_(holder) {} | 267 holder_(holder) {} |
| 268 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, | 268 PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, |
| 269 Representation field_representation, Type* field_type, | 269 Type* field_type, Type* receiver_type) |
| 270 Type* receiver_type) | 270 : kind_(kDataField), |
| 271 : kind_(kData), | |
| 272 receiver_type_(receiver_type), | 271 receiver_type_(receiver_type), |
| 273 holder_(holder), | 272 holder_(holder), |
| 274 field_index_(field_index), | 273 field_index_(field_index), |
| 275 field_representation_(field_representation), | |
| 276 field_type_(field_type) {} | 274 field_type_(field_type) {} |
| 277 | 275 |
| 278 bool IsDataConstant() const { return kind() == kDataConstant; } | 276 bool IsDataConstant() const { return kind() == kDataConstant; } |
| 279 bool IsData() const { return kind() == kData; } | 277 bool IsDataField() const { return kind() == kDataField; } |
| 280 | 278 |
| 281 Kind kind() const { return kind_; } | 279 Kind kind() const { return kind_; } |
| 282 MaybeHandle<JSObject> holder() const { return holder_; } | 280 MaybeHandle<JSObject> holder() const { return holder_; } |
| 283 Handle<Object> constant() const { return constant_; } | 281 Handle<Object> constant() const { return constant_; } |
| 284 FieldIndex field_index() const { return field_index_; } | 282 FieldIndex field_index() const { return field_index_; } |
| 285 Representation field_representation() const { return field_representation_; } | |
| 286 Type* field_type() const { return field_type_; } | 283 Type* field_type() const { return field_type_; } |
| 287 Type* receiver_type() const { return receiver_type_; } | 284 Type* receiver_type() const { return receiver_type_; } |
| 288 | 285 |
| 289 private: | 286 private: |
| 290 Kind kind_; | 287 Kind kind_; |
| 291 Type* receiver_type_; | 288 Type* receiver_type_; |
| 292 Handle<Object> constant_; | 289 Handle<Object> constant_; |
| 293 MaybeHandle<JSObject> holder_; | 290 MaybeHandle<JSObject> holder_; |
| 294 FieldIndex field_index_; | 291 FieldIndex field_index_; |
| 295 Representation field_representation_; | |
| 296 Type* field_type_ = Type::Any(); | 292 Type* field_type_ = Type::Any(); |
| 297 }; | 293 }; |
| 298 | 294 |
| 299 | 295 |
| 300 namespace { | 296 namespace { |
| 301 | 297 |
| 302 bool CanInlinePropertyAccess(Handle<Map> map) { | 298 bool CanInlinePropertyAccess(Handle<Map> map) { |
| 303 // TODO(bmeurer): Do something about the number stuff. | 299 // TODO(bmeurer): Do something about the number stuff. |
| 304 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; | 300 if (map->instance_type() == HEAP_NUMBER_TYPE) return false; |
| 305 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; | 301 if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; |
| 306 return map->IsJSObjectMap() && !map->is_dictionary_map() && | 302 return map->IsJSObjectMap() && !map->is_dictionary_map() && |
| 307 !map->has_named_interceptor() && | 303 !map->has_named_interceptor() && |
| 308 // TODO(verwaest): Whitelist contexts to which we have access. | 304 // TODO(verwaest): Whitelist contexts to which we have access. |
| 309 !map->is_access_check_needed(); | 305 !map->is_access_check_needed(); |
| 310 } | 306 } |
| 311 | 307 |
| 312 } // namespace | 308 } // namespace |
| 313 | 309 |
| 314 | 310 |
| 315 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( | 311 bool JSNativeContextSpecialization::ComputePropertyAccessInfo( |
| 316 Handle<Map> map, Handle<Name> name, PropertyAccessInfo* access_info) { | 312 Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, |
| 313 PropertyAccessInfo* access_info) { | |
| 317 MaybeHandle<JSObject> holder; | 314 MaybeHandle<JSObject> holder; |
| 318 Type* receiver_type = Type::Class(map, graph()->zone()); | 315 Type* receiver_type = Type::Class(map, graph()->zone()); |
| 319 while (CanInlinePropertyAccess(map)) { | 316 while (CanInlinePropertyAccess(map)) { |
| 320 // Check for special JSObject field accessors. | 317 // Check for special JSObject field accessors. |
| 321 int offset; | 318 int offset; |
| 322 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { | 319 if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { |
| 320 // Don't bother optimizing stores to special JSObject field accessors. | |
| 321 if (access_mode == kStore) { | |
| 322 break; | |
| 323 } | |
| 323 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); | 324 FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); |
| 324 Representation field_representation = Representation::Tagged(); | |
| 325 Type* field_type = Type::Tagged(); | 325 Type* field_type = Type::Tagged(); |
| 326 if (map->IsStringMap()) { | 326 if (map->IsStringMap()) { |
| 327 DCHECK(Name::Equals(factory()->length_string(), name)); | 327 DCHECK(Name::Equals(factory()->length_string(), name)); |
| 328 // The String::length property is always a smi in the range | 328 // The String::length property is always a smi in the range |
| 329 // [0, String::kMaxLength]. | 329 // [0, String::kMaxLength]. |
| 330 field_representation = Representation::Smi(); | |
| 331 field_type = Type::Intersect( | 330 field_type = Type::Intersect( |
| 332 Type::Range(0.0, String::kMaxLength, graph()->zone()), | 331 Type::Range(0.0, String::kMaxLength, graph()->zone()), |
| 333 Type::TaggedSigned(), graph()->zone()); | 332 Type::TaggedSigned(), graph()->zone()); |
| 334 } else if (map->IsJSArrayMap()) { | 333 } else if (map->IsJSArrayMap()) { |
| 335 DCHECK(Name::Equals(factory()->length_string(), name)); | 334 DCHECK(Name::Equals(factory()->length_string(), name)); |
| 336 // The JSArray::length property is a smi in the range | 335 // The JSArray::length property is a smi in the range |
| 337 // [0, FixedDoubleArray::kMaxLength] in case of fast double | 336 // [0, FixedDoubleArray::kMaxLength] in case of fast double |
| 338 // elements, a smi in the range [0, FixedArray::kMaxLength] | 337 // elements, a smi in the range [0, FixedArray::kMaxLength] |
| 339 // in case of other fast elements, and [0, kMaxUInt32-1] in | 338 // in case of other fast elements, and [0, kMaxUInt32-1] in |
| 340 // case of other arrays. | 339 // case of other arrays. |
| 341 double field_type_upper = kMaxUInt32 - 1; | 340 double field_type_upper = kMaxUInt32 - 1; |
| 342 if (IsFastElementsKind(map->elements_kind())) { | 341 if (IsFastElementsKind(map->elements_kind())) { |
| 343 field_representation = Representation::Smi(); | |
| 344 field_type_upper = IsFastDoubleElementsKind(map->elements_kind()) | 342 field_type_upper = IsFastDoubleElementsKind(map->elements_kind()) |
| 345 ? FixedDoubleArray::kMaxLength | 343 ? FixedDoubleArray::kMaxLength |
| 346 : FixedArray::kMaxLength; | 344 : FixedArray::kMaxLength; |
| 347 } | 345 } |
| 348 field_type = | 346 field_type = |
| 349 Type::Intersect(Type::Range(0.0, field_type_upper, graph()->zone()), | 347 Type::Intersect(Type::Range(0.0, field_type_upper, graph()->zone()), |
| 350 Type::TaggedSigned(), graph()->zone()); | 348 Type::TaggedSigned(), graph()->zone()); |
| 351 } | 349 } |
| 352 *access_info = PropertyAccessInfo::Data( | 350 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, |
| 353 receiver_type, field_index, field_representation, field_type, holder); | 351 field_type, holder); |
| 354 return true; | 352 return true; |
| 355 } | 353 } |
| 356 | 354 |
| 357 // Lookup the named property on the {map}. | 355 // Lookup the named property on the {map}. |
| 358 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); | 356 Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); |
| 359 int const number = descriptors->SearchWithCache(*name, *map); | 357 int const number = descriptors->SearchWithCache(*name, *map); |
| 360 if (number != DescriptorArray::kNotFound) { | 358 if (number != DescriptorArray::kNotFound) { |
| 361 PropertyDetails const details = descriptors->GetDetails(number); | 359 PropertyDetails const details = descriptors->GetDetails(number); |
| 362 if (details.type() == DATA_CONSTANT) { | 360 if (details.type() == DATA_CONSTANT) { |
| 363 *access_info = PropertyAccessInfo::DataConstant( | 361 *access_info = PropertyAccessInfo::DataConstant( |
| 364 receiver_type, handle(descriptors->GetValue(number), isolate()), | 362 receiver_type, handle(descriptors->GetValue(number), isolate()), |
| 365 holder); | 363 holder); |
| 366 return true; | 364 return true; |
| 367 } else if (details.type() == DATA) { | 365 } else if (details.type() == DATA) { |
| 366 // Don't bother optimizing stores to read-only properties. | |
| 367 if (access_mode == kStore) { | |
| 368 break; | |
| 369 } | |
| 368 int index = descriptors->GetFieldIndex(number); | 370 int index = descriptors->GetFieldIndex(number); |
| 369 Representation field_representation = details.representation(); | 371 Representation field_representation = details.representation(); |
| 370 FieldIndex field_index = FieldIndex::ForPropertyIndex( | 372 FieldIndex field_index = FieldIndex::ForPropertyIndex( |
| 371 *map, index, field_representation.IsDouble()); | 373 *map, index, field_representation.IsDouble()); |
| 372 Type* field_type = Type::Any(); | 374 Type* field_type = Type::Any(); |
| 373 if (field_representation.IsSmi()) { | 375 if (field_representation.IsSmi()) { |
| 374 field_type = Type::Intersect(Type::SignedSmall(), | 376 field_type = Type::Intersect(Type::SignedSmall(), |
| 375 Type::TaggedSigned(), graph()->zone()); | 377 Type::TaggedSigned(), graph()->zone()); |
| 376 } else if (field_representation.IsDouble()) { | 378 } else if (field_representation.IsDouble()) { |
| 379 if (access_mode == kStore) { | |
| 380 // TODO(bmeurer): Add support for storing to double fields. | |
| 381 break; | |
| 382 } | |
| 377 field_type = Type::Intersect(Type::Number(), Type::UntaggedFloat64(), | 383 field_type = Type::Intersect(Type::Number(), Type::UntaggedFloat64(), |
| 378 graph()->zone()); | 384 graph()->zone()); |
| 379 } else if (field_representation.IsHeapObject()) { | 385 } else if (field_representation.IsHeapObject()) { |
| 380 field_type = Type::TaggedPointer(); | 386 // Extract the field type from the property details (make sure its |
| 387 // representation is TaggedPointer to reflect the heap object case). | |
| 388 field_type = Type::Intersect( | |
| 389 Type::Convert<HeapType>( | |
| 390 handle(descriptors->GetFieldType(number), isolate()), | |
| 391 graph()->zone()), | |
| 392 Type::TaggedPointer(), graph()->zone()); | |
| 393 if (field_type->Is(Type::None())) { | |
| 394 if (access_mode == kStore) { | |
| 395 // Store is not safe if the field type was cleared. | |
| 396 break; | |
| 397 } | |
| 398 | |
| 399 // The field type was cleared by the GC, so we don't know anything | |
| 400 // about the contents now. | |
| 401 // TODO(bmeurer): It would be awesome to make this saner in the | |
| 402 // runtime/GC interaction. | |
| 403 field_type = Type::TaggedPointer(); | |
| 404 } else { | |
| 405 // Add proper code dependencies in case of stable field map(s). | |
| 406 if (field_type->NumClasses() > 0 && field_type->NowStable()) { | |
| 407 dependencies()->AssumeFieldType( | |
| 408 handle(map->FindFieldOwner(number), isolate())); | |
| 409 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { | |
| 410 dependencies()->AssumeMapStable(i.Current()); | |
| 411 } | |
| 412 } else { | |
| 413 field_type = Type::TaggedPointer(); | |
| 414 } | |
| 415 } | |
| 416 DCHECK(field_type->Is(Type::TaggedPointer())); | |
| 381 } | 417 } |
| 382 *access_info = | 418 *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, |
| 383 PropertyAccessInfo::Data(receiver_type, field_index, | 419 field_type, holder); |
| 384 field_representation, field_type, holder); | |
| 385 return true; | 420 return true; |
| 386 } else { | 421 } else { |
| 387 // TODO(bmeurer): Add support for accessors. | 422 // TODO(bmeurer): Add support for accessors. |
| 388 break; | 423 break; |
| 389 } | 424 } |
| 390 } | 425 } |
| 391 | 426 |
| 392 // Don't search on the prototype chain for special indices in case of | 427 // Don't search on the prototype chain for special indices in case of |
| 393 // integer indexed exotic objects (see ES6 section 9.4.5). | 428 // integer indexed exotic objects (see ES6 section 9.4.5). |
| 394 if (map->IsJSTypedArrayMap() && name->IsString() && | 429 if (map->IsJSTypedArrayMap() && name->IsString() && |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 409 } | 444 } |
| 410 map = handle(map_prototype->map(), isolate()); | 445 map = handle(map_prototype->map(), isolate()); |
| 411 holder = map_prototype; | 446 holder = map_prototype; |
| 412 } | 447 } |
| 413 return false; | 448 return false; |
| 414 } | 449 } |
| 415 | 450 |
| 416 | 451 |
| 417 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( | 452 bool JSNativeContextSpecialization::ComputePropertyAccessInfos( |
| 418 MapHandleList const& maps, Handle<Name> name, | 453 MapHandleList const& maps, Handle<Name> name, |
| 454 PropertyAccessMode access_mode, | |
| 419 ZoneVector<PropertyAccessInfo>* access_infos) { | 455 ZoneVector<PropertyAccessInfo>* access_infos) { |
| 420 for (Handle<Map> map : maps) { | 456 for (Handle<Map> map : maps) { |
| 421 PropertyAccessInfo access_info; | 457 PropertyAccessInfo access_info; |
| 422 if (!ComputePropertyAccessInfo(map, name, &access_info)) return false; | 458 if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { |
| 459 return false; | |
| 460 } | |
| 423 access_infos->push_back(access_info); | 461 access_infos->push_back(access_info); |
| 424 } | 462 } |
| 425 return true; | 463 return true; |
| 426 } | 464 } |
| 427 | 465 |
| 428 | 466 |
| 429 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { | 467 Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
| 430 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); | 468 DCHECK_EQ(IrOpcode::kJSLoadNamed, node->opcode()); |
| 431 LoadNamedParameters const p = LoadNamedParametersOf(node->op()); | 469 LoadNamedParameters const p = LoadNamedParametersOf(node->op()); |
| 432 Handle<Name> name = p.name(); | 470 Handle<Name> name = p.name(); |
| 433 Node* receiver = NodeProperties::GetValueInput(node, 0); | 471 Node* receiver = NodeProperties::GetValueInput(node, 0); |
| 434 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | 472 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); |
| 435 Node* effect = NodeProperties::GetEffectInput(node); | 473 Node* effect = NodeProperties::GetEffectInput(node); |
| 436 Node* control = NodeProperties::GetControlInput(node); | 474 Node* control = NodeProperties::GetControlInput(node); |
| 437 | 475 |
| 438 // Not much we can do if deoptimization support is disabled. | 476 // Not much we can do if deoptimization support is disabled. |
| 439 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | 477 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); |
| 440 | 478 |
| 441 // Extract receiver maps from the LOAD_IC using the LoadICNexus. | 479 // Extract receiver maps from the LOAD_IC using the LoadICNexus. |
| 442 MapHandleList receiver_maps; | 480 MapHandleList receiver_maps; |
| 443 if (!p.feedback().IsValid()) return NoChange(); | 481 if (!p.feedback().IsValid()) return NoChange(); |
| 444 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); | 482 LoadICNexus nexus(p.feedback().vector(), p.feedback().slot()); |
| 445 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | 483 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); |
| 446 DCHECK_LT(0, receiver_maps.length()); | 484 DCHECK_LT(0, receiver_maps.length()); |
| 447 | 485 |
| 448 // Compute property access infos for the receiver maps. | 486 // Compute property access infos for the receiver maps. |
| 449 ZoneVector<PropertyAccessInfo> access_infos(zone()); | 487 ZoneVector<PropertyAccessInfo> access_infos(zone()); |
| 450 if (!ComputePropertyAccessInfos(receiver_maps, name, &access_infos)) { | 488 if (!ComputePropertyAccessInfos(receiver_maps, name, kLoad, &access_infos)) { |
| 451 return NoChange(); | 489 return NoChange(); |
| 452 } | 490 } |
| 453 DCHECK(!access_infos.empty()); | 491 DCHECK(!access_infos.empty()); |
| 454 | 492 |
| 455 // The final states for every polymorphic branch. We join them with | 493 // The final states for every polymorphic branch. We join them with |
| 456 // Merge+Phi+EffectPhi at the bottom. | 494 // Merge+Phi+EffectPhi at the bottom. |
| 457 ZoneVector<Node*> values(zone()); | 495 ZoneVector<Node*> values(zone()); |
| 458 ZoneVector<Node*> effects(zone()); | 496 ZoneVector<Node*> effects(zone()); |
| 459 ZoneVector<Node*> controls(zone()); | 497 ZoneVector<Node*> controls(zone()); |
| 460 | 498 |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 555 } | 593 } |
| 556 } | 594 } |
| 557 } | 595 } |
| 558 | 596 |
| 559 // Generate the actual property access. | 597 // Generate the actual property access. |
| 560 if (access_info.IsDataConstant()) { | 598 if (access_info.IsDataConstant()) { |
| 561 this_value = jsgraph()->Constant(access_info.constant()); | 599 this_value = jsgraph()->Constant(access_info.constant()); |
| 562 } else { | 600 } else { |
| 563 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | 601 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some |
| 564 // common code once we also have support for stores. | 602 // common code once we also have support for stores. |
| 565 DCHECK(access_info.IsData()); | 603 DCHECK(access_info.IsDataField()); |
| 566 FieldIndex const field_index = access_info.field_index(); | 604 FieldIndex const field_index = access_info.field_index(); |
| 567 Representation const field_representation = | |
| 568 access_info.field_representation(); | |
| 569 Type* const field_type = access_info.field_type(); | 605 Type* const field_type = access_info.field_type(); |
| 570 if (!field_index.is_inobject()) { | 606 if (!field_index.is_inobject()) { |
| 571 this_value = this_effect = graph()->NewNode( | 607 this_value = this_effect = graph()->NewNode( |
| 572 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | 608 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), |
| 573 this_value, this_effect, this_control); | 609 this_value, this_effect, this_control); |
| 574 } | 610 } |
| 575 FieldAccess field_access; | 611 FieldAccess field_access; |
| 576 field_access.base_is_tagged = kTaggedBase; | 612 field_access.base_is_tagged = kTaggedBase; |
| 577 field_access.offset = field_index.offset(); | 613 field_access.offset = field_index.offset(); |
| 578 field_access.name = name; | 614 field_access.name = name; |
| 579 field_access.type = field_type; | 615 field_access.type = field_type; |
| 580 field_access.machine_type = kMachAnyTagged; | 616 field_access.machine_type = kMachAnyTagged; |
| 581 if (field_representation.IsDouble()) { | 617 if (field_type->Is(Type::UntaggedFloat64())) { |
| 582 if (!field_index.is_inobject() || field_index.is_hidden_field() || | 618 if (!field_index.is_inobject() || field_index.is_hidden_field() || |
| 583 !FLAG_unbox_double_fields) { | 619 !FLAG_unbox_double_fields) { |
| 584 this_value = this_effect = | 620 this_value = this_effect = |
| 585 graph()->NewNode(simplified()->LoadField(field_access), | 621 graph()->NewNode(simplified()->LoadField(field_access), |
| 586 this_value, this_effect, this_control); | 622 this_value, this_effect, this_control); |
| 587 field_access.offset = HeapNumber::kValueOffset; | 623 field_access.offset = HeapNumber::kValueOffset; |
| 588 field_access.name = MaybeHandle<Name>(); | 624 field_access.name = MaybeHandle<Name>(); |
| 589 } | 625 } |
| 590 field_access.machine_type = kMachFloat64; | 626 field_access.machine_type = kMachFloat64; |
| 591 } | 627 } |
| 592 this_value = this_effect = | 628 this_value = this_effect = |
| 593 graph()->NewNode(simplified()->LoadField(field_access), this_value, | 629 graph()->NewNode(simplified()->LoadField(field_access), this_value, |
| 594 this_effect, this_control); | 630 this_effect, this_control); |
| 595 } | 631 } |
| 596 | 632 |
| 597 // Remember the final state for this property access. | 633 // Remember the final state for this property access. |
| 598 values.push_back(this_value); | 634 values.push_back(this_value); |
| 599 effects.push_back(this_effect); | 635 effects.push_back(this_effect); |
| 600 controls.push_back(this_control); | 636 controls.push_back(this_control); |
| 601 } | 637 } |
| 602 | 638 |
| 603 // Collect the fallthru control as final "exit" control. | 639 // Collect the fallthru control as final "exit" control. |
| 604 exit_controls.push_back(fallthrough_control); | 640 exit_controls.push_back(fallthrough_control); |
| 605 | 641 |
| 606 // TODO(bmeurer/mtrofin): Splintering cannot currently deal with deferred | |
| 607 // blocks that contain only a single non-deoptimize instruction (i.e. a | |
| 608 // jump). Generating a single Merge here, which joins all the deoptimizing | |
| 609 // controls would generate a lot of these basic blocks, however. So this | |
| 610 // is disabled for now until splintering is fixed. | |
| 611 #if 1 | |
| 612 // Generate the single "exit" point, where we get if either all map/instance | 642 // Generate the single "exit" point, where we get if either all map/instance |
| 613 // type checks failed, or one of the assumptions inside one of the cases | 643 // type checks failed, or one of the assumptions inside one of the cases |
| 614 // failes (i.e. failing prototype chain check). | 644 // failes (i.e. failing prototype chain check). |
| 615 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | 645 // TODO(bmeurer): Consider falling back to IC here if deoptimization is |
| 616 // disabled. | 646 // disabled. |
| 617 int const exit_control_count = static_cast<int>(exit_controls.size()); | 647 int const exit_control_count = static_cast<int>(exit_controls.size()); |
| 618 Node* exit_control = | 648 Node* exit_control = |
| 619 (exit_control_count == 1) | 649 (exit_control_count == 1) |
| 620 ? exit_controls.front() | 650 ? exit_controls.front() |
| 621 : graph()->NewNode(common()->Merge(exit_control_count), | 651 : graph()->NewNode(common()->Merge(exit_control_count), |
| 622 exit_control_count, &exit_controls.front()); | 652 exit_control_count, &exit_controls.front()); |
| 623 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | 653 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, |
| 624 exit_effect, exit_control); | 654 exit_effect, exit_control); |
| 625 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | 655 // TODO(bmeurer): This should be on the AdvancedReducer somehow. |
| 626 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | 656 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); |
| 627 #else | |
| 628 for (Node* const exit_control : exit_controls) { | |
| 629 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 630 exit_effect, exit_control); | |
| 631 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 632 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 633 } | |
| 634 #endif | |
| 635 | 657 |
| 636 // Generate the final merge point for all (polymorphic) branches. | 658 // Generate the final merge point for all (polymorphic) branches. |
| 637 Node* value; | 659 Node* value; |
| 638 int const control_count = static_cast<int>(controls.size()); | 660 int const control_count = static_cast<int>(controls.size()); |
| 639 if (control_count == 1) { | 661 if (control_count == 1) { |
| 640 value = values.front(); | 662 value = values.front(); |
| 641 effect = effects.front(); | 663 effect = effects.front(); |
| 642 control = controls.front(); | 664 control = controls.front(); |
| 643 } else { | 665 } else { |
| 644 control = graph()->NewNode(common()->Merge(control_count), control_count, | 666 control = graph()->NewNode(common()->Merge(control_count), control_count, |
| 645 &controls.front()); | 667 &controls.front()); |
| 646 values.push_back(control); | 668 values.push_back(control); |
| 647 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), | 669 value = graph()->NewNode(common()->Phi(kMachAnyTagged, control_count), |
| 648 control_count + 1, &values.front()); | 670 control_count + 1, &values.front()); |
| 649 effects.push_back(control); | 671 effects.push_back(control); |
| 650 effect = graph()->NewNode(common()->EffectPhi(control_count), | 672 effect = graph()->NewNode(common()->EffectPhi(control_count), |
| 651 control_count + 1, &effects.front()); | 673 control_count + 1, &effects.front()); |
| 652 } | 674 } |
| 653 return Replace(node, value, effect, control); | 675 return Replace(node, value, effect, control); |
| 654 } | 676 } |
| 655 | 677 |
| 656 | 678 |
| 679 Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { | |
| 680 DCHECK_EQ(IrOpcode::kJSStoreNamed, node->opcode()); | |
| 681 StoreNamedParameters const p = StoreNamedParametersOf(node->op()); | |
| 682 Handle<Name> name = p.name(); | |
| 683 Node* receiver = NodeProperties::GetValueInput(node, 0); | |
| 684 Node* value = NodeProperties::GetValueInput(node, 1); | |
| 685 Node* frame_state = NodeProperties::GetFrameStateInput(node, 1); | |
| 686 Node* effect = NodeProperties::GetEffectInput(node); | |
| 687 Node* control = NodeProperties::GetControlInput(node); | |
| 688 | |
| 689 // Not much we can do if deoptimization support is disabled. | |
| 690 if (!(flags() & kDeoptimizationEnabled)) return NoChange(); | |
| 691 | |
| 692 // Extract receiver maps from the STORE_IC using the StoreICNexus. | |
| 693 MapHandleList receiver_maps; | |
| 694 if (!p.feedback().IsValid()) return NoChange(); | |
| 695 StoreICNexus nexus(p.feedback().vector(), p.feedback().slot()); | |
| 696 if (nexus.ExtractMaps(&receiver_maps) == 0) return NoChange(); | |
| 697 DCHECK_LT(0, receiver_maps.length()); | |
| 698 | |
| 699 // Compute property access infos for the receiver maps. | |
| 700 ZoneVector<PropertyAccessInfo> access_infos(zone()); | |
| 701 if (!ComputePropertyAccessInfos(receiver_maps, name, kStore, &access_infos)) { | |
| 702 return NoChange(); | |
| 703 } | |
| 704 DCHECK(!access_infos.empty()); | |
| 705 | |
| 706 // The final states for every polymorphic branch. We join them with | |
| 707 // Merge+EffectPhi at the bottom. | |
| 708 ZoneVector<Node*> effects(zone()); | |
| 709 ZoneVector<Node*> controls(zone()); | |
| 710 | |
| 711 // The list of "exiting" controls, which currently go to a single deoptimize. | |
| 712 // TODO(bmeurer): Consider using an IC as fallback. | |
| 713 Node* const exit_effect = effect; | |
| 714 ZoneVector<Node*> exit_controls(zone()); | |
| 715 | |
| 716 // Ensure that {receiver} is a heap object. | |
| 717 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), receiver); | |
| 718 Node* branch = | |
| 719 graph()->NewNode(common()->Branch(BranchHint::kFalse), check, control); | |
| 720 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 721 control = graph()->NewNode(common()->IfFalse(), branch); | |
| 722 | |
| 723 // Load the {receiver} map. The resulting effect is the dominating effect for | |
| 724 // all (polymorphic) branches. | |
| 725 Node* receiver_map = effect = | |
| 726 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 727 receiver, effect, control); | |
| 728 | |
| 729 // Generate code for the various different property access patterns. | |
| 730 Node* fallthrough_control = control; | |
| 731 for (PropertyAccessInfo const& access_info : access_infos) { | |
| 732 Node* this_receiver = receiver; | |
| 733 Node* this_effect = effect; | |
| 734 Node* this_control; | |
| 735 | |
| 736 // Perform map check on {receiver}. | |
| 737 Type* receiver_type = access_info.receiver_type(); | |
| 738 if (receiver_type->Is(Type::String())) { | |
| 739 // Emit an instance type check for strings. | |
| 740 Node* receiver_instance_type = this_effect = graph()->NewNode( | |
| 741 simplified()->LoadField(AccessBuilder::ForMapInstanceType()), | |
| 742 receiver_map, this_effect, fallthrough_control); | |
| 743 Node* check = | |
| 744 graph()->NewNode(machine()->Uint32LessThan(), receiver_instance_type, | |
| 745 jsgraph()->Uint32Constant(FIRST_NONSTRING_TYPE)); | |
| 746 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 747 check, fallthrough_control); | |
| 748 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 749 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 750 } else { | |
| 751 // Emit a (sequence of) map checks for other properties. | |
| 752 ZoneVector<Node*> this_controls(zone()); | |
| 753 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
| 754 i.Advance()) { | |
| 755 Handle<Map> map = i.Current(); | |
| 756 Node* check = | |
| 757 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 758 receiver_map, jsgraph()->Constant(map)); | |
| 759 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 760 check, fallthrough_control); | |
| 761 this_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 762 fallthrough_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 763 } | |
| 764 int const this_control_count = static_cast<int>(this_controls.size()); | |
| 765 this_control = | |
| 766 (this_control_count == 1) | |
| 767 ? this_controls.front() | |
| 768 : graph()->NewNode(common()->Merge(this_control_count), | |
| 769 this_control_count, &this_controls.front()); | |
| 770 } | |
| 771 | |
| 772 // Determine actual holder and perform prototype chain checks. | |
| 773 Handle<JSObject> holder; | |
| 774 if (access_info.holder().ToHandle(&holder)) { | |
| 775 this_receiver = jsgraph()->Constant(holder); | |
| 776 for (auto i = access_info.receiver_type()->Classes(); !i.Done(); | |
| 777 i.Advance()) { | |
| 778 Handle<Map> map = i.Current(); | |
| 779 PrototypeIterator j(map); | |
| 780 while (true) { | |
| 781 // Check that the {prototype} still has the same map. For stable | |
| 782 // maps, we can add a stability dependency on the prototype map; | |
| 783 // for everything else we need to perform a map check at runtime. | |
| 784 Handle<JSReceiver> prototype = | |
| 785 PrototypeIterator::GetCurrent<JSReceiver>(j); | |
| 786 if (prototype->map()->is_stable()) { | |
| 787 dependencies()->AssumeMapStable( | |
| 788 handle(prototype->map(), isolate())); | |
| 789 } else { | |
| 790 Node* prototype_map = this_effect = graph()->NewNode( | |
| 791 simplified()->LoadField(AccessBuilder::ForMap()), | |
| 792 jsgraph()->Constant(prototype), this_effect, this_control); | |
| 793 Node* check = graph()->NewNode( | |
| 794 simplified()->ReferenceEqual(Type::Internal()), prototype_map, | |
| 795 jsgraph()->Constant(handle(prototype->map(), isolate()))); | |
| 796 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 797 check, this_control); | |
| 798 exit_controls.push_back( | |
| 799 graph()->NewNode(common()->IfFalse(), branch)); | |
| 800 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 801 } | |
| 802 // Stop once we get to the holder. | |
| 803 if (prototype.is_identical_to(holder)) break; | |
| 804 j.Advance(); | |
| 805 } | |
| 806 } | |
| 807 } | |
| 808 | |
| 809 // Generate the actual property access. | |
| 810 if (access_info.IsDataConstant()) { | |
| 811 Node* check = | |
| 812 graph()->NewNode(simplified()->ReferenceEqual(Type::Tagged()), value, | |
| 813 jsgraph()->Constant(access_info.constant())); | |
| 814 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 815 check, this_control); | |
| 816 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
| 817 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 818 } else { | |
| 819 // TODO(bmeurer): This is sort of adhoc, and must be refactored into some | |
| 820 // common code once we also have support for stores. | |
|
Jarin
2015/10/22 09:27:30
Update comment.
Benedikt Meurer
2015/10/22 11:38:40
Done.
| |
| 821 DCHECK(access_info.IsDataField()); | |
| 822 FieldIndex const field_index = access_info.field_index(); | |
| 823 Type* const field_type = access_info.field_type(); | |
| 824 if (!field_index.is_inobject()) { | |
| 825 this_receiver = this_effect = graph()->NewNode( | |
| 826 simplified()->LoadField(AccessBuilder::ForJSObjectProperties()), | |
| 827 this_receiver, this_effect, this_control); | |
| 828 } | |
| 829 FieldAccess field_access; | |
| 830 field_access.base_is_tagged = kTaggedBase; | |
| 831 field_access.offset = field_index.offset(); | |
| 832 field_access.name = name; | |
| 833 field_access.type = field_type; | |
| 834 field_access.machine_type = kMachAnyTagged; | |
| 835 if (field_type->Is(Type::TaggedSigned())) { | |
| 836 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
| 837 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 838 check, this_control); | |
| 839 exit_controls.push_back(graph()->NewNode(common()->IfFalse(), branch)); | |
| 840 this_control = graph()->NewNode(common()->IfTrue(), branch); | |
| 841 } else if (field_type->Is(Type::TaggedPointer())) { | |
| 842 Node* check = graph()->NewNode(simplified()->ObjectIsSmi(), value); | |
| 843 Node* branch = graph()->NewNode(common()->Branch(BranchHint::kFalse), | |
| 844 check, this_control); | |
| 845 exit_controls.push_back(graph()->NewNode(common()->IfTrue(), branch)); | |
| 846 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 847 if (field_type->NumClasses() > 0) { | |
| 848 // Emit a (sequence of) map checks for the value. | |
| 849 ZoneVector<Node*> this_controls(zone()); | |
| 850 Node* value_map = this_effect = | |
| 851 graph()->NewNode(simplified()->LoadField(AccessBuilder::ForMap()), | |
| 852 value, this_effect, this_control); | |
| 853 for (auto i = field_type->Classes(); !i.Done(); i.Advance()) { | |
| 854 Handle<Map> field_map(i.Current()); | |
| 855 check = | |
|
Jarin
2015/10/22 09:27:30
How about not reusing the variable here and naming
| |
| 856 graph()->NewNode(simplified()->ReferenceEqual(Type::Internal()), | |
| 857 value_map, jsgraph()->Constant(field_map)); | |
| 858 branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), | |
| 859 check, this_control); | |
| 860 this_control = graph()->NewNode(common()->IfFalse(), branch); | |
| 861 this_controls.push_back( | |
| 862 graph()->NewNode(common()->IfTrue(), branch)); | |
| 863 } | |
| 864 exit_controls.push_back(this_control); | |
| 865 int const this_control_count = static_cast<int>(this_controls.size()); | |
| 866 this_control = (this_control_count == 1) | |
| 867 ? this_controls.front() | |
| 868 : graph()->NewNode( | |
| 869 common()->Merge(this_control_count), | |
| 870 this_control_count, &this_controls.front()); | |
| 871 } | |
| 872 } else { | |
| 873 DCHECK(field_type->Is(Type::Tagged())); | |
| 874 } | |
| 875 this_effect = | |
| 876 graph()->NewNode(simplified()->StoreField(field_access), | |
| 877 this_receiver, value, this_effect, this_control); | |
| 878 } | |
| 879 | |
| 880 // Remember the final state for this property access. | |
| 881 effects.push_back(this_effect); | |
| 882 controls.push_back(this_control); | |
| 883 } | |
| 884 | |
| 885 // Collect the fallthru control as final "exit" control. | |
| 886 exit_controls.push_back(fallthrough_control); | |
| 887 | |
| 888 // Generate the single "exit" point, where we get if either all map/instance | |
| 889 // type checks failed, or one of the assumptions inside one of the cases | |
| 890 // failes (i.e. failing prototype chain check). | |
| 891 // TODO(bmeurer): Consider falling back to IC here if deoptimization is | |
| 892 // disabled. | |
| 893 int const exit_control_count = static_cast<int>(exit_controls.size()); | |
| 894 Node* exit_control = | |
| 895 (exit_control_count == 1) | |
| 896 ? exit_controls.front() | |
| 897 : graph()->NewNode(common()->Merge(exit_control_count), | |
| 898 exit_control_count, &exit_controls.front()); | |
| 899 Node* deoptimize = graph()->NewNode(common()->Deoptimize(), frame_state, | |
| 900 exit_effect, exit_control); | |
| 901 // TODO(bmeurer): This should be on the AdvancedReducer somehow. | |
| 902 NodeProperties::MergeControlToEnd(graph(), common(), deoptimize); | |
| 903 | |
| 904 // Generate the final merge point for all (polymorphic) branches. | |
| 905 int const control_count = static_cast<int>(controls.size()); | |
| 906 if (control_count == 1) { | |
| 907 effect = effects.front(); | |
| 908 control = controls.front(); | |
| 909 } else { | |
| 910 control = graph()->NewNode(common()->Merge(control_count), control_count, | |
| 911 &controls.front()); | |
| 912 effects.push_back(control); | |
| 913 effect = graph()->NewNode(common()->EffectPhi(control_count), | |
| 914 control_count + 1, &effects.front()); | |
| 915 } | |
| 916 return Replace(node, value, effect, control); | |
| 917 } | |
| 918 | |
| 919 | |
| 657 Reduction JSNativeContextSpecialization::Replace(Node* node, | 920 Reduction JSNativeContextSpecialization::Replace(Node* node, |
| 658 Handle<Object> value) { | 921 Handle<Object> value) { |
| 659 return Replace(node, jsgraph()->Constant(value)); | 922 return Replace(node, jsgraph()->Constant(value)); |
| 660 } | 923 } |
| 661 | 924 |
| 662 | 925 |
| 663 bool JSNativeContextSpecialization::LookupInScriptContextTable( | 926 bool JSNativeContextSpecialization::LookupInScriptContextTable( |
| 664 Handle<Name> name, ScriptContextTableLookupResult* result) { | 927 Handle<Name> name, ScriptContextTableLookupResult* result) { |
| 665 if (!name->IsString()) return false; | 928 if (!name->IsString()) return false; |
| 666 Handle<ScriptContextTable> script_context_table( | 929 Handle<ScriptContextTable> script_context_table( |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 709 } | 972 } |
| 710 | 973 |
| 711 | 974 |
| 712 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { | 975 SimplifiedOperatorBuilder* JSNativeContextSpecialization::simplified() const { |
| 713 return jsgraph()->simplified(); | 976 return jsgraph()->simplified(); |
| 714 } | 977 } |
| 715 | 978 |
| 716 } // namespace compiler | 979 } // namespace compiler |
| 717 } // namespace internal | 980 } // namespace internal |
| 718 } // namespace v8 | 981 } // namespace v8 |
| OLD | NEW |