The MASM Forum

64 bit assembler => UASM Assembler Development => Topic started by: jj2007 on April 08, 2021, 08:39:27 PM

Title: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 08, 2021, 08:39:27 PM
Hi everybody,

I'd like to find out how a C/C++ compiler deals with mixed int/real arguments, so I picked gluPartialDisk(), see below.
Unfortunately I can't use my Visual C compiler, because it has the infamous Command line error D8037 bug - despite a complete reinstallation.

So I tried with GCC, and stumbled over several compile errors that I could eliminate by creating a private gluJJ.h and removing the offending (unrelated) parts (C compilers are not compatible to each other, apparently).

Now it does compile, but the linker throws an error: it complains about an "undefined reference to `gluPartialDisk@44", which is pretty weird because
a) the byte count is 48 - see below
b) the lib has indeed a gluPartialDisk@44 :rolleyes:

Most likely this is a 32-bit library (strange that the linker does not notice that...), so I checked for another glu32.lib and found one at C:\Program Files (x86)\Windows Kits\8.1\Lib\winv6.3\um\x64\GlU32.Lib, with a header file at C:\Program Files (x86)\Windows Kits\8.1\Include\um\gl\GLU.h. The latter throws an error saying it can't find winapifamily.h :sad:

Any experts around who can make this work? I attach the relevant files, including files used to build a Masm64 SDK version (glutest.asm) that assembles fine but does not use xmm regs as prescribed by the X64 ABI.

#include <stdio.h>
// C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Include\gl\GLU.h throws errors
#include <\Masm32\examples64\Glu32\GLUJJ.h>
#pragma comment( lib, "GlU32.Lib" )

/*
void APIENTRY gluPartialDisk (
    GLUquadric          *qobj, 8+
    GLdouble            innerRadius, 8+
    GLdouble            outerRadius, 8+
    GLint               slices, 4+
    GLint               loops, 4+
    GLdouble            startAngle, 8+
    GLdouble            sweepAngle); 8=48
*/

int main(int argc, char* argv[]) {
  GLUquadric *qobj;
  double innerRadius, outerRadius, startAngle, sweepAngle;
  int slices, loops;
  gluPartialDisk(qobj, innerRadius, outerRadius, slices, loops, startAngle, sweepAngle);
  printf("running - all is fine");
}


P.S.: I'm pretty sure that UAsm and AsmC can handle this correctly, but I would really like to see how exactly an industry standard C or C++ compiler passes the arguments to gluPartialDisk.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: nidud on April 08, 2021, 09:20:03 PM
deleted
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: HSE on April 08, 2021, 09:51:57 PM
Hi Nidud!

Is using edi and esi instead of ecx and edx. What convention is that?

Thanks.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 08, 2021, 10:00:52 PM
Quote from: HSE on April 08, 2021, 09:51:57 PM
Is using edi and esi instead of ecx and edx. What convention is that?

I noticed that, too. Otherwise, it's a very useful suggestion - thanks, Nidud :thup:

Test it with a full example: (https://gcc.godbolt.org/)
#include <stdio.h>
double foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6){
    printf("You passed 6 args: %i %f %i %f %i %f", arg1, arg2, arg3, arg4, arg5, arg6);
    return arg4*2;
}

double bar(int a) {
int pass1=123;
double pass2=123.456;
int pass3=456;
double pass4=456.789;
int pass5=555;
float pass6=666.666;
    return foo(pass1, pass2, pass3, pass4, pass5, pass6);
}
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: nidud on April 08, 2021, 10:02:29 PM
deleted
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: johnsa on April 08, 2021, 10:03:12 PM
If you're getting esi/edi as the regs used in the calling convention that sounds like you're targeting SYSTEM-V (Linux ABI).

REAL8 should be passed in XMMn register as is REAL4, unless it goes into the stack space parameters.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: HSE on April 08, 2021, 10:16:56 PM
Perfect. Thanks  :thumbsup:
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 08, 2021, 10:30:07 PM
Ok, I tried with a more detailed example:
#include <stdio.h>
double foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6){
    printf("You passed 6 args: %i %f %i %f %i %f", arg1, arg2, arg3, arg4, arg5, arg6);
    return arg4*2;
}

