D 2.0 - dalegates to function
Frits van Bommel
fvbommel at REMwOVExCAPSs.nl
Thu Mar 1 03:43:18 PST 2007
sergk wrote:
> Perhaps it's time to to bump an old request postponed to 2.0 times =)
>
> So, how about to add some thunking magic - how complex/complicated to implement it?
(I'll be assuming an x86 for this post, and DMD. x86 because that's the
assembler language I know (as well as the only architecture DMD runs
on), and DMD because that's the calling convention documented at
http://www.digitalmars.com/d/abi.html. Portability concerns are noted at
the end)
Did you mean converting delegates to function pointers (without an
explicit this pointer) or creating delegates to functions?
I think both can be implemented efficiently[1], perhaps even in library
space. AFAIK it can't be done in an entirely generic fashion in the
current though.
The big problem currently is detection of out/inout parameters, and
specifically whether the last parameter is one.
[1]: Meaning: without re-pushing parameters.
Pseudo-code for creating a delegate that calls a regular function:
===
dg.ptr = &func;
static if (Parameters.length > 0 && "last parameter fits in EAX but is
not 3 bytes large") {
dg.funcptr = RetType function(Parameters) {
asm {
naked;
pop ECX; // return address
mov EDX, EAX; // move function pointer out of the way
pop EAX; // last parameter must be in EAX
push ECX; // restore return address
jmp [EDX]; // jump directly to function entry point
}
}
} else {
dg.funcptr = RetType function(Parameters) {
asm {
naked;
jmp [EAX];
}
}
}
===
We need to know whether the last parameter is out/inout to implement the
static if(); if it wouldn't normally be passed in EAX but is out or
inout it's still in there.
This is a reduced variant of the "perfect forwarding" problem (which
requires that knowledge for all parameters). IIRC Andrei made some posts
about how they're going to try to fix that one. Once that's done the
above should be feasible.
Converting delegates to function pointers:
This could be done by heap-allocating a stub that loads EAX with the
correct value and then performs the right jump. This would have to be
done such that both of those values (the EAX parameter and the function
address) are correctly aligned in the code (so the gc can see them); nop
instructions may be needed here.
The heap-allocated code could look something like this (pseudo-asm):
===
asm {
naked;
static if(last param is in EAX) {
pop ECX; // pop return address
push EAX; // push last parameter on stack
push ECX; // restore return address
}
// some NOPs for alignment perhaps
mov EAX, dword 0x........
// some more NOPs?
jmp 0x........
}
===
Where the "0x........" parts are filled at runtime.
Notes and warnings:
* First of all, above code is completely untested and written directly
to this mail window. Use at own risk ;).
* This code assumes both the delegate and the function pointer have the
same calling convention, and that it's the one used by DMD on x86.
Different code will have to be written for other architectures, calling
conventions and (if it's even possible) cross-calling-convention conversion.
* The second code sample assumes the heap is mapped with execute
permission, which may be a non-portable assumption. For one thing, IIRC
amd64 (aka x86-64) has an optional no-execute (NX) flag for memory pages
that would break this; on OSs that use this for heap pages system calls
would be required to disable this flag.
* It also assumes the instructions are encodable such that the
"0x........" parts look like regular pointers to the GC (i.e. absolute
addresses). This may be a non-portable assumption as well, I'm not even
sure x86 allows this (I think so though). An alternative would be for
the GC to recognize such stubs and special-case them.
More information about the Digitalmars-d
mailing list