xo-gc: refactor: move forward_inplace_aux() to GCObjectStore

This commit is contained in:
Roland Conybeare 2026-04-03 15:53:25 -04:00
commit c67e1cf52c
4 changed files with 217 additions and 8 deletions

View file

@ -392,7 +392,8 @@ namespace xo {
/** Evacuate object at @p *lhs_data to to-space.
* Replace original with forwarding pointer to new location
**/
void _forward_inplace_aux(AGCObject * lhs_iface, void ** lhs_data);
void _forward_inplace_aux(AGCObject * lhs_iface, void ** lhs_data, Generation upto);
/** Verify that pointer {@p iface, @p data} is valid:
* destination either in to-space, or somewhere outside this collector
**/

View file

@ -135,6 +135,18 @@ namespace xo {
void * gco_data,
Generation upto) const noexcept;
/** Evacuate object at @p *lhs_data to to-space, during collection phase
* acting on generations g in [0 ,.., upto).
* Need @p gc to pass to invoke AGCObject methods shallow_copy() and
* forward_children()
*
* Replace original with forwarding pointer to new location
**/
void _forward_inplace_aux(DX1Collector * gc,
AGCObject * lhs_iface,
void ** lhs_data,
Generation upto);
/** For each generation g in [0 ,.., upto)
* swap arenas assigned to {to-space, from-space}.
* Invoked once at the beginning of each gc cycle.

View file

@ -1094,9 +1094,11 @@ namespace xo {
DX1Collector::forward_inplace(AGCObject * lhs_iface,
void ** lhs_data)
{
Generation upto = runstate_.gc_upto();
if (runstate_.is_running()) {
// called during collection phase
this->_forward_inplace_aux(lhs_iface, lhs_data);
this->_forward_inplace_aux(lhs_iface, lhs_data, upto);
} else if (runstate_.is_verify()) {
// called during verify_ok
this->_verify_aux(lhs_iface, *lhs_data);
@ -1108,8 +1110,14 @@ namespace xo {
void
DX1Collector::_forward_inplace_aux(AGCObject * lhs_iface,
void ** lhs_data)
void ** lhs_data,
Generation upto)
{
// upto == runstate_.gc_upto()
gco_store_._forward_inplace_aux(this, lhs_iface, lhs_data, upto);
#ifdef MARKED
scope log(XO_DEBUG(config_.debug_flag_),
xtag("lhs_data", lhs_data),
xtag("*lhs_data", lhs_data ? *lhs_data : nullptr));
@ -1137,7 +1145,7 @@ namespace xo {
if (!object_data) {
/* trivial to forward nullptr */
return;
} else if (!this->contains(role::from_space(), object_data)) {
} else if (!gco_store_.contains(role::from_space(), object_data)) {
/* *lhs_data either:
* 1. already in to-space
* 2. not in GC-allocated space at all
@ -1162,7 +1170,7 @@ namespace xo {
* allocated object data.
* Only using this to get alloc header
**/
DArena * some_arena = this->from_space(Generation(0));
DArena * some_arena = gco_store_.from_space(Generation(0));
DArena::header_type * p_header
= some_arena->obj2hdr(object_data);
@ -1192,7 +1200,7 @@ namespace xo {
*/
assert(alloc_z >= sizeof(uintptr_t));
if (this->is_forwarding_header(alloc_hdr)) {
if (gco_store_.is_forwarding_header(alloc_hdr)) {
/* *lhs_data already refers to a forwarding pointer */
/*
@ -1245,7 +1253,7 @@ namespace xo {
xtag("tname", TypeRegistry::id2name(typeseq(info.tseq()))),
xtag("age", info.age()), xtag("size", info.size()));
}
} else if (this->check_move_policy(alloc_hdr, object_data)) {
} else if (gco_store_._check_move_policy(alloc_hdr, object_data, upto)) {
/* copy object *lhs + replace with forwarding pointer */
log && log("forward object now");
@ -1289,7 +1297,8 @@ namespace xo {
* e.g. incremental collection + object is tenured
*/
}
} /*_forward_inplace*/
#endif
} /*_forward_inplace_aux*/
void
DX1Collector::_verify_aux(AGCObject * iface, void * data)

View file

@ -4,6 +4,7 @@
**/
#include "GCObjectStore.hpp"
#include "X1Collector.hpp"
#include <xo/object2/Dictionary.hpp>
#include <xo/object2/Array.hpp>
@ -371,6 +372,192 @@ namespace xo {
return (g < upto);
}
void
GCObjectStore::_forward_inplace_aux(DX1Collector * gc,
AGCObject * lhs_iface,
void ** lhs_data,
Generation upto)
{
// upto == runstate_.gc_upto()
scope log(XO_DEBUG(config_.debug_flag_),
xtag("lhs_data", lhs_data),
xtag("*lhs_data", lhs_data ? *lhs_data : nullptr));
/* coordinates with DX1Collector::_deep_move() */
/*
* lhs obj<AGCObject>
* | +---------+ +---+-+----+
* \--->| .iface | | T |G|size| header
* +---------+ object_data +---+-+----+
* | .data x----------------->| alloc |
* +---------+ | data |
* | for |
* | instance |
* | ... |
* +----------+
*/
void * object_data = (std::byte *)*lhs_data;
if (!object_data) {
/* trivial to forward nullptr */
return;
} else if (!this->contains(role::from_space(), object_data)) {
/* *lhs_data either:
* 1. already in to-space
* 2. not in GC-allocated space at all
* (small number of niche examples of this)
*
* It's important we recognize case (2) up front.
* Since not allocated from GC, they don't have
* an alloc-header.
*/
log && log("disposition: not in from-space. Don't forward, but check children");
obj<AGCObject> gco(lhs_iface, object_data);
gco.forward_children(gc->ref<ACollector>());
return;
}
log && log("disposition: in from-space");
/** NOTE: for form's sake:
* lookup actual arena that
* allocated object data.
* Only using this to get alloc header
**/
DArena * some_arena = this->from_space(Generation(0));
DArena::header_type * p_header
= some_arena->obj2hdr(object_data);
DArena::header_type alloc_hdr = *p_header;
/* recover allocation size */
std::size_t alloc_z = some_arena->config_.header_.size_with_padding(alloc_hdr);
if (log) {
log(xtag("some_arena.lo", some_arena->lo_),
xtag("p_header", p_header),
xtag("alloc_z", alloc_z));
AllocInfo info = this->alloc_info((std::byte *)object_data);
log(xtag("tseq", info.tseq()),
xtag("tname", TypeRegistry::id2name(typeseq(info.tseq()))),
xtag("is_forwarding_tseq", info.is_forwarding_tseq()),
xtag("age", info.age()),
xtag("size", info.size()));
}
/* need to be able to fit forwarding pointer
* in place of forwarded object.
*
* This is guaranteed anyway, by alignment rules
*/
assert(alloc_z >= sizeof(uintptr_t));
if (this->is_forwarding_header(alloc_hdr)) {
/* *lhs_data already refers to a forwarding pointer */
/*
* lhs obj<AGCObject> (from-space)
* | +---------+ +---+-+----+
* \--->| .iface | |FWD|G|size| alloc_hdr
* +---------+ object_data +---+-+----+
* | .data x----------------->| x--------\
* +---------+ | | | dest
* | | |
* +----------+ |
* |
* (to-space) |
* +---+-+----+ |
* |TSQ|G|size| |
* +---+-+----+ |
* | | <-/
* | |
* | |
* +----------+
*/
void * dest = *(void**)object_data;
*lhs_data = dest;
/*
* lhs obj<AGCObject>
* | +---------+
* \--->| .iface |
* +---------+
* | .data x------------\
* +---------+ |
* | dest
* |
* |
* | (to-space)
* | +---+-+----+
* | |TSQ|G|size|
* | +---+-+----+
* \---> | |
* | |
* | |
* +----------+
*/
if (log) {
log("lhs_data already forwarded", xtag("dest", dest));
AllocInfo info = this->alloc_info((std::byte *)dest);
log(xtag("tseq", info.tseq()),
xtag("tname", TypeRegistry::id2name(typeseq(info.tseq()))),
xtag("age", info.age()), xtag("size", info.size()));
}
} else if (this->_check_move_policy(alloc_hdr, object_data, upto)) {
/* copy object *lhs + replace with forwarding pointer */
log && log("forward object now");
/*
* lhs obj<AGCObject> (from-space)
* | +---------+ +---+-+----+
* \--->| .iface | |TSQ|G|size| alloc_hdr
* +---------+ object_data +---+-+----+
* | .data x----------------> | |
* +---------+ | |
* | |
* +----------+
*/
*lhs_data = this->_shallow_move(gc->ref<AAllocator>(), lhs_iface, *lhs_data);
/*
* lhs obj<AGCObject> (from-space)
* | +---------+ +---+-+----+
* \--->| .iface | |FWD|G|SIZE|
* +---------+ +---+-+----+
* | .data x------------\ | x--------\
* +---------+ | | | |
* | | | |
* dest | +----------+ |
* | |
* | (to-space) |
* | +---+-+----+ |
* | |TSQ|G|size| |
* | +---+-+----+ |
* \---> | | <-/
* | |
* | |
* +----------+
*/
} else {
log && log("object not eligible/required to forward");
/* object doesn't need to move.
* e.g. incremental collection + object is tenured
*/
}
} /*_forward_inplace_aux*/
void
GCObjectStore::swap_roles(Generation upto) noexcept
{