Revert of [ignition/turbo] Perform liveness analysis on the bytecodes (patchset #17 id:320001 of https://codereview.chromium.org/2523893003/ )
Reason for revert: Breaks the build: https://build.chromium.org/p/client.v8/builders/V8%20Linux%20-%20shared/builds/14886 Original issue's description: > [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. > > Committed: https://crrev.com/1852300954c216c29cf93444430681d213e87925 > Cr-Commit-Position: refs/heads/master@{#41344} TBR=jarin@chromium.org,rmcilroy@chromium.org,yangguo@chromium.org # Skipping CQ checks because original CL landed less than 1 days ago. NOPRESUBMIT=true NOTREECHECKS=true NOTRY=true Review-Url: https://codereview.chromium.org/2541443002 Cr-Commit-Position: refs/heads/master@{#41346}
This commit is contained in:
parent
777e142ca1
commit
a2e2a39ff1
2
BUILD.gn
2
BUILD.gn
@ -1024,8 +1024,6 @@ v8_source_set("v8_base") {
|
|||||||
"src/compiler/bytecode-analysis.h",
|
"src/compiler/bytecode-analysis.h",
|
||||||
"src/compiler/bytecode-graph-builder.cc",
|
"src/compiler/bytecode-graph-builder.cc",
|
||||||
"src/compiler/bytecode-graph-builder.h",
|
"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/c-linkage.cc",
|
||||||
"src/compiler/checkpoint-elimination.cc",
|
"src/compiler/checkpoint-elimination.cc",
|
||||||
"src/compiler/checkpoint-elimination.h",
|
"src/compiler/checkpoint-elimination.h",
|
||||||
|
1
src/DEPS
1
src/DEPS
@ -10,7 +10,6 @@ include_rules = [
|
|||||||
"+src/heap/heap-inl.h",
|
"+src/heap/heap-inl.h",
|
||||||
"-src/inspector",
|
"-src/inspector",
|
||||||
"-src/interpreter",
|
"-src/interpreter",
|
||||||
"+src/interpreter/bytecode-array-accessor.h",
|
|
||||||
"+src/interpreter/bytecode-array-iterator.h",
|
"+src/interpreter/bytecode-array-iterator.h",
|
||||||
"+src/interpreter/bytecode-array-reverse-iterator.h",
|
"+src/interpreter/bytecode-array-reverse-iterator.h",
|
||||||
"+src/interpreter/bytecode-decoder.h",
|
"+src/interpreter/bytecode-decoder.h",
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
#include "src/compiler/bytecode-analysis.h"
|
#include "src/compiler/bytecode-analysis.h"
|
||||||
|
|
||||||
#include "src/interpreter/bytecode-array-iterator.h"
|
|
||||||
#include "src/interpreter/bytecode-array-reverse-iterator.h"
|
#include "src/interpreter/bytecode-array-reverse-iterator.h"
|
||||||
#include "src/objects-inl.h"
|
#include "src/objects-inl.h"
|
||||||
|
|
||||||
@ -12,262 +11,30 @@ namespace v8 {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
|
|
||||||
using namespace interpreter;
|
|
||||||
|
|
||||||
BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
|
BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
|
||||||
Zone* zone, bool do_liveness_analysis)
|
Zone* zone)
|
||||||
: bytecode_array_(bytecode_array),
|
: bytecode_array_(bytecode_array),
|
||||||
do_liveness_analysis_(do_liveness_analysis),
|
|
||||||
zone_(zone),
|
zone_(zone),
|
||||||
loop_stack_(zone),
|
loop_stack_(zone),
|
||||||
end_to_header_(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() {
|
void BytecodeAnalysis::Analyze() {
|
||||||
loop_stack_.push(-1);
|
loop_stack_.push(-1);
|
||||||
|
|
||||||
// The last JumpLoop that we haven't done a guaranteed valid liveness pass
|
interpreter::BytecodeArrayReverseIterator iterator(bytecode_array(), zone());
|
||||||
// over. See the below wall of text for a more thorough explanation.
|
while (!iterator.done()) {
|
||||||
int last_invalid_jumploop_offset = -1;
|
interpreter::Bytecode bytecode = iterator.current_bytecode();
|
||||||
|
if (bytecode == interpreter::Bytecode::kJumpLoop) {
|
||||||
BytecodeArrayReverseIterator iterator(bytecode_array(), zone());
|
PushLoop(iterator.GetJumpTargetOffset(), iterator.current_offset());
|
||||||
for (; !iterator.done(); iterator.Advance()) {
|
} else if (iterator.current_offset() == loop_stack_.top()) {
|
||||||
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();
|
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_.size(), 1u);
|
||||||
DCHECK_EQ(loop_stack_.top(), -1);
|
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) {
|
void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
|
||||||
@ -325,163 +92,6 @@ int BytecodeAnalysis::GetParentLoopFor(int header_offset) const {
|
|||||||
return header_to_parent_.find(header_offset)->second;
|
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 compiler
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -5,9 +5,6 @@
|
|||||||
#ifndef V8_COMPILER_BYTECODE_ANALYSIS_H_
|
#ifndef V8_COMPILER_BYTECODE_ANALYSIS_H_
|
||||||
#define 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/handles.h"
|
||||||
#include "src/zone/zone-containers.h"
|
#include "src/zone/zone-containers.h"
|
||||||
|
|
||||||
@ -18,10 +15,9 @@ class BytecodeArray;
|
|||||||
|
|
||||||
namespace compiler {
|
namespace compiler {
|
||||||
|
|
||||||
class V8_EXPORT_PRIVATE BytecodeAnalysis BASE_EMBEDDED {
|
class BytecodeAnalysis BASE_EMBEDDED {
|
||||||
public:
|
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
|
// Analyze the bytecodes to find the loop ranges and nesting. No other
|
||||||
// methods in this class return valid information until this has been called.
|
// methods in this class return valid information until this has been called.
|
||||||
@ -36,33 +32,13 @@ class V8_EXPORT_PRIVATE BytecodeAnalysis BASE_EMBEDDED {
|
|||||||
// at {header_offset}, or -1 for outer-most loops.
|
// at {header_offset}, or -1 for outer-most loops.
|
||||||
int GetParentLoopFor(int header_offset) const;
|
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:
|
private:
|
||||||
void PushLoop(int loop_header, int loop_end);
|
void PushLoop(int loop_header, int loop_end);
|
||||||
|
|
||||||
#if DEBUG
|
|
||||||
bool LivenessIsValid();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Zone* zone() const { return zone_; }
|
Zone* zone() const { return zone_; }
|
||||||
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
|
Handle<BytecodeArray> bytecode_array() const { return bytecode_array_; }
|
||||||
|
|
||||||
private:
|
|
||||||
Handle<BytecodeArray> bytecode_array_;
|
Handle<BytecodeArray> bytecode_array_;
|
||||||
bool do_liveness_analysis_;
|
|
||||||
Zone* zone_;
|
Zone* zone_;
|
||||||
|
|
||||||
ZoneStack<int> loop_stack_;
|
ZoneStack<int> loop_stack_;
|
||||||
@ -70,8 +46,6 @@ class V8_EXPORT_PRIVATE BytecodeAnalysis BASE_EMBEDDED {
|
|||||||
ZoneMap<int, int> end_to_header_;
|
ZoneMap<int, int> end_to_header_;
|
||||||
ZoneMap<int, int> header_to_parent_;
|
ZoneMap<int, int> header_to_parent_;
|
||||||
|
|
||||||
BytecodeLivenessMap liveness_map_;
|
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysis);
|
DISALLOW_COPY_AND_ASSIGN(BytecodeAnalysis);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
|||||||
|
|
||||||
Node* LookupAccumulator() const;
|
Node* LookupAccumulator() const;
|
||||||
Node* LookupRegister(interpreter::Register the_register) const;
|
Node* LookupRegister(interpreter::Register the_register) const;
|
||||||
|
void MarkAllRegistersLive();
|
||||||
|
|
||||||
void BindAccumulator(Node* node,
|
void BindAccumulator(Node* node,
|
||||||
FrameStateAttachmentMode mode = kDontAttachFrameState);
|
FrameStateAttachmentMode mode = kDontAttachFrameState);
|
||||||
@ -55,7 +56,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
|||||||
// Preserve a checkpoint of the environment for the IR graph. Any
|
// Preserve a checkpoint of the environment for the IR graph. Any
|
||||||
// further mutation of the environment will not affect checkpoints.
|
// further mutation of the environment will not affect checkpoints.
|
||||||
Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine,
|
Node* Checkpoint(BailoutId bytecode_offset, OutputFrameStateCombine combine,
|
||||||
bool owner_has_exception, const BitVector* liveness);
|
bool owner_has_exception);
|
||||||
|
|
||||||
// Control dependency tracked by this environment.
|
// Control dependency tracked by this environment.
|
||||||
Node* GetControlDependency() const { return control_dependency_; }
|
Node* GetControlDependency() const { return control_dependency_; }
|
||||||
@ -75,20 +76,21 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
|||||||
void PrepareForLoopExit(Node* loop);
|
void PrepareForLoopExit(Node* loop);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit Environment(const Environment* copy);
|
Environment(const Environment* copy, LivenessAnalyzerBlock* liveness_block);
|
||||||
void PrepareForLoop();
|
void PrepareForLoop();
|
||||||
|
|
||||||
bool StateValuesRequireUpdate(Node** state_values, Node** values, int count);
|
bool StateValuesRequireUpdate(Node** state_values, int offset, int count);
|
||||||
void UpdateStateValues(Node** state_values, Node** values, int count);
|
void UpdateStateValues(Node** state_values, int offset, int count);
|
||||||
void UpdateStateValuesWithCache(Node** state_values, Node** values,
|
|
||||||
int count);
|
|
||||||
|
|
||||||
int RegisterToValuesIndex(interpreter::Register the_register) const;
|
int RegisterToValuesIndex(interpreter::Register the_register) const;
|
||||||
|
|
||||||
|
bool IsLivenessBlockConsistent() const;
|
||||||
|
|
||||||
Zone* zone() const { return builder_->local_zone(); }
|
Zone* zone() const { return builder_->local_zone(); }
|
||||||
Graph* graph() const { return builder_->graph(); }
|
Graph* graph() const { return builder_->graph(); }
|
||||||
CommonOperatorBuilder* common() const { return builder_->common(); }
|
CommonOperatorBuilder* common() const { return builder_->common(); }
|
||||||
BytecodeGraphBuilder* builder() const { return builder_; }
|
BytecodeGraphBuilder* builder() const { return builder_; }
|
||||||
|
LivenessAnalyzerBlock* liveness_block() const { return liveness_block_; }
|
||||||
const NodeVector* values() const { return &values_; }
|
const NodeVector* values() const { return &values_; }
|
||||||
NodeVector* values() { return &values_; }
|
NodeVector* values() { return &values_; }
|
||||||
int register_base() const { return register_base_; }
|
int register_base() const { return register_base_; }
|
||||||
@ -97,6 +99,7 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
|||||||
BytecodeGraphBuilder* builder_;
|
BytecodeGraphBuilder* builder_;
|
||||||
int register_count_;
|
int register_count_;
|
||||||
int parameter_count_;
|
int parameter_count_;
|
||||||
|
LivenessAnalyzerBlock* liveness_block_;
|
||||||
Node* context_;
|
Node* context_;
|
||||||
Node* control_dependency_;
|
Node* control_dependency_;
|
||||||
Node* effect_dependency_;
|
Node* effect_dependency_;
|
||||||
@ -106,10 +109,6 @@ class BytecodeGraphBuilder::Environment : public ZoneObject {
|
|||||||
Node* accumulator_state_values_;
|
Node* accumulator_state_values_;
|
||||||
int register_base_;
|
int register_base_;
|
||||||
int accumulator_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_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -124,14 +123,16 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder,
|
|||||||
: builder_(builder),
|
: builder_(builder),
|
||||||
register_count_(register_count),
|
register_count_(register_count),
|
||||||
parameter_count_(parameter_count),
|
parameter_count_(parameter_count),
|
||||||
|
liveness_block_(builder->is_liveness_analysis_enabled_
|
||||||
|
? builder_->liveness_analyzer()->NewBlock()
|
||||||
|
: nullptr),
|
||||||
context_(context),
|
context_(context),
|
||||||
control_dependency_(control_dependency),
|
control_dependency_(control_dependency),
|
||||||
effect_dependency_(control_dependency),
|
effect_dependency_(control_dependency),
|
||||||
values_(builder->local_zone()),
|
values_(builder->local_zone()),
|
||||||
parameters_state_values_(nullptr),
|
parameters_state_values_(nullptr),
|
||||||
registers_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:
|
// The layout of values_ is:
|
||||||
//
|
//
|
||||||
// [receiver] [parameters] [registers] [accumulator]
|
// [receiver] [parameters] [registers] [accumulator]
|
||||||
@ -156,15 +157,15 @@ BytecodeGraphBuilder::Environment::Environment(BytecodeGraphBuilder* builder,
|
|||||||
// Accumulator
|
// Accumulator
|
||||||
accumulator_base_ = static_cast<int>(values()->size());
|
accumulator_base_ = static_cast<int>(values()->size());
|
||||||
values()->push_back(undefined_constant);
|
values()->push_back(undefined_constant);
|
||||||
|
|
||||||
state_value_working_area_.resize(register_count_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BytecodeGraphBuilder::Environment::Environment(
|
BytecodeGraphBuilder::Environment::Environment(
|
||||||
const BytecodeGraphBuilder::Environment* other)
|
const BytecodeGraphBuilder::Environment* other,
|
||||||
|
LivenessAnalyzerBlock* liveness_block)
|
||||||
: builder_(other->builder_),
|
: builder_(other->builder_),
|
||||||
register_count_(other->register_count_),
|
register_count_(other->register_count_),
|
||||||
parameter_count_(other->parameter_count_),
|
parameter_count_(other->parameter_count_),
|
||||||
|
liveness_block_(liveness_block),
|
||||||
context_(other->context_),
|
context_(other->context_),
|
||||||
control_dependency_(other->control_dependency_),
|
control_dependency_(other->control_dependency_),
|
||||||
effect_dependency_(other->effect_dependency_),
|
effect_dependency_(other->effect_dependency_),
|
||||||
@ -173,9 +174,7 @@ BytecodeGraphBuilder::Environment::Environment(
|
|||||||
registers_state_values_(nullptr),
|
registers_state_values_(nullptr),
|
||||||
accumulator_state_values_(nullptr),
|
accumulator_state_values_(nullptr),
|
||||||
register_base_(other->register_base_),
|
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_;
|
values_ = other->values_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -189,7 +188,16 @@ int BytecodeGraphBuilder::Environment::RegisterToValuesIndex(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool BytecodeGraphBuilder::Environment::IsLivenessBlockConsistent() const {
|
||||||
|
return !builder_->IsLivenessAnalysisEnabled() ==
|
||||||
|
(liveness_block() == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const {
|
Node* BytecodeGraphBuilder::Environment::LookupAccumulator() const {
|
||||||
|
DCHECK(IsLivenessBlockConsistent());
|
||||||
|
if (liveness_block() != nullptr) {
|
||||||
|
liveness_block()->LookupAccumulator();
|
||||||
|
}
|
||||||
return values()->at(accumulator_base_);
|
return values()->at(accumulator_base_);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,15 +212,32 @@ Node* BytecodeGraphBuilder::Environment::LookupRegister(
|
|||||||
return builder()->GetNewTarget();
|
return builder()->GetNewTarget();
|
||||||
} else {
|
} else {
|
||||||
int values_index = RegisterToValuesIndex(the_register);
|
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);
|
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(
|
void BytecodeGraphBuilder::Environment::BindAccumulator(
|
||||||
Node* node, FrameStateAttachmentMode mode) {
|
Node* node, FrameStateAttachmentMode mode) {
|
||||||
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
|
if (mode == FrameStateAttachmentMode::kAttachFrameState) {
|
||||||
builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(0));
|
builder()->PrepareFrameState(node, OutputFrameStateCombine::PokeAt(0));
|
||||||
}
|
}
|
||||||
|
DCHECK(IsLivenessBlockConsistent());
|
||||||
|
if (liveness_block() != nullptr) {
|
||||||
|
liveness_block()->BindAccumulator();
|
||||||
|
}
|
||||||
values()->at(accumulator_base_) = node;
|
values()->at(accumulator_base_) = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,6 +250,10 @@ void BytecodeGraphBuilder::Environment::BindRegister(
|
|||||||
accumulator_base_ - values_index));
|
accumulator_base_ - values_index));
|
||||||
}
|
}
|
||||||
values()->at(values_index) = node;
|
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(
|
void BytecodeGraphBuilder::Environment::BindRegistersToProjections(
|
||||||
@ -252,22 +281,41 @@ void BytecodeGraphBuilder::Environment::RecordAfterState(
|
|||||||
BytecodeGraphBuilder::Environment*
|
BytecodeGraphBuilder::Environment*
|
||||||
BytecodeGraphBuilder::Environment::CopyForLoop() {
|
BytecodeGraphBuilder::Environment::CopyForLoop() {
|
||||||
PrepareForLoop();
|
PrepareForLoop();
|
||||||
return new (zone()) Environment(this);
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
BytecodeGraphBuilder::Environment*
|
BytecodeGraphBuilder::Environment*
|
||||||
BytecodeGraphBuilder::Environment::CopyForOsrEntry() {
|
BytecodeGraphBuilder::Environment::CopyForOsrEntry() {
|
||||||
return new (zone()) Environment(this);
|
return new (zone())
|
||||||
|
Environment(this, builder_->liveness_analyzer()->NewBlock());
|
||||||
}
|
}
|
||||||
|
|
||||||
BytecodeGraphBuilder::Environment*
|
BytecodeGraphBuilder::Environment*
|
||||||
BytecodeGraphBuilder::Environment::CopyForConditional() {
|
BytecodeGraphBuilder::Environment::CopyForConditional() {
|
||||||
return new (zone()) Environment(this);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void BytecodeGraphBuilder::Environment::Merge(
|
void BytecodeGraphBuilder::Environment::Merge(
|
||||||
BytecodeGraphBuilder::Environment* other) {
|
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
|
// Create a merge of the control dependencies of both environments and update
|
||||||
// the current environment's control dependency accordingly.
|
// the current environment's control dependency accordingly.
|
||||||
Node* control = builder()->MergeControl(GetControlDependency(),
|
Node* control = builder()->MergeControl(GetControlDependency(),
|
||||||
@ -335,7 +383,7 @@ void BytecodeGraphBuilder::Environment::PrepareForOsrEntry() {
|
|||||||
|
|
||||||
BailoutId loop_id(builder_->bytecode_iterator().current_offset());
|
BailoutId loop_id(builder_->bytecode_iterator().current_offset());
|
||||||
Node* frame_state =
|
Node* frame_state =
|
||||||
Checkpoint(loop_id, OutputFrameStateCombine::Ignore(), false, nullptr);
|
Checkpoint(loop_id, OutputFrameStateCombine::Ignore(), false);
|
||||||
Node* checkpoint =
|
Node* checkpoint =
|
||||||
graph()->NewNode(common()->Checkpoint(), frame_state, entry, entry);
|
graph()->NewNode(common()->Checkpoint(), frame_state, entry, entry);
|
||||||
UpdateEffectDependency(checkpoint);
|
UpdateEffectDependency(checkpoint);
|
||||||
@ -353,13 +401,15 @@ void BytecodeGraphBuilder::Environment::PrepareForOsrEntry() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
|
bool BytecodeGraphBuilder::Environment::StateValuesRequireUpdate(
|
||||||
Node** state_values, Node** values, int count) {
|
Node** state_values, int offset, int count) {
|
||||||
if (*state_values == nullptr) {
|
if (*state_values == nullptr) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
DCHECK_EQ((*state_values)->InputCount(), count);
|
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++) {
|
for (int i = 0; i < count; i++) {
|
||||||
if ((*state_values)->InputAt(i) != values[i]) {
|
if ((*state_values)->InputAt(i) != env_values[i]) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -393,51 +443,21 @@ void BytecodeGraphBuilder::Environment::PrepareForLoopExit(Node* loop) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
|
void BytecodeGraphBuilder::Environment::UpdateStateValues(Node** state_values,
|
||||||
Node** values,
|
int offset,
|
||||||
int count) {
|
int count) {
|
||||||
if (StateValuesRequireUpdate(state_values, values, count)) {
|
if (StateValuesRequireUpdate(state_values, offset, count)) {
|
||||||
const Operator* op = common()->StateValues(count);
|
const Operator* op = common()->StateValues(count);
|
||||||
(*state_values) = graph()->NewNode(op, count, values);
|
(*state_values) = graph()->NewNode(op, count, &values()->at(offset));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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(
|
Node* BytecodeGraphBuilder::Environment::Checkpoint(
|
||||||
BailoutId bailout_id, OutputFrameStateCombine combine,
|
BailoutId bailout_id, OutputFrameStateCombine combine,
|
||||||
bool owner_has_exception, const BitVector* liveness) {
|
bool owner_has_exception) {
|
||||||
UpdateStateValues(¶meters_state_values_, &values()->at(0),
|
UpdateStateValues(¶meters_state_values_, 0, parameter_count());
|
||||||
parameter_count());
|
UpdateStateValues(®isters_state_values_, register_base(),
|
||||||
|
register_count());
|
||||||
if (liveness) {
|
UpdateStateValues(&accumulator_state_values_, accumulator_base(), 1);
|
||||||
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(®isters_state_values_,
|
|
||||||
state_value_working_area_.data(),
|
|
||||||
register_count());
|
|
||||||
|
|
||||||
UpdateStateValues(&accumulator_state_values_, &accumulator_value, 1);
|
|
||||||
} else {
|
|
||||||
UpdateStateValuesWithCache(®isters_state_values_,
|
|
||||||
&values()->at(register_base()),
|
|
||||||
register_count());
|
|
||||||
UpdateStateValues(&accumulator_state_values_,
|
|
||||||
&values()->at(accumulator_base()), 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const Operator* op = common()->FrameState(
|
const Operator* op = common()->FrameState(
|
||||||
bailout_id, combine, builder()->frame_state_function_info());
|
bailout_id, combine, builder()->frame_state_function_info());
|
||||||
@ -446,6 +466,18 @@ Node* BytecodeGraphBuilder::Environment::Checkpoint(
|
|||||||
accumulator_state_values_, Context(), builder()->GetFunctionClosure(),
|
accumulator_state_values_, Context(), builder()->GetFunctionClosure(),
|
||||||
builder()->graph()->start());
|
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;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -473,6 +505,9 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
|
|||||||
exit_controls_(local_zone),
|
exit_controls_(local_zone),
|
||||||
is_liveness_analysis_enabled_(FLAG_analyze_environment_liveness),
|
is_liveness_analysis_enabled_(FLAG_analyze_environment_liveness),
|
||||||
state_values_cache_(jsgraph),
|
state_values_cache_(jsgraph),
|
||||||
|
liveness_analyzer_(
|
||||||
|
static_cast<size_t>(bytecode_array()->register_count()), true,
|
||||||
|
local_zone),
|
||||||
source_positions_(source_positions),
|
source_positions_(source_positions),
|
||||||
start_position_(info->shared_info()->start_position(), inlining_id) {
|
start_position_(info->shared_info()->start_position(), inlining_id) {
|
||||||
// Bytecode graph builder assumes deoptimziation is enabled.
|
// Bytecode graph builder assumes deoptimziation is enabled.
|
||||||
@ -553,6 +588,8 @@ bool BytecodeGraphBuilder::CreateGraph(bool stack_check) {
|
|||||||
Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs);
|
Node* end = graph()->NewNode(common()->End(input_count), input_count, inputs);
|
||||||
graph()->SetEnd(end);
|
graph()->SetEnd(end);
|
||||||
|
|
||||||
|
ClearNonLiveSlotsInFrameStates();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -565,12 +602,8 @@ void BytecodeGraphBuilder::PrepareEagerCheckpoint() {
|
|||||||
DCHECK_EQ(IrOpcode::kDead,
|
DCHECK_EQ(IrOpcode::kDead,
|
||||||
NodeProperties::GetFrameStateInput(node)->opcode());
|
NodeProperties::GetFrameStateInput(node)->opcode());
|
||||||
BailoutId bailout_id(bytecode_iterator().current_offset());
|
BailoutId bailout_id(bytecode_iterator().current_offset());
|
||||||
|
|
||||||
const BitVector* liveness_before = bytecode_analysis()->GetInLivenessFor(
|
|
||||||
bytecode_iterator().current_offset());
|
|
||||||
|
|
||||||
Node* frame_state_before = environment()->Checkpoint(
|
Node* frame_state_before = environment()->Checkpoint(
|
||||||
bailout_id, OutputFrameStateCombine::Ignore(), false, liveness_before);
|
bailout_id, OutputFrameStateCombine::Ignore(), false);
|
||||||
NodeProperties::ReplaceFrameStateInput(node, frame_state_before);
|
NodeProperties::ReplaceFrameStateInput(node, frame_state_before);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -585,19 +618,28 @@ void BytecodeGraphBuilder::PrepareFrameState(Node* node,
|
|||||||
NodeProperties::GetFrameStateInput(node)->opcode());
|
NodeProperties::GetFrameStateInput(node)->opcode());
|
||||||
BailoutId bailout_id(bytecode_iterator().current_offset());
|
BailoutId bailout_id(bytecode_iterator().current_offset());
|
||||||
bool has_exception = NodeProperties::IsExceptionalCall(node);
|
bool has_exception = NodeProperties::IsExceptionalCall(node);
|
||||||
|
Node* frame_state_after =
|
||||||
const BitVector* liveness_after = bytecode_analysis()->GetOutLivenessFor(
|
environment()->Checkpoint(bailout_id, combine, has_exception);
|
||||||
bytecode_iterator().current_offset());
|
|
||||||
|
|
||||||
Node* frame_state_after = environment()->Checkpoint(
|
|
||||||
bailout_id, combine, has_exception, liveness_after);
|
|
||||||
NodeProperties::ReplaceFrameStateInput(node, frame_state_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) {
|
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();
|
bytecode_analysis.Analyze();
|
||||||
set_bytecode_analysis(&bytecode_analysis);
|
set_bytecode_analysis(&bytecode_analysis);
|
||||||
|
|
||||||
@ -606,14 +648,7 @@ void BytecodeGraphBuilder::VisitBytecodes(bool stack_check) {
|
|||||||
SourcePositionTableIterator source_position_iterator(
|
SourcePositionTableIterator source_position_iterator(
|
||||||
bytecode_array()->source_position_table());
|
bytecode_array()->source_position_table());
|
||||||
|
|
||||||
if (FLAG_trace_environment_liveness) {
|
|
||||||
OFStream of(stdout);
|
|
||||||
|
|
||||||
bytecode_analysis.PrintLivenessTo(of);
|
|
||||||
}
|
|
||||||
|
|
||||||
BuildOSRNormalEntryPoint();
|
BuildOSRNormalEntryPoint();
|
||||||
|
|
||||||
for (; !iterator.done(); iterator.Advance()) {
|
for (; !iterator.done(); iterator.Advance()) {
|
||||||
int current_offset = iterator.current_offset();
|
int current_offset = iterator.current_offset();
|
||||||
UpdateCurrentSourcePosition(&source_position_iterator, current_offset);
|
UpdateCurrentSourcePosition(&source_position_iterator, current_offset);
|
||||||
@ -1734,6 +1769,7 @@ void BytecodeGraphBuilder::VisitDebugger() {
|
|||||||
Node* call =
|
Node* call =
|
||||||
NewNode(javascript()->CallRuntime(Runtime::kHandleDebuggerStatement));
|
NewNode(javascript()->CallRuntime(Runtime::kHandleDebuggerStatement));
|
||||||
environment()->BindAccumulator(call, Environment::kAttachFrameState);
|
environment()->BindAccumulator(call, Environment::kAttachFrameState);
|
||||||
|
environment()->MarkAllRegistersLive();
|
||||||
}
|
}
|
||||||
|
|
||||||
// We cannot create a graph from the debugger copy of the bytecode array.
|
// We cannot create a graph from the debugger copy of the bytecode array.
|
||||||
|
@ -135,6 +135,10 @@ class BytecodeGraphBuilder {
|
|||||||
// Conceptually this frame state is "after" a given operation.
|
// Conceptually this frame state is "after" a given operation.
|
||||||
void PrepareFrameState(Node* node, OutputFrameStateCombine combine);
|
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);
|
void BuildCreateArguments(CreateArgumentsType type);
|
||||||
Node* BuildLoadGlobal(Handle<Name> name, uint32_t feedback_slot_index,
|
Node* BuildLoadGlobal(Handle<Name> name, uint32_t feedback_slot_index,
|
||||||
TypeofMode typeof_mode);
|
TypeofMode typeof_mode);
|
||||||
@ -256,6 +260,8 @@ class BytecodeGraphBuilder {
|
|||||||
bytecode_analysis_ = bytecode_analysis;
|
bytecode_analysis_ = bytecode_analysis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LivenessAnalyzer* liveness_analyzer() { return &liveness_analyzer_; }
|
||||||
|
|
||||||
bool IsLivenessAnalysisEnabled() const {
|
bool IsLivenessAnalysisEnabled() const {
|
||||||
return this->is_liveness_analysis_enabled_;
|
return this->is_liveness_analysis_enabled_;
|
||||||
}
|
}
|
||||||
@ -301,6 +307,9 @@ class BytecodeGraphBuilder {
|
|||||||
|
|
||||||
StateValuesCache state_values_cache_;
|
StateValuesCache state_values_cache_;
|
||||||
|
|
||||||
|
// Analyzer of register liveness.
|
||||||
|
LivenessAnalyzer liveness_analyzer_;
|
||||||
|
|
||||||
// The Turbofan source position table, to be populated.
|
// The Turbofan source position table, to be populated.
|
||||||
SourcePositionTable* source_positions_;
|
SourcePositionTable* source_positions_;
|
||||||
|
|
||||||
|
@ -1,41 +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-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
|
|
@ -1,55 +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_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_
|
|
@ -189,12 +189,6 @@ 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 interpreter
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace v8
|
} // namespace v8
|
||||||
|
@ -48,8 +48,6 @@ class V8_EXPORT_PRIVATE BytecodeArrayAccessor {
|
|||||||
// not for a jump or conditional jump.
|
// not for a jump or conditional jump.
|
||||||
int GetJumpTargetOffset() const;
|
int GetJumpTargetOffset() const;
|
||||||
|
|
||||||
std::ostream& PrintTo(std::ostream& os) const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool OffsetInBounds() const;
|
bool OffsetInBounds() const;
|
||||||
|
|
||||||
|
@ -722,25 +722,22 @@ void BytecodeGenerator::VisitIterationHeader(IterationStatement* stmt,
|
|||||||
LoopBuilder* loop_builder) {
|
LoopBuilder* loop_builder) {
|
||||||
// Recall that stmt->yield_count() is always zero inside ordinary
|
// Recall that stmt->yield_count() is always zero inside ordinary
|
||||||
// (i.e. non-generator) functions.
|
// (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();
|
|
||||||
}
|
|
||||||
|
|
||||||
loop_builder->LoopHeader(&resume_points_in_loop);
|
// 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);
|
||||||
|
|
||||||
|
if (stmt->yield_count() > 0) {
|
||||||
// If we are not resuming, fall through to loop body.
|
// If we are not resuming, fall through to loop body.
|
||||||
// If we are resuming, perform state dispatch.
|
// If we are resuming, perform state dispatch.
|
||||||
BytecodeLabel not_resuming;
|
BytecodeLabel not_resuming;
|
||||||
|
@ -17,7 +17,7 @@ class BytecodeArrayBuilder;
|
|||||||
// label is bound, it represents a known position in the bytecode
|
// label is bound, it represents a known position in the bytecode
|
||||||
// array. For labels that are forward references there can be at most
|
// array. For labels that are forward references there can be at most
|
||||||
// one reference whilst it is unbound.
|
// one reference whilst it is unbound.
|
||||||
class V8_EXPORT_PRIVATE BytecodeLabel final {
|
class BytecodeLabel final {
|
||||||
public:
|
public:
|
||||||
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
|
BytecodeLabel() : bound_(false), offset_(kInvalidOffset) {}
|
||||||
|
|
||||||
@ -54,7 +54,7 @@ class V8_EXPORT_PRIVATE BytecodeLabel final {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Class representing a branch target of multiple jumps.
|
// Class representing a branch target of multiple jumps.
|
||||||
class V8_EXPORT_PRIVATE BytecodeLabels {
|
class BytecodeLabels {
|
||||||
public:
|
public:
|
||||||
explicit BytecodeLabels(Zone* zone) : labels_(zone) {}
|
explicit BytecodeLabels(Zone* zone) : labels_(zone) {}
|
||||||
|
|
||||||
|
@ -471,12 +471,6 @@ class V8_EXPORT_PRIVATE Bytecodes final {
|
|||||||
IsConditionalJumpConstant(bytecode);
|
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
|
// Returns true if the bytecode is a jump or a conditional jump taking
|
||||||
// an immediate byte operand (OperandType::kImm).
|
// an immediate byte operand (OperandType::kImm).
|
||||||
static CONSTEXPR bool IsJumpImmediate(Bytecode bytecode) {
|
static CONSTEXPR bool IsJumpImmediate(Bytecode bytecode) {
|
||||||
@ -506,12 +500,6 @@ class V8_EXPORT_PRIVATE Bytecodes final {
|
|||||||
return IsJumpImmediate(bytecode) || IsJumpConstant(bytecode);
|
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.
|
// Returns true if the bytecode is a conditional jump, a jump, or a return.
|
||||||
static CONSTEXPR bool IsJumpOrReturn(Bytecode bytecode) {
|
static CONSTEXPR bool IsJumpOrReturn(Bytecode bytecode) {
|
||||||
return bytecode == Bytecode::kReturn || IsJump(bytecode);
|
return bytecode == Bytecode::kReturn || IsJump(bytecode);
|
||||||
|
@ -55,10 +55,8 @@ void LoopBuilder::LoopHeader(ZoneVector<BytecodeLabel>* additional_labels) {
|
|||||||
// and misplaced between the headers.
|
// and misplaced between the headers.
|
||||||
DCHECK(break_labels_.empty() && continue_labels_.empty());
|
DCHECK(break_labels_.empty() && continue_labels_.empty());
|
||||||
builder()->Bind(&loop_header_);
|
builder()->Bind(&loop_header_);
|
||||||
if (additional_labels != nullptr) {
|
for (auto& label : *additional_labels) {
|
||||||
for (auto& label : *additional_labels) {
|
builder()->Bind(&label);
|
||||||
builder()->Bind(&label);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ namespace v8 {
|
|||||||
namespace internal {
|
namespace internal {
|
||||||
namespace interpreter {
|
namespace interpreter {
|
||||||
|
|
||||||
class V8_EXPORT_PRIVATE ControlFlowBuilder BASE_EMBEDDED {
|
class ControlFlowBuilder BASE_EMBEDDED {
|
||||||
public:
|
public:
|
||||||
explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
|
explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
|
||||||
: builder_(builder) {}
|
: builder_(builder) {}
|
||||||
@ -29,8 +29,7 @@ class V8_EXPORT_PRIVATE ControlFlowBuilder BASE_EMBEDDED {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
|
DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
|
||||||
};
|
};
|
||||||
|
|
||||||
class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
|
class BreakableControlFlowBuilder : public ControlFlowBuilder {
|
||||||
: public ControlFlowBuilder {
|
|
||||||
public:
|
public:
|
||||||
explicit BreakableControlFlowBuilder(BytecodeArrayBuilder* builder)
|
explicit BreakableControlFlowBuilder(BytecodeArrayBuilder* builder)
|
||||||
: ControlFlowBuilder(builder), break_labels_(builder->zone()) {}
|
: ControlFlowBuilder(builder), break_labels_(builder->zone()) {}
|
||||||
@ -64,8 +63,7 @@ class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
|
|||||||
|
|
||||||
|
|
||||||
// Class to track control flow for block statements (which can break in JS).
|
// Class to track control flow for block statements (which can break in JS).
|
||||||
class V8_EXPORT_PRIVATE BlockBuilder final
|
class BlockBuilder final : public BreakableControlFlowBuilder {
|
||||||
: public BreakableControlFlowBuilder {
|
|
||||||
public:
|
public:
|
||||||
explicit BlockBuilder(BytecodeArrayBuilder* builder)
|
explicit BlockBuilder(BytecodeArrayBuilder* builder)
|
||||||
: BreakableControlFlowBuilder(builder) {}
|
: BreakableControlFlowBuilder(builder) {}
|
||||||
@ -79,7 +77,7 @@ class V8_EXPORT_PRIVATE BlockBuilder final
|
|||||||
|
|
||||||
// A class to help with co-ordinating break and continue statements with
|
// A class to help with co-ordinating break and continue statements with
|
||||||
// their loop.
|
// their loop.
|
||||||
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
|
class LoopBuilder final : public BreakableControlFlowBuilder {
|
||||||
public:
|
public:
|
||||||
explicit LoopBuilder(BytecodeArrayBuilder* builder)
|
explicit LoopBuilder(BytecodeArrayBuilder* builder)
|
||||||
: BreakableControlFlowBuilder(builder),
|
: BreakableControlFlowBuilder(builder),
|
||||||
@ -87,7 +85,7 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
|
|||||||
header_labels_(builder->zone()) {}
|
header_labels_(builder->zone()) {}
|
||||||
~LoopBuilder();
|
~LoopBuilder();
|
||||||
|
|
||||||
void LoopHeader(ZoneVector<BytecodeLabel>* additional_labels = nullptr);
|
void LoopHeader(ZoneVector<BytecodeLabel>* additional_labels);
|
||||||
void JumpToHeader(int loop_depth);
|
void JumpToHeader(int loop_depth);
|
||||||
void BindContinueTarget();
|
void BindContinueTarget();
|
||||||
void EndLoop();
|
void EndLoop();
|
||||||
@ -111,8 +109,7 @@ class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
|
|||||||
|
|
||||||
|
|
||||||
// A class to help with co-ordinating break statements with their switch.
|
// A class to help with co-ordinating break statements with their switch.
|
||||||
class V8_EXPORT_PRIVATE SwitchBuilder final
|
class SwitchBuilder final : public BreakableControlFlowBuilder {
|
||||||
: public BreakableControlFlowBuilder {
|
|
||||||
public:
|
public:
|
||||||
explicit SwitchBuilder(BytecodeArrayBuilder* builder, int number_of_cases)
|
explicit SwitchBuilder(BytecodeArrayBuilder* builder, int number_of_cases)
|
||||||
: BreakableControlFlowBuilder(builder),
|
: BreakableControlFlowBuilder(builder),
|
||||||
@ -142,7 +139,7 @@ class V8_EXPORT_PRIVATE SwitchBuilder final
|
|||||||
|
|
||||||
|
|
||||||
// A class to help with co-ordinating control flow in try-catch statements.
|
// A class to help with co-ordinating control flow in try-catch statements.
|
||||||
class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
|
class TryCatchBuilder final : public ControlFlowBuilder {
|
||||||
public:
|
public:
|
||||||
explicit TryCatchBuilder(BytecodeArrayBuilder* builder,
|
explicit TryCatchBuilder(BytecodeArrayBuilder* builder,
|
||||||
HandlerTable::CatchPrediction catch_prediction)
|
HandlerTable::CatchPrediction catch_prediction)
|
||||||
@ -163,7 +160,7 @@ class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
|
|||||||
|
|
||||||
|
|
||||||
// A class to help with co-ordinating control flow in try-finally statements.
|
// A class to help with co-ordinating control flow in try-finally statements.
|
||||||
class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder {
|
class TryFinallyBuilder final : public ControlFlowBuilder {
|
||||||
public:
|
public:
|
||||||
explicit TryFinallyBuilder(BytecodeArrayBuilder* builder,
|
explicit TryFinallyBuilder(BytecodeArrayBuilder* builder,
|
||||||
HandlerTable::CatchPrediction catch_prediction)
|
HandlerTable::CatchPrediction catch_prediction)
|
||||||
|
@ -550,12 +550,10 @@
|
|||||||
'compiler/basic-block-instrumentor.h',
|
'compiler/basic-block-instrumentor.h',
|
||||||
'compiler/branch-elimination.cc',
|
'compiler/branch-elimination.cc',
|
||||||
'compiler/branch-elimination.h',
|
'compiler/branch-elimination.h',
|
||||||
'compiler/bytecode-analysis.cc',
|
|
||||||
'compiler/bytecode-analysis.h',
|
|
||||||
'compiler/bytecode-graph-builder.cc',
|
'compiler/bytecode-graph-builder.cc',
|
||||||
'compiler/bytecode-graph-builder.h',
|
'compiler/bytecode-graph-builder.h',
|
||||||
'compiler/bytecode-liveness-map.cc',
|
'compiler/bytecode-analysis.cc',
|
||||||
'compiler/bytecode-liveness-map.h',
|
'compiler/bytecode-analysis.h',
|
||||||
'compiler/c-linkage.cc',
|
'compiler/c-linkage.cc',
|
||||||
'compiler/checkpoint-elimination.cc',
|
'compiler/checkpoint-elimination.cc',
|
||||||
'compiler/checkpoint-elimination.h',
|
'compiler/checkpoint-elimination.h',
|
||||||
|
@ -24,8 +24,6 @@
|
|||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
|
||||||
// Flags: --noanalyze-environment-liveness
|
|
||||||
|
|
||||||
|
|
||||||
Debug = debug.Debug
|
Debug = debug.Debug
|
||||||
|
@ -24,8 +24,6 @@
|
|||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
|
||||||
// Flags: --noanalyze-environment-liveness
|
|
||||||
|
|
||||||
|
|
||||||
Debug = debug.Debug
|
Debug = debug.Debug
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright 2015 the V8 project authors. All rights reserved.
|
// Copyright 2015 the V8 project authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
//
|
|
||||||
// Flags: --noanalyze-environment-liveness
|
|
||||||
|
|
||||||
|
|
||||||
Debug = debug.Debug
|
Debug = debug.Debug
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright 2012 the V8 project authors. All rights reserved.
|
// Copyright 2012 the V8 project authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
//
|
|
||||||
// Flags: --noanalyze-environment-liveness
|
|
||||||
|
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
@ -24,8 +24,6 @@
|
|||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// 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
|
// Test that a variable in the local scope that shadows a context-allocated
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
// Copyright 2016 the V8 project authors. All rights reserved.
|
// Copyright 2016 the V8 project authors. All rights reserved.
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
//
|
|
||||||
// Flags: --noanalyze-environment-liveness
|
|
||||||
|
|
||||||
|
|
||||||
var Debug = debug.Debug;
|
var Debug = debug.Debug;
|
||||||
|
@ -24,8 +24,7 @@
|
|||||||
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
//
|
|
||||||
// Flags: --noanalyze-environment-liveness
|
|
||||||
|
|
||||||
Debug = debug.Debug
|
Debug = debug.Debug
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// Flags: --expose-debug-as debug --noanalyze-environment-liveness
|
// Flags: --expose-debug-as debug
|
||||||
// Get the Debug object exposed from the debug context global object.
|
// Get the Debug object exposed from the debug context global object.
|
||||||
Debug = debug.Debug
|
Debug = debug.Debug
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// Flags: --expose-debug-as debug --noanalyze-environment-liveness
|
// Flags: --expose-debug-as debug
|
||||||
// Get the Debug object exposed from the debug context global object.
|
// Get the Debug object exposed from the debug context global object.
|
||||||
Debug = debug.Debug
|
Debug = debug.Debug
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// Flags: --expose-debug-as debug --allow-natives-syntax --noanalyze-environment-liveness
|
// Flags: --expose-debug-as debug --allow-natives-syntax
|
||||||
// The functions used for testing backtraces. They are at the top to make the
|
// The functions used for testing backtraces. They are at the top to make the
|
||||||
// testing of source line/column easier.
|
// testing of source line/column easier.
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
// Flags: --expose-debug-as debug --allow-natives-syntax --noanalyze-environment-liveness
|
// Flags: --expose-debug-as debug --allow-natives-syntax
|
||||||
// The functions used for testing backtraces. They are at the top to make the
|
// The functions used for testing backtraces. They are at the top to make the
|
||||||
// testing of source line/column easier.
|
// testing of source line/column easier.
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
// Use of this source code is governed by a BSD-style license that can be
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// Flags: --expose-debug-as debug --noanalyze-environment-liveness
|
// Flags: --expose-debug-as debug
|
||||||
|
|
||||||
var Debug = debug.Debug;
|
var Debug = debug.Debug;
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
// MODULE
|
// MODULE
|
||||||
// Flags: --expose-debug-as debug --allow-natives-syntax --noanalyze-environment-liveness
|
// Flags: --expose-debug-as debug --allow-natives-syntax
|
||||||
|
|
||||||
// These tests are copied from mjsunit/debug-scopes.js and adapted for modules.
|
// These tests are copied from mjsunit/debug-scopes.js and adapted for modules.
|
||||||
|
|
||||||
|
@ -31,7 +31,6 @@ v8_executable("unittests") {
|
|||||||
"compiler-dispatcher/compiler-dispatcher-job-unittest.cc",
|
"compiler-dispatcher/compiler-dispatcher-job-unittest.cc",
|
||||||
"compiler-dispatcher/compiler-dispatcher-tracer-unittest.cc",
|
"compiler-dispatcher/compiler-dispatcher-tracer-unittest.cc",
|
||||||
"compiler/branch-elimination-unittest.cc",
|
"compiler/branch-elimination-unittest.cc",
|
||||||
"compiler/bytecode-analysis-unittest.cc",
|
|
||||||
"compiler/checkpoint-elimination-unittest.cc",
|
"compiler/checkpoint-elimination-unittest.cc",
|
||||||
"compiler/common-operator-reducer-unittest.cc",
|
"compiler/common-operator-reducer-unittest.cc",
|
||||||
"compiler/common-operator-unittest.cc",
|
"compiler/common-operator-unittest.cc",
|
||||||
|
@ -1,415 +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/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
|
|
@ -27,7 +27,6 @@
|
|||||||
'cancelable-tasks-unittest.cc',
|
'cancelable-tasks-unittest.cc',
|
||||||
'char-predicates-unittest.cc',
|
'char-predicates-unittest.cc',
|
||||||
'compiler/branch-elimination-unittest.cc',
|
'compiler/branch-elimination-unittest.cc',
|
||||||
'compiler/bytecode-analysis-unittest.cc',
|
|
||||||
'compiler/checkpoint-elimination-unittest.cc',
|
'compiler/checkpoint-elimination-unittest.cc',
|
||||||
'compiler/common-operator-reducer-unittest.cc',
|
'compiler/common-operator-reducer-unittest.cc',
|
||||||
'compiler/common-operator-unittest.cc',
|
'compiler/common-operator-unittest.cc',
|
||||||
|
Loading…
Reference in New Issue
Block a user