forgot this
[mono.git] / mono / jit / trampoline.c
1 /*
2  * trampoline.c: JIT trampoline code
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2001 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/appdomain.h>
14 #include <mono/metadata/tabledefs.h>
15 #include <mono/arch/x86/x86-codegen.h>
16 #include <mono/metadata/mono-debug-debugger.h>
17
18 #include "jit.h"
19 #include "codegen.h"
20
21 /*
22  * Address of the x86 trampoline code.  This is used by the debugger to check
23  * whether a method is a trampoline.
24  */
25 guint8 *mono_generic_trampoline_code = NULL;
26
27 /*
28  * get_unbox_trampoline:
29  * @m: method pointer
30  * @addr: pointer to native code for @m
31  *
32  * when value type methods are called through the vtable we need to unbox the
33  * this argument. This method returns a pointer to a trampoline which does
34  * unboxing before calling the method
35  */
36 static gpointer
37 get_unbox_trampoline (MonoMethod *m, gpointer addr)
38 {
39         guint8 *code, *start;
40         int this_pos = 4;
41
42         if (!m->signature->ret->byref && m->signature->ret->type == MONO_TYPE_VALUETYPE)
43                 this_pos = 8;
44             
45         start = code = g_malloc (16);
46
47         x86_alu_membase_imm (code, X86_ADD, X86_ESP, this_pos, sizeof (MonoObject));
48         x86_jump_code (code, addr);
49         g_assert ((code - start) < 16);
50
51         return start;
52 }
53
54 /**
55  * x86_magic_trampoline:
56  * @eax: saved x86 register 
57  * @ecx: saved x86 register 
58  * @edx: saved x86 register 
59  * @esi: saved x86 register 
60  * @edi: saved x86 register 
61  * @ebx: saved x86 register
62  * @code: pointer into caller code
63  * @method: the method to translate
64  *
65  * This method is called by the trampoline functions for virtual
66  * methods. It inspects the caller code to find the address of the
67  * vtable slot, then calls the JIT compiler and writes the address
68  * of the compiled method back to the vtable. All virtual methods 
69  * are called with: x86_call_membase (inst, basereg, disp). We always
70  * use 32 bit displacement to ensure that the length of the call 
71  * instruction is 6 bytes. We need to get the value of the basereg 
72  * and the constant displacement.
73  */
74 static gpointer
75 x86_magic_trampoline (int eax, int ecx, int edx, int esi, int edi, 
76                       int ebx, guint8 *code, MonoMethod *m)
77 {
78         guint8 reg;
79         gint32 disp;
80         char *o;
81         gpointer addr;
82
83         EnterCriticalSection (metadata_section);
84         addr = mono_compile_method (m);
85         LeaveCriticalSection (metadata_section);
86         g_assert (addr);
87
88         /* go to the start of the call instruction
89          *
90          * address_byte = (m << 6) | (o << 3) | reg
91          * call opcode: 0xff address_byte displacement
92          * 0xff m=1,o=2 imm8
93          * 0xff m=2,o=2 imm32
94          */
95         code -= 6;
96         if ((code [1] != 0xe8) && (code [3] == 0xff) && ((code [4] & 0x18) == 0x10) && ((code [4] >> 6) == 1)) {
97                 reg = code [4] & 0x07;
98                 disp = (signed char)code [5];
99         } else {
100                 if ((code [0] == 0xff) && ((code [1] & 0x18) == 0x10) && ((code [1] >> 6) == 2)) {
101                         reg = code [1] & 0x07;
102                         disp = *((gint32*)(code + 2));
103                 } else if ((code [1] == 0xe8)) {
104                         *((guint32*)(code + 2)) = (guint)addr - ((guint)code + 1) - 5; 
105                         return addr;
106                 } else {
107                         printf ("Invalid trampoline sequence: %x %x %x %x %x %x %x\n", code [0], code [1], code [2], code [3],
108                                 code [4], code [5], code [6]);
109                         g_assert_not_reached ();
110                 }
111         }
112
113         switch (reg) {
114         case X86_EAX:
115                 o = (gpointer)eax;
116                 break;
117         case X86_EDX:
118                 o = (gpointer)edx;
119                 break;
120         case X86_ECX:
121                 o = (gpointer)ecx;
122                 break;
123         case X86_ESI:
124                 o = (gpointer)esi;
125                 break;
126         case X86_EDI:
127                 o = (gpointer)edi;
128                 break;
129         case X86_EBX:
130                 o = (gpointer)ebx;
131                 break;
132         default:
133                 g_assert_not_reached ();
134         }
135
136         o += disp;
137
138         if (m->klass->valuetype) {
139                 return *((gpointer *)o) = get_unbox_trampoline (m, addr);
140         } else {
141                 return *((gpointer *)o) = addr;
142         }
143 }
144
145 /**
146  * arch_create_jit_trampoline:
147  * @method: pointer to the method info
148  *
149  * Creates a trampoline function for virtual methods. If the created
150  * code is called it first starts JIT compilation of method,
151  * and then calls the newly created method. I also replaces the
152  * corresponding vtable entry (see x86_magic_trampoline).
153  * 
154  * Returns: a pointer to the newly created code 
155  */
156 gpointer
157 arch_create_jit_trampoline (MonoMethod *method)
158 {
159         guint8 *code, *buf;
160
161         /* previously created trampoline code */
162         if (method->info)
163                 return method->info;
164
165         /* we immediately compile runtime provided functions */
166         if (method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME) {
167                 method->info = mono_compile_method (method);
168                 return method->info;
169         }
170
171         /* icalls use method->addr */
172         if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
173             (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
174                 MonoMethod *nm;
175                 
176                 if (!method->addr && (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL))
177                         mono_lookup_pinvoke_call (method);
178
179 #ifdef MONO_USE_EXC_TABLES
180                 if (mono_method_blittable (method)) {
181                         method->info = method->addr;
182                 } else {
183 #endif
184                         nm = mono_marshal_get_native_wrapper (method);
185                         method->info = mono_compile_method (nm);
186 #ifdef MONO_USE_EXC_TABLES
187                 }
188 #endif
189                 return method->info;
190         }
191
192         if (!mono_generic_trampoline_code) {
193                 mono_generic_trampoline_code = buf = g_malloc (256);
194                 /* save caller save regs because we need to do a call */ 
195                 x86_push_reg (buf, X86_EDX);
196                 x86_push_reg (buf, X86_EAX);
197                 x86_push_reg (buf, X86_ECX);
198
199                 /* save LMF begin */
200
201                 /* save the IP (caller ip) */
202                 x86_push_membase (buf, X86_ESP, 16);
203
204                 x86_push_reg (buf, X86_EBX);
205                 x86_push_reg (buf, X86_EDI);
206                 x86_push_reg (buf, X86_ESI);
207                 x86_push_reg (buf, X86_EBP);
208
209                 /* save method info */
210                 x86_push_membase (buf, X86_ESP, 32);
211                 /* get the address of lmf for the current thread */
212                 x86_call_code (buf, mono_get_lmf_addr);
213                 /* push lmf */
214                 x86_push_reg (buf, X86_EAX); 
215                 /* push *lfm (previous_lmf) */
216                 x86_push_membase (buf, X86_EAX, 0);
217                 /* *(lmf) = ESP */
218                 x86_mov_membase_reg (buf, X86_EAX, 0, X86_ESP, 4);
219                 /* save LFM end */
220
221                 /* push the method info */
222                 x86_push_membase (buf, X86_ESP, 44);
223                 /* push the return address onto the stack */
224                 x86_push_membase (buf, X86_ESP, 52);
225
226                 /* save all register values */
227                 x86_push_reg (buf, X86_EBX);
228                 x86_push_reg (buf, X86_EDI);
229                 x86_push_reg (buf, X86_ESI);
230                 x86_push_membase (buf, X86_ESP, 64); /* EDX */
231                 x86_push_membase (buf, X86_ESP, 64); /* ECX */
232                 x86_push_membase (buf, X86_ESP, 64); /* EAX */
233
234                 x86_call_code (buf, x86_magic_trampoline);
235                 x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 8*4);
236
237                 /* restore LMF start */
238                 /* ebx = previous_lmf */
239                 x86_pop_reg (buf, X86_EBX);
240                 /* edi = lmf */
241                 x86_pop_reg (buf, X86_EDI);
242                 /* *(lmf) = previous_lmf */
243                 x86_mov_membase_reg (buf, X86_EDI, 0, X86_EBX, 4);
244                 /* discard method info */
245                 x86_pop_reg (buf, X86_ESI);
246                 /* restore caller saved regs */
247                 x86_pop_reg (buf, X86_EBP);
248                 x86_pop_reg (buf, X86_ESI);
249                 x86_pop_reg (buf, X86_EDI);
250                 x86_pop_reg (buf, X86_EBX);
251                 /* discard save IP */
252                 x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 4);             
253                 /* restore LMF end */
254
255                 x86_alu_reg_imm (buf, X86_ADD, X86_ESP, 16);
256
257                 /* call the compiled method */
258                 x86_jump_reg (buf, X86_EAX);
259
260                 g_assert ((buf - mono_generic_trampoline_code) <= 256);
261         }
262
263         code = buf = g_malloc (16);
264         x86_push_imm (buf, method);
265         x86_jump_code (buf, mono_generic_trampoline_code);
266         g_assert ((buf - code) <= 16);
267
268         /* store trampoline address */
269         method->info = code;
270
271         mono_jit_stats.method_trampolines++;
272
273         return code;
274 }