The MASM Forum

64 bit assembler => 64 Bit Assembler => Topic started by: jj2007 on April 09, 2021, 11:23:41 PM

Title: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 09, 2021, 11:23:41 PM
This one uses the Masm64 SDK and really intrigues me - it works like a charm:
Code: [Select]
include \masm32\include64\masm64rt.inc
include \masm32\include64\msvcrt.inc
includelib \masm32\lib64\msvcrt.lib

.data?
buffer db 100 dup(?)

.code
txTitle db "Expected value: 12345.678", 0
txFormat db "Test %f", 13, 10, 0
MyR8 REAL8 12345.67890

entry_point proc
  invoke vc_sprintf, addr buffer, addr txFormat, MyR8
  invoke MessageBox, 0, addr buffer, addr txTitle, MB_OK
  invoke ExitProcess, 0
  ret
entry_point endp

end

Here is a nice x64 ABI tutorial (https://sonictk.github.io/asm_tutorial/), see in particular Mixing parameter types
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 09, 2021, 11:53:12 PM
Here is a nice x64 ABI tutorial (https://sonictk.github.io/asm_tutorial/), see in particular Mixing parameter types

 :eusa_naughty: Tutorial is against x64 ABI

Floats in 4 first positions must be in XMM and GPR (both)!

 The function sprintf read from GPR.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 10, 2021, 12:00:41 AM
Floats in 4 first positions must be in XMM and GPR (both)!

You are right, good point :thumbsup:

Parameter passing (https://docs.microsoft.com/en-us/cpp/build/x64-calling-convention?view=msvc-160#parameter-passing)

Quote
Any floating-point and double-precision arguments in the first four parameters are passed in XMM0 - XMM3, depending on position. Floating-point values are only placed in the integer registers RCX, RDX, R8, and R9 when there are varargs arguments.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 10, 2021, 12:22:25 AM
Quote
For floating-point values only, both the integer register and the floating-point register must contain the value, in case the callee expects the value in the integer registers.

 :thumbsup: At this point invoke macro don't make distintions. That was removed from ml because M$ relly in compilers to do that.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 10, 2021, 12:24:11 AM
Quote
For floating-point values only, both the integer register and the floating-point register must contain the value, in case the callee expects the value in the integer registers.

 :thumbsup: At this point invoke macro don't make distintions. That was removed from ml because M$ relly in compilers to do that.

The next version of jinvoke will make distinctions :cool:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 10, 2021, 12:31:17 AM
The next version of jinvoke will make distinctions :cool:

Great!!
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: hutch-- on April 10, 2021, 11:28:37 AM
In MASM, a 64 bit hole is a 64 bit hole, if you are passing REAL8 args, they fit into 64 bit integer registers with no problems. You can also return a 64 bit result in RAX as long as you transfer it to a 64 bit REAL8 to work on as FP.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 10, 2021, 06:10:05 PM
if you are passing REAL8 args, they fit into 64 bit integer registers with no problems.

That's correct, but it doesn't respect the x64 ABI, and some WinAPIs may not find the double they expect in xmm0...xmm3 :cool:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: hutch-- on April 10, 2021, 06:48:19 PM
Well the obvious is that a 128 bit register and a 64 bit integer register are different animals and the two should not be confused. If an API expects an XMM register, that is what you pass it, if instead it expects a 64 bit memory operand or a 64 bit register, either a REAL8 or a QWORD, then you pass the 64 bit format.

I mentioned the obvious, a QWORD and a REAL8 fit the same hole and you can successfully pass a return value in RAX as long as you load it into a REAL8 memory operand for further processing. We are not dealing with strongly typed compilers here, we are dealing with an assembler.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 10, 2021, 11:00:43 PM
x64 calling convention make a clear distinction between Prototyped functions, Vararg functions and Unprototyped functions then, and I'm sure Hutch will dislike this, Prototypes are an essential part the x64 ABI.

ML deal directly with prototypes. M$ maked something different with ML64 because are compilers (used as preprocessors to generate assembly code) what deal with prototypes. Then to use ML64 following x64 ABI requiere a lot more effort, or to use some macros that deal with prototypes. And that part of more effort dislike me :biggrin:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 11, 2021, 12:20:49 AM

In MASM, a 64 bit hole is a 64 bit hole, if you are passing REAL8 args, they fit into 64 bit integer registers with no problems. You can also return a 64 bit result in RAX as long as you transfer it to a 64 bit REAL8 to work on as FP.

That could be a nice world. You perhaps don't need stronger prototypes in that world. For some reason people solve problems in strange ways.
I think everybody must use REAL8, but all GPU, SIMD, graffics engines,  etc. insist to use REAL4!!!. For sure they are totally wrong  :biggrin: :biggrin: :biggrin:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: hutch-- on April 11, 2021, 01:07:03 AM
Hector,

You are confusing compilers and assemblers with prototypes. MASM does not support them.  :tongue:

> I think everybody must use REAL8, but all GPU, SIMD, graffics engines,  etc. insist to use REAL4!!!. For sure they are totally wrong

Games and number crunching are different animals. The only problems with 64 bit FP is the precision is not high enough for all tasks. 80 bit x87 is higher but not even recognised in x64 although it runs OK.

You probably understand why I am not a consensus person or you would be using a visual garbage generator.  :dazzled:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 11, 2021, 01:32:55 AM
Coming soon - work in progress :biggrin:

Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; ## builds in 32- or 64-bit mode with UAsm, ML, AsmC ##

DefProc SayHi proc argString:SIZE_P, argDword, argDouble:REAL8 ; #1 is a pointer size argument
Local v1, v2:REAL8, rc:RECT
  Print Str$("\nThe locals are cleared: %i %i %i...%i\n", v1, v2, rc.left, rc.bottom)
  Print Str$("\nThe arguments:\n%s\n%i\n%f\n", argString, argDword, argDouble)
  ret
SayHi endp

SetGlobals MyDW=12345 ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
Init
  ; jinvoke SayHi, Chr$("Argument #1 is a string"), 22222, FP8(33.3), 1 ; ## line 12: too many arguments for SayHi ##
  ; jinvoke SayHi, Chr$("Argument #1 is a string"), 22222 ; ## line 13: not enough arguments for SayHi ##
  ; jinvoke SayHi, Chr$("Argument #1 is a string"), 22222, MyDW ; ## line 14: argument #3 should be REAL8 ##
  ; jinvoke SayHi, Chr$("Argument #1 is a string"), 22222, 123456789 ; ## line 15: argument #3 should be REAL8 ##
  jinvoke SayHi, Chr$("Argument #1 is a string"), 22222, FP8(33.3) ; OK!
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
EndOfCode XP ; add an XP manifest (not with ML64)
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 11, 2021, 01:40:07 AM
Hi Hutch!
You are confusing compilers and assemblers with prototypes. MASM does not support them.  :tongue:
MASM is just the programming tool. x64 ABI require prototypes. (I said it, you don't like that  :thumbsup:) 

The only problems with 64 bit FP is the precision is not high enough for all tasks. 80 bit x87 is higher but not even recognised in x64 although it runs OK.
By design, programmers never have to use 80 bit x87. For precisions higher than REAL8 there is BigNumbers libraries. Long time ago REAL8 only existed in libraries.

You probably understand why I am not a consensus person or you would be using a visual garbage generator.  :dazzled:
:biggrin: There is thousands of other places for consensus persons.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 11, 2021, 01:50:15 AM
Coming soon - work in progress :biggrin:

 :thumbsup: Remember we need prototypes for share libraries. It's almost irrelevant for our own internal functions.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 11, 2021, 02:14:07 AM
Coming soon - work in progress :biggrin:

 :thumbsup: Remember we need prototypes for share libraries. It's almost irrelevant for our own internal functions.

I know, and that's a problem. Not for my macros, actually, they do handle prototypes correctly. I guess I have to extract them from some C compiler's headers. Have you ever checked \Masm32\include\*.inc for the correctness of the PROTOs? :cool:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: hutch-- on April 11, 2021, 02:32:51 AM
Hector,

Same old problem, you are confusing compilers and assemblers, most compilers are designed to use prototypes but not all. The Microsoft x64 ABI is based off C++ which is a compiler but again, MASM does not support prototypes as it is NOT a compiler. Now with the sense of prototypes that you have in mind, to be x64 ABI compliant, you would be using C++, not MASM and while that is a viable route, MASM simply does not support prototyping so it is a dead issue at best.

Remove C++ from your equasion and you can routinely write ABI compliant assembler code in MASM.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: HSE on April 11, 2021, 02:36:29 AM
I guess I have to extract them from some C compiler's headers. Have you ever checked \Masm32\include\*.inc for the correctness of the PROTOs? :cool:
I think libraries don't care what assembler you use. If you have doubts, you can try others include sets like Nidud's, Biterider's, Yves', no need to make a new one.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: hutch-- on April 11, 2021, 03:32:20 AM
 :biggrin:

I am not writing shared libraries, I only write MASM libraries and while others may be able to use them, they will have to cobble together their own prototypes because in case you have missed it, MASM does not use prototypes. The Watcom derivatives may want to end up as compilers but MASM does not need to do that, that is what 64 bit CL.EXE is for.

> If you have doubts, you can try others include sets like Nidud's, Biterider's, Yves', no need to make a new one.

Why bother wasting time when MASM does not need or use Watcom derivative's prototypes. I already have a near exhaustive set for the linker, why would I settle for the booby prize.  :tongue:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: tenkey on April 11, 2021, 08:11:22 AM
Back to "REAL8 in xmm reg?"
If you are interfacing to MSVC C/C++ APIs (gcc has a different one), the answer is yes if it's one of the first four arguments of the C/C++ function definition or call. Win64 functions are MSVC types of functions.
I compiled a simple C program in VS2019 (Community) to find out what regs are used.
Code: [Select]
#include <stdio.h>
float sumf(float a, float b, float c, float d)
{
return a + b + c + d;
}

double sumd(double a, double b, double c, double d)
{
return a + b + c + d;
}

int main(int argc, char** argv)
{
float f = sumf(1.0, 2.0, 3.0, 4.0);
double d = sumd(1.0, 2.0, 3.0, 4.0);
printf("%d: float = %f, double = %f\n", 1, f, d);
}

Compiling in release mode with optimizations enabled produces:
Code: [Select]
; Function compile flags: /Ogtpy
; COMDAT sumf
_TEXT SEGMENT
a$ = 8
b$ = 16
c$ = 24
d$ = 32
sumf PROC ; COMDAT
; File D:\Henry\Win64asm\C++\fpargs\fpargs\fpargs.cpp
; Line 4
addss xmm0, xmm1
addss xmm0, xmm2
addss xmm0, xmm3
; Line 5
ret 0
sumf ENDP
_TEXT ENDS
; Function compile flags: /Ogtpy
; COMDAT sumd
_TEXT SEGMENT
a$ = 8
b$ = 16
c$ = 24
d$ = 32
sumd PROC ; COMDAT
; File D:\Henry\Win64asm\C++\fpargs\fpargs\fpargs.cpp
; Line 9
addsd xmm0, xmm1
addsd xmm0, xmm2
addsd xmm0, xmm3
; Line 10
ret 0
sumd ENDP
_TEXT ENDS
; Function compile flags: /Ogtpy
; COMDAT main
_TEXT SEGMENT
argc$ = 48
argv$ = 56
main PROC ; COMDAT
; File D:\Henry\Win64asm\C++\fpargs\fpargs\fpargs.cpp
; Line 13
$LN8:
sub rsp, 40 ; 00000028H
; Line 16
movsd xmm2, QWORD PTR __real@4024000000000000
lea rcx, OFFSET FLAT:??_C@_0BN@KKHEANPC@?$CFd?3?5float?5?$DN?5?$CFf?0?5double?5?$DN?5?$CFf?6@
movaps xmm3, xmm2
movq r9, xmm2
movq r8, xmm2
mov edx, 1
call printf
; Line 17
xor eax, eax
add rsp, 40 ; 00000028H
ret 0
main ENDP
_TEXT ENDS

sumf expects the values to be in xmm0, xmm1, xmm2, and xmm3.
It leaves the result in xmm0.
rcx, rdx, r8, r9, and the shadow space are untouched.
It uses REAL4 instructions,

sumd is the same, except that it uses REAL8 instructions.

Because it is in the same file and the functions are called with constants, the compiler has optimized away the calls to sumf and sumd. The main function is reduced to:
Code: [Select]
    printf("%d: float = %f, double = %f\n", 1, 10.0, 10.0);

The REAL8 10.0 is stored in CONST memory, and because printf has varargs, the 10.0 is loaded into xmm2, xmm3, r8, and r9.

The unoptimized debug version retains the function calls:
Code: [Select]
; Line 14
movss xmm3, DWORD PTR __real@40800000
movss xmm2, DWORD PTR __real@40400000
movss xmm1, DWORD PTR __real@40000000
movss xmm0, DWORD PTR __real@3f800000
call sumf
movss DWORD PTR f$[rbp], xmm0
; Line 15
movsd xmm3, QWORD PTR __real@4010000000000000
movsd xmm2, QWORD PTR __real@4008000000000000
movsd xmm1, QWORD PTR __real@4000000000000000
movsd xmm0, QWORD PTR __real@3ff0000000000000
call sumd
movsd QWORD PTR d$[rbp], xmm0

Because sumf and sumd do not have varargs, registers rcx, rdx, r8, and r9 are untouched.

That's how MSVC works.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 11, 2021, 09:54:40 AM
Nice example, tenkey :thumbsup:

Here is my translation of your example (naked means no shuffling of passed regs into shadow space); to make it more complicated, I used mixed arguments:
Code: [Select]
include \Masm32\MasmBasic\Res\JBasic.inc ; ## builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
DefProc sumD proc !<naked!> argDouble0:REAL8, argDword1, argDouble2:REAL8, argDword3
  addsd xmm0, xmm2 ; REAL8 add in xmm regs
  movd rax, xmm0
  add rdx, r9 ; sum of two integer args
  cvtsi2sd xmm1, rdx ; to double
  addsd xmm0, xmm1 ; REAL8 add in xmm regs
  movd rax, xmm0 ; return REAL8 in xmm0 and rax
  ret
sumD endp
DefProc sumF proc !<naked!> argDouble0:REAL4, argDword1, argDouble2:REAL4, argDword3
  addss xmm0, xmm2 ; REAL4 add in xmm regs
  cvtss2sd xmm0, xmm0 ; REAL4 to REAL8
  add rdx, r9 ; sum of two integer args
  cvtsi2sd xmm1, rdx ; to double
  addsd xmm0, xmm1 ; REAL8 add in xmm regs
  movd rax, xmm0 ; return REAL8 in xmm0 and rax
  ret
sumF endp
Init ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
;   int 3
  Print Str$("Result sum D: %f\n", rv(sumD, FP8(1000.11111), 100, FP8(10.0), 1))
  Print Str$("Result sum F: %f\n", rv(sumF, FP4(1000.11111), 100, FP4(10.0), 1))
EndOfCode ; OPT_Assembler  ML

Output:
Code: [Select]
This program was assembled with ml64 in 64-bit format.
Result sum D: 1111.111110
Result sum F: 1111.111084

One interesting aspect here: you can't feed REAL4 ("single") values to printf(), and you can't feed it directly with xmm regs. The CRT printf() wants a double in rax (or another register or REAL8 memory location). Therefore both sumD and sumF return doubles in xmm0 and rax.

I attach two executables, one with an int 3 right before the call to sumD. Run it from a DOS prompt, it won't wait for a keypress.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: hutch-- on April 11, 2021, 10:25:29 AM
tenkey is correct,

sumf expects the values to be in xmm0, xmm1, xmm2, and xmm3.
It leaves the result in xmm0.
rcx, rdx, r8, r9, and the shadow space are untouched.
It uses REAL4 instructions,

The Microsoft x64 ABI specifies the first 4 XMM registers for arguments and the return value if any in XMM0. This is how you would interact with an external high level language that requires ABI calling convention comformity, with a 64 bit MASM app you can use the first 6 XMM registers and still remain ABI compliant in terms of register usage but it is not compatible with either the Win API or external procedure calls.

Also note that at least some CRT functions use the XMM registers and will over write any of the first 4 XMM registers.

This is why in a macro I posted above, you DO NOT MIX integer registers and XMM registers.

  ; -------------------------------------------------------------------
  ; with invoke, the integer args must come first. FP load must be last
  ; -------------------------------------------------------------------
    invoke LoadTest,1111,2222,3333,4444, xloadrv(1234.1234, 5678.5678)

It certainly can be done other ways but this tests up reliably.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 11, 2021, 10:41:16 AM
Compiling in release mode with optimizations enabled produces:

Code: [Select]
; Line 16
movsd xmm2, QWORD PTR __real@4024000000000000
lea rcx, OFFSET FLAT:??_C@_0BN@KKHEANPC@?$CFd?3?5float?5?$DN?5?$CFf?0?5double?5?$DN?5?$CFf?6@
movaps xmm3, xmm2
movq r9, xmm2
movq r8, xmm2
mov edx, 1
call printf

Because sumf and sumd do not have varargs, registers rcx, rdx, r8, and r9 are untouched.

That's how MSVC works.

The first four arguments are in rcx rdx r8 r9, but as you write correctly, they are untouched because sumd and sumf are not VARARG. However, printf() is a VARARG function:
Code: [Select]
float f = sumf(1.0, 2.0, 3.0, 4.0);
double d = sumd(1.0, 2.0, 3.0, 4.0);
printf("%d: float = %f, double = %f\n", 1, f, d);
Code: [Select]
rcx is the format string
rdx is the constant 1
r8 is f
r9 is d
xmm2 is a copy of f, d (the compiler seems to know that they are identical)
xmm3 is a copy of f, d

From my tests it seems that xmm2 and xmm3 are ignored by printf().

MSVC may behave differently if you add, in sumf and sumd, a random value to the sum, such as GetTickCount().
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: nidud on April 12, 2021, 12:03:48 AM
deleted
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 12, 2021, 12:09:15 AM
Code: [Select]
    movsd xmm0,1.0
    movsd xmm1,2.0

    printf("xmm0: %f\nxmm1: %f\n", xmm0, xmm1)
Output:

xmm0: 1.000000
xmm1: 2.000000


Post the executable :biggrin:
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: nidud on April 12, 2021, 02:15:19 AM
deleted
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 12, 2021, 06:34:13 AM
Assembly is actually a low-level programming language believe it or not

I believe you :tongue:

Code: [Select]
        movq    r8, xmm1                                ; 0014 _ 66 49: 0F 7E. C8
        movq    rdx, xmm0                               ; 0019 _ 66 48: 0F 7E. C2
        lea     rcx, [DS0000]                           ; 001E _ 48: 8D. 0D, 00000000(rel)
        call    printf                                  ; 0025 _ E8, 00000000(rel)

Thanks, that's why I asked you to post the exe :cool:

It confirms my tests: printf uses internally the reg64, not the xmmregs; on the web, some people pretend that you must pass in eax resp. al the number of xmmregs used, but that doesn't look plausible.
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: nidud on April 12, 2021, 08:14:42 AM
deleted
Title: Re: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 12, 2021, 08:17:40 AM
Quote
It confirms my tests: printf uses internally the reg64, not the xmmregs; on the web, some people pretend that you must pass in eax resp. al the number of xmmregs used, but that doesn't look plausible.

That is true for Linux.

Ok, makes sense. I wonder how it works with mixed arguments, such printf("%f %f %f %f", xmm0, MyDouble1, xmm1, MyDouble2)
Title: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: nidud on April 12, 2021, 08:04:36 PM
deleted
Title: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 12, 2021, 08:08:29 PM
it kicks in at the :VARARG position.

Good point, thanks :thumbsup:

Is there any "mixed" WinAPI call that you know of? There are more than 1,000 VARARG API calls in \Masm32\include...

Just for fun, I checked it (source & exe attached):
Code: [Select]
include \masm32\MasmBasic\MasmBasic.inc
  SetGlobals ctVar, ctMixed
  Init
  GetFiles "\Masm32\include\*.inc"
  PrintLine Str$("%i files found\n", eax)
  For_ fcount=0 To Files$(?)-1
Recall Files$(fcount), L$()
For_ ct=0 To eax-1
mov esi, L$(ct)
.if Instr_(esi, ":VARARG")
inc ctVar
lea ecx, [edx+6] ; +6 for len(":VARARG")
.if Len(esi)!=ecx
inc ctMixed
Print Str$("\n%___i", ct), esi
.endif
.endif
Next
  Next
  Inkey Str$("\n%i vararg calls found", ctVar), Str$(", of which %i are mixed", ctMixed)
EndOfCode
Title: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: nidud on April 12, 2021, 11:22:17 PM
deleted
Title: Re: X64 ABI, REAL8 passed in xmmreg?
Post by: jj2007 on April 12, 2021, 11:43:13 PM
It's common in graphic code to use long arrays of floats and VARARG

There are some other candidates, too:
Code: [Select]
frequency of VARARG:
184  \Masm32\include\wldap32.inc
150  \Masm32\include\wdmsec.inc
102  \Masm32\include\psxdll.inc
076  \Masm32\include\wdm.inc
065  \Masm32\include\ntoskrnl.inc
063  \Masm32\include\netlib.inc
056  \Masm32\include\miglib.inc
050  \Masm32\include\parser.inc
049  \Masm32\include\npptools.inc
042  \Masm32\include\thunk32.inc
026  \Masm32\include\dnslib.inc
025  \Masm32\include\mapi32.inc
021  \Masm32\include\dnsrpc.inc
013  \Masm32\include\vfw32.inc
013  \Masm32\include\rpcrt4.inc
010  \Masm32\include\cabinet.inc
008  \Masm32\include\rxapi.inc
008  \Masm32\include\odbc32.inc
008  \Masm32\include\ntdll.inc
007  \Masm32\include\ntvdm.inc

With GetFiles (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1056) "\Masm32\include\g*.inc":
Code: [Select]
frequency of VARARG:
000  \Masm32\include\gpedit.inc
000  \Masm32\include\glu32.inc
000  \Masm32\include\glmf32.inc
000  \Masm32\include\glaux.inc
000  \Masm32\include\gdiplus.inc
000  \Masm32\include\gdi32.inc
000  \Masm32\include\gapi32.inc