Page 3 of 3
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 12:14 pm
by Psychophanta
I meant in order to do a general calling function which is useful for every returned value type.
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 12:50 pm
by wilbert
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.
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 4:07 pm
by Danilo
CallFastF/D/A/U/B/L/I/Q/W/ is OK, isn't it?
Re: How to create Arguments like CallFunctionFast
Posted: Sat Dec 17, 2011 5:41 pm
by wilbert
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
Posted: Mon Jun 01, 2015 9:30 am
by cas
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() 
Re: How to create Arguments like CallFunctionFast
Posted: Mon Jun 01, 2015 9:41 am
by wilbert
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 .
Re: How to create Arguments like CallFunctionFast
Posted: Mon Jun 01, 2015 6:05 pm
by cas
wilbert wrote:libffi
Thanks for suggestion.
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
Posted: Tue Jun 02, 2015 10:44 am
by cas
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.
Re: How to create Arguments like CallFunctionFast
Posted: Wed Jun 03, 2015 6:50 am
by wilbert
This seems to work on OS X but I don't know if it works on Windows or Linux
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
Test
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
Re: How to create Arguments like CallFunctionFast
Posted: Wed Jun 03, 2015 7:47 am
by cas
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?

Re: How to create Arguments like CallFunctionFast
Posted: Wed Jun 03, 2015 8:40 am
by wilbert
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.
Re: How to create Arguments like CallFunctionFast
Posted: Wed Jun 03, 2015 10:25 am
by cas
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:
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(...)
With x86, this code snippet actually (suprisingly) does not crash compiler:
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
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:
Code: Select all
extern int ffi_test(int val1){
return val1*2;
}
and recompiled binaries, then tried this 4 line code in purebasic:
Code: Select all
ImportC "libffi.a"
ffi_test.l(p1.l)
EndImport
Debug ffi_test(25)
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:
- ImportC "libffi-3.2.1\i686-pc-mingw32\.libs\libffi.a"
OpenLibrary(0,"libffi-3.2.1\i686-pc-mingw32\.libs\libffi-6.dll")
for 64bit:
- 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")