2005-07-06 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mono / mini / tramp-ia64.c
1 /*
2  * tramp-ia64.c: JIT trampoline code for ia64
3  *
4  * Authors:
5  *   Zoltan Varga (vargaz@gmail.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/marshal.h>
15 #include <mono/metadata/tabledefs.h>
16 #include <mono/arch/ia64/ia64-codegen.h>
17 #include <mono/metadata/mono-debug-debugger.h>
18
19 #include "mini.h"
20 #include "mini-ia64.h"
21
22 #define NOT_IMPLEMENTED g_assert_not_reached ()
23
24 #define GP_SCRATCH_REG 31
25 #define GP_SCRATCH_REG2 30
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 *buf;
40         gpointer func_addr, func_gp;
41         Ia64CodegenState code;
42         int this_reg = 0;
43         MonoDomain *domain = mono_domain_get ();
44
45         /* FIXME: Optimize this */
46
47         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
48                 this_reg = 1;
49
50         func_addr = ((gpointer*)addr) [0];
51         func_gp = ((gpointer*)addr) [1];
52
53         mono_domain_lock (domain);
54         buf = mono_code_manager_reserve (domain->code_mp, 256);
55         mono_domain_unlock (domain);
56
57         /* Since the this reg is a stacked register, its a bit hard to access it */
58         ia64_codegen_init (code, buf);
59         ia64_alloc (code, 40, 8, 1, 0, 0);
60         ia64_adds_imm (code, 32 + this_reg, sizeof (MonoObject), 32 + this_reg);
61         ia64_mov_to_ar_i (code, IA64_PFS, 40);  
62         ia64_movl (code, GP_SCRATCH_REG, func_addr);
63         ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG);
64         ia64_br_cond_reg (code, IA64_B6);
65         ia64_codegen_close (code);
66
67         g_assert (code.buf - buf < 256);
68
69         /* FIXME: */
70
71         gpointer *desc = g_malloc0 (sizeof (gpointer) * 2);
72         desc [0] = buf;
73         desc [1] = func_gp;
74
75         return desc;
76 }
77
78 /**
79  * ia64_magic_trampoline:
80  */
81 static gpointer
82 ia64_magic_trampoline (long *regs, guint8 *code, MonoMethod *m, guint8* tramp)
83 {
84         gpointer addr;
85         gpointer *vtable_slot;
86
87         addr = mono_compile_method (m);
88         g_assert (addr);
89
90         //printf ("ENTER: %s\n", mono_method_full_name (m, TRUE));
91
92         /* the method was jumped to */
93         if (!code)
94                 /* FIXME: Optimize the case when the call is from a delegate wrapper */
95                 return addr;
96
97         vtable_slot = mono_arch_get_vcall_slot_addr (code, (gpointer*)regs);
98
99         if (vtable_slot) {
100                 if (m->klass->valuetype)
101                         addr = get_unbox_trampoline (m, addr);
102
103                 g_assert (*vtable_slot);
104
105                 if (mono_aot_is_got_entry (code, (guint8*)vtable_slot) || mono_domain_owns_vtable_slot (mono_domain_get (), vtable_slot))
106                         *vtable_slot = addr;
107         }
108         else {
109                 /* FIXME: Patch calling code */
110         }
111
112         return addr;
113 }
114
115 /*
116  * ia64_aot_trampoline:
117  *
118  *   This trampoline handles calls made from AOT code. We try to bypass the 
119  * normal JIT compilation logic to avoid loading the metadata for the method.
120  */
121 static gpointer
122 ia64_aot_trampoline (long *regs, guint8 *code, guint8 *token_info, 
123                                           guint8* tramp)
124 {
125         NOT_IMPLEMENTED;
126
127         return NULL;
128 }
129
130 /**
131  * ia64_class_init_trampoline:
132  *
133  * This method calls mono_runtime_class_init () to run the static constructor
134  * for the type, then patches the caller code so it is not called again.
135  */
136 static void
137 ia64_class_init_trampoline (long *regs, guint8 *code, MonoVTable *vtable, guint8 *tramp)
138 {
139         mono_runtime_class_init (vtable);
140         
141         /* FIXME: Patch calling code */
142 }
143
144 guchar*
145 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
146 {
147         guint8 *buf, *tramp;
148         int i, offset, saved_regs_offset, saved_fpregs_offset, framesize;
149         int in0, local0, out0, l0, l1, l2, l3, l4, l5, l6, l7, l8, o0, o1, o2, o3;
150         gboolean has_caller;
151         Ia64CodegenState code;
152         unw_dyn_info_t *di;
153         unw_dyn_region_info_t *r_pro;
154
155         if (tramp_type == MONO_TRAMPOLINE_JUMP)
156                 has_caller = FALSE;
157         else
158                 has_caller = TRUE;
159
160         buf = mono_global_codeman_reserve (2048);
161
162         ia64_codegen_init (code, buf);
163
164         /* FIXME: Save/restore lmf */
165
166         /* Stacked Registers */
167         in0 = 32;
168         local0 = in0 + 8;
169         out0 = local0 + 16;
170         l0 = 40;
171         l1 = 41;
172         l2 = 42;
173         l3 = 43;
174         l4 = 44;
175         l5 = 45; /* saved ar.pfs */
176         l6 = 46; /* arg */
177         l7 = 47; /* code */
178         l8 = 48; /* saved sp */
179         o0 = out0 + 0; /* regs */
180         o1 = out0 + 1; /* code */
181         o2 = out0 + 2; /* arg */
182         o3 = out0 + 3; /* tramp */
183
184         framesize = (128 * 8) + 1024;
185         framesize = (framesize + (MONO_ARCH_FRAME_ALIGNMENT - 1)) & ~ (MONO_ARCH_FRAME_ALIGNMENT - 1);
186
187         /*
188          * Allocate a new register+memory stack frame.
189          * 8 input registers (the max used by the ABI)
190          * 16 locals
191          * 4 output (number of parameters passed to trampoline)
192          */
193         ia64_alloc (code, l5, local0 - in0, out0 - local0, 4, 0);
194         ia64_mov (code, l8, IA64_SP);
195         ia64_adds_imm (code, IA64_SP, (-framesize), IA64_SP);
196
197         offset = 16; /* scratch area */
198
199         /* Save the argument received from the specific trampoline */
200         ia64_mov (code, l6, GP_SCRATCH_REG);
201
202         /* Save the calling address */
203         ia64_mov_from_br (code, l7, IA64_B0);
204
205         /* Create unwind info for the prolog */
206         r_pro = g_malloc0 (_U_dyn_region_info_size (3));
207         r_pro->op_count = 3;
208         r_pro->insn_count = 16;
209         i = 0;
210         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 2,
211                                                 /* reg=*/ UNW_IA64_AR_PFS, /* dst=*/ UNW_IA64_GR + local0 + 5);
212         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 5,
213                                                 /* reg=*/ UNW_IA64_SP, /* dst=*/ UNW_IA64_GR + local0 + 8);
214         _U_dyn_op_save_reg (&r_pro->op[i++], _U_QP_TRUE, /* when=*/ 14,
215                                                 /* reg=*/ UNW_IA64_RP, /* dst=*/ UNW_IA64_GR + local0 + 7);
216         g_assert ((unsigned) i <= r_pro->op_count);     
217
218         /* Save registers */
219         saved_regs_offset = offset;
220         offset += 128 * 8;
221         /* 
222          * Only the registers which are needed for computing vtable slots need
223          * to be saved.
224          */
225         for (i = 0; i < 64; ++i)
226                 if ((1 << i) & MONO_ARCH_CALLEE_REGS) {
227                         ia64_adds_imm (code, l1, saved_regs_offset + (i * 8), IA64_SP);
228                         ia64_st8_hint (code, l1, i, 0);
229                 }
230
231         /* Save fp registers */
232         saved_fpregs_offset = offset;
233         offset += 8 * 8;
234         ia64_adds_imm (code, l1, saved_fpregs_offset, IA64_SP);
235         for (i = 0; i < 8; ++i)
236                 ia64_stfd_inc_imm_hint (code, l1, i + 8, 8, 0);
237
238         g_assert (offset < framesize);
239
240         /* Arg1 is the pointer to the saved registers */
241         ia64_adds_imm (code, o0, saved_regs_offset, IA64_SP);
242
243         /* Arg2 is the address of the calling code */
244         if (has_caller)
245                 ia64_mov (code, o1, l7);
246         else
247                 ia64_mov (code, o1, 0);
248
249         /* Arg3 is the method/vtable ptr */
250         ia64_mov (code, o2, l6);
251
252         /* Arg4 is the trampoline address */
253         /* FIXME: */
254         ia64_mov (code, o3, 0);
255
256         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
257                 tramp = (guint8*)ia64_class_init_trampoline;
258         else if (tramp_type == MONO_TRAMPOLINE_AOT)
259                 tramp = (guint8*)ia64_aot_trampoline;
260         else
261                 tramp = (guint8*)ia64_magic_trampoline;
262
263         /* Call the trampoline using an indirect call */
264         ia64_movl (code, l0, tramp);
265         ia64_ld8_inc_imm (code, l1, l0, 8);
266         ia64_mov_to_br (code, IA64_B6, l1);
267         ia64_ld8 (code, IA64_GP, l0);
268         ia64_br_call_reg (code, 0, IA64_B6);
269
270         /* Restore fp regs */
271         ia64_adds_imm (code, l1, saved_fpregs_offset, IA64_SP);
272         for (i = 0; i < 8; ++i)
273                 ia64_ldfd_inc_imm (code, i + 8, l1, 8);
274
275         /* FIXME: Handle NATs in fp regs / scratch regs */
276
277         if (tramp_type != MONO_TRAMPOLINE_CLASS_INIT) {
278                 /* Load method address from function descriptor */
279                 ia64_ld8 (code, l0, IA64_R8);
280                 ia64_mov_to_br (code, IA64_B6, l0);
281         }
282
283         /* Clean up register/memory stack frame */
284         ia64_adds_imm (code, IA64_SP, framesize, IA64_SP);
285         ia64_mov_to_ar_i (code, IA64_PFS, l5);
286
287         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
288                 ia64_mov_ret_to_br (code, IA64_B0, l7);
289                 ia64_br_ret_reg (code, IA64_B0);
290         }
291         else {
292                 /* Call the compiled method */
293                 ia64_mov_to_br (code, IA64_B0, l7);
294                 ia64_br_cond_reg (code, IA64_B6);
295         }
296
297         ia64_codegen_close (code);
298
299         g_assert ((code.buf - buf) <= 2048);
300
301         /* FIXME: emit unwind info for epilog */
302         di = g_malloc0 (sizeof (unw_dyn_info_t));
303         di->start_ip = (unw_word_t) buf;
304         di->end_ip = (unw_word_t) code.buf;
305         di->gp = 0;
306         di->format = UNW_INFO_FORMAT_DYNAMIC;
307         di->u.pi.name_ptr = (unw_word_t)"ia64_generic_trampoline";
308         di->u.pi.regions = r_pro;
309
310         _U_dyn_register (di);
311
312         mono_arch_flush_icache (buf, code.buf - buf);
313
314         return buf;
315 }
316
317 #define TRAMPOLINE_SIZE 128
318
319 static gpointer
320 create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
321 {
322         guint8 *buf, *tramp;
323         gint64 disp;
324         Ia64CodegenState code;
325
326         tramp = mono_get_trampoline_code (tramp_type);
327
328         mono_domain_lock (domain);
329         buf = mono_code_manager_reserve (domain->code_mp, TRAMPOLINE_SIZE);
330         mono_domain_unlock (domain);
331
332         /* FIXME: Optimize this */
333
334         ia64_codegen_init (code, buf);
335
336         ia64_movl (code, GP_SCRATCH_REG, arg1);
337
338         ia64_begin_bundle (code);
339         disp = (tramp - code.buf) >> 4;
340         if (ia64_is_imm21 (disp)) {
341                 ia64_br_cond (code, disp);
342         }
343         else {
344                 ia64_movl (code, GP_SCRATCH_REG2, tramp);
345                 ia64_mov_to_br (code, IA64_B6, GP_SCRATCH_REG2);
346                 ia64_br_cond_reg (code, IA64_B6);
347         }
348
349         ia64_codegen_close (code);
350
351         g_assert (code.buf - buf <= TRAMPOLINE_SIZE);
352
353         mono_arch_flush_icache (buf, code.buf - buf);
354
355         if (code_len)
356                 *code_len = code.buf - buf;
357
358         return buf;
359 }
360
361 MonoJitInfo*
362 mono_arch_create_jump_trampoline (MonoMethod *method)
363 {
364         MonoJitInfo *ji;
365         gpointer code;
366         guint32 code_size;
367
368         code = create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP, mono_domain_get (), &code_size);
369
370         ji = g_new0 (MonoJitInfo, 1);
371         ji->code_start = code;
372         ji->code_size = code_size;
373         ji->method = method;
374
375         return ji;
376 }
377
378 gpointer
379 mono_arch_create_jit_trampoline (MonoMethod *method)
380 {
381         return create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC, mono_domain_get (), NULL);
382 }
383
384 gpointer
385 mono_arch_create_jit_trampoline_from_token (MonoImage *image, guint32 token)
386 {
387         MonoDomain *domain = mono_domain_get ();
388         guint8 *buf, *start;
389
390         mono_domain_lock (domain);
391         buf = start = mono_code_manager_reserve (domain->code_mp, 2 * sizeof (gpointer));
392         mono_domain_unlock (domain);
393
394         *(gpointer*)(gpointer)buf = image;
395         buf += sizeof (gpointer);
396         *(guint32*)(gpointer)buf = token;
397
398         return create_specific_trampoline (start, MONO_TRAMPOLINE_AOT, domain, NULL);
399 }
400
401 /**
402  * mono_arch_create_class_init_trampoline:
403  *  @vtable: the type to initialize
404  *
405  * Creates a trampoline function to run a type initializer. 
406  * If the trampoline is called, it calls mono_runtime_class_init with the
407  * given vtable, then patches the caller code so it does not get called any
408  * more.
409  * 
410  * Returns: a pointer to the newly created code 
411  */
412 gpointer
413 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
414 {
415         return create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT, vtable->domain, NULL);
416 }
417
418 void
419 mono_arch_invalidate_method (MonoJitInfo *ji, void *func, gpointer func_arg)
420 {
421         NOT_IMPLEMENTED;
422 }
423
424 gpointer
425 mono_debugger_create_notification_function (gpointer *notification_address)
426 {
427         NOT_IMPLEMENTED;
428
429         return NULL;
430 }