bytecode-analysis.cc 34.8 KB
Newer Older
1 2 3 4 5 6
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/compiler/bytecode-analysis.h"

7
#include "src/interpreter/bytecode-array-iterator.h"
8
#include "src/interpreter/bytecode-array-random-iterator.h"
9
#include "src/utils/ostreams.h"
10
#include "src/objects/objects-inl.h"
11 12 13 14 15

namespace v8 {
namespace internal {
namespace compiler {

16 17 18
using interpreter::Bytecode;
using interpreter::Bytecodes;
using interpreter::OperandType;
19

20 21 22 23 24 25 26 27 28 29 30 31 32 33
BytecodeLoopAssignments::BytecodeLoopAssignments(int parameter_count,
                                                 int register_count, Zone* zone)
    : parameter_count_(parameter_count),
      bit_vector_(new (zone)
                      BitVector(parameter_count + register_count, zone)) {}

void BytecodeLoopAssignments::Add(interpreter::Register r) {
  if (r.is_parameter()) {
    bit_vector_->Add(r.ToParameterIndex(parameter_count_));
  } else {
    bit_vector_->Add(parameter_count_ + r.index());
  }
}

34
void BytecodeLoopAssignments::AddList(interpreter::Register r, uint32_t count) {
35
  if (r.is_parameter()) {
36 37 38 39
    for (uint32_t i = 0; i < count; i++) {
      DCHECK(interpreter::Register(r.index() + i).is_parameter());
      bit_vector_->Add(r.ToParameterIndex(parameter_count_) + i);
    }
40
  } else {
41 42 43 44
    for (uint32_t i = 0; i < count; i++) {
      DCHECK(!interpreter::Register(r.index() + i).is_parameter());
      bit_vector_->Add(parameter_count_ + r.index() + i);
    }
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
  }
}


void BytecodeLoopAssignments::Union(const BytecodeLoopAssignments& other) {
  bit_vector_->Union(*other.bit_vector_);
}

bool BytecodeLoopAssignments::ContainsParameter(int index) const {
  DCHECK_GE(index, 0);
  DCHECK_LT(index, parameter_count());
  return bit_vector_->Contains(index);
}

bool BytecodeLoopAssignments::ContainsLocal(int index) const {
  DCHECK_GE(index, 0);
  DCHECK_LT(index, local_count());
  return bit_vector_->Contains(parameter_count_ + index);
}

65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
ResumeJumpTarget::ResumeJumpTarget(int suspend_id, int target_offset,
                                   int final_target_offset)
    : suspend_id_(suspend_id),
      target_offset_(target_offset),
      final_target_offset_(final_target_offset) {}

ResumeJumpTarget ResumeJumpTarget::Leaf(int suspend_id, int target_offset) {
  return ResumeJumpTarget(suspend_id, target_offset, target_offset);
}

ResumeJumpTarget ResumeJumpTarget::AtLoopHeader(int loop_header_offset,
                                                const ResumeJumpTarget& next) {
  return ResumeJumpTarget(next.suspend_id(), loop_header_offset,
                          next.target_offset());
}

81
BytecodeAnalysis::BytecodeAnalysis(Handle<BytecodeArray> bytecode_array,
82
                                   Zone* zone, bool do_liveness_analysis)
83
    : bytecode_array_(bytecode_array),
84
      do_liveness_analysis_(do_liveness_analysis),
85 86
      zone_(zone),
      loop_stack_(zone),
87
      loop_end_index_queue_(zone),
88
      resume_jump_targets_(zone),
89
      end_to_header_(zone),
90
      header_to_info_(zone),
91
      osr_entry_point_(-1),
92 93 94 95
      liveness_map_(bytecode_array->length(), zone) {}

namespace {

96
void UpdateInLiveness(Bytecode bytecode, BytecodeLivenessState& in_liveness,
97
                      const interpreter::BytecodeArrayAccessor& accessor) {
98 99 100
  int num_operands = Bytecodes::NumberOfOperands(bytecode);
  const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);

101 102 103 104 105 106
  // Special case Suspend and Resume to just pass through liveness.
  if (bytecode == Bytecode::kSuspendGenerator) {
    // The generator object has to be live.
    in_liveness.MarkRegisterLive(accessor.GetRegisterOperand(0).index());
    // Suspend additionally reads and returns the accumulator
    DCHECK(Bytecodes::ReadsAccumulator(bytecode));
107
    in_liveness.MarkAccumulatorLive();
108 109 110 111 112 113 114 115
    return;
  }
  if (bytecode == Bytecode::kResumeGenerator) {
    // The generator object has to be live.
    in_liveness.MarkRegisterLive(accessor.GetRegisterOperand(0).index());
    return;
  }

116
  if (Bytecodes::WritesAccumulator(bytecode)) {
117
    in_liveness.MarkAccumulatorDead();
118 119 120 121 122 123
  }
  for (int i = 0; i < num_operands; ++i) {
    switch (operand_types[i]) {
      case OperandType::kRegOut: {
        interpreter::Register r = accessor.GetRegisterOperand(i);
        if (!r.is_parameter()) {
124
          in_liveness.MarkRegisterDead(r.index());
125 126 127
        }
        break;
      }
128 129 130 131 132 133 134 135 136 137 138
      case OperandType::kRegOutList: {
        interpreter::Register r = accessor.GetRegisterOperand(i++);
        uint32_t reg_count = accessor.GetRegisterCountOperand(i);
        if (!r.is_parameter()) {
          for (uint32_t j = 0; j < reg_count; ++j) {
            DCHECK(!interpreter::Register(r.index() + j).is_parameter());
            in_liveness.MarkRegisterDead(r.index() + j);
          }
        }
        break;
      }
139 140 141 142
      case OperandType::kRegOutPair: {
        interpreter::Register r = accessor.GetRegisterOperand(i);
        if (!r.is_parameter()) {
          DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
143 144
          in_liveness.MarkRegisterDead(r.index());
          in_liveness.MarkRegisterDead(r.index() + 1);
145 146 147 148 149 150 151 152
        }
        break;
      }
      case OperandType::kRegOutTriple: {
        interpreter::Register r = accessor.GetRegisterOperand(i);
        if (!r.is_parameter()) {
          DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
          DCHECK(!interpreter::Register(r.index() + 2).is_parameter());
153 154 155
          in_liveness.MarkRegisterDead(r.index());
          in_liveness.MarkRegisterDead(r.index() + 1);
          in_liveness.MarkRegisterDead(r.index() + 2);
156 157 158 159 160 161 162 163 164
        }
        break;
      }
      default:
        DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
        break;
    }
  }

165
  if (Bytecodes::ReadsAccumulator(bytecode)) {
166
    in_liveness.MarkAccumulatorLive();
167 168 169 170 171 172
  }
  for (int i = 0; i < num_operands; ++i) {
    switch (operand_types[i]) {
      case OperandType::kReg: {
        interpreter::Register r = accessor.GetRegisterOperand(i);
        if (!r.is_parameter()) {
173
          in_liveness.MarkRegisterLive(r.index());
174 175 176 177 178 179 180
        }
        break;
      }
      case OperandType::kRegPair: {
        interpreter::Register r = accessor.GetRegisterOperand(i);
        if (!r.is_parameter()) {
          DCHECK(!interpreter::Register(r.index() + 1).is_parameter());
181 182
          in_liveness.MarkRegisterLive(r.index());
          in_liveness.MarkRegisterLive(r.index() + 1);
183 184 185 186 187 188 189 190 191
        }
        break;
      }
      case OperandType::kRegList: {
        interpreter::Register r = accessor.GetRegisterOperand(i++);
        uint32_t reg_count = accessor.GetRegisterCountOperand(i);
        if (!r.is_parameter()) {
          for (uint32_t j = 0; j < reg_count; ++j) {
            DCHECK(!interpreter::Register(r.index() + j).is_parameter());
192
            in_liveness.MarkRegisterLive(r.index() + j);
193 194
          }
        }
195
        break;
196 197 198 199 200 201 202 203
      }
      default:
        DCHECK(!Bytecodes::IsRegisterInputOperandType(operand_types[i]));
        break;
    }
  }
}

204 205
void UpdateOutLiveness(Bytecode bytecode, BytecodeLivenessState& out_liveness,
                       BytecodeLivenessState* next_bytecode_in_liveness,
206
                       const interpreter::BytecodeArrayAccessor& accessor,
207
                       Handle<BytecodeArray> bytecode_array,
208 209 210
                       const BytecodeLivenessMap& liveness_map) {
  int current_offset = accessor.current_offset();

211 212 213 214 215 216 217
  // Special case Suspend and Resume to just pass through liveness.
  if (bytecode == Bytecode::kSuspendGenerator ||
      bytecode == Bytecode::kResumeGenerator) {
    out_liveness.Union(*next_bytecode_in_liveness);
    return;
  }

218 219 220 221 222
  // Update from jump target (if any). Skip loops, we update these manually in
  // the liveness iterations.
  if (Bytecodes::IsForwardJump(bytecode)) {
    int target_offset = accessor.GetJumpTargetOffset();
    out_liveness.Union(*liveness_map.GetInLiveness(target_offset));
223 224 225 226
  } else if (Bytecodes::IsSwitch(bytecode)) {
    for (const auto& entry : accessor.GetJumpTableTargetOffsets()) {
      out_liveness.Union(*liveness_map.GetInLiveness(entry.target_offset));
    }
227 228
  }

229 230 231 232 233
  // Update from next bytecode (unless there isn't one or this is an
  // unconditional jump).
  if (next_bytecode_in_liveness != nullptr &&
      !Bytecodes::IsUnconditionalJump(bytecode)) {
    out_liveness.Union(*next_bytecode_in_liveness);
234 235 236 237 238 239
  }

  // Update from exception handler (if any).
  if (!interpreter::Bytecodes::IsWithoutExternalSideEffects(bytecode)) {
    int handler_context;
    // TODO(leszeks): We should look up this range only once per entry.
240
    HandlerTable table(*bytecode_array);
241
    int handler_offset =
242
        table.LookupRange(current_offset, &handler_context, nullptr);
243 244

    if (handler_offset != -1) {
245
      bool was_accumulator_live = out_liveness.AccumulatorIsLive();
246
      out_liveness.Union(*liveness_map.GetInLiveness(handler_offset));
247
      out_liveness.MarkRegisterLive(handler_context);
248 249 250 251 252 253 254 255 256 257 258 259
      if (!was_accumulator_live) {
        // The accumulator is reset to the exception on entry into a handler,
        // and so shouldn't be considered live coming out of this bytecode just
        // because it's live coming into the handler. So, kill the accumulator
        // if the handler is the only thing that made it live.
        out_liveness.MarkAccumulatorDead();

        // TODO(leszeks): Ideally the accumulator wouldn't be considered live at
        // the start of the handler, but looking up if the current bytecode is
        // the start of a handler is not free, so we should only do it if we
        // decide it's necessary.
      }
260 261 262 263
    }
  }
}

264 265 266
void UpdateLiveness(Bytecode bytecode, BytecodeLiveness& liveness,
                    BytecodeLivenessState** next_bytecode_in_liveness,
                    const interpreter::BytecodeArrayAccessor& accessor,
267
                    Handle<BytecodeArray> bytecode_array,
268 269
                    const BytecodeLivenessMap& liveness_map) {
  UpdateOutLiveness(bytecode, *liveness.out, *next_bytecode_in_liveness,
270
                    accessor, bytecode_array, liveness_map);
271 272 273 274 275 276
  liveness.in->CopyFrom(*liveness.out);
  UpdateInLiveness(bytecode, *liveness.in, accessor);

  *next_bytecode_in_liveness = liveness.in;
}

277
void UpdateAssignments(Bytecode bytecode, BytecodeLoopAssignments& assignments,
278
                       const interpreter::BytecodeArrayAccessor& accessor) {
279 280 281 282 283 284 285 286 287
  int num_operands = Bytecodes::NumberOfOperands(bytecode);
  const OperandType* operand_types = Bytecodes::GetOperandTypes(bytecode);

  for (int i = 0; i < num_operands; ++i) {
    switch (operand_types[i]) {
      case OperandType::kRegOut: {
        assignments.Add(accessor.GetRegisterOperand(i));
        break;
      }
288 289 290 291 292 293
      case OperandType::kRegOutList: {
        interpreter::Register r = accessor.GetRegisterOperand(i++);
        uint32_t reg_count = accessor.GetRegisterCountOperand(i);
        assignments.AddList(r, reg_count);
        break;
      }
294
      case OperandType::kRegOutPair: {
295
        assignments.AddList(accessor.GetRegisterOperand(i), 2);
296 297 298
        break;
      }
      case OperandType::kRegOutTriple: {
299
        assignments.AddList(accessor.GetRegisterOperand(i), 3);
300 301 302 303 304 305 306 307 308
        break;
      }
      default:
        DCHECK(!Bytecodes::IsRegisterOutputOperandType(operand_types[i]));
        break;
    }
  }
}

309
}  // namespace
310

311 312
void BytecodeAnalysis::Analyze(BailoutId osr_bailout_id) {
  loop_stack_.push({-1, nullptr});
313

314
  BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
315

316 317 318 319
  bool is_osr = !osr_bailout_id.IsNone();
  int osr_loop_end_offset = is_osr ? osr_bailout_id.ToInt() : -1;

  int generator_switch_index = -1;
320

321
  interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
322
  for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
323 324 325
    Bytecode bytecode = iterator.current_bytecode();
    int current_offset = iterator.current_offset();

326 327 328 329 330
    if (bytecode == Bytecode::kSwitchOnGeneratorState) {
      DCHECK_EQ(generator_switch_index, -1);
      generator_switch_index = iterator.current_index();
    }

331
    if (bytecode == Bytecode::kJumpLoop) {
332 333 334
      // Every byte up to and including the last byte within the backwards jump
      // instruction is considered part of the loop, set loop end accordingly.
      int loop_end = current_offset + iterator.current_bytecode_size();
335 336
      int loop_header = iterator.GetJumpTargetOffset();
      PushLoop(loop_header, loop_end);
337

338
      if (current_offset == osr_loop_end_offset) {
339
        osr_entry_point_ = loop_header;
340 341 342 343 344
      } else if (current_offset < osr_loop_end_offset) {
        // Check we've found the osr_entry_point if we've gone past the
        // osr_loop_end_offset. Note, we are iterating the bytecode in reverse,
        // so the less than in the check is correct.
        DCHECK_NE(-1, osr_entry_point_);
345 346
      }

347 348 349
      // Save the index so that we can do another pass later.
      if (do_liveness_analysis_) {
        loop_end_index_queue_.push_back(iterator.current_index());
350
      }
351 352 353 354 355 356 357 358 359 360
    } else if (loop_stack_.size() > 1) {
      LoopStackEntry& current_loop = loop_stack_.top();
      LoopInfo* current_loop_info = current_loop.loop_info;

      // TODO(leszeks): Ideally, we'd only set values that were assigned in
      // the loop *and* are live when the loop exits. However, this requires
      // tracking the out-liveness of *all* loop exits, which is not
      // information we currently have.
      UpdateAssignments(bytecode, current_loop_info->assignments(), iterator);

361 362 363 364 365 366 367 368 369
      // Update suspend counts for this loop, though only if not OSR.
      if (!is_osr && bytecode == Bytecode::kSuspendGenerator) {
        int suspend_id = iterator.GetUnsignedImmediateOperand(3);
        int resume_offset = current_offset + iterator.current_bytecode_size();
        current_loop_info->AddResumeTarget(
            ResumeJumpTarget::Leaf(suspend_id, resume_offset));
      }

      // If we've reached the header of the loop, pop it off the stack.
370 371 372
      if (current_offset == current_loop.header_offset) {
        loop_stack_.pop();
        if (loop_stack_.size() > 1) {
373 374 375 376
          // If there is still an outer loop, propagate inner loop assignments.
          LoopInfo* parent_loop_info = loop_stack_.top().loop_info;

          parent_loop_info->assignments().Union(
377
              current_loop_info->assignments());
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413

          // Also, propagate resume targets. Instead of jumping to the target
          // itself, the outer loop will jump to this loop header for any
          // targets that are inside the current loop, so that this loop stays
          // reducible. Hence, a nested loop of the form:
          //
          //                switch (#1 -> suspend1, #2 -> suspend2)
          //                loop {
          //     suspend1:    suspend #1
          //                  loop {
          //     suspend2:      suspend #2
          //                  }
          //                }
          //
          // becomes:
          //
          //                switch (#1 -> loop1, #2 -> loop1)
          //     loop1:     loop {
          //                  switch (#1 -> suspend1, #2 -> loop2)
          //     suspend1:    suspend #1
          //     loop2:       loop {
          //                    switch (#2 -> suspend2)
          //     suspend2:      suspend #2
          //                  }
          //                }
          for (const auto& target : current_loop_info->resume_jump_targets()) {
            parent_loop_info->AddResumeTarget(
                ResumeJumpTarget::AtLoopHeader(current_offset, target));
          }

        } else {
          // Otherwise, just propagate inner loop suspends to top-level.
          for (const auto& target : current_loop_info->resume_jump_targets()) {
            resume_jump_targets_.push_back(
                ResumeJumpTarget::AtLoopHeader(current_offset, target));
          }
414 415
        }
      }
416 417 418 419 420 421 422 423
    } else if (!is_osr && bytecode == Bytecode::kSuspendGenerator) {
      // If we're not in a loop, we still need to look for suspends.
      // TODO(leszeks): It would be nice to de-duplicate this with the in-loop
      // case
      int suspend_id = iterator.GetUnsignedImmediateOperand(3);
      int resume_offset = current_offset + iterator.current_bytecode_size();
      resume_jump_targets_.push_back(
          ResumeJumpTarget::Leaf(suspend_id, resume_offset));
424
    }
425 426

    if (do_liveness_analysis_) {
427 428
      BytecodeLiveness& liveness = liveness_map_.InitializeLiveness(
          current_offset, bytecode_array()->register_count(), zone());
429
      UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
430
                     bytecode_array(), liveness_map_);
431
    }
432 433 434
  }

