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