64-bit inline assembler

General FreeBASIC programming questions.
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Oct 24, 2016 9:11

thank you :-)
TeeEmCee
Posts: 268
Joined: Jul 22, 2006 0:54
Location: Auckland

Re: 64-bit inline assembler

Postby TeeEmCee » Oct 25, 2016 2:19

Sigh... I'd love to try the latest llvm-as, but XCode 5.1.1 is the last one supported on my version of OSX, and when I tried to install llvm-3.9 with macports (which I use for everything), it started installing llvm-3.4 instead (despite the fact that it previously installed 3.5, 3.6 and 3.7), and then started upgrading every package on my system, and then errored out and said I need to delete macports and reinstall, because they switched from libstdc++ to libc++ (isn't the point of installing multiple versions of absolutely everything whether you want it or not to prevent such issues?!). I can't do that, I need a functioning system. Macports has been absolutely nothing but trouble, I am definitely never using it again. Maybe I can install homebrew in parallel...
marcov
Posts: 2839
Joined: Jun 16, 2005 9:45
Location: Eindhoven, NL
Contact:

Re: 64-bit inline assembler

Postby marcov » Oct 25, 2016 7:50

TeeEmCee wrote: (because OSX uses a 30 year old fork of GNU binutils),


It doesn't, at least not GNU-, it is based on original BSD binutils, before most of them switched to GNU. But afaik several of them also switched to LLVM, the GNU usage was always heavily debated in the BSD camp due to the restrictive license.
TeeEmCee
Posts: 268
Joined: Jul 22, 2006 0:54
Location: Auckland

Re: 64-bit inline assembler

Postby TeeEmCee » Oct 26, 2016 13:26

