// Copyright 2014 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/objects/feedback-vector.h"

#include <bit>
#include <optional>

#include "src/base/platform/mutex.h"
#include "src/common/globals.h"
#include "src/deoptimizer/deoptimizer.h"
#include "src/diagnostics/code-tracer.h"
#include "src/heap/heap-inl.h"
#include "src/heap/local-factory-inl.h"
#include "src/ic/handler-configuration-inl.h"
#include "src/ic/ic-inl.h"
#include "src/objects/data-handler-inl.h"
#include "src/objects/feedback-vector-inl.h"
#include "src/objects/hash-table-inl.h"
#include "src/objects/map-inl.h"
#include "src/objects/objects.h"

namespace v8::internal {

FeedbackSlot FeedbackVectorSpec::AddSlot(FeedbackSlotKind kind) {
  int slot = slot_count();
  int entries_per_slot = FeedbackMetadata::GetSlotSize(kind);
  append(kind);
  for (int i = 1; i < entries_per_slot; i++) {
    append(FeedbackSlotKind::kInvalid);
  }
  return FeedbackSlot(slot);
}

static bool IsPropertyNameFeedback(Tagged<MaybeObject> feedback) {
  Tagged<HeapObject> heap_object;
  if (!feedback.GetHeapObjectIfStrong(&heap_object)) return false;
  if (IsString(heap_object)) {
    DCHECK(IsInternalizedString(heap_object));
    return true;
  }
  if (!IsSymbol(heap_object)) return false;
  Tagged<Symbol> symbol = Cast<Symbol>(heap_object);
  ReadOnlyRoots roots = GetReadOnlyRoots();
  return symbol != roots.uninitialized_symbol() &&
         symbol != roots.mega_dom_symbol() &&
         symbol != roots.megamorphic_symbol();
}

std::ostream& operator<<(std::ostream& os, FeedbackSlotKind kind) {
  return os << FeedbackMetadata::Kind2String(kind);
}

FeedbackSlotKind FeedbackMetadata::GetKind(FeedbackSlot slot) const {
  int index = VectorICComputer::index(0, slot.ToInt());
  int data = get(index);
  return VectorICComputer::decode(data, slot.ToInt());
}

void FeedbackMetadata::SetKind(FeedbackSlot slot, FeedbackSlotKind kind) {
  int index = VectorICComputer::index(0, slot.ToInt());
  int data = get(index);
  int new_data = VectorICComputer::encode(data, slot.ToInt(), kind);
  set(index, new_data);
}

uint16_t FeedbackMetadata::GetCreateClosureParameterCount(int index) const {
  DCHECK_LT(index, create_closure_slot_count());
  int offset = kHeaderSize + word_count() * kInt32Size + index * kUInt16Size;
  return ReadField<uint16_t>(offset);
}

void FeedbackMetadata::SetCreateClosureParameterCount(
    int index, uint16_t parameter_count) {
  DCHECK_LT(index, create_closure_slot_count());
  int offset = kHeaderSize + word_count() * kInt32Size + index * kUInt16Size;
  return WriteField<uint16_t>(offset, parameter_count);
}

// static
template <typename IsolateT>
Handle<FeedbackMetadata> FeedbackMetadata::New(IsolateT* isolate,
                                               const FeedbackVectorSpec* spec) {
  auto* factory = isolate->factory();

  const int slot_count = spec->slot_count();
  const int create_closure_slot_count = spec->create_closure_slot_count();
  if (slot_count == 0 && create_closure_slot_count == 0) {
    return factory->empty_feedback_metadata();
  }
#ifdef DEBUG
  for (int i = 0; i < slot_count;) {
    FeedbackSlotKind kind = spec->GetKind(FeedbackSlot(i));
    int entry_size = FeedbackMetadata::GetSlotSize(kind);
    for (int j = 1; j < entry_size; j++) {
      kind = spec->GetKind(FeedbackSlot(i + j));
      DCHECK_EQ(FeedbackSlotKind::kInvalid, kind);
    }
    i += entry_size;
  }
#endif

  Handle<FeedbackMetadata> metadata =
      factory->NewFeedbackMetadata(slot_count, create_closure_slot_count);

  // Initialize the slots. The raw data section has already been pre-zeroed in
  // NewFeedbackMetadata.
  for (int i = 0; i < slot_count; i++) {
    FeedbackSlot slot(i);
    FeedbackSlotKind kind = spec->GetKind(slot);
    metadata->SetKind(slot, kind);
  }

  for (int i = 0; i < create_closure_slot_count; i++) {
    uint16_t parameter_count = spec->GetCreateClosureParameterCount(i);
    metadata->SetCreateClosureParameterCount(i, parameter_count);
  }

  return metadata;
}

template Handle<FeedbackMetadata> FeedbackMetadata::New(
    Isolate* isolate, const FeedbackVectorSpec* spec);
template Handle<FeedbackMetadata> FeedbackMetadata::New(
    LocalIsolate* isolate, const FeedbackVectorSpec* spec);

bool FeedbackMetadata::SpecDiffersFrom(
    const FeedbackVectorSpec* other_spec) const {
  if (other_spec->slot_count() != slot_count()) {
    return true;
  }

  int slots = slot_count();
  for (int i = 0; i < slots;) {
    FeedbackSlot slot(i);
    FeedbackSlotKind kind = GetKind(slot);
    int entry_size = FeedbackMetadata::GetSlotSize(kind);

    if (kind != other_spec->GetKind(slot)) {
      return true;
    }
    i += entry_size;
  }
  return false;
}

const char* FeedbackMetadata::Kind2String(FeedbackSlotKind kind) {
  switch (kind) {
    case FeedbackSlotKind::kInvalid:
      return "Invalid";
    case FeedbackSlotKind::kCall:
      return "Call";
    case FeedbackSlotKind::kLoadProperty:
      return "LoadProperty";
    case FeedbackSlotKind::kLoadGlobalInsideTypeof:
      return "LoadGlobalInsideTypeof";
    case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
      return "LoadGlobalNotInsideTypeof";
    case FeedbackSlotKind::kLoadKeyed:
      return "LoadKeyed";
    case FeedbackSlotKind::kHasKeyed:
      return "HasKeyed";
    case FeedbackSlotKind::kSetNamedSloppy:
      return "SetNamedSloppy";
    case FeedbackSlotKind::kSetNamedStrict:
      return "SetNamedStrict";
    case FeedbackSlotKind::kDefineNamedOwn:
      return "DefineNamedOwn";
    case FeedbackSlotKind::kDefineKeyedOwn:
      return "DefineKeyedOwn";
    case FeedbackSlotKind::kStoreGlobalSloppy:
      return "StoreGlobalSloppy";
    case FeedbackSlotKind::kStoreGlobalStrict:
      return "StoreGlobalStrict";
    case FeedbackSlotKind::kSetKeyedSloppy:
      return "StoreKeyedSloppy";
    case FeedbackSlotKind::kSetKeyedStrict:
      return "StoreKeyedStrict";
    case FeedbackSlotKind::kStoreInArrayLiteral:
      return "StoreInArrayLiteral";
    case FeedbackSlotKind::kBinaryOp:
      return "BinaryOp";
    case FeedbackSlotKind::kCompareOp:
      return "CompareOp";
    case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
      return "DefineKeyedOwnPropertyInLiteral";
    case FeedbackSlotKind::kLiteral:
      return "Literal";
    case FeedbackSlotKind::kForIn:
      return "ForIn";
    case FeedbackSlotKind::kInstanceOf:
      return "InstanceOf";
    case FeedbackSlotKind::kTypeOf:
      return "TypeOf";
    case FeedbackSlotKind::kCloneObject:
      return "CloneObject";
    case FeedbackSlotKind::kJumpLoop:
      return "JumpLoop";
    case FeedbackSlotKind::kStringAddAndInternalize:
      return "StringAddAndInternalize";
  }
  UNREACHABLE();
}

FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot) const {
  DCHECK(!is_empty());
  return metadata()->GetKind(slot);
}

FeedbackSlotKind FeedbackVector::GetKind(FeedbackSlot slot,
                                         AcquireLoadTag tag) const {
  DCHECK(!is_empty());
  return metadata(tag)->GetKind(slot);
}

// static
DirectHandle<ClosureFeedbackCellArray> ClosureFeedbackCellArray::New(
    Isolate* isolate, DirectHandle<SharedFunctionInfo> shared,
    AllocationType allocation) {
  int length = shared->feedback_metadata()->create_closure_slot_count();
  if (length == 0) {
    return isolate->factory()->empty_closure_feedback_cell_array();
  }

  // Pre-allocate the cells s.t. we can initialize `result` without further
  // allocation.
  DirectHandleVector<FeedbackCell> cells(isolate);
  cells.reserve(length);
  for (int i = 0; i < length; i++) {
    DirectHandle<FeedbackCell> cell = isolate->factory()->NewNoClosuresCell();
    uint16_t parameter_count =
        shared->feedback_metadata()->GetCreateClosureParameterCount(i);
    auto initial_code = BUILTIN_CODE(isolate, CompileLazy);
    FeedbackCell::AllocateAndInstallJSDispatchHandle(
        cell, FeedbackCell::kDispatchHandleOffset, isolate, parameter_count,
        initial_code);
    cells.push_back(cell);
  }

  std::optional<DisallowGarbageCollection> no_gc;
  auto result = Allocate(isolate, length, &no_gc, allocation);
  for (int i = 0; i < length; i++) {
    result->set(i, *cells[i]);
  }

  return result;
}

// static
Handle<FeedbackVector> FeedbackVector::New(
    Isolate* isolate, DirectHandle<SharedFunctionInfo> shared,
    DirectHandle<ClosureFeedbackCellArray> closure_feedback_cell_array,
    DirectHandle<FeedbackCell> parent_feedback_cell,
    IsCompiledScope* is_compiled_scope) {
  DCHECK(is_compiled_scope->is_compiled());
  Factory* factory = isolate->factory();

  DirectHandle<FeedbackMetadata> feedback_metadata(shared->feedback_metadata(),
                                                   isolate);
  const int slot_count = feedback_metadata->slot_count();

  Handle<FeedbackVector> vector = factory->NewFeedbackVector(
      shared, closure_feedback_cell_array, parent_feedback_cell);

  DCHECK_EQ(vector->length(), slot_count);

  DCHECK_EQ(vector->shared_function_info(), *shared);
  DCHECK_EQ(vector->invocation_count(), 0);

  // Ensure we can skip the write barrier
  DirectHandle<Symbol> uninitialized_sentinel = UninitializedSentinel(isolate);
  DCHECK_EQ(ReadOnlyRoots(isolate).uninitialized_symbol(),
            *uninitialized_sentinel);
  for (int i = 0; i < slot_count;) {
    FeedbackSlot slot(i);
    FeedbackSlotKind kind = feedback_metadata->GetKind(slot);
    int entry_size = FeedbackMetadata::GetSlotSize(kind);

    Tagged<MaybeObject> extra_value = *uninitialized_sentinel;
    switch (kind) {
      case FeedbackSlotKind::kLoadGlobalInsideTypeof:
      case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
      case FeedbackSlotKind::kStoreGlobalSloppy:
      case FeedbackSlotKind::kStoreGlobalStrict:
      case FeedbackSlotKind::kJumpLoop:
        vector->Set(slot, kClearedWeakValue, SKIP_WRITE_BARRIER);
        break;
      case FeedbackSlotKind::kForIn:
      case FeedbackSlotKind::kCompareOp:
      case FeedbackSlotKind::kBinaryOp:
      case FeedbackSlotKind::kTypeOf:
        vector->Set(slot, Smi::zero(), SKIP_WRITE_BARRIER);
        break;
      case FeedbackSlotKind::kLiteral:
        vector->Set(slot, Smi::zero(), SKIP_WRITE_BARRIER);
        break;
      case FeedbackSlotKind::kCall:
        vector->Set(slot, *uninitialized_sentinel, SKIP_WRITE_BARRIER);
        extra_value = Smi::zero();
        break;
      case FeedbackSlotKind::kCloneObject:
      case FeedbackSlotKind::kLoadProperty:
      case FeedbackSlotKind::kLoadKeyed:
      case FeedbackSlotKind::kHasKeyed:
      case FeedbackSlotKind::kSetNamedSloppy:
      case FeedbackSlotKind::kSetNamedStrict:
      case FeedbackSlotKind::kDefineNamedOwn:
      case FeedbackSlotKind::kDefineKeyedOwn:
      case FeedbackSlotKind::kSetKeyedSloppy:
      case FeedbackSlotKind::kSetKeyedStrict:
      case FeedbackSlotKind::kStoreInArrayLiteral:
      case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
      case FeedbackSlotKind::kInstanceOf:
        vector->Set(slot, *uninitialized_sentinel, SKIP_WRITE_BARRIER);
        break;
      case FeedbackSlotKind::kStringAddAndInternalize:
        vector->Set(slot, Smi::zero(), SKIP_WRITE_BARRIER);
        break;

      case FeedbackSlotKind::kInvalid:
        UNREACHABLE();
    }
    for (int j = 1; j < entry_size; j++) {
      vector->Set(slot.WithOffset(j), extra_value, SKIP_WRITE_BARRIER);
    }
    i += entry_size;
  }

  if (!isolate->is_best_effort_code_coverage()) {
    AddToVectorsForProfilingTools(isolate, vector);
  }
  parent_feedback_cell->set_value(*vector, kReleaseStore);
  return vector;
}

// static
Handle<FeedbackVector> FeedbackVector::NewForTesting(
    Isolate* isolate, const FeedbackVectorSpec* spec) {
  DirectHandle<FeedbackMetadata> metadata =
      FeedbackMetadata::New(isolate, spec);
  DirectHandle<SharedFunctionInfo> shared =
      isolate->factory()->NewSharedFunctionInfoForBuiltin(
          isolate->factory()->empty_string(), Builtin::kIllegal, 0, kDontAdapt);
  // Set the raw feedback metadata to circumvent checks that we are not
  // overwriting existing metadata.
  shared->set_raw_outer_scope_info_or_feedback_metadata(*metadata);
  DirectHandle<ClosureFeedbackCellArray> closure_feedback_cell_array =
      ClosureFeedbackCellArray::New(isolate, shared);
  DirectHandle<FeedbackCell> parent_cell =
      isolate->factory()->NewNoClosuresCell();

  IsCompiledScope is_compiled_scope(shared->is_compiled_scope(isolate));
  return FeedbackVector::New(isolate, shared, closure_feedback_cell_array,
                             parent_cell, &is_compiled_scope);
}

// static
Handle<FeedbackVector> FeedbackVector::NewWithOneBinarySlotForTesting(
    Zone* zone, Isolate* isolate) {
  FeedbackVectorSpec one_slot(zone);
  one_slot.AddBinaryOpICSlot();
  return NewForTesting(isolate, &one_slot);
}

// static
Handle<FeedbackVector> FeedbackVector::NewWithOneCompareSlotForTesting(
    Zone* zone, Isolate* isolate) {
  FeedbackVectorSpec one_slot(zone);
  one_slot.AddCompareICSlot();
  return NewForTesting(isolate, &one_slot);
}

// static
void FeedbackVector::AddToVectorsForProfilingTools(
    Isolate* isolate, DirectHandle<FeedbackVector> vector) {
  DCHECK(!isolate->is_best_effort_code_coverage());
  if (!vector->shared_function_info()->IsSubjectToDebugging()) return;
  DirectHandle<ArrayList> list = Cast<ArrayList>(
      isolate->factory()->feedback_vectors_for_profiling_tools());
  list = ArrayList::Add(isolate, list, vector);
  isolate->SetFeedbackVectorsForProfilingTools(*list);
}

void FeedbackVector::set_tiering_in_progress(bool in_progress) {
  set_flags(TieringInProgressBit::update(flags(), in_progress));
}

void FeedbackVector::reset_flags() {
  set_flags(
      TieringInProgressBit::encode(false) |
      OsrTieringInProgressBit::encode(false) |
      MaybeHasMaglevOsrCodeBit::encode(false) |
      MaybeHasTurbofanOsrCodeBit::encode(false));
}

void FeedbackVector::SetOptimizedOsrCode(Isolate* isolate, FeedbackSlot slot,
                                         Tagged<Code> code) {
  DCHECK(CodeKindIsOptimizedJSFunction(code->kind()));
  DCHECK(!slot.IsInvalid());
  auto current = GetOptimizedOsrCode(isolate, {}, slot);
  if (V8_UNLIKELY(current && current.value()->kind() > code->kind())) {
    return;
  }
  Set(slot, MakeWeak(code->wrapper()));
  set_maybe_has_optimized_osr_code(true, code->kind());
}

bool FeedbackVector::osr_tiering_in_progress() {
  return OsrTieringInProgressBit::decode(flags());
}

void FeedbackVector::set_osr_tiering_in_progress(bool osr_in_progress) {
  set_flags(OsrTieringInProgressBit::update(flags(), osr_in_progress));
}

bool FeedbackVector::ClearSlots(Isolate* isolate, ClearBehavior behavior) {
  if (!shared_function_info()->HasFeedbackMetadata()) return false;
  Tagged<MaybeObject> uninitialized_sentinel =
      FeedbackVector::RawUninitializedSentinel(isolate);

  bool feedback_updated = false;
  DisallowGarbageCollection no_gc;
  FeedbackMetadataIterator iter(metadata(), no_gc);
  while (iter.HasNext()) {
    FeedbackSlot slot = iter.Next();

    Tagged<MaybeObject> obj = Get(slot);
    if (obj != uninitialized_sentinel) {
      FeedbackNexus nexus(isolate, *this, slot);
      feedback_updated |= nexus.Clear(behavior);
    }
  }
  return feedback_updated;
}

#ifdef V8_TRACE_FEEDBACK_UPDATES

// static
void FeedbackVector::TraceFeedbackChange(Isolate* isolate,
                                         Tagged<FeedbackVector> vector,
                                         FeedbackSlot slot,
                                         const char* reason) {
  int slot_count = vector->metadata()->slot_count();
  StdoutStream os;
  if (slot.IsInvalid()) {
    os << "[Feedback slots in ";
  } else {
    FeedbackSlotKind kind = vector->metadata()->GetKind(slot);
    os << "[Feedback slot " << slot.ToInt() << "/" << slot_count << " ("
       << FeedbackMetadata::Kind2String(kind) << ")"
       << " in ";
  }
  ShortPrint(vector->shared_function_info(), os);
  if (slot.IsInvalid()) {
    os << " updated - ";
  } else {
    os << " updated to ";
    vector->FeedbackSlotPrint(os, slot);
    os << " - ";
  }
  os << reason << "]" << std::endl;
}

#endif

MaybeObjectHandle NexusConfig::NewHandle(Tagged<MaybeObject> object) const {
  if (mode() == Mode::MainThread) {
    return handle(object, isolate_);
  }
  DCHECK_EQ(mode(), Mode::BackgroundThread);
  return handle(object, local_heap_);
}

void NexusConfig::SetFeedbackPair(Tagged<FeedbackVector> vector,
                                  FeedbackSlot start_slot,
                                  Tagged<MaybeObject> feedback,
                                  WriteBarrierMode mode,
                                  Tagged<MaybeObject> feedback_extra,
                                  WriteBarrierMode mode_extra) const {
  CHECK(can_write());
  CHECK_GT(vector->length(), start_slot.WithOffset(1).ToInt());
  base::MutexGuard mutex_guard(isolate()->feedback_vector_access());
  vector->Set(start_slot, feedback, mode);
  vector->Set(start_slot.WithOffset(1), feedback_extra, mode_extra);
}

std::pair<Tagged<MaybeObject>, Tagged<MaybeObject>>
NexusConfig::GetFeedbackPair(Tagged<FeedbackVector> vector,
                             FeedbackSlot slot) const {
  base::MutexGuardIf guard(isolate()->feedback_vector_access(),
                           mode() == BackgroundThread);
  Tagged<MaybeObject> feedback = vector->Get(slot);
  Tagged<MaybeObject> feedback_extra = vector->Get(slot.WithOffset(1));
  return std::make_pair(feedback, feedback_extra);
}

FeedbackNexus::FeedbackNexus(Isolate* isolate, Handle<FeedbackVector> vector,
                             FeedbackSlot slot)
    : vector_handle_(vector),
      slot_(slot),
      config_(NexusConfig::FromMainThread(isolate)) {
  kind_ = vector.is_null() ? FeedbackSlotKind::kInvalid : vector->GetKind(slot);
}

FeedbackNexus::FeedbackNexus(Isolate* isolate, Tagged<FeedbackVector> vector,
                             FeedbackSlot slot)
    : vector_(vector),
      slot_(slot),
      config_(NexusConfig::FromMainThread(isolate)) {
  kind_ = vector.is_null() ? FeedbackSlotKind::kInvalid : vector->GetKind(slot);
}

FeedbackNexus::FeedbackNexus(Handle<FeedbackVector> vector, FeedbackSlot slot,
                             const NexusConfig& config)
    : vector_handle_(vector),
      slot_(slot),
      kind_(vector->GetKind(slot, kAcquireLoad)),
      config_(config) {}

DirectHandle<WeakFixedArray> FeedbackNexus::CreateArrayOfSize(int length) {
  DCHECK(config()->can_write());
  DirectHandle<WeakFixedArray> array =
      config()->isolate()->factory()->NewWeakFixedArray(length);
  return array;
}

void FeedbackNexus::ConfigureUninitialized() {
  switch (kind()) {
    case FeedbackSlotKind::kStoreGlobalSloppy:
    case FeedbackSlotKind::kStoreGlobalStrict:
    case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
    case FeedbackSlotKind::kLoadGlobalInsideTypeof:
      SetFeedback(kClearedWeakValue, SKIP_WRITE_BARRIER,
                  UninitializedSentinel(), SKIP_WRITE_BARRIER);
      break;
    case FeedbackSlotKind::kCloneObject:
    case FeedbackSlotKind::kCall:
      SetFeedback(UninitializedSentinel(), SKIP_WRITE_BARRIER, Smi::zero(),
                  SKIP_WRITE_BARRIER);
      break;
    case FeedbackSlotKind::kInstanceOf:
      SetFeedback(UninitializedSentinel(), SKIP_WRITE_BARRIER);
      break;
    case FeedbackSlotKind::kSetNamedSloppy:
    case FeedbackSlotKind::kSetNamedStrict:
    case FeedbackSlotKind::kSetKeyedSloppy:
    case FeedbackSlotKind::kSetKeyedStrict:
    case FeedbackSlotKind::kStoreInArrayLiteral:
    case FeedbackSlotKind::kDefineNamedOwn:
    case FeedbackSlotKind::kDefineKeyedOwn:
    case FeedbackSlotKind::kLoadProperty:
    case FeedbackSlotKind::kLoadKeyed:
    case FeedbackSlotKind::kHasKeyed:
    case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
      SetFeedback(UninitializedSentinel(), SKIP_WRITE_BARRIER,
                  UninitializedSentinel(), SKIP_WRITE_BARRIER);
      break;
    case FeedbackSlotKind::kJumpLoop:
      SetFeedback(kClearedWeakValue, SKIP_WRITE_BARRIER);
      break;
    default:
      UNREACHABLE();
  }
}

bool FeedbackNexus::Clear(ClearBehavior behavior) {
  bool feedback_updated = false;

  switch (kind()) {
    case FeedbackSlotKind::kCompareOp:
    case FeedbackSlotKind::kForIn:
    case FeedbackSlotKind::kBinaryOp:
    case FeedbackSlotKind::kTypeOf:
      if (V8_LIKELY(behavior == ClearBehavior::kDefault)) {
        // We don't clear these, either.
      } else if (!IsCleared()) {
        DCHECK_EQ(behavior, ClearBehavior::kClearAll);
        SetFeedback(Smi::zero(), SKIP_WRITE_BARRIER);
        feedback_updated = true;
      }
      break;

    case FeedbackSlotKind::kLiteral:
      if (!IsCleared()) {
        SetFeedback(Smi::zero(), SKIP_WRITE_BARRIER);
        feedback_updated = true;
      }
      break;

    case FeedbackSlotKind::kSetNamedSloppy:
    case FeedbackSlotKind::kSetNamedStrict:
    case FeedbackSlotKind::kSetKeyedSloppy:
    case FeedbackSlotKind::kSetKeyedStrict:
    case FeedbackSlotKind::kStoreInArrayLiteral:
    case FeedbackSlotKind::kDefineNamedOwn:
    case FeedbackSlotKind::kDefineKeyedOwn:
    case FeedbackSlotKind::kLoadProperty:
    case FeedbackSlotKind::kLoadKeyed:
    case FeedbackSlotKind::kHasKeyed:
    case FeedbackSlotKind::kStoreGlobalSloppy:
    case FeedbackSlotKind::kStoreGlobalStrict:
    case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
    case FeedbackSlotKind::kLoadGlobalInsideTypeof:
    case FeedbackSlotKind::kCall:
    case FeedbackSlotKind::kInstanceOf:
    case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral:
    case FeedbackSlotKind::kCloneObject:
    case FeedbackSlotKind::kJumpLoop:
      if (!IsCleared()) {
        ConfigureUninitialized();
        feedback_updated = true;
      }
      break;

    case FeedbackSlotKind::kStringAddAndInternalize:
      if (V8_LIKELY(behavior == ClearBehavior::kDefault)) {
        // We don't clear these, either.
      } else if (!IsCleared()) {
        DCHECK_EQ(behavior, ClearBehavior::kClearAll);
        SetFeedback(Smi::zero(), SKIP_WRITE_BARRIER, UninitializedSentinel(),
                    SKIP_WRITE_BARRIER);
        feedback_updated = true;
      }
      break;

    case FeedbackSlotKind::kInvalid:
      UNREACHABLE();
  }
  return feedback_updated;
}

bool FeedbackNexus::ConfigureMegamorphic() {
  DisallowGarbageCollection no_gc;
  Tagged<MaybeObject> sentinel = MegamorphicSentinel();
  if (GetFeedback() != sentinel) {
    SetFeedback(sentinel, SKIP_WRITE_BARRIER, kClearedWeakValue);
    return true;
  }

  return false;
}

void FeedbackNexus::ConfigureMegaDOM(const MaybeObjectDirectHandle& handler) {
  DisallowGarbageCollection no_gc;
  Tagged<MaybeObject> sentinel = MegaDOMSentinel();

  SetFeedback(sentinel, SKIP_WRITE_BARRIER, *handler, UPDATE_WRITE_BARRIER);
}

bool FeedbackNexus::ConfigureMegamorphic(IcCheckType property_type) {
  DisallowGarbageCollection no_gc;
  Tagged<MaybeObject> sentinel = MegamorphicSentinel();
  Tagged<MaybeObject> maybe_extra =
      Smi::FromInt(static_cast<int>(property_type));

  auto feedback = GetFeedbackPair();
  bool update_required =
      feedback.first != sentinel || feedback.second != maybe_extra;
  if (update_required) {
    SetFeedback(sentinel, SKIP_WRITE_BARRIER, maybe_extra, SKIP_WRITE_BARRIER);
  }
  return update_required;
}

Tagged<Map> FeedbackNexus::GetFirstMap() const {
  FeedbackIterator it(this);
  if (!it.done()) {
    return it.map();
  }

  return Map();
}

InlineCacheState FeedbackNexus::ic_state() const {
  Tagged<MaybeObject> feedback, extra;
  std::tie(feedback, extra) = GetFeedbackPair();

  switch (kind()) {
    case FeedbackSlotKind::kLiteral:
      if (IsSmi(feedback)) return InlineCacheState::UNINITIALIZED;
      return InlineCacheState::MONOMORPHIC;

    case FeedbackSlotKind::kStoreGlobalSloppy:
    case FeedbackSlotKind::kStoreGlobalStrict:
    case FeedbackSlotKind::kLoadGlobalNotInsideTypeof:
    case FeedbackSlotKind::kLoadGlobalInsideTypeof:
    case FeedbackSlotKind::kJumpLoop: {
      if (IsSmi(feedback)) return InlineCacheState::MONOMORPHIC;

      DCHECK(feedback.IsWeakOrCleared());
      if (!feedback.IsCleared() || extra != UninitializedSentinel()) {
        return InlineCacheState::MONOMORPHIC;
      }
      return InlineCacheState::UNINITIALIZED;
    }

    case FeedbackSlotKind::kSetNamedSloppy:
    case FeedbackSlotKind::kSetNamedStrict:
    case FeedbackSlotKind::kSetKeyedSloppy:
    case FeedbackSlotKind::kSetKeyedStrict:
    case FeedbackSlotKind::kStoreInArrayLiteral:
    case FeedbackSlotKind::kDefineNamedOwn:
    case FeedbackSlotKind::kDefineKeyedOwn:
    case FeedbackSlotKind::kLoadProperty:
    case FeedbackSlotKind::kLoadKeyed:
    case FeedbackSlotKind::kHasKeyed: {
      if (feedback == UninitializedSentinel()) {
        return InlineCacheState::UNINITIALIZED;
      }
      if (feedback == MegamorphicSentinel()) {
        return InlineCacheState::MEGAMORPHIC;
      }
      if (feedback == MegaDOMSentinel()) {
        DCHECK(IsLoadICKind(kind()));
        return InlineCacheState::MEGADOM;
      }
      if (feedback.IsWeakOrCleared()) {
        // Don't check if the map is cleared.
        return InlineCacheState::MONOMORPHIC;
      }
      Tagged<HeapObject> heap_object;
      if (feedback.GetHeapObjectIfStrong(&heap_object)) {
        if (IsWeakFixedArray(heap_object)) {
          // Determine state purely by our structure, don't check if the maps
          // are cleared.
          return InlineCacheState::POLYMORPHIC;
        }
        if (IsName(heap_object)) {
          DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedStoreICKind(kind()) ||
                 IsKeyedHasICKind(kind()) || IsDefineKeyedOwnICKind(kind()));
          Tagged<Object> extra_object = extra.GetHeapObjectAssumeStrong();
          Tagged<WeakFixedArray> extra_array =
              Cast<WeakFixedArray>(extra_object);
          return extra_array->length() > 2 ? InlineCacheState::POLYMORPHIC
                                           : InlineCacheState::MONOMORPHIC;
        }
      }
      // TODO(1393773): Remove once the issue is solved.
      Address vector_ptr = vector().ptr();
      config_.isolate()->PushParamsAndDie(
          reinterpret_cast<void*>(feedback.ptr()),
          reinterpret_cast<void*>(extra.ptr()),
          reinterpret_cast<void*>(vector_ptr),
          reinterpret_cast<void*>(static_cast<intptr_t>(slot_.ToInt())),
          reinterpret_cast<void*>(static_cast<intptr_t>(kind())),
          // Include part of the feedback vector containing the slot.
          reinterpret_cast<void*>(
              vector_ptr + FeedbackVector::OffsetOfElementAt(slot_.ToInt())));
      UNREACHABLE();
    }
    case FeedbackSlotKind::kCall: {
      Tagged<HeapObject> heap_object;
      if (feedback == MegamorphicSentinel()) {
        return InlineCacheState::GENERIC;
      } else if (feedback.IsWeakOrCleared()) {
        if (feedback.GetHeapObjectIfWeak(&heap_object)) {
          if (IsFeedbackCell(heap_object)) {
            return InlineCacheState::POLYMORPHIC;
          }
          CHECK(IsJSFunction(heap_object) || IsJSBoundFunction(heap_object));
        }
        return InlineCacheState::MONOMORPHIC;
      } else if (feedback.GetHeapObjectIfStrong(&heap_object) &&
                 IsAllocationSite(heap_object)) {
        return InlineCacheState::MONOMORPHIC;
      }

      CHECK_EQ(feedback, UninitializedSentinel());
      return InlineCacheState::UNINITIALIZED;
    }
    case FeedbackSlotKind::kBinaryOp:
    case FeedbackSlotKind::kStringAddAndInternalize: {
      BinaryOperationHint hint = GetBinaryOperationFeedback();
      if (hint == BinaryOperationHint::kNone) {
        return InlineCacheState::UNINITIALIZED;
      } else if (hint == BinaryOperationHint::kAny) {
        return InlineCacheState::GENERIC;
      }

      return InlineCacheState::MONOMORPHIC;
    }
    case FeedbackSlotKind::kCompareOp: {
      CompareOperationHint hint = GetCompareOperationFeedback();
      if (hint == CompareOperationHint::kNone) {
        return InlineCacheState::UNINITIALIZED;
      } else if (hint == CompareOperationHint::kAny) {
        return InlineCacheState::GENERIC;
      }

      return InlineCacheState::MONOMORPHIC;
    }
    case FeedbackSlotKind::kForIn: {
      ForInHint hint = GetForInFeedback();
      if (hint == ForInHint::kNone) {
        return InlineCacheState::UNINITIALIZED;
      } else if (hint == ForInHint::kAny) {
        return InlineCacheState::GENERIC;
      }
      return InlineCacheState::MONOMORPHIC;
    }
    case FeedbackSlotKind::kTypeOf: {
      if (feedback == Smi::zero()) {
        return InlineCacheState::UNINITIALIZED;
      } else if (feedback == Smi::FromInt(TypeOfFeedback::kAny)) {
        return InlineCacheState::MEGAMORPHIC;
      } else if (base::bits::CountPopulation(
                     static_cast<uint32_t>(feedback.ToSmi().value())) == 1) {
        return InlineCacheState::MONOMORPHIC;
      } else {
        return InlineCacheState::POLYMORPHIC;
      }
    }
    case FeedbackSlotKind::kInstanceOf: {
      if (feedback == UninitializedSentinel()) {
        return InlineCacheState::UNINITIALIZED;
      } else if (feedback == MegamorphicSentinel()) {
        return InlineCacheState::MEGAMORPHIC;
      }
      return InlineCacheState::MONOMORPHIC;
    }
    case FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral: {
      if (feedback == UninitializedSentinel()) {
        return InlineCacheState::UNINITIALIZED;
      } else if (feedback.IsWeakOrCleared()) {
        // Don't check if the map is cleared.
        return InlineCacheState::MONOMORPHIC;
      }

      return InlineCacheState::MEGAMORPHIC;
    }

    case FeedbackSlotKind::kCloneObject: {
      if (feedback == UninitializedSentinel()) {
        return InlineCacheState::UNINITIALIZED;
      }
      if (feedback == MegamorphicSentinel()) {
        return InlineCacheState::MEGAMORPHIC;
      }
      if (feedback.IsWeakOrCleared()) {
        return InlineCacheState::MONOMORPHIC;
      }

      DCHECK(IsWeakFixedArray(feedback.GetHeapObjectAssumeStrong()));
      return InlineCacheState::POLYMORPHIC;
    }

    case FeedbackSlotKind::kInvalid:
      UNREACHABLE();
  }
  return InlineCacheState::UNINITIALIZED;
}

Builtin FeedbackNexus::ic_handler(Tagged<MaybeObject> feedback_extra,
                                  FeedbackSlotKind kind) {
  if (kind != FeedbackSlotKind::kLoadProperty) return Builtin::kIllegal;

  if (IsSmi(feedback_extra)) {
    int handler = feedback_extra.ToSmi().value();
    LoadHandler::Kind handler_kind = LoadHandler::KindBits::decode(handler);
    // LoadField.
    if (handler_kind == LoadHandler::Kind::kField) {
      int field_index = LoadHandler::FieldIndexBits::decode(handler);
      bool is_inobject = LoadHandler::IsInobjectBits::decode(handler);
      bool is_double = LoadHandler::IsDoubleBits::decode(handler);
      return GetLoadICHandlerForFieldIndex(field_index, is_inobject, is_double);
    }
  } else {
    Tagged<HeapObject> heap_object;
    // LoadConstantFromPrototype.
    if (feedback_extra.GetHeapObjectIfStrong(&heap_object) &&
        IsDataHandler(heap_object)) {
      Tagged<DataHandler> handler = Cast<DataHandler>(heap_object);
      Tagged<UnionOf<Smi, Code>> smi_handler = handler->smi_handler();
      if (IsSmi(smi_handler)) {
        int value = smi_handler.ToSmi().value();
        LoadHandler::Kind handler_kind = LoadHandler::KindBits::decode(value);
        if (handler_kind == LoadHandler::Kind::kConstantFromPrototype) {
          return Builtin::kLoadICConstantFromPrototypeBaseline;
        }
      }
    }
  }

  // ICs that are not specialized for a specific handler are considered generic.
  return Builtin::kLoadICGenericBaseline;
}

Builtin FeedbackNexus::ic_handler() const {
  DCHECK_EQ(kind(), FeedbackSlotKind::kLoadProperty);
  return FeedbackNexus::ic_handler(GetFeedbackExtra(), kind());
}

void FeedbackNexus::ConfigurePropertyCellMode(DirectHandle<PropertyCell> cell) {
  DCHECK(IsGlobalICKind(kind()));
  SetFeedback(MakeWeak(*cell), UPDATE_WRITE_BARRIER, UninitializedSentinel(),
              SKIP_WRITE_BARRIER);
}

#if DEBUG
namespace {
bool shouldStressLexicalIC(int script_context_index, int context_slot_index) {
  return (script_context_index + context_slot_index) % 100 == 0;
}
}  // namespace
#endif

bool FeedbackNexus::ConfigureLexicalVarMode(int script_context_index,
                                            int context_slot_index,
                                            bool immutable) {
  DCHECK(IsGlobalICKind(kind()));
  DCHECK_LE(0, script_context_index);
  DCHECK_LE(0, context_slot_index);
#if DEBUG
  if (v8_flags.stress_ic &&
      shouldStressLexicalIC(script_context_index, context_slot_index)) {
    return false;
  }
#endif
  if (!ContextIndexBits::is_valid(script_context_index) ||
      !SlotIndexBits::is_valid(context_slot_index) ||
      !ImmutabilityBit::is_valid(immutable)) {
    return false;
  }
  int config = ContextIndexBits::encode(script_context_index) |
               SlotIndexBits::encode(context_slot_index) |
               ImmutabilityBit::encode(immutable);

  SetFeedback(Smi::From31BitPattern(config), SKIP_WRITE_BARRIER,
              UninitializedSentinel(), SKIP_WRITE_BARRIER);
  return true;
}

void FeedbackNexus::ConfigureHandlerMode(
    const MaybeObjectDirectHandle& handler) {
  DCHECK(IsGlobalICKind(kind()));
  DCHECK(IC::IsHandler(*handler));
  SetFeedback(kClearedWeakValue, UPDATE_WRITE_BARRIER, *handler,
              UPDATE_WRITE_BARRIER);
}

void FeedbackNexus::ConfigureCloneObject(
    DirectHandle<Map> source_map, const MaybeObjectHandle& handler_handle) {
  // TODO(olivf): Introduce a CloneHandler to deal with all the logic of this
  // state machine which is now spread between Runtime_CloneObjectIC_Miss and
  // this method.
  auto GetHandler = [=]() {
    if (IsSmi(*handler_handle)) {
      return *handler_handle;
    }
    return MakeWeak(*handler_handle);
  };
  DCHECK(config()->can_write());
  Isolate* isolate = config()->isolate();
  DirectHandle<HeapObject> feedback;
  {
    Tagged<MaybeObject> maybe_feedback = GetFeedback();
    if (maybe_feedback.IsStrongOrWeak()) {
      feedback = direct_handle(maybe_feedback.GetHeapObject(), isolate);
    } else {
      DCHECK(maybe_feedback.IsCleared());
    }
  }
  switch (ic_state()) {
    case InlineCacheState::UNINITIALIZED:
      // Cache the first map seen which meets the fast case requirements.
      SetFeedback(MakeWeak(*source_map), UPDATE_WRITE_BARRIER, GetHandler());
      break;
    case InlineCacheState::MONOMORPHIC:
      if (feedback.is_null() || feedback.is_identical_to(source_map) ||
          Cast<Map>(*feedback)->is_deprecated()) {
        SetFeedback(MakeWeak(*source_map), UPDATE_WRITE_BARRIER, GetHandler());
      } else {
        // Transition to POLYMORPHIC.
        DirectHandle<WeakFixedArray> array =
            CreateArrayOfSize(2 * kCloneObjectPolymorphicEntrySize);
        DisallowGarbageCollection no_gc;
        Tagged<WeakFixedArray> raw_array = *array;
        raw_array->set(0, MakeWeak(*feedback));
        raw_array->set(1, GetFeedbackExtra());
        raw_array->set(2, MakeWeak(*source_map));
        raw_array->set(3, GetHandler());
        SetFeedback(raw_array, UPDATE_WRITE_BARRIER, kClearedWeakValue);
      }
      break;
    case InlineCacheState::POLYMORPHIC: {
      const int kMaxElements = v8_flags.max_valid_polymorphic_map_count *
                               kCloneObjectPolymorphicEntrySize;
      DirectHandle<WeakFixedArray> array = Cast<WeakFixedArray>(feedback);
      int i = 0;
      for (; i < array->length(); i += kCloneObjectPolymorphicEntrySize) {
        Tagged<MaybeObject> feedback_map = array->get(i);
        if (feedback_map.IsCleared()) break;
        DirectHandle<Map> cached_map(Cast<Map>(feedback_map.GetHeapObject()),
                                     isolate);
        if (cached_map.is_identical_to(source_map) ||
            cached_map->is_deprecated())
          break;
      }

      if (i >= array->length()) {
        if (i == kMaxElements) {
          // Transition to MEGAMORPHIC.
          Tagged<MaybeObject> sentinel = MegamorphicSentinel();
          SetFeedback(sentinel, SKIP_WRITE_BARRIER, kClearedWeakValue);
          break;
        }

        // Grow polymorphic feedback array.
        DirectHandle<WeakFixedArray> new_array = CreateArrayOfSize(
            array->length() + kCloneObjectPolymorphicEntrySize);
        for (int j = 0; j < array->length(); ++j) {
          new_array->set(j, array->get(j));
        }
        SetFeedback(*new_array);
        array = new_array;
      }

      array->set(i, MakeWeak(*source_map));
      array->set(i + 1, GetHandler());
      break;
    }

    default:
      UNREACHABLE();
  }
}

int FeedbackNexus::GetCallCount() {
  DCHECK(IsCallICKind(kind()));

  Tagged<Object> call_count = Cast<Object>(GetFeedbackExtra());
  CHECK(IsSmi(call_count));
  uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
  return CallCountField::decode(value);
}

void FeedbackNexus::SetSpeculationMode(SpeculationMode mode) {
  DCHECK(IsCallICKind(kind()));

  Tagged<Object> call_count = Cast<Object>(GetFeedbackExtra());
  CHECK(IsSmi(call_count));
  uint32_t count = static_cast<uint32_t>(Smi::ToInt(call_count));
  count = SpeculationModeField::update(count, mode);
  Tagged<MaybeObject> feedback = GetFeedback();
  // We could've skipped WB here (since we set the slot to the same value again)
  // but we don't to make WB verification happy.
  SetFeedback(feedback, UPDATE_WRITE_BARRIER, Smi::FromInt(count),
              SKIP_WRITE_BARRIER);
}

void FeedbackNexus::NextSpeculationMode(SpeculationMode mode) {
  SpeculationMode cur = GetSpeculationMode();
  if (cur == SpeculationMode::kAllowSpeculation) {
    SetSpeculationMode(mode);
  } else {
    SetSpeculationMode(SpeculationMode::kDisallowSpeculation);
  }
}

SpeculationMode FeedbackNexus::GetSpeculationMode() {
  DCHECK(IsCallICKind(kind()));

  Tagged<Object> call_count = Cast<Object>(GetFeedbackExtra());
  CHECK(IsSmi(call_count));
  uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
  return SpeculationModeField::decode(value);
}

CallFeedbackContent FeedbackNexus::GetCallFeedbackContent() {
  DCHECK(IsCallICKind(kind()));

  Tagged<Object> call_count = Cast<Object>(GetFeedbackExtra());
  CHECK(IsSmi(call_count));
  uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
  return CallFeedbackContentField::decode(value);
}

float FeedbackNexus::ComputeCallFrequency() {
  DCHECK(IsCallICKind(kind()));

  double const invocation_count = vector()->invocation_count(kRelaxedLoad);
  double const call_count = GetCallCount();
  if (invocation_count == 0.0) {  // Prevent division by 0.
    return 0.0f;
  }
  return static_cast<float>(call_count / invocation_count);
}

void FeedbackNexus::ConfigureMonomorphic(
    DirectHandle<Name> name, DirectHandle<Map> receiver_map,
    const MaybeObjectDirectHandle& handler) {
  DCHECK(handler.is_null() || IC::IsHandler(*handler));
  if (kind() == FeedbackSlotKind::kDefineKeyedOwnPropertyInLiteral) {
    SetFeedback(MakeWeak(*receiver_map), UPDATE_WRITE_BARRIER, *name);
  } else {
    if (name.is_null()) {
      SetFeedback(MakeWeak(*receiver_map), UPDATE_WRITE_BARRIER, *handler);
    } else {
      DirectHandle<WeakFixedArray> array = CreateArrayOfSize(2);
      array->set(0, MakeWeak(*receiver_map));
      array->set(1, *handler);
      SetFeedback(*name, UPDATE_WRITE_BARRIER, *array);
    }
  }
}

void FeedbackNexus::ConfigurePolymorphic(
    DirectHandle<Name> name, MapsAndHandlers const& maps_and_handlers) {
  int receiver_count = static_cast<int>(maps_and_handlers.size());
  DCHECK_GT(receiver_count, 1);
  DirectHandle<WeakFixedArray> array = CreateArrayOfSize(receiver_count * 2);

  int current = 0;

  for (; current < receiver_count; ++current) {
    auto [map, handler] = maps_and_handlers[current];
    if (map->is_deprecated()) continue;
    array->set(current * 2, MakeWeak(*map));
    DCHECK(IC::IsHandler(*handler));
    array->set(current * 2 + 1, *handler);
  }

  for (; current < receiver_count; ++current) {
    auto [map, handler] = maps_and_handlers[current];
    if (!map->is_deprecated()) continue;
    array->set(current * 2, MakeWeak(*map));
    DCHECK(IC::IsHandler(*handler));
    array->set(current * 2 + 1, *handler);
  }

  if (name.is_null()) {
    SetFeedback(*array, UPDATE_WRITE_BARRIER, UninitializedSentinel(),
                SKIP_WRITE_BARRIER);
  } else {
    SetFeedback(*name, UPDATE_WRITE_BARRIER, *array);
  }
}

int FeedbackNexus::ExtractMaps(MapHandles* maps) const {
  DisallowGarbageCollection no_gc;
  int found = 0;
  for (FeedbackIterator it(this); !it.done(); it.Advance()) {
    maps->push_back(config()->NewHandle(it.map()));
    found++;
  }

  return found;
}

MaybeObjectHandle FeedbackNexus::ExtractMegaDOMHandler() {
  DCHECK(ic_state() == InlineCacheState::MEGADOM);
  DisallowGarbageCollection no_gc;

  auto pair = GetFeedbackPair();
  Tagged<MaybeObject> maybe_handler = pair.second;
  if (!maybe_handler.IsCleared()) {
    MaybeObjectHandle handler = config()->NewHandle(maybe_handler);
    return handler;
  }

  return MaybeObjectHandle();
}

int FeedbackNexus::ExtractMapsAndHandlers(MapsAndHandlers* maps_and_handlers,
                                          TryUpdateHandler map_handler) const {
  DCHECK(!IsDefineKeyedOwnPropertyInLiteralKind(kind()));
  DisallowGarbageCollection no_gc;
  int found = 0;

  for (FeedbackIterator it(this); !it.done(); it.Advance()) {
    Handle<Map> map = config()->NewHandle(it.map());
    Tagged<MaybeObject> maybe_handler = it.handler();
    if (!maybe_handler.IsCleared()) {
      DCHECK(IC::IsHandler(maybe_handler));
      MaybeObjectHandle handler = config()->NewHandle(maybe_handler);
      if (map_handler && !(map_handler(map).ToHandle(&map))) {
        continue;
      }
      maps_and_handlers->emplace_back(map, handler);
      found++;
    }
  }

  return found;
}

MaybeObjectDirectHandle FeedbackNexus::FindHandlerForMap(
    DirectHandle<Map> map) const {
  DCHECK(!IsStoreInArrayLiteralICKind(kind()));

  for (FeedbackIterator it(this); !it.done(); it.Advance()) {
    if (it.map() == *map && !it.handler().IsCleared()) {
      return config()->NewHandle(it.handler());
    }
  }
  return MaybeObjectDirectHandle();
}

Tagged<Name> FeedbackNexus::GetName() const {
  if (IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
      IsKeyedHasICKind(kind()) || IsDefineKeyedOwnICKind(kind())) {
    Tagged<MaybeObject> feedback = GetFeedback();
    if (IsPropertyNameFeedback(feedback)) {
      return Cast<Name>(feedback.GetHeapObjectAssumeStrong());
    }
  }
  if (IsDefineKeyedOwnPropertyInLiteralKind(kind())) {
    Tagged<MaybeObject> extra = GetFeedbackExtra();
    if (IsPropertyNameFeedback(extra)) {
      return Cast<Name>(extra.GetHeapObjectAssumeStrong());
    }
  }
  return {};
}

KeyedAccessLoadMode FeedbackNexus::GetKeyedAccessLoadMode() const {
  DCHECK(IsKeyedLoadICKind(kind()) || IsKeyedHasICKind(kind()));
  // TODO(victorgomes): The KeyedAccessLoadMode::kInBounds is doing double duty
  // here. It shouldn't be used for property loads.
  if (GetKeyType() == IcCheckType::kProperty) {
    return KeyedAccessLoadMode::kInBounds;
  }
  MapsAndHandlers maps_and_handlers(isolate_);
  ExtractMapsAndHandlers(&maps_and_handlers);
  KeyedAccessLoadMode mode = KeyedAccessLoadMode::kInBounds;
  for (auto [_, handler] : maps_and_handlers) {
    mode = GeneralizeKeyedAccessLoadMode(
        mode, LoadHandler::GetKeyedAccessLoadMode(*handler));
  }
  return mode;
}

namespace {

bool BuiltinHasKeyedAccessStoreMode(Builtin builtin) {
  DCHECK(Builtins::IsBuiltinId(builtin));
  switch (builtin) {
    case Builtin::kKeyedStoreIC_SloppyArguments_InBounds:
    case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionGrowAndHandleCOW:
    case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreTypedArrayOOB:
    case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
    case Builtin::kStoreFastElementIC_InBounds:
    case Builtin::kStoreFastElementIC_NoTransitionGrowAndHandleCOW:
    case Builtin::kStoreFastElementIC_NoTransitionIgnoreTypedArrayOOB:
    case Builtin::kStoreFastElementIC_NoTransitionHandleCOW:
    case Builtin::kElementsTransitionAndStore_InBounds:
    case Builtin::kElementsTransitionAndStore_NoTransitionGrowAndHandleCOW:
    case Builtin::kElementsTransitionAndStore_NoTransitionIgnoreTypedArrayOOB:
    case Builtin::kElementsTransitionAndStore_NoTransitionHandleCOW:
      return true;
    default:
      return false;
  }
  UNREACHABLE();
}

KeyedAccessStoreMode KeyedAccessStoreModeForBuiltin(Builtin builtin) {
  DCHECK(BuiltinHasKeyedAccessStoreMode(builtin));
  switch (builtin) {
    case Builtin::kKeyedStoreIC_SloppyArguments_InBounds:
    case Builtin::kStoreFastElementIC_InBounds:
    case Builtin::kElementsTransitionAndStore_InBounds:
      return KeyedAccessStoreMode::kInBounds;
    case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionGrowAndHandleCOW:
    case Builtin::kStoreFastElementIC_NoTransitionGrowAndHandleCOW:
    case Builtin::kElementsTransitionAndStore_NoTransitionGrowAndHandleCOW:
      return KeyedAccessStoreMode::kGrowAndHandleCOW;
    case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionIgnoreTypedArrayOOB:
    case Builtin::kStoreFastElementIC_NoTransitionIgnoreTypedArrayOOB:
    case Builtin::kElementsTransitionAndStore_NoTransitionIgnoreTypedArrayOOB:
      return KeyedAccessStoreMode::kIgnoreTypedArrayOOB;
    case Builtin::kKeyedStoreIC_SloppyArguments_NoTransitionHandleCOW:
    case Builtin::kStoreFastElementIC_NoTransitionHandleCOW:
    case Builtin::kElementsTransitionAndStore_NoTransitionHandleCOW:
      return KeyedAccessStoreMode::kHandleCOW;
    default:
      UNREACHABLE();
  }
}

}  // namespace

KeyedAccessStoreMode FeedbackNexus::GetKeyedAccessStoreMode() const {
  DCHECK(IsKeyedStoreICKind(kind()) || IsStoreInArrayLiteralICKind(kind()) ||
         IsDefineKeyedOwnPropertyInLiteralKind(kind()) ||
         IsDefineKeyedOwnICKind(kind()));
  KeyedAccessStoreMode mode = KeyedAccessStoreMode::kInBounds;

  if (GetKeyType() == IcCheckType::kProperty) return mode;

  MapsAndHandlers maps_and_handlers(isolate_);
  ExtractMapsAndHandlers(&maps_and_handlers);
  for (auto [_, maybe_code_handler] : maps_and_handlers) {
    // The first handler that isn't the slow handler will have the bits we need.
    Builtin builtin_handler = Builtin::kNoBuiltinId;
    if (IsStoreHandler(*maybe_code_handler.object())) {
      auto data_handler = Cast<StoreHandler>(maybe_code_handler.object());

      if (IsSmi(data_handler->smi_handler())) {
        // Decode the KeyedAccessStoreMode information from the Handler.
        mode =
            StoreHandler::GetKeyedAccessStoreMode(data_handler->smi_handler());
        if (!StoreModeIsInBounds(mode)) return mode;
        continue;
      } else {
        Tagged<Code> code = TrustedCast<Code>(data_handler->smi_handler());
        builtin_handler = code->builtin_id();
      }

    } else if (IsSmi(*maybe_code_handler.object())) {
      // Skip for Proxy Handlers.
      if (*maybe_code_handler.object() == StoreHandler::StoreProxy()) {
        continue;
      }
      // Decode the KeyedAccessStoreMode information from the Handler.
      mode = StoreHandler::GetKeyedAccessStoreMode(*maybe_code_handler);
      if (!StoreModeIsInBounds(mode)) return mode;
      continue;
    } else if (IsDefineKeyedOwnICKind(kind())) {
      mode = StoreHandler::GetKeyedAccessStoreMode(*maybe_code_handler);
      if (!StoreModeIsInBounds(mode)) return mode;
      continue;
    } else {
      // Element store without prototype chain check.
      Tagged<Code> code = TrustedCast<Code>(*maybe_code_handler.object());
      builtin_handler = code->builtin_id();
    }

    if (Builtins::IsBuiltinId(builtin_handler)) {
      if (!BuiltinHasKeyedAccessStoreMode(builtin_handler)) continue;

      mode = KeyedAccessStoreModeForBuiltin(builtin_handler);
      break;
    }
  }

  return mode;
}

