[LLVMdev] Frontend: How to use Member to Function Pointer as callbacks
Virgile Bello
virgile.bello at gmail.com
Sat Nov 29 09:25:35 PST 2014
Hello,
As part of a MSIL (i.e. C#) to LLVM frontend I am currently working on (
https://github.com/xen2/SharpLang ), I would need some help/hint about how
to properly design "PInvoke callbacks".
Through "PInvoke" mechanism .NET allows you to call C functions, i.e.:
C#:
[DllImport("libc.so")] extern void mempcy(void* dest, void* src, int size);
// declaration of C function
memcpy(ptr1, ptr2, 32); // let's use it in C# code
That's quite easy to support.
However, the tricky part is that C# functions pointers (callbacks, a.k.a
delegate) can be transmitted to those C functions:
C:
void MethodWithCallback(void(*callback)(int));
C#:
delegate void CallbackType(int result); // <-- Point to a member to
function pointer (need "this")
[DllImport("mylib.so")] extern void MethodWithCallback(CallbackType
callback);
An extra "this" parameter is needed to call the real method, but of course
the calling C code doesn't know about it (MethodWithCallback expects a
non-member function pointer).
This is similar to C++ not being able to cast pointer to function member
(containing this) as normal function pointers, because they are not
compatible.
Using a JIT, it would be quite easy to deal with (generate thunk/code) but
I would like to support full AOT scenario where executable memory can't be
modified (and also not have to embed/use LLVM at runtime, just like a plain
C/C++ executable).
One (rather complicated) option I was thinking is:
- Define a maximum number of those callback alive (let's say 4096) -- it's
not per function signature, but global
- Define i8* thunkTargets[4096]
- Define i8* ThunkIdToFuncPtr[4096]
- Define 4096 funcs (not even sure how to do that with LLVM, might need to
emit assembly?)
Thunk0: jmp thunkTargets[0];
Thunk1: jmp thunkTargets[1];
...
Thunk4095: jmp thunkTargets[4095];
- When I call a C function from C# with a callback, what happens is:
- Find an unused slot in this thunk table (X)
- Register C# member to function pointer in ThunkIdToFuncPtr[X]
- Replace thunkTargets[X] with pointer address to
"RedirectMethodFuncWithIntParameter" (one such function per callback
signature)
- This redirect method would receive arguments unmodified from C
functions (since previous call was a simple jmp)
- It would check in the call stack the current slot X being called (up
in the callstack, if call instruction is "call Thunk3" from address Thunk3
we know X is 3 -- it will need assembly, might be difficult to compute and
won't be portable...)
- ThunkIdToFuncPtr[X] would give us the actual method to forward to
- RedirectMethodFuncWithIntParameter would call
ThunkIdToFuncPtr[X](arg1)
This design allow to use the slot number X of MethodX to differentiate the
actual C# callback to call (code is small, so OK to have many, 4096 in this
case), but still have only ONE actual dispatcher/redirect method per
signature (code is much bigger).
Does that seem feasible? I don't like the fact that I would have to step
out of LLVM bitcode and generate some non-portable assembly code (I was
trying to stick with LLVM bitcode so far).
Any other idea, or maybe some LLVM infrastructure/system/subproject or
another LLVM frontend that had the same issue that might help me there?
Also, I am not sure whether LLVM trampoline could help me there? (not sure
if they could do that, and if they work in full AOT scenarios, where JIT is
not allowed?)
Thanks,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20141130/4e60c66f/attachment.html>
More information about the llvm-dev
mailing list