Question about x86 delegate calling convention
Frits van Bommel
fvbommel at REMwOVExCAPSs.nl
Sat Jul 1 09:46:57 PDT 2006
Lionello Lunesu wrote:
> "Walter Bright" <newshound at digitalmars.com> wrote in message
> news:e82j04$28fs$1 at 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.
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)
More information about the Digitalmars-d
mailing list