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

Unified Diff: src/hydrogen.cc

Issue 11365174: A change in the way we place TransitionElementKinds in the tree. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Visited set needs to consider edges, not vertexes on down propagation through the network. Created 8 years, 1 month 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: src/hydrogen.cc
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index ddf86c5e4706c7e2168d6ace7096bb3edb337db9..020966c5b018990c12cebd51c6a53462c81c0d9b 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -2449,6 +2449,751 @@ void HGraph::InitializeInferredTypes(int from_inclusive, int to_inclusive) {
}
+typedef ZoneList<HInstruction*> TransitionInputList;
+typedef ZoneList<ArrayInstruction*> ArrayInstructionList;
+typedef EnumSet<ElementsKind> ElementsKindSet;
+
+
+class MapHandleMap BASE_EMBEDDED {
+ public:
+ explicit MapHandleMap(Zone* zone) :
+ map_(MapPointerMatch, ZoneAllocationPolicy(zone)),
+ indexer_(IndexMatch, ZoneAllocationPolicy(zone)),
+ zone_(zone),
+ null_handle_(Handle<Map>::null()) {
+ }
+
+ Handle<Map>& Lookup(Map* map) {
+ MapHandleTable::Iterator i = map_.find(map, false,
+ ZoneAllocationPolicy(zone_));
+ if (i != map_.end()) {
+ return *(i->second);
+ }
+
+ return null_handle_;
+ }
+
+ Map* Lookup(Map* family, ElementsKind kind) {
+ IndexKey lookupKey(family, kind);
+ IndexKeyTable::Iterator i = indexer_.find(
+ &lookupKey, false, ZoneAllocationPolicy(zone_));
+ if (i != indexer_.end()) {
+ return i->second;
+ }
+
+ return NULL;
+ }
+
+ void Populate(ArrayInstruction* instruction) {
+ for (int r = 0; r < instruction->bookmarks(); r++) {
+ Add(instruction->bookmark(r));
+ }
+ }
+
+ private:
+ struct IndexKey: public ZoneObject {
+ IndexKey(Map* family_in, ElementsKind kind_in) :
+ family(family_in),
+ kind(kind_in) {
+ }
+
+ int Hash() {
+ return family->Hash() + (31*kind);
+ }
+
+ Map* family;
+ ElementsKind kind;
+ };
+
+ static bool MapPointerMatch(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
+ static bool IndexMatch(void* key1, void* key2) {
+ IndexKey* k1 = reinterpret_cast<IndexKey*>(key1);
+ IndexKey* k2 = reinterpret_cast<IndexKey*>(key2);
+ return k1->Hash() == k2->Hash();
+ }
+
+ void Add(TransitionElementsBookmark* record) {
+ Handle<Map>* family = record->family_pointer();
+ InsertIfMissing(record->from_pointer(), family);
+ InsertIfMissing(record->to_pointer(), family);
+ InsertIfMissing(record->pessimistic_holey_pointer(), family);
+ }
+
+ void Insert(Handle<Map>* handle, Handle<Map>* family) {
+ Map* map = *(handle->location());
+ Map* family_map = *(family->location());
+
+ MapHandleTable::Iterator i = map_.find(map, true,
+ ZoneAllocationPolicy(zone_));
+ ASSERT(i != map_.end());
+ i->second = handle;
+
+ IndexKey *key = new(zone()) IndexKey(family_map, map->elements_kind());
+ IndexKeyTable::Iterator iki = indexer_.find(
+ key, true,
+ ZoneAllocationPolicy(zone_));
+ ASSERT(iki != indexer_.end());
+ iki->second = map;
+ }
+
+ void InsertIfMissing(Handle<Map>* handle, Handle<Map>* family) {
+ if (Lookup(*(handle->location())).is_null()) {
+ Insert(handle, family);
+ }
+ }
+
+ Zone* zone() { return zone_; }
+
+ typedef TemplateHashMap<Map, Handle<Map>, ZoneAllocationPolicy>
+ MapHandleTable;
+ MapHandleTable map_;
+ typedef TemplateHashMap<IndexKey, Map, ZoneAllocationPolicy>
+ IndexKeyTable;
+ IndexKeyTable indexer_;
+ Zone* zone_;
+ Handle<Map> null_handle_;
+};
+
+
+class InstructionPlacer: public ZoneObject {
+ public:
+ InstructionPlacer(HInstruction* transition_input, Zone* zone) :
+ zone_(zone),
+ transition_input_(transition_input),
+ last_in_chain_(NULL) {
+
+ }
+
+ // For instruction emission
+ bool RequiresTransitionElementsKind(ElementsKind from) const {
+ return !from_elements_.Contains(from);
+ }
+
+ void InsertTransitionElementsKind(Handle<Map> from, Handle<Map> to) {
+ ASSERT(RequiresTransitionElementsKind(from->elements_kind()));
+ // At the site, we maintain a most recent instruction we added, and
+ // new instructions should appear at the end of the chain.
+ HInstruction* addafter = last_in_chain_;
+ if (addafter == NULL) {
+ // This is the first instruction to add.
+ // We must emit a checknonsmi
+ addafter = new(zone()) HCheckNonSmi(transition_input_);
+ addafter->InsertAfter(transition_input_);
+ }
+
+ HTransitionElementsKind* transition = NULL;
+ if (last_in_chain_ == NULL) {
+ transition = new(zone()) HTransitionElementsKind(transition_input_, from,
+ to);
+ transition->InsertAfter(addafter);
+
+ // Careful tree surgery
+ for (HUseIterator it(transition_input_->uses()); !it.Done();
+ it.Advance()) {
+ HValue* use = it.value();
+ if (use != HValue::cast(addafter) &&
+ use != HValue::cast(transition)) {
+ CarefullyReplaceOperand(&it, transition);
+ }
+ }
+ } else {
+ transition = new(zone()) HTransitionElementsKind(last_in_chain_, from,
+ to);
+ transition->InsertAfter(last_in_chain_);
+
+ // Careful tree surgery
+ for (HUseIterator it(last_in_chain_->uses()); !it.Done();
+ it.Advance()) {
+ HValue* use = it.value();
+ if (use != HValue::cast(transition)) {
+ CarefullyReplaceOperand(&it, transition);
+ }
+ }
+ }
+
+ last_in_chain_ = transition;
+ from_elements_.Add(from->elements_kind());
+ }
+
+ HInstruction* transition_input() const { return transition_input_; }
+
+ private:
+ void CarefullyReplaceOperand(HUseIterator* it, HInstruction* with) {
+ HValue* use = it->value();
+ if (!use->block()->IsStartBlock()) {
+ if (!use->IsSimulate() || use->block() != with->block()) {
+ use->SetOperandAt(it->index(), with);
+ } else {
+ // Because of the way our new transition elements can be placed at the
+ // start of a block, but ONLY AFTER any simulate instruction, we can
+ // only replace usages of the with instruction if we are sure our new
+ // instruction is defined in the block somewhere before the simulate.
+ HInstruction* cur = HInstruction::cast(use)->previous();
+ while (cur != NULL) {
+ if (cur == with) break;
+ cur = cur->previous();
+ }
+ if (cur == with) {
+ // We can safely do the replacement
+ use->SetOperandAt(it->index(), with);
+ }
+ }
+ }
+ }
+
+ Zone* zone() { return zone_; }
+ Zone* zone_;
+ HInstruction* transition_input_;
+ ElementsKindSet from_elements_;
+ HTransitionElementsKind* last_in_chain_;
+};
+
+
+class ResolutionTableValue: public ZoneObject {
+ public:
+ ResolutionTableValue() :
+ to_(NULL),
+ family_(NULL),
+ poisoned_(false),
+ placer_(NULL) {
+
+ }
+
+ Map* to() { return to_; }
+ Map* family() { return family_; }
+ bool poisoned() { return poisoned_; }
+ ElementsKindSet from_set() { return from_elements_; }
+
+ void Initialize(ArrayInstruction* instr) {
+ if (!instr->hoistable()) {
+ poisoned_ = true;
+ return;
+ }
+
+ family_ = instr->map_family(); // may be null
+ if (family_ != NULL) {
+ // We should have bookmarks to initialize with
+ ASSERT(instr->bookmarks() > 0);
+ for (int i=0;i<instr->bookmarks();i++) {
+ TransitionElementsBookmark* tr = instr->bookmark(i);
+ from_elements_.Add(tr->elementskind_from());
+ to_ = *(tr->to_pointer()->location());
+ }
+ }
+ }
+
+ void Initialize(HPhi* phi, int maximum_graph_id, Zone* zone) {
+ visited_set_ = new(zone) BitVector(maximum_graph_id, zone);
+ }
+
+ bool Merge(ResolutionTableValue* from_value) {
+
+ // a) Poison must always be imbibed
+ if (from_value->poisoned() && !poisoned()) {
+ poisoned_ = true;
+ return true;
+ }
+
+ // b) from_value has no information. No change.
+ if (from_value->to() == NULL) {
+ return false;
+ }
+
+ // c) We are uninitialized. Copy from_value.
+ ASSERT(from_value->family() != NULL);
+ if (to() == NULL) {
+ to_ = from_value->to();
+ from_elements_.Add(from_value->from_set());
+ family_ = from_value->family();
+ return true;
+ }
+
+ // d) We are both initialized. Negotiate
+ if (from_value->family() != family()) {
+ // We cannot change families!
+ poisoned_ = true;
+ return true;
+ }
+
+ bool changed = false;
+ if (to() != from_value->to()) {
+ // figure out unified map
+ ElementsKind unified_elements_kind = GetUnifiedFastElementsKind(
+ to()->elements_kind(),
+ from_value->to()->elements_kind());
+ // make sure both maps have a path to success
+ Map* unified_map = to()->LookupElementsTransitionMap(unified_elements_kind);
+ Map* unified_from_value_map = from_value->to()->LookupElementsTransitionMap(unified_elements_kind);
+ if (unified_map == NULL || unified_from_value_map == NULL ||
+ (unified_map != unified_from_value_map)) {
+ // impossible
+ poisoned_ = true;
+ return true;
+ }
+
+ // TODO(mvstanton): on merges "down" the tree from roots back to load/store sites,
+ // we know we already did the unification step above, and that the current ResolutionTableValue
+ // should just accept the from.to_ map. Is it worth adding something like "mergemode"
+ // to save the trouble of the calls above?
+ to_ = unified_map;
+ changed = true;
+ }
+
+ if (!(from_set() == from_value->from_set())) {
+ from_elements_.Add(from_value->from_set());
+ changed = true;
+ }
+
+ return changed;
+ }
+
+ bool VisitedBy(HValue* value) {
+ return visited_set_->Contains(value->id());
+ }
+
+ void MarkVisitedBy(HValue* value) {
+ visited_set_->Add(value->id());
+ }
+
+ InstructionPlacer* placer() { return placer_; }
+ void set_placer(InstructionPlacer* placer) { placer_ = placer; }
+ private:
+ Map* to_;
+ Map* family_;
+ bool poisoned_;
+ ElementsKindSet from_elements_;
+ InstructionPlacer* placer_; // Field only used for root nodes
+ BitVector* visited_set_; // only used by phi nodes
+};
+
+
+class ResolutionTable BASE_EMBEDDED {
+ public:
+ explicit ResolutionTable(Isolate* isolate, Zone* zone) :
+ dictionary_(NodeMatch, 32, ZoneAllocationPolicy(zone)),
+ zone_(zone) {
+ }
+
+ void Insert(HValue* node, ResolutionTableValue* value) {
+ ZoneHashMap::Entry* entry = dictionary_.Lookup(node,
+ node->id(),
+ true,
+ ZoneAllocationPolicy(zone_));
+ ASSERT(entry->value == NULL);
+ entry->value = value;
+ }
+
+ ResolutionTableValue* Lookup(HValue* node) {
+ ZoneHashMap::Entry* entry = dictionary_.Lookup(node,
+ node->id(),
+ false,
+ ZoneAllocationPolicy(zone_));
+
+ if (entry == NULL) {
+ return NULL;
+ }
+
+ ResolutionTableValue* value =
+ reinterpret_cast<ResolutionTableValue*>(entry->value);
+ return value;
+ }
+
+ ZoneHashMap::Entry* Start() {
+ return dictionary_.Start();
+ }
+
+ inline HValue* KeyFromEntry(ZoneHashMap::Entry* entry) {
+ return reinterpret_cast<HValue*>(entry->key);
+ }
+
+ inline ResolutionTableValue* ValueFromEntry(ZoneHashMap::Entry* entry) {
+ return reinterpret_cast<ResolutionTableValue*>(entry->value);
+ }
+
+ ZoneHashMap::Entry* Next(ZoneHashMap::Entry* previous_entry) {
+ return dictionary_.Next(previous_entry);
+ }
+
+ private:
+ Zone* zone() { return zone_; }
+ static bool NodeMatch(void* key1, void* key2) {
+ return key1 == key2;
+ }
+
+ ZoneHashMap dictionary_;
+ Zone* zone_;
+};
+
+
+struct WorkItem {
+ WorkItem(HValue* from_in, HValue* to_in, HValue* context_in)
+ : from(from_in), to(to_in), context(context_in) {
+ }
+ bool operator==(const WorkItem& pair) const {
+ return pair.from == from && pair.to == to && pair.context == context;
+ }
+ HValue* from;
+ HValue* to;
+ HValue* context;
+};
+
+
+void HoistTransitionsFromArrayInstructionSites(ArrayInstruction* load_or_store,
+ ResolutionTableValue* load_or_store_value,
+ HValue* root,
+ ResolutionTableValue* root_value,
+ MapHandleMap* map_handles,
+ Zone* zone) {
+
+ // Are we poisonous? We can't participate in this wonderful scheme...
+ if (load_or_store_value->poisoned()) {
+ // Poison should have flowed up and down the graph from sites to roots
+ ASSERT(root_value->poisoned());
+ return;
+ }
+
+ // In here we'll
+ // 1) remove any transition statements from the load_or_store site
+ // 2) place correct statements at the root site, avoiding duplicates
+ if (load_or_store->bookmarks() > 0 && root_value->placer() == NULL) {
+ ASSERT(root->IsInstruction());
+ root_value->set_placer(new(zone) InstructionPlacer(HInstruction::cast(root),
+ zone));
+ }
+
+ ASSERT(root_value->to() == load_or_store_value->to());
+ Handle<Map>& handle = map_handles->Lookup(root_value->to());
+ InstructionPlacer* placer = root_value->placer();
+ for (int i = 0; i < load_or_store->bookmarks(); i++) {
+ TransitionElementsBookmark* bookmark = load_or_store->bookmark(i);
+
+ // TODO(mvstanton): lots of asserts in this method that the information
+ // at the instruction site and the root site are the same or subsets.
+ // Unnecessary redundancy?
+ ASSERT(root_value->from_set().Contains(bookmark->elementskind_from()));
+ ASSERT(load_or_store_value->from_set().Contains(
+ bookmark->elementskind_from()));
+
+ if (placer->RequiresTransitionElementsKind(bookmark->elementskind_from())) {
+ placer->InsertTransitionElementsKind(bookmark->from(), handle);
+
+ if (bookmark->transition_instruction()->block() != NULL) {
+ // The transition instruction should only be referred to by a map check instruction.
+ for (HUseIterator it(bookmark->transition_instruction()->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ ASSERT(value->IsCheckMaps());
+ HCheckMaps::cast(value)->RemoveTypeCheck();
+ }
+
+ bookmark->transition_instruction()->DeleteAndReplaceWith(NULL);
+ }
+ }
+ }
+}
+
+
+void UpdateMapCheck(HCheckMaps* check_maps,
+ ResolutionTableValue* nearest_value,
+ MapHandleMap* map_handles,
+ Zone* zone) {
+ // Only manipulate this mapcheck if at least one of the {from,to} maps from
+ // nearest_value is in the check.
+ Map* family = nearest_value->family();
+ Map* to = nearest_value->to();
+ ASSERT(to != NULL && family != NULL);
+ bool contains_to = false;
+ bool contains_from = false;
+ for (int i = 0; i < check_maps->map_set()->length(); i++) {
+ Map* current = *(check_maps->map_set()->at(i).location());
+ if (current != to) {
+ Map* in_family = map_handles->Lookup(family,
+ current->elements_kind());
+ if (current == in_family) {
+ contains_from = true;
+ }
+ } else {
+ contains_to = true;
+ }
+ }
+
+ if (!contains_from && !contains_to) {
+ // This map check deals with a different family. Leave it alone
+ return;
+ }
+
+ if (!contains_to) {
+ // We definitely need the to map in the checkmaps instruction
+ Handle<Map>& handle = map_handles->Lookup(to);
+ ASSERT(!handle.is_null());
+ check_maps->map_set()->Add(handle, zone);
+ }
+
+ if (contains_from) {
+ // The whole from set should be removed from the map check, since we have
+ // the to map in there.
+ for (int i = check_maps->map_set()->length() - 1; i >= 0; i--) {
+ Map* current = *(check_maps->map_set()->at(i).location());
+ if (current != to) {
+ Map* in_family = map_handles->Lookup(family,
+ current->elements_kind());
+ if (current == in_family) {
+ // Delete this map
+ check_maps->map_set()->RemoveAt(i);
+ }
+ }
+ }
+ }
+
+ if (!contains_to) {
+ // We should sort the map since we added one, removing others
+ check_maps->map_set()->Sort();
+ }
+}
+
+
+void HGraph::InsertElementsTransitions() {
+ ZoneAllocationPolicy allocator(zone());
+ HPhase phase("H_Place elements transitions", this);
+
+ MapHandleMap mapHandleMap(zone());
+ ResolutionTable table(isolate(),zone());
+
+ ZoneList<WorkItem> worklist(10, zone());
+
+ // Walk the graph looking for store and load keyed instructions.
+ for (int i = 0; i < blocks()->length(); ++i) {
+ for (HInstruction* instr = blocks()->at(i)->first();
+ instr != NULL;
+ instr = instr->next()) {
+ ArrayInstruction* array_instruction = NULL;
+ if (instr->IsLoadKeyed()) {
+ HLoadKeyed* op = HLoadKeyed::cast(instr);
+ array_instruction = static_cast<ArrayInstruction*>(op);
+ } else if (instr->IsStoreKeyed()) {
+ HStoreKeyed* op = HStoreKeyed::cast(instr);
+ array_instruction = static_cast<ArrayInstruction*>(op);
+ } else {
+ continue;
+ }
+
+ HValue* elements = array_instruction->elements();
+ if (elements->IsLoadElements()) {
+ // Add to our table
+ ResolutionTableValue* value = new(zone()) ResolutionTableValue();
+ value->Initialize(array_instruction);
+ table.Insert(array_instruction, value);
+
+ // Add to our handle map
+ mapHandleMap.Populate(array_instruction);
+
+ // Add to our worklist. Here I am skipping over elements,
+ HLoadElements* start = HLoadElements::cast(elements);
+ WorkItem item(array_instruction, start->value(), array_instruction);
+ worklist.Add(item, zone());
+
+ if (FLAG_trace_transition_placement) {
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ array_instruction->PrintElementPlacementTo(&stream);
+ PrintF("%s", *stream.ToCString());
+ }
+ } else {
+ // No need to consider external arrays in our case.
+ ASSERT(IsExternalArrayElementsKind(array_instruction->GetElementsKind()));
+ array_instruction->Finalize();
+ }
+ }
+ }
+
+ // Propagate information up the graph.
+ while (!worklist.is_empty()) {
+ WorkItem item = worklist.RemoveLast();
+ ASSERT(table.Lookup(item.from) != NULL);
+ bool changed = false;
+ ResolutionTableValue* to_value = table.Lookup(item.to);
+ if (to_value == NULL) {
+ to_value = new(zone()) ResolutionTableValue();
+ table.Insert(item.to, to_value);
+ if (item.to->IsPhi()) {
+ to_value->Initialize(HPhi::cast(item.to),GetMaximumValueID(), zone());
+ }
+ changed = true;
+ }
+
+ changed |= to_value->Merge(table.Lookup(item.from));
+ if (item.to->IsPhi()) {
+ if (changed) {
+ HPhi* phi = HPhi::cast(item.to);
+ for (int i = 0; i < phi->OperandCount(); i++) {
+ worklist.Add(WorkItem(phi, phi->OperandAt(i), item.context), zone());
+ }
+ }
+ } else {
+ // Item.to is a root node. The context is useful to print
+ if (FLAG_trace_transition_placement) {
+ HeapStringAllocator string_allocator;
+ StringStream stream(&string_allocator);
+ ArrayInstruction* instr = reinterpret_cast<ArrayInstruction*>(item.context);
+ stream.Add("ARRAY INSTRUCTION: block%d %d: ", instr->block()->block_id(),
+ instr->id());
+ instr->PrintTo(&stream);
+ stream.Add("\n");
+ stream.Add(" maps to\n");
+ HInstruction* root = HInstruction::cast(item.to);
+ stream.Add(" block%d %d: ", root->block()->block_id(), root->id());
+ root->PrintNameTo(&stream);
+ stream.Add(" ");
+ root->PrintTo(&stream);
+ stream.Add("\n");
+ if (to_value->to() != NULL) {
+ stream.Add(" To: %s(0x%p)\n",
+ ElementsKindToString(to_value->to()->elements_kind()),
+ to_value->to());
+ } else {
+ stream.Add(" To: n/a\n");
+ }
+
+ PrintF("%s", *stream.ToCString());
+ }
+ }
+ }
+
+ // Now propagate information back down to input sites, starting from root
+ // nodes in the table
+ // Table contains phi nodes, arrayinstructions, and roots.
+ ZoneHashMap::Entry* current = table.Start();
+ while (current != NULL) {
+ HValue* current_value = table.KeyFromEntry(current);
+ if (!current_value->IsPhi() && !current_value->IsLoadKeyed() &&
+ !current_value->IsStoreKeyed()) {
+ HInstruction* current_instr = HInstruction::cast(current_value);
+
+ for (HUseIterator it(current_instr->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ if (value->IsPhi()) {
+ worklist.Add(WorkItem(current_value, value, current_value), zone());
+ } else if (value->IsLoadElements()) {
+ // Walk through to the StoreKeyed(s)
+ for (HUseIterator it_load(value->uses()); !it_load.Done(); it_load.Advance()) {
+ HValue* value_load = it_load.value();
+ if (value_load->IsStoreKeyed() || value_load->IsLoadKeyed()) {
+ worklist.Add(WorkItem(current_value, value_load, current_value), zone());
+ }
+ }
+ } else if (value->IsCheckMaps()) {
+ // CheckMaps don't have rows in the table, as they are periphery instructions
+ // we might need to update.
+ worklist.Add(WorkItem(current_value, value, current_value), zone());
+ }
+ }
+ }
+
+ current = table.Next(current);
+ }
+
+ while (!worklist.is_empty()) {
+ WorkItem item = worklist.RemoveLast();
+ /*
+ PrintF("Downpropagation: (%d,%d,%d)\n",
+ item.from->id(),
+ item.to->id(),
+ item.context->id());
+ */
+ bool changed = false;
+
+ ResolutionTableValue* from_value = table.Lookup(item.from);
+ ASSERT(from_value != NULL);
+ ResolutionTableValue* to_value = table.Lookup(item.to);
+
+ if (to_value == NULL && item.to->IsPhi()) {
+ // We walk through phis on the way down, even if we didn't see
+ // them on the way up. There may be interesting nodes in these
+ // new locations (checkmaps, store/loads).
+ to_value = new(zone()) ResolutionTableValue();
+ to_value->Initialize(HPhi::cast(item.to), GetMaximumValueID(), zone());
+ table.Insert(item.to, to_value);
+ }
+
+ if (to_value != NULL) {
+ changed = to_value->Merge(from_value);
+ }
+
+ if (item.to->IsPhi() && (changed || !to_value->VisitedBy(item.context))) {
+ to_value->MarkVisitedBy(item.context);
+
+ ASSERT(to_value != NULL);
+ for (HUseIterator it(item.to->uses()); !it.Done(); it.Advance()) {
+ HValue* value = it.value();
+ if (value->IsPhi()) {
+ worklist.Add(WorkItem(item.to, value, item.context), zone());
+ } else if (value->IsLoadElements()) {
+ // Walk through to the StoreKeyed(s)
+ for (HUseIterator it_load(value->uses()); !it_load.Done(); it_load.Advance()) {
+ HValue* value_load = it_load.value();
+ if (value_load->IsStoreKeyed() || value_load->IsLoadKeyed()) {
+ worklist.Add(WorkItem(item.to, value_load, item.context), zone());
+ }
+ }
+ } else if (value->IsCheckMaps()) {
+ worklist.Add(WorkItem(item.to, value, item.context), zone());
+ }
+ }
+ } else if (item.to->IsLoadKeyed() || item.to->IsStoreKeyed()) {
+ // Either it's the first time we are coming here OR the change is effectively no change
+ // This is because we begin the graph traversal at ROOT nodes, which have comprehensive
+ // information. Any edge in the worklist which leads to the array instruction will have
+ // that full information already, so there aren't really any changes at this point.
+
+ ASSERT(to_value != NULL);
+ ArrayInstruction* array_instruction = reinterpret_cast<ArrayInstruction*>(item.to);
+ // It can now be finalized
+ ResolutionTableValue* context_value = table.Lookup(item.context);
+ ASSERT(context_value != NULL);
+ if (to_value->to() != NULL) {
+ array_instruction->Finalize(to_value->to()->elements_kind());
+ if (array_instruction->bookmarks() > 0) {
+ // We have work to do here with the context variable.
+ // It is the root node where we'll move transitions to.
+ HoistTransitionsFromArrayInstructionSites(array_instruction,
+ to_value,
+ item.context,
+ context_value,
+ &mapHandleMap,
+ zone());
+ }
+ } else {
+ // It's possible there is no exchange of typing information for a site.
+ array_instruction->Finalize();
+ }
+ } else if (item.to->IsCheckMaps()) {
+ // There is no to_value for this case. We just need to try and update the
+ // checkmaps
+ ASSERT(to_value == NULL);
+ if (from_value->to() != NULL) {
+ UpdateMapCheck(HCheckMaps::cast(item.to), from_value, &mapHandleMap, zone());
+ }
+ }
+ }
+
+#ifdef DEBUG
+ // Verify that we didn't miss initialization of any array instructions.
+ current = table.Start();
+ while (current != NULL) {
+ HValue* current_value = table.KeyFromEntry(current);
+ if (current_value->IsLoadKeyed() || current_value->IsStoreKeyed()) {
+ ArrayInstruction* array_instr = reinterpret_cast<ArrayInstruction*>(current_value);
+ ASSERT(array_instr->Initialized());
+ }
+
+ current = table.Next(current);
+ }
+#endif // DEBUG
+}
+
+
void HGraph::PropagateMinusZeroChecks(HValue* value, BitVector* visited) {
HValue* current = value;
while (current != NULL) {
@@ -3274,6 +4019,8 @@ bool HGraph::Optimize(SmartArrayPointer<char>* bailout_reason) {
if (FLAG_eliminate_dead_phis) EliminateUnreachablePhis();
CollectPhis();
+ if (FLAG_use_place_elements_transitions) InsertElementsTransitions();
+
if (has_osr_loop_entry()) {
const ZoneList<HPhi*>* phis = osr_loop_entry()->phis();
for (int j = 0; j < phis->length(); j++) {
@@ -3670,7 +4417,7 @@ void HGraph::EliminateRedundantBoundsChecks() {
}
-static void DehoistArrayIndex(ArrayInstructionInterface* array_operation) {
+static void DehoistArrayIndex(ArrayInstruction* array_operation) {
HValue* index = array_operation->GetKey();
if (!index->representation().IsInteger32()) return;
@@ -3726,13 +4473,13 @@ void HGraph::DehoistSimpleArrayIndexComputations() {
for (HInstruction* instr = blocks()->at(i)->first();
instr != NULL;
instr = instr->next()) {
- ArrayInstructionInterface* array_instruction = NULL;
+ ArrayInstruction* array_instruction = NULL;
if (instr->IsLoadKeyed()) {
HLoadKeyed* op = HLoadKeyed::cast(instr);
- array_instruction = static_cast<ArrayInstructionInterface*>(op);
+ array_instruction = static_cast<ArrayInstruction*>(op);
} else if (instr->IsStoreKeyed()) {
HStoreKeyed* op = HStoreKeyed::cast(instr);
- array_instruction = static_cast<ArrayInstructionInterface*>(op);
+ array_instruction = static_cast<ArrayInstruction*>(op);
} else {
continue;
}
@@ -4577,7 +5324,8 @@ void HGraphBuilder::VisitForInStatement(ForInStatement* stmt) {
environment()->ExpressionStackAt(2), // Enum cache.
environment()->ExpressionStackAt(0), // Iteration index.
environment()->ExpressionStackAt(0),
- FAST_ELEMENTS));
+ FAST_ELEMENTS,
+ zone()));
// Check if the expected map still matches that of the enumerable.
// If not just deoptimize.
@@ -5190,7 +5938,8 @@ void HGraphBuilder::VisitArrayLiteral(ArrayLiteral* expr) {
elements,
key,
value,
- boilerplate_elements_kind));
+ boilerplate_elements_kind,
+ zone()));
break;
default:
UNREACHABLE();
@@ -6016,7 +6765,7 @@ HInstruction* HGraphBuilder::BuildLoadKeyedGeneric(HValue* object,
}
-HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
+ArrayInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
HValue* external_elements,
HValue* checked_key,
HValue* val,
@@ -6055,12 +6804,13 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
return new(zone()) HStoreKeyed(external_elements,
checked_key,
val,
- elements_kind);
+ elements_kind,
+ zone());
} else {
ASSERT(val == NULL);
HLoadKeyed* load =
new(zone()) HLoadKeyed(
- external_elements, checked_key, dependency, elements_kind);
+ external_elements, checked_key, dependency, elements_kind, zone());
if (FLAG_opt_safe_uint32_operations &&
elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) {
graph()->RecordUint32Instruction(load);
@@ -6070,12 +6820,12 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess(
}
-HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
- HValue* checked_key,
- HValue* val,
- HValue* load_dependency,
- ElementsKind elements_kind,
- bool is_store) {
+ArrayInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
+ HValue* checked_key,
+ HValue* val,
+ HValue* load_dependency,
+ ElementsKind elements_kind,
+ bool is_store) {
if (is_store) {
ASSERT(val != NULL);
switch (elements_kind) {
@@ -6089,7 +6839,7 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
case FAST_DOUBLE_ELEMENTS:
case FAST_HOLEY_DOUBLE_ELEMENTS:
return new(zone()) HStoreKeyed(
- elements, checked_key, val, elements_kind);
+ elements, checked_key, val, elements_kind, zone());
default:
UNREACHABLE();
return NULL;
@@ -6099,28 +6849,32 @@ HInstruction* HGraphBuilder::BuildFastElementAccess(HValue* elements,
return new(zone()) HLoadKeyed(elements,
checked_key,
load_dependency,
- elements_kind);
+ elements_kind,
+ zone());
}
-HInstruction* HGraphBuilder::BuildMonomorphicElementAccess(HValue* object,
- HValue* key,
- HValue* val,
- HValue* dependency,
- Handle<Map> map,
- bool is_store) {
+ArrayInstruction* HGraphBuilder::BuildMonomorphicElementAccess(
+ HValue* object,
+ HValue* key,
+ HValue* val,
+ HValue* dependency,
+ Handle<Map> map,
+ bool is_store) {
HCheckMaps* mapcheck = new(zone()) HCheckMaps(object, map,
zone(), dependency);
AddInstruction(mapcheck);
if (dependency) {
mapcheck->ClearGVNFlag(kDependsOnElementsKind);
}
- return BuildUncheckedMonomorphicElementAccess(object, key, val,
- mapcheck, map, is_store);
+
+ ArrayInstruction* instr = BuildUncheckedMonomorphicElementAccess(
+ object, key, val, mapcheck, map, is_store);
+ return instr;
}
-HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
+ArrayInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* object,
HValue* key,
HValue* val,
@@ -6176,7 +6930,7 @@ HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
}
-HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
+ArrayInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
HValue* object,
HValue* key,
HValue* val,
@@ -6223,9 +6977,8 @@ HInstruction* HGraphBuilder::TryBuildConsolidatedElementLoad(
HCheckMaps* check_maps = new(zone()) HCheckMaps(object, maps, zone());
AddInstruction(check_maps);
- HInstruction* instr = BuildUncheckedMonomorphicElementAccess(
+ return BuildUncheckedMonomorphicElementAccess(
object, key, val, check_maps, most_general_consolidated_map, false);
- return instr;
}
@@ -6238,7 +6991,9 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
bool is_store,
bool* has_side_effects) {
*has_side_effects = false;
- AddInstruction(new(zone()) HCheckNonSmi(object));
+ bool use_groups = FLAG_use_place_elements_transitions;
+ HCheckNonSmi* checknonsmi = new(zone()) HCheckNonSmi(object);
+ AddInstruction(checknonsmi);
SmallMapList* maps = prop->GetReceiverTypes();
bool todo_external_array = false;
@@ -6284,6 +7039,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
int num_untransitionable_maps = 0;
Handle<Map> untransitionable_map;
HTransitionElementsKind* transition = NULL;
+ ZoneList<TransitionElementsBookmark> bookmarks(10, zone());
for (int i = 0; i < maps->length(); ++i) {
Handle<Map> map = maps->at(i);
ASSERT(map->IsMap());
@@ -6294,6 +7050,14 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
transition = new(zone()) HTransitionElementsKind(
object, map, transition_target.at(i));
AddInstruction(transition);
+
+ if (use_groups) {
+ TransitionElementsBookmark tr(transition,
+ map,
+ transition_target.at(i),
+ isolate());
+ bookmarks.Add(tr, zone());
+ }
} else {
type_todo[map->elements_kind()] = true;
if (IsExternalArrayElementsKind(map->elements_kind())) {
@@ -6312,8 +7076,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
instr = AddInstruction(is_store ? BuildStoreKeyedGeneric(object, key, val)
: BuildLoadKeyedGeneric(object, key));
} else {
- instr = AddInstruction(BuildMonomorphicElementAccess(
- object, key, val, transition, untransitionable_map, is_store));
+ ArrayInstruction* array_instr = BuildMonomorphicElementAccess(
+ object, key, val, transition, untransitionable_map, is_store);
+ instr = AddInstruction(array_instr);
+ if (use_groups && bookmarks.length() > 0) {
+ array_instr->AddBookmarks(bookmarks);
+ }
}
*has_side_effects |= instr->HasObservableSideEffects();
if (position != RelocInfo::kNoPosition) instr->set_position(position);
@@ -6365,11 +7133,13 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
set_current_block(if_true);
HInstruction* access;
+ HCheckMaps* checkmaps = NULL;
if (IsFastElementsKind(elements_kind)) {
if (is_store && !IsFastDoubleElementsKind(elements_kind)) {
- AddInstruction(new(zone()) HCheckMaps(
+ checkmaps = new(zone()) HCheckMaps(
elements, isolate()->factory()->fixed_array_map(),
- zone(), elements_kind_branch));
+ zone(), elements_kind_branch);
+ AddInstruction(checkmaps);
}
// TODO(jkummerow): The need for these two blocks could be avoided
// in one of two ways:
@@ -6393,9 +7163,12 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
HType::Smi()));
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length,
ALLOW_SMI_KEY));
- access = AddInstruction(BuildFastElementAccess(
+ ArrayInstruction* array_instruction = BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
- elements_kind, is_store));
+ elements_kind, is_store);
+ array_instruction->set_hoistable(false);
+
+ access = AddInstruction(array_instruction);
if (!is_store) {
Push(access);
}
@@ -6410,9 +7183,11 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements));
checked_key = AddInstruction(new(zone()) HBoundsCheck(key, length,
ALLOW_SMI_KEY));
- access = AddInstruction(BuildFastElementAccess(
+ array_instruction = BuildFastElementAccess(
elements, checked_key, val, elements_kind_branch,
- elements_kind, is_store));
+ elements_kind, is_store);
+ array_instruction->set_hoistable(false);
+ access = AddInstruction(array_instruction);
} else if (elements_kind == DICTIONARY_ELEMENTS) {
if (is_store) {
access = AddInstruction(BuildStoreKeyedGeneric(object, key, val));
@@ -6429,6 +7204,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object,
if (!is_store) {
Push(access);
}
+
current_block()->Goto(join);
set_current_block(if_false);
}
@@ -7546,6 +8322,10 @@ bool HGraphBuilder::TryCallApply(Call* expr) {
// transitions of each other. Returns either NULL if they are not from the same
// family, or a Map* indicating the map with the first elements kind of the
// family that is in the list.
+
+//
+// TODO(mvstanton) - can I use this function?
+//
static Map* CheckSameElementsFamily(SmallMapList* types) {
if (types->length() <= 1) return NULL;
// Check if all maps belong to the same transition family.
@@ -9775,6 +10555,10 @@ void HEnvironment::PrintToStd() {
}
+
+
+
+
void HTracer::TraceCompilation(FunctionLiteral* function) {
Tag tag(this, "compilation");
Handle<String> name = function->debug_name();
« no previous file with comments | « src/hydrogen.h ('k') | src/hydrogen-instructions.h » ('j') | src/hydrogen-instructions.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698