OK, my mistake. I also never knew that BSDs used any of the GNU binutils aside from GCC itself. However, the assembler in cctools (OSX equivalent of binutils, I didn't know at the time that some of those utilties are obsoleted by llvm), as of a couple months ago when I downloaded it, is an ancient fork of GNU gas (sometimes referred to as FSF gas since Apple as also calls/called itself gas). And looking at the changelog, it only received a couple of commits per year on average!

I see the switch to llvm-as was introduced in XCode 7, last year.
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: 64-bit inline assembler

Postby MichaelW » Nov 04, 2016 17:14

This is a test of variable access from inline assembly:

Code: Select all


dim shared as integer shared1 = 1, shared2
dim shared as integer shared3 = 1, shared4

sub Test
    print shared1, shared2
    asm
        mov     rax, shared1
        mov     shared2, rax
    end asm
    print shared1, shared2   
end sub

dim as integer module1 = 1, module2

print
print shared3, shared4
print module1, module2

asm
    mov     rax, shared3
    mov     shared4, rax
    mov     rax, module1
    mov     module2, rax   
end asm

print shared3, shared4
print module1, module2

print

Test

sleep


Variable access by variable name is simple and straightforward, and here is why:

Code: Select all

   .file   "VarAccess.c"
   .intel_syntax noprefix
   .section   .text.unlikely,"x"
.LCOLDB0:
   .text
.LHOTB0:
   .p2align 4,,15
   .globl   TEST
   .def   TEST;   .scl   2;   .type   32;   .endef
TEST:
.L2:
   push   r15
   push   r14
   xor   ecx, ecx
   push   r13
   push   r12
   mov   r8d, 2
   push   rdi
   push   rsi
   push   rbx
   sub   rsp, 32
   mov   rdx, QWORD PTR SHARED1$[rip]
   call   fb_PrintLongint
   mov   rdx, QWORD PTR SHARED2$[rip]
   mov   r8d, 1
   xor   ecx, ecx
   call   fb_PrintLongint
/APP
 # 27 "VarAccess.c" 1
   mov     rax, QWORD PTR SHARED1$[rip]
 # 0 "" 2
 # 28 "VarAccess.c" 1
   mov     QWORD PTR SHARED2$[rip], rax
 # 0 "" 2
/NO_APP
   mov   rdx, QWORD PTR SHARED1$[rip]
   mov   r8d, 2
   xor   ecx, ecx
   call   fb_PrintLongint
   mov   rdx, QWORD PTR SHARED2$[rip]
   add   rsp, 32
   mov   r8d, 1
   pop   rbx
   pop   rsi
   pop   rdi
   pop   r12
   pop   r13
   pop   r14
   pop   r15
   xor   ecx, ecx
   jmp   fb_PrintLongint
   .section   .text.unlikely,"x"
.LCOLDE0:
   .text
.LHOTE0:
   .def   __main;   .scl   2;   .type   32;   .endef
   .section   .text.unlikely,"x"
.LCOLDB1:
   .section   .text.startup,"x"
.LHOTB1:
   .p2align 4,,15
   .globl   main
   .def   main;   .scl   2;   .type   32;   .endef
main:
   push   r15
   push   r14
   push   r13
   push   r12
   push   rdi
   push   rsi
   mov   rsi, rdx
   push   rbx
   mov   ebx, ecx
   sub   rsp, 48
   call   __main
   xor   r8d, r8d
   mov   rdx, rsi
   mov   ecx, ebx
   call   fb_Init
   xor   ecx, ecx
   mov   QWORD PTR 40[rsp], 0
   mov   edx, 1
   mov   QWORD PTR 32[rsp], 1
   call   fb_PrintVoid
   mov   rdx, QWORD PTR SHARED3$[rip]
   xor   ecx, ecx
   mov   r8d, 2
   call   fb_PrintLongint
   mov   rdx, QWORD PTR SHARED4$[rip]
   xor   ecx, ecx
   mov   r8d, 1
   call   fb_PrintLongint
   mov   rdx, QWORD PTR 32[rsp]
   xor   ecx, ecx
   mov   r8d, 2
   call   fb_PrintLongint
   mov   rdx, QWORD PTR 40[rsp]
   mov   r8d, 1
   xor   ecx, ecx
   call   fb_PrintLongint
/APP
 # 49 "VarAccess.c" 1
   mov     rax, QWORD PTR SHARED3$[rip]
 # 0 "" 2
 # 50 "VarAccess.c" 1
   mov     QWORD PTR SHARED4$[rip], rax
 # 0 "" 2
 # 51 "VarAccess.c" 1
   mov     rax, QWORD PTR 32[rsp]
 # 0 "" 2
 # 52 "VarAccess.c" 1
   mov     QWORD PTR 40[rsp], rax   
 # 0 "" 2
/NO_APP
   mov   rdx, QWORD PTR SHARED3$[rip]
   mov   r8d, 2
   xor   ecx, ecx
   call   fb_PrintLongint
   mov   rdx, QWORD PTR SHARED4$[rip]
   mov   r8d, 1
   xor   ecx, ecx
   call   fb_PrintLongint
   mov   rdx, QWORD PTR 32[rsp]
   mov   r8d, 2
   xor   ecx, ecx
   call   fb_PrintLongint
   mov   rdx, QWORD PTR 40[rsp]
   mov   r8d, 1
   xor   ecx, ecx
   call   fb_PrintLongint
   mov   edx, 1
   xor   ecx, ecx
   call   fb_PrintVoid
   call   TEST
   mov   ecx, -1
   call   fb_Sleep
   xor   ecx, ecx
   call   fb_End
   add   rsp, 48
   xor   eax, eax
   pop   rbx
   pop   rsi
   pop   rdi
   pop   r12
   pop   r13
   pop   r14
   pop   r15
   ret
   .section   .text.unlikely,"x"
.LCOLDE1:
   .section   .text.startup,"x"
.LHOTE1:
.lcomm SHARED4$,8,8
   .data
   .align 8
SHARED3$:
   .quad   1
.lcomm SHARED2$,8,8
   .align 8
SHARED1$:
   .quad   1
   .ident   "GCC: (x86_64-win32-sjlj-rev0, Built by MinGW-W64 project) 5.2.0"
   .def   fb_PrintLongint;   .scl   2;   .type   32;   .endef
   .def   fb_Init;   .scl   2;   .type   32;   .endef
   .def   fb_PrintVoid;   .scl   2;   .type   32;   .endef
   .def   fb_Sleep;   .scl   2;   .type   32;   .endef
   .def   fb_End;   .scl   2;   .type   32;   .endef

For variable access the compiler is setting up RIP-relative addressing, which provides for position-independent code, and a smaller instruction encoding, but which limits the combined size of the code and static data to 2 GB.
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Nov 04, 2016 17:37

hello MichaelW
thank you for the example, works ok on Windows and Linux, but not on my Mac
FreeBASIC Compiler - Version 1.06.0 (11-02-2016), built for darwin-x86_64 (64bit)
Copyright (C) 2004-2016 The FreeBASIC development team.
target: darwin-x86_64, x86-64, 64bit
compiling: test35.bas -o test35.c (main module)
assembling: as -arch x86_64 "test35.asm" -o "test35.o"
test35.asm:31:2: error: 32-bit absolute addressing is not supported in 64-bit mode
mov rax, shared1
^
assembling failed: 'as' terminated with exit code 1
Compilation failed.

edit: apparently on os x x64 all code must be relocatable
MichaelW
Posts: 3500
Joined: May 16, 2006 22:34
Location: USA

Re: 64-bit inline assembler

Postby MichaelW » Nov 06, 2016 2:07

Per Agner Fog's calling_conventions.pdf, available here, for Linux and BSD, for the default small memory model the compiler can use 32-bit signed absolute addresses. There are two other models listed for application programs (medium and large).
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Nov 06, 2016 2:50

thank you for the reference.
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Jun 19, 2017 18:22

another very simple example
<I am glad no one looks at my silly posts because I can correct any blunders before anyone notices them. ha ha>

Code: Select all

#ifdef __FB_WIN32__
   #print "__FB_WIN32__ is defined"
   #ifdef __FB_64BIT__
      #print "__FB_64BIT__ is defined"
      sub add (byval operand1 as long, byval operand2 as long, byref result as long)
         asm
            mov eax, dword ptr [operand1]
            add eax, dword ptr [operand2]
            mov rcx, qword ptr [result]
            mov dword ptr [rcx], eax
         end asm
      end sub
   #else
      sub add (byval operand1 as long, byval operand2 as long, byref result as long)
         asm
            mov eax, dword ptr [operand1]
            add eax, dword ptr [operand2]
            mov ecx, dword ptr [result]
            mov dword ptr [ecx], eax
         end asm
      end sub
   #endif
#endif
TeeEmCee
Posts: 268
Joined: Jul 22, 2006 0:54
Location: Auckland

Re: 64-bit inline assembler

Postby TeeEmCee » Jun 20, 2017 4:29

Since the entire sub is written in assembly, you could make it a naked function. I've never used them.
Also, instead of taking the result byref, why not return the result instead?
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Jun 20, 2017 6:20

yes, a function seems logical, not only that but the example needs to show the difference between 32 and 64 bit.

Code: Select all

#ifdef __FB_WIN32__
   #print "__FB_WIN32__ is defined"
   #ifdef __FB_64BIT__
      #print "__FB_64BIT__ is defined"
      function add (byval operand1 as Integer, byval operand2 as Integer) as Integer
         asm
            mov rax, qword ptr [operand1]
            add rax, qword ptr [operand2]
            mov qword ptr [function], rax
         end asm
      end function
   #else
      function add (byval operand1 as Integer, byval operand2 as Integer) as Integer
         asm
            mov eax, dword ptr [operand1]
            add eax, dword ptr [operand2]
            mov dword ptr [function], eax
         end asm
      end function
   #endif
#endif
dim as Integer a=123,b=456,c

c=add(a,b)
print a;" +";b;" = ";c
TeeEmCee
Posts: 268
Joined: Jul 22, 2006 0:54
Location: Auckland

Re: 64-bit inline assembler

Postby TeeEmCee » Jun 21, 2017 5:04

Interesting, I didn't realise that you put the result in "function".

It also didn't occur to me that you can't use function/sub argument names as shorthand in a naked function/sub. It's too bad, but it makes sense. Here's the naked version:

Code: Select all

function add naked cdecl (byval operand1 as Integer, byval operand2 as Integer) as Integer
    asm
        #ifdef __FB_64BIT__
        ' Arguments are passed in rdi, rsi, rdx, rcx, r8, r9
            mov rax, rdi
            add rax, rsi
            ret
        #else
            mov eax, dword ptr [esp+4]
            add eax, dword ptr [esp+8]
            ret
        #endif
    end asm
end function

dim as Integer a=123,b=456,c

c=add(a,b)
print a;" +";b;" = ";c
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Jun 21, 2017 12:45

thank you TeeEmCee for the naked function.
however the calling convention on Windows x64 differs from that of other OS's
I amended your code to accommodate for Windows x64, hope you don't mind.

Code: Select all

function add naked cdecl (byval operand1 as Integer, byval operand2 as Integer) as Integer
    asm
      #ifdef __FB_WIN32__ ' returns true even on Windows x64
         #ifdef __FB_64BIT__
            ' in Windows x64 Arguments are passed in rcx, rdx, r8 and r9
            ' see https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
            mov rax, rcx
            add rax, rdx
            ret
         #else
            mov eax, dword ptr [esp+4]
            add eax, dword ptr [esp+8]
            ret
         #endif
      #else ' if OS is Solaris, Linux, BSD or OS X then
         #ifdef __FB_64BIT__
            ' Arguments are passed in rdi, rsi, rdx, rcx, r8, r9
            mov rax, rdi
            add rax, rsi
            ret
         #else
            mov eax, dword ptr [esp+4]
            add eax, dword ptr [esp+8]
            ret
         #endif
      #endif
    end asm
end function

dim as Integer a=123,b=456,c

c=add(a,b)
print a;" +";b;" = ";c

double version

Code: Select all

function add naked cdecl (byval operand1 as Double, byval operand2 as Double) as Double
    asm
      #ifdef __FB_WIN32__ ' returns true even on Windows x64
         #ifdef __FB_64BIT__
            ' in Windows x64 Arguments are passed in rcx, rdx, r8 and r9
            ' the floats are passed in registers xmm0, xmm1, xmm2 and xmm3
            ' see https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
            addsd xmm0, xmm1 'result is left in xmm0
            ret
         #else
            fld qword ptr [esp+4]
            fadd qword ptr [esp+12] 'result is left in st(0)
            ret
         #endif
      #else ' if OS is Solaris, Linux, BSD or OS X then
         #ifdef __FB_64BIT__
            ' Arguments are passed in rdi, rsi, rdx, rcx, r8, r9
            ' xmm0-7
            addsd xmm0, xmm1 'result is left in xmm0
            ret
         #else
            fld qword ptr [esp+4]
            fadd qword ptr [esp+12] 'result is left in st(0)
            ret
         #endif
      #endif
    end asm
end function

dim as Double a=123,b=456,c

c=add(a,b)
print a;" +";b;" = ";c
srvaldez
Posts: 2253
Joined: Sep 25, 2005 21:54

Re: 64-bit inline assembler

Postby srvaldez » Jun 21, 2017 14:59

TeeEmCee wrote:
srvaldez wrote:I know that the Mac is not a supported platform but naked functions fail...
the main program calls a decorated function whereas the naked function was not decorated.
btw, it works ok on windows and linux

I noticed that bug and and a pile of other OSX ones and fixed it, but I haven't submitted a pull request yet. You can try it though.

I just compiled your git repo at https://github.com/rversteegen/fbc but still get the same error for naked functions, does that repo not have your fixes?
any help is much appreciated.
TeeEmCee
Posts: 268
Joined: Jul 22, 2006 0:54
Location: Auckland

Re: 64-bit inline assembler

Postby TeeEmCee » Jun 21, 2017 15:22

Oh! Right, I compiled on Linux x64. I didn't try on Mac.
Also, I see I added "cdecl" to the function definition without thinking; on 32-bit Windows that changes the calling convention (but I don't know about 64-bit Windows)

What error do you get? Did you use at&t or intel syntax asm? My FB branch defaults to att syntax on Mac, so you may need -asm intel. Did you use -gen gas?
Oh, did you use my 'mac' branch, or master (which is just a copy of upstream)? You should see all the mac-specific changes in the git log (which I've sadly been too lazy to finish)

As we discussed earlier in this thread, recent versions of XCode include a completely different assembler which is part of the llvm/clang suite, which had horribly broken Intel syntax support, not the old GNU-derived one used previously (Apple GAS), which also has completely broken Intel syntax support. But recent versions of XCode don't run on OSX 10.8, which is what I still have on my Mac, so I can't test it. I'm not sure I can even easily install recent clang via macports, due to recent OSX switching to libstdc++; it's a total mess. I haven't touched the FB Mac port in a long time.

Use the most recent XCode possible; a major bug in the llvm assembler misassembling the "push" instruction when using intel syntax was only fixed half a year ago. Alternatively, install clang directly.

If llvm-as is less buggy now, it's possible that all of "-gen gas -asm intel", "-gen gcc -asm intel" and "-gen gcc -asm att" could work, so you can try them all.

Return to “General”

Who is online

Users browsing this forum: No registered users and 2 guests