From eba6e437be424b114ee869442db69168f206908b Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Fri, 27 Mar 2026 13:16:10 -0400 Subject: [PATCH] xo-gc: verify visits+checks to-space contents --- include/xo/gc/DX1Collector.hpp | 19 +++++++++--- src/gc/DX1Collector.cpp | 53 +++++++++++++++++++++++++++++++--- 2 files changed, 64 insertions(+), 8 deletions(-) diff --git a/include/xo/gc/DX1Collector.hpp b/include/xo/gc/DX1Collector.hpp index a686d45..2c14985 100644 --- a/include/xo/gc/DX1Collector.hpp +++ b/include/xo/gc/DX1Collector.hpp @@ -84,12 +84,23 @@ namespace xo { * **/ struct VerifyStats { + bool is_ok() const noexcept { + return (n_from_ == 0) && (n_fwd_ == 0) && (n_no_iface_ == 0); + } + void clear() { *this = VerifyStats(); } - std::uint32_t n_gc_root_ = 0; - std::uint32_t n_ext_ = 0; - std::uint32_t n_from_ = 0; - std::uint32_t n_to_ = 0; + /** number of gc roots examined **/ + std::uint32_t n_gc_root_ = 0; + std::uint32_t n_ext_ = 0; + /** number of from-space objects encountered. Fatal if non-zero **/ + std::uint32_t n_from_ = 0; + /** number of to-space objects encountered. **/ + std::uint32_t n_to_ = 0; + /** counts forwarding object encountered in to-space scan. Fatal if non-zero **/ + std::uint32_t n_fwd_ = 0; + /** counts missing GCObject interface. Fatal if non-zero **/ + std::uint32_t n_no_iface_ = 0; }; // ----- DX1Collector ----- diff --git a/src/gc/DX1Collector.cpp b/src/gc/DX1Collector.cpp index 031e922..cd6ba5d 100644 --- a/src/gc/DX1Collector.cpp +++ b/src/gc/DX1Collector.cpp @@ -367,6 +367,8 @@ namespace xo { // Add run state so DX1Collector can recognize forward_inplace() // calls made for the purpose of checking child pointers. + auto self = this->ref(); + GCRunState saved_runstate = runstate_; { this->runstate_ = GCRunState::verify(); @@ -386,7 +388,7 @@ namespace xo { // - X1Collector::forward_inplace() -> _verify_aux() // - gco.forward_children(this->ref()); + gco.forward_children(self); } @@ -397,12 +399,49 @@ namespace xo { ++verify_stats_.n_gc_root_; } + + // 3. scan to-space for each generation + for (Generation g(0); g < config_.n_generation_; ++g) { + const DArena * space = this->get_space(role::to_space(), g); + + for (const AllocInfo & info : *space) { + + if (info.is_forwarding_tseq()) { + ++verify_stats_.n_fwd_; + + } else { + typeseq tseq(info.tseq()); + + const AGCObject * iface = this->lookup_type(tseq); + + if (iface) { + const void * data = info.payload().first; + + // assembled fop for gc-aware object + obj gco(iface, const_cast(data)); + + // forward_children is hijacked here to verify + // child pointer validity. + // + // Nested control reenters + // X1Collector::forward_inplace() + // + gco.forward_children(self); + } else { + ++verify_stats_.n_no_iface_; + continue; + } + } + } + } } // restore run state at end of verify cycle this->runstate_ = saved_runstate; - return true; + bool ok = verify_stats_.is_ok(); + + return ok; } const AGCObject * @@ -424,6 +463,7 @@ namespace xo { xtag("types.hi", object_types_.hi_)); assert(false); + return nullptr; } return target; @@ -487,6 +527,7 @@ namespace xo { if (config_.sanitize_flag_) { log && log("step 0a : verify"); this->verify_ok(); + } log && log("step 0b : update run state"); @@ -514,12 +555,16 @@ namespace xo { if (config_.sanitize_flag_) { log && log("step 4b : verify"); - this->verify_ok(); + bool ok = this->verify_ok(); log && log(xtag("n-gc-root", verify_stats_.n_gc_root_), xtag("n-ext", verify_stats_.n_ext_), xtag("n-from", verify_stats_.n_from_), - xtag("n-to", verify_stats_.n_to_)); + xtag("n-to", verify_stats_.n_to_), + xtag("n-fwd", verify_stats_.n_fwd_), + xtag("n-no-iface", verify_stats_.n_no_iface_)); + + assert(ok); } }