double bar(int a) {
int pass1=123;
double pass2=123.456;
int pass3=456;
double pass4=456.789;
int pass5=555;
float pass6=666.666;
    return foo(pass1, pass2, pass3, pass4, pass5, pass6);
}


Godbolt (https://gcc.godbolt.org/) replies as follows, using x64 msvc v19.latest:
$SG5204 DB 'You passed 6 args: %i %f %i %f %i %f', 00H
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
__real@4426aaa0 DD 04426aaa0r ; 666.666
__real@407c8c9fbe76c8b4 DQ 0407c8c9fbe76c8b4r ; 456.789
__real@405edd2f1a9fbe77 DQ 0405edd2f1a9fbe77r ; 123.456
__real@4000000000000000 DQ 04000000000000000r ; 2

arg1$ = 80
arg2$ = 88
arg3$ = 96
arg4$ = 104
arg5$ = 112
arg6$ = 120
double foo(int,double,int,double,int,float) PROC ; foo
$LN3:
  movsd QWORD PTR [rsp+32], xmm3
  mov DWORD PTR [rsp+24], r8d
  movsd QWORD PTR [rsp+16], xmm1
  mov DWORD PTR [rsp+8], ecx
  sub rsp, 72 ; 00000048H
  cvtss2sd xmm0, DWORD PTR arg6$[rsp]
  movsd QWORD PTR [rsp+48], xmm0
  mov eax, DWORD PTR arg5$[rsp]
  mov DWORD PTR [rsp+40], eax
  movsd xmm0, QWORD PTR arg4$[rsp]
  movsd QWORD PTR [rsp+32], xmm0
  mov r9d, DWORD PTR arg3$[rsp]
  movsd xmm2, QWORD PTR arg2$[rsp]
  movq r8, xmm2
  mov edx, DWORD PTR arg1$[rsp]
  lea rcx, OFFSET FLAT:$SG5204
  call printf
  movsd xmm0, QWORD PTR arg4$[rsp]
  mulsd xmm0, QWORD PTR __real@4000000000000000
  add rsp, 72 ; 00000048H
  ret 0
double foo(int,double,int,double,int,float) ENDP ; foo

pass6$ = 48
pass5$ = 52
pass3$ = 56
pass1$ = 60
pass4$ = 64
pass2$ = 72
a$ = 96
double bar(int) PROC ; bar
$LN3:
  mov DWORD PTR [rsp+8], ecx
  sub rsp, 88 ; 00000058H
  mov DWORD PTR pass1$[rsp], 123 ; 0000007bH
  movsd xmm0, QWORD PTR __real@405edd2f1a9fbe77
  movsd QWORD PTR pass2$[rsp], xmm0
  mov DWORD PTR pass3$[rsp], 456 ; 000001c8H
  movsd xmm0, QWORD PTR __real@407c8c9fbe76c8b4
  movsd QWORD PTR pass4$[rsp], xmm0
  mov DWORD PTR pass5$[rsp], 555 ; 0000022bH
  movss xmm0, DWORD PTR __real@4426aaa0
  movss DWORD PTR pass6$[rsp], xmm0
  movss xmm0, DWORD PTR pass6$[rsp]
  movss DWORD PTR [rsp+40], xmm0
  mov eax, DWORD PTR pass5$[rsp]
  mov DWORD PTR [rsp+32], eax
  movsd xmm3, QWORD PTR pass4$[rsp]
  mov r8d, DWORD PTR pass3$[rsp]
  movsd xmm1, QWORD PTR pass2$[rsp]
  mov ecx, DWORD PTR pass1$[rsp]
  call double foo(int,double,int,double,int,float) ; foo
  add rsp, 88 ; 00000058H
  ret 0
double bar(int) ENDP ; bar


It looks ok, but the button to the right of x64 msvc v19.latest remains orange instead of green :sad:
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: hutch-- on April 08, 2021, 10:55:38 PM
I have seen it in the past that various compilers including C compilers do not use the 32 or 64 bit ABI but a scheme of their own that allows them to perform a number of compiler optimisation tricks. I imagine they must still comply with the ABI when interacting with the OS.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 08, 2021, 11:05:19 PM
Quote from: hutch-- on April 08, 2021, 10:55:38 PMI imagine they must still comply with the ABI when interacting with the OS.

I added a GetTickCount():

#include <stdio.h>
#include <Windows.h>
double foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6){
    printf("You passed 6 args: %i %f %i %f %i %f", arg1, arg2, arg3, arg4, arg5, arg6);
    return arg4*GetTickCount();  //###### force interacting with the OS ########
}

