Thread overview
inline ASM function calling conventions.
Sep 30, 2018
Sjoerd Nijboer
Sep 30, 2018
Basile B.
Sep 30, 2018
Basile B.
Sep 30, 2018
Sjoerd Nijboer
Sep 30, 2018
kinke
Sep 30, 2018
Sjoerd Nijboer
September 30, 2018
I'm kinda puzzled.

I'm having trouble getting started with inline asm in D.
Suppowse I have the following:

void Foo(MyStrunct* first_arg, MyStrunct* second_arg)
{
    asm
    {
        naked;
        version(X86)
        {
            /* Do something with the content of I and J. */
        }
        version(X86_64)
        {
            /* Do something with the content of I and J. *?
        }
    }
}

void Bar()
{
    MyStrunct heapStructA, heapStructB;

    // do some initialization.

    Foo(heapStructA, heapStructB);
}

Suppose I would like to pass first_arg and second_arg by register to `Foo`, what calling convention should I use? I'm having trouble identifying the correct calling convention I should be using for X86 and X86_64 in D.

I've tried a couple using https://godbolt.org/ and DMD parameters '-g -m32', but I can't seem to find any that will pass by register. Is there one? Will it also work for LDC and GDC?
September 30, 2018
On Sunday, 30 September 2018 at 10:46:33 UTC, Sjoerd Nijboer wrote:
> I'm kinda puzzled.
>
> I'm having trouble getting started with inline asm in D.
> Suppowse I have the following:
>
> void Foo(MyStrunct* first_arg, MyStrunct* second_arg)
> {
>     asm
>     {
>         naked;
>         version(X86)
>         {
>             /* Do something with the content of I and J. */
>         }
>         version(X86_64)
>         {
>             /* Do something with the content of I and J. *?
>         }
>     }
> }
>
> void Bar()
> {
>     MyStrunct heapStructA, heapStructB;
>
>     // do some initialization.
>
>     Foo(heapStructA, heapStructB);
> }
>
> Suppose I would like to pass first_arg and second_arg by register to `Foo`, what calling convention should I use? I'm having trouble identifying the correct calling convention I should be using for X86 and X86_64 in D.
>
> I've tried a couple using https://godbolt.org/ and DMD parameters '-g -m32', but I can't seem to find any that will pass by register. Is there one? Will it also work for LDC and GDC?

Hello, i think this should be here (https://dlang.org/spec/abi.html) because myself i never remember them correctly without playing a bit with a disassembler.
After playing a bit:

Linux x86_64:

extern(D) void Foo(MyStruct* s1, MyStruct* s2, MyStruct* s3, MyStruct* s4)
{
    asm
    {
        naked;
        // rcx -> s1
        // rdx -> s2
        // rsi -> s3
        // rdi -> s4
        ret;
    }
}

====================

Linux x86 / Window x86:

extern(D) void Foo(MyStruct* s1, MyStruct* s2, MyStruct* s3, MyStruct* s4)
{
    asm
    {
        naked;
        // ebx -> s1
        // edx -> s2
        // ecx -> s3
        // eax -> s4
        ret;
    }
}

====================

Win64 has a different calling convention than Linux64, i cant remember the difference.

====================

Linux x86_64 example:

module runnable;

import std.stdio;

alias writeInt = writeln!int;

struct MyStruct{int i;}

extern(D) void foo(MyStruct* s1, MyStruct* s2, MyStruct* s3, MyStruct* s4)
{
    asm
    {
        naked;
        mov     RAX, RDI; // move s4 here since we call something...

        push    RDX;    // save s2
        push    RSI;    // save s3
        push    RAX;    // save s4
        mov     RDI, MyStruct.i.offsetof[RCX];
        call    writeInt;
        pop     RAX;    // restore...
        pop     RSI;
        pop     RDX;

        push    RSI;  // save s3
        push    RAX;  // save s4
        mov     RDI, MyStruct.i.offsetof[RDX];
        call    writeInt;
        pop     RAX; // restore
        pop     RSI;

        push    RAX; // save s4
        mov     RDI, MyStruct.i.offsetof[RSI];
        call    writeInt;
        pop     RAX; // restore


        mov     RDI, MyStruct.i.offsetof[RAX];
        call    writeInt;

        ret;
    }
}

void main()
{
    MyStruct s1 = MyStruct(1);
    MyStruct s2 = MyStruct(2);
    MyStruct s3 = MyStruct(3);
    MyStruct s4 = MyStruct(4);
    foo(&s1, &s2, &s3, &s4);
}

here too :

https://run.dlang.io/is/CWd4aO
September 30, 2018
On Sunday, 30 September 2018 at 11:53:17 UTC, Basile B. wrote:
> On Sunday, 30 September 2018 at 10:46:33 UTC, Sjoerd Nijboer wrote:
>> [...]
>
> Hello, i think this should be here (https://dlang.org/spec/abi.html) because myself i never remember them correctly without playing a bit with a disassembler.
> After playing a bit:
>

Without all the save/restore BS:

module runnable;

import std.stdio;

alias write4Int = writeln!(int,int,int,int);

struct MyStruct{int i;}

extern(D) void foo(ref MyStruct s1, ref MyStruct s2, ref MyStruct s3, ref MyStruct s4)
{
    asm
    {
        naked;
        mov     RCX, MyStruct.i.offsetof[RCX];
        mov     RDX, MyStruct.i.offsetof[RDX];
        mov     RSI, MyStruct.i.offsetof[RSI];
        mov     RDI, MyStruct.i.offsetof[RDI];
        call    write4Int;
        ret;
    }
}

void main()
{
    MyStruct s1 = MyStruct(1);
    MyStruct s2 = MyStruct(2);
    MyStruct s3 = MyStruct(3);
    MyStruct s4 = MyStruct(4);
    foo(s1, s2, s3, s4);
}
September 30, 2018
On Sunday, 30 September 2018 at 10:46:33 UTC, Sjoerd Nijboer wrote:
> I'm kinda puzzled.
>
> I'm having trouble getting started with inline asm in D.
> Suppowse I have the following:
>
> void Foo(MyStrunct* first_arg, MyStrunct* second_arg)
> {
>     asm
>     {
>         naked;
>         version(X86)
>         {
>             /* Do something with the content of I and J. */
>         }
>         version(X86_64)
>         {
>             /* Do something with the content of I and J. *?
>         }
>     }
> }
>
> void Bar()
> {
>     MyStrunct heapStructA, heapStructB;
>
>     // do some initialization.
>
>     Foo(heapStructA, heapStructB);
> }
>
> Suppose I would like to pass first_arg and second_arg by register to `Foo`, what calling convention should I use? I'm having trouble identifying the correct calling convention I should be using for X86 and X86_64 in D.
>
> I've tried a couple using https://godbolt.org/ and DMD parameters '-g -m32', but I can't seem to find any that will pass by register. Is there one? Will it also work for LDC and GDC?

1) `asm {}` is supported by DMD and LDC, but not by GDC.
2) `extern(D)` reverses the args - `foo(a, b)` is actually `foo(b, a)` on a lower level.
3) The 32-bit x86 D calling convention (independent from OS) is specified here: https://dlang.org/spec/abi.html#function_calling_conventions
   All args are passed on the stack, except for the last one under certain circumstances, see point 38.12.3.3.
4) For x86_64, there are 2 (completely different) ABIs, Win64 and the System V one. Specs can be found online.

In your case:

void Foo(MyStrunct* first_arg, MyStrunct* second_arg)
{
    asm { naked; }
    version (D_InlineAsm_X86)
    {
        // first_arg is on the stack, at [ESP+4]
        // second_arg is in EAX
    }
    else version (D_InlineAsm_X86_64)
    {
        version (Win64)
        {
            // first_arg is in RDX
            // second_arg is in RCX
        }
        else
        {
            // first_arg is in RSI
            // second_arg is in RDI
        }
    }
}
September 30, 2018
On Sunday, 30 September 2018 at 12:07:53 UTC, Basile B. wrote:
> On Sunday, 30 September 2018 at 11:53:17 UTC, Basile B. wrote:
>>
>> Hello, i think this should be here (https://dlang.org/spec/abi.html) because myself i never remember them correctly without playing a bit with a disassembler.
>> After playing a bit:

Thank you for the link, very informative!
I'm spitting it through right now.

>
> Without all the save/restore BS:
>
> module runnable;
>
> import std.stdio;
>
> alias write4Int = writeln!(int,int,int,int);
>
> struct MyStruct{int i;}
>
> extern(D) void foo(ref MyStruct s1, ref MyStruct s2, ref MyStruct s3, ref MyStruct s4)
> {
>     asm
>     {
>         naked;
>         mov     RCX, MyStruct.i.offsetof[RCX];
>         mov     RDX, MyStruct.i.offsetof[RDX];
>         mov     RSI, MyStruct.i.offsetof[RSI];
>         mov     RDI, MyStruct.i.offsetof[RDI];
>         call    write4Int;
>         ret;
>     }
> }
>
> void main()
> {
>     MyStruct s1 = MyStruct(1);
>     MyStruct s2 = MyStruct(2);
>     MyStruct s3 = MyStruct(3);
>     MyStruct s4 = MyStruct(4);
>     foo(s1, s2, s3, s4);
> }

In X86_64 it works beautiful!
But in X86 it will pass them via the stack, and I'm trying to get rid of that.


September 30, 2018
On Sunday, 30 September 2018 at 12:32:08 UTC, kinke wrote:
> 1) `asm {}` is supported by DMD and LDC, but not by GDC.

Good to know.
Guess I will be targeting DMD and LDC then.

> 4) For x86_64, there are 2 (completely different) ABIs, Win64 and the System V one. Specs can be found online.
>
> In your case:
>
> void Foo(MyStrunct* first_arg, MyStrunct* second_arg)
> {
>     asm { naked; }
>     version (D_InlineAsm_X86)
>     {
>         // first_arg is on the stack, at [ESP+4]
>         // second_arg is in EAX
>     }
>     else version (D_InlineAsm_X86_64)
>     {
>         version (Win64)
>         {
>             // first_arg is in RDX
>             // second_arg is in RCX
>         }
>         else
>         {
>             // first_arg is in RSI
>             // second_arg is in RDI
>         }
>     }
> }

So in X86 asm, if I want to pas two parameters to a function I can have one in a register, but not two. The second will go via the stack. But on X86_64 it won't?
Guess I'll just have to live with that.
Thank you!