xo-interpreter adds + explict mm arg to ctors (retiring Object::mm)

This commit is contained in:
Roland Conybeare 2025-11-16 20:10:23 -05:00
commit f3e7330d92
38 changed files with 664 additions and 103 deletions

View file

@ -116,6 +116,7 @@ add_subdirectory(xo-expression)
add_subdirectory(xo-pyexpression)
add_subdirectory(xo-tokenizer)
add_subdirectory(xo-reader)
add_subdirectory(xo-interpreter)
add_subdirectory(xo-jit)
add_subdirectory(xo-pyjit)
#
@ -124,6 +125,6 @@ add_subdirectory(xo-imgui)
# ----------------------------------------------------------------
# documentation. must follow add_subdirectory() for satellite projects
xo_umbrella_doxygen_deps(xo_alloc indentlog xo_flatstring xo_ratio xo_unit xo_tokenizer xo_reader xo_jit)
xo_umbrella_doxygen_deps(xo_alloc indentlog xo_flatstring xo_ratio xo_unit xo_tokenizer xo_reader xo_interpreter xo_jit)
xo_umbrella_doxygen_config()
xo_umbrella_sphinx_config(index.rst docs/install.rst docs/glossary.rst)

View file

@ -177,15 +177,31 @@ To setup a unit test coverage build/ccov/all-merged
Then run unit tests
.. code-block::
$ (cd .build && ctest)
To build coverage report
.. code-block::
$ (.build/gen-ccov)
Html report in ``.build/ccov/html/index.html``
Address Sanitizer Setup
-----------------------
To run address sanitizer
.. code-block::
# can reuse phase 1 cmake-macros-install
# phase 2
$ cmake -B .build -S . -DCMAKE_INSTALL_PREFIX=$PREFIX -DCMAKE_BUILD_TYPE=asan
Sphinx Autobuild Setup
----------------------

View file

