Revision: 20796
Author: [email protected]
Date: Wed Apr 16 11:41:09 2014 UTC
Log: 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
[email protected]
Review URL: https://codereview.chromium.org/239923004
http://code.google.com/p/v8/source/detail?r=20796
Modified:
/branches/bleeding_edge/src/hydrogen-check-elimination.cc
/branches/bleeding_edge/src/hydrogen-instructions.cc
/branches/bleeding_edge/src/hydrogen-instructions.h
/branches/bleeding_edge/src/hydrogen.cc
/branches/bleeding_edge/src/hydrogen.h
/branches/bleeding_edge/test/mjsunit/field-type-tracking.js
=======================================
--- /branches/bleeding_edge/src/hydrogen-check-elimination.cc Tue Apr 15
07:36:47 2014 UTC
+++ /branches/bleeding_edge/src/hydrogen-check-elimination.cc Wed Apr 16
11:41:09 2014 UTC
@@ -383,9 +383,9 @@
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;
}
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.cc Tue Apr 15
10:14:50 2014 UTC
+++ /branches/bleeding_edge/src/hydrogen-instructions.cc Wed Apr 16
11:41:09 2014 UTC
@@ -3382,8 +3382,12 @@
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()) {
=======================================
--- /branches/bleeding_edge/src/hydrogen-instructions.h Tue Apr 15 10:14:50
2014 UTC
+++ /branches/bleeding_edge/src/hydrogen-instructions.h Wed Apr 16 11:41:09
2014 UTC
@@ -6144,8 +6144,22 @@
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 @@
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 @@
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 @@
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 @@
virtual bool IsDeletable() const V8_OVERRIDE { return true; }
HObjectAccess access_;
- Unique<Map> map_;
+ UniqueSet<Map> map_set_;
};
=======================================
--- /branches/bleeding_edge/src/hydrogen.cc Wed Apr 16 09:48:32 2014 UTC
+++ /branches/bleeding_edge/src/hydrogen.cc Wed Apr 16 11:41:09 2014 UTC
@@ -5370,7 +5370,7 @@
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 @@
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 @@
}
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 @@
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 @@
}
-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 @@
access_ = HObjectAccess::ForField(map, &lookup_, name_);
// Load field map for heap objects.
- LoadFieldMap(transition());
+ LoadFieldMaps(transition());
return true;
}
return false;
=======================================
--- /branches/bleeding_edge/src/hydrogen.h Tue Apr 15 10:14:50 2014 UTC
+++ /branches/bleeding_edge/src/hydrogen.h Wed Apr 16 11:41:09 2014 UTC
@@ -2416,17 +2416,18 @@
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 @@
Handle<JSFunction> accessor_;
Handle<JSObject> api_holder_;
Handle<Object> constant_;
- Handle<Map> field_map_;
+ SmallMapList field_maps_;
HObjectAccess access_;
};
=======================================
--- /branches/bleeding_edge/test/mjsunit/field-type-tracking.js Tue Apr 15
07:36:47 2014 UTC
+++ /branches/bleeding_edge/test/mjsunit/field-type-tracking.js Wed Apr 16
11:41:09 2014 UTC
@@ -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);
+})();
--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
---
You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.