2008-02-22 Mark Probst <mark.probst@gmail.com>
[mono.git] / mono / mini / tramp-hppa.c
1 /*
2  * tramp-hppa.c: JIT trampoline code for hppa
3  *
4  * Copyright (c) 2007 Randolph Chung
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22  * THE SOFTWARE.
23  *
24  */
25
26 #include <config.h>
27 #include <glib.h>
28
29 #include <mono/arch/hppa/hppa-codegen.h>
30 #include <mono/metadata/appdomain.h>
31 #include <mono/metadata/marshal.h>
32 #include <mono/metadata/tabledefs.h>
33
34 #include "mini.h"
35 #include "mini-hppa.h"
36
37 /* sizeof (MonoLMF) == 320 + HPPA_STACK_LMF_OFFSET + linkage area (64 bytes) 
38  * leave some room to spare
39  */
40 #define TRAMP_STACK_SIZE        512
41 /* Method-specific trampoline code fragment size */
42 #define METHOD_TRAMPOLINE_SIZE  128
43 /* Jump-specific trampoline code fragment size */
44 #define JUMP_TRAMPOLINE_SIZE   64
45
46 /*
47  * mono_arch_get_unbox_trampoline:
48  * @m: method pointer
49  * @addr: pointer to native code for @m
50  *
51  * when value type methods are called through the vtable we need to unbox the
52  * this argument. This method returns a pointer to a trampoline which does
53  * unboxing before calling the method
54  */
55 gpointer
56 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
57 {
58         guint8 *code, *start;
59         int this_pos = hppa_r26;
60         MonoDomain *domain = mono_domain_get ();
61
62         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
63                 this_pos = hppa_r25;
64             
65         mono_domain_lock (domain);
66         start = code = mono_code_manager_reserve (domain->code_mp, 20);
67         mono_domain_unlock (domain);
68
69         hppa_set (code, addr, hppa_r1);
70         hppa_ldo (code, sizeof (MonoObject), this_pos, this_pos);
71         hppa_bv (code, hppa_r0, hppa_r1);
72         hppa_nop (code);
73
74         mono_arch_flush_icache (start, code - start);
75         g_assert ((code - start) <= 20);
76         return start;
77 }
78
79 void
80 mono_arch_patch_callsite (guint8 *p, guint8 *addr)
81 {
82         guint32 *code = (void *)p;
83         /* Search for and patch the calling sequence
84          *
85          * Possibilities are:
86          *
87          * CEE_CALL outputs the following sequence:
88          *
89          * ldil L'<addr>, r1
90          * ldo R'<addr>, r1
91          * bb,>=,n r1, 30, .+16
92          * depwi 0, 31, 2, r1
93          * ldw 4(r1), r19
94          * ldw 0(r1), r1
95          * ble 0(sr4,r1)
96          * copy r31, rp
97          * XXXXXXXXXXXXXXXX      <- code points here
98          */
99
100         /* Go back to the branching insn */
101
102         code -= 2;
103         /* We can patch the code only if it is a direct call. In some cases
104          * we can reach here via a reg-indirect call. In that case we can't
105          * patch the callsite
106          */
107         if ((code[0] >> 26) == 0x39 && /* ble */
108             (code[-4] >> 26) == 0x31 && /* bb */
109             (code[-6] >> 26) == 0x08) /* ldil */ {
110                 hppa_patch (&code[-6], addr);
111                 mono_arch_flush_icache (&code[-6], 8);
112         } else {
113                 printf("Can't patch callsite!\n");
114         }
115 }
116
117 void
118 mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
119 {
120         g_assert_not_reached ();
121 }
122
123 void
124 mono_arch_nullify_class_init_trampoline (guint8 *code8, gssize *regs)
125 {
126         guint32 *buf = (guint32 *)((unsigned long)code8 & ~3);
127         guint32 *code = buf;
128
129         code -= 2;
130         if (code[0] == 0x08000240) /* nop - already nullified */
131                 return;
132         g_assert ((code[0] >> 26) == 0x39); /* ble */
133         hppa_nop (code);
134         hppa_nop (code);
135
136         mono_arch_flush_icache (buf, 8);
137
138 }
139
140 void
141 mono_arch_nullify_plt_entry (guint8 *code)
142 {
143         g_assert_not_reached ();
144 }
145
146 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
147
148 /*
149  * Stack frame description when the generic trampoline is called.
150  * caller frame
151  * --------------------
152  *  MonoLMF
153  *  -------------------
154  *  incoming argument registers
155  *  -------------------
156  *  linkage area
157  *  -------------------
158  */
159 guchar*
160 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
161 {
162         guint8 *buf, *code = NULL;
163         int i, offset;
164
165         code = buf = mono_global_codeman_reserve (1024);
166
167         /* Trampoline is called with "method" in r20 */
168         hppa_stw (buf, hppa_r2, -20, hppa_sp);
169         hppa_copy (buf, hppa_sp, hppa_r1);
170         hppa_ldo (buf, TRAMP_STACK_SIZE, hppa_sp, hppa_sp);
171
172         /* Save the MonoLMF structure on the stack */
173         offset = HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, regs);
174         for (i = 0; i < 32; i++) {
175                 if (HPPA_IS_SAVED_GREG (i)) {
176                         hppa_stw (buf, i, offset, hppa_r1);
177                         offset += sizeof(ulong);
178                 }
179         }
180         hppa_copy (buf, hppa_r1, hppa_r3);
181         hppa_set (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, fregs), hppa_r1);
182         for (i = 0; i < 32; i++) {
183                 if (HPPA_IS_SAVED_FREG (i)) {
184                         hppa_fstdx (buf, i, hppa_r1, hppa_r3);
185                         hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
186                 }
187         }
188         /* Save the method info stored in r20 */
189         hppa_stw (buf, hppa_r20, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, method), hppa_r3);
190         hppa_stw (buf, hppa_r3, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, ebp), hppa_sp);
191         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
192                 hppa_stw (buf, hppa_r0, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, eip), hppa_r3);
193         } else {
194                 hppa_stw (buf, hppa_r2, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, eip), hppa_r3);
195         }
196
197         /* Save the incoming arguments too, before they can trashed by the 
198          * call to the magic trampoline
199          */
200         offset = HPPA_STACK_LMF_OFFSET + sizeof (MonoLMF);
201         for (i = hppa_r23; i <= hppa_r26; i++) {
202                 hppa_stw (buf, i, offset, hppa_r3);
203                 offset += sizeof(ulong);
204         }
205         hppa_stw (buf, hppa_r28, offset, hppa_r3);
206         offset += sizeof(ulong);
207         offset = ALIGN_TO (offset, sizeof(double));
208         hppa_ldo (buf, offset, hppa_r3, hppa_r1);
209         for (i = hppa_fr4; i <= hppa_fr7; i++) {
210                 hppa_fstds (buf, i, 0, hppa_r1);
211                 hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
212         }
213
214         /* Call mono_get_lmf_addr */
215         hppa_set (buf, mono_get_lmf_addr, hppa_r1);
216         hppa_depi (buf, 0, 31, 2, hppa_r1);
217         hppa_ldw (buf, 0, hppa_r1, hppa_r1);
218         hppa_ble (buf, 0, hppa_r1);
219         hppa_copy (buf, hppa_r31, hppa_r2);
220
221         /* r28 now points at the (MonoLMF **) for the current thread. */
222
223         /* new_lmf->lmf_addr = lmf_addr */
224         hppa_stw (buf, hppa_r28, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, lmf_addr), hppa_r3);
225
226         /* new_lmf->previous_lmf = *lmf_addr */
227         hppa_ldw (buf, 0, hppa_r28, hppa_r1);
228         hppa_stw (buf, hppa_r1, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, previous_lmf), hppa_r3);
229         /* *lmf_addr = new_lmf */
230         hppa_ldo (buf, HPPA_STACK_LMF_OFFSET, hppa_r3, hppa_r1);
231         hppa_stw (buf, hppa_r1, 0, hppa_r28);
232
233         /* Call mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp) */
234         hppa_ldo (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, regs), hppa_r3, hppa_r26);
235         hppa_ldw (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, method), hppa_r3, hppa_r24);
236         if (tramp_type == MONO_TRAMPOLINE_JUMP)
237                 hppa_copy (buf, hppa_r0, hppa_r25);
238         else {
239                 hppa_ldw (buf, -20, hppa_r3, hppa_r25);
240                 /* clear the lower two (privilege) bits */
241                 hppa_depi (buf, 0, 31, 2, hppa_r25);
242         }
243         hppa_copy (buf, hppa_r0, hppa_r23);
244
245         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
246                 hppa_set (buf, mono_class_init_trampoline, hppa_r1);
247         else
248                 hppa_set (buf, mono_magic_trampoline, hppa_r1);
249         
250         hppa_depi (buf, 0, 31, 2, hppa_r1);
251         hppa_ldw (buf, 0, hppa_r1, hppa_r1);
252         hppa_ble (buf, 0, hppa_r1);
253         hppa_copy (buf, hppa_r31, hppa_r2);
254
255         /* Code address is now in r28 */
256
257         /* Unwind LMF */
258         hppa_ldw (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, previous_lmf), hppa_r3, hppa_r20);
259         hppa_ldw (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, lmf_addr), hppa_r3, hppa_r21);
260         hppa_stw (buf, hppa_r20, 0, hppa_r21);
261
262         hppa_copy (buf, hppa_r28, hppa_r20);
263
264         /* Restore arguments */
265         offset = HPPA_STACK_LMF_OFFSET + sizeof (MonoLMF);
266         for (i = hppa_r23; i <= hppa_r26; i++) {
267                 hppa_ldw (buf, offset, hppa_r3, i);
268                 offset += sizeof(ulong);
269         }
270         hppa_ldw (buf, offset, hppa_r3, hppa_r28);
271         offset += sizeof(ulong);
272         offset = ALIGN_TO (offset, sizeof(double));
273         hppa_ldo (buf, offset, hppa_r3, hppa_r1);
274         for (i = hppa_fr4; i <= hppa_fr7; i++) {
275                 hppa_fldds (buf, 0, hppa_r1, i);
276                 hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
277         }
278
279         /* Restore registers */
280         hppa_set (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, fregs), hppa_r1);
281         for (i = 0; i < 32; i++) {
282                 if (HPPA_IS_SAVED_FREG (i)) {
283                         hppa_flddx (buf, hppa_r1, hppa_r3, i);
284                         hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
285                 }
286         }
287
288         hppa_copy (buf, hppa_r3, hppa_r1);
289         offset = HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, regs);
290         for (i = 0; i < 32; i++) {
291                 if (HPPA_IS_SAVED_GREG (i)) {
292                         hppa_ldw (buf, offset, hppa_r1, i);
293                         offset += sizeof(ulong);
294                 }
295         }
296         /* Jump to the compiled code in hppa_r28 */
297         hppa_ldw (buf, -20, hppa_r1, hppa_r2);
298         hppa_bv (buf, hppa_r0, hppa_r20);
299         hppa_ldo (buf, -TRAMP_STACK_SIZE, hppa_sp, hppa_sp);
300
301         g_assert ((buf - code) <= 1024);
302         return code;
303 }
304
305 /*
306  * This method is only called when running in the Mono Debugger.
307  */
308 gpointer
309 mono_debugger_create_notification_function (void)
310 {
311         guint8 *ptr, *buf;
312
313         ptr = buf = mono_global_codeman_reserve (16);
314
315         g_assert_not_reached ();
316
317         return ptr;
318 }
319
320 /**
321  * mono_arch_create_class_init_trampoline:
322  *  @vtable: the type to initialize
323  *
324  * Creates a trampoline function to run a type initializer. 
325  * If the trampoline is called, it calls mono_runtime_class_init with the
326  * given vtable, then patches the caller code so it does not get called any
327  * more.
328  * 
329  * Returns: a pointer to the newly created code 
330  */
331 gpointer
332 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
333 {
334         guint8 *code, *buf, *tramp;
335         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
336         /* This is the method-specific part of the trampoline. Its purpose is
337            to provide the generic part with the MonoMethod *method pointer. */
338         mono_domain_lock (vtable->domain);
339         code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
340         mono_domain_unlock (vtable->domain);
341
342         hppa_stw (buf, hppa_r2, -20, hppa_sp);
343         hppa_copy (buf, hppa_r3, hppa_r1);
344         hppa_copy (buf, hppa_sp, hppa_r3);
345         hppa_stwm (buf, hppa_r1, 64, hppa_sp);
346
347         /* mono_class_init_trampoline (regs, code, vtable, tramp) */
348         hppa_copy (buf, hppa_r0, hppa_r26);
349         hppa_copy (buf, hppa_r2, hppa_r25);
350         hppa_set (buf, vtable, hppa_r24);
351         hppa_copy (buf, hppa_r0, hppa_r23);
352
353         hppa_set (buf, mono_class_init_trampoline, hppa_r1);
354         hppa_depi (buf, 0, 31, 2, hppa_r1);
355         hppa_ldw (buf, 0, hppa_r1, hppa_r1);
356         hppa_ble (buf, 0, hppa_r1);
357         hppa_copy (buf, hppa_r31, hppa_r2);
358
359         hppa_ldw (buf, -20, hppa_r3, hppa_r2);
360         hppa_ldo (buf, 64, hppa_r3, hppa_sp);
361         hppa_bv (buf, hppa_r0, hppa_r2);
362         hppa_ldwm (buf, -64, hppa_sp, hppa_r3);
363
364         /* Flush instruction cache, since we've generated code */
365         mono_arch_flush_icache (code, buf - code);
366                 
367         /* Sanity check */
368         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
369
370         mono_jit_stats.method_trampolines++;
371
372         return code;
373 }
374
375 static MonoJitInfo*
376 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) 
377 {
378         guint8 *code, *buf;
379         MonoJitInfo *ji;
380
381         mono_domain_lock (domain);
382         code = buf = mono_code_manager_reserve (domain->code_mp, 20);
383         mono_domain_unlock (domain);
384
385         /* Call trampoline, with the "method" pointer in r20 */
386         hppa_set (buf, tramp, hppa_r1);
387         hppa_ldil (buf, hppa_lsel (method), hppa_r20);
388         hppa_bv (buf, hppa_r0, hppa_r1);
389         hppa_ldo (buf, hppa_rsel (method), hppa_r20, hppa_r20);
390
391         /* Flush instruction cache, since we've generated code */
392         mono_arch_flush_icache (code, buf - code);
393
394         g_assert ((buf - code) <= 20);
395
396         ji = g_new0 (MonoJitInfo, 1);
397         ji->method = method;
398         ji->code_start = code;
399         ji->code_size = buf - code;
400
401         mono_jit_stats.method_trampolines++;
402
403         return ji;
404 }
405
406
407 MonoJitInfo*
408 mono_arch_create_jump_trampoline (MonoMethod *method)
409 {
410         guint8 *tramp;
411         MonoDomain* domain = mono_domain_get ();
412         
413         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
414         return create_specific_tramp (method, tramp, domain);
415 }
416
417 /**
418  * arch_create_jit_trampoline:
419  * @method: pointer to the method info
420  *
421  * Creates a trampoline function for virtual methods. If the created
422  * code is called it first starts JIT compilation of method,
423  * and then calls the newly created method. It also replaces the
424  * corresponding vtable entry (see mono_magic_trampoline).
425  *
426  * A trampoline consists of two parts: a main fragment, shared by all method
427  * trampolines, and some code specific to each method, which hard-codes a
428  * reference to that method and then calls the main fragment.
429  *
430  * The main fragment contains a call to 'arm_magic_trampoline', which performs
431  * call to the JIT compiler and substitutes the method-specific fragment with
432  * some code that directly calls the JIT-compiled method.
433  * 
434  * Returns: a pointer to the newly created code 
435  */
436 gpointer
437 mono_arch_create_jit_trampoline (MonoMethod *method)
438 {
439         guint8 *tramp;
440         MonoJitInfo *ji;
441         MonoDomain* domain = mono_domain_get ();
442         gpointer code_start;
443
444         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC);
445         ji = create_specific_tramp (method, tramp, domain);
446         code_start = ji->code_start;
447         g_free (ji);
448
449         return code_start;
450 }
451
452 gpointer
453 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
454 {
455         /* FIXME: implement! */
456         g_assert_not_reached ();
457         return NULL;
458 }
459
460 guint32
461 mono_arch_get_rgctx_lazy_fetch_offset (gpointer *regs)
462 {
463         /* FIXME: implement! */
464         g_assert_not_reached ();
465         return 0;
466 }