xo-gc: + ACollector.assign_member()
This commit is contained in:
parent
80179c3f5d
commit
5909e9797f
16 changed files with 128 additions and 20 deletions
|
|
@ -8,7 +8,6 @@
|
|||
#include <xo/arena/MemorySizeInfo.hpp>
|
||||
#include <xo/arena/AllocError.hpp>
|
||||
#include "AllocInfo.hpp"
|
||||
//#include "AllocIterator.hpp"
|
||||
#include "AllocRange.hpp"
|
||||
#include "typeseq.hpp"
|
||||
#include <xo/facet/obj.hpp>
|
||||
|
|
|
|||
|
|
@ -76,6 +76,15 @@ namespace xo {
|
|||
**/
|
||||
virtual void request_gc(Opaque d, generation upto) = 0;
|
||||
|
||||
/** Assign pointer @p p_lhs to destination @p rhs, within parent allocation @p parent
|
||||
*
|
||||
* Require: gc not in progress
|
||||
**/
|
||||
virtual void assign_member(Opaque d,
|
||||
void * parent,
|
||||
obj<AGCObject> * p_lhs,
|
||||
obj<AGCObject> & rhs) = 0;
|
||||
|
||||
/** evacuate @p *lhs, that refers to state with interface @p lhs_iface,
|
||||
* to collector @p d's to-space. Replace *lhs_data with forwarding pointer
|
||||
*
|
||||
|
|
|
|||
|
|
@ -53,6 +53,11 @@ public:
|
|||
/** @defgroup mm-gcobject-methods **/
|
||||
///@{
|
||||
// const methods
|
||||
/** An uninitialized AGCObject instance will have zero vtable pointer (per {linux,osx} abi).
|
||||
* Use case for this is narrow. We go to some lengths to avoid null vtable pointers. For example
|
||||
* obj<AFacet> will have non-null vtable (via IFacet_Any) with all methods terminating.
|
||||
**/
|
||||
bool _has_null_vptr() const noexcept { return *reinterpret_cast<const void * const *>(this) == nullptr; }
|
||||
/** RTTI: unique id# for actual runtime data representation **/
|
||||
virtual typeseq _typeseq() const noexcept = 0;
|
||||
/** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ namespace xo {
|
|||
[[noreturn]] void add_gc_root_poly(Opaque, obj<AGCObject> *) override { _fatal(); }
|
||||
[[noreturn]] void remove_gc_root_poly(Opaque, obj<AGCObject> *) override { _fatal(); }
|
||||
[[noreturn]] void request_gc(Opaque, generation) override { _fatal(); }
|
||||
[[noreturn]] void assign_member(Opaque, void *,
|
||||
obj<AGCObject> *, obj<AGCObject> &) override { _fatal(); }
|
||||
[[noreturn]] void forward_inplace(Opaque, AGCObject *, void **) override { _fatal(); }
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -59,6 +59,10 @@ namespace xo {
|
|||
void request_gc(Opaque d, generation upto) override {
|
||||
I::request_gc(_dcast(d), upto);
|
||||
}
|
||||
void assign_member(Opaque d, void * parent,
|
||||
obj<AGCObject> * p_lhs, obj<AGCObject> & rhs) override {
|
||||
I::assign_member(_dcast(d), parent, p_lhs, rhs);
|
||||
}
|
||||
void forward_inplace(Opaque d,
|
||||
AGCObject * lhs_iface, void ** lhs_data) override {
|
||||
I::forward_inplace(_dcast(d), lhs_iface, lhs_data);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,11 @@ namespace xo {
|
|||
void add_gc_root_poly(obj<AGCObject> * p_root) { O::iface()->add_gc_root_poly(O::data(), p_root); }
|
||||
void remove_gc_root_poly(obj<AGCObject> * p_root) { O::iface()->remove_gc_root_poly(O::data(), p_root); }
|
||||
void request_gc(generation g) { O::iface()->request_gc(O::data(), g); }
|
||||
|
||||
void assign_member(void * parent,
|
||||
obj<AGCObject> * p_lhs,
|
||||
obj<AGCObject> & rhs) { O::iface()->assign_member(O::data(), parent, p_lhs, rhs); }
|
||||
|
||||
void forward_inplace(AGCObject * lhs_iface, void ** lhs_data) { O::iface()->forward_inplace(O::data(), lhs_iface, lhs_data); }
|
||||
|
||||
/** add root @p p_root **/
|
||||
|
|
|
|||
|
|
@ -43,9 +43,27 @@ namespace xo {
|
|||
void
|
||||
RCollector<Object>::forward_pivot_inplace(obj<AFacet, DRepr> * p_objs)
|
||||
{
|
||||
auto e = xo::facet::FacetRegistry::instance().variant<AGCObject,AFacet>(*p_objs);
|
||||
this->forward_inplace(e.iface(), (void **)&(p_objs->data_));
|
||||
if (*p_objs) {
|
||||
auto e = xo::facet::FacetRegistry::instance().variant<AGCObject,AFacet>(*p_objs);
|
||||
this->forward_inplace(e.iface(), (void **)&(p_objs->data_));
|
||||
}
|
||||
}
|
||||
|
||||
/** gc-aware assignment; engage special book-keeping for cross-gen pointers **/
|
||||
inline void mm_do_assign(obj<ACollector> & gc,
|
||||
void * parent,
|
||||
obj<AGCObject> * p_lhs,
|
||||
obj<AGCObject> & rhs)
|
||||
{
|
||||
if (gc.data()) {
|
||||
gc.assign_member(parent, p_lhs, rhs);
|
||||
} else {
|
||||
// assume null collector downstream from allocator that does not provide collection.
|
||||
// In that no additional assignment work.
|
||||
|
||||
*p_lhs = rhs;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ namespace xo {
|
|||
explicit constexpr generation(value_type x) : value_{x} {}
|
||||
|
||||
static generation nursery() { return generation{0}; }
|
||||
static generation sentinel() { return generation(c_max_generation); }
|
||||
|
||||
bool is_sentinel() const noexcept { return value_ == c_max_generation; }
|
||||
|
||||
constexpr operator value_type() const { return value_; }
|
||||
|
||||
|
|
@ -30,6 +33,19 @@ namespace xo {
|
|||
|
||||
std::uint32_t value_ = 0;
|
||||
};
|
||||
|
||||
inline bool operator==(generation lhs, generation rhs) {
|
||||
return lhs.value_ == rhs.value_;
|
||||
}
|
||||
|
||||
inline bool operator<(generation lhs, generation rhs) {
|
||||
return lhs.value_ < rhs.value_;
|
||||
}
|
||||
|
||||
inline bool operator>(generation lhs, generation rhs) {
|
||||
return lhs.value_ > rhs.value_;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -46,6 +46,11 @@ public:
|
|||
/** @defgroup mm-resourcevisitor-methods **/
|
||||
///@{
|
||||
// const methods
|
||||
/** An uninitialized AResourceVisitor instance will have zero vtable pointer (per {linux,osx} abi).
|
||||
* Use case for this is narrow. We go to some lengths to avoid null vtable pointers. For example
|
||||
* obj<AFacet> will have non-null vtable (via IFacet_Any) with all methods terminating.
|
||||
**/
|
||||
bool _has_null_vptr() const noexcept { return *reinterpret_cast<const void * const *>(this) == nullptr; }
|
||||
/** RTTI: unique id# for actual runtime data representation **/
|
||||
virtual typeseq _typeseq() const noexcept = 0;
|
||||
/** destroy instance @p d; calls c++ dtor only for actual runtime type; does not recover memory **/
|
||||
|
|
|
|||
|
|
@ -11,7 +11,9 @@
|
|||
|
||||
namespace xo {
|
||||
namespace mm {
|
||||
/*
|
||||
/**
|
||||
* @brief specifies alloc header layout
|
||||
*
|
||||
* Each allocation is preceded by a 64-bit header.
|
||||
* Header is split into 3 configurable-width bit fields,
|
||||
* labelled (from hi to lo bit order) {tseq, age, size}.
|
||||
|
|
@ -36,7 +38,7 @@ namespace xo {
|
|||
* 0..............01111111 gen_mask_unshifted
|
||||
* 0..011111110..........0 gen_mask_shifted
|
||||
* > < gen_shift
|
||||
*/
|
||||
**/
|
||||
struct AllocHeaderConfig {
|
||||
using repr_type = AllocHeader;
|
||||
using span_type = std::pair<const std::byte *, const std::byte *>;
|
||||
|
|
|
|||
|
|
@ -103,6 +103,9 @@ namespace xo {
|
|||
/** @defgroup mm-arena-methods **/
|
||||
///@{
|
||||
|
||||
/** false -> not eligible for GC (allocates own memory + not moveable) **/
|
||||
static constexpr bool is_gc_eligible() { return false; }
|
||||
|
||||
/** Reserved memory, in bytes. This is the maximum size of this arena. **/
|
||||
size_type reserved() const noexcept { return hi_ - lo_; }
|
||||
/** Allocated memory in bytes: memory consumed by allocs from this arena,
|
||||
|
|
@ -125,6 +128,9 @@ namespace xo {
|
|||
**/
|
||||
bool contains(const void * addr) const noexcept { return (lo_ <= addr) && (addr < hi_); }
|
||||
|
||||
/** Truee iff address @p addr is owned by this arena and in allocated regions **/
|
||||
bool contains_allocated(const void * addr) const noexcept { return (lo_ <= addr) && (addr < free_); }
|
||||
|
||||
/** true if arena is mapped i.e. has a reserved address range **/
|
||||
bool is_mapped() const noexcept { return (lo_ != nullptr) && (hi_ != nullptr); }
|
||||
|
||||
|
|
@ -147,6 +153,8 @@ namespace xo {
|
|||
|
||||
/** get header from allocated object address **/
|
||||
header_type * obj2hdr(void * obj) noexcept;
|
||||
/** get header from allocated object address (const version) **/
|
||||
const header_type * obj2hdr(void * obj) const noexcept;
|
||||
|
||||
/** report alloc book-keeping info for allocation at @p mem
|
||||
*
|
||||
|
|
|
|||
|
|
@ -68,6 +68,9 @@ namespace xo {
|
|||
size_type hint_max_capacity = 0,
|
||||
bool debug_flag = false);
|
||||
|
||||
/** true for types that support the AGCObject facet; DArenaHashMap gets its own memory! **/
|
||||
static constexpr bool is_gc_eligible() { return false; }
|
||||
|
||||
size_type empty() const noexcept { return store_.empty(); }
|
||||
size_type groups() const noexcept { return store_.n_group_; }
|
||||
size_type size() const noexcept { return store_.size_; }
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@ namespace xo {
|
|||
const_iterator cend() const noexcept { return this->_address_of(size_); }
|
||||
const_iterator end() const noexcept { return this->cend(); }
|
||||
|
||||
constexpr const DArena * store() const { return &store_; }
|
||||
constexpr T * data() { return reinterpret_cast<T*>(store_.lo_); }
|
||||
constexpr const T * data() const { return reinterpret_cast<const T*>(store_.lo_); }
|
||||
|
||||
|
|
@ -223,7 +224,7 @@ namespace xo {
|
|||
//
|
||||
store_.restore(zero_ckp_);
|
||||
store_.alloc(xo::reflect::typeseq::id<std::byte>(), req_z);
|
||||
|
||||
|
||||
this->size_ = z;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,8 +170,17 @@ namespace xo {
|
|||
return (header_type *)((byte *)obj - sizeof(header_type));
|
||||
}
|
||||
|
||||
auto
|
||||
DArena::obj2hdr(void * obj) const noexcept -> const header_type *
|
||||
{
|
||||
assert(config_.store_header_flag_);
|
||||
|
||||
return (const header_type *)((byte *)obj - sizeof(header_type));
|
||||
}
|
||||
|
||||
void
|
||||
DArena::visit_pools(const MemorySizeVisitor & fn) const {
|
||||
DArena::visit_pools(const MemorySizeVisitor & fn) const
|
||||
{
|
||||
/** arena can't tell purpose of allocated memory;
|
||||
* must assume it's all used
|
||||
**/
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ namespace xo {
|
|||
using repr_type = xo::map::DArenaHashMap<key_type, Binding::slot_type>;
|
||||
using ACollector = xo::mm::ACollector;
|
||||
using AAllocator = xo::mm::AAllocator;
|
||||
using AGCObject = xo::mm::AGCObject;
|
||||
using MemorySizeVisitor = xo::mm::MemorySizeVisitor;
|
||||
using ppindentinfo = xo::print::ppindentinfo;
|
||||
using size_type = std::uint32_t;
|
||||
|
|
@ -47,10 +48,16 @@ namespace xo {
|
|||
* Use memory from @p mm for DGlobalSymtab instance.
|
||||
* Hashmap for variables per @p var_cfg; for types per @p type_cfg.
|
||||
**/
|
||||
static dp<DGlobalSymtab> make(obj<AAllocator> mm,
|
||||
obj<AAllocator> fixed_mm,
|
||||
const ArenaHashMapConfig & var_cfg,
|
||||
const ArenaHashMapConfig & type_cfg);
|
||||
static DGlobalSymtab * _make(obj<AAllocator> mm,
|
||||
obj<AAllocator> fixed_mm,
|
||||
const ArenaHashMapConfig & var_cfg,
|
||||
const ArenaHashMapConfig & type_cfg);
|
||||
|
||||
/** like _make(..), but create fop **/
|
||||
static obj<AGCObject,DGlobalSymtab> make(obj<AAllocator> mm,
|
||||
obj<AAllocator> fixed_mm,
|
||||
const ArenaHashMapConfig & var_cfg,
|
||||
const ArenaHashMapConfig & type_cfg);
|
||||
|
||||
/** non-trivial destructor for @ref map_ **/
|
||||
~DGlobalSymtab() = default;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* @author Roland Conybeare, Jan 2026
|
||||
**/
|
||||
|
||||
#include "DGlobalSymtab.hpp"
|
||||
#include "GlobalSymtab.hpp"
|
||||
#include "Typename.hpp"
|
||||
#include "Binding.hpp"
|
||||
#include "DUniqueString.hpp"
|
||||
|
|
@ -29,11 +29,11 @@ namespace xo {
|
|||
{
|
||||
}
|
||||
|
||||
dp<DGlobalSymtab>
|
||||
DGlobalSymtab::make(obj<AAllocator> mm,
|
||||
obj<AAllocator> aux_mm,
|
||||
const ArenaHashMapConfig & var_cfg,
|
||||
const ArenaHashMapConfig & type_cfg)
|
||||
DGlobalSymtab *
|
||||
DGlobalSymtab::_make(obj<AAllocator> mm,
|
||||
obj<AAllocator> aux_mm,
|
||||
const ArenaHashMapConfig & var_cfg,
|
||||
const ArenaHashMapConfig & type_cfg)
|
||||
{
|
||||
/* note: using aux_mm for DArenaHashMap superstructure.
|
||||
* {variable, type} storage allocated from mm.
|
||||
|
|
@ -51,14 +51,26 @@ namespace xo {
|
|||
|
||||
DArray * types = DArray::empty(mm, type_map->capacity());
|
||||
|
||||
auto symtab = dp<DGlobalSymtab>::make(mm,
|
||||
std::move(var_map), vars,
|
||||
std::move(type_map), types);
|
||||
void * mem = mm.alloc_for<DGlobalSymtab>();
|
||||
|
||||
auto symtab = new (mem) DGlobalSymtab(std::move(var_map),
|
||||
vars,
|
||||
std::move(type_map),
|
||||
types);
|
||||
assert(symtab);
|
||||
|
||||
return symtab;
|
||||
}
|
||||
|
||||
obj<AGCObject,DGlobalSymtab>
|
||||
DGlobalSymtab::make(obj<AAllocator> mm,
|
||||
obj<AAllocator> aux_mm,
|
||||
const ArenaHashMapConfig & var_cfg,
|
||||
const ArenaHashMapConfig & type_cfg)
|
||||
{
|
||||
return obj<AGCObject,DGlobalSymtab>(_make(mm, aux_mm, var_cfg, type_cfg));
|
||||
}
|
||||
|
||||
void
|
||||
DGlobalSymtab::visit_pools(const MemorySizeVisitor & visitor) const
|
||||
{
|
||||
|
|
@ -275,6 +287,9 @@ namespace xo {
|
|||
{
|
||||
// map_ doesn't contain any gc-owned data, can skip
|
||||
|
||||
static_assert(var_map_.is_gc_eligible() == false);
|
||||
static_assert(type_map_.is_gc_eligible() == false);
|
||||
|
||||
gc.forward_inplace(&vars_);
|
||||
gc.forward_inplace(&types_);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue