2008-08-22 Zoltan Varga <vargaz@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  * @gsctx: the generic sharing context
49  * @m: method pointer
50  * @addr: pointer to native code for @m
51  *
52  * when value type methods are called through the vtable we need to unbox the
53  * this argument. This method returns a pointer to a trampoline which does
54  * unboxing before calling the method
55  */
56 gpointer
57 mono_arch_get_unbox_trampoline (MonoGenericSharingContext *gsctx, MonoMethod *m, gpointer addr)
58 {
59         guint8 *code, *start;
60         int this_pos = hppa_r26;
61         MonoDomain *domain = mono_domain_get ();
62
63         if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
64                 this_pos = hppa_r25;
65             
66         mono_domain_lock (domain);
67         start = code = mono_code_manager_reserve (domain->code_mp, 20);
68         mono_domain_unlock (domain);
69
70         hppa_set (code, addr, hppa_r1);
71         hppa_ldo (code, sizeof (MonoObject), this_pos, this_pos);
72         hppa_bv (code, hppa_r0, hppa_r1);
73         hppa_nop (code);
74
75         mono_arch_flush_icache (start, code - start);
76         g_assert ((code - start) <= 20);
77         return start;
78 }
79
80 void
81 mono_arch_patch_callsite (guint8 *method_start, guint8 *p, guint8 *addr)
82 {
83         guint32 *code = (void *)p;
84         /* Search for and patch the calling sequence
85          *
86          * Possibilities are:
87          *
88          * CEE_CALL outputs the following sequence:
89          *
90          * ldil L'<addr>, r1
91          * ldo R'<addr>, r1
92          * bb,>=,n r1, 30, .+16
93          * depwi 0, 31, 2, r1
94          * ldw 4(r1), r19
95          * ldw 0(r1), r1
96          * ble 0(sr4,r1)
97          * copy r31, rp
98          * XXXXXXXXXXXXXXXX      <- code points here
99          */
100
101         /* Go back to the branching insn */
102
103         code -= 2;
104         /* We can patch the code only if it is a direct call. In some cases
105          * we can reach here via a reg-indirect call. In that case we can't
106          * patch the callsite
107          */
108         if ((code[0] >> 26) == 0x39 && /* ble */
109             (code[-4] >> 26) == 0x31 && /* bb */
110             (code[-6] >> 26) == 0x08) /* ldil */ {
111                 hppa_patch (&code[-6], addr);
112                 mono_arch_flush_icache (&code[-6], 8);
113         } else {
114                 printf("Can't patch callsite!\n");
115         }
116 }
117
118 void
119 mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
120 {
121         g_assert_not_reached ();
122 }
123
124 void
125 mono_arch_nullify_class_init_trampoline (guint8 *code8, gssize *regs)
126 {
127         guint32 *buf = (guint32 *)((unsigned long)code8 & ~3);
128         guint32 *code = buf;
129
130         code -= 2;
131         if (code[0] == 0x08000240) /* nop - already nullified */
132                 return;
133         g_assert ((code[0] >> 26) == 0x39); /* ble */
134         hppa_nop (code);
135         hppa_nop (code);
136
137         mono_arch_flush_icache (buf, 8);
138
139 }
140
141 void
142 mono_arch_nullify_plt_entry (guint8 *code)
143 {
144         g_assert_not_reached ();
145 }
146
147 #define ALIGN_TO(val,align) (((val) + ((align) - 1)) & ~((align) - 1))
148
149 /*
150  * Stack frame description when the generic trampoline is called.
151  * caller frame
152  * --------------------
153  *  MonoLMF
154  *  -------------------
155  *  incoming argument registers
156  *  -------------------
157  *  linkage area
158  *  -------------------
159  */
160 guchar*
161 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
162 {
163         guint8 *buf, *code = NULL;
164         int i, offset;
165
166         code = buf = mono_global_codeman_reserve (1024);
167
168         /* Trampoline is called with "method" in r20 */
169         hppa_stw (buf, hppa_r2, -20, hppa_sp);
170         hppa_copy (buf, hppa_sp, hppa_r1);
171         hppa_ldo (buf, TRAMP_STACK_SIZE, hppa_sp, hppa_sp);
172
173         /* Save the MonoLMF structure on the stack */
174         offset = HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, regs);
175         for (i = 0; i < 32; i++) {
176                 if (HPPA_IS_SAVED_GREG (i)) {
177                         hppa_stw (buf, i, offset, hppa_r1);
178                         offset += sizeof(ulong);
179                 }
180         }
181         hppa_copy (buf, hppa_r1, hppa_r3);
182         hppa_set (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, fregs), hppa_r1);
183         for (i = 0; i < 32; i++) {
184                 if (HPPA_IS_SAVED_FREG (i)) {
185                         hppa_fstdx (buf, i, hppa_r1, hppa_r3);
186                         hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
187                 }
188         }
189         /* Save the method info stored in r20 */
190         hppa_stw (buf, hppa_r20, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, method), hppa_r3);
191         hppa_stw (buf, hppa_r3, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, ebp), hppa_sp);
192         if (tramp_type == MONO_TRAMPOLINE_JUMP) {
193                 hppa_stw (buf, hppa_r0, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, eip), hppa_r3);
194         } else {
195                 hppa_stw (buf, hppa_r2, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, eip), hppa_r3);
196         }
197
198         /* Save the incoming arguments too, before they can trashed by the 
199          * call to the magic trampoline
200          */
201         offset = HPPA_STACK_LMF_OFFSET + sizeof (MonoLMF);
202         for (i = hppa_r23; i <= hppa_r26; i++) {
203                 hppa_stw (buf, i, offset, hppa_r3);
204                 offset += sizeof(ulong);
205         }
206         hppa_stw (buf, hppa_r28, offset, hppa_r3);
207         offset += sizeof(ulong);
208         offset = ALIGN_TO (offset, sizeof(double));
209         hppa_ldo (buf, offset, hppa_r3, hppa_r1);
210         for (i = hppa_fr4; i <= hppa_fr7; i++) {
211                 hppa_fstds (buf, i, 0, hppa_r1);
212                 hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
213         }
214
215         /* Call mono_get_lmf_addr */
216         hppa_set (buf, mono_get_lmf_addr, hppa_r1);
217         hppa_depi (buf, 0, 31, 2, hppa_r1);
218         hppa_ldw (buf, 0, hppa_r1, hppa_r1);
219         hppa_ble (buf, 0, hppa_r1);
220         hppa_copy (buf, hppa_r31, hppa_r2);
221
222         /* r28 now points at the (MonoLMF **) for the current thread. */
223
224         /* new_lmf->lmf_addr = lmf_addr */
225         hppa_stw (buf, hppa_r28, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, lmf_addr), hppa_r3);
226
227         /* new_lmf->previous_lmf = *lmf_addr */
228         hppa_ldw (buf, 0, hppa_r28, hppa_r1);
229         hppa_stw (buf, hppa_r1, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, previous_lmf), hppa_r3);
230         /* *lmf_addr = new_lmf */
231         hppa_ldo (buf, HPPA_STACK_LMF_OFFSET, hppa_r3, hppa_r1);
232         hppa_stw (buf, hppa_r1, 0, hppa_r28);
233
234         /* Call mono_magic_trampoline (gssize *regs, guint8 *code, MonoMethod *m, guint8* tramp) */
235         hppa_ldo (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, regs), hppa_r3, hppa_r26);
236         hppa_ldw (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, method), hppa_r3, hppa_r24);
237         if (tramp_type == MONO_TRAMPOLINE_JUMP)
238                 hppa_copy (buf, hppa_r0, hppa_r25);
239         else {
240                 hppa_ldw (buf, -20, hppa_r3, hppa_r25);
241                 /* clear the lower two (privilege) bits */
242                 hppa_depi (buf, 0, 31, 2, hppa_r25);
243         }
244         hppa_copy (buf, hppa_r0, hppa_r23);
245
246         if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
247                 hppa_set (buf, mono_class_init_trampoline, hppa_r1);
248         else
249                 hppa_set (buf, mono_magic_trampoline, hppa_r1);
250         
251         hppa_depi (buf, 0, 31, 2, hppa_r1);
252         hppa_ldw (buf, 0, hppa_r1, hppa_r1);
253         hppa_ble (buf, 0, hppa_r1);
254         hppa_copy (buf, hppa_r31, hppa_r2);
255
256         /* Code address is now in r28 */
257
258         /* Unwind LMF */
259         hppa_ldw (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, previous_lmf), hppa_r3, hppa_r20);
260         hppa_ldw (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, lmf_addr), hppa_r3, hppa_r21);
261         hppa_stw (buf, hppa_r20, 0, hppa_r21);
262
263         hppa_copy (buf, hppa_r28, hppa_r20);
264
265         /* Restore arguments */
266         offset = HPPA_STACK_LMF_OFFSET + sizeof (MonoLMF);
267         for (i = hppa_r23; i <= hppa_r26; i++) {
268                 hppa_ldw (buf, offset, hppa_r3, i);
269                 offset += sizeof(ulong);
270         }
271         hppa_ldw (buf, offset, hppa_r3, hppa_r28);
272         offset += sizeof(ulong);
273         offset = ALIGN_TO (offset, sizeof(double));
274         hppa_ldo (buf, offset, hppa_r3, hppa_r1);
275         for (i = hppa_fr4; i <= hppa_fr7; i++) {
276                 hppa_fldds (buf, 0, hppa_r1, i);
277                 hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
278         }
279
280         /* Restore registers */
281         hppa_set (buf, HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, fregs), hppa_r1);
282         for (i = 0; i < 32; i++) {
283                 if (HPPA_IS_SAVED_FREG (i)) {
284                         hppa_flddx (buf, hppa_r1, hppa_r3, i);
285                         hppa_ldo (buf, sizeof(double), hppa_r1, hppa_r1);
286                 }
287         }
288
289         hppa_copy (buf, hppa_r3, hppa_r1);
290         offset = HPPA_STACK_LMF_OFFSET + G_STRUCT_OFFSET (MonoLMF, regs);
291         for (i = 0; i < 32; i++) {
292                 if (HPPA_IS_SAVED_GREG (i)) {
293                         hppa_ldw (buf, offset, hppa_r1, i);
294                         offset += sizeof(ulong);
295                 }
296         }
297         /* Jump to the compiled code in hppa_r28 */
298         hppa_ldw (buf, -20, hppa_r1, hppa_r2);
299         hppa_bv (buf, hppa_r0, hppa_r20);
300         hppa_ldo (buf, -TRAMP_STACK_SIZE, hppa_sp, hppa_sp);
301
302         g_assert ((buf - code) <= 1024);
303         return code;
304 }
305
306 /**
307  * mono_arch_create_class_init_trampoline:
308  *  @vtable: the type to initialize
309  *
310  * Creates a trampoline function to run a type initializer. 
311  * If the trampoline is called, it calls mono_runtime_class_init with the
312  * given vtable, then patches the caller code so it does not get called any
313  * more.
314  * 
315  * Returns: a pointer to the newly created code 
316  */
317 gpointer
318 mono_arch_create_class_init_trampoline (MonoVTable *vtable)
319 {
320         guint8 *code, *buf, *tramp;
321         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_CLASS_INIT);
322         /* This is the method-specific part of the trampoline. Its purpose is
323            to provide the generic part with the MonoMethod *method pointer. */
324         mono_domain_lock (vtable->domain);
325         code = buf = mono_code_manager_reserve (vtable->domain->code_mp, METHOD_TRAMPOLINE_SIZE);
326         mono_domain_unlock (vtable->domain);
327
328         hppa_stw (buf, hppa_r2, -20, hppa_sp);
329         hppa_copy (buf, hppa_r3, hppa_r1);
330         hppa_copy (buf, hppa_sp, hppa_r3);
331         hppa_stwm (buf, hppa_r1, 64, hppa_sp);
332
333         /* mono_class_init_trampoline (regs, code, vtable, tramp) */
334         hppa_copy (buf, hppa_r0, hppa_r26);
335         hppa_copy (buf, hppa_r2, hppa_r25);
336         hppa_set (buf, vtable, hppa_r24);
337         hppa_copy (buf, hppa_r0, hppa_r23);
338
339         hppa_set (buf, mono_class_init_trampoline, hppa_r1);
340         hppa_depi (buf, 0, 31, 2, hppa_r1);
341         hppa_ldw (buf, 0, hppa_r1, hppa_r1);
342         hppa_ble (buf, 0, hppa_r1);
343         hppa_copy (buf, hppa_r31, hppa_r2);
344
345         hppa_ldw (buf, -20, hppa_r3, hppa_r2);
346         hppa_ldo (buf, 64, hppa_r3, hppa_sp);
347         hppa_bv (buf, hppa_r0, hppa_r2);
348         hppa_ldwm (buf, -64, hppa_sp, hppa_r3);
349
350         /* Flush instruction cache, since we've generated code */
351         mono_arch_flush_icache (code, buf - code);
352                 
353         /* Sanity check */
354         g_assert ((buf - code) <= METHOD_TRAMPOLINE_SIZE);
355
356         mono_jit_stats.method_trampolines++;
357
358         return code;
359 }
360
361 static MonoJitInfo*
362 create_specific_tramp (MonoMethod *method, guint8* tramp, MonoDomain *domain) 
363 {
364         guint8 *code, *buf;
365         MonoJitInfo *ji;
366
367         mono_domain_lock (domain);
368         code = buf = mono_code_manager_reserve (domain->code_mp, 20);
369         mono_domain_unlock (domain);
370
371         /* Call trampoline, with the "method" pointer in r20 */
372         hppa_set (buf, tramp, hppa_r1);
373         hppa_ldil (buf, hppa_lsel (method), hppa_r20);
374         hppa_bv (buf, hppa_r0, hppa_r1);
375         hppa_ldo (buf, hppa_rsel (method), hppa_r20, hppa_r20);
376
377         /* Flush instruction cache, since we've generated code */
378         mono_arch_flush_icache (code, buf - code);
379
380         g_assert ((buf - code) <= 20);
381
382         ji = g_new0 (MonoJitInfo, 1);
383         ji->method = method;
384         ji->code_start = code;
385         ji->code_size = buf - code;
386
387         mono_jit_stats.method_trampolines++;
388
389         return ji;
390 }
391
392
393 MonoJitInfo*
394 mono_arch_create_jump_trampoline (MonoMethod *method)
395 {
396         guint8 *tramp;
397         MonoDomain* domain = mono_domain_get ();
398         
399         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JUMP);
400         return create_specific_tramp (method, tramp, domain);
401 }
402
403 /**
404  * arch_create_jit_trampoline:
405  * @method: pointer to the method info
406  *
407  * Creates a trampoline function for virtual methods. If the created
408  * code is called it first starts JIT compilation of method,
409  * and then calls the newly created method. It also replaces the
410  * corresponding vtable entry (see mono_magic_trampoline).
411  *
412  * A trampoline consists of two parts: a main fragment, shared by all method
413  * trampolines, and some code specific to each method, which hard-codes a
414  * reference to that method and then calls the main fragment.
415  *
416  * The main fragment contains a call to 'arm_magic_trampoline', which performs
417  * call to the JIT compiler and substitutes the method-specific fragment with
418  * some code that directly calls the JIT-compiled method.
419  * 
420  * Returns: a pointer to the newly created code 
421  */
422 gpointer
423 mono_arch_create_jit_trampoline (MonoMethod *method)
424 {
425         guint8 *tramp;
426         MonoJitInfo *ji;
427         MonoDomain* domain = mono_domain_get ();
428         gpointer code_start;
429
430         tramp = mono_get_trampoline_code (MONO_TRAMPOLINE_JIT);
431         ji = create_specific_tramp (method, tramp, domain);
432         code_start = ji->code_start;
433         g_free (ji);
434
435         return code_start;
436 }
437
438 gpointer
439 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
440 {
441         /* FIXME: implement! */
442         g_assert_not_reached ();
443         return NULL;
444 }