Netlist: It is now possible to have multiple handlers per device ...

... for updates. This will make device implementation more flexible and
faster. A nice side-effect is that there was some minor (<5%)
performance increase already. Each input is now assigned a notification
handler. Currently this is update, but going forward this may be a
custom handler. In addition
- fixed MEMPOOL on OSX
- removed dead code
- avoid bit-rot
- added delegate support for emscripten and arm processors
- added delegate support for VS 2015 x64
[Couriersud]
This commit is contained in:
couriersud 2017-02-04 01:23:12 +01:00
parent 427cf984db
commit 70051f6c1f
12 changed files with 232 additions and 115 deletions

View File

@ -172,15 +172,18 @@ maketree: $(sort $(OBJDIRS))
# Special targets
#-------------------------------------------------
.PHONY: clang mingw doc
.PHONY: clang clang-5 mingw doc
clang:
$(MAKE) CC=clang++-5.0 LD=clang++-5.0 CEXTRAFLAGS="-march=native -Weverything -Werror -Wno-padded -Wno-weak-vtables -Wno-missing-variable-declarations -Wconversion -Wno-c++98-compat -Wno-float-equal -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-format-nonliteral -Wno-weak-template-vtables -Wno-exit-time-destructors"
$(MAKE) CC=clang++ LD=clang++ CEXTRAFLAGS="-march=native -Weverything -Werror -Wno-padded -Wno-weak-vtables -Wno-missing-variable-declarations -Wconversion -Wno-c++98-compat -Wno-float-equal -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-format-nonliteral -Wno-weak-template-vtables -Wno-exit-time-destructors"
clang-5:
$(MAKE) CC=clang++-5.0 LD=clang++-5.0 CEXTRAFLAGS="-march=native -Weverything -Werror -Wno-unreachable-code -Wno-padded -Wno-weak-vtables -Wno-missing-variable-declarations -Wconversion -Wno-c++98-compat -Wno-float-equal -Wno-global-constructors -Wno-c++98-compat-pedantic -Wno-format-nonliteral -Wno-weak-template-vtables -Wno-exit-time-destructors"
#
# Mostly done: -Wno-weak-vtables -Wno-cast-align
# FIXME: -Wno-weak-vtables -Wno-missing-variable-declarations -Wno-conversion -Wno-exit-time-destructors
#
# FIXME: -Wunreachable-code : False warnings, this a documented clang bug: https://llvm.org/bugs/show_bug.cgi?id=28994
mingw:
$(MAKE) LDEXTRAFLAGS="-Wl,--subsystem,console" LIBS= MD=@mkdir.exe SHELL=sh.exe

View File

@ -15,14 +15,8 @@
#define xstr(s) # s
#if 0
#define ENTRY1(nic, name, defparam) factory.register_device<nic>( # name, xstr(nic), defparam );
#define ENTRY(nic, name, defparam) ENTRY1(NETLIB_NAME(nic), name, defparam)
#endif
#define NETLIB_DEVICE_DECL(chip) extern factory::constructor_ptr_t decl_ ## chip;
//#define ENTRYX1(nic, name, defparam, decl) factory.register_device( decl (# name, xstr(nic), defparam) );
#define ENTRYX1(nic, name, defparam, decl) factory.register_device( decl (pstring(# name), pstring(xstr(nic)), pstring(defparam)) );
#define ENTRYX(nic, name, defparam) { NETLIB_DEVICE_DECL(nic) ENTRYX1(NETLIB_NAME(nic), name, defparam, decl_ ## nic) }

View File

