EuroAssembler Index Manual Download Source Macros

Sitemap Links Forum Tests Projects


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

Macroinstructions Invoke, Procedure and EndProcedure implement the prologue and epilogue of Microsoft x64 calling convention [MSx64conv] , 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 (or in XMM0, XMM1, XMM2, XMM3 when they are floating-point numbers).

The block of code defined between macros Procedure .. EndProcedure can be called by macro Invoke or as a callback procedure from Windows function, for instance see WndProc in sample projects.

Macro Invoke can be also used to call functions from third-party static or dynamically linked libraries. Nevertheless, for invocation of Windows functions it's better to use specialized macros WinABI or WinAPI.

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.

Macro Invoke takes care of stack alignment to OWORD just before execution of instruction CALL MyProc (step 5. in the following example). RSP at the Invoke entry might 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 prologue. 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, 11,,) and RSP is OWORD aligned,
  2. or when number of arguments is even (<=4, 6, 8, 10,,) and RSP is unaligned.

Thanks to this, RSP is always OWORD aligned before step 5. (CALL MyProc) is executed, and the stack is restored to equilibrium (OrigRSP) after Invoke, no matter if it was OWORD aligned or not.

If macro SaveToShadow is used in Procedure block, it copies first four parameters from registers 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 implementation is compatible with [MSx64conv] convention and it allows to Invoke 64bit external or imported functions available in 3rd party libraries or in Windows ABI. It also allows to create FastCall functions invokable from other libraries or as system callback procedures.

I'm not sure if all crash analysers and debuggers will be able to unwind exception raised in Procedure, though. If your procedure is called from your own programs only, I recommend to abandon bad-designed Microsoft FastCall convention and use more efficient private register calling convention, tailored to the function's needs.

Definition of procedure MyProc with five parameters and two local stack variables with sizes 8 and 16:
MyProc Procedure Par1,Par2,Par3,Par4,Par5 ; Parameters Par1,Par2,Par3,Par4 are loaded in RCX,RDX,R8,R9. Par5 is pushed on stack. SaveToShadow ; Now are the parameter values available by formal names [%Par1],[%Par2],[%Par],[%Par4],[%Par5] ; and also by generic names [%Arg1],[%Arg2],[%Arg],[%Arg4],[%Arg5] (see %FastCall assignment). Uses RDI ; Exploit this macro only when some callee-save registers will be actually used in MyProc body. LocV1 LocalVar ; Declaration of procedure's local memory variables on machine stack. LocV2 LocalVar Size=16 ; Temporary stack variables are now available as [%LocV1] and [%LocV2]. ClearLocalVar ; This fills all temporary stack variables with 0. ; Programmer should emit instructions of the MyProc body here ; and load the result of MyProc into return-register (RAX or XMM0). EndProcedure MyProc ; This macro discards local variables, pops used callee-save registers, ; pops RBP and returns below Invoke statement at run-time.

Following diagram shows the stack frame created by invoking MyProc defined in the example above.

Macros of fastcall convention will create and update following "global" %variables at asm-time:
%`NrOfArguments`MyProc %SETA 5
%`UsesRegList`MyProc %SET RDI
%`LocalVarSize`MyProc %SETA 8+16
RSP after the step #. │ Stack ┌ 0.>┌───────┐<16. ┐ │1.PUSH RSP │origRSP│ 16.POP RSP │ │ 1.>├───────┤ │ │2.PUSH RSP;ADD [RSP],8 │origRSP│ │ │ 2.>├───────┤<15. │ │3.PUSH Arg5 │ Arg5 │ 15.LEA RSP,[RSP+5*8] │ │ ┌ 3.>├═══════┤ ├ Invoke Invoke ┤ │ │ undef │ │ epilogue prologue │ │ ├───────┤ │ │ │ │ undef │ │ │ shadow space ─┤ ├═══════┤ │ │ │ │ undef │ │ │ │ ├───────┤ │ │4.SUB RSP,4*8 │ │ undef │ ┘ │ oword-aligned>└ 4.>├═══════┤<14. ┐ │5.CALL MyProc │return │ 14.RET │ └ 5.>├───────┤<13. │ Procedure┤6.PUSH RBP;MOV RBP,RSP │origRBP│ 13.POP RBP │ 6.>├───────┤<12. │ Uses ┤7.PUSH RDI │origRDI│ 12.POP RDI │ ┌ 7.>├───────┤<11. ├EndProcedure │8.SUB RSP,SIZE#LocV1 │ LocV1 │ 11.ADD RSP,%LocalFrameSize│ │ 8.>├───────┤ │ LocalVar ┤ │ │ │ │ │ LocV2 │ │ │9.SUB RSP,SIZE#LocV2 │ │ │ └ 9.>└───────┘<10. ┘ 10. MyProc body

