News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

fastcall macros

Started by gabor, January 04, 2007, 03:55:00 PM

Previous topic - Next topic

gabor

Hello!

After reading some calling convention and optimization docs I've decided to create fastcall macros to declare and to call procedures with 3 arguments passed in regs. I bumped into a big obstacle: how can I compare a macro argument to a text? Let's have a look on my fastcall macro:

;===============================================================
;
; Calling a fastcall procedure
;  parameters are passed in eax,ecx,edx and the stack
;
;===============================================================
fastcall  MACRO procname:REQ,arglist:VARARG
LOCAL count
LOCAL bytes
LOCAL regtext

count = 0
bytes = 0
regtext TEXTEQU <eaxecxedx>

FOR arg,<arglist>
count = count + 1

IF count EQ 1
mov eax,arg
ENDIF
IF count EQ 2
mov ecx,arg
ENDIF
IF count EQ 3
mov edx,arg
ENDIF
IF count GT 3
push arg
bytes = bytes + sizeof &arg
ENDIF
ENDM

call &procname

IF bytes NE 0
add esp,bytes
ENDIF
ENDM


This works but must be improved. If the first parameter was already eax, then it would insert the errorous "mov eax,eax" line. I need to check whether the arg was already eax (or ecx, edx).
I tried this:

IF arg NE <eax>
mov eax,arg
ENDIF

This does not work.  :'(
Basicly I need a method to be able to check whether the argument passed to the macro was a register, and which register it was...


The point here is to hide the fastcall thing as much as possible, so for the declaration the best solution would be

__fastcall procedure PROC USES register,argumentlist

