| Index: src/compiler/bytecode-analysis.cc
|
| diff --git a/src/compiler/bytecode-analysis.cc b/src/compiler/bytecode-analysis.cc
|
| index 539f760c6cdb1fdcc6485a38cebc09daab56bf4f..f0e870739b62d7b271907a7bdca8cfb1613d7989 100644
|
| --- a/src/compiler/bytecode-analysis.cc
|
| +++ b/src/compiler/bytecode-analysis.cc
|
| @@ -14,6 +14,73 @@ namespace compiler {
|
|
|
| using namespace interpreter;
|
|
|
| +BytecodeLoopAssignments::BytecodeLoopAssignments(int parameter_count,
|
| + int register_count, Zone* zone)
|
| + : parameter_count_(parameter_count),
|
| + bit_vector_(new (zone)
|
| + BitVector(parameter_count + register_count, zone)) {}
|
| +
|
| +void BytecodeLoopAssignments::Add(interpreter::Register r) {
|
| + if (r.is_parameter()) {
|
| + bit_vector_->Add(r.ToParameterIndex(parameter_count_));
|
| + } else {
|
| + bit_vector_->Add(parameter_count_ + r.index());
|
| + }
|
| +}
|
| +
|
| +void BytecodeLoopAssignments::AddPair(interpreter::Register r) {
|
| + if (r.is_parameter()) {
|
| + DCHECK(interpreter::Register(r.index() + 1).is_parameter());
|
| + bit_vector_->Add(r.ToParameterIndex(parameter_count_));
|
| + bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
|
| + } else {
|
| + DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
|
| + bit_vector_->Add(parameter_count_ + r.index());
|
| + bit_vector_->Add(parameter_count_ + r.index() + 1);
|
| + }
|
| +}
|
| +
|
| +void BytecodeLoopAssignments::AddTriple(interpreter::Register r) {
|
| + if (r.is_parameter()) {
|
| + DCHECK(interpreter::Register(r.index() + 1).is_parameter());
|
| + DCHECK(interpreter::Register(r.index() + 2).is_parameter());
|
| + bit_vector_->Add(r.ToParameterIndex(parameter_count_));
|
| + bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
|
| + bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 2);
|
| + } else {
|
| + DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
|
| + DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
|
| + bit_vector_->Add(parameter_count_ + r.index());
|
| + bit_vector_->Add(parameter_count_ + r.index() + 1);
|
| + bit_vector_->Add(parameter_count_ + r.index() + 2);
|
| + }
|
| +}
|
| +
|
| +void BytecodeLoopAssignments::AddAll() { bit_vector_->AddAll(); }
|
| +
|
| +void BytecodeLoopAssignments::Union(const BytecodeLoopAssignments& other) {
|
| + bit_vector_->Union(*other.bit_vector_);
|
| +}
|
| +
|
| +bool BytecodeLoopAssignments::ContainsParameter(int index) const {
|
| + DCHECK_GE(index, 0);
|
| + DCHECK_LT(index, parameter_count());
|
| + return bit_vector_->Contains(index);
|
| +}
|
| +
|
| +bool BytecodeLoopAssignments::ContainsLocal(int index) const {
|
| + DCHECK_GE(index, 0);
|
| + DCHECK_LT(index, local_count());
|
| + return bit_vector_->Contains(parameter_count_ + index);
|
| +}
|
| +
|
| +bool BytecodeLoopAssignments::ContainsAccumulator() const {
|
| + // TODO(leszeks): This assumes the accumulator is always assigned. This is
|
| + // probably correct, but that assignment is also probably dead, so we should
|
| + // check liveness.
|
| + return true;
|
| +}
|
| +
|
| BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
|
| Zone* zone, bool do_liveness_analysis)
|
| : bytecode_array_(bytecode_array),
|
| @@ -22,7 +89,7 @@ BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
|
| loop_stack_(zone),
|
| loop_end_index_queue_(zone),
|
| end_to_header_(zone),
|
| - header_to_parent_(zone),
|
| + header_to_info_(zone),
|
| liveness_map_(bytecode_array->length(), zone) {}
|
|
|
| namespace {
|
| @@ -145,13 +212,42 @@ void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness,
|
| }
|
| }
|
|
|
| +void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments,
|
| + const BytecodeArrayAccessor& accessor) {
|
| + int num_operands = Bytecodes::NumberOfOperands(bytecode);
|
| + const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
|
| +
|
| + for (int i = 0; i < num_operands; ++i) {
|
| + switch (operand_types[i]) {
|
| + case OperandType::kRegOut: {
|
| + assignments.Add(accessor.GetRegisterOperand(i));
|
| + break;
|
| + }
|
| + case OperandType::kRegOutPair: {
|
| + assignments.AddPair(accessor.GetRegisterOperand(i));
|
| + break;
|
| + }
|
| + case OperandType::kRegOutTriple: {
|
| + assignments.AddTriple(accessor.GetRegisterOperand(i));
|
| + break;
|
| + }
|
| + default:
|
| + DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
|
| + break;
|
| + }
|
| + }
|
| +}
|
| +
|
| } // namespace
|
|
|
| -void BytecodeAnalysis::Analyze() {
|
| - loop_stack_.push(-1);
|
| +void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
|
| + loop_stack_.push({-1, nullptr});
|
|
|
| BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
|
|
|
| + int osr_loop_end_offset =
|
| + osr_bailout_id.IsNone() ? -1 : osr_bailout_id.ToInt();
|
| +
|
| BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
|
| for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
|
| Bytecode bytecode = iterator.current_bytecode();
|
| @@ -163,12 +259,46 @@ void BytecodeAnalysis::Analyze() {
|
| int loop_end = current_offset + iterator.current_bytecode_size();
|
| PushLoop(iterator.GetJumpTargetOffset(), loop_end);
|
|
|
| + // Normally prefixed bytecodes are treated as if the prefix's offset was
|
| + // the actual bytecode's offset. However, the OSR id is the offset of the
|
| + // actual JumpLoop bytecode, so we need to find the location of that
|
| + // bytecode ignoring the prefix.
|
| + int jump_loop_offset = current_offset + iterator.current_prefix_offset();
|
| + bool is_osr_loop = (jump_loop_offset == osr_loop_end_offset);
|
| +
|
| + // Check that is_osr_loop is set iff the osr_loop_end_offset is within
|
| + // this bytecode.
|
| + DCHECK(!is_osr_loop ||
|
| + iterator.OffsetWithinBytecode(osr_loop_end_offset));
|
| +
|
| + // OSR "assigns" everything to OSR values on entry into an OSR loop, so we
|
| + // need to make sure to considered everything to be assigned.
|
| + if (is_osr_loop) {
|
| + loop_stack_.top().loop_info->assignments().AddAll();
|
| + }
|
| +
|
| // Save the index so that we can do another pass later.
|
| if (do_liveness_analysis_) {
|
| loop_end_index_queue_.push_back(iterator.current_index());
|
| }
|
| - } else if (current_offset == loop_stack_.top()) {
|
| - loop_stack_.pop();
|
| + } else if (loop_stack_.size() > 1) {
|
| + LoopStackEntry& current_loop = loop_stack_.top();
|
| + LoopInfo* current_loop_info = current_loop.loop_info;
|
| +
|
| + // TODO(leszeks): Ideally, we'd only set values that were assigned in
|
| + // the loop *and* are live when the loop exits. However, this requires
|
| + // tracking the out-liveness of *all* loop exits, which is not
|
| + // information we currently have.
|
| + UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);
|
| +
|
| + if (current_offset == current_loop.header_offset) {
|
| + loop_stack_.pop();
|
| + if (loop_stack_.size() > 1) {
|
| + // Propagate inner loop assignments to outer loop.
|
| + loop_stack_.top().loop_info->assignments().Union(
|
| + current_loop_info->assignments());
|
| + }
|
| + }
|
| }
|
|
|
| if (do_liveness_analysis_) {
|
| @@ -185,7 +315,7 @@ void BytecodeAnalysis::Analyze() {
|
| }
|
|
|
| DCHECK_EQ(loop_stack_.size(), 1u);
|
| - DCHECK_EQ(loop_stack_.top(), -1);
|
| + DCHECK_EQ(loop_stack_.top().header_offset, -1);
|
|
|
| if (!do_liveness_analysis_) return;
|
|
|
| @@ -259,18 +389,24 @@ void BytecodeAnalysis::Analyze() {
|
|
|
| void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
|
| DCHECK(loop_header < loop_end);
|
| - DCHECK(loop_stack_.top() < loop_header);
|
| + DCHECK(loop_stack_.top().header_offset < loop_header);
|
| DCHECK(end_to_header_.find(loop_end) == end_to_header_.end());
|
| - DCHECK(header_to_parent_.find(loop_header) == header_to_parent_.end());
|
| + DCHECK(header_to_info_.find(loop_header) == header_to_info_.end());
|
| +
|
| + int parent_offset = loop_stack_.top().header_offset;
|
| +
|
| + end_to_header_.insert({loop_end, loop_header});
|
| + auto it = header_to_info_.insert(
|
| + {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(),
|
| + bytecode_array_->register_count(), zone_)});
|
| + // Get the loop info pointer from the output of insert.
|
| + LoopInfo* loop_info = &it.first->second;
|
|
|
| - end_to_header_.insert(ZoneMap<int, int>::value_type(loop_end, loop_header));
|
| - header_to_parent_.insert(
|
| - ZoneMap<int, int>::value_type(loop_header, loop_stack_.top()));
|
| - loop_stack_.push(loop_header);
|
| + loop_stack_.push({loop_header, loop_info});
|
| }
|
|
|
| bool BytecodeAnalysis::IsLoopHeader(int offset) const {
|
| - return header_to_parent_.find(offset) != header_to_parent_.end();
|
| + return header_to_info_.find(offset) != header_to_info_.end();
|
| }
|
|
|
| int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
|
| @@ -300,16 +436,16 @@ int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
|
| // | `- end
|
| // |
|
| // `- end
|
| - // We just return the parent of the next loop header (might be -1).
|
| - DCHECK(header_to_parent_.upper_bound(offset) != header_to_parent_.end());
|
| + // We just return the parent of the next loop (might be -1).
|
| + DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end());
|
|
|
| - return header_to_parent_.upper_bound(offset)->second;
|
| + return header_to_info_.upper_bound(offset)->second.parent_offset();
|
| }
|
|
|
| -int BytecodeAnalysis::GetParentLoopFor(int header_offset) const {
|
| +const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const {
|
| DCHECK(IsLoopHeader(header_offset));
|
|
|
| - return header_to_parent_.find(header_offset)->second;
|
| + return header_to_info_.find(header_offset)->second;
|
| }
|
|
|
| const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
|
|
|