 Chromium Code Reviews
 Chromium Code Reviews Issue 395943003:
  Support allocation sinking for compound objects.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
    
  
    Issue 395943003:
  Support allocation sinking for compound objects.  (Closed) 
  Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart| Index: runtime/vm/flow_graph_optimizer.cc | 
| diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc | 
| index fa5db4ded883864c43848e90aa74406a86314450..769a5b10eb3ceddbbe893b43f2a02931016ebf27 100644 | 
| --- a/runtime/vm/flow_graph_optimizer.cc | 
| +++ b/runtime/vm/flow_graph_optimizer.cc | 
| @@ -5667,103 +5667,38 @@ void LICM::Optimize() { | 
| } | 
| -// Alias represents a family of locations. It is used to capture aliasing | 
| -// between stores and loads. Store can alias another load or store if and only | 
| -// if they have the same alias. | 
| -class Alias : public ValueObject { | 
| - public: | 
| - Alias(const Alias& other) : ValueObject(), alias_(other.alias_) { } | 
| - | 
| - // All indexed load/stores alias each other. | 
| - // TODO(vegorov): incorporate type of array into alias to disambiguate | 
| - // different typed data and normal arrays. | 
| - static Alias UnknownIndex(intptr_t id) { | 
| - return Alias(kUnknownIndexAlias, id); | 
| - } | 
| - | 
| - static Alias ConstantIndex(intptr_t id) { | 
| - ASSERT(id != 0); | 
| - return Alias(kConstantIndex, id); | 
| - } | 
| - | 
| - // Field load/stores alias each other only when they access the same field. | 
| - // AliasedSet assigns ids to a combination of instance and field during | 
| - // the optimization phase. | 
| - static Alias Field(intptr_t id) { | 
| - ASSERT(id != 0); | 
| - return Alias(kFieldAlias, id); | 
| - } | 
| - | 
| - // VMField load/stores alias each other when field offset matches. | 
| - // TODO(vegorov) storing a context variable does not alias loading array | 
| - // length. | 
| - static Alias VMField(intptr_t id) { | 
| - ASSERT(id != 0); | 
| - return Alias(kVMFieldAlias, id); | 
| - } | 
| - | 
| - // Current context load/stores alias each other. | 
| - static Alias CurrentContext() { | 
| - return Alias(kCurrentContextAlias, 0); | 
| - } | 
| - | 
| - // Operation does not alias anything. | 
| - static Alias None() { | 
| - return Alias(kNoneAlias); | 
| - } | 
| - | 
| - bool IsNone() const { | 
| - return alias_ == kNoneAlias; | 
| - } | 
| - | 
| - // Convert this alias to a positive array index. | 
| - intptr_t ToIndex() const { | 
| - ASSERT(!IsNone()); | 
| - return alias_; | 
| - } | 
| - | 
| - private: | 
| - enum { | 
| - // Number of bits required to encode Kind value. | 
| - // The payload occupies the rest of the bits, but leaves the MSB (sign bit) | 
| - // empty so that the resulting encoded value is always a positive integer. | 
| - kBitsForKind = 3, | 
| - kBitsForPayload = kWordSize * kBitsPerByte - kBitsForKind - 1, | 
| - }; | 
| - | 
| - enum Kind { | 
| - kNoneAlias = -1, | 
| - kCurrentContextAlias = 0, | 
| - kUnknownIndexAlias = 1, | 
| - kFieldAlias = 2, | 
| - kVMFieldAlias = 3, | 
| - kConstantIndex = 4, | 
| - kNumKinds = kConstantIndex + 1 | 
| - }; | 
| - COMPILE_ASSERT(kNumKinds < ((1 << kBitsForKind) - 1)); | 
| - | 
| - explicit Alias(intptr_t alias) : alias_(alias) { } | 
| - | 
| - Alias(Kind kind, uword payload) | 
| - : alias_(KindField::encode(kind) | PayloadField::encode(payload)) { } | 
| - | 
| - uword payload() const { | 
| - return PayloadField::decode(alias_); | 
| - } | 
| - | 
| - Kind kind() const { | 
| - return IsNone() ? kNoneAlias : KindField::decode(alias_); | 
| - } | 
| - | 
| - typedef BitField<Kind, 0, kBitsForKind> KindField; | 
| - typedef BitField<uword, kBitsForKind, kBitsForPayload> PayloadField; | 
| - | 
| - const intptr_t alias_; | 
| -}; | 
| - | 
| - | 
| // Place describes an abstract location (e.g. field) that IR can load | 
| // from or store to. | 
| +// | 
| +// Places are also used to describe wild-card locations also known as aliases, | 
| +// that essentially represent sets of places that alias each other. Places A | 
| +// and B are said to alias each other if store into A can affect load from B. | 
| +// | 
| +// We distinguish the following aliases: | 
| +// | 
| +// - for fields | 
| +// - *.f, *.@offs - field inside some object; | 
| +// - X.f, X.@offs - field inside an allocated object X; | 
| +// - for indexed accesses | 
| +// - *[*] - non-constant index inside some object; | 
| +// - *[C] - constant index inside some object; | 
| +// - X[*] - non-constant index inside an allocated object X; | 
| +// - X[C] - constant index inside an allocated object X. | 
| +// | 
| +// Separating allocation results from other objects is improves precision of the | 
| 
Florian Schneider
2014/07/16 15:02:03
Something is missing in this comment.
 
Vyacheslav Egorov (Google)
2014/07/17 17:03:48
Done.
 | 
