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