summaryrefslogblamecommitdiffstatshomepage
path: root/src/lib/netlist/plib/ppmf.h
blob: bbbe95ded053dff0bc2465b291f2c0d07d3f9388 (plain) (tree)
1
2
3
4
5
6

                               



               




























                                                                                       

                    
                               
                  
 
 
 










                                                              
                                                              



































                                                                                                                                                                              



                                                  



                                                
                
 
                           




                                                                          



                                                             



                                                            
                                    
      


                                                     
                                            
                                                                                               
                 

                                                                                             

                 

                                                                                                     


                                                                              
                                                        
                                                                                 


                                                                         



                                                                             
                                                                                             
                 


                                                                             

                                                                                                                                            


































                                                                                                                                            
 






                                                                                                                                                          
 
                                                                                              
                                                 
                                               








                                               

                                                                                                                                                                                           

                                                                                                                                            
     
                                                                        
      






                                                                            





                                                                 
                                                                       

                                    
                                                                         

                                                                
                                                                                          

                                                                       
                                     
                                                                         

                                                                                  


                                                    
 




                                                 

                                        
                          
                             
      















                                                                                     


                                                                          





                                                                                      
                                                                  



                                                                                                           
                                                                          
                                                                                 









                                                  

                                  
                                                                                            
 

                                                                    

                                                           
                                          
                 

                                                                          

                 

                                                               




                                                                          
                                                                        


                                                                               
 

                                                                             




                                     
                   
 
                 
// license:GPL-2.0+
// copyright-holders:Couriersud

#ifndef PPMF_H_
#define PPMF_H_

///
/// \file ppmf.h
///
///
/// PMF_TYPE_GNUC_PMF
///      Use standard pointer to member function syntax C++11
///
///  PMF_TYPE_GNUC_PMF_CONV
///      Use gnu extension and convert the pmf to a function pointer.
///      This is not standard compliant and needs
///      -Wno-pmf-conversions to compile.
///
///  PMF_TYPE_INTERNAL
///      Use the same approach as MAME for deriving the function pointer.
///      This is compiler-dependent as well
///
///  Benchmarks for ./nltool -c run -f src/mame/machine/nl_pong.cpp -t 10 -n pong_fast
///
///  PMF_TYPE_INTERNAL:       215%    215%
///  PMF_TYPE_GNUC_PMF:       163%    196%
///  PMF_TYPE_GNUC_PMF_CONV:  215%    215%
///  PMF_TYPE_VIRTUAL:        213%    209%
///
///  The whole exercise was done to avoid virtual calls. In prior versions of
///  netlist, the INTERNAL and GNUC_PMF_CONV approach provided significant improvement.
///  Since than, "hot" was removed from functions declared as virtual.
///  This may explain that the recent benchmarks show no difference at all.
///

#include "pconfig.h"

#include <cstdint> // uintptr_t
#include <utility>



//============================================================
//  Macro magic
//============================================================

//#define PPMF_TYPE 0

#define PPMF_TYPE_PMF             0
#define PPMF_TYPE_GNUC_PMF_CONV   1
#define PPMF_TYPE_INTERNAL        2

#if defined(__GNUC__)
	// does not work in versions over 4.7.x of 32bit MINGW
	#if defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 7)))
		#define PHAS_PMF_INTERNAL 0
	#elif defined(__MINGW32__) && !defined(__x86_64) && defined(__i386__)
		#define PHAS_PMF_INTERNAL 1
		#define MEMBER_ABI _thiscall
	#elif defined(__clang__) && defined(__i386__) && defined(_WIN32)
		#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

#ifndef MEMBER_ABI
	#define MEMBER_ABI
#endif

#ifndef PPMF_TYPE
	#if (PHAS_PMF_INTERNAL > 0)
		#define PPMF_TYPE PPMF_TYPE_INTERNAL
	#else
		#define PPMF_TYPE PPMF_TYPE_PMF
	#endif
#else
	#undef PHAS_PMF_INTERNAL
	#define PHAS_PMF_INTERNAL 0
	#undef MEMBER_ABI
	#define MEMBER_ABI
#endif


#if (PPMF_TYPE == PPMF_TYPE_GNUC_PMF_CONV)
#pragma GCC diagnostic ignored "-Wpmf-conversions"
#endif