| +// load forwarding pass because of the following two properties: | 
| +// | 
| +// - if X can be proven to have no aliases itself (i.e. there is no other SSA | 
| +// variable that points to X) then no place inside X can be aliased with any | 
| +// wildcard dependent place (*.f, *.@offs, *[*], *[C]); | 
| +// - given allocations X and Y no place inside X can be aliased with any place | 
| +// inside Y even if any of them or both escape. | 
| +// | 
| +// It important to realize that single place can belong to multiple aliases. | 
| +// For example place X.f with aliased allocation X belongs both to X.f and *.f | 
| +// aliases. Likewise X[C] with non-aliased allocation X belongs to X[C] and X[*] | 
| +// aliases. | 
| +// | 
| class Place : public ValueObject { | 
| public: | 
| enum Kind { | 
| @@ -5778,9 +5713,12 @@ class Place : public ValueObject { | 
| // being accessed and offset to the field. | 
| kVMField, | 
| - // Indexed location. | 
| + // Indexed location with a non-constant index. | 
| kIndexed, | 
| + // Indexed location with a constant index. | 
| + kConstantIndexed, | 
| + | 
| // Current context. | 
| kContext | 
| }; | 
| @@ -5852,21 +5790,19 @@ class Place : public ValueObject { | 
| case Instruction::kLoadIndexed: { | 
| LoadIndexedInstr* load_indexed = instr->AsLoadIndexed(); | 
| - kind_ = kIndexed; | 
| representation_ = load_indexed->representation(); | 
| instance_ = load_indexed->array()->definition()->OriginalDefinition(); | 
| - index_ = load_indexed->index()->definition(); | 
| + SetIndex(load_indexed->index()->definition()); | 
| *is_load = true; | 
| break; | 
| } | 
| case Instruction::kStoreIndexed: { | 
| StoreIndexedInstr* store_indexed = instr->AsStoreIndexed(); | 
| - kind_ = kIndexed; | 
| representation_ = store_indexed-> | 
| RequiredInputRepresentation(StoreIndexedInstr::kValuePos); | 
| instance_ = store_indexed->array()->definition()->OriginalDefinition(); | 
| - index_ = store_indexed->index()->definition(); | 
| + SetIndex(store_indexed->index()->definition()); | 
| *is_store = true; | 
| break; | 
| } | 
| @@ -5891,20 +5827,75 @@ class Place : public ValueObject { | 
| } | 
| } | 
| + // Create object representing *[*] alias. | 
| + static Place* CreateAnyInstanceAnyIndexAlias(Isolate* isolate, | 
| + intptr_t id) { | 
| + return Wrap(isolate, Place(kIndexed, NULL, 0), id); | 
| + } | 
| + | 
| + // Return least generic alias for this place. Given that aliases are | 
| + // essentially sets of places we define least generic alias as a smallest | 
| + // alias that contains this place. | 
| + // | 
| + // We obtain such alias by a simple transformation: | 
| + // | 
| + // - for places that depend on an instance X.f, X.@offs, X[i], X[C] | 
| + // we drop X if X is not an allocation because in this case X does not | 
| + // posess an identity obtaining aliases *.f, *.@offs, *[i] and *[C] | 
| + // respectively; | 
| + // - for non-constant indexed places X[i] we drop information about the | 
| + // index obtaining alias X[*]. | 
| + // | 
| + Place ToAlias() const { | 
| + return Place( | 
| + kind_, | 
| + (DependsOnInstance() && IsAllocation(instance())) ? instance() : NULL, | 
| + (kind() == kIndexed) ? 0 : raw_selector_); | 
| + } | 
| + | 
| + bool DependsOnInstance() const { | 
| + switch (kind()) { | 
| + case kField: | 
| + case kVMField: | 
| + case kIndexed: | 
| + case kConstantIndexed: | 
| + return true; | 
| + | 
| + case kContext: | 
| + case kNone: | 
| + return false; | 
| + } | 
| + | 
| + UNREACHABLE(); | 
| + return false; | 
| + } | 
| + | 
| + // Given instance dependent alias X.f, X.@offs, X[C], X[*] return | 
| + // wild-card dependent alias *.f, *.@offs, *[C] or *[*] respectively. | 
| + Place CopyWithoutInstance() const { | 
| + ASSERT(DependsOnInstance()); | 
| + return Place(kind_, NULL, raw_selector_); | 
| + } | 
| + | 
| + // Given alias X[C] or *[C] return X[*] and *[*] respectively. | 
| + Place CopyWithoutIndex() const { | 
| + ASSERT(kind_ == kConstantIndexed); | 
| + return Place(kIndexed, instance_, 0); | 
| + } | 
| + | 
| intptr_t id() const { return id_; } | 
| - void set_id(intptr_t id) { id_ = id; } | 
| Kind kind() const { return kind_; } | 
| Representation representation() const { return representation_; } | 
| Definition* instance() const { | 
| - ASSERT((kind_ == kField) || (kind_ == kVMField) || (kind_ == kIndexed)); | 
| + ASSERT(DependsOnInstance()); | 
| return instance_; | 
| } | 
| void set_instance(Definition* def) { | 
| - ASSERT((kind_ == kField) || (kind_ == kVMField) || (kind_ == kIndexed)); | 
| + ASSERT(DependsOnInstance()); | 
| instance_ = def->OriginalDefinition(); | 
| } | 
| @@ -5923,6 +5914,20 @@ class Place : public ValueObject { | 
| return index_; | 
| } | 
| + intptr_t index_constant() const { | 
| + ASSERT(kind_ == kConstantIndexed); | 
| + return index_constant_; | 
| + } | 
| + | 
| + static const char* DefinitionName(Definition* def) { | 
| + if (def == NULL) { | 
| + return "*"; | 
| + } else { | 
| + return Isolate::Current()->current_zone()->PrintToString( | 
| + "v%" Pd, def->ssa_temp_index()); | 
| + } | 
| + } | 
| + | 
| const char* ToCString() const { | 
| switch (kind_) { | 
| case kNone: | 
| @@ -5930,25 +5935,32 @@ class Place : public ValueObject { | 
| case kField: { | 
| const char* field_name = String::Handle(field().name()).ToCString(); | 
| - if (instance() == NULL) { | 
| - return field_name; | 
| + if (field().is_static()) { | 
| + return Isolate::Current()->current_zone()->PrintToString( | 
| + "<%s>", field_name); | 
| + } else { | 
| + return Isolate::Current()->current_zone()->PrintToString( | 
| + "<%s.%s>", DefinitionName(instance()), field_name); | 
| } | 
| - return Isolate::Current()->current_zone()->PrintToString( | 
| - "<v%" Pd ".%s>", instance()->ssa_temp_index(), field_name); | 
| } | 
| - case kVMField: { | 
| + case kVMField: | 
| return Isolate::Current()->current_zone()->PrintToString( | 
| - "<v%" Pd "@%" Pd ">", | 
| - instance()->ssa_temp_index(), offset_in_bytes()); | 
| - } | 
| + "<%s.@%" Pd ">", | 
| + DefinitionName(instance()), | 
| + offset_in_bytes()); | 
| - case kIndexed: { | 
| + case kIndexed: | 
| return Isolate::Current()->current_zone()->PrintToString( | 
| - "<v%" Pd "[v%" Pd "]>", | 
| - instance()->ssa_temp_index(), | 
| - index()->ssa_temp_index()); | 
| - } | 
| + "<%s[%s]>", | 
| + DefinitionName(instance()), | 
| + DefinitionName(index())); | 
| + | 
| + case kConstantIndexed: | 
| + return Isolate::Current()->current_zone()->PrintToString( | 
| + "<%s[%" Pd "]>", | 
| + DefinitionName(instance()), | 
| + index_constant()); | 
| case kContext: | 
| return "<context>"; | 
| @@ -5966,18 +5978,35 @@ class Place : public ValueObject { | 
| representation_ * 15 + FieldHashcode(); | 
| } | 
| - bool Equals(Place* other) const { | 
| + bool Equals(const Place* other) const { | 
| return (kind_ == other->kind_) && | 
| (representation_ == other->representation_) && | 
| (instance_ == other->instance_) && | 
| SameField(other); | 
| } | 
| - // Create a zone allocated copy of this place. | 
| - static Place* Wrap(Isolate* isolate, const Place& place); | 
| + // Create a zone allocated copy of this place and assign given id to it. | 
| + static Place* Wrap(Isolate* isolate, const Place& place, intptr_t id); | 
| + | 
| + static bool IsAllocation(Definition* defn) { | 
| + // TODO(vegorov): add CreateContext to this list. | 
| + return (defn != NULL) && | 
| + (defn->IsAllocateObject() || | 
| + defn->IsCreateArray() || | 
| + (defn->IsStaticCall() && | 
| + defn->AsStaticCall()->IsRecognizedFactory())); | 
| + } | 
| private: | 
| - bool SameField(Place* other) const { | 
| + Place(Kind kind, Definition* instance, intptr_t selector) | 
| + : kind_(kind), | 
| + representation_(kNoRepresentation), | 
| + instance_(instance), | 
| + raw_selector_(selector), | 
| + id_(0) { | 
| + } | 
| + | 
| + bool SameField(const Place* other) const { | 
| return (kind_ == kField) ? (field().raw() == other->field().raw()) | 
| : (offset_in_bytes_ == other->offset_in_bytes_); | 
| } | 
| @@ -5987,6 +6016,17 @@ class Place : public ValueObject { | 
| : offset_in_bytes_; | 
| } | 
| + void SetIndex(Definition* index) { | 
| + ConstantInstr* index_constant = index->AsConstant(); | 
| + if ((index_constant != NULL) && index_constant->value().IsSmi()) { | 
| + kind_ = kConstantIndexed; | 
| + index_constant_ = Smi::Cast(index_constant->value()).Value(); | 
| + } else { | 
| + kind_ = kIndexed; | 
| + index_ = index; | 
| + } | 
| + } | 
| + | 
| Kind kind_; | 
| Representation representation_; | 
| Definition* instance_; | 
| @@ -5994,6 +6034,7 @@ class Place : public ValueObject { | 
| intptr_t raw_selector_; | 
| const Field* field_; | 
| intptr_t offset_in_bytes_; | 
| + intptr_t index_constant_; | 
| Definition* index_; | 
| }; | 
| @@ -6012,8 +6053,10 @@ class ZonePlace : public ZoneAllocated { | 
| }; | 
| -Place* Place::Wrap(Isolate* isolate, const Place& place) { | 
| - return (new(isolate) ZonePlace(place))->place(); | 
| +Place* Place::Wrap(Isolate* isolate, const Place& place, intptr_t id) { | 
| + Place* wrapped = (new(isolate) ZonePlace(place))->place(); | 
| + wrapped->id_ = id; | 
| + return wrapped; | 
| } | 
| @@ -6073,141 +6116,38 @@ class AliasedSet : public ZoneAllocated { | 
| : isolate_(isolate), | 
| places_(*places), | 
| phi_moves_(phi_moves), | 
| - sets_(), | 
| - aliased_by_effects_(new(isolate) BitVector(places->length())), | 
| - max_field_id_(0), | 
| - field_ids_(), | 
| - max_index_id_(0), | 
| - index_ids_(), | 
| - max_unknown_index_id_(0), | 
| - unknown_index_ids_(), | 
| - max_vm_field_id_(0), | 
| - vm_field_ids_() { } | 
| - | 
| - Alias ComputeAlias(Place* place) { | 
| - switch (place->kind()) { | 
| - case Place::kIndexed: | 
| - if (place->index()->IsConstant()) { | 
| - const Object& index = place->index()->AsConstant()->value(); | 
| - if (index.IsSmi()) { | 
| - return Alias::ConstantIndex( | 
| - GetInstanceIndexId(place->instance(), | 
| - Smi::Cast(index).Value())); | 
| - } | 
| - } | 
| - return Alias::UnknownIndex(GetUnknownIndexId(place->instance())); | 
| - case Place::kField: | 
| - return Alias::Field( | 
| - GetInstanceFieldId(place->instance(), place->field())); | 
| - case Place::kVMField: | 
| - return Alias::VMField( | 
| - GetInstanceVMFieldId(place->instance(), place->offset_in_bytes())); | 
| - case Place::kContext: | 
| - return Alias::CurrentContext(); | 
| - case Place::kNone: | 
| - UNREACHABLE(); | 
| - } | 
| - | 
| - UNREACHABLE(); | 
| - return Alias::None(); | 
| - } | 
| - | 
| - Alias ComputeAliasForStore(Instruction* instr) { | 
| - StoreIndexedInstr* store_indexed = instr->AsStoreIndexed(); | 
| - if (store_indexed != NULL) { | 
| - Definition* instance = store_indexed->array()->definition(); | 
| - if (store_indexed->index()->definition()->IsConstant()) { | 
| - const Object& index = | 
| - store_indexed->index()->definition()->AsConstant()->value(); | 
| - if (index.IsSmi()) { | 
| - return Alias::ConstantIndex( | 
| - GetInstanceIndexId(instance, Smi::Cast(index).Value())); | 
| - } | 
| - } | 
| - return Alias::UnknownIndex(GetUnknownIndexId(instance)); | 
| + aliases_(5), | 
| + aliases_map_(), | 
| + representatives_(), | 
| + killed_(), | 
| + aliased_by_effects_(new(isolate) BitVector(places->length())) { | 
| + InsertAlias(Place::CreateAnyInstanceAnyIndexAlias(isolate_, | 
| + kAnyInstanceAnyIndexAlias)); | 
| + for (intptr_t i = 0; i < places_.length(); i++) { | 
| + AddRepresentative(places_[i]); | 
| } | 
| - | 
| - StoreInstanceFieldInstr* store_instance_field = | 
| - instr->AsStoreInstanceField(); | 
| - if (store_instance_field != NULL) { | 
| - Definition* instance = store_instance_field->instance()->definition(); | 
| - if (!store_instance_field->field().IsNull()) { | 
| - return Alias::Field( | 
| - GetInstanceFieldId(instance, store_instance_field->field())); | 
| - } | 
| - return Alias::VMField( | 
| - GetInstanceVMFieldId(instance, | 
| - store_instance_field->offset_in_bytes())); | 
| - } | 
| - | 
| - if (instr->IsStoreContext()) { | 
| - return Alias::CurrentContext(); | 
| - } | 
| - | 
| - StoreStaticFieldInstr* store_static_field = instr->AsStoreStaticField(); | 
| - if (store_static_field != NULL) { | 
| - return Alias::Field(GetStaticFieldId(store_static_field->field())); | 
| - } | 
| - | 
| - return Alias::None(); | 
| + ComputeKillSets(); | 
| } | 
| - BitVector* Get(const Alias alias) { | 
| - const intptr_t idx = alias.ToIndex(); | 
| - BitVector* ret = (idx < sets_.length()) ? sets_[idx] : NULL; | 
| - return ret; | 
| + intptr_t LookupAliasId(const Place& alias) { | 
| + const Place* result = aliases_map_.Lookup(&alias); | 
| + return (result != NULL) ? result->id() : kNoAlias; | 
| } | 
| - void AddRepresentative(Place* place) { | 
| - if (!place->IsFinalField()) { | 
| - AddIdForAlias(ComputeAlias(place), place->id()); | 
| - if (!IsIndependentFromEffects(place)) { | 
| - aliased_by_effects_->Add(place->id()); | 
| + bool IsStore(Instruction* instr, BitVector** killed) { | 
| + bool is_load = false, is_store = false; | 
| + Place place(instr, &is_load, &is_store); | 
| + if (is_store && (place.kind() != Place::kNone)) { | 
| + const intptr_t alias_id = LookupAliasId(place.ToAlias()); | 
| + if (alias_id != kNoAlias) { | 
| + *killed = GetKilledSet(alias_id); | 
| } | 
| } | 
| + return is_store; | 
| } | 
| - void EnsureAliasingForUnknownIndices() { | 
| - // Ids start at 1 because the hash-map uses 0 for element not found. | 
| - for (intptr_t unknown_index_id = 1; | 
| - unknown_index_id <= max_unknown_index_id_; | 
| - unknown_index_id++) { | 
| - BitVector* unknown_index = Get(Alias::UnknownIndex(unknown_index_id)); | 
| - if (unknown_index == NULL) { | 
| - return; | 
| - } | 
| - | 
| - // Constant indexes alias all non-constant indexes. | 
| - // Non-constant indexes alias all constant indexes. | 
| - // First update alias set for const-indices, then | 
| - // update set for all indices. Ids start at 1. | 
| - for (intptr_t id = 1; id <= max_index_id_; id++) { | 
| - BitVector* const_indexes = Get(Alias::ConstantIndex(id)); | 
| - if (const_indexes != NULL) { | 
| - const_indexes->AddAll(unknown_index); | 
| - } | 
| - } | 
| - | 
| - for (intptr_t id = 1; id <= max_index_id_; id++) { | 
| - BitVector* const_indexes = Get(Alias::ConstantIndex(id)); | 
| - if (const_indexes != NULL) { | 
| - unknown_index->AddAll(const_indexes); | 
| - } | 
| - } | 
| - } | 
| - } | 
| - | 
| - void AddIdForAlias(const Alias alias, intptr_t place_id) { | 
| - const intptr_t idx = alias.ToIndex(); | 
| - while (sets_.length() <= idx) { | 
| - sets_.Add(NULL); | 
| - } | 
| - | 
| - if (sets_[idx] == NULL) { | 
| - sets_[idx] = new(isolate_) BitVector(max_place_id()); | 
| - } | 
| - | 
| - sets_[idx]->Add(place_id); | 
| + BitVector* GetKilledSet(intptr_t alias) { | 
| + return (alias < killed_.length()) ? killed_[alias] : NULL; | 
| } | 
| intptr_t max_place_id() const { return places().length(); } | 
| @@ -6234,158 +6174,209 @@ class AliasedSet : public ZoneAllocated { | 
| const PhiPlaceMoves* phi_moves() const { return phi_moves_; } | 
| - // Returns true if the result of an allocation instruction can be aliased by | 
| - // some other SSA variable and false otherwise. Currently simply checks if | 
| - // this value is stored in a field, escapes to another function or | 
| - // participates in a phi. | 
| - static bool CanBeAliased(Definition* alloc) { | 
| - ASSERT(alloc->IsAllocateObject() || | 
| - alloc->IsCreateArray() || | 
| - (alloc->IsStaticCall() && | 
| - alloc->AsStaticCall()->IsRecognizedFactory())); | 
| - if (alloc->Identity() == kIdentityUnknown) { | 
| - bool escapes = false; | 
| - for (Value* use = alloc->input_use_list(); | 
| - use != NULL; | 
| - use = use->next_use()) { | 
| - Instruction* instr = use->instruction(); | 
| - if (instr->IsPushArgument() || | 
| - (instr->IsStoreInstanceField() | 
| - && (use->use_index() != StoreInstanceFieldInstr::kInstancePos)) || | 
| - (instr->IsStoreIndexed() | 
| - && (use->use_index() == StoreIndexedInstr::kValuePos)) || | 
| - instr->IsStoreStaticField() || | 
| - instr->IsPhi() || | 
| - instr->IsAssertAssignable() || | 
| - instr->IsRedefinition()) { | 
| - escapes = true; | 
| - break; | 
| - } | 
| - } | 
| - | 
| - alloc->SetIdentity(escapes ? kIdentityAliased : kIdentityNotAliased); | 
| + void RollbackAliasedIdentites() { | 
| + for (intptr_t i = 0; i < identity_rollback_.length(); ++i) { | 
| + identity_rollback_[i]->SetIdentity(AliasIdentity::Unknown()); | 
| } | 
| - | 
| - return alloc->Identity() != kIdentityNotAliased; | 
| } | 
| - private: | 
| - // Get id assigned to the given field. Assign a new id if the field is seen | 
| - // for the first time. | 
| - intptr_t GetFieldId(intptr_t instance_id, const Field& field) { | 
| - intptr_t id = field_ids_.Lookup(FieldIdPair::Key(instance_id, &field)); | 
| - if (id == 0) { | 
| - id = ++max_field_id_; | 
| - field_ids_.Insert(FieldIdPair(FieldIdPair::Key(instance_id, &field), id)); | 
| + // Returns false if the result of an allocation instruction can't be aliased | 
| + // by another SSA variable and true otherwise. | 
| + bool CanBeAliased(Definition* alloc) { | 
| + if (!Place::IsAllocation(alloc)) { | 
| + return true; | 
| } | 
| - return id; | 
| - } | 
| - intptr_t GetIndexId(intptr_t instance_id, intptr_t index) { | 
| - intptr_t id = index_ids_.Lookup( | 
| - ConstantIndexIdPair::Key(instance_id, index)); | 
| - if (id == 0) { | 
| - // Zero is used to indicate element not found. The first id is one. | 
| - id = ++max_index_id_; | 
| - index_ids_.Insert(ConstantIndexIdPair( | 
| - ConstantIndexIdPair::Key(instance_id, index), id)); | 
| + if (alloc->Identity().IsUnknown()) { | 
| + ComputeAliasing(alloc); | 
| } | 
| - return id; | 
| + | 
| + return !alloc->Identity().IsNotAliased(); | 
| } | 
| + private: | 
| enum { | 
| - kAnyInstance = -1 | 
| + kNoAlias = 0, | 
| + | 
| + // Artificial alias that is used to collect all representatives of the | 
| + // *[C], X[C] aliases for arbitrary C. | 
| + kAnyConstantIndexedAlias = 1, | 
| + | 
| + // Artificial alias that is used to collect all representatives of | 
| + // *[C] alias for arbitrary C. | 
| + kUnknownInstanceConstantIndexedAlias = 2, | 
| + | 
| + // Artificial alias that is used to collect all representatives of | 
| + // X[*] alias for all X. | 
| + kAnyAllocationIndexedAlias = 3, | 
| + | 
| + // *[*] alias. | 
| + kAnyInstanceAnyIndexAlias = 4 | 
| }; | 
| - // Get or create an identifier for an instance field belonging to the | 
| - // given instance. | 
| - // The space of identifiers assigned to instance fields is split into | 
| - // parts based on the instance that contains the field. | 
| - // If compiler can prove that instance has a single SSA name in the compiled | 
| - // function then we use that SSA name to distinguish fields of this object | 
| - // from the same fields in other objects. | 
| - // 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() == (defn == NULL)); | 
| + // Compute least generic alias for the place and assign alias id to it. | 
| + void AddRepresentative(Place* place) { | 
| + if (!place->IsFinalField()) { | 
| + const Place* alias = CanonicalizeAlias(place->ToAlias()); | 
| + EnsureSet(&representatives_, alias->id())->Add(place->id()); | 
| + | 
| + // Update cumulative representative sets that are used during | 
| + // killed sets computation. | 
| + if (alias->kind() == Place::kConstantIndexed) { | 
| + if (CanBeAliased(alias->instance())) { | 
| + EnsureSet(&representatives_, kAnyConstantIndexedAlias)-> | 
| + Add(place->id()); | 
| + } | 
| - intptr_t instance_id = kAnyInstance; | 
| + if (alias->instance() == NULL) { | 
| + EnsureSet(&representatives_, kUnknownInstanceConstantIndexedAlias)-> | 
| + Add(place->id()); | 
| + } | 
| + } else if ((alias->kind() == Place::kIndexed) && | 
| + CanBeAliased(place->instance())) { | 
| + EnsureSet(&representatives_, kAnyAllocationIndexedAlias)-> | 
| + Add(place->id()); | 
| + } | 
| - if (defn != NULL) { | 
| - AllocateObjectInstr* alloc = defn->AsAllocateObject(); | 
| - if ((alloc != NULL) && !CanBeAliased(alloc)) { | 
| - instance_id = alloc->ssa_temp_index(); | 
| - ASSERT(instance_id != kAnyInstance); | 
| + if (!IsIndependentFromEffects(place)) { | 
| + aliased_by_effects_->Add(place->id()); | 
| } | 
| } | 
| - | 
| - return GetFieldId(instance_id, field); | 
| } | 
| - intptr_t GetInstanceVMFieldId(Definition* defn, intptr_t offset) { | 
| - intptr_t instance_id = kAnyInstance; | 
| - | 
| - ASSERT(defn != NULL); | 
| - if ((defn->IsAllocateObject() || | 
| - defn->IsCreateArray() || | 
| - (defn->IsStaticCall() && | 
| - defn->AsStaticCall()->IsRecognizedFactory())) && | 
| - !CanBeAliased(defn)) { | 
| - instance_id = defn->ssa_temp_index(); | 
| - ASSERT(instance_id != kAnyInstance); | 
| + void ComputeKillSets() { | 
| + for (intptr_t i = 0; i < aliases_.length(); ++i) { | 
| + const Place* alias = aliases_[i]; | 
| + // Add all representatives to the kill set. | 
| + AddAllRepresentatives(alias->id(), alias->id()); | 
| + ComputeKillSet(alias); | 
| } | 
| - intptr_t id = vm_field_ids_.Lookup(VMFieldIdPair::Key(instance_id, offset)); | 
| - if (id == 0) { | 
| - id = ++max_vm_field_id_; | 
| - vm_field_ids_.Insert( | 
| - VMFieldIdPair(VMFieldIdPair::Key(instance_id, offset), id)); | 
| + if (FLAG_trace_load_optimization) { | 
| + OS::Print("Aliases KILL sets:\n"); | 
| + for (intptr_t i = 0; i < aliases_.length(); ++i) { | 
| + const Place* alias = aliases_[i]; | 
| + BitVector* kill = GetKilledSet(alias->id()); | 
| + | 
| + OS::Print("%s: ", alias->ToCString()); | 
| + if (kill != NULL) { | 
| + PrintSet(kill); | 
| + } | 
| + OS::Print("\n"); | 
| + } | 
| } | 
| - return id; | 
| } | 
| - intptr_t GetInstanceIndexId(Definition* defn, intptr_t index) { | 
| - intptr_t instance_id = kAnyInstance; | 
| + void InsertAlias(const Place* alias) { | 
| + aliases_map_.Insert(alias); | 
| + aliases_.Add(alias); | 
| + } | 
| - ASSERT(defn != NULL); | 
| - if ((defn->IsCreateArray() || | 
| - (defn->IsStaticCall() && | 
| - defn->AsStaticCall()->IsRecognizedFactory())) && | 
| - !CanBeAliased(defn)) { | 
| - instance_id = defn->ssa_temp_index(); | 
| - ASSERT(instance_id != kAnyInstance); | 
| + const Place* CanonicalizeAlias(const Place& alias) { | 
| + const Place* canonical = aliases_map_.Lookup(&alias); | 
| + if (canonical == NULL) { | 
| + canonical = Place::Wrap(isolate_, | 
| + alias, | 
| + kAnyInstanceAnyIndexAlias + aliases_.length()); | 
| + InsertAlias(canonical); | 
| } | 
| + return canonical; | 
| + } | 
| - return GetIndexId(instance_id, index); | 
| + BitVector* GetRepresentativesSet(intptr_t alias) { | 
| + return (alias < representatives_.length()) ? representatives_[alias] : NULL; | 
| } | 
| - intptr_t GetUnknownIndexId(Definition* defn) { | 
| - intptr_t instance_id = kAnyInstance; | 
| + BitVector* EnsureSet(GrowableArray<BitVector*>* sets, | 
| + intptr_t alias) { | 
| + while (sets->length() <= alias) { | 
| + sets->Add(NULL); | 
| + } | 
| - ASSERT(defn != NULL); | 
| - if ((defn->IsCreateArray() || | 
| - (defn->IsStaticCall() && | 
| - defn->AsStaticCall()->IsRecognizedFactory())) && | 
| - !CanBeAliased(defn)) { | 
| - instance_id = defn->ssa_temp_index(); | 
| - ASSERT(instance_id != kAnyInstance); | 
| + BitVector* set = (*sets)[alias]; | 
| + if (set == NULL) { | 
| + (*sets)[alias] = set = new(isolate_) BitVector(max_place_id()); | 
| } | 
| + return set; | 
| + } | 
| - intptr_t id = unknown_index_ids_.Lookup( | 
| - UnknownIndexIdPair::Key(instance_id)); | 
| - if (id == 0) { | 
| - // Zero is used to indicate element not found. The first id is one. | 
| - id = ++max_unknown_index_id_; | 
| - unknown_index_ids_.Insert( | 
| - UnknownIndexIdPair(UnknownIndexIdPair::Key(instance_id), id)); | 
| + void AddAllRepresentatives(const Place* to, intptr_t from) { | 
| + AddAllRepresentatives(to->id(), from); | 
| + } | 
| + | 
| + void AddAllRepresentatives(intptr_t to, intptr_t from) { | 
| + BitVector* from_set = GetRepresentativesSet(from); | 
| + if (from_set != NULL) { | 
| + EnsureSet(&killed_, to)->AddAll(from_set); | 
| } | 
| - return id; | 
| } | 
| - // Get or create an identifier for a static field. | 
| - intptr_t GetStaticFieldId(const Field& field) { | 
| - ASSERT(field.is_static()); | 
| - return GetFieldId(kAnyInstance, field); | 
| + void CrossAlias(const Place* to, const Place& from) { | 
| + const intptr_t from_id = LookupAliasId(from); | 
| + if (from_id == kNoAlias) { | 
| + return; | 
| + } | 
| + CrossAlias(to, from_id); | 
| + } | 
| + | 
| + void CrossAlias(const Place* to, intptr_t from) { | 
| + AddAllRepresentatives(to->id(), from); | 
| + AddAllRepresentatives(from, to->id()); | 
| + } | 
| + | 
| + // When computing kill sets we let less generic alias insert its | 
| + // representatives into more generic alias'es kill set. For example | 
| + // when visiting alias X[*] instead of searching for all aliases X[C] | 
| + // and inserting their representatives into kill set for X[*] we update | 
| + // kill set for X[*] each time we visit new X[C] for some C. | 
| + // There is an exception however: if both aliases are parametric like *[C] | 
| + // and X[*] which cross alias when X is an aliased allocation then we use | 
| + // artificial aliases that contain all possible representatives for the given | 
| + // alias for any value of the parameter to compute resulting kill set. | 
| + void ComputeKillSet(const Place* alias) { | 
| + switch (alias->kind()) { | 
| + case Place::kIndexed: // Either *[*] or X[*] alias. | 
| + if (alias->instance() == NULL) { | 
| + // *[*] aliases with X[*], X[C], *[C]. | 
| + AddAllRepresentatives(alias, kAnyConstantIndexedAlias); | 
| + AddAllRepresentatives(alias, kAnyAllocationIndexedAlias); | 
| + } else if (CanBeAliased(alias->instance())) { | 
| + // X[*] aliases with X[C]. | 
| + // If X can be aliased then X[*] also aliases with *[C], *[*]. | 
| + CrossAlias(alias, kAnyInstanceAnyIndexAlias); | 
| + AddAllRepresentatives(alias, kUnknownInstanceConstantIndexedAlias); | 
| + } | 
| + break; | 
| + | 
| + case Place::kConstantIndexed: // Either X[C] or *[C] alias. | 
| + if (alias->instance() == NULL) { | 
| + // *[C] aliases with X[C], X[*], *[*]. | 
| + AddAllRepresentatives(alias, kAnyAllocationIndexedAlias); | 
| + CrossAlias(alias, kAnyInstanceAnyIndexAlias); | 
| + } else { | 
| + // X[C] aliases with X[*]. | 
| + // If X can be aliased then X[C] also aliases with *[C], *[*]. | 
| + CrossAlias(alias, alias->CopyWithoutIndex()); | 
| + if (CanBeAliased(alias->instance())) { | 
| + CrossAlias(alias, alias->CopyWithoutInstance()); | 
| + CrossAlias(alias, kAnyInstanceAnyIndexAlias); | 
| + } | 
| + } | 
| + break; | 
| + | 
| + case Place::kField: | 
| + case Place::kVMField: | 
| + if (CanBeAliased(alias->instance())) { | 
| + // X.f or X.@offs alias with *.f and *.@offs respectively. | 
| + CrossAlias(alias, alias->CopyWithoutInstance()); | 
| + } | 
| + | 
| + case Place::kContext: | 
| + return; | 
| + | 
| + case Place::kNone: | 
| + UNREACHABLE(); | 
| + } | 
| } | 
| // Returns true if the given load is unaffected by external side-effects. | 
| @@ -6408,154 +6399,127 @@ class AliasedSet : public ZoneAllocated { | 
| return true; | 
| } | 
| - if (((place->kind() == Place::kField) || | 
| + return ((place->kind() == Place::kField) || | 
| (place->kind() == Place::kVMField)) && | 
| - (place->instance() != NULL)) { | 
| - AllocateObjectInstr* alloc = place->instance()->AsAllocateObject(); | 
| - return (alloc != NULL) && !CanBeAliased(alloc); | 
| - } | 
| - | 
| - return false; | 
| + !CanBeAliased(place->instance()); | 
| } | 
| - class FieldIdPair { | 
| - public: | 
| - struct Key { | 
| - Key(intptr_t instance_id, const Field* field) | 
| - : instance_id_(instance_id), field_(field) { } | 
| - | 
| - intptr_t instance_id_; | 
| - const Field* field_; | 
| - }; | 
| - | 
| - typedef intptr_t Value; | 
| - typedef FieldIdPair Pair; | 
| - | 
| - FieldIdPair(Key key, Value value) : key_(key), value_(value) { } | 
| - | 
| - static Key KeyOf(Pair kv) { | 
| - return kv.key_; | 
| - } | 
| - | 
| - static Value ValueOf(Pair kv) { | 
| - return kv.value_; | 
| - } | 
| - | 
| - static intptr_t Hashcode(Key key) { | 
| - return String::Handle(key.field_->name()).Hash(); | 
| - } | 
| + // Returns true if there are direct loads from the given place. | 
| + bool HasLoadsFromPlace(Definition* defn, const Place* place) { | 
| + ASSERT((place->kind() == Place::kField) || | 
| + (place->kind() == Place::kVMField)); | 
| + ASSERT(place->instance() == defn); | 
| - static inline bool IsKeyEqual(Pair kv, Key key) { | 
| - return (KeyOf(kv).field_->raw() == key.field_->raw()) && | 
| - (KeyOf(kv).instance_id_ == key.instance_id_); | 
| - } | 
| + for (Value* use = defn->input_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + bool is_load = false, is_store; | 
| + Place load_place(use->instruction(), &is_load, &is_store); | 
| - private: | 
| - Key key_; | 
| - Value value_; | 
| - }; | 
| - | 
| - class ConstantIndexIdPair { | 
| - public: | 
| - struct Key { | 
| - Key(intptr_t instance_id, intptr_t index) | 
| - : instance_id_(instance_id), index_(index) { } | 
| - | 
| - intptr_t instance_id_; | 
| - intptr_t index_; | 
| - }; | 
| - typedef intptr_t Value; | 
| - typedef ConstantIndexIdPair Pair; | 
| - | 
| - ConstantIndexIdPair(Key key, Value value) : key_(key), value_(value) { } | 
| - | 
| - static Key KeyOf(ConstantIndexIdPair kv) { | 
| - return kv.key_; | 
| - } | 
| - | 
| - static Value ValueOf(ConstantIndexIdPair kv) { | 
| - return kv.value_; | 
| - } | 
| - | 
| - static intptr_t Hashcode(Key key) { | 
| - return (key.instance_id_ + 1) * 1024 + key.index_; | 
| - } | 
| - | 
| - static inline bool IsKeyEqual(ConstantIndexIdPair kv, Key key) { | 
| - return (KeyOf(kv).index_ == key.index_) | 
| - && (KeyOf(kv).instance_id_ == key.instance_id_); | 
| + if (is_load && load_place.Equals(place)) { | 
| + return true; | 
| + } | 
| } | 
| - private: | 
| - Key key_; | 
| - Value value_; | 
| - }; | 
| - | 
| - class UnknownIndexIdPair { | 
| - public: | 
| - typedef intptr_t Key; | 
| - typedef intptr_t Value; | 
| - typedef UnknownIndexIdPair Pair; | 
| - | 
| - UnknownIndexIdPair(Key key, Value value) : key_(key), value_(value) { } | 
| + return false; | 
| + } | 
| - static Key KeyOf(UnknownIndexIdPair kv) { | 
| - return kv.key_; | 
| - } | 
| + // Check if any use of the definition can create an alias. | 
| + // Can add more objects into aliasing_worklist_. | 
| + bool AnyUseCreatesAlias(Definition* defn) { | 
| + for (Value* use = defn->input_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + Instruction* instr = use->instruction(); | 
| + if (instr->IsPushArgument() || | 
| + (instr->IsStoreIndexed() | 
| + && (use->use_index() == StoreIndexedInstr::kValuePos)) || | 
| + instr->IsStoreStaticField() || | 
| + instr->IsPhi() || | 
| + instr->IsAssertAssignable() || | 
| + instr->IsRedefinition()) { | 
| + return true; | 
| + } else if ((instr->IsStoreInstanceField() | 
| + && (use->use_index() != StoreInstanceFieldInstr::kInstancePos))) { | 
| + ASSERT(use->use_index() == StoreInstanceFieldInstr::kValuePos); | 
| + // If we store this value into an object that is not aliased itself | 
| + // and we never load again then the store does not create an alias. | 
| + StoreInstanceFieldInstr* store = instr->AsStoreInstanceField(); | 
| + Definition* instance = store->instance()->definition(); | 
| + if (instance->IsAllocateObject() && !instance->Identity().IsAliased()) { | 
| + bool is_load, is_store; | 
| + Place store_place(instr, &is_load, &is_store); | 
| + | 
| + if (!HasLoadsFromPlace(instance, &store_place)) { | 
| + // No loads found that match this store. If it is yet unknown if | 
| + // the object is not aliased then optimistically assume this but | 
| + // add it to the worklist to check its uses transitively. | 
| + if (instance->Identity().IsUnknown()) { | 
| + instance->SetIdentity(AliasIdentity::NotAliased()); | 
| + aliasing_worklist_.Add(instance); | 
| + } | 
| + continue; | 
| + } | 
| + } | 
| - static Value ValueOf(UnknownIndexIdPair kv) { | 
| - return kv.value_; | 
| + return true; | 
| + } | 
| } | 
| + return false; | 
| + } | 
| - static intptr_t Hashcode(Key key) { | 
| - return key + 1; | 
| + // Mark any value stored into the given object as potentially aliased. | 
| + void MarkStoredValuesEscaping(Definition* defn) { | 
| + if (!defn->IsAllocateObject()) { | 
| + return; | 
| } | 
| - static inline bool IsKeyEqual(UnknownIndexIdPair kv, Key key) { | 
| - return KeyOf(kv) == key; | 
| + // Find all stores into this object. | 
| + for (Value* use = defn->input_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + if ((use->use_index() == StoreInstanceFieldInstr::kInstancePos) && | 
| + use->instruction()->IsStoreInstanceField()) { | 
| + StoreInstanceFieldInstr* store = | 
| + use->instruction()->AsStoreInstanceField(); | 
| + Definition* value = store->value()->definition(); | 
| + if (value->Identity().IsNotAliased()) { | 
| + value->SetIdentity(AliasIdentity::Aliased()); | 
| + identity_rollback_.Add(value); | 
| + | 
| + // Add to worklist to propagate the mark transitively. | 
| + aliasing_worklist_.Add(value); | 
| + } | 
| + } | 
| } | 
| + } | 
| - private: | 
| - Key key_; | 
| - Value value_; | 
| - }; | 
| - | 
| - class VMFieldIdPair { | 
| - public: | 
| - struct Key { | 
| - Key(intptr_t instance_id, intptr_t offset) | 
| - : instance_id_(instance_id), offset_(offset) { } | 
| - | 
| - intptr_t instance_id_; | 
| - intptr_t offset_; | 
| - }; | 
| - | 
| - typedef intptr_t Value; | 
| - typedef VMFieldIdPair Pair; | 
| - | 
| - VMFieldIdPair(Key key, Value value) : key_(key), value_(value) { } | 
| + // Determine if the given definition can't be aliased. | 
| + void ComputeAliasing(Definition* alloc) { | 
| + ASSERT(alloc->Identity().IsUnknown()); | 
| + ASSERT(aliasing_worklist_.is_empty()); | 
| - static Key KeyOf(Pair kv) { | 
| - return kv.key_; | 
| - } | 
| + alloc->SetIdentity(AliasIdentity::NotAliased()); | 
| + aliasing_worklist_.Add(alloc); | 
| - static Value ValueOf(Pair kv) { | 
| - return kv.value_; | 
| - } | 
| + while (!aliasing_worklist_.is_empty()) { | 
| + Definition* defn = aliasing_worklist_.RemoveLast(); | 
| - static intptr_t Hashcode(Key key) { | 
| - return (key.instance_id_ + 1) * 1024 + key.offset_; | 
| - } | 
| + // If the definition in the worklist was optimistically marked as | 
| + // not-aliased check that optimistic assumption still holds: check if | 
| + // any of its uses can create an alias. | 
| + if (!defn->Identity().IsAliased() && AnyUseCreatesAlias(defn)) { | 
| + defn->SetIdentity(AliasIdentity::Aliased()); | 
| + identity_rollback_.Add(defn); | 
| + } | 
| - static inline bool IsKeyEqual(Pair kv, Key key) { | 
| - return (KeyOf(kv).offset_ == key.offset_) && | 
| - (KeyOf(kv).instance_id_ == key.instance_id_); | 
| + // If the allocation site is marked as aliased conservatively mark | 
| + // any values stored into the object aliased too. | 
| + if (defn->Identity().IsAliased()) { | 
| + MarkStoredValuesEscaping(defn); | 
| + } | 
| } | 
| - | 
| - private: | 
| - Key key_; | 
| - Value value_; | 
| - }; | 
| + } | 
| Isolate* isolate_; | 
| @@ -6563,24 +6527,33 @@ class AliasedSet : public ZoneAllocated { | 
| const PhiPlaceMoves* phi_moves_; | 
| - // Maps alias index to a set of ssa indexes corresponding to loads with the | 
| - // given alias. | 
| - GrowableArray<BitVector*> sets_; | 
| + // A list of all seen aliases and a map that allows looking up canonical | 
| + // alias object. | 
| + GrowableArray<const Place*> aliases_; | 
| + DirectChainedHashMap<PointerKeyValueTrait<const Place> > aliases_map_; | 
| - BitVector* aliased_by_effects_; | 
| + // Maps alias id to set of ids of places representing the alias. | 
| + // Place represents an alias if this alias is least generic alias for | 
| + // the place. | 
| + // (see ToAlias for the definition of least generic alias). | 
| + GrowableArray<BitVector*> representatives_; | 
| - // Table mapping static field to their id used during optimization pass. | 
| - intptr_t max_field_id_; | 
| - DirectChainedHashMap<FieldIdPair> field_ids_; | 
| + // Maps alias id to set of ids of places aliased. | 
| + GrowableArray<BitVector*> killed_; | 
| - intptr_t max_index_id_; | 
| - DirectChainedHashMap<ConstantIndexIdPair> index_ids_; | 
| + // Set of ids of places that can be affected by side-effects other then | 
| 
Florian Schneider
2014/07/16 15:02:03
s/then/than/
 
Vyacheslav Egorov (Google)
2014/07/18 11:25:08
Done.
 | 
| + // explicit stores (i.e. through calls). | 
| + BitVector* aliased_by_effects_; | 
| - intptr_t max_unknown_index_id_; | 
| - DirectChainedHashMap<UnknownIndexIdPair> unknown_index_ids_; | 
| + // Worklist used during alias analysis. | 
| + GrowableArray<Definition*> aliasing_worklist_; | 
| - intptr_t max_vm_field_id_; | 
| - DirectChainedHashMap<VMFieldIdPair> vm_field_ids_; | 
| + // List of definitions that had their identity set to Aliased. At the end | 
| + // of load optimization their identity will be rolled back to Unknown to | 
| + // avoid treating them as Aliased at later stages without checking first | 
| + // as optimizations can potentially eliminate instructions leading to | 
| + // aliasing. | 
| + GrowableArray<Definition*> identity_rollback_; | 
| }; | 
| @@ -6643,8 +6616,7 @@ static PhiPlaceMoves* ComputePhiMoves( | 
| Place* result = map->Lookup(&input_place); | 
| if (result == NULL) { | 
| - input_place.set_id(places->length()); | 
| - result = Place::Wrap(isolate, input_place); | 
| + result = Place::Wrap(isolate, input_place, places->length()); | 
| map->Insert(result); | 
| places->Add(result); | 
| if (FLAG_trace_optimization) { | 
| @@ -6698,8 +6670,7 @@ static AliasedSet* NumberPlaces( | 
| Place* result = map->Lookup(&place); | 
| if (result == NULL) { | 
| - place.set_id(places->length()); | 
| - result = Place::Wrap(isolate, place); | 
| + result = Place::Wrap(isolate, place, places->length()); | 
| map->Insert(result); | 
| places->Add(result); | 
| @@ -6724,15 +6695,7 @@ static AliasedSet* NumberPlaces( | 
| PhiPlaceMoves* phi_moves = ComputePhiMoves(map, places); | 
| // Build aliasing sets mapping aliases to loads. | 
| - AliasedSet* aliased_set = new(isolate) AliasedSet(isolate, places, phi_moves); | 
| - for (intptr_t i = 0; i < places->length(); i++) { | 
| - Place* place = (*places)[i]; | 
| - aliased_set->AddRepresentative(place); | 
| - } | 
| - | 
| - aliased_set->EnsureAliasingForUnknownIndices(); | 
| - | 
| - return aliased_set; | 
| + return new(isolate) AliasedSet(isolate, places, phi_moves); | 
| } | 
| @@ -6766,6 +6729,10 @@ class LoadOptimizer : public ValueObject { | 
| } | 
| } | 
| + ~LoadOptimizer() { | 
| + aliased_set_->RollbackAliasedIdentites(); | 
| + } | 
| + | 
| Isolate* isolate() const { return graph_->isolate(); } | 
| static bool OptimizeGraph(FlowGraph* graph) { | 
| @@ -6831,11 +6798,8 @@ class LoadOptimizer : public ValueObject { | 
| instr_it.Advance()) { | 
| Instruction* instr = instr_it.Current(); | 
| - const Alias alias = aliased_set_->ComputeAliasForStore(instr); | 
| - if (!alias.IsNone()) { | 
| - // Interfering stores kill only loads from the same offset. | 
| - BitVector* killed = aliased_set_->Get(alias); | 
| - | 
| + BitVector* killed = NULL; | 
| + if (aliased_set_->IsStore(instr, &killed)) { | 
| if (killed != NULL) { | 
| kill->AddAll(killed); | 
| // There is no need to clear out_values when clearing GEN set | 
| @@ -6899,7 +6863,7 @@ class LoadOptimizer : public ValueObject { | 
| // TODO(vegorov): record null-values at least for not final fields of | 
| // escaping object. | 
| AllocateObjectInstr* alloc = instr->AsAllocateObject(); | 
| - if ((alloc != NULL) && !AliasedSet::CanBeAliased(alloc)) { | 
| + if ((alloc != NULL) && !aliased_set_->CanBeAliased(alloc)) { | 
| for (Value* use = alloc->input_use_list(); | 
| use != NULL; | 
| use = use->next_use()) { | 
| @@ -7691,8 +7655,8 @@ class StoreOptimizer : public LivenessAnalysis { | 
| // Handle loads. | 
| Definition* defn = instr->AsDefinition(); | 
| if ((defn != NULL) && IsLoadEliminationCandidate(defn)) { | 
| - const Alias alias = aliased_set_->ComputeAlias(&place); | 
| - live_in->AddAll(aliased_set_->Get(alias)); | 
| + const intptr_t alias = aliased_set_->LookupAliasId(place.ToAlias()); | 
| + live_in->AddAll(aliased_set_->GetKilledSet(alias)); | 
| continue; | 
| } | 
| } | 
| @@ -9883,16 +9847,63 @@ void FlowGraphOptimizer::EliminateEnvironments() { | 
| } | 
| +enum SafeUseCheck { kOptimisticCheck, kStrictCheck }; | 
| + | 
| +// Check if the use is safe for allocation sinking. Allocation sinking | 
| +// candidates can only be used at store instructions: | 
| +// | 
| +// - any store into the allocation candidate itself is unconditionally safe | 
| +// as it just changes the rematerialization state of this candidate; | 
| +// - store into another object is only safe if another object is allocation | 
| +// candidate. | 
| +// | 
| +// We use a simple fix-point algorithm to discover the set of valid candidates | 
| +// (see CollectCandidates method), that's why this IsSafeUse can operate in two | 
| +// modes: | 
| +// | 
| +// - optimistic, when every allocation is assumed to be an allocation | 
| +// sinking candidate; | 
| +// - strict, when only marked allocations are assumed to be allocation | 
| +// sinking candidates. | 
| +// | 
| +// Fix-point algorithm in CollectCandiates first collects a set of allocations | 
| +// optimistically and then checks each collected candidate strictly and unmarks | 
| +// invalid candidates transitively until only strictly valid ones remain. | 
| +static bool IsSafeUse(Value* use, SafeUseCheck check_type) { | 
| + if (use->instruction()->IsMaterializeObject()) { | 
| + return true; | 
| + } | 
| + | 
| + StoreInstanceFieldInstr* store = use->instruction()->AsStoreInstanceField(); | 
| + if (store != NULL) { | 
| + if (use == store->value()) { | 
| + Definition* instance = store->instance()->definition(); | 
| + return instance->IsAllocateObject() && | 
| + ((check_type == kOptimisticCheck) || | 
| + instance->Identity().IsAllocationSinkingCandidate()); | 
| + } | 
| + return true; | 
| + } | 
| + | 
| + return false; | 
| +} | 
| + | 
| + | 
| // Right now we are attempting to sink allocation only into | 
| // deoptimization exit. So candidate should only be used in StoreInstanceField | 
| // instructions that write into fields of the allocated object. | 
| // We do not support materialization of the object that has type arguments. | 
| -static bool IsAllocationSinkingCandidate(AllocateObjectInstr* alloc) { | 
| +static bool IsAllocationSinkingCandidate(AllocateObjectInstr* alloc, | 
| + SafeUseCheck check_type) { | 
| for (Value* use = alloc->input_use_list(); | 
| use != NULL; | 
| use = use->next_use()) { | 
| - if (!(use->instruction()->IsStoreInstanceField() && | 
| - (use->use_index() == 0))) { | 
| + if (!IsSafeUse(use, check_type)) { | 
| + if (FLAG_trace_optimization) { | 
| + OS::Print("use of %s at %s is unsafe for allocation sinking\n", | 
| + alloc->ToCString(), | 
| + use->instruction()->ToCString()); | 
| + } | 
| return false; | 
| } | 
| } | 
| @@ -9901,16 +9912,42 @@ static bool IsAllocationSinkingCandidate(AllocateObjectInstr* alloc) { | 
| } | 
| +// If the given use is a store into an object then return an object we are | 
| +// storing into. | 
| +static Definition* StoreInto(Value* use) { | 
| + StoreInstanceFieldInstr* store = use->instruction()->AsStoreInstanceField(); | 
| + if (store != NULL) { | 
| + return store->instance()->definition(); | 
| + } | 
| + | 
| + return NULL; | 
| +} | 
| + | 
| + | 
| // Remove the given allocation from the graph. It is not observable. | 
| // If deoptimization occurs the object will be materialized. | 
| -static void EliminateAllocation(AllocateObjectInstr* alloc) { | 
| - ASSERT(IsAllocationSinkingCandidate(alloc)); | 
| +void AllocationSinking::EliminateAllocation(AllocateObjectInstr* alloc) { | 
| + ASSERT(IsAllocationSinkingCandidate(alloc, kStrictCheck)); | 
| if (FLAG_trace_optimization) { | 
| OS::Print("removing allocation from the graph: v%" Pd "\n", | 
| alloc->ssa_temp_index()); | 
| } | 
| + // First normalize materializations: if they reference this allocation | 
| + // from some materialization then replace this reference with a | 
| + // materialization which should have been computed for this side-exit. | 
| + // (CollectAllExits should have collected this exit). | 
| + Value* next_use; | 
| + for (Value* use = alloc->input_use_list(); | 
| + use != NULL; | 
| + use = next_use) { | 
| + next_use = use->next_use(); | 
| + if (use->instruction()->IsMaterializeObject()) { | 
| + use->BindTo(MaterializationFor(alloc, use->instruction())); | 
| + } | 
| + } | 
| + | 
| // As an allocation sinking candidate it is only used in stores to its own | 
| // fields. Remove these stores. | 
| for (Value* use = alloc->input_use_list(); | 
| @@ -9921,7 +9958,13 @@ static void EliminateAllocation(AllocateObjectInstr* alloc) { | 
| // There should be no environment uses. The pass replaced them with | 
| // MaterializeObject instructions. | 
| - ASSERT(alloc->env_use_list() == NULL); | 
| +#ifdef DEBUG | 
| + for (Value* use = alloc->env_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + ASSERT(use->instruction()->IsMaterializeObject()); | 
| + } | 
| +#endif | 
| ASSERT(alloc->input_use_list() == NULL); | 
| alloc->RemoveFromGraph(); | 
| if (alloc->ArgumentCount() > 0) { | 
| @@ -9933,30 +9976,149 @@ static void EliminateAllocation(AllocateObjectInstr* alloc) { | 
| } | 
| -void AllocationSinking::Optimize() { | 
| - GrowableArray<AllocateObjectInstr*> candidates(5); | 
| - | 
| - // Collect sinking candidates. | 
| - const GrowableArray<BlockEntryInstr*>& postorder = flow_graph_->postorder(); | 
| - for (BlockIterator block_it(postorder); | 
| +// Find allocation instructions that can be potentially eliminated and | 
| +// rematerialized at deoptimization exits if needed. See IsSafeUse | 
| +// for the description of algorithm used below. | 
| +void AllocationSinking::CollectCandidates() { | 
| + // Optimistically collect all potential candidates. | 
| + for (BlockIterator block_it = flow_graph_->reverse_postorder_iterator(); | 
| !block_it.Done(); | 
| block_it.Advance()) { | 
| BlockEntryInstr* block = block_it.Current(); | 
| for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 
| AllocateObjectInstr* alloc = it.Current()->AsAllocateObject(); | 
| - if ((alloc != NULL) && IsAllocationSinkingCandidate(alloc)) { | 
| - if (FLAG_trace_optimization) { | 
| - OS::Print("discovered allocation sinking candidate: v%" Pd "\n", | 
| - alloc->ssa_temp_index()); | 
| + if ((alloc != NULL) && | 
| + IsAllocationSinkingCandidate(alloc, kOptimisticCheck)) { | 
| + alloc->SetIdentity(AliasIdentity::AllocationSinkingCandidate()); | 
| + candidates_.Add(alloc); | 
| + } | 
| + } | 
| + } | 
| + | 
| + // Transitively unmark all candidates that are not strictly valid. | 
| + bool changed; | 
| + do { | 
| + changed = false; | 
| + for (intptr_t i = 0; i < candidates_.length(); i++) { | 
| + AllocateObjectInstr* alloc = candidates_[i]; | 
| + if (alloc->Identity().IsAllocationSinkingCandidate()) { | 
| + if (!IsAllocationSinkingCandidate(alloc, kStrictCheck)) { | 
| + alloc->SetIdentity(AliasIdentity::Unknown()); | 
| + changed = true; | 
| } | 
| + } | 
| + } | 
| + } while (changed); | 
| - // All sinking candidate are known to be not aliased. | 
| - alloc->SetIdentity(kIdentityNotAliased); | 
| + // Shrink the list of candidates removing all unmarked ones. | 
| + intptr_t j = 0; | 
| + for (intptr_t i = 0; i < candidates_.length(); i++) { | 
| + AllocateObjectInstr* alloc = candidates_[i]; | 
| + if (alloc->Identity().IsAllocationSinkingCandidate()) { | 
| + if (FLAG_trace_optimization) { | 
| + OS::Print("discovered allocation sinking candidate: v%" Pd "\n", | 
| + alloc->ssa_temp_index()); | 
| + } | 
| - candidates.Add(alloc); | 
| + if (j != i) { | 
| + candidates_[j] = alloc; | 
| } | 
| + j++; | 
| } | 
| } | 
| + candidates_.TruncateTo(j); | 
| +} | 
| + | 
| + | 
| +// Some candidates might stop being eligible for allocation sinking after | 
| +// the load forwarding because they flow into phis that load forwarding | 
| +// inserts. Discover such allocations and remove them from the list | 
| +// of allocation sinking candidates undoing all changes that we did | 
| +// in preparation for sinking these allocations. | 
| +void AllocationSinking::DiscoverFailedCandidates() { | 
| + // Transitively unmark all candidates that are not strictly valid. | 
| + bool changed; | 
| + do { | 
| + changed = false; | 
| + for (intptr_t i = 0; i < candidates_.length(); i++) { | 
| + AllocateObjectInstr* alloc = candidates_[i]; | 
| + if (alloc->Identity().IsAllocationSinkingCandidate()) { | 
| + if (!IsAllocationSinkingCandidate(alloc, kStrictCheck)) { | 
| + alloc->SetIdentity(AliasIdentity::Unknown()); | 
| + changed = true; | 
| + } | 
| + } | 
| + } | 
| + } while (changed); | 
| + | 
| + // Remove all failed candidates from the candidates list. | 
| + intptr_t j = 0; | 
| + for (intptr_t i = 0; i < candidates_.length(); i++) { | 
| + AllocateObjectInstr* alloc = candidates_[i]; | 
| + if (!alloc->Identity().IsAllocationSinkingCandidate()) { | 
| + if (FLAG_trace_optimization) { | 
| + OS::Print("allocation v%" Pd " can't be eliminated\n", | 
| + alloc->ssa_temp_index()); | 
| + } | 
| + | 
| +#ifdef DEBUG | 
| + for (Value* use = alloc->env_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + ASSERT(use->instruction()->IsMaterializeObject()); | 
| + } | 
| +#endif | 
| + | 
| + // All materializations will be removed from the graph. Remove inserted | 
| + // loads first and detach materializations from allocation's environment | 
| + // use list: we will reconstruct it when we start removing | 
| + // materializations. | 
| + alloc->set_env_use_list(NULL); | 
| + for (Value* use = alloc->input_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + if (use->instruction()->IsLoadField()) { | 
| + LoadFieldInstr* load = use->instruction()->AsLoadField(); | 
| + load->ReplaceUsesWith(flow_graph_->constant_null()); | 
| + load->RemoveFromGraph(); | 
| + } else { | 
| + ASSERT(use->instruction()->IsMaterializeObject() || | 
| + use->instruction()->IsPhi()); | 
| + } | 
| + } | 
| + } else { | 
| + if (j != i) { | 
| + candidates_[j] = alloc; | 
| + } | 
| + j++; | 
| + } | 
| + } | 
| + | 
| + if (j != candidates_.length()) { // Something was removed from candidates. | 
| + intptr_t k = 0; | 
| + for (intptr_t i = 0; i < materializations_.length(); i++) { | 
| + MaterializeObjectInstr* mat = materializations_[i]; | 
| + if (!mat->allocation()->Identity().IsAllocationSinkingCandidate()) { | 
| + // Restore environment uses of the allocation that were replaced | 
| + // by this materialization and drop materialization. | 
| + mat->ReplaceUsesWith(mat->allocation()); | 
| + mat->RemoveFromGraph(); | 
| + } else { | 
| + if (k != i) { | 
| + materializations_[k] = mat; | 
| + } | 
| + k++; | 
| + } | 
| + } | 
| + materializations_.TruncateTo(k); | 
| + } | 
| + | 
| + candidates_.TruncateTo(j); | 
| +} | 
| + | 
| + | 
| +void AllocationSinking::Optimize() { | 
| + CollectCandidates(); | 
| // Insert MaterializeObject instructions that will describe the state of the | 
| // object at all deoptimization points. Each inserted materialization looks | 
| @@ -9965,8 +10127,8 @@ void AllocationSinking::Optimize() { | 
| // ... | 
| // v_N <- LoadField(v_0, field_N) | 
| // v_{N+1} <- MaterializeObject(field_1 = v_1, ..., field_N = v_{N}) | 
| - for (intptr_t i = 0; i < candidates.length(); i++) { | 
| - InsertMaterializations(candidates[i]); | 
| + for (intptr_t i = 0; i < candidates_.length(); i++) { | 
| + InsertMaterializations(candidates_[i]); | 
| } | 
| // Run load forwarding to eliminate LoadField instructions inserted above. | 
| @@ -9977,15 +10139,15 @@ void AllocationSinking::Optimize() { | 
| // external effects from calls. | 
| LoadOptimizer::OptimizeGraph(flow_graph_); | 
| - if (FLAG_trace_optimization) { | 
| - FlowGraphPrinter::PrintGraph("Sinking", flow_graph_); | 
| - } | 
| + // If any candidates are no longer eligible for allocation sinking abort | 
| + // the optimization for them and undo any changes we did in preparation. | 
| + DiscoverFailedCandidates(); | 
| // At this point we have computed the state of object at each deoptimization | 
| // point and we can eliminate it. Loads inserted above were forwarded so there | 
| // are no uses of the allocation just as in the begging of the pass. | 
| - for (intptr_t i = 0; i < candidates.length(); i++) { | 
| - EliminateAllocation(candidates[i]); | 
| + for (intptr_t i = 0; i < candidates_.length(); i++) { | 
| + EliminateAllocation(candidates_[i]); | 
| } | 
| // Process materializations and unbox their arguments: materializations | 
| @@ -10010,21 +10172,22 @@ void AllocationSinking::Optimize() { | 
| // as part of the environment not as a real instruction. | 
| void AllocationSinking::DetachMaterializations() { | 
| for (intptr_t i = 0; i < materializations_.length(); i++) { | 
| - ASSERT(materializations_[i]->input_use_list() == NULL); | 
| + // ASSERT(materializations_[i]->input_use_list() == NULL); | 
| 
Florian Schneider
2014/07/16 15:02:03
Remove obsolete ASSERT?
 
Vyacheslav Egorov (Google)
2014/07/17 17:03:48
Done.
 | 
| materializations_[i]->previous()->LinkTo(materializations_[i]->next()); | 
| } | 
| } | 
| // Add a field/offset to the list of fields if it is not yet present there. | 
| -static void AddSlot(ZoneGrowableArray<const Object*>* slots, | 
| +static bool AddSlot(ZoneGrowableArray<const Object*>* slots, | 
| const Object& slot) { | 
| for (intptr_t i = 0; i < slots->length(); i++) { | 
| if ((*slots)[i]->raw() == slot.raw()) { | 
| - return; | 
| + return false; | 
| } | 
| } | 
| slots->Add(&slot); | 
| + return true; | 
| } | 
| @@ -10042,6 +10205,47 @@ static void AddInstruction(GrowableArray<Instruction*>* exits, | 
| } | 
| +// Find deoptimization exit for the given materialization assuming that all | 
| +// materializations are emitted right before the instruction which is a | 
| +// deoptimization exit. | 
| +static Instruction* ExitForMaterialization(MaterializeObjectInstr* mat) { | 
| + while (mat->next()->IsMaterializeObject()) { | 
| + mat = mat->next()->AsMaterializeObject(); | 
| + } | 
| + return mat->next(); | 
| +} | 
| + | 
| + | 
| +// Given the deoptimization exit find firt materialization that was inserted | 
| 
Florian Schneider
2014/07/16 15:02:02
s/firt/first/
 
Vyacheslav Egorov (Google)
2014/07/17 17:03:48
Done.
 | 
| +// at it. | 
| 
Florian Schneider
2014/07/16 15:02:02
s/at/before/ ?
 
Vyacheslav Egorov (Google)
2014/07/17 17:03:48
Done.
 | 
| +static Instruction* FirstMaterializationAt(Instruction* exit) { | 
| + while (exit->previous()->IsMaterializeObject()) { | 
| + exit = exit->previous(); | 
| + } | 
| + return exit; | 
| +} | 
| + | 
| + | 
| +// Given the allocation and deoptimization exit try to find MaterializeObject | 
| +// instruction corresponding to this allocation at this exit. | 
| +MaterializeObjectInstr* AllocationSinking::MaterializationFor( | 
| + Definition* alloc, Instruction* exit) { | 
| + if (exit->IsMaterializeObject()) { | 
| + exit = ExitForMaterialization(exit->AsMaterializeObject()); | 
| + } | 
| + | 
| + for (MaterializeObjectInstr* mat = exit->previous()->AsMaterializeObject(); | 
| + mat != NULL; | 
| + mat = mat->previous()->AsMaterializeObject()) { | 
| + if (mat->allocation() == alloc) { | 
| + return mat; | 
| + } | 
| + } | 
| + | 
| + return NULL; | 
| +} | 
| + | 
| + | 
| // Insert MaterializeObject instruction for the given allocation before | 
| // the given instruction that can deoptimize. | 
| void AllocationSinking::CreateMaterializationAt( | 
| @@ -10052,6 +10256,11 @@ void AllocationSinking::CreateMaterializationAt( | 
| ZoneGrowableArray<Value*>* values = | 
| new(I) ZoneGrowableArray<Value*>(slots.length()); | 
| + // All loads should be inserted before the first materialization so that | 
| + // IR follows the following pattern: loads, materializations, deoptimizing | 
| + // instruction. | 
| + Instruction* load_point = FirstMaterializationAt(exit); | 
| + | 
| // Insert load instruction for every field. | 
| for (intptr_t i = 0; i < slots.length(); i++) { | 
| LoadFieldInstr* load = slots[i]->IsField() | 
| @@ -10066,12 +10275,12 @@ void AllocationSinking::CreateMaterializationAt( | 
| AbstractType::ZoneHandle(I), | 
| alloc->token_pos()); | 
| flow_graph_->InsertBefore( | 
| - exit, load, NULL, FlowGraph::kValue); | 
| + load_point, load, NULL, FlowGraph::kValue); | 
| values->Add(new(I) Value(load)); | 
| } | 
| MaterializeObjectInstr* mat = | 
| - new(I) MaterializeObjectInstr(cls, slots, values); | 
| + new(I) MaterializeObjectInstr(alloc, cls, slots, values); | 
| flow_graph_->InsertBefore(exit, mat, NULL, FlowGraph::kValue); | 
| // Replace all mentions of this allocation with a newly inserted | 
| @@ -10089,11 +10298,50 @@ void AllocationSinking::CreateMaterializationAt( | 
| } | 
| } | 
| + // Mark MaterializeObject as an environment use of this allocation. | 
| + // This will allow us to discover it when we are looking for deoptimization | 
| + // exits for another allocation that potentially flows into this one. | 
| + Value* val = new(I) Value(alloc); | 
| + val->set_instruction(mat); | 
| + alloc->AddEnvUse(val); | 
| + | 
| // Record inserted materialization. | 
| materializations_.Add(mat); | 
| } | 
| +// Transitively collect all deoptimization exits that might need this allocation | 
| +// rematerialized. It is not enough to collect only environment uses of this | 
| +// allocation because it can flow into other objects that will be | 
| +// dematerialized and that are referenced by deopt environments that | 
| +// don't contain this allocation explicitly. | 
| +static void CollectAllExits(GrowableArray<Instruction*>* exits, | 
| + Definition* alloc) { | 
| + for (Value* use = alloc->env_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + if (use->instruction()->IsMaterializeObject()) { | 
| + AddInstruction(exits, ExitForMaterialization( | 
| + use->instruction()->AsMaterializeObject())); | 
| + } else { | 
| + AddInstruction(exits, use->instruction()); | 
| + } | 
| + } | 
| + | 
| + // Check if this allocation is stored into any other allocation sinking | 
| + // candidate and conservatively collect all exits for that candidate as | 
| + // well because they potentially might see this object as well. | 
| + for (Value* use = alloc->input_use_list(); | 
| + use != NULL; | 
| + use = use->next_use()) { | 
| + Definition* obj = StoreInto(use); | 
| + if ((obj != NULL) && (obj != alloc)) { | 
| + CollectAllExits(exits, obj); | 
| + } | 
| + } | 
| +} | 
| + | 
| + | 
| void AllocationSinking::InsertMaterializations(AllocateObjectInstr* alloc) { | 
| // Collect all fields that are written for this instance. | 
| ZoneGrowableArray<const Object*>* slots = | 
| @@ -10103,10 +10351,12 @@ void AllocationSinking::InsertMaterializations(AllocateObjectInstr* alloc) { | 
| use != NULL; | 
| use = use->next_use()) { | 
| StoreInstanceFieldInstr* store = use->instruction()->AsStoreInstanceField(); | 
| - if (!store->field().IsNull()) { | 
| - AddSlot(slots, store->field()); | 
| - } else { | 
| - AddSlot(slots, Smi::ZoneHandle(I, Smi::New(store->offset_in_bytes()))); | 
| + if ((store != NULL) && (store->instance()->definition() == alloc)) { | 
| + if (!store->field().IsNull()) { | 
| + AddSlot(slots, store->field()); | 
| + } else { | 
| + AddSlot(slots, Smi::ZoneHandle(I, Smi::New(store->offset_in_bytes()))); | 
| + } | 
| } | 
| } | 
| @@ -10118,11 +10368,7 @@ void AllocationSinking::InsertMaterializations(AllocateObjectInstr* alloc) { | 
| // Collect all instructions that mention this object in the environment. | 
| GrowableArray<Instruction*> exits(10); | 
| - for (Value* use = alloc->env_use_list(); | 
| - use != NULL; | 
| - use = use->next_use()) { | 
| - AddInstruction(&exits, use->instruction()); | 
| - } | 
| + CollectAllExits(&exits, alloc); | 
| // Insert materializations at environment uses. | 
| for (intptr_t i = 0; i < exits.length(); i++) { |