fastcall HEAD
↑ %FastCall
Argument values pushed on stack during Invoke and during SaveToShadow are available inside the Procedure body as formal names or as generic names defined here:
%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 Procedure declares beginning of FastCall-procedure, which unlike ordinary pseudoinstruction PROC, expects ordinal operands having been pushed on stack or loaded to registers.
Using of the macro Procedure requires the corresponding EndProcedure be used in the same program.

Macro operands formal names will be assigned to preprocessing %variables represented with the formal name prefixed by percent sign. Arguments are available in Procedure block as formal names declared here in Procedure prototype (%OperandX) or as generic names (%ArgX).
Pay attention that this is true for 5th and higher argument only. The first four arguments are passed in registers, so if we want to access them by formal or generic name, they have to be saved to shadow space in the beginning of our procedure, either by SaveToShadow, or manually with

MOVQ [%Arg1],RCX ; or MOVQ [%Arg1],XMM0 (when the argument is floating-point number). MOVQ [%Arg2],RDX ; or MOVQ [%Arg2],XMM1 (when the argument is floating-point number). MOVQ [%Arg3],R8 ; or MOVQ [%Arg3],XMM2 (when the argument is floating-point number). MOVQ [%Arg4],R9 ; or MOVQ [%Arg4],XMM3 (when the argument is floating-point number).
Label of Procedure statement is mandatory, it gives the procedure a callable name.
Operands are represented by their formal names (without the leading percent sign); their number is not limited.
Macroinstruction will define public symbol with procedure name and assign formal operand names to the corresponding %variables.
It does not push registers (except for RBP). Programmer is responsible for saving and restoring callee-save registers RBX, RSI, RDI, R12..R15, XMM6..XMM15 if they are used in procedure body. This can be done either manually, or with macro Uses.
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 local variables defined with LocalVar.
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 [%Source],RCX ; Saving register-passed arguments to shadow-space by formal name. MOV [%Arg2],RDX ; Ditto, saving by generic name. MOV [%Arg3],R8 ; Ditto, saving by generic name. 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. ;; 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. Uses RSI,RDI ; Those registers must be unchanged by fastcall Procedure. MOV RSI,RCX ; Source. MOV RDI,RDX ; Destination. MOV RCX,R8 ; Size. REP MOVSB ; Perform the actual copy. EndProcedure Move ; Restore used registers RDI,RSI, return from the procedure Move. Macro Move Procedure in the previous example will assign %`NrOfArguments`Move %SET 3 ; This %variable propagates to the corresponding macro EndProcedure Move. %`UsesRegList`Move %SET ; This %variable propagates to the macro Uses and EndProcedure. %`LocalVarSize`Move %SETA 0 ; This %variable propagates to the macro LocalVar and EndProcedure. %Source %SET RBP+16 ; This %variable represents formal name of Arg1. %Destination %SET RBP+24 ; This %variable represents formal name of Arg2. %Size %SET RBP+32 ; This %variable represents formal name of Arg3. Macro Uses in the previous example will assign %Uses_Move %SET RSI,RDI ; This %variable propagates to the corresponding macro EndProcedure Move. Invoke Move, RSI, OutBuffer, SIZE# OutBuffer ; Example of procedure invocation.
See also
32bit StdCall macro with the same name Procedure.
Procedure %MACRO FormalName1, FormalName2,,,,
 %IF "%:" === ""
   %ERROR ID=5921, 'Macro "Procedure" requires a label.'
   %EXITMACRO Procedure
 %%`NrOfArg`%:      %SETX %#    ; Initialize with number of arguments.
 %%`UsesRegList`%:  %SETX       ; Initialize as empty list.
 %%`LocalVarSize`%: %SETX 0     ; Initialize as zero.
 ArgNr %FOR 1..%#, STEP=+1
         %%%*{%ArgNr} %SETX RBP+8*(%ArgNr+1) ; Assign the formal name to the corresponding %variable.
       %ENDFOR ArgNr
%::: PROC %=*, NESTINGCHECK=OFF ; Open the namespace and define entry symbol from macro label %: as GLOBAL.
  MOV RBP,RSP                   ; Initialize the frame pointer.
%ENDMACRO Procedure
↑ SaveToShadow

Macro SaveToShadow stores the first four arguments of fast-called Procedure in 64bit mode to the shadow space reserved by Invoke. This enables the first four arguments be referred by their formal names or by generic names %Arg1, %Arg2, %Arg3, %Arg4.

Macro SaveToShadow should be used inside Procedure..EndProcedure block, near its beginning.
When it is omitted, the first four arguments are available only in registers RCX, RDX, R8, R9 (or XMM0..XMM3) and the shadow space contains undefined garbage.

When some of arguments contains floating-point number, it is passed to Procedure in SIMD register instead of GPR, and it must be therefore copied to the corresponding GPR prior to SaveToShadow. Example (Radius is FP):

MyCircle Procedure Xcoord, Ycoord, Radius MOVQ R8,XMM2 SaveToShadow Uses RSI,RDI MyLoc1 LocalVar Size=16 MyLoc2 LocalVar Size=24 ClearLocalVar ; Procedure body now has the 1st argument available at [RBP+16] alias [%Xcoord] alias [%Arg1] alias RCX. Et cetera. EndProcedure MyCircle
RCX, RDX, R8, R9 contain the first four arguments, respectively.
Shadow space (4*8 bytes at RBP+16) is populated with Procedure arguments.
SaveToShadow %MACRO
     %IF "%^PROC" === ""
       %ERROR ID=5926,'Macro "%0" is unexpected here.'
     MOV [%Arg1],RCX
     MOV [%Arg2],RDX
     MOV [%Arg3],R8
     MOV [%Arg4],R9
   %ENDMACRO SaveToShadow
↑ Uses Register1, Register2,,,,

Macro Uses specifies which callee-save registers does the Procedure use, so they are pushed on stack here (and they will be restored in EndProcedure epilogue).

Macro Uses can be used in 64bit mode only, right after the statement Procedure and before local stack variables are defined with LocalVar.

Callee-save registers RBX,RSI,RDI,R12..R15,XMM6..XMM15, should be enumerated here only if they are actually used in Procedure..EndProcedure block.
Callee-save registers RBP,RSP should not be mentioned here, they are always saved automatically in Procedure prologue.
It is useless to enumerate scratch registers RCX,RDX,R8,R9,XMM1..XMM5 here, because the caller of our Procedure cannot expect them to be preserved.
Registers RAX,XMM0 may not be enumerated here, because they wouldn't return the expected value after their restoration.

RegisterX is GPR or XMM register. Only lower QWORD of XMM register is saved.
Macro will push all enumerated registers on stack and assign their list to a unique %variable named %`UsesRegList`ProcedureName in reversed order. This %variable will be used by EndProcedure for restoration of callee-save registers.
Uses %MACRO Register1,Register2,...
     %IF "%^PROC" === ""
       %ERROR ID=5926,'Macro "%0" is unexpected here.'
     %UsesRegList %SET2 %%`UsesRegList`%^PROC
 reg %FOR %*
       %IF REGTYPE#(%reg) = 'Q' ; General-purpose 64bit register.
         PUSHQ %reg
         %UsesRegList %SET %reg,%UsesRegList
       %IF REGTYPE#(%reg) = 'X' ; SIMD XMM register.
         SUB RSP,8
         MOVQ [RSP],%reg
         %UsesRegList %SET %reg,%UsesRegList
       %IF REGTYPE#(%reg) != 'Q' && REGTYPE#(%reg) != 'X'
         %ERROR ID=5927,'Macro "Uses" does not support operand "%reg".'
     %ENDFOR reg
     %%`UsesRegList`%^PROC %SETX %UsesRegList
↑ 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 which has its name derived from the label of macro LocalVar.
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 the memory variable 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.
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 %`LocalVarSize`ProcedureName which was initialized in macro Procedure and which will be used for zeroing local variables in ClearLocalVar and for discarding local variables in EndProcedure.
ProcName: Procedure Param1 ; This example uses two local stack variables with sizes 8 and 1K. Uses RDI ; Saved callee-save register is now available at RBP-8. BlockSize LocalVar ; %BlockSize is now assigned with RBP-16 (8+8). Block LocalVar Size=1024 ; %Block is now assigned with RBP-1040 (8+8+1024). ClearLocalVar ; Fill %Block and %BlockSize with 0. MOV [%BlockSize],1K, DATA=QWORD LEA RDI,[%Block] ; more instructions... EndProcedure ProcName
LocalVar %MACRO Size=8
  %IF "%^PROC" === ""
    %ERROR ID=5926,'Macro "%0" is unexpected here.'
    %EXITMACRO LocalVar
  %IF "%:" === ""
    %ERROR ID=5922, 'Macro "%0" requires a label.'
    %EXITMACRO LocalVar
  %IF %#
    %ERROR ID=5923, 'Macro "%0" does not expect ordinal parameters.'
%: %COMMENT         ; This empty comment block makes the label of macro void,
   %ENDCOMMENT %:   ;   so it does not declare a symbol.
   %ThisSize     %SETA (%Size + 7) & ~7          ; Round up to the nearest multiple of 8.
   %LocalVarSize %SET2 %%`LocalVarSize`%^PROC    ; Total size of previously defined local variables.
   %LocalVarSize %SETA %LocalVarSize + %ThisSize ; Add this variable size.
   SUB RSP, %Size                                ; Stack memory allocation.
   %%`LocalVarSize`%^PROC %SETX %LocalVarSize    ; Propagate the new sum to the following LocalVar definitions and to EndProcedure.
   %UsesRegList           %SET2 %%`UsesRegList`%^PROC
   %UsesRegListLength     %SETL %UsesRegList     ; Number of registers pushed by macro Uses.
   %UsesFrameSize         %SETA %UsesRegListLength * 8
%%%: %SETX RBP-%UsesFrameSize-%LocalVarSize      ; Assign formal %name to the id %: specified as LocalVar label.
↑ ClearLocalVar
This macro zeroes all variables on stack previously declared with LocalVar. ClearLocalVar should be expanded right after the last LocalVar declarations, before any stack operations are made.
We could as well decide to initialize each local variable individually, e.g. MOVQ [%MyLocalVar],0 in this case the macro ClearLocalVar will not be used in the Procedure body at all.
Macro does not use explicit parameters. The cleared memory is pointed to with RSP, its size is specified with "global" variable %`LocalVarSize`ProcedureName.
ClearLocalVar %MACRO
  %IF "%^PROC" === ""
    %ERROR ID=5926,'Macro "%0" is unexpected here.'
    %EXITMACRO ClearLocalVar
  %LocalVarSize %SET2 %%`LocalVarSize`%^PROC
  %IF %LocalVarSize  ; Do nothing if no LocalVar was used in this Procedure (%LocalVarSize is 0).
      LEA RDI,[RSP+2*8] ; Skip pushed RCX,RDI.
      MOV ECX,%LocalVarSize >> 3
%ENDMACRO ClearLocalVar
↑ EndProcedure ProcName

Macro EndProcedure terminates context of the previously opened Procedure . This epilogue of FastCall convention will

Operands 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.
ProcName This macroinstruction requires exactly one operand which is identical with the label of previous corresponding Procedure statement.
Stack frame is released, current process returns below Invoke statement.
EndProcedure %MACRO ProcName
    %IF %# <> 1
      %ERROR ID=5924, 'Macro "EndProcedure" requires one operand.'
      %EXITMACRO EndProcedure
    %ProcNameStripped %SET %ProcName
    %WHILE '%ProcNameStripped[%&]' === ':' ; Get rid of trailing colon(s), if used.
      %ProcNameStripped %SET %ProcNameStripped[1..%&-1]
    %IF '%ProcNameStripped' !=== '%^PROC'
      %ERROR ID=5925, 'Nesting mismatch, "%ProcName Procedure" missing.'
      %EXITMACRO EndProcedure
    %LocalVarSize %SET2 %%`LocalVarSize`%^PROC
    %IF %LocalVarSize
      ADD RSP,%LocalVarSize        ; Discard local variables.
    %UsesRegList %SET2 %%`UsesRegList`%^PROC
reg %FOR %UsesRegList              ; Callee-save registers saved by macro Uses.
      %IF REGTYPE#(%reg) = 'Q'     ; General-purpose 64bit register.
        POPQ %reg
      %IF REGTYPE#(%reg) = 'X'     ; SIMD XMM register.
        MOVQ %reg,[RSP]
        ADD RSP,8
    %ENDFOR reg
    POP RBP                        ; Restore caller's frame pointer.
    RET                            ; Return below Invoke which called %ProcName.
  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 backwards on stack as QWORDs, starting from the last to the fifth. The first four parameters are loaded to RCX, RDX, R8, R9 and not pushed on stack. Nevertheless, room for the first four arguments is always reserved on stack (so called shadow space) even when the invoked function has less than four arguments.
When the Procedure expects argument in floating-point format instead of integer, pointer or immediate, the first four arguments are loaded to XMM0, XMM1, XMM2, XMM3 rather than to GPR.
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.

Argument of Invoke may have many formats:

Each argument may be suffixed with type specificator #SS or #SD (case insensitive), which signalizes that it represents floating-point value in Scalar Single or Scalar Double precision format, and that it should be therefore passed to the ProcName in XMM register instead of GPR.
Suffix is not necessary in 5th and higher arguments (they are passed via machine stack regardless of their type).
Suffix is not necessary with XMM register (argument passed in XMM is always assumed to contain floating-point number).

From parsing reason do not use single apostrophe ' in string literals used as macro arguments. Please prefer double quote: Invoke MyFunction,="Text" instead of Invoke MyFunction, ='Text'

FastCall Invoke in MS Windows does not keep the original contents of Rflags, RAX, RCX, RDX, R8..R11, XMM0..XMM5.
It expects and keeps Direction Flag on zero.

Registers RAX, RCX, RDX, R8, R9, XMM0..XMM3 should not be used as macro arguments, because they are being overwritten in prologue. Or they must be used in the exact ABI-specified order, e.g. Invoke MyProcedure, RCX, XMM1, R8, XMM3.

ProcName The first operand is the name of invoked procedure.
Arg* is the list of procedure arguments.
The returned registers and flags depend on the invoked Procedure. If the invoked procedure complies with Windows FastCall convention, result of the invocation 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 invocation.
See also
WinABI or WinAPI - the preferred method for invocation of MS Windows API functions.
Invoke %MACRO ProcName, Arg1, Arg2,,,,
  PUSH RSP                       ; Store original stack pointer value (equilibrum).
  TEST SPL,1000b                 ; Test stack OWORD alignment at run-time.
  %IF %# & 1b || %# <= 5         ;>If the number of Procedure arguments is 0,1,2,3,4,6,8,10,,
     JZ  .Invoke%.:              ;   store 2nd copy of equilibrum when RSP is OWORD-unaligned.
  %ELSE                          ; If the number of arguments is 5,7,9,11,,,
     JNZ .Invoke%.:              ;   store 2nd copy of equilibrum when RSP is OWORD-aligned.
  PUSH RSP                       ; Store and update 2nd copy of original RSP (equilibrum).
  ADDQ [RSP],8                   ; Those two instructions aren't executed if RSP was properly aligned.
  %GPR  %SET RCX,RDX,R8,R9       ; Declare order of registers used for first four arguments.
  %ArgNr %SETA %#                ; Number of arguments + 1.
  %WHILE %ArgNr > 1              ; Pass all arguments, start with the last one.
    %Arg %SET %*{%ArgNr}
    %ArgNr %SETA %ArgNr - 1      ; %ArgNr is now the ordinal argument number ..5,4,3,2,1.
    %IF '%Arg[%&-2..%&-1]'=='#S' ; If suffix #SS or #SD is present.
      %suffix %SET %Arg[%&-1..%&]; %suffix is now SS or SD.
      %Arg %SET %Arg[1..%&-3]    ; Remove the suffix from %Arg.
      %suffix %SET Q             ; Otherwise %suffix is Q (use MOVQ instead of MOVSS or MOVSD).
 a5 %IF %ArgNr > 4               ; Argument %5 and higher is passed by stack.
   s5 %IF TYPE#(SEGMENT#(%Arg))='N' ; Nonrelocable (scalar) argument.
     x5 %IF REGTYPE#(%Arg)='X'
          SUB RSP,8              ; Pseudo"push" XMM on stack.
          MOV%suffix [RSP],%Arg
        %ELSE x5
          PUSHQ %Arg             ; Other non-XMM scalar argument PUSH as is.
        %ENDIF x5
      %ELSE s5                   ; Argument needs relocation.
     m5 %IF '%Arg[1]' === '['    ; Argument is m64, e.g. [RelocSymbol+RSI].
          PUSHQ %Arg
        %ELSE M5                 ; Otherwise it is relocable offset (pointer).
          LEA RAX,[%Arg],ADDR=ABS
          PUSH RAX               ; Borrow RAX for pushing pointers.
        %ENDIF m5
      %ENDIF s5
    %ELSE a5                      ; Argument %1..%4 will be loaded to GPR or SIMD register.
   s4 %IF TYPE#(SEGMENT#(%Arg))='N' ; Nonrelocable (scalar) argument or register.
     q4 %IF REGTYPE#(%Arg) = 'Q'  ; Argument is GPR.
       i4 %IF '%suffix'==='Q'     ; Argument is integer, it goes to GPR.
            %IF '%GPR{%ArgNr}' !== '%Arg' ; Emit only if it's not already there.
              MOVQ %GPR{%ArgNr},%Arg
          %ELSE i4                ; Argument is float, it goes to SIMD.
            MOVQ %SIMD{%ArgNr},%Arg
          %ENDIF i4
        %ELSE q4                  ; Argument is not GPR.
       x4 %IF REGTYPE#(%Arg)='X'  ; Argument is SIMD.
            %IF '%SIMD{%ArgNr}' !== '%Arg' ; Emit only if it's not already there.
              MOV%suffix %SIMD{%ArgNr},%Arg
          %ELSE x4                ; Nonrelocable argument is not SIMD neither GPR.
         m4 %IF 'Arg[1]' === '['  ; Argument is m64, e.g. [RBP+8] or [RBP+8]#SS.
           i3 %IF '%suffix' === 'Q' ; Argument is integer, it goes to GPR.
                MOVQ %GPR{%ArgNr},%Arg
              %ELSE i3
                MOV%suffix %SIMD{%ArgNr},%Arg ; Argument is float, it goes to SIMD.
              %ENDIF i3
            %ELSE m4              ; Argument is immediate scalar.
              %IF '%suffix' !=== 'Q'
                %ERROR ID=5958,"Floating-point immediate argument is not supported."
              MOVQ %GPR{%ArgNr},%Arg
            %ENDIF m4
          %ENDIF x4
        %ENDIF q4
      %ELSE s4                     ; Argument needs relocation.
     m3 %IF '%Arg[1]' === '['      ; Argument is m64, e.g. [RelocSymbol+RSI].
        i2 %IF '%suffix' === 'Q'   ; Argument is integer, it goes to GPR.
              MOVQ %GPR{%ArgNr},%Arg
           %ELSE i2
              MOV%suffix %SIMD{%ArgNr},%Arg
           %ENDIF i2
        %ELSE m3                   ; Argument is immediate pointer.
        i1 %IF '%suffix' === 'Q'   ; Argument is integer, it goes to GPR.
             LEA %GPR{%ArgNr},[%Arg],ADDR=ABS
           %ELSE i1
             %ERROR ID=5958,"Floating-point immediate argument is not supported."
             LEA RAX,[%Arg],ADDR=ABS
             MOVQ %SIMD{%ArgNr},RAX
           %ENDIF i1
        %ENDIF m3
      %ENDIF s4
    %ENDIF a5
  SUB RSP,4*8                      ; Make room for shadow space. RSP is now OWORD-aligned.
  CALL %ProcName
  %IF %# > 5                       ; When returned from the invoked procedure, remove arguments from stack.
    %ArgNr %SETA %# - 1
    %ArgNr %SETA 4
  LEA RSP,[RSP+8*%ArgNr]           ; Let RSP skip all pushed arguments, keeping Rflags,
  POP RSP                          ;  and restore RSP to equilibrum from 1st or 2nd copy.
  ENDHEAD fastcall

▲Back to the top▲