Commit 0c1727ad authored by leszeks's avatar leszeks Committed by Commit bot

[ignition/turbo] Add liveness analysis for the accumulator

Adds a boolean flag to the liveness analysis which makes it also analyze
the accumulator. This can help prevent the accumulator escaping loops,
as well as decreasing the number of distinct state values nodes in the
graph.

The flag is a kind of ugly way to hack this in, however it is probably
the simplest to add, and (more importantly) to remove once the AST graph
builder is gone.

I measure a 2.6% improvement on Mandreel on my x64 machine, and a ~2%
improvement on Navier-Stokes. Other improvements are expected.

Review-Url: https://codereview.chromium.org/2428503002
Cr-Commit-Position: refs/heads/master@{#40359}
parent 7078c31d
......@@ -433,7 +433,7 @@ AstGraphBuilder::AstGraphBuilder(Zone* local_zone, CompilationInfo* info,
type_hint_analysis_(type_hint_analysis),
state_values_cache_(jsgraph),
liveness_analyzer_(static_cast<size_t>(info->scope()->num_stack_slots()),
local_zone),
false, local_zone),
frame_state_function_info_(common()->CreateFrameStateFunctionInfo(
FrameStateType::kJavaScriptFunction, info->num_parameters() + 1,
info->scope()->num_stack_slots(), info->shared_info())) {
......@@ -613,7 +613,7 @@ void AstGraphBuilder::ClearNonLiveSlotsInFrameStates() {
NonLiveFrameStateSlotReplacer replacer(
&state_values_cache_, jsgraph()->OptimizedOutConstant(),
liveness_analyzer()->local_count(), local_zone());
liveness_analyzer()->local_count(), false, local_zone());
Variable* arguments = info()->scope()->arguments();
if (arguments != nullptr && arguments->IsStackAllocated()) {
replacer.MarkPermanentlyLive(arguments->index());
......
......@@ -259,6 +259,10 @@ bool BytecodeGraphBuilder::Environment::IsLivenessBlockConsistent() const {
}
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const {
DCHECK(IsLivenessBlockConsistent());
if (liveness_block() != nullptr) {
liveness_block()->LookupAccumulator();
}
return values()->at(accumulator_base_);
}
......@@ -295,6 +299,10 @@ void BytecodeGraphBuilder::Environment::BindAccumulator(
if (states) {
states->AddToNode(node, OutputFrameStateCombine::PokeAt(0));
}
DCHECK(IsLivenessBlockConsistent());
if (liveness_block() != nullptr) {
liveness_block()->BindAccumulator();
}
values()->at(accumulator_base_) = node;
}
......@@ -612,7 +620,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
info->is_deoptimization_enabled()),
state_values_cache_(jsgraph),
liveness_analyzer_(
static_cast<size_t>(bytecode_array()->register_count()), local_zone),
static_cast<size_t>(bytecode_array()->register_count()), true,
local_zone),
source_positions_(source_positions) {}
Node* BytecodeGraphBuilder::GetNewTarget() {
......@@ -698,7 +707,7 @@ void BytecodeGraphBuilder::ClearNonLiveSlotsInFrameStates() {
}
NonLiveFrameStateSlotReplacer replacer(
&state_values_cache_, jsgraph()->OptimizedOutConstant(),
liveness_analyzer()->local_count(), local_zone());
liveness_analyzer()->local_count(), true, local_zone());
liveness_analyzer()->Run(&replacer);
if (FLAG_trace_environment_liveness) {
OFStream os(stdout);
......
......@@ -13,10 +13,13 @@ namespace v8 {
namespace internal {
namespace compiler {
LivenessAnalyzer::LivenessAnalyzer(size_t local_count, Zone* zone)
: zone_(zone), blocks_(zone), local_count_(local_count), queue_(zone) {}
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_) {
......@@ -28,8 +31,8 @@ void LivenessAnalyzer::Print(std::ostream& os) {
LivenessAnalyzerBlock* LivenessAnalyzer::NewBlock() {
LivenessAnalyzerBlock* result =
new (zone()->New(sizeof(LivenessAnalyzerBlock)))
LivenessAnalyzerBlock(blocks_.size(), local_count_, zone());
new (zone()->New(sizeof(LivenessAnalyzerBlock))) LivenessAnalyzerBlock(
blocks_.size(), local_count_, has_accumulator_, zone());
blocks_.push_back(result);
return result;
}
......@@ -52,8 +55,8 @@ void LivenessAnalyzer::Queue(LivenessAnalyzerBlock* block) {
void LivenessAnalyzer::Run(NonLiveFrameStateSlotReplacer* replacer) {
if (local_count_ == 0) {
// No local variables => nothing to do.
if (local_count_ == 0 && !has_accumulator_) {
// No variables => nothing to do.
return;
}
......@@ -64,7 +67,8 @@ void LivenessAnalyzer::Run(NonLiveFrameStateSlotReplacer* replacer) {
}
// Compute the fix-point.
BitVector working_area(static_cast<int>(local_count_), zone_);
BitVector working_area(
static_cast<int>(local_count_) + (has_accumulator_ ? 1 : 0), zone_);
while (!queue_.empty()) {
LivenessAnalyzerBlock* block = queue_.front();
queue_.pop();
......@@ -84,11 +88,12 @@ void LivenessAnalyzer::Run(NonLiveFrameStateSlotReplacer* replacer) {
}
LivenessAnalyzerBlock::LivenessAnalyzerBlock(size_t id, size_t local_count,
Zone* zone)
bool has_accumulator, Zone* zone)
: entries_(zone),
predecessors_(zone),
live_(local_count == 0 ? 1 : static_cast<int>(local_count), zone),
live_(static_cast<int>(local_count) + (has_accumulator ? 1 : 0), zone),
queued_(false),
has_accumulator_(has_accumulator),
id_(id) {}
void LivenessAnalyzerBlock::Process(BitVector* result,
......@@ -123,19 +128,32 @@ bool LivenessAnalyzerBlock::UpdateLive(BitVector* 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 = static_cast<int>(StateValuesAccess(locals_state).size());
DCHECK_EQ(count == 0 ? 1 : count, liveness->length());
int count = liveness->length() - (has_accumulator_ ? 1 : 0);
DCHECK_EQ(count, static_cast<int>(StateValuesAccess(locals_state).size()));
for (int i = 0; i < count; i++) {
bool live = liveness->Contains(i) || permanently_live_.Contains(i);
if (!live || locals_state->InputAt(i) != replacement_node_) {
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<int>(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);
}
}
}
......@@ -175,10 +193,18 @@ void LivenessAnalyzerBlock::Print(std::ostream& os) {
os << " ";
switch (entry.kind()) {
case Entry::kLookup:
os << "- Lookup " << entry.var() << std::endl;
if (has_accumulator_ && entry.var() == live_.length() - 1) {
os << "- Lookup accumulator" << std::endl;
} else {
os << "- Lookup " << entry.var() << std::endl;
}
break;
case Entry::kBind:
os << "- Bind " << entry.var() << std::endl;
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;
......
......@@ -18,20 +18,22 @@ class LivenessAnalyzerBlock;
class Node;
class StateValuesCache;
class NonLiveFrameStateSlotReplacer {
public:
void ClearNonLiveFrameStateSlots(Node* frame_state, BitVector* liveness);
NonLiveFrameStateSlotReplacer(StateValuesCache* state_values_cache,
Node* replacement, size_t local_count,
Zone* local_zone)
bool has_accumulator, Zone* local_zone)
: replacement_node_(replacement),
state_values_cache_(state_values_cache),
local_zone_(local_zone),
permanently_live_(local_count == 0 ? 1 : static_cast<int>(local_count),
local_zone),
inputs_buffer_(local_zone) {}
permanently_live_(
static_cast<int>(local_count) + (has_accumulator ? 1 : 0),
local_zone),
inputs_buffer_(local_zone),
has_accumulator_(has_accumulator) {}
// TODO(leszeks): Not used by bytecode, remove once AST graph builder is gone.
void MarkPermanentlyLive(int var) { permanently_live_.Add(var); }
private:
......@@ -49,11 +51,13 @@ class NonLiveFrameStateSlotReplacer {
Zone* local_zone_;
BitVector permanently_live_;
NodeVector inputs_buffer_;
bool has_accumulator_;
};
class V8_EXPORT_PRIVATE LivenessAnalyzer {
public:
LivenessAnalyzer(size_t local_count, Zone* zone);
LivenessAnalyzer(size_t local_count, bool has_accumulator, Zone* zone);
LivenessAnalyzerBlock* NewBlock();
LivenessAnalyzerBlock* NewBlock(LivenessAnalyzerBlock* predecessor);
......@@ -73,6 +77,10 @@ class V8_EXPORT_PRIVATE LivenessAnalyzer {
ZoneDeque<LivenessAnalyzerBlock*> blocks_;
size_t local_count_;
// TODO(leszeks): Always true for bytecode, remove once AST graph builder is
// gone.
bool has_accumulator_;
ZoneQueue<LivenessAnalyzerBlock*> queue_;
};
......@@ -83,6 +91,17 @@ class LivenessAnalyzerBlock {
void Lookup(int var) { entries_.push_back(Entry(Entry::kLookup, var)); }
void Bind(int var) { entries_.push_back(Entry(Entry::kBind, var)); }
void LookupAccumulator() {
DCHECK(has_accumulator_);
// The last entry is the accumulator entry.
entries_.push_back(Entry(Entry::kLookup, live_.length() - 1));
}
void BindAccumulator() {
DCHECK(has_accumulator_);
// The last entry is the accumulator entry.
entries_.push_back(Entry(Entry::kBind, live_.length() - 1));
}
void Checkpoint(Node* node) { entries_.push_back(Entry(node)); }
void AddPredecessor(LivenessAnalyzerBlock* b) { predecessors_.push_back(b); }
LivenessAnalyzerBlock* GetPredecessor() {
......@@ -116,7 +135,8 @@ class LivenessAnalyzerBlock {
Node* node_;
};
LivenessAnalyzerBlock(size_t id, size_t local_count, Zone* zone);
LivenessAnalyzerBlock(size_t id, size_t local_count, bool has_accumulator,
Zone* zone);
void Process(BitVector* result, NonLiveFrameStateSlotReplacer* relaxer);
bool UpdateLive(BitVector* working_area);
......@@ -138,6 +158,7 @@ class LivenessAnalyzerBlock {
BitVector live_;
bool queued_;
bool has_accumulator_;
size_t id_;
};
......
......@@ -27,21 +27,20 @@ class LivenessAnalysisTest : public GraphTest {
javascript_(zone()),
jsgraph_(isolate(), graph(), common(), &javascript_, nullptr,
&machine_),
analyzer_(locals_count, zone()),
analyzer_(locals_count, false, zone()),
empty_values_(graph()->NewNode(common()->StateValues(0), 0, nullptr)),
next_checkpoint_id_(0),
current_block_(nullptr) {}
protected:
JSGraph* jsgraph() { return &jsgraph_; }
LivenessAnalyzer* analyzer() { return &analyzer_; }
void Run() {
StateValuesCache cache(jsgraph());
NonLiveFrameStateSlotReplacer replacer(&cache,
jsgraph()->UndefinedConstant(),
analyzer()->local_count(), zone());
NonLiveFrameStateSlotReplacer replacer(
&cache, jsgraph()->UndefinedConstant(), analyzer()->local_count(),
false, zone());
analyzer()->Run(&replacer);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment