EuroAssembler Index Manual Download Source Macros


Sitemap Links Forum Tests Projects

fastcall.htm
Enumeration
%FastCall
Macros
ClearLocalVar
EndProcedure
Invoke
LocalVar
Procedure

This file can be included to 64bit Windows programs written in EuroAssembler. The library contains macroinstructions which extend generic (pseudo)instruction CALL, PROC, ENDPROC.

Macroinstructions Invoke, Procedure and EndProcedure hide the prologue and epilogue of Microsoft x64 calling convention [CallingConv] , where the arguments are pushed backwards and they are removed by the caller.
The first 4 arguments are provided in registers RCX, RDX, R8, R9.

Macroinstructions in this library communicate with each other at assembly time using the preprocessing %variables %LocalFrameSize and %NrOfArg_ProcedureName.


fastcall HEAD
↑ %FastCall

Implementation of FastCall convention in €ASM allows to use formal %names for accessing Procedure parameters and local stack-memory variables.

Number of arguments provided in macro Invoke must exactly match the number of arguments declared in macro Procedure. Nevertheless, the number of arguments internally supplied by invokation is at least four, and they create shadow space on the stack. When the Procedure is declared with 0, 1, 2 or 3 parameters, the missing arguments are surrogated with the current contents of RCX, RDX, R8 or R9, respectively.

Macro Invoke takes care of stack alignment to OWORD just before execution of instruction CALL MyFunction (step 7. in the following example). RSP at the Invoke entry may already have been OWORD aligned, or it may be only QWORD aligned (=unaligned). That is why RSP will be pushed once or twice in the beginning. Instructions of step 2. (PUSH RSP and ADD [RSP],8) are emitted always but they will be skipped at run-time

  1. when number of arguments is odd (5,7,9..) and RSP is OWORD aligned,
  2. or when number of arguments is even (4,6,8..) and RSP is unaligned.
Thanks to this, RSP is always OWORD aligned before step 7. (CALL) is executed, and the stack is restored to equilibrium (OrigRSP) after Invoke, no matter if it was OWORD aligned or not.

Macro Invoke will push all parameters on stack in reversed order, and only then it loads registers for parameter transfer (RCX,RDX,R8,R9) from the stack. Thanks to this, invokation parameters may be provided in arbitraty order by any registers, including those scratch ones.

Macro Procedure copies first four parameters into shadow space. Thanks to this, those parameters are available not only in RCX,RDX,R8,R9, but as stack variables %Arg1,%Arg2,%Arg3,%Arg4 alias their formal operand %names, too. This is redundant when the Procedure is invoked by the macro Invoke from this library, but it may be useful if our Procedure is invoked from 3rd party code, for instance as a callback from MS Windows.

Such implementation of FastCall convention is not much economical but it is compatible and allows to Invoke 64bit functions from 3rd party libraries or from Windows ABI, and it allows to create FastCall functions invokable from other libraries or as system callback procedures.

If your procedure is called from your own programs only, I recommend to prefer more efficient private register calling convention, tailored to the function's needs.

Example
; Definition of procedure MyFunction with three arguments ; and two local stack variables with sizes 8 and 16: MyFunction Procedure Arg1, Arg2, Arg3 LocalVar1 LocalVar LocalVar2 LocalVar Size=16 ; Arguments Arg1,Arg2,Arg3 are loaded in RCX,RDX,R8. RBP is pushed on stack. ; Programmer should now: PUSH all calee-save registers which are used in the procedure body, ; emit instructions of the procedure body, ; load result into return-register (RAX or XMM0), ; POP used callee-save registers. EndProcedure MyFunction ; This macro discards local variables, pops RBP and returns.

Following diagram shows the stack frame created by invoking MyFunction procedure defined above.
Prologue of MyFunction Procedure Arg1, Arg2, Arg3 will assign "global" %variables %NrOfArg_MyFunction %SETA 4 and %LocalFrameSize %SETA 8+16 , which will be used later by epilogue in EndProcedure MyFunction.

RSP after the step | Prologue in 10 steps |Stack frame Epilogue in 5 steps | 0.>┌─────────┐<16.continue below Invoke with OrigRSP 1.PUSH RSP │ OrigRSP │ 1.>├─────────┤ 2.PUSH RSP; ADD [RSP],8 │ OrigRSP │ 16.POP RSP 2.>├─────────┤<15. 3.PUSH R9 │ R9 │ 3.>├─────────┤ 4.PUSH Arg3 │ %Arg3 │ 4.>├─────────┤ 5.PUSH Arg2 │ %Arg2 │ 5.>├─────────┤ 6.PUSH Arg1 │ %Arg1 │ 15.LEA RSP,[RSP+4*8] 6.>├─────────┤<14. 7.CALL MyFunction │return VA│ 14.RET 7.>├─────────┤<13. 8.PUSH RBP; MOV RBP,RSP │ OrigRBP │ 13.POP RBP 8.>├─────────┤<12. 9.SUB RSP,SIZE#LocalVar1 │LocalVar1│ 9.>├─────────┤ │ │ 10.SUB RSP,SIZE#LocalVar2 │LocalVar2│ │ │ 12.MOV RSP,RBP 10.>└─────────┘<11. 11.Procedure body
Step 1 is executed always.
Step 2 is executed only if OrigRSP is OWORD-aligned (otherwise it's jumped over).
Steps 3..6 create shadow space.
Steps 1..7 are emitted by macro Invoke.
Step 8 is emitted by macro Procedure.
Steps 9..10 are emitted by macro LocalVar.
Steps 11 are statements of the procedure body supplied by the programmer.
Steps 12..14 are emitted by macro EndProcedure.
Steps 15..16 are emitted by macro Invoke.
; Assignment of generic formal names of Procedure's arguments:
%Arg8    %SET RBP+72
%Arg7    %SET RBP+64
%Arg6    %SET RBP+56
%Arg5    %SET RBP+48
%Arg4    %SET RBP+40
%Arg3    %SET RBP+32
%Arg2    %SET RBP+24
%Arg1    %SET RBP+16
↑ Procedure Operand1, Operand2,,,

This macro declares FastCall-procedure prologue which, unlike ordinary pseudoinstruction PROC, accepts ordinal operands.
Using of the macro Procedure requires the corresponding EndProcedure be used in the same program.

Macro operands will be assigned to preprocessing %variables using the operand formal name prefixed with a percent sign.

Input
Label of Procedure statement is mandatory, it gives the procedure a callable name.
Operands must be PUSHQ-able, their number is not limited.
Output
Macroinstruction will define global symbol and assign formal operand names to %variables.
It does not push registers, programmer is responsible for saving and restoring calee-save registers RBX, RSI, RDI, R12, R13, R14, R15, if they are used in procedure body.
RBP must not be changed while the procedure body uses preprocessing %variables referring the formal operands names, or if it declares and uses temporary stack variables defined with LocalVar.
Example
Move Procedure Source,Destination,Size ; Declaration of procedure Move and its formal arguments. PUSH RSI,RDI ; Those registers must be unchanged by fastcall procedure. MOV RSI,[%Source] MOV RDI,[%Destination] MOV RCX,[%Size] REP MOVSB POP RDI,RSI ; Restore calee-save registers. EndProcedure Move ; Return from the procedure Move. ;; Instead of %variables created from operand formal names it could alternatively use generic names: Move Procedure Source,Destination,Size ; Declaration of procedure Move and its formal arguments. PUSH RSI,RDI ; Those registers must be unchanged by fastcall procedure. MOV RSI,[%Arg1] MOV RDI,[%Arg2] MOV RCX,[%Arg3] REP MOVSB POP RDI,RSI ; Restore calee-save registers. EndProcedure Move ; Return from the procedure Move. ;; With the knowledge of FastCall convention it may be also written more efficiently: Move Procedure Source,Destination,Size ; Declaration of procedure Move and its formal arguments. PUSH RSI,RDI ; Those registers must be unchanged by fastcall Procedure. MOV RSI,RCX MOV RDI,RDX MOV RCX,R8 REP MOVSB POP RDI,RSI ; Restore calee-save registers. EndProcedure Move ; Return from the procedure Move. Macro Move Procedure in the previous example will assign %NrOfArg_Move %SET 3 ; This %variable propagates to the corresponding macro EndProcedure Move. %LocalFrameSize %SETA 0 ; This %variable propagates to macro LocalVar, if used in Move Procedure. %Source %SET RBP+16 ; This %variable represens formal name of Arg1. %Destination %SET RBP+24 ; This %variable represens formal name of Arg2. %Size %SET RBP+32 ; This %variable represens formal name of Arg3.
See also
32bit StdCall macro with the same name Procedure.
Procedure %MACRO Operands
 LblCheck %IF "%:" == ""
           %ERROR ID=5921, 'Macro "Procedure" requires a label.'
           %EXITMACRO Procedure
          %ENDIF LblCheck
          %%NrOfArg_%: %SETX %#    ; Name of this %variable must be unique (for the case when Procedures are nested).
          %LocalFrameSize %SETA 0  ; This %variable maintains the size of local stack-memory variables.
          ArgNr %FOR 1..%#, STEP=+1
            %%%*{%ArgNr} %SETX RBP+(8+%ArgNr<<3) ; Assign the formal name to a %variable.                              >>
          %ENDFOR ArgNr
      %::: PROC %=*, NESTINGCHECK=OFF ; Open the namespace and define entry symbol from macro label %: as GLOBAL.
           PUSH RBP
           MOV RBP,RSP                ; Initialize the frame pointer.
           %IF %# >= 1 && '%1' !== 'RCX' ; Make sure that first four arguments are accessible
             MOV [RBP+16],RCX            ;  both in registers and on stack as %Arg1..%Arg4.
           %ENDIF
           %IF %# >= 2 && '%2' !== 'RDX'
             MOV [RBP+24],RDX
           %ENDIF
           %IF %# >= 3 && '%3' !== 'R8'
             MOV [RBP+32],R8
           %ENDIF
           %IF %# >= 4 && '%4' !== 'R9'
             MOV [RBP+40],R9
           %ENDIF
        %ENDMACRO Procedure
↑ LocalVar Size=8
This macro declares local memory variable with the given Size allocated on machine stack and assigns its name to a preprocessing %variable with name derived from the label of macro LocalVar.
Input
Plain identifier must be defined as a label of LocalVar statement. It does not need to be unique in the program because it does not declare assembly symbol. The name will be prefixed with % and used as preprocessing %variable for addressing parameters withing the procedure body.
Size=8 specifies how many bytes should be reserved for the local stack variable. The Size is automatically rounded up to the nearest multiple of 8.
Output
Macro will define a preprocessing %variable with the name which was defined as the label but it is now prefixed with percent sign. Then it will emit machine instruction SUB RSP,%LocalVarSize to reserve room on the machine stack.
Macro LocalVar also maintains the "global" preprocessing %variable %LocalFrameSize which was initialized in macro Procedure.
Example
ProcName: Procedure Param1 ; This example uses two local stack variables with sizes 8 and 1K. BlockSize LocalVar ; %BlockSize is now assigned with RBP-8. Block LocalVar Size=1024 ; %Block is now assigned with RBP-1032. ClearLocalVar ; Fill %Block and %BlockSize with 0. MOV [%BlockSize],1K, DATA=QWORD LEA RAX,[%Block] ; etc... EndProcedure ProcName
LocalVar %MACRO Size=8
 LblCheck %IF "%:" == ""
            %ERROR ID=5922, 'Macro "LocalVar" requires a label.'
            %EXITMACRO LocalVar
          %ENDIF LblCheck
OrdCheck  %IF %#
            %ERROR ID=5923, 'Macro "LocalVar" does not expect ordinal parameters.'
          %ENDIF OrdCheck
       %: %COMMENT         ; This empty block makes the label of macro invokation void,
          %ENDCOMMENT %:   ;  so it does not declare a symbol.
          %LocalVarSize   %SETA (%Size + 7) & ~7 ; Round up to he nearest multiple of QWORD size.
          %LocalFrameSize %SETA %LocalFrameSize + %LocalVarSize
          SUB RSP, %LocalVarSize
     %%%: %SETX RBP-%LocalFrameSize ; Assign formal %name to the id %: specified as LocalVar label.
         %ENDMACRO LocalVar
↑ ClearLocalVar
This macro zeroes all variables on stack previously declared with LocalVar. ClearLocalVar should be expanded right after LocalVar declarations, before any stack operations are made.
We could as well decide to initialize each local variable individually, in this case the macro ClearLocalVar will not be used in the Procedure body at all.
Input
Macro does not use explicite parameters. Pointer to the cleared memory is specified with RSP, cleared size is specified with "global" variable %LocalFrameSize.
Output
RAX=RCX=0,
RDI= RSP + %LocalFrameSize
ClearLocalVar %MACRO
       %IF %LocalFrameSize         ; Do nothing if no LocalVar was used in this Procedure.
         PUSH RDI
          MOV RDI,RSP
          ADD RDI,8
          MOV ECX,%LocalFrameSize/8
          XOR EAX,EAX
          REP STOSQ
         POP RDI
       %ENDIF
    %ENDMACRO ClearLocalVar
↑ EndProcedure ProcName

Macro EndProcedure terminates context of the previously opened Procedure . This epilogue of FastCall convention will discard local variables defined with LocalVar using machine instructions MOV RSP,RBP, restore caller's frame pointer with POP RBP and then return with near RET to the parent code which the Procedure was Invoked from. Operand are not removed from stack, that's the job of Invoke.

Programmer should never use explicit machine instruction RET to return from the block defined with Procedure .. EndProcedure . If premature return is required, jump to the label of EndProcedure statement instead.
Input
ProcName This macroinstruction requires exactly one operand which is identical with the label of previous corresponding Procedure statement.
Output
Stack frame is released, current process returns below Invoke statement.
EndProcedure %MACRO ProcName
 OpCheck  %IF "%ProcName" === ""
            %ERROR ID=5924, 'Macro "EndProcedure" requires one operand.'
            %EXITMACRO EndProcedure
          %ENDIF OpCheck
          %ProcNameStrip %SET %ProcName
          %WHILE "%ProcNameStrip[%&]" === ":" ; Get rid of trailing colons, if used.
            %ProcNameStrip %SET %ProcNameStrip[1..%&-1]
          %ENDWHILE
 %NrOfArg %SET2 %%NrOfArg_%ProcNameStrip
 NestChck %IF "%NrOfArg" === ""
             %ERROR ID=5925, '"%ProcName Procedure" statement missing.'
             %EXITMACRO EndProcedure
          %ENDIF NestChck
          MOV RSP,RBP
          POP RBP
          RET
          ENDP %ProcName, NESTINGCHECK=OFF ; Terminate the namespace.
      %ENDMACRO EndProcedure
↑ Invoke ProcName, Arg1, Arg2, ...

Macro Invoke is a replacement of standard CALL instruction which can pass parameters to the Procedure in FastCall convention. Arguments are pushed on stack as QWORDs, starting from the last. The first four parameters are then loaded to RCX, RDX, R8, R9, even when the Procedure requires less than four arguments.
Stack will be OWORD aligned before the CALL instruction is performed.

Invoked procedure does not remove arguments from stack, it terminates with a simple RETN. Epilogue of macro Invoke restores RSP to its original value.

It is necessary to Invoke a procedure with exactly the same number of arguments which were declared by the Procedure macro.

Input
ProcName The first operand is the name of invoked procedure.
Arg* is a list of procedure arguments.
Output
The returned registers and flags depend on the invoked Procedure. If the Procedure complies with Windows FastCall convention, result of the invokation is returned in RAX, ST0, XMM0, YMM0, ZMM0.
Registers RBX,RBP,RSP,RSI,RDI,R12,R13,R14,R15,XMM6..XMM15 are preserved.
Registers RCX,RDX,R8,R9,R10,R11,K0..K7,XMM1..XMM6,YMM1..YMM15,ZMM10..ZMM15 may be changed by the invokation.
Invoke %MACRO ProcName, Arg1, Arg2,,,,
  %IF %# = 1               ; If no arguments.
    %ArgList %SET RCX,RDX,R8,R9
  %ENDIF
  %IF %# = 2               ; If one argument.
     %ArgList %SET %2,RDX,R8,R9
  %ENDIF
  %IF %# = 3               ; if two arguments.
    %ArgList %SET %2,%3,R8,R9
  %ENDIF
  %IF %# = 4               ; If three arguments.
    %ArgList %SET %2,%3,%4,R9
  %ENDIF
  %IF %# >= 5              ; If four or more arguments.
    %ArgList %SET %*{2..}
  %ENDIF
  %ArgNr %SETL %ArgList    ; Number of arguments, at least 4.
  PUSH RSP                 ; Save original stack pointer value.
  TEST SPL,1000b           ; Test stack OWORD alignment at run-time.
  %IF %ArgNr & 1b          ; When the number of arguments is odd,
    JNZ .Invoke%.:         ;   skip padding if RSP is unaligned.
  %ELSE                    ; When the number of arguments is even,
    JZ .Invoke%.:          ;   skip padding if RSP is OWORD aligned.
  %ENDIF
  PUSH RSP                 ; Additional PUSH for padding the stack
  ADDQ [RSP],8             ; Update copy of original stack pointer value.
.Invoke%.:                 ; A temporary local label, individualized with expansion number %.
  %WHILE %ArgNr > 0
    PUSHQ %ArgList{%ArgNr} ; Push all arguments on stack, begin with the last one.
    %ArgNr %SETA %ArgNr-1
  %ENDWHILE
  %IF "%ArgList{1}" !== "RCX"
    MOV RCX,[RSP+0]        ; Load first four arguments to registers due to FastCall convention.
  %ENDIF
  %IF "%ArgList{2}" !== "RDX"
    MOV RDX,[RSP+8]
  %ENDIF
  %IF "%ArgList{3}" !== "R8"
    MOV R8,[RSP+16]
  %ENDIF
  %IF "%ArgList{4}" !== "R9"
    MOV R9,[RSP+24]
  %ENDIF
  CALL %ProcName
  %ArgNr %SETL %ArgList    ; Reload the number of arguments again.
  LEA RSP,[RSP+8*%ArgNr]   ; Let RSP skip all pushed arguments
  POP RSP                  ;  and restore its original value (possibly not OWORD aligned).
%ENDMACRO Invoke
  ENDHEAD fastcall

▲Back to the top▲