double bar(int a) {
int pass1=123;
double pass2=123.456;
int pass3=456;
double pass4=456.789;
int pass5=555;
float pass6=666.666;
    return foo(pass1, pass2, pass3, pass4, pass5, pass6);
}


The process of passing args looks oddly complicated:
$LN3: ; foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6)
  mov DWORD PTR [rsp+8], ecx
  sub rsp, 88 ; 00000058H
  mov DWORD PTR pass1$[rsp], 123 ; 0000007bH
  movsd xmm0, QWORD PTR __real@405edd2f1a9fbe77
  movsd QWORD PTR pass2$[rsp], xmm0
  mov DWORD PTR pass3$[rsp], 456 ; 000001c8H
  movsd xmm0, QWORD PTR __real@407c8c9fbe76c8b4
  movsd QWORD PTR pass4$[rsp], xmm0
  mov DWORD PTR pass5$[rsp], 555 ; 0000022bH
  movss xmm0, DWORD PTR __real@4426aaa0
  movss DWORD PTR pass6$[rsp], xmm0
  movss xmm0, DWORD PTR pass6$[rsp] ; REAL4
  movss DWORD PTR [rsp+40], xmm0
  mov eax, DWORD PTR pass5$[rsp] ; DWORD
  mov DWORD PTR [rsp+32], eax
  movsd xmm3, QWORD PTR pass4$[rsp] ; REAL8
  mov r8d, DWORD PTR pass3$[rsp] ; DWORD
  movsd xmm1, QWORD PTR pass2$[rsp] ; REAL8
  mov ecx, DWORD PTR pass1$[rsp] ; DWORD
  call double foo(int,double,int,double,int,float) ; foo
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: KradMoonRa on April 09, 2021, 07:45:52 AM

#include <stdio.h>#include <stdio.h>
#include <intrin.h>
#include <stdio.h>

__pragma(optimize("g", on))
__pragma(pack(push, 16) )

// Exclude rarely-used stuff from Windows headers
#ifndef VC_EXTRALEAN
#define VC_EXTRALEAN
#endif
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#ifndef STRICT
#define STRICT
#endif

#include <Windows.h>

extern int const pass1=123;
extern double const pass2=123.456;
extern int const pass3=456;
extern double const pass4=456.789;
extern int const pass5=555;
extern float const pass6=666.666f;
extern const char passedrags[] ="You passed 6 args: %i %f %i %f %i %f";

double foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6){
    printf(passedrags, arg1, arg2, arg3, arg4, arg5, arg6);
    return arg4*GetTickCount();  //###### force interacting with the OS ########
}

double bar(int a) {
    return foo(pass1, pass2, pass3, pass4, pass5, pass6);
}



