// 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/crankshaft/hydrogen-gvn.h" #include "src/crankshaft/hydrogen.h" #include "src/objects-inl.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->IsStoreNamedField()) { HStoreNamedField* store = HStoreNamedField::cast(instr); HConstant* target = HConstant::cast(store->object()); if (ComputeGlobalVar(Unique<PropertyCell>::cast(target->GetUnique()), &index)) { result.RemoveFlag(kGlobalVars); result.AddSpecial(GlobalVar(index)); return result; } } for (index = 0; index < kNumberOfGlobalVars; ++index) { result.AddSpecial(GlobalVar(index)); } } else 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->IsLoadNamedField()) { HLoadNamedField* load = HLoadNamedField::cast(instr); HConstant* target = HConstant::cast(load->object()); if (ComputeGlobalVar(Unique<PropertyCell>::cast(target->GetUnique()), &index)) { result.RemoveFlag(kGlobalVars); result.AddSpecial(GlobalVar(index)); return result; } } for (index = 0; index < kNumberOfGlobalVars; ++index) { result.AddSpecial(GlobalVar(index)); } } else 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<PropertyCell> 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::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 graph()->allow_code_motion() && !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 internal } // namespace v8