  DCHECK_EQ(loop_stack_.size(), 1u);
435
  DCHECK_EQ(loop_stack_.top().header_offset, -1);
436

437 438
  DCHECK(ResumeJumpTargetsAreValid());

439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460
  if (!do_liveness_analysis_) return;

  // At this point, every bytecode has a valid in and out liveness, except for
  // propagating liveness across back edges (i.e. JumpLoop). Subsequent liveness
  // analysis iterations can only add additional liveness bits that are pulled
  // across these back edges.
  //
  // Furthermore, a loop header's in-liveness can only change based on any
  // bytecodes *after* the loop end --  it cannot change as a result of the
  // JumpLoop liveness being updated, as the only liveness bits than can be
  // added to the loop body are those of the loop header.
  //
  // So, if we know that the liveness of bytecodes after a loop header won't
  // change (e.g. because there are no loops in them, or we have already ensured
  // those loops are valid), we can safely update the loop end and pass over the
  // loop body, and then never have to pass over that loop end again, because we
  // have shown that its target, the loop header, can't change from the entries
  // after the loop, and can't change from any loop body pass.
  //
  // This means that in a pass, we can iterate backwards over the bytecode
  // array, process any loops that we encounter, and on subsequent passes we can
  // skip processing those loops (though we still have to process inner loops).
461 462 463 464
  //
  // Equivalently, we can queue up loop ends from back to front, and pass over
  // the loops in that order, as this preserves both the bottom-to-top and
  // outer-to-inner requirements.
465

466 467
  for (int loop_end_index : loop_end_index_queue_) {
    iterator.GoToIndex(loop_end_index);
468 469 470

    DCHECK_EQ(iterator.current_bytecode(), Bytecode::kJumpLoop);

471 472
    int header_offset = iterator.GetJumpTargetOffset();
    int end_offset = iterator.current_offset();
473

474 475 476
    BytecodeLiveness& header_liveness =
        liveness_map_.GetLiveness(header_offset);
    BytecodeLiveness& end_liveness = liveness_map_.GetLiveness(end_offset);
477

478 479 480 481 482 483
    if (!end_liveness.out->UnionIsChanged(*header_liveness.in)) {
      // Only update the loop body if the loop end liveness changed.
      continue;
    }
    end_liveness.in->CopyFrom(*end_liveness.out);
    next_bytecode_in_liveness = end_liveness.in;
484

485 486 487 488 489
    // Advance into the loop body.
    --iterator;
    for (; iterator.current_offset() > header_offset; --iterator) {
      Bytecode bytecode = iterator.current_bytecode();
      int current_offset = iterator.current_offset();
490
      BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
491

492
      UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
493
                     bytecode_array(), liveness_map_);
494
    }
495 496 497
    // Now we are at the loop header. Since the in-liveness of the header
    // can't change, we need only to update the out-liveness.
    UpdateOutLiveness(iterator.current_bytecode(), *header_liveness.out,
498 499
                      next_bytecode_in_liveness, iterator, bytecode_array(),
                      liveness_map_);
500 501
  }

502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
  // Process the generator switch statement separately, once the loops are done.
  // This has to be a separate pass because the generator switch can jump into
  // the middle of loops (and is the only kind of jump that can jump across a
  // loop header).
  if (generator_switch_index != -1) {
    iterator.GoToIndex(generator_switch_index);
    DCHECK_EQ(iterator.current_bytecode(), Bytecode::kSwitchOnGeneratorState);

    int current_offset = iterator.current_offset();
    BytecodeLiveness& switch_liveness =
        liveness_map_.GetLiveness(current_offset);

    bool any_changed = false;
    for (const auto& entry : iterator.GetJumpTableTargetOffsets()) {
      if (switch_liveness.out->UnionIsChanged(
              *liveness_map_.GetInLiveness(entry.target_offset))) {
        any_changed = true;
      }
    }

    // If the switch liveness changed, we have to propagate it up the remaining
    // bytecodes before it.
    if (any_changed) {
      switch_liveness.in->CopyFrom(*switch_liveness.out);
      UpdateInLiveness(Bytecode::kSwitchOnGeneratorState, *switch_liveness.in,
                       iterator);
      next_bytecode_in_liveness = switch_liveness.in;
      for (--iterator; iterator.IsValid(); --iterator) {
        Bytecode bytecode = iterator.current_bytecode();
        int current_offset = iterator.current_offset();
        BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);

        // There shouldn't be any more loops.
        DCHECK_NE(bytecode, Bytecode::kJumpLoop);

        UpdateLiveness(bytecode, liveness, &next_bytecode_in_liveness, iterator,
538
                       bytecode_array(), liveness_map_);
539 540 541 542
      }
    }
  }

543 544 545 546 547 548
  DCHECK(do_liveness_analysis_);
  if (FLAG_trace_environment_liveness) {
    StdoutStream of;
    PrintLivenessTo(of);
  }

549
  DCHECK(LivenessIsValid());
550 551 552 553
}

void BytecodeAnalysis::PushLoop(int loop_header, int loop_end) {
  DCHECK(loop_header < loop_end);
554
  DCHECK(loop_stack_.top().header_offset < loop_header);
555
  DCHECK(end_to_header_.find(loop_end) == end_to_header_.end());
556 557 558 559 560 561 562 563 564 565
  DCHECK(header_to_info_.find(loop_header) == header_to_info_.end());

  int parent_offset = loop_stack_.top().header_offset;

  end_to_header_.insert({loop_end, loop_header});
  auto it = header_to_info_.insert(
      {loop_header, LoopInfo(parent_offset, bytecode_array_->parameter_count(),
                             bytecode_array_->register_count(), zone_)});
  // Get the loop info pointer from the output of insert.
  LoopInfo* loop_info = &it.first->second;
566

567
  loop_stack_.push({loop_header, loop_info});
568 569 570
}

bool BytecodeAnalysis::IsLoopHeader(int offset) const {
571
  return header_to_info_.find(offset) != header_to_info_.end();
572 573 574
}