#if defined(__GNUC__) && (__GNUC__ > 6)
#pragma GCC diagnostic ignored "-Wnoexcept-type"
#endif

namespace plib {

#if (PHAS_PMF_INTERNAL > 0)
	///
	/// \brief Used to derive a pointer to a member function.
	///
	/// The following class was derived from the MAME delegate.h code.
	///
	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_dummy1(0), m_dummy2(0), m_size(sizeof(mfp))
		{
			*reinterpret_cast<MemberFunctionType *>(this) = mftp; // NOLINT
			// NOLINTNEXTLINE(clang-analyzer-optin.cplusplus.UninitializedObject)
		}

		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);
			generic_function rfunc(nullptr);
			auto 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
		void convert_to_generic(generic_function &func, generic_class *&object) const
		{
			if (PHAS_PMF_INTERNAL == 1)
			{
				// apply the "this" delta to the object first
				// NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
				auto 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))
					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);
			}

		}

		// actual state
		uintptr_t               m_function;         // first item can be one of two things:
													//    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

#if (PPMF_TYPE == PPMF_TYPE_PMF)
	template<typename R, typename... Targs>
	class pmfp_base
	{
	public:
		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)];
#else
		using generic_function = R (generic_class::*)(Targs...);
#endif
		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)
		{
			using function_ptr = R (O::*)(Targs...);
			function_ptr t = mftp;
			*reinterpret_cast<function_ptr *>(&m_func) = t;
		}
		template<typename O>
		inline R call(O *obj, Targs... args) const noexcept(true)
		{
			using function_ptr = R (O::*)(Targs...);
			function_ptr t = *reinterpret_cast<const function_ptr *>(&m_func);
			return (obj->*t)(std::forward<Targs>(args)...);
		}
		bool is_set() const {
#if defined(_MSC_VER) || (defined (__INTEL_COMPILER) && defined (_M_X64))
			const int *p = reinterpret_cast<const int *>(&m_func);
			const int *e = p + sizeof(generic_function) / sizeof(int);
			for (; p < e; p++)
				if (*p != 0)
					return true;

			return false;
#else
			return m_func != nullptr;
#endif
		}
	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))
	template<typename R, typename... Targs>
	class pmfp_base
	{
	public:
		using generic_function = void (*)();

		pmfp_base() : m_func(nullptr) {}

		template<typename MemberFunctionType, typename O>
		void set_base(MemberFunctionType mftp, O *object)
		{
	#if (PPMF_TYPE == PPMF_TYPE_INTERNAL)
			using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args);
			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));
	#endif
		}
		template<typename O>
		R call(O *obj, Targs... args) const noexcept(true)
		{
			using function_ptr = MEMBER_ABI R (*)(O *obj, Targs... args);
			return (reinterpret_cast<function_ptr>(m_func))(obj, std::forward<Targs>(args)...);
		}
		bool is_set() const noexcept { return m_func != nullptr; }
		generic_function get_function() const noexcept { return m_func; }
	private:
		generic_function m_func;
	};
#endif

	template<typename R, typename... Targs>
	class pmfp : public pmfp_base<R, Targs...>
	{
	public:
		class generic_class;

		template <class C>
		using MemberFunctionType =  R (C::*)(Targs...); // noexcept(true) --> c++-17

		pmfp() : pmfp_base<R, Targs...>(), m_obj(nullptr) {}

		template<typename O>
		pmfp(MemberFunctionType<O> mftp, O *object)
		: pmfp_base<R, Targs...>()
		{
			this->set_base(mftp, object);
			m_obj = reinterpret_cast<generic_class *>(object);
		}

		template<typename O>
		void set(MemberFunctionType<O> mftp, O *object)
		{
			this->set_base(mftp, object);
			m_obj = reinterpret_cast<generic_class *>(object);
		}

		inline R operator()(Targs ... args) const noexcept(true)
		{
			return this->call(m_obj, std::forward<Targs>(args)...);
		}

		generic_class *object() const noexcept { return m_obj; }
		bool has_object() const noexcept { return m_obj != nullptr; }
	private:
		generic_class *m_obj;
	};


} // namespace plib

#endif // PPMF_H_