Esempio n. 1
0
gpointer
mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
{
    guint8 *start, *code;
    MonoJumpInfo *ji = NULL;
    GSList *unwind_ops = NULL;
    int i, ctx_reg, size;
    guint8 *labels [16];

    size = 256;
    code = start = mono_global_codeman_reserve (size);

    arm_movx (code, ARMREG_IP0, ARMREG_R0);
    ctx_reg = ARMREG_IP0;

    /* Restore fregs */
    arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, has_fregs));
    labels [0] = code;
    arm_cbzx (code, ARMREG_IP1, 0);
    for (i = 0; i < 32; ++i)
        arm_ldrfpx (code, i, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8));
    mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ);
    /* Restore gregs */
    // FIXME: Restore less registers
    // FIXME: fp should be restored later
    code = mono_arm_emit_load_regarray (code, 0xffffffff & ~(1 << ctx_reg) & ~(1 << ARMREG_SP), ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs));
    /* ip0/ip1 doesn't need to be restored */
    /* ip1 = pc */
    arm_ldrx (code, ARMREG_IP1, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc));
    /* ip0 = sp */
    arm_ldrx (code, ARMREG_IP0, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_SP * 8));
    /* Restore sp, ctx is no longer valid */
    arm_movspx (code, ARMREG_SP, ARMREG_IP0);
    /* Branch to pc */
    arm_brx (code, ARMREG_IP1);
    /* Not reached */
    arm_brk (code, 0);

    g_assert ((code - start) < size);
    mono_arch_flush_icache (start, code - start);
    mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

    if (info)
        *info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);

    return start;
}
Esempio n. 2
0
/*
 * arch_get_restore_context:
 *
 * Returns a pointer to a method which restores a previously saved sigcontext.
 * The first argument in r0 is the pointer to the context.
 */
gpointer
mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
{
	guint8 *code;
	guint8 *start;
	int ctx_reg;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	start = code = mono_global_codeman_reserve (128);

	/* 
	 * Move things to their proper place so we can restore all the registers with
	 * one instruction.
	 */

	ctx_reg = ARMREG_R0;

	if (!mono_arch_is_soft_float ()) {
		ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, fregs));
		ARM_FLDMD (code, ARM_VFP_D0, 16, ARMREG_IP);
	}

	/* move pc to PC */
	ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc));
	ARM_STR_IMM (code, ARMREG_IP, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_PC * sizeof (mgreg_t)));

	/* restore everything */
	ARM_ADD_REG_IMM8 (code, ARMREG_IP, ctx_reg, MONO_STRUCT_OFFSET(MonoContext, regs));
	ARM_LDM (code, ARMREG_IP, 0xffff);

	/* never reached */
	ARM_DBRK (code);

	g_assert ((code - start) < 128);

	mono_arch_flush_icache (start, code - start);

	if (info)
		*info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 3
0
/*
 * 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).
 */
gpointer
mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
{
	guint8 *code;
	guint8* start;
	int ctx_reg;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	/* call_filter (MonoContext *ctx, unsigned long eip, gpointer exc) */
	start = code = mono_global_codeman_reserve (320);

	/* save all the regs on the stack */
	ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_SP);
	ARM_PUSH (code, MONO_ARM_REGSAVE_MASK);

	ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);

	/* restore all the regs from ctx (in r0), but not sp, the stack pointer */
	ctx_reg = ARMREG_R0;
	ARM_LDR_IMM (code, ARMREG_IP, ctx_reg, MONO_STRUCT_OFFSET (MonoContext, pc));
	ARM_ADD_REG_IMM8 (code, ARMREG_LR, ctx_reg, MONO_STRUCT_OFFSET(MonoContext, regs) + (MONO_ARM_FIRST_SAVED_REG * sizeof (mgreg_t)));
	ARM_LDM (code, ARMREG_LR, MONO_ARM_REGSAVE_MASK);
	/* call handler at eip (r1) and set the first arg with the exception (r2) */
	ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R2);
	ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
	ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);

	ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 8);

	/* epilog */
	ARM_POP_NWB (code, 0xff0 | ((1 << ARMREG_SP) | (1 << ARMREG_PC)));

	g_assert ((code - start) < 320);

	mono_arch_flush_icache (start, code - start);

	if (info)
		*info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 4
0
/*
 * mono_arch_get_restore_context:
 *
 * Returns a pointer to a method which restores a previously saved sigcontext.
 */
gpointer
mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
{
	guint8 *start = NULL;
	guint8 *code;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;
	int i, gregs_offset;

	/* restore_contect (MonoContext *ctx) */

	start = code = (guint8 *)mono_global_codeman_reserve (256);

	amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8);

	/* Restore all registers except %rip and %r11 */
	gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs);
	for (i = 0; i < AMD64_NREG; ++i) {
#if defined(__native_client_codegen__)
		if (i == AMD64_R15)
			continue;
#endif
		if (i != AMD64_RIP && i != AMD64_RSP && i != AMD64_R8 && i != AMD64_R9 && i != AMD64_R10 && i != AMD64_R11)
			amd64_mov_reg_membase (code, i, AMD64_R11, gregs_offset + (i * 8), 8);
	}

	/*
	 * The context resides on the stack, in the stack frame of the
	 * caller of this function.  The stack pointer that we need to
	 * restore is potentially many stack frames higher up, so the
	 * distance between them can easily be more than the red zone
	 * size.  Hence the stack pointer can be restored only after
	 * we have finished loading everything from the context.
	 */
	amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  gregs_offset + (AMD64_RSP * 8), 8);
	amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11,  gregs_offset + (AMD64_RIP * 8), 8);
	amd64_mov_reg_reg (code, AMD64_RSP, AMD64_R8, 8);

	/* jump to the saved IP */
	amd64_jump_reg (code, AMD64_R11);

	nacl_global_codeman_validate (&start, 256, &code);

	mono_arch_flush_icache (start, code - start);
	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

	if (info)
		*info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 5
0
MonoContinuationRestore
mono_tasklets_arch_restore (void)
{
	static guint8* saved = NULL;
	guint8 *code, *start;

#ifdef __native_client_codegen__
	g_print("mono_tasklets_arch_restore needs to be aligned for Native Client\n");
#endif
	if (saved)
		return (MonoContinuationRestore)saved;
	code = start = mono_global_codeman_reserve (48);
	/* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
	/* put cont in edx */
	x86_mov_reg_membase (code, X86_EDX, X86_ESP, 4, 4);
        /* state in eax, so it's setup as the return value */
        x86_mov_reg_membase (code, X86_EAX, X86_ESP, 8, 4);

	/* setup the copy of the stack */
	x86_mov_reg_membase (code, X86_ECX, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, stack_used_size), 4);
	x86_shift_reg_imm (code, X86_SHR, X86_ECX, 2);
	x86_cld (code);
	x86_mov_reg_membase (code, X86_ESI, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, saved_stack), 4);
	x86_mov_reg_membase (code, X86_EDI, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, return_sp), 4);
	x86_prefix (code, X86_REP_PREFIX);
	x86_movsl (code);

	/* now restore the registers from the LMF */
	x86_mov_reg_membase (code, X86_ECX, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, lmf), 4);
	x86_mov_reg_membase (code, X86_EBX, X86_ECX, MONO_STRUCT_OFFSET (MonoLMF, ebx), 4);
	x86_mov_reg_membase (code, X86_EBP, X86_ECX, MONO_STRUCT_OFFSET (MonoLMF, ebp), 4);
	x86_mov_reg_membase (code, X86_ESI, X86_ECX, MONO_STRUCT_OFFSET (MonoLMF, esi), 4);
	x86_mov_reg_membase (code, X86_EDI, X86_ECX, MONO_STRUCT_OFFSET (MonoLMF, edi), 4);

	/* restore the lmf chain */
	/*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4);
	x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/

	x86_jump_membase (code, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, return_ip));
	g_assert ((code - start) <= 48);
	saved = start;
	return (MonoContinuationRestore)saved;
}
Esempio n. 6
0
MonoContinuationRestore
mono_tasklets_arch_restore (void)
{
	static guint8* saved = NULL;
	guint8 *code, *start;
	int cont_reg = AMD64_R9; /* register usable on both call conventions */
	const guint kMaxCodeSize = 64;
	

	if (saved)
		return (MonoContinuationRestore)saved;
	code = start = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);
	/* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
	/* cont is in AMD64_ARG_REG1 ($rcx or $rdi)
	 * state is in AMD64_ARG_REG2 ($rdx or $rsi)
	 * lmf_addr is in AMD64_ARG_REG3 ($r8 or $rdx)
	 * We move cont to cont_reg since we need both rcx and rdi for the copy
	 * state is moved to $rax so it's setup as the return value and we can overwrite $rsi
 	 */
	amd64_mov_reg_reg (code, cont_reg, MONO_AMD64_ARG_REG1, 8);
	amd64_mov_reg_reg (code, AMD64_RAX, MONO_AMD64_ARG_REG2, 8);
	/* setup the copy of the stack */
	amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, stack_used_size), sizeof (int));
	amd64_shift_reg_imm (code, X86_SHR, AMD64_RCX, 3);
	x86_cld (code);
	amd64_mov_reg_membase (code, AMD64_RSI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, saved_stack), sizeof (gpointer));
	amd64_mov_reg_membase (code, AMD64_RDI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_sp), sizeof (gpointer));
	amd64_prefix (code, X86_REP_PREFIX);
	amd64_movsl (code);

	/* now restore the registers from the LMF */
	amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, lmf), 8);
	amd64_mov_reg_membase (code, AMD64_RBP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rbp), 8);
	amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rsp), 8);

#ifdef WIN32
	amd64_mov_reg_reg (code, AMD64_R14, AMD64_ARG_REG3, 8);
#else
	amd64_mov_reg_reg (code, AMD64_R12, AMD64_ARG_REG3, 8);
#endif

	/* state is already in rax */
	amd64_jump_membase (code, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_ip));
	g_assert ((code - start) <= kMaxCodeSize);

	mono_arch_flush_icache (start, code - start);
	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

	saved = start;
	return (MonoContinuationRestore)saved;
}
Esempio n. 7
0
MonoContinuationRestore
mono_tasklets_arch_restore (void)
{
	static guint8* saved = NULL;
	guint8 *code, *start;

	if (saved)
		return (MonoContinuationRestore)saved;
	code = start = mono_global_codeman_reserve (48);
	/* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
	/* put cont in edx */
	x86_mov_reg_membase (code, X86_EDX, X86_ESP, 4, 4);
	/* state in eax, so it's setup as the return value */
	x86_mov_reg_membase (code, X86_EAX, X86_ESP, 8, 4);
	/* lmf_addr in ebx */
	x86_mov_reg_membase(code, X86_EBX, X86_ESP, 0x0C, 4);

	/* setup the copy of the stack */
	x86_mov_reg_membase (code, X86_ECX, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, stack_used_size), 4);
	x86_shift_reg_imm (code, X86_SHR, X86_ECX, 2);
	x86_cld (code);
	x86_mov_reg_membase (code, X86_ESI, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, saved_stack), 4);
	x86_mov_reg_membase (code, X86_EDI, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, return_sp), 4);
	x86_prefix (code, X86_REP_PREFIX);
	x86_movsl (code);

	/* now restore the registers from the LMF */
	x86_mov_reg_membase (code, X86_ECX, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, lmf), 4);
	x86_mov_reg_membase (code, X86_EBP, X86_ECX, MONO_STRUCT_OFFSET (MonoLMF, ebp), 4);
	x86_mov_reg_membase (code, X86_ESP, X86_ECX, MONO_STRUCT_OFFSET (MonoLMF, esp), 4);

	/* restore the lmf chain */
	/*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4);
	x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/

	x86_jump_membase (code, X86_EDX, MONO_STRUCT_OFFSET (MonoContinuation, return_ip));

	mono_arch_flush_icache (start, code - start);
	MONO_PROFILER_RAISE (jit_code_buffer, (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL));
	g_assert ((code - start) <= 48);

	saved = start;
	return (MonoContinuationRestore)saved;
}
Esempio n. 8
0
/*
 * mono_win32_get_handle_stackoverflow (void):
 *
 * Returns a pointer to a method which restores the current context stack
 * and calls handle_exceptions, when done restores the original stack.
 */
