/* Copyright (c) 2015 Microsoft Corporation. All rights reserved. Released under Apache 2.0 license as described in the file LICENSE. Author: Leonardo de Moura */ #pragma once #include "util/thread.h" #include #include namespace lean { class obj; enum class obj_kind { Constructor, Closure, MPN }; class obj_cell { atomic m_rc; unsigned m_kind:2; unsigned m_size:14; unsigned m_cidx:16; // constructor idx if Constructor, and arity if closure bool dec_ref_core() { if (atomic_fetch_sub_explicit(&m_rc, 1u, memory_order_release) == 1u) { atomic_thread_fence(memory_order_acquire); return true; } else { return false; } } void dealloc(); obj_cell(unsigned cidx, unsigned sz, obj const * fs); obj_cell(void * fn, unsigned arity, unsigned sz, obj const * fs); friend obj mk_obj(unsigned cidx, unsigned n, obj const * fs); friend obj mk_closure_core(void * fn, unsigned arity, unsigned n, obj const * fs); void ** field_addr() { return reinterpret_cast(reinterpret_cast(this)+sizeof(obj_cell)); } void ** fn_ptr_addr() { return field_addr() + m_size; } void copy_fields(obj_cell const & src); public: obj_cell(obj_cell const & src, obj const & a1); obj_cell(obj_cell const & src, obj const & a1, obj const & a2); obj_cell(obj_cell const & src, obj const & a1, obj const & a2, obj const & a3); obj_cell(obj_cell const & src, obj const & a1, obj const & a2, obj const & a3, obj const & a4); obj_cell(obj_cell const & src, obj const & a1, obj const & a2, obj const & a3, obj const & a4, obj const & a5); obj_cell(obj_cell const & src, obj const & a1, obj const & a2, obj const & a3, obj const & a4, obj const & a5, obj const & a6); obj_cell(obj_cell const & src, obj const & a1, obj const & a2, obj const & a3, obj const & a4, obj const & a5, obj const & a6, obj const & a7); obj_cell(obj_cell const & src, obj const & a1, obj const & a2, obj const & a3, obj const & a4, obj const & a5, obj const & a6, obj const & a7, obj const & a8); unsigned get_rc() const { return atomic_load(&m_rc); } void inc_ref() { atomic_fetch_add_explicit(&m_rc, 1u, memory_order_relaxed); } void dec_ref() { if (dec_ref_core()) { dealloc(); } } obj_kind kind() const { return static_cast(m_kind); } size_t cidx() const { return m_cidx; } unsigned size() const { return m_size; } unsigned arity() const { return m_cidx; } obj const * field_ptr() const { return reinterpret_cast(reinterpret_cast(this)+sizeof(obj_cell)); } void * fn_ptr() const { return *(reinterpret_cast(const_cast(field_ptr()))+m_size); } }; #define LEAN_IS_PTR(obj) ((reinterpret_cast(obj) & 1) == 0) #define LEAN_BOX(num) (reinterpret_cast((num << 1) | 1)) #define LEAN_UNBOX(obj) (reinterpret_cast(obj) >> 1) class obj { friend class obj_cell; obj_cell * m_data; void copy_fields(std::vector & r); obj apply() const; obj_cell * steal_ptr() { obj_cell * r = m_data; m_data = LEAN_BOX(0); return r; } public: obj():m_data(LEAN_BOX(0)) { static_assert(sizeof(obj) == sizeof(void *), "unexpected class obj size"); } // NOLINT obj(unsigned cidx):m_data(LEAN_BOX(cidx)) {} obj(obj_cell * c):m_data(c) { m_data->inc_ref(); } obj(obj const & o):m_data(o.m_data) { if (LEAN_IS_PTR(m_data)) m_data->inc_ref(); } obj(obj && o):m_data(o.m_data) { o.m_data = LEAN_BOX(0); } ~obj() { if (LEAN_IS_PTR(m_data)) m_data->dec_ref(); } obj & operator=(obj const & s) { if (LEAN_IS_PTR(s.m_data)) s.m_data->inc_ref(); auto new_data = s.m_data; if (LEAN_IS_PTR(m_data)) m_data->dec_ref(); m_data = new_data; return *this; } obj & operator=(obj && s) { if (LEAN_IS_PTR(m_data)) m_data->dec_ref(); m_data = s.m_data; s.m_data = LEAN_BOX(0); return *this; } obj_cell const & data() const { return *m_data; } size_t cidx() const { return LEAN_IS_PTR(m_data) ? m_data->cidx() : LEAN_UNBOX(m_data); } unsigned size() const { return m_data->size(); } obj const & fld(unsigned fidx) const { return m_data->field_ptr()[fidx]; } obj const & operator[](unsigned fidx) const { return fld(fidx); } unsigned arity() const { return m_data->arity(); } void * fn_ptr() const { return m_data->fn_ptr(); } obj apply(obj const &) const; obj apply(obj const &, obj const &) const; obj apply(obj const &, obj const &, obj const &) const; obj apply(obj const &, obj const &, obj const &, obj const &) const; obj apply(obj const &, obj const &, obj const &, obj const &, obj const &) const; obj apply(obj const &, obj const &, obj const &, obj const &, obj const &, obj const &) const; obj apply(obj const &, obj const &, obj const &, obj const &, obj const &, obj const &, obj const &) const; obj apply(obj const &, obj const &, obj const &, obj const &, obj const &, obj const &, obj const &, obj const &) const; obj apply(unsigned, obj const *) const; }; inline obj mk_obj(unsigned cidx) { return obj(cidx); } obj mk_obj(unsigned cidx, unsigned n, obj const * fs); inline obj mk_obj(unsigned cidx, obj const & o) { return mk_obj(cidx, 1, &o); } inline obj mk_obj(unsigned cidx, std::initializer_list const & os) { return mk_obj(cidx, os.size(), os.begin()); } obj mk_closure_core(void * fn, unsigned arity, unsigned n, obj const * fs); template obj mk_closure(T fn, unsigned arity, unsigned n, obj const * fs) { return mk_closure_core(reinterpret_cast(fn), arity, n, fs); } template obj mk_closure(T fn, unsigned arity, std::initializer_list const & os) { return mk_closure_core(reinterpret_cast(fn), arity, os.size(), os.begin()); } }