char const * const passedrags DB 'You passed 6 args: %i %f %i %f %i %f', 00H ; passedrags
unsigned __int64 `__local_stdio_printf_options'::`2'::_OptionsStorage DQ 01H DUP (?) ; `__local_stdio_printf_options'::`2'::_OptionsStorage
__real@4426aaa0 DD 04426aaa0r             ; 666.666
__real@407c8c9fbe76c8b4 DQ 0407c8c9fbe76c8b4r   ; 456.789
__real@405edd2f1a9fbe77 DQ 0405edd2f1a9fbe77r   ; 123.456

arg1$ = 96
arg2$ = 104
arg3$ = 112
arg4$ = 120
arg5$ = 128
arg6$ = 136
double foo(int,double,int,double,int,float) PROC                              ; foo
$LN4:
        sub     rsp, 88                             ; 00000058H
        movss   xmm0, DWORD PTR arg6$[rsp]
        movaps  xmm2, xmm1
        mov     eax, DWORD PTR arg5$[rsp]
        mov     r9d, r8d
        cvtps2pd xmm0, xmm0
        mov     edx, ecx
        lea     rcx, OFFSET FLAT:char const * const passedrags       ; passedrags
        movaps  XMMWORD PTR [rsp+64], xmm6
        movq    r8, xmm2
        movsd   QWORD PTR [rsp+48], xmm0
        movaps  xmm6, xmm3
        mov     DWORD PTR [rsp+40], eax
        movsd   QWORD PTR [rsp+32], xmm3
        call    printf
        call    QWORD PTR __imp_GetTickCount
        xorps   xmm0, xmm0
        mov     eax, eax
        cvtsi2sd xmm0, rax
        mulsd   xmm0, xmm6
        movaps  xmm6, XMMWORD PTR [rsp+64]
        add     rsp, 88                             ; 00000058H
        ret     0
double foo(int,double,int,double,int,float) ENDP                              ; foo

a$ = 64
double bar(int) PROC                                 ; bar
$LN4:
        sub     rsp, 56                             ; 00000038H
        movss   xmm0, DWORD PTR __real@4426aaa0
        mov     r8d, 456                      ; 000001c8H
        movsd   xmm3, QWORD PTR __real@407c8c9fbe76c8b4
        mov     ecx, 123                      ; 0000007bH
        movsd   xmm1, QWORD PTR __real@405edd2f1a9fbe77
        movss   DWORD PTR [rsp+40], xmm0
        mov     DWORD PTR [rsp+32], 555             ; 0000022bH
        call    double foo(int,double,int,double,int,float)                 ; foo
        add     rsp, 56                             ; 00000038H
        ret     0
double bar(int) ENDP                                 ; bar
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 09, 2021, 10:41:24 AM
Thanks, KradMoonRa. Can you see any real difference to the disassembly I posted above (under The process of passing args looks oddly complicated)?
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: tenkey on April 14, 2021, 01:45:36 PM
int is 32-bit in MSVC and is not converted to 64-bit. float is REAL4 and is not converted to REAL8. I don't see anything else that seems complicated.

Quote from: jj2007 on April 08, 2021, 11:05:19 PM
The process of passing args looks oddly complicated:
$LN3: ; foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6)
  mov DWORD PTR [rsp+8], ecx
  sub rsp, 88 ; 00000058H
  mov DWORD PTR pass1$[rsp], 123 ; 0000007bH
  movsd xmm0, QWORD PTR __real@405edd2f1a9fbe77
  movsd QWORD PTR pass2$[rsp], xmm0
  mov DWORD PTR pass3$[rsp], 456 ; 000001c8H
  movsd xmm0, QWORD PTR __real@407c8c9fbe76c8b4
  movsd QWORD PTR pass4$[rsp], xmm0
  mov DWORD PTR pass5$[rsp], 555 ; 0000022bH
  movss xmm0, DWORD PTR __real@4426aaa0
  movss DWORD PTR pass6$[rsp], xmm0
  movss xmm0, DWORD PTR pass6$[rsp] ; REAL4, in mem
  movss DWORD PTR [rsp+40], xmm0
  mov eax, DWORD PTR pass5$[rsp] ; DWORD, in mem
  mov DWORD PTR [rsp+32], eax
  movsd xmm3, QWORD PTR pass4$[rsp] ; REAL8, not in r9, in xmm3
  mov r8d, DWORD PTR pass3$[rsp] ; DWORD, in r8 (32-bit half), not in xmm2
  movsd xmm1, QWORD PTR pass2$[rsp] ; REAL8, not in rdx, in xmm1
  mov ecx, DWORD PTR pass1$[rsp] ; DWORD, in rcx (32-bit half), not in xmm0
  call double foo(int,double,int,double,int,float) ; foo

Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: TimoVJL on April 14, 2021, 03:50:48 PM
With Pelles C 1031: double foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6){

foo:
  [0000000000000000] 4883EC58                     sub               rsp,58
  [0000000000000004] 660F7F742440                 movdqa            xmmword ptr [rsp+40],xmm6
  [000000000000000A] 89CA                         mov               edx,ecx
  [000000000000000C] 660F28D1                     movapd            xmm2,xmm1
  [0000000000000010] 4589C1                       mov               r9d,r8d
  [0000000000000013] 660F28F3                     movapd            xmm6,xmm3
  [0000000000000017] 8B842480000000               mov               eax,dword ptr [rsp+80]
32:     printf(passedrags, arg1, arg2, arg3, arg4, arg5, arg6);
  [000000000000001E] F30F10842488000000           movss             xmm0,dword ptr [rsp+88]
  [0000000000000027] F30F5AC0                     cvtss2sd          xmm0,xmm0
  [000000000000002B] F20F11442430                 movsd             qword ptr [rsp+30],xmm0
  [0000000000000031] 89442428                     mov               dword ptr [rsp+28],eax
  [0000000000000035] F20F11742420                 movsd             qword ptr [rsp+20],xmm6
  [000000000000003B] 488D0D00000000               lea               rcx,[passedrags]
  [0000000000000042] 66490F7ED0                   movq              r8,xmm2
  [0000000000000047] E800000000                   call              printf
33:     return arg4*GetTickCount();  //###### force interacting with the OS ########
  [000000000000004C] FF1500000000                 call              qword ptr [__imp_GetTickCount]
  [0000000000000052] F2480F2AC0                   cvtsi2sd          xmm0,rax
  [0000000000000057] F20F59C6                     mulsd             xmm0,xmm6
  [000000000000005B] 660F6F742440                 movdqa            xmm6,xmmword ptr [rsp+40]
  [0000000000000061] 4883C458                     add               rsp,58
  [0000000000000065] C3                           ret               
  [0000000000000066] 90                           nop               
  [0000000000000067] 660F1F840000000000           nop               [rax+rax+0]
34: }
clangfoo:
00000000  4883EC58                 sub rsp, 58h
00000004  0F29742440               movaps xmmword ptr [rsp+40h], xmm6
00000009  660F28F3                 movapd xmm6, xmm3
0000000D  4589C1                   mov r9d, r8d
00000010  89CA                     mov edx, ecx
00000012  8B842480000000           mov eax, dword ptr [rsp+80h]
00000019  F30F10842488000000       movss xmm0, dword ptr [rsp+88h]
00000022  F30F5AC0                 cvtss2sd xmm0, xmm0
00000026  F20F11442430             movsd qword ptr [rsp+30h], xmm0
0000002C  89442428                 mov dword ptr [rsp+28h], eax
00000030  F20F115C2420             movsd qword ptr [rsp+20h], xmm3
00000036  488D0D00000000           lea rcx, [passedrags]
0000003D  660F6FD1                 movdqa xmm2, xmm1
00000041  66490F7EC8               movq r8, xmm1
00000046  E800000000               call printf
0000004B  FF1500000000             call qword ptr [__imp_GetTickCount]
00000051  89C0                     mov eax, eax
00000053  0F57C0                   xorps xmm0, xmm0
00000056  F2480F2AC0               cvtsi2sd xmm0, rax
0000005B  F20F59C6                 mulsd xmm0, xmm6
0000005F  0F28742440               movaps xmm6, xmmword ptr [rsp+40h]
00000064  4883C458                 add rsp, 58h
00000068  C3                       ret
00000069  0F1F8000000000           nop dword ptr [rax], eax
bar:
00000070  4883EC38                 sub rsp, 38h
00000074  48B80000000054D58440     mov rax, 4084D55400000000h
0000007E  4889442430               mov qword ptr [rsp+30h], rax
00000083  48B8B4C876BE9F8C7C40     mov rax, 407C8C9FBE76C8B4h
0000008D  4889442420               mov qword ptr [rsp+20h], rax
00000092  C74424282B020000         mov dword ptr [rsp+28h], 22Bh
0000009A  488D0D00000000           lea rcx, [passedrags]
000000A1  F30F7E1500000000         movq xmm2, qword ptr [__real@405edd2f1a9fbe77]
000000A9  BA7B000000               mov edx, 7Bh
000000AE  66490F7ED0               movq r8, xmm2
000000B3  41B9C8010000             mov r9d, 1C8h
000000B9  E800000000               call printf
000000BE  FF1500000000             call qword ptr [__imp_GetTickCount]
000000C4  89C0                     mov eax, eax
000000C6  F2480F2AC0               cvtsi2sd xmm0, rax
000000CB  F20F590500000000         mulsd xmm0, qword ptr [__real@407c8c9fbe76c8b4]
000000D3  4883C438                 add rsp, 38h
000000D7  C3                       ret
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: LiaoMi on April 14, 2021, 04:15:05 PM
Quote from: tenkey on April 14, 2021, 01:45:36 PM
int is 32-bit in MSVC and is not converted to 64-bit. float is REAL4 and is not converted to REAL8. I don't see anything else that seems complicated.

Quote from: jj2007 on April 08, 2021, 11:05:19 PM
The process of passing args looks oddly complicated:
$LN3: ; foo(int arg1, double arg2, int arg3, double arg4, int arg5, float arg6)
  mov DWORD PTR [rsp+8], ecx
  sub rsp, 88 ; 00000058H
  mov DWORD PTR pass1$[rsp], 123 ; 0000007bH
  movsd xmm0, QWORD PTR __real@405edd2f1a9fbe77
  movsd QWORD PTR pass2$[rsp], xmm0
  mov DWORD PTR pass3$[rsp], 456 ; 000001c8H
  movsd xmm0, QWORD PTR __real@407c8c9fbe76c8b4
  movsd QWORD PTR pass4$[rsp], xmm0
  mov DWORD PTR pass5$[rsp], 555 ; 0000022bH
  movss xmm0, DWORD PTR __real@4426aaa0
  movss DWORD PTR pass6$[rsp], xmm0
  movss xmm0, DWORD PTR pass6$[rsp] ; REAL4, in mem
  movss DWORD PTR [rsp+40], xmm0
  mov eax, DWORD PTR pass5$[rsp] ; DWORD, in mem
  mov DWORD PTR [rsp+32], eax
  movsd xmm3, QWORD PTR pass4$[rsp] ; REAL8, not in r9, in xmm3
  mov r8d, DWORD PTR pass3$[rsp] ; DWORD, in r8 (32-bit half), not in xmm2
  movsd xmm1, QWORD PTR pass2$[rsp] ; REAL8, not in rdx, in xmm1
  mov ecx, DWORD PTR pass1$[rsp] ; DWORD, in rcx (32-bit half), not in xmm0
  call double foo(int,double,int,double,int,float) ; foo


Hi tenkey,

"Plain ints have the natural size suggested by the architecture of the execution environment44; the other signed integer types are provided to meet special needs." - https://timsong-cpp.github.io/cppwp/n3337/basic.fundamental

Here is a typical case of abstraction, when you need to decide what type a function should take in different bit systems.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: tenkey on April 15, 2021, 07:41:04 AM
Do you have any opinions about whether MSVC interpreting plain int as a 32-bit integer (instead of a 64-bit integer in a 64-bit system) is correct or not?
We are also trying to say what "rcx (64-bit reg) contains the 1st argument" means. Does it mean the function expects the argument to always be a 64-bit value or not? The answer is "not always".
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 15, 2021, 08:25:48 AM
Below a table by Martin Liversage in a reply to What is the bit size of long on 64-bit Windows? (https://stackoverflow.com/questions/384502/what-is-the-bit-size-of-long-on-64-bit-windows) on SOF.

Looks simple, right? But M$ has 146 or so different data types to offer, see attachment, plus over 50 ways to execute the call instruction.

Re "correct": Google offers 9,650,000 answers for data types x86 x64. Pick the one that you like most :biggrin:

What do we really need?
chars
DWORDs
pointers
REAL4
REAL8

Plus every now and then a WORD or a QWORD :cool:

(I've marked in red the one type that is different in x86 vs x64)
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: HSE on April 15, 2021, 08:50:27 AM
Hi JJ!

Quote from: jj2007 on April 15, 2021, 08:25:48 AM
What do we really need?

You can follow Hutch' golden standard:

  qword
  sqword
  real8

Not just a coincidence is almost same as Unprototyped functions in x64 ABI (sqword and real8). That are the absolutly basics.

For most purposes you also can need others basics:

  byte
  sbyte
  word
  sword
  dword
  sdword
  real4

For very specifics and unusual purposes :

  fword
  oword
  real10

Everything else is one of previous one (perhaps a couple more in other processors:AVX, etc)

I'm wrong?  :biggrin:



Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 15, 2021, 08:58:17 AM
Right. You rarely need the signed/unsigned distinction, as most instructions don't care about the sign. A fild dword behaves like a fild sdword. The only exception are jumps resp .if .while .repeat ... .until (I use the signed (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1282) equate a lot), but for the call instruction the sign is irrelevant.

The jinvoke version I'm working on will bark at you if you pass 11 or 13 parameters to CreateWindowEx, and it will throw an error if you pass a REAL8 to a function that expects a REAL4 (like many Gdi+ functions). All the rest boils down to RTFM :cool:

P.S.: The great majority of WINAPI calls in x64 still wants DWORDs, not QWORDs. Only pointers (and handles) require 64 bits.
QuoteYou can follow Hutch' golden standard:

  qword
  sqword
  real8

Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: HSE on April 15, 2021, 09:29:39 AM
Quote from: jj2007 on April 15, 2021, 08:58:17 AM
You rarely need the signed/unsigned distinction, as most instructions don't care about the sign.
You allow promotion, that it's important.

Quote from: jj2007 on April 15, 2021, 08:58:17 AM
P.S.: The great majority of WINAPI calls in x64 still wants DWORDs, not QWORDs. Only pointers (and handles) require 64 bits.
For unsigneds is the same, but I'm reviewing that  :thumbsup: 
There is very clear concepts, but past never gone  :biggrin:
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 15, 2021, 06:27:06 PM
Quote from: HSE on April 15, 2021, 09:29:39 AM
Quote from: jj2007 on April 15, 2021, 08:58:17 AM
You rarely need the signed/unsigned distinction, as most instructions don't care about the sign.
You allow promotion, that it's important.

What do you mean with "promotion"?

The point with the signed/unsigned distinction is that it almost never matters:
include \Masm32\MasmBasic\Res\JBasic.inc ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
MyUnsignedDword DWORD -12345
MySignedPointer SQWORD ?
.code
DefProc ShowTheArgs proc argString:QWORD, argSigned:SDWORD, argDouble:REAL8
  Inkey Str$("\nThe arguments:\n%s\n%i\n%f\n", argString, argSigned, argDouble)
  ret
ShowTheArgs endp
Init
  PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
  mov rax, Chr$("Argument #1 is a string")
  mov MySignedPointer, rax ; oops, absolutely ILLEGAL!
  jinvoke ShowTheArgs, rax, MyUnsignedDword, FP8(33333.33333)
EndOfCode


Output:
This program was assembled with ml64 in 64-bit format.

The arguments:
Argument #1 is a string
-12345
33333.333330


We illegally assigned a negative value to MyUnsignedDword and to the string pointer, we illegally passed an unsigned DWORD to a proc that expects a SIGNED DWORD, and yet, the proc has no problem with that. We know why, the programmer of a HLL probably doesn't.

If you try the same game with REALs, trouble is ahead, because REAL4 and REAL8 are not at all compatible.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: HSE on April 15, 2021, 09:39:24 PM
Quote from: jj2007 on April 15, 2021, 06:27:06 PM
What do you mean with "promotion"?

In HLL you can use a SBYTE arg to invoke a function that only read SDWORD. Compiler make transformation. Here invoke macro can make that, or you can make that manually before to invoke.  This transformation from a shorter type to a longer type is "promotion"

x64 ABI Unprototyped and Vararg functions very often require promotion.

Another strange HLL concept is "ellipsis", which apparently is similar to Vararg, but using the last declared type.
Title: Re: X64 ABI: how does a C compiler pass a REAL8?
Post by: jj2007 on April 15, 2021, 11:02:02 PM
Quote from: HSE on April 15, 2021, 09:39:24 PMIn HLL you can use a SBYTE arg to invoke a function that only read SDWORD

Thanks, got it. Something like movsx rax, al :cool: