Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(843)

Unified Diff: runtime/vm/flow_graph_optimizer.cc

Issue 17101028: Refactor load forwarding pass to use a Place abstraction. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: runtime/vm/flow_graph_optimizer.cc
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc
index 36646d3fbd7d7012cf077c0a58c6865f0f37b542..f98d393cb112b3ed2c730fb91af1096304350741 100644
--- a/runtime/vm/flow_graph_optimizer.cc
+++ b/runtime/vm/flow_graph_optimizer.cc
@@ -33,6 +33,8 @@ DEFINE_FLAG(bool, trace_range_analysis, false, "Trace range analysis progress");
DEFINE_FLAG(bool, truncating_left_shift, true,
"Optimize left shift to truncate if possible");
DEFINE_FLAG(bool, use_cha, true, "Use class hierarchy analysis.");
+DEFINE_FLAG(bool, trace_load_optimization_verbose, false,
Kevin Millikin (Google) 2013/06/25 09:18:29 I don't think I want 'verbose' in the name if ther
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+ "Print live sets for load optimization pass.");
DECLARE_FLAG(bool, eliminate_type_checks);
DECLARE_FLAG(bool, enable_type_checks);
DECLARE_FLAG(bool, trace_type_check_elimination);
@@ -413,6 +415,7 @@ void FlowGraphOptimizer::ConvertUse(Value* use, Representation from_rep) {
InsertConversion(from_rep, to_rep, use, insert_before, deopt_target);
}
+
void FlowGraphOptimizer::InsertConversionsFor(Definition* def) {
const Representation from_rep = def->representation();
@@ -3502,9 +3505,9 @@ static bool IsLoopInvariantLoad(ZoneGrowableArray<BitVector*>* sets,
intptr_t loop_header_index,
Instruction* instr) {
return (sets != NULL) &&
- instr->HasExprId() &&
+ instr->HasPlaceId() &&
((*sets)[loop_header_index] != NULL) &&
- (*sets)[loop_header_index]->Contains(instr->expr_id());
+ (*sets)[loop_header_index]->Contains(instr->place_id());
}
@@ -3634,44 +3637,263 @@ class Alias : public ValueObject {
};
-// Set mapping alias to a list of loads sharing this alias. Additionally
-// carries a set of loads that can be aliased by side-effects, essentially
-// those that are affected by calls.
-class AliasedSet : public ZoneAllocated {
+// Place describes an abstract location (e.g. field) that IR can load
+// from or store to.
+class Place : public ValueObject {
public:
- explicit AliasedSet(intptr_t max_expr_id)
- : max_expr_id_(max_expr_id),
- sets_(),
- // BitVector constructor throws if requested length is 0.
- aliased_by_effects_(max_expr_id > 0 ? new BitVector(max_expr_id)
- : NULL),
- max_field_id_(0),
- field_ids_() { }
+ enum Kind {
+ kNone,
- Alias ComputeAliasForLoad(Definition* defn) {
- if (defn->IsLoadIndexed()) {
- // We are assuming that LoadField is never used to load the first word.
- return Alias::Indexes();
- }
+ // Field location. For instance fields is represented as a pair of a Field
+ // object and an instance (SSA definition) that is being accessed.
+ // For static fields instance is NULL.
+ kField,
- LoadFieldInstr* load_field = defn->AsLoadField();
- if (load_field != NULL) {
- if (load_field->field() != NULL) {
- Definition* instance = load_field->instance()->definition();
- return Alias::Field(GetInstanceFieldId(instance, *load_field->field()));
- } else {
- return Alias::VMField(load_field->offset_in_bytes());
+ // VMField location. Represented as a pair of an instance (SSA definition)
+ // being accessed and offset to the field.
+ kVMField,
+
+ // Indexed location.
+ kIndexed,
+
+ // Current context.
+ kContext
Florian Schneider 2013/06/25 09:19:24 It would be more natural to have the context just
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Completely agreed. I began tracking context as a p
+ };
+
+ Place(const Place& other)
+ : ValueObject(),
+ kind_(other.kind_),
+ instance_(other.instance_),
+ field_(other.field_),
Kevin Millikin (Google) 2013/06/25 09:18:29 Eeek.
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Added a field called raw_selector_ and started cop
+ id_(other.id_) {
+ }
+
+ // Construct a place from instruction if instruction accesses any place.
+ // Otherwise constructs kNone place.
+ Place(Instruction* instr, bool* is_load)
+ : kind_(kNone), instance_(0), field_(0), id_(0) {
Kevin Millikin (Google) 2013/06/25 09:18:29 ..., instance_(NULL), field_(NULL), ...
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+ switch (instr->tag()) {
+ case Instruction::kLoadField: {
+ LoadFieldInstr* load_field = instr->AsLoadField();
+ instance_ = load_field->instance()->definition();
+ if (load_field->field() != NULL) {
+ kind_ = kField;
+ field_ = load_field->field();
+ } else {
+ kind_ = kVMField;
+ offset_in_bytes_ = load_field->offset_in_bytes();
+ }
+ *is_load = true;
+ break;
+ }
+
+ case Instruction::kStoreInstanceField: {
+ StoreInstanceFieldInstr* store_instance_field =
+ instr->AsStoreInstanceField();
+ kind_ = kField;
+ instance_ = store_instance_field->instance()->definition();
+ field_ = &store_instance_field->field();
Kevin Millikin (Google) 2013/06/25 09:18:29 This creeps me out. Isn't it simpler to make the
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Unions can't have members of reference type.
+ break;
}
+
+ case Instruction::kStoreVMField: {
+ StoreVMFieldInstr* store_vm_field = instr->AsStoreVMField();
+ kind_ = kVMField;
+ instance_ = store_vm_field->dest()->definition();
+ offset_in_bytes_ = store_vm_field->offset_in_bytes();
+ break;
+ }
+
+ case Instruction::kLoadStaticField:
+ kind_ = kField;
+ field_ = &instr->AsLoadStaticField()->StaticField();
+ *is_load = true;
+ break;
+
+ case Instruction::kStoreStaticField:
+ kind_ = kField;
+ field_ = &instr->AsStoreStaticField()->field();
+ break;
+
+ case Instruction::kLoadIndexed: {
+ LoadIndexedInstr* load_indexed = instr->AsLoadIndexed();
+ kind_ = kIndexed;
+ instance_ = load_indexed->array()->definition();
+ index_ = load_indexed->index()->definition();
+ *is_load = true;
+ break;
+ }
+
+ case Instruction::kStoreIndexed: {
+ StoreIndexedInstr* store_indexed = instr->AsStoreIndexed();
+ kind_ = kIndexed;
+ instance_ = store_indexed->array()->definition();
+ index_ = store_indexed->index()->definition();
+ break;
+ }
+
+ case Instruction::kCurrentContext:
+ kind_ = kContext;
+ *is_load = true;
+ break;
+
+ case Instruction::kChainContext:
+ case Instruction::kStoreContext:
+ kind_ = kContext;
+ break;
+
+ default:
+ break;
}
+ }
- if (defn->IsCurrentContext()) {
- return Alias::CurrentContext();
+ intptr_t id() const { return id_; }
+ void set_id(intptr_t id) { id_ = id; }
+
+ Kind kind() const { return kind_; }
+
+ Definition* instance() const {
+ ASSERT((kind_ == kField) || (kind_ == kVMField) || (kind_ == kIndexed));
+ return instance_;
+ }
+
+ void set_instance(Definition* def) {
+ ASSERT((kind_ == kField) || (kind_ == kVMField) || (kind_ == kIndexed));
+ instance_ = def;
+ }
+
+ const Field& field() const {
+ ASSERT(kind_ == kField);
+ return *field_;
+ }
+
+ intptr_t offset_in_bytes() const {
+ ASSERT(kind_ == kVMField);
+ return offset_in_bytes_;
+ }
+
+ Definition* index() const {
+ ASSERT(kind_ == kIndexed);
+ return index_;
+ }
+
+ const char* ToCString() const {
+ switch (kind_) {
+ case kNone:
+ return "<none>";
+
+ case kField: {
+ const char* field_name = String::Handle(field().name()).ToCString();
+ if (instance() == NULL) {
+ return field_name;
+ }
+ return Isolate::Current()->current_zone()->PrintToString(
+ "<v%"Pd".%s>", instance()->ssa_temp_index(), field_name);
+ }
+
+ case kVMField: {
+ return Isolate::Current()->current_zone()->PrintToString(
+ "<v%"Pd"@%"Pd">", instance()->ssa_temp_index(), offset_in_bytes());
+ }
+
+ case kIndexed: {
+ return Isolate::Current()->current_zone()->PrintToString(
+ "<v%"Pd"[v%"Pd"]>",
+ instance()->ssa_temp_index(),
+ index()->ssa_temp_index());
+ }
+
+ case kContext:
+ return "<context>";
}
+ UNREACHABLE();
+ return "<?>";
+ }
+
+ bool IsFinalField() const {
+ return (kind() == Place::kField) && field().is_final();
Kevin Millikin (Google) 2013/06/25 09:18:29 No need for Place:: here.
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+ }
+
+ intptr_t Hashcode() const {
+ return (kind_ * 63 + reinterpret_cast<intptr_t>(instance_)) * 31 +
+ FieldHashcode();
+ }
+
+ bool Equals(Place* other) const {
+ return (kind_ == other->kind_) &&
+ (instance_ == other->instance_) &&
+ SameField(other);
+ }
+
+ // Create a zone allocated copy of this place.
+ static Place* Wrap(const Place& place);
+
+ private:
+ bool SameField(Place* other) const {
+ return (kind_ == kField) ? (field().raw() == other->field().raw())
+ : (offset_in_bytes_ == other->offset_in_bytes_);
+ }
+
+ intptr_t FieldHashcode() const {
+ return (kind_ == kField) ? reinterpret_cast<intptr_t>(field().raw())
+ : offset_in_bytes_;
+ }
+
+ Kind kind_;
+ Definition* instance_;
+ union {
+ const Field* field_;
+ intptr_t offset_in_bytes_;
+ Definition* index_;
+ };
+
+ intptr_t id_;
+};
+
+
+class ZonePlace : public ZoneAllocated {
+ public:
+ explicit ZonePlace(const Place& place) : place_(place) { }
- LoadStaticFieldInstr* load_static_field = defn->AsLoadStaticField();
- if (load_static_field != NULL) {
- return Alias::Field(GetFieldId(kAnyInstance,
- load_static_field->StaticField()));
+ Place* place() { return &place_; }
+
+ private:
+ Place place_;
+};
+
+
+Place* Place::Wrap(const Place& place) {
+ return (new ZonePlace(place))->place();
+}
+
+
+// Set mapping alias to a list of places sharing this alias. Additionally
Kevin Millikin (Google) 2013/06/25 09:18:29 First sentence confusing? "A map from aliases to
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+// carries a set of places that can be aliased by side-effects, essentially
+// those that are affected by calls.
+class AliasedSet : public ZoneAllocated {
+ public:
+ explicit AliasedSet(ZoneGrowableArray<Place*>* places)
+ : places_(*places),
+ sets_(),
+ // BitVector constructor throws if requested length is 0.
Kevin Millikin (Google) 2013/06/25 09:18:29 I don't think it does any more, so this can be sim
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+ aliased_by_effects_(places->length() > 0 ?
+ new BitVector(places->length()) : NULL),
+ max_field_id_(0),
+ field_ids_() { }
+
+ Alias ComputeAlias(Place* place) {
+ switch (place->kind()) {
+ case Place::kIndexed:
+ return Alias::Indexes();
+ case Place::kField:
+ return Alias::Field(
+ GetInstanceFieldId(place->instance(), place->field()));
+ case Place::kVMField:
+ return Alias::VMField(place->offset_in_bytes());
+ case Place::kContext:
+ return Alias::CurrentContext();
+ case Place::kNone:
+ UNREACHABLE();
}
UNREACHABLE();
@@ -3708,24 +3930,21 @@ class AliasedSet : public ZoneAllocated {
return Alias::None();
}
- bool Contains(const Alias alias) {
- const intptr_t idx = alias.ToIndex();
- return (idx < sets_.length()) && (sets_[idx] != NULL);
- }
-
BitVector* Get(const Alias alias) {
- ASSERT(Contains(alias));
- return sets_[alias.ToIndex()];
+ const intptr_t idx = alias.ToIndex();
+ return (idx < sets_.length()) ? sets_[idx] : NULL;
}
- void AddRepresentative(Definition* defn) {
- AddIdForAlias(ComputeAliasForLoad(defn), defn->expr_id());
- if (!IsIndependentFromEffects(defn)) {
- aliased_by_effects_->Add(defn->expr_id());
+ void AddRepresentative(Place* place) {
+ if (!place->IsFinalField()) {
+ AddIdForAlias(ComputeAlias(place), place->id());
+ if (!IsIndependentFromEffects(place)) {
+ aliased_by_effects_->Add(place->id());
+ }
}
}
- void AddIdForAlias(const Alias alias, intptr_t expr_id) {
+ void AddIdForAlias(const Alias alias, intptr_t place_id) {
const intptr_t idx = alias.ToIndex();
while (sets_.length() <= idx) {
@@ -3733,17 +3952,67 @@ class AliasedSet : public ZoneAllocated {
}
if (sets_[idx] == NULL) {
- sets_[idx] = new BitVector(max_expr_id_);
+ sets_[idx] = new BitVector(max_place_id());
}
- sets_[idx]->Add(expr_id);
+ sets_[idx]->Add(place_id);
}
- intptr_t max_expr_id() const { return max_expr_id_; }
- bool IsEmpty() const { return max_expr_id_ == 0; }
+ void ForwardPhiLoad(BlockEntryInstr* block, intptr_t from, intptr_t to) {
+ const intptr_t block_num = block->preorder_number();
+ while (forwarded_phi_loads_.length() <= block_num) {
+ forwarded_phi_loads_.Add(NULL);
+ }
+
+ if (forwarded_phi_loads_[block_num] == NULL) {
+ forwarded_phi_loads_[block_num] = new ZoneGrowableArray<LoadPair>(5);
+ }
+
+ forwarded_phi_loads_[block_num]->Add(LoadPair(from, to));
+ }
+
+ class LoadPair {
Florian Schneider 2013/06/25 09:19:24 Could be made a struct with public members from, t
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 I prefer a class.
+ public:
+ LoadPair(intptr_t from, intptr_t to) : from_(from), to_(to) { }
+
+ intptr_t from() const { return from_; }
+ intptr_t to() const { return to_; }
+
+ private:
+ intptr_t from_;
+ intptr_t to_;
+ };
+
+ typedef const ZoneGrowableArray<LoadPair>* ForwardedLoadsList;
+
+ ForwardedLoadsList GetForwardedLoads(BlockEntryInstr* block) {
+ const intptr_t block_num = block->preorder_number();
+ return (block_num < forwarded_phi_loads_.length()) ?
+ forwarded_phi_loads_[block_num] : NULL;
+ }
+
+ intptr_t max_place_id() const { return places().length(); }
+ bool IsEmpty() const { return max_place_id() == 0; }
BitVector* aliased_by_effects() const { return aliased_by_effects_; }
+ const ZoneGrowableArray<Place*>& places() const {
+ return places_;
+ }
+
+ void PrintSet(BitVector* set) {
+ bool comma = false;
+ for (BitVector::Iterator it(set);
+ !it.Done();
+ it.Advance()) {
+ if (comma) {
+ OS::Print(", ");
+ }
+ OS::Print("%s", places_[it.Current()]->ToCString());
+ comma = true;
+ }
+ }
+
private:
// Get id assigned to the given field. Assign a new id if the field is seen
// for the first time.
@@ -3770,14 +4039,16 @@ class AliasedSet : public ZoneAllocated {
// If multiple SSA names can point to the same object then we use
// kAnyInstance instead of a concrete SSA name.
intptr_t GetInstanceFieldId(Definition* defn, const Field& field) {
- ASSERT(!field.is_static());
+ ASSERT(field.is_static() == (defn == NULL));
intptr_t instance_id = kAnyInstance;
- AllocateObjectInstr* alloc = defn->AsAllocateObject();
- if ((alloc != NULL) && !CanBeAliased(alloc)) {
- instance_id = alloc->ssa_temp_index();
- ASSERT(instance_id != kAnyInstance);
+ if (defn != NULL) {
+ AllocateObjectInstr* alloc = defn->AsAllocateObject();
+ if ((alloc != NULL) && !CanBeAliased(alloc)) {
+ instance_id = alloc->ssa_temp_index();
+ ASSERT(instance_id != kAnyInstance);
+ }
}
return GetFieldId(instance_id, field);
@@ -3820,9 +4091,8 @@ class AliasedSet : public ZoneAllocated {
// Returns true if the given load is unaffected by external side-effects.
// This essentially means that no stores to the same location can
// occur in other functions.
- bool IsIndependentFromEffects(Definition* defn) {
- LoadFieldInstr* load_field = defn->AsLoadField();
- if (load_field != NULL) {
+ bool IsIndependentFromEffects(Place* place) {
+ if (place->IsFinalField()) {
// Note that we can't use LoadField's is_immutable attribute here because
// some VM-fields (those that have no corresponding Field object and
// accessed through offset alone) can share offset but have different
@@ -3835,19 +4105,14 @@ class AliasedSet : public ZoneAllocated {
// conservative assumption for the VM-properties.
// TODO(vegorov): disambiguate immutable and non-immutable VM-fields with
// the same offset e.g. through recognized kind.
- if ((load_field->field() != NULL) &&
- (load_field->field()->is_final())) {
- return true;
- }
-
- AllocateObjectInstr* alloc =
- load_field->instance()->definition()->AsAllocateObject();
- return (alloc != NULL) && !CanBeAliased(alloc);
+ return true;
}
- LoadStaticFieldInstr* load_static_field = defn->AsLoadStaticField();
- if (load_static_field != NULL) {
- return load_static_field->StaticField().is_final();
+ if (((place->kind() == Place::kField) ||
+ (place->kind() == Place::kVMField)) &&
+ (place->instance() != NULL)) {
+ AllocateObjectInstr* alloc = place->instance()->AsAllocateObject();
+ return (alloc != NULL) && !CanBeAliased(alloc);
}
return false;
@@ -3890,7 +4155,7 @@ class AliasedSet : public ZoneAllocated {
Value value_;
};
- const intptr_t max_expr_id_;
+ const ZoneGrowableArray<Place*>& places_;
// Maps alias index to a set of ssa indexes corresponding to loads with the
// given alias.
@@ -3898,6 +4163,8 @@ class AliasedSet : public ZoneAllocated {
BitVector* aliased_by_effects_;
+ GrowableArray<ZoneGrowableArray<LoadPair>* > forwarded_phi_loads_;
+
// Table mapping static field to their id used during optimization pass.
intptr_t max_field_id_;
DirectChainedHashMap<FieldIdPair> field_ids_;
@@ -3933,114 +4200,14 @@ static Definition* GetStoredValue(Instruction* instr) {
}
-// KeyValueTrait used for numbering of loads. Allows to lookup loads
-// corresponding to stores.
-class LoadKeyValueTrait {
- public:
- typedef Definition* Value;
- typedef Instruction* Key;
- typedef Definition* Pair;
-
- static Key KeyOf(Pair kv) {
- return kv;
- }
-
- static Value ValueOf(Pair kv) {
- return kv;
- }
-
- static inline intptr_t Hashcode(Key key) {
- intptr_t object = 0;
- intptr_t location = 0;
-
- if (key->IsLoadIndexed()) {
- LoadIndexedInstr* load_indexed = key->AsLoadIndexed();
- object = load_indexed->array()->definition()->ssa_temp_index();
- location = load_indexed->index()->definition()->ssa_temp_index();
- } else if (key->IsStoreIndexed()) {
- StoreIndexedInstr* store_indexed = key->AsStoreIndexed();
- object = store_indexed->array()->definition()->ssa_temp_index();
- location = store_indexed->index()->definition()->ssa_temp_index();
- } else if (key->IsLoadField()) {
- LoadFieldInstr* load_field = key->AsLoadField();
- object = load_field->instance()->definition()->ssa_temp_index();
- location = load_field->offset_in_bytes();
- } else if (key->IsStoreInstanceField()) {
- StoreInstanceFieldInstr* store_field = key->AsStoreInstanceField();
- object = store_field->instance()->definition()->ssa_temp_index();
- location = store_field->field().Offset();
- } else if (key->IsStoreVMField()) {
- StoreVMFieldInstr* store_field = key->AsStoreVMField();
- object = store_field->dest()->definition()->ssa_temp_index();
- location = store_field->offset_in_bytes();
- } else if (key->IsLoadStaticField()) {
- LoadStaticFieldInstr* load_static_field = key->AsLoadStaticField();
- object = String::Handle(load_static_field->StaticField().name()).Hash();
- } else if (key->IsStoreStaticField()) {
- StoreStaticFieldInstr* store_static_field = key->AsStoreStaticField();
- object = String::Handle(store_static_field->field().name()).Hash();
- } else {
- ASSERT(key->IsStoreContext() ||
- key->IsCurrentContext() ||
- key->IsChainContext());
- }
-
- return object * 31 + location;
- }
-
- static inline bool IsKeyEqual(Pair kv, Key key) {
- if (kv->Equals(key)) return true;
-
- if (kv->IsLoadIndexed()) {
- if (key->IsStoreIndexed()) {
- LoadIndexedInstr* load_indexed = kv->AsLoadIndexed();
- StoreIndexedInstr* store_indexed = key->AsStoreIndexed();
- return load_indexed->array()->Equals(store_indexed->array()) &&
- load_indexed->index()->Equals(store_indexed->index());
- }
- return false;
- }
-
- if (kv->IsLoadStaticField()) {
- if (key->IsStoreStaticField()) {
- LoadStaticFieldInstr* load_static_field = kv->AsLoadStaticField();
- StoreStaticFieldInstr* store_static_field = key->AsStoreStaticField();
- return load_static_field->StaticField().raw() ==
- store_static_field->field().raw();
- }
- return false;
- }
-
- if (kv->IsCurrentContext()) {
- return key->IsStoreContext() || key->IsChainContext();
- }
-
- ASSERT(kv->IsLoadField());
- LoadFieldInstr* load_field = kv->AsLoadField();
- if (key->IsStoreVMField()) {
- StoreVMFieldInstr* store_field = key->AsStoreVMField();
- return load_field->instance()->Equals(store_field->dest()) &&
- (load_field->offset_in_bytes() == store_field->offset_in_bytes());
- } else if (key->IsStoreInstanceField()) {
- StoreInstanceFieldInstr* store_field = key->AsStoreInstanceField();
- return load_field->instance()->Equals(store_field->instance()) &&
- (load_field->offset_in_bytes() == store_field->field().Offset());
- }
-
- return false;
- }
-};
-
-
-static AliasedSet* NumberLoadExpressions(
+static AliasedSet* NumberPlaces(
FlowGraph* graph,
- DirectChainedHashMap<LoadKeyValueTrait>* map) {
- intptr_t expr_id = 0;
-
+ DirectChainedHashMap<PointerKeyValueTrait<Place> >* map) {
// Loads representing different expression ids will be collected and
// used to build per offset kill sets.
- GrowableArray<Definition*> loads(10);
+ ZoneGrowableArray<Place*>* places = new ZoneGrowableArray<Place*>(10);
+ bool has_loads = false;
for (BlockIterator it = graph->reverse_postorder_iterator();
!it.Done();
it.Advance()) {
@@ -4048,33 +4215,106 @@ static AliasedSet* NumberLoadExpressions(
for (ForwardInstructionIterator instr_it(block);
!instr_it.Done();
instr_it.Advance()) {
- Definition* defn = instr_it.Current()->AsDefinition();
- if ((defn == NULL) || !IsLoadEliminationCandidate(defn)) {
+ Instruction* instr = instr_it.Current();
+
+ Place place(instr, &has_loads);
+ if (place.kind() == Place::kNone) {
continue;
}
- Definition* result = map->Lookup(defn);
+
+ Place* result = map->Lookup(&place);
if (result == NULL) {
- map->Insert(defn);
- defn->set_expr_id(expr_id++);
- loads.Add(defn);
- } else {
- defn->set_expr_id(result->expr_id());
+ place.set_id(places->length());
+ result = Place::Wrap(place);
+ map->Insert(result);
+ places->Add(result);
+
+ if (FLAG_trace_optimization) {
+ OS::Print("numbering %s as %"Pd"\n",
+ result->ToCString(),
+ result->id());
+ }
}
+ instr->set_place_id(result->id());
+ }
+ }
+
+ if (!has_loads) {
+ return NULL;
+ }
+
+ for (intptr_t i = 0; i < places->length(); i++) {
Kevin Millikin (Google) 2013/06/25 09:18:29 This needs a bit of comment, especially since it's
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+ Place* place = (*places)[i];
+
+ if (((place->kind() == Place::kField) ||
+ (place->kind() == Place::kVMField)) &&
+ (place->instance() != NULL) &&
+ (place->instance()->IsPhi())) {
+ PhiInstr* phi = place->instance()->AsPhi();
+
if (FLAG_trace_optimization) {
- OS::Print("load v%"Pd" is numbered as %"Pd"\n",
- defn->ssa_temp_index(),
- defn->expr_id());
+ OS::Print("phi dependant place %s\n", place->ToCString());
+ }
+ Place input_place(*place);
+ for (intptr_t j = 0; j < phi->InputCount(); j++) {
+ Definition* input = phi->InputAt(j)->definition();
+ if (input->IsPhi()) {
Kevin Millikin (Google) 2013/06/25 09:18:29 Why only phis?
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 The reasoning was: if place was not numbered due t
+ input_place.set_instance(input);
+ Place* result = map->Lookup(&input_place);
+ if (result == NULL) {
+ input_place.set_id(places->length());
+ result = Place::Wrap(input_place);
+ map->Insert(result);
+ places->Add(result);
+ if (FLAG_trace_optimization) {
+ OS::Print(" adding place %s as %"Pd"\n",
+ result->ToCString(),
+ result->id());
+ }
+ }
+ }
}
}
}
// Build aliasing sets mapping aliases to loads.
- AliasedSet* aliased_set = new AliasedSet(expr_id);
- for (intptr_t i = 0; i < loads.length(); i++) {
- Definition* defn = loads[i];
- aliased_set->AddRepresentative(defn);
+ AliasedSet* aliased_set = new AliasedSet(places);
+ for (intptr_t i = 0; i < places->length(); i++) {
+ Place* place = (*places)[i];
+ aliased_set->AddRepresentative(place);
+
+ if (((place->kind() == Place::kField) ||
+ (place->kind() == Place::kVMField)) &&
+ (place->instance() != NULL) &&
+ (place->instance()->IsPhi())) {
+ if (FLAG_trace_optimization) {
+ OS::Print("phi dependant place %"Pd"\n", place->id());
+ }
+
+ PhiInstr* phi = place->instance()->AsPhi();
+
+ Place input_place(*place);
+ for (intptr_t j = 0; j < phi->InputCount(); j++) {
+ BlockEntryInstr* pred = phi->GetBlock()->PredecessorAt(j);
+ Definition* input = phi->InputAt(j)->definition();
+ input_place.set_instance(input);
+ Place* result = map->Lookup(&input_place);
+ if (result != NULL) {
Kevin Millikin (Google) 2013/06/25 09:18:29 Specifically, it's not clear if these are exactly
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 I merged phi related parts of these two loops toge
+ if (FLAG_trace_optimization) {
+ OS::Print(" input place @%"Pd" (from v%"Pd"): %s\n",
+ j,
+ input->ssa_temp_index(),
+ result->ToCString());
+ }
+ aliased_set->ForwardPhiLoad(pred, result->id(), place->id());
+ } else {
+ aliased_set->ForwardPhiLoad(pred, -1, place->id());
+ }
+ }
+ }
}
+
return aliased_set;
}
@@ -4083,7 +4323,7 @@ class LoadOptimizer : public ValueObject {
public:
LoadOptimizer(FlowGraph* graph,
AliasedSet* aliased_set,
- DirectChainedHashMap<LoadKeyValueTrait>* map)
+ DirectChainedHashMap<PointerKeyValueTrait<Place> >* map)
: graph_(graph),
map_(map),
aliased_set_(aliased_set),
@@ -4099,10 +4339,10 @@ class LoadOptimizer : public ValueObject {
forwarded_(false) {
const intptr_t num_blocks = graph_->preorder().length();
for (intptr_t i = 0; i < num_blocks; i++) {
- out_.Add(new BitVector(aliased_set_->max_expr_id()));
- gen_.Add(new BitVector(aliased_set_->max_expr_id()));
- kill_.Add(new BitVector(aliased_set_->max_expr_id()));
- in_.Add(new BitVector(aliased_set_->max_expr_id()));
+ out_.Add(NULL);
+ gen_.Add(new BitVector(aliased_set_->max_place_id()));
+ kill_.Add(new BitVector(aliased_set_->max_place_id()));
+ in_.Add(new BitVector(aliased_set_->max_place_id()));
exposed_values_.Add(NULL);
out_values_.Add(NULL);
@@ -4111,10 +4351,13 @@ class LoadOptimizer : public ValueObject {
static bool OptimizeGraph(FlowGraph* graph) {
ASSERT(FLAG_load_cse);
+ if (FLAG_trace_load_optimization_verbose) {
+ FlowGraphPrinter::PrintGraph("Before LoadOptimizer", graph);
+ }
- DirectChainedHashMap<LoadKeyValueTrait> map;
- AliasedSet* aliased_set = NumberLoadExpressions(graph, &map);
- if (!aliased_set->IsEmpty()) {
+ DirectChainedHashMap<PointerKeyValueTrait<Place> > map;
+ AliasedSet* aliased_set = NumberPlaces(graph, &map);
+ if ((aliased_set != NULL) && !aliased_set->IsEmpty()) {
// If any loads were forwarded return true from Optimize to run load
// forwarding again. This will allow to forward chains of loads.
// This is especially important for context variables as they are built
@@ -4130,12 +4373,18 @@ class LoadOptimizer : public ValueObject {
private:
bool Optimize() {
ComputeInitialSets();
+ ComputeOutSets();
ComputeOutValues();
if (graph_->is_licm_allowed()) {
MarkLoopInvariantLoads();
}
ForwardLoads();
EmitPhis();
+
+ if (FLAG_trace_load_optimization_verbose) {
+ FlowGraphPrinter::PrintGraph("After LoadOptimizer", graph_);
+ }
+
return forwarded_;
}
@@ -4146,6 +4395,8 @@ class LoadOptimizer : public ValueObject {
// Loads that are locally redundant will be replaced as we go through
// instructions.
void ComputeInitialSets() {
+ BitVector* forwarded_loads = new BitVector(aliased_set_->max_place_id());
+
for (BlockIterator block_it = graph_->reverse_postorder_iterator();
!block_it.Done();
block_it.Advance()) {
@@ -4166,31 +4417,36 @@ class LoadOptimizer : public ValueObject {
const Alias alias = aliased_set_->ComputeAliasForStore(instr);
if (!alias.IsNone()) {
// Interfering stores kill only loads from the same offset.
- if (aliased_set_->Contains(alias)) {
- BitVector* killed = aliased_set_->Get(alias);
+ BitVector* killed = aliased_set_->Get(alias);
+
+ if (killed != NULL) {
kill->AddAll(killed);
// There is no need to clear out_values when clearing GEN set
// because only those values that are in the GEN set
// will ever be used.
gen->RemoveAll(killed);
+ }
- // Only forward stores to normal arrays and float64 arrays
- // to loads because other array stores (intXX/uintXX/float32)
- // may implicitly convert the value stored.
- StoreIndexedInstr* array_store = instr->AsStoreIndexed();
- if (array_store == NULL ||
- array_store->class_id() == kArrayCid ||
- array_store->class_id() == kTypedDataFloat64ArrayCid) {
- Definition* load = map_->Lookup(instr);
- if (load != NULL) {
- // Store has a corresponding numbered load. Try forwarding
- // stored value to it.
- gen->Add(load->expr_id());
- if (out_values == NULL) out_values = CreateBlockOutValues();
- (*out_values)[load->expr_id()] = GetStoredValue(instr);
- }
+ // Only forward stores to normal arrays and float64 arrays
+ // to loads because other array stores (intXX/uintXX/float32)
+ // may implicitly convert the value stored.
+ StoreIndexedInstr* array_store = instr->AsStoreIndexed();
+ if (array_store == NULL ||
+ array_store->class_id() == kArrayCid ||
+ array_store->class_id() == kTypedDataFloat64ArrayCid) {
+ bool is_load = false;
+ Place store_place(instr, &is_load);
+ ASSERT(!is_load);
+ Place* place = map_->Lookup(&store_place);
+ if (place != NULL) {
+ // Store has a corresponding numbered place that might have a
+ // load. Try forwarding stored value to it.
+ gen->Add(place->id());
+ if (out_values == NULL) out_values = CreateBlockOutValues();
+ (*out_values)[place->id()] = GetStoredValue(instr);
}
}
+
ASSERT(!instr->IsDefinition() ||
!IsLoadEliminationCandidate(instr->AsDefinition()));
continue;
@@ -4240,9 +4496,9 @@ class LoadOptimizer : public ValueObject {
LoadFieldInstr* load = use->instruction()->AsLoadField();
if (load != NULL) {
// Found a load. Initialize current value of the field to null.
- gen->Add(load->expr_id());
+ gen->Add(load->place_id());
if (out_values == NULL) out_values = CreateBlockOutValues();
- (*out_values)[load->expr_id()] = graph_->constant_null();
+ (*out_values)[load->place_id()] = graph_->constant_null();
}
}
continue;
@@ -4252,12 +4508,12 @@ class LoadOptimizer : public ValueObject {
continue;
}
- const intptr_t expr_id = defn->expr_id();
- if (gen->Contains(expr_id)) {
+ const intptr_t place_id = defn->place_id();
+ if (gen->Contains(place_id)) {
// This is a locally redundant load.
- ASSERT((out_values != NULL) && ((*out_values)[expr_id] != NULL));
+ ASSERT((out_values != NULL) && ((*out_values)[place_id] != NULL));
- Definition* replacement = (*out_values)[expr_id];
+ Definition* replacement = (*out_values)[place_id];
EnsureSSATempIndex(graph_, defn, replacement);
if (FLAG_trace_optimization) {
OS::Print("Replacing load v%"Pd" with v%"Pd"\n",
@@ -4269,7 +4525,7 @@ class LoadOptimizer : public ValueObject {
instr_it.RemoveCurrentFromGraph();
forwarded_ = true;
continue;
- } else if (!kill->Contains(expr_id)) {
+ } else if (!kill->Contains(place_id)) {
// This is an exposed load: it is the first representative of a
// given expression id and it is not killed on the path from
// the block entry.
@@ -4277,33 +4533,60 @@ class LoadOptimizer : public ValueObject {
static const intptr_t kMaxExposedValuesInitialSize = 5;
exposed_values = new ZoneGrowableArray<Definition*>(
Utils::Minimum(kMaxExposedValuesInitialSize,
- aliased_set_->max_expr_id()));
+ aliased_set_->max_place_id()));
}
exposed_values->Add(defn);
}
- gen->Add(expr_id);
+ gen->Add(place_id);
if (out_values == NULL) out_values = CreateBlockOutValues();
- (*out_values)[expr_id] = defn;
+ (*out_values)[place_id] = defn;
+ }
+
+ AliasedSet::ForwardedLoadsList forwarded =
+ aliased_set_->GetForwardedLoads(block);
+ if (forwarded != NULL) {
+ ComputePhiLoads(forwarded, gen, forwarded_loads);
}
- out_[preorder_number]->CopyFrom(gen);
exposed_values_[preorder_number] = exposed_values;
out_values_[preorder_number] = out_values;
}
}
- // Compute OUT sets and corresponding out_values mappings by propagating them
- // iteratively until fix point is reached.
- // No replacement is done at this point and thus any out_value[expr_id] is
- // changed at most once: from NULL to an actual value.
- // When merging incoming loads we might need to create a phi.
- // These phis are not inserted at the graph immediately because some of them
- // might become redundant after load forwarding is done.
- void ComputeOutValues() {
- BitVector* temp = new BitVector(aliased_set_->max_expr_id());
+ static void ComputePhiLoads(AliasedSet::ForwardedLoadsList forwarded,
+ BitVector* out,
+ BitVector* forwarded_loads) {
+ forwarded_loads->Clear();
+
+ for (intptr_t i = 0; i < forwarded->length(); i++) {
+ const intptr_t from = (*forwarded)[i].from();
+ const intptr_t to = (*forwarded)[i].to();
+ if (from == to) continue;
+
+ if ((from != -1) && out->Contains(from)) {
+ forwarded_loads->Add(to);
+ }
+ }
+
+ for (intptr_t i = 0; i < forwarded->length(); i++) {
+ const intptr_t from = (*forwarded)[i].from();
+ const intptr_t to = (*forwarded)[i].to();
+ if (from == to) continue;
+
+ out->Remove(to);
+ }
+
+ out->AddAll(forwarded_loads);
+ }
+
+ // Compute OUT sets by propagating them iteratively until fix point
+ // is reached.
+ void ComputeOutSets() {
+ BitVector* temp = new BitVector(aliased_set_->max_place_id());
+ BitVector* forwarded_loads = new BitVector(aliased_set_->max_place_id());
bool changed = true;
while (changed) {
@@ -4321,72 +4604,158 @@ class LoadOptimizer : public ValueObject {
BitVector* block_kill = kill_[preorder_number];
BitVector* block_gen = gen_[preorder_number];
- if (FLAG_trace_optimization) {
- OS::Print("B%"Pd"", block->block_id());
- block_in->Print();
- block_out->Print();
- block_kill->Print();
- block_gen->Print();
- OS::Print("\n");
- }
-
- ZoneGrowableArray<Definition*>* block_out_values =
- out_values_[preorder_number];
-
// Compute block_in as the intersection of all out(p) where p
// is a predecessor of the current block.
if (block->IsGraphEntry()) {
temp->Clear();
} else {
- // TODO(vegorov): this can be optimized for the case of a single
- // predecessor.
- // TODO(vegorov): this can be reordered to reduce amount of operations
- // temp->CopyFrom(first_predecessor)
temp->SetAll();
ASSERT(block->PredecessorCount() > 0);
for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
BlockEntryInstr* pred = block->PredecessorAt(i);
BitVector* pred_out = out_[pred->preorder_number()];
- temp->Intersect(pred_out);
+ if (pred_out != NULL) {
+ temp->Intersect(pred_out);
+ }
}
}
- if (!temp->Equals(*block_in)) {
+ if (!temp->Equals(*block_in) || (block_out == NULL)) {
// If IN set has changed propagate the change to OUT set.
block_in->CopyFrom(temp);
- if (block_out->KillAndAdd(block_kill, block_in)) {
- // If OUT set has changed then we have new values available out of
- // the block. Compute these values creating phi where necessary.
- for (BitVector::Iterator it(block_out);
- !it.Done();
- it.Advance()) {
- const intptr_t expr_id = it.Current();
-
- if (block_out_values == NULL) {
- out_values_[preorder_number] = block_out_values =
- CreateBlockOutValues();
- }
- if ((*block_out_values)[expr_id] == NULL) {
- ASSERT(block->PredecessorCount() > 0);
- (*block_out_values)[expr_id] =
- MergeIncomingValues(block, expr_id);
- }
+ temp->RemoveAll(block_kill);
+ temp->AddAll(block_gen);
+
+ AliasedSet::ForwardedLoadsList forwarded =
+ aliased_set_->GetForwardedLoads(block);
+ if (forwarded != NULL) {
+ ComputePhiLoads(forwarded, temp, forwarded_loads);
+ }
+
+ if ((block_out == NULL) || !block_out->Equals(*temp)) {
+ if (block_out == NULL) {
+ block_out = out_[preorder_number] =
+ new BitVector(aliased_set_->max_place_id());
}
+ block_out->CopyFrom(temp);
changed = true;
}
}
+ }
+ }
+ }
- if (FLAG_trace_optimization) {
- OS::Print("after B%"Pd"", block->block_id());
- block_in->Print();
- block_out->Print();
- block_kill->Print();
- block_gen->Print();
- OS::Print("\n");
+ // Compute out_values mappings by propagating them in reverse postorder once
+ // through the graph. Generate phis on back edges where eager merge is
+ // impossible.
+ // No replacement is done at this point and thus any out_value[place_id] is
+ // changed at most once: from NULL to an actual value.
+ // When merging incoming loads we might need to create a phi.
+ // These phis are not inserted at the graph immediately because some of them
+ // might become redundant after load forwarding is done.
+ void ComputeOutValues() {
+ GrowableArray<PhiInstr*> pending_phis(5);
+ ZoneGrowableArray<Definition*>* temp_forwarded_values = NULL;
+
+ for (BlockIterator block_it = graph_->reverse_postorder_iterator();
+ !block_it.Done();
+ block_it.Advance()) {
+ BlockEntryInstr* block = block_it.Current();
+
+ const bool can_merge_eagerly = CanMergeEagerly(block);
+
+ const intptr_t preorder_number = block->preorder_number();
+
+ ZoneGrowableArray<Definition*>* block_out_values =
+ out_values_[preorder_number];
+
+
+ // If OUT set has changed then we have new values available out of
+ // the block. Compute these values creating phi where necessary.
+ for (BitVector::Iterator it(out_[preorder_number]);
+ !it.Done();
+ it.Advance()) {
+ const intptr_t place_id = it.Current();
+
+ if (block_out_values == NULL) {
+ out_values_[preorder_number] = block_out_values =
+ CreateBlockOutValues();
}
+
+ if ((*block_out_values)[place_id] == NULL) {
+ ASSERT(block->PredecessorCount() > 0);
+ Definition* in_value = can_merge_eagerly ?
+ MergeIncomingValues(block, place_id) : NULL;
+ if ((in_value == NULL) &&
+ (in_[preorder_number]->Contains(place_id))) {
+ PhiInstr* phi = new PhiInstr(block->AsJoinEntry(),
+ block->PredecessorCount());
+ phi->set_place_id(place_id);
+ pending_phis.Add(phi);
+ in_value = phi;
+ }
+ (*block_out_values)[place_id] = in_value;
+ }
+ }
+
+ AliasedSet::ForwardedLoadsList forwarded =
+ aliased_set_->GetForwardedLoads(block);
+ if ((forwarded != NULL) && (block_out_values != NULL)) {
+ if (temp_forwarded_values == NULL) {
+ temp_forwarded_values = CreateBlockOutValues();
+ }
+
+ for (intptr_t i = 0; i < forwarded->length(); i++) {
+ const intptr_t from = (*forwarded)[i].from();
+ const intptr_t to = (*forwarded)[i].to();
+ if (from == to) continue;
+
+ (*temp_forwarded_values)[to] = NULL;
+ if (from != -1) {
+ (*temp_forwarded_values)[to] = (*block_out_values)[from];
+ }
+ }
+
+ for (intptr_t i = 0; i < forwarded->length(); i++) {
+ const intptr_t from = (*forwarded)[i].from();
+ const intptr_t to = (*forwarded)[i].to();
+ if (from == to) continue;
+
+ (*block_out_values)[to] = (*temp_forwarded_values)[to];
+ }
+ }
+
+ if (FLAG_trace_load_optimization_verbose) {
+ OS::Print("B%"Pd"\n", block->block_id());
+ OS::Print(" IN: ");
+ aliased_set_->PrintSet(in_[preorder_number]);
+ OS::Print("\n");
+
+ OS::Print(" KILL: ");
+ aliased_set_->PrintSet(kill_[preorder_number]);
+ OS::Print("\n");
+
+ OS::Print(" OUT: ");
+ aliased_set_->PrintSet(out_[preorder_number]);
+ OS::Print("\n");
+ }
+ }
+
+ // All blocks were visited. Fuly
Kevin Millikin (Google) 2013/06/25 09:18:29 Fuly?
Vyacheslav Egorov (Google) 2013/06/25 18:02:39 Done.
+ for (intptr_t i = 0; i < pending_phis.length(); i++) {
+ FillPhiInputs(pending_phis[i]);
+ }
+ }
+
+ bool CanMergeEagerly(BlockEntryInstr* block) {
+ for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
+ BlockEntryInstr* pred = block->PredecessorAt(i);
+ if (pred->postorder_number() < block->postorder_number()) {
+ return false;
}
}
+ return true;
}
void MarkLoopInvariantLoads() {
@@ -4404,7 +4773,7 @@ class LoadOptimizer : public ValueObject {
continue;
}
- BitVector* loop_gen = new BitVector(aliased_set_->max_expr_id());
+ BitVector* loop_gen = new BitVector(aliased_set_->max_place_id());
for (BitVector::Iterator loop_it(header->loop_info());
!loop_it.Done();
loop_it.Advance()) {
@@ -4421,8 +4790,8 @@ class LoadOptimizer : public ValueObject {
if (FLAG_trace_optimization) {
for (BitVector::Iterator it(loop_gen); !it.Done(); it.Advance()) {
- OS::Print("load %"Pd" is loop invariant for B%"Pd"\n",
- it.Current(),
+ OS::Print("place %s is loop invariant for B%"Pd"\n",
+ aliased_set_->places()[it.Current()]->ToCString(),
header->block_id());
}
}
@@ -4436,41 +4805,53 @@ class LoadOptimizer : public ValueObject {
// Compute incoming value for the given expression id.
// Will create a phi if different values are incoming from multiple
// predecessors.
- Definition* MergeIncomingValues(BlockEntryInstr* block, intptr_t expr_id) {
+ Definition* MergeIncomingValues(BlockEntryInstr* block, intptr_t place_id) {
// First check if the same value is coming in from all predecessors.
+ static Definition* const kDifferentValuesMarker =
+ reinterpret_cast<Definition*>(-1);
Definition* incoming = NULL;
for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
BlockEntryInstr* pred = block->PredecessorAt(i);
ZoneGrowableArray<Definition*>* pred_out_values =
out_values_[pred->preorder_number()];
- if (incoming == NULL) {
- incoming = (*pred_out_values)[expr_id];
- } else if (incoming != (*pred_out_values)[expr_id]) {
- incoming = NULL;
- break;
+ if ((pred_out_values == NULL) || ((*pred_out_values)[place_id] == NULL)) {
+ return NULL;
+ } else if (incoming == NULL) {
+ incoming = (*pred_out_values)[place_id];
+ } else if (incoming != (*pred_out_values)[place_id]) {
+ incoming = kDifferentValuesMarker;
}
}
- if (incoming != NULL) {
+ if (incoming != kDifferentValuesMarker) {
+ ASSERT(incoming != NULL);
return incoming;
}
// Incoming values are different. Phi is required to merge.
PhiInstr* phi = new PhiInstr(
block->AsJoinEntry(), block->PredecessorCount());
+ phi->set_place_id(place_id);
+ FillPhiInputs(phi);
+ return phi;
+ }
+
+ void FillPhiInputs(PhiInstr* phi) {
+ BlockEntryInstr* block = phi->GetBlock();
+ const intptr_t place_id = phi->place_id();
for (intptr_t i = 0; i < block->PredecessorCount(); i++) {
BlockEntryInstr* pred = block->PredecessorAt(i);
ZoneGrowableArray<Definition*>* pred_out_values =
out_values_[pred->preorder_number()];
- ASSERT((*pred_out_values)[expr_id] != NULL);
+ ASSERT((*pred_out_values)[place_id] != NULL);
// Sets of outgoing values are not linked into use lists so
// they might contain values that were replaced and removed
// from the graph by this iteration.
// To prevent using them we additionally mark definitions themselves
// as replaced and store a pointer to the replacement.
- Definition* replacement = (*pred_out_values)[expr_id]->Replacement();
+ Definition* replacement = (*pred_out_values)[place_id]->Replacement();
Value* input = new Value(replacement);
phi->SetInputAt(i, input);
replacement->AddInputUse(input);
@@ -4479,7 +4860,12 @@ class LoadOptimizer : public ValueObject {
phi->set_ssa_temp_index(graph_->alloc_ssa_temp_index());
phis_.Add(phi); // Postpone phi insertion until after load forwarding.
- return phi;
+ if (FLAG_trace_load_optimization_verbose) {
+ OS::Print("created pending phi %s for %s at B%"Pd"\n",
+ phi->ToCString(),
+ aliased_set_->places()[place_id]->ToCString(),
+ block->block_id());
+ }
}
// Iterate over basic blocks and replace exposed loads with incoming
@@ -4498,9 +4884,10 @@ class LoadOptimizer : public ValueObject {
for (intptr_t i = 0; i < loads->length(); i++) {
Definition* load = (*loads)[i];
- if (!in->Contains(load->expr_id())) continue; // No incoming value.
+ if (!in->Contains(load->place_id())) continue; // No incoming value.
- Definition* replacement = MergeIncomingValues(block, load->expr_id());
+ Definition* replacement = MergeIncomingValues(block, load->place_id());
+ ASSERT(replacement != NULL);
// Sets of outgoing values are not linked into use lists so
// they might contain values that were replace and removed
@@ -4580,16 +4967,117 @@ class LoadOptimizer : public ValueObject {
return true;
}
+ bool AddPhiPairToWorklist(PhiInstr* a, PhiInstr* b) {
+ // Can't compare two phis from different blocks.
+ if (a->block() != b->block()) {
+ return false;
+ }
+
+ // If a is already in the worklist check if it is being compared to b.
+ // Give up if it is not.
+ if (in_worklist_->Contains(a->ssa_temp_index())) {
+ for (intptr_t i = 0; i < worklist_.length(); i += 2) {
+ if (a == worklist_[i]) {
+ return (b == worklist_[i + 1]);
+ }
+ }
+ UNREACHABLE();
+ }
+
+ worklist_.Add(a);
+ worklist_.Add(b);
+ in_worklist_->Add(a->ssa_temp_index());
+ return true;
+ }
+
+ // Replace the given phi with another if they are equal.
+ // Returns true if succeeds.
+ bool ReplacePhiWith(PhiInstr* phi, PhiInstr* replacement) {
+ ASSERT(phi->InputCount() == replacement->InputCount());
+ ASSERT(phi->block() == replacement->block());
+
+ worklist_.Clear();
+ if (in_worklist_ == NULL) {
+ in_worklist_ = new BitVector(graph_->current_ssa_temp_index());
+ } else {
+ in_worklist_->Clear();
+ }
+
+ // During the comparison worklist contains pairs of phis to be compared.
+ AddPhiPairToWorklist(phi, replacement);
+
+ // Process the worklist. It might grow during each comparison step.
+ for (intptr_t i = 0; i < worklist_.length(); i += 2) {
+ PhiInstr* a = worklist_[i];
+ PhiInstr* b = worklist_[i + 1];
+
+ // Compare phi inputs.
+ for (intptr_t j = 0; j < a->InputCount(); j++) {
+ Definition* inputA = a->InputAt(j)->definition();
+ Definition* inputB = b->InputAt(j)->definition();
+
+ if (inputA != inputB) {
+ // If inputs are unequal by they are phis then add them to
+ // the worklist for recursive comparison.
+ if (inputA->IsPhi() && inputB->IsPhi() &&
+ AddPhiPairToWorklist(inputA->AsPhi(), inputB->AsPhi())) {
+ continue;
+ }
+ return false; // Not equal.
+ }
+ }
+ }
+
+ // At this point worklist contains pairs of equal phis. Replace the first
+ // phi in the pair with the second.
+ for (intptr_t i = 0; i < worklist_.length(); i += 2) {
+ PhiInstr* a = worklist_[i];
+ PhiInstr* b = worklist_[i + 1];
+ a->ReplaceUsesWith(b);
+ if (a->is_alive()) {
+ a->mark_dead();
+ a->block()->RemovePhi(a);
+ }
+ }
+
+ return true;
+ }
+
+ // Insert the given phi into the graph. Attempt to find an equal one in the
+ // target block first.
+ // Returns true if the phi was inserted and false if it was replaced.
+ bool EmitPhi(PhiInstr* phi) {
+ for (PhiIterator it(phi->block()); !it.Done(); it.Advance()) {
+ if (ReplacePhiWith(phi, it.Current())) {
+ return false;
+ }
+ }
+
+ phi->mark_alive();
+ phi->block()->InsertPhi(phi);
+ return true;
+ }
+
// Phis have not yet been inserted into the graph but they have uses of
// their inputs. Insert the non-redundant ones and clear the input uses
// of the redundant ones.
void EmitPhis() {
+ // First eliminate all redundant phis.
for (intptr_t i = 0; i < phis_.length(); i++) {
PhiInstr* phi = phis_[i];
- if (phi->HasUses() && !EliminateRedundantPhi(phi)) {
- phi->mark_alive();
- phi->block()->InsertPhi(phi);
- } else {
+ if (!phi->HasUses() || EliminateRedundantPhi(phi)) {
+ for (intptr_t j = phi->InputCount() - 1; j >= 0; --j) {
+ phi->InputAt(j)->RemoveFromUseList();
+ }
+ phis_[i] = NULL;
+ }
+ }
+
+ // Now emit phis or replace them with equal phis already present in the
+ // graph.
+ for (intptr_t i = 0; i < phis_.length(); i++) {
+ PhiInstr* phi = phis_[i];
+ if ((phi != NULL) && (!phi->HasUses() || !EmitPhi(phi))) {
for (intptr_t j = phi->InputCount() - 1; j >= 0; --j) {
phi->InputAt(j)->RemoveFromUseList();
}
@@ -4599,15 +5087,15 @@ class LoadOptimizer : public ValueObject {
ZoneGrowableArray<Definition*>* CreateBlockOutValues() {
ZoneGrowableArray<Definition*>* out =
- new ZoneGrowableArray<Definition*>(aliased_set_->max_expr_id());
- for (intptr_t i = 0; i < aliased_set_->max_expr_id(); i++) {
+ new ZoneGrowableArray<Definition*>(aliased_set_->max_place_id());
+ for (intptr_t i = 0; i < aliased_set_->max_place_id(); i++) {
out->Add(NULL);
}
return out;
}
FlowGraph* graph_;
- DirectChainedHashMap<LoadKeyValueTrait>* map_;
+ DirectChainedHashMap<PointerKeyValueTrait<Place> >* map_;
// Mapping between field offsets in words and expression ids of loads from
// that offset.

Powered by Google App Engine
This is Rietveld 408576698