5 #include "src/compiler/bytecode-analysis.h" 7 #include "src/interpreter/bytecode-array-iterator.h" 8 #include "src/interpreter/bytecode-array-random-iterator.h" 9 #include "src/objects-inl.h" 15 using interpreter::Bytecode;
16 using interpreter::Bytecodes;
17 using interpreter::OperandType;
19 BytecodeLoopAssignments::BytecodeLoopAssignments(
int parameter_count,
20 int register_count, Zone* zone)
21 : parameter_count_(parameter_count),
22 bit_vector_(new (zone)
23 BitVector(parameter_count + register_count, zone)) {}
25 void BytecodeLoopAssignments::Add(interpreter::Register r) {
26 if (r.is_parameter()) {
27 bit_vector_->Add(r.ToParameterIndex(parameter_count_));
29 bit_vector_->Add(parameter_count_ + r.index());
33 void BytecodeLoopAssignments::AddList(interpreter::Register r,
uint32_t count) {
34 if (r.is_parameter()) {
36 DCHECK(interpreter::Register(r.index() +
i).is_parameter());
37 bit_vector_->Add(r.ToParameterIndex(parameter_count_) +
i);
41 DCHECK(!interpreter::Register(r.index() +
i).is_parameter());
42 bit_vector_->Add(parameter_count_ + r.index() +
i);
48 void BytecodeLoopAssignments::Union(
const BytecodeLoopAssignments& other) {
49 bit_vector_->Union(*other.bit_vector_);
52 bool BytecodeLoopAssignments::ContainsParameter(
int index)
const {
54 DCHECK_LT(index, parameter_count());
55 return bit_vector_->Contains(index);
58 bool BytecodeLoopAssignments::ContainsLocal(
int index)
const {
60 DCHECK_LT(index, local_count());
61 return bit_vector_->Contains(parameter_count_ + index);
64 ResumeJumpTarget::ResumeJumpTarget(
int suspend_id,
int target_offset,
65 int final_target_offset)
66 : suspend_id_(suspend_id),
67 target_offset_(target_offset),
68 final_target_offset_(final_target_offset) {}
70 ResumeJumpTarget ResumeJumpTarget::Leaf(
int suspend_id,
int target_offset) {
71 return ResumeJumpTarget(suspend_id, target_offset, target_offset);
74 ResumeJumpTarget ResumeJumpTarget::AtLoopHeader(
int loop_header_offset,
75 const ResumeJumpTarget& next) {
76 return ResumeJumpTarget(next.suspend_id(), loop_header_offset,
77 next.target_offset());
80 BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
81 Zone* zone,
bool do_liveness_analysis)
82 : bytecode_array_(bytecode_array),
83 do_liveness_analysis_(do_liveness_analysis),
86 loop_end_index_queue_(zone),
87 resume_jump_targets_(zone),
89 header_to_info_(zone),
91 liveness_map_(bytecode_array->length(), zone) {}
95 void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness,
96 const interpreter::BytecodeArrayAccessor& accessor) {
97 int num_operands = Bytecodes::NumberOfOperands(bytecode);
98 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
101 if (bytecode == Bytecode::kSuspendGenerator) {
103 in_liveness.MarkRegisterLive(accessor.GetRegisterOperand(0).index());
105 DCHECK(Bytecodes::ReadsAccumulator(bytecode));
106 in_liveness.MarkAccumulatorLive();
109 if (bytecode == Bytecode::kResumeGenerator) {
111 in_liveness.MarkRegisterLive(accessor.GetRegisterOperand(0).index());
115 if (Bytecodes::WritesAccumulator(bytecode)) {
116 in_liveness.MarkAccumulatorDead();
118 for (
int i = 0;
i < num_operands; ++
i) {
119 switch (operand_types[
i]) {
120 case OperandType::kRegOut: {
121 interpreter::Register r = accessor.GetRegisterOperand(
i);
122 if (!r.is_parameter()) {
123 in_liveness.MarkRegisterDead(r.index());
127 case OperandType::kRegOutList: {
128 interpreter::Register r = accessor.GetRegisterOperand(
i++);
129 uint32_t reg_count = accessor.GetRegisterCountOperand(
i);
130 if (!r.is_parameter()) {
131 for (
uint32_t j = 0; j < reg_count; ++j) {
132 DCHECK(!interpreter::Register(r.index() + j).is_parameter());
133 in_liveness.MarkRegisterDead(r.index() + j);
138 case OperandType::kRegOutPair: {
139 interpreter::Register r = accessor.GetRegisterOperand(
i);
140 if (!r.is_parameter()) {
141 DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
142 in_liveness.MarkRegisterDead(r.index());
143 in_liveness.MarkRegisterDead(r.index() + 1);
147 case OperandType::kRegOutTriple: {
148 interpreter::Register r = accessor.GetRegisterOperand(
i);
149 if (!r.is_parameter()) {
150 DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
151 DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
152 in_liveness.MarkRegisterDead(r.index());
153 in_liveness.MarkRegisterDead(r.index() + 1);
154 in_liveness.MarkRegisterDead(r.index() + 2);
159 DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[
i]));
164 if (Bytecodes::ReadsAccumulator(bytecode)) {
165 in_liveness.MarkAccumulatorLive();
167 for (
int i = 0;
i < num_operands; ++
i) {
168 switch (operand_types[
i]) {
169 case OperandType::kReg: {
170 interpreter::Register r = accessor.GetRegisterOperand(
i);
171 if (!r.is_parameter()) {
172 in_liveness.MarkRegisterLive(r.index());
176 case OperandType::kRegPair: {
177 interpreter::Register r = accessor.GetRegisterOperand(
i);
178 if (!r.is_parameter()) {
179 DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
180 in_liveness.MarkRegisterLive(r.index());
181 in_liveness.MarkRegisterLive(r.index() + 1);
185 case OperandType::kRegList: {
186 interpreter::Register r = accessor.GetRegisterOperand(
i++);
187 uint32_t reg_count = accessor.GetRegisterCountOperand(
i);
188 if (!r.is_parameter()) {
189 for (
uint32_t j = 0; j < reg_count; ++j) {
190 DCHECK(!interpreter::Register(r.index() + j).is_parameter());
191 in_liveness.MarkRegisterLive(r.index() + j);
197 DCHECK(!Bytecodes::IsRegisterInputOperandType(operand_types[
i]));
203 void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness,
204 BytecodeLivenessState* next_bytecode_in_liveness,
205 const interpreter::BytecodeArrayAccessor& accessor,
206 const BytecodeLivenessMap& liveness_map) {
207 int current_offset = accessor.current_offset();
208 const Handle<BytecodeArray>& bytecode_array = accessor.bytecode_array();
211 if (bytecode == Bytecode::kSuspendGenerator ||
212 bytecode == Bytecode::kResumeGenerator) {
213 out_liveness.Union(*next_bytecode_in_liveness);
219 if (Bytecodes::IsForwardJump(bytecode)) {
220 int target_offset = accessor.GetJumpTargetOffset();
221 out_liveness.Union(*liveness_map.GetInLiveness(target_offset));
222 }
else if (Bytecodes::IsSwitch(bytecode)) {
223 for (
const auto& entry : accessor.GetJumpTableTargetOffsets()) {
224 out_liveness.Union(*liveness_map.GetInLiveness(entry.target_offset));
230 if (next_bytecode_in_liveness !=
nullptr &&
231 !Bytecodes::IsUnconditionalJump(bytecode)) {
232 out_liveness.Union(*next_bytecode_in_liveness);
236 if (!interpreter::Bytecodes::IsWithoutExternalSideEffects(bytecode)) {
239 HandlerTable table(*bytecode_array);
241 table.LookupRange(current_offset, &handler_context,
nullptr);
243 if (handler_offset != -1) {
244 bool was_accumulator_live = out_liveness.AccumulatorIsLive();
245 out_liveness.Union(*liveness_map.GetInLiveness(handler_offset));
246 out_liveness.MarkRegisterLive(handler_context);
247 if (!was_accumulator_live) {
252 out_liveness.MarkAccumulatorDead();
263 void UpdateLiveness(Bytecode bytecode, BytecodeLiveness& liveness,
264 BytecodeLivenessState** next_bytecode_in_liveness,
265 const interpreter::BytecodeArrayAccessor& accessor,
266 const BytecodeLivenessMap& liveness_map) {
267 UpdateOutLiveness(bytecode, *liveness.out, *next_bytecode_in_liveness,
268 accessor, liveness_map);
269 liveness.in->CopyFrom(*liveness.out);
270 UpdateInLiveness(bytecode, *liveness.in, accessor);
272 *next_bytecode_in_liveness = liveness.in;
275 void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments,
276 const interpreter::BytecodeArrayAccessor& accessor) {
277 int num_operands = Bytecodes::NumberOfOperands(bytecode);
278 const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
280 for (
int i = 0;
i < num_operands; ++
i) {
281 switch (operand_types[
i]) {
282 case OperandType::kRegOut: {
283 assignments.Add(accessor.GetRegisterOperand(
i));
286 case OperandType::kRegOutList: {
287 interpreter::Register r = accessor.GetRegisterOperand(
i++);
288 uint32_t reg_count = accessor.GetRegisterCountOperand(
i);
289 assignments.AddList(r, reg_count);
292 case OperandType::kRegOutPair: {
293 assignments.AddList(accessor.GetRegisterOperand(
i), 2);
296 case OperandType::kRegOutTriple: {
297 assignments.AddList(accessor.GetRegisterOperand(
i), 3);
301 DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[
i]));
309 void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
310 loop_stack_.push({-1,
nullptr});
312 BytecodeLivenessState* next_bytecode_in_liveness =
nullptr;
314 bool is_osr = !osr_bailout_id.IsNone();
315 int osr_loop_end_offset = is_osr ? osr_bailout_id.ToInt() : -1;
317 int generator_switch_index = -1;
319 interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
320 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
321 Bytecode bytecode = iterator.current_bytecode();
322 int current_offset = iterator.current_offset();
324 if (bytecode == Bytecode::kSwitchOnGeneratorState) {
325 DCHECK_EQ(generator_switch_index, -1);
326 generator_switch_index = iterator.current_index();
329 if (bytecode == Bytecode::kJumpLoop) {
332 int loop_end = current_offset + iterator.current_bytecode_size();
333 int loop_header = iterator.GetJumpTargetOffset();
334 PushLoop(loop_header, loop_end);
336 if (current_offset == osr_loop_end_offset) {
337 osr_entry_point_ = loop_header;
338 }
else if (current_offset < osr_loop_end_offset) {
342 DCHECK_NE(-1, osr_entry_point_);
346 if (do_liveness_analysis_) {
347 loop_end_index_queue_.push_back(iterator.current_index());
349 }
else if (loop_stack_.size() > 1) {
350 LoopStackEntry& current_loop = loop_stack_.top();
351 LoopInfo* current_loop_info = current_loop.loop_info;
357 UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);
360 if (!is_osr && bytecode == Bytecode::kSuspendGenerator) {
361 int suspend_id = iterator.GetUnsignedImmediateOperand(3);
362 int resume_offset = current_offset + iterator.current_bytecode_size();
363 current_loop_info->AddResumeTarget(
364 ResumeJumpTarget::Leaf(suspend_id, resume_offset));
368 if (current_offset == current_loop.header_offset) {
370 if (loop_stack_.size() > 1) {
372 LoopInfo* parent_loop_info = loop_stack_.top().loop_info;
374 parent_loop_info->assignments().Union(
375 current_loop_info->assignments());
401 for (
const auto& target : current_loop_info->resume_jump_targets()) {
402 parent_loop_info->AddResumeTarget(
403 ResumeJumpTarget::AtLoopHeader(current_offset, target));
408 for (
const auto& target : current_loop_info->resume_jump_targets()) {
409 resume_jump_targets_.push_back(
410 ResumeJumpTarget::AtLoopHeader(current_offset, target));
414 }
else if (!is_osr && bytecode == Bytecode::kSuspendGenerator) {
418 int suspend_id = iterator.GetUnsignedImmediateOperand(3);
419 int resume_offset = current_offset + iterator.current_bytecode_size();
420 resume_jump_targets_.push_back(
421 ResumeJumpTarget::Leaf(suspend_id, resume_offset));
424 if (do_liveness_analysis_) {
425 BytecodeLiveness& liveness = liveness_map_.InitializeLiveness(
426 current_offset, bytecode_array()->register_count(), zone());
427 UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
432 DCHECK_EQ(loop_stack_.size(), 1u);
433 DCHECK_EQ(loop_stack_.top().header_offset, -1);
435 DCHECK(ResumeJumpTargetsAreValid());
437 if (!do_liveness_analysis_)
return;
464 for (
int loop_end_index : loop_end_index_queue_) {
465 iterator.GoToIndex(loop_end_index);
467 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
469 int header_offset = iterator.GetJumpTargetOffset();
470 int end_offset = iterator.current_offset();
472 BytecodeLiveness& header_liveness =
473 liveness_map_.GetLiveness(header_offset);
474 BytecodeLiveness& end_liveness = liveness_map_.GetLiveness(end_offset);
476 if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) {
480 end_liveness.in->CopyFrom(*end_liveness.out);
481 next_bytecode_in_liveness = end_liveness.in;
485 for (; iterator.current_offset() > header_offset; --iterator) {
486 Bytecode bytecode = iterator.current_bytecode();
487 int current_offset = iterator.current_offset();
488 BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
490 UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
495 UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out,
496 next_bytecode_in_liveness, iterator, liveness_map_);
503 if (generator_switch_index != -1) {
504 iterator.GoToIndex(generator_switch_index);
505 DCHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnGeneratorState);
507 int current_offset = iterator.current_offset();
508 BytecodeLiveness& switch_liveness =
509 liveness_map_.GetLiveness(current_offset);
511 bool any_changed =
false;
512 for (
const auto& entry : iterator.GetJumpTableTargetOffsets()) {
513 if (switch_liveness.out->UnionIsChanged(
514 *liveness_map_.GetInLiveness(entry.target_offset))) {
522 switch_liveness.in->CopyFrom(*switch_liveness.out);
523 UpdateInLiveness(Bytecode::kSwitchOnGeneratorState, *switch_liveness.in,
525 next_bytecode_in_liveness = switch_liveness.in;
526 for (--iterator; iterator.IsValid(); --iterator) {
527 Bytecode bytecode = iterator.current_bytecode();
528 int current_offset = iterator.current_offset();
529 BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
532 DCHECK_NE(bytecode, Bytecode::kJumpLoop);
534 UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
540 DCHECK(LivenessIsValid());
543 void BytecodeAnalysis::PushLoop(
int loop_header,
int loop_end) {
544 DCHECK(loop_header < loop_end);
545 DCHECK(loop_stack_.top().header_offset < loop_header);
546 DCHECK(end_to_header_.find(loop_end) == end_to_header_.end());
547 DCHECK(header_to_info_.find(loop_header) == header_to_info_.end());
549 int parent_offset = loop_stack_.top().header_offset;
551 end_to_header_.insert({loop_end, loop_header});
552 auto it = header_to_info_.insert(
553 {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(),
554 bytecode_array_->register_count(), zone_)});
556 LoopInfo* loop_info = &it.first->second;
558 loop_stack_.push({loop_header, loop_info});
561 bool BytecodeAnalysis::IsLoopHeader(
int offset)
const {
562 return header_to_info_.find(offset) != header_to_info_.end();
565 int BytecodeAnalysis::GetLoopOffsetFor(
int offset)
const {
566 auto loop_end_to_header = end_to_header_.upper_bound(offset);
568 if (loop_end_to_header == end_to_header_.end()) {
578 if (loop_end_to_header->second <= offset) {
579 return loop_end_to_header->second;
593 DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end());
595 return header_to_info_.upper_bound(offset)->second.parent_offset();
598 const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(
int header_offset)
const {
599 DCHECK(IsLoopHeader(header_offset));
601 return header_to_info_.find(header_offset)->second;
604 const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
606 if (!do_liveness_analysis_)
return nullptr;
608 return liveness_map_.GetInLiveness(offset);
611 const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor(
613 if (!do_liveness_analysis_)
return nullptr;
615 return liveness_map_.GetOutLiveness(offset);
618 std::ostream& BytecodeAnalysis::PrintLivenessTo(std::ostream& os)
const {
619 interpreter::BytecodeArrayIterator iterator(bytecode_array());
621 for (; !iterator.done(); iterator.Advance()) {
622 int current_offset = iterator.current_offset();
624 const BitVector& in_liveness =
625 GetInLivenessFor(current_offset)->bit_vector();
626 const BitVector& out_liveness =
627 GetOutLivenessFor(current_offset)->bit_vector();
629 for (
int i = 0;
i < in_liveness.length(); ++
i) {
630 os << (in_liveness.Contains(
i) ?
"L" :
".");
634 for (
int i = 0;
i < out_liveness.length(); ++
i) {
635 os << (out_liveness.Contains(
i) ?
"L" :
".");
638 os <<
" | " << current_offset <<
": ";
639 iterator.PrintTo(os) << std::endl;
646 bool BytecodeAnalysis::ResumeJumpTargetsAreValid() {
650 interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
651 for (iterator.GoToStart(); iterator.IsValid(); ++iterator) {
652 if (iterator.current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
660 if (!iterator.IsValid() || HasOsrEntryPoint()) {
662 if (!resume_jump_targets().empty()) {
664 "Found %zu top-level resume targets but no resume switch\n",
665 resume_jump_targets().size());
669 for (
const std::pair<const int, LoopInfo>& loop_info : header_to_info_) {
670 if (!loop_info.second.resume_jump_targets().empty()) {
672 "Found %zu resume targets at loop at offset %d, but no resume " 674 loop_info.second.resume_jump_targets().size(), loop_info.first);
688 std::map<int, int> unresolved_suspend_ids;
689 for (
const interpreter::JumpTableTargetOffset& offset :
690 iterator.GetJumpTableTargetOffsets()) {
691 int suspend_id = offset.case_value;
692 int resume_offset = offset.target_offset;
694 unresolved_suspend_ids[suspend_id] = resume_offset;
698 if (!ResumeJumpTargetLeavesResolveSuspendIds(-1, resume_jump_targets(),
699 &unresolved_suspend_ids)) {
703 for (
const std::pair<const int, LoopInfo>& loop_info : header_to_info_) {
704 if (!ResumeJumpTargetLeavesResolveSuspendIds(
705 loop_info.first, loop_info.second.resume_jump_targets(),
706 &unresolved_suspend_ids)) {
712 if (!unresolved_suspend_ids.empty()) {
714 "Found suspend ids that are not resolved by a final leaf resume " 717 for (
const std::pair<const int, int>& target : unresolved_suspend_ids) {
718 PrintF(stderr,
" %d -> %d\n", target.first, target.second);
726 bool BytecodeAnalysis::ResumeJumpTargetLeavesResolveSuspendIds(
727 int parent_offset,
const ZoneVector<ResumeJumpTarget>& resume_jump_targets,
728 std::map<int, int>* unresolved_suspend_ids) {
730 for (
const ResumeJumpTarget& target : resume_jump_targets) {
731 std::map<int, int>::iterator it =
732 unresolved_suspend_ids->find(target.suspend_id());
733 if (it == unresolved_suspend_ids->end()) {
736 "No unresolved suspend found for resume target with suspend id %d\n",
737 target.suspend_id());
741 int expected_target = it->second;
743 if (target.is_leaf()) {
745 if (target.target_offset() != expected_target) {
748 "Expected leaf resume target for id %d to have target offset %d, " 750 target.suspend_id(), expected_target, target.target_offset());
754 interpreter::BytecodeArrayAccessor assessor(bytecode_array(),
755 target.target_offset());
756 if (assessor.current_bytecode() != Bytecode::kResumeGenerator) {
758 "Expected resume target for id %d, offset %d, to be " 759 "ResumeGenerator, but found %s\n",
760 target.suspend_id(), target.target_offset(),
761 Bytecodes::ToString(assessor.current_bytecode()));
768 unresolved_suspend_ids->erase(it);
771 if (!IsLoopHeader(target.target_offset())) {
773 "Expected non-leaf resume target for id %d to have a loop " 774 "header at target offset %d\n",
775 target.suspend_id(), target.target_offset());
778 LoopInfo loop_info = GetLoopInfoFor(target.target_offset());
779 if (loop_info.parent_offset() != parent_offset) {
781 "Expected non-leaf resume target for id %d to have a direct " 782 "inner loop at target offset %d\n",
783 target.suspend_id(), target.target_offset());
794 bool BytecodeAnalysis::LivenessIsValid() {
795 interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
797 BytecodeLivenessState previous_liveness(bytecode_array()->register_count(),
800 int invalid_offset = -1;
801 int which_invalid = -1;
803 BytecodeLivenessState* next_bytecode_in_liveness =
nullptr;
806 for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
807 Bytecode bytecode = iterator.current_bytecode();
809 int current_offset = iterator.current_offset();
811 BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
813 previous_liveness.CopyFrom(*liveness.out);
815 UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
816 iterator, liveness_map_);
818 if (bytecode == Bytecode::kJumpLoop) {
819 int target_offset = iterator.GetJumpTargetOffset();
820 liveness.out->Union(*liveness_map_.GetInLiveness(target_offset));
823 if (!liveness.out->Equals(previous_liveness)) {
825 liveness.out->CopyFrom(previous_liveness);
826 invalid_offset = current_offset;
831 previous_liveness.CopyFrom(*liveness.in);
833 liveness.in->CopyFrom(*liveness.out);
834 UpdateInLiveness(bytecode, *liveness.in, iterator);
836 if (!liveness.in->Equals(previous_liveness)) {
838 liveness.in->CopyFrom(previous_liveness);
839 invalid_offset = current_offset;
844 next_bytecode_in_liveness = liveness.in;
849 for (iterator.GoToStart(); iterator.IsValid() && invalid_offset == -1;
851 Bytecode bytecode = iterator.current_bytecode();
852 int current_offset = iterator.current_offset();
853 int loop_header = GetLoopOffsetFor(current_offset);
856 if (loop_header == -1)
continue;
859 if (!Bytecodes::IsJump(bytecode))
continue;
861 int jump_target = iterator.GetJumpTargetOffset();
864 if (Bytecodes::IsForwardJump(bytecode) &&
865 GetLoopOffsetFor(jump_target) == loop_header) {
870 if (liveness_map_.GetLiveness(jump_target).in->AccumulatorIsLive()) {
871 invalid_offset = jump_target;
877 if (invalid_offset != -1) {
879 of <<
"Invalid liveness:" << std::endl;
885 interpreter::BytecodeArrayIterator forward_iterator(bytecode_array());
886 for (; !forward_iterator.done(); forward_iterator.Advance()) {
887 int current_offset = forward_iterator.current_offset();
888 const BitVector& in_liveness =
889 GetInLivenessFor(current_offset)->bit_vector();
890 const BitVector& out_liveness =
891 GetOutLivenessFor(current_offset)->bit_vector();
893 for (
int i = 0;
i < in_liveness.length(); ++
i) {
894 of << (in_liveness.Contains(
i) ?
'L' :
'.');
899 for (
int i = 0;
i < out_liveness.length(); ++
i) {
900 of << (out_liveness.Contains(
i) ?
'L' :
'.');
903 of <<
" : " << current_offset <<
" : ";
907 if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
910 for (
int i = 0;
i < loop_indent; ++
i) {
913 if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
915 }
else if (IsLoopHeader(current_offset)) {
919 forward_iterator.PrintTo(of);
920 if (Bytecodes::IsJump(forward_iterator.current_bytecode())) {
921 of <<
" (@" << forward_iterator.GetJumpTargetOffset() <<
")";
925 if (current_offset == invalid_offset) {
927 if (which_invalid == 0) {
928 for (
int i = 0;
i < in_liveness.length(); ++
i) {
931 for (
int i = 0;
i < out_liveness.length() + 3; ++
i) {
935 for (
int i = 0;
i < in_liveness.length() + 3; ++
i) {
938 for (
int i = 0;
i < out_liveness.length(); ++
i) {
944 of <<
" : " << current_offset <<
" : ";
945 for (
int i = 0;
i < loop_indent; ++
i) {
954 return invalid_offset == -1;