@ -54,6 +54,8 @@ namespace xo {
bool allow_incremental_gc_ = true;
/** true to report statistics **/
bool stats_flag_ = false;
/** true to capture per-type object statistics **/
bool object_stats_flag_ = false;
/** remember basic gc statistics for this many GC's; separately for incremental + full GCs **/
std::size_t stats_history_z_ = 256;
/** true to enable debug logging **/

View file

@ -258,8 +258,9 @@ namespace xo {
**/
class Cpof {
public:
explicit Cpof(const Object * src) : src_{src} {}
explicit Cpof(gc::IAlloc * mm, const Object * src) : mm_{mm}, src_{src} {}
gc::IAlloc * mm_ = nullptr;
const void * src_ = nullptr;
};
} /*namespace xo*/

View file

@ -26,6 +26,11 @@ namespace xo {
void * base = mmap(nullptr, z, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
log && log("acquired memory [lo,hi) using mmap",
xtag("lo", base),
xtag("z", z),
xtag("hi", reinterpret_cast<std::byte *>(base) + z));
// could use this as fallback..
//base = (new std::byte [z]);
@ -39,7 +44,7 @@ namespace xo {
this->checkpoint_ = lo_;
this->free_ptr_ = lo_;
this->limit_ = lo_ + z;
this->hi_ = limit_;
this->hi_ = lo_ + z;
this->debug_flag_ = debug_flag;
if (!lo_) {
@ -52,22 +57,25 @@ namespace xo {
ArenaAlloc::~ArenaAlloc()
{
scope log(XO_DEBUG(debug_flag_));
// hygiene..
if (lo_) {
log && log("unmap [lo,hi)", xtag("lo", lo_), xtag("z", hi_ - lo_), xtag("hi", hi_));
munmap(lo_, hi_ - lo_);
}
// could use this as fallback if not using uncommitted technique
// could use this as fallback if we dropped the uncommitted technique
//delete [] this->lo_;
this->lo_ = nullptr;
this->lo_ = nullptr;
this->committed_z_ = 0;
this->checkpoint_ = nullptr;
this->free_ptr_ = nullptr;
this->limit_ = nullptr;
this->hi_ = nullptr;
this->debug_flag_ = false;
this->checkpoint_ = nullptr;
this->free_ptr_ = nullptr;
this->limit_ = nullptr;
this->hi_ = nullptr;
this->debug_flag_ = false;
}
up<ArenaAlloc>
@ -94,26 +102,41 @@ namespace xo {
}
bool
ArenaAlloc::expand(size_t offset_z) {
ArenaAlloc::expand(size_t offset_z)
{
scope log(XO_DEBUG(debug_flag_), xtag("offset_z", offset_z), xtag("committed_z", committed_z_));
if (offset_z <= committed_z_)
if (offset_z <= committed_z_) {
log && log("trivial success, offset within committed range",
xtag("offset_z", offset_z),
xtag("committed_z", committed_z_));
return true;
std::size_t align_offset_z = align_lub(offset_z, page_z_);
std::byte * commit_start = lo_ + committed_z_;
std::size_t new_commit_z = align_offset_z - committed_z_;
log && log(xtag("align_offset_z", align_offset_z),
xtag("new_commit_z", new_commit_z));
if (mprotect(commit_start, new_commit_z, PROT_READ | PROT_WRITE) != 0) {
throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure",
xtag("committed_z", committed_z_),
xtag("new_commit_z", new_commit_z)));
}
this->committed_z_ = align_offset_z;
if (lo_ + offset_z > limit_) {
throw std::runtime_error(tostr("ArenaAlloc::expand: requested size exceeds reserved size",
xtag("requested", offset_z), xtag("reserved", reserved())));
}
std::size_t aligned_offset_z = align_lub(offset_z, page_z_);
std::byte * commit_start = lo_ + committed_z_;
std::size_t add_commit_z = aligned_offset_z - committed_z_;
log && log(xtag("aligned_offset_z", aligned_offset_z),
xtag("add_commit_z", add_commit_z));
log && log("expand committed range",
xtag("commit_start", commit_start),
xtag("add_commit_z", add_commit_z),
xtag("commit_end", commit_start + add_commit_z));
if (mprotect(commit_start, add_commit_z, PROT_READ | PROT_WRITE) != 0) {
throw std::runtime_error(tostr("ArenaAlloc::expand: commit failure",
xtag("committed_z", committed_z_),
xtag("add_commit_z", add_commit_z)));
}
this->committed_z_ = aligned_offset_z;
this->limit_ = this->lo_ + committed_z_;
return true;
@ -167,6 +190,8 @@ namespace xo {
std::byte * p = lo_;
while (p < free_ptr_) {
log && log(xtag("p", (void *)p));
Object * obj = reinterpret_cast<Object *>(p);
TaggedPtr tp = obj->self_tp();
std::size_t z = obj->_shallow_size();

View file

@ -15,9 +15,9 @@ set(SELF_SRCS
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
# xo-unit used for time measurement
xo_dependency(${SELF_LIB} xo_unit)
xo_headeronly_dependency(${SELF_LIB} xo_unit)
xo_dependency(${SELF_LIB} indentlog)
xo_dependency(${SELF_LIB} reflect)
xo_dependency(${SELF_LIB} callback)
xo_headeronly_dependency(${SELF_LIB} callback)
#end CMakeLists.txt

View file

@ -66,16 +66,36 @@ namespace xo {
std::size_t nursery_size = config.initial_nursery_z_;
std::size_t tenured_size = config.initial_tenured_z_;
nursery_[role2int(role::from_space)]
if (config_.incr_gc_threshold_ > nursery_size) {
throw std::runtime_error(tostr("GC::ctor: expected nursery gc threshold < nursery size",
xtag("nursery-gc-threshold", config_.incr_gc_threshold_),
xtag("nursery-size", nursery_size)));
}
if (nursery_size + config_.full_gc_threshold_ > tenured_size) {
throw std::runtime_error(tostr("GC::ctor: expected nursery size + tennured gc threshold < tenured size",
xtag("nursery-size", nursery_size),
xtag("tenured-size", tenured_size),
xtag("full-gc-threshold", config_.full_gc_threshold_)
));
}
if (config_.incr_gc_threshold_ > nursery_size)
this->config_.incr_gc_threshold_ = nursery_size;
if (config_.full_gc_threshold_ > tenured_size)
this->config_.full_gc_threshold_ = tenured_size;
this->nursery_[role2int(role::from_space)]
= ArenaAlloc::make("NA", nursery_size, config.debug_flag_);
nursery_[role2int(role::to_space) ]
this->nursery_[role2int(role::to_space) ]
= ArenaAlloc::make("NB", nursery_size, config.debug_flag_);
tenured_[role2int(role::from_space)]
this->tenured_[role2int(role::from_space)]
= ArenaAlloc::make("TA", tenured_size, config.debug_flag_);
tenured_[role2int(role::to_space) ]
this->tenured_[role2int(role::to_space) ]
= ArenaAlloc::make("TB", tenured_size, config.debug_flag_);
nursery_[role2int(role::from_space)]->expand(config.incr_gc_threshold_);
@ -83,9 +103,9 @@ namespace xo {
tenured_[role2int(role::from_space)]->expand(config.full_gc_threshold_);
tenured_[role2int(role::to_space) ]->expand(config.full_gc_threshold_);
mutation_log_[role2int(role::from_space)] = std::make_unique<MutationLog>();
mutation_log_[role2int(role::to_space )] = std::make_unique<MutationLog>();
defer_mutation_log_ = std::make_unique<MutationLog>();
this->mutation_log_[role2int(role::from_space)] = std::make_unique<MutationLog>();
this->mutation_log_[role2int(role::to_space )] = std::make_unique<MutationLog>();
this->defer_mutation_log_ = std::make_unique<MutationLog>();
this->gc_history_ = CircularBuffer<GcStatisticsHistoryItem>(config.stats_history_z_);
@ -96,23 +116,25 @@ namespace xo {
/* hygiene */
this->clear();
nursery_[role2int(role::from_space)].reset();
nursery_[role2int(role::to_space) ].reset();
this->nursery_[role2int(role::from_space)].reset();
this->nursery_[role2int(role::to_space) ].reset();
tenured_[role2int(role::from_space)].reset();
tenured_[role2int(role::to_space) ].reset();
this->tenured_[role2int(role::from_space)].reset();
this->tenured_[role2int(role::to_space) ].reset();
mutation_log_[role2int(role::from_space)].reset();
mutation_log_[role2int(role::to_space) ].reset();
defer_mutation_log_.reset();
this->gc_root_v_.clear();
this->mutation_log_[role2int(role::from_space)].reset();
this->mutation_log_[role2int(role::to_space) ].reset();
this->defer_mutation_log_.reset();
}
up<GC>
GC::make(const Config & config)
{
GC * gc = new GC(config);
//GC * gc = new GC(config);
return up<GC>{gc};
return std::make_unique<GC>(config);
}
const std::string &
@ -608,16 +630,18 @@ namespace xo {
void
GC::capture_object_statistics(generation upto, capture_phase phase)
{
/* scan nursery */
this->nursery_[role2int(role::to_space)]->capture_object_statistics
(phase,
&object_statistics_sab_[gen2int(generation::nursery)]);
if (upto == generation::tenured) {
/* scan tenured */
this->tenured_[role2int(role::to_space)]->capture_object_statistics
if (config_.object_stats_flag_) {
/* scan nursery */
this->nursery_[role2int(role::to_space)]->capture_object_statistics
(phase,
&object_statistics_sab_[gen2int(generation::tenured)]);
&object_statistics_sab_[gen2int(generation::nursery)]);
if (upto == generation::tenured) {
/* scan tenured */
this->tenured_[role2int(role::to_space)]->capture_object_statistics
(phase,
&object_statistics_sab_[gen2int(generation::tenured)]);
}
}
}

View file

@ -14,7 +14,7 @@ operator new (std::size_t z, const xo::Cpof & cpof)
{
using xo::gc::GC;
GC * gc = reinterpret_cast<GC *>(xo::Object::mm);
GC * gc = reinterpret_cast<GC *>(cpof.mm_);
return gc->alloc_gc_copy(z, cpof.src_);
}

View file

@ -33,7 +33,7 @@ namespace xo {
void display(std::ostream & os) const final override { os << data_; }
virtual std::size_t _shallow_size() const final override { return sizeof(*this); }
virtual Object * _shallow_copy() const final override { return new (Cpof(this)) DummyObject(*this); }
virtual Object * _shallow_copy() const final override { return new (Cpof(Object::mm, this)) DummyObject(*this); }
virtual std::size_t _forward_children() final override { return _shallow_size(); }
private:

View file

@ -306,8 +306,8 @@ public:
std::size_t tenured_tospace_scale() const;
GcStateDescription snapshot_gc_state() const;
void generate_random_mutation();
void generate_random_mutations();
void generate_random_mutation(GC * gc);
void generate_random_mutations(GC * gc);
public:
int alloc_per_cycle_ = 1;
@ -415,10 +415,10 @@ AppState::snapshot_gc_state() const {
}
void
AppState::generate_random_mutation() {
AppState::generate_random_mutation(GC * gc) {
if (rng_() % 1000 > (5 * 1000) / 7) {
/* p=16% integer */
gc_root_v_[next_root_++] = Integer::make(next_int_);
gc_root_v_[next_root_++] = Integer::make(gc, next_int_);
} else if (rng_() % 1000 > (3 * 1000) / 7) {
/* p=16% cons */
gp<Object> random_car = gc_root_v_.at(rng_() % gc_root_v_.size());
@ -454,9 +454,9 @@ AppState::generate_random_mutation() {
}
void
AppState::generate_random_mutations() {
AppState::generate_random_mutations(GC * gc) {
for (int i = 0; i < this->alloc_per_cycle_; ++i) {
this->generate_random_mutation();
this->generate_random_mutation(gc);
}
}
@ -1921,7 +1921,7 @@ int main(int, char **)
case draw_state_type::alloc:
{
/** generate random alloc **/
app_state.generate_random_mutations();
app_state.generate_random_mutations(app_state.gc_.get());
gcstate = app_state.snapshot_gc_state();

View file

@ -20,13 +20,12 @@ add_definitions(${PROJECT_CXX_FLAGS})
# note on ordering: must read .cmake defn of lib before configuring any examples
add_subdirectory(src/interpreter)
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
# ----------------------------------------------------------------
#add_subdirectory(example)
#add_subdirectory(utest)
add_subdirectory(utest)
# ----------------------------------------------------------------

View file

@ -0,0 +1,35 @@
# ----------------------------------------------------------------
# for example:
# $ PREFIX=/usr/local # for example
# $ cmake -DCMAKE_MODULE_PATH=prefix -DCMAKE_INSTALL_PREFIX=$PREFIX -B .build
#
# will get
# CMAKE_MODULE_PATH
# from xo-cmake-config --cmake-module-path
#
# and expect .cmake macros in
# CMAKE_MODULE_PATH/xo_macros/xo_cxx.cmake
# ----------------------------------------------------------------
find_program(XO_CMAKE_CONFIG_EXECUTABLE NAMES xo-cmake-config REQUIRED)
if ("${XO_CMAKE_CONFIG_EXECUTABLE}" STREQUAL "XO_CMAKE_CONFIG_EXECUTABLE-NOT_FOUND")
message(FATAL "could not find xo-cmake-config executable")
endif()
message(STATUS "XO_CMAKE_CONFIG_EXECUTABLE=${XO_CMAKE_CONFIG_EXECUTABLE}")
if (NOT XO_SUBMODULE_BUILD)
if (("${CMAKE_MODULE_PATH}" STREQUAL "") OR ("${CMAKE_MODULE_PATH}" STREQUAL prefix))
# default to typical install location for xo-project-macros
execute_process(COMMAND ${XO_CMAKE_CONFIG_EXECUTABLE} --cmake-module-path OUTPUT_VARIABLE CMAKE_MODULE_PATH)
message(STATUS "CMAKE_MODULE_PATH=${CMAKE_MODULE_PATH}")
endif()
endif()
# needs to have been installed somewhere on CMAKE_MODULE_PATH,
# (e.g. from xo-cmake with the same value for CMAKE_INSTALL_PREFIX)
#
include(xo_macros/xo_cxx)
xo_cxx_bootstrap_message()

View file

@ -0,0 +1,7 @@
@PACKAGE_INIT@
include(CMakeFindDependencyMacro)
find_dependency(xo_alloc)
#find_dependency(xo_flatstring)
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
check_required_components("@PROJECT_NAME@")

View file

@ -0,0 +1,9 @@
# xo-alloc/docs/CMakeLists.txt
xo_doxygen_collect_deps()
xo_docdir_doxygen_config()
xo_docdir_sphinx_config(
index.rst install.rst)
# see xo-reader/doc or xo-unit/doc for working examples
# example.rst install.rst implementation.rst

View file

@ -0,0 +1,39 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = 'xo interpreter documentation'
copyright = '2025, Roland Conybeare'
author = 'Roland Conybeare'
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
#extensions = []
extensions = [ "breathe",
"sphinx.ext.mathjax", # inline math
"sphinx.ext.autodoc", # generate info from docstrings
"sphinxcontrib.ditaa", # diagrams-through-ascii-art
"sphinxcontrib.plantuml" # text -> uml diagrams
]
# note: breathe requires doxygen xml output -> must have GENERATE_XML = YES in Doxyfile.in
# match project name in Doxyfile.in
breathe_default_project = "xodoxxml"
templates_path = ['_templates']
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
pygments_style = 'sphinx'
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
#html_theme = 'alabaster'
html_theme = 'sphinx_rtd_theme'
html_static_path = ['_static']
html_favicon = '_static/img/favicon.ico'

View file

@ -1,6 +1,8 @@
/** @file StackFrame.hpp **/
#include "xo/alloc/IAlloc.hpp"
#include "xo/alloc/Object.hpp"
#include <cstdint>
namespace xo {
namespace scm {
@ -8,15 +10,59 @@ namespace xo {
* @brief Represent a single runtime stack frame for a Schematika function
*
* StackFrame intended to be used for interpreted functions.
* Compiled functions will stil likely have stack frames, but need not use the
* Compiled functions will still likely have stack frames, but need not use the
* @ref StackFrame class
*
* memory layout:
*
* +------------+
* | vtable |
* +------------+
* | .n_ |
* +------------+
* | .v_ +------\
* +------------+ <--/
* | .v_[0] |
* +------------+
* . .. .
* +------------+
* | .v_[.n_-1] |
* +------------+
**/
class StackFrame : public Object {
public:
StackFrame(std::size_t n_slot)
using TaggedPtr = xo::reflect::TaggedPtr;
public:
StackFrame(gc::IAlloc * mm, std::size_t n_slot);
/** create frame using allocator @p mm,
* with exactly @p n_slot object pointers
**/
static gp<StackFrame> make(gc::IAlloc * mm, std::size_t n_slot);
/** reflect StackFrame object representation **/
static void reflect_self();
std::size_t n_slot() const { return n_slot_; }
gp<Object> lookup(std::size_t i) const { return v_[i]; }
gp<Object> & lookup(std::size_t i) { return v_[i]; }
gp<Object> operator[](std::size_t i) const { return lookup(i); }
gp<Object> & operator[](std::size_t i) { return lookup(i); }
// inherited from Object..
virtual TaggedPtr self_tp() const final override;
virtual void display(std::ostream & os) const final override;
virtual std::size_t _shallow_size() const final override;
virtual Object * _shallow_copy() const final override;
virtual std::size_t _forward_children() final override;
private:
/** number of elements in frame **/
std::size_t n_slot_ = 0;
/** contiguous array of object pointers: v[0] .. v[n-1] **/
gp<Object> * v_ = nullptr;
};
} /*namespace scm*/
} /*namespace xo*/

View file

@ -0,0 +1,21 @@
/** @file init_interpreter.hpp
*
* author: Roland Conybeare, Nov 2025
**/
#pragma once
#include "xo/subsys/Subsystem.hpp"
namespace xo {
/* tag to represent the interpreter/ subsystem in ordered initialization */
enum S_interpreter_tag {};
template<>
struct InitSubsys<S_interpreter_tag> {
static void init();
static InitEvidence require();
};
}
/* end init_interpreter.hpp */

View file

@ -0,0 +1,13 @@
# interpreter/CMakeLists.txt
set(SELF_LIB xo_interpreter)
set(SELF_SRCS
init_interpreter.cpp
StackFrame.cpp
VirtualSchematikaMachine.cpp
)
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
xo_dependency(${SELF_LIB} xo_alloc)
#xo_dependency(${SELF_LIB} xo_reader)
xo_headeronly_dependency(${SELF_LIB} subsys)

View file

@ -0,0 +1,120 @@
/** @file StackFrame.cpp **/
#include "StackFrame.hpp"
#include "xo/reflect/Reflect.hpp"
#include "xo/reflect/StructReflector.hpp"
#include <cstring>
namespace xo {
using xo::reflect::Reflect;
using xo::reflect::StructReflector;
using xo::reflect::TaggedPtr;
using xo::print::quot;
namespace scm {
namespace {
std::size_t
slot_array_size(std::size_t n) {
return n * sizeof(gp<Object>);
}
}
StackFrame::StackFrame(gc::IAlloc * mm, std::size_t n_slot)
: n_slot_{n_slot}, v_{nullptr}
{
if (n_slot > 0) {
std::byte * mem = mm->alloc(slot_array_size(n_slot));
this->v_ = new (mem) gp<Object>[n_slot];
}
}
gp<StackFrame>
StackFrame::make(gc::IAlloc * mm, std::size_t n_slot)
{
return new (MMPtr(mm)) StackFrame(mm, n_slot);
}
TaggedPtr
StackFrame::self_tp() const
{
return Reflect::make_tp(const_cast<StackFrame *>(this));
}
void
StackFrame::display(std::ostream & os) const
{
os << "<stack-frame"
<< xtag("n_slot", n_slot_);
#ifdef NOT_YET
for (std::size_t i = 0, n = n_slot(); i < n; ++i) {
char buf[24];
snprintf(buf, sizeof(buf), "v[%lu]", i);
os << xtag(buf, lookup(i));
}
#endif
os << ">";
}
std::size_t
StackFrame::_shallow_size() const
{
std::size_t retval = sizeof(StackFrame);
retval += gc::IAlloc::with_padding(slot_array_size(n_slot_));
return retval;
}
Object *
StackFrame::_shallow_copy() const
{
Cpof cpof(Object::mm, this);
StackFrame * copy = new (cpof) StackFrame(cpof.mm_, n_slot_);
void * v_dest = copy->v_;
if (v_) {
::memcpy(v_dest, v_, slot_array_size(n_slot_));
}
#ifdef OBSOLETE
for (size_t i = 0, n = n_slot_; i < n; ++i) {
copy->v_[i] = v_[i];
}
#endif
return copy;
}
std::size_t
StackFrame::_forward_children()
{
for (std::size_t i = 0, n = n_slot_; i < n; ++i) {
Object::_forward_inplace(lookup(i));
}
return _shallow_size();
}
void
StackFrame::reflect_self()
{
StructReflector<StackFrame> sr;
if (sr.is_incomplete()) {
REFLECT_MEMBER(sr, n_slot);
// non-trivial to reflect frame members,
// effectively need separate reflection for each cardinality;
// or: reflect .v_[] as nested element
}
}
} /*namespace scm*/
} /*namespace xo*/
/* end StackFrame.cpp */

View file

@ -0,0 +1,27 @@
/** @file init_interpreter.cpp
*
* author: Roland Conybeare, Nov 2025
*/
#include "init_interpreter.hpp"
#include "StackFrame.hpp"
#include "xo/subsys/Subsystem.hpp"
namespace xo {
using xo::scm::StackFrame;
void
InitSubsys<S_interpreter_tag>::init()
{
StackFrame::reflect_self();
}
InitEvidence
InitSubsys<S_interpreter_tag>::require()
{
return Subsystem::provide<S_interpreter_tag>("interpreter", &init);
}
} /*namespace xo*/
/* end init_interpreter.cpp */

View file

@ -0,0 +1,12 @@
# build unittest interpreter/utest
set(UTEST_EXE utest.interpreter)
set(UTEST_SRCS
interpreter_utest_main.cpp
StackFrame.test.cpp
)
xo_add_utest_executable(${UTEST_EXE} ${UTEST_SRCS})
xo_self_dependency(${UTEST_EXE} xo_interpreter)
xo_dependency(${UTEST_EXE} xo_object)
xo_external_target_dependency(${UTEST_EXE} Catch2 Catch2::Catch2)

View file

@ -0,0 +1,133 @@
/** @file StackFrame.test.cpp **/
#include "xo/interpreter/init_interpreter.hpp"
#include "xo/interpreter/StackFrame.hpp"
#include "xo/object/Integer.hpp"
#include "xo/alloc/GC.hpp"
#include <catch2/catch.hpp>
#include <vector>
#include <cstdint>
namespace xo {
using xo::scm::StackFrame;
using xo::obj::Integer;
using xo::gc::GC;
using xo::gc::ArenaAlloc;
using xo::gc::generation;
using xo::gc::generation_result;
using xo::reflect::TaggedPtr;
namespace ut {
static InitEvidence s_init = (InitSubsys<S_interpreter_tag>::require());
namespace {
struct Testcase_StackFrame {
Testcase_StackFrame(const std::vector<std::int32_t> & contents) : contents_{contents} {}
/* build xo::obj::Integer for each contents_[i], store in F[i] for new StackFrame F */
std::vector<std::int32_t> contents_;
};
std::vector<Testcase_StackFrame>
s_testcase_v = {
Testcase_StackFrame({}),
Testcase_StackFrame({}),
Testcase_StackFrame({111}),
};
}
TEST_CASE("StackFrame", "[StackFrame][interpreter]")
{
Subsystem::initialize_all();
constexpr bool c_debug_flag = false;
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
scope log(XO_DEBUG(c_debug_flag), xtag("test", "StackFrame2"), xtag("i_tc", i_tc));
const Testcase_StackFrame & tc = s_testcase_v[i_tc];
up<ArenaAlloc> alloc = ArenaAlloc::make("utest", 16384, c_debug_flag);
REQUIRE(alloc.get());
Object::mm = alloc.get();
std::size_t n = tc.contents_.size();
gp<StackFrame> frame = StackFrame::make(alloc.get(), n);
TaggedPtr tp = frame->self_tp();
REQUIRE(tp.is_struct());
}
}
TEST_CASE("StackFrame2", "[StackFrame][gc][interpreter]")
{
Subsystem::initialize_all();
constexpr bool c_debug_flag = false;
try {
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
scope log(XO_DEBUG(c_debug_flag), xtag("test", "StackFrame2"), xtag("i_tc", i_tc));
const Testcase_StackFrame & tc = s_testcase_v[i_tc];
up<GC> gc = GC::make(
{.initial_nursery_z_ = 16384,
.initial_tenured_z_ = 32768,
.incr_gc_threshold_ = 4096,
.full_gc_threshold_ = 4096,
.object_stats_flag_ = true,
.debug_flag_ = c_debug_flag,
});
REQUIRE(gc.get());
/* use gc for all Object allocs */
GC * mm = gc.get();
Object::mm = mm;
std::size_t n = tc.contents_.size();
gp<Integer> x = Integer::make(gc.get(), 42);
gc->add_gc_root(reinterpret_cast<Object **>(&x));
REQUIRE(gc->tospace_generation_of(x.ptr()) == generation_result::nursery);
gp<StackFrame> frame = StackFrame::make(gc.get(), n);
StackFrame ** frame_pp = frame.ptr_address();
gc->add_gc_root(reinterpret_cast<Object **>(frame_pp));
/* verifying allocated in N1 */
REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery);
for (std::size_t i = 0; i < n; ++i)
frame->lookup(i) = Integer::make(mm, tc.contents_.at(i));
std::size_t expected_alloc_z = frame->_shallow_size();
REQUIRE(expected_alloc_z >= sizeof(StackFrame) + n * sizeof(gp<Object>));
gc->request_gc(generation::nursery); // <<<<<<<<< GC here <<<<<<<<<
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::nursery)].n_gc_ == 1);
REQUIRE(gc->native_gc_statistics().gen_v_[gen2int(generation::tenured)].n_gc_ == 0);
/* verify Integer x preserved across gc */
REQUIRE(gc->tospace_generation_of(x.ptr()) == generation_result::nursery);
/* verify StackFrame preserved across gc */
REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery);
REQUIRE(frame->n_slot() == n);
for (std::size_t i = 0; i < n; ++i) {
//REQUIRE(Integer::from(frame->lookup(i)).ptr());
//REQUIRE(Integer::from(frame->lookup(i))->value() == tc.contents_.at(i));
}
}
} catch (std::exception & ex) {
std::cerr << "exception: " << ex.what() << std::endl;
REQUIRE(false);
}
}
}
}
/* end StackFrame.test.cpp */

View file

@ -0,0 +1,6 @@
/** @file interpreter_utest_main.cpp **/
#define CATCH_CONFIG_MAIN
#include "catch2/catch.hpp"
/* end interpreter_utest_main.cpp */

View file

@ -10,7 +10,7 @@ if (ENABLE_TESTING)
xo_add_utest_executable(${SELF_EXE} ${SELF_SRCS})
xo_self_dependency(${SELF_EXE} xo_jit)
xo_dependency(${SELF_EXE} xo_ratio)
xo_dependency(${SELF_EXE} xo_reflectutil)
xo_headeronly_dependency(${SELF_EXE} xo_reflectutil)
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)
endif()

View file

@ -11,6 +11,7 @@ namespace xo {
namespace obj {
class Integer : public Number {
public:
using IAlloc = xo::gc::IAlloc;
using int_type = long long;
public:
@ -18,7 +19,7 @@ namespace xo {
explicit Integer(int_type x);
/** create instance holding integer value @p x **/
static gp<Integer> make(int_type x);
static gp<Integer> make(IAlloc * mm, int_type x);
/** downcast from @p x iff x is actually an Integer. Otherwise nullptr **/
static gp<Integer> from(gp<Object> x);

View file

@ -8,4 +8,8 @@ set(SELF_SRCS
Integer.cpp)
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
xo_headeronly_dependency(${SELF_LIB} xo_reflectutil)
xo_headeronly_dependency(${SELF_LIB} xo_unit)
xo_headeronly_dependency(${SELF_LIB} callback)
xo_dependency(${SELF_LIB} xo_alloc)
xo_dependency(${SELF_LIB} reflect)

View file

@ -10,12 +10,13 @@
namespace xo {
using xo::reflect::Reflect;
using xo::reflect::TaggedPtr;
using xo::gc::IAlloc;
namespace obj {
Integer::Integer(int_type x) : value_{x} {}
gp<Integer>
Integer::make(int_type x) {
Integer::make(IAlloc * mm, int_type x) {
return new (MMPtr(mm)) Integer(x);
}
@ -41,7 +42,7 @@ namespace xo {
Object *
Integer::_shallow_copy() const {
Cpof cpof(this);
Cpof cpof(Object::mm, this);
return new (cpof) Integer(*this);
}

View file

@ -102,7 +102,7 @@ namespace xo {
assert(!(this->is_nil()));
Cpof cpof(this);
Cpof cpof(Object::mm, this);
return new (cpof) List(*this);
}

View file

@ -130,7 +130,7 @@ namespace xo {
{
// Reminder: String must come before secondary allocation,
Cpof cpof(this);
Cpof cpof(Object::mm, this);
// might expect to write:
// gp<String> copy = new (gcm) String(Object::mm, owner_, z_chars_, chars_);

View file

@ -55,10 +55,10 @@ namespace xo {
/* use gc for all Object allocs */
Object::mm = gc.get();
gp<List> l = List::list(Integer::make(1));
gp<List> l = List::list(Integer::make(gc.get(), 1));
gc->add_gc_root(reinterpret_cast<Object**>(l.ptr_address()));
gp<List> l2 = List::list(Integer::make(10));
gp<List> l2 = List::list(Integer::make(gc.get(), 10));
gc->add_gc_root(reinterpret_cast<Object**>(l2.ptr_address()));
{
@ -80,7 +80,7 @@ namespace xo {
// mutation, but not {xgen, xckp} since parent,child both in N0
l->assign_head(Integer::make(2));
l->assign_head(Integer::make(gc.get(), 2));
{
REQUIRE(gc->native_gc_statistics().n_mutation_ == 1);
REQUIRE(gc->native_gc_statistics().n_logged_mutation_ == 0);
@ -106,7 +106,7 @@ namespace xo {
// mutation, xckp since parent in N1, child in N0
l->assign_head(Integer::make(3));
l->assign_head(Integer::make(gc.get(), 3));
{
REQUIRE(Integer::from(l->head())->value() == 3);
@ -373,11 +373,11 @@ namespace xo {
RandomMutationModel(std::size_t m, std::size_t n, std::size_t r, std::size_t rr, std::size_t k)
: m_{m}, n_{n}, r_{r}, rr_{rr}, k_{k} {}
void generate_seed_values();
void generate_seed_values(GC * gc);
void generate_random_roots(GC * gc, xoshiro256ss * p_rgen);
void generate_random_mutations(xoshiro256ss * p_rgen);
void rejuvenate_seed_values();
void rejuvenate_seed_values(GC * gc);
void alter_random_roots(xoshiro256ss * p_rgen);
/* create m random list cells */
@ -400,7 +400,7 @@ namespace xo {
std::vector<gp<Object>> root_v_;
};
void RandomMutationModel::generate_seed_values()
void RandomMutationModel::generate_seed_values(GC * gc)
{
w1_.clear();
w2_.clear();
@ -415,7 +415,7 @@ namespace xo {
{
std::copy(w1_.begin(), w1_.end(), std::back_inserter(w2_));
for (size_t j = 0; j < n_; ++j) {
w2_.push_back(Integer::make((this->start_)++));
w2_.push_back(Integer::make(gc, (this->start_)++));
}
REQUIRE(w2_.size() == m_ + n_);
}
@ -468,7 +468,7 @@ namespace xo {
}
}
void RandomMutationModel::rejuvenate_seed_values()
void RandomMutationModel::rejuvenate_seed_values(GC * gc)
{
for (std::size_t i = 0; i < w1_.size(); ++i) {
INFO(xtag("i", i));
@ -492,7 +492,7 @@ namespace xo {
REQUIRE(w2_[j].ptr());
} else {
/* w2[j] is garbage, replace */
w2_[j] = Integer::make((this->start_)++);
w2_[j] = Integer::make(gc, (this->start_)++);
REQUIRE(w2_[j].ptr());
}
}
@ -616,7 +616,7 @@ namespace xo {
INFO(xtag("cycle", cycle));
if (cycle == 0) {
data_model.generate_seed_values();
data_model.generate_seed_values(gc.get());
data_model.generate_random_roots(gc.get(), &rgen);
} else {
/* figure out values in {data_model_.w1_, data_model_.w2_} that
@ -626,7 +626,7 @@ namespace xo {
* (For example want to verify behavior of GC w.r.t. cells that are alive only
* because of a mutation)
*/
data_model.rejuvenate_seed_values();
data_model.rejuvenate_seed_values(gc.get());
data_model.alter_random_roots(&rgen);
}

View file

@ -24,8 +24,8 @@ namespace xo {
Object::mm = gc.get();
gp<Integer> i1 = Integer::make(123);
gp<Integer> i2 = Integer::make(-321);
gp<Integer> i1 = Integer::make(gc.get(), 123);
gp<Integer> i2 = Integer::make(gc.get(), -321);
REQUIRE(i1->value() == 123);
REQUIRE(i2->value() == -321);

View file

@ -289,9 +289,10 @@ namespace xo {
up<ArenaAlloc> alloc = ArenaAlloc::make("arena", 1024, c_debug_flag);
Object::mm = alloc.get();
ArenaAlloc * mm = alloc.get();
Object::mm = mm;
gp<List> l = List::list(Integer::make(1), Integer::make(2), Integer::make(3));
gp<List> l = List::list(Integer::make(mm, 1), Integer::make(mm, 2), Integer::make(mm, 3));
REQUIRE(l->size() == 3);

View file

@ -17,7 +17,7 @@ if (ENABLE_TESTING)
xo_self_headeronly_dependency(${SELF_EXE} xo_ratio)
xo_dependency(${SELF_EXE} reflect)
# need explicit header-only dependencies, at least in submodule build
xo_dependency(${SELF_EXE} xo_reflectutil)
xo_headeronly_dependency(${SELF_EXE} xo_reflectutil)
xo_dependency(${SELF_EXE} randomgen)
xo_dependency(${SELF_EXE} indentlog)
xo_external_target_dependency(${SELF_EXE} Catch2 Catch2::Catch2)

View file

@ -24,12 +24,13 @@ Install
One-step Install
----------------
Install along with the rest of *XO* from `xo-umbrella2 source`_
Install along with the rest of *XO* from `xo-umbrella2 source`_:
see install instructions for xo-umrbella2
.. _xo-umbrella2 source: https://github.com/rconybea/xo-umbrella2
Minimal Dependencies
--------------------
Essential Xo Dependencies
-------------------------
``xo-reader`` uses several supporting libraries from the *XO* project:

View file

@ -39,10 +39,10 @@ namespace xo {
template<typename T>
static TypeDescrW establish() {
TypeDescrW td = TypeDescrBase::require(&typeid(T),
std::string(type_name<T>()),
nullptr /*tdextra*/,
nullptr /*invoker*/);
static TypeDescrW td = TypeDescrBase::require(&typeid(T),
std::string(type_name<T>()),
nullptr /*tdextra*/,
nullptr /*invoker*/);
#ifdef NOT_USING
std::function<TaggedPtr (void *)> to_self_tp;

View file

@ -13,7 +13,7 @@ set(SELF_SRCS
xo_add_shared_library4(${SELF_LIB} ${PROJECT_NAME}Targets ${PROJECT_VERSION} 1 ${SELF_SRCS})
xo_dependency(${SELF_LIB} refcnt)
xo_dependency(${SELF_LIB} indentlog)
xo_dependency(${SELF_LIB} subsys)
xo_headeronly_dependency(${SELF_LIB} subsys)
#xo_boost_dependency(${SELF_LIB})
# end CMakeLists.txt

View file

@ -57,6 +57,10 @@ namespace xo {
detail::Invoker * invoker,
std::unique_ptr<TypeDescrExtra> tdextra)
{
scope log(XO_DEBUG(false));
log && log(xtag("canonical_name", canonical_name));
if (native_tinfo) {
/* 1. lookup by tinfo hash_code in s_type_table_map
* Not available for manually-constructed type descriptions.
@ -64,19 +68,29 @@ namespace xo {
{
auto ix = s_native_type_table_map.find(TypeInfoRef(native_tinfo));
if ((ix != s_native_type_table_map.end()) && ix->second)
if ((ix != s_native_type_table_map.end()) && ix->second) {
log && log("TypeDescrBase::require"
": using s_native_type_table_map[TypeInfoRef(native_tinfo)]");
return ix->second;
}
}
/* 2. lookup by tinfo hash_code in s_coalesced_type_table_map */
{
auto ix = s_coalesced_type_table_map.find(TypeInfoRef(native_tinfo));
if ((ix != s_coalesced_type_table_map.end()) && ix->second)
if ((ix != s_coalesced_type_table_map.end()) && ix->second) {
log && log("TypeDescrBase::require"
": using s_coalesced_type_table_map[TypeInfoRef(native_tinfo)]");
return ix->second;
}
}
}
log && log("TypeDescrBase::require: try lookup by canonical name");
/* 3. lookup by canonical_name, before we create a new slot.
*
* Have to accept that on clang type_info objects aren't always unique (!$@#!!)
@ -85,6 +99,7 @@ namespace xo {
auto ix = s_canonical_type_table_map.find(canonical_name);
if (ix != s_canonical_type_table_map.end()) {
/** assume existing slot, with same canonical name,
* represents the same type as native_tinfo
**/
@ -145,6 +160,8 @@ namespace xo {
TypeId new_td_id = TypeId::allocate();
log && log("TypeDescrBase::require", xtag("new_td_id", new_td_id));
if (s_type_table_v.size() <= new_td_id.id())
s_type_table_v.resize(new_td_id.id() + 1);

View file

@ -7,17 +7,17 @@
#include "xo/subsys/Subsystem.hpp"
namespace xo {
void
InitSubsys<S_reflect_tag>::init()
{
/* placeholder -- expecting there to be non-trivial content soon */
} /*init*/
void
InitSubsys<S_reflect_tag>::init()
{
/* placeholder -- expecting there to be non-trivial content soon */
} /*init*/
InitEvidence
InitSubsys<S_reflect_tag>::require()
{
return Subsystem::provide<S_reflect_tag>("reflect", &init);
} /*require*/
InitEvidence
InitSubsys<S_reflect_tag>::require()
{
return Subsystem::provide<S_reflect_tag>("reflect", &init);
} /*require*/
} /*namespace xo*/
/* end init_reflect.cpp */