int BytecodeAnalysis::GetLoopOffsetFor(int offset) const {
575
  auto loop_end_to_header = end_to_header_.upper_bound(offset);
576 577 578 579
  // If there is no next end => offset is not in a loop.
  if (loop_end_to_header == end_to_header_.end()) {
    return -1;
  }
580
  // If the header precedes the offset, this is the loop
581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
  //
  //   .> header  <--loop_end_to_header
  //   |
  //   |  <--offset
  //   |
  //   `- end
  if (loop_end_to_header->second <= offset) {
    return loop_end_to_header->second;
  }
  // Otherwise there is a (potentially nested) loop after this offset.
  //
  //    <--offset
  //
  //   .> header
  //   |
  //   | .> header  <--loop_end_to_header
  //   | |
  //   | `- end
  //   |
  //   `- end
601 602
  // We just return the parent of the next loop (might be -1).
  DCHECK(header_to_info_.upper_bound(offset) != header_to_info_.end());
603

604
  return header_to_info_.upper_bound(offset)->second.parent_offset();
605 606
}

607
const LoopInfo& BytecodeAnalysis::GetLoopInfoFor(int header_offset) const {
608 609
  DCHECK(IsLoopHeader(header_offset));

610
  return header_to_info_.find(header_offset)->second;
611 612
}