static gpointer
mono_win32_get_handle_stackoverflow (void)
{
	static guint8 *start = NULL;
	guint8 *code;

	if (start)
		return start;

	/* restore_contect (void *sigctx) */
	start = code = mono_global_codeman_reserve (128);

	/* load context into ebx */
	x86_mov_reg_membase (code, X86_EBX, X86_ESP, 4, 4);

	/* move current stack into edi for later restore */
	x86_mov_reg_reg (code, X86_EDI, X86_ESP, 4);

	/* use the new freed stack from sigcontext */
	/* XXX replace usage of struct sigcontext with MonoContext so we can use MONO_STRUCT_OFFSET */
	x86_mov_reg_membase (code, X86_ESP, X86_EBX,  G_STRUCT_OFFSET (struct sigcontext, esp), 4);

	/* get the current domain */
	x86_call_code (code, mono_domain_get);

	/* get stack overflow exception from domain object */
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (MonoDomain, stack_overflow_ex), 4);

	/* call mono_arch_handle_exception (sctx, stack_overflow_exception_obj) */
	x86_push_reg (code, X86_EAX);
	x86_push_reg (code, X86_EBX);
	x86_call_code (code, mono_arch_handle_exception);

	/* restore the SEH handler stack */
	x86_mov_reg_reg (code, X86_ESP, X86_EDI, 4);

	/* return */
	x86_ret (code);

	return start;
}
Esempio n. 9
0
MonoContinuationRestore
mono_tasklets_arch_restore (void)
{
	static guint8* saved = NULL;
	guint8 *code, *start;
	int cont_reg = AMD64_R9; /* register usable on both call conventions */
	const guint kMaxCodeSize = NACL_SIZE (64, 128);
	

	if (saved)
		return (MonoContinuationRestore)saved;
	code = start = mono_global_codeman_reserve (kMaxCodeSize);
	/* the signature is: restore (MonoContinuation *cont, int state, MonoLMF **lmf_addr) */
	/* cont is in AMD64_ARG_REG1 ($rcx or $rdi)
	 * state is in AMD64_ARG_REG2 ($rdx or $rsi)
	 * lmf_addr is in AMD64_ARG_REG3 ($r8 or $rdx)
	 * We move cont to cont_reg since we need both rcx and rdi for the copy
	 * state is moved to $rax so it's setup as the return value and we can overwrite $rsi
 	 */
	amd64_mov_reg_reg (code, cont_reg, MONO_AMD64_ARG_REG1, 8);
	amd64_mov_reg_reg (code, AMD64_RAX, MONO_AMD64_ARG_REG2, 8);
	/* setup the copy of the stack */
	amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, stack_used_size), sizeof (int));
	amd64_shift_reg_imm (code, X86_SHR, AMD64_RCX, 3);
	x86_cld (code);
	amd64_mov_reg_membase (code, AMD64_RSI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, saved_stack), sizeof (gpointer));
	amd64_mov_reg_membase (code, AMD64_RDI, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_sp), sizeof (gpointer));
	amd64_prefix (code, X86_REP_PREFIX);
	amd64_movsl (code);

	/* now restore the registers from the LMF */
	NOT_IMPLEMENTED;
	amd64_mov_reg_membase (code, AMD64_RCX, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, lmf), 8);
	amd64_mov_reg_membase (code, AMD64_RSP, AMD64_RCX, MONO_STRUCT_OFFSET (MonoLMF, rsp), 8);

	/* restore the lmf chain */
	/*x86_mov_reg_membase (code, X86_ECX, X86_ESP, 12, 4);
	x86_mov_membase_reg (code, X86_ECX, 0, X86_EDX, 4);*/

	/* state is already in rax */
	amd64_jump_membase (code, cont_reg, MONO_STRUCT_OFFSET (MonoContinuation, return_ip));
	g_assert ((code - start) <= kMaxCodeSize);

	nacl_global_codeman_validate(&start, kMaxCodeSize, &code);

	saved = start;
	return (MonoContinuationRestore)saved;
}
Esempio n. 10
0
gpointer
mono_arch_create_monitor_enter_trampoline (MonoTrampInfo **info, gboolean is_v4, gboolean aot)
{
	guint8	*tramp,
		*code, *buf;
	gint16	*jump_obj_null, 
		*jump_sync_null, 
		*jump_cs_failed, 
		*jump_other_owner, 
		*jump_tid, 
		*jump_sync_thin_hash = NULL,
		*jump_lock_taken_true = NULL;
	int tramp_size,
	    status_reg = s390_r0,
	    lock_taken_reg = s390_r1,
	    obj_reg = s390_r2,
	    sync_reg = s390_r3,
	    tid_reg = s390_r4,
	    status_offset,
	    nest_offset;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	g_assert (MONO_ARCH_MONITOR_OBJECT_REG == obj_reg);
#ifdef MONO_ARCH_MONITOR_LOCK_TAKEN_REG
	g_assert (MONO_ARCH_MONITOR_LOCK_TAKEN_REG == lock_taken_reg);
#else
	g_assert (!is_v4);
#endif

	mono_monitor_threads_sync_members_offset (&status_offset, &nest_offset);
	g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (status_offset) == sizeof (guint32));
	g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
	status_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (status_offset);
	nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);

	tramp_size = 160;

	code = buf = mono_global_codeman_reserve (tramp_size);

	unwind_ops = mono_arch_get_cie_program ();

	if (mono_thread_get_tls_offset () != -1) {
		/* MonoObject* obj is in obj_reg */
		/* is obj null? */
		s390_ltgr (code, obj_reg, obj_reg);
		/* if yes, jump to actual trampoline */
		s390_jz (code, 0); CODEPTR(code, jump_obj_null);

		if (is_v4) {
			s390_cli (code, lock_taken_reg, 0, 1);
			/* if *lock_taken is 1, jump to actual trampoline */
			s390_je (code, 0); CODEPTR(code, jump_lock_taken_true);
		}

		/* load obj->synchronization to sync_reg */
		s390_lg (code, sync_reg, 0, obj_reg, MONO_STRUCT_OFFSET (MonoObject, synchronisation));

		if (mono_gc_is_moving ()) {
			/*if bit zero is set it's a thin hash*/
			s390_tmll (code, sync_reg, 1);
			s390_jo  (code, 0); CODEPTR(code, jump_sync_thin_hash);

			/* Clear bits used by the gc */
			s390_nill (code, sync_reg, ~0x3);
		}

		/* is synchronization null? */
		s390_ltgr (code, sync_reg, sync_reg);
		/* if yes, jump to actual trampoline */
		s390_jz (code, 0); CODEPTR(code, jump_sync_null);

		/* load MonoInternalThread* into tid_reg */
		s390_ear (code, s390_r5, 0);
		s390_sllg(code, s390_r5, s390_r5, 0, 32);
		s390_ear (code, s390_r5, 1);
		/* load tid */
		s390_lg  (code, tid_reg, 0, s390_r5, mono_thread_get_tls_offset ());
		s390_lgf (code, tid_reg, 0, tid_reg, MONO_STRUCT_OFFSET (MonoInternalThread, small_id));

		/* is synchronization->owner free */
		s390_lgf  (code, status_reg, 0, sync_reg, status_offset);
		s390_nilf (code, status_reg, OWNER_MASK);
		/* if not, jump to next case */
		s390_jnz  (code, 0); CODEPTR(code, jump_tid);

		/* if yes, try a compare-exchange with the TID */
		/* Form new status in tid_reg */
		s390_xr (code, tid_reg, status_reg);
		/* compare and exchange */
		s390_cs (code, status_reg, tid_reg, sync_reg, status_offset);
		s390_jnz (code, 0); CODEPTR(code, jump_cs_failed);
		/* if successful, return */
		if (is_v4)
			s390_mvi (code, lock_taken_reg, 0, 1);
		s390_br (code, s390_r14);

		/* next case: synchronization->owner is not null */
		PTRSLOT(code, jump_tid);
		/* is synchronization->owner == TID? */
		s390_nilf (code, status_reg, OWNER_MASK);
		s390_cr (code, status_reg, tid_reg);
		/* if not, jump to actual trampoline */
		s390_jnz (code, 0); CODEPTR(code, jump_other_owner);
		/* if yes, increment nest */
		s390_lgf (code, s390_r5, 0, sync_reg, nest_offset);
		s390_ahi (code, s390_r5, 1);
		s390_st  (code, s390_r5, 0, sync_reg, nest_offset);
		/* return */
		if (is_v4)
			s390_mvi (code, lock_taken_reg, 0, 1);
		s390_br (code, s390_r14);

		PTRSLOT (code, jump_obj_null);
		if (jump_sync_thin_hash)
			PTRSLOT (code, jump_sync_thin_hash);
		PTRSLOT (code, jump_sync_null);
		PTRSLOT (code, jump_cs_failed);
		PTRSLOT (code, jump_other_owner);
		if (is_v4)
			PTRSLOT (code, jump_lock_taken_true);
	}

	/* jump to the actual trampoline */
	if (is_v4)
		tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_MONITOR_ENTER_V4, mono_get_root_domain (), NULL);
	else
		tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_MONITOR_ENTER, mono_get_root_domain (), NULL);

	/* jump to the actual trampoline */
	S390_SET (code, s390_r1, tramp);
	s390_br (code, s390_r1);

	mono_arch_flush_icache (code, code - buf);
	mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_MONITOR, NULL);
	g_assert (code - buf <= tramp_size);

	if (info) {
		if (is_v4)
			*info = mono_tramp_info_create ("monitor_enter_v4_trampoline", buf, code - buf, ji, unwind_ops);
		else
			*info = mono_tramp_info_create ("monitor_enter_trampoline", buf, code - buf, ji, unwind_ops);
	}

	return buf;
}
Esempio n. 11
0
gpointer
mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
{
	guint8 *tramp;
	guint8 *code, *buf;
	guint8 **rgctx_null_jumps;
	int tramp_size;
	int depth, index;
	int i;
	gboolean mrgctx;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	unwind_ops = mono_arch_get_cie_program ();

	mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
	index = MONO_RGCTX_SLOT_INDEX (slot);
	if (mrgctx)
		index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (target_mgreg_t);
	for (depth = 0; ; ++depth) {
		int size = mono_class_rgctx_get_array_size (depth, mrgctx);

		if (index < size - 1)
			break;
		index -= size - 1;
	}

	tramp_size = (aot ? 64 : 36) + 6 * depth;

	code = buf = mono_global_codeman_reserve (tramp_size);

	rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));

	/* load vtable/mrgctx ptr */
	x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);
	if (!mrgctx) {
		/* load rgctx ptr from vtable */
		x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context), 4);
		/* is the rgctx ptr null? */
		x86_test_reg_reg (code, X86_EAX, X86_EAX);
		/* if yes, jump to actual trampoline */
		rgctx_null_jumps [0] = code;
		x86_branch8 (code, X86_CC_Z, -1, 1);
	}

	for (i = 0; i < depth; ++i) {
		/* load ptr to next array */
		if (mrgctx && i == 0)
			x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT, 4);
		else
			x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, 4);
		/* is the ptr null? */
		x86_test_reg_reg (code, X86_EAX, X86_EAX);
		/* if yes, jump to actual trampoline */
		rgctx_null_jumps [i + 1] = code;
		x86_branch8 (code, X86_CC_Z, -1, 1);
	}

	/* fetch slot */
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, sizeof (target_mgreg_t) * (index + 1), 4);
	/* is the slot null? */
	x86_test_reg_reg (code, X86_EAX, X86_EAX);
	/* if yes, jump to actual trampoline */
	rgctx_null_jumps [depth + 1] = code;
	x86_branch8 (code, X86_CC_Z, -1, 1);
	/* otherwise return */
	x86_ret (code);

	for (i = mrgctx ? 1 : 0; i <= depth + 1; ++i)
		x86_patch (rgctx_null_jumps [i], code);

	g_free (rgctx_null_jumps);

	x86_mov_reg_membase (code, MONO_ARCH_VTABLE_REG, X86_ESP, 4, 4);

	if (aot) {
		code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_SPECIFIC_TRAMPOLINE_LAZY_FETCH_ADDR, GUINT_TO_POINTER (slot));
		x86_jump_reg (code, X86_EAX);
	} else {
		tramp = (guint8*)mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);

		/* jump to the actual trampoline */
		x86_jump_code (code, tramp);
	}

	mono_arch_flush_icache (buf, code - buf);
	MONO_PROFILER_RAISE (jit_code_buffer, (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL));

	g_assert (code - buf <= tramp_size);

	char *name = mono_get_rgctx_fetch_trampoline_name (slot);
	*info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
	g_free (name);

	return buf;
}
Esempio n. 12
0
/*
 * 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).
 */
gpointer
mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
{
	guint8* start;
	guint8 *code;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;
	guint kMaxCodeSize = 64;

	/* call_filter (MonoContext *ctx, unsigned long eip) */
	start = code = mono_global_codeman_reserve (kMaxCodeSize);

	x86_push_reg (code, X86_EBP);
	x86_mov_reg_reg (code, X86_EBP, X86_ESP, 4);
	x86_push_reg (code, X86_EBX);
	x86_push_reg (code, X86_EDI);
	x86_push_reg (code, X86_ESI);

	/* load ctx */
	x86_mov_reg_membase (code, X86_EAX, X86_EBP, 8, 4);
	/* load eip */
	x86_mov_reg_membase (code, X86_ECX, X86_EBP, 12, 4);
	/* save EBP */
	x86_push_reg (code, X86_EBP);

	/* set new EBP */
	x86_mov_reg_membase (code, X86_EBP, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, ebp), 4);
	/* restore registers used by global register allocation (EBX & ESI) */
	x86_mov_reg_membase (code, X86_EBX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, ebx), 4);
	x86_mov_reg_membase (code, X86_ESI, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, esi), 4);
	x86_mov_reg_membase (code, X86_EDI, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, edi), 4);

	/* align stack and save ESP */
	x86_mov_reg_reg (code, X86_EDX, X86_ESP, 4);
	x86_alu_reg_imm (code, X86_AND, X86_ESP, -MONO_ARCH_FRAME_ALIGNMENT);
	g_assert (MONO_ARCH_FRAME_ALIGNMENT >= 8);
	x86_alu_reg_imm (code, X86_SUB, X86_ESP, MONO_ARCH_FRAME_ALIGNMENT - 8);
	x86_push_reg (code, X86_EDX);

	/* call the handler */
	x86_call_reg (code, X86_ECX);

	/* restore ESP */
	x86_pop_reg (code, X86_ESP);

	/* restore EBP */
	x86_pop_reg (code, X86_EBP);

	/* restore saved regs */
	x86_pop_reg (code, X86_ESI);
	x86_pop_reg (code, X86_EDI);
	x86_pop_reg (code, X86_EBX);
	x86_leave (code);
	x86_ret (code);

	if (info)
		*info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);
	else {
		GSList *l;

		for (l = unwind_ops; l; l = l->next)
			g_free (l->data);
		g_slist_free (unwind_ops);
	}

	mono_arch_flush_icache (start, code - start);
	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

	g_assert ((code - start) < kMaxCodeSize);
	return start;
}
Esempio n. 13
0
/*
 * mono_arch_get_restore_context:
 *
 * Returns a pointer to a method which restores a previously saved sigcontext.
 */
gpointer
mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
{
	guint8 *start = NULL;
	guint8 *code;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	/* restore_contect (MonoContext *ctx) */

	start = code = mono_global_codeman_reserve (128);
	
	/* load ctx */
	x86_mov_reg_membase (code, X86_EAX, X86_ESP, 4, 4);

	/* restore EBX */
	x86_mov_reg_membase (code, X86_EBX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, ebx), 4);

	/* restore EDI */
	x86_mov_reg_membase (code, X86_EDI, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, edi), 4);

	/* restore ESI */
	x86_mov_reg_membase (code, X86_ESI, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, esi), 4);

	/* restore EDX */
	x86_mov_reg_membase (code, X86_EDX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, edx), 4);

	/*
	 * The context resides on the stack, in the stack frame of the
	 * caller of this function.  The stack pointer that we need to
	 * restore is potentially many stack frames higher up, so the
	 * distance between them can easily be more than the red zone
	 * size.  Hence the stack pointer can be restored only after
	 * we have finished loading everything from the context.
	 */

	/* load ESP into EBP */
	x86_mov_reg_membase (code, X86_EBP, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, esp), 4);
	/* load return address into ECX */
	x86_mov_reg_membase (code, X86_ECX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, eip), 4);
	/* save the return addr to the restored stack - 4 */
	x86_mov_membase_reg (code, X86_EBP, -4, X86_ECX, 4);

	/* load EBP into ECX */
	x86_mov_reg_membase (code, X86_ECX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, ebp), 4);
	/* save EBP to the restored stack - 8 */
	x86_mov_membase_reg (code, X86_EBP, -8, X86_ECX, 4);

	/* load EAX into ECX */
	x86_mov_reg_membase (code, X86_ECX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, eax), 4);
	/* save EAX to the restored stack - 12 */
	x86_mov_membase_reg (code, X86_EBP, -12, X86_ECX, 4);

	/* restore ECX */
	x86_mov_reg_membase (code, X86_ECX, X86_EAX,  MONO_STRUCT_OFFSET (MonoContext, ecx), 4);

	/* restore ESP - 12 */
	x86_lea_membase (code, X86_ESP, X86_EBP, -12);
	/* restore EAX */
	x86_pop_reg (code, X86_EAX);
	/* restore EBP */
	x86_pop_reg (code, X86_EBP);
	/* jump to the saved IP */
	x86_ret (code);

	if (info)
		*info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);
	else {
		GSList *l;

		for (l = unwind_ops; l; l = l->next)
			g_free (l->data);
		g_slist_free (unwind_ops);
	}

	mono_arch_flush_icache (start, code - start);
	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

	return start;
}
Esempio n. 14
0
/*
 * get_throw_trampoline:
 *
 *  Generate a call to mono_amd64_throw_exception/
 * mono_amd64_throw_corlib_exception.
 */
