current commit
261004.7 · deps: update V8 to 15.2.124.38
// Copyright 2011 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/codegen/safepoint-table.h"

#include <iomanip>

#include "src/codegen/assembler-inl.h"
#include "src/codegen/macro-assembler.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/diagnostics/disasm.h"
#include "src/execution/frames-inl.h"
#include "src/utils/ostreams.h"

#if V8_ENABLE_WEBASSEMBLY
#include "src/wasm/wasm-code-manager.h"
#endif  // V8_ENABLE_WEBASSEMBLY

namespace v8 {
namespace internal {

// Chosen for convenience.
// PCs start at 1 to make deduplicated early entries count as initialized.
// Deopt indices start at -1 because a delta of 0 has special meaning (namely
// "this entry has no deopt index"), and a real index of 0 should be encodable
// (with delta=1).
static constexpr int kImplicitStartPC = 1;
static constexpr int kImplicitStartDeoptIndex = -1;
static constexpr int kImplicitStartTrampoline = 0;

SafepointTable::SafepointTable(Isolate* isolate, Address pc, Tagged<Code> code)
    : SafepointTable(code->InstructionStart(isolate, pc),
                     code->safepoint_table_address()) {
  DCHECK(code->is_turbofanned());
}

SafepointTable::SafepointTable(Isolate* isolate, Address pc,
                               Tagged<GcSafeCode> code)
    : SafepointTable(code->InstructionStart(isolate, pc),
                     code->safepoint_table_address()) {
  DCHECK(code->is_turbofanned());
}

#if V8_ENABLE_WEBASSEMBLY
SafepointTable::SafepointTable(const wasm::WasmCode* code)
    : SafepointTable(
          code->instruction_start(),
          code->instruction_start() + code->safepoint_table_offset()) {}
#endif  // V8_ENABLE_WEBASSEMBLY

SafepointTable::SafepointTable(Address instruction_start,
                               Address safepoint_table_address)
    : instruction_start_(instruction_start),
      safepoint_table_address_(
          reinterpret_cast<const uint8_t*>(safepoint_table_address)),
      stack_slots_(base::Memory<SafepointTableStackSlotsField_t>(
          safepoint_table_address + kStackSlotsOffset)),
      byte_length_(ByteLengthField::decode(
          base::Memory<uint32_t>(safepoint_table_address + kConfigOffset))),
      has_tagged_registers_(HasTaggedRegistersField::decode(
          base::Memory<uint32_t>(safepoint_table_address + kConfigOffset))),
      has_deopt_data_(HasDeoptDataField::decode(
          base::Memory<uint32_t>(safepoint_table_address + kConfigOffset))),
      end_(safepoint_table_address_ + byte_length_) {
  ResetIteration();
}

namespace {

// TODO(jkummerow): Consider a faster encoding scheme.
V8_INLINE void emit_uleb(Assembler* assembler, uint32_t value) {
  while (value >= 0x80) {
    assembler->db((value & 0x7f) | 0x80);
    value >>= 7;
  }
  assembler->db(value);
}
V8_INLINE uint32_t read_uleb(const uint8_t** ptr) {
  uint8_t byte = *(*ptr)++;
  if (!(byte & 0x80)) return byte;
  uint32_t result = byte & 0x7F;
  int shift = 7;
  do {
    byte = *(*ptr)++;
    result |= (byte & 0x7F) << shift;
    shift += 7;
  } while (byte & 0x80);
  return result;
}

// Fewer than 7 skipped bits are more efficient to store in the xor mask.
// Consequence: we can slightly extend the range of a single skip byte by
// subtracting this value from the encoded value.
constexpr int kMinSkipBits = 7;
}  // namespace

void EncodeSafepointEntry(int stack_slot_count, uint32_t common_prefix,
                          const BitVector* patch, Assembler* assembler) {
  if (patch == nullptr) {
    assembler->db(0);
    return;
  }
  DCHECK_LT(common_prefix, stack_slot_count);
  DCHECK_NE(patch->length(), 0);
  // The extra -1 ensures that the highest index is mapped to 0.
  auto flip = [stack_slot_count](int index) {
    return stack_slot_count - 1 - index;
  };
  uint32_t tail_bits = stack_slot_count - common_prefix;
  uint32_t tail_bytes = (tail_bits + kBitsPerByte - 1) / kBitsPerByte;
  uint32_t skipped_bits = tail_bits - patch->length();

  // Estimate the sizes of both encoding possibilities. These are
  // approximations because we assume that all counts fit into one LEB byte.
  // If they don't, then the overall size will be so large that the estimate
  // being off by one doesn't really change the picture.
  int byte_for_length = 1;
  int approx_bytes_full_encoding = byte_for_length + tail_bytes;
  int bytes_for_skip = 0;
  int leb_payload_bits = patch->length();
  if (skipped_bits >= kMinSkipBits) {
    bytes_for_skip = 1;
  } else {
    leb_payload_bits += skipped_bits;
  }
  int approx_bytes_compact_encoding =
      bytes_for_skip + (leb_payload_bits + 6) / 7;
  auto it = patch->rbegin();
  auto end = patch->rend();

  // Full encoding.
  if (approx_bytes_full_encoding < approx_bytes_compact_encoding) {
    emit_uleb(assembler, tail_bytes << 1);
#if DEBUG
    int debug_pos_before_stackmap = assembler->pc_offset();
#endif
    int end_of_current = kBitsPerByte;
    uint8_t current = 0;
    while (it != end) {
      DCHECK_LT(common_prefix + *it, stack_slot_count);
      DCHECK_LT(*it, tail_bits);
      int bit = flip(common_prefix + *it);
      while (bit >= end_of_current) {
        assembler->db(current);
        current = 0;
        end_of_current += kBitsPerByte;
      }
      current |= (1 << (bit % kBitsPerByte));
      ++it;
    }
    assembler->db(current);
    DCHECK_EQ(tail_bytes, assembler->pc_offset() - debug_pos_before_stackmap);
    return;
  }

  // Compact encoding.
  if (skipped_bits >= kMinSkipBits) {
    uint32_t encoded_skipped_bits = skipped_bits - kMinSkipBits;
    DCHECK_EQ(encoded_skipped_bits, (encoded_skipped_bits << 2) >> 2);
    emit_uleb(assembler, (encoded_skipped_bits << 2) | 0b11);
  } else {
    skipped_bits = 0;
  }

  uint8_t current = 0b01;
  int start_of_current = skipped_bits - 2 /* tag size */;
  int end_of_current = start_of_current + 7;
  while (it != end) {
    int bit = flip(common_prefix + *it);
    DCHECK_LT(bit, stack_slot_count);
    DCHECK_GE(bit, start_of_current);
    while (bit >= end_of_current) {
      assembler->db(0x80 | current);
      current = 0;
      start_of_current = end_of_current;
      end_of_current = start_of_current + 7;
    }
    current |= 1 << (bit - start_of_current);
    ++it;
  }
  assembler->db(current);
}

// Updates {ptr} as it consumes input.
template <bool update_tagged_slots>
void DecodeSafepointEntry(const uint8_t** ptr,
                          base::OwnedVector<uint8_t>& tagged_slots) {
  uint8_t first_byte = **ptr;
  if (first_byte == 0) {
    (*ptr)++;
    return;
  }
  uint8_t* cursor = tagged_slots.data();

  // Just skip the right number of bytes if actual decoding is not requested.
  if constexpr (!update_tagged_slots) {
    if ((first_byte & 1) == 0) {
      uint32_t num_bytes = read_uleb(ptr) >> 1;
      *ptr += num_bytes;
      return;
    }
    if ((first_byte & 0b11) == 0b11) {
      read_uleb(ptr);  // skip_bits
    }
    for (uint8_t byte = *(*ptr)++; byte & 0x80; byte = *(*ptr)++) continue;
    return;
  }

  // Full encoding.
  if ((first_byte & 1) == 0) {
    uint32_t num_bytes = read_uleb(ptr) >> 1;
    while (num_bytes > kSystemPointerSize) {
      uintptr_t current;
      memcpy(&current, cursor, kSystemPointerSize);
      uintptr_t patch;
      memcpy(&patch, *ptr, kSystemPointerSize);
      current ^= patch;
      memcpy(cursor, &current, kSystemPointerSize);
      cursor += kSystemPointerSize;
      *ptr += kSystemPointerSize;
      num_bytes -= kSystemPointerSize;
    }
    while (num_bytes > 0) {
      *cursor++ ^= *(*ptr)++;
      --num_bytes;
    }
    return;
  }

  // Compact encoding.
  uint32_t skip_bits = 0;

  if ((first_byte & 0b11) == 0b11) {
    skip_bits = (read_uleb(ptr) >> 2) + kMinSkipBits;
    if (skip_bits >= kBitsPerByte) {
      cursor += skip_bits / kBitsPerByte;
      skip_bits %= kBitsPerByte;
    }
  }
  // The first byte is special: it has the tag, so only 5 bits of payload.
  uint8_t patch_byte = *(*ptr)++;
  uint32_t patch = ((patch_byte >> 2) & 0x1F) << skip_bits;
  int bits_in_patch = 5 + skip_bits;
  while (true) {
    // If we have accumulated enough bits in {patch}, apply them.
    if (bits_in_patch >= kBitsPerByte) {
      *cursor++ ^= patch;
      patch >>= kBitsPerByte;
      bits_in_patch -= kBitsPerByte;
    }
    // If there are no more bytes to decode, apply any remaining patch bits
    // and terminate the loop...
    if (!(patch_byte & 0x80)) {
      // Checking for {patch != 0} is important: while xor-applying an all-zero
      // byte would be a no-op, it could be an out-of-bounds no-op.
      if (patch != 0) *cursor ^= patch;
      break;
    }
    // ...otherwise read the next byte.
    patch_byte = *(*ptr)++;
    patch |= (patch_byte & 0x7F) << bits_in_patch;
    bits_in_patch += 7;
  }
}

template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void DecodeSafepointEntry<
    true>(const uint8_t** ptr, base::OwnedVector<uint8_t>& tagged_slots);
template EXPORT_TEMPLATE_DEFINE(V8_EXPORT_PRIVATE) void DecodeSafepointEntry<
    false>(const uint8_t** ptr, base::OwnedVector<uint8_t>& tagged_slots);

void SafepointTable::ResetIteration() {
  ptr_ = safepoint_table_address_ + kHeaderSize;
  entry_.ResetTaggedSlots(stack_slots_);
  entry_.pc_ = kImplicitStartPC;
  entry_.deopt_index_ = SafepointEntry::kNoDeoptIndex;
  entry_.trampoline_pc_ = SafepointEntry::kNoTrampolinePC;
  last_deopt_index_ = kImplicitStartDeoptIndex;
  last_trampoline_ = kImplicitStartTrampoline;
  next_pc_ = has_more() ? kImplicitStartPC + read_uleb(&ptr_) : kMaxUInt32;
}

template <bool update_tagged_slots>
void SafepointTable::Advance() {
  DCHECK(has_more());
  entry_.pc_ = next_pc_;
  if (has_deopt_data_) {
    uint32_t deopt_index_delta = read_uleb(&ptr_);
    if (deopt_index_delta == 0) {
      entry_.deopt_index_ = SafepointEntry::kNoDeoptIndex;
      entry_.trampoline_pc_ = SafepointEntry::kNoTrampolinePC;
    } else {
      last_deopt_index_ += deopt_index_delta;
      uint32_t trampoline_delta = read_uleb(&ptr_);
      last_trampoline_ += trampoline_delta;
      entry_.deopt_index_ = last_deopt_index_;
      entry_.trampoline_pc_ = last_trampoline_;
    }
  }
  if (has_tagged_registers_) {
    entry_.tagged_register_indexes_ = read_uleb(&ptr_);
  }
  DecodeSafepointEntry<update_tagged_slots>(&ptr_, entry_.tagged_slots_);
  next_pc_ = has_more() ? next_pc_ + read_uleb(&ptr_) : kMaxUInt32;
}

int SafepointTable::FindReturnPC(int pc_offset) {
  while (has_more()) {
    Advance<false>();
    if (entry_.trampoline_pc() == pc_offset || entry_.pc() == pc_offset) {
      return entry_.pc();
    }
  }
  UNREACHABLE();
}

template <bool update_tagged_slots>
void SafepointTable::FindEntryImpl(Address pc) {
  // Special case: if all entries were identical to the implicit default
  // (except for their pc), they won't be stored, so we have no bytes to
  // decode. We'll take this early exit when {ResetIteration()} found
  // {!has_more()} even before decoding the first pc; we use an alternative
  // phrasing here to protect against API misuse: if some future caller calls
  // {FindEntry()} multiple times on the same {SafepointTable}, the DCHECK
  // below will tell them that they're holding it wrong, whereas if we had
  // "if (!has_more()) return" here, that would silently give wrong results.
  if (ptr_ == safepoint_table_address_ + kHeaderSize) return;
  // We could support multiple searches, but currently we have no reason to.
  DCHECK(has_more());
  int pc_offset = static_cast<int>(pc - instruction_start_);

  // This iteration is a bit tricky because:
  // (1) we can only step forward
  // (2) each step is relatively costly
  // (3) trampoline PCs are interleaved with regular PCs
  // (4) we can only return the current entry, which might already be too far.
  // For regular pc hits, we address this by decoding {next_pc_} as part of
  // the previous step. For trampoline hits, we have to restart from the
  // beginning to get back to the entry we need to return.
  // Consider the following example:
  //   pc = 10
  //   pc = 12, trampoline = 20
  //   pc = 14
  //   pc = 16, trampoline = 22
  //   pc = 18
  // When we enter this function, {next_pc_ == 10}.
  // If we're looking for:
  //   - 8: we should return immediately.
  //   - 10: we should return when entry_.pc_ == 10.
  //   - 11: we should return when next_pc_ == 12. Can be folded with case "10"
  //         into one condition: return when next_pc_ > entry_.pc_.
  //   - 19: we should return when we reach the end, because we haven't found
  //         any trampoline candidates.
  //   - 20: we should return when trampoline == 20.
  //   - 21: when we reach "trampoline == 22", we realize that we're too far,
  //         so we must restart and iterate until "trampoline == 20".
  //   - 25: when we reach the end, we realize that we're too far, so we must
  //         restart and iterate until "trampoline == 22".

  int trampoline_candidate = 0;
  while (next_pc_ <= pc_offset && has_more()) {
    Advance<update_tagged_slots>();
    int trampoline_pc = entry_.trampoline_pc();
    if (trampoline_pc != SafepointEntry::kNoTrampolinePC) {
      // In the "20" example, return now.
      if (pc_offset == trampoline_pc) return;
      // In the "21" example, remember "trampoline == 20".
      if (pc_offset >= trampoline_pc) {
        trampoline_candidate = entry_.trampoline_pc_;
      }
      // In the "21" example, stop searching at "trampoline == 22".
      if (trampoline_candidate && trampoline_pc > pc_offset) break;
    }
  }
  // The "8", "10", "11", "19" examples return here.
  if (trampoline_candidate == 0) return;
  // The "21" and "25" examples must restart the iteration in order to go back
  // to the correct entry.
  ResetIteration();
  while (entry_.trampoline_pc() != trampoline_candidate) {
    Advance<update_tagged_slots>();
  }
}

SafepointEntry& SafepointTable::FindEntry(Address pc) {
  FindEntryImpl<true>(pc);
  CHECK(entry_.is_initialized());
  return entry_;
}

SafepointEntry& SafepointTable::FindEntry_NoStackSlots(Address pc) {
  FindEntryImpl<false>(pc);
  CHECK(entry_.is_initialized());
  return entry_;
}

void SafepointTable::Print(std::ostream& os) {
  os << "Safepoints (stack slots = " << stack_slots_
     << ", byte size = " << byte_length_ << ")\n";
  if (!has_more()) {
    os << "All bits in all entries are zero, no data was stored.\n";
    return;
  }

  while (has_more()) {
    Advance<true>();
    os << reinterpret_cast<const void*>(instruction_start_ + entry_.pc()) << " "
       << std::setw(6) << std::hex << entry_.pc() << std::dec;

    if (!entry_.tagged_slots().empty()) {
      os << "  slots (sp->fp): ";
      uint32_t i = 0;
      for (uint8_t bits : entry_.tagged_slots()) {
        for (int bit = 0; bit < kBitsPerByte && i < stack_slots_; ++bit, ++i) {
          os << ((bits >> bit) & 1);
        }
      }
      // The tagged slots bitfield ends at the min stack slot (rounded up to the
      // nearest byte) -- we might have some slots left over in the stack frame
      // before the fp, so print zeros for those.
      for (; i < stack_slots_; ++i) {
        os << 0;
      }
    }

    if (entry_.tagged_register_indexes() != 0) {
      os << "  registers: ";
      uint32_t register_bits = entry_.tagged_register_indexes();
      int bits = 32 - base::bits::CountLeadingZeros32(register_bits);
      for (int j = bits - 1; j >= 0; --j) {
        os << ((register_bits >> j) & 1);
      }
    }

    if (entry_.has_deoptimization_index()) {
      os << "  deopt " << std::setw(6) << entry_.deoptimization_index()
         << " trampoline: " << std::setw(6) << std::hex
         << entry_.trampoline_pc();
    }
    os << "\n";
  }
}

SafepointTableBuilder::Safepoint SafepointTableBuilder::DefineSafepoint(
    Assembler* assembler, int pc_offset) {
  pc_offset = pc_offset ? pc_offset : assembler->pc_offset_for_safepoint();
  std::swap(current_entry_, previous_entry_);
  current_entry_->Reset(pc_offset);
  return SafepointTableBuilder::Safepoint(current_entry_, this);
}

void SafepointTableBuilder::Commit(Safepoint* safepoint) {
  uint32_t common_bits;
  BitVector* stack_indexes =
      CompareAndCreateXorPatch(zone_, current_entry_->stack_indexes,
                               previous_entry_->stack_indexes, &common_bits);
  // There's no differing suffix if all bits were identical.
  DCHECK_IMPLIES(stack_indexes == nullptr, common_bits == kMaxUInt32);

  entries_.emplace_back(current_entry_->pc, current_entry_->register_indexes,
                        common_bits, stack_indexes);
}

int SafepointTableBuilder::UpdateDeoptimizationInfo(int pc, int trampoline,
                                                    int start,
                                                    int deopt_index) {
  DCHECK_NE(SafepointEntry::kNoTrampolinePC, trampoline);
  DCHECK_NE(SafepointEntry::kNoDeoptIndex, deopt_index);
  has_deopt_data_ = true;
  auto it = entries_.begin() + start;
  DCHECK(std::any_of(it, entries_.end(),
                     [pc](auto& entry) { return entry.pc == pc; }));
  int index = start;
  while (it->pc != pc) ++it, ++index;
  it->trampoline = trampoline;
  it->deopt_index = deopt_index;
  return index;
}

void SafepointTableBuilder::Emit(Assembler* assembler, int stack_slot_count) {
#if V8_TARGET_ARCH_ARM || V8_TARGET_ARCH_ARM64
  // We cannot emit a const pool within the safepoint table.
  Assembler::BlockConstPoolScope block_const_pool(assembler);
#endif

  // Make sure the safepoint table is properly aligned. Pad with nops.
  assembler->Align(InstructionStream::kMetadataAlignment);
  assembler->RecordComment(";;; Safepoint table.");
  const int start_offset = assembler->pc_offset();
  set_safepoint_table_offset(start_offset);

  // Emit the table header.
  static_assert(SafepointTable::kStackSlotsOffset == 0 * kIntSize);
  static_assert(SafepointTable::kConfigOffset == 1 * kIntSize);
  static_assert(SafepointTable::kHeaderSize == 2 * kIntSize);
  assembler->dd(stack_slot_count);
  int config_offset = assembler->pc_offset();
  assembler->dd(0u);  // For "config", will be patched later.

  // Emit entries, sorted by pc offsets.
  CompressedEntry implicit_start(kImplicitStartPC, 0, 0, nullptr);
  CompressedEntry& previous = implicit_start;
  // These may be different from the respective {previous.foo} field because
  // they are optional fields. Here we store the last value that was present.
  int last_deopt_index = kImplicitStartDeoptIndex;
  int last_trampoline = kImplicitStartTrampoline;
  for (const CompressedEntry& entry : entries_) {
    // Entries are ordered by PC.
    DCHECK_GT(entry.pc, previous.pc);

    // Skip identical (except for PC) entries. Note: this could end up
    // skipping all entries, if they're all identical to {implicit_start}!
    // The {SafepointTable} decoder can handle that.
    if (entry.deopt_index == previous.deopt_index &&
        entry.register_indexes == previous.register_indexes &&
        entry.stack_indexes_tail == nullptr) {
      DCHECK_EQ(entry.trampoline, previous.trampoline);
      continue;
    }

    emit_uleb(assembler, entry.pc - previous.pc);
    if (has_deopt_data_) {
      if (entry.trampoline == SafepointEntry::kNoTrampolinePC) {
        // An entry either has trampoline and deopt index, or none of the two.
        DCHECK(entry.deopt_index == SafepointEntry::kNoDeoptIndex);
        assembler->db(0);
      } else {
        DCHECK_NE(entry.deopt_index, SafepointEntry::kNoDeoptIndex);
        // Deopt indices and trampoline PCs are increasing.
        DCHECK_GT(entry.deopt_index, previous.deopt_index);
        DCHECK_GT(entry.trampoline, previous.trampoline);
        // Trampoline PCs are larger than regular PCs.
        DCHECK_GT(entry.trampoline, entries_.back().pc);

        emit_uleb(assembler, entry.deopt_index - last_deopt_index);
        last_deopt_index = entry.deopt_index;
        emit_uleb(assembler, entry.trampoline - last_trampoline);
        last_trampoline = entry.trampoline;
      }
    } else {
      DCHECK_EQ(SafepointEntry::kNoDeoptIndex, entry.deopt_index);
      DCHECK_EQ(SafepointEntry::kNoTrampolinePC, entry.trampoline);
    }
    if (has_tagged_registers_) {
      emit_uleb(assembler, entry.register_indexes);
    }

    // Emit bitmaps of tagged stack slots. Note the slot list is reversed in the
    // encoding.
    EncodeSafepointEntry(stack_slot_count, entry.common_bits,
                         entry.stack_indexes_tail, assembler);
    previous = entry;
  }
  uint32_t byte_length = assembler->pc_offset() - start_offset;
  uint32_t config =
      SafepointTable::ByteLengthField::encode(byte_length) |
      SafepointTable::HasTaggedRegistersField::encode(has_tagged_registers_) |
      SafepointTable::HasDeoptDataField::encode(has_deopt_data_);
  WriteUnalignedValue(
      reinterpret_cast<Address>(assembler->buffer_start() + config_offset),
      config);
}

// Contract: Xoring the result of this function onto {a}, starting at
// {common_prefix_bits}, yields a vector with the same bits set as {b}.
// We consider the vectors to be sets, i.e. they have no upper bound, instead
// they are assumed to continue with 0-bits to infinity.
// This implies that both {a} and {b} may be empty ({length() == 0}), which
// is treated the same as an arbitrary-length vector full of 0-bits.
// Example:
//   other:  110010100 (UsedLength() == 7, length() doesn't matter)
//   this:   110000000 (UsedLength() == 2, length() doesn't matter)
//   result:     101   (length() == 3), common_prefix_bits=4
// For identical sets we return {nullptr} and, as an approximation of
// "infinity", {common_prefix_bits} = kMaxUInt32.
// For non-nullptr results, {common_prefix_bits} is a meaningful value, and
// the {length()} of the result indicates the range containing all differing
// bits, starting at that position.
BitVector* CompareAndCreateXorPatch(Zone* zone, const GrowableBitVector& v1,
                                    const GrowableBitVector& v2,
                                    uint32_t* common_prefix_bits) {
  // This function is prepared to work on overallocated GrowableBitVectors,
  // so rather than relying on {length()} we compute the actual length (i.e.
  // position of the last bit).
  const BitVector& a = v1.bits_;
  const BitVector& b = v2.bits_;
  constexpr int kDataBits = BitVector::kDataBits;
  constexpr int kDataBitShift = BitVector::kDataBitShift;

  int a_length = a.UsedLength();
  int b_length = b.UsedLength();
  int a_word_length = (a_length + kDataBits - 1) >> kDataBitShift;
  int b_word_length = (b_length + kDataBits - 1) >> kDataBitShift;
  int max_common_bits = std::min(a_length, b_length);
  int max_common_words = (max_common_bits + kDataBits - 1) >> kDataBitShift;
  int different_word = 0;
  int different_bit = 0;
  while (different_word < max_common_words &&
         a.data_begin_[different_word] == b.data_begin_[different_word]) {
    ++different_word;
  }
  // We may have found a difference already. Otherwise, if we reached the
  // end of one of the vectors, see if the other has any non-zero bits.
  if (different_word == max_common_words) {
    while (different_word < b_word_length &&
           b.data_begin_[different_word] == 0) {
      ++different_word;
    }
    while (different_word < a_word_length &&
           a.data_begin_[different_word] == 0) {
      ++different_word;
    }
  }
  // If the overlapping part was identical and only zeros followed in the
  // longer vector, they count as identical.
  if (different_word >= b_word_length && different_word >= a_word_length) {
    *common_prefix_bits = kMaxUInt32;
    return nullptr;
  }
  // Otherwise we must have found a word that differs.
  uintptr_t a_word =
      different_word < a_word_length ? a.data_begin_[different_word] : 0;
  uintptr_t b_word =
      different_word < b_word_length ? b.data_begin_[different_word] : 0;
  DCHECK_NE(a_word, b_word);
  uintptr_t diff = a_word ^ b_word;
  different_bit = base::bits::CountTrailingZeros(diff);
  *common_prefix_bits = different_word * kDataBits + different_bit;

  // Find the last difference.
  // If the vectors have different lengths, then the end of the longer one
  // is the last difference. Otherwise, skip any identical tails.
  int result_end;
  if (a_length != b_length) {
    result_end = std::max(a_length, b_length);
  } else {
    int highest_bit = a_length - 1;
    int result_end_word = highest_bit >> kDataBitShift;
    while (a.data_begin_[result_end_word] == b.data_begin_[result_end_word]) {
      result_end_word--;
    }
    a_word = a.data_begin_[result_end_word];
    b_word = b.data_begin_[result_end_word];
    DCHECK_NE(a_word, b_word);
    diff = a_word ^ b_word;
    int identical_tail = base::bits::CountLeadingZeros(diff);
    result_end = (result_end_word + 1) * kDataBits - identical_tail;
    DCHECK_GE(result_end, *common_prefix_bits);
  }

  // Allocate and populate the result.
  int suffix_length = result_end - *common_prefix_bits;
  DCHECK_NE(suffix_length, 0);
  BitVector* result = zone->New<BitVector>(suffix_length, zone);
  int result_words = result->data_length();
  if (different_bit == 0) {
    for (int i = 0; i < result_words; i++) {
      int read_i = different_word + i;
      a_word = read_i < a_word_length ? a.data_begin_[read_i] : 0;
      b_word = read_i < b_word_length ? b.data_begin_[read_i] : 0;
      result->data_begin_[i] = a_word ^ b_word;
    }
  } else {
    a_word = different_word < a_word_length ? a.data_begin_[different_word] : 0;
    b_word = different_word < b_word_length ? b.data_begin_[different_word] : 0;
    uintptr_t carry = (a_word ^ b_word) >> different_bit;
    int left_shift = kDataBits - different_bit;
    for (int i = 0; i < result_words; i++) {
      int read_i = different_word + i + 1;
      a_word = read_i < a_word_length ? a.data_begin_[read_i] : 0;
      b_word = read_i < b_word_length ? b.data_begin_[read_i] : 0;
      uintptr_t word = a_word ^ b_word;
      result->data_begin_[i] = carry | (word << left_shift);
      carry = word >> different_bit;
    }
  }
#if DEBUG
  // The patch always begins and ends with a bit that needs to be flipped.
  DCHECK(result->Contains(0));
  DCHECK(result->Contains(suffix_length - 1));
  // Any trailing bits in the backing store are unset.
  if (suffix_length != result_words * kDataBits) {
    uintptr_t last_word = result->data_begin_[result_words - 1];
    DCHECK_EQ(0, last_word >> (suffix_length % kDataBits));
  }
#endif  // DEBUG
  return result;
}

}  // namespace internal
}  // namespace v8