xo-interpreter: + Env + LocalEnv + GlobalEnv scaffold
This commit is contained in:
parent
75f5aa91a6
commit
85bfd34c0d
7 changed files with 518 additions and 2 deletions
|
|
@ -18,7 +18,7 @@ namespace xo {
|
|||
**/
|
||||
class Env : public Object {
|
||||
public:
|
||||
//gp<Object> lookup_symbol(xxx);
|
||||
//gp<Object> lookup_symbol(const std::string & name) const;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
|
|
|||
57
xo-interpreter/include/xo/interpreter/GlobalEnv.hpp
Normal file
57
xo-interpreter/include/xo/interpreter/GlobalEnv.hpp
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/** @file GlobalEnv.hpp **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Env.hpp"
|
||||
#include "xo/alloc/IAlloc.hpp"
|
||||
#include "xo/expression/GlobalSymtab.hpp"
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** @class GlobalEnv
|
||||
* @brief Top-level global environment
|
||||
**/
|
||||
class GlobalEnv : public Env {
|
||||
public:
|
||||
/** Create top-level global environment, allocating via @p mm.
|
||||
* Expect one of these per interpreter session.
|
||||
**/
|
||||
static gp<GlobalEnv> make_empty(gc::IAlloc * mm, const rp<GlobalSymtab> & symtab);
|
||||
|
||||
// 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:
|
||||
GlobalEnv(gc::IAlloc * mm, const rp<GlobalSymtab> & symtab);
|
||||
|
||||
private:
|
||||
/** memory manager to use **/
|
||||
gc::IAlloc * mm_;
|
||||
|
||||
/** global symbol table.
|
||||
* variables known to @c symtab_ are represented by
|
||||
* corresponding values in @p slot_map_
|
||||
**/
|
||||
rp<GlobalSymtab> symtab_;
|
||||
|
||||
/** environment contents.
|
||||
* expression @c symtab_->lookup_binding(vname)
|
||||
* has associated value @c slot_map_.at(vname)
|
||||
*
|
||||
* TODO: replace with something subject to GC ?
|
||||
* every member of @ref slot_map_ will have to be a
|
||||
* GC root
|
||||
*
|
||||
* TODO: probably want to hash here instead.
|
||||
* May also want lhs names to be separately hashed symbols
|
||||
**/
|
||||
std::map<std::string, gp<Object>> slot_map_;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GlobalEnv.hpp */
|
||||
113
xo-interpreter/include/xo/interpreter/LocalEnv.hpp
Normal file
113
xo-interpreter/include/xo/interpreter/LocalEnv.hpp
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/** @file LocalEnv.hpp **/
|
||||
|
||||
#include "Env.hpp"
|
||||
#include "xo/alloc/IAlloc.hpp"
|
||||
#include "xo/expression/LocalSymtab.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace scm {
|
||||
/** gc-only vector
|
||||
**/
|
||||
template <typename ElementType>
|
||||
class CVector {
|
||||
public:
|
||||
using value_type = ElementType;
|
||||
|
||||
public:
|
||||
CVector(gc::IAlloc * mm, std::size_t n)
|
||||
: n_{n}, v_{nullptr}
|
||||
{
|
||||
if (n_ > 0) {
|
||||
std::byte * mem = mm->alloc(n_ * sizeof(ElementType));
|
||||
this->v_ = new (mem) ElementType[n];
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t size() const { return n_; }
|
||||
|
||||
ElementType operator[](std::size_t i) const { return v_[i]; }
|
||||
ElementType & operator[](std::size_t i) { return v_[i]; }
|
||||
|
||||
friend class LocalEnv;
|
||||
private:
|
||||
/** number of elements in @ref v_ **/
|
||||
std::size_t n_ = 0;
|
||||
/** contiguous array of pointers **/
|
||||
ElementType * v_ = nullptr;
|
||||
};
|
||||
|
||||
/** @class LocalEnv
|
||||
* @brief Represent a single runtime stack frame for a Schematika function
|
||||
*
|
||||
* LocalEnv intended to be used for interpreted functions.
|
||||
*
|
||||
* Compiled functions will still likely have stack frames, but need not use the
|
||||
* @ref LocalEnv class
|
||||
*
|
||||
* memory layout:
|
||||
* ^
|
||||
* +-----------------------+ |
|
||||
* | vtable | |
|
||||
* +-----------------------+ |
|
||||
* | .parent +------/
|
||||
* +------------+----------+
|
||||
* | .slot_v_ | .n_ |
|
||||
* | +----------+
|
||||
* | | .v_ +------\
|
||||
* +------------+----------+ <--/
|
||||
* | .v_[0] +---------> Object(1)
|
||||
* +-----------------------+
|
||||
* . .. .
|
||||
* +-----------------------+
|
||||
* | .v_[.n_-1] +---------> Object(n)
|
||||
* +-----------------------+
|
||||
**/
|
||||
class LocalEnv : public Env {
|
||||
public:
|
||||
using TaggedPtr = xo::reflect::TaggedPtr;
|
||||
|
||||
public:
|
||||
LocalEnv(gc::IAlloc * mm, gp<LocalEnv> p, const rp<LocalSymtab> & s, std::size_t n);
|
||||
|
||||
/** create frame using allocator @p mm,
|
||||
* with parent @p p and exactly @p n_slot object pointers.
|
||||
* variable types are taken from symbol table @p s.
|
||||
**/
|
||||
static gp<LocalEnv> make(gc::IAlloc * mm,
|
||||
gp<LocalEnv> p,
|
||||
const rp<LocalSymtab> & s,
|
||||
std::size_t n_slot);
|
||||
|
||||
/** reflect LocalEnv object representation **/
|
||||
static void reflect_self();
|
||||
|
||||
gp<LocalEnv> parent() const { return parent_; }
|
||||
std::size_t size() const { return slot_v_.size(); }
|
||||
|
||||
gp<Object> operator[](std::size_t i) const { return slot_v_[i]; }
|
||||
gp<Object> & operator[](std::size_t i) { return slot_v_[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:
|
||||
/** parent stack frame **/
|
||||
gp<LocalEnv> parent_;
|
||||
/** origin symbol table. records variable names and bindings.
|
||||
* for a binding path p with leaf slot index j = p.j_slot_:
|
||||
* @c slot_v_[j] holds value associated with variable @c symtab_->argv_[j]
|
||||
**/
|
||||
rp<LocalSymtab> symtab_;
|
||||
/** environment contents **/
|
||||
CVector<gp<Object>> slot_v_;
|
||||
};
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end LocalEnv.hpp */
|
||||
|
|
@ -3,8 +3,9 @@
|
|||
set(SELF_LIB xo_interpreter)
|
||||
set(SELF_SRCS
|
||||
init_interpreter.cpp
|
||||
StackFrame.cpp
|
||||
Schematika.cpp
|
||||
LocalEnv.cpp
|
||||
GlobalEnv.cpp
|
||||
VirtualSchematikaMachine.cpp
|
||||
)
|
||||
|
||||
|
|
|
|||
62
xo-interpreter/src/interpreter/GlobalEnv.cpp
Normal file
62
xo-interpreter/src/interpreter/GlobalEnv.cpp
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/** @file GlobalEnv.cpp **/
|
||||
|
||||
#include "GlobalEnv.hpp"
|
||||
#include "xo/reflect/Reflect.hpp"
|
||||
|
||||
namespace xo {
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::TaggedPtr;
|
||||
|
||||
namespace scm {
|
||||
gp<GlobalEnv>
|
||||
GlobalEnv::make_empty(gc::IAlloc * mm, const rp<GlobalSymtab> & symtab)
|
||||
{
|
||||
/* by design: GlobalEnv and GlobalEnv.slot_map_ are heap-allocated */
|
||||
|
||||
return new GlobalEnv(mm, symtab);
|
||||
}
|
||||
|
||||
GlobalEnv::GlobalEnv(gc::IAlloc * mm,
|
||||
const rp<GlobalSymtab> & symtab) : mm_{mm}, symtab_{symtab}
|
||||
{}
|
||||
|
||||
TaggedPtr
|
||||
GlobalEnv::self_tp() const
|
||||
{
|
||||
return Reflect::make_tp(const_cast<GlobalEnv *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
GlobalEnv::display(std::ostream & os) const
|
||||
{
|
||||
os << "<global-env" << xtag("n", slot_map_.size()) << ">";
|
||||
}
|
||||
|
||||
std::size_t
|
||||
GlobalEnv::_shallow_size() const
|
||||
{
|
||||
/** 0: since not allocated in gc-space */
|
||||
return 0;
|
||||
}
|
||||
|
||||
Object *
|
||||
GlobalEnv::_shallow_copy() const
|
||||
{
|
||||
/* by design, don't copy; not subject to GC */
|
||||
return const_cast<GlobalEnv *>(this);
|
||||
}
|
||||
|
||||
std::size_t
|
||||
GlobalEnv::_forward_children()
|
||||
{
|
||||
/* All global slots are treated as GC roots; this means we
|
||||
* don't have to forward them
|
||||
*
|
||||
* This works only as long as global env is immortal.
|
||||
*/
|
||||
return _shallow_size();
|
||||
}
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end GlobalEnv.cpp */
|
||||
149
xo-interpreter/src/interpreter/LocalEnv.cpp
Normal file
149
xo-interpreter/src/interpreter/LocalEnv.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
s/** @file LocalEnv.cpp **/
|
||||
|
||||
#include "LocalEnv.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::TypeDescrW;
|
||||
using xo::reflect::TaggedPtr;
|
||||
using xo::reflect::TypeDescrExtra;
|
||||
using xo::reflect::EstablishTypeDescr;
|
||||
using xo::reflect::StlVectorTdx;
|
||||
using xo::print::quot;
|
||||
|
||||
namespace scm {
|
||||
namespace {
|
||||
std::size_t
|
||||
slot_array_size(std::size_t n) {
|
||||
return n * sizeof(gp<Object>);
|
||||
}
|
||||
}
|
||||
|
||||
gp<LocalEnv>
|
||||
LocalEnv::make(gc::IAlloc * mm,
|
||||
gp<LocalEnv> p,
|
||||
const rp<LocalSymtab> & s,
|
||||
std::size_t n)
|
||||
{
|
||||
if (s) {
|
||||
assert(static_cast<int>(n) == s->n_arg());
|
||||
}
|
||||
|
||||
return new (MMPtr(mm)) LocalEnv(mm, p, s, n);
|
||||
}
|
||||
|
||||
LocalEnv::LocalEnv(gc::IAlloc * mm,
|
||||
gp<LocalEnv> p,
|
||||
const rp<LocalSymtab> & s,
|
||||
std::size_t n) : parent_{p},
|
||||
symtab_{s},
|
||||
slot_v_{mm, n}
|
||||
{}
|
||||
|
||||
TaggedPtr
|
||||
LocalEnv::self_tp() const
|
||||
{
|
||||
return Reflect::make_tp(const_cast<LocalEnv *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
LocalEnv::display(std::ostream & os) const
|
||||
{
|
||||
os << "<local-env"
|
||||
<< xtag("n", slot_v_.size());
|
||||
|
||||
#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
|
||||
LocalEnv::_shallow_size() const
|
||||
{
|
||||
std::size_t retval = sizeof(LocalEnv);
|
||||
|
||||
retval += gc::IAlloc::with_padding(slot_array_size(slot_v_.size()));
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
Object *
|
||||
LocalEnv::_shallow_copy() const
|
||||
{
|
||||
Cpof cpof(Object::mm, this);
|
||||
|
||||
size_t z = size();
|
||||
|
||||
LocalEnv * copy = new (cpof) LocalEnv(cpof.mm_, parent_, symtab_, z);
|
||||
|
||||
void * v_dest = copy->slot_v_.v_;
|
||||
|
||||
if (slot_v_.v_) {
|
||||
::memcpy(v_dest, slot_v_.v_, slot_array_size(z));
|
||||
}
|
||||
|
||||
#ifdef OBSOLETE
|
||||
for (size_t i = 0, n = n_slot_; i < n; ++i) {
|
||||
copy->v_[i] = v_[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
std::size_t
|
||||
LocalEnv::_forward_children()
|
||||
{
|
||||
static_assert(decltype(symtab_)::is_gc_ptr == false);
|
||||
|
||||
Object::_forward_inplace(parent_);
|
||||
// Object::_forward_inplace(symtab_); // not a gp yet
|
||||
for (std::size_t i = 0, n = slot_v_.size(); i < n; ++i) {
|
||||
Object::_forward_inplace((*this)[i]);
|
||||
}
|
||||
|
||||
return _shallow_size();
|
||||
}
|
||||
|
||||
void
|
||||
LocalEnv::reflect_self()
|
||||
{
|
||||
StructReflector<LocalEnv> sr;
|
||||
|
||||
if (sr.is_incomplete()) {
|
||||
/* reflect CVector<gp<Object>>
|
||||
*
|
||||
* note: placement here works b/c CVector<T> not used anywhere else
|
||||
*/
|
||||
using VectorType = CVector<gp<Object>>;
|
||||
|
||||
/* custom reflection for array of Object pointers.
|
||||
* Can use StlVectorTdx here, treating CVector<T> as a vector
|
||||
* via .size() and .operator[] members
|
||||
*/
|
||||
std::unique_ptr<TypeDescrExtra> tdx1
|
||||
= std::make_unique<StlVectorTdx<VectorType>>();
|
||||
TypeDescrW td1
|
||||
= EstablishTypeDescr::establish<VectorType>();
|
||||
td1->assign_tdextra(Reflect::get_final_invoker<VectorType>(),
|
||||
std::move(tdx1));
|
||||
|
||||
REFLECT_MEMBER(sr, parent);
|
||||
REFLECT_MEMBER(sr, slot_v);
|
||||
}
|
||||
}
|
||||
} /*namespace scm*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/* end LocalEnv.cpp */
|
||||
134
xo-interpreter/utest/LocalEnv.test.cpp
Normal file
134
xo-interpreter/utest/LocalEnv.test.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/** @file LocalEnv.test.cpp **/
|
||||
|
||||
#include "xo/interpreter/init_interpreter.hpp"
|
||||
#include "xo/interpreter/LocalEnv.hpp"
|
||||
#include "xo/object/Integer.hpp"
|
||||
#include "xo/alloc/GC.hpp"
|
||||
#include <catch2/catch.hpp>
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
using xo::scm::LocalEnv;
|
||||
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_LocalEnv {
|
||||
Testcase_LocalEnv(const std::vector<std::int32_t> & contents) : contents_{contents} {}
|
||||
|
||||
/* build xo::obj::Integer for each contents_[i], store in F[i] for new LocalEnv F */
|
||||
std::vector<std::int32_t> contents_;
|
||||
};
|
||||
|
||||
std::vector<Testcase_LocalEnv>
|
||||
s_testcase_v = {
|
||||
Testcase_LocalEnv({}),
|
||||
Testcase_LocalEnv({}),
|
||||
Testcase_LocalEnv({111}),
|
||||
Testcase_LocalEnv({111, 222}),
|
||||
};
|
||||
}
|
||||
|
||||
TEST_CASE("LocalEnv", "[LocalEnv][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", "LocalEnv2"), xtag("i_tc", i_tc));
|
||||
|
||||
const Testcase_LocalEnv & 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<LocalEnv> frame = LocalEnv::make(alloc.get(), nullptr /*parent*/, nullptr /*symtab*/, n);
|
||||
|
||||
TaggedPtr tp = frame->self_tp();
|
||||
|
||||
REQUIRE(tp.is_struct());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("LocalEnv2", "[LocalEnv][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", "LocalEnv2"), xtag("i_tc", i_tc));
|
||||
|
||||
const Testcase_LocalEnv & 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<LocalEnv> frame = LocalEnv::make(gc.get(), nullptr /*parent*/, nullptr /*symtab*/, n);
|
||||
LocalEnv ** 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)[i] = Integer::make(mm, tc.contents_.at(i));
|
||||
|
||||
std::size_t expected_alloc_z = frame->_shallow_size();
|
||||
REQUIRE(expected_alloc_z >= sizeof(LocalEnv) + 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 LocalEnv preserved across gc */
|
||||
REQUIRE(gc->tospace_generation_of(frame.ptr()) == generation_result::nursery);
|
||||
REQUIRE(frame->size() == 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 LocalEnv.test.cpp */
|
||||
Loading…
Add table
Add a link
Reference in a new issue