bool FeedbackNexus::IsOneMapManyNames() const {
  // "1 map, many names" mode is currently only used for Wasm maps, and
  // {IsWasmObjectMap} is currently only defined for Wasm-enabled builds.
#if V8_ENABLE_WEBASSEMBLY
  if (!IsKeyedLoadICKind(kind()) && !IsKeyedStoreICKind(kind())) return false;
  auto pair = GetFeedbackPair();
  Tagged<HeapObject> heap_object;
  if (!pair.first.GetHeapObjectIfWeak(&heap_object)) return false;
  if (!IsMap(heap_object)) return false;
  if (!IsWasmObjectMap(Cast<Map>(heap_object))) return false;
  if (!IsSmi(pair.second)) return false;
  Tagged<Smi> handler = pair.second.ToSmi();
  return handler == LoadHandler::LoadGeneric() ||
         handler == StoreHandler::StoreGeneric();
#else
  return false;
#endif  // V8_ENABLE_WEBASSEMBLY
}

IcCheckType FeedbackNexus::GetKeyType() const {
  DCHECK(IsKeyedStoreICKind(kind()) || IsKeyedLoadICKind(kind()) ||
         IsStoreInArrayLiteralICKind(kind()) || IsKeyedHasICKind(kind()) ||
         IsDefineKeyedOwnPropertyInLiteralKind(kind()) ||
         IsDefineKeyedOwnICKind(kind()));
  auto pair = GetFeedbackPair();
  Tagged<MaybeObject> feedback = pair.first;
  if (feedback == MegamorphicSentinel()) {
    return static_cast<IcCheckType>(Smi::ToInt(Cast<Smi>(pair.second)));
  }
  if (IsOneMapManyNames()) {
    return IcCheckType::kProperty;
  }
  Tagged<MaybeObject> maybe_name =
      IsDefineKeyedOwnPropertyInLiteralKind(kind()) ||
              IsDefineKeyedOwnICKind(kind())
          ? pair.second
          : feedback;
  return IsPropertyNameFeedback(maybe_name) ? IcCheckType::kProperty
                                            : IcCheckType::kElement;
}

BinaryOperationHint FeedbackNexus::GetBinaryOperationFeedback() const {
  DCHECK(kind() == FeedbackSlotKind::kBinaryOp ||
         kind() == FeedbackSlotKind::kStringAddAndInternalize);
  int feedback = GetFeedback().ToSmi().value();
  return BinaryOperationHintFromFeedback(feedback);
}

CompareOperationHint FeedbackNexus::GetCompareOperationFeedback() const {
  DCHECK_EQ(kind(), FeedbackSlotKind::kCompareOp);
  int feedback = GetFeedback().ToSmi().value();
  return CompareOperationHintFromFeedback(feedback);
}

TypeOfFeedback::Result FeedbackNexus::GetTypeOfFeedback() const {
  DCHECK_EQ(kind(), FeedbackSlotKind::kTypeOf);
  return static_cast<TypeOfFeedback::Result>(GetFeedback().ToSmi().value());
}

ForInHint FeedbackNexus::GetForInFeedback() const {
  DCHECK_EQ(kind(), FeedbackSlotKind::kForIn);
  int feedback = GetFeedback().ToSmi().value();
  return ForInHintFromFeedback(static_cast<ForInFeedback>(feedback));
}

MaybeDirectHandle<JSObject> FeedbackNexus::GetConstructorFeedback() const {
  DCHECK_EQ(kind(), FeedbackSlotKind::kInstanceOf);
  Tagged<MaybeObject> feedback = GetFeedback();
  Tagged<HeapObject> heap_object;
  if (feedback.GetHeapObjectIfWeak(&heap_object)) {
    return config()->NewHandle(Cast<JSObject>(heap_object));
  }
  return MaybeDirectHandle<JSObject>();
}

FeedbackIterator::FeedbackIterator(const FeedbackNexus* nexus)
    : done_(false), index_(-1), state_(kOther) {
  DCHECK(
      IsLoadICKind(nexus->kind()) || IsSetNamedICKind(nexus->kind()) ||
      IsKeyedLoadICKind(nexus->kind()) || IsKeyedStoreICKind(nexus->kind()) ||
      IsDefineNamedOwnICKind(nexus->kind()) ||
      IsDefineKeyedOwnPropertyInLiteralKind(nexus->kind()) ||
      IsStoreInArrayLiteralICKind(nexus->kind()) ||
      IsKeyedHasICKind(nexus->kind()) || IsDefineKeyedOwnICKind(nexus->kind()));

  DisallowGarbageCollection no_gc;
  auto pair = nexus->GetFeedbackPair();
  Tagged<MaybeObject> feedback = pair.first;
  bool is_named_feedback = IsPropertyNameFeedback(feedback);
  Tagged<HeapObject> heap_object;

  if ((feedback.GetHeapObjectIfStrong(&heap_object) &&
       IsWeakFixedArray(heap_object)) ||
      is_named_feedback) {
    index_ = 0;
    state_ = kPolymorphic;
    heap_object = feedback.GetHeapObjectAssumeStrong();
    if (is_named_feedback) {
      polymorphic_feedback_ = nexus->config()->NewHandle(
          Cast<WeakFixedArray>(pair.second.GetHeapObjectAssumeStrong()));
    } else {
      polymorphic_feedback_ =
          nexus->config()->NewHandle(Cast<WeakFixedArray>(heap_object));
    }
    AdvancePolymorphic();
  } else if (feedback.GetHeapObjectIfWeak(&heap_object)) {
    state_ = kMonomorphic;
    Tagged<MaybeObject> handler = pair.second;
    map_ = Cast<Map>(heap_object);
    handler_ = handler;
  } else {
    done_ = true;
  }
}

void FeedbackIterator::Advance() {
  CHECK(!done_);

  if (state_ == kMonomorphic) {
    done_ = true;
    return;
  }

  CHECK_EQ(state_, kPolymorphic);
  AdvancePolymorphic();
}

void FeedbackIterator::AdvancePolymorphic() {
  CHECK(!done_);
  CHECK_EQ(state_, kPolymorphic);
  int length = polymorphic_feedback_->length();
  Tagged<HeapObject> heap_object;

  while (index_ < length) {
    if (polymorphic_feedback_->get(index_).GetHeapObjectIfWeak(&heap_object)) {
      Tagged<MaybeObject> handler =
          polymorphic_feedback_->get(index_ + kHandlerOffset);
      map_ = Cast<Map>(heap_object);
      handler_ = handler;
      index_ += kEntrySize;
      return;
    }
    index_ += kEntrySize;
  }

  CHECK_EQ(index_, length);
  done_ = true;
}
}  // namespace v8::internal