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
11namespace v8 {
12namespace internal {
13namespace compiler {
14
15using namespace interpreter;
16
17BytecodeLoopAssignments::BytecodeLoopAssignments(int parameter_count,
18                                                 int register_count, Zone* zone)
19    : parameter_count_(parameter_count),
20      bit_vector_(new (zone)
21                      BitVector(parameter_count + register_count, zone)) {}
22
23void BytecodeLoopAssignments::Add(interpreter::Register r) {
24  if (r.is_parameter()) {
25    bit_vector_->Add(r.ToParameterIndex(parameter_count_));
26  } else {
27    bit_vector_->Add(parameter_count_ + r.index());
28  }
29}
30
31void BytecodeLoopAssignments::AddPair(interpreter::Register r) {
32  if (r.is_parameter()) {
33    DCHECK(interpreter::Register(r.index() + 1).is_parameter());
34    bit_vector_->Add(r.ToParameterIndex(parameter_count_));
35    bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
36  } else {
37    DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
38    bit_vector_->Add(parameter_count_ + r.index());
39    bit_vector_->Add(parameter_count_ + r.index() + 1);
40  }
41}
42
43void BytecodeLoopAssignments::AddTriple(interpreter::Register r) {
44  if (r.is_parameter()) {
45    DCHECK(interpreter::Register(r.index() + 1).is_parameter());
46    DCHECK(interpreter::Register(r.index() + 2).is_parameter());
47    bit_vector_->Add(r.ToParameterIndex(parameter_count_));
48    bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 1);
49    bit_vector_->Add(r.ToParameterIndex(parameter_count_) + 2);
50  } else {
51    DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
52    DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
53    bit_vector_->Add(parameter_count_ + r.index());
54    bit_vector_->Add(parameter_count_ + r.index() + 1);
55    bit_vector_->Add(parameter_count_ + r.index() + 2);
56  }
57}
58
59void BytecodeLoopAssignments::AddAll() { bit_vector_->AddAll(); }
60
61void BytecodeLoopAssignments::Union(const BytecodeLoopAssignments& other) {
62  bit_vector_->Union(*other.bit_vector_);
63}
64
65bool BytecodeLoopAssignments::ContainsParameter(int index) const {
66  DCHECK_GE(index, 0);
67  DCHECK_LT(index, parameter_count());
68  return bit_vector_->Contains(index);
69}
70
71bool BytecodeLoopAssignments::ContainsLocal(int index) const {
72  DCHECK_GE(index, 0);
73  DCHECK_LT(index, local_count());
74  return bit_vector_->Contains(parameter_count_ + index);
75}
76
77bool BytecodeLoopAssignments::ContainsAccumulator() const {
78  // TODO(leszeks): This assumes the accumulator is always assigned. This is
79  // probably correct, but that assignment is also probably dead, so we should
80  // check liveness.
81  return true;
82}
83
84BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
85                                   Zone* zone, bool do_liveness_analysis)
86    : bytecode_array_(bytecode_array),
87      do_liveness_analysis_(do_liveness_analysis),
88      zone_(zone),
89      loop_stack_(zone),
90      loop_end_index_queue_(zone),
91      end_to_header_(zone),
92      header_to_info_(zone),
93      liveness_map_(bytecode_array->length(), zone) {}
94
95namespace {
96
97void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness,
98                      const BytecodeArrayAccessor& accessor) {
99  int num_operands = Bytecodes::NumberOfOperands(bytecode);
100  const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
101
102  if (Bytecodes::WritesAccumulator(bytecode)) {
103    in_liveness.MarkAccumulatorDead();
104  }
105  for (int i = 0; i < num_operands; ++i) {
106    switch (operand_types[i]) {
107      case OperandType::kRegOut: {
108        interpreter::Register r = accessor.GetRegisterOperand(i);
109        if (!r.is_parameter()) {
110          in_liveness.MarkRegisterDead(r.index());
111        }
112        break;
113      }
114      case OperandType::kRegOutPair: {
115        interpreter::Register r = accessor.GetRegisterOperand(i);
116        if (!r.is_parameter()) {
117          DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
118          in_liveness.MarkRegisterDead(r.index());
119          in_liveness.MarkRegisterDead(r.index() + 1);
120        }
121        break;
122      }
123      case OperandType::kRegOutTriple: {
124        interpreter::Register r = accessor.GetRegisterOperand(i);
125        if (!r.is_parameter()) {
126          DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
127          DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
128          in_liveness.MarkRegisterDead(r.index());
129          in_liveness.MarkRegisterDead(r.index() + 1);
130          in_liveness.MarkRegisterDead(r.index() + 2);
131        }
132        break;
133      }
134      default:
135        DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
136        break;
137    }
138  }
139
140  if (Bytecodes::ReadsAccumulator(bytecode)) {
141    in_liveness.MarkAccumulatorLive();
142  }
143  for (int i = 0; i < num_operands; ++i) {
144    switch (operand_types[i]) {
145      case OperandType::kReg: {
146        interpreter::Register r = accessor.GetRegisterOperand(i);
147        if (!r.is_parameter()) {
148          in_liveness.MarkRegisterLive(r.index());
149        }
150        break;
151      }
152      case OperandType::kRegPair: {
153        interpreter::Register r = accessor.GetRegisterOperand(i);
154        if (!r.is_parameter()) {
155          DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
156          in_liveness.MarkRegisterLive(r.index());
157          in_liveness.MarkRegisterLive(r.index() + 1);
158        }
159        break;
160      }
161      case OperandType::kRegList: {
162        interpreter::Register r = accessor.GetRegisterOperand(i++);
163        uint32_t reg_count = accessor.GetRegisterCountOperand(i);
164        if (!r.is_parameter()) {
165          for (uint32_t j = 0; j < reg_count; ++j) {
166            DCHECK(!interpreter::Register(r.index() + j).is_parameter());
167            in_liveness.MarkRegisterLive(r.index() + j);
168          }
169        }
170      }
171      default:
172        DCHECK(!Bytecodes::IsRegisterInputOperandType(operand_types[i]));
173        break;
174    }
175  }
176}
177
178void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness,
179                       BytecodeLivenessState* next_bytecode_in_liveness,
180                       const BytecodeArrayAccessor& accessor,
181                       const BytecodeLivenessMap& liveness_map) {
182  int current_offset = accessor.current_offset();
183  const Handle<BytecodeArray>& bytecode_array = accessor.bytecode_array();
184
185  // Update from jump target (if any). Skip loops, we update these manually in
186  // the liveness iterations.
187  if (Bytecodes::IsForwardJump(bytecode)) {
188    int target_offset = accessor.GetJumpTargetOffset();
189    out_liveness.Union(*liveness_map.GetInLiveness(target_offset));
190  }
191
192  // Update from next bytecode (unless there isn't one or this is an
193  // unconditional jump).
194  if (next_bytecode_in_liveness != nullptr &&
195      !Bytecodes::IsUnconditionalJump(bytecode)) {
196    out_liveness.Union(*next_bytecode_in_liveness);
197  }
198
199  // Update from exception handler (if any).
200  if (!interpreter::Bytecodes::IsWithoutExternalSideEffects(bytecode)) {
201    int handler_context;
202    // TODO(leszeks): We should look up this range only once per entry.
203    HandlerTable* table = HandlerTable::cast(bytecode_array->handler_table());
204    int handler_offset =
205        table->LookupRange(current_offset, &handler_context, nullptr);
206
207    if (handler_offset != -1) {
208      out_liveness.Union(*liveness_map.GetInLiveness(handler_offset));
209      out_liveness.MarkRegisterLive(handler_context);
210    }
211  }
212}
213
214void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments,
215                       const BytecodeArrayAccessor& accessor) {
216  int num_operands = Bytecodes::NumberOfOperands(bytecode);
217  const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);
218
219  for (int i = 0; i < num_operands; ++i) {
220    switch (operand_types[i]) {
221      case OperandType::kRegOut: {
222        assignments.Add(accessor.GetRegisterOperand(i));
223        break;
224      }
225      case OperandType::kRegOutPair: {
226        assignments.AddPair(accessor.GetRegisterOperand(i));
227        break;
228      }
229      case OperandType::kRegOutTriple: {
230        assignments.AddTriple(accessor.GetRegisterOperand(i));
231        break;
232      }
233      default:
234        DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
235        break;
236    }
237  }
238}
239
240}  // namespace
241
242void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
243  loop_stack_.push({-1, nullptr});
244
245  BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
246
247  int osr_loop_end_offset =
248      osr_bailout_id.IsNone() ? -1 : osr_bailout_id.ToInt();
249
250  BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
251  for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
252    Bytecode bytecode = iterator.current_bytecode();
253    int current_offset = iterator.current_offset();
254
255    if (bytecode == Bytecode::kJumpLoop) {
256      // Every byte up to and including the last byte within the backwards jump
257      // instruction is considered part of the loop, set loop end accordingly.
258      int loop_end = current_offset + iterator.current_bytecode_size();
259      PushLoop(iterator.GetJumpTargetOffset(), loop_end);
260
261      // Normally prefixed bytecodes are treated as if the prefix's offset was
262      // the actual bytecode's offset. However, the OSR id is the offset of the
263      // actual JumpLoop bytecode, so we need to find the location of that
264      // bytecode ignoring the prefix.
265      int jump_loop_offset = current_offset + iterator.current_prefix_offset();
266      bool is_osr_loop = (jump_loop_offset == osr_loop_end_offset);
267
268      // Check that is_osr_loop is set iff the osr_loop_end_offset is within
269      // this bytecode.
270      DCHECK(!is_osr_loop ||
271             iterator.OffsetWithinBytecode(osr_loop_end_offset));
272
273      // OSR "assigns" everything to OSR values on entry into an OSR loop, so we
274      // need to make sure to considered everything to be assigned.
275      if (is_osr_loop) {
276        loop_stack_.top().loop_info->assignments().AddAll();
277      }
278
279      // Save the index so that we can do another pass later.
280      if (do_liveness_analysis_) {
281        loop_end_index_queue_.push_back(iterator.current_index());
282      }
283    } else if (loop_stack_.size() > 1) {
284      LoopStackEntry& current_loop = loop_stack_.top();
285      LoopInfo* current_loop_info = current_loop.loop_info;
286
287      // TODO(leszeks): Ideally, we'd only set values that were assigned in
288      // the loop *and* are live when the loop exits. However, this requires
289      // tracking the out-liveness of *all* loop exits, which is not
290      // information we currently have.
291      UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);
292
293      if (current_offset == current_loop.header_offset) {
294        loop_stack_.pop();
295        if (loop_stack_.size() > 1) {
296          // Propagate inner loop assignments to outer loop.
297          loop_stack_.top().loop_info->assignments().Union(
298              current_loop_info->assignments());
299        }
300      }
301    }
302
303    if (do_liveness_analysis_) {
304      BytecodeLiveness& liveness = liveness_map_.InitializeLiveness(
305          current_offset, bytecode_array()->register_count(), zone());
306
307      UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
308                        iterator, liveness_map_);
309      liveness.in->CopyFrom(*liveness.out);
310      UpdateInLiveness(bytecode, *liveness.in, iterator);
311
312      next_bytecode_in_liveness = liveness.in;
313    }
314  }
315
316  DCHECK_EQ(loop_stack_.size(), 1u);
317  DCHECK_EQ(loop_stack_.top().header_offset, -1);
318
319  if (!do_liveness_analysis_) return;
320
321  // At this point, every bytecode has a valid in and out liveness, except for
322  // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness
323  // analysis iterations can only add additional liveness bits that are pulled
324  // across these back edges.
325  //
326  // Furthermore, a loop header's in-liveness can only change based on any
327  // bytecodes *after* the loop end --  it cannot change as a result of the
328  // JumpLoop liveness being updated, as the only liveness bits than can be
329  // added to the loop body are those of the loop header.
330  //
331  // So, if we know that the liveness of bytecodes after a loop header won't
332  // change (e.g. because there are no loops in them, or we have already ensured
333  // those loops are valid), we can safely update the loop end and pass over the
334  // loop body, and then never have to pass over that loop end again, because we
335  // have shown that its target, the loop header, can't change from the entries
336  // after the loop, and can't change from any loop body pass.
337  //
338  // This means that in a pass, we can iterate backwards over the bytecode
339  // array, process any loops that we encounter, and on subsequent passes we can
340  // skip processing those loops (though we still have to process inner loops).
341  //
342  // Equivalently, we can queue up loop ends from back to front, and pass over
343  // the loops in that order, as this preserves both the bottom-to-top and
344  // outer-to-inner requirements.
345
346  for (int loop_end_index : loop_end_index_queue_) {
347    iterator.GoToIndex(loop_end_index);
348
349    DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);
350
351    int header_offset = iterator.GetJumpTargetOffset();
352    int end_offset = iterator.current_offset();
353
354    BytecodeLiveness& header_liveness =
355        liveness_map_.GetLiveness(header_offset);
356    BytecodeLiveness& end_liveness = liveness_map_.GetLiveness(end_offset);
357
358    if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) {
359      // Only update the loop body if the loop end liveness changed.
360      continue;
361    }
362    end_liveness.in->CopyFrom(*end_liveness.out);
363    next_bytecode_in_liveness = end_liveness.in;
364
365    // Advance into the loop body.
366    --iterator;
367    for (; iterator.current_offset() > header_offset; --iterator) {
368      Bytecode bytecode = iterator.current_bytecode();
369
370      int current_offset = iterator.current_offset();
371      BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
372
373      UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
374                        iterator, liveness_map_);
375      liveness.in->CopyFrom(*liveness.out);
376      UpdateInLiveness(bytecode, *liveness.in, iterator);
377
378      next_bytecode_in_liveness = liveness.in;
379    }
380    // Now we are at the loop header. Since the in-liveness of the header
381    // can't change, we need only to update the out-liveness.
382    UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out,
383                      next_bytecode_in_liveness, iterator, liveness_map_);
384  }
385
386  DCHECK(LivenessIsValid());
387}
388
389void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
390  DCHECK(loop_header < loop_end);
391  DCHECK(loop_stack_.top().header_offset < loop_header);
392  DCHECK(end_to_header_.find(loop_end) == end_to_header_.end());
393  DCHECK(header_to_info_.find(loop_header) == header_to_info_.end());
394
395  int parent_offset = loop_stack_.top().header_offset;
396
397  end_to_header_.insert({loop_end, loop_header});
398  auto it = header_to_info_.insert(
399      {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(),
400                             bytecode_array_->register_count(), zone_)});
401  // Get the loop info pointer from the output of insert.
402  LoopInfo* loop_info = &it.first->second;
403
404  loop_stack_.push({loop_header, loop_info});
405}
406
407bool BytecodeAnalysis::IsLoopHeader(int offset) const {
408  return header_to_info_.find(offset) != header_to_info_.end();
409}
410
411int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
412  auto loop_end_to_header = end_to_header_.upper_bound(offset);
413  // If there is no next end => offset is not in a loop.
414  if (loop_end_to_header == end_to_header_.end()) {
415    return -1;
416  }
417  // If the header preceeds the offset, this is the loop
418  //
419  //   .> header  <--loop_end_to_header
420  //   |
421  //   |  <--offset
422  //   |
423  //   `- end
424  if (loop_end_to_header->second <= offset) {
425    return loop_end_to_header->second;
426  }
427  // Otherwise there is a (potentially nested) loop after this offset.
428  //
429  //    <--offset
430  //
431  //   .> header
432  //   |
433  //   | .> header  <--loop_end_to_header
434  //   | |
435  //   | `- end
436  //   |
437  //   `- end
438  // We just return the parent of the next loop (might be -1).
439  DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end());
440
441  return header_to_info_.upper_bound(offset)->second.parent_offset();
442}
443
444const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const {
445  DCHECK(IsLoopHeader(header_offset));
446
447  return header_to_info_.find(header_offset)->second;
448}
449
450const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
451    int offset) const {
452  if (!do_liveness_analysis_) return nullptr;
453
454  return liveness_map_.GetInLiveness(offset);
455}
456
457const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor(
458    int offset) const {
459  if (!do_liveness_analysis_) return nullptr;
460
461  return liveness_map_.GetOutLiveness(offset);
462}
463
464std::ostream& BytecodeAnalysis::PrintLivenessTo(std::ostream& os) const {
465  interpreter::BytecodeArrayIterator iterator(bytecode_array());
466
467  for (; !iterator.done(); iterator.Advance()) {
468    int current_offset = iterator.current_offset();
469
470    const BitVector& in_liveness =
471        GetInLivenessFor(current_offset)->bit_vector();
472    const BitVector& out_liveness =
473        GetOutLivenessFor(current_offset)->bit_vector();
474
475    for (int i = 0; i < in_liveness.length(); ++i) {
476      os << (in_liveness.Contains(i) ? "L" : ".");
477    }
478    os << " -> ";
479
480    for (int i = 0; i < out_liveness.length(); ++i) {
481      os << (out_liveness.Contains(i) ? "L" : ".");
482    }
483
484    os << " | " << current_offset << ": ";
485    iterator.PrintTo(os) << std::endl;
486  }
487
488  return os;
489}
490
491#if DEBUG
492bool BytecodeAnalysis::LivenessIsValid() {
493  BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
494
495  BytecodeLivenessState previous_liveness(bytecode_array()->register_count(),
496                                          zone());
497
498  int invalid_offset = -1;
499  int which_invalid = -1;
500
501  BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
502
503  // Ensure that there are no liveness changes if we iterate one more time.
504  for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
505    Bytecode bytecode = iterator.current_bytecode();
506
507    int current_offset = iterator.current_offset();
508
509    BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
510
511    previous_liveness.CopyFrom(*liveness.out);
512
513    UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
514                      iterator, liveness_map_);
515    // UpdateOutLiveness skips kJumpLoop, so we update it manually.
516    if (bytecode == Bytecode::kJumpLoop) {
517      int target_offset = iterator.GetJumpTargetOffset();
518      liveness.out->Union(*liveness_map_.GetInLiveness(target_offset));
519    }
520
521    if (!liveness.out->Equals(previous_liveness)) {
522      // Reset the invalid liveness.
523      liveness.out->CopyFrom(previous_liveness);
524      invalid_offset = current_offset;
525      which_invalid = 1;
526      break;
527    }
528
529    previous_liveness.CopyFrom(*liveness.in);
530
531    liveness.in->CopyFrom(*liveness.out);
532    UpdateInLiveness(bytecode, *liveness.in, iterator);
533
534    if (!liveness.in->Equals(previous_liveness)) {
535      // Reset the invalid liveness.
536      liveness.in->CopyFrom(previous_liveness);
537      invalid_offset = current_offset;
538      which_invalid = 0;
539      break;
540    }
541
542    next_bytecode_in_liveness = liveness.in;
543  }
544
545  if (invalid_offset != -1) {
546    OFStream of(stderr);
547    of << "Invalid liveness:" << std::endl;
548
549    // Dump the bytecode, annotated with the liveness and marking loops.
550
551    int loop_indent = 0;
552
553    BytecodeArrayIterator forward_iterator(bytecode_array());
554    for (; !forward_iterator.done(); forward_iterator.Advance()) {
555      int current_offset = forward_iterator.current_offset();
556      const BitVector& in_liveness =
557          GetInLivenessFor(current_offset)->bit_vector();
558      const BitVector& out_liveness =
559          GetOutLivenessFor(current_offset)->bit_vector();
560
561      for (int i = 0; i < in_liveness.length(); ++i) {
562        of << (in_liveness.Contains(i) ? 'L' : '.');
563      }
564
565      of << " | ";
566
567      for (int i = 0; i < out_liveness.length(); ++i) {
568        of << (out_liveness.Contains(i) ? 'L' : '.');
569      }
570
571      of << " : " << current_offset << " : ";
572
573      // Draw loop back edges by indentin everything between loop headers and
574      // jump loop instructions.
575      if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
576        loop_indent--;
577      }
578      for (int i = 0; i < loop_indent; ++i) {
579        of << " | ";
580      }
581      if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
582        of << " `-" << current_offset;
583      } else if (IsLoopHeader(current_offset)) {
584        of << " .>" << current_offset;
585        loop_indent++;
586      }
587      forward_iterator.PrintTo(of) << std::endl;
588
589      if (current_offset == invalid_offset) {
590        // Underline the invalid liveness.
591        if (which_invalid == 0) {
592          for (int i = 0; i < in_liveness.length(); ++i) {
593            of << '^';
594          }
595        } else {
596          for (int i = 0; i < in_liveness.length() + 3; ++i) {
597            of << ' ';
598          }
599          for (int i = 0; i < out_liveness.length(); ++i) {
600            of << '^';
601          }
602        }
603
604        // Make sure to draw the loop indentation marks on this additional line.
605        of << " : " << current_offset << " : ";
606        for (int i = 0; i < loop_indent; ++i) {
607          of << " | ";
608        }
609
610        of << std::endl;
611      }
612    }
613  }
614
615  return invalid_offset == -1;
616}
617#endif
618
619}  // namespace compiler
620}  // namespace internal
621}  // namespace v8
622