Thread overview | ||||||||
---|---|---|---|---|---|---|---|---|
|
September 30, 2018 inline ASM function calling conventions. | ||||
---|---|---|---|---|
| ||||
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 Re: inline ASM function calling conventions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sjoerd Nijboer | 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 Re: inline ASM function calling conventions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Basile B. | 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 Re: inline ASM function calling conventions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Sjoerd Nijboer | 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 Re: inline ASM function calling conventions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to Basile B. | 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 Re: inline ASM function calling conventions. | ||||
---|---|---|---|---|
| ||||
Posted in reply to kinke | 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! |
Copyright © 1999-2021 by the D Language Foundation