Current WIP (rev 383) doesn't compile with AS_MAX_PORTABILITY
You can patch your local version by changing GetReturnPointer to GetAddressOfReturnLocation. They do basically the same thing, except that the new function allocates memory space if the return type is a registered value type.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
Oh, I don't know if you were planning to do this yourself, but I modified scriptstdstring.cpp for use with AS_MAX_PORTABILITY as well.
By the way, have you seen the new automatic generation of wrapper functions for the generic calling convention that George Yohng contributed? (add_on\autowrapper\aswrappedcall.h).
I haven't tested it for class methods yet. But it works great for global functions.
I believe you were working on something similar, and would like your feedback.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
Quote: Original post by WitchLord
By the way, have you seen the new automatic generation of wrapper functions for the generic calling convention that George Yohng contributed? (add_on\autowrapper\aswrappedcall.h).
It looks pretty similar to the stuff I used with the exception that I also bound arbitrary function objects rather than just function pointers, and having GetAddressOfReturnLocation() makes it more efficient since it can bypass the creation of some temporaries. Though, I'm not sure what the point behind all the asNativeFuncPtr stuff is.
I was thinking that there might be a way to simplify it a bit, but haven't really studied it. I do know however that some of the stuff is there just to make it work on GNUC, so care must be taken to test it on both compilers when changing anything.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game
template <typename T>class Helper { public: static const size_t size_on_stack = sizeof(T *) / sizeof(asDWORD); static inline void * deref(void * ptr) { return *static_cast<void **>(ptr); } T d; private: Helper(const Helper &); Helper & operator=(const Helper &);};template <typename T>class Helper<T &> { public: static const size_t size_on_stack = sizeof(T *) / sizeof(asDWORD); static inline void * deref(void * ptr) { return ptr; } T & d; private: Helper(const Helper &); Helper & operator=(const Helper &);};template <typename T>class Helper<const T &> { public: static const size_t size_on_stack = sizeof(T *) / sizeof(asDWORD); static inline void * deref(void * ptr) { return ptr; } const T & d; private: Helper(const Helper &); Helper & operator=(const Helper &);};template <typename T>class Helper<T *> { public: static const size_t size_on_stack = sizeof(T *) / sizeof(asDWORD); static inline void * deref(void * ptr) { return ptr; } T * d; private: Helper(const Helper &); Helper & operator=(const Helper &);};template <typename T>class Helper<const T *> { public: static const size_t size_on_stack = sizeof(T *) / sizeof(asDWORD); static inline void * deref(void * ptr) { return ptr; } const T * d; private: Helper(const Helper &); Helper & operator=(const Helper &);};#define HELPER_PRIM(PrimT) \ template <> \ class Helper<PrimT> { \ public: \ static const size_t size_on_stack = (sizeof(PrimT) + sizeof(asDWORD) - 1) / sizeof(asDWORD); \ static inline void * deref(void * ptr) { return ptr; } \ PrimT d; \ private: \ Helper(const Helper &); \ Helper & operator=(const Helper &); \ };HELPER_PRIM(bool);HELPER_PRIM(char);HELPER_PRIM(unsigned char);HELPER_PRIM(signed char);HELPER_PRIM(signed short);HELPER_PRIM(unsigned short);HELPER_PRIM(signed long);HELPER_PRIM(unsigned long);HELPER_PRIM(signed long long);HELPER_PRIM(unsigned long long);HELPER_PRIM(float);HELPER_PRIM(double);#undef HELPER_PRIM// 0 arginline void direct_call_worker(void (*fn_ptr)(void), void *, void *, asDWORD *) { fn_ptr(); }template <typename T>inline void direct_call_worker(void (T::*fn_ptr)(void), void * obj, void *, asDWORD *) { T * self = static_cast<T *>(obj); (self->*fn_ptr)();}template <typename R>inline void direct_call_worker(R (*fn_ptr)(void), void *, void * ret_addr, asDWORD *) { new (ret_addr) R(fn_ptr());}template <typename T, typename R>inline void direct_call_worker(R (T::*fn_ptr)(void), void * obj, void * ret_addr, asDWORD *) { T * self = static_cast<T *>(obj); new (ret_addr) R((self->*fn_ptr)());}// 1 argtemplate <typename A0>inline void direct_call_worker(void (*fn_ptr)(A0), void *, void *, asDWORD * stack) { void * ptrs[1]; ptrs[0] = stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); fn_ptr( static_cast<Helper<A0> *>(ptrs[0])->d );}template <typename T, typename A0>inline void direct_call_worker(void (T::*fn_ptr)(A0), void * obj, void *, asDWORD * stack) { void * ptrs[1]; ptrs[0] = stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); T * self = static_cast<T *>(obj); (self->*fn_ptr)( static_cast<Helper<A0> *>(ptrs[0])->d );}template <typename R, typename A0>inline void direct_call_worker(R (*fn_ptr)(A0), void *, void * ret_addr, asDWORD * stack) { void * ptrs[1]; ptrs[0] = stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); new (ret_addr) R(fn_ptr( static_cast<Helper<A0> *>(ptrs[0])->d ));}template <typename T, typename R, typename A0>inline void direct_call_worker(R (T::*fn_ptr)(A0), void * obj, void * ret_addr, asDWORD * stack) { void * ptrs[1]; ptrs[0] = stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); T * self = static_cast<T *>(obj); new (ret_addr) R((self->*fn_ptr)( static_cast<Helper<A0> *>(ptrs[0])->d ));}// 2 argtemplate <typename A0, typename A1>inline void direct_call_worker(void (*fn_ptr)(A0, A1), void *, void *, asDWORD * stack) { void * ptrs[2]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); fn_ptr( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d );}template <typename T, typename A0, typename A1>inline void direct_call_worker(void (T::*fn_ptr)(A0, A1), void * obj, void *, asDWORD * stack) { void * ptrs[2]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); T * self = static_cast<T *>(obj); (self->*fn_ptr)( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d );}template <typename R, typename A0, typename A1>inline void direct_call_worker(R (*fn_ptr)(A0, A1), void *, void * ret_addr, asDWORD * stack) { void * ptrs[2]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); new (ret_addr) R(fn_ptr( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d ));}template <typename T, typename R, typename A0, typename A1>inline void direct_call_worker(R (T::*fn_ptr)(A0, A1), void * obj, void * ret_addr, asDWORD * stack) { void * ptrs[2]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); T * self = static_cast<T *>(obj); new (ret_addr) R((self->*fn_ptr)( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d ));}// 3 argtemplate <typename A0, typename A1, typename A2>inline void direct_call_worker(void (*fn_ptr)(A0, A1, A2), void *, void *, asDWORD * stack) { void * ptrs[3]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[2] = reinterpret_cast<asDWORD *>(ptrs[1]) + Helper<A1>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); ptrs[2] = Helper<A2>::deref(ptrs[2]); fn_ptr( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d, static_cast<Helper<A2> *>(ptrs[2])->d );}template <typename T, typename A0, typename A1, typename A2>inline void direct_call_worker(void (T::*fn_ptr)(A0, A1, A2), void * obj, void *, asDWORD * stack) { void * ptrs[3]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[2] = reinterpret_cast<asDWORD *>(ptrs[1]) + Helper<A1>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); ptrs[2] = Helper<A2>::deref(ptrs[2]); T * self = static_cast<T *>(obj); (self->*fn_ptr)( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d, static_cast<Helper<A2> *>(ptrs[2])->d );}template <typename R, typename A0, typename A1, typename A2>inline void direct_call_worker(R (*fn_ptr)(A0, A1, A2), void *, void * ret_addr, asDWORD * stack) { void * ptrs[3]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[2] = reinterpret_cast<asDWORD *>(ptrs[1]) + Helper<A1>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); ptrs[2] = Helper<A2>::deref(ptrs[2]); new (ret_addr) R(fn_ptr( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d, static_cast<Helper<A2> *>(ptrs[2])->d ));}template <typename T, typename R, typename A0, typename A1, typename A2>inline void direct_call_worker(R (T::*fn_ptr)(A0, A1, A2), void * obj, void * ret_addr, asDWORD * stack) { void * ptrs[3]; ptrs[0] = stack; ptrs[1] = reinterpret_cast<asDWORD *>(ptrs[0]) + Helper<A0>::size_on_stack; ptrs[2] = reinterpret_cast<asDWORD *>(ptrs[1]) + Helper<A1>::size_on_stack; ptrs[0] = Helper<A0>::deref(ptrs[0]); ptrs[1] = Helper<A1>::deref(ptrs[1]); ptrs[2] = Helper<A2>::deref(ptrs[2]); T * self = static_cast<T *>(obj); new (ret_addr) R((self->*fn_ptr)( static_cast<Helper<A0> *>(ptrs[0])->d, static_cast<Helper<A1> *>(ptrs[1])->d, static_cast<Helper<A2> *>(ptrs[2])->d ));}#define asDECLARE_DIRECT_WRAPPER(wrapper_name, func) \ static void wrapper_name(void * obj, void * ret_addr, asDWORD * stack) { \ direct_call_worker(func, obj, ret_addr, stack); \ }typedef void (*DirectCallFn)(void *, void *, asDWORD *);
Here the user would create the wrapped functions like wrapped generic call functions, and pass those to the engine. Calling the functions from a script context would use a variation on the CallGeneric() member function:
int asCContext::CallDirect(int id, void *objectPointer){ asCScriptFunction *sysFunction = engine->scriptFunctions[id]; asSSystemFunctionInterface *sysFunc = sysFunction->sysFuncIntf; DirectCallFn func = reinterpret_cast<DirectCallFn>(sysFunc->func); int popSize = sysFunc->paramSize; asDWORD *args = stackPointer; // Verify the object pointer if it is a class method void *currentObject = 0; if( sysFunc->callConv == ICC_DIRECT_METHOD ) { if( objectPointer ) { currentObject = objectPointer; // Don't increase the reference of this pointer // since it will not have been constructed yet } else { // The object pointer should be popped from the context stack popSize += PTR_SIZE; // Check for null pointer currentObject = (void*)*(size_t*)(args); if( currentObject == 0 ) { SetInternalException(TXT_NULL_POINTER_ACCESS); return 0; } // Add the base offset for multiple inheritance currentObject = (void*)(size_t(currentObject) + sysFunc->baseOffset); // Skip object pointer args += PTR_SIZE; } } void * ret_addr = 0; asCDataType & dt = sysFunction->returnType; if (dt.IsObject() && !dt.IsReference()) { if( dt.GetObjectType()->flags & asOBJ_VALUE ) { ret_addr = engine->CallAlloc(dt.GetObjectType()); } else { ret_addr = &objectRegister; } } else { ret_addr = ®ister1; } isCallingSystemFunction = true; func(currentObject, ret_addr, args); isCallingSystemFunction = false; if (dt.IsObject() && !dt.IsReference()) { if( dt.GetObjectType()->flags & asOBJ_VALUE ) { objectRegister = ret_addr; } } objectType = sysFunction->returnType.GetObjectType(); // Clean up function parameters int offset = 0; for( asUINT n = 0; n < sysFunction->parameterTypes.GetLength(); n++ ) { if( sysFunction->parameterTypes[n].IsObject() && !sysFunction->parameterTypes[n].IsReference() ) { void *obj = *(void**)&args[offset]; if( obj ) { // Release the object asSTypeBehaviour *beh = &sysFunction->parameterTypes[n].GetObjectType()->beh; if( beh->release ) engine->CallObjectMethod(obj, beh->release); else { // Call the destructor then free the memory if( beh->destruct ) engine->CallObjectMethod(obj, beh->destruct); engine->CallFree(obj); } } } offset += sysFunction->parameterTypes[n].GetSizeOnStackDWords(); } // Return how much should be popped from the stack return popSize;}
I think the performance here would more or less match that of the native function calls, if not a bit better since the direct_call_worker()s have no conditional branches, but I haven't actually tried integrating this into my local copy yet.
edit: forum eats \ followed by newline in source, remove the spaces afterwards if you actually want to compile this.
The asCGeneric class is just providing the interface to the stack and some basic validations to make sure the user isn't trying to do things the wrong way. What your code does is just bypassing the logic implemented in the asCGeneric class.
I already have plans to optimize the GetAddressOfArg and GetAddressOfReturnLocation in the asCGeneric class, so that it doesn't have to do as much processing. For example, the offset of each argument should be precalculated upon registration and stored in a simple lookup table. The currently rather complex if conditions should also be reduced to a simple flag. This ought to reduce most of the current overhead.
Still. I'll keep a link to this thread as it might come in handy when I really start working on optimizations for AS.
AngelCode.com - game development and more - Reference DB - game developer references
AngelScript - free scripting library - BMFont - free bitmap font generator - Tower - free puzzle game