I meant in order to do a general calling function which is useful for every returned value type.Danilo wrote:http://msdn.microsoft.com/en-us/library/k2b2ssfy.aspxPsychophanta wrote:What about the returned value?
It could be a float, or double, or quad...
How to create Arguments like CallFunctionFast
- Psychophanta
- Always Here
- Posts: 5153
- Joined: Wed Jun 11, 2003 9:33 pm
- Location: Anare
- Contact:
Re: How to create Arguments like CallFunctionFast
Re: How to create Arguments like CallFunctionFast
You can't create a single function for all return types.
Floating point is different because it needs to pop a floating point value.
What probably can be done is use different prototypes that are pointed to the same function.
Floating point is different because it needs to pop a floating point value.
What probably can be done is use different prototypes that are pointed to the same function.
Re: How to create Arguments like CallFunctionFast
CallFastF/D/A/U/B/L/I/Q/W/ is OK, isn't it?
Re: How to create Arguments like CallFunctionFast
Does this work for you ?
Code: Select all
Enumeration 1
#VAT_string
#VAT_long
#VAT_quad
#VAT_float
#VAT_double
EndEnumeration
Structure VARARGS ; 16 bytes
arg_size_l.l ; offset 0
StructureUnion ; offset 4
d.d
f.f
i.i
l.l
q.q
*p.i
EndStructureUnion
type.l ; offset 12
EndStructure
Procedure.l CallFastL(hFunc,Array Args.VARARGS(1))
Protected addr.l=@Args(), size.l=ArraySize(Args())
EnableASM
MOV eax, hFunc
MOV ecx, size
MOV edx, addr
!call CallFast_Main
DisableASM
ProcedureReturn
!CallFast_Main:
; exit if function pointer isn't passed
!and eax, eax
!jz CallFast_NoFunc
; count the number of bytes to copy to align the
; stack to 16 bytes for OS X
!push ebx
!push esi
!xor ebx, ebx
!shl ecx, 4
!jz CallFast_NoCount
!mov esi, ecx
!CallFast_CountSizes:
!add ebx, [edx + ecx]
!sub ecx, 16
!jnz CallFast_CountSizes
!mov ecx, esi
!CallFast_NoCount:
; make sure the stack is 16 bytes aligned after
; copying all arguments
!push ebp
!mov ebp, esp
!shl ebx, 2
!sub esp, ebx
!and esp, 0xfffffff0
!add esp, ebx
!and ecx, ecx
!jz CallFast_NoCopy
!CallFast_CopyArgs:
; copy the actual arguments
!lea esi, [edx + ecx]
!mov ebx, [esi]
!CallFast_CopyArg:
!push dword [esi + ebx * 4]
!dec ebx
!jnz CallFast_CopyArg
!sub ecx, 16
!jnz CallFast_CopyArgs
!CallFast_NoCopy:
; make the funcion call
!call eax
!mov esp, ebp
!pop ebp
!pop esi
!pop ebx
!CallFast_NoFunc:
!ret
EndProcedure
Procedure.q CallFastQ(hFunc,Array Args.VARARGS(1))
Protected addr.l=@Args(), size.l=ArraySize(Args())
EnableASM
MOV eax, hFunc
MOV ecx, size
MOV edx, addr
!call CallFast_Main
DisableASM
ProcedureReturn
EndProcedure
Procedure.f CallFastF(hFunc,Array Args.VARARGS(1))
Protected addr.l=@Args(), size.l=ArraySize(Args())
EnableASM
MOV eax, hFunc
MOV ecx, size
MOV edx, addr
!call CallFast_Main
DisableASM
ProcedureReturn
EndProcedure
Procedure VAstring(*p.VARARGS,*str)
*p\type = #VAT_string : *p\arg_size_l = SizeOf(Integer) >> 2 : *p\p = *str
EndProcedure
Procedure VAlong(*p.VARARGS,lng)
*p\type = #VAT_long : *p\arg_size_l = 1 : *p\l = lng
EndProcedure
Procedure VAquad(*p.VARARGS,quad.q)
*p\type = #VAT_quad : *p\arg_size_l = 2 : *p\q = quad
EndProcedure
Procedure VAfloat(*p.VARARGS,f.f)
*p\type = #VAT_float : *p\arg_size_l = 1 : *p\f = f
EndProcedure
Procedure VAdouble(*p.VARARGS,d.d)
*p\type = #VAT_double : *p\arg_size_l = 2 : *p\d = d
EndProcedure
; code example (Windows)
Dim a.VARARGS(5)
VAstring(@a(1),@"hello world! arg1 = %d, arg2 = %lld, arg3 = %f, arg4 = %s")
VAlong (@a(2),123)
VAquad (@a(3),456)
VAdouble(@a(4),1.234)
VAstring(@a(5),@"PureBasic")
printf.l
CompilerIf #PB_Compiler_Unicode
!extrn _wprintf
!push dword _wprintf
CompilerElse
!extrn _printf
!push dword _printf
CompilerEndIf
!pop dword [v_printf]
OpenConsole()
CallFastL(printf,a())
Input()
Re: How to create Arguments like CallFunctionFast
Just what i was looking for. Unfortunately, it does not work with 64bit compiler.
Maybe someone could throw in some CompilerIf's here and there to make it compatible...
And maybe add CallFastD()
Maybe someone could throw in some CompilerIf's here and there to make it compatible...

And maybe add CallFastD()

Re: How to create Arguments like CallFunctionFast
It's not that simple to make it work for 64 bit.
It uses a more complicated calling convention. Some arguments need to be passed by normal registers, floats need to be passed by xmm registers, if there are a lot of arguments part of them need to be passed by stack.
Maybe you could take a look at libffi .
It uses a more complicated calling convention. Some arguments need to be passed by normal registers, floats need to be passed by xmm registers, if there are a lot of arguments part of them need to be passed by stack.
Maybe you could take a look at libffi .
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: How to create Arguments like CallFunctionFast
Thanks for suggestion.wilbert wrote:libffi
I can compile it on linux, and i see there is xcode project inside so on mac os it should also be no problem.
But i can not figure out how to build it on windows platform. I tried with pellesc and tdm-gcc since i need both x86 and x64 static libraries and also i do not want extra dependencies (cygwin, msvc, ...).
And i tried to search for precompiled binaries but i only found some very old versions.
Re: How to create Arguments like CallFunctionFast
I finally managed to compile both 32bit (i686-pc-mingw32) and 64bit (x86_64-w64-mingw64) static libs.
But it does not work with 32bit purebasic. I get "Pelles Linker has stopped working" and "POLINK: warning: Multiple '.eh_frame' sections found with different flags (0x40000040 and 0xc0000040)."
With 64bit purebasic it works but it looks like i did not translate ffi.h include correctly to purebasic and i can only call functions with zero arguments, else i get invalid memory access error.
I would greatly appreciate it if anyone could provide purebasic include (ffi.h) or help me compile 32bit static lib.
But it does not work with 32bit purebasic. I get "Pelles Linker has stopped working" and "POLINK: warning: Multiple '.eh_frame' sections found with different flags (0x40000040 and 0xc0000040)."
With 64bit purebasic it works but it looks like i did not translate ffi.h include correctly to purebasic and i can only call functions with zero arguments, else i get invalid memory access error.
I would greatly appreciate it if anyone could provide purebasic include (ffi.h) or help me compile 32bit static lib.
Re: How to create Arguments like CallFunctionFast
This seems to work on OS X but I don't know if it works on Windows or Linux
Test
Code: Select all
Enumeration
#FFI_OK
#FFI_BAD_TYPEDEF
#FFI_BAD_ABI
EndEnumeration
Enumeration
#FFI_FIRST_ABI
#FFI_SYSV
#FFI_LAST_ABI
#FFI_DEFAULT_ABI = #FFI_SYSV
EndEnumeration
Structure ffi_type Align #PB_Structure_AlignC
size.i
alignment.u
type.u
*elements.ffi_type
EndStructure
Structure ffi_cif Align #PB_Structure_AlignC
abi.l
nargs.l
*ptr_arg_types.ffi_type
*rtype.ffi_type
bytes.l
flags.l
EndStructure
PrototypeC.i __ffi_prep_cif(*cif.ffi_cif, abi.l, nargs, *rtype.ffi_type, *ptr_atypes.ffi_type)
PrototypeC __ffi_call(*cif.ffi_cif, *fn, *rvalue, *ptr_avalue)
Macro M_DQ
"
EndMacro
Macro M_GetFunction(name)
Global name.__#name = GetFunction(0, M_DQ#name#M_DQ)
EndMacro
Macro M_GetVariable(name)
Global name.i = GetFunction(0, M_DQ#name#M_DQ)
EndMacro
If OpenLibrary(0, "libffi.dylib")
; get variables
M_GetVariable(ffi_type_void)
M_GetVariable(ffi_type_uint8)
M_GetVariable(ffi_type_sint8)
M_GetVariable(ffi_type_uint16)
M_GetVariable(ffi_type_sint16)
M_GetVariable(ffi_type_uint32)
M_GetVariable(ffi_type_sint32)
M_GetVariable(ffi_type_uint64)
M_GetVariable(ffi_type_sint64)
M_GetVariable(ffi_type_float)
M_GetVariable(ffi_type_double)
M_GetVariable(ffi_type_pointer)
; get functions
M_GetFunction(ffi_prep_cif)
M_GetFunction(ffi_call)
EndIf
CompilerIf SizeOf(Integer) = 8
Global ffi_type_uint.i = ffi_type_uint64
Global ffi_type_sint.i = ffi_type_sint64
CompilerElse
Global ffi_type_uint.i = ffi_type_uint32
Global ffi_type_sint.i = ffi_type_sint32
CompilerEndIf
Code: Select all
ProcedureC.c foo(x.i, y.f)
ProcedureReturn x - y
EndProcedure
Define cif.ffi_cif
Dim *arg_types.ffi_type(1)
Dim *arg_values(1)
*arg_types(0) = ffi_type_uint
*arg_types(1) = ffi_type_float
status = ffi_prep_cif(@cif, #FFI_DEFAULT_ABI, 2, ffi_type_uint8, @*arg_types())
If status <> #FFI_OK
Debug "error"
End
EndIf
arg1.i = 42
arg2.f = 5.1
*arg_values(0) = @arg1
*arg_values(1) = @arg2
ffi_call(@cif, @foo(), @result, @*arg_values())
Debug result
Last edited by wilbert on Wed Jun 03, 2015 8:32 am, edited 2 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: How to create Arguments like CallFunctionFast
Thanks. Yes, it works on windows but i had to comment out CloseLibrary(0).
Now only if i could figure out how to make 32bit static lib to work...
When i compile libffi, it generates "libffi.a" and "libffi-6.dll" at the same time.
64bit version - both static and dynamic lib work perfectly with purebasic.
32bit version - only dynamic lib works, static lib crashes linker. I fixed "multiple .eh_frame sections" error with gcc compiler flag (disabled creation of unwind tables). But now i get empty error message.
After i press F5 to Compile/Run:

And after a couple of seconds it shows this:

After i click on "Close program":

Could this be a bug?
Now only if i could figure out how to make 32bit static lib to work...
When i compile libffi, it generates "libffi.a" and "libffi-6.dll" at the same time.
64bit version - both static and dynamic lib work perfectly with purebasic.
32bit version - only dynamic lib works, static lib crashes linker. I fixed "multiple .eh_frame sections" error with gcc compiler flag (disabled creation of unwind tables). But now i get empty error message.
After i press F5 to Compile/Run:

And after a couple of seconds it shows this:

After i click on "Close program":

Could this be a bug?

Re: How to create Arguments like CallFunctionFast
I don't know if you can use OpenLibrary for static files.
You should be able to use them using ImportC but in that case the import of external variables didn't work on OS X.
You should be able to use them using ImportC but in that case the import of external variables didn't work on OS X.
Windows (x64)
Raspberry Pi OS (Arm64)
Raspberry Pi OS (Arm64)
Re: How to create Arguments like CallFunctionFast
I use ImportC, but it does not work with 32bit purebasic. This is simplified pseudocode to show how i do it and it works perfectly with 64bit:
With x86, this code snippet actually (suprisingly) does not crash compiler:
So, all good if i import only variables. But when i import ffi_prep_cif() or ffi_call() then Pelles Linker crashes. Weird.
Then i added simple function to libffi c source:
and recompiled binaries, then tried this 4 line code in purebasic:
I get correct output ("50" in this case), but only with 64bit purebasic. 32bit purebasic - pelles linker crash.
So, to summarize:
- ImportC for external variables works with both 32bit and 64bit purebasic
- ImportC for external functions works only with 64bit purebasic; linker crashes with 32bit
- dynamic libraries work perfectly with both 32bit and 64bit purebasic but i want static lib
And yes,
- i tried Import instead of ImportC
- i am sure that i am not making mistake by importing 64bit static lib with 32bit purebasic version (or vice versa), i use CompilerIf and use these:
for 32bit:
Code: Select all
ImportC "libffi.a"
ffi_prep_cif(...)
ffi_call(...)
ffi_type_sint8
ffi_type_uint8
ffi_type_sint16
ffi_type_uint16
ffi_type_sint32
...
EndImport
...
ffi_prep_cif(@cif,#FFI_WIN64,nArgs,@ffi_type_sint32,*argTypes)
ffi_call(...)
Code: Select all
ImportC "libffi.a"
ffi_type_sint8
ffi_type_uint8
ffi_type_...
EndImport
Define *ptr.ffi_type=@ffi_type_uint32
Debug *ptr\size ;outputs 4
Debug *ptr\type ;outputs 9
Debug *ptr\alignment;outputs 4
Then i added simple function to libffi c source:
Code: Select all
extern int ffi_test(int val1){
return val1*2;
}
Code: Select all
ImportC "libffi.a"
ffi_test.l(p1.l)
EndImport
Debug ffi_test(25)
So, to summarize:
- ImportC for external variables works with both 32bit and 64bit purebasic
- ImportC for external functions works only with 64bit purebasic; linker crashes with 32bit
- dynamic libraries work perfectly with both 32bit and 64bit purebasic but i want static lib
And yes,
- i tried Import instead of ImportC
- i am sure that i am not making mistake by importing 64bit static lib with 32bit purebasic version (or vice versa), i use CompilerIf and use these:
for 32bit:
- ImportC "libffi-3.2.1\i686-pc-mingw32\.libs\libffi.a"
OpenLibrary(0,"libffi-3.2.1\i686-pc-mingw32\.libs\libffi-6.dll")
- ImportC "libffi-3.2.1\x86_64-w64-mingw64\.libs\libffi.a"
OpenLibrary(0,"libffi-3.2.1\x86_64-w64-mingw64\.libs\libffi-6.dll")