merge r98600
[mono.git] / mono / mini / tramp-ppc.c
1 /*
2  * tramp-ppc.c: JIT trampoline code for PowerPC
3  *
4  * Authors:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *   Paolo Molaro (lupus@ximian.com)
7  *   Carlos Valiente <yo@virutass.net>
8  *
9  * (C) 2001 Ximian, Inc.
10  */
11
12 #include <config.h>
13 #include <glib.h>
14
15 #include <mono/metadata/appdomain.h>
16 #include <mono/metadata/marshal.h>
17 #include <mono/metadata/tabledefs.h>
18 #include <mono/arch/ppc/ppc-codegen.h>
19
20 #include "mini.h"
21 #include "mini-ppc.h"
22
23 /*
24  * Return the instruction to jump from code to target, 0 if not
25  * reachable with a single instruction
26  */
27 static guint32
28 branch_for_target_reachable (guint8 *branch, guint8 *target)
29 {
30         gint diff = target - branch;
31         g_assert ((diff & 3) == 0);
32         if (diff >= 0) {
33                 if (diff <= 33554431)
34                         return (18 << 26) | (diff);
35         } else {
36                 /* diff between 0 and -33554432 */
37                 if (diff >= -33554432)
38                         return (18 << 26) | (diff & ~0xfc000000);
39         }
40         return 0;
41 }
42
43 /*
44  * get_unbox_trampoline:
45  * @m: method pointer
46  * @addr: pointer to native code for @m
47  *
48  * when value type methods are called through the vtable we need to unbox the
49  * this argument. This method returns a pointer to a trampoline which does
50  * unboxing before calling the method
51  */
52 gpointer
53 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
54 {
55         guint8 *code, *start;
56         int this_pos = 3;
57         guint32 short_branch;
58         MonoDomain *domain = mono_domain_get ();
59
60         if (!mono_method_signature (m)->ret->byref && MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
61                 this_pos = 4;
62             
63         mono_domain_lock (domain);
64         start = code = mono_code_manager_reserve (domain->code_mp, 20);
65         short_branch = branch_for_target_reachable (code + 4, addr);
66         if (short_branch)
67                 mono_code_manager_commit (domain->code_mp, code, 20, 8);
68         mono_domain_unlock (domain);
69
70         if (short_branch) {
71                 ppc_addi (code, this_pos, this_pos, sizeof (MonoObject));
72                 ppc_emit32 (code, short_branch);
73         } else {
74                 ppc_load (code, ppc_r0, addr);
75                 ppc_mtctr (code, ppc_r0);
76                 ppc_addi (code, this_pos, this_pos, sizeof (MonoObject));
77                 ppc_bcctr (code, 20, 0);
78         }
79         mono_arch_flush_icache (start, code - start);
80         g_assert ((code - start) <= 20);
81         /*g_print ("unbox trampoline at %d for %s:%s\n", this_pos, m->klass->name, m->name);
82         g_print ("unbox code is at %p for method at %p\n", start, addr);*/
83
84         return start;
85 }
86
87 void
88 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
89 {
90         guint32 *code = (guint32*)code_ptr;
91         /* This is the 'blrl' instruction */
92         --code;
93         
94         /*
95          * Note that methods are called also with the bl opcode.
96          */
97         if (((*code) >> 26) == 18) {
98                 /*g_print ("direct patching\n");*/
99                 ppc_patch ((char*)code, addr);
100                 mono_arch_flush_icache ((char*)code, 4);
101                 return;
102         }
103         
104         /* Sanity check: instruction must be 'blrl' */
105         g_assert(*code == 0x4e800021);
106
107         /* the thunk-less direct call sequence: lis/ori/mtlr/blrl */
108         if ((code [-1] >> 26) == 31 && (code [-2] >> 26) == 24 && (code [-3] >> 26) == 15) {
109                 ppc_patch ((char*)code, addr);
110                 return;
111         }
112         g_assert_not_reached ();
113 }
114
115 void
116 mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
117 {
118         g_assert_not_reached ();
119 }
120
121 void
122 mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs)
123 {
124         return;
125 }
126
127 void
128 mono_arch_nullify_plt_entry (guint8 *code)
129 {
130         g_assert_not_reached ();
131 }
132
133 /* Stack size for trampoline function 
134  * PPC_MINIMAL_STACK_SIZE + 16 (args + alignment to ppc_magic_trampoline)
135  * + MonoLMF + 14 fp regs + 13 gregs + alignment
136  * #define STACK (PPC_MINIMAL_STACK_SIZE + 4 * sizeof (gulong) + sizeof (MonoLMF) + 14 * sizeof (double) + 13 * (sizeof (gulong)))
137  * STACK would be 444 for 32 bit darwin
138  */
139 #define STACK (448)
140
141 /* Method-specific trampoline code fragment size */
142 #define METHOD_TRAMPOLINE_SIZE 64
143
144 /* Jump-specific trampoline code fragment size */
145 #define JUMP_TRAMPOLINE_SIZE   64
146
147 /*
148  * Stack frame description when the generic trampoline is called.
149  * caller frame
150  * --------------------
151  *  MonoLMF
152  *  -------------------
153  *  Saved FP registers 0-13
154  *  -------------------
155  *  Saved general registers 0-12
156  *  -------------------
157  *  param area for 3 args to ppc_magic_trampoline
158  *  -------------------
159  *  linkage area
160  *  -------------------
161  */
162 guchar*
163 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
164 {
165         guint8 *buf, *code = NULL;
166         int i, offset;
167         gpointer tramp_handler;
168
169         if(!code) {
170                 /* Now we'll create in 'buf' the PowerPC trampoline code. This
171                  is the trampoline code common to all methods  */
172                 
173                 code = buf = mono_global_codeman_reserve (512);
174                 
175                 ppc_stwu (buf, ppc_r1, -STACK, ppc_r1);
176
177                 /* start building the MonoLMF on the stack */
178                 offset = STACK - sizeof (double) * MONO_SAVED_FREGS;
179                 for (i = 14; i < 32; i++) {
180                         ppc_stfd (buf, i, offset, ppc_r1);
181                         offset += sizeof (double);
182                 }
183                 /* 
184                  * now the integer registers.
185                  */
186                 offset = STACK - sizeof (MonoLMF) + G_STRUCT_OFFSET (MonoLMF, iregs);
187                 ppc_stmw (buf, ppc_r13, ppc_r1, offset);
188
189                 /* Now save the rest of the registers below the MonoLMF struct, first 14
190                  * fp regs and then the 13 gregs.
191                  */
192                 offset = STACK - sizeof (MonoLMF) - (14 * sizeof (double));
193                 for (i = 0; i < 14; i++) {
194                         ppc_stfd (buf, i, offset, ppc_r1);
195                         offset += sizeof (double);
196                 }
197 #define GREGS_OFFSET (STACK - sizeof (MonoLMF) - (14 * sizeof (double)) - (13 * sizeof (gulong)))
198                 offset = GREGS_OFFSET;
199                 for (i = 0; i < 13; i++) {
200                         ppc_stw (buf, i, offset, ppc_r1);
201                         offset += sizeof (gulong);
202                 }
203                 /* we got here through a jump to the ctr reg, we must save the lr
204                  * in the parent frame (we do it here to reduce the size of the
205                  * method-specific trampoline)
206                  */
207                 ppc_mflr (buf, ppc_r0);
208                 ppc_stw (buf, ppc_r0, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
209
210                 /* ok, now we can continue with the MonoLMF setup, mostly untouched 
211                  * from emit_prolog in mini-ppc.c
212                  */
213                 ppc_load (buf, ppc_r0, mono_get_lmf_addr);
214                 ppc_mtlr (buf, ppc_r0);
215                 ppc_blrl (buf);
216                 /* we build the MonoLMF structure on the stack - see mini-ppc.h
217                  * The pointer to the struct is put in ppc_r11.
218                  */
219                 ppc_addi (buf, ppc_r11, ppc_sp, STACK - sizeof (MonoLMF));
220                 ppc_stw (buf, ppc_r3, G_STRUCT_OFFSET(MonoLMF, lmf_addr), ppc_r11);
221                 /* new_lmf->previous_lmf = *lmf_addr */
222                 ppc_lwz (buf, ppc_r0, G_STRUCT_OFFSET(MonoLMF, previous_lmf), ppc_r3);
223                 ppc_stw (buf, ppc_r0, G_STRUCT_OFFSET(MonoLMF, previous_lmf), ppc_r11);
224                 /* *(lmf_addr) = r11 */
225                 ppc_stw (buf, ppc_r11, G_STRUCT_OFFSET(MonoLMF, previous_lmf), ppc_r3);
226                 /* save method info (it's stored on the stack, so get it first and put it
227                  * in r5 as it's the third argument to the function)
228                  */
229                 ppc_lwz (buf, ppc_r5, GREGS_OFFSET, ppc_r1);
230                 if ((tramp_type == MONO_TRAMPOLINE_GENERIC) || (tramp_type == MONO_TRAMPOLINE_JUMP))
231                         ppc_stw (buf, ppc_r5, G_STRUCT_OFFSET(MonoLMF, method), ppc_r11);
232                 ppc_stw (buf, ppc_sp, G_STRUCT_OFFSET(MonoLMF, ebp), ppc_r11);
233                 /* save the IP (caller ip) */
234                 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
235                         ppc_li (buf, ppc_r0, 0);
236                 } else {
237                         ppc_lwz (buf, ppc_r0, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
238                 }
239                 ppc_stw (buf, ppc_r0, G_STRUCT_OFFSET(MonoLMF, eip), ppc_r11);
240
241                 /*
242                  * Now we're ready to call trampoline (gssize *regs, guint8 *code, gpointer value, guint8 *tramp)
243                  * Note that the last argument is unused.
244                  */
245                 /* Arg 1: a pointer to the registers */
246                 ppc_addi (buf, ppc_r3, ppc_r1, GREGS_OFFSET);
247                 
248                 /* Arg 2: code (next address to the instruction that called us) */
249                 if (tramp_type == MONO_TRAMPOLINE_JUMP) {
250                         ppc_li (buf, ppc_r4, 0);
251                 } else {
252                         ppc_lwz  (buf, ppc_r4, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
253                 }
254                 
255                 /* Arg 3: MonoMethod *method. It was put in r5 already above */
256                 /*ppc_mr  (buf, ppc_r5, ppc_r5);*/
257
258                 tramp_handler = mono_get_trampoline_func (tramp_type);
259                 ppc_lis  (buf, ppc_r0, (guint32) tramp_handler >> 16);
260                 ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) tramp_handler & 0xffff);
261                 ppc_mtlr (buf, ppc_r0);
262                 ppc_blrl (buf);
263                 
264                 /* OK, code address is now on r3. Move it to the counter reg
265                  * so it will be ready for the final jump: this is safe since we
266                  * won't do any more calls.
267                  */
268                 ppc_mtctr (buf, ppc_r3);
269
270                 /*
271                  * Now we restore the MonoLMF (see emit_epilogue in mini-ppc.c)
272                  * and the rest of the registers, so the method called will see
273                  * the same state as before we executed.
274                  * The pointer to MonoLMF is in ppc_r11.
275                  */
276                 ppc_addi (buf, ppc_r11, ppc_r1, STACK - sizeof (MonoLMF));
277                 /* r5 = previous_lmf */
278                 ppc_lwz (buf, ppc_r5, G_STRUCT_OFFSET(MonoLMF, previous_lmf), ppc_r11);
279                 /* r6 = lmf_addr */
280                 ppc_lwz (buf, ppc_r6, G_STRUCT_OFFSET(MonoLMF, lmf_addr), ppc_r11);
281                 /* *(lmf_addr) = previous_lmf */
282                 ppc_stw (buf, ppc_r5, G_STRUCT_OFFSET(MonoLMF, previous_lmf), ppc_r6);
283                 /* restore iregs */
284                 ppc_lmw (buf, ppc_r13, ppc_r11, G_STRUCT_OFFSET(MonoLMF, iregs));
285                 /* restore fregs */
286                 for (i = 14; i < 32; i++) {
287                         ppc_lfd (buf, i, G_STRUCT_OFFSET(MonoLMF, fregs) + ((i-14) * sizeof (gdouble)), ppc_r11);
288                 }
289
290                 /* restore the volatile registers, we skip r1, of course */
291                 offset = STACK - sizeof (MonoLMF) - (14 * sizeof (double));
292                 for (i = 0; i < 14; i++) {
293                         ppc_lfd (buf, i, offset, ppc_r1);
294                         offset += sizeof (double);
295                 }
296                 offset = STACK - sizeof (MonoLMF) - (14 * sizeof (double)) - (13 * sizeof (gulong));
297                 ppc_lwz (buf, ppc_r0, offset, ppc_r1);
298                 offset += 2 * sizeof (gulong);
299                 for (i = 2; i < 13; i++) {
300                         ppc_lwz (buf, i, offset, ppc_r1);
301                         offset += sizeof (gulong);
302                 }
303
304                 /* Non-standard function epilogue. Instead of doing a proper
305                  * return, we just jump to the compiled code.
306                  */
307                 /* Restore stack pointer and LR and jump to the code */
308                 ppc_lwz  (buf, ppc_r1,  0, ppc_r1);
309                 ppc_lwz  (buf, ppc_r11, PPC_RET_ADDR_OFFSET, ppc_r1);
310                 ppc_mtlr (buf, ppc_r11);
311                 if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) {
312                         ppc_blr (buf);
313                 } else {
314                         ppc_bcctr (buf, 20, 0);
315                 }
316
317                 /* Flush instruction cache, since we've generated code */
318                 mono_arch_flush_icache (code, buf - code);
319         
320                 /* Sanity check */
321                 g_assert ((buf - code) <= 512);
322         }
323
324         return code;
325 }
326
327 #define TRAMPOLINE_SIZE 24
328 gpointer
329 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
330 {
331         guint8 *code, *buf, *tramp;
332         guint32 short_branch;
333
334         tramp = mono_get_trampoline_code (tramp_type);
335
336         mono_domain_lock (domain);
337         code = buf = mono_code_manager_reserve_align (domain->code_mp, TRAMPOLINE_SIZE, 4);
338         short_branch = branch_for_target_reachable (code + 8, tramp);
339         if (short_branch)
340                 mono_code_manager_commit (domain->code_mp, code, TRAMPOLINE_SIZE, 12);
341         mono_domain_unlock (domain);
342
343         if (short_branch) {
344                 ppc_lis  (buf, ppc_r0, (guint32) arg1 >> 16);
345                 ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) arg1 & 0xffff);
346                 ppc_emit32 (buf, short_branch);
347         } else {
348                 /* Prepare the jump to the generic trampoline code.*/
349                 ppc_lis  (buf, ppc_r0, (guint32) tramp >> 16);
350                 ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) tramp & 0xffff);
351                 ppc_mtctr (buf, ppc_r0);
352         
353                 /* And finally put 'arg1' in r0 and fly! */
354                 ppc_lis  (buf, ppc_r0, (guint32) arg1 >> 16);
355                 ppc_ori  (buf, ppc_r0, ppc_r0, (guint32) arg1 & 0xffff);
356                 ppc_bcctr (buf, 20, 0);
357         }
358         
359         /* Flush instruction cache, since we've generated code */
360         mono_arch_flush_icache (code, buf - code);
361
362         g_assert ((buf - code) <= TRAMPOLINE_SIZE);
363         if (code_len)
364                 *code_len = buf - code;
365
366         return code;
367 }
368
369 /*
370  * This method is only called when running in the Mono Debugger.
371  */
372 gpointer
373 mono_debugger_create_notification_function (void)
374 {
375         guint8 *ptr, *buf;
376
377         ptr = buf = mono_global_codeman_reserve (8);
378         ppc_break (buf);
379         ppc_blr (buf);
380         mono_arch_flush_icache (ptr, buf - ptr);
381
382         return ptr;
383 }
384
385 gpointer
386 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
387 {
388         /* FIXME: implement! */
389         g_assert_not_reached ();
390         return NULL;
391 }
392
393 guint32
394 mono_arch_get_rgctx_lazy_fetch_offset (gpointer *regs)
395 {
396         /* FIXME: implement! */
397         g_assert_not_reached ();
398         return 0;
399 }