digitalmars.D.learn - Inline assembly question
- Dibyendu Majumdar (28/28) Nov 12 2017 Hi,
- Nicholas Wilson (4/32) Nov 12 2017 You could do it with a mixin, it would be rather ugly though. Not
- Eugene Wissner (28/56) Nov 12 2017 Here is an example with mixins:
- Dibyendu Majumdar (12/73) Nov 12 2017 Thank you - I probably could use something like this. It is
- Basile B. (4/18) Nov 12 2017 https://dlang.org/spec/iasm.html#agregate_member_offsets
- Dibyendu Majumdar (5/26) Nov 12 2017 Sorry I didn't phrase my question accurately. Presumably to use
- Eugene Wissner (6/34) Nov 12 2017 You can use just string parameters instead of enums, then you can
- Dibyendu Majumdar (8/20) Nov 12 2017 Okay thank you. Sigh. It would be so much simpler to be able to
- Basile B. (15/38) Nov 12 2017 yeah about stack frame..., also don't forget to mark the asm
- Dibyendu Majumdar (28/46) Nov 12 2017 I am not sure I have understood above; will DMD generate the
- Basile B. (3/7) Nov 12 2017 no in naked mode you have to save and restore by hand.
- Dibyendu Majumdar (8/15) Nov 12 2017 So how does one manually generate the .pdata and .xdata sections?
- Guillaume Piolat (5/10) Nov 13 2017 naked;
- Dibyendu Majumdar (7/14) Nov 13 2017 Hi, any further info on this? I am not talking here of the
- Basile B. (4/15) Nov 12 2017 I see...the problem is not the input parameters but functions
- Dibyendu Majumdar (11/26) Nov 12 2017 Not sure I understand the question. Once the defines are there I
- Basile B. (27/55) Nov 13 2017 TBH I wonder if this is not worth a enhancement (or even a DIP)
- Basile B. (4/32) Nov 13 2017 Hmmm being the fact that "First" might be XMM0 i actually think
- Guillaume Piolat (5/26) Nov 13 2017 Something that happen quite frequently is duplicating very
- Dibyendu Majumdar (6/29) Nov 13 2017 Hi, that would be nice but I won't be holding my breath for such
Hi, I have recently started work on building a VM for Lua (actually a derivative of Lua) in X86-64 assembly. I am using the dynasm tool that is part of LuaJIT. I was wondering whether I could also write this in D's inline assembly perhaps, but there is one aspect that I am not sure how to do. The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. |.if X64WIN |.define CARG1, rcx // x64/WIN64 C call arguments. |.define CARG2, rdx |.define CARG3, r8 |.define CARG4, r9 |.else |.define CARG1, rdi // x64/POSIX C call arguments. |.define CARG2, rsi |.define CARG3, rdx |.define CARG4, rcx |.endif With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures. How would one do this in D inline assembly? Thanks and Regards Dibyendu
Nov 12 2017
On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Hi, I have recently started work on building a VM for Lua (actually a derivative of Lua) in X86-64 assembly. I am using the dynasm tool that is part of LuaJIT. I was wondering whether I could also write this in D's inline assembly perhaps, but there is one aspect that I am not sure how to do. The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. |.if X64WIN |.define CARG1, rcx // x64/WIN64 C call arguments. |.define CARG2, rdx |.define CARG3, r8 |.define CARG4, r9 |.else |.define CARG1, rdi // x64/POSIX C call arguments. |.define CARG2, rsi |.define CARG3, rdx |.define CARG4, rcx |.endif With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures. How would one do this in D inline assembly? Thanks and Regards DibyenduYou could do it with a mixin, it would be rather ugly though. Not sure of another way off the top of my head.
Nov 12 2017
On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Hi, I have recently started work on building a VM for Lua (actually a derivative of Lua) in X86-64 assembly. I am using the dynasm tool that is part of LuaJIT. I was wondering whether I could also write this in D's inline assembly perhaps, but there is one aspect that I am not sure how to do. The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. |.if X64WIN |.define CARG1, rcx // x64/WIN64 C call arguments. |.define CARG2, rdx |.define CARG3, r8 |.define CARG4, r9 |.else |.define CARG1, rdi // x64/POSIX C call arguments. |.define CARG2, rsi |.define CARG3, rdx |.define CARG4, rcx |.endif With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures. How would one do this in D inline assembly? Thanks and Regards DibyenduHere is an example with mixins: version (Windows) { enum Reg : string { CARG1 = "RCX", CARG2 = "RDX", } } else { enum Reg : string { CARG1 = "RDI", CARG2 = "RSI", } } template Instruction(string I, Reg target, Reg source) { enum string Instruction = "asm { mov " ~ target ~ ", " ~ source ~ "; }"; } void func() { mixin(Instruction!("mov", Reg.CARG1, Reg.CARG2)); }
Nov 12 2017
On Sunday, 12 November 2017 at 11:55:23 UTC, Eugene Wissner wrote:On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Thank you - I probably could use something like this. It is uglier than the simpler approach in dynasm of course. How about when I need to combine this with some struct/union access? In dynasm I can write: | mov BASE, CI->u.l.base // BASE = ci->u.l.base (volatile) | mov PC, CI->u.l.savedpc // PC = CI->u.l.savedpc How can I mix the mixin above and combine with struct offsets? Thanks and Regards DibyenduI have recently started work on building a VM for Lua (actually a derivative of Lua) in X86-64 assembly. I am using the dynasm tool that is part of LuaJIT. I was wondering whether I could also write this in D's inline assembly perhaps, but there is one aspect that I am not sure how to do. The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. |.if X64WIN |.define CARG1, rcx // x64/WIN64 C call arguments. |.define CARG2, rdx |.define CARG3, r8 |.define CARG4, r9 |.else |.define CARG1, rdi // x64/POSIX C call arguments. |.define CARG2, rsi |.define CARG3, rdx |.define CARG4, rcx |.endif With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures. How would one do this in D inline assembly? Thanks and Regards DibyenduHere is an example with mixins: version (Windows) { enum Reg : string { CARG1 = "RCX", CARG2 = "RDX", } } else { enum Reg : string { CARG1 = "RDI", CARG2 = "RSI", } } template Instruction(string I, Reg target, Reg source) { enum string Instruction = "asm { mov " ~ target ~ ", " ~ source ~ "; }"; } void func() { mixin(Instruction!("mov", Reg.CARG1, Reg.CARG2)); }
Nov 12 2017
On Sunday, 12 November 2017 at 12:17:51 UTC, Dibyendu Majumdar wrote:On Sunday, 12 November 2017 at 11:55:23 UTC, Eugene Wissner wrote:https://dlang.org/spec/iasm.html#agregate_member_offsets aggregate.member.offsetof[someregister][...]Thank you - I probably could use something like this. It is uglier than the simpler approach in dynasm of course. How about when I need to combine this with some struct/union access? In dynasm I can write: | mov BASE, CI->u.l.base // BASE = ci->u.l.base (volatile) | mov PC, CI->u.l.savedpc // PC = CI->u.l.savedpc How can I mix the mixin above and combine with struct offsets? Thanks and Regards Dibyendu
Nov 12 2017
On Sunday, 12 November 2017 at 12:32:09 UTC, Basile B. wrote:On Sunday, 12 November 2017 at 12:17:51 UTC, Dibyendu Majumdar wrote:Sorry I didn't phrase my question accurately. Presumably to use above with the mnemonics I would need additional mixin templates where the aggregate type and member etc would need to be parameters?On Sunday, 12 November 2017 at 11:55:23 UTC, Eugene Wissner wrote:https://dlang.org/spec/iasm.html#agregate_member_offsets aggregate.member.offsetof[someregister][...]Thank you - I probably could use something like this. It is uglier than the simpler approach in dynasm of course. How about when I need to combine this with some struct/union access? In dynasm I can write: | mov BASE, CI->u.l.base // BASE = ci->u.l.base (volatile) | mov PC, CI->u.l.savedpc // PC = CI->u.l.savedpc How can I mix the mixin above and combine with struct offsets?
Nov 12 2017
On Sunday, 12 November 2017 at 15:25:43 UTC, Dibyendu Majumdar wrote:On Sunday, 12 November 2017 at 12:32:09 UTC, Basile B. wrote:You can use just string parameters instead of enums, then you can pass arbitrary arguments to the instructions. The compiler will tell you if something is wrong with the syntax of the generated assembly.On Sunday, 12 November 2017 at 12:17:51 UTC, Dibyendu Majumdar wrote:Sorry I didn't phrase my question accurately. Presumably to use above with the mnemonics I would need additional mixin templates where the aggregate type and member etc would need to be parameters?On Sunday, 12 November 2017 at 11:55:23 UTC, Eugene Wissner wrote:https://dlang.org/spec/iasm.html#agregate_member_offsets aggregate.member.offsetof[someregister][...]Thank you - I probably could use something like this. It is uglier than the simpler approach in dynasm of course. How about when I need to combine this with some struct/union access? In dynasm I can write: | mov BASE, CI->u.l.base // BASE = ci->u.l.base (volatile) | mov PC, CI->u.l.savedpc // PC = CI->u.l.savedpc How can I mix the mixin above and combine with struct offsets?
Nov 12 2017
On Sunday, 12 November 2017 at 18:48:02 UTC, Eugene Wissner wrote:Okay thank you. Sigh. It would be so much simpler to be able to just define mnemonics for registers. Anyway, another question: Does the compiler generate appropriate unwind information on Win64? Prsumably if a function is marked 'naked' then it doesn't? Thanks and Regards DibyenduYou can use just string parameters instead of enums, then you can pass arbitrary arguments to the instructions. The compiler will tell you if something is wrong with the syntax of the generated assembly.https://dlang.org/spec/iasm.html#agregate_member_offsets aggregate.member.offsetof[someregister]Sorry I didn't phrase my question accurately. Presumably to use above with the mnemonics I would need additional mixin templates where the aggregate type and member etc would need to be parameters?
Nov 12 2017
On Sunday, 12 November 2017 at 21:27:28 UTC, Dibyendu Majumdar wrote:On Sunday, 12 November 2017 at 18:48:02 UTC, Eugene Wissner wrote:yeah about stack frame..., also don't forget to mark the asm block "pure nothrow" if possible... It's not documented but the syntax is like that: ``` void foo() { asm pure nothrow { naked; ret; } } ```Okay thank you. Sigh. It would be so much simpler to be able to just define mnemonics for registers. Anyway, another question: Does the compiler generate appropriate unwind information on Win64? Prsumably if a function is marked 'naked' then it doesn't? Thanks and Regards DibyenduYou can use just string parameters instead of enums, then you can pass arbitrary arguments to the instructions. The compiler will tell you if something is wrong with the syntax of the generated assembly.https://dlang.org/spec/iasm.html#agregate_member_offsets aggregate.member.offsetof[someregister]Sorry I didn't phrase my question accurately. Presumably to use above with the mnemonics I would need additional mixin templates where the aggregate type and member etc would need to be parameters?
Nov 12 2017
On Sunday, 12 November 2017 at 22:00:58 UTC, Basile B. wrote:On Sunday, 12 November 2017 at 21:27:28 UTC, Dibyendu MajumdarI am not sure I have understood above; will DMD generate the right Win64 unwind info for this contrived example: int luaV_interp(lua_State *L) { asm pure nothrow { naked; push RDI; push RSI; push RBX; push R12; push R13; push R14; push R15; sub RSP, 5*8; mov RAX, 0; add RSP, 5*8; pop R15; pop R14; pop R13; pop R12; pop RBX; pop RSI; pop RDI; pop RBP; ret; } }Does the compiler generate appropriate unwind information on Win64? Prsumably if a function is marked 'naked' then it doesn't?yeah about stack frame..., also don't forget to mark the asm block "pure nothrow" if possible... It's not documented but the syntax is like that: ``` void foo() { asm pure nothrow { naked; ret; } } ```
Nov 12 2017
On Sunday, 12 November 2017 at 22:20:46 UTC, Dibyendu Majumdar wrote:On Sunday, 12 November 2017 at 22:00:58 UTC, Basile B. wrote:no in naked mode you have to save and restore by hand.On Sunday, 12 November 2017 at 21:27:28 UTC, Dibyendu MajumdarI am not sure I have understood above; will DMD generate the right Win64 unwind info for this contrived example:
Nov 12 2017
On Sunday, 12 November 2017 at 22:24:08 UTC, Basile B. wrote:On Sunday, 12 November 2017 at 22:20:46 UTC, Dibyendu Majumdar wrote:So how does one manually generate the .pdata and .xdata sections? Are you saying that this is what I would need to do? Another question - how can I tell DMD to no generate the frame pointer? Thanks for answering my questions. Regards DibyenduOn Sunday, 12 November 2017 at 22:00:58 UTC, Basile B. wrote:no in naked mode you have to save and restore by hand.On Sunday, 12 November 2017 at 21:27:28 UTC, Dibyendu MajumdarI am not sure I have understood above; will DMD generate the right Win64 unwind info for this contrived example:
Nov 12 2017
Note that in Win64 even if not naked, you'll have to save/restore some registers like XMMx with x >= 6.On Sunday, 12 November 2017 at 22:20:46 UTC, Dibyendu Majumdar no in naked mode you have to save and restore by hand.Another question - how can I tell DMD to no generate the frame pointer?naked; However with naked; you have the problem of respecting the various ABI out there.
Nov 13 2017
On Sunday, 12 November 2017 at 22:40:06 UTC, Dibyendu Majumdar wrote:Hi, any further info on this? I am not talking here of the assembly push/pop instructions, rather the .pdata and .xdata sections needed on Win64. Thanks and Regards DibyenduSo how does one manually generate the .pdata and .xdata sections? Are you saying that this is what I would need to do? Another question - how can I tell DMD to no generate the frame pointer?On Sunday, 12 November 2017 at 22:00:58 UTC, Basile B. wrote:no in naked mode you have to save and restore by hand.
Nov 13 2017
On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Hi, [...] The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. [...] With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures.I see...the problem is not the input parameters but functions calls **inside** iasm, right ?
Nov 12 2017
On Sunday, 12 November 2017 at 12:00:00 UTC, Basile B. wrote:On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Not sure I understand the question. Once the defines are there I can write following: | // Call luaF_close | mov CARG1, L // arg1 = L | mov CARG2, BASE // arg2 = base | call extern luaF_close // call luaF_close As you can see above, CARG1, L, CARG2, BASE are all mnemonics that map to registers. However this is only defined in one place. Regards Dibyendu[...] The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. [...] With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures.I see...the problem is not the input parameters but functions calls **inside** iasm, right ?
Nov 12 2017
On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Hi, I have recently started work on building a VM for Lua (actually a derivative of Lua) in X86-64 assembly. I am using the dynasm tool that is part of LuaJIT. I was wondering whether I could also write this in D's inline assembly perhaps, but there is one aspect that I am not sure how to do. The assembly code uses static allocation of registers, but because of the differences in how registers are used in Win64 versus Unix X64 - different registers are assigned depending on the architecture. dynasm makes this easy to do using macros; e.g. below. |.if X64WIN |.define CARG1, rcx // x64/WIN64 C call arguments. |.define CARG2, rdx |.define CARG3, r8 |.define CARG4, r9 |.else |.define CARG1, rdi // x64/POSIX C call arguments. |.define CARG2, rsi |.define CARG3, rdx |.define CARG4, rcx |.endif With above in place, the code can use the mnemonics to refer to the registers rather than the registers themselves. This allows the assembly code to be coded once for both architectures. How would one do this in D inline assembly? Thanks and Regards DibyenduTBH I wonder if this is not worth a enhancement (or even a DIP) to have in asm blocks a special alias syntax... { asm { version(...) { alias First = RDI; alias Second = RSI; // ... } else { alias First = RCX; alias Second = RDX; } mov First, Second; call aFunctionWithOneParam; // called with 2nd parent param as 1st param } } since the whole mixin solution make the custom asm unreadable just because of this problem. And Even maybe some special identifiers since extern(...) may lead to problems...
Nov 13 2017
On Monday, 13 November 2017 at 18:40:42 UTC, Basile B. wrote:On Sunday, 12 November 2017 at 11:01:39 UTC, Dibyendu Majumdar wrote:Hmmm being the fact that "First" might be XMM0 i actually think the predefined identifiers is clearly not a solution. "alias" in asm blocks still stands though[...]TBH I wonder if this is not worth a enhancement (or even a DIP) to have in asm blocks a special alias syntax... { asm { version(...) { alias First = RDI; alias Second = RSI; // ... } else { alias First = RCX; alias Second = RDX; } mov First, Second; call aFunctionWithOneParam; // called with 2nd parent param as 1st param } } since the whole mixin solution make the custom asm unreadable just because of this problem. And Even maybe some special identifiers since extern(...) may lead to problems...
Nov 13 2017
On Monday, 13 November 2017 at 18:40:42 UTC, Basile B. wrote:TBH I wonder if this is not worth a enhancement (or even a DIP) to have in asm blocks a special alias syntax... { asm { version(...) { alias First = RDI; alias Second = RSI; // ... } else { alias First = RCX; alias Second = RDX; } mov First, Second; call aFunctionWithOneParam; // called with 2nd parent param as 1st param } }Something that happen quite frequently is duplicating very similar blocks of assembly between x86 32-bit and 64-bit. However in almost all cases the differences are small and if "version" blocks would be accepted, it would be enough.
Nov 13 2017
On Monday, 13 November 2017 at 18:40:42 UTC, Basile B. wrote:TBH I wonder if this is not worth a enhancement (or even a DIP) to have in asm blocks a special alias syntax... { asm { version(...) { alias First = RDI; alias Second = RSI; // ... } else { alias First = RCX; alias Second = RDX; } mov First, Second; call aFunctionWithOneParam; // called with 2nd parent param as 1st param } } since the whole mixin solution make the custom asm unreadable just because of this problem.Hi, that would be nice but I won't be holding my breath for such a feature to appear. I have a simple solution - I will just run it past a C pre-processor or maybe a custom one. Regards Dibyendu
Nov 13 2017