/** * mono_arch_get_throw_exception_by_name: * * Returns a function pointer which can be used to raise * corlib exceptions. The returned function has the following * signature: void (*func) (char *exc_name, gpointer ip); */ gpointer mono_arch_get_throw_exception_by_name (void) { static guint32 *start; static int inited = 0; guint32 *code; int reg; if (inited) return start; inited = 1; code = start = mono_global_codeman_reserve (64 * sizeof (guint32)); #ifdef SPARCV9 reg = sparc_g4; #else reg = sparc_g1; #endif sparc_save_imm (code, sparc_sp, -160, sparc_sp); sparc_mov_reg_reg (code, sparc_i0, sparc_o2); sparc_set (code, mono_defaults.corlib, sparc_o0); sparc_set (code, "System", sparc_o1); sparc_set (code, mono_exception_from_name, sparc_o7); sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite); sparc_nop (code); /* Return to the caller, so exception handling does not see this frame */ sparc_restore (code, sparc_o0, sparc_g0, sparc_o0); /* Put original return address into %o7 */ sparc_mov_reg_reg (code, sparc_o1, sparc_o7); sparc_set (code, mono_arch_get_throw_exception (), reg); /* Use a jmp instead of a call so o7 is preserved */ sparc_jmpl_imm (code, reg, 0, sparc_g0); sparc_nop (code); g_assert ((code - start) < 32); mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start); return start; }
/** * mono_arch_get_throw_corlib_exception: * * Returns a function pointer which can be used to raise * corlib exceptions. The returned function has the following * signature: void (*func) (guint32 ex_token, guint32 offset); * Here, offset is the offset which needs to be substracted from the caller IP * to get the IP of the throw. Passing the offset has the advantage that it * needs no relocations in the caller. */ gpointer mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot) { static guint32 *start; static int inited = 0; guint32 *code; int reg; g_assert (!aot); if (info) *info = NULL; if (inited) return start; inited = 1; code = start = mono_global_codeman_reserve (64 * sizeof (guint32)); #ifdef SPARCV9 reg = sparc_g4; #else reg = sparc_g1; #endif sparc_mov_reg_reg (code, sparc_o7, sparc_o2); sparc_save_imm (code, sparc_sp, -160, sparc_sp); sparc_set (code, MONO_TOKEN_TYPE_DEF, sparc_o7); sparc_add (code, FALSE, sparc_i0, sparc_o7, sparc_o1); sparc_set (code, mono_defaults.exception_class->image, sparc_o0); sparc_set (code, mono_exception_from_token, sparc_o7); sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite); sparc_nop (code); /* Return to the caller, so exception handling does not see this frame */ sparc_restore (code, sparc_o0, sparc_g0, sparc_o0); /* Compute throw ip */ sparc_sll_imm (code, sparc_o1, 2, sparc_o1); sparc_sub (code, 0, sparc_o2, sparc_o1, sparc_o7); sparc_set (code, mono_arch_get_throw_exception (NULL, FALSE), reg); /* Use a jmp instead of a call so o7 is preserved */ sparc_jmpl_imm (code, reg, 0, sparc_g0); sparc_nop (code); g_assert ((code - start) < 32); mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start); return start; }
static gpointer get_throw_exception (gboolean rethrow) { guint32 *start, *code; code = start = mono_global_codeman_reserve (16 * sizeof (guint32)); sparc_save_imm (code, sparc_sp, -512, sparc_sp); sparc_flushw (code); sparc_mov_reg_reg (code, sparc_i0, sparc_o0); sparc_mov_reg_reg (code, sparc_fp, sparc_o1); sparc_mov_reg_reg (code, sparc_i7, sparc_o2); sparc_set (code, rethrow, sparc_o3); sparc_set (code, throw_exception, sparc_o7); sparc_jmpl (code, sparc_o7, sparc_g0, sparc_callsite); sparc_nop (code); g_assert ((code - start) <= 16); mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start); return start; }
static inline guint32* emit_save_parameters (guint32 *p, MonoMethodSignature *sig, guint stack_size, gboolean use_memcpy) { guint i, fr, gr, stack_par_pos, struct_pos, cur_struct_pos; guint32 simpletype; fr = gr = 0; stack_par_pos = MINIMAL_STACK_SIZE * SLOT_SIZE + BIAS; if (sig->hasthis) { if (use_memcpy) { /* we don't need to save a thing. */ } else sparc_mov_reg_reg (p, sparc_i2, sparc_o0); gr ++; } if (use_memcpy) { cur_struct_pos = struct_pos = stack_par_pos; for (i = 0; i < sig->param_count; i++) { if (sig->params[i]->byref) continue; if (sig->params[i]->type == MONO_TYPE_VALUETYPE && !sig->params[i]->data.klass->enumtype) { gint size; guint32 align; size = mono_class_native_size (sig->params[i]->data.klass, &align); #if SPARCV9 if (size != 4) { #else if (1) { #endif /* Add alignment */ stack_par_pos = (stack_par_pos + (align - 1)) & (~(align - 1)); /* need to call memcpy here */ sparc_add_imm (p, 0, sparc_sp, stack_par_pos, sparc_o0); sparc_ld_imm_ptr (p, sparc_i3, i*16, sparc_o1); sparc_set (p, (guint32)size, sparc_o2); sparc_set_ptr (p, (void *)memmove, sparc_l0); sparc_jmpl_imm (p, sparc_l0, 0, sparc_callsite); sparc_nop (p); stack_par_pos += (size + (SLOT_SIZE - 1)) & (~(SLOT_SIZE - 1)); } } } } if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->byref) { MonoClass *klass = sig->ret->data.klass; if (!klass->enumtype) { gint size = mono_class_native_size (klass, NULL); DEBUG(fprintf(stderr, "retval value type size: %d\n", size)); #if SPARCV9 if (size > 32) { #else { #endif /* pass on buffer in interp.c to called function */ sparc_ld_imm_ptr (p, sparc_i1, 0, sparc_l0); sparc_st_imm_ptr (p, sparc_l0, sparc_sp, 64); } } } DEBUG(fprintf(stderr, "%s\n", sig_to_name(sig, FALSE))); for (i = 0; i < sig->param_count; i++) { if (sig->params[i]->byref) { SAVE_PTR_IN_GENERIC_REGISTER; continue; } simpletype = sig->params[i]->type; enum_calc_size: switch (simpletype) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_I1: case MONO_TYPE_U1: case MONO_TYPE_I2: case MONO_TYPE_U2: case MONO_TYPE_CHAR: case MONO_TYPE_I4: case MONO_TYPE_U4: if (gr < OUT_REGS) { sparc_ld_imm (p, ARG_BASE, i*ARG_SIZE, sparc_o0 + gr); gr++; } else { sparc_ld_imm (p, ARG_BASE, i*ARG_SIZE, sparc_l0); sparc_st_imm_word (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } break; case MONO_TYPE_R4: #if SPARCV9 sparc_lddf_imm (p, ARG_BASE, i*ARG_SIZE, sparc_f30); /* fix using this fixed reg */ sparc_fdtos(p, sparc_f30, sparc_f0 + 2 * gr + 1); gr++; break; #else /* Convert from double to single */ sparc_lddf_imm (p, ARG_BASE, i*ARG_SIZE, sparc_f0); sparc_fdtos (p, sparc_f0, sparc_f0); /* * FIXME: Is there an easier way to do an * freg->ireg move ? */ sparc_stf_imm (p, sparc_f0, sparc_sp, stack_par_pos); if (gr < OUT_REGS) { sparc_ld_imm (p, sparc_sp, stack_par_pos, sparc_o0 + gr); gr++; } else { sparc_ldf_imm (p, sparc_sp, stack_par_pos, sparc_f0); sparc_stf_imm (p, sparc_f0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } break; #endif case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_PTR: case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: case MONO_TYPE_STRING: case MONO_TYPE_SZARRAY: SAVE_PTR_IN_GENERIC_REGISTER; break; case MONO_TYPE_VALUETYPE: { gint size; guint32 align; MonoClass *klass = sig->params[i]->data.klass; if (klass->enumtype) { simpletype = klass->enum_basetype->type; goto enum_calc_size; } size = mono_class_native_size (klass, &align); #if SPARCV9 if (size <= 16) { if (gr < OUT_REGS) { p = v9_struct_arg(p, i, klass, size, &gr); } else { sparc_ld_imm_ptr (p, ARG_BASE, i*ARG_SIZE, sparc_l0); sparc_ld_imm (p, sparc_l0, 0, sparc_l0); sparc_st_imm_word (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } break; } #else /* * FIXME: The 32bit ABI docs do not mention that small * structures are passed in registers. */ /* if (size == 4) { if (gr < OUT_REGS) { sparc_ld_imm_ptr (p, ARG_BASE, i*ARG_SIZE, sparc_l0); sparc_ld_imm (p, sparc_l0, 0, sparc_o0 + gr); gr++; } else { sparc_ld_imm_ptr (p, ARG_BASE, i*ARG_SIZE, sparc_l0); sparc_ld_imm (p, sparc_l0, 0, sparc_l0); sparc_st_imm_word (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } break; } */ #endif cur_struct_pos = (cur_struct_pos + (align - 1)) & (~(align - 1)); if (gr < OUT_REGS) { sparc_add_imm (p, 0, sparc_sp, cur_struct_pos, sparc_o0 + gr); gr ++; } else { sparc_ld_imm_ptr (p, sparc_sp, cur_struct_pos, sparc_l1); sparc_st_imm_ptr (p, sparc_l1, sparc_sp, stack_par_pos); } cur_struct_pos += (size + (SLOT_SIZE - 1)) & (~(SLOT_SIZE - 1)); break; } #if SPARCV9 case MONO_TYPE_I8: if (gr < OUT_REGS) { sparc_ldx_imm (p, ARG_BASE, i*ARG_SIZE, sparc_o0 + gr); gr++; } else { sparc_ldx_imm (p, ARG_BASE, i*ARG_SIZE, sparc_l0); sparc_stx_imm (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } break; case MONO_TYPE_R8: sparc_lddf_imm (p, ARG_BASE, i*ARG_SIZE, sparc_f0 + 2 * i); break; #else case MONO_TYPE_I8: case MONO_TYPE_R8: if (gr < (OUT_REGS - 1)) { sparc_ld_imm (p, ARG_BASE, i*ARG_SIZE, sparc_o0 + gr); gr ++; sparc_ld_imm (p, ARG_BASE, (i*ARG_SIZE) + 4, sparc_o0 + gr); gr ++; } else if (gr == (OUT_REGS - 1)) { /* Split register/stack */ sparc_ld_imm (p, ARG_BASE, i*ARG_SIZE, sparc_o0 + gr); gr ++; sparc_ld_imm (p, ARG_BASE, (i*ARG_SIZE) + 4, sparc_l0); sparc_st_imm (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } else { sparc_ld_imm (p, ARG_BASE, i*ARG_SIZE, sparc_l0); sparc_st_imm (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; sparc_ld_imm (p, ARG_BASE, (i*ARG_SIZE) + 4, sparc_l0); sparc_st_imm (p, sparc_l0, sparc_sp, stack_par_pos); stack_par_pos += SLOT_SIZE; } break; #endif default: g_error ("Can't trampoline 0x%x", sig->params[i]->type); } } g_assert ((stack_par_pos - BIAS) <= stack_size); return p; } static inline guint32 * alloc_code_memory (guint code_size) { guint32 *p; p = g_malloc(code_size); return p; } static inline guint32 * emit_call_and_store_retval (guint32 *p, MonoMethodSignature *sig, guint stack_size, gboolean string_ctor) { guint32 simpletype; /* call "callme" */ sparc_jmpl_imm (p, sparc_i0, 0, sparc_callsite); sparc_nop (p); #if !SPARCV9 if (sig->ret->type == MONO_TYPE_VALUETYPE && !sig->ret->data.klass->enumtype) { int size = mono_class_native_size (sig->ret->data.klass, NULL); sparc_unimp (p, size & 4095); } #endif /* get return value */ if (sig->ret->byref || string_ctor) { sparc_st_ptr (p, sparc_o0, sparc_i1, 0); } else { simpletype = sig->ret->type; enum_retval: switch (simpletype) { case MONO_TYPE_BOOLEAN: case MONO_TYPE_I1: case MONO_TYPE_U1: sparc_stb (p, sparc_o0, sparc_i1, 0); break; case MONO_TYPE_CHAR: case MONO_TYPE_I2: case MONO_TYPE_U2: sparc_sth (p, sparc_o0, sparc_i1, 0); break; case MONO_TYPE_I4: case MONO_TYPE_U4: sparc_st (p, sparc_o0, sparc_i1, 0); break; case MONO_TYPE_I: case MONO_TYPE_U: case MONO_TYPE_CLASS: case MONO_TYPE_OBJECT: case MONO_TYPE_SZARRAY: case MONO_TYPE_ARRAY: case MONO_TYPE_STRING: case MONO_TYPE_PTR: sparc_st_ptr (p, sparc_o0, sparc_i1, 0); break; case MONO_TYPE_R4: sparc_stf (p, sparc_f0, sparc_i1, 0); break; case MONO_TYPE_R8: sparc_stdf (p, sparc_f0, sparc_i1, 0); break; case MONO_TYPE_I8: #if SPARCV9 sparc_stx (p, sparc_o0, sparc_i1, 0); #else sparc_std (p, sparc_o0, sparc_i1, 0); #endif break; case MONO_TYPE_VALUETYPE: { gint size; if (sig->ret->data.klass->enumtype) { simpletype = sig->ret->data.klass->enum_basetype->type; goto enum_retval; } #if SPARCV9 size = mono_class_native_size (sig->ret->data.klass, NULL); if (size <= 32) { int n_regs = size / 8; int j; sparc_ldx_imm (p, sparc_i1, 0, sparc_i1); /* wrong if there are floating values in the struct... */ for (j = 0; j < n_regs; j++) { sparc_stx_imm (p, sparc_o0 + j, sparc_i1, j * 8); } size -= n_regs * 8; if (size > 0) { int last_reg = sparc_o0 + n_regs; /* get value right aligned in register */ sparc_srlx_imm(p, last_reg, 64 - 8 * size, last_reg); if ((size & 1) != 0) { sparc_stb_imm (p, last_reg, sparc_i1, n_regs * 8 + size - 1); size--; if (size > 0) sparc_srlx_imm(p, last_reg, 8, last_reg); } if ((size & 2) != 0) { sparc_sth_imm (p, last_reg, sparc_i1, n_regs * 8 + size - 2); size -= 2; if (size > 0) sparc_srlx_imm(p, last_reg, 16, last_reg); } if ((size & 4) != 0) sparc_st_imm (p, last_reg, sparc_i1, n_regs * 8); } } #endif } case MONO_TYPE_VOID: break; default: g_error ("Can't handle as return value 0x%x", sig->ret->type); } } return p; }
/* * mono_arch_get_call_filter: * * Returns a pointer to a method which calls an exception filter. We * also use this function to call finally handlers (we pass NULL as * @exc object in this case). * * call_filter (MonoContext *ctx, gpointer ip) */ gpointer mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot) { static guint32 *start; static int inited = 0; guint32 *code; int i; g_assert (!aot); if (info) *info = NULL; if (inited) return start; code = start = mono_global_codeman_reserve (64 * sizeof (guint32)); /* * There are two frames here: * - the first frame is used by call_filter * - the second frame is used to run the filter code */ /* Create first frame */ sparc_save_imm (code, sparc_sp, -256, sparc_sp); sparc_mov_reg_reg (code, sparc_i1, sparc_o0); sparc_ldi_imm (code, sparc_i0, G_STRUCT_OFFSET (MonoContext, sp), sparc_o1); /* Create second frame */ sparc_save_imm (code, sparc_sp, -256, sparc_sp); sparc_mov_reg_reg (code, sparc_i0, sparc_o0); sparc_mov_reg_reg (code, sparc_i1, sparc_o1); /* * We need to change %fp to point to the stack frame of the method * containing the filter. But changing %fp also changes the %sp of * the parent frame (the first frame), so if the OS saves the first frame, * it saves it to the stack frame of the method, which is not good. * So flush all register windows to memory before changing %fp. */ sparc_flushw (code); sparc_mov_reg_reg (code, sparc_fp, sparc_o7); /* * Modify the second frame so it is identical to the one used in the * method containing the filter. */ for (i = 0; i < 16; ++i) sparc_ldi_imm (code, sparc_o1, MONO_SPARC_STACK_BIAS + i * sizeof (gpointer), sparc_l0 + i); /* Save %fp to a location reserved in mono_arch_allocate_vars */ sparc_sti_imm (code, sparc_o7, sparc_fp, MONO_SPARC_STACK_BIAS - sizeof (gpointer)); /* Call the filter code, after this returns, %o0 will hold the result */ sparc_call_imm (code, sparc_o0, 0); sparc_nop (code); /* Restore original %fp */ sparc_ldi_imm (code, sparc_fp, MONO_SPARC_STACK_BIAS - sizeof (gpointer), sparc_fp); sparc_mov_reg_reg (code, sparc_o0, sparc_i0); /* Return to first frame */ sparc_restore (code, sparc_g0, sparc_g0, sparc_g0); /* FIXME: Save locals to the stack */ /* Return to caller */ sparc_ret (code); /* Return result in delay slot */ sparc_restore (code, sparc_o0, sparc_g0, sparc_o0); g_assert ((code - start) < 64); mono_arch_flush_icache ((guint8*)start, (guint8*)code - (guint8*)start); inited = 1; return start; }
guchar* mono_arch_create_generic_trampoline (MonoTrampolineType tramp_type, MonoTrampInfo **info, gboolean aot) { guint8 *buf, *code, *tramp_addr; guint32 lmf_offset, regs_offset, method_reg, i; gboolean has_caller; g_assert (!aot); *info = NULL; if (tramp_type == MONO_TRAMPOLINE_JUMP) has_caller = FALSE; else has_caller = TRUE; code = buf = mono_global_codeman_reserve (1024); sparc_save_imm (code, sparc_sp, -1608, sparc_sp); #ifdef SPARCV9 method_reg = sparc_g4; #else method_reg = sparc_g1; #endif regs_offset = MONO_SPARC_STACK_BIAS + 1000; /* Save r1 needed by the IMT code */ sparc_sti_imm (code, sparc_g1, sparc_sp, regs_offset + (sparc_g1 * sizeof (gpointer))); /* * sparc_g5 contains the return address, the trampoline argument is stored in the * instruction stream after the call. */ sparc_ld_imm (code, sparc_g5, 8, method_reg); #ifdef SPARCV9 /* Save fp regs since they are not preserved by calls */ for (i = 0; i < 16; i ++) sparc_stdf_imm (code, sparc_f0 + (i * 2), sparc_sp, MONO_SPARC_STACK_BIAS + 320 + (i * 8)); #endif /* We receive the method address in %r1, so save it here */ sparc_sti_imm (code, method_reg, sparc_sp, MONO_SPARC_STACK_BIAS + 200); /* Save lmf since compilation can raise exceptions */ lmf_offset = MONO_SPARC_STACK_BIAS - sizeof (MonoLMF); /* Save the data for the parent (managed) frame */ /* Save ip */ sparc_sti_imm (code, sparc_i7, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ip)); /* Save sp */ sparc_sti_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_ldi_imm (code, sparc_fp, MONO_SPARC_STACK_BIAS + (sparc_i6 - 16) * sizeof (gpointer), sparc_o7); sparc_sti_imm (code, sparc_o7, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, ebp)); /* Save method */ sparc_sti_imm (code, method_reg, sparc_fp, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method)); sparc_set (code, mono_get_lmf_addr, sparc_o7); sparc_jmpl (code, sparc_o7, sparc_g0, sparc_o7); sparc_nop (code); code = mono_sparc_emit_save_lmf (code, lmf_offset); if (has_caller) { /* Load all registers of the caller into a table inside this frame */ /* first the out registers */ for (i = 0; i < 8; ++i) sparc_sti_imm (code, sparc_i0 + i, sparc_sp, regs_offset + ((sparc_o0 + i) * sizeof (gpointer))); /* then the in+local registers */ for (i = 0; i < 16; i ++) { sparc_ldi_imm (code, sparc_fp, MONO_SPARC_STACK_BIAS + (i * sizeof (gpointer)), sparc_o7); sparc_sti_imm (code, sparc_o7, sparc_sp, regs_offset + ((sparc_l0 + i) * sizeof (gpointer))); } } tramp_addr = mono_get_trampoline_func (tramp_type); sparc_ldi_imm (code, sparc_sp, MONO_SPARC_STACK_BIAS + 200, sparc_o2); /* pass address of register table as third argument */ sparc_add_imm (code, FALSE, sparc_sp, regs_offset, sparc_o0); sparc_set (code, tramp_addr, sparc_o7); /* set %o1 to caller address */ if (has_caller) sparc_mov_reg_reg (code, sparc_i7, sparc_o1); else sparc_set (code, 0, sparc_o1); sparc_set (code, 0, sparc_o3); sparc_jmpl (code, sparc_o7, sparc_g0, sparc_o7); sparc_nop (code); /* Save result */ sparc_sti_imm (code, sparc_o0, sparc_sp, MONO_SPARC_STACK_BIAS + 304); /* Check for thread interruption */ sparc_set (code, (guint8*)mono_thread_force_interruption_checkpoint, sparc_o7); sparc_jmpl (code, sparc_o7, sparc_g0, sparc_o7); sparc_nop (code); /* Restore lmf */ code = mono_sparc_emit_restore_lmf (code, lmf_offset); /* Reload result */ sparc_ldi_imm (code, sparc_sp, MONO_SPARC_STACK_BIAS + 304, sparc_o0); #ifdef SPARCV9 /* Reload fp regs */ for (i = 0; i < 16; i ++) sparc_lddf_imm (code, sparc_sp, MONO_SPARC_STACK_BIAS + 320 + (i * 8), sparc_f0 + (i * 2)); #endif sparc_jmpl (code, sparc_o0, sparc_g0, sparc_g0); /* restore previous frame in delay slot */ sparc_restore_simple (code); /* { gpointer addr; sparc_save_imm (code, sparc_sp, -608, sparc_sp); addr = code; sparc_call_simple (code, 16); sparc_nop (code); sparc_rett_simple (code); sparc_nop (code); sparc_save_imm (code, sparc_sp, -608, sparc_sp); sparc_ta (code, 1); tramp_addr = &sparc_magic_trampoline; sparc_call_simple (code, tramp_addr - code); sparc_nop (code); sparc_rett_simple (code); sparc_nop (code); } */ g_assert ((code - buf) <= 512); mono_arch_flush_icache (buf, code - buf); return buf; }