Advertisement

Current WIP (rev 383) doesn't compile with AS_MAX_PORTABILITY

Started by March 24, 2009 09:59 AM
9 comments, last by SiCrane 15 years, 8 months ago
Pretty much what the thread title says. The current WIP (rev 383) doesn't compile when AS_MAX_PORTABILITY is defined. as_scriptstruct.cpp and as_arrayobject.cpp have some references to the deprecated asIScriptGeneric::GetReturnPointer() function.
Thanks for alerting me. I'll have those fixed right away.

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

Advertisement
Sounds good. Thanks.

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.
I was planning to do it, but haven't gotten to it yet. Thanks for providing the code for me.

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

I've checked in the latest changes.

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.
Advertisement
To be honest, I'm not too sure about it either. I took it pretty much as George sent it to me, and just made a few minor changes.

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

This does give me an idea of another way of approaching function binding. Basically the idea is take AngelScript internal stack and use that to build the function call directly rather than using an indirection like the asCScriptGeneric object. The concrete types of the arguments can be used to find the relative position of the arguments on the AngelScript stack from a stack pointer and then those can be dereferenced in a function call.
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 = &register1;  }  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.
Perhaps the biggest problem with this is that it requires exact knowledge of the AngelScript internal calling convention. Other than that this is basically just an optimized version of what the current implementation does.

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

Well, I never claimed it was a practical idea. :) As you may have noticed, I just enjoy messing around with template programming.

This topic is closed to new replies.

Advertisement