Jump to page: 1 2
Thread overview
Inline assembly question
Nov 12, 2017
Dibyendu Majumdar
Nov 12, 2017
Nicholas Wilson
Nov 12, 2017
Eugene Wissner
Nov 12, 2017
Dibyendu Majumdar
Nov 12, 2017
Basile B.
Nov 12, 2017
Dibyendu Majumdar
Nov 12, 2017
Eugene Wissner
Nov 12, 2017
Dibyendu Majumdar
Nov 12, 2017
Basile B.
Nov 12, 2017
Dibyendu Majumdar
Nov 12, 2017
Basile B.
Nov 12, 2017
Dibyendu Majumdar
Nov 13, 2017
Guillaume Piolat
Nov 13, 2017
Dibyendu Majumdar
Nov 12, 2017
Basile B.
Nov 12, 2017
Dibyendu Majumdar
Nov 13, 2017
Basile B.
Nov 13, 2017
Basile B.
Nov 13, 2017
Guillaume Piolat
Nov 13, 2017
Dibyendu Majumdar
November 12, 2017
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
November 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
> Dibyendu

You could do it with a mixin, it would be rather ugly though. Not sure of another way off the top of my head.

November 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
> Dibyendu

Here 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));
}
November 12, 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 ?
November 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:
>> [...]
>> 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 ?

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
November 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:
>> 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
>
> Here 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));
> }

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
November 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:
>> [...]
>
> 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

https://dlang.org/spec/iasm.html#agregate_member_offsets

aggregate.member.offsetof[someregister]
November 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:
>> On Sunday, 12 November 2017 at 11:55:23 UTC, Eugene Wissner 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?
>>
>
> 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?


November 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:
>> 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:
>>>> [...]
>>>
>>> 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?
>>>
>>
>> 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?

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.
November 12, 2017
On Sunday, 12 November 2017 at 18:48:02 UTC, Eugene Wissner wrote:
>>> 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?
>
> 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.

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
Dibyendu

« First   ‹ Prev
1 2