diff --git a/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp b/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp index 74c1c6ba..2fd71285 100644 --- a/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp +++ b/xo-alloc2/include/xo/alloc2/alloc/AAllocator.hpp @@ -8,7 +8,6 @@ #include #include #include "AllocInfo.hpp" -//#include "AllocIterator.hpp" #include "AllocRange.hpp" #include "typeseq.hpp" #include diff --git a/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp b/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp index 72d3802e..32ee2b2f 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ACollector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ACollector.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 * p_lhs, + obj & 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 * diff --git a/xo-alloc2/include/xo/alloc2/gc/AGCObject.hpp b/xo-alloc2/include/xo/alloc2/gc/AGCObject.hpp index c420f0ff..1037f253 100644 --- a/xo-alloc2/include/xo/alloc2/gc/AGCObject.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/AGCObject.hpp @@ -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 will have non-null vtable (via IFacet_Any) with all methods terminating. + **/ + bool _has_null_vptr() const noexcept { return *reinterpret_cast(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 **/ diff --git a/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp b/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp index 555adc6e..dbff1109 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ICollector_Any.hpp @@ -41,6 +41,8 @@ namespace xo { [[noreturn]] void add_gc_root_poly(Opaque, obj *) override { _fatal(); } [[noreturn]] void remove_gc_root_poly(Opaque, obj *) override { _fatal(); } [[noreturn]] void request_gc(Opaque, generation) override { _fatal(); } + [[noreturn]] void assign_member(Opaque, void *, + obj *, obj &) override { _fatal(); } [[noreturn]] void forward_inplace(Opaque, AGCObject *, void **) override { _fatal(); } private: diff --git a/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp b/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp index 4100c8c5..dbb6655d 100644 --- a/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/ICollector_Xfer.hpp @@ -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 * p_lhs, obj & 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); diff --git a/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp b/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp index b9d3e3ed..f62c02fb 100644 --- a/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/RCollector.hpp @@ -51,6 +51,11 @@ namespace xo { void add_gc_root_poly(obj * p_root) { O::iface()->add_gc_root_poly(O::data(), p_root); } void remove_gc_root_poly(obj * 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 * p_lhs, + obj & 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 **/ diff --git a/xo-alloc2/include/xo/alloc2/gc/RCollector_aux.hpp b/xo-alloc2/include/xo/alloc2/gc/RCollector_aux.hpp index 5e560358..4fc59b97 100644 --- a/xo-alloc2/include/xo/alloc2/gc/RCollector_aux.hpp +++ b/xo-alloc2/include/xo/alloc2/gc/RCollector_aux.hpp @@ -43,9 +43,27 @@ namespace xo { void RCollector::forward_pivot_inplace(obj * p_objs) { - auto e = xo::facet::FacetRegistry::instance().variant(*p_objs); - this->forward_inplace(e.iface(), (void **)&(p_objs->data_)); + if (*p_objs) { + auto e = xo::facet::FacetRegistry::instance().variant(*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 & gc, + void * parent, + obj * p_lhs, + obj & 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; + } + }; } } diff --git a/xo-alloc2/include/xo/alloc2/generation.hpp b/xo-alloc2/include/xo/alloc2/generation.hpp index 12b36d06..08c85322 100644 --- a/xo-alloc2/include/xo/alloc2/generation.hpp +++ b/xo-alloc2/include/xo/alloc2/generation.hpp @@ -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_; + } + } } diff --git a/xo-alloc2/include/xo/alloc2/visitor/AResourceVisitor.hpp b/xo-alloc2/include/xo/alloc2/visitor/AResourceVisitor.hpp index c8ab3744..64d9b136 100644 --- a/xo-alloc2/include/xo/alloc2/visitor/AResourceVisitor.hpp +++ b/xo-alloc2/include/xo/alloc2/visitor/AResourceVisitor.hpp @@ -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 will have non-null vtable (via IFacet_Any) with all methods terminating. + **/ + bool _has_null_vptr() const noexcept { return *reinterpret_cast(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 **/ diff --git a/xo-arena/include/xo/arena/AllocHeaderConfig.hpp b/xo-arena/include/xo/arena/AllocHeaderConfig.hpp index ab560886..d2b6db99 100644 --- a/xo-arena/include/xo/arena/AllocHeaderConfig.hpp +++ b/xo-arena/include/xo/arena/AllocHeaderConfig.hpp @@ -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; diff --git a/xo-arena/include/xo/arena/DArena.hpp b/xo-arena/include/xo/arena/DArena.hpp index 83ebd9c3..73ca67db 100644 --- a/xo-arena/include/xo/arena/DArena.hpp +++ b/xo-arena/include/xo/arena/DArena.hpp @@ -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 * diff --git a/xo-arena/include/xo/arena/DArenaHashMap.hpp b/xo-arena/include/xo/arena/DArenaHashMap.hpp index 50e66698..c97f7bad 100644 --- a/xo-arena/include/xo/arena/DArenaHashMap.hpp +++ b/xo-arena/include/xo/arena/DArenaHashMap.hpp @@ -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_; } diff --git a/xo-arena/include/xo/arena/DArenaVector.hpp b/xo-arena/include/xo/arena/DArenaVector.hpp index 79653fe0..0ac5d689 100644 --- a/xo-arena/include/xo/arena/DArenaVector.hpp +++ b/xo-arena/include/xo/arena/DArenaVector.hpp @@ -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(store_.lo_); } constexpr const T * data() const { return reinterpret_cast(store_.lo_); } @@ -223,7 +224,7 @@ namespace xo { // store_.restore(zero_ckp_); store_.alloc(xo::reflect::typeseq::id(), req_z); - + this->size_ = z; } diff --git a/xo-arena/src/arena/DArena.cpp b/xo-arena/src/arena/DArena.cpp index 2cd36dc1..e5bbb112 100644 --- a/xo-arena/src/arena/DArena.cpp +++ b/xo-arena/src/arena/DArena.cpp @@ -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 **/ diff --git a/xo-expression2/include/xo/expression2/DGlobalSymtab.hpp b/xo-expression2/include/xo/expression2/DGlobalSymtab.hpp index 5fe1edb3..2f8b1e14 100644 --- a/xo-expression2/include/xo/expression2/DGlobalSymtab.hpp +++ b/xo-expression2/include/xo/expression2/DGlobalSymtab.hpp @@ -31,6 +31,7 @@ namespace xo { using repr_type = xo::map::DArenaHashMap; 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 make(obj mm, - obj fixed_mm, - const ArenaHashMapConfig & var_cfg, - const ArenaHashMapConfig & type_cfg); + static DGlobalSymtab * _make(obj mm, + obj fixed_mm, + const ArenaHashMapConfig & var_cfg, + const ArenaHashMapConfig & type_cfg); + + /** like _make(..), but create fop **/ + static obj make(obj mm, + obj fixed_mm, + const ArenaHashMapConfig & var_cfg, + const ArenaHashMapConfig & type_cfg); /** non-trivial destructor for @ref map_ **/ ~DGlobalSymtab() = default; diff --git a/xo-expression2/src/expression2/DGlobalSymtab.cpp b/xo-expression2/src/expression2/DGlobalSymtab.cpp index 65eea5a6..158326ac 100644 --- a/xo-expression2/src/expression2/DGlobalSymtab.cpp +++ b/xo-expression2/src/expression2/DGlobalSymtab.cpp @@ -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::make(obj mm, - obj aux_mm, - const ArenaHashMapConfig & var_cfg, - const ArenaHashMapConfig & type_cfg) + DGlobalSymtab * + DGlobalSymtab::_make(obj mm, + obj 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::make(mm, - std::move(var_map), vars, - std::move(type_map), types); + void * mem = mm.alloc_for(); + + auto symtab = new (mem) DGlobalSymtab(std::move(var_map), + vars, + std::move(type_map), + types); assert(symtab); return symtab; } + obj + DGlobalSymtab::make(obj mm, + obj aux_mm, + const ArenaHashMapConfig & var_cfg, + const ArenaHashMapConfig & type_cfg) + { + return obj(_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_);