static gpointer
get_throw_trampoline (MonoTrampInfo **info, gboolean rethrow, gboolean corlib, gboolean llvm_abs, gboolean resume_unwind, const char *tramp_name, gboolean aot)
{
	guint8* start;
	guint8 *code;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;
	int i, stack_size, arg_offsets [16], ctx_offset, regs_offset, dummy_stack_space;
	const guint kMaxCodeSize = NACL_SIZE (256, 512);

#ifdef TARGET_WIN32
	dummy_stack_space = 6 * sizeof(mgreg_t);	/* Windows expects stack space allocated for all 6 dummy args. */
#else
	dummy_stack_space = 0;
#endif

	start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);

	/* The stack is unaligned on entry */
	stack_size = ALIGN_TO (sizeof (MonoContext) + 64 + dummy_stack_space, MONO_ARCH_FRAME_ALIGNMENT) + 8;

	code = start;

	if (info)
		unwind_ops = mono_arch_get_cie_program ();

	/* Alloc frame */
	amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, stack_size);
	if (info)
		mono_add_unwind_op_def_cfa_offset (unwind_ops, code, start, stack_size + 8);

	/*
	 * To hide linux/windows calling convention differences, we pass all arguments on
	 * the stack by passing 6 dummy values in registers.
	 */

	arg_offsets [0] = dummy_stack_space + 0;
	arg_offsets [1] = dummy_stack_space + sizeof(mgreg_t);
	arg_offsets [2] = dummy_stack_space + sizeof(mgreg_t) * 2;
	ctx_offset = dummy_stack_space + sizeof(mgreg_t) * 4;
	regs_offset = ctx_offset + MONO_STRUCT_OFFSET (MonoContext, gregs);

	/* Save registers */
	for (i = 0; i < AMD64_NREG; ++i)
		if (i != AMD64_RSP)
			amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (i * sizeof(mgreg_t)), i, sizeof(mgreg_t));
	/* Save RSP */
	amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, stack_size + sizeof(mgreg_t));
	amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RSP * sizeof(mgreg_t)), X86_EAX, sizeof(mgreg_t));
	/* Save IP */
	if (llvm_abs)
		amd64_alu_reg_reg (code, X86_XOR, AMD64_RAX, AMD64_RAX);
	else
		amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, stack_size, sizeof(mgreg_t));
	amd64_mov_membase_reg (code, AMD64_RSP, regs_offset + (AMD64_RIP * sizeof(mgreg_t)), AMD64_RAX, sizeof(mgreg_t));
	/* Set arg1 == ctx */
	amd64_lea_membase (code, AMD64_RAX, AMD64_RSP, ctx_offset);
	amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [0], AMD64_RAX, sizeof(mgreg_t));
	/* Set arg2 == exc/ex_token_index */
	if (resume_unwind)
		amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [1], 0, sizeof(mgreg_t));
	else
		amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [1], AMD64_ARG_REG1, sizeof(mgreg_t));
	/* Set arg3 == rethrow/pc offset */
	if (resume_unwind) {
		amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], 0, sizeof(mgreg_t));
	} else if (corlib) {
		amd64_mov_membase_reg (code, AMD64_RSP, arg_offsets [2], AMD64_ARG_REG2, sizeof(mgreg_t));
		if (llvm_abs)
			/* 
			 * The caller is LLVM code which passes the absolute address not a pc offset,
			 * so compensate by passing 0 as 'rip' and passing the negated abs address as
			 * the pc offset.
			 */
			amd64_neg_membase (code, AMD64_RSP, arg_offsets [2]);
	} else {
		amd64_mov_membase_imm (code, AMD64_RSP, arg_offsets [2], rethrow, sizeof(mgreg_t));
	}

	if (aot) {
		const char *icall_name;

		if (resume_unwind)
			icall_name = "mono_amd64_resume_unwind";
		else if (corlib)
			icall_name = "mono_amd64_throw_corlib_exception";
		else
			icall_name = "mono_amd64_throw_exception";
		ji = mono_patch_info_list_prepend (ji, code - start, MONO_PATCH_INFO_JIT_ICALL_ADDR, icall_name);
		amd64_mov_reg_membase (code, AMD64_R11, AMD64_RIP, 0, 8);
	} else {
		amd64_mov_reg_imm (code, AMD64_R11, resume_unwind ? ((gpointer)mono_amd64_resume_unwind) : (corlib ? (gpointer)mono_amd64_throw_corlib_exception : (gpointer)mono_amd64_throw_exception));
	}
	amd64_call_reg (code, AMD64_R11);
	amd64_breakpoint (code);

	mono_arch_flush_icache (start, code - start);

	g_assert ((code - start) < kMaxCodeSize);

	nacl_global_codeman_validate(&start, kMaxCodeSize, &code);
	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

	if (info)
		*info = mono_tramp_info_create (tramp_name, start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 15
0
/*
 * 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).
 */
gpointer
mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
{
	guint8 *start;
	int i, gregs_offset;
	guint8 *code;
	guint32 pos;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;
	const guint kMaxCodeSize = NACL_SIZE (128, 256);

	start = code = (guint8 *)mono_global_codeman_reserve (kMaxCodeSize);

	/* call_filter (MonoContext *ctx, unsigned long eip) */
	code = start;

	/* Alloc new frame */
	amd64_push_reg (code, AMD64_RBP);
	amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);

	/* Save callee saved regs */
	pos = 0;
	for (i = 0; i < AMD64_NREG; ++i)
		if (AMD64_IS_CALLEE_SAVED_REG (i)) {
			amd64_push_reg (code, i);
			pos += 8;
		}

	/* Save EBP */
	pos += 8;
	amd64_push_reg (code, AMD64_RBP);

	/* Make stack misaligned, the call will make it aligned again */
	if (! (pos & 8))
		amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);

	gregs_offset = MONO_STRUCT_OFFSET (MonoContext, gregs);

	/* set new EBP */
	amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, gregs_offset + (AMD64_RBP * 8), 8);
	/* load callee saved regs */
	for (i = 0; i < AMD64_NREG; ++i) {
#if defined(__native_client_codegen__)
		if (i == AMD64_R15)
			continue;
#endif
		if (AMD64_IS_CALLEE_SAVED_REG (i) && i != AMD64_RBP)
			amd64_mov_reg_membase (code, i, AMD64_ARG_REG1, gregs_offset + (i * 8), 8);
	}
	/* load exc register */
	amd64_mov_reg_membase (code, AMD64_RAX, AMD64_ARG_REG1,  gregs_offset + (AMD64_RAX * 8), 8);

	/* call the handler */
	amd64_call_reg (code, AMD64_ARG_REG2);

	if (! (pos & 8))
		amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);

	/* restore RBP */
	amd64_pop_reg (code, AMD64_RBP);

	/* Restore callee saved regs */
	for (i = AMD64_NREG; i >= 0; --i)
		if (AMD64_IS_CALLEE_SAVED_REG (i))
			amd64_pop_reg (code, i);

	amd64_leave (code);
	amd64_ret (code);

	g_assert ((code - start) < kMaxCodeSize);

	nacl_global_codeman_validate(&start, kMaxCodeSize, &code);

	mono_arch_flush_icache (start, code - start);
	mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

	if (info)
		*info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 16
0
gpointer
mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
{
	guint8 *code, *buf;
	int buf_len, cfa_offset;
	GSList *unwind_ops = NULL;
	MonoJumpInfo *ji = NULL;
	guint8 *br_out, *br [16], *br_ret [16];
	int i, offset, arg_reg, npushed, info_offset, mrgctx_offset;
	int caller_reg_area_offset, caller_freg_area_offset, callee_reg_area_offset, callee_freg_area_offset;
	int lr_offset, fp, br_ret_index, args_size;

	buf_len = 784;
	buf = code = mono_global_codeman_reserve (buf_len);

	arg_reg = ARMREG_R0;
	/* Registers pushed by the arg trampoline */
	npushed = 4;

	// ios abi compatible frame
	fp = ARMREG_R7;
	cfa_offset = npushed * TARGET_SIZEOF_VOID_P;
	mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
	ARM_PUSH (code, (1 << fp) | (1 << ARMREG_LR));
	cfa_offset += 2 * TARGET_SIZEOF_VOID_P;
	mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
	mono_add_unwind_op_offset (unwind_ops, code, buf, fp, (- cfa_offset));
	mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, ((- cfa_offset) + 4));
	ARM_MOV_REG_REG (code, fp, ARMREG_SP);
	mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, fp);
	/* Allocate stack frame */
	ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 32 + (16 * sizeof (double)));
	if (MONO_ARCH_FRAME_ALIGNMENT > 8)
		ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, (MONO_ARCH_FRAME_ALIGNMENT - 8));
	offset = 4;
	info_offset = -offset;
	offset += 4;
	mrgctx_offset = -offset;
	offset += 4 * 4;
	callee_reg_area_offset = -offset;
	offset += 8 * 8;
	caller_freg_area_offset = -offset;
	offset += 8 * 8;
	callee_freg_area_offset = -offset;

	caller_reg_area_offset = cfa_offset - (npushed * TARGET_SIZEOF_VOID_P);
	lr_offset = 4;
	/* Save info struct which is in r0 */
	ARM_STR_IMM (code, arg_reg, fp, info_offset);
	/* Save rgctx reg */
	ARM_STR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
	/* Allocate callee area */
	ARM_LDR_IMM (code, ARMREG_IP, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
	ARM_SUB_REG_REG (code, ARMREG_SP, ARMREG_SP, ARMREG_IP);
	/* Allocate callee register area just below the callee area so the slots are correct */
	ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * TARGET_SIZEOF_VOID_P);
	if (mono_arm_is_hard_float ()) {
		/* Save caller fregs */
		ARM_SUB_REG_IMM8 (code, ARMREG_IP, fp, -caller_freg_area_offset);
		for (i = 0; i < 8; ++i)
			ARM_FSTD (code, i * 2, ARMREG_IP, (i * sizeof (double)));
	}

	/*
	 * The stack now looks like this:
	 * <caller frame>
	 * <saved r0-r3, lr>
	 * <saved fp> <- fp
	 * <our frame>
	 * <callee area> <- sp
	 */
	g_assert (mono_arm_thumb_supported ());

	/* Call start_gsharedvt_call () */
	/* 6 arguments, needs 2 stack slot, need to clean it up after the call */
	args_size = 2 * TARGET_SIZEOF_VOID_P;
	ARM_SUB_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);
	/* arg1 == info */
	ARM_LDR_IMM (code, ARMREG_R0, fp, info_offset);
	/* arg2 == caller stack area */
	ARM_ADD_REG_IMM8 (code, ARMREG_R1, fp, cfa_offset - 4 * TARGET_SIZEOF_VOID_P);
	/* arg3 == callee stack area */
	ARM_ADD_REG_IMM8 (code, ARMREG_R2, ARMREG_SP, args_size);
	/* arg4 == mrgctx reg */
	ARM_LDR_IMM (code, ARMREG_R3, fp, mrgctx_offset);
	/* arg5 == caller freg area */
	ARM_SUB_REG_IMM8 (code, ARMREG_IP, fp, -caller_freg_area_offset);
	ARM_STR_IMM (code, ARMREG_IP, ARMREG_SP, 0);
	/* arg6 == callee freg area */
	ARM_SUB_REG_IMM8 (code, ARMREG_IP, fp, -callee_freg_area_offset);
	ARM_STR_IMM (code, ARMREG_IP, ARMREG_SP, 4);
	/* Make the call */
	if (aot) {
		ji = mono_patch_info_list_prepend (ji, code - buf, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
		ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
		ARM_B (code, 0);
		*(gpointer*)code = NULL;
		code += 4;
		ARM_LDR_REG_REG (code, ARMREG_IP, ARMREG_PC, ARMREG_IP);
	} else {
		ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
		ARM_B (code, 0);
		*(gpointer*)code = (gpointer)mono_arm_start_gsharedvt_call;
		code += 4;
	}
	ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_PC);
	code = emit_bx (code, ARMREG_IP);
	/* Clean up stack */
	ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, args_size);

	/* Make the real method call */
	/* R0 contains the addr to call */
	ARM_MOV_REG_REG (code, ARMREG_IP, ARMREG_R0);
	/* Load argument registers */
	ARM_LDM (code, ARMREG_SP, (1 << ARMREG_R0) | (1 << ARMREG_R1) | (1 << ARMREG_R2) | (1 << ARMREG_R3));
	if (mono_arm_is_hard_float ()) {
		/* Load argument fregs */
		ARM_SUB_REG_IMM8 (code, ARMREG_LR, fp, -callee_freg_area_offset);
		for (i = 0; i < 8; ++i)
			ARM_FLDD (code, i * 2, ARMREG_LR, (i * sizeof (double)));
	}
	/* Pop callee register area */
	ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, 4 * TARGET_SIZEOF_VOID_P);
	/* Load rgctx */
	ARM_LDR_IMM (code, MONO_ARCH_RGCTX_REG, fp, mrgctx_offset);
	/* Make the call */
#if 0
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, addr));
#endif
	/* mono_arch_find_imt_method () depends on this */
	ARM_ADD_REG_IMM8 (code, ARMREG_LR, ARMREG_PC, 4);
	ARM_BX (code, ARMREG_IP);
	*((gpointer*)code) = NULL;
	code += 4;

	br_ret_index = 0;

	/* Branch between IN/OUT cases */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));

	ARM_CMP_REG_IMM8 (code, ARMREG_IP, 1);
	br_out = code;
	ARM_B_COND (code, ARMCOND_NE, 0);

	/* IN CASE */

	/* LR == return marshalling type */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));

	/* Continue if no marshalling required */
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_NONE);
	br_ret [br_ret_index ++] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);

	/* Compute vret area address in LR */
	ARM_LDR_IMM (code, ARMREG_LR, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
	/* The slot value is off by 4 */
	ARM_SUB_REG_IMM8 (code, ARMREG_LR, ARMREG_LR, 4);
	ARM_SHL_IMM (code, ARMREG_LR, ARMREG_LR, 2);
	ARM_ADD_REG_REG (code, ARMREG_LR, ARMREG_LR, ARMREG_SP);

	/* Branch to specific marshalling code */
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
	br [0] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
	br [1] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I1);
	br [2] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
	br [3] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_I2);
	br [4] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U2);
	br [5] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R4);
	br [6] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R8);
	br [7] = code;
	ARM_B_COND (code, ARMCOND_EQ, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);

	/* IN IREG case */
	arm_patch (br [0], code);
	ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* IN IREGS case */
	arm_patch (br [1], code);
	ARM_LDR_IMM (code, ARMREG_R0, ARMREG_LR, 0);
	ARM_LDR_IMM (code, ARMREG_R1, ARMREG_LR, 4);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* I1 case */
	arm_patch (br [2], code);
	ARM_LDRSB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* U1 case */
	arm_patch (br [3], code);
	ARM_LDRB_IMM (code, ARMREG_R0, ARMREG_LR, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* I2 case */
	arm_patch (br [4], code);
	ARM_LDRSH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* U2 case */
	arm_patch (br [5], code);
	ARM_LDRH_IMM (code, ARMREG_R0, ARMREG_LR, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* R4 case */
	arm_patch (br [6], code);
	ARM_FLDS (code, ARM_VFP_D0, ARMREG_LR, 0);
	code += 4;
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	/* R8 case */
	arm_patch (br [7], code);
	ARM_FLDD (code, ARM_VFP_D0, ARMREG_LR, 0);
	code += 4;
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);

	/* OUT CASE */
	arm_patch (br_out, code);

	/* Marshal return value */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));

	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREGS);
	br [0] = code;
	ARM_B_COND (code, ARMCOND_NE, 0);

	/* OUT IREGS case */
	/* Load vtype ret addr from the caller arg regs */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
	ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
	ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
	ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
	/* Save both registers for simplicity */
	ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
	ARM_STR_IMM (code, ARMREG_R1, ARMREG_IP, 4);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	arm_patch (br [0], code);

	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_IREG);
	br [0] = code;
	ARM_B_COND (code, ARMCOND_NE, 0);

	/* OUT IREG case */
	/* Load vtype ret addr from the caller arg regs */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
	ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
	ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
	ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
	/* Save the return value to the buffer pointed to by the vret addr */
	ARM_STR_IMM (code, ARMREG_R0, ARMREG_IP, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	arm_patch (br [0], code);

	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_U1);
	br [0] = code;
	ARM_B_COND (code, ARMCOND_NE, 0);

	/* OUT U1 case */
	/* Load vtype ret addr from the caller arg regs */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
	ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
	ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
	ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
	/* Save the return value to the buffer pointed to by the vret addr */
	ARM_STRB_IMM (code, ARMREG_R0, ARMREG_IP, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	arm_patch (br [0], code);

	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R4);
	br [0] = code;
	ARM_B_COND (code, ARMCOND_NE, 0);

	/* OUT R4 case */
	/* Load vtype ret addr from the caller arg regs */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
	ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
	ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
	ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
	/* Save the return value to the buffer pointed to by the vret addr */
	ARM_FSTS (code, ARM_VFP_D0, ARMREG_IP, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	arm_patch (br [0], code);

	ARM_CMP_REG_IMM8 (code, ARMREG_IP, GSHAREDVT_RET_VFP_R8);
	br [0] = code;
	ARM_B_COND (code, ARMCOND_NE, 0);

	/* OUT R8 case */
	/* Load vtype ret addr from the caller arg regs */
	ARM_LDR_IMM (code, ARMREG_IP, fp, info_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));
	ARM_SHL_IMM (code, ARMREG_IP, ARMREG_IP, 2);
	ARM_ADD_REG_REG (code, ARMREG_IP, ARMREG_IP, fp);
	ARM_ADD_REG_IMM8 (code, ARMREG_IP, ARMREG_IP, caller_reg_area_offset);
	ARM_LDR_IMM (code, ARMREG_IP, ARMREG_IP, 0);
	/* Save the return value to the buffer pointed to by the vret addr */
	ARM_FSTD (code, ARM_VFP_D0, ARMREG_IP, 0);
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);
	arm_patch (br [0], code);

	/* OUT other cases */
	br_ret [br_ret_index ++] = code;
	ARM_B (code, 0);

	for (i = 0; i < br_ret_index; ++i)
		arm_patch (br_ret [i], code);

	/* Normal return */
	/* Restore registers + stack */
	ARM_MOV_REG_REG (code, ARMREG_SP, fp);
	ARM_LDM (code, fp, (1 << fp) | (1 << ARMREG_LR));
	ARM_ADD_REG_IMM8 (code, ARMREG_SP, ARMREG_SP, cfa_offset);
	/* Return */
	ARM_BX (code, ARMREG_LR);

	g_assert ((code - buf) < buf_len);

	if (info)
		*info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);

	mono_arch_flush_icache (buf, code - buf);
	return buf;
}
Esempio n. 17
0
gpointer
mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
{
	guint8 *code, *buf;
	int buf_len, cfa_offset;
	GSList *unwind_ops = NULL;
	MonoJumpInfo *ji = NULL;
	guint8 *br_out, *br [16];
	int info_offset, mrgctx_offset;

	buf_len = 320;
	buf = code = mono_global_codeman_reserve (buf_len);

	/*
	 * This trampoline is responsible for marshalling calls between normal code and gsharedvt code. The
	 * caller is a normal or gshared method which uses the signature of the inflated method to make the call, while
	 * the callee is a gsharedvt method which has a signature which uses valuetypes in place of type parameters, i.e.
	 * caller:
	 * foo<bool> (bool b)
	 * callee:
	 * T=<type used to represent vtype type arguments, currently TypedByRef>
	 * foo<T> (T b)
	 * The trampoline is responsible for marshalling the arguments and marshalling the result back. To simplify
	 * things, we create our own stack frame, and do most of the work in a C function, which receives a
	 * GSharedVtCallInfo structure as an argument. The structure should contain information to execute the C function to
	 * be as fast as possible. The argument is received in EAX from a gsharedvt trampoline. So the real
	 * call sequence looks like this:
	 * caller -> gsharedvt trampoline -> gsharevt in trampoline -> start_gsharedvt_call
	 * FIXME: Optimize this.
	 */

	cfa_offset = sizeof (gpointer);
	mono_add_unwind_op_def_cfa (unwind_ops, code, buf, X86_ESP, cfa_offset);
	mono_add_unwind_op_offset (unwind_ops, code, buf, X86_NREG, -cfa_offset);
	x86_push_reg (code, X86_EBP);
	cfa_offset += sizeof (gpointer);
	mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
	mono_add_unwind_op_offset (unwind_ops, code, buf, X86_EBP, - cfa_offset);
	x86_mov_reg_reg (code, X86_EBP, X86_ESP, sizeof (gpointer));
	mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, X86_EBP);
	/* Alloc stack frame/align stack */
	x86_alu_reg_imm (code, X86_SUB, X86_ESP, 8);
	info_offset = -4;
	mrgctx_offset = - 8;
	/* The info struct is put into EAX by the gsharedvt trampoline */
	/* Save info struct addr */
	x86_mov_membase_reg (code, X86_EBP, info_offset, X86_EAX, 4);
	/* Save rgctx */
	x86_mov_membase_reg (code, X86_EBP, mrgctx_offset, MONO_ARCH_RGCTX_REG, 4);

	/* Allocate stack area used to pass arguments to the method */
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), sizeof (gpointer));
	x86_alu_reg_reg (code, X86_SUB, X86_ESP, X86_EAX);

#if 0
	/* Stack alignment check */
	x86_mov_reg_reg (code, X86_ECX, X86_ESP, 4);
	x86_alu_reg_imm (code, X86_AND, X86_ECX, MONO_ARCH_FRAME_ALIGNMENT - 1);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, 0);
	x86_branch_disp (code, X86_CC_EQ, 3, FALSE);
	x86_breakpoint (code);
#endif

	/* ecx = caller argument area */
	x86_mov_reg_reg (code, X86_ECX, X86_EBP, 4);
	x86_alu_reg_imm (code, X86_ADD, X86_ECX, 8);
	/* eax = callee argument area */
	x86_mov_reg_reg (code, X86_EAX, X86_ESP, 4);

	/* Call start_gsharedvt_call */
	/* Arg 4 */
	x86_push_membase (code, X86_EBP, mrgctx_offset);
	/* Arg3 */
	x86_push_reg (code, X86_EAX);
	/* Arg2 */
	x86_push_reg (code, X86_ECX);
	/* Arg1 */
	x86_push_membase (code, X86_EBP, info_offset);
	if (aot) {
		code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_x86_start_gsharedvt_call");
		x86_call_reg (code, X86_EAX);
	} else {
		x86_call_code (code, mono_x86_start_gsharedvt_call);
	}
	x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4 * 4);
	/* The address to call is in eax */
	/* The stack is now setup for the real call */
	/* Load info struct */
	x86_mov_reg_membase (code, X86_ECX, X86_EBP, info_offset, 4);
	/* Load rgctx */
	x86_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, X86_EBP, mrgctx_offset, sizeof (gpointer));
	/* Make the call */
	x86_call_reg (code, X86_EAX);
	/* The return value is either in registers, or stored to an area beginning at sp [info->vret_slot] */
	/* EAX/EDX might contain the return value, only ECX is free */
	/* Load info struct */
	x86_mov_reg_membase (code, X86_ECX, X86_EBP, info_offset, 4);

	/* Branch to the in/out handling code */
	x86_alu_membase_imm (code, X86_CMP, X86_ECX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1);	
	br_out = code;
	x86_branch32 (code, X86_CC_NE, 0, TRUE);

	/*
	 * IN CASE
	 */

	/* Load ret marshal type */
	x86_mov_reg_membase (code, X86_ECX, X86_ECX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_NONE);
	br [0] = code;
	x86_branch8 (code, X86_CC_NE, 0, TRUE);

	/* Normal return, no marshalling required */
	x86_leave (code);
	x86_ret (code);

	/* Return value marshalling */
	x86_patch (br [0], code);
	/* Load info struct */
	x86_mov_reg_membase (code, X86_EAX, X86_EBP, info_offset, 4);
	/* Load 'vret_slot' */
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
	/* Compute ret area address */
	x86_shift_reg_imm (code, X86_SHL, X86_EAX, 2);
	x86_alu_reg_reg (code, X86_ADD, X86_EAX, X86_ESP);
	/* The callee does a ret $4, so sp is off by 4 */
	x86_alu_reg_imm (code, X86_SUB, X86_EAX, sizeof (gpointer));

	/* Branch to specific marshalling code */
	// FIXME: Move the I4 case to the top */
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_DOUBLE_FPSTACK);
	br [1] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_FLOAT_FPSTACK);
	br [2] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_STACK_POP);
	br [3] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_I1);
	br [4] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_U1);
	br [5] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_I2);
	br [6] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_U2);
	br [7] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	/* IREGS case */
	/* Load both eax and edx for simplicity */
	x86_mov_reg_membase (code, X86_EDX, X86_EAX, sizeof (gpointer), sizeof (gpointer));
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, sizeof (gpointer));
	x86_leave (code);
	x86_ret (code);
	/* DOUBLE_FPSTACK case */
	x86_patch (br [1], code);
	x86_fld_membase (code, X86_EAX, 0, TRUE);
	x86_jump8 (code, 0);
	x86_leave (code);
	x86_ret (code);
	/* FLOAT_FPSTACK case */
	x86_patch (br [2], code);
	x86_fld_membase (code, X86_EAX, 0, FALSE);
	x86_leave (code);
	x86_ret (code);
	/* STACK_POP case */
	x86_patch (br [3], code);
	x86_leave (code);
	x86_ret_imm (code, 4);
	/* I1 case */
	x86_patch (br [4], code);
	x86_widen_membase (code, X86_EAX, X86_EAX, 0, TRUE, FALSE);
	x86_leave (code);
	x86_ret (code);
	/* U1 case */
	x86_patch (br [5], code);
	x86_widen_membase (code, X86_EAX, X86_EAX, 0, FALSE, FALSE);
	x86_leave (code);
	x86_ret (code);
	/* I2 case */
	x86_patch (br [6], code);
	x86_widen_membase (code, X86_EAX, X86_EAX, 0, TRUE, TRUE);
	x86_leave (code);
	x86_ret (code);
	/* U2 case */
	x86_patch (br [7], code);
	x86_widen_membase (code, X86_EAX, X86_EAX, 0, FALSE, TRUE);
	x86_leave (code);
	x86_ret (code);

	/*
	 * OUT CASE
	 */

	x86_patch (br_out, code);
	/* Load ret marshal type into ECX */
	x86_mov_reg_membase (code, X86_ECX, X86_ECX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_NONE);
	br [0] = code;
	x86_branch8 (code, X86_CC_NE, 0, TRUE);

	/* Normal return, no marshalling required */
	x86_leave (code);
	x86_ret (code);

	/* Return value marshalling */
	x86_patch (br [0], code);

	/* EAX might contain the return value */
	// FIXME: Use moves
	x86_push_reg (code, X86_EAX);

	/* Load info struct */
	x86_mov_reg_membase (code, X86_EAX, X86_EBP, info_offset, 4);
	/* Load 'vret_arg_slot' */
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_slot), 4);
	/* Compute ret area address in the caller frame in EAX */
	x86_shift_reg_imm (code, X86_SHL, X86_EAX, 2);
	x86_alu_reg_reg (code, X86_ADD, X86_EAX, X86_EBP);
	x86_alu_reg_imm (code, X86_ADD, X86_EAX, 8);
	x86_mov_reg_membase (code, X86_EAX, X86_EAX, 0, sizeof (gpointer));

	/* Branch to specific marshalling code */
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_DOUBLE_FPSTACK);
	br [1] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_FLOAT_FPSTACK);
	br [2] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_STACK_POP);
	br [3] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	x86_alu_reg_imm (code, X86_CMP, X86_ECX, GSHAREDVT_RET_IREGS);
	br [4] = code;
	x86_branch8 (code, X86_CC_E, 0, TRUE);
	/* IREG case */
	x86_mov_reg_reg (code, X86_ECX, X86_EAX, sizeof (gpointer));
	x86_pop_reg (code, X86_EAX);
	x86_mov_membase_reg (code, X86_ECX, 0, X86_EAX, sizeof (gpointer));
	x86_leave (code);
	x86_ret_imm (code, 4);
	/* IREGS case */
	x86_patch (br [4], code);
	x86_mov_reg_reg (code, X86_ECX, X86_EAX, sizeof (gpointer));
	x86_pop_reg (code, X86_EAX);
	x86_mov_membase_reg (code, X86_ECX, sizeof (gpointer), X86_EDX, sizeof (gpointer));
	x86_mov_membase_reg (code, X86_ECX, 0, X86_EAX, sizeof (gpointer));
	x86_leave (code);
	x86_ret_imm (code, 4);
	/* DOUBLE_FPSTACK case */
	x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
	x86_patch (br [1], code);
	x86_fst_membase (code, X86_EAX, 0, TRUE, TRUE);
	x86_jump8 (code, 0);
	x86_leave (code);
	x86_ret_imm (code, 4);
	/* FLOAT_FPSTACK case */
	x86_alu_reg_imm (code, X86_ADD, X86_ESP, 4);
	x86_patch (br [2], code);
	x86_fst_membase (code, X86_EAX, 0, FALSE, TRUE);
	x86_leave (code);
	x86_ret_imm (code, 4);
	/* STACK_POP case */
	x86_patch (br [3], code);
	x86_leave (code);
	x86_ret_imm (code, 4);

	g_assert ((code - buf) < buf_len);

	if (info)
		*info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);

	mono_arch_flush_icache (buf, code - buf);
	return buf;
}
Esempio n. 18
0
gpointer
mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
{
	guint8 *code, *buf;
	int buf_len, cfa_offset;
	GSList *unwind_ops = NULL;
	MonoJumpInfo *ji = NULL;
	int n_arg_regs, n_arg_fregs, framesize, i;
	int info_offset, offset, rgctx_arg_reg_offset;
	int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
	guint8 *br_out, *br [64], *br_ret [64];
	int b_ret_index;
	int reg_area_size;

	buf_len = 2048;
	buf = code = mono_global_codeman_reserve (buf_len + MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE);

	/*
	 * We are being called by an gsharedvt arg trampoline, the info argument is in AMD64_RAX.
	 */
	n_arg_regs = PARAM_REGS;
	n_arg_fregs = FLOAT_PARAM_REGS;

	/* Compute stack frame size and offsets */
	offset = 0;
	/* info reg */
	info_offset = offset;
	offset += 8;

	/* rgctx reg */
	rgctx_arg_reg_offset = offset;
	offset += 8;

	/*callconv in regs */
	caller_reg_area_offset = offset;
	reg_area_size = ALIGN_TO ((n_arg_regs + n_arg_fregs) * 8, MONO_ARCH_FRAME_ALIGNMENT);
	offset += reg_area_size;

	framesize = offset;

	g_assert (framesize % MONO_ARCH_FRAME_ALIGNMENT == 0);
	g_assert (reg_area_size % MONO_ARCH_FRAME_ALIGNMENT == 0);

	/* unwind markers 1/3 */
	cfa_offset = sizeof (gpointer);
	mono_add_unwind_op_def_cfa (unwind_ops, code, buf, AMD64_RSP, cfa_offset);
	mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RIP, -cfa_offset);

	/* save the old frame pointer */
	amd64_push_reg (code, AMD64_RBP);

	/* unwind markers 2/3 */
	cfa_offset += sizeof (gpointer);
	mono_add_unwind_op_def_cfa_offset (unwind_ops, code, buf, cfa_offset);
	mono_add_unwind_op_offset (unwind_ops, code, buf, AMD64_RBP, - cfa_offset);

	/* set it as the new frame pointer */
	amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));

	/* unwind markers 3/3 */
	mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, AMD64_RBP);
	mono_add_unwind_op_fp_alloc (unwind_ops, code, buf, AMD64_RBP, 0);

	/* setup the frame */
	amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, framesize);
	
	/* save stuff */

	/* save info */
	amd64_mov_membase_reg (code, AMD64_RSP, info_offset, AMD64_RAX, sizeof (mgreg_t));
	/* save rgctx */
	amd64_mov_membase_reg (code, AMD64_RSP, rgctx_arg_reg_offset, MONO_ARCH_RGCTX_REG, sizeof (mgreg_t));

	for (i = 0; i < n_arg_regs; ++i)
		amd64_mov_membase_reg (code, AMD64_RSP, caller_reg_area_offset + i * 8, param_regs [i], sizeof (mgreg_t));

	for (i = 0; i < n_arg_fregs; ++i)
		amd64_sse_movsd_membase_reg (code, AMD64_RSP, caller_reg_area_offset + (i + n_arg_regs) * 8, i);

	/* TODO Allocate stack area used to pass arguments to the method */


	/* Allocate callee register area just below the caller area so it can be accessed from start_gsharedvt_call using negative offsets */
	/* XXX figure out alignment */
	callee_reg_area_offset = reg_area_size - ((n_arg_regs + n_arg_fregs) * 8); /* Ensure alignment */
	callee_stack_area_offset = callee_reg_area_offset + reg_area_size;
	amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, reg_area_size);

	/* Allocate stack area used to pass arguments to the method */
	amd64_mov_reg_membase (code, AMD64_R11, AMD64_RAX, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage), 4);
	amd64_alu_reg_reg (code, X86_SUB, AMD64_RSP, AMD64_R11);

	/* The stack now looks like this:

	<caller stack params area>
	<return address>
	<old frame pointer>
	<caller registers area>
	<rgctx>
	<gsharedvt info>
	<callee stack area>
	<callee reg area>
	 */

	/* Call start_gsharedvt_call () */
	/* arg1 == info */
	amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG1, AMD64_RAX, sizeof(mgreg_t));
	/* arg2 = caller stack area */
	amd64_lea_membase (code, MONO_AMD64_ARG_REG2, AMD64_RBP, -(framesize - caller_reg_area_offset)); 

	/* arg3 == callee stack area */
	amd64_lea_membase (code, MONO_AMD64_ARG_REG3, AMD64_RSP, callee_reg_area_offset);

	/* arg4 = mrgctx reg */
	amd64_mov_reg_reg (code, MONO_AMD64_ARG_REG4, MONO_ARCH_RGCTX_REG, sizeof(mgreg_t));

	if (aot) {
		code = mono_arch_emit_load_aotconst (buf, code, &ji, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_amd64_start_gsharedvt_call");
		#ifdef TARGET_WIN32
			/* Since we are doing a call as part of setting up stackframe, the reserved shadow stack used by Windows platform is allocated up in
			the callee stack area but currently the callee reg area is in between. Windows calling convention dictates that room is made on stack where
			callee can save any parameters passed in registers. Since Windows x64 calling convention
			uses 4 registers for the first 4 parameters, stack needs to be adjusted before making the call.
			NOTE, Windows calling convention assumes that space for all registers have been reserved, regardless
			of the number of function parameters actually used.
			*/
			int shadow_reg_size = 0;

			shadow_reg_size = ALIGN_TO (PARAM_REGS * sizeof(gpointer), MONO_ARCH_FRAME_ALIGNMENT);
			amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, shadow_reg_size);
			amd64_call_reg (code, AMD64_R11);
			amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, shadow_reg_size);
		#else
			amd64_call_reg (code, AMD64_R11);
		#endif
	} else {
		amd64_call_code (code, mono_amd64_start_gsharedvt_call);
	}

	/* Method to call is now on RAX. Restore regs and jump */
	amd64_mov_reg_reg (code, AMD64_R11, AMD64_RAX, sizeof(mgreg_t));

	for (i = 0; i < n_arg_regs; ++i)
		amd64_mov_reg_membase (code, param_regs [i], AMD64_RSP, callee_reg_area_offset + i * 8, sizeof (mgreg_t));

	for (i = 0; i < n_arg_fregs; ++i)
		amd64_sse_movsd_reg_membase (code, i, AMD64_RSP, callee_reg_area_offset + (i + n_arg_regs) * 8);

	//load rgctx
	amd64_mov_reg_membase (code, MONO_ARCH_RGCTX_REG, AMD64_RBP, -(framesize - rgctx_arg_reg_offset), sizeof (mgreg_t));

	/* Clear callee reg area */
	amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, reg_area_size);

	/* Call the thing */
	amd64_call_reg (code, AMD64_R11);

	/* Marshal return value. Available registers: R10 and R11 */
	/* Load info struct */
	amd64_mov_reg_membase (code, AMD64_R10, AMD64_RBP, -(framesize - info_offset), sizeof (mgreg_t));

	/* Branch to the in/out handling code */
	amd64_alu_membase_imm_size (code, X86_CMP, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in), 1, 4);

	b_ret_index = 0;
	br_out = code;
	x86_branch32 (code, X86_CC_NE, 0, TRUE);

	/*
	 * IN CASE
	 */

	/* Load vret_slot */
	/* Use first input parameter register as scratch since it is volatile on all platforms */
	amd64_mov_reg_membase (code, MONO_AMD64_ARG_REG1, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot), 4);
	amd64_alu_reg_imm (code, X86_SUB, MONO_AMD64_ARG_REG1, n_arg_regs + n_arg_fregs);
	amd64_shift_reg_imm (code, X86_SHL, MONO_AMD64_ARG_REG1, 3);

	/* vret address is RBP - (framesize - caller_reg_area_offset) */
	amd64_mov_reg_reg (code, AMD64_R11, AMD64_RSP, sizeof(mgreg_t));
	amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, MONO_AMD64_ARG_REG1);

	/* Load ret marshal type */
	/* Load vret address in R11 */
	amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);

	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
		br [i] = code;
		amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
	}
	x86_breakpoint (code); /* unhandled case */

	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		mono_amd64_patch (br [i], code);
		switch (i) {
		case GSHAREDVT_RET_NONE:
			break;
		case GSHAREDVT_RET_I1:
			amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, FALSE);
			break;
		case GSHAREDVT_RET_U1:
			amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, FALSE);
			break;
		case GSHAREDVT_RET_I2:
			amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, TRUE, TRUE);
			break;
		case GSHAREDVT_RET_U2:
			amd64_widen_membase (code, AMD64_RAX, AMD64_R11, 0, FALSE, TRUE);
			break;
		case GSHAREDVT_RET_I4: // CORRECT
		case GSHAREDVT_RET_U4: // THIS IS INCORRECT. WHY IS IT NOT FAILING?
			amd64_movsxd_reg_membase (code, AMD64_RAX, AMD64_R11, 0);
			break;
		case GSHAREDVT_RET_I8:
			amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11, 0, 8);
			break;
		case GSHAREDVT_RET_IREGS_1:
			amd64_mov_reg_membase (code, return_regs [i - GSHAREDVT_RET_IREGS_1], AMD64_R11, 0, 8);
			break;
		case GSHAREDVT_RET_R8:
			amd64_sse_movsd_reg_membase (code, AMD64_XMM0, AMD64_R11, 0);
			break;
		default:
			x86_breakpoint (code); /* can't handle specific case */
		}

		br_ret [b_ret_index ++] = code;
		x86_jump32 (code, 0);
	}

	/*
	 * OUT CASE
	 */
	mono_amd64_patch (br_out, code);

	/*
		Address to write return to is in the original value of the register specified by vret_arg_reg.
		This will be either RSI, RDI (System V) or RCX, RDX (Windows) depending on whether this is a static call.
		Its location:
		We alloc 'framesize' bytes below RBP to save regs, info and rgctx. RSP = RBP - framesize
		We store RDI (System V), RCX (Windows) at RSP + caller_reg_area_offset + slot_index_of (register) * 8.

		address: RBP - framesize + caller_reg_area_offset + 8*slot
	*/

	int caller_vret_offset = caller_reg_area_offset - framesize;

	/* Load vret address in R11 */
	/* Position to return to is passed as a hidden argument. Load 'vret_arg_slot' to find it */
	amd64_movsxd_reg_membase (code, AMD64_R11, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_arg_reg));

	// In the GSHAREDVT_RET_NONE case, vret_arg_slot is -1. In this case, skip marshalling.
	amd64_alu_reg_imm (code, X86_CMP, AMD64_R11, 0);
	br_ret [b_ret_index ++] = code;
	amd64_branch32 (code, X86_CC_LT, 0, TRUE);

	/* Compute ret area address in the caller frame, *( ((gpointer *)RBP) [R11+2] ) */
	amd64_shift_reg_imm (code, X86_SHL, AMD64_R11, 3);
	amd64_alu_reg_imm (code, X86_ADD, AMD64_R11, caller_vret_offset);
	amd64_alu_reg_reg (code, X86_ADD, AMD64_R11, AMD64_RBP);
	amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11, 0, sizeof (gpointer));

	/* Load ret marshal type in R10 */
	amd64_mov_reg_membase (code, AMD64_R10, AMD64_R10, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal), 4);

	// Switch table for ret_marshal value
	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		amd64_alu_reg_imm (code, X86_CMP, AMD64_R10, i);
		br [i] = code;
		amd64_branch8 (code, X86_CC_EQ, 0, TRUE);
	}
	x86_breakpoint (code); /* unhandled case */

	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		mono_amd64_patch (br [i], code);
		switch (i) {
		case GSHAREDVT_RET_NONE:
			break;
		case GSHAREDVT_RET_IREGS_1:
			amd64_mov_membase_reg (code, AMD64_R11, 0, return_regs [i - GSHAREDVT_RET_IREGS_1], 8);
			break;
		case GSHAREDVT_RET_R8:
			amd64_sse_movsd_membase_reg (code, AMD64_R11, 0, AMD64_XMM0);
			break;
		default:
			x86_breakpoint (code); /* can't handle specific case */
		}

		br_ret [b_ret_index ++] = code;
		x86_jump32 (code, 0);
	}

	/* exit path */
	for (i = 0; i < b_ret_index; ++i)
		mono_amd64_patch (br_ret [i], code);

	/* Exit code path */
#if TARGET_WIN32
	amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
	amd64_pop_reg (code, AMD64_RBP);
	mono_add_unwind_op_same_value (unwind_ops, code, buf, AMD64_RBP);
#else
	amd64_leave (code);
#endif
	amd64_ret (code);

	g_assert ((code - buf) < buf_len);
	g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_MAX_TRAMPOLINE_UNWINDINFO_SIZE));

	if (info)
		*info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);

	mono_arch_flush_icache (buf, code - buf);
	return buf;
}
Esempio n. 19
0
gpointer
mono_arch_get_gsharedvt_trampoline (MonoTrampInfo **info, gboolean aot)
{
	guint8 *code, *buf;
	int buf_len, cfa_offset;
	GSList *unwind_ops = NULL;
	MonoJumpInfo *ji = NULL;
	guint8 *br_out, *br [64], *br_ret [64], *bcc_ret [64];
	int i, n_arg_regs, n_arg_fregs, offset, arg_reg, info_offset, rgctx_arg_reg_offset;
	int caller_reg_area_offset, callee_reg_area_offset, callee_stack_area_offset;
	int br_ret_index, bcc_ret_index;

	buf_len = 2048;
	buf = code = mono_global_codeman_reserve (buf_len);

	/*
	 * We are being called by an gsharedvt arg trampoline, the info argument is in IP1.
	 */
	arg_reg = ARMREG_IP1;
	n_arg_regs = NUM_GSHAREDVT_ARG_GREGS;
	n_arg_fregs = NUM_GSHAREDVT_ARG_FREGS;

	/* Compute stack frame size and offsets */
	offset = 0;
	/* frame block */
	offset += 2 * 8;
	/* info argument */
	info_offset = offset;
	offset += 8;
	/* saved rgctx */
	rgctx_arg_reg_offset = offset;
	offset += 8;
	/* alignment */
	offset += 8;
	/* argument regs */
	caller_reg_area_offset = offset;
	offset += (n_arg_regs + n_arg_fregs) * 8;

	/* We need the argument regs to be saved at the top of the frame */
	g_assert (offset % MONO_ARCH_FRAME_ALIGNMENT == 0);

	cfa_offset = offset;

	/* Setup frame */
	arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -cfa_offset);
	mono_add_unwind_op_def_cfa (unwind_ops, code, buf, ARMREG_SP, cfa_offset);
	mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_FP, -cfa_offset + 0);
	mono_add_unwind_op_offset (unwind_ops, code, buf, ARMREG_LR, -cfa_offset + 8);
	arm_movspx (code, ARMREG_FP, ARMREG_SP);
	mono_add_unwind_op_def_cfa_reg (unwind_ops, code, buf, ARMREG_FP);

	/* Save info argument */
	arm_strx (code, arg_reg, ARMREG_FP, info_offset);

	/* Save rgxctx */
	arm_strx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);

	/* Save argument regs below the stack arguments */
	for (i = 0; i < n_arg_regs; ++i)
		arm_strx (code, i, ARMREG_SP, caller_reg_area_offset + (i * 8));
	// FIXME: Only do this if fp regs are used
	for (i = 0; i < n_arg_fregs; ++i)
		arm_strfpx (code, i, ARMREG_SP, caller_reg_area_offset + ((n_arg_regs + i) * 8));

	/* Allocate callee area */
	arm_ldrw (code, ARMREG_IP0, arg_reg, MONO_STRUCT_OFFSET (GSharedVtCallInfo, stack_usage));
	arm_movspx (code, ARMREG_LR, ARMREG_SP);
	arm_subx (code, ARMREG_LR, ARMREG_LR, ARMREG_IP0);
	arm_movspx (code, ARMREG_SP, ARMREG_LR);
	/* Allocate callee register area just below the callee area so it can be accessed from start_gsharedvt_call using negative offsets */
	/* The + 8 is for alignment */
	callee_reg_area_offset = 8;
	callee_stack_area_offset = callee_reg_area_offset + (n_arg_regs * sizeof (gpointer));
	arm_subx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8);

	/*
	 * The stack now looks like this:
	 * <caller frame>
	 * <saved r0-r8>
	 * <our frame>
	 * <saved fp, lr> <- fp
	 * <callee area> <- sp
	 */

	/* Call start_gsharedvt_call () */
	/* arg1 == info */
	arm_ldrx (code, ARMREG_R0, ARMREG_FP, info_offset);
	/* arg2 = caller stack area */
	arm_addx_imm (code, ARMREG_R1, ARMREG_FP, caller_reg_area_offset);
	/* arg3 == callee stack area */
	arm_addx_imm (code, ARMREG_R2, ARMREG_SP, callee_reg_area_offset);
	/* arg4 = mrgctx reg */
	arm_ldrx (code, ARMREG_R3, ARMREG_FP, rgctx_arg_reg_offset);

	if (aot)
		code = mono_arm_emit_aotconst (&ji, code, buf, ARMREG_IP0, MONO_PATCH_INFO_JIT_ICALL_ADDR, "mono_arm_start_gsharedvt_call");
	else
		code = mono_arm_emit_imm64 (code, ARMREG_IP0, (guint64)mono_arm_start_gsharedvt_call);
	arm_blrx (code, ARMREG_IP0);

	/* Make the real method call */
	/* R0 contains the addr to call */
	arm_movx (code, ARMREG_IP1, ARMREG_R0);
	/* Load rgxctx */
	arm_ldrx (code, MONO_ARCH_RGCTX_REG, ARMREG_FP, rgctx_arg_reg_offset);
	/* Load argument registers */
	// FIXME:
	for (i = 0; i < n_arg_regs; ++i)
		arm_ldrx (code, i, ARMREG_SP, callee_reg_area_offset + (i * 8));
	// FIXME: Only do this if needed
	for (i = 0; i < n_arg_fregs; ++i)
		arm_ldrfpx (code, i, ARMREG_SP, callee_reg_area_offset + ((n_arg_regs + i) * 8));
	/* Clear callee reg area */
	arm_addx_imm (code, ARMREG_SP, ARMREG_SP, ((n_arg_regs + n_arg_fregs) * sizeof (gpointer)) + 8);
	/* Make the call */
	arm_blrx (code, ARMREG_IP1);

	br_ret_index = 0;
	bcc_ret_index = 0;

	// FIXME: Use a switch
	/* Branch between IN/OUT cases */
	arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
	arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, gsharedvt_in));
	br_out = code;
	arm_cbzx (code, ARMREG_IP1, 0);

	/* IN CASE */

	/* IP1 == return marshalling type */
	arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
	arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));

	/* Continue if no marshalling required */
	// FIXME: Use cmpx_imm
	code = mono_arm_emit_imm64 (code, ARMREG_IP0, GSHAREDVT_RET_NONE);
	arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
	bcc_ret [bcc_ret_index ++] = code;
	arm_bcc (code, ARMCOND_EQ, 0);

	/* Compute vret area address in LR */
	arm_ldrx (code, ARMREG_LR, ARMREG_FP, info_offset);
	arm_ldrw (code, ARMREG_LR, ARMREG_LR, MONO_STRUCT_OFFSET (GSharedVtCallInfo, vret_slot));
	arm_subx_imm (code, ARMREG_LR, ARMREG_LR, n_arg_regs + n_arg_fregs);
	arm_lslx (code, ARMREG_LR, ARMREG_LR, 3);
	arm_movspx (code, ARMREG_IP0, ARMREG_SP);
	arm_addx (code, ARMREG_LR, ARMREG_IP0, ARMREG_LR);

	/* Branch to specific marshalling code */
	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		code = mono_arm_emit_imm64 (code, ARMREG_IP0, i);
		arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
		br [i] = code;
		arm_bcc (code, ARMCOND_EQ, 0);
	}

	arm_brk (code, 0);

	/*
	 * The address of the return value area is in LR, have to load it into
	 * registers.
	 */
	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
		switch (i) {
		case GSHAREDVT_RET_NONE:
			break;
		case GSHAREDVT_RET_I8:
			arm_ldrx (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_I1:
			arm_ldrsbx (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_U1:
			arm_ldrb (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_I2:
			arm_ldrshx (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_U2:
			arm_ldrh (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_I4:
			arm_ldrswx (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_U4:
			arm_ldrw (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_R8:
			arm_ldrfpx (code, ARMREG_D0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_R4:
			arm_ldrfpw (code, ARMREG_D0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_IREGS_1:
		case GSHAREDVT_RET_IREGS_2:
		case GSHAREDVT_RET_IREGS_3:
		case GSHAREDVT_RET_IREGS_4:
		case GSHAREDVT_RET_IREGS_5:
		case GSHAREDVT_RET_IREGS_6:
		case GSHAREDVT_RET_IREGS_7:
		case GSHAREDVT_RET_IREGS_8: {
			int j;

			for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
				arm_ldrx (code, j, ARMREG_LR, j * 8);
			break;
		}
		case GSHAREDVT_RET_HFAR8_1:
		case GSHAREDVT_RET_HFAR8_2:
		case GSHAREDVT_RET_HFAR8_3:
		case GSHAREDVT_RET_HFAR8_4: {
			int j;

			for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
				arm_ldrfpx (code, j, ARMREG_LR, j * 8);
			break;
		}
		case GSHAREDVT_RET_HFAR4_1:
		case GSHAREDVT_RET_HFAR4_2:
		case GSHAREDVT_RET_HFAR4_3:
		case GSHAREDVT_RET_HFAR4_4: {
			int j;

			for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
				arm_ldrfpw (code, j, ARMREG_LR, j * 4);
			break;
		}
		default:
			g_assert_not_reached ();
			break;
		}
		br_ret [br_ret_index ++] = code;
		arm_b (code, 0);
	}

	/* OUT CASE */
	mono_arm_patch (br_out, code, MONO_R_ARM64_CBZ);

	/* Compute vret area address in LR */
	arm_ldrx (code, ARMREG_LR, ARMREG_FP, caller_reg_area_offset + (ARMREG_R8 * 8));

	/* IP1 == return marshalling type */
	arm_ldrx (code, ARMREG_IP1, ARMREG_FP, info_offset);
	arm_ldrw (code, ARMREG_IP1, ARMREG_IP1, MONO_STRUCT_OFFSET (GSharedVtCallInfo, ret_marshal));

	/* Branch to specific marshalling code */
	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		code = mono_arm_emit_imm64 (code, ARMREG_IP0, i);
		arm_cmpx (code, ARMREG_IP0, ARMREG_IP1);
		br [i] = code;
		arm_bcc (code, ARMCOND_EQ, 0);
	}

	/*
	 * The return value is in registers, need to save to the return area passed by the caller in
	 * R8.
	 */
	for (i = GSHAREDVT_RET_NONE; i < GSHAREDVT_RET_NUM; ++i) {
		mono_arm_patch (br [i], code, MONO_R_ARM64_BCC);
		switch (i) {
		case GSHAREDVT_RET_NONE:
			break;
		case GSHAREDVT_RET_I8:
			arm_strx (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_I1:
		case GSHAREDVT_RET_U1:
			arm_strb (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_I2:
		case GSHAREDVT_RET_U2:
			arm_strh (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_I4:
		case GSHAREDVT_RET_U4:
			arm_strw (code, ARMREG_R0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_R8:
			arm_strfpx (code, ARMREG_D0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_R4:
			arm_strfpw (code, ARMREG_D0, ARMREG_LR, 0);
			break;
		case GSHAREDVT_RET_IREGS_1:
		case GSHAREDVT_RET_IREGS_2:
		case GSHAREDVT_RET_IREGS_3:
		case GSHAREDVT_RET_IREGS_4:
		case GSHAREDVT_RET_IREGS_5:
		case GSHAREDVT_RET_IREGS_6:
		case GSHAREDVT_RET_IREGS_7:
		case GSHAREDVT_RET_IREGS_8: {
			int j;

			for (j = 0; j < i - GSHAREDVT_RET_IREGS_1 + 1; ++j)
				arm_strx (code, j, ARMREG_LR, j * 8);
			break;
		}
		case GSHAREDVT_RET_HFAR8_1:
		case GSHAREDVT_RET_HFAR8_2:
		case GSHAREDVT_RET_HFAR8_3:
		case GSHAREDVT_RET_HFAR8_4: {
			int j;

			for (j = 0; j < i - GSHAREDVT_RET_HFAR8_1 + 1; ++j)
				arm_strfpx (code, j, ARMREG_LR, j * 8);
			break;
		}
		case GSHAREDVT_RET_HFAR4_1:
		case GSHAREDVT_RET_HFAR4_2:
		case GSHAREDVT_RET_HFAR4_3:
		case GSHAREDVT_RET_HFAR4_4: {
			int j;

			for (j = 0; j < i - GSHAREDVT_RET_HFAR4_1 + 1; ++j)
				arm_strfpw (code, j, ARMREG_LR, j * 4);
			break;
		}
		default:
			arm_brk (code, i);
			break;
		}
		br_ret [br_ret_index ++] = code;
		arm_b (code, 0);
	}

	arm_brk (code, 0);

	for (i = 0; i < br_ret_index; ++i)
		mono_arm_patch (br_ret [i], code, MONO_R_ARM64_B);
	for (i = 0; i < bcc_ret_index; ++i)
		mono_arm_patch (bcc_ret [i], code, MONO_R_ARM64_BCC);

	/* Normal return */
	arm_movspx (code, ARMREG_SP, ARMREG_FP);
	arm_ldpx_post (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, offset);
	arm_retx (code, ARMREG_LR);

	g_assert ((code - buf) < buf_len);

	if (info)
		*info = mono_tramp_info_create ("gsharedvt_trampoline", buf, code - buf, ji, unwind_ops);

	mono_arch_flush_icache (buf, code - buf);
	return buf;
}
Esempio n. 20
0
gpointer
mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot, MonoTrampInfo **info, gboolean aot)
{
	guint8 *tramp;
	guint8 *code, *buf;
	guint8 **rgctx_null_jumps;
	gint32 displace;
	int tramp_size,
	    depth, 
	    index, 
	    iPatch = 0,
	    i;
	gboolean mrgctx;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
	index = MONO_RGCTX_SLOT_INDEX (slot);
	if (mrgctx)
		index += MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (gpointer);
	for (depth = 0; ; ++depth) {
		int size = mono_class_rgctx_get_array_size (depth, mrgctx);

		if (index < size - 1)
			break;
		index -= size - 1;
	}

	tramp_size = 48 + 16 * depth;
	if (mrgctx)
		tramp_size += 4;
	else
		tramp_size += 12;

	code = buf = mono_global_codeman_reserve (tramp_size);

	unwind_ops = mono_arch_get_cie_program ();

	rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));

	if (mrgctx) {
		/* get mrgctx ptr */
		s390_lgr (code, s390_r1, s390_r2);
	} else {
		/* load rgctx ptr from vtable */
		s390_lg (code, s390_r1, 0, s390_r2, MONO_STRUCT_OFFSET(MonoVTable, runtime_generic_context));
		/* is the rgctx ptr null? */
		s390_ltgr (code, s390_r1, s390_r1);
		/* if yes, jump to actual trampoline */
		rgctx_null_jumps [iPatch++] = code;
		s390_jge (code, 0);
	}

	for (i = 0; i < depth; ++i) {
		/* load ptr to next array */
		if (mrgctx && i == 0)
			s390_lg (code, s390_r1, 0, s390_r1, MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT);
		else
			s390_lg (code, s390_r1, 0, s390_r1, 0);
		s390_ltgr (code, s390_r1, s390_r1);
		/* if the ptr is null then jump to actual trampoline */
		rgctx_null_jumps [iPatch++] = code;
		s390_jge (code, 0);
	}

	/* fetch slot */
	s390_lg (code, s390_r1, 0, s390_r1, (sizeof (gpointer) * (index  + 1)));
	/* is the slot null? */
	s390_ltgr (code, s390_r1, s390_r1);
	/* if yes, jump to actual trampoline */
	rgctx_null_jumps [iPatch++] = code;
	s390_jge (code, 0);
	/* otherwise return r1 */
	s390_lgr (code, s390_r2, s390_r1);
	s390_br  (code, s390_r14);

	for (i = 0; i < iPatch; i++) {
		displace = ((uintptr_t) code - (uintptr_t) rgctx_null_jumps[i]) / 2;
		s390_patch_rel ((rgctx_null_jumps [i] + 2), displace);
	}

	g_free (rgctx_null_jumps);

	/* move the rgctx pointer to the VTABLE register */
	s390_lgr (code, MONO_ARCH_VTABLE_REG, s390_r2);

	tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot),
		MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), NULL);

	/* jump to the actual trampoline */
	displace = (tramp - code) / 2;
	s390_jg (code, displace);

	mono_arch_flush_icache (buf, code - buf);
	mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_GENERICS_TRAMPOLINE, NULL);

	g_assert (code - buf <= tramp_size);

	char *name = mono_get_rgctx_fetch_trampoline_name (slot);
	*info = mono_tramp_info_create (name, buf, code - buf, ji, unwind_ops);
	g_free (name);

	return(buf);
}	
Esempio n. 21
0
/* FIXME: Do this in the JIT, where specialized allocation sequences can be created
 * for each class. This is currently not easy to do, as it is hard to generate basic 
 * blocks + branches, but it is easy with the linear IL codebase.
 *
 * For this to work we'd need to solve the TLAB race, first.  Now we
 * require the allocator to be in a few known methods to make sure
 * that they are executed atomically via the restart mechanism.
 */
static MonoMethod*
create_allocator (int atype)
{
	int p_var, size_var;
	guint32 slowpath_branch, max_size_branch;
	MonoMethodBuilder *mb;
	MonoMethod *res;
	MonoMethodSignature *csig;
	static gboolean registered = FALSE;
	int tlab_next_addr_var, new_next_var;
	int num_params, i;
	const char *name = NULL;
	AllocatorWrapperInfo *info;

#ifdef HAVE_KW_THREAD
	int tlab_next_addr_offset = -1;
	int tlab_temp_end_offset = -1;

	MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
	MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);

	mono_tls_key_set_offset (TLS_KEY_SGEN_TLAB_NEXT_ADDR, tlab_next_addr_offset);
	mono_tls_key_set_offset (TLS_KEY_SGEN_TLAB_TEMP_END, tlab_temp_end_offset);

	g_assert (tlab_next_addr_offset != -1);
	g_assert (tlab_temp_end_offset != -1);
#endif

	if (!registered) {
		mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
		mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
		mono_register_jit_icall (mono_gc_alloc_string, "mono_gc_alloc_string", mono_create_icall_signature ("object ptr int int32"), FALSE);
		registered = TRUE;
	}

	if (atype == ATYPE_SMALL) {
		num_params = 2;
		name = "AllocSmall";
	} else if (atype == ATYPE_NORMAL) {
		num_params = 1;
		name = "Alloc";
	} else if (atype == ATYPE_VECTOR) {
		num_params = 2;
		name = "AllocVector";
	} else if (atype == ATYPE_STRING) {
		num_params = 2;
		name = "AllocString";
	} else {
		g_assert_not_reached ();
	}

	csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
	if (atype == ATYPE_STRING) {
		csig->ret = &mono_defaults.string_class->byval_arg;
		csig->params [0] = &mono_defaults.int_class->byval_arg;
		csig->params [1] = &mono_defaults.int32_class->byval_arg;
	} else {
		csig->ret = &mono_defaults.object_class->byval_arg;
		for (i = 0; i < num_params; ++i)
			csig->params [i] = &mono_defaults.int_class->byval_arg;
	}

	mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);

#ifndef DISABLE_JIT
	size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
	if (atype == ATYPE_SMALL) {
		/* size_var = size_arg */
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_stloc (mb, size_var);
	} else if (atype == ATYPE_NORMAL) {
		/* size = vtable->klass->instance_size; */
		mono_mb_emit_ldarg (mb, 0);
		mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
		mono_mb_emit_byte (mb, CEE_ADD);
		mono_mb_emit_byte (mb, CEE_LDIND_I);
		mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, instance_size));
		mono_mb_emit_byte (mb, CEE_ADD);
		/* FIXME: assert instance_size stays a 4 byte integer */
		mono_mb_emit_byte (mb, CEE_LDIND_U4);
		mono_mb_emit_byte (mb, CEE_CONV_I);
		mono_mb_emit_stloc (mb, size_var);
	} else if (atype == ATYPE_VECTOR) {
		MonoExceptionClause *clause;
		int pos, pos_leave, pos_error;
		MonoClass *oom_exc_class;
		MonoMethod *ctor;

		/*
		 * n > MONO_ARRAY_MAX_INDEX => OutOfMemoryException
		 * n < 0                    => OverflowException
		 *
		 * We can do an unsigned comparison to catch both cases, then in the error
		 * case compare signed to distinguish between them.
		 */
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
		mono_mb_emit_byte (mb, CEE_CONV_U);
		pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);

		mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
		mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_icon (mb, 0);
		pos_error = mono_mb_emit_short_branch (mb, CEE_BLT_S);
		mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
		mono_mb_patch_short_branch (mb, pos_error);
		mono_mb_emit_exception (mb, "OverflowException", NULL);

		mono_mb_patch_short_branch (mb, pos);

		clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
		clause->try_offset = mono_mb_get_label (mb);

		/* vtable->klass->sizes.element_size */
		mono_mb_emit_ldarg (mb, 0);
		mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoVTable, klass));
		mono_mb_emit_byte (mb, CEE_ADD);
		mono_mb_emit_byte (mb, CEE_LDIND_I);
		mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoClass, sizes));
		mono_mb_emit_byte (mb, CEE_ADD);
		mono_mb_emit_byte (mb, CEE_LDIND_U4);
		mono_mb_emit_byte (mb, CEE_CONV_I);

		/* * n */
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_byte (mb, CEE_MUL_OVF_UN);
		/* + sizeof (MonoArray) */
		mono_mb_emit_icon (mb, sizeof (MonoArray));
		mono_mb_emit_byte (mb, CEE_ADD_OVF_UN);
		mono_mb_emit_stloc (mb, size_var);

		pos_leave = mono_mb_emit_branch (mb, CEE_LEAVE);

		/* catch */
		clause->flags = MONO_EXCEPTION_CLAUSE_NONE;
		clause->try_len = mono_mb_get_pos (mb) - clause->try_offset;
		clause->data.catch_class = mono_class_from_name (mono_defaults.corlib,
				"System", "OverflowException");
		g_assert (clause->data.catch_class);
		clause->handler_offset = mono_mb_get_label (mb);

		oom_exc_class = mono_class_from_name (mono_defaults.corlib,
				"System", "OutOfMemoryException");
		g_assert (oom_exc_class);
		ctor = mono_class_get_method_from_name (oom_exc_class, ".ctor", 0);
		g_assert (ctor);

		mono_mb_emit_byte (mb, CEE_POP);
		mono_mb_emit_op (mb, CEE_NEWOBJ, ctor);
		mono_mb_emit_byte (mb, CEE_THROW);

		clause->handler_len = mono_mb_get_pos (mb) - clause->handler_offset;
		mono_mb_set_clauses (mb, 1, clause);
		mono_mb_patch_branch (mb, pos_leave);
		/* end catch */
	} else if (atype == ATYPE_STRING) {
		int pos;

		/*
		 * a string allocator method takes the args: (vtable, len)
		 *
		 * bytes = offsetof (MonoString, chars) + ((len + 1) * 2)
		 *
		 * condition:
		 *
		 * bytes <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
		 *
		 * therefore:
		 *
		 * offsetof (MonoString, chars) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
		 * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - offsetof (MonoString, chars)) / 2 - 1
		 */
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - MONO_STRUCT_OFFSET (MonoString, chars)) / 2 - 1);
		pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);

		mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
		mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
		mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
		mono_mb_patch_short_branch (mb, pos);

		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_icon (mb, 1);
		mono_mb_emit_byte (mb, MONO_CEE_SHL);
		//WE manually fold the above + 2 here
		mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, chars) + 2);
		mono_mb_emit_byte (mb, CEE_ADD);
		mono_mb_emit_stloc (mb, size_var);
	} else {
		g_assert_not_reached ();
	}

	if (atype != ATYPE_SMALL) {
		/* size += ALLOC_ALIGN - 1; */
		mono_mb_emit_ldloc (mb, size_var);
		mono_mb_emit_icon (mb, ALLOC_ALIGN - 1);
		mono_mb_emit_byte (mb, CEE_ADD);
		/* size &= ~(ALLOC_ALIGN - 1); */
		mono_mb_emit_icon (mb, ~(ALLOC_ALIGN - 1));
		mono_mb_emit_byte (mb, CEE_AND);
		mono_mb_emit_stloc (mb, size_var);
	}

	/* if (size > MAX_SMALL_OBJ_SIZE) goto slowpath */
	if (atype != ATYPE_SMALL) {
		mono_mb_emit_ldloc (mb, size_var);
		mono_mb_emit_icon (mb, MAX_SMALL_OBJ_SIZE);
		max_size_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BGT_UN_S);
	}

	/*
	 * We need to modify tlab_next, but the JIT only supports reading, so we read
	 * another tls var holding its address instead.
	 */

	/* tlab_next_addr (local) = tlab_next_addr (TLS var) */
	tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
	EMIT_TLS_ACCESS_NEXT_ADDR (mb);
	mono_mb_emit_stloc (mb, tlab_next_addr_var);

	/* p = (void**)tlab_next; */
	p_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
	mono_mb_emit_ldloc (mb, tlab_next_addr_var);
	mono_mb_emit_byte (mb, CEE_LDIND_I);
	mono_mb_emit_stloc (mb, p_var);
	
	/* new_next = (char*)p + size; */
	new_next_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
	mono_mb_emit_ldloc (mb, p_var);
	mono_mb_emit_ldloc (mb, size_var);
	mono_mb_emit_byte (mb, CEE_CONV_I);
	mono_mb_emit_byte (mb, CEE_ADD);
	mono_mb_emit_stloc (mb, new_next_var);

	/* if (G_LIKELY (new_next < tlab_temp_end)) */
	mono_mb_emit_ldloc (mb, new_next_var);
	EMIT_TLS_ACCESS_TEMP_END (mb);
	slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);

	/* Slowpath */
	if (atype != ATYPE_SMALL)
		mono_mb_patch_short_branch (mb, max_size_branch);

	mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
	mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);

	/* FIXME: mono_gc_alloc_obj takes a 'size_t' as an argument, not an int32 */
	mono_mb_emit_ldarg (mb, 0);
	mono_mb_emit_ldloc (mb, size_var);
	if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
		mono_mb_emit_icall (mb, mono_gc_alloc_obj);
	} else if (atype == ATYPE_VECTOR) {
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_icall (mb, mono_gc_alloc_vector);
	} else if (atype == ATYPE_STRING) {
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_icall (mb, mono_gc_alloc_string);
	} else {
		g_assert_not_reached ();
	}
	mono_mb_emit_byte (mb, CEE_RET);

	/* Fastpath */
	mono_mb_patch_short_branch (mb, slowpath_branch);

	/* FIXME: Memory barrier */

	/* tlab_next = new_next */
	mono_mb_emit_ldloc (mb, tlab_next_addr_var);
	mono_mb_emit_ldloc (mb, new_next_var);
	mono_mb_emit_byte (mb, CEE_STIND_I);

	/*The tlab store must be visible before the the vtable store. This could be replaced with a DDS but doing it with IL would be tricky. */
	mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
	mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
	mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);

	/* *p = vtable; */
	mono_mb_emit_ldloc (mb, p_var);
	mono_mb_emit_ldarg (mb, 0);
	mono_mb_emit_byte (mb, CEE_STIND_I);

	if (atype == ATYPE_VECTOR) {
		/* arr->max_length = max_length; */
		mono_mb_emit_ldloc (mb, p_var);
		mono_mb_emit_ldflda (mb, MONO_STRUCT_OFFSET (MonoArray, max_length));
		mono_mb_emit_ldarg (mb, 1);
#ifdef MONO_BIG_ARRAYS
		mono_mb_emit_byte (mb, CEE_STIND_I);
#else
		mono_mb_emit_byte (mb, CEE_STIND_I4);
#endif
	} else 	if (atype == ATYPE_STRING) {
		/* need to set length and clear the last char */
		/* s->length = len; */
		mono_mb_emit_ldloc (mb, p_var);
		mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, length));
		mono_mb_emit_byte (mb, MONO_CEE_ADD);
		mono_mb_emit_ldarg (mb, 1);
		mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
		/* s->chars [len] = 0; */
		mono_mb_emit_ldloc (mb, p_var);
		mono_mb_emit_ldloc (mb, size_var);
		mono_mb_emit_icon (mb, 2);
		mono_mb_emit_byte (mb, MONO_CEE_SUB);
		mono_mb_emit_byte (mb, MONO_CEE_ADD);
		mono_mb_emit_icon (mb, 0);
		mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
	}

	/*
	We must make sure both vtable and max_length are globaly visible before returning to managed land.
	*/
	mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
	mono_mb_emit_byte (mb, CEE_MONO_MEMORY_BARRIER);
	mono_mb_emit_i4 (mb, MONO_MEMORY_BARRIER_REL);

	/* return p */
	mono_mb_emit_ldloc (mb, p_var);
	mono_mb_emit_byte (mb, CEE_RET);
#endif

	res = mono_mb_create_method (mb, csig, 8);
	mono_mb_free (mb);
	mono_method_get_header (res)->init_locals = FALSE;

	info = mono_image_alloc0 (mono_defaults.corlib, sizeof (AllocatorWrapperInfo));
	info->gc_name = "sgen";
	info->alloc_type = atype;
	mono_marshal_set_wrapper_info (res, info);

	return res;
}
Esempio n. 22
0
/*
 * 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).
 */
gpointer
mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
{
	guint8 *start;
	int i;
	guint8 *code;
	guint32 pos;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;
	const guint kMaxCodeSize = NACL_SIZE (128, 256);

	start = code = mono_global_codeman_reserve (kMaxCodeSize);

	/* call_filter (MonoContext *ctx, unsigned long eip) */
	code = start;

	/* Alloc new frame */
	amd64_push_reg (code, AMD64_RBP);
	amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, 8);

	/* Save callee saved regs */
	pos = 0;
	for (i = 0; i < AMD64_NREG; ++i)
		if (AMD64_IS_CALLEE_SAVED_REG (i)) {
			amd64_push_reg (code, i);
			pos += 8;
		}

	/* Save EBP */
	pos += 8;
	amd64_push_reg (code, AMD64_RBP);

	/* Make stack misaligned, the call will make it aligned again */
	if (! (pos & 8))
		amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, 8);

	/* set new EBP */
	amd64_mov_reg_membase (code, AMD64_RBP, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoContext, rbp), 8);
	/* load callee saved regs */
	amd64_mov_reg_membase (code, AMD64_RBX, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoContext, rbx), 8);
	amd64_mov_reg_membase (code, AMD64_R12, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoContext, r12), 8);
	amd64_mov_reg_membase (code, AMD64_R13, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoContext, r13), 8);
	amd64_mov_reg_membase (code, AMD64_R14, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoContext, r14), 8);
