xo-interpreter2 stack: + reason arg to visit_gco_children()

Helps streamline DX1Collector in xo-gc/.
Want both forward and verify entry points for the same
representation.
This commit is contained in:
Roland Conybeare 2026-04-10 01:10:03 -04:00
commit dc31e0f772
10 changed files with 55 additions and 35 deletions

View file

@ -274,20 +274,13 @@ namespace xo {
/** Execute gc immediately, for all generations < @p upto **/
void execute_gc(Generation upto) noexcept;
#ifdef OBSOLETE // replaced by visit_child()
/** Evacuate object at @p *lhs_data to to-space.
* Replace original with forwarding pointer to new location
**/
void forward_inplace(AGCObject * lhs_iface, void ** lhs_data);
#endif
/** Supports GCObjectVisitor facet.
* During gc phase:
* During gc phase (@p reason is 'forward')
* 1. evacuate object at @p *lhs_data to to-space.
* 2. replace @p *lhs_data with forwarding pointer
* to new location.
**/
void visit_child(AGCObject * lhs_iface, void ** lhs_data);
void visit_child(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data);
// ----- allocation -----

View file

@ -58,7 +58,7 @@ Source must be owned by this collector.
Increments object age **/
static void * alloc_copy(DX1Collector & self, std::byte * src);
/** visit child of a gc-aware object. May update child in-place! **/
static void visit_child(DX1Collector & self, AGCObject * iface, void ** pp_data) noexcept;
static void visit_child(DX1Collector & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept;
///@}
};

View file

@ -404,7 +404,7 @@ namespace xo {
// - X1Collector::forward_inplace() -> _verify_aux()
//
gco.visit_gco_children(self);
gco.visit_gco_children(VisitReason::verify(), self);
}
@ -585,21 +585,28 @@ namespace xo {
}
void
DX1Collector::visit_child(AGCObject * lhs_iface,
DX1Collector::visit_child(VisitReason reason,
AGCObject * lhs_iface,
void ** lhs_data)
{
// MAYBE: adapter distinct from DX1Collector that supports GCObjectVisitor facet,
// calls DX1Collector::_verify_aux()
if (runstate_.is_running()) {
switch (reason.code()) {
case VisitReason::code::forward:
{
Generation upto = runstate_.gc_upto();
// called during collection phase
gco_store_.forward_inplace_aux(this->ref<AGCObjectVisitor>(), lhs_iface, lhs_data, upto);
} else if (runstate_.is_verify()) {
gco_store_.forward_inplace_aux
(this->ref<AGCObjectVisitor>(), lhs_iface, lhs_data, upto);
break;
}
case VisitReason::code::verify:
// called during verify_ok
this->_verify_aux(lhs_iface, *lhs_data);
} else {
break;
default:
// should be unreachable
assert(false);
}

View file

@ -501,7 +501,7 @@ namespace xo {
log && log("disposition: not in from-space. Don't forward, but check children");
obj<AGCObject> gco(lhs_iface, object_data);
gco.visit_gco_children(gc);
gco.visit_gco_children(VisitReason::forward(), gc);
return;
}
@ -714,7 +714,7 @@ namespace xo {
// Nested control reenters
// X1Collector::forward_inplace() -> _verify_aux()
//
gco.visit_gco_children(gc);
gco.visit_gco_children(VisitReason::forward(), gc);
} else {
++(p_verify_stats->n_no_iface_);
continue;
@ -783,7 +783,7 @@ namespace xo {
GCMoveCheckpoint gray_lo_v
= this->snap_move_checkpoint(upto);
from_src.visit_gco_children(gc);
from_src.visit_gco_children(VisitReason::forward(), gc);
// For each generation g:
// traverse objects newer than gray_lo_v[g], to make sure children
@ -1013,7 +1013,7 @@ namespace xo {
assert(iface->_has_null_vptr() == false);
iface->visit_gco_children(src, gc);
iface->visit_gco_children(src, VisitReason::forward(), gc);
gray_lo_v[g] = ((std::byte *)src) + z;

View file

@ -33,9 +33,9 @@ namespace xo {
return self.alloc_copy(src);
}
auto
IGCObjectVisitor_DX1Collector::visit_child(DX1Collector & self, AGCObject * iface, void ** pp_data) noexcept -> void
IGCObjectVisitor_DX1Collector::visit_child(DX1Collector & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept -> void
{
self.visit_child(iface, pp_data);
self.visit_child(reason, iface, pp_data);
}
} /*namespace mm*/

View file

@ -21,9 +21,12 @@ namespace xo {
}
void
DMockCollector::visit_child(AGCObject * lhs_iface, void ** lhs_data)
DMockCollector::visit_child(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data)
{
p_gco_store_->forward_inplace_aux(this->ref<AGCObjectVisitor>(), lhs_iface, lhs_data, upto_);
(void)reason;
p_gco_store_->forward_inplace_aux
(this->ref<AGCObjectVisitor>(), lhs_iface, lhs_data, upto_);
}
std::byte *

View file

@ -26,7 +26,7 @@ namespace xo {
Generation generation_of(Role r, const void * addr) const noexcept;
AllocInfo alloc_info(void * mem) const noexcept;
void visit_child(AGCObject * lhs_iface, void ** lhs_data);
void visit_child(VisitReason reason, AGCObject * lhs_iface, void ** lhs_data);
std::byte * alloc_copy(void * src) noexcept;
private:

View file

@ -295,7 +295,8 @@ namespace ut {
* 2. arena2 doesn't have concept of installed types.
* It doesn't have or require any builtin ability to traverse an object model
**/
DArena arena2 = DArena::map(ArenaConfig().with_name("arena2-reference")
DArena arena2
= DArena::map(ArenaConfig().with_name("arena2-reference")
.with_size(tc.gc_size_ * tc.n_gen_)
.with_store_header_flag(true));
@ -325,7 +326,7 @@ namespace ut {
}
}
// verify basic arena partitioning
// verify basic arena partitioning + sizing
{
REQUIRE(g0 != g1);
REQUIRE(gcos.new_space());
@ -371,7 +372,7 @@ namespace ut {
}
}
// allocator
// allocator api
auto alloc = obj<AAllocator,DArena>(gcos.new_space());
// create object(s).
@ -420,6 +421,7 @@ namespace ut {
REQUIRE(gcos.header2size(obj_info.header()) == obj_info.size());
REQUIRE(gcos.header2age(obj_info.header()) == object_age{0});
REQUIRE(gcos.header2tseq(obj_info.header()) == obj_info.tseq());
REQUIRE(gcos.is_forwarding_header(obj_info.header()) == false);
}
// new objects appear in to-space for generation 0
@ -441,6 +443,7 @@ namespace ut {
for (size_t i = 0, n = x1_v.size(); i < n; ++i) {
const auto & x1 = x1_v.at(i);
REQUIRE(gcos.contains(Role::from_space(), x1.gco_.data()));
REQUIRE(gcos.contains_allocated(Role::from_space(), x1.gco_.data()));
AllocInfo obj_info = gcos.alloc_info((std::byte *)x1.gco_.data());
REQUIRE(obj_info.size() >= x1.alloc_z_);
@ -578,7 +581,8 @@ namespace ut {
// can still try to move something.
// but will fail since type isn't registered
auto x1p_data = gcos.deep_move_root(mock_gc_visitor, x1.gco_, g1);
auto x1p_data
= gcos.deep_move_root(mock_gc_visitor, x1.gco_, g1);
// control here under normal GC use
// would represent a configuration fail
@ -590,10 +594,23 @@ namespace ut {
// Things to test:
// - deep_move_interior() // used from MutationLogStore
// - forward_inplace_aux() // used from DX1Collector.visit_child
// - cleanup_phase() // used from DX1Collector._cleanup_phase
// - report_object_types
// - report_object_ages()
bool sanitize_flag = true;
// swaps to- and from- spaces again
// Now from-space will be empty, all live objects in to-space
gcos.cleanup_phase(g1, sanitize_flag);
#ifdef NOT_YET
gcos.verify_ok(xxx objectvisitor,
xxx &verify_stats);
#endif
// - verify_ok
}
}

View file

@ -33,9 +33,9 @@ namespace xo {
return self.alloc_copy(src);
}
auto
IGCObjectVisitor_DMockCollector::visit_child(DMockCollector & self, AGCObject * iface, void ** pp_data) noexcept -> void
IGCObjectVisitor_DMockCollector::visit_child(DMockCollector & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept -> void
{
self.visit_child(iface, pp_data);
self.visit_child(reason, iface, pp_data);
}
} /*namespace mm*/

View file

@ -58,7 +58,7 @@ Source must be owned by this collector.
Increments object age **/
static void * alloc_copy(DMockCollector & self, std::byte * src);
/** visit child of a gc-aware object. May update child in-place! **/
static void visit_child(DMockCollector & self, AGCObject * iface, void ** pp_data) noexcept;
static void visit_child(DMockCollector & self, VisitReason reason, AGCObject * iface, void ** pp_data) noexcept;
///@}
};