/*
 * Copyright (c) 2019, 2025, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

#include "classfile/classLoaderData.hpp"
#include "gc/shared/gc_globals.hpp"
#include "gc/shared/isGCActiveMark.hpp"
#include "gc/z/zAddress.inline.hpp"
#include "gc/z/zGenerationId.hpp"
#include "gc/z/zHeap.inline.hpp"
#include "gc/z/zNMethod.hpp"
#include "gc/z/zPageAllocator.hpp"
#include "gc/z/zResurrection.hpp"
#include "gc/z/zRootsIterator.hpp"
#include "gc/z/zStackWatermark.hpp"
#include "gc/z/zStat.hpp"
#include "gc/z/zStoreBarrierBuffer.inline.hpp"
#include "gc/z/zVerify.hpp"
#include "memory/allocation.hpp"
#include "memory/iterator.inline.hpp"
#include "memory/resourceArea.hpp"
#include "oops/oop.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/globals.hpp"
#include "runtime/handles.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/stackFrameStream.inline.hpp"
#include "runtime/stackWatermark.inline.hpp"
#include "runtime/stackWatermarkSet.inline.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/preserveException.hpp"
#include "utilities/resourceHash.hpp"
#include "utilities/vmError.hpp"

#ifdef ASSERT

// Used to verify that safepoints operations can't be scheduled concurrently
// with callers to this function. Typically used to verify that object oops
// and headers are safe to access.
void z_verify_safepoints_are_blocked() {
  if (VMError::is_error_reported() && VMError::is_error_reported_in_current_thread()) {
    // The current thread has crashed and is creating an error report.
    // This may occur from any thread state, skip the safepoint_are_blocked
    // verification.
    return;
  }

  Thread* current = Thread::current();

  if (current->is_ConcurrentGC_thread()) {
    assert(current->is_suspendible_thread(), // Thread prevents safepoints
        "Safepoints are not blocked by current thread");

  } else if (current->is_Worker_thread()) {
    assert(// Check if ...
        // the thread prevents safepoints
        current->is_suspendible_thread() ||
        // the coordinator thread is the safepointing VMThread
        current->is_indirectly_safepoint_thread() ||
        // the coordinator thread prevents safepoints
        current->is_indirectly_suspendible_thread() ||
        // the RelocateQueue prevents safepoints
        //
        // RelocateQueue acts as a pseudo STS leaver/joiner and blocks
        // safepoints. There's currently no infrastructure  to check if the
        // current thread is active or not, so check the global states instead.
        ZGeneration::young()->is_relocate_queue_active() ||
        ZGeneration::old()->is_relocate_queue_active(),
        "Safepoints are not blocked by current thread");

  } else if (current->is_Java_thread()) {
    JavaThreadState state = JavaThread::cast(current)->thread_state();
    assert(state == _thread_in_Java || state == _thread_in_vm || state == _thread_new,
        "Safepoints are not blocked by current thread from state: %d", state);

  } else if (current->is_JfrSampler_thread()) {
    // The JFR sampler thread blocks out safepoints with this lock.
    assert_lock_strong(Threads_lock);

  } else if (current->is_VM_thread()) {
    // The VM Thread doesn't schedule new safepoints while executing
    // other safepoint or handshake operations.

  } else {
    fatal("Unexpected thread type");
  }
}

#endif

#define BAD_OOP_ARG(o, p)   "Bad oop " PTR_FORMAT " found at " PTR_FORMAT, untype(o), p2i(p)

static bool z_is_null_relaxed(zpointer o) {
  const uintptr_t color_mask = ZPointerAllMetadataMask | ZPointerReservedMask;
  return (untype(o) & ~color_mask) == 0;
}

static void z_verify_oop_object(zaddress addr, zpointer o, void* p) {
  const oop obj = cast_to_oop(addr);
  guarantee(oopDesc::is_oop(obj), BAD_OOP_ARG(o, p));
}

static void z_verify_root_oop_object(zaddress addr, void* p) {
  const oop obj = cast_to_oop(addr);
  guarantee(oopDesc::is_oop(obj), BAD_OOP_ARG(addr, p));
}

static void z_verify_old_oop(zpointer* p) {
  const zpointer o = *p;
  assert(o != zpointer::null, "Old should not contain raw null");
  if (!z_is_null_relaxed(o)) {
    if (ZPointer::is_mark_good(o)) {
      // Even though the pointer is mark good, we can't verify that it should
      // be in the remembered set in old mark end. We have to wait to the verify
      // safepoint after reference processing, where we hold the driver lock and
      // know there is no concurrent remembered set processing in the young generation.
      const zaddress addr = ZPointer::uncolor(o);
      z_verify_oop_object(addr, o, p);
    } else {
      const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o);
      // Old to young pointers might not be mark good if the young
      // marking has not finished, which is responsible for coloring
      // these pointers.
      if (ZHeap::heap()->is_old(addr) || !ZGeneration::young()->is_phase_mark()) {
        // Old to old pointers are allowed to have bad young bits
        guarantee(ZPointer::is_marked_old(o),  BAD_OOP_ARG(o, p));
        guarantee(ZHeap::heap()->is_old(p), BAD_OOP_ARG(o, p));
      }
    }
  }
}

static void z_verify_young_oop(zpointer* p) {
  const zpointer o = *p;
  if (!z_is_null_relaxed(o)) {
    guarantee(ZHeap::heap()->is_young(p), BAD_OOP_ARG(o, p));
    guarantee(ZPointer::is_marked_young(o),  BAD_OOP_ARG(o, p));

    if (ZPointer::is_load_good(o)) {
      z_verify_oop_object(ZPointer::uncolor(o), o, p);
    }
  }
}

static void z_verify_uncolored_root_oop(zaddress* p) {
  assert(!ZHeap::heap()->is_in((uintptr_t)p), "Roots shouldn't be in heap");
  const zaddress o = *p;
  if (!is_null(o)) {
    z_verify_root_oop_object(o, p);
  }
}

static void z_verify_possibly_weak_oop(zpointer* p) {
  const zpointer o = *p;
  if (!z_is_null_relaxed(o)) {
    guarantee(ZPointer::is_marked_old(o) || ZPointer::is_marked_finalizable(o), BAD_OOP_ARG(o, p));

    const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o);
    guarantee(ZHeap::heap()->is_old(addr) || ZPointer::is_marked_young(o), BAD_OOP_ARG(o, p));
    guarantee(ZHeap::heap()->is_young(addr) || ZHeap::heap()->is_object_live(addr), BAD_OOP_ARG(o, p));
    z_verify_oop_object(addr, o, p);

    // Verify no missing remset entries. We are holding the driver lock here and that
    // allows us to more precisely verify the remembered set, as there is no concurrent
    // young generation collection going on at this point.
    const uintptr_t remset_bits = untype(o) & ZPointerRememberedMask;
    const uintptr_t prev_remembered = ZPointerRemembered ^ ZPointerRememberedMask;
    guarantee(remset_bits != prev_remembered, BAD_OOP_ARG(o, p));
    guarantee(remset_bits == ZPointerRememberedMask ||
              ZGeneration::young()->is_remembered(p) ||
              ZStoreBarrierBuffer::is_in(p), BAD_OOP_ARG(o, p));
  }
}

class ZVerifyColoredRootClosure : public OopClosure {
private:
  const bool _verify_marked_old;

public:
  ZVerifyColoredRootClosure(bool verify_marked_old)
    : OopClosure(),
      _verify_marked_old(verify_marked_old) {}

  virtual void do_oop(oop* p_) {
    zpointer* const p = (zpointer*)p_;

    assert(!ZHeap::heap()->is_in((uintptr_t)p), "Roots shouldn't be in heap");

    const zpointer o = *p;

    if (z_is_null_relaxed(o)) {
      // Skip verifying nulls
      return;
    }

    assert(is_valid(o), "Catch me!");

    if (_verify_marked_old) {
      guarantee(ZPointer::is_marked_old(o), BAD_OOP_ARG(o, p));

      // Minor collections could have relocated the object;
      // use load barrier to find correct object.
      const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o);
      z_verify_oop_object(addr, o, p);
    } else {
      // Don't know the state of the oop
      if (is_valid(o)) {
        // it looks like a valid colored oop;
        // use load barrier to find correct object.
        const zaddress addr = ZBarrier::load_barrier_on_oop_field_preloaded(nullptr, o);
        z_verify_oop_object(addr, o, p);
      }
    }
  }

  virtual void do_oop(narrowOop*) {
    ShouldNotReachHere();
  }
};

class ZVerifyUncoloredRootClosure : public OopClosure {
public:
  virtual void do_oop(oop* p_) {
    zaddress* const p = (zaddress*)p_;
    z_verify_uncolored_root_oop(p);
  }

  virtual void do_oop(narrowOop*) {
    ShouldNotReachHere();
  }
};

class ZVerifyOldOopClosure : public BasicOopIterateClosure {
private:
  const bool _verify_weaks;

public:
  ZVerifyOldOopClosure(bool verify_weaks)
    : _verify_weaks(verify_weaks) {}

  virtual void do_oop(oop* p_) {
    zpointer* const p = (zpointer*)p_;
    if (_verify_weaks) {
      z_verify_possibly_weak_oop(p);
    } else {
      // We should never encounter finalizable oops through strong
      // paths. This assumes we have only visited strong roots.
      z_verify_old_oop(p);
    }
  }

  virtual void do_oop(narrowOop* p) {
    ShouldNotReachHere();
  }

  virtual ReferenceIterationMode reference_iteration_mode() {
    return _verify_weaks ? DO_FIELDS : DO_FIELDS_EXCEPT_REFERENT;
  }
};

class ZVerifyYoungOopClosure : public BasicOopIterateClosure {
private:
  const bool _verify_weaks;

public:
  ZVerifyYoungOopClosure(bool verify_weaks)
    : _verify_weaks(verify_weaks) {}

  virtual void do_oop(oop* p_) {
    zpointer* const p = (zpointer*)p_;
    if (_verify_weaks) {
      //z_verify_possibly_weak_oop(p);
      z_verify_young_oop(p);
    } else {
      // We should never encounter finalizable oops through strong
      // paths. This assumes we have only visited strong roots.
      z_verify_young_oop(p);
    }
  }

  virtual void do_oop(narrowOop* p) {
    ShouldNotReachHere();
  }

  virtual ReferenceIterationMode reference_iteration_mode() {
    return _verify_weaks ? DO_FIELDS : DO_FIELDS_EXCEPT_REFERENT;
  }

  // Don't follow this metadata when verifying oops
  virtual void do_method(Method* m) {}
  virtual void do_nmethod(nmethod* nm) {}
};

typedef ClaimingCLDToOopClosure<ClassLoaderData::_claim_none> ZVerifyCLDClosure;

class ZVerifyThreadClosure : public ThreadClosure {
private:
  OopClosure* const _verify_cl;

public:
  ZVerifyThreadClosure(OopClosure* verify_cl)
    : _verify_cl(verify_cl) {}

  virtual void do_thread(Thread* thread) {
    JavaThread* const jt = JavaThread::cast(thread);
    const ZStackWatermark* const watermark = StackWatermarkSet::get<ZStackWatermark>(jt, StackWatermarkKind::gc);
    if (watermark->processing_started_acquire()) {
      thread->oops_do_no_frames(_verify_cl, nullptr);

       if (watermark->processing_completed_acquire()) {
         thread->oops_do_frames(_verify_cl, nullptr);
       }
    }
  }
};

class ZVerifyNMethodClosure : public NMethodClosure {
private:
  OopClosure* const        _cl;
  BarrierSetNMethod* const _bs_nm;

public:
  ZVerifyNMethodClosure(OopClosure* cl)
    : _cl(cl),
      _bs_nm(BarrierSet::barrier_set()->barrier_set_nmethod()) {}

  virtual void do_nmethod(nmethod* nm) {
    if (_bs_nm->is_armed(nm)) {
      // Can't verify
      return;
    }

    ZNMethod::nmethod_oops_do(nm, _cl);
  }
};

void ZVerify::roots_strong(bool verify_after_old_mark) {
  assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");

  {
    ZVerifyColoredRootClosure cl(verify_after_old_mark);
    ZVerifyCLDClosure cld_cl(&cl);

    ZRootsIteratorStrongColored roots_strong_colored(ZGenerationIdOptional::none);
    roots_strong_colored.apply(&cl,
                               &cld_cl);
  }

  {
    ZVerifyUncoloredRootClosure cl;
    ZVerifyThreadClosure thread_cl(&cl);
    ZVerifyNMethodClosure nm_cl(&cl);

    ZRootsIteratorStrongUncolored roots_strong_uncolored(ZGenerationIdOptional::none);
    roots_strong_uncolored.apply(&thread_cl,
                                 &nm_cl);
  }
}

void ZVerify::roots_weak() {
  assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
  assert(!ZResurrection::is_blocked(), "Invalid phase");

  ZVerifyColoredRootClosure cl(true /* verify_after_old_mark*/);
  ZRootsIteratorWeakColored roots_weak_colored(ZGenerationIdOptional::none);
  roots_weak_colored.apply(&cl);
}

