1 More about PInvoke and Internal calls
2 =====================================
6 PInvoke stands for Platform Invoke. It is possible to call functions contained
7 in native shared libraries, for example you can declare:
9 [DllImport("cygwin1.dll", EntryPoint="puts", CharSet=CharSet.Ansi)]
10 public static extern int puts (string name);
12 If you then call "puts(...)" it invokes the native "puts" functions in
15 2.) What are internal calls
17 Some class library functions are implemented in C, because it is either not
18 possible to implement them in C# or because of performance gains. Internal
19 functions are contained in the mono executable itself. Here is an example form
20 our array implementation:
22 [MethodImplAttribute(MethodImplOptions.InternalCall)]
23 public extern int GetRank ();
25 If you call this GetRank() function it invokes
26 ves_icall_System_Array_GetRank() inside the mono runtime.
28 2.) Runtime considerations
30 Invoking native (unmanaged) code has several implications:
32 - We need to handle exceptions inside unmanaged code. The JIT simply saves some
33 informations at each transition from managed to unmanaged code (in a linked
34 list), called Last Managed Frame (LMF). If an exception occurs the runtime
35 first looks if the exception was inside managed code. If not there must be a
36 LMF entry which contains all necessary information to unwind the stack.
38 Creation of those LMF structure clearly involves some overhead, so calling
39 into unmanaged code is not as cheap as it looks like at first glance. Maybe
40 we can introduce a special attribute to avoid the creation of LMF on internal
41 call methods that cant raise exceptions.
43 - PInvoke has the possibility to convert argument types. For example Strings
44 are marshalled as Char*. So each String argument is translated into a
45 char*. The encoding is specified in the CharSet of the DllImport attribute.
48 3.) When/how does the runtime call unmanaged PInvoke code
50 - LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate
51 wrapper code when we load the function with LDFTN, so that all arguments are
52 marshalled in the right format. We also need to save/restore the LMF.
54 - MethodBase::Invoke (runtime invoke): We need to marshal all arguments in
55 they right format and save/restore the LMF
57 - CALL: We need to marshal all arguments in they right format and save/restore
60 The easiest way to implement this is to always create a wrapper function for
61 PInvoke calls, which takes care of argument marshalling and LMF save/restore.
63 3.) When/how does the runtime call unmanaged internal calls
65 We don't need to convert and arguments, so we need only take care of the LMF
68 - LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate
69 wrapper code when we load the function with LDFTN which saves/restores the
72 - MethodBase::Invoke (runtime invoke): We need to save/restore the LMF.
74 - CALL: We need to save/restore the LMF.
76 - CALLVIRT (through the vtable): We must generate wrapper code to save/restore
79 Please notice that we can call internal function with CALLVIRT, i.e. we can
80 call those function through a VTable. But we cant know in advance if a vtable
81 slot contains an internal call or managed code. So again it is best to generate
82 a wrapper functions for internal calls in order to save/restore the LMF.
84 Unfortunately we need to push all arguments 2 times, because we have to save
85 the LMF, and the LMF is currently allocated on the stack. So the stack looks
96 AFAIK this is the way ORP works. Another way is to allocate the LMF not on the
97 stack, but then we have additional overhead to allocate/free LMF structures
98 (and another call to arch_get_lmf_addr).
100 Maybe it is possible to avoid this addiotional copy for internal calls by
101 including the LMF in the C function signature. Lets say we hav a puts()
102 function which is a internal call:
104 ves_icall_puts (MonoString *string);
106 If we simply modify that to include the LMF we can avoid to copy all arguments:
108 ves_icall_puts (MonoLMF lmf, MonoString *string);
110 But this depends somehow on the calling conventions, and I don't know if that
111 works on all plattforms?
113 4.) What is stored in the LMF
115 - all caller saved registers (since we can trust unmanaged code)
116 - the instruction pointer of the last managed instruction
117 - a MonoMethod pointer for the unmanaged function
118 - the address of the thread local lfm_addr pointer (to avoid another call to
119 arch_get_lmf_addr when restoring LMF
121 The LMF is allocated on the stack, so we also know the stack position for