613 614
const BytecodeLivenessState* BytecodeAnalysis::GetInLivenessFor(
    int offset) const {
615 616 617 618 619
  if (!do_liveness_analysis_) return nullptr;

  return liveness_map_.GetInLiveness(offset);
}

620 621
const BytecodeLivenessState* BytecodeAnalysis::GetOutLivenessFor(
    int offset) const {
622 623 624 625 626 627 628 629 630 631 632
  if (!do_liveness_analysis_) return nullptr;

  return liveness_map_.GetOutLiveness(offset);
}

std::ostream& BytecodeAnalysis::PrintLivenessTo(std::ostream& os) const {
  interpreter::BytecodeArrayIterator iterator(bytecode_array());

  for (; !iterator.done(); iterator.Advance()) {
    int current_offset = iterator.current_offset();

633 634 635 636
    const BitVector& in_liveness =
        GetInLivenessFor(current_offset)->bit_vector();
    const BitVector& out_liveness =
        GetOutLivenessFor(current_offset)->bit_vector();
637

638 639
    for (int i = 0; i < in_liveness.length(); ++i) {
      os << (in_liveness.Contains(i) ? "L" : ".");
640 641 642
    }
    os << " -> ";

643 644
    for (int i = 0; i < out_liveness.length(); ++i) {
      os << (out_liveness.Contains(i) ? "L" : ".");
645 646 647 648 649 650 651 652 653 654
    }

    os << " | " << current_offset << ": ";
    iterator.PrintTo(os) << std::endl;
  }

  return os;
}

#if DEBUG
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677
bool BytecodeAnalysis::ResumeJumpTargetsAreValid() {
  bool valid = true;

  // Find the generator switch.
  interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
  for (iterator.GoToStart(); iterator.IsValid(); ++iterator) {
    if (iterator.current_bytecode() == Bytecode::kSwitchOnGeneratorState) {
      break;
    }
  }

  // If the iterator is invalid, we've reached the end without finding the
  // generator switch. Similarly, if we are OSR-ing, we're not resuming, so we
  // need no jump targets. So, ensure there are no jump targets and exit.
  if (!iterator.IsValid() || HasOsrEntryPoint()) {
    // Check top-level.
    if (!resume_jump_targets().empty()) {
      PrintF(stderr,
             "Found %zu top-level resume targets but no resume switch\n",
             resume_jump_targets().size());
      valid = false;
    }
    // Check loops.
678
    for (const std::pair<const int, LoopInfo>& loop_info : header_to_info_) {
679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711
      if (!loop_info.second.resume_jump_targets().empty()) {
        PrintF(stderr,
               "Found %zu resume targets at loop at offset %d, but no resume "
               "switch\n",
               loop_info.second.resume_jump_targets().size(), loop_info.first);
        valid = false;
      }
    }

    return valid;
  }

  // Otherwise, we've found the resume switch. Check that the top level jumps
  // only to leaves and loop headers, then check that each loop header handles
  // all the unresolved jumps, also jumping only to leaves and inner loop
  // headers.

  // First collect all required suspend ids.
  std::map<int, int> unresolved_suspend_ids;
  for (const interpreter::JumpTableTargetOffset& offset :
       iterator.GetJumpTableTargetOffsets()) {
    int suspend_id = offset.case_value;
    int resume_offset = offset.target_offset;

    unresolved_suspend_ids[suspend_id] = resume_offset;
  }

  // Check top-level.
  if (!ResumeJumpTargetLeavesResolveSuspendIds(-1, resume_jump_targets(),
                                               &unresolved_suspend_ids)) {
    valid = false;
  }
  // Check loops.
712
  for (const std::pair<const int, LoopInfo>& loop_info : header_to_info_) {
713 714 715 716 717 718 719 720 721 722 723 724 725
    if (!ResumeJumpTargetLeavesResolveSuspendIds(
            loop_info.first, loop_info.second.resume_jump_targets(),
            &unresolved_suspend_ids)) {
      valid = false;
    }
  }

  // Check that everything is resolved.
  if (!unresolved_suspend_ids.empty()) {
    PrintF(stderr,
           "Found suspend ids that are not resolved by a final leaf resume "
           "jump:\n");

726
    for (const std::pair<const int, int>& target : unresolved_suspend_ids) {
727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762
      PrintF(stderr, "  %d -> %d\n", target.first, target.second);
    }
    valid = false;
  }

  return valid;
}

