LCOV - code coverage report
Current view: top level - src/compiler - bytecode-analysis.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 176 209 84.2 %
Date: 2019-01-20 Functions: 19 24 79.2 %

          Line data    Source code
       1             : // Copyright 2016 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/compiler/bytecode-analysis.h"
       6             : 
       7             : #include "src/interpreter/bytecode-array-iterator.h"
       8             : #include "src/interpreter/bytecode-array-random-iterator.h"
       9             : #include "src/objects-inl.h"
      10             : 
      11             : namespace v8 {
      12             : namespace internal {
      13             : namespace compiler {
      14             : 
      15             : using interpreter::Bytecode;
      16             : using interpreter::Bytecodes;
      17             : using interpreter::OperandType;
      18             : 
      19       58032 : BytecodeLoopAssignments::BytecodeLoopAssignments(int parameter_count,
      20             :                                                  int register_count, Zone* zone)
      21             :     : parameter_count_(parameter_count),
      22             :       bit_vector_(new (zone)
      23      116065 :                       BitVector(parameter_count + register_count, zone)) {}
      24             : 
      25      530274 : void BytecodeLoopAssignments::Add(interpreter::Register r) {
      26      530274 :   if (r.is_parameter()) {
      27        2770 :     bit_vector_->Add(r.ToParameterIndex(parameter_count_));
      28             :   } else {
      29      527504 :     bit_vector_->Add(parameter_count_ + r.index());
      30             :   }
      31      530274 : }
      32             : 
      33         949 : void BytecodeLoopAssignments::AddList(interpreter::Register r, uint32_t count) {
      34         949 :   if (r.is_parameter()) {
      35           0 :     for (uint32_t i = 0; i < count; i++) {
      36             :       DCHECK(interpreter::Register(r.index() + i).is_parameter());
      37           0 :       bit_vector_->Add(r.ToParameterIndex(parameter_count_) + i);
      38             :     }
      39             :   } else {
      40        5848 :     for (uint32_t i = 0; i < count; i++) {
      41             :       DCHECK(!interpreter::Register(r.index() + i).is_parameter());
      42       11696 :       bit_vector_->Add(parameter_count_ + r.index() + i);
      43             :     }
      44             :   }
      45         949 : }
      46             : 
      47             : 
      48           0 : void BytecodeLoopAssignments::Union(const BytecodeLoopAssignments& other) {
      49        6335 :   bit_vector_->Union(*other.bit_vector_);
      50           0 : }
      51             : 
      52      396775 : bool BytecodeLoopAssignments::ContainsParameter(int index) const {
      53             :   DCHECK_GE(index, 0);
      54             :   DCHECK_LT(index, parameter_count());
      55      793550 :   return bit_vector_->Contains(index);
      56             : }
      57             : 
      58     3613421 : bool BytecodeLoopAssignments::ContainsLocal(int index) const {
      59             :   DCHECK_GE(index, 0);
      60             :   DCHECK_LT(index, local_count());
      61     7226842 :   return bit_vector_->Contains(parameter_count_ + index);
      62             : }
      63             : 
      64           0 : ResumeJumpTarget::ResumeJumpTarget(int suspend_id, int target_offset,
      65             :                                    int final_target_offset)
      66             :     : suspend_id_(suspend_id),
      67             :       target_offset_(target_offset),
      68           0 :       final_target_offset_(final_target_offset) {}
      69             : 
      70           0 : ResumeJumpTarget ResumeJumpTarget::Leaf(int suspend_id, int target_offset) {
      71           0 :   return ResumeJumpTarget(suspend_id, target_offset, target_offset);
      72             : }
      73             : 
      74           0 : ResumeJumpTarget ResumeJumpTarget::AtLoopHeader(int loop_header_offset,
      75         580 :                                                 const ResumeJumpTarget& next) {
      76             :   return ResumeJumpTarget(next.suspend_id(), loop_header_offset,
      77           0 :                           next.target_offset());
      78             : }
      79             : 
      80      523131 : 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),
      84             :       zone_(zone),
      85             :       loop_stack_(zone),
      86             :       loop_end_index_queue_(zone),
      87             :       resume_jump_targets_(zone),
      88             :       end_to_header_(zone),
      89             :       header_to_info_(zone),
      90             :       osr_entry_point_(-1),
      91     1569410 :       liveness_map_(bytecode_array->length(), zone) {}
      92             : 
      93             : namespace {
      94             : 
      95    17456810 : 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);
      99             : 
     100             :   // Special case Suspend and Resume to just pass through liveness.
     101    17456810 :   if (bytecode == Bytecode::kSuspendGenerator) {
     102             :     // The generator object has to be live.
     103       13972 :     in_liveness.MarkRegisterLive(accessor.GetRegisterOperand(0).index());
     104             :     // Suspend additionally reads and returns the accumulator
     105             :     DCHECK(Bytecodes::ReadsAccumulator(bytecode));
     106        6986 :     in_liveness.MarkAccumulatorLive();
     107        6986 :     return;
     108             :   }
     109    17449824 :   if (bytecode == Bytecode::kResumeGenerator) {
     110             :     // The generator object has to be live.
     111       13972 :     in_liveness.MarkRegisterLive(accessor.GetRegisterOperand(0).index());
     112        6986 :     return;
     113             :   }
     114             : 
     115    17442838 :   if (Bytecodes::WritesAccumulator(bytecode)) {
     116             :     in_liveness.MarkAccumulatorDead();
     117             :   }
     118    23148622 :   for (int i = 0; i < num_operands; ++i) {
     119    23148605 :     switch (operand_types[i]) {
     120             :       case OperandType::kRegOut: {
     121     4893586 :         interpreter::Register r = accessor.GetRegisterOperand(i);
     122     4893582 :         if (!r.is_parameter()) {
     123             :           in_liveness.MarkRegisterDead(r.index());
     124             :         }
     125             :         break;
     126             :       }
     127             :       case OperandType::kRegOutList: {
     128           0 :         interpreter::Register r = accessor.GetRegisterOperand(i++);
     129           0 :         uint32_t reg_count = accessor.GetRegisterCountOperand(i);
     130           0 :         if (!r.is_parameter()) {
     131           0 :           for (uint32_t j = 0; j < reg_count; ++j) {
     132             :             DCHECK(!interpreter::Register(r.index() + j).is_parameter());
     133           0 :             in_liveness.MarkRegisterDead(r.index() + j);
     134             :           }
     135             :         }
     136             :         break;
     137             :       }
     138             :       case OperandType::kRegOutPair: {
     139         684 :         interpreter::Register r = accessor.GetRegisterOperand(i);
     140         684 :         if (!r.is_parameter()) {
     141             :           DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
     142             :           in_liveness.MarkRegisterDead(r.index());
     143         684 :           in_liveness.MarkRegisterDead(r.index() + 1);
     144             :         }
     145             :         break;
     146             :       }
     147             :       case OperandType::kRegOutTriple: {
     148        1761 :         interpreter::Register r = accessor.GetRegisterOperand(i);
     149        1761 :         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        1761 :           in_liveness.MarkRegisterDead(r.index() + 1);
     154        1761 :           in_liveness.MarkRegisterDead(r.index() + 2);
     155             :         }
     156             :         break;
     157             :       }
     158             :       default:
     159             :         DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
     160             :         break;
     161             :     }
     162             :   }
     163             : 
     164             :   if (Bytecodes::ReadsAccumulator(bytecode)) {
     165     8765803 :     in_liveness.MarkAccumulatorLive();
     166             :   }
     167    22657243 :   for (int i = 0; i < num_operands; ++i) {
     168    22657221 :     switch (operand_types[i]) {
     169             :       case OperandType::kReg: {
     170     5412659 :         interpreter::Register r = accessor.GetRegisterOperand(i);
     171     5412684 :         if (!r.is_parameter()) {
     172             :           in_liveness.MarkRegisterLive(r.index());
     173             :         }
     174             :         break;
     175             :       }
     176             :       case OperandType::kRegPair: {
     177        3380 :         interpreter::Register r = accessor.GetRegisterOperand(i);
     178        3380 :         if (!r.is_parameter()) {
     179             :           DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
     180             :           in_liveness.MarkRegisterLive(r.index());
     181        3380 :           in_liveness.MarkRegisterLive(r.index() + 1);
     182             :         }
     183             :         break;
     184             :       }
     185             :       case OperandType::kRegList: {
     186      491395 :         interpreter::Register r = accessor.GetRegisterOperand(i++);
     187      491392 :         uint32_t reg_count = accessor.GetRegisterCountOperand(i);
     188      491392 :         if (!r.is_parameter()) {
     189     1457244 :           for (uint32_t j = 0; j < reg_count; ++j) {
     190             :             DCHECK(!interpreter::Register(r.index() + j).is_parameter());
     191     1457244 :             in_liveness.MarkRegisterLive(r.index() + j);
     192             :           }
     193             :         }
     194             :         break;
     195             :       }
     196             :       default:
     197             :         DCHECK(!Bytecodes::IsRegisterInputOperandType(operand_types[i]));
     198             :         break;
     199             :     }
     200             :   }
     201             : }
     202             : 
     203    17510128 : void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness,
     204             :                        BytecodeLivenessState* next_bytecode_in_liveness,
     205    17510128 :                        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();
     209             : 
     210             :   // Special case Suspend and Resume to just pass through liveness.
     211    17510128 :   if (bytecode == Bytecode::kSuspendGenerator ||
     212             :       bytecode == Bytecode::kResumeGenerator) {
     213             :     out_liveness.Union(*next_bytecode_in_liveness);
     214    17510141 :     return;
     215             :   }
     216             : 
     217             :   // Update from jump target (if any). Skip loops, we update these manually in
     218             :   // the liveness iterations.
     219    17496156 :   if (Bytecodes::IsForwardJump(bytecode)) {
     220      936735 :     int target_offset = accessor.GetJumpTargetOffset();
     221             :     out_liveness.Union(*liveness_map.GetInLiveness(target_offset));
     222    16559421 :   } else if (Bytecodes::IsSwitch(bytecode)) {
     223       22700 :     for (const auto& entry : accessor.GetJumpTableTargetOffsets()) {
     224       15866 :       out_liveness.Union(*liveness_map.GetInLiveness(entry.target_offset));
     225             :     }
     226             :   }
     227             : 
     228             :   // Update from next bytecode (unless there isn't one or this is an
     229             :   // unconditional jump).
     230    34470367 :   if (next_bytecode_in_liveness != nullptr &&
     231             :       !Bytecodes::IsUnconditionalJump(bytecode)) {
     232             :     out_liveness.Union(*next_bytecode_in_liveness);
     233             :   }
     234             : 
     235             :   // Update from exception handler (if any).
     236    17496157 :   if (!interpreter::Bytecodes::IsWithoutExternalSideEffects(bytecode)) {
     237             :     int handler_context;
     238             :     // TODO(leszeks): We should look up this range only once per entry.
     239     7623883 :     HandlerTable table(*bytecode_array);
     240             :     int handler_offset =
     241     7623879 :         table.LookupRange(current_offset, &handler_context, nullptr);
     242             : 
     243     7623881 :     if (handler_offset != -1) {
     244             :       bool was_accumulator_live = out_liveness.AccumulatorIsLive();
     245             :       out_liveness.Union(*liveness_map.GetInLiveness(handler_offset));
     246      432450 :       out_liveness.MarkRegisterLive(handler_context);
     247      432450 :       if (!was_accumulator_live) {
     248             :         // The accumulator is reset to the exception on entry into a handler,
     249             :         // and so shouldn't be considered live coming out of this bytecode just
     250             :         // because it's live coming into the handler. So, kill the accumulator
     251             :         // if the handler is the only thing that made it live.
     252             :         out_liveness.MarkAccumulatorDead();
     253             : 
     254             :         // TODO(leszeks): Ideally the accumulator wouldn't be considered live at
     255             :         // the start of the handler, but looking up if the current bytecode is
     256             :         // the start of a handler is not free, so we should only do it if we
     257             :         // decide it's necessary.
     258             :       }
     259             :     }
     260             :   }
     261             : }
     262             : 
     263    17456556 : 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    17456556 :                     accessor, liveness_map);
     269    17456569 :   liveness.in->CopyFrom(*liveness.out);
     270    17456567 :   UpdateInLiveness(bytecode, *liveness.in, accessor);
     271             : 
     272    17456559 :   *next_bytecode_in_liveness = liveness.in;
     273    17456559 : }
     274             : 
     275     1852232 : 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);
     279             : 
     280     4435294 :   for (int i = 0; i < num_operands; ++i) {
     281     2583059 :     switch (operand_types[i]) {
     282             :       case OperandType::kRegOut: {
     283      530271 :         assignments.Add(accessor.GetRegisterOperand(i));
     284      530274 :         break;
     285             :       }
     286             :       case OperandType::kRegOutList: {
     287         533 :         interpreter::Register r = accessor.GetRegisterOperand(i++);
     288         533 :         uint32_t reg_count = accessor.GetRegisterCountOperand(i);
     289         533 :         assignments.AddList(r, reg_count);
     290             :         break;
     291             :       }
     292             :       case OperandType::kRegOutPair: {
     293         279 :         assignments.AddList(accessor.GetRegisterOperand(i), 2);
     294         279 :         break;
     295             :       }
     296             :       case OperandType::kRegOutTriple: {
     297         137 :         assignments.AddList(accessor.GetRegisterOperand(i), 3);
     298         137 :         break;
     299             :       }
     300             :       default:
     301             :         DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
     302             :         break;
     303             :     }
     304             :   }
     305     1852235 : }
     306             : 
     307             : }  // namespace
     308             : 
     309    16620377 : void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
     310     1046274 :   loop_stack_.push({-1, nullptr});
     311             : 
     312      523140 :   BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
     313             : 
     314      523140 :   bool is_osr = !osr_bailout_id.IsNone();
     315      523140 :   int osr_loop_end_offset = is_osr ? osr_bailout_id.ToInt() : -1;
     316             : 
     317             :   int generator_switch_index = -1;
     318             : 
     319      523140 :   interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
     320    16146176 :   for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
     321    15623018 :     Bytecode bytecode = iterator.current_bytecode();
     322    15623016 :     int current_offset = iterator.current_offset();
     323             : 
     324    15623016 :     if (bytecode == Bytecode::kSwitchOnGeneratorState) {
     325             :       DCHECK_EQ(generator_switch_index, -1);
     326        2213 :       generator_switch_index = iterator.current_index();
     327             :     }
     328             : 
     329    15623016 :     if (bytecode == Bytecode::kJumpLoop) {
     330             :       // Every byte up to and including the last byte within the backwards jump
     331             :       // instruction is considered part of the loop, set loop end accordingly.
     332       58032 :       int loop_end = current_offset + iterator.current_bytecode_size();
     333       58032 :       int loop_header = iterator.GetJumpTargetOffset();
     334       58033 :       PushLoop(loop_header, loop_end);
     335             : 
     336       58033 :       if (current_offset == osr_loop_end_offset) {
     337        4917 :         osr_entry_point_ = loop_header;
     338             :       } else if (current_offset < osr_loop_end_offset) {
     339             :         // Check we've found the osr_entry_point if we've gone past the
     340             :         // osr_loop_end_offset. Note, we are iterating the bytecode in reverse,
     341             :         // so the less than in the check is correct.
     342             :         DCHECK_NE(-1, osr_entry_point_);
     343             :       }
     344             : 
     345             :       // Save the index so that we can do another pass later.
     346       58033 :       if (do_liveness_analysis_) {
     347      115818 :         loop_end_index_queue_.push_back(iterator.current_index());
     348             :       }
     349    15564984 :     } else if (loop_stack_.size() > 1) {
     350             :       LoopStackEntry& current_loop = loop_stack_.top();
     351     1852235 :       LoopInfo* current_loop_info = current_loop.loop_info;
     352             : 
     353             :       // TODO(leszeks): Ideally, we'd only set values that were assigned in
     354             :       // the loop *and* are live when the loop exits. However, this requires
     355             :       // tracking the out-liveness of *all* loop exits, which is not
     356             :       // information we currently have.
     357     1852235 :       UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);
     358             : 
     359             :       // Update suspend counts for this loop, though only if not OSR.
     360     1852234 :       if (!is_osr && bytecode == Bytecode::kSuspendGenerator) {
     361         501 :         int suspend_id = iterator.GetUnsignedImmediateOperand(3);
     362         501 :         int resume_offset = current_offset + iterator.current_bytecode_size();
     363             :         current_loop_info->AddResumeTarget(
     364        1002 :             ResumeJumpTarget::Leaf(suspend_id, resume_offset));
     365             :       }
     366             : 
     367             :       // If we've reached the header of the loop, pop it off the stack.
     368     1852234 :       if (current_offset == current_loop.header_offset) {
     369             :         loop_stack_.pop();
     370       58033 :         if (loop_stack_.size() > 1) {
     371             :           // If there is still an outer loop, propagate inner loop assignments.
     372        6335 :           LoopInfo* parent_loop_info = loop_stack_.top().loop_info;
     373             : 
     374             :           parent_loop_info->assignments().Union(
     375             :               current_loop_info->assignments());
     376             : 
     377             :           // Also, propagate resume targets. Instead of jumping to the target
     378             :           // itself, the outer loop will jump to this loop header for any
     379             :           // targets that are inside the current loop, so that this loop stays
     380             :           // reducible. Hence, a nested loop of the form:
     381             :           //
     382             :           //                switch (#1 -> suspend1, #2 -> suspend2)
     383             :           //                loop {
     384             :           //     suspend1:    suspend #1
     385             :           //                  loop {
     386             :           //     suspend2:      suspend #2
     387             :           //                  }
     388             :           //                }
     389             :           //
     390             :           // becomes:
     391             :           //
     392             :           //                switch (#1 -> loop1, #2 -> loop1)
     393             :           //     loop1:     loop {
     394             :           //                  switch (#1 -> suspend1, #2 -> loop2)
     395             :           //     suspend1:    suspend #1
     396             :           //     loop2:       loop {
     397             :           //                    switch (#2 -> suspend2)
     398             :           //     suspend2:      suspend #2
     399             :           //                  }
     400             :           //                }
     401       12749 :           for (const auto& target : current_loop_info->resume_jump_targets()) {
     402             :             parent_loop_info->AddResumeTarget(
     403         158 :                 ResumeJumpTarget::AtLoopHeader(current_offset, target));
     404             :           }
     405             : 
     406             :         } else {
     407             :           // Otherwise, just propagate inner loop suspends to top-level.
     408      103897 :           for (const auto& target : current_loop_info->resume_jump_targets()) {
     409             :             resume_jump_targets_.push_back(
     410        1002 :                 ResumeJumpTarget::AtLoopHeader(current_offset, target));
     411             :           }
     412             :         }
     413             :       }
     414    13712749 :     } else if (!is_osr && bytecode == Bytecode::kSuspendGenerator) {
     415             :       // If we're not in a loop, we still need to look for suspends.
     416             :       // TODO(leszeks): It would be nice to de-duplicate this with the in-loop
     417             :       // case
     418        5858 :       int suspend_id = iterator.GetUnsignedImmediateOperand(3);
     419        5858 :       int resume_offset = current_offset + iterator.current_bytecode_size();
     420             :       resume_jump_targets_.push_back(
     421       11716 :           ResumeJumpTarget::Leaf(suspend_id, resume_offset));
     422             :     }
     423             : 
     424    15623016 :     if (do_liveness_analysis_) {
     425             :       BytecodeLiveness& liveness = liveness_map_.InitializeLiveness(
     426    15574115 :           current_offset, bytecode_array()->register_count(), zone());
     427             :       UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
     428    15574107 :                      liveness_map_);
     429             :     }
     430             :   }
     431             : 
     432             :   DCHECK_EQ(loop_stack_.size(), 1u);
     433             :   DCHECK_EQ(loop_stack_.top().header_offset, -1);
     434             : 
     435             :   DCHECK(ResumeJumpTargetsAreValid());
     436             : 
     437     1046251 :   if (!do_liveness_analysis_) return;
     438             : 
     439             :   // At this point, every bytecode has a valid in and out liveness, except for
     440             :   // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness
     441             :   // analysis iterations can only add additional liveness bits that are pulled
     442             :   // across these back edges.
     443             :   //
     444             :   // Furthermore, a loop header's in-liveness can only change based on any
     445             :   // bytecodes *after* the loop end --  it cannot change as a result of the
     446             :   // JumpLoop liveness being updated, as the only liveness bits than can be
     447             :   // added to the loop body are those of the loop header.
     448             :   //
     449             :   // So, if we know that the liveness of bytecodes after a loop header won't
     450             :   // change (e.g. because there are no loops in them, or we have already ensured
     451             :   // those loops are valid), we can safely update the loop end and pass over the
     452             :   // loop body, and then never have to pass over that loop end again, because we
     453             :   // have shown that its target, the loop header, can't change from the entries
     454             :   // after the loop, and can't change from any loop body pass.
     455             :   //
     456             :   // This means that in a pass, we can iterate backwards over the bytecode
     457             :   // array, process any loops that we encounter, and on subsequent passes we can
     458             :   // skip processing those loops (though we still have to process inner loops).
     459             :   //
     460             :   // Equivalently, we can queue up loop ends from back to front, and pass over
     461             :   // the loops in that order, as this preserves both the bottom-to-top and
     462             :   // outer-to-inner requirements.
     463             : 
     464     1101795 :   for (int loop_end_index : loop_end_index_queue_) {
     465             :     iterator.GoToIndex(loop_end_index);
     466             : 
     467             :     DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
     468             : 
     469       57908 :     int header_offset = iterator.GetJumpTargetOffset();
     470       57909 :     int end_offset = iterator.current_offset();
     471             : 
     472             :     BytecodeLiveness& header_liveness =
     473       57909 :         liveness_map_.GetLiveness(header_offset);
     474       57909 :     BytecodeLiveness& end_liveness = liveness_map_.GetLiveness(end_offset);
     475             : 
     476      115816 :     if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) {
     477             :       // Only update the loop body if the loop end liveness changed.
     478             :       continue;
     479             :     }
     480       53564 :     end_liveness.in->CopyFrom(*end_liveness.out);
     481       53564 :     next_bytecode_in_liveness = end_liveness.in;
     482             : 
     483             :     // Advance into the loop body.
     484             :     --iterator;
     485     1936028 :     for (; iterator.current_offset() > header_offset; --iterator) {
     486     1882463 :       Bytecode bytecode = iterator.current_bytecode();
     487     1882463 :       int current_offset = iterator.current_offset();
     488     1882463 :       BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
     489             : 
     490             :       UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
     491     1882462 :                      liveness_map_);
     492             :     }
     493             :     // Now we are at the loop header. Since the in-liveness of the header
     494             :     // can't change, we need only to update the out-liveness.
     495             :     UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out,
     496       53565 :                       next_bytecode_in_liveness, iterator, liveness_map_);
     497             :   }
     498             : 
     499             :   // Process the generator switch statement separately, once the loops are done.
     500             :   // This has to be a separate pass because the generator switch can jump into
     501             :   // the middle of loops (and is the only kind of jump that can jump across a
     502             :   // loop header).
     503      521944 :   if (generator_switch_index != -1) {
     504             :     iterator.GoToIndex(generator_switch_index);
     505             :     DCHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnGeneratorState);
     506             : 
     507        2184 :     int current_offset = iterator.current_offset();
     508             :     BytecodeLiveness& switch_liveness =
     509        2184 :         liveness_map_.GetLiveness(current_offset);
     510             : 
     511             :     bool any_changed = false;
     512        8560 :     for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
     513        6376 :       if (switch_liveness.out->UnionIsChanged(
     514       12752 :               *liveness_map_.GetInLiveness(entry.target_offset))) {
     515             :         any_changed = true;
     516             :       }
     517             :     }
     518             : 
     519             :     // If the switch liveness changed, we have to propagate it up the remaining
     520             :     // bytecodes before it.
     521        2184 :     if (any_changed) {
     522         288 :       switch_liveness.in->CopyFrom(*switch_liveness.out);
     523             :       UpdateInLiveness(Bytecode::kSwitchOnGeneratorState, *switch_liveness.in,
     524         288 :                        iterator);
     525         288 :       next_bytecode_in_liveness = switch_liveness.in;
     526         288 :       for (--iterator; iterator.IsValid(); --iterator) {
     527           0 :         Bytecode bytecode = iterator.current_bytecode();
     528           0 :         int current_offset = iterator.current_offset();
     529           0 :         BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
     530             : 
     531             :         // There shouldn't be any more loops.
     532             :         DCHECK_NE(bytecode, Bytecode::kJumpLoop);
     533             : 
     534             :         UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
     535           0 :                        liveness_map_);
     536             :       }
     537             :     }
     538             :   }
     539             : 
     540             :   DCHECK(LivenessIsValid());
     541             : }
     542             : 
     543       58032 : 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());
     548             : 
     549       58032 :   int parent_offset = loop_stack_.top().header_offset;
     550             : 
     551       58032 :   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      116064 :                              bytecode_array_->register_count(), zone_)});
     555             :   // Get the loop info pointer from the output of insert.
     556       58032 :   LoopInfo* loop_info = &it.first->second;
     557             : 
     558      116065 :   loop_stack_.push({loop_header, loop_info});
     559       58033 : }
     560             : 
     561    13491439 : bool BytecodeAnalysis::IsLoopHeader(int offset) const {
     562    13491444 :   return header_to_info_.find(offset) != header_to_info_.end();
     563             : }
     564             : 
     565     2159341 : int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
     566             :   auto loop_end_to_header = end_to_header_.upper_bound(offset);
     567             :   // If there is no next end => offset is not in a loop.
     568     2159341 :   if (loop_end_to_header == end_to_header_.end()) {
     569             :     return -1;
     570             :   }
     571             :   // If the header precedes the offset, this is the loop
     572             :   //
     573             :   //   .> header  <--loop_end_to_header
     574             :   //   |
     575             :   //   |  <--offset
     576             :   //   |
     577             :   //   `- end
     578      734311 :   if (loop_end_to_header->second <= offset) {
     579             :     return loop_end_to_header->second;
     580             :   }
     581             :   // Otherwise there is a (potentially nested) loop after this offset.
     582             :   //
     583             :   //    <--offset
     584             :   //
     585             :   //   .> header
     586             :   //   |
     587             :   //   | .> header  <--loop_end_to_header
     588             :   //   | |
     589             :   //   | `- end
     590             :   //   |
     591             :   //   `- end
     592             :   // We just return the parent of the next loop (might be -1).
     593             :   DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end());
     594             : 
     595      417116 :   return header_to_info_.upper_bound(offset)->second.parent_offset();
     596             : }
     597             : 
     598      223103 : const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const {
     599             :   DCHECK(IsLoopHeader(header_offset));
     600             : 
     601      223103 :   return header_to_info_.find(header_offset)->second;
     602             : }
     603             : 
     604     5370676 : const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
     605             :     int offset) const {
     606     5370676 :   if (!do_liveness_analysis_) return nullptr;
     607             : 
     608    10700582 :   return liveness_map_.GetInLiveness(offset);
     609             : }
     610             : 
     611     3765414 : const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor(
     612             :     int offset) const {
     613     3765414 :   if (!do_liveness_analysis_) return nullptr;
     614             : 
     615     7501128 :   return liveness_map_.GetOutLiveness(offset);
     616             : }
     617             : 
     618           0 : std::ostream& BytecodeAnalysis::PrintLivenessTo(std::ostream& os) const {
     619           0 :   interpreter::BytecodeArrayIterator iterator(bytecode_array());
     620             : 
     621           0 :   for (; !iterator.done(); iterator.Advance()) {
     622           0 :     int current_offset = iterator.current_offset();
     623             : 
     624           0 :     const BitVector& in_liveness =
     625             :         GetInLivenessFor(current_offset)->bit_vector();
     626           0 :     const BitVector& out_liveness =
     627             :         GetOutLivenessFor(current_offset)->bit_vector();
     628             : 
     629           0 :     for (int i = 0; i < in_liveness.length(); ++i) {
     630           0 :       os << (in_liveness.Contains(i) ? "L" : ".");
     631             :     }
     632           0 :     os << " -> ";
     633             : 
     634           0 :     for (int i = 0; i < out_liveness.length(); ++i) {
     635           0 :       os << (out_liveness.Contains(i) ? "L" : ".");
     636             :     }
     637             : 
     638           0 :     os << " | " << current_offset << ": ";
     639           0 :     iterator.PrintTo(os) << std::endl;
     640             :   }
     641             : 
     642           0 :   return os;
     643             : }
     644             : 
     645             : #if DEBUG
     646             : bool BytecodeAnalysis::ResumeJumpTargetsAreValid() {
     647             :   bool valid = true;
     648             : 
     649             :   // Find the generator switch.
     650             :   interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
     651             :   for (iterator.GoToStart(); iterator.IsValid(); ++iterator) {
     652             :     if (iterator.current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
     653             :       break;
     654             :     }
     655             :   }
     656             : 
     657             :   // If the iterator is invalid, we've reached the end without finding the
     658             :   // generator switch. Similarly, if we are OSR-ing, we're not resuming, so we
     659             :   // need no jump targets. So, ensure there are no jump targets and exit.
     660             :   if (!iterator.IsValid() || HasOsrEntryPoint()) {
     661             :     // Check top-level.
     662             :     if (!resume_jump_targets().empty()) {
     663             :       PrintF(stderr,
     664             :              "Found %zu top-level resume targets but no resume switch\n",
     665             :              resume_jump_targets().size());
     666             :       valid = false;
     667             :     }
     668             :     // Check loops.
     669             :     for (const std::pair<const int, LoopInfo>& loop_info : header_to_info_) {
     670             :       if (!loop_info.second.resume_jump_targets().empty()) {
     671             :         PrintF(stderr,
     672             :                "Found %zu resume targets at loop at offset %d, but no resume "
     673             :                "switch\n",
     674             :                loop_info.second.resume_jump_targets().size(), loop_info.first);
     675             :         valid = false;
     676             :       }
     677             :     }
     678             : 
     679             :     return valid;
     680             :   }
     681             : 
     682             :   // Otherwise, we've found the resume switch. Check that the top level jumps
     683             :   // only to leaves and loop headers, then check that each loop header handles
     684             :   // all the unresolved jumps, also jumping only to leaves and inner loop
     685             :   // headers.
     686             : 
     687             :   // First collect all required suspend ids.
     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;
     693             : 
     694             :     unresolved_suspend_ids[suspend_id] = resume_offset;
     695             :   }
     696             : 
     697             :   // Check top-level.
     698             :   if (!ResumeJumpTargetLeavesResolveSuspendIds(-1, resume_jump_targets(),
     699             :                                                &unresolved_suspend_ids)) {
     700             :     valid = false;
     701             :   }
     702             :   // Check loops.
     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)) {
     707             :       valid = false;
     708             :     }
     709             :   }
     710             : 
     711             :   // Check that everything is resolved.
     712             :   if (!unresolved_suspend_ids.empty()) {
     713             :     PrintF(stderr,
     714             :            "Found suspend ids that are not resolved by a final leaf resume "
     715             :            "jump:\n");
     716             : 
     717             :     for (const std::pair<const int, int>& target : unresolved_suspend_ids) {
     718             :       PrintF(stderr, "  %d -> %d\n", target.first, target.second);
     719             :     }
     720             :     valid = false;
     721             :   }
     722             : 
     723             :   return valid;
     724             : }
     725             : 
     726             : bool BytecodeAnalysis::ResumeJumpTargetLeavesResolveSuspendIds(
     727             :     int parent_offset, const ZoneVector<ResumeJumpTarget>& resume_jump_targets,
     728             :     std::map<int, int>* unresolved_suspend_ids) {
     729             :   bool valid = true;
     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()) {
     734             :       PrintF(
     735             :           stderr,
     736             :           "No unresolved suspend found for resume target with suspend id %d\n",
     737             :           target.suspend_id());
     738             :       valid = false;
     739             :       continue;
     740             :     }
     741             :     int expected_target = it->second;
     742             : 
     743             :     if (target.is_leaf()) {
     744             :       // Leaves should have the expected target as their target.
     745             :       if (target.target_offset() != expected_target) {
     746             :         PrintF(
     747             :             stderr,
     748             :             "Expected leaf resume target for id %d to have target offset %d, "
     749             :             "but had %d\n",
     750             :             target.suspend_id(), expected_target, target.target_offset());
     751             :         valid = false;
     752             :       } else {
     753             :         // Make sure we're resuming to a Resume bytecode
     754             :         interpreter::BytecodeArrayAccessor assessor(bytecode_array(),
     755             :                                                     target.target_offset());
     756             :         if (assessor.current_bytecode() != Bytecode::kResumeGenerator) {
     757             :           PrintF(stderr,
     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()));
     762             : 
     763             :           valid = false;
     764             :         }
     765             :       }
     766             :       // We've resolved this suspend id, so erase it to make sure we don't
     767             :       // resolve it twice.
     768             :       unresolved_suspend_ids->erase(it);
     769             :     } else {
     770             :       // Non-leaves should have a direct inner loop header as their target.
     771             :       if (!IsLoopHeader(target.target_offset())) {
     772             :         PrintF(stderr,
     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());
     776             :         valid = false;
     777             :       } else {
     778             :         LoopInfo loop_info = GetLoopInfoFor(target.target_offset());
     779             :         if (loop_info.parent_offset() != parent_offset) {
     780             :           PrintF(stderr,
     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());
     784             :           valid = false;
     785             :         }
     786             :         // If the target loop is a valid inner loop, we'll check its validity
     787             :         // when we analyze its resume targets.
     788             :       }
     789             :     }
     790             :   }
     791             :   return valid;
     792             : }
     793             : 
     794             : bool BytecodeAnalysis::LivenessIsValid() {
     795             :   interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
     796             : 
     797             :   BytecodeLivenessState previous_liveness(bytecode_array()->register_count(),
     798             :                                           zone());
     799             : 
     800             :   int invalid_offset = -1;
     801             :   int which_invalid = -1;
     802             : 
     803             :   BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
     804             : 
     805             :   // Ensure that there are no liveness changes if we iterate one more time.
     806             :   for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
     807             :     Bytecode bytecode = iterator.current_bytecode();
     808             : 
     809             :     int current_offset = iterator.current_offset();
     810             : 
     811             :     BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
     812             : 
     813             :     previous_liveness.CopyFrom(*liveness.out);
     814             : 
     815             :     UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
     816             :                       iterator, liveness_map_);
     817             :     // UpdateOutLiveness skips kJumpLoop, so we update it manually.
     818             :     if (bytecode == Bytecode::kJumpLoop) {
     819             :       int target_offset = iterator.GetJumpTargetOffset();
     820             :       liveness.out->Union(*liveness_map_.GetInLiveness(target_offset));
     821             :     }
     822             : 
     823             :     if (!liveness.out->Equals(previous_liveness)) {
     824             :       // Reset the invalid liveness.
     825             :       liveness.out->CopyFrom(previous_liveness);
     826             :       invalid_offset = current_offset;
     827             :       which_invalid = 1;
     828             :       break;
     829             :     }
     830             : 
     831             :     previous_liveness.CopyFrom(*liveness.in);
     832             : 
     833             :     liveness.in->CopyFrom(*liveness.out);
     834             :     UpdateInLiveness(bytecode, *liveness.in, iterator);
     835             : 
     836             :     if (!liveness.in->Equals(previous_liveness)) {
     837             :       // Reset the invalid liveness.
     838             :       liveness.in->CopyFrom(previous_liveness);
     839             :       invalid_offset = current_offset;
     840             :       which_invalid = 0;
     841             :       break;
     842             :     }
     843             : 
     844             :     next_bytecode_in_liveness = liveness.in;
     845             :   }
     846             : 
     847             :   // Ensure that the accumulator is not live when jumping out of a loop, or on
     848             :   // the back-edge of a loop.
     849             :   for (iterator.GoToStart(); iterator.IsValid() && invalid_offset == -1;
     850             :        ++iterator) {
     851             :     Bytecode bytecode = iterator.current_bytecode();
     852             :     int current_offset = iterator.current_offset();
     853             :     int loop_header = GetLoopOffsetFor(current_offset);
     854             : 
     855             :     // We only care if we're inside a loop.
     856             :     if (loop_header == -1) continue;
     857             : 
     858             :     // We only care about jumps.
     859             :     if (!Bytecodes::IsJump(bytecode)) continue;
     860             : 
     861             :     int jump_target = iterator.GetJumpTargetOffset();
     862             : 
     863             :     // If this is a forward jump to somewhere else in the same loop, ignore it.
     864             :     if (Bytecodes::IsForwardJump(bytecode) &&
     865             :         GetLoopOffsetFor(jump_target) == loop_header) {
     866             :       continue;
     867             :     }
     868             : 
     869             :     // The accumulator must be dead at the start of the target of the jump.
     870             :     if (liveness_map_.GetLiveness(jump_target).in->AccumulatorIsLive()) {
     871             :       invalid_offset = jump_target;
     872             :       which_invalid = 0;
     873             :       break;
     874             :     }
     875             :   }
     876             : 
     877             :   if (invalid_offset != -1) {
     878             :     OFStream of(stderr);
     879             :     of << "Invalid liveness:" << std::endl;
     880             : 
     881             :     // Dump the bytecode, annotated with the liveness and marking loops.
     882             : 
     883             :     int loop_indent = 0;
     884             : 
     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();
     892             : 
     893             :       for (int i = 0; i < in_liveness.length(); ++i) {
     894             :         of << (in_liveness.Contains(i) ? 'L' : '.');
     895             :       }
     896             : 
     897             :       of << " | ";
     898             : 
     899             :       for (int i = 0; i < out_liveness.length(); ++i) {
     900             :         of << (out_liveness.Contains(i) ? 'L' : '.');
     901             :       }
     902             : 
     903             :       of << " : " << current_offset << " : ";
     904             : 
     905             :       // Draw loop back edges by indentin everything between loop headers and
     906             :       // jump loop instructions.
     907             :       if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
     908             :         loop_indent--;
     909             :       }
     910             :       for (int i = 0; i < loop_indent; ++i) {
     911             :         of << "| ";
     912             :       }
     913             :       if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
     914             :         of << "`-";
     915             :       } else if (IsLoopHeader(current_offset)) {
     916             :         of << ".>";
     917             :         loop_indent++;
     918             :       }
     919             :       forward_iterator.PrintTo(of);
     920             :       if (Bytecodes::IsJump(forward_iterator.current_bytecode())) {
     921             :         of << " (@" << forward_iterator.GetJumpTargetOffset() << ")";
     922             :       }
     923             :       of << std::endl;
     924             : 
     925             :       if (current_offset == invalid_offset) {
     926             :         // Underline the invalid liveness.
     927             :         if (which_invalid == 0) {
     928             :           for (int i = 0; i < in_liveness.length(); ++i) {
     929             :             of << '^';
     930             :           }
     931             :           for (int i = 0; i < out_liveness.length() + 3; ++i) {
     932             :             of << ' ';
     933             :           }
     934             :         } else {
     935             :           for (int i = 0; i < in_liveness.length() + 3; ++i) {
     936             :             of << ' ';
     937             :           }
     938             :           for (int i = 0; i < out_liveness.length(); ++i) {
     939             :             of << '^';
     940             :           }
     941             :         }
     942             : 
     943             :         // Make sure to draw the loop indentation marks on this additional line.
     944             :         of << " : " << current_offset << " : ";
     945             :         for (int i = 0; i < loop_indent; ++i) {
     946             :           of << "| ";
     947             :         }
     948             : 
     949             :         of << std::endl;
     950             :       }
     951             :     }
     952             :   }
     953             : 
     954             :   return invalid_offset == -1;
     955             : }
     956             : #endif
     957             : 
     958             : }  // namespace compiler
     959             : }  // namespace internal
     960      183867 : }  // namespace v8

Generated by: LCOV version 1.10