[ignition] Replace branch+loop analysis with a single pass

Now that we have a JumpLoop bytecode, we can heavily simplify the
branch/loop analysis by assuming that only JumpLoop bytecodes are
backwards edges, and performing the loop analysis as a single
(backwards) pass.

This allows us to get rid of the branch analysis entirely, and builds a
framework to do liveness analysis in the same pass.

Review-Url: https://codereview.chromium.org/2519983002
Cr-Commit-Position: refs/heads/master@{#41194}
This commit is contained in:
leszeks 2016-11-22 10:05:03 -08:00 committed by Commit bot
parent 7a1ad0c581
commit 292c4a0a2a
11 changed files with 175 additions and 313 deletions

View File

@ -1018,12 +1018,10 @@ v8_source_set("v8_base") {
"src/compiler/basic-block-instrumentor.h",
"src/compiler/branch-elimination.cc",
"src/compiler/branch-elimination.h",
"src/compiler/bytecode-branch-analysis.cc",
"src/compiler/bytecode-branch-analysis.h",
"src/compiler/bytecode-analysis.cc",
"src/compiler/bytecode-analysis.h",
"src/compiler/bytecode-graph-builder.cc",
"src/compiler/bytecode-graph-builder.h",
"src/compiler/bytecode-loop-analysis.cc",
"src/compiler/bytecode-loop-analysis.h",
"src/compiler/c-linkage.cc",
"src/compiler/checkpoint-elimination.cc",
"src/compiler/checkpoint-elimination.h",

View File

@ -11,6 +11,7 @@ include_rules = [
"-src/inspector",
"-src/interpreter",
"+src/interpreter/bytecode-array-iterator.h",
"+src/interpreter/bytecode-array-reverse-iterator.h",
"+src/interpreter/bytecode-decoder.h",
"+src/interpreter/bytecode-flags.h",
"+src/interpreter/bytecode-register.h",

View File

@ -0,0 +1,97 @@
// Copyright 2016 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/compiler/bytecode-analysis.h"
#include "src/interpreter/bytecode-array-reverse-iterator.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
Zone* zone)
: bytecode_array_(bytecode_array),
zone_(zone),
loop_stack_(zone),
end_to_header_(zone),
header_to_parent_(zone) {}
void BytecodeAnalysis::Analyze() {
loop_stack_.push(-1);
interpreter::BytecodeArrayReverseIterator iterator(bytecode_array(), zone());
while (!iterator.done()) {
interpreter::Bytecode bytecode = iterator.current_bytecode();
if (bytecode == interpreter::Bytecode::kJumpLoop) {
PushLoop(iterator.GetJumpTargetOffset(), iterator.current_offset());
} else if (iterator.current_offset() == loop_stack_.top()) {
loop_stack_.pop();
}
iterator.Advance();
}
DCHECK_EQ(loop_stack_.size(), 1u);
DCHECK_EQ(loop_stack_.top(), -1);
}
void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
DCHECK(loop_header < loop_end);
DCHECK(loop_stack_.top() < loop_header);
DCHECK(end_to_header_.find(loop_end) == end_to_header_.end());
DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end());
end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header));
header_to_parent_.insert(
ZoneMap<int, int>::value_type(loop_header, loop_stack_.top()));
loop_stack_.push(loop_header);
}
bool BytecodeAnalysis::IsLoopHeader(int offset) const {
return header_to_parent_.find(offset) != header_to_parent_.end();
}
int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
auto loop_end_to_header = end_to_header_.lower_bound(offset);
// If there is no next end => offset is not in a loop.
if (loop_end_to_header == end_to_header_.end()) {
return -1;
}
// If the header preceeds the offset, this is the loop
//
// .> header <--loop_end_to_header
// |
// | <--offset
// |
// `- end
if (loop_end_to_header->second <= offset) {
return loop_end_to_header->second;
}
// Otherwise there is a (potentially nested) loop after this offset.
//
// <--offset
//
// .> header
// |
// | .> header <--loop_end_to_header
// | |
// | `- end
// |
// `- end
// We just return the parent of the next loop header (might be -1).
DCHECK(header_to_parent_.upper_bound(offset) != header_to_parent_.end());
return header_to_parent_.upper_bound(offset)->second;
}
int BytecodeAnalysis::GetParentLoopFor(int header_offset) const {
DCHECK(IsLoopHeader(header_offset));
return header_to_parent_.find(header_offset)->second;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,56 @@
// Copyright 2016 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.
#ifndef V8_COMPILER_BYTECODE_ANALYSIS_H_
#define V8_COMPILER_BYTECODE_ANALYSIS_H_
#include "src/handles.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class BytecodeArray;
namespace compiler {
class BytecodeAnalysis BASE_EMBEDDED {
public:
BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, Zone* zone);
// Analyze the bytecodes to find the loop ranges and nesting. No other
// methods in this class return valid information until this has been called.
void Analyze();
// Return true if the given offset is a loop header
bool IsLoopHeader(int offset) const;
// Get the loop header offset of the containing loop for arbitrary
// {offset}, or -1 if the {offset} is not inside any loop.
int GetLoopOffsetFor(int offset) const;
// Gets the loop header offset of the parent loop of the loop header
// at {header_offset}, or -1 for outer-most loops.
int GetParentLoopFor(int header_offset) const;
private:
void PushLoop(int loop_header, int loop_end);
Zone* zone() const { return zone_; }
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
Handle<BytecodeArray> bytecode_array_;
Zone* zone_;
ZoneStack<int> loop_stack_;
ZoneMap<int, int> end_to_header_;
ZoneMap<int, int> header_to_parent_;
DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysis);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_BYTECODE_ANALYSIS_H_

View File

@ -1,43 +0,0 @@
// Copyright 2015 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/compiler/bytecode-branch-analysis.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
BytecodeBranchAnalysis::BytecodeBranchAnalysis(
Handle<BytecodeArray> bytecode_array, Zone* zone)
: bytecode_array_(bytecode_array),
is_backward_target_(bytecode_array->length(), zone),
is_forward_target_(bytecode_array->length(), zone),
zone_(zone) {}
void BytecodeBranchAnalysis::Analyze() {
interpreter::BytecodeArrayIterator iterator(bytecode_array());
while (!iterator.done()) {
interpreter::Bytecode bytecode = iterator.current_bytecode();
int current_offset = iterator.current_offset();
if (interpreter::Bytecodes::IsJump(bytecode)) {
AddBranch(current_offset, iterator.GetJumpTargetOffset());
}
iterator.Advance();
}
}
void BytecodeBranchAnalysis::AddBranch(int source_offset, int target_offset) {
if (source_offset < target_offset) {
is_forward_target_.Add(target_offset);
} else {
is_backward_target_.Add(target_offset);
}
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -1,65 +0,0 @@
// Copyright 2015 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.
#ifndef V8_COMPILER_BYTECODE_BRANCH_ANALYSIS_H_
#define V8_COMPILER_BYTECODE_BRANCH_ANALYSIS_H_
#include "src/bit-vector.h"
#include "src/handles.h"
namespace v8 {
namespace internal {
class BytecodeArray;
namespace compiler {
// A class for identifying branch targets within a bytecode array.
// This information can be used to construct the local control flow
// logic for high-level IR graphs built from bytecode.
//
// N.B. If this class is used to determine loop headers, then such a
// usage relies on the only backwards branches in bytecode being jumps
// back to loop headers.
class BytecodeBranchAnalysis BASE_EMBEDDED {
public:
BytecodeBranchAnalysis(Handle<BytecodeArray> bytecode_array, Zone* zone);
// Analyze the bytecodes to find the branch sites and their
// targets. No other methods in this class return valid information
// until this has been called.
void Analyze();
// Returns true if there are any forward branches to the bytecode at
// |offset|.
bool forward_branches_target(int offset) const {
return is_forward_target_.Contains(offset);
}
// Returns true if there are any backward branches to the bytecode
// at |offset|.
bool backward_branches_target(int offset) const {
return is_backward_target_.Contains(offset);
}
private:
void AddBranch(int origin_offset, int target_offset);
Zone* zone() const { return zone_; }
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
Handle<BytecodeArray> bytecode_array_;
BitVector is_backward_target_;
BitVector is_forward_target_;
Zone* zone_;
DISALLOW_COPY_AND_ASSIGN(BytecodeBranchAnalysis);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_BYTECODE_BRANCH_ANALYSIS_H_

View File

@ -7,7 +7,6 @@
#include "src/ast/ast.h"
#include "src/ast/scopes.h"
#include "src/compilation-info.h"
#include "src/compiler/bytecode-branch-analysis.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/linkage.h"
#include "src/compiler/operator-properties.h"
@ -638,12 +637,9 @@ void BytecodeGraphBuilder::ClearNonLiveSlotsInFrameStates() {
}
void BytecodeGraphBuilder::VisitBytecodes(bool stack_check) {
BytecodeBranchAnalysis analysis(bytecode_array(), local_zone());
BytecodeLoopAnalysis loop_analysis(bytecode_array(), &analysis, local_zone());
analysis.Analyze();
loop_analysis.Analyze();
set_branch_analysis(&analysis);
set_loop_analysis(&loop_analysis);
BytecodeAnalysis bytecode_analysis(bytecode_array(), local_zone());
bytecode_analysis.Analyze();
set_bytecode_analysis(&bytecode_analysis);
interpreter::BytecodeArrayIterator iterator(bytecode_array());
set_bytecode_iterator(&iterator);
@ -677,8 +673,7 @@ void BytecodeGraphBuilder::VisitBytecodes(bool stack_check) {
}
}
}
set_branch_analysis(nullptr);
set_bytecode_analysis(nullptr);
set_bytecode_iterator(nullptr);
DCHECK(exception_handlers_.empty());
}
@ -1905,7 +1900,7 @@ void BytecodeGraphBuilder::SwitchToMergeEnvironment(int current_offset) {
}
void BytecodeGraphBuilder::BuildLoopHeaderEnvironment(int current_offset) {
if (branch_analysis()->backward_branches_target(current_offset)) {
if (bytecode_analysis()->IsLoopHeader(current_offset)) {
// Add loop header and store a copy so we can connect merged back
// edge inputs to the loop header.
merge_environments_[current_offset] = environment()->CopyForLoop();
@ -1948,9 +1943,8 @@ void BytecodeGraphBuilder::BuildOSRNormalEntryPoint() {
// For OSR add an {OsrNormalEntry} as the the top-level environment start.
// It will be replaced with {Dead} by the OSR deconstruction.
NewNode(common()->OsrNormalEntry());
// Note that the requested OSR entry point must be the target of a backward
// branch, otherwise there will not be a proper loop header available.
DCHECK(branch_analysis()->backward_branches_target(osr_ast_id_.ToInt()));
// Note that the requested OSR entry point must be the header of a loop.
DCHECK(bytecode_analysis()->IsLoopHeader(osr_ast_id_.ToInt()));
}
}
@ -1958,17 +1952,18 @@ void BytecodeGraphBuilder::BuildLoopExitsForBranch(int target_offset) {
int origin_offset = bytecode_iterator().current_offset();
// Only build loop exits for forward edges.
if (target_offset > origin_offset) {
BuildLoopExitsUntilLoop(loop_analysis()->GetLoopOffsetFor(target_offset));
BuildLoopExitsUntilLoop(
bytecode_analysis()->GetLoopOffsetFor(target_offset));
}
}
void BytecodeGraphBuilder::BuildLoopExitsUntilLoop(int loop_offset) {
int origin_offset = bytecode_iterator().current_offset();
int current_loop = loop_analysis()->GetLoopOffsetFor(origin_offset);
int current_loop = bytecode_analysis()->GetLoopOffsetFor(origin_offset);
while (loop_offset < current_loop) {
Node* loop_node = merge_environments_[current_loop]->GetControlDependency();
environment()->PrepareForLoopExit(loop_node);
current_loop = loop_analysis()->GetParentLoopFor(current_loop);
current_loop = bytecode_analysis()->GetParentLoopFor(current_loop);
}
}

View File

@ -5,8 +5,7 @@
#ifndef V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_
#define V8_COMPILER_BYTECODE_GRAPH_BUILDER_H_
#include "src/compiler/bytecode-branch-analysis.h"
#include "src/compiler/bytecode-loop-analysis.h"
#include "src/compiler/bytecode-analysis.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/liveness-analyzer.h"
#include "src/compiler/state-values-utils.h"
@ -254,18 +253,12 @@ class BytecodeGraphBuilder {
bytecode_iterator_ = bytecode_iterator;
}
const BytecodeBranchAnalysis* branch_analysis() const {
return branch_analysis_;
const BytecodeAnalysis* bytecode_analysis() const {
return bytecode_analysis_;
}
void set_branch_analysis(const BytecodeBranchAnalysis* branch_analysis) {
branch_analysis_ = branch_analysis;
}
const BytecodeLoopAnalysis* loop_analysis() const { return loop_analysis_; }
void set_loop_analysis(const BytecodeLoopAnalysis* loop_analysis) {
loop_analysis_ = loop_analysis;
void set_bytecode_analysis(const BytecodeAnalysis* bytecode_analysis) {
bytecode_analysis_ = bytecode_analysis;
}
LivenessAnalyzer* liveness_analyzer() { return &liveness_analyzer_; }
@ -286,8 +279,7 @@ class BytecodeGraphBuilder {
Handle<TypeFeedbackVector> feedback_vector_;
const FrameStateFunctionInfo* frame_state_function_info_;
const interpreter::BytecodeArrayIterator* bytecode_iterator_;
const BytecodeBranchAnalysis* branch_analysis_;
const BytecodeLoopAnalysis* loop_analysis_;
const BytecodeAnalysis* bytecode_analysis_;
Environment* environment_;
BailoutId osr_ast_id_;

View File

@ -1,100 +0,0 @@
// Copyright 2016 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/compiler/bytecode-loop-analysis.h"
#include "src/compiler/bytecode-branch-analysis.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/objects-inl.h"
namespace v8 {
namespace internal {
namespace compiler {
BytecodeLoopAnalysis::BytecodeLoopAnalysis(
Handle<BytecodeArray> bytecode_array,
const BytecodeBranchAnalysis* branch_analysis, Zone* zone)
: bytecode_array_(bytecode_array),
branch_analysis_(branch_analysis),
zone_(zone),
current_loop_offset_(-1),
found_current_backedge_(false),
backedge_to_header_(zone),
loop_header_to_parent_(zone) {}
void BytecodeLoopAnalysis::Analyze() {
current_loop_offset_ = -1;
found_current_backedge_ = false;
interpreter::BytecodeArrayIterator iterator(bytecode_array());
while (!iterator.done()) {
interpreter::Bytecode bytecode = iterator.current_bytecode();
int current_offset = iterator.current_offset();
if (branch_analysis_->backward_branches_target(current_offset)) {
AddLoopEntry(current_offset);
} else if (interpreter::Bytecodes::IsJump(bytecode)) {
AddBranch(current_offset, iterator.GetJumpTargetOffset());
}
iterator.Advance();
}
}
void BytecodeLoopAnalysis::AddLoopEntry(int entry_offset) {
if (found_current_backedge_) {
// We assume that all backedges of a loop must occur together and before
// another loop entry or an outer loop backedge.
// This is guaranteed by the invariants from AddBranch, such that every
// backedge must either go to the current loop or be the first of the
// backedges to the parent loop.
// Thus here, the current loop actually ended before and we have a loop
// with the same parent.
current_loop_offset_ = loop_header_to_parent_[current_loop_offset_];
found_current_backedge_ = false;
}
loop_header_to_parent_[entry_offset] = current_loop_offset_;
current_loop_offset_ = entry_offset;
}
void BytecodeLoopAnalysis::AddBranch(int origin_offset, int target_offset) {
// If this is a backedge, record it.
if (target_offset < origin_offset) {
backedge_to_header_[origin_offset] = target_offset;
// Check whether this is actually a backedge of the outer loop and we have
// already finished the current loop.
if (target_offset < current_loop_offset_) {
DCHECK(found_current_backedge_);
int parent_offset = loop_header_to_parent_[current_loop_offset_];
DCHECK_EQ(target_offset, parent_offset);
current_loop_offset_ = parent_offset;
} else {
DCHECK_EQ(target_offset, current_loop_offset_);
found_current_backedge_ = true;
}
}
}
int BytecodeLoopAnalysis::GetLoopOffsetFor(int offset) const {
auto next_backedge = backedge_to_header_.lower_bound(offset);
// If there is no next backedge => offset is not in a loop.
if (next_backedge == backedge_to_header_.end()) {
return -1;
}
// If the header preceeds the offset, it is the backedge of the containing
// loop.
if (next_backedge->second <= offset) {
return next_backedge->second;
}
// Otherwise there is a nested loop after this offset. We just return the
// parent of the next nested loop.
return loop_header_to_parent_.upper_bound(offset)->second;
}
int BytecodeLoopAnalysis::GetParentLoopFor(int header_offset) const {
auto parent = loop_header_to_parent_.find(header_offset);
DCHECK(parent != loop_header_to_parent_.end());
return parent->second;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -1,67 +0,0 @@
// Copyright 2016 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.
#ifndef V8_COMPILER_BYTECODE_LOOP_ANALYSIS_H_
#define V8_COMPILER_BYTECODE_LOOP_ANALYSIS_H_
#include "src/handles.h"
#include "src/zone/zone-containers.h"
namespace v8 {
namespace internal {
class BytecodeArray;
namespace compiler {
class BytecodeBranchAnalysis;
class BytecodeLoopAnalysis BASE_EMBEDDED {
public:
BytecodeLoopAnalysis(Handle<BytecodeArray> bytecode_array,
const BytecodeBranchAnalysis* branch_analysis,
Zone* zone);
// Analyze the bytecodes to find the branch sites and their
// targets. No other methods in this class return valid information
// until this has been called.
void Analyze();
// Get the loop header offset of the containing loop for arbitrary
// {offset}, or -1 if the {offset} is not inside any loop.
int GetLoopOffsetFor(int offset) const;
// Gets the loop header offset of the parent loop of the loop header
// at {header_offset}, or -1 for outer-most loops.
int GetParentLoopFor(int header_offset) const;
private:
void AddLoopEntry(int entry_offset);
void AddBranch(int origin_offset, int target_offset);
Zone* zone() const { return zone_; }
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
Handle<BytecodeArray> bytecode_array_;
const BytecodeBranchAnalysis* branch_analysis_;
Zone* zone_;
int current_loop_offset_;
bool found_current_backedge_;
// Map from the offset of a backedge jump to the offset of the corresponding
// loop header. There might be multiple backedges for do-while loops.
ZoneMap<int, int> backedge_to_header_;
// Map from the offset of a loop header to the offset of its parent's loop
// header. This map will have as many entries as there are loops in the
// function.
ZoneMap<int, int> loop_header_to_parent_;
DISALLOW_COPY_AND_ASSIGN(BytecodeLoopAnalysis);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_BYTECODE_LOOP_ANALYSIS_H_

View File

@ -548,12 +548,10 @@
'compiler/basic-block-instrumentor.h',
'compiler/branch-elimination.cc',
'compiler/branch-elimination.h',
'compiler/bytecode-branch-analysis.cc',
'compiler/bytecode-branch-analysis.h',
'compiler/bytecode-graph-builder.cc',
'compiler/bytecode-graph-builder.h',
'compiler/bytecode-loop-analysis.cc',
'compiler/bytecode-loop-analysis.h',
'compiler/bytecode-analysis.cc',
'compiler/bytecode-analysis.h',
'compiler/c-linkage.cc',
'compiler/checkpoint-elimination.cc',
'compiler/checkpoint-elimination.h',