2 * tramp-ppc.c: JIT trampoline code for PowerPC
5 * Dietmar Maurer (dietmar@ximian.com)
6 * Paolo Molaro (lupus@ximian.com)
7 * Carlos Valiente <yo@virutass.net>
9 * (C) 2001 Ximian, Inc.
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>
24 * Return the instruction to jump from code to target, 0 if not
25 * reachable with a single instruction
28 branch_for_target_reachable (guint8 *branch, guint8 *target)
30 gint diff = target - branch;
31 g_assert ((diff & 3) == 0);
34 return (18 << 26) | (diff);
36 /* diff between 0 and -33554432 */
37 if (diff >= -33554432)
38 return (18 << 26) | (diff & ~0xfc000000);
44 * get_unbox_trampoline:
46 * @addr: pointer to native code for @m
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
53 mono_arch_get_unbox_trampoline (MonoMethod *m, gpointer addr)
58 MonoDomain *domain = mono_domain_get ();
60 if (MONO_TYPE_ISSTRUCT (mono_method_signature (m)->ret))
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);
67 mono_code_manager_commit (domain->code_mp, code, 20, 8);
68 mono_domain_unlock (domain);
71 ppc_addi (code, this_pos, this_pos, sizeof (MonoObject));
72 ppc_emit32 (code, short_branch);
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);
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);*/
88 mono_arch_patch_callsite (guint8 *method_start, guint8 *code_ptr, guint8 *addr)
90 guint32 *code = (guint32*)code_ptr;
91 /* This is the 'blrl' instruction */
95 * Note that methods are called also with the bl opcode.
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);
104 /* Sanity check: instruction must be 'blrl' */
105 g_assert(*code == 0x4e800021);
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);
112 g_assert_not_reached ();
116 mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
118 g_assert_not_reached ();
122 mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs)
128 mono_arch_nullify_plt_entry (guint8 *code)
130 g_assert_not_reached ();
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
141 /* Method-specific trampoline code fragment size */
142 #define METHOD_TRAMPOLINE_SIZE 64
144 /* Jump-specific trampoline code fragment size */
145 #define JUMP_TRAMPOLINE_SIZE 64
148 * Stack frame description when the generic trampoline is called.
150 * --------------------
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 * -------------------
160 * -------------------
163 mono_arch_create_trampoline_code (MonoTrampolineType tramp_type)
165 guint8 *buf, *code = NULL;
167 gpointer tramp_handler;
170 /* Now we'll create in 'buf' the PowerPC trampoline code. This
171 is the trampoline code common to all methods */
173 code = buf = mono_global_codeman_reserve (512);
175 ppc_stwu (buf, ppc_r1, -STACK, ppc_r1);
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);
184 * now the integer registers.
186 offset = STACK - sizeof (MonoLMF) + G_STRUCT_OFFSET (MonoLMF, iregs);
187 ppc_stmw (buf, ppc_r13, ppc_r1, offset);
189 /* Now save the rest of the registers below the MonoLMF struct, first 14
190 * fp regs and then the 13 gregs.
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);
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);
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)
207 ppc_mflr (buf, ppc_r0);
208 ppc_stw (buf, ppc_r0, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
210 /* ok, now we can continue with the MonoLMF setup, mostly untouched
211 * from emit_prolog in mini-ppc.c
213 ppc_load (buf, ppc_r0, mono_get_lmf_addr);
214 ppc_mtlr (buf, ppc_r0);
216 /* we build the MonoLMF structure on the stack - see mini-ppc.h
217 * The pointer to the struct is put in ppc_r11.
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)
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);
237 ppc_lwz (buf, ppc_r0, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
239 ppc_stw (buf, ppc_r0, G_STRUCT_OFFSET(MonoLMF, eip), ppc_r11);
242 * Now we're ready to call trampoline (gssize *regs, guint8 *code, gpointer value, guint8 *tramp)
243 * Note that the last argument is unused.
245 /* Arg 1: a pointer to the registers */
246 ppc_addi (buf, ppc_r3, ppc_r1, GREGS_OFFSET);
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);
252 ppc_lwz (buf, ppc_r4, STACK + PPC_RET_ADDR_OFFSET, ppc_r1);
255 /* Arg 3: MonoMethod *method. It was put in r5 already above */
256 /*ppc_mr (buf, ppc_r5, ppc_r5);*/
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);
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.
268 ppc_mtctr (buf, ppc_r3);
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.
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);
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);
284 ppc_lmw (buf, ppc_r13, ppc_r11, G_STRUCT_OFFSET(MonoLMF, iregs));
286 for (i = 14; i < 32; i++) {
287 ppc_lfd (buf, i, G_STRUCT_OFFSET(MonoLMF, fregs) + ((i-14) * sizeof (gdouble)), ppc_r11);
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);
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);
304 /* Non-standard function epilogue. Instead of doing a proper
305 * return, we just jump to the compiled code.
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) {
314 ppc_bcctr (buf, 20, 0);
317 /* Flush instruction cache, since we've generated code */
318 mono_arch_flush_icache (code, buf - code);
321 g_assert ((buf - code) <= 512);
327 #define TRAMPOLINE_SIZE 24
329 mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type, MonoDomain *domain, guint32 *code_len)
331 guint8 *code, *buf, *tramp;
332 guint32 short_branch;
334 tramp = mono_get_trampoline_code (tramp_type);
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);
340 mono_code_manager_commit (domain->code_mp, code, TRAMPOLINE_SIZE, 12);
341 mono_domain_unlock (domain);
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);
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);
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);
359 /* Flush instruction cache, since we've generated code */
360 mono_arch_flush_icache (code, buf - code);
362 g_assert ((buf - code) <= TRAMPOLINE_SIZE);
364 *code_len = buf - code;
370 mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
372 /* FIXME: implement! */
373 g_assert_not_reached ();
378 mono_arch_get_rgctx_lazy_fetch_offset (gpointer *regs)
380 /* FIXME: implement! */
381 g_assert_not_reached ();