zaddress zverify_broken_object = zaddress::null;

class ZVerifyObjectClosure : public ObjectClosure, public OopFieldClosure {
private:
  const bool         _verify_weaks;

  zaddress           _visited_base;
  volatile zpointer* _visited_p;
  zpointer           _visited_ptr_pre_loaded;

public:
  ZVerifyObjectClosure(bool verify_weaks)
    : _verify_weaks(verify_weaks),
      _visited_base(),
      _visited_p(),
      _visited_ptr_pre_loaded() {}

  void log_dead_object(zaddress addr) {
    tty->print_cr("ZVerify found dead object: " PTR_FORMAT " at p: " PTR_FORMAT " ptr: " PTR_FORMAT, untype(addr), p2i((void*)_visited_p), untype(_visited_ptr_pre_loaded));
    to_oop(addr)->print();
    tty->print_cr("--- From --- ");
    if (_visited_base != zaddress::null) {
      to_oop(_visited_base)->print();
    }
    tty->cr();

    if (zverify_broken_object == zaddress::null) {
      zverify_broken_object = addr;
    }
  }

  void verify_live_object(oop obj) {
    // Verify that its pointers are sane
    ZVerifyOldOopClosure cl(_verify_weaks);
    ZIterator::oop_iterate_safe(obj, &cl);
  }