@ -22,6 +22,8 @@ namespace netlist
{
}
NETLIB_UPDATEI();
public:
void shift();
@ -109,6 +111,11 @@ namespace netlist
m_last_CP = m_CP();
}
NETLIB_UPDATE(Am2847_shifter)
{
/* do nothing */
}
inline NETLIB_FUNC_VOID(Am2847_shifter, shift, (void))
{
uint32_t out = m_buffer[0] & 1;

View File

@ -25,31 +25,35 @@ namespace netlist
{
namespace detail
{
#if (USE_MEMPOOL)
static plib::mempool pool(6553600, 64);
void * object_t::operator new (size_t size)
{
return pool.alloc(size);
}
static plib::mempool *pool()
{
static plib::mempool *s_pool = nullptr;
if (s_pool == nullptr)
s_pool = new plib::mempool(65536, 16);
return s_pool;
}
void object_t::operator delete (void * mem)
{
if (mem)
pool.free(mem);
}
#else
void * object_t::operator new (size_t size)
{
return ::operator new(size);
}
void * object_t::operator new (size_t size)
{
void *ret = nullptr;
if ((USE_MEMPOOL))
ret = pool()->alloc(size);
else
ret = ::operator new(size);
return ret;
}
void object_t::operator delete (void * mem)
{
if (mem)
::operator delete(mem);
}
#endif
void object_t::operator delete (void * mem)
{
if (mem)
{
if ((USE_MEMPOOL))
pool()->free(mem);
else
::operator delete(mem);
}
}
}
@ -361,12 +365,6 @@ void netlist_t::start()
/* resolve inputs */
setup().resolve_inputs();
/* Make sure devices are fully created - now done in register_dev */
log().debug("Setting delegate pointers ...\n");
for (auto &dev : m_devices)
dev->set_delegate_pointer();
log().verbose("looking for two terms connected to rail nets ...");
for (auto & t : get_device_list<analog::NETLIB_NAME(twoterm)>())
{
@ -391,6 +389,14 @@ void netlist_t::start()
else
m_solver->post_start();
for (auto &n : m_nets)
for (auto & term : n->m_core_terms)
{
//core_device_t *dev = reinterpret_cast<core_device_t *>(term->m_delegate.object());
core_device_t *dev = &term->device();
dev->set_default_delegate(*term);
}
}
void netlist_t::stop()
@ -427,6 +433,14 @@ void netlist_t::reset()
//if (m_solver != nullptr)
// m_solver->do_reset();
std::unordered_map<core_device_t *, bool> m;
for (auto &d : m_devices)
{
m[d.get()] = d->get_hint_deactivate();
}
// Reset all nets once !
for (auto & n : m_nets)
n->reset();
@ -444,15 +458,66 @@ void netlist_t::reset()
* INFO: The order here affects power up of e.g. breakout. However, such
* variations are explicitly stated in the breakout manual.
*/
#if 0
for (std::size_t i = 0; i < m_devices.size(); i++)
const unsigned startup_strategy = 1; //! \note make this a parameter
switch (startup_strategy)
{
m_devices[i]->update_dev();
case 0:
{
std::vector<core_device_t *> d;
std::vector<nldelegate *> t;
log().verbose("Using default startup strategy");
for (auto &n : m_nets)
for (auto & term : n->m_core_terms)
if (term->m_delegate.has_object())
{
if (!plib::container::contains(t, &term->m_delegate))
{
t.push_back(&term->m_delegate);
term->m_delegate();
}
core_device_t *dev = reinterpret_cast<core_device_t *>(term->m_delegate.object());
if (!plib::container::contains(d, dev))
d.push_back(dev);
}
log().verbose("Call update on devices which need parameter update:");
for (auto & dev : m_devices)
if (dev->needs_update_after_param_change())
{
if (!plib::container::contains(d, dev.get()))
{
d.push_back(dev.get());
log().verbose("\t ...{1}", dev->name());
dev->update_dev();
}
}
log().verbose("Devices not yet updated:");
for (auto &dev : m_devices)
if (!plib::container::contains(d, dev.get()))
log().verbose("\t ...{1}", dev->name());
//x->update_dev();
}
break;
case 1: // brute force backward
{
std::size_t i = m_devices.size();
while (i>0)
m_devices[--i]->update_dev();
}
break;
case 2: // brute force forward
{
for (std::size_t i = 0; i < m_devices.size(); i++)
m_devices[i]->update_dev();
}
break;
}
#else
std::size_t i = m_devices.size();
while (i>0)
m_devices[--i]->update_dev();
#if 1
/* the above may screw up m_active and the list */
for (auto &n : m_nets)
n->rebuild_list();
#endif
}
@ -594,9 +659,6 @@ core_device_t::core_device_t(netlist_t &owner, const pstring &name)
, logic_family_t()
, netlist_ref(owner)
, m_hint_deactivate(false)
#if (!NL_USE_PMF_VIRTUAL)
, m_static_update()
#endif
{
if (logic_family() == nullptr)
set_logic_family(family_TTL());
@ -607,9 +669,6 @@ core_device_t::core_device_t(core_device_t &owner, const pstring &name)
, logic_family_t()
, netlist_ref(owner.netlist())
, m_hint_deactivate(false)
#if (!NL_USE_PMF_VIRTUAL)
, m_static_update()
#endif
{
set_logic_family(owner.logic_family());
if (logic_family() == nullptr)
@ -621,11 +680,9 @@ core_device_t::~core_device_t()
{
}
void core_device_t::set_delegate_pointer()
void core_device_t::set_default_delegate(detail::core_terminal_t &term)
{
#if (!NL_USE_PMF_VIRTUAL)
m_static_update.set_base(&core_device_t::update, this);
#endif
term.m_delegate.set(&core_device_t::update, this);
}
plib::plog_base<NL_DEBUG> &core_device_t::log()
@ -789,7 +846,7 @@ void detail::net_t::update_devs() NL_NOEXCEPT
{
p.device().m_stat_call_count.inc();
if ((p.state() & mask) != 0)
p.device().update_dev();
p.m_delegate();
}
}
@ -891,6 +948,8 @@ detail::core_terminal_t::core_terminal_t(core_device_t &dev, const pstring &anam
, m_net(nullptr)
, m_state(*this, "m_state", state)
{
//m_delegate.set(&core_device_t::update, &dev);
//m_delegate.set(&core_device_t::update_dev, &dev);
}
detail::core_terminal_t::~core_terminal_t()

View File

@ -455,6 +455,10 @@ namespace netlist
core_device_t & m_device;
};
/*! Delegate type for device notification.
*
*/
typedef plib::pmfp<void> nldelegate;
// -----------------------------------------------------------------------------
// core_terminal_t
@ -509,6 +513,8 @@ namespace netlist
void reset();
nldelegate m_delegate;
private:
net_t * m_net;
state_var<state_e> m_state;
@ -1044,14 +1050,16 @@ namespace netlist
void do_reset() { reset(); }
void set_hint_deactivate(bool v) { m_hint_deactivate = v; }
bool get_hint_deactivate() { return m_hint_deactivate; }
void set_delegate_pointer();
void set_default_delegate(detail::core_terminal_t &term);
/* stats */
nperftime_t m_stat_total_time;
nperfcount_t m_stat_call_count;
nperfcount_t m_stat_inc_active;
protected:
virtual void update() NL_NOEXCEPT { }
@ -1061,11 +1069,7 @@ namespace netlist
void do_update() NL_NOEXCEPT
{
#if (NL_USE_PMF_VIRTUAL)
update();
#else
m_static_update.call(this);
#endif
}
plib::plog_base<NL_DEBUG> &log();
@ -1081,10 +1085,6 @@ namespace netlist
private:
bool m_hint_deactivate;
#if (!NL_USE_PMF_VIRTUAL)
plib::pmfp_base<void> m_static_update;
#endif
};
// -----------------------------------------------------------------------------

View File

@ -95,16 +95,6 @@ using nl_double = double;
* approach will be automatically selected.
*/
//#define NL_USE_PMF_VIRTUAL 1
#ifndef NL_USE_PMF_VIRTUAL
#if PPMF_TYPE == PPMF_TYPE_PMF
#define NL_USE_PMF_VIRTUAL 1
#else
#define NL_USE_PMF_VIRTUAL 0
#endif
#endif
//============================================================
// WARNINGS
//============================================================
@ -115,9 +105,4 @@ using nl_double = double;
#endif
#endif
#ifdef __APPLE__
#undef USE_MEMPOOL
#define USE_MEMPOOL (0)
#endif
#endif /* NLCONFIG_H_ */

View File

@ -54,7 +54,7 @@ namespace netlist
struct entry_t final
{
constexpr entry_t() { }
constexpr entry_t() : m_exec_time(), m_object(nullptr) { }
constexpr entry_t(const Time &t, const Element &o) : m_exec_time(t), m_object(o) { }
constexpr entry_t(const entry_t &e) : m_exec_time(e.m_exec_time), m_object(e.m_object) { }
constexpr entry_t(entry_t &&e) : m_exec_time(e.m_exec_time), m_object(e.m_object) { }

23
src/lib/netlist/plib/palloc.cpp Normal file → Executable file
View File

@ -5,6 +5,8 @@
*
*/
#include <algorithm>
#include "pconfig.h"
#include "palloc.h"
#include "pfmtlog.h"
@ -27,7 +29,7 @@ mempool::~mempool()
{
fprintf(stderr, "Found block with %d dangling allocations\n", static_cast<int>(b.m_num_alloc));
}
delete b.data;
::operator delete(b.data);
}
m_blocks.clear();
}
@ -35,7 +37,7 @@ mempool::~mempool()
size_t mempool::new_block()
{
block b;
b.data = new char[m_min_alloc];
b.data = static_cast<char *>(::operator new(m_min_alloc));
b.cur_ptr = b.data;
b.m_free = m_min_alloc;
b.m_num_alloc = 0;
@ -43,10 +45,19 @@ size_t mempool::new_block()
return m_blocks.size() - 1;
}
size_t mempool::mininfosize()
{
size_t sinfo = sizeof(mempool::info);
size_t ma = 8;
#ifdef __APPLE__
ma = 16;
#endif
return ((std::max(m_min_align, sinfo) + ma - 1) / ma) * ma;
}
void *mempool::alloc(size_t size)
{
size_t rs = (size + sizeof(info) + m_min_align - 1) & ~(m_min_align - 1);
size_t rs = (size + mininfosize() + m_min_align - 1) & ~(m_min_align - 1);
for (size_t bn=0; bn < m_blocks.size(); bn++)
{
auto &b = m_blocks[bn];
@ -56,7 +67,7 @@ void *mempool::alloc(size_t size)
b.m_num_alloc++;
auto i = reinterpret_cast<info *>(b.cur_ptr);
i->m_block = bn;
auto ret = reinterpret_cast<void *>(b.cur_ptr + sizeof(info));
auto ret = reinterpret_cast<void *>(b.cur_ptr + mininfosize());
b.cur_ptr += rs;
return ret;
}
@ -68,7 +79,7 @@ void *mempool::alloc(size_t size)
b.m_free = m_min_alloc - rs;
auto i = reinterpret_cast<info *>(b.cur_ptr);
i->m_block = bn;
auto ret = reinterpret_cast<void *>(b.cur_ptr + sizeof(info));
auto ret = reinterpret_cast<void *>(b.cur_ptr + mininfosize());
b.cur_ptr += rs;
return ret;
}
@ -78,7 +89,7 @@ void mempool::free(void *ptr)
{
auto p = reinterpret_cast<char *>(ptr);
auto i = reinterpret_cast<info *>(p - sizeof(info));
auto i = reinterpret_cast<info *>(p - mininfosize());
block *b = &m_blocks[i->m_block];
if (b->m_num_alloc == 0)
fprintf(stderr, "Argh .. double free\n");

View File

@ -144,6 +144,7 @@ private:
};
size_t new_block();
size_t mininfosize();
struct info
{
@ -151,6 +152,11 @@ private:
size_t m_block;
};
size_t m_min_alloc;
size_t m_min_align;
std::vector<block> m_blocks;
public:
mempool(size_t min_alloc, size_t min_align);
~mempool();
@ -158,10 +164,6 @@ public:
void *alloc(size_t size);
void free(void *ptr);
size_t m_min_alloc;
size_t m_min_align;
std::vector<block> m_blocks;
};
}

10
src/lib/netlist/plib/pconfig.h Normal file → Executable file
View File

@ -71,13 +71,13 @@ typedef __int128_t INT128;
#define MEMBER_ABI _thiscall
#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
#define PHAS_PMF_INTERNAL 0
#elif defined(EMSCRIPTEN)
#define PHAS_PMF_INTERNAL 0
#elif defined(__arm__) || defined(__ARMEL__)
#define PHAS_PMF_INTERNAL 0
#elif defined(__arm__) || defined(__ARMEL__) || defined(__aarch64__) || defined(__MIPSEL__) || defined(__mips_isa_rev) || defined(__mips64) || defined(EMSCRIPTEN)
#define PHAS_PMF_INTERNAL 2
#else
#define PHAS_PMF_INTERNAL 1
#endif
#elif defined(_MSC_VER) && defined (_M_X64)
#define PHAS_PMF_INTERNAL 3
#else
#define PHAS_PMF_INTERNAL 0
#endif
@ -87,7 +87,7 @@ typedef __int128_t INT128;
#endif
#ifndef PPMF_TYPE
#if PHAS_PMF_INTERNAL
#if (PHAS_PMF_INTERNAL > 0)
#define PPMF_TYPE PPMF_TYPE_INTERNAL
#else
#define PPMF_TYPE PPMF_TYPE_PMF

95
src/lib/netlist/plib/ppmf.h Normal file → Executable file
View File

@ -51,44 +51,82 @@ namespace plib {
* It derives a pointer to a member function.
*/
#if (PHAS_PMF_INTERNAL)
#if (PHAS_PMF_INTERNAL > 0)
class mfp
{
public:
// construct from any member function pointer
#ifdef _MSC_VER
class __single_inheritance si_generic_class;
class generic_class { };
#else
class generic_class;
#endif
using generic_function = void (*)();
template<typename MemberFunctionType>
mfp(MemberFunctionType mftp)
: m_function(0), m_this_delta(0)
: m_function(0), m_this_delta(0), m_size(sizeof(mfp))
{
*reinterpret_cast<MemberFunctionType *>(this) = mftp;
}
template<typename FunctionType, typename MemberFunctionType, typename ObjectType>
static FunctionType get_mfp(MemberFunctionType mftp, ObjectType *object)
template<typename MemberFunctionType, typename FunctionType, typename ObjectType>
static void get_mfp(MemberFunctionType mftp, FunctionType &func, ObjectType *&object)
{
mfp mfpo(mftp);
//return mfpo.update_after_bind<FunctionType>(object);
return reinterpret_cast<FunctionType>(
mfpo.convert_to_generic(reinterpret_cast<generic_class *>(object)));
generic_function rfunc(nullptr);
generic_class *robject = reinterpret_cast<generic_class *>(object);
mfpo.convert_to_generic(rfunc, robject);
func = reinterpret_cast<FunctionType>(rfunc);
object = reinterpret_cast<ObjectType *>(robject);
}
private:
// extract the generic function and adjust the object pointer
generic_function convert_to_generic(generic_class * object) const
void convert_to_generic(generic_function &func, generic_class *&object) const
{
// apply the "this" delta to the object first
generic_class * o_p_delta = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
if (PHAS_PMF_INTERNAL == 1)
{
// apply the "this" delta to the object first
generic_class * o_p_delta = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
// if the low bit of the vtable index is clear, then it is just a raw function pointer
if (!(m_function & 1))
return reinterpret_cast<generic_function>(m_function);
// if the low bit of the vtable index is clear, then it is just a raw function pointer
if (!(m_function & 1))
func = reinterpret_cast<generic_function>(m_function);
else
{
// otherwise, it is the byte index into the vtable where the actual function lives
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(o_p_delta);
func = *reinterpret_cast<generic_function *>(vtable_base + m_function - 1);
}
object = o_p_delta;
}
else if (PHAS_PMF_INTERNAL == 2)
{
if ((m_this_delta & 1) == 0) {
object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
func = reinterpret_cast<generic_function>(m_function);
}
else
{
object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object));
// otherwise, it is the byte index into the vtable where the actual function lives
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(object);
func = *reinterpret_cast<generic_function *>(vtable_base + m_function + m_this_delta - 1);
}
}
else if (PHAS_PMF_INTERNAL == 3)
{
const int SINGLE_MEMFUNCPTR_SIZE = sizeof(void (generic_class::*)());
func = reinterpret_cast<generic_function>(m_function);
if (m_size == SINGLE_MEMFUNCPTR_SIZE + sizeof(int))
object = reinterpret_cast<generic_class *>(reinterpret_cast<std::uint8_t *>(object) + m_this_delta);
}
// otherwise, it is the byte index into the vtable where the actual function lives
std::uint8_t *vtable_base = *reinterpret_cast<std::uint8_t **>(o_p_delta);
return *reinterpret_cast<generic_function *>(vtable_base + m_function - 1);
}
// actual state
@ -96,6 +134,10 @@ namespace plib {
// if even, it's a pointer to the function
// if odd, it's the byte offset into the vtable
int m_this_delta; // delta to apply to the 'this' pointer
int m_dummy1; // only used for visual studio x64
int m_dummy2;
int m_size;
};
#endif
@ -107,12 +149,18 @@ namespace plib {
class generic_class;
#if defined (__INTEL_COMPILER) && defined (_M_X64) // needed for "Intel(R) C++ Intel(R) 64 Compiler XE for applications running on Intel(R) 64, Version 14.0.2.176 Build 20140130" at least
using generic_function = int [((sizeof(void *) + 4 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
#elif defined(_MSC_VER)// all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller
using generic_function = int [((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
#elif defined(_MSC_VER) // all other cases - for MSVC maximum size is one pointer, plus 3 ints; all other implementations seem to be smaller
using generic_function = int[((sizeof(void *) + 3 * sizeof(int)) + (sizeof(int) - 1)) / sizeof(int)];
#else
using generic_function = R (generic_class::*)(Targs...);
#endif
pmfp_base() {}
pmfp_base()
{
int *p = reinterpret_cast<int *>(&m_func);
int *e = p + sizeof(generic_function) / sizeof(int);
for (; p < e; p++)
*p = 0;
}
template<typename MemberFunctionType, typename O>
void set_base(MemberFunctionType mftp, O *object)
@ -130,6 +178,9 @@ namespace plib {
}
private:
generic_function m_func;
#if 0 && defined(_MSC_VER)
int dummy[4];
#endif
};
#elif ((PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV) || (PPMF_TYPE == PPMF_TYPE_INTERNAL))
@ -146,7 +197,9 @@ namespace plib {
{
#if (PPMF_TYPE == PPMF_TYPE_INTERNAL)
using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args);
m_func = reinterpret_cast<generic_function>(plib::mfp::get_mfp<function_ptr>(mftp, object));
function_ptr func(nullptr);
plib::mfp::get_mfp(mftp, func, object);
m_func = reinterpret_cast<generic_function>(func);
#elif (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV)
R (O::* pFunc)(Targs...) = mftp;
m_func = reinterpret_cast<generic_function>((object->*pFunc));
@ -159,6 +212,7 @@ namespace plib {
return (reinterpret_cast<function_ptr>(m_func))(obj, std::forward<Targs>(args)...);
}
bool is_set() { return m_func != nullptr; }
generic_function get_function() const { return m_func; }
private:
generic_function m_func;
};
@ -188,6 +242,9 @@ namespace plib {
{
return this->call(m_obj, std::forward<Targs>(args)...);
}
generic_class *object() const { return m_obj; }
bool has_object() const { return m_obj != nullptr; }
private:
generic_class *m_obj;
};

View File

@ -404,7 +404,6 @@ void NETLIB_NAME(solver)::post_start()
}
// FIXME ...
ms->set_delegate_pointer();
ms->setup(grp);
log().verbose("Solver {1}", ms->name());