[turbofan] Remove remaining uses of Class type from the compiler.

We (mis)used Type::Class to track stable field maps in the past. But
that always more or less unsupport and wrong for various reasons, mostly
because the class types do not really present static information and
thus it is possible to violate fundamental assumptions of the type
system (i.e. intersecting class types and other types produces
"interesting" results).

Now it is possible to finally nuke the class types completely and thus
simplify (and ideally correctify) the type system further.

Note to performance sheriff: We do expect to see some performance
regressions from this change. This is because we do not yet have a sane
replacement mechanism to track known field maps and utilize them during
LoadElimination. This will be accomplished in a follow up CL.

BUG=v8:5270,v8:5267
R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2293343002
Cr-Commit-Position: refs/heads/master@{#39031}
This commit is contained in:
bmeurer 2016-08-30 22:44:20 -07:00 committed by Commit bot
parent 9c00c88902
commit 2b93899057
7 changed files with 57 additions and 46 deletions

View File

@ -81,9 +81,11 @@ PropertyAccessInfo PropertyAccessInfo::DataConstant(
PropertyAccessInfo PropertyAccessInfo::DataField(
MapList const& receiver_maps, FieldIndex field_index,
MachineRepresentation field_representation, Type* field_type,
MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map) {
MaybeHandle<Map> field_map, MaybeHandle<JSObject> holder,
MaybeHandle<Map> transition_map) {
return PropertyAccessInfo(holder, transition_map, field_index,
field_representation, field_type, receiver_maps);
field_representation, field_type, field_map,
receiver_maps);
}
// static
@ -119,14 +121,15 @@ PropertyAccessInfo::PropertyAccessInfo(Kind kind, MaybeHandle<JSObject> holder,
PropertyAccessInfo::PropertyAccessInfo(
MaybeHandle<JSObject> holder, MaybeHandle<Map> transition_map,
FieldIndex field_index, MachineRepresentation field_representation,
Type* field_type, MapList const& receiver_maps)
Type* field_type, MaybeHandle<Map> field_map, MapList const& receiver_maps)
: kind_(kDataField),
receiver_maps_(receiver_maps),
transition_map_(transition_map),
holder_(holder),
field_index_(field_index),
field_representation_(field_representation),
field_type_(field_type) {}
field_type_(field_type),
field_map_(field_map) {}
bool PropertyAccessInfo::Merge(PropertyAccessInfo const* that) {
if (this->kind_ != that->kind_) return false;
@ -293,9 +296,10 @@ bool AccessInfoFactory::ComputePropertyAccessInfo(
Representation details_representation = details.representation();
FieldIndex field_index = FieldIndex::ForPropertyIndex(
*map, index, details_representation.IsDouble());
Type* field_type = Type::Tagged();
Type* field_type = Type::NonInternal();
MachineRepresentation field_representation =
MachineRepresentation::kTagged;
MaybeHandle<Map> field_map;
if (details_representation.IsSmi()) {
field_type = type_cache_.kSmi;
field_representation = MachineRepresentation::kTaggedSigned;
@ -306,27 +310,28 @@ bool AccessInfoFactory::ComputePropertyAccessInfo(
// Extract the field type from the property details (make sure its
// representation is TaggedPointer to reflect the heap object case).
field_representation = MachineRepresentation::kTaggedPointer;
field_type = descriptors->GetFieldType(number)->Convert(zone());
if (field_type->Is(Type::None())) {
Handle<FieldType> descriptors_field_type(
descriptors->GetFieldType(number), isolate());
if (descriptors_field_type->IsNone()) {
// Store is not safe if the field type was cleared.
if (access_mode == AccessMode::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::Any();
} else if (!Type::Any()->Is(field_type)) {
} else if (descriptors_field_type->IsClass()) {
// Add proper code dependencies in case of stable field map(s).
field_representation = MachineRepresentation::kTaggedPointer;
Handle<Map> field_owner_map(map->FindFieldOwner(number),
isolate());
dependencies()->AssumeFieldType(field_owner_map);
// Remember the field map, and try to infer a useful type.
field_type = Type::For(descriptors_field_type->AsClass());
field_map = descriptors_field_type->AsClass();
}
}
*access_info = PropertyAccessInfo::DataField(
MapList{receiver_map}, field_index, field_representation,
field_type, holder);
field_type, field_map, holder);
return true;
}
case ACCESSOR_CONSTANT: {
@ -484,7 +489,8 @@ bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
Representation details_representation = details.representation();
FieldIndex field_index = FieldIndex::ForPropertyIndex(
*transition_map, index, details_representation.IsDouble());
Type* field_type = Type::Tagged();
Type* field_type = Type::NonInternal();
MaybeHandle<Map> field_map;
MachineRepresentation field_representation = MachineRepresentation::kTagged;
if (details_representation.IsSmi()) {
field_type = type_cache_.kSmi;
@ -496,23 +502,27 @@ bool AccessInfoFactory::LookupTransition(Handle<Map> map, Handle<Name> name,
// Extract the field type from the property details (make sure its
// representation is TaggedPointer to reflect the heap object case).
field_representation = MachineRepresentation::kTaggedPointer;
field_type =
transition_map->instance_descriptors()->GetFieldType(number)->Convert(
zone());
if (field_type->Is(Type::None())) {
Handle<FieldType> descriptors_field_type(
transition_map->instance_descriptors()->GetFieldType(number),
isolate());
if (descriptors_field_type->IsNone()) {
// Store is not safe if the field type was cleared.
return false;
} else if (!Type::Any()->Is(field_type)) {
} else if (descriptors_field_type->IsClass()) {
// 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);
// Remember the field map, and try to infer a useful type.
field_type = Type::For(descriptors_field_type->AsClass());
field_map = descriptors_field_type->AsClass();
}
}
dependencies()->AssumeMapNotDeprecated(transition_map);
*access_info = PropertyAccessInfo::DataField(
MapList{map}, field_index, field_representation, field_type, holder,
transition_map);
MapList{map}, field_index, field_representation, field_type, field_map,
holder, transition_map);
return true;
}
return false;

View File

@ -71,6 +71,7 @@ class PropertyAccessInfo final {
static PropertyAccessInfo DataField(
MapList const& receiver_maps, FieldIndex field_index,
MachineRepresentation field_representation, Type* field_type,
MaybeHandle<Map> field_map = MaybeHandle<Map>(),
MaybeHandle<JSObject> holder = MaybeHandle<JSObject>(),
MaybeHandle<Map> transition_map = MaybeHandle<Map>());
static PropertyAccessInfo AccessorConstant(MapList const& receiver_maps,
@ -97,6 +98,7 @@ class PropertyAccessInfo final {
MachineRepresentation field_representation() const {
return field_representation_;
}
MaybeHandle<Map> field_map() const { return field_map_; }
MapList const& receiver_maps() const { return receiver_maps_; }
private:
@ -107,7 +109,8 @@ class PropertyAccessInfo final {
PropertyAccessInfo(MaybeHandle<JSObject> holder,
MaybeHandle<Map> transition_map, FieldIndex field_index,
MachineRepresentation field_representation,
Type* field_type, MapList const& receiver_maps);
Type* field_type, MaybeHandle<Map> field_map,
MapList const& receiver_maps);
Kind kind_;
MapList receiver_maps_;
@ -117,6 +120,7 @@ class PropertyAccessInfo final {
FieldIndex field_index_;
MachineRepresentation field_representation_;
Type* field_type_;
MaybeHandle<Map> field_map_;
};

View File

@ -112,10 +112,11 @@ Reduction JSGlobalObjectSpecialization::ReduceJSLoadGlobal(Node* node) {
} else if (property_cell_value->IsNumber()) {
property_cell_value_type = type_cache_.kHeapNumber;
} else {
// TODO(turbofan): Track the property_cell_value_map on the FieldAccess
// below and use it in LoadElimination to eliminate map checks.
Handle<Map> property_cell_value_map(
Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
property_cell_value_type =
Type::Class(property_cell_value_map, graph()->zone());
property_cell_value_type = Type::For(property_cell_value_map);
}
}
Node* value = effect = graph()->NewNode(

View File

@ -906,6 +906,8 @@ JSNativeContextSpecialization::BuildPropertyAccess(
}
field_access.machine_type = MachineType::Float64();
}
// TODO(turbofan): Track the field_map (if any) on the {field_access} and
// use it in LoadElimination to eliminate map checks.
value = effect = graph()->NewNode(simplified()->LoadField(field_access),
storage, effect, control);
} else {
@ -956,14 +958,12 @@ JSNativeContextSpecialization::BuildPropertyAccess(
// Ensure that {value} is a HeapObject.
value = effect = graph()->NewNode(simplified()->CheckTaggedPointer(),
value, effect, control);
if (field_type->NumClasses() == 1) {
Handle<Map> field_map;
if (access_info.field_map().ToHandle(&field_map)) {
// Emit a map check for the value.
Node* field_map =
jsgraph()->Constant(field_type->Classes().Current());
effect = graph()->NewNode(simplified()->CheckMaps(1), value,
field_map, effect, control);
} else {
DCHECK_EQ(0, field_type->NumClasses());
jsgraph()->HeapConstant(field_map), effect,
control);
}
} else {
// DCHECK(field_type->Is(Type::Tagged()));

View File

@ -97,9 +97,6 @@ MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
Handle<Map> object_map(
Handle<HeapObject>::cast(object_type->AsConstant()->Value())->map());
if (object_map->is_stable()) return object_map;
} else if (object_type->IsClass()) {
Handle<Map> object_map = object_type->AsClass()->Map();
if (object_map->is_stable()) return object_map;
}
return MaybeHandle<Map>();
}
@ -107,11 +104,8 @@ MaybeHandle<Map> GetStableMapFromObjectType(Type* object_type) {
} // namespace
Reduction TypedOptimization::ReduceCheckMaps(Node* node) {
// The CheckMaps(o, ...map...) can be eliminated if map is stable and
// either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// The CheckMaps(o, ...map...) can be eliminated if map is stable,
// o has type Constant(object) and map == object->map, and either
// (1) map cannot transition further, or
// (2) we can add a code dependency on the stability of map
// (to guard the Constant type information).
@ -151,10 +145,8 @@ Reduction TypedOptimization::ReduceLoadField(Node* node) {
FieldAccess const& access = FieldAccessOf(node->op());
if (access.base_is_tagged == kTaggedBase &&
access.offset == HeapObject::kMapOffset) {
// We can replace LoadField[Map](o) with map if is stable and either
// (a) o has type Constant(object) and map == object->map, or
// (b) o has type Class(map),
// and either
// We can replace LoadField[Map](o) with map if is stable, and
// o has type Constant(object) and map == object->map, and either
// (1) map cannot transition further, or
// (2) deoptimization is enabled and we can add a code dependency on the
// stability of map (to guard the Constant type information).

View File

@ -1453,10 +1453,9 @@ Type* Typer::Visitor::TypeJSForInNext(Node* node) {
Type* Typer::Visitor::TypeJSForInPrepare(Node* node) {
STATIC_ASSERT(Map::EnumLengthBits::kMax <= FixedArray::kMaxLength);
Factory* const f = isolate()->factory();
Type* const cache_type = Type::Union(
typer_->cache_.kSmi, Type::Class(f->meta_map(), zone()), zone());
Type* const cache_array = Type::Class(f->fixed_array_map(), zone());
Type* const cache_type =
Type::Union(typer_->cache_.kSmi, Type::OtherInternal(), zone());
Type* const cache_array = Type::OtherInternal();
Type* const cache_length = typer_->cache_.kFixedArrayLengthType;
return Type::Tuple(cache_type, cache_array, cache_length, zone());
}

View File

@ -764,6 +764,11 @@ class Type {
return Of(*value, zone);
}
static Type* For(i::Map* map) {
return BitsetType::New(BitsetType::ExpandInternals(BitsetType::Lub(map)));
}
static Type* For(i::Handle<i::Map> map) { return For(*map); }
// Extraction of components.
static Type* Representation(Type* t, Zone* zone);
static Type* Semantic(Type* t, Zone* zone);