xo-alloc + xo-allocutil: refactor to shrink dep surface area

This commit is contained in:
Roland Conybeare 2025-12-01 01:20:49 -05:00
commit 30a00be262
12 changed files with 100 additions and 413 deletions

View file

@ -5,7 +5,7 @@
#pragma once
#include "IAlloc.hpp"
#include "xo/allocutil/IAlloc.hpp"
#include "ObjectStatistics.hpp"
namespace xo {
@ -175,8 +175,8 @@ namespace xo {
virtual void clear() final override;
virtual void checkpoint() final override;
virtual std::byte * alloc(std::size_t z) final override;
virtual bool check_owned(IObject * src) const final override;
virtual bool check_owned(Object * src) const final override;
ArenaAlloc & operator=(const ArenaAlloc &) = delete;
ArenaAlloc & operator=(ArenaAlloc &&) = delete;

View file

@ -19,18 +19,18 @@ namespace xo {
**/
class Forwarding1 : public Object {
public:
explicit Forwarding1(gp<Object> dest);
explicit Forwarding1(gp<IObject> dest);
// inherited from Object..
virtual TaggedPtr self_tp() const final override;
virtual void display(std::ostream & os) const final override;
virtual bool _is_forwarded() const final override { return true; }
virtual Object * _offset_destination(Object * src) const final override;
virtual Object * _destination() final override;
virtual IObject * _offset_destination(IObject * src) const final override;
virtual IObject * _destination() final override;
/** required by Object i/face, but never called on Forwarding1 **/
virtual std::size_t _shallow_size() const final override;
/** required by Object i/face, but never called on Forwarding1 **/
virtual Object * _shallow_copy(gc::IAlloc * mm) const final override;
virtual IObject * _shallow_copy(gc::IAlloc * mm) const final override;
/** required by Object i/face, but never called on Forwarding1 **/
virtual std::size_t _forward_children(gc::IAlloc * mm) final override;
@ -47,7 +47,7 @@ namespace xo {
* UB revealed when GC traverses a pointer that relies on the 2nd
* vtable to index virtual methods.
**/
gp<Object> dest_;
gp<IObject> dest_;
};
} /*namespace obj*/

View file

@ -88,29 +88,30 @@ namespace xo {
class MutationLogEntry {
public:
MutationLogEntry(Object * parent, Object ** lhs) : parent_{parent}, lhs_{lhs} {}
MutationLogEntry(IObject * parent, IObject ** lhs)
: parent_{parent}, lhs_{lhs} {}
Object * parent() const { return parent_; }
Object ** lhs() const { return lhs_; }
IObject * parent() const { return parent_; }
IObject ** lhs() const { return lhs_; }
Object * child() const { return *lhs_; }
IObject * child() const { return *lhs_; }
bool is_child_forwarded() const;
bool is_parent_forwarded() const;
Object * parent_destination() const;
IObject * parent_destination() const;
/** Flag obsolete mutation.
* Future proofing, never happens for regular objects
**/
bool is_dead() const { return false; }
MutationLogEntry update_parent_moved(Object * parent_to) const;
void fixup_parent_child_moved(Object * child_to) { *lhs_ = child_to; }
MutationLogEntry update_parent_moved(IObject * parent_to) const;
void fixup_parent_child_moved(IObject * child_to) { *lhs_ = child_to; }
private:
Object * parent_;
Object ** lhs_;
IObject * parent_ = nullptr;
IObject ** lhs_ = nullptr;
};
using MutationLog = std::vector<MutationLogEntry>;
@ -235,15 +236,15 @@ namespace xo {
/** add gc root at address @p addr . Gc will keep alive anything reachable
* from @c *addr
**/
void add_gc_root(Object ** addr);
void add_gc_root(IObject ** addr);
/** reverse the effect of previous call to @ref add_gc_root **/
void remove_gc_root(Object ** addr);
void remove_gc_root(IObject ** addr);
/** convenience wrapper **/
template <typename T>
void add_gc_root_dwim(gp<T> * p) { this->add_gc_root(reinterpret_cast<Object**>(p->ptr_address())); }
void add_gc_root_dwim(gp<T> * p) { this->add_gc_root(reinterpret_cast<IObject**>(p->ptr_address())); }
template <typename T>
void remove_gc_root_dwim(gp<T> * p) { this->remove_gc_root(reinterpret_cast<Object**>(p->ptr_address())); }
void remove_gc_root_dwim(gp<T> * p) { this->remove_gc_root(reinterpret_cast<IObject**>(p->ptr_address())); }
/** may optionally use this to observe GC copy phase.
* Will be invoked once _per surviving object_, so not cheap.
@ -308,17 +309,17 @@ namespace xo {
* @param lhs. address of a member variable within the allocation of @p parent.
* @param rhs. new target for @p *lhs
**/
virtual void assign_member(Object * parent, Object ** lhs, Object* rhs) final override;
virtual void assign_member(IObject * parent, IObject ** lhs, IObject* rhs) final override;
/** during GC check for source objects owned by GC.
* See Object::_shallow_move.
**/
virtual bool check_owned(Object * src) const final override;
virtual bool check_owned(IObject * src) const final override;
/** queries during GC to determine if object at address @p src should move:
* - full GC -> always
* - incr GC -> if not tenured
**/
virtual bool check_move(Object * src) const final override;
virtual bool check_move(IObject * src) const final override;
virtual std::byte * alloc(std::size_t z) final override;
virtual std::byte * alloc_gc_copy(std::size_t z, const void * src) final override;
@ -349,7 +350,7 @@ namespace xo {
/** scan to-space for object statistics before GC */
void capture_object_statistics(generation upto, capture_phase phase);
/** copy object **/
void copy_object(Object ** addr, generation upto, ObjectStatistics * object_stats);
void copy_object(IObject ** addr, generation upto, ObjectStatistics * object_stats);
/** copy everything reachable from global gc roots **/
void copy_globals(generation g);
/** review mutation log; may discover+rescue reachable objects.
@ -426,7 +427,7 @@ namespace xo {
* Application can introduce new root object pointers at any time provided GC not running,
* but cannot withdraw them.
**/
std::vector<Object**> gc_root_v_;
std::vector<IObject**> gc_root_v_;
/** log cross-generational and cross-checkpoint mutations.
* These need to be adjusted on next incremental collection

View file

@ -1,129 +0,0 @@
/* file IAlloc.hpp
*
* author: Roland Conybeare, Jul 2025
*/
#pragma once
#include <memory>
#include <cstdint>
namespace xo {
template <typename T>
using up = std::unique_ptr<T>;
class Object;
namespace gc {
/** @class IAllocator
* @brief arena allocation interface with limited garbage collector support
*
* Garbage collector support methods:
* - checkpoint()
* - assign_member()
* - alloc_gc_copy()
*
* See class GC for copying incremental collector.
* See class ArenaAlloc for arena allocator
**/
class IAlloc {
public:
virtual ~IAlloc() {}
/** compute padding to add to an allocation of size z to bring it up to
* a multiple of word size (8 bytes on x86_64)
**/
static std::uint32_t alloc_padding(std::size_t z);
/** z + alloc_padding(z) **/
static std::size_t with_padding(std::size_t z);
/** optional name for this allocator; labelling for diagnostics **/
virtual const std::string & name() const = 0;
/** allocator size in bytes (up to reserved limit)
* Includes unallocated mmeory
**/
virtual std::size_t size() const = 0;
/** committed size in bytes **/
virtual std::size_t committed() const = 0;
/** number of unallocated bytes available (up to soft limit)
* from this allocator
**/
virtual std::size_t available() const = 0;
/** number of bytes allocated from this allocator **/
virtual std::size_t allocated() const = 0;
/** true iff pointer x comes from this allocator **/
virtual bool contains(const void * x) const = 0;
/** true iff object at address @p x was allocated by this allocator,
* and before checkpoint
**/
virtual bool is_before_checkpoint(const void * x) const = 0;
/** number of bytes allocated before @ref checkpoint **/
virtual std::size_t before_checkpoint() const = 0;
/** number of bytes allocated since @ref checkpoint **/
virtual std::size_t after_checkpoint() const = 0;
/** @return true iff debug logging enabled **/
virtual bool debug_flag() const = 0;
/** remember allocator state. All currently-allocated addresses xo
* will satisfy is_before_checkpoint(x). Subsequent allocations x
* will fail is_before_checkpoint(x), until checkpoint superseded
* by @ref clear or another call to @ref checkpoint
**/
virtual void checkpoint() = 0;
/** allocate @p z bytes of memory. returns pointer to first address **/
virtual std::byte * alloc(std::size_t z) = 0;
/** reset allocator to empty state. **/
virtual void clear() = 0;
// ----- GC-specific methods -----
/** true iff this allocator owns object at address @p src.
* Use to assist Object::_shallow_move
**/
virtual bool check_owned(Object * src) const;
/** true iff object at address @p src must move as part of
* in-progress collection phase
**/
virtual bool check_move(Object * src) const;
/** perform assignment
* @code
* *lhs = rhs
* @endcode
* plus additional book keeping if needed (e.g. in @ref GC)
* Default implementation just does the assignment.
**/
virtual void assign_member(Object * parent, Object ** lhs, Object * rhs);
/** allocate @p z bytes for copy of object at @p src.
* Only used in @ref GC. Default implementation asserts and returns nullptr
**/
virtual std::byte * alloc_gc_copy(std::size_t z, const void * src);
};
} /*namespace gc*/
class MMPtr {
public:
explicit MMPtr(gc::IAlloc * mm) : mm_{mm} {}
gc::IAlloc * mm_ = nullptr;
};
} /*namespace xo*/
inline void * operator new (std::size_t z, const xo::MMPtr & mmp) {
return mmp.mm_->alloc(z);
}
//inline void operator delete (void * p, const MMPtr & mmp) {
// mmp.mm_->free(reinterpret_cast<std::byte *>(p));
//}
inline void * operator new[] (std::size_t z, const xo::MMPtr & mmp) {
return mmp.mm_->alloc(z);
}
//inline void operator delete[] (void * p, const MMPtr & mmp) {
// mmp.mm_->free(reinterpret_cast<std::byte *>(p));
//}
/* end IAlloc.hpp */

View file

@ -7,6 +7,8 @@
#include "xo/reflect/TaggedPtr.hpp"
#include "IAlloc.hpp"
#include "xo/allocutil/IObject.hpp"
#include "xo/allocutil/gc_ptr.hpp"
#include <concepts>
#include <cstdint>
@ -16,71 +18,6 @@ namespace xo {
class ObjectStatistics;
};
template <typename T>
class gc_ptr;
template <typename T>
using gp = gc_ptr<T>;
/** wrapper for a pointer to garbage-collector-eligible T.
* Application code will usually use the alias template gp<T>
**/
template <typename T>
class gc_ptr {
public:
using element_type = T;
public:
gc_ptr() = default;
gc_ptr(T * p) : ptr_{p} {}
gc_ptr(const gc_ptr & x) : ptr_{x.ptr_} {}
/** create from gc_ptr to some related type @tparam S **/
template <typename S>
gc_ptr(const gc_ptr<S> & x) : ptr_{x.ptr()} {}
/** runtime downcast. shorthand for dynamic_cast<T*> **/
template <typename S>
static gc_ptr<T> from(const gc_ptr<S> & x) { return gc_ptr<T>{dynamic_cast<T*>(x.ptr())}; }
/** convenience for static asserts **/
static constexpr bool is_gc_ptr = true;
/** see also: xo/refcnt/Refcounted.hpp **/
static constexpr bool is_rc_ptr = false;
static bool is_eq(gc_ptr x1, gc_ptr x2) {
std::uintptr_t u1 = reinterpret_cast<std::uintptr_t>(x1.ptr());
std::uintptr_t u2 = reinterpret_cast<std::uintptr_t>(x2.ptr());
// multiple inheritance shenanigans.
// (allow interface pointers separated by one pointer)
if (u1 >= u2)
return (u1 <= u2 + sizeof(std::uintptr_t));
else
return (u2 <= u1 + sizeof(std::uintptr_t));
}
/** (for consistency's sake) **/
T * get() const { return ptr_; }
T * ptr() const { return ptr_; }
T ** ptr_address() { return &ptr_; }
bool is_null() const { return ptr_ == nullptr; }
void make_null() { ptr_ = nullptr; }
void assign_ptr(T * x) { ptr_ = x; }
gc_ptr & operator=(const gc_ptr & x) { ptr_ = x.ptr(); return *this; }
T * operator->() const { return ptr_; }
T & operator*() const { return *ptr_; }
private:
T * ptr_ = nullptr;
};
/** Root class for all xo GC-collectable objects.
*
* Design note:
@ -92,11 +29,15 @@ namespace xo {
* Would be feasible to relax the must-inherit-from-Object constraint
* by having GC use its own wrapper, at cost of an extra layer of indirection
**/
class Object {
class Object : public IObject {
public:
using TaggedPtr = xo::reflect::TaggedPtr;
public:
static gp<Object> from(gp<IObject> x) {
return dynamic_cast<Object*>(x.ptr());
}
virtual ~Object() = default;
/** memory allocator for objects. Likely this will be a GC instance,
@ -111,7 +52,7 @@ namespace xo {
* add mutation log entry
**/
template <typename T>
static void assign_member(gp<Object> parent, gp<T> * lhs, gp<Object> rhs);
static void assign_member(gp<IObject> parent, gp<T> * lhs, gp<IObject> rhs);
/** use from GC aux functions **/
static gc::GC * _gc() { return reinterpret_cast<gc::GC*>(mm); }
@ -125,11 +66,11 @@ namespace xo {
* @p src. source object to be forwarded
* @p gc. garbage collector
*/
static Object * _forward(Object * src, gc::IAlloc * gc);
static IObject * _forward(IObject * src, gc::IAlloc * gc);
template <typename T>
static void _forward_inplace(T ** src_addr, gc::IAlloc * gc) {
Object * fwd = _forward(*src_addr, gc);
IObject * fwd = _forward(*src_addr, gc);
*src_addr = reinterpret_cast<T *>(fwd);
}
@ -160,12 +101,12 @@ namespace xo {
* @param gc garbage collector
* @param stats per-object-type GC statistics
**/
static Object * _deep_move(Object * src, gc::GC * gc, gc::ObjectStatistics * stats);
static IObject * _deep_move(IObject * src, gc::GC * gc, gc::ObjectStatistics * stats);
/** copy @p src to to-space. Overwrite original with forwarding pointer to new location.
* return the new location
**/
static Object * _shallow_move(Object * src, gc::IAlloc * gc);
static IObject * _shallow_move(IObject * src, gc::IAlloc * gc);
// Reflection support
@ -176,89 +117,25 @@ namespace xo {
/** print on stream @p os **/
virtual void display(std::ostream & os) const = 0;
// GC support
// Inherited from IObject..
/** true iff this object represents a forwarding pointer.
* Forwarding pointers are exclusively created by the garbage collector;
* forwarding pointers (and only forwarding pointers) return true here.
**/
virtual bool _is_forwarded() const { return false; }
//virtual bool _is_forwarded() const override { return false; }
//virtual IObject * _offset_destination(IObject * src) const override { return src; };
virtual void _forward_to(IObject * dest) override;
//virtual IObject * _destination() override { return nullptr; }
/** offset for uncommon situation where pointer address is offset from object
* base address
**/
virtual Object * _offset_destination(Object * src) const { return src; };
/** replace this object with a forwarding pointer referring to @p dest.
**/
virtual void _forward_to(Object * dest);
/** if this object represents a forwarding pointer, return its new location.
* forwarding pointers belong to the garbage collector implementation.
* (if you have to ask -- no, your class is not a forwarding pointer)
* all other objects return nullptr here.
**/
virtual Object * _destination() { return nullptr; }
/** return amount of storage (including padding) consumed by this object,
* excluding immediate Object-pointer children
**/
virtual std::size_t _shallow_size() const = 0;
/** if subject is allocated by GC:
* - create copy C in to-space
* - destination C will be nursery|tenured depending on location of this.
* else
* - return this to disengage from GC
*
* Require: @ref mm is an instance of @ref gc::GC
**/
virtual Object * _shallow_copy(gc::IAlloc * gc) const = 0;
/** update child pointers that refer to forwarding pointers,
* replacing them with the correct destination.
* See @ref Object::deep_move
*
* this gray object, located in to-space.
* fwd1 forwarding objects.
* Located in from-space. Invalid at end of GC cycle.
* p1,p2 source pointers.
* D1,D2 already-forwarded objects. located in to-space.
*
* before:
* this fwd1
* +----+ +-+
* | p1 ----->|x|-------> D1
* | | +-+
* | |
* | p2 ----------------> D2
* +----+
*
* after:
* this
* +----+
* | p1 ----------------> D1
* | |
* | |
* | p2 ----------------> D2
* +----+
*
* this is now white
*
* @return shallow size of *this. Must exactly match the amount of memory in to-space
* allocated by @ref _shallow_move
*
**/
virtual std::size_t _forward_children(gc::IAlloc * gc) = 0;
virtual std::size_t _shallow_size() const override = 0;
virtual IObject * _shallow_copy(gc::IAlloc * gc) const override = 0;
virtual std::size_t _forward_children(gc::IAlloc * gc) override = 0;
};
template <typename T>
void
Object::assign_member(gp<Object> parent, gp<T> * lhs, gp<Object> rhs)
Object::assign_member(gp<IObject> parent, gp<T> * lhs, gp<IObject> rhs)
{
Object::mm->assign_member(parent.ptr(),
reinterpret_cast<Object **>(lhs->ptr_address()),
rhs.ptr());
Object::mm->assign_member(reinterpret_cast<IObject *>(parent.ptr()),
reinterpret_cast<IObject **>(lhs->ptr_address()),
reinterpret_cast<IObject *>(rhs.ptr()));
}
std::ostream &

View file

@ -357,7 +357,7 @@ namespace xo {
}
bool
ArenaAlloc::check_owned(Object * src) const
ArenaAlloc::check_owned(IObject * src) const
{
byte * addr = reinterpret_cast<byte *>(src);

View file

@ -5,7 +5,7 @@
#include "Blob.hpp"
#include "xo/reflect/Reflect.hpp"
#include "xo/alloc/IAlloc.hpp"
#include "xo/allocutil/IAlloc.hpp"
namespace xo {
using xo::reflect::Reflect;

View file

@ -2,7 +2,6 @@
set(SELF_LIB xo_alloc)
set(SELF_SRCS
IAlloc.cpp
ArenaAlloc.cpp
ListAlloc.cpp
GC.cpp
@ -15,6 +14,7 @@ set(SELF_SRCS
)
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
xo_headeronly_dependency(${SELF_LIB} xo_allocutil)
# xo-unit used for time measurement
xo_headeronly_dependency(${SELF_LIB} xo_unit)
xo_dependency(${SELF_LIB} indentlog)

View file

@ -13,7 +13,7 @@ namespace xo {
using xo::reflect::TaggedPtr;
namespace obj {
Forwarding1::Forwarding1(gp<Object> dest)
Forwarding1::Forwarding1(gp<IObject> dest)
: dest_{dest}
{}
@ -26,18 +26,21 @@ namespace xo {
void
Forwarding1::display(std::ostream & os) const
{
os << "<fwd" << xtag("dest-td", dest_->self_tp().td()->short_name()) << ">";
os << "<fwd"
<< xtag("dest", (void*)dest_.ptr())
// << xtag("dest-td", dest_->self_tp().td()->short_name())
<< ">";
}
Object *
Forwarding1::_offset_destination(Object * src) const
IObject *
Forwarding1::_offset_destination(IObject * src) const
{
intptr_t offset = src - static_cast<const Object *>(this);
intptr_t offset = src - static_cast<const IObject *>(this);
return dest_.ptr() + offset;
}
Object *
IObject *
Forwarding1::_destination() {
return dest_.ptr();
}
@ -51,8 +54,10 @@ namespace xo {
// LCOV_EXCL_STOP
// LCOV_EXCL_START
Object *
IObject *
Forwarding1::_shallow_copy(gc::IAlloc *) const {
/* forwarding objects are never copied */
assert(false);
return nullptr;
}
@ -61,6 +66,8 @@ namespace xo {
// LCOV_EXCL_START
std::size_t
Forwarding1::_forward_children(gc::IAlloc *) {
/* forwarding objects are never traced */
assert(false);
return 0;
}

View file

@ -27,7 +27,7 @@ namespace xo {
return parent_->_is_forwarded();
}
Object *
IObject *
MutationLogEntry::parent_destination() const
{
//const bool c_debug_flag = true;
@ -45,7 +45,7 @@ namespace xo {
}
MutationLogEntry
MutationLogEntry::update_parent_moved(Object * parent_to) const
MutationLogEntry::update_parent_moved(IObject * parent_to) const
{
std::byte * parent_from = reinterpret_cast<std::byte *>(parent_);
std::byte * lhs_from = reinterpret_cast<std::byte *>(lhs_);
@ -55,7 +55,7 @@ namespace xo {
std::byte * lhs_to = reinterpret_cast<std::byte *>(parent_to) + offset;
return MutationLogEntry(parent_to,
reinterpret_cast<Object **>(lhs_to));
reinterpret_cast<IObject **>(lhs_to));
}
GC::GC(const Config & config)
@ -395,13 +395,13 @@ namespace xo {
}
void
GC::add_gc_root(Object ** addr)
GC::add_gc_root(IObject ** addr)
{
gc_root_v_.push_back(addr);
}
void
GC::remove_gc_root(Object ** addr)
GC::remove_gc_root(IObject ** addr)
{
/* Multithreaded GC not supported */
@ -450,7 +450,9 @@ namespace xo {
std::byte *
GC::alloc_gc_copy(std::size_t z, const void * src)
{
scope log(XO_DEBUG(config_.debug_flag_), xtag("z", z), xtag("+pad", IAlloc::alloc_padding(z)));
scope log(XO_DEBUG(config_.debug_flag_),
xtag("z", z),
xtag("+pad", IAlloc::alloc_padding(z)));
generation_result src_gr = this->fromspace_generation_of(src);
@ -483,7 +485,8 @@ namespace xo {
gc_copy_cbset_.invoke(&GcCopyCallback::notify_gc_copy,
z, src, retval, generation::nursery, generation::tenured);
this->gc_statistics_.total_promoted_ += IAlloc::with_padding(z);
this->gc_statistics_.total_promoted_
+= IAlloc::with_padding(z);
} else {
log && log("nursery");
@ -509,7 +512,7 @@ namespace xo {
}
void
GC::assign_member(Object * parent, Object ** lhs, Object * rhs)
GC::assign_member(IObject * parent, IObject ** lhs, IObject * rhs)
{
++gc_statistics_.n_mutation_;
@ -566,13 +569,13 @@ namespace xo {
}
bool
GC::check_owned(Object * src) const
GC::check_owned(IObject * src) const
{
return this->fromspace_contains(src);
}
bool
GC::check_move(Object * src) const
GC::check_move(IObject * src) const
{
return (this->runstate().full_move()
|| (this->tospace_generation_of(src) != gc::generation_result::tenured));
@ -682,7 +685,9 @@ namespace xo {
}
void
GC::copy_object(Object ** pp_object, generation upto, ObjectStatistics * object_stats)
GC::copy_object(IObject ** pp_object,
generation upto,
ObjectStatistics * object_stats)
{
void * object_address = *pp_object;
@ -707,7 +712,7 @@ namespace xo {
scope log(XO_DEBUG(config_.debug_flag_),
xtag("roots", gc_root_v_.size()));
for (Object ** pp_root : gc_root_v_) {
for (IObject ** pp_root : gc_root_v_) {
this->copy_object(pp_root, upto,
&object_statistics_sae_[gen2int(upto)]);
}
@ -778,7 +783,7 @@ namespace xo {
// obsolete mutation -- no longer belongs to parent, discard
} else {
// note: child obtained (as it must be) by reading from parent's memory _now_.
Object * child_from = from_entry.child();
IObject * child_from = from_entry.child();
if (child_from) {
if (!child_from->_is_forwarded()) {
@ -813,7 +818,7 @@ namespace xo {
// P->C, C moved to C'
// Includes cases (a),(c) from above
Object * child_to = child_from->_destination();
IObject * child_to = child_from->_destination();
from_entry.fixup_parent_child_moved(child_to);
@ -843,7 +848,7 @@ namespace xo {
// follows that loc(P') = T
// already have P'->C' when parent moved separately
Object * parent_to = from_entry.parent_destination();
IObject * parent_to = from_entry.parent_destination();
log && log(xtag("parent_to", (void*)parent_to));
@ -851,7 +856,7 @@ namespace xo {
MutationLogEntry to_entry = from_entry.update_parent_moved(parent_to);
Object * child_to = to_entry.child(); // after moving
IObject * child_to = to_entry.child(); // after moving
if (tospace_generation_of(child_to) == generation_result::nursery) {
if (to_entry.is_dead()) {
@ -954,7 +959,7 @@ namespace xo {
log && (i_from % 10000 == 0) && log(xtag("i_from", i_from));
if (from_entry.is_parent_forwarded()) {
Object * parent_to = from_entry.parent_destination();
IObject * parent_to = from_entry.parent_destination();
log && log(xtag("parent_to", (void*)parent_to));
@ -964,7 +969,7 @@ namespace xo {
// note: child obtained (as it must be) by reading from prarent's memory _now_.
// Since parent has moved, child has too
Object * child_to = to_entry.child(); // after moveing
IObject * child_to = to_entry.child(); // after moveing
if (tospace_generation_of(parent_to) == generation_result::tenured)
{

View file

@ -1,74 +0,0 @@
/* @file IAlloc.cpp
*
* author: Roland Conybeare, Aug 2025
*/
#include "IAlloc.hpp"
#include <cassert>
#include <cstddef>
namespace xo {
namespace gc {
std::uint32_t
IAlloc::alloc_padding(std::size_t z)
{
/* word size for alignment */
constexpr uint32_t c_bpw = sizeof(std::uintptr_t);
/* round up to multiple of c_bpw, but map 0 -> 0
* (table assuming c_bpw==8)
*
* z%c_bpw dz
* ------------
* 0 0
* 1 7
* 2 6
* .. ..
* 7 1
*/
std::uint32_t dz = (c_bpw - (z % c_bpw)) % c_bpw;
z += dz;
assert(z % c_bpw == 0ul);
return dz;
}
std::size_t
IAlloc::with_padding(std::size_t z)
{
return z + alloc_padding(z);
}
void
IAlloc::assign_member(Object * /*parent*/, Object ** lhs, Object * rhs)
{
*lhs = rhs;
}
bool
IAlloc::check_owned(Object * /*obj*/) const
{
return false;
}
bool
IAlloc::check_move(Object * /*obj*/) const
{
return false;
}
// LCOV_EXCL_START
std::byte *
IAlloc::alloc_gc_copy(std::size_t /*z*/, const void * /*src*/)
{
assert(false);
return nullptr;
}
// LCOV_EXCL_STOP
} /*namespace gc*/
} /*namespace xo*/
/* end IAlloc.cpp */

View file

@ -23,8 +23,8 @@ namespace xo {
gc::IAlloc *
Object::mm = nullptr;
Object *
Object::_forward(Object * src, gc::IAlloc * gc)
IObject *
Object::_forward(IObject * src, gc::IAlloc * gc)
{
if (!src)
return src;
@ -43,15 +43,15 @@ namespace xo {
}
}
Object *
Object::_deep_move(Object * from_src, gc::GC * gc, gc::ObjectStatistics * /*stats*/)
IObject *
Object::_deep_move(IObject * from_src, gc::GC * gc, gc::ObjectStatistics * /*stats*/)
{
using gc::generation;
if (!from_src)
return nullptr;
Object * retval = from_src->_destination();
IObject * retval = from_src->_destination();
if (retval)
return retval;
@ -124,7 +124,7 @@ namespace xo {
std::array<std::byte *, gen2int(generation::N)> gray_lo_v
= { gc->free_ptr(generation::nursery), gc->free_ptr(generation::tenured) };
Object * to_src = Object::_shallow_move(from_src, gc);
IObject * to_src = Object::_shallow_move(from_src, gc);
std::size_t fixup_work = 0;
do {
@ -158,15 +158,15 @@ namespace xo {
return to_src;
} /*deep_move*/
Object *
Object::_shallow_move(Object * src, gc::IAlloc * gc)
IObject *
Object::_shallow_move(IObject * src, gc::IAlloc * gc)
{
/* filter for source objects that are owned by GC.
* Care required though -- during GC from/to spaces have been swapped already
*/
if (gc->check_owned(src))
{
Object * dest = src->_shallow_copy(gc);
IObject * dest = src->_shallow_copy(gc);
if (dest != src)
src->_forward_to(dest);
@ -178,7 +178,7 @@ namespace xo {
}
void
Object::_forward_to(Object * dest)
Object::_forward_to(IObject * dest)
{
char * mem = reinterpret_cast<char *>(this);