[ignition/turbo] Perform liveness analysis on the bytecodes

Replaces the graph-based liveness analyzer in the bytecode graph builder
with an initial bytecode-based liveness analysis pass, which is added to
the existing loop extent analysis.

Now the StateValues in the graph have their inputs initialised to
optimized_out, rather than being modified after the graph is built.

Review-Url: https://codereview.chromium.org/2523893003
Cr-Commit-Position: refs/heads/master@{#41355}
This commit is contained in:
leszeks 2016-11-29 04:26:54 -08:00 committed by Commit bot
parent 5fd2b71236
commit 2bf71f888f
33 changed files with 1104 additions and 174 deletions

View File

@ -1024,6 +1024,8 @@ v8_source_set("v8_base") {
"src/compiler/bytecode-analysis.h",
"src/compiler/bytecode-graph-builder.cc",
"src/compiler/bytecode-graph-builder.h",
"src/compiler/bytecode-liveness-map.cc",
"src/compiler/bytecode-liveness-map.h",
"src/compiler/c-linkage.cc",
"src/compiler/checkpoint-elimination.cc",
"src/compiler/checkpoint-elimination.h",

View File

@ -10,6 +10,7 @@ include_rules = [
"+src/heap/heap-inl.h",
"-src/inspector",
"-src/interpreter",
"+src/interpreter/bytecode-array-accessor.h",
"+src/interpreter/bytecode-array-iterator.h",
"+src/interpreter/bytecode-array-reverse-iterator.h",
"+src/interpreter/bytecode-decoder.h",

View File

@ -4,6 +4,7 @@
#include "src/compiler/bytecode-analysis.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-array-reverse-iterator.h"
#include "src/objects-inl.h"
@ -11,30 +12,262 @@ namespace v8 {
namespace internal {
namespace compiler {
using namespace interpreter;
BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
Zone* zone)
Zone* zone, bool do_liveness_analysis)
: bytecode_array_(bytecode_array),
do_liveness_analysis_(do_liveness_analysis),
zone_(zone),
loop_stack_(zone),
end_to_header_(zone),
header_to_parent_(zone) {}
header_to_parent_(zone),
liveness_map_(bytecode_array->length(), zone) {}
namespace {
void UpdateInLiveness(Bytecode bytecode, BitVector& in_liveness,
const BytecodeArrayAccessor& accessor) {
int num_operands = Bytecodes::NumberOfOperands(bytecode);
const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
AccumulatorUse accumulator_use = Bytecodes::GetAccumulatorUse(bytecode);
if (accumulator_use == AccumulatorUse::kWrite) {
in_liveness.Remove(in_liveness.length() - 1);
}
for (int i = 0; i < num_operands; ++i) {
switch (operand_types[i]) {
case OperandType::kRegOut: {
interpreter::Register r = accessor.GetRegisterOperand(i);
if (!r.is_parameter()) {
in_liveness.Remove(r.index());
}
break;
}
case OperandType::kRegOutPair: {
interpreter::Register r = accessor.GetRegisterOperand(i);
if (!r.is_parameter()) {
DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
in_liveness.Remove(r.index());
in_liveness.Remove(r.index() + 1);
}
break;
}
case OperandType::kRegOutTriple: {
interpreter::Register r = accessor.GetRegisterOperand(i);
if (!r.is_parameter()) {
DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
in_liveness.Remove(r.index());
in_liveness.Remove(r.index() + 1);
in_liveness.Remove(r.index() + 2);
}
break;
}
default:
DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
break;
}
}
if (accumulator_use == AccumulatorUse::kRead) {
in_liveness.Add(in_liveness.length() - 1);
}
for (int i = 0; i < num_operands; ++i) {
switch (operand_types[i]) {
case OperandType::kReg: {
interpreter::Register r = accessor.GetRegisterOperand(i);
if (!r.is_parameter()) {
in_liveness.Add(r.index());
}
break;
}
case OperandType::kRegPair: {
interpreter::Register r = accessor.GetRegisterOperand(i);
if (!r.is_parameter()) {
DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
in_liveness.Add(r.index());
in_liveness.Add(r.index() + 1);
}
break;
}
case OperandType::kRegList: {
interpreter::Register r = accessor.GetRegisterOperand(i++);
uint32_t reg_count = accessor.GetRegisterCountOperand(i);
if (!r.is_parameter()) {
for (uint32_t j = 0; j < reg_count; ++j) {
DCHECK(!interpreter::Register(r.index() + j).is_parameter());
in_liveness.Add(r.index() + j);
}
}
}
default:
DCHECK(!Bytecodes::IsRegisterInputOperandType(operand_types[i]));
break;
}
}
}
void UpdateOutLiveness(Bytecode bytecode, BitVector& out_liveness,
const BytecodeArrayAccessor& accessor,
const BytecodeLivenessMap& liveness_map) {
int current_offset = accessor.current_offset();
const Handle<BytecodeArray>& bytecode_array = accessor.bytecode_array();
// Update from jump target (if any). Skip loops, we update these manually in
// the liveness iterations.
if (Bytecodes::IsForwardJump(bytecode)) {
int target_offset = accessor.GetJumpTargetOffset();
out_liveness.Union(*liveness_map.GetInLiveness(target_offset));
}
// Update from next bytecode (unless this is an unconditional jump).
if (!Bytecodes::IsUnconditionalJump(bytecode)) {
int next_offset = current_offset + accessor.current_bytecode_size();
if (next_offset < bytecode_array->length()) {
out_liveness.Union(*liveness_map.GetInLiveness(next_offset));
}
}
// Update from exception handler (if any).
if (!interpreter::Bytecodes::IsWithoutExternalSideEffects(bytecode)) {
int handler_context;
// TODO(leszeks): We should look up this range only once per entry.
HandlerTable* table = HandlerTable::cast(bytecode_array->handler_table());
int handler_offset =
table->LookupRange(current_offset, &handler_context, nullptr);
if (handler_offset != -1) {
out_liveness.Union(*liveness_map.GetInLiveness(handler_offset));
out_liveness.Add(handler_context);
}
}
}
} // namespace
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()) {
// The last JumpLoop that we haven't done a guaranteed valid liveness pass
// over. See the below wall of text for a more thorough explanation.
int last_invalid_jumploop_offset = -1;
BytecodeArrayReverseIterator iterator(bytecode_array(), zone());
for (; !iterator.done(); iterator.Advance()) {
Bytecode bytecode = iterator.current_bytecode();
int current_offset = iterator.current_offset();
if (bytecode == Bytecode::kJumpLoop) {
PushLoop(iterator.GetJumpTargetOffset(), current_offset);
// Save the last offset so that we can do another pass later.
if (last_invalid_jumploop_offset == -1) {
last_invalid_jumploop_offset = current_offset;
}
} else if (current_offset == loop_stack_.top()) {
loop_stack_.pop();
}
iterator.Advance();
if (do_liveness_analysis_) {
// The liveness vector had bits for the liveness of the registers, and one
// more bit for the liveness of the accumulator.
Liveness& liveness = liveness_map_.InitializeLiveness(
current_offset, bytecode_array()->register_count() + 1, zone());
UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_);
liveness.in->CopyFrom(*liveness.out);
UpdateInLiveness(bytecode, *liveness.in, iterator);
}
}
DCHECK_EQ(loop_stack_.size(), 1u);
DCHECK_EQ(loop_stack_.top(), -1);
if (!do_liveness_analysis_) return;
// At this point, every bytecode has a valid in and out liveness, except for
// propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness
// analysis iterations can only add additional liveness bits that are pulled
// across these back edges.
//
// Furthermore, a loop header's in-liveness can only change based on any
// bytecodes *after* the loop end -- it cannot change as a result of the
// JumpLoop liveness being updated, as the only liveness bits than can be
// added to the loop body are those of the loop header.
//
// So, if we know that the liveness of bytecodes after a loop header won't
// change (e.g. because there are no loops in them, or we have already ensured
// those loops are valid), we can safely update the loop end and pass over the
// loop body, and then never have to pass over that loop end again, because we
// have shown that its target, the loop header, can't change from the entries
// after the loop, and can't change from any loop body pass.
//
// This means that in a pass, we can iterate backwards over the bytecode
// array, process any loops that we encounter, and on subsequent passes we can
// skip processing those loops (though we still have to process inner loops).
while (last_invalid_jumploop_offset != -1) {
// TODO(leszeks): We shouldn't need to iterate here, we should just have a
// random access iterator.
iterator.Reset();
while (last_invalid_jumploop_offset < iterator.current_offset()) {
iterator.Advance();
}
last_invalid_jumploop_offset = -1;
DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
for (; !iterator.done(); iterator.Advance()) {
Bytecode bytecode = iterator.current_bytecode();
if (bytecode != Bytecode::kJumpLoop) {
// Skip bytecodes until we hit a JumpLoop. This check isn't needed for
// the first loop we see (thanks to saving its offset), but it is for
// subsequent ones we want to process on this pass.
continue;
}
int header_offset = iterator.GetJumpTargetOffset();
int end_offset = iterator.current_offset();
Liveness& header_liveness = liveness_map_.GetLiveness(header_offset);
Liveness& end_liveness = liveness_map_.GetLiveness(end_offset);
if (end_liveness.out->UnionIsChanged(*header_liveness.in)) {
// Only update the loop body if the loop end liveness changed.
end_liveness.in->CopyFrom(*end_liveness.out);
// Advance into the loop body.
iterator.Advance();
for (; iterator.current_offset() > header_offset; iterator.Advance()) {
bytecode = iterator.current_bytecode();
if (bytecode == Bytecode::kJumpLoop) {
// We can't validate this loop at the moment because we can't
// guarantee that its header is valid yet. Save it for later.
if (last_invalid_jumploop_offset == -1) {
last_invalid_jumploop_offset = iterator.current_offset();
}
}
int current_offset = iterator.current_offset();
Liveness& liveness = liveness_map_.GetLiveness(current_offset);
UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_);
liveness.in->CopyFrom(*liveness.out);
UpdateInLiveness(bytecode, *liveness.in, iterator);
}
// Now we are at the loop header. Since the in-liveness of the header
// can't change, we need only to update the out-liveness.
bytecode = iterator.current_bytecode();
UpdateOutLiveness(bytecode, *header_liveness.out, iterator,
liveness_map_);
}
// Keep the iterator going so that we can find other loops.
}
}
DCHECK(LivenessIsValid());
}
void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
@ -92,6 +325,163 @@ int BytecodeAnalysis::GetParentLoopFor(int header_offset) const {
return header_to_parent_.find(header_offset)->second;
}
const BitVector* BytecodeAnalysis::GetInLivenessFor(int offset) const {
if (!do_liveness_analysis_) return nullptr;
return liveness_map_.GetInLiveness(offset);
}
const BitVector* BytecodeAnalysis::GetOutLivenessFor(int offset) const {
if (!do_liveness_analysis_) return nullptr;
return liveness_map_.GetOutLiveness(offset);
}
std::ostream& BytecodeAnalysis::PrintLivenessTo(std::ostream& os) const {
interpreter::BytecodeArrayIterator iterator(bytecode_array());
for (; !iterator.done(); iterator.Advance()) {
int current_offset = iterator.current_offset();
const BitVector* in_liveness = GetInLivenessFor(current_offset);
const BitVector* out_liveness = GetOutLivenessFor(current_offset);
for (int i = 0; i < in_liveness->length(); ++i) {
os << (in_liveness->Contains(i) ? "L" : ".");
}
os << " -> ";
for (int i = 0; i < out_liveness->length(); ++i) {
os << (out_liveness->Contains(i) ? "L" : ".");
}
os << " | " << current_offset << ": ";
iterator.PrintTo(os) << std::endl;
}
return os;
}
#if DEBUG
bool BytecodeAnalysis::LivenessIsValid() {
BytecodeArrayReverseIterator iterator(bytecode_array(), zone());
BitVector previous_liveness(bytecode_array()->register_count() + 1, zone());
int invalid_offset = -1;
int which_invalid = -1;
// Ensure that there are no liveness changes if we iterate one more time.
for (iterator.Reset(); !iterator.done(); iterator.Advance()) {
Bytecode bytecode = iterator.current_bytecode();
int current_offset = iterator.current_offset();
Liveness& liveness = liveness_map_.GetLiveness(current_offset);
previous_liveness.CopyFrom(*liveness.out);
UpdateOutLiveness(bytecode, *liveness.out, iterator, liveness_map_);
// UpdateOutLiveness skips kJumpLoop, so we update it manually.
if (bytecode == Bytecode::kJumpLoop) {
int target_offset = iterator.GetJumpTargetOffset();
liveness.out->Union(*liveness_map_.GetInLiveness(target_offset));
}
if (!liveness.out->Equals(previous_liveness)) {
// Reset the invalid liveness.
liveness.out->CopyFrom(previous_liveness);
invalid_offset = current_offset;
which_invalid = 1;
break;
}
previous_liveness.CopyFrom(*liveness.in);
liveness.in->CopyFrom(*liveness.out);
UpdateInLiveness(bytecode, *liveness.in, iterator);
if (!liveness.in->Equals(previous_liveness)) {
// Reset the invalid liveness.
liveness.in->CopyFrom(previous_liveness);
invalid_offset = current_offset;
which_invalid = 0;
break;
}
}
if (invalid_offset != -1) {
OFStream of(stderr);
of << "Invalid liveness:" << std::endl;
// Dump the bytecode, annotated with the liveness and marking loops.
int loop_indent = 0;
BytecodeArrayIterator forward_iterator(bytecode_array());
for (; !forward_iterator.done(); forward_iterator.Advance()) {
int current_offset = forward_iterator.current_offset();
BitVector* in_liveness = liveness_map_.GetInLiveness(current_offset);
BitVector* out_liveness = liveness_map_.GetOutLiveness(current_offset);
for (int i = 0; i < in_liveness->length(); ++i) {
of << (in_liveness->Contains(i) ? 'L' : '.');
}
of << " | ";
for (int i = 0; i < out_liveness->length(); ++i) {
of << (out_liveness->Contains(i) ? 'L' : '.');
}
of << " : " << current_offset << " : ";
// Draw loop back edges by indentin everything between loop headers and
// jump loop instructions.
if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
loop_indent--;
}
for (int i = 0; i < loop_indent; ++i) {
of << " | ";
}
if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
of << " `-" << current_offset;
} else if (IsLoopHeader(current_offset)) {
of << " .>" << current_offset;
loop_indent++;
}
forward_iterator.PrintTo(of) << std::endl;
if (current_offset == invalid_offset) {
// Underline the invalid liveness.
if (which_invalid == 0) {
for (int i = 0; i < in_liveness->length(); ++i) {
of << '^';
}
} else {
for (int i = 0; i < in_liveness->length() + 3; ++i) {
of << ' ';
}
for (int i = 0; i < out_liveness->length(); ++i) {
of << '^';
}
}
// Make sure to draw the loop indentation marks on this additional line.
of << " : " << current_offset << " : ";
for (int i = 0; i < loop_indent; ++i) {
of << " | ";
}
of << std::endl;
}
}
}
return invalid_offset == -1;
}
#endif
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -5,6 +5,9 @@
#ifndef V8_COMPILER_BYTECODE_ANALYSIS_H_
#define V8_COMPILER_BYTECODE_ANALYSIS_H_
#include "src/base/hashmap.h"
#include "src/bit-vector.h"
#include "src/compiler/bytecode-liveness-map.h"
#include "src/handles.h"
#include "src/zone/zone-containers.h"
@ -15,9 +18,10 @@ class BytecodeArray;
namespace compiler {
class BytecodeAnalysis BASE_EMBEDDED {
class V8_EXPORT_PRIVATE BytecodeAnalysis BASE_EMBEDDED {
public:
BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, Zone* zone);
BytecodeAnalysis(Handle<BytecodeArray> bytecode_array, Zone* zone,
bool do_liveness_analysis);
// Analyze the bytecodes to find the loop ranges and nesting. No other
// methods in this class return valid information until this has been called.
@ -32,13 +36,33 @@ class BytecodeAnalysis BASE_EMBEDDED {
// at {header_offset}, or -1 for outer-most loops.
int GetParentLoopFor(int header_offset) const;
// Gets the in-liveness for the bytecode at {offset}. The liveness bit vector
// represents the liveness of the registers and the accumulator, with the last
// bit being the accumulator liveness bit, and so is (register count + 1) bits
// long.
const BitVector* GetInLivenessFor(int offset) const;
// Gets the out-liveness for the bytecode at {offset}. The liveness bit vector
// represents the liveness of the registers and the accumulator, with the last
// bit being the accumulator liveness bit, and so is (register count + 1) bits
// long.
const BitVector* GetOutLivenessFor(int offset) const;
std::ostream& PrintLivenessTo(std::ostream& os) const;
private:
void PushLoop(int loop_header, int loop_end);
#if DEBUG
bool LivenessIsValid();
#endif
Zone* zone() const { return zone_; }
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
private:
Handle<BytecodeArray> bytecode_array_;
bool do_liveness_analysis_;
Zone* zone_;
ZoneStack<int> loop_stack_;
@ -46,6 +70,8 @@ class BytecodeAnalysis BASE_EMBEDDED {
ZoneMap<int, int> end_to_header_;
ZoneMap<int, int> header_to_parent_;
BytecodeLivenessMap liveness_map_;
DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysis);
};

View File

@ -35,7 +35,6 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
Node* LookupAccumulator() const;
Node* LookupRegister(interpreter::Register the_register) const;
void MarkAllRegistersLive();
void BindAccumulator(Node* node,
FrameStateAttachmentMode mode = kDontAttachFrameState);
@ -56,7 +55,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
// Preserve a checkpoint of the environment for the IR graph. Any
// further mutation of the environment will not affect checkpoints.
Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine,
bool owner_has_exception);
bool owner_has_exception, const BitVector* liveness);
// Control dependency tracked by this environment.
Node* GetControlDependency() const { return control_dependency_; }
@ -76,21 +75,20 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
void PrepareForLoopExit(Node* loop);
private:
Environment(const Environment* copy, LivenessAnalyzerBlock* liveness_block);
explicit Environment(const Environment* copy);
void PrepareForLoop();
bool StateValuesRequireUpdate(Node** state_values, int offset, int count);
void UpdateStateValues(Node** state_values, int offset, int count);
bool StateValuesRequireUpdate(Node** state_values, Node** values, int count);
void UpdateStateValues(Node** state_values, Node** values, int count);
void UpdateStateValuesWithCache(Node** state_values, Node** values,
int count);
int RegisterToValuesIndex(interpreter::Register the_register) const;
bool IsLivenessBlockConsistent() const;
Zone* zone() const { return builder_->local_zone(); }
Graph* graph() const { return builder_->graph(); }
CommonOperatorBuilder* common() const { return builder_->common(); }
BytecodeGraphBuilder* builder() const { return builder_; }
LivenessAnalyzerBlock* liveness_block() const { return liveness_block_; }
const NodeVector* values() const { return &values_; }
NodeVector* values() { return &values_; }
int register_base() const { return register_base_; }
@ -99,7 +97,6 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
BytecodeGraphBuilder* builder_;
int register_count_;
int parameter_count_;
LivenessAnalyzerBlock* liveness_block_;
Node* context_;
Node* control_dependency_;
Node* effect_dependency_;
@ -109,6 +106,10 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
Node* accumulator_state_values_;
int register_base_;
int accumulator_base_;
// A working area for writing maybe-dead values to when updating the state
// values for registers.
NodeVector state_value_working_area_;
};
@ -123,16 +124,14 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder,
: builder_(builder),
register_count_(register_count),
parameter_count_(parameter_count),
liveness_block_(builder->is_liveness_analysis_enabled_
? builder_->liveness_analyzer()->NewBlock()
: nullptr),
context_(context),
control_dependency_(control_dependency),
effect_dependency_(control_dependency),
values_(builder->local_zone()),
parameters_state_values_(nullptr),
registers_state_values_(nullptr),
accumulator_state_values_(nullptr) {
accumulator_state_values_(nullptr),
state_value_working_area_(builder->local_zone()) {
// The layout of values_ is:
//
// [receiver] [parameters] [registers] [accumulator]
@ -157,15 +156,15 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder,
// Accumulator
accumulator_base_ = static_cast<int>(values()->size());
values()->push_back(undefined_constant);
state_value_working_area_.resize(register_count_);
}
BytecodeGraphBuilder::Environment::Environment(
const BytecodeGraphBuilder::Environment* other,
LivenessAnalyzerBlock* liveness_block)
const BytecodeGraphBuilder::Environment* other)
: builder_(other->builder_),
register_count_(other->register_count_),
parameter_count_(other->parameter_count_),
liveness_block_(liveness_block),
context_(other->context_),
control_dependency_(other->control_dependency_),
effect_dependency_(other->effect_dependency_),
@ -174,7 +173,9 @@ BytecodeGraphBuilder::Environment::Environment(
registers_state_values_(nullptr),
accumulator_state_values_(nullptr),
register_base_(other->register_base_),
accumulator_base_(other->accumulator_base_) {
accumulator_base_(other->accumulator_base_),
// Environments can share their working area.
state_value_working_area_(other->state_value_working_area_) {
values_ = other->values_;
}
@ -188,16 +189,7 @@ int BytecodeGraphBuilder::Environment::RegisterToValuesIndex(
}
}
bool BytecodeGraphBuilder::Environment::IsLivenessBlockConsistent() const {
return !builder_->IsLivenessAnalysisEnabled() ==
(liveness_block() == nullptr);
}
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const {
DCHECK(IsLivenessBlockConsistent());
if (liveness_block() != nullptr) {
liveness_block()->LookupAccumulator();
}
return values()->at(accumulator_base_);
}
@ -212,32 +204,15 @@ Node* BytecodeGraphBuilder::Environment::LookupRegister(
return builder()->GetNewTarget();
} else {
int values_index = RegisterToValuesIndex(the_register);
if (liveness_block() != nullptr && !the_register.is_parameter()) {
DCHECK(IsLivenessBlockConsistent());
liveness_block()->Lookup(the_register.index());
}
return values()->at(values_index);
}
}
void BytecodeGraphBuilder::Environment::MarkAllRegistersLive() {
DCHECK(IsLivenessBlockConsistent());
if (liveness_block() != nullptr) {
for (int i = 0; i < register_count(); ++i) {
liveness_block()->Lookup(i);
}
}
}
void BytecodeGraphBuilder::Environment::BindAccumulator(
Node* node, FrameStateAttachmentMode mode) {
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(0));
}
DCHECK(IsLivenessBlockConsistent());
if (liveness_block() != nullptr) {
liveness_block()->BindAccumulator();
}
values()->at(accumulator_base_) = node;
}
@ -250,10 +225,6 @@ void BytecodeGraphBuilder::Environment::BindRegister(
accumulator_base_ - values_index));
}
values()->at(values_index) = node;
if (liveness_block() != nullptr && !the_register.is_parameter()) {
DCHECK(IsLivenessBlockConsistent());
liveness_block()->Bind(the_register.index());
}
}
void BytecodeGraphBuilder::Environment::BindRegistersToProjections(
@ -281,41 +252,22 @@ void BytecodeGraphBuilder::Environment::RecordAfterState(
BytecodeGraphBuilder::Environment*
BytecodeGraphBuilder::Environment::CopyForLoop() {
PrepareForLoop();
if (liveness_block() != nullptr) {
// Finish the current block before copying.
liveness_block_ = builder_->liveness_analyzer()->NewBlock(liveness_block());
}
return new (zone()) Environment(this, liveness_block());
return new (zone()) Environment(this);
}
BytecodeGraphBuilder::Environment*
BytecodeGraphBuilder::Environment::CopyForOsrEntry() {
return new (zone())
Environment(this, builder_->liveness_analyzer()->NewBlock());
return new (zone()) Environment(this);
}
BytecodeGraphBuilder::Environment*
BytecodeGraphBuilder::Environment::CopyForConditional() {
LivenessAnalyzerBlock* copy_liveness_block = nullptr;
if (liveness_block() != nullptr) {
copy_liveness_block =
builder_->liveness_analyzer()->NewBlock(liveness_block());
liveness_block_ = builder_->liveness_analyzer()->NewBlock(liveness_block());
}
return new (zone()) Environment(this, copy_liveness_block);
return new (zone()) Environment(this);
}
void BytecodeGraphBuilder::Environment::Merge(
BytecodeGraphBuilder::Environment* other) {
if (builder_->is_liveness_analysis_enabled_) {
if (GetControlDependency()->opcode() != IrOpcode::kLoop) {
liveness_block_ =
builder()->liveness_analyzer()->NewBlock(liveness_block());
}
liveness_block()->AddPredecessor(other->liveness_block());
}
// Create a merge of the control dependencies of both environments and update
// the current environment's control dependency accordingly.
Node* control = builder()->MergeControl(GetControlDependency(),
@ -383,7 +335,7 @@ void BytecodeGraphBuilder::Environment::PrepareForOsrEntry() {
BailoutId loop_id(builder_->bytecode_iterator().current_offset());
Node* frame_state =
Checkpoint(loop_id, OutputFrameStateCombine::Ignore(), false);
Checkpoint(loop_id, OutputFrameStateCombine::Ignore(), false, nullptr);
Node* checkpoint =
graph()->NewNode(common()->Checkpoint(), frame_state, entry, entry);
UpdateEffectDependency(checkpoint);
@ -401,15 +353,13 @@ void BytecodeGraphBuilder::Environment::PrepareForOsrEntry() {
}
bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
Node** state_values, int offset, int count) {
Node** state_values, Node** values, int count) {
if (*state_values == nullptr) {
return true;
}
DCHECK_EQ((*state_values)->InputCount(), count);
DCHECK_LE(static_cast<size_t>(offset + count), values()->size());
Node** env_values = (count == 0) ? nullptr : &values()->at(offset);
for (int i = 0; i < count; i++) {
if ((*state_values)->InputAt(i) != env_values[i]) {
if ((*state_values)->InputAt(i) != values[i]) {
return true;
}
}
@ -443,21 +393,51 @@ void BytecodeGraphBuilder::Environment::PrepareForLoopExit(Node* loop) {
}
void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
int offset,
Node** values,
int count) {
if (StateValuesRequireUpdate(state_values, offset, count)) {
if (StateValuesRequireUpdate(state_values, values, count)) {
const Operator* op = common()->StateValues(count);
(*state_values) = graph()->NewNode(op, count, &values()->at(offset));
(*state_values) = graph()->NewNode(op, count, values);
}
}
void BytecodeGraphBuilder::Environment::UpdateStateValuesWithCache(
Node** state_values, Node** values, int count) {
*state_values = builder_->state_values_cache_.GetNodeForValues(
values, static_cast<size_t>(count));
}
Node* BytecodeGraphBuilder::Environment::Checkpoint(
BailoutId bailout_id, OutputFrameStateCombine combine,
bool owner_has_exception) {
UpdateStateValues(&parameters_state_values_, 0, parameter_count());
UpdateStateValues(&registers_state_values_, register_base(),
register_count());
UpdateStateValues(&accumulator_state_values_, accumulator_base(), 1);
bool owner_has_exception, const BitVector* liveness) {
UpdateStateValues(&parameters_state_values_, &values()->at(0),
parameter_count());
if (liveness) {
Node* optimized_out = builder()->jsgraph()->OptimizedOutConstant();
for (int i = 0; i < register_count(); ++i) {
state_value_working_area_[i] = liveness->Contains(i)
? values()->at(register_base() + i)
: optimized_out;
}
Node* accumulator_value = liveness->Contains(register_count())
? values()->at(accumulator_base())
: optimized_out;
UpdateStateValuesWithCache(&registers_state_values_,
state_value_working_area_.data(),
register_count());
UpdateStateValues(&accumulator_state_values_, &accumulator_value, 1);
} else {
UpdateStateValuesWithCache(&registers_state_values_,
&values()->at(register_base()),
register_count());
UpdateStateValues(&accumulator_state_values_,
&values()->at(accumulator_base()), 1);
}
const Operator* op = common()->FrameState(
bailout_id, combine, builder()->frame_state_function_info());
@ -466,18 +446,6 @@ Node* BytecodeGraphBuilder::Environment::Checkpoint(
accumulator_state_values_, Context(), builder()->GetFunctionClosure(),
builder()->graph()->start());
if (liveness_block() != nullptr) {
// If the owning node has an exception, register the checkpoint to the
// predecessor so that the checkpoint is used for both the normal and the
// exceptional paths. Yes, this is a terrible hack and we might want
// to use an explicit frame state for the exceptional path.
if (owner_has_exception) {
liveness_block()->GetPredecessor()->Checkpoint(result);
} else {
liveness_block()->Checkpoint(result);
}
}
return result;
}
@ -505,9 +473,6 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
exit_controls_(local_zone),
is_liveness_analysis_enabled_(FLAG_analyze_environment_liveness),
state_values_cache_(jsgraph),
liveness_analyzer_(
static_cast<size_t>(bytecode_array()->register_count()), true,
local_zone),
source_positions_(source_positions),
start_position_(info->shared_info()->start_position(), inlining_id) {
// Bytecode graph builder assumes deoptimziation is enabled.
@ -588,8 +553,6 @@ bool BytecodeGraphBuilder::CreateGraph(bool stack_check) {
Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs);
graph()->SetEnd(end);
ClearNonLiveSlotsInFrameStates();
return true;
}
@ -602,8 +565,12 @@ void BytecodeGraphBuilder::PrepareEagerCheckpoint() {
DCHECK_EQ(IrOpcode::kDead,
NodeProperties::GetFrameStateInput(node)->opcode());
BailoutId bailout_id(bytecode_iterator().current_offset());
const BitVector* liveness_before = bytecode_analysis()->GetInLivenessFor(
bytecode_iterator().current_offset());
Node* frame_state_before = environment()->Checkpoint(
bailout_id, OutputFrameStateCombine::Ignore(), false);
bailout_id, OutputFrameStateCombine::Ignore(), false, liveness_before);
NodeProperties::ReplaceFrameStateInput(node, frame_state_before);
}
}
@ -618,28 +585,19 @@ void BytecodeGraphBuilder::PrepareFrameState(Node* node,
NodeProperties::GetFrameStateInput(node)->opcode());
BailoutId bailout_id(bytecode_iterator().current_offset());
bool has_exception = NodeProperties::IsExceptionalCall(node);
Node* frame_state_after =
environment()->Checkpoint(bailout_id, combine, has_exception);
const BitVector* liveness_after = bytecode_analysis()->GetOutLivenessFor(
bytecode_iterator().current_offset());
Node* frame_state_after = environment()->Checkpoint(
bailout_id, combine, has_exception, liveness_after);
NodeProperties::ReplaceFrameStateInput(node, frame_state_after);
}
}
void BytecodeGraphBuilder::ClearNonLiveSlotsInFrameStates() {
if (!IsLivenessAnalysisEnabled()) {
return;
}
NonLiveFrameStateSlotReplacer replacer(
&state_values_cache_, jsgraph()->OptimizedOutConstant(),
liveness_analyzer()->local_count(), true, local_zone());
liveness_analyzer()->Run(&replacer);
if (FLAG_trace_environment_liveness) {
OFStream os(stdout);
liveness_analyzer()->Print(os);
}
}
void BytecodeGraphBuilder::VisitBytecodes(bool stack_check) {
BytecodeAnalysis bytecode_analysis(bytecode_array(), local_zone());
BytecodeAnalysis bytecode_analysis(bytecode_array(), local_zone(),
FLAG_analyze_environment_liveness);
bytecode_analysis.Analyze();
set_bytecode_analysis(&bytecode_analysis);
@ -648,7 +606,14 @@ void BytecodeGraphBuilder::VisitBytecodes(bool stack_check) {
SourcePositionTableIterator source_position_iterator(
bytecode_array()->source_position_table());
if (FLAG_trace_environment_liveness) {
OFStream of(stdout);
bytecode_analysis.PrintLivenessTo(of);
}
BuildOSRNormalEntryPoint();
for (; !iterator.done(); iterator.Advance()) {
int current_offset = iterator.current_offset();
UpdateCurrentSourcePosition(&source_position_iterator, current_offset);
@ -1769,7 +1734,6 @@ void BytecodeGraphBuilder::VisitDebugger() {
Node* call =
NewNode(javascript()->CallRuntime(Runtime::kHandleDebuggerStatement));
environment()->BindAccumulator(call, Environment::kAttachFrameState);
environment()->MarkAllRegistersLive();
}
// We cannot create a graph from the debugger copy of the bytecode array.

View File

@ -135,10 +135,6 @@ class BytecodeGraphBuilder {
// Conceptually this frame state is "after" a given operation.
void PrepareFrameState(Node* node, OutputFrameStateCombine combine);
// Computes register liveness and replaces dead ones in frame states with the
// undefined values.
void ClearNonLiveSlotsInFrameStates();
void BuildCreateArguments(CreateArgumentsType type);
Node* BuildLoadGlobal(Handle<Name> name, uint32_t feedback_slot_index,
TypeofMode typeof_mode);
@ -260,8 +256,6 @@ class BytecodeGraphBuilder {
bytecode_analysis_ = bytecode_analysis;
}
LivenessAnalyzer* liveness_analyzer() { return &liveness_analyzer_; }
bool IsLivenessAnalysisEnabled() const {
return this->is_liveness_analysis_enabled_;
}
@ -307,9 +301,6 @@ class BytecodeGraphBuilder {
StateValuesCache state_values_cache_;
// Analyzer of register liveness.
LivenessAnalyzer liveness_analyzer_;
// The Turbofan source position table, to be populated.
SourcePositionTable* source_positions_;

View File

@ -0,0 +1,41 @@
// 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-liveness-map.h"
namespace v8 {
namespace internal {
namespace compiler {
Liveness::Liveness(int size, Zone* zone)
: in(new (zone) BitVector(size, zone)),
out(new (zone) BitVector(size, zone)) {}
BytecodeLivenessMap::BytecodeLivenessMap(int bytecode_size, Zone* zone)
: liveness_map_(base::bits::RoundUpToPowerOfTwo32(bytecode_size / 4 + 1),
base::KeyEqualityMatcher<int>(),
ZoneAllocationPolicy(zone)) {}
uint32_t OffsetHash(int offset) { return offset; }
Liveness& BytecodeLivenessMap::InitializeLiveness(int offset, int size,
Zone* zone) {
return liveness_map_
.LookupOrInsert(offset, OffsetHash(offset),
[&]() { return Liveness(size, zone); },
ZoneAllocationPolicy(zone))
->value;
}
Liveness& BytecodeLivenessMap::GetLiveness(int offset) {
return liveness_map_.Lookup(offset, OffsetHash(offset))->value;
}
const Liveness& BytecodeLivenessMap::GetLiveness(int offset) const {
return liveness_map_.Lookup(offset, OffsetHash(offset))->value;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,55 @@
// 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_LIVENESS_MAP_H_
#define V8_COMPILER_BYTECODE_LIVENESS_MAP_H_
#include "src/base/hashmap.h"
#include "src/bit-vector.h"
#include "src/zone/zone.h"
namespace v8 {
namespace internal {
class Zone;
namespace compiler {
struct Liveness {
BitVector* in;
BitVector* out;
Liveness(int size, Zone* zone);
};
class V8_EXPORT_PRIVATE BytecodeLivenessMap {
public:
BytecodeLivenessMap(int size, Zone* zone);
Liveness& InitializeLiveness(int offset, int size, Zone* zone);
Liveness& GetLiveness(int offset);
const Liveness& GetLiveness(int offset) const;
BitVector* GetInLiveness(int offset) { return GetLiveness(offset).in; }
const BitVector* GetInLiveness(int offset) const {
return GetLiveness(offset).in;
}
BitVector* GetOutLiveness(int offset) { return GetLiveness(offset).out; }
const BitVector* GetOutLiveness(int offset) const {
return GetLiveness(offset).out;
}
private:
base::TemplateHashMapImpl<int, Liveness, base::KeyEqualityMatcher<int>,
ZoneAllocationPolicy>
liveness_map_;
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_BYTECODE_LIVENESS_MAP_H_

View File

@ -189,6 +189,12 @@ int BytecodeArrayAccessor::GetJumpTargetOffset() const {
}
}
std::ostream& BytecodeArrayAccessor::PrintTo(std::ostream& os) const {
return BytecodeDecoder::Decode(
os, bytecode_array()->GetFirstBytecodeAddress() + bytecode_offset_,
bytecode_array()->parameter_count());
}
} // namespace interpreter
} // namespace internal
} // namespace v8

View File

@ -48,6 +48,8 @@ class V8_EXPORT_PRIVATE BytecodeArrayAccessor {
// not for a jump or conditional jump.
int GetJumpTargetOffset() const;
std::ostream& PrintTo(std::ostream& os) const;
private:
bool OffsetInBounds() const;

View File

@ -722,22 +722,25 @@ void BytecodeGenerator::VisitIterationHeader(IterationStatement* stmt,
LoopBuilder* loop_builder) {
// Recall that stmt->yield_count() is always zero inside ordinary
// (i.e. non-generator) functions.
if (stmt->yield_count() == 0) {
loop_builder->LoopHeader();
} else {
// Collect all labels for generator resume points within the loop (if any)
// so that they can be bound to the loop header below. Also create fresh
// labels for these resume points, to be used inside the loop.
ZoneVector<BytecodeLabel> resume_points_in_loop(zone());
size_t first_yield = stmt->first_yield_id();
DCHECK_LE(first_yield + stmt->yield_count(),
generator_resume_points_.size());
for (size_t id = first_yield; id < first_yield + stmt->yield_count();
id++) {
auto& label = generator_resume_points_[id];
resume_points_in_loop.push_back(label);
generator_resume_points_[id] = BytecodeLabel();
}
// Collect all labels for generator resume points within the loop (if any) so
// that they can be bound to the loop header below. Also create fresh labels
// for these resume points, to be used inside the loop.
ZoneVector<BytecodeLabel> resume_points_in_loop(zone());
size_t first_yield = stmt->first_yield_id();
DCHECK_LE(first_yield + stmt->yield_count(), generator_resume_points_.size());
for (size_t id = first_yield; id < first_yield + stmt->yield_count(); id++) {
auto& label = generator_resume_points_[id];
resume_points_in_loop.push_back(label);
generator_resume_points_[id] = BytecodeLabel();
}
loop_builder->LoopHeader(&resume_points_in_loop);
loop_builder->LoopHeader(&resume_points_in_loop);
if (stmt->yield_count() > 0) {
// If we are not resuming, fall through to loop body.
// If we are resuming, perform state dispatch.
BytecodeLabel not_resuming;

View File

@ -17,7 +17,7 @@ class BytecodeArrayBuilder;
// label is bound, it represents a known position in the bytecode
// array. For labels that are forward references there can be at most
// one reference whilst it is unbound.
class BytecodeLabel final {
class V8_EXPORT_PRIVATE BytecodeLabel final {
public:
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
@ -54,7 +54,7 @@ class BytecodeLabel final {
};
// Class representing a branch target of multiple jumps.
class BytecodeLabels {
class V8_EXPORT_PRIVATE BytecodeLabels {
public:
explicit BytecodeLabels(Zone* zone) : labels_(zone) {}

View File

@ -471,6 +471,12 @@ class V8_EXPORT_PRIVATE Bytecodes final {
IsConditionalJumpConstant(bytecode);
}
// Returns true if the bytecode is an unconditional jump.
static CONSTEXPR bool IsUnconditionalJump(Bytecode bytecode) {
return bytecode == Bytecode::kJump || bytecode == Bytecode::kJumpConstant ||
bytecode == Bytecode::kJumpLoop;
}
// Returns true if the bytecode is a jump or a conditional jump taking
// an immediate byte operand (OperandType::kImm).
static CONSTEXPR bool IsJumpImmediate(Bytecode bytecode) {
@ -500,6 +506,12 @@ class V8_EXPORT_PRIVATE Bytecodes final {
return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode);
}
// Returns true if the bytecode is a forward jump or conditional jump taking
// any kind of operand.
static CONSTEXPR bool IsForwardJump(Bytecode bytecode) {
return bytecode != Bytecode::kJumpLoop && IsJump(bytecode);
}
// Returns true if the bytecode is a conditional jump, a jump, or a return.
static CONSTEXPR bool IsJumpOrReturn(Bytecode bytecode) {
return bytecode == Bytecode::kReturn || IsJump(bytecode);

View File

@ -55,8 +55,10 @@ void LoopBuilder::LoopHeader(ZoneVector<BytecodeLabel>* additional_labels) {
// and misplaced between the headers.
DCHECK(break_labels_.empty() && continue_labels_.empty());
builder()->Bind(&loop_header_);
for (auto& label : *additional_labels) {
builder()->Bind(&label);
if (additional_labels != nullptr) {
for (auto& label : *additional_labels) {
builder()->Bind(&label);
}
}
}

View File

@ -14,7 +14,7 @@ namespace v8 {
namespace internal {
namespace interpreter {
class ControlFlowBuilder BASE_EMBEDDED {
class V8_EXPORT_PRIVATE ControlFlowBuilder BASE_EMBEDDED {
public:
explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
: builder_(builder) {}
@ -29,7 +29,8 @@ class ControlFlowBuilder BASE_EMBEDDED {
DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
};
class BreakableControlFlowBuilder : public ControlFlowBuilder {
class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
: public ControlFlowBuilder {
public:
explicit BreakableControlFlowBuilder(BytecodeArrayBuilder* builder)
: ControlFlowBuilder(builder), break_labels_(builder->zone()) {}
@ -63,7 +64,8 @@ class BreakableControlFlowBuilder : public ControlFlowBuilder {
// Class to track control flow for block statements (which can break in JS).
class BlockBuilder final : public BreakableControlFlowBuilder {
class V8_EXPORT_PRIVATE BlockBuilder final
: public BreakableControlFlowBuilder {
public:
explicit BlockBuilder(BytecodeArrayBuilder* builder)
: BreakableControlFlowBuilder(builder) {}
@ -77,7 +79,7 @@ class BlockBuilder final : public BreakableControlFlowBuilder {
// A class to help with co-ordinating break and continue statements with
// their loop.
class LoopBuilder final : public BreakableControlFlowBuilder {
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
public:
explicit LoopBuilder(BytecodeArrayBuilder* builder)
: BreakableControlFlowBuilder(builder),
@ -85,7 +87,7 @@ class LoopBuilder final : public BreakableControlFlowBuilder {
header_labels_(builder->zone()) {}
~LoopBuilder();
void LoopHeader(ZoneVector<BytecodeLabel>* additional_labels);
void LoopHeader(ZoneVector<BytecodeLabel>* additional_labels = nullptr);
void JumpToHeader(int loop_depth);
void BindContinueTarget();
void EndLoop();
@ -109,7 +111,8 @@ class LoopBuilder final : public BreakableControlFlowBuilder {
// A class to help with co-ordinating break statements with their switch.
class SwitchBuilder final : public BreakableControlFlowBuilder {
class V8_EXPORT_PRIVATE SwitchBuilder final
: public BreakableControlFlowBuilder {
public:
explicit SwitchBuilder(BytecodeArrayBuilder* builder, int number_of_cases)
: BreakableControlFlowBuilder(builder),
@ -139,7 +142,7 @@ class SwitchBuilder final : public BreakableControlFlowBuilder {
// A class to help with co-ordinating control flow in try-catch statements.
class TryCatchBuilder final : public ControlFlowBuilder {
class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
public:
explicit TryCatchBuilder(BytecodeArrayBuilder* builder,
HandlerTable::CatchPrediction catch_prediction)
@ -160,7 +163,7 @@ class TryCatchBuilder final : public ControlFlowBuilder {
// A class to help with co-ordinating control flow in try-finally statements.
class TryFinallyBuilder final : public ControlFlowBuilder {
class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder {
public:
explicit TryFinallyBuilder(BytecodeArrayBuilder* builder,
HandlerTable::CatchPrediction catch_prediction)

View File

@ -19,7 +19,7 @@ class Isolate;
namespace interpreter {
// A helper class for constructing exception handler tables for the interpreter.
class HandlerTableBuilder final BASE_EMBEDDED {
class V8_EXPORT_PRIVATE HandlerTableBuilder final BASE_EMBEDDED {
public:
explicit HandlerTableBuilder(Zone* zone);

View File

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

View File

@ -24,6 +24,8 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Flags: --noanalyze-environment-liveness
Debug = debug.Debug

View File

@ -24,6 +24,8 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Flags: --noanalyze-environment-liveness
Debug = debug.Debug

View File

@ -1,6 +1,8 @@
// 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.
//
// Flags: --noanalyze-environment-liveness
Debug = debug.Debug

View File

@ -1,6 +1,8 @@
// Copyright 2012 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.
//
// Flags: --noanalyze-environment-liveness
'use strict';

View File

@ -24,6 +24,8 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Flags: --noanalyze-environment-liveness
// Test that a variable in the local scope that shadows a context-allocated

View File

@ -1,6 +1,8 @@
// 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.
//
// Flags: --noanalyze-environment-liveness
var Debug = debug.Debug;

View File

@ -24,7 +24,8 @@
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Flags: --noanalyze-environment-liveness
Debug = debug.Debug

View File

@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Flags: --expose-debug-as debug --noanalyze-environment-liveness
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug

View File

@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug
// Flags: --expose-debug-as debug --noanalyze-environment-liveness
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug

View File

@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug --allow-natives-syntax
// Flags: --expose-debug-as debug --allow-natives-syntax --noanalyze-environment-liveness
// The functions used for testing backtraces. They are at the top to make the
// testing of source line/column easier.

View File

@ -25,7 +25,7 @@
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --expose-debug-as debug --allow-natives-syntax
// Flags: --expose-debug-as debug --allow-natives-syntax --noanalyze-environment-liveness
// The functions used for testing backtraces. They are at the top to make the
// testing of source line/column easier.

View File

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --expose-debug-as debug
// Flags: --expose-debug-as debug --noanalyze-environment-liveness
var Debug = debug.Debug;

View File

@ -3,7 +3,7 @@
// found in the LICENSE file.
// MODULE
// Flags: --expose-debug-as debug --allow-natives-syntax
// Flags: --expose-debug-as debug --allow-natives-syntax --noanalyze-environment-liveness
// These tests are copied from mjsunit/debug-scopes.js and adapted for modules.

View File

@ -31,6 +31,7 @@ v8_executable("unittests") {
"compiler-dispatcher/compiler-dispatcher-job-unittest.cc",
"compiler-dispatcher/compiler-dispatcher-tracer-unittest.cc",
"compiler/branch-elimination-unittest.cc",
"compiler/bytecode-analysis-unittest.cc",
"compiler/checkpoint-elimination-unittest.cc",
"compiler/common-operator-reducer-unittest.cc",
"compiler/common-operator-unittest.cc",

View File

@ -0,0 +1,415 @@
// 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/v8.h"
#include "src/compiler/bytecode-analysis.h"
#include "src/interpreter/bytecode-array-builder.h"
#include "src/interpreter/bytecode-array-iterator.h"
#include "src/interpreter/bytecode-decoder.h"
#include "src/interpreter/bytecode-label.h"
#include "src/interpreter/control-flow-builders.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace compiler {
class BytecodeAnalysisTest : public TestWithIsolateAndZone {
public:
BytecodeAnalysisTest() {}
~BytecodeAnalysisTest() override {}
static void SetUpTestCase() {
old_FLAG_ignition_peephole_ = i::FLAG_ignition_peephole;
i::FLAG_ignition_peephole = false;
old_FLAG_ignition_reo_ = i::FLAG_ignition_reo;
i::FLAG_ignition_reo = false;
TestWithIsolateAndZone::SetUpTestCase();
}
static void TearDownTestCase() {
TestWithIsolateAndZone::TearDownTestCase();
i::FLAG_ignition_peephole = old_FLAG_ignition_peephole_;
i::FLAG_ignition_reo = old_FLAG_ignition_reo_;
}
std::string ToLivenessString(const BitVector* liveness) const {
std::string out;
out.resize(liveness->length());
for (int i = 0; i < liveness->length(); ++i) {
if (liveness->Contains(i)) {
out[i] = 'L';
} else {
out[i] = '.';
}
}
return out;
}
void EnsureLivenessMatches(
Handle<BytecodeArray> bytecode,
const std::vector<std::pair<std::string, std::string>>&
expected_liveness) {
BytecodeAnalysis analysis(bytecode, zone(), true);
analysis.Analyze();
interpreter::BytecodeArrayIterator iterator(bytecode);
for (auto liveness : expected_liveness) {
std::stringstream ss;
ss << std::setw(4) << iterator.current_offset() << " : ";
iterator.PrintTo(ss);
EXPECT_EQ(liveness.first, ToLivenessString(analysis.GetInLivenessFor(
iterator.current_offset())))
<< " at bytecode " << ss.str();
EXPECT_EQ(liveness.second, ToLivenessString(analysis.GetOutLivenessFor(
iterator.current_offset())))
<< " at bytecode " << ss.str();
iterator.Advance();
}
EXPECT_TRUE(iterator.done());
}
private:
static bool old_FLAG_ignition_peephole_;
static bool old_FLAG_ignition_reo_;
DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysisTest);
};
bool BytecodeAnalysisTest::old_FLAG_ignition_peephole_;
bool BytecodeAnalysisTest::old_FLAG_ignition_reo_;
TEST_F(BytecodeAnalysisTest, EmptyBlock) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, SimpleLoad) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("L...", "...L");
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, StoreThenLoad) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back("...L", "L...");
builder.LoadNull();
expected_liveness.emplace_back("L...", "L...");
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("L...", "...L");
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, DiamondLoad) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
interpreter::Register reg_1(1);
interpreter::Register reg_2(2);
interpreter::BytecodeLabel ld1_label;
interpreter::BytecodeLabel end_label;
builder.JumpIfTrue(&ld1_label);
expected_liveness.emplace_back("LLLL", "LLL.");
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("L.L.", "..L.");
builder.Jump(&end_label);
expected_liveness.emplace_back("..L.", "..L.");
builder.Bind(&ld1_label);
builder.LoadAccumulatorWithRegister(reg_1);
expected_liveness.emplace_back(".LL.", "..L.");
builder.Bind(&end_label);
builder.LoadAccumulatorWithRegister(reg_2);
expected_liveness.emplace_back("..L.", "...L");
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, DiamondLookupsAndBinds) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
interpreter::Register reg_1(1);
interpreter::Register reg_2(2);
interpreter::BytecodeLabel ld1_label;
interpreter::BytecodeLabel end_label;
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".LLL", "LLLL");
builder.JumpIfTrue(&ld1_label);
expected_liveness.emplace_back("LLLL", "LLL.");
{
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("L...", "...L");
builder.StoreAccumulatorInRegister(reg_2);
expected_liveness.emplace_back("...L", "..L.");
builder.Jump(&end_label);
expected_liveness.emplace_back("..L.", "..L.");
}
builder.Bind(&ld1_label);
{
builder.LoadAccumulatorWithRegister(reg_1);
expected_liveness.emplace_back(".LL.", "..L.");
}
builder.Bind(&end_label);
builder.LoadAccumulatorWithRegister(reg_2);
expected_liveness.emplace_back("..L.", "...L");
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, SimpleLoop) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
interpreter::Register reg_1(1);
interpreter::Register reg_2(2);
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back("..LL", "L.LL");
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
{
builder.JumpIfTrue(loop_builder.break_labels()->New());
expected_liveness.emplace_back("L.LL", "L.L.");
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("L...", "L..L");
builder.StoreAccumulatorInRegister(reg_2);
expected_liveness.emplace_back("L..L", "L.LL");
loop_builder.BindContinueTarget();
loop_builder.JumpToHeader(0);
expected_liveness.emplace_back("L.LL", "L.LL");
}
loop_builder.EndLoop();
builder.LoadAccumulatorWithRegister(reg_2);
expected_liveness.emplace_back("..L.", "...L");
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, TryCatch) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
interpreter::Register reg_1(1);
interpreter::Register reg_context(2);
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".LLL", "LLL.");
interpreter::TryCatchBuilder try_builder(&builder, HandlerTable::CAUGHT);
try_builder.BeginTry(reg_context);
{
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("LLL.", ".LLL");
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".LLL", ".LL.");
builder.CallRuntime(Runtime::kThrow);
expected_liveness.emplace_back(".LL.", ".LLL");
builder.StoreAccumulatorInRegister(reg_0);
// Star can't throw, so doesn't take handler liveness
expected_liveness.emplace_back("...L", "...L");
}
try_builder.EndTry();
expected_liveness.emplace_back("...L", "...L");
// Catch
{
builder.LoadAccumulatorWithRegister(reg_1);
expected_liveness.emplace_back(".L..", "...L");
}
try_builder.EndCatch();
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, DiamondInLoop) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
interpreter::Register reg_1(1);
interpreter::Register reg_2(2);
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back("...L", "L..L");
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
{
builder.JumpIfTrue(loop_builder.break_labels()->New());
expected_liveness.emplace_back("L..L", "L..L");
interpreter::BytecodeLabel ld1_label;
interpreter::BytecodeLabel end_label;
builder.JumpIfTrue(&ld1_label);
expected_liveness.emplace_back("L..L", "L..L");
{
builder.Jump(&end_label);
expected_liveness.emplace_back("L..L", "L..L");
}
builder.Bind(&ld1_label);
{
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("L...", "L..L");
}
builder.Bind(&end_label);
loop_builder.BindContinueTarget();
loop_builder.JumpToHeader(0);
expected_liveness.emplace_back("L..L", "L..L");
}
loop_builder.EndLoop();
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
TEST_F(BytecodeAnalysisTest, KillingLoopInsideLoop) {
interpreter::BytecodeArrayBuilder builder(isolate(), zone(), 3, 0, 3);
std::vector<std::pair<std::string, std::string>> expected_liveness;
interpreter::Register reg_0(0);
interpreter::Register reg_1(1);
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".L.L", "LL..");
interpreter::LoopBuilder loop_builder(&builder);
loop_builder.LoopHeader();
{
builder.LoadAccumulatorWithRegister(reg_0);
expected_liveness.emplace_back("LL..", ".L..");
builder.LoadAccumulatorWithRegister(reg_1);
expected_liveness.emplace_back(".L..", ".L.L");
builder.JumpIfTrue(loop_builder.break_labels()->New());
expected_liveness.emplace_back(".L.L", ".L.L");
interpreter::LoopBuilder inner_loop_builder(&builder);
inner_loop_builder.LoopHeader();
{
builder.StoreAccumulatorInRegister(reg_0);
expected_liveness.emplace_back(".L.L", "LL.L");
builder.JumpIfTrue(inner_loop_builder.break_labels()->New());
expected_liveness.emplace_back("LL.L", "LL.L");
inner_loop_builder.BindContinueTarget();
inner_loop_builder.JumpToHeader(1);
expected_liveness.emplace_back(".L.L", ".L.L");
}
inner_loop_builder.EndLoop();
loop_builder.BindContinueTarget();
loop_builder.JumpToHeader(0);
expected_liveness.emplace_back("LL..", "LL..");
}
loop_builder.EndLoop();
builder.Return();
expected_liveness.emplace_back("...L", "....");
Handle<BytecodeArray> bytecode = builder.ToBytecodeArray(isolate());
EnsureLivenessMatches(bytecode, expected_liveness);
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -27,6 +27,7 @@
'cancelable-tasks-unittest.cc',
'char-predicates-unittest.cc',
'compiler/branch-elimination-unittest.cc',
'compiler/bytecode-analysis-unittest.cc',
'compiler/checkpoint-elimination-unittest.cc',
'compiler/common-operator-reducer-unittest.cc',
'compiler/common-operator-unittest.cc',