Commit 42c67d5f authored by bmeurer@chromium.org's avatar bmeurer@chromium.org

Allow merging of monomorphic accesses to tracked fields.

Also add stability dependency only on maps that can transition,
and delay adding the dependencies until we are actually using
them, either in a HLoadNamedField or an HCheckMaps.

TEST=mjsunit/field-type-tracking
R=svenpanne@chromium.org

Review URL: https://codereview.chromium.org/239923004

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@20796 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 17b33fa1
......@@ -383,9 +383,9 @@ class HCheckTable : public ZoneObject {
void ReduceLoadNamedField(HLoadNamedField* instr) {
// Reduce a load of the map field when it is known to be a constant.
if (!IsMapAccess(instr->access())) {
// Check if we introduce a map here.
if (!instr->map().IsNull()) {
Insert(instr, instr, instr->map());
// Check if we introduce field maps here.
if (instr->map_set().size() != 0) {
Insert(instr, instr, instr->map_set().Copy(phase_->zone()));
}
return;
}
......
......@@ -3382,8 +3382,12 @@ void HLoadNamedField::PrintDataTo(StringStream* stream) {
object()->PrintNameTo(stream);
access_.PrintTo(stream);
if (!map().IsNull()) {
stream->Add(" (%p)", *map().handle());
if (map_set_.size() != 0) {
stream->Add(" [%p", *map_set_.at(0).handle());
for (int i = 1; i < map_set_.size(); ++i) {
stream->Add(",%p", *map_set_.at(i).handle());
}
stream->Add("]");
}
if (HasDependency()) {
......
......@@ -6144,8 +6144,22 @@ class HLoadNamedField V8_FINAL : public HTemplateInstruction<2> {
public:
DECLARE_INSTRUCTION_FACTORY_P3(HLoadNamedField, HValue*, HValue*,
HObjectAccess);
DECLARE_INSTRUCTION_FACTORY_P4(HLoadNamedField, HValue*, HValue*,
HObjectAccess, Handle<Map>);
static HLoadNamedField* New(Zone* zone, HValue* context,
HValue* object, HValue* dependency,
HObjectAccess access, SmallMapList* maps,
CompilationInfo* info) {
HLoadNamedField* load_named_field = HLoadNamedField::New(
zone, context, object, dependency, access);
for (int i = 0; i < maps->length(); ++i) {
Handle<Map> map(maps->at(i));
load_named_field->map_set_.Add(Unique<Map>(map), zone);
if (map->CanTransition()) {
Map::AddDependentCompilationInfo(
map, DependentCode::kPrototypeCheckGroup, info);
}
}
return load_named_field;
}
HValue* object() { return OperandAt(0); }
HValue* dependency() {
......@@ -6158,7 +6172,7 @@ class HLoadNamedField V8_FINAL : public HTemplateInstruction<2> {
return access_.representation();
}
Unique<Map> map() const { return map_; }
UniqueSet<Map> map_set() const { return map_set_; }
virtual bool HasEscapingOperandAt(int index) V8_OVERRIDE { return false; }
virtual bool HasOutOfBoundsAccess(int size) V8_OVERRIDE {
......@@ -6179,7 +6193,7 @@ class HLoadNamedField V8_FINAL : public HTemplateInstruction<2> {
protected:
virtual bool DataEquals(HValue* other) V8_OVERRIDE {
HLoadNamedField* b = HLoadNamedField::cast(other);
return access_.Equals(b->access_) && map_ == b->map_;
return access_.Equals(b->access_) && this->map_set_.Equals(&b->map_set_);
}
private:
......@@ -6187,7 +6201,7 @@ class HLoadNamedField V8_FINAL : public HTemplateInstruction<2> {
HValue* dependency,
HObjectAccess access,
Handle<Map> map = Handle<Map>::null())
: access_(access), map_(map) {
: access_(access) {
ASSERT(object != NULL);
SetOperandAt(0, object);
SetOperandAt(1, dependency != NULL ? dependency : object);
......@@ -6221,7 +6235,7 @@ class HLoadNamedField V8_FINAL : public HTemplateInstruction<2> {
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
HObjectAccess access_;
Unique<Map> map_;
UniqueSet<Map> map_set_;
};
......
......@@ -5370,7 +5370,7 @@ HInstruction* HOptimizedGraphBuilder::BuildLoadNamedField(
access = HObjectAccess::ForHeapNumberValue();
}
return New<HLoadNamedField>(
checked_object, static_cast<HValue*>(NULL), access, info->field_map());
checked_object, checked_object, access, info->field_maps(), top_info());
}
......@@ -5414,10 +5414,15 @@ HInstruction* HOptimizedGraphBuilder::BuildStoreNamedField(
value, STORE_TO_INITIALIZED_ENTRY);
}
} else {
if (!info->field_map().is_null()) {
if (!info->field_maps()->is_empty()) {
ASSERT(field_access.representation().IsHeapObject());
BuildCheckHeapObject(value);
value = BuildCheckMap(value, info->field_map());
if (info->field_maps()->length() == 1) {
// TODO(bmeurer): Also apply stable maps optimization to the else case!
value = BuildCheckMap(value, info->field_maps()->first());
} else {
value = Add<HCheckMaps>(value, info->field_maps());
}
// TODO(bmeurer): This is a dirty hack to avoid repeating the smi check
// that was already performed by the HCheckHeapObject above in the
......@@ -5489,11 +5494,24 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::IsCompatible(
}
if (info->access_.offset() != access_.offset()) return false;
if (info->access_.IsInobject() != access_.IsInobject()) return false;
if (!field_map_.is_identical_to(info->field_map_)) {
if (!IsLoad()) return false;
// Throw away type information for merging polymorphic loads.
info->field_map_ = Handle<Map>::null();
if (IsLoad()) {
if (field_maps_.is_empty()) {
info->field_maps_.Clear();
} else if (!info->field_maps_.is_empty()) {
for (int i = 0; i < field_maps_.length(); ++i) {
info->field_maps_.AddMapIfMissing(field_maps_.at(i), info->zone());
}
info->field_maps_.Sort();
}
} else {
// We can only merge stores that agree on their field maps. The comparison
// below is safe, since we keep the field maps sorted.
if (field_maps_.length() != info->field_maps_.length()) return false;
for (int i = 0; i < field_maps_.length(); ++i) {
if (!field_maps_.at(i).is_identical_to(info->field_maps_.at(i))) {
return false;
}
}
}
info->GeneralizeRepresentation(r);
return true;
......@@ -5518,7 +5536,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
access_ = HObjectAccess::ForField(map, &lookup_, name_);
// Load field map for heap objects.
LoadFieldMap(map);
LoadFieldMaps(map);
} else if (lookup_.IsPropertyCallbacks()) {
Handle<Object> callback(lookup_.GetValueFromMap(*map), isolate());
if (!callback->IsAccessorPair()) return false;
......@@ -5545,26 +5563,36 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::LoadResult(Handle<Map> map) {
}
void HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMap(Handle<Map> map) {
// Clear any previous field map.
field_map_ = Handle<Map>::null();
void HOptimizedGraphBuilder::PropertyAccessInfo::LoadFieldMaps(
Handle<Map> map) {
// Figure out the field type from the accessor map.
HeapType* field_type = lookup_.GetFieldTypeFromMap(*map);
if (field_type->IsClass()) {
ASSERT(access_.representation().IsHeapObject());
Handle<Map> field_map = field_type->AsClass();
if (field_map->is_stable()) {
field_map_ = field_map;
Map::AddDependentCompilationInfo(
field_map_, DependentCode::kPrototypeCheckGroup, top_info());
Handle<HeapType> field_type(lookup_.GetFieldTypeFromMap(*map), isolate());
// Add dependency on the map that introduced the field.
Map::AddDependentCompilationInfo(
handle(lookup_.GetFieldOwnerFromMap(*map), isolate()),
DependentCode::kFieldTypeGroup, top_info());
// Collect the (stable) maps from the field type.
int num_field_maps = field_type->NumClasses();
if (num_field_maps == 0) {
field_maps_.Clear();
return;
}
ASSERT(access_.representation().IsHeapObject());
field_maps_.Reserve(num_field_maps, zone());
HeapType::Iterator<Map> it = field_type->Classes();
while (!it.Done()) {
Handle<Map> field_map = it.Current();
if (!field_map->is_stable()) {
field_maps_.Clear();
return;
}
field_maps_.Add(field_map, zone());
it.Advance();
}
field_maps_.Sort();
ASSERT_EQ(num_field_maps, field_maps_.length());
// Add dependency on the map that introduced the field.
Map::AddDependentCompilationInfo(
handle(lookup_.GetFieldOwnerFromMap(*map), isolate()),
DependentCode::kFieldTypeGroup, top_info());
}
......@@ -5608,7 +5636,7 @@ bool HOptimizedGraphBuilder::PropertyAccessInfo::CanAccessMonomorphic() {
access_ = HObjectAccess::ForField(map, &lookup_, name_);
// Load field map for heap objects.
LoadFieldMap(transition());
LoadFieldMaps(transition());
return true;
}
return false;
......
......@@ -2416,17 +2416,18 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
Handle<JSFunction> accessor() { return accessor_; }
Handle<Object> constant() { return constant_; }
Handle<Map> transition() { return handle(lookup_.GetTransitionTarget()); }
Handle<Map> field_map() { return field_map_; }
SmallMapList* field_maps() { return &field_maps_; }
HObjectAccess access() { return access_; }
private:
Type* ToType(Handle<Map> map) { return builder_->ToType(map); }
Zone* zone() { return builder_->zone(); }
Isolate* isolate() { return lookup_.isolate(); }
CompilationInfo* top_info() { return builder_->top_info(); }
CompilationInfo* current_info() { return builder_->current_info(); }
bool LoadResult(Handle<Map> map);
void LoadFieldMap(Handle<Map> map);
void LoadFieldMaps(Handle<Map> map);
bool LookupDescriptor();
bool LookupInPrototypes();
bool IsCompatible(PropertyAccessInfo* other);
......@@ -2445,7 +2446,7 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
Handle<JSFunction> accessor_;
Handle<JSObject> api_holder_;
Handle<Object> constant_;
Handle<Map> field_map_;
SmallMapList field_maps_;
HObjectAccess access_;
};
......
......@@ -147,3 +147,21 @@
baz(f3, {a: -1});
assertUnoptimized(baz);
})();
(function() {
function Foo(x) { this.x = x; this.a = x; }
function Bar(x) { this.x = x; this.b = x; }
function readA(o) { return o.x.a; }
var f = new Foo({a:1});
var b = new Bar({a:2});
assertEquals(readA(f), 1);
assertEquals(readA(b), 2);
assertEquals(readA(f), 1);
assertEquals(readA(b), 2);
%OptimizeFunctionOnNextCall(readA);
assertEquals(readA(f), 1);
assertEquals(readA(b), 2);
assertOptimized(readA);
f.a.y = 0;
assertUnoptimized(readA);
})();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment