/* * tramp-sparc.c: JIT trampoline code for Sparc 64 * * Authors: * Mark Crichton (crichton@gimp.org) * Dietmar Maurer (dietmar@ximian.com) * * (C) 2003 Ximian, Inc. */ #include #include #include #include #include #include #include #include "mini.h" #include "mini-sparc.h" typedef enum { MONO_TRAMPOLINE_GENERIC, MONO_TRAMPOLINE_JUMP, MONO_TRAMPOLINE_CLASS_INIT } MonoTrampolineType; /* adapt to mini later... */ #define mono_jit_share_code (1) /* * Address of the Sparc trampoline code. This is used by the debugger to check * whether a method is a trampoline. */ guint8 *mono_generic_trampoline_code = NULL; /* * get_unbox_trampoline: * @m: method pointer * @addr: pointer to native code for @m * * when value type methods are called through the vtable we need to unbox the * this argument. This method returns a pointer to a trampoline which does * unboxing before calling the method */ static gpointer get_unbox_trampoline (MonoMethod *m, gpointer addr) { guint8 *code, *start; int this_pos = 4; if (!m->signature->ret->byref && MONO_TYPE_ISSTRUCT (m->signature->ret)) this_pos = 8; start = code = g_malloc (32); /* This executes in the context of the caller, hence o0 */ sparc_add_imm (code, 0, sparc_o0, sizeof (MonoObject), sparc_o0); sparc_set (code, addr, sparc_g1); sparc_jmpl (code, sparc_g1, sparc_g0, sparc_g0); sparc_nop (code); g_assert ((code - start) <= 32); mono_arch_flush_icache (start, code - start); return start; } /** * sparc_magic_trampoline: * @m: the method to translate * @code: the address of the call instruction * @fp: address of the stack frame for the caller * * This method is called by the trampoline functions for methods. It calls the * JIT compiler to compile the method, then patches the calling instruction so * further calls will bypass the trampoline. For virtual methods, it finds the * address of the vtable slot and updates it. */ static gpointer sparc_magic_trampoline (MonoMethod *m, guint32 *code, guint32 *fp) { gpointer addr; gpointer *vtable_slot; addr = mono_compile_method (m); g_assert (addr); /* * Check whenever this is a virtual call, and call an unbox trampoline if * needed. */ if (mono_sparc_is_virtual_call (code)) { if (m->klass->valuetype) addr = get_unbox_trampoline (m, addr); /* Compute address of vtable slot */ vtable_slot = mono_sparc_get_vcall_slot_addr (code, fp); *vtable_slot = addr; } else { /* Patch calling code */ if (sparc_inst_op (*code) == 0x1) sparc_call_simple (code, (guint8*)addr - (guint8*)code); } return addr; } static void sparc_class_init_trampoline (MonoVTable *vtable, guint32 *code) { mono_runtime_class_init (vtable); /* Patch calling code */ sparc_nop (code); } static guchar* create_trampoline_code (MonoTrampolineType tramp_type) { guint8 *buf, *code, *tramp_addr; guint32 lmf_offset; static guint8* generic_jump_trampoline = NULL; static guint8 *generic_class_init_trampoline = NULL; switch (tramp_type) { case MONO_TRAMPOLINE_GENERIC: if (mono_generic_trampoline_code) return mono_generic_trampoline_code; break; case MONO_TRAMPOLINE_JUMP: if (generic_jump_trampoline) return generic_jump_trampoline; break; case MONO_TRAMPOLINE_CLASS_INIT: if (generic_class_init_trampoline) return generic_class_init_trampoline; break; } code = buf = g_malloc (256); /* FIXME: save lmf etc */ sparc_save_imm (code, sparc_sp, -200, sparc_sp); /* We receive the method address in %r1, so save it here */ sparc_st_imm (code, sparc_g1, sparc_sp, 128); /* Save lmf since compilation can raise exceptions */ lmf_offset = - sizeof (MonoLMF); /* Save the data for the parent (managed) frame */ /* Save ip */ sparc_st_imm (code, sparc_i7, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ip)); /* Save sp */ sparc_st_imm (code, sparc_fp, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, sp)); /* Save fp */ /* Load previous fp from the saved register window */ sparc_flushw (code); sparc_ld_imm (code, sparc_fp, (sparc_i6 - 16) * 4, sparc_o7); sparc_st_imm (code, sparc_o7, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp)); /* Save method */ sparc_st_imm (code, sparc_g1, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method)); sparc_call_simple (code, (guint8*)mono_get_lmf_addr - (guint8*)code); sparc_nop (code); code = mono_sparc_emit_save_lmf (code, lmf_offset); if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) tramp_addr = &sparc_class_init_trampoline; else tramp_addr = &sparc_magic_trampoline; sparc_ld_imm (code, sparc_sp, 128, sparc_o0); /* pass parent frame address as third argument */ sparc_mov_reg_reg (code, sparc_fp, sparc_o2); sparc_call_simple (code, tramp_addr - code); /* set %o1 to caller address in delay slot */ sparc_mov_reg_reg (code, sparc_i7, sparc_o1); /* Save result */ sparc_st_imm (code, sparc_o0, sparc_sp, 128); /* Restore lmf */ code = mono_sparc_emit_restore_lmf (code, lmf_offset); /* Reload result */ sparc_ld_imm (code, sparc_sp, 128, sparc_o0); if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT) sparc_ret (code); else sparc_jmpl (code, sparc_o0, sparc_g0, sparc_g0); /* restore previous frame in delay slot */ sparc_restore_simple (code); g_assert ((code - buf) <= 256); switch (tramp_type) { case MONO_TRAMPOLINE_GENERIC: mono_generic_trampoline_code = buf; break; case MONO_TRAMPOLINE_JUMP: generic_jump_trampoline = buf; break; case MONO_TRAMPOLINE_CLASS_INIT: generic_class_init_trampoline = buf; break; } mono_arch_flush_icache (buf, code - buf); return buf; } #define TRAMPOLINE_SIZE (((SPARC_SET_MAX_SIZE >> 2) * 2) + 2) static MonoJitInfo* create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_type) { MonoJitInfo *ji; guint32 *code, *buf, *tramp; tramp = create_trampoline_code (tramp_type); code = buf = g_malloc (TRAMPOLINE_SIZE * 4); /* We have to use g5 here because there is no other free register */ sparc_set (code, tramp, sparc_g5); sparc_set (code, arg1, sparc_r1); sparc_jmpl (code, sparc_g5, sparc_g0, sparc_g0); sparc_nop (code); g_assert ((code - buf) <= TRAMPOLINE_SIZE); ji = g_new0 (MonoJitInfo, 1); ji->code_start = buf; ji->code_size = (code - buf) * 4; mono_jit_stats.method_trampolines++; mono_arch_flush_icache (ji->code_start, ji->code_size); return ji; } MonoJitInfo* mono_arch_create_jump_trampoline (MonoMethod *method) { MonoJitInfo *ji = create_specific_trampoline (method, MONO_TRAMPOLINE_JUMP); ji->method = method; return ji; } /** * mono_arch_create_jit_trampoline: * @method: pointer to the method info * * Creates a trampoline function for virtual methods. If the created * code is called it first starts JIT compilation of method, * and then calls the newly created method. I also replaces the * corresponding vtable entry (see sparc_magic_trampoline). * * Returns: a pointer to the newly created code */ gpointer mono_arch_create_jit_trampoline (MonoMethod *method) { MonoJitInfo *ji; /* previously created trampoline code */ if (method->info) return method->info; if (method->iflags & METHOD_IMPL_ATTRIBUTE_SYNCHRONIZED) return mono_arch_create_jit_trampoline (mono_marshal_get_synchronized_wrapper (method)); ji = create_specific_trampoline (method, MONO_TRAMPOLINE_GENERIC); method->info = ji->code_start; g_free (ji); return method->info; } /** * mono_arch_create_class_init_trampoline: * @vtable: the type to initialize * * Creates a trampoline function to run a type initializer. * If the trampoline is called, it calls mono_runtime_class_init with the * given vtable, then patches the caller code so it does not get called any * more. * * Returns: a pointer to the newly created code */ gpointer mono_arch_create_class_init_trampoline (MonoVTable *vtable) { MonoJitInfo *ji; gpointer code; ji = create_specific_trampoline (vtable, MONO_TRAMPOLINE_CLASS_INIT); code = ji->code_start; g_free (ji); return code; } /* * This method is only called when running in the Mono Debugger. */ gpointer mono_debugger_create_notification_function (gpointer *notification_address) { guint8 *ptr, *buf; ptr = buf = g_malloc0 (16); //x86_breakpoint (buf); if (notification_address) *notification_address = buf; //x86_ret (buf); return ptr; }