Index: src/compiler/js-native-context-specialization.cc |
diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc |
index d96cf15b6e3438c9e1bc356cecacb5351b7a11cd..3c2c4f4ef67e9088cb9651854f144e46a7046591 100644 |
--- a/src/compiler/js-native-context-specialization.cc |
+++ b/src/compiler/js-native-context-specialization.cc |
@@ -39,7 +39,8 @@ JSNativeContextSpecialization::JSNativeContextSpecialization( |
native_context_(global_object->native_context(), isolate()), |
dependencies_(dependencies), |
zone_(zone), |
- type_cache_(TypeCache::Get()) {} |
+ type_cache_(TypeCache::Get()), |
+ access_info_factory_(dependencies, native_context(), graph()->zone()) {} |
Reduction JSNativeContextSpecialization::Reduce(Node* node) { |
@@ -298,319 +299,6 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { |
} |
-// This class encapsulates all information required to access a certain |
-// object property, either on the object itself or on the prototype chain. |
-class JSNativeContextSpecialization::PropertyAccessInfo final { |
- public: |
- enum Kind { kInvalid, kDataConstant, kDataField, kTransitionToField }; |
- |
- static PropertyAccessInfo DataConstant(Type* receiver_type, |
- Handle<Object> constant, |
- MaybeHandle<JSObject> holder) { |
- return PropertyAccessInfo(holder, constant, receiver_type); |
- } |
- static PropertyAccessInfo DataField( |
- Type* receiver_type, FieldIndex field_index, Type* field_type, |
- MaybeHandle<JSObject> holder = MaybeHandle<JSObject>()) { |
- return PropertyAccessInfo(holder, field_index, field_type, receiver_type); |
- } |
- static PropertyAccessInfo TransitionToField(Type* receiver_type, |
- FieldIndex field_index, |
- Type* field_type, |
- Handle<Map> transition_map, |
- MaybeHandle<JSObject> holder) { |
- return PropertyAccessInfo(holder, transition_map, field_index, field_type, |
- receiver_type); |
- } |
- |
- PropertyAccessInfo() : kind_(kInvalid) {} |
- PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Object> constant, |
- Type* receiver_type) |
- : kind_(kDataConstant), |
- receiver_type_(receiver_type), |
- constant_(constant), |
- holder_(holder) {} |
- PropertyAccessInfo(MaybeHandle<JSObject> holder, FieldIndex field_index, |
- Type* field_type, Type* receiver_type) |
- : kind_(kDataField), |
- receiver_type_(receiver_type), |
- holder_(holder), |
- field_index_(field_index), |
- field_type_(field_type) {} |
- PropertyAccessInfo(MaybeHandle<JSObject> holder, Handle<Map> transition_map, |
- FieldIndex field_index, Type* field_type, |
- Type* receiver_type) |
- : kind_(kTransitionToField), |
- receiver_type_(receiver_type), |
- transition_map_(transition_map), |
- holder_(holder), |
- field_index_(field_index), |
- field_type_(field_type) {} |
- |
- bool IsDataConstant() const { return kind() == kDataConstant; } |
- bool IsDataField() const { return kind() == kDataField; } |
- bool IsTransitionToField() const { return kind() == kTransitionToField; } |
- |
- Kind kind() const { return kind_; } |
- MaybeHandle<JSObject> holder() const { return holder_; } |
- Handle<Object> constant() const { return constant_; } |
- Handle<Object> transition_map() const { return transition_map_; } |
- FieldIndex field_index() const { return field_index_; } |
- Type* field_type() const { return field_type_; } |
- Type* receiver_type() const { return receiver_type_; } |
- |
- private: |
- Kind kind_; |
- Type* receiver_type_; |
- Handle<Object> constant_; |
- Handle<Map> transition_map_; |
- MaybeHandle<JSObject> holder_; |
- FieldIndex field_index_; |
- Type* field_type_ = Type::Any(); |
-}; |
- |
- |
-namespace { |
- |
-bool CanInlinePropertyAccess(Handle<Map> map) { |
- // TODO(bmeurer): Do something about the number stuff. |
- if (map->instance_type() == HEAP_NUMBER_TYPE) return false; |
- if (map->instance_type() < FIRST_NONSTRING_TYPE) return true; |
- return map->IsJSObjectMap() && !map->is_dictionary_map() && |
- !map->has_named_interceptor() && |
- // TODO(verwaest): Whitelist contexts to which we have access. |
- !map->is_access_check_needed(); |
-} |
- |
-} // namespace |
- |
- |
-bool JSNativeContextSpecialization::ComputePropertyAccessInfo( |
- Handle<Map> map, Handle<Name> name, PropertyAccessMode access_mode, |
- PropertyAccessInfo* access_info) { |
- // Check if it is safe to inline property access for the {map}. |
- if (!CanInlinePropertyAccess(map)) return false; |
- |
- // Compute the receiver type. |
- Handle<Map> receiver_map = map; |
- Type* receiver_type = Type::Class(receiver_map, graph()->zone()); |
- |
- // We support fast inline cases for certain JSObject getters. |
- if (access_mode == kLoad) { |
- // Check for special JSObject field accessors. |
- int offset; |
- if (Accessors::IsJSObjectFieldAccessor(map, name, &offset)) { |
- FieldIndex field_index = FieldIndex::ForInObjectOffset(offset); |
- Type* field_type = Type::Tagged(); |
- if (map->IsStringMap()) { |
- DCHECK(Name::Equals(factory()->length_string(), name)); |
- // The String::length property is always a smi in the range |
- // [0, String::kMaxLength]. |
- field_type = type_cache_.kStringLengthType; |
- } else if (map->IsJSArrayMap()) { |
- DCHECK(Name::Equals(factory()->length_string(), name)); |
- // The JSArray::length property is a smi in the range |
- // [0, FixedDoubleArray::kMaxLength] in case of fast double |
- // elements, a smi in the range [0, FixedArray::kMaxLength] |
- // in case of other fast elements, and [0, kMaxUInt32] in |
- // case of other arrays. |
- if (IsFastDoubleElementsKind(map->elements_kind())) { |
- field_type = type_cache_.kFixedDoubleArrayLengthType; |
- } else if (IsFastElementsKind(map->elements_kind())) { |
- field_type = type_cache_.kFixedArrayLengthType; |
- } else { |
- field_type = type_cache_.kJSArrayLengthType; |
- } |
- } |
- *access_info = |
- PropertyAccessInfo::DataField(receiver_type, field_index, field_type); |
- return true; |
- } |
- } |
- |
- MaybeHandle<JSObject> holder; |
- while (true) { |
- // Lookup the named property on the {map}. |
- Handle<DescriptorArray> descriptors(map->instance_descriptors(), isolate()); |
- int const number = descriptors->SearchWithCache(*name, *map); |
- if (number != DescriptorArray::kNotFound) { |
- PropertyDetails const details = descriptors->GetDetails(number); |
- if (access_mode == kStore) { |
- // Don't bother optimizing stores to read-only properties. |
- if (details.IsReadOnly()) { |
- return false; |
- } |
- // Check for store to data property on a prototype. |
- if (details.kind() == kData && !holder.is_null()) { |
- // We need to add the data field to the receiver. Leave the loop |
- // and check whether we already have a transition for this field. |
- // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) |
- break; |
- } |
- } |
- if (details.type() == DATA_CONSTANT) { |
- *access_info = PropertyAccessInfo::DataConstant( |
- receiver_type, handle(descriptors->GetValue(number), isolate()), |
- holder); |
- return true; |
- } else if (details.type() == DATA) { |
- int index = descriptors->GetFieldIndex(number); |
- Representation field_representation = details.representation(); |
- FieldIndex field_index = FieldIndex::ForPropertyIndex( |
- *map, index, field_representation.IsDouble()); |
- Type* field_type = Type::Tagged(); |
- if (field_representation.IsSmi()) { |
- field_type = type_cache_.kSmi; |
- } else if (field_representation.IsDouble()) { |
- field_type = type_cache_.kFloat64; |
- } else if (field_representation.IsHeapObject()) { |
- // Extract the field type from the property details (make sure its |
- // representation is TaggedPointer to reflect the heap object case). |
- field_type = Type::Intersect( |
- Type::Convert<HeapType>( |
- handle(descriptors->GetFieldType(number), isolate()), |
- graph()->zone()), |
- Type::TaggedPointer(), graph()->zone()); |
- if (field_type->Is(Type::None())) { |
- // Store is not safe if the field type was cleared. |
- if (access_mode == kStore) return false; |
- |
- // The field type was cleared by the GC, so we don't know anything |
- // about the contents now. |
- // TODO(bmeurer): It would be awesome to make this saner in the |
- // runtime/GC interaction. |
- field_type = Type::TaggedPointer(); |
- } else if (!Type::Any()->Is(field_type)) { |
- // Add proper code dependencies in case of stable field map(s). |
- Handle<Map> field_owner_map(map->FindFieldOwner(number), isolate()); |
- dependencies()->AssumeFieldType(field_owner_map); |
- } |
- DCHECK(field_type->Is(Type::TaggedPointer())); |
- } |
- *access_info = PropertyAccessInfo::DataField(receiver_type, field_index, |
- field_type, holder); |
- return true; |
- } else { |
- // TODO(bmeurer): Add support for accessors. |
- return false; |
- } |
- } |
- |
- // Don't search on the prototype chain for special indices in case of |
- // integer indexed exotic objects (see ES6 section 9.4.5). |
- if (map->IsJSTypedArrayMap() && name->IsString() && |
- IsSpecialIndex(isolate()->unicode_cache(), String::cast(*name))) { |
- return false; |
- } |
- |
- // Don't lookup private symbols on the prototype chain. |
- if (name->IsPrivate()) return false; |
- |
- // Walk up the prototype chain. |
- if (!map->prototype()->IsJSObject()) { |
- // Perform the implicit ToObject for primitives here. |
- // Implemented according to ES6 section 7.3.2 GetV (V, P). |
- Handle<JSFunction> constructor; |
- if (Map::GetConstructorFunction(map, native_context()) |
- .ToHandle(&constructor)) { |
- map = handle(constructor->initial_map(), isolate()); |
- DCHECK(map->prototype()->IsJSObject()); |
- } else if (map->prototype()->IsNull()) { |
- // Store to property not found on the receiver or any prototype, we need |
- // to transition to a new data property. |
- // Implemented according to ES6 section 9.1.9 [[Set]] (P, V, Receiver) |
- if (access_mode == kStore) { |
- break; |
- } |
- // TODO(bmeurer): Handle the not found case if the prototype is null. |
- return false; |
- } else { |
- return false; |
- } |
- } |
- Handle<JSObject> map_prototype(JSObject::cast(map->prototype()), isolate()); |
- if (map_prototype->map()->is_deprecated()) { |
- // Try to migrate the prototype object so we don't embed the deprecated |
- // map into the optimized code. |
- JSObject::TryMigrateInstance(map_prototype); |
- } |
- map = handle(map_prototype->map(), isolate()); |
- holder = map_prototype; |
- |
- // Check if it is safe to inline property access for the {map}. |
- if (!CanInlinePropertyAccess(map)) return false; |
- } |
- DCHECK_EQ(kStore, access_mode); |
- |
- // Check if the {receiver_map} has a data transition with the given {name}. |
- if (receiver_map->unused_property_fields() == 0) return false; |
- if (Map* transition = TransitionArray::SearchTransition(*receiver_map, kData, |
- *name, NONE)) { |
- Handle<Map> transition_map(transition, isolate()); |
- int const number = transition_map->LastAdded(); |
- PropertyDetails const details = |
- transition_map->instance_descriptors()->GetDetails(number); |
- // Don't bother optimizing stores to read-only properties. |
- if (details.IsReadOnly()) return false; |
- // TODO(bmeurer): Handle transition to data constant? |
- if (details.type() != DATA) return false; |
- int const index = details.field_index(); |
- Representation field_representation = details.representation(); |
- FieldIndex field_index = FieldIndex::ForPropertyIndex( |
- *transition_map, index, field_representation.IsDouble()); |
- Type* field_type = Type::Tagged(); |
- if (field_representation.IsSmi()) { |
- field_type = type_cache_.kSmi; |
- } else if (field_representation.IsDouble()) { |
- // TODO(bmeurer): Add support for storing to double fields. |
- return false; |
- } else if (field_representation.IsHeapObject()) { |
- // Extract the field type from the property details (make sure its |
- // representation is TaggedPointer to reflect the heap object case). |
- field_type = Type::Intersect( |
- Type::Convert<HeapType>( |
- handle( |
- transition_map->instance_descriptors()->GetFieldType(number), |
- isolate()), |
- graph()->zone()), |
- Type::TaggedPointer(), graph()->zone()); |
- if (field_type->Is(Type::None())) { |
- // Store is not safe if the field type was cleared. |
- return false; |
- } else if (!Type::Any()->Is(field_type)) { |
- // Add proper code dependencies in case of stable field map(s). |
- Handle<Map> field_owner_map(transition_map->FindFieldOwner(number), |
- isolate()); |
- dependencies()->AssumeFieldType(field_owner_map); |
- } |
- DCHECK(field_type->Is(Type::TaggedPointer())); |
- } |
- dependencies()->AssumeMapNotDeprecated(transition_map); |
- *access_info = PropertyAccessInfo::TransitionToField( |
- receiver_type, field_index, field_type, transition_map, holder); |
- return true; |
- } |
- return false; |
-} |
- |
- |
-bool JSNativeContextSpecialization::ComputePropertyAccessInfos( |
- MapHandleList const& maps, Handle<Name> name, |
- PropertyAccessMode access_mode, |
- ZoneVector<PropertyAccessInfo>* access_infos) { |
- for (Handle<Map> map : maps) { |
- if (Map::TryUpdate(map).ToHandle(&map)) { |
- PropertyAccessInfo access_info; |
- if (!ComputePropertyAccessInfo(map, name, access_mode, &access_info)) { |
- return false; |
- } |
- access_infos->push_back(access_info); |
- } |
- } |
- return true; |
-} |
- |
- |
Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
Node* node, Node* value, MapHandleList const& receiver_maps, |
Handle<Name> name, PropertyAccessMode access_mode) { |
@@ -626,8 +314,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
// Compute property access infos for the receiver maps. |
ZoneVector<PropertyAccessInfo> access_infos(zone()); |
- if (!ComputePropertyAccessInfos(receiver_maps, name, access_mode, |
- &access_infos)) { |
+ if (!access_info_factory().ComputePropertyAccessInfos( |
+ receiver_maps, name, access_mode, &access_infos)) { |
return NoChange(); |
} |
@@ -711,7 +399,7 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
// Generate the actual property access. |
if (access_info.IsDataConstant()) { |
this_value = jsgraph()->Constant(access_info.constant()); |
- if (access_mode == kStore) { |
+ if (access_mode == PropertyAccessMode::kStore) { |
Node* check = graph()->NewNode( |
simplified()->ReferenceEqual(Type::Tagged()), value, this_value); |
Node* branch = graph()->NewNode(common()->Branch(BranchHint::kTrue), |
@@ -720,10 +408,11 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
this_control = graph()->NewNode(common()->IfTrue(), branch); |
} |
} else { |
- DCHECK(access_info.IsDataField() || access_info.IsTransitionToField()); |
+ DCHECK(access_info.IsDataField()); |
FieldIndex const field_index = access_info.field_index(); |
Type* const field_type = access_info.field_type(); |
- if (access_mode == kLoad && access_info.holder().ToHandle(&holder)) { |
+ if (access_mode == PropertyAccessMode::kLoad && |
+ access_info.holder().ToHandle(&holder)) { |
this_receiver = jsgraph()->Constant(holder); |
} |
Node* this_storage = this_receiver; |
@@ -745,12 +434,12 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
} |
field_access.machine_type = kMachFloat64; |
} |
- if (access_mode == kLoad) { |
+ if (access_mode == PropertyAccessMode::kLoad) { |
this_value = this_effect = |
graph()->NewNode(simplified()->LoadField(field_access), |
this_storage, this_effect, this_control); |
} else { |
- DCHECK_EQ(kStore, access_mode); |
+ DCHECK_EQ(PropertyAccessMode::kStore, access_mode); |
if (field_type->Is(Type::UntaggedFloat64())) { |
Node* check = |
graph()->NewNode(simplified()->ObjectIsNumber(), this_value); |
@@ -806,17 +495,17 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess( |
} else { |
DCHECK(field_type->Is(Type::Tagged())); |
} |
- if (access_info.IsTransitionToField()) { |
+ Handle<Map> transition_map; |
+ if (access_info.transition_map().ToHandle(&transition_map)) { |
this_effect = graph()->NewNode(common()->BeginRegion(), this_effect); |
this_effect = graph()->NewNode( |
simplified()->StoreField(AccessBuilder::ForMap()), this_receiver, |
- jsgraph()->Constant(access_info.transition_map()), this_effect, |
- this_control); |
+ jsgraph()->Constant(transition_map), this_effect, this_control); |
} |
this_effect = graph()->NewNode(simplified()->StoreField(field_access), |
this_storage, this_value, this_effect, |
this_control); |
- if (access_info.IsTransitionToField()) { |
+ if (!access_info.transition_map().is_null()) { |
this_effect = |
graph()->NewNode(common()->FinishRegion(), |
jsgraph()->UndefinedConstant(), this_effect); |
@@ -893,7 +582,8 @@ Reduction JSNativeContextSpecialization::ReduceJSLoadNamed(Node* node) { |
DCHECK_LT(0, receiver_maps.length()); |
// Try to lower the named access based on the {receiver_maps}. |
- return ReduceNamedAccess(node, value, receiver_maps, p.name(), kLoad); |
+ return ReduceNamedAccess(node, value, receiver_maps, p.name(), |
+ PropertyAccessMode::kLoad); |
} |
@@ -910,7 +600,8 @@ Reduction JSNativeContextSpecialization::ReduceJSStoreNamed(Node* node) { |
DCHECK_LT(0, receiver_maps.length()); |
// Try to lower the named access based on the {receiver_maps}. |
- return ReduceNamedAccess(node, value, receiver_maps, p.name(), kStore); |
+ return ReduceNamedAccess(node, value, receiver_maps, p.name(), |
+ PropertyAccessMode::kStore); |
} |
@@ -924,7 +615,7 @@ bool JSNativeContextSpecialization::LookupInScriptContextTable( |
Handle<Name> name, ScriptContextTableLookupResult* result) { |
if (!name->IsString()) return false; |
Handle<ScriptContextTable> script_context_table( |
- global_object()->native_context()->script_context_table()); |
+ native_context()->script_context_table()); |
ScriptContextTable::LookupResult lookup_result; |
if (!ScriptContextTable::Lookup(script_context_table, |
Handle<String>::cast(name), &lookup_result)) { |