| // Copyright 2013 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/hydrogen.h" |
| #include "src/hydrogen-gvn.h" |
| #include "src/v8.h" |
| |
| namespace v8 { |
| namespace internal { |
| |
| class HInstructionMap FINAL : public ZoneObject { |
| public: |
| HInstructionMap(Zone* zone, SideEffectsTracker* side_effects_tracker) |
| : array_size_(0), |
| lists_size_(0), |
| count_(0), |
| array_(NULL), |
| lists_(NULL), |
| free_list_head_(kNil), |
| side_effects_tracker_(side_effects_tracker) { |
| ResizeLists(kInitialSize, zone); |
| Resize(kInitialSize, zone); |
| } |
| |
| void Kill(SideEffects side_effects); |
| |
| void Add(HInstruction* instr, Zone* zone) { |
| present_depends_on_.Add(side_effects_tracker_->ComputeDependsOn(instr)); |
| Insert(instr, zone); |
| } |
| |
| HInstruction* Lookup(HInstruction* instr) const; |
| |
| HInstructionMap* Copy(Zone* zone) const { |
| return new(zone) HInstructionMap(zone, this); |
| } |
| |
| bool IsEmpty() const { return count_ == 0; } |
| |
| private: |
| // A linked list of HInstruction* values. Stored in arrays. |
| struct HInstructionMapListElement { |
| HInstruction* instr; |
| int next; // Index in the array of the next list element. |
| }; |
| static const int kNil = -1; // The end of a linked list |
| |
| // Must be a power of 2. |
| static const int kInitialSize = 16; |
| |
| HInstructionMap(Zone* zone, const HInstructionMap* other); |
| |
| void Resize(int new_size, Zone* zone); |
| void ResizeLists(int new_size, Zone* zone); |
| void Insert(HInstruction* instr, Zone* zone); |
| uint32_t Bound(uint32_t value) const { return value & (array_size_ - 1); } |
| |
| int array_size_; |
| int lists_size_; |
| int count_; // The number of values stored in the HInstructionMap. |
| SideEffects present_depends_on_; |
| HInstructionMapListElement* array_; |
| // Primary store - contains the first value |
| // with a given hash. Colliding elements are stored in linked lists. |
| HInstructionMapListElement* lists_; |
| // The linked lists containing hash collisions. |
| int free_list_head_; // Unused elements in lists_ are on the free list. |
| SideEffectsTracker* side_effects_tracker_; |
| }; |
| |
| |
| class HSideEffectMap FINAL BASE_EMBEDDED { |
| public: |
| HSideEffectMap(); |
| explicit HSideEffectMap(HSideEffectMap* other); |
| HSideEffectMap& operator= (const HSideEffectMap& other); |
| |
| void Kill(SideEffects side_effects); |
| |
| void Store(SideEffects side_effects, HInstruction* instr); |
| |
| bool IsEmpty() const { return count_ == 0; } |
| |
| inline HInstruction* operator[](int i) const { |
| DCHECK(0 <= i); |
| DCHECK(i < kNumberOfTrackedSideEffects); |
| return data_[i]; |
| } |
| inline HInstruction* at(int i) const { return operator[](i); } |
| |
| private: |
| int count_; |
| HInstruction* data_[kNumberOfTrackedSideEffects]; |
| }; |
| |
| |
| void TraceGVN(const char* msg, ...) { |
| va_list arguments; |
| va_start(arguments, msg); |
| base::OS::VPrint(msg, arguments); |
| va_end(arguments); |
| } |
| |
| |
| // Wrap TraceGVN in macros to avoid the expense of evaluating its arguments when |
| // --trace-gvn is off. |
| #define TRACE_GVN_1(msg, a1) \ |
| if (FLAG_trace_gvn) { \ |
| TraceGVN(msg, a1); \ |
| } |
| |
| #define TRACE_GVN_2(msg, a1, a2) \ |
| if (FLAG_trace_gvn) { \ |
| TraceGVN(msg, a1, a2); \ |
| } |
| |
| #define TRACE_GVN_3(msg, a1, a2, a3) \ |
| if (FLAG_trace_gvn) { \ |
| TraceGVN(msg, a1, a2, a3); \ |
| } |
| |
| #define TRACE_GVN_4(msg, a1, a2, a3, a4) \ |
| if (FLAG_trace_gvn) { \ |
| TraceGVN(msg, a1, a2, a3, a4); \ |
| } |
| |
| #define TRACE_GVN_5(msg, a1, a2, a3, a4, a5) \ |
| if (FLAG_trace_gvn) { \ |
| TraceGVN(msg, a1, a2, a3, a4, a5); \ |
| } |
| |
| |
| HInstructionMap::HInstructionMap(Zone* zone, const HInstructionMap* other) |
| : array_size_(other->array_size_), |
| lists_size_(other->lists_size_), |
| count_(other->count_), |
| present_depends_on_(other->present_depends_on_), |
| array_(zone->NewArray<HInstructionMapListElement>(other->array_size_)), |
| lists_(zone->NewArray<HInstructionMapListElement>(other->lists_size_)), |
| free_list_head_(other->free_list_head_), |
| side_effects_tracker_(other->side_effects_tracker_) { |
| MemCopy(array_, other->array_, |
| array_size_ * sizeof(HInstructionMapListElement)); |
| MemCopy(lists_, other->lists_, |
| lists_size_ * sizeof(HInstructionMapListElement)); |
| } |
| |
| |
| void HInstructionMap::Kill(SideEffects changes) { |
| if (!present_depends_on_.ContainsAnyOf(changes)) return; |
| present_depends_on_.RemoveAll(); |
| for (int i = 0; i < array_size_; ++i) { |
| HInstruction* instr = array_[i].instr; |
| if (instr != NULL) { |
| // Clear list of collisions first, so we know if it becomes empty. |
| int kept = kNil; // List of kept elements. |
| int next; |
| for (int current = array_[i].next; current != kNil; current = next) { |
| next = lists_[current].next; |
| HInstruction* instr = lists_[current].instr; |
| SideEffects depends_on = side_effects_tracker_->ComputeDependsOn(instr); |
| if (depends_on.ContainsAnyOf(changes)) { |
| // Drop it. |
| count_--; |
| lists_[current].next = free_list_head_; |
| free_list_head_ = current; |
| } else { |
| // Keep it. |
| lists_[current].next = kept; |
| kept = current; |
| present_depends_on_.Add(depends_on); |
| } |
| } |
| array_[i].next = kept; |
| |
| // Now possibly drop directly indexed element. |
| instr = array_[i].instr; |
| SideEffects depends_on = side_effects_tracker_->ComputeDependsOn(instr); |
| if (depends_on.ContainsAnyOf(changes)) { // Drop it. |
| count_--; |
| int head = array_[i].next; |
| if (head == kNil) { |
| array_[i].instr = NULL; |
| } else { |
| array_[i].instr = lists_[head].instr; |
| array_[i].next = lists_[head].next; |
| lists_[head].next = free_list_head_; |
| free_list_head_ = head; |
| } |
| } else { |
| present_depends_on_.Add(depends_on); // Keep it. |
| } |
| } |
| } |
| } |
| |
| |
| HInstruction* HInstructionMap::Lookup(HInstruction* instr) const { |
| uint32_t hash = static_cast<uint32_t>(instr->Hashcode()); |
| uint32_t pos = Bound(hash); |
| if (array_[pos].instr != NULL) { |
| if (array_[pos].instr->Equals(instr)) return array_[pos].instr; |
| int next = array_[pos].next; |
| while (next != kNil) { |
| if (lists_[next].instr->Equals(instr)) return lists_[next].instr; |
| next = lists_[next].next; |
| } |
| } |
| return NULL; |
| } |
| |
| |
| void HInstructionMap::Resize(int new_size, Zone* zone) { |
| DCHECK(new_size > count_); |
| // Hashing the values into the new array has no more collisions than in the |
| // old hash map, so we can use the existing lists_ array, if we are careful. |
| |
| // Make sure we have at least one free element. |
| if (free_list_head_ == kNil) { |
| ResizeLists(lists_size_ << 1, zone); |
| } |
| |
| HInstructionMapListElement* new_array = |
| zone->NewArray<HInstructionMapListElement>(new_size); |
| memset(new_array, 0, sizeof(HInstructionMapListElement) * new_size); |
| |
| HInstructionMapListElement* old_array = array_; |
| int old_size = array_size_; |
| |
| int old_count = count_; |
| count_ = 0; |
| // Do not modify present_depends_on_. It is currently correct. |
| array_size_ = new_size; |
| array_ = new_array; |
| |
| if (old_array != NULL) { |
| // Iterate over all the elements in lists, rehashing them. |
| for (int i = 0; i < old_size; ++i) { |
| if (old_array[i].instr != NULL) { |
| int current = old_array[i].next; |
| while (current != kNil) { |
| Insert(lists_[current].instr, zone); |
| int next = lists_[current].next; |
| lists_[current].next = free_list_head_; |
| free_list_head_ = current; |
| current = next; |
| } |
| // Rehash the directly stored instruction. |
| Insert(old_array[i].instr, zone); |
| } |
| } |
| } |
| USE(old_count); |
| DCHECK(count_ == old_count); |
| } |
| |
| |
| void HInstructionMap::ResizeLists(int new_size, Zone* zone) { |
| DCHECK(new_size > lists_size_); |
| |
| HInstructionMapListElement* new_lists = |
| zone->NewArray<HInstructionMapListElement>(new_size); |
| memset(new_lists, 0, sizeof(HInstructionMapListElement) * new_size); |
| |
| HInstructionMapListElement* old_lists = lists_; |
| int old_size = lists_size_; |
| |
| lists_size_ = new_size; |
| lists_ = new_lists; |
| |
| if (old_lists != NULL) { |
| MemCopy(lists_, old_lists, old_size * sizeof(HInstructionMapListElement)); |
| } |
| for (int i = old_size; i < lists_size_; ++i) { |
| lists_[i].next = free_list_head_; |
| free_list_head_ = i; |
| } |
| } |
| |
| |
| void HInstructionMap::Insert(HInstruction* instr, Zone* zone) { |
| DCHECK(instr != NULL); |
| // Resizing when half of the hashtable is filled up. |
| if (count_ >= array_size_ >> 1) Resize(array_size_ << 1, zone); |
| DCHECK(count_ < array_size_); |
| count_++; |
| uint32_t pos = Bound(static_cast<uint32_t>(instr->Hashcode())); |
| if (array_[pos].instr == NULL) { |
| array_[pos].instr = instr; |
| array_[pos].next = kNil; |
| } else { |
| if (free_list_head_ == kNil) { |
| ResizeLists(lists_size_ << 1, zone); |
| } |
| int new_element_pos = free_list_head_; |
| DCHECK(new_element_pos != kNil); |
| free_list_head_ = lists_[free_list_head_].next; |
| lists_[new_element_pos].instr = instr; |
| lists_[new_element_pos].next = array_[pos].next; |
| DCHECK(array_[pos].next == kNil || lists_[array_[pos].next].instr != NULL); |
| array_[pos].next = new_element_pos; |
| } |
| } |
| |
| |
| HSideEffectMap::HSideEffectMap() : count_(0) { |
| memset(data_, 0, kNumberOfTrackedSideEffects * kPointerSize); |
| } |
| |
| |
| HSideEffectMap::HSideEffectMap(HSideEffectMap* other) : count_(other->count_) { |
| *this = *other; // Calls operator=. |
| } |
| |
| |
| HSideEffectMap& HSideEffectMap::operator=(const HSideEffectMap& other) { |
| if (this != &other) { |
| MemCopy(data_, other.data_, kNumberOfTrackedSideEffects * kPointerSize); |
| } |
| return *this; |
| } |
| |
| |
| void HSideEffectMap::Kill(SideEffects side_effects) { |
| for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { |
| if (side_effects.ContainsFlag(GVNFlagFromInt(i))) { |
| if (data_[i] != NULL) count_--; |
| data_[i] = NULL; |
| } |
| } |
| } |
| |
| |
| void HSideEffectMap::Store(SideEffects side_effects, HInstruction* instr) { |
| for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { |
| if (side_effects.ContainsFlag(GVNFlagFromInt(i))) { |
| if (data_[i] == NULL) count_++; |
| data_[i] = instr; |
| } |
| } |
| } |
| |
| |
| SideEffects SideEffectsTracker::ComputeChanges(HInstruction* instr) { |
| int index; |
| SideEffects result(instr->ChangesFlags()); |
| if (result.ContainsFlag(kGlobalVars)) { |
| if (instr->IsStoreGlobalCell() && |
| ComputeGlobalVar(HStoreGlobalCell::cast(instr)->cell(), &index)) { |
| result.RemoveFlag(kGlobalVars); |
| result.AddSpecial(GlobalVar(index)); |
| } else { |
| for (index = 0; index < kNumberOfGlobalVars; ++index) { |
| result.AddSpecial(GlobalVar(index)); |
| } |
| } |
| } |
| if (result.ContainsFlag(kInobjectFields)) { |
| if (instr->IsStoreNamedField() && |
| ComputeInobjectField(HStoreNamedField::cast(instr)->access(), &index)) { |
| result.RemoveFlag(kInobjectFields); |
| result.AddSpecial(InobjectField(index)); |
| } else { |
| for (index = 0; index < kNumberOfInobjectFields; ++index) { |
| result.AddSpecial(InobjectField(index)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| SideEffects SideEffectsTracker::ComputeDependsOn(HInstruction* instr) { |
| int index; |
| SideEffects result(instr->DependsOnFlags()); |
| if (result.ContainsFlag(kGlobalVars)) { |
| if (instr->IsLoadGlobalCell() && |
| ComputeGlobalVar(HLoadGlobalCell::cast(instr)->cell(), &index)) { |
| result.RemoveFlag(kGlobalVars); |
| result.AddSpecial(GlobalVar(index)); |
| } else { |
| for (index = 0; index < kNumberOfGlobalVars; ++index) { |
| result.AddSpecial(GlobalVar(index)); |
| } |
| } |
| } |
| if (result.ContainsFlag(kInobjectFields)) { |
| if (instr->IsLoadNamedField() && |
| ComputeInobjectField(HLoadNamedField::cast(instr)->access(), &index)) { |
| result.RemoveFlag(kInobjectFields); |
| result.AddSpecial(InobjectField(index)); |
| } else { |
| for (index = 0; index < kNumberOfInobjectFields; ++index) { |
| result.AddSpecial(InobjectField(index)); |
| } |
| } |
| } |
| return result; |
| } |
| |
| |
| std::ostream& operator<<(std::ostream& os, const TrackedEffects& te) { |
| SideEffectsTracker* t = te.tracker; |
| const char* separator = ""; |
| os << "["; |
| for (int bit = 0; bit < kNumberOfFlags; ++bit) { |
| GVNFlag flag = GVNFlagFromInt(bit); |
| if (te.effects.ContainsFlag(flag)) { |
| os << separator; |
| separator = ", "; |
| switch (flag) { |
| #define DECLARE_FLAG(Type) \ |
| case k##Type: \ |
| os << #Type; \ |
| break; |
| GVN_TRACKED_FLAG_LIST(DECLARE_FLAG) |
| GVN_UNTRACKED_FLAG_LIST(DECLARE_FLAG) |
| #undef DECLARE_FLAG |
| default: |
| break; |
| } |
| } |
| } |
| for (int index = 0; index < t->num_global_vars_; ++index) { |
| if (te.effects.ContainsSpecial(t->GlobalVar(index))) { |
| os << separator << "[" << *t->global_vars_[index].handle() << "]"; |
| separator = ", "; |
| } |
| } |
| for (int index = 0; index < t->num_inobject_fields_; ++index) { |
| if (te.effects.ContainsSpecial(t->InobjectField(index))) { |
| os << separator << t->inobject_fields_[index]; |
| separator = ", "; |
| } |
| } |
| os << "]"; |
| return os; |
| } |
| |
| |
| bool SideEffectsTracker::ComputeGlobalVar(Unique<Cell> cell, int* index) { |
| for (int i = 0; i < num_global_vars_; ++i) { |
| if (cell == global_vars_[i]) { |
| *index = i; |
| return true; |
| } |
| } |
| if (num_global_vars_ < kNumberOfGlobalVars) { |
| if (FLAG_trace_gvn) { |
| OFStream os(stdout); |
| os << "Tracking global var [" << *cell.handle() << "] " |
| << "(mapped to index " << num_global_vars_ << ")" << std::endl; |
| } |
| *index = num_global_vars_; |
| global_vars_[num_global_vars_++] = cell; |
| return true; |
| } |
| return false; |
| } |
| |
| |
| bool SideEffectsTracker::ComputeInobjectField(HObjectAccess access, |
| int* index) { |
| for (int i = 0; i < num_inobject_fields_; ++i) { |
| if (access.Equals(inobject_fields_[i])) { |
| *index = i; |
| return true; |
| } |
| } |
| if (num_inobject_fields_ < kNumberOfInobjectFields) { |
| if (FLAG_trace_gvn) { |
| OFStream os(stdout); |
| os << "Tracking inobject field access " << access << " (mapped to index " |
| << num_inobject_fields_ << ")" << std::endl; |
| } |
| *index = num_inobject_fields_; |
| inobject_fields_[num_inobject_fields_++] = access; |
| return true; |
| } |
| return false; |
| } |
| |
| |
| HGlobalValueNumberingPhase::HGlobalValueNumberingPhase(HGraph* graph) |
| : HPhase("H_Global value numbering", graph), |
| removed_side_effects_(false), |
| block_side_effects_(graph->blocks()->length(), zone()), |
| loop_side_effects_(graph->blocks()->length(), zone()), |
| visited_on_paths_(graph->blocks()->length(), zone()) { |
| DCHECK(!AllowHandleAllocation::IsAllowed()); |
| block_side_effects_.AddBlock( |
| SideEffects(), graph->blocks()->length(), zone()); |
| loop_side_effects_.AddBlock( |
| SideEffects(), graph->blocks()->length(), zone()); |
| } |
| |
| |
| void HGlobalValueNumberingPhase::Run() { |
| DCHECK(!removed_side_effects_); |
| for (int i = FLAG_gvn_iterations; i > 0; --i) { |
| // Compute the side effects. |
| ComputeBlockSideEffects(); |
| |
| // Perform loop invariant code motion if requested. |
| if (FLAG_loop_invariant_code_motion) LoopInvariantCodeMotion(); |
| |
| // Perform the actual value numbering. |
| AnalyzeGraph(); |
| |
| // Continue GVN if we removed any side effects. |
| if (!removed_side_effects_) break; |
| removed_side_effects_ = false; |
| |
| // Clear all side effects. |
| DCHECK_EQ(block_side_effects_.length(), graph()->blocks()->length()); |
| DCHECK_EQ(loop_side_effects_.length(), graph()->blocks()->length()); |
| for (int i = 0; i < graph()->blocks()->length(); ++i) { |
| block_side_effects_[i].RemoveAll(); |
| loop_side_effects_[i].RemoveAll(); |
| } |
| visited_on_paths_.Clear(); |
| } |
| } |
| |
| |
| void HGlobalValueNumberingPhase::ComputeBlockSideEffects() { |
| for (int i = graph()->blocks()->length() - 1; i >= 0; --i) { |
| // Compute side effects for the block. |
| HBasicBlock* block = graph()->blocks()->at(i); |
| SideEffects side_effects; |
| if (block->IsReachable() && !block->IsDeoptimizing()) { |
| int id = block->block_id(); |
| for (HInstructionIterator it(block); !it.Done(); it.Advance()) { |
| HInstruction* instr = it.Current(); |
| side_effects.Add(side_effects_tracker_.ComputeChanges(instr)); |
| } |
| block_side_effects_[id].Add(side_effects); |
| |
| // Loop headers are part of their loop. |
| if (block->IsLoopHeader()) { |
| loop_side_effects_[id].Add(side_effects); |
| } |
| |
| // Propagate loop side effects upwards. |
| if (block->HasParentLoopHeader()) { |
| HBasicBlock* with_parent = block; |
| if (block->IsLoopHeader()) side_effects = loop_side_effects_[id]; |
| do { |
| HBasicBlock* parent_block = with_parent->parent_loop_header(); |
| loop_side_effects_[parent_block->block_id()].Add(side_effects); |
| with_parent = parent_block; |
| } while (with_parent->HasParentLoopHeader()); |
| } |
| } |
| } |
| } |
| |
| |
| void HGlobalValueNumberingPhase::LoopInvariantCodeMotion() { |
| TRACE_GVN_1("Using optimistic loop invariant code motion: %s\n", |
| graph()->use_optimistic_licm() ? "yes" : "no"); |
| for (int i = graph()->blocks()->length() - 1; i >= 0; --i) { |
| HBasicBlock* block = graph()->blocks()->at(i); |
| if (block->IsLoopHeader()) { |
| SideEffects side_effects = loop_side_effects_[block->block_id()]; |
| if (FLAG_trace_gvn) { |
| OFStream os(stdout); |
| os << "Try loop invariant motion for " << *block << " changes " |
| << Print(side_effects) << std::endl; |
| } |
| HBasicBlock* last = block->loop_information()->GetLastBackEdge(); |
| for (int j = block->block_id(); j <= last->block_id(); ++j) { |
| ProcessLoopBlock(graph()->blocks()->at(j), block, side_effects); |
| } |
| } |
| } |
| } |
| |
| |
| void HGlobalValueNumberingPhase::ProcessLoopBlock( |
| HBasicBlock* block, |
| HBasicBlock* loop_header, |
| SideEffects loop_kills) { |
| HBasicBlock* pre_header = loop_header->predecessors()->at(0); |
| if (FLAG_trace_gvn) { |
| OFStream os(stdout); |
| os << "Loop invariant code motion for " << *block << " depends on " |
| << Print(loop_kills) << std::endl; |
| } |
| HInstruction* instr = block->first(); |
| while (instr != NULL) { |
| HInstruction* next = instr->next(); |
| if (instr->CheckFlag(HValue::kUseGVN)) { |
| SideEffects changes = side_effects_tracker_.ComputeChanges(instr); |
| SideEffects depends_on = side_effects_tracker_.ComputeDependsOn(instr); |
| if (FLAG_trace_gvn) { |
| OFStream os(stdout); |
| os << "Checking instruction i" << instr->id() << " (" |
| << instr->Mnemonic() << ") changes " << Print(changes) |
| << ", depends on " << Print(depends_on) << ". Loop changes " |
| << Print(loop_kills) << std::endl; |
| } |
| bool can_hoist = !depends_on.ContainsAnyOf(loop_kills); |
| if (can_hoist && !graph()->use_optimistic_licm()) { |
| can_hoist = block->IsLoopSuccessorDominator(); |
| } |
| |
| if (can_hoist) { |
| bool inputs_loop_invariant = true; |
| for (int i = 0; i < instr->OperandCount(); ++i) { |
| if (instr->OperandAt(i)->IsDefinedAfter(pre_header)) { |
| inputs_loop_invariant = false; |
| } |
| } |
| |
| if (inputs_loop_invariant && ShouldMove(instr, loop_header)) { |
| TRACE_GVN_2("Hoisting loop invariant instruction i%d to block B%d\n", |
| instr->id(), pre_header->block_id()); |
| // Move the instruction out of the loop. |
| instr->Unlink(); |
| instr->InsertBefore(pre_header->end()); |
| if (instr->HasSideEffects()) removed_side_effects_ = true; |
| } |
| } |
| } |
| instr = next; |
| } |
| } |
| |
| |
| bool HGlobalValueNumberingPhase::AllowCodeMotion() { |
| return info()->IsStub() || info()->opt_count() + 1 < FLAG_max_opt_count; |
| } |
| |
| |
| bool HGlobalValueNumberingPhase::ShouldMove(HInstruction* instr, |
| HBasicBlock* loop_header) { |
| // If we've disabled code motion or we're in a block that unconditionally |
| // deoptimizes, don't move any instructions. |
| return AllowCodeMotion() && !instr->block()->IsDeoptimizing() && |
| instr->block()->IsReachable(); |
| } |
| |
| |
| SideEffects |
| HGlobalValueNumberingPhase::CollectSideEffectsOnPathsToDominatedBlock( |
| HBasicBlock* dominator, HBasicBlock* dominated) { |
| SideEffects side_effects; |
| for (int i = 0; i < dominated->predecessors()->length(); ++i) { |
| HBasicBlock* block = dominated->predecessors()->at(i); |
| if (dominator->block_id() < block->block_id() && |
| block->block_id() < dominated->block_id() && |
| !visited_on_paths_.Contains(block->block_id())) { |
| visited_on_paths_.Add(block->block_id()); |
| side_effects.Add(block_side_effects_[block->block_id()]); |
| if (block->IsLoopHeader()) { |
| side_effects.Add(loop_side_effects_[block->block_id()]); |
| } |
| side_effects.Add(CollectSideEffectsOnPathsToDominatedBlock( |
| dominator, block)); |
| } |
| } |
| return side_effects; |
| } |
| |
| |
| // Each instance of this class is like a "stack frame" for the recursive |
| // traversal of the dominator tree done during GVN (the stack is handled |
| // as a double linked list). |
| // We reuse frames when possible so the list length is limited by the depth |
| // of the dominator tree but this forces us to initialize each frame calling |
| // an explicit "Initialize" method instead of a using constructor. |
| class GvnBasicBlockState: public ZoneObject { |
| public: |
| static GvnBasicBlockState* CreateEntry(Zone* zone, |
| HBasicBlock* entry_block, |
| HInstructionMap* entry_map) { |
| return new(zone) |
| GvnBasicBlockState(NULL, entry_block, entry_map, NULL, zone); |
| } |
| |
| HBasicBlock* block() { return block_; } |
| HInstructionMap* map() { return map_; } |
| HSideEffectMap* dominators() { return &dominators_; } |
| |
| GvnBasicBlockState* next_in_dominator_tree_traversal( |
| Zone* zone, |
| HBasicBlock** dominator) { |
| // This assignment needs to happen before calling next_dominated() because |
| // that call can reuse "this" if we are at the last dominated block. |
| *dominator = block(); |
| GvnBasicBlockState* result = next_dominated(zone); |
| if (result == NULL) { |
| GvnBasicBlockState* dominator_state = pop(); |
| if (dominator_state != NULL) { |
| // This branch is guaranteed not to return NULL because pop() never |
| // returns a state where "is_done() == true". |
| *dominator = dominator_state->block(); |
| result = dominator_state->next_dominated(zone); |
| } else { |
| // Unnecessary (we are returning NULL) but done for cleanness. |
| *dominator = NULL; |
| } |
| } |
| return result; |
| } |
| |
| private: |
| void Initialize(HBasicBlock* block, |
| HInstructionMap* map, |
| HSideEffectMap* dominators, |
| bool copy_map, |
| Zone* zone) { |
| block_ = block; |
| map_ = copy_map ? map->Copy(zone) : map; |
| dominated_index_ = -1; |
| length_ = block->dominated_blocks()->length(); |
| if (dominators != NULL) { |
| dominators_ = *dominators; |
| } |
| } |
| bool is_done() { return dominated_index_ >= length_; } |
| |
| GvnBasicBlockState(GvnBasicBlockState* previous, |
| HBasicBlock* block, |
| HInstructionMap* map, |
| HSideEffectMap* dominators, |
| Zone* zone) |
| : previous_(previous), next_(NULL) { |
| Initialize(block, map, dominators, true, zone); |
| } |
| |
| GvnBasicBlockState* next_dominated(Zone* zone) { |
| dominated_index_++; |
| if (dominated_index_ == length_ - 1) { |
| // No need to copy the map for the last child in the dominator tree. |
| Initialize(block_->dominated_blocks()->at(dominated_index_), |
| map(), |
| dominators(), |
| false, |
| zone); |
| return this; |
| } else if (dominated_index_ < length_) { |
| return push(zone, block_->dominated_blocks()->at(dominated_index_)); |
| } else { |
| return NULL; |
| } |
| } |
| |
| GvnBasicBlockState* push(Zone* zone, HBasicBlock* block) { |
| if (next_ == NULL) { |
| next_ = |
| new(zone) GvnBasicBlockState(this, block, map(), dominators(), zone); |
| } else { |
| next_->Initialize(block, map(), dominators(), true, zone); |
| } |
| return next_; |
| } |
| GvnBasicBlockState* pop() { |
| GvnBasicBlockState* result = previous_; |
| while (result != NULL && result->is_done()) { |
| TRACE_GVN_2("Backtracking from block B%d to block b%d\n", |
| block()->block_id(), |
| previous_->block()->block_id()) |
| result = result->previous_; |
| } |
| return result; |
| } |
| |
| GvnBasicBlockState* previous_; |
| GvnBasicBlockState* next_; |
| HBasicBlock* block_; |
| HInstructionMap* map_; |
| HSideEffectMap dominators_; |
| int dominated_index_; |
| int length_; |
| }; |
| |
| |
| // This is a recursive traversal of the dominator tree but it has been turned |
| // into a loop to avoid stack overflows. |
| // The logical "stack frames" of the recursion are kept in a list of |
| // GvnBasicBlockState instances. |
| void HGlobalValueNumberingPhase::AnalyzeGraph() { |
| HBasicBlock* entry_block = graph()->entry_block(); |
| HInstructionMap* entry_map = |
| new(zone()) HInstructionMap(zone(), &side_effects_tracker_); |
| GvnBasicBlockState* current = |
| GvnBasicBlockState::CreateEntry(zone(), entry_block, entry_map); |
| |
| while (current != NULL) { |
| HBasicBlock* block = current->block(); |
| HInstructionMap* map = current->map(); |
| HSideEffectMap* dominators = current->dominators(); |
| |
| TRACE_GVN_2("Analyzing block B%d%s\n", |
| block->block_id(), |
| block->IsLoopHeader() ? " (loop header)" : ""); |
| |
| // If this is a loop header kill everything killed by the loop. |
| if (block->IsLoopHeader()) { |
| map->Kill(loop_side_effects_[block->block_id()]); |
| dominators->Kill(loop_side_effects_[block->block_id()]); |
| } |
| |
| // Go through all instructions of the current block. |
| for (HInstructionIterator it(block); !it.Done(); it.Advance()) { |
| HInstruction* instr = it.Current(); |
| if (instr->CheckFlag(HValue::kTrackSideEffectDominators)) { |
| for (int i = 0; i < kNumberOfTrackedSideEffects; i++) { |
| HValue* other = dominators->at(i); |
| GVNFlag flag = GVNFlagFromInt(i); |
| if (instr->DependsOnFlags().Contains(flag) && other != NULL) { |
| TRACE_GVN_5("Side-effect #%d in %d (%s) is dominated by %d (%s)\n", |
| i, |
| instr->id(), |
| instr->Mnemonic(), |
| other->id(), |
| other->Mnemonic()); |
| if (instr->HandleSideEffectDominator(flag, other)) { |
| removed_side_effects_ = true; |
| } |
| } |
| } |
| } |
| // Instruction was unlinked during graph traversal. |
| if (!instr->IsLinked()) continue; |
| |
| SideEffects changes = side_effects_tracker_.ComputeChanges(instr); |
| if (!changes.IsEmpty()) { |
| // Clear all instructions in the map that are affected by side effects. |
| // Store instruction as the dominating one for tracked side effects. |
| map->Kill(changes); |
| dominators->Store(changes, instr); |
| if (FLAG_trace_gvn) { |
| OFStream os(stdout); |
| os << "Instruction i" << instr->id() << " changes " << Print(changes) |
| << std::endl; |
| } |
| } |
| if (instr->CheckFlag(HValue::kUseGVN) && |
| !instr->CheckFlag(HValue::kCantBeReplaced)) { |
| DCHECK(!instr->HasObservableSideEffects()); |
| HInstruction* other = map->Lookup(instr); |
| if (other != NULL) { |
| DCHECK(instr->Equals(other) && other->Equals(instr)); |
| TRACE_GVN_4("Replacing instruction i%d (%s) with i%d (%s)\n", |
| instr->id(), |
| instr->Mnemonic(), |
| other->id(), |
| other->Mnemonic()); |
| if (instr->HasSideEffects()) removed_side_effects_ = true; |
| instr->DeleteAndReplaceWith(other); |
| } else { |
| map->Add(instr, zone()); |
| } |
| } |
| } |
| |
| HBasicBlock* dominator_block; |
| GvnBasicBlockState* next = |
| current->next_in_dominator_tree_traversal(zone(), |
| &dominator_block); |
| |
| if (next != NULL) { |
| HBasicBlock* dominated = next->block(); |
| HInstructionMap* successor_map = next->map(); |
| HSideEffectMap* successor_dominators = next->dominators(); |
| |
| // Kill everything killed on any path between this block and the |
| // dominated block. We don't have to traverse these paths if the |
| // value map and the dominators list is already empty. If the range |
| // of block ids (block_id, dominated_id) is empty there are no such |
| // paths. |
| if ((!successor_map->IsEmpty() || !successor_dominators->IsEmpty()) && |
| dominator_block->block_id() + 1 < dominated->block_id()) { |
| visited_on_paths_.Clear(); |
| SideEffects side_effects_on_all_paths = |
| CollectSideEffectsOnPathsToDominatedBlock(dominator_block, |
| dominated); |
| successor_map->Kill(side_effects_on_all_paths); |
| successor_dominators->Kill(side_effects_on_all_paths); |
| } |
| } |
| current = next; |
| } |
| } |
| |
| } } // namespace v8::internal |