// Copyright 2014 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/base/adapters.h" #include "src/compiler/js-graph.h" #include "src/compiler/liveness-analyzer.h" #include "src/compiler/node.h" #include "src/compiler/node-matchers.h" #include "src/compiler/state-values-utils.h" namespace v8 { namespace internal { namespace compiler { LivenessAnalyzer::LivenessAnalyzer(size_t local_count, bool has_accumulator, Zone* zone) : zone_(zone), blocks_(zone), local_count_(local_count), has_accumulator_(has_accumulator), queue_(zone) {} void LivenessAnalyzer::Print(std::ostream& os) { for (auto block : blocks_) { block->Print(os); os << std::endl; } } LivenessAnalyzerBlock* LivenessAnalyzer::NewBlock() { LivenessAnalyzerBlock* result = new (zone()->New(sizeof(LivenessAnalyzerBlock))) LivenessAnalyzerBlock( blocks_.size(), local_count_, has_accumulator_, zone()); blocks_.push_back(result); return result; } LivenessAnalyzerBlock* LivenessAnalyzer::NewBlock( LivenessAnalyzerBlock* predecessor) { LivenessAnalyzerBlock* result = NewBlock(); result->AddPredecessor(predecessor); return result; } void LivenessAnalyzer::Queue(LivenessAnalyzerBlock* block) { if (!block->IsQueued()) { block->SetQueued(); queue_.push(block); } } void LivenessAnalyzer::Run(NonLiveFrameStateSlotReplacer* replacer) { if (local_count_ == 0 && !has_accumulator_) { // No variables => nothing to do. return; } // Put all blocks into the queue. DCHECK(queue_.empty()); for (auto block : blocks_) { Queue(block); } // Compute the fix-point. BitVector working_area( static_cast(local_count_) + (has_accumulator_ ? 1 : 0), zone_); while (!queue_.empty()) { LivenessAnalyzerBlock* block = queue_.front(); queue_.pop(); block->Process(&working_area, nullptr); for (auto i = block->pred_begin(); i != block->pred_end(); i++) { if ((*i)->UpdateLive(&working_area)) { Queue(*i); } } } // Update the frame states according to the liveness. for (auto block : blocks_) { block->Process(&working_area, replacer); } } LivenessAnalyzerBlock::LivenessAnalyzerBlock(size_t id, size_t local_count, bool has_accumulator, Zone* zone) : entries_(zone), predecessors_(zone), live_(static_cast(local_count) + (has_accumulator ? 1 : 0), zone), queued_(false), has_accumulator_(has_accumulator), id_(id) {} void LivenessAnalyzerBlock::Process(BitVector* result, NonLiveFrameStateSlotReplacer* replacer) { queued_ = false; // Copy the bitvector to the target bit vector. result->CopyFrom(live_); for (auto entry : base::Reversed(entries_)) { switch (entry.kind()) { case Entry::kLookup: result->Add(entry.var()); break; case Entry::kBind: result->Remove(entry.var()); break; case Entry::kCheckpoint: if (replacer != nullptr) { replacer->ClearNonLiveFrameStateSlots(entry.node(), result); } break; } } } bool LivenessAnalyzerBlock::UpdateLive(BitVector* working_area) { return live_.UnionIsChanged(*working_area); } void NonLiveFrameStateSlotReplacer::ClearNonLiveFrameStateSlots( Node* frame_state, BitVector* liveness) { DCHECK_EQ(liveness->length(), permanently_live_.length()); DCHECK_EQ(frame_state->opcode(), IrOpcode::kFrameState); Node* locals_state = frame_state->InputAt(1); DCHECK_EQ(locals_state->opcode(), IrOpcode::kStateValues); int count = liveness->length() - (has_accumulator_ ? 1 : 0); DCHECK_EQ(count, static_cast(StateValuesAccess(locals_state).size())); for (int i = 0; i < count; i++) { if (!liveness->Contains(i) && !permanently_live_.Contains(i)) { Node* new_values = ClearNonLiveStateValues(locals_state, liveness); frame_state->ReplaceInput(1, new_values); break; } } if (has_accumulator_) { DCHECK_EQ(frame_state->InputAt(2)->opcode(), IrOpcode::kStateValues); DCHECK_EQ( static_cast(StateValuesAccess(frame_state->InputAt(2)).size()), 1); int index = liveness->length() - 1; if (!liveness->Contains(index) && !permanently_live_.Contains(index)) { Node* new_value = state_values_cache()->GetNodeForValues(&replacement_node_, 1); frame_state->ReplaceInput(2, new_value); } } } Node* NonLiveFrameStateSlotReplacer::ClearNonLiveStateValues( Node* values, BitVector* liveness) { DCHECK(inputs_buffer_.empty()); int var = 0; for (Node* value_node : values->inputs()) { // Make sure this isn't a state value tree DCHECK(value_node->opcode() != IrOpcode::kStateValues); // Index of the next variable is its furure index in the inputs buffer, // i.e., the buffer's size. bool live = liveness->Contains(var) || permanently_live_.Contains(var); inputs_buffer_.push_back(live ? value_node : replacement_node_); var++; } Node* result = state_values_cache()->GetNodeForValues( inputs_buffer_.empty() ? nullptr : &(inputs_buffer_.front()), inputs_buffer_.size()); inputs_buffer_.clear(); return result; } void LivenessAnalyzerBlock::Print(std::ostream& os) { os << "Block " << id(); bool first = true; for (LivenessAnalyzerBlock* pred : predecessors_) { if (!first) { os << ", "; } else { os << "; predecessors: "; first = false; } os << pred->id(); } os << std::endl; for (auto entry : entries_) { os << " "; switch (entry.kind()) { case Entry::kLookup: if (has_accumulator_ && entry.var() == live_.length() - 1) { os << "- Lookup accumulator" << std::endl; } else { os << "- Lookup " << entry.var() << std::endl; } break; case Entry::kBind: if (has_accumulator_ && entry.var() == live_.length() - 1) { os << "- Bind accumulator" << std::endl; } else { os << "- Bind " << entry.var() << std::endl; } break; case Entry::kCheckpoint: os << "- Checkpoint " << entry.node()->id() << std::endl; break; } } if (live_.length() > 0) { os << " Live set: "; for (int i = 0; i < live_.length(); i++) { os << (live_.Contains(i) ? "L" : "."); } os << std::endl; } } } // namespace compiler } // namespace internal } // namespace v8