  virtual void do_object(oop obj) {
    guarantee(oopDesc::is_oop_or_null(obj), "Must be");

    const zaddress addr = to_zaddress(obj);
    if (ZHeap::heap()->is_old(addr)) {
      if (ZHeap::heap()->is_object_live(addr)) {
        verify_live_object(obj);
      } else {
        log_dead_object(addr);
      }
    } else {
      // Young object - no verification
    }
  }

  virtual void do_field(oop base, oop* p) {
    _visited_base = to_zaddress(base);
    _visited_p = (volatile zpointer*)p;
    _visited_ptr_pre_loaded = Atomic::load(_visited_p);
  }
};

void ZVerify::threads_start_processing() {
  class StartProcessingClosure : public ThreadClosure {
  public:
    void do_thread(Thread* thread) {
      StackWatermarkSet::start_processing(JavaThread::cast(thread), StackWatermarkKind::gc);
    }
  };

  ZJavaThreadsIterator threads_iterator(ZGenerationIdOptional::none);
  StartProcessingClosure cl;
  threads_iterator.apply(&cl);
}

void ZVerify::objects(bool verify_weaks) {
  if (ZAbort::should_abort()) {
    // Invariants might be a bit mushy if the young generation
    // collection was forced to shut down. So let's be a bit forgiving here.
    return;
  }
  assert(SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
  assert(ZGeneration::young()->is_phase_mark_complete() ||
         ZGeneration::old()->is_phase_mark_complete(), "Invalid phase");
  assert(!ZResurrection::is_blocked(), "Invalid phase");

  // Note that object verification will fix the pointers and
  // only verify that the resulting objects are sane.

  // The verification VM_Operation doesn't start the thread processing.
  // Do it here, after the roots have been verified.
  threads_start_processing();

  ZVerifyObjectClosure object_cl(verify_weaks);
  ZHeap::heap()->object_and_field_iterate_for_verify(&object_cl, &object_cl, verify_weaks);
}

void ZVerify::before_zoperation() {
  // Verify strong roots
  if (ZVerifyRoots) {
    roots_strong(false /* verify_after_old_mark */);
  }
}

void ZVerify::after_mark() {
  // Verify all strong roots and strong references
  if (ZVerifyRoots) {
    roots_strong(true /* verify_after_old_mark */);
  }
  if (ZVerifyObjects) {
    objects(false /* verify_weaks */);
    guarantee(zverify_broken_object == zaddress::null, "Verification failed");
  }
}

void ZVerify::after_weak_processing() {
  // Verify all roots and all references
  if (ZVerifyRoots) {
    roots_strong(true /* verify_after_old_mark */);
    roots_weak();
  }
  if (ZVerifyObjects) {
    objects(true /* verify_weaks */);
  }
}

//
// Remembered set verification
//

typedef ResourceHashtable<volatile zpointer*, bool, 1009, AnyObj::C_HEAP, mtGC> ZStoreBarrierBufferTable;

static ZStoreBarrierBufferTable* z_verify_store_barrier_buffer_table = nullptr;

#define BAD_REMSET_ARG(p, ptr, addr) \
  "Missing remembered set at " PTR_FORMAT " pointing at " PTR_FORMAT \
  " (" PTR_FORMAT " + %zd)" \
  , p2i(p), untype(ptr), untype(addr), p2i(p) - untype(addr)

class ZVerifyRemsetBeforeOopClosure : public BasicOopIterateClosure {
private:
  ZForwarding*    _forwarding;
  zaddress_unsafe _from_addr;

public:
  ZVerifyRemsetBeforeOopClosure(ZForwarding* forwarding)
    : _forwarding(forwarding),
      _from_addr(zaddress_unsafe::null) {}

  void set_from_addr(zaddress_unsafe addr) {
    _from_addr = addr;
  }

  virtual void do_oop(oop* p_) {
    volatile zpointer* const p = (volatile zpointer*)p_;
    const zpointer ptr = *p;

    if (ZPointer::is_remembered_exact(ptr)) {
      // When the remembered bits are 11, it means that it is intentionally
      // not part of the remembered set
      return;
    }

    if (ZBufferStoreBarriers && z_verify_store_barrier_buffer_table->get(p) != nullptr) {
      // If this oop location is in the store barrier buffer, we can't assume
      // that it should have a remset entry
      return;
    }

    if (_forwarding->find(_from_addr) != zaddress::null) {
      // If the mutator has already relocated the object to to-space, we defer
      // and do to-space verification afterwards instead, because store barrier
      // buffers could have installed the remembered set entry in to-space and
      // then flushed the store barrier buffer, and then start young marking
      return;
    }

    ZPage* page = _forwarding->page();

    if (ZGeneration::old()->active_remset_is_current()) {
      guarantee(page->is_remembered(p), BAD_REMSET_ARG(p, ptr, _from_addr));
    } else {
      guarantee(page->was_remembered(p), BAD_REMSET_ARG(p, ptr, _from_addr));
    }
  }

  virtual void do_oop(narrowOop* p) {
    ShouldNotReachHere();
  }

  virtual ReferenceIterationMode reference_iteration_mode() {
    return DO_FIELDS;
  }
};

void ZVerify::on_color_flip() {
  if (!ZVerifyRemembered || !ZBufferStoreBarriers) {
    return;
  }

  // Reset the table tracking the stale stores of the store barrier buffer
  delete z_verify_store_barrier_buffer_table;
  z_verify_store_barrier_buffer_table = new (mtGC) ZStoreBarrierBufferTable();

  // Gather information from store barrier buffers as we currently can't verify
  // remset entries for oop locations touched by the store barrier buffer

  for (JavaThreadIteratorWithHandle jtiwh; JavaThread* const jt = jtiwh.next(); ) {
    const ZStoreBarrierBuffer* const buffer = ZThreadLocalData::store_barrier_buffer(jt);

    for (size_t i = buffer->current(); i < ZStoreBarrierBuffer::BufferLength; ++i) {
      volatile zpointer* const p = buffer->_buffer[i]._p;
      bool created = false;
      z_verify_store_barrier_buffer_table->put_if_absent(p, true, &created);
    }
  }
}

void ZVerify::before_relocation(ZForwarding* forwarding) {
  if (!ZVerifyRemembered) {
    return;
  }

  if (forwarding->from_age() != ZPageAge::old) {
    // Only supports verification of old-to-old relocations now
    return;
  }

  // Verify that the inactive remset is cleared
  if (ZGeneration::old()->active_remset_is_current()) {
    forwarding->page()->verify_remset_cleared_previous();
  } else {
    forwarding->page()->verify_remset_cleared_current();
  }

  ZVerifyRemsetBeforeOopClosure cl(forwarding);

  forwarding->object_iterate([&](oop obj) {
    const zaddress_unsafe addr = to_zaddress_unsafe(cast_from_oop<uintptr_t>(obj));
    cl.set_from_addr(addr);
    obj->oop_iterate(&cl);
  });
}

class ZVerifyRemsetAfterOopClosure : public BasicOopIterateClosure {
private:
  ZForwarding* const _forwarding;
  zaddress_unsafe    _from_addr;
  zaddress           _to_addr;

public:
  ZVerifyRemsetAfterOopClosure(ZForwarding* forwarding)
    : _forwarding(forwarding),
      _from_addr(zaddress_unsafe::null),
      _to_addr(zaddress::null) {}

  void set_from_addr(zaddress_unsafe addr) {
    _from_addr = addr;
  }

  void set_to_addr(zaddress addr) {
    _to_addr = addr;
  }

  virtual void do_oop(oop* p_) {
    volatile zpointer* const p = (volatile zpointer*)p_;
    const zpointer ptr = Atomic::load(p);

    // Order this load w.r.t. the was_remembered load which can race when
    // the remset scanning of the to-space object is concurrently forgetting
    // an entry.
    OrderAccess::loadload();

    if (ZPointer::is_remembered_exact(ptr)) {
      // When the remembered bits are 11, it means that it is intentionally
      // not part of the remembered set
      return;
    }

    if (ZPointer::is_store_good(ptr)) {
      // In to-space, there could be stores racing with the verification.
      // Such stores may not have reliably manifested in the remembered
      // sets yet.
      return;
    }

    if (ZBufferStoreBarriers && z_verify_store_barrier_buffer_table->get(p) != nullptr) {
      // If this to-space oop location is in the store barrier buffer, we
      // can't assume that it should have a remset entry
      return;
    }

    const uintptr_t p_offset = uintptr_t(p) - untype(_to_addr);
    volatile zpointer* const fromspace_p = (volatile zpointer*)(untype(_from_addr) + p_offset);

    if (ZBufferStoreBarriers && z_verify_store_barrier_buffer_table->get(fromspace_p) != nullptr) {
      // If this from-space oop location is in the store barrier buffer, we
      // can't assume that it should have a remset entry
      return;
    }

    ZPage* page = ZHeap::heap()->page(p);

    if (page->is_remembered(p) || page->was_remembered(p)) {
      // No missing remembered set entry
      return;
    }

    OrderAccess::loadload();
    if (Atomic::load(p) != ptr) {
      // Order the was_remembered bitmap load w.r.t. the reload of the zpointer.
      // Sometimes the was_remembered() call above races with clearing of the
      // previous bits, when the to-space object is concurrently forgetting
      // remset entries because they were not so useful. When that happens,
      // we have already self healed the pointers to have 11 in the remset
      // bits.
      return;
    }

    guarantee(ZGeneration::young()->is_phase_mark(), "Should be in the mark phase " BAD_REMSET_ARG(p, ptr, _to_addr));
    guarantee(_forwarding->relocated_remembered_fields_published_contains(p), BAD_REMSET_ARG(p, ptr, _to_addr));
  }

  virtual void do_oop(narrowOop* p) {
    ShouldNotReachHere();
  }

  virtual ReferenceIterationMode reference_iteration_mode() {
    return DO_FIELDS;
  }
};

void ZVerify::after_relocation_internal(ZForwarding* forwarding) {
  ZVerifyRemsetAfterOopClosure cl(forwarding);

  forwarding->address_unsafe_iterate_via_table([&](zaddress_unsafe from_addr) {
    // If no field in this object was in the store barrier buffer
    // when relocation started, we should be able to verify trivially
    ZGeneration* const from_generation = forwarding->from_age() == ZPageAge::old ? (ZGeneration*)ZGeneration::old()
                                                                           : (ZGeneration*)ZGeneration::young();
    const zaddress to_addr = from_generation->remap_object(from_addr);

    cl.set_from_addr(from_addr);
    cl.set_to_addr(to_addr);
    const oop to_obj = to_oop(to_addr);
    to_obj->oop_iterate(&cl);
  });
}

void ZVerify::after_relocation(ZForwarding* forwarding) {
  if (!ZVerifyRemembered) {
    return;
  }

  if (forwarding->to_age() != ZPageAge::old) {
    // No remsets to verify in the young gen
    return;
  }

  if (ZGeneration::young()->is_phase_mark() &&
      forwarding->relocated_remembered_fields_is_concurrently_scanned()) {
    // Can't verify to-space objects if concurrent YC rejected published
    // remset information, because that data is incomplete. The YC might
    // not have finished scanning the forwarding, and might be about to
    // insert required remembered set entries.
    return;
  }

  after_relocation_internal(forwarding);
}

void ZVerify::after_scan(ZForwarding* forwarding) {
  if (!ZVerifyRemembered) {
    return;
  }

  if (ZAbort::should_abort()) {
    // We can't verify remembered set accurately when shutting down the VM
    return;
  }

  if (!ZGeneration::old()->is_phase_relocate() ||
      !forwarding->relocated_remembered_fields_is_concurrently_scanned()) {
    // Only verify remembered set from remembered set scanning, when the
    // remembered set scanning rejected the publishing information of concurrent
    // old generation relocation
    return;
  }

  after_relocation_internal(forwarding);
}
