digitalmars.D.learn - inline ASM function calling conventions.
- Sjoerd Nijboer (31/31) Sep 30 2018 I'm kinda puzzled.
- Basile B. (85/117) Sep 30 2018 Hello, i think this should be here
- Basile B. (28/36) Sep 30 2018 Without all the save/restore BS:
- Sjoerd Nijboer (6/40) Sep 30 2018 Thank you for the link, very informative!
- kinke (35/67) Sep 30 2018 1) `asm {}` is supported by DMD and LDC, but not by GDC.
- Sjoerd Nijboer (8/34) Sep 30 2018 Good to know.
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?
Sep 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
Sep 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: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); }[...]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:
Sep 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:Thank you for the link, very informative! I'm spitting it through right now.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); }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.
Sep 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 } } }
Sep 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!
Sep 30 2018