#if !defined(__native_client_codegen__)
	amd64_mov_reg_membase (code, AMD64_R15, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoContext, r15), 8);
#endif
#ifdef TARGET_WIN32
	amd64_mov_reg_membase (code, AMD64_RDI, AMD64_ARG_REG1,  MONO_STRUCT_OFFSET (MonoContext, rdi), 8);
	amd64_mov_reg_membase (code, AMD64_RSI, AMD64_ARG_REG1,  MONO_STRUCT_OFFSET (MonoContext, rsi), 8);
#endif

	/* call the handler */
	amd64_call_reg (code, AMD64_ARG_REG2);

	if (! (pos & 8))
		amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, 8);

	/* restore RBP */
	amd64_pop_reg (code, AMD64_RBP);

	/* Restore callee saved regs */
	for (i = AMD64_NREG; i >= 0; --i)
		if (AMD64_IS_CALLEE_SAVED_REG (i))
			amd64_pop_reg (code, i);

	amd64_leave (code);
	amd64_ret (code);

	g_assert ((code - start) < kMaxCodeSize);

	nacl_global_codeman_validate(&start, kMaxCodeSize, &code);

	mono_arch_flush_icache (start, code - start);

	if (info)
		*info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 23
0
/*
 * mono_arch_get_restore_context:
 *
 * Returns a pointer to a method which restores a previously saved sigcontext.
 */
gpointer
mono_arch_get_restore_context (MonoTrampInfo **info, gboolean aot)
{
	guint8 *start = NULL;
	guint8 *code;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;

	/* restore_contect (MonoContext *ctx) */

	start = code = mono_global_codeman_reserve (256);

	amd64_mov_reg_reg (code, AMD64_R11, AMD64_ARG_REG1, 8);

	/* Restore all registers except %rip and %r11 */
	amd64_mov_reg_membase (code, AMD64_RAX, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rax), 8);
	amd64_mov_reg_membase (code, AMD64_RCX, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rcx), 8);
	amd64_mov_reg_membase (code, AMD64_RDX, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rdx), 8);
	amd64_mov_reg_membase (code, AMD64_RBX, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rbx), 8);
	amd64_mov_reg_membase (code, AMD64_RBP, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rbp), 8);
	amd64_mov_reg_membase (code, AMD64_RSI, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rsi), 8);
	amd64_mov_reg_membase (code, AMD64_RDI, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rdi), 8);
	//amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r8), 8);
	//amd64_mov_reg_membase (code, AMD64_R9, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r9), 8);
	//amd64_mov_reg_membase (code, AMD64_R10, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r10), 8);
	amd64_mov_reg_membase (code, AMD64_R12, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r12), 8);
	amd64_mov_reg_membase (code, AMD64_R13, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r13), 8);
	amd64_mov_reg_membase (code, AMD64_R14, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r14), 8);
#if !defined(__native_client_codegen__)
	amd64_mov_reg_membase (code, AMD64_R15, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, r15), 8);
#endif

	/*
	 * The context resides on the stack, in the stack frame of the
	 * caller of this function.  The stack pointer that we need to
	 * restore is potentially many stack frames higher up, so the
	 * distance between them can easily be more than the red zone
	 * size.  Hence the stack pointer can be restored only after
	 * we have finished loading everything from the context.
	 */
	amd64_mov_reg_membase (code, AMD64_R8, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rsp), 8);
	amd64_mov_reg_membase (code, AMD64_R11, AMD64_R11,  MONO_STRUCT_OFFSET (MonoContext, rip), 8);
	amd64_mov_reg_reg (code, AMD64_RSP, AMD64_R8, 8);

	/* jump to the saved IP */
	amd64_jump_reg (code, AMD64_R11);

	nacl_global_codeman_validate(&start, 256, &code);

	mono_arch_flush_icache (start, code - start);

	if (info)
		*info = mono_tramp_info_create ("restore_context", start, code - start, ji, unwind_ops);

	return start;
}
Esempio n. 24
0
gpointer
mono_arch_get_call_filter (MonoTrampInfo **info, gboolean aot)
{
    guint8 *code;
    guint8* start;
    int i, size, offset, gregs_offset, fregs_offset, ctx_offset, num_fregs, frame_size;
    MonoJumpInfo *ji = NULL;
    GSList *unwind_ops = NULL;
    guint8 *labels [16];

    size = 512;
    start = code = mono_global_codeman_reserve (size);

    /* Compute stack frame size and offsets */
    offset = 0;
    /* frame block */
    offset += 2 * 8;
    /* gregs */
    gregs_offset = offset;
    offset += 32 * 8;
    /* fregs */
    num_fregs = 8;
    fregs_offset = offset;
    offset += num_fregs * 8;
    ctx_offset = offset;
    ctx_offset += 8;
    frame_size = ALIGN_TO (offset, MONO_ARCH_FRAME_ALIGNMENT);

    /*
     * We are being called from C code, ctx is in r0, the address to call is in r1.
     * We need to save state, restore ctx, make the call, then restore the previous state,
     * returning the value returned by the call.
     */

    /* Setup a frame */
    arm_stpx_pre (code, ARMREG_FP, ARMREG_LR, ARMREG_SP, -frame_size);
    arm_movspx (code, ARMREG_FP, ARMREG_SP);

    /* Save ctx */
    arm_strx (code, ARMREG_R0, ARMREG_FP, ctx_offset);
    /* Save gregs */
    code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS | (1 << ARMREG_FP), ARMREG_FP, gregs_offset);
    /* Save fregs */
    for (i = 0; i < num_fregs; ++i)
        arm_strfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8));

    /* Load regs from ctx */
    code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs));
    /* Load fregs */
    arm_ldrx (code, ARMREG_IP0, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, has_fregs));
    labels [0] = code;
    arm_cbzx (code, ARMREG_IP0, 0);
    for (i = 0; i < num_fregs; ++i)
        arm_ldrfpx (code, ARMREG_D8 + i, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, fregs) + (i * 8));
    mono_arm_patch (labels [0], code, MONO_R_ARM64_CBZ);
    /* Load fp */
    arm_ldrx (code, ARMREG_FP, ARMREG_R0, MONO_STRUCT_OFFSET (MonoContext, regs) + (ARMREG_FP * 8));

    /* Make the call */
    arm_blrx (code, ARMREG_R1);
    /* For filters, the result is in R0 */

    /* Restore fp */
    arm_ldrx (code, ARMREG_FP, ARMREG_SP, gregs_offset + (ARMREG_FP * 8));
    /* Load ctx */
    arm_ldrx (code, ARMREG_IP0, ARMREG_FP, ctx_offset);
    /* Save registers back to ctx */
    /* This isn't strictly neccessary since we don't allocate variables used in eh clauses to registers */
    code = mono_arm_emit_store_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_IP0, MONO_STRUCT_OFFSET (MonoContext, regs));

    /* Restore regs */
    code = mono_arm_emit_load_regarray (code, MONO_ARCH_CALLEE_SAVED_REGS, ARMREG_FP, gregs_offset);
    /* Restore fregs */
    for (i = 0; i < num_fregs; ++i)
        arm_ldrfpx (code, ARMREG_D8 + i, ARMREG_FP, fregs_offset + (i * 8));
    /* Destroy frame */
    code = mono_arm_emit_destroy_frame (code, frame_size, (1 << ARMREG_IP0));
    arm_retx (code, ARMREG_LR);

    g_assert ((code - start) < size);
    mono_arch_flush_icache (start, code - start);
    mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_EXCEPTION_HANDLING, NULL);

    if (info)
        *info = mono_tramp_info_create ("call_filter", start, code - start, ji, unwind_ops);

    return start;
}
Esempio n. 25
0
gpointer
mono_arch_create_monitor_exit_trampoline (MonoTrampInfo **info, gboolean aot)
{
	guint8	*tramp,
		*code, *buf;
	gint16	*jump_obj_null, 
		*jump_have_waiters, 
		*jump_sync_null, 
		*jump_not_owned, 
		*jump_cs_failed,
		*jump_next,
		*jump_sync_thin_hash = NULL;
	int	tramp_size,
		status_offset, nest_offset;
	MonoJumpInfo *ji = NULL;
	GSList *unwind_ops = NULL;
	int	obj_reg = s390_r2,
		sync_reg = s390_r3,
		status_reg = s390_r4;

	g_assert (obj_reg == MONO_ARCH_MONITOR_OBJECT_REG);

	mono_monitor_threads_sync_members_offset (&status_offset, &nest_offset);
	g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (status_offset) == sizeof (guint32));
	g_assert (MONO_THREADS_SYNC_MEMBER_SIZE (nest_offset) == sizeof (guint32));
	status_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (status_offset);
	nest_offset = MONO_THREADS_SYNC_MEMBER_OFFSET (nest_offset);

	tramp_size = 160;

	code = buf = mono_global_codeman_reserve (tramp_size);

	unwind_ops = mono_arch_get_cie_program ();

	if (mono_thread_get_tls_offset () != -1) {
		/* MonoObject* obj is in obj_reg */
		/* is obj null? */
		s390_ltgr (code, obj_reg, obj_reg);
		/* if yes, jump to actual trampoline */
		s390_jz (code, 0); CODEPTR(code, jump_obj_null);

		/* load obj->synchronization to RCX */
		s390_lg (code, sync_reg, 0, obj_reg, MONO_STRUCT_OFFSET (MonoObject, synchronisation));

		if (mono_gc_is_moving ()) {
			/*if bit zero is set it's a thin hash*/
			s390_tmll (code, sync_reg, 1);
			s390_jo   (code, 0); CODEPTR(code, jump_sync_thin_hash);

			/* Clear bits used by the gc */
			s390_nill (code, sync_reg, ~0x3);
		}

		/* is synchronization null? */
		s390_ltgr (code, sync_reg, sync_reg);
		/* if yes, jump to actual trampoline */
		s390_jz (code, 0); CODEPTR(code, jump_sync_null);

		/* next case: synchronization is not null */
		/* load MonoInternalThread* into r5 */
		s390_ear (code, s390_r5, 0);
		s390_sllg(code, s390_r5, s390_r5, 0, 32);
		s390_ear (code, s390_r5, 1);
		/* load TID into r1 */
		s390_lg  (code, s390_r1, 0, s390_r5, mono_thread_get_tls_offset ());
		s390_lgf (code, s390_r1, 0, s390_r1, MONO_STRUCT_OFFSET (MonoInternalThread, small_id));
		/* is synchronization->owner == TID */
		s390_lgf (code, status_reg, 0, sync_reg, status_offset);
		s390_xr  (code, s390_r1, status_reg);
		s390_tmlh (code, s390_r1, OWNER_MASK);
		/* if not, jump to actual trampoline */
		s390_jno (code, 0); CODEPTR(code, jump_not_owned);

		/* next case: synchronization->owner == TID */
		/* is synchronization->nest == 1 */
		s390_lgf (code, s390_r0, 0, sync_reg, nest_offset);
		s390_chi (code, s390_r0, 1);
		/* if not, jump to next case */
		s390_jne (code, 0); CODEPTR(code, jump_next);
		/* if yes, is synchronization->entry_count greater than zero */
		s390_cfi (code, status_reg, ENTRY_COUNT_WAITERS);
		/* if not, jump to actual trampoline */
		s390_jnz (code, 0); CODEPTR(code, jump_have_waiters);
		/* if yes, try to set synchronization->owner to null and return */
		/* old status in s390_r0 */
		s390_lgfr (code, s390_r0, status_reg);
		/* form new status */
		s390_nilf (code, status_reg, ENTRY_COUNT_MASK);
		/* compare and exchange */
		s390_cs (code, s390_r0, status_reg, sync_reg, status_offset);
		/* if not successful, jump to actual trampoline */
		s390_jnz (code, 0); CODEPTR(code, jump_cs_failed);
		s390_br  (code, s390_r14);

		/* next case: synchronization->nest is not 1 */
		PTRSLOT (code, jump_next);
		/* decrease synchronization->nest and return */
		s390_lgf (code, s390_r0, 0, sync_reg, nest_offset);
		s390_ahi (code, s390_r0, -1);
		s390_st  (code, s390_r0, 0, sync_reg, nest_offset);
		s390_br  (code, s390_r14);

		PTRSLOT (code, jump_obj_null);
		if (jump_sync_thin_hash)
			PTRSLOT (code, jump_sync_thin_hash);
		PTRSLOT (code, jump_have_waiters);
		PTRSLOT (code, jump_not_owned);
		PTRSLOT (code, jump_cs_failed);
		PTRSLOT (code, jump_sync_null);
	}

	/* jump to the actual trampoline */
	tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_MONITOR_EXIT, mono_get_root_domain (), NULL);

	S390_SET (code, s390_r1, tramp);
	s390_br (code, s390_r1);

	mono_arch_flush_icache (code, code - buf);
	mono_profiler_code_buffer_new (buf, code - buf, MONO_PROFILER_CODE_BUFFER_MONITOR, NULL);
	g_assert (code - buf <= tramp_size);

	if (info)
		*info = mono_tramp_info_create ("monitor_exit_trampoline", buf, code - buf, ji, unwind_ops);

	return buf;
}