digitalmars.D - Question about x86 delegate calling convention
- Jarrett Billingsley (20/20) Jun 28 2006 I was stepping through some assembly when debugging one of my programs, ...
- James Dunne (17/46) Jun 29 2006 A wild stab in the dark here, but perhaps the dup context ptr in EBX is
- Walter Bright (2/2) Jun 30 2006 It's just an artifact of the register allocator. The context pointer is
- Jarrett Billingsley (3/5) Jun 30 2006 OK, thanks :)
- Lionello Lunesu (8/10) Jul 01 2006 While on the subject, why can't a function pointer be (implicitly) conve...
- Walter Bright (2/12) Jul 01 2006 Some functions pass parameters in EAX (!)
- Frits van Bommel (70/80) Jul 01 2006 Ok, so Walter beat me to posting the obvious reason. This was my reply,
- BCS (4/37) Jul 01 2006 [a lot of cool code...]
- Frits van Bommel (12/16) Jul 01 2006 Yes I would assume it could, and probably better than me as presumably
I was stepping through some assembly when debugging one of my programs, and I noticed something a bit odd about how delegates were called. Delegates consist basically of two things: a context pointer (either 'this' for class members or the outer function's frame pointer for nested functions), and the address of the actual code. This is how D seems to call delegates: EAX = context ptr; EDX = code address; EBX = context ptr; EDX(); Inside the delegate, only the EAX context pointer is ever used. 'this' is also passed in EAX when calling class methods directly (a.method()). My question is: why is EBX filled with the context pointer? It's never used in the delegate; in fact, if the delegate has some complex code, and the codegen needs another register, it will preserve EBX (pushing it) before using it. This happens even in release mode. Is this a vestige of some older calling convention for delegates? I also want to know this because I'm working on a (blatantly non-portable) method of dynamically calling functions at run-time (for fun).
Jun 28 2006
Jarrett Billingsley wrote:I was stepping through some assembly when debugging one of my programs, and I noticed something a bit odd about how delegates were called. Delegates consist basically of two things: a context pointer (either 'this' for class members or the outer function's frame pointer for nested functions), and the address of the actual code. This is how D seems to call delegates: EAX = context ptr; EDX = code address; EBX = context ptr; EDX(); Inside the delegate, only the EAX context pointer is ever used. 'this' is also passed in EAX when calling class methods directly (a.method()). My question is: why is EBX filled with the context pointer? It's never used in the delegate; in fact, if the delegate has some complex code, and the codegen needs another register, it will preserve EBX (pushing it) before using it. This happens even in release mode. Is this a vestige of some older calling convention for delegates? I also want to know this because I'm working on a (blatantly non-portable) method of dynamically calling functions at run-time (for fun).A wild stab in the dark here, but perhaps the dup context ptr in EBX is coincidentally the same value used for a different feature implementation? Try all the cases of delegate calling, nested function calling, virtual method calling, variadic function calling, etc. and check the value of EBX vs. EAX. I have a suspicion your answer will lie in there somewhere. :) Then again, perhaps it's just bad codegen... but let's hope not. I don't think anyone could answer this 100% other than Walter. -- -----BEGIN GEEK CODE BLOCK----- Version: 3.1 GCS/MU/S d-pu s:+ a-->? C++++$ UL+++ P--- L+++ !E W-- N++ o? K? w--- O M-- V? PS PE Y+ PGP- t+ 5 X+ !R tv-->!tv b- DI++(+) D++ G e++>e h>--->++ r+++ y+++ ------END GEEK CODE BLOCK------ James Dunne
Jun 29 2006
It's just an artifact of the register allocator. The context pointer is passed in EAX.
Jun 30 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:e82j04$28fs$1 digitaldaemon.com...It's just an artifact of the register allocator. The context pointer is passed in EAX.OK, thanks :)
Jun 30 2006
"Walter Bright" <newshound digitalmars.com> wrote in message news:e82j04$28fs$1 digitaldaemon.com...It's just an artifact of the register allocator. The context pointer is passed in EAX.While on the subject, why can't a function pointer be (implicitly) converted to a delegate pointer? The context can just be ignored int that case so it'll probably be slightly less performant, but it's handy: a library could use delegates for all callbacks, without forcing the users to use either classes or nested functions. They can still use globals if they'd want. L.
Jul 01 2006
Lionello Lunesu wrote:"Walter Bright" <newshound digitalmars.com> wrote in message news:e82j04$28fs$1 digitaldaemon.com...Some functions pass parameters in EAX (!)It's just an artifact of the register allocator. The context pointer is passed in EAX.While on the subject, why can't a function pointer be (implicitly) converted to a delegate pointer? The context can just be ignored int that case so it'll probably be slightly less performant, but it's handy: a library could use delegates for all callbacks, without forcing the users to use either classes or nested functions. They can still use globals if they'd want.
Jul 01 2006
Lionello Lunesu wrote:"Walter Bright" <newshound digitalmars.com> wrote in message news:e82j04$28fs$1 digitaldaemon.com...Ok, so Walter beat me to posting the obvious reason. This was my reply, all typed up: The calling convention for extern(D) functions (the default) is to pass one of the arguments (the last one) in EAX[1] if it fits, so it's not ignored. It's used for something else. I believe Walter once said it was a feature he wanted to eventually implement though. I think he was even considering converting delegates to function pointers, by having it call dynamically generated code. If you don't mind using asm hacks, you could construct your delegates using an asm stub like below as the code to call. (the function pointer is stored as the context pointer) //------------------------------------------------------------ // The appropriate function to call for a delegate that receives a // void function(int) in EAX. // Note: It should also work for any other delegate that has the same // calling convention as the respective delegate, except that the last // (max 4 bytes) parameter is pushed to the stack instead of being put // in EAX. // That also means the return type shouldn't matter, as long as it's // stored in registers. // It just adjusts the stack to what such a function expects and jumps // into it. // This is of course specific to the calling convention and delegate // layout DMD uses // currently (does GDC the same ones?). // Also, this will obviously only work on x86 machines. void __function_delegate_simple() { asm { naked; pop EDX; // store return address mov ECX, EAX; // preserve function pointer pop EAX; // put last parameter in EAX push EDX; // put return address back in place jmp ECX; // jump directly to function } } /* Some test code: */ import std.stdio; void func(int i) { writefln("func(%d)", i); } // a convenient way to construct a delegate from scratch union dg { struct { void function(int) fn; void function() stub = &__function_delegate_simple; } void delegate(int) dg; } void main() { dg test; test.fn = &func; void delegate(int) my_dg = test.dg; // or use test.dg() directly my_dg(0); my_dg(1); my_dg(42); } //------------------------------------------------------------ [1]: At least if the return value is a primitive. When returning things like structs that are too big to fit in registers, I believe EAX is used as an indication where to put the return value. Don't feel like looking through a lot of disassembled code to figure out the specifics though. (Like what calling convention struct-returning delegates use)It's just an artifact of the register allocator. The context pointer is passed in EAX.While on the subject, why can't a function pointer be (implicitly) converted to a delegate pointer? The context can just be ignored int that case so it'll probably be slightly less performant, but it's handy: a library could use delegates for all callbacks, without forcing the users to use either classes or nested functions. They can still use globals if they'd want.
Jul 01 2006
Frits van Bommel wrote:Lionello Lunesu wrote:[a lot of cool code...] Couldn't the compiler generate the needed stub(s) for you? With that functionality, a function to delegate cast could be allowed."Walter Bright" <newshound digitalmars.com> wrote in message news:e82j04$28fs$1 digitaldaemon.com...Ok, so Walter beat me to posting the obvious reason. This was my reply, all typed up: The calling convention for extern(D) functions (the default) is to pass one of the arguments (the last one) in EAX[1] if it fits, so it's not ignored. It's used for something else. I believe Walter once said it was a feature he wanted to eventually implement though. I think he was even considering converting delegates to function pointers, by having it call dynamically generated code. If you don't mind using asm hacks, you could construct your delegates using an asm stub like below as the code to call. (the function pointer is stored as the context pointer)It's just an artifact of the register allocator. The context pointer is passed in EAX.While on the subject, why can't a function pointer be (implicitly) converted to a delegate pointer? The context can just be ignored int that case so it'll probably be slightly less performant, but it's handy: a library could use delegates for all callbacks, without forcing the users to use either classes or nested functions. They can still use globals if they'd want.
Jul 01 2006
BCS wrote:Frits van Bommel wrote: [a lot of cool code...]Thanks :)Couldn't the compiler generate the needed stub(s) for you? With that functionality, a function to delegate cast could be allowed.Yes I would assume it could, and probably better than me as presumably those implementing the compiler have a better understanding of the calling conventions involved :). Might be a bit of work to make sure it handles all the corner cases though... Not sure how many of those would pop up, but there's bound to be some: Struct return values I mentioned in my post, and maybe array returns unless it uses EDX:EAX (or was it the other way around?) for that like it does for longs. Then there's associative arrays and last arguments that don't fit in EAX...
Jul 01 2006