2002-03-14 Dietmar Maurer <dietmar@ximian.com>
[mono.git] / docs / unmanaged-calls
1 More about PInvoke and Internal calls
2 =====================================
3
4 1.) What is PInvoke
5
6 PInvoke stands for Platform Invoke. It is possible to call functions contained
7 in native shared libraries, for example you can declare:
8
9         [DllImport("cygwin1.dll", EntryPoint="puts", CharSet=CharSet.Ansi)]
10         public static extern int puts (string name);
11
12 If you then call "puts(...)" it invokes the native "puts" functions in
13 "cygwin1.dll"
14
15 2.) What are internal calls
16
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:
21
22         [MethodImplAttribute(MethodImplOptions.InternalCall)]
23         public extern int GetRank ();
24
25 If you call this GetRank() function it invokes
26 ves_icall_System_Array_GetRank() inside the mono runtime.
27
28 2.) Runtime considerations
29
30 Invoking native (unmanaged) code has several implications:
31
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. 
37
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.
42
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.
46   
47
48 3.) When/how does the runtime call unmanaged PInvoke code
49
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.
53
54 - MethodBase::Invoke (runtime invoke): We need to marshal all arguments in
55   they right format and save/restore the LMF
56
57 - CALL: We need to marshal all arguments in they right format and save/restore
58   the LMF
59
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.
62
63 3.) When/how does the runtime call unmanaged internal calls
64
65 We don't need to convert and arguments, so we need only take care of the LMF
66 structure. 
67
68 - LDFTN, CALLI, Delegate::Invoke, Delegate::BeginInvoke: We must generate
69   wrapper code when we load the function with LDFTN which saves/restores the
70   LMF.
71
72 - MethodBase::Invoke (runtime invoke): We need to save/restore the LMF.
73
74 - CALL: We need to save/restore the LMF.
75
76 - CALLVIRT (through the vtable): We must generate wrapper code to save/restore
77   the LMF.
78
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.
83
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
86 like:
87
88     --------------------
89     | method arguments |
90     --------------------
91     | LMF              |
92     --------------------
93     | copied arguments |
94     --------------------
95
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).
99
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:
103
104 ves_icall_puts (MonoString *string);
105
106 If we simply modify that to include the LMF we can avoid to copy all arguments:
107
108 ves_icall_puts (MonoLMF lmf, MonoString *string);
109
110 But this depends somehow on the calling conventions, and I don't know if that
111 works on all plattforms? 
112
113 4.) What is stored in the LMF
114
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
120
121 The LMF is allocated on the stack, so we also know the stack position for
122 stack unwinding.