I am afraid to process this kind of syntax in a macro would be too complex, so I simply leave it (thus giving opportunity for coding errors  :( )
Any idea about this second problem is appreciated!

Greets, Gábor

u


ifdif <arg>,<eax>
mov eax,arg
endif
Please use a smaller graphic in your signature.

Vortex

Nice work Gabor. You would like to have a look at libraries exporting FASTCALL API functions, an example is dnsapi.lib :

LIBRARY dnsapi
EXPORTS
"@DnsGetDomainName@4"
"@DnsIsAMailboxType@4"
"@DnsIsStatusRcode@4"
"@DnsMapRcodeToStatus@4"
"@DnsStatusString@4"
"@DnsUnicodeToUtf8@16"
"@DnsUtf8ToUnicode@16"
"@Dns_ReadPacketName@28"
"@Dns_ReadPacketNameAllocate@28"
"@Dns_SkipPacketName@8"
"@Dns_WriteDottedNameToPacket@24"


POASM supports the FASTCALL calling convention.

u

#3
With just a bit of argument-verification:
see last post
Note that I removed the "add esp,bytes", since I think it'll be faster to base on stdcall rather than c-call.
Please use a smaller graphic in your signature.

gabor

Hello!

Thank you very much for the posts, they are very helpful!

Vortex!
Thanks for the suggestion! How could I have a look on those lib? (Sorry for the lame question.) Will that help me in the declaration of fastcall procedures?
PoASM is really great for supporting this, I guess I have to check it out more seriously.

Ultrano!
I used stdcall because AFAIK this is the only way of calling a procedure with variable argument count.  I am afraid what you posted won't work because the argument are passed on the stack but the called procedure won't recognize how many args were passed. Of course I may be wrong...

Still, my greater problem is the declaration of fastcall procedures. I want it to look like a normal PROTO or PROC declaration (so all parameters given) but processing the macro would remove the first 3 parameters (since they are passed via registers).

Thanks again!

Greets, Gábor

u

Fixed and added:

fastcall  MACRO procname:REQ,arglist:VARARG
LOCAL count,arg,tempnum
LOCAL usedEAX,usedECX


usedEAX=0
usedECX=0


;----[ first, push excessive to stack ]--------[
count = 0
FOR arg,<arglist>
count = count + 1
endm
if count GT 3
tempnum = count
while tempnum gt 3
count = 0
for arg,<arglist>
count = count+1
if count eq tempnum
push arg
endif
endm
tempnum = tempnum-1
endm
endif
;----------------------------------------------/

;----[ set registers ]------------------------[
count = 0
FOR arg,<arglist>
count = count + 1
IF count EQ 1
ifdifi <arg>,<eax>
mov eax,arg
usedEAX=1
endif
ENDIF
IF count EQ 2
ifdifi <arg>,<ecx>
mov ecx,arg
usedECX=1
endif
ifidni <arg>,<eax>
if usedEAX
.err <Second parameter can't be EAX>
endif
endif
ENDIF
IF count EQ 3
ifdifi <arg>,<edx>
mov edx,arg
endif
ifidni <arg>,<eax>
if usedEAX
.err <Third parameter can't be EAX>
endif
endif
ifidni <arg>,<ecx>
if usedECX
.err <Third parameter can't be ECX>
endif
endif
ENDIF
endm
;---------------------------------------------/

call &procname

ENDM


FastCall macro args:VarArg
local arg,text
local count
count=0
for arg,<args>
if count eq 0
arg CATSTR <eax>
elseif count eq 1
arg CATSTR <ecx>
elseif count eq 2
arg CATSTR <edx>
elseif count eq 3
text CATSTR <arg>
else
text CATSTR text,<,>,<arg>
endif

count = count+1
endm
if count LT 4
exitm <>
else
exitm <text>
endif
endm



Example usage:

blah proc FastCall(param1,param2,param3,param4,param5,param6)
print param1
print param2
print param3
print param4
print param5
print param6
ret
blah endp

main proc

fastcall blah,17,2,3,10,5,999

ret
main endp


Just watch-out when overwriting eax,ecx and edx in "blah" :)
Please use a smaller graphic in your signature.

Vortex

Hi gabor,

You are welcome. Attached file includes dnsapi.def and dnsapi.lib  Notice that FASTCALL functions should have a prepended @ symbol :

@DnsGetDomainName@4

PS : I mentioned about this library because it's an example of FASTCALL API functions.

[attachment deleted by admin]

Ghirai

MASM32 Project/RadASM mirror - http://ghirai.com/hutch/mmi.html

gabor

Hello again!

Vortex, thanks for the lib, but I am afraid it won't help me too much since it only contains the names of the fastcall procs. What I need is a macro that hides the fastcall mechanism. Like

__fastcall procname PROTO arg1,arg2,arg3,arg4
is change via macros into
procname PROTO arg4

The first 3 args are passed in eax,ecx,edx, so they won't be passed via the stack.

Of course since I would use the fastcall macro to invoke the proc I can simply leave off the PROTO. (I could put it into a comment just to show the signature of the proc.)

Really important is this:
__fastcall procname PROC USES reglist,arg1,arg2,arg3,arg4
I understand that to parse such a text could be too much so the approach Ultrano gave would be just fine:
procname PROC USES reglist,@Fastcall(arg1,arg2,arg3,arg4)

There is 1 problem with this.
arg CATSTR <eax>
This causes that the text <arg> will be substitued later also, at unwanted places in the code. This gives errors.
An example:
MyProc1  PROC @FastCall(arg1,arg2,arg3,arg4) ; causes arg1=<eax>, arg2=<ecx>, arg3=<edx>

; later in the code

MyProc2 PROC arg1:DWORD,arg2:DWORD ; is changed to MyProc2 PROC eax:DWORD,ecx:DWORD --> gives sytax error


Is it somehow possible to make a literal (arg1,arg2) lose its value, to undefine it?

Greets, Gábor

u

The only way I found is to later rename Arg1 into Arg1__


FastCall macro args:VarArg
local arg,text
local count

count=0
for arg,<args>
if count eq 0
$FastCall$Z1 CATSTR <arg>,<__>
$FastCall$P1 textequ <eax>
arg textequ <$FastCall$P1>
elseif count eq 1
$FastCall$Z2 CATSTR <arg>,<__>
$FastCall$P2 textequ <ecx>
arg textequ <$FastCall$P2>
elseif count eq 2
$FastCall$Z3 CATSTR <arg>,<__>
$FastCall$P3 textequ <edx>
arg textequ <$FastCall$P3>
elseif count eq 3
text CATSTR <arg>
else
text CATSTR text,<,>,<arg>
endif
count = count+1
endm

if count EQ 0
$FastCall$P1 textequ $FastCall$Z1
$FastCall$P2 textequ $FastCall$Z2
$FastCall$P3 textequ $FastCall$Z3
endif

if count LT 4
exitm <>
else
exitm <text>
endif
endm


And you'll have to use FastCall like this:

blah proc FastCall(param1,param2,param3,param4,param5,param6)
print param1
print param2
print param3
print param4
print param5
print param6

ret
blah endp FastCall()



Btw, the "fastcall" calling method doesn't actually boost your code even with 2 cycles...
Please use a smaller graphic in your signature.