bool BytecodeAnalysis::ResumeJumpTargetLeavesResolveSuspendIds(
    int parent_offset, const ZoneVector<ResumeJumpTarget>& resume_jump_targets,
    std::map<int, int>* unresolved_suspend_ids) {
  bool valid = true;
  for (const ResumeJumpTarget& target : resume_jump_targets) {
    std::map<int, int>::iterator it =
        unresolved_suspend_ids->find(target.suspend_id());
    if (it == unresolved_suspend_ids->end()) {
      PrintF(
          stderr,
          "No unresolved suspend found for resume target with suspend id %d\n",
          target.suspend_id());
      valid = false;
      continue;
    }
    int expected_target = it->second;

    if (target.is_leaf()) {
      // Leaves should have the expected target as their target.
      if (target.target_offset() != expected_target) {
        PrintF(
            stderr,
            "Expected leaf resume target for id %d to have target offset %d, "
            "but had %d\n",
            target.suspend_id(), expected_target, target.target_offset());
        valid = false;
      } else {
        // Make sure we're resuming to a Resume bytecode
763
        interpreter::BytecodeArrayAccessor accessor(bytecode_array(),
764
                                                    target.target_offset());
765
        if (accessor.current_bytecode() != Bytecode::kResumeGenerator) {
766 767 768 769
          PrintF(stderr,
                 "Expected resume target for id %d, offset %d, to be "
                 "ResumeGenerator, but found %s\n",
                 target.suspend_id(), target.target_offset(),
770
                 Bytecodes::ToString(accessor.current_bytecode()));
771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802

          valid = false;
        }
      }
      // We've resolved this suspend id, so erase it to make sure we don't
      // resolve it twice.
      unresolved_suspend_ids->erase(it);
    } else {
      // Non-leaves should have a direct inner loop header as their target.
      if (!IsLoopHeader(target.target_offset())) {
        PrintF(stderr,
               "Expected non-leaf resume target for id %d to have a loop "
               "header at target offset %d\n",
               target.suspend_id(), target.target_offset());
        valid = false;
      } else {
        LoopInfo loop_info = GetLoopInfoFor(target.target_offset());
        if (loop_info.parent_offset() != parent_offset) {
          PrintF(stderr,
                 "Expected non-leaf resume target for id %d to have a direct "
                 "inner loop at target offset %d\n",
                 target.suspend_id(), target.target_offset());
          valid = false;
        }
        // If the target loop is a valid inner loop, we'll check its validity
        // when we analyze its resume targets.
      }
    }
  }
  return valid;
}

803
bool BytecodeAnalysis::LivenessIsValid() {
804
  interpreter::BytecodeArrayRandomIterator iterator(bytecode_array(), zone());
805

806 807
  BytecodeLivenessState previous_liveness(bytecode_array()->register_count(),
                                          zone());
808 809 810 811

  int invalid_offset = -1;
  int which_invalid = -1;

812
  BytecodeLivenessState* next_bytecode_in_liveness = nullptr;
813

814
  // Ensure that there are no liveness changes if we iterate one more time.
815
  for (iterator.GoToEnd(); iterator.IsValid(); --iterator) {
816 817 818 819
    Bytecode bytecode = iterator.current_bytecode();

    int current_offset = iterator.current_offset();

820
    BytecodeLiveness& liveness = liveness_map_.GetLiveness(current_offset);
821 822 823

    previous_liveness.CopyFrom(*liveness.out);

824
    UpdateOutLiveness(bytecode, *liveness.out, next_bytecode_in_liveness,
825
                      iterator, bytecode_array(), liveness_map_);
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851
    // UpdateOutLiveness skips kJumpLoop, so we update it manually.
    if (bytecode == Bytecode::kJumpLoop) {
      int target_offset = iterator.GetJumpTargetOffset();
      liveness.out->Union(*liveness_map_.GetInLiveness(target_offset));
    }

    if (!liveness.out->Equals(previous_liveness)) {
      // Reset the invalid liveness.
      liveness.out->CopyFrom(previous_liveness);
      invalid_offset = current_offset;
      which_invalid = 1;
      break;
    }

    previous_liveness.CopyFrom(*liveness.in);

    liveness.in->CopyFrom(*liveness.out);
    UpdateInLiveness(bytecode, *liveness.in, iterator);

    if (!liveness.in->Equals(previous_liveness)) {
      // Reset the invalid liveness.
      liveness.in->CopyFrom(previous_liveness);
      invalid_offset = current_offset;
      which_invalid = 0;
      break;
    }
852 853

    next_bytecode_in_liveness = liveness.in;
854 855
  }

856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885
  // Ensure that the accumulator is not live when jumping out of a loop, or on
  // the back-edge of a loop.
  for (iterator.GoToStart(); iterator.IsValid() && invalid_offset == -1;
       ++iterator) {
    Bytecode bytecode = iterator.current_bytecode();
    int current_offset = iterator.current_offset();
    int loop_header = GetLoopOffsetFor(current_offset);

    // We only care if we're inside a loop.
    if (loop_header == -1) continue;

    // We only care about jumps.
    if (!Bytecodes::IsJump(bytecode)) continue;

    int jump_target = iterator.GetJumpTargetOffset();

    // If this is a forward jump to somewhere else in the same loop, ignore it.
    if (Bytecodes::IsForwardJump(bytecode) &&
        GetLoopOffsetFor(jump_target) == loop_header) {
      continue;
    }

    // The accumulator must be dead at the start of the target of the jump.
    if (liveness_map_.GetLiveness(jump_target).in->AccumulatorIsLive()) {
      invalid_offset = jump_target;
      which_invalid = 0;
      break;
    }
  }

886 887 888 889 890 891 892 893
  if (invalid_offset != -1) {
    OFStream of(stderr);
    of << "Invalid liveness:" << std::endl;

    // Dump the bytecode, annotated with the liveness and marking loops.

    int loop_indent = 0;

894
    interpreter::BytecodeArrayIterator forward_iterator(bytecode_array());
895 896
    for (; !forward_iterator.done(); forward_iterator.Advance()) {
      int current_offset = forward_iterator.current_offset();
897 898 899 900
      const BitVector& in_liveness =
          GetInLivenessFor(current_offset)->bit_vector();
      const BitVector& out_liveness =
          GetOutLivenessFor(current_offset)->bit_vector();
901

902 903
      for (int i = 0; i < in_liveness.length(); ++i) {
        of << (in_liveness.Contains(i) ? 'L' : '.');
904 905 906 907
      }

      of << " | ";

908 909
      for (int i = 0; i < out_liveness.length(); ++i) {
        of << (out_liveness.Contains(i) ? 'L' : '.');
910 911 912 913 914 915 916 917 918 919
      }

      of << " : " << current_offset << " : ";

      // Draw loop back edges by indentin everything between loop headers and
      // jump loop instructions.
      if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
        loop_indent--;
      }
      for (int i = 0; i < loop_indent; ++i) {
920
        of << "| ";
921 922
      }
      if (forward_iterator.current_bytecode() == Bytecode::kJumpLoop) {
923
        of << "`-";
924
      } else if (IsLoopHeader(current_offset)) {
925
        of << ".>";
926 927
        loop_indent++;
      }
928 929 930 931 932
      forward_iterator.PrintTo(of);
      if (Bytecodes::IsJump(forward_iterator.current_bytecode())) {
        of << " (@" << forward_iterator.GetJumpTargetOffset() << ")";
      }
      of << std::endl;
933 934 935 936

      if (current_offset == invalid_offset) {
        // Underline the invalid liveness.
        if (which_invalid == 0) {
937
          for (int i = 0; i < in_liveness.length(); ++i) {
938 939
            of << '^';
          }
940 941 942
          for (int i = 0; i < out_liveness.length() + 3; ++i) {
            of << ' ';
          }
943
        } else {
944
          for (int i = 0; i < in_liveness.length() + 3; ++i) {
945 946
            of << ' ';
          }
947
          for (int i = 0; i < out_liveness.length(); ++i) {
948 949 950 951 952 953 954
            of << '^';
          }
        }

        // Make sure to draw the loop indentation marks on this additional line.
        of << " : " << current_offset << " : ";
        for (int i = 0; i < loop_indent; ++i) {
955
          of << "| ";
956 957 958 959 960 961 962 963 964 965 966
        }

        of << std::endl;
      }
    }
  }

  return invalid_offset == -1;
}
#endif

967 968 969
}  // namespace compiler
}  // namespace internal
}  // namespace v8