-/*
- * mini-amd64.c: AMD64 backend for the Mono code generator
+/**
+ * \file
+ * AMD64 backend for the Mono code generator
*
* Based on mini-x86.c.
*
#include "mini.h"
#include <string.h>
#include <math.h>
+#include <assert.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
/**
* mono_arch_compute_omit_fp:
- *
- * Determine whenever the frame pointer can be eliminated.
+ * Determine whether the frame pointer can be eliminated.
*/
static void
mono_arch_compute_omit_fp (MonoCompile *cfg)
if (cfg->method->save_lmf) {
cfg->lmf_ir = TRUE;
-#if !defined(TARGET_WIN32)
- if (!optimize_for_xen)
- cfg->lmf_ir_mono_lmf = TRUE;
-#endif
}
}
}
/*
- * mono_amd64_emit_tls_get:
- * @code: buffer to store code to
- * @dreg: hard register where to place the result
- * @tls_offset: offset info
+ * \param code buffer to store code to
+ * \param dreg hard register where to place the result
+ * \param tls_offset offset info
+ * \return a pointer to the end of the stored code
*
- * mono_amd64_emit_tls_get emits in @code the native code that puts in
+ * mono_amd64_emit_tls_get emits in \p code the native code that puts in
* the dreg register the item in the thread local storage identified
* by tls_offset.
- *
- * Returns: a pointer to the end of the stored code
*/
static guint8*
mono_amd64_emit_tls_get (guint8* code, int dreg, int tls_offset)
/* Copy arguments on the stack to our argument area */
for (i = 0; i < call->stack_usage; i += sizeof(mgreg_t)) {
amd64_mov_reg_membase (code, AMD64_RAX, AMD64_RSP, i, sizeof(mgreg_t));
- amd64_mov_membase_reg (code, AMD64_RBP, 16 + i, AMD64_RAX, sizeof(mgreg_t));
+ amd64_mov_membase_reg (code, AMD64_RBP, ARGS_OFFSET + i, AMD64_RAX, sizeof(mgreg_t));
}
+#ifdef TARGET_WIN32
+ amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+ amd64_pop_reg (code, AMD64_RBP);
+ mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
+#else
amd64_leave (code);
+#endif
}
offset = code - cfg->native_code;
{
/* The signature doesn't matter */
mono_register_jit_icall (mono_amd64_throw_exception, "mono_amd64_throw_exception", mono_create_icall_signature ("void"), TRUE);
+
+#if defined(TARGET_WIN32) || defined(HOST_WIN32)
+#if _MSC_VER
+ extern void __chkstk (void);
+ mono_register_jit_icall_full (__chkstk, "mono_chkstk_win64", NULL, TRUE, FALSE, "__chkstk");
+#else
+ extern void ___chkstk_ms (void);
+ mono_register_jit_icall_full (___chkstk_ms, "mono_chkstk_win64", NULL, TRUE, FALSE, "___chkstk_ms");
+#endif
+#endif
}
void
} \
} while (0)
+#ifdef TARGET_WIN32
+static guint8 *
+emit_prolog_setup_sp_win64 (MonoCompile *cfg, guint8 *code, int alloc_size, int *cfa_offset_input)
+{
+ int cfa_offset = *cfa_offset_input;
+
+ /* Allocate windows stack frame using stack probing method */
+ if (alloc_size) {
+
+ if (alloc_size >= 0x1000) {
+ amd64_mov_reg_imm (code, AMD64_RAX, alloc_size);
+ code = emit_call_body (cfg, code, MONO_PATCH_INFO_INTERNAL_METHOD, "mono_chkstk_win64");
+ }
+
+ amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, alloc_size);
+ if (cfg->arch.omit_fp) {
+ cfa_offset += alloc_size;
+ mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
+ async_exc_point (code);
+ }
+
+ // NOTE, in a standard win64 prolog the alloc unwind info is always emitted, but since mono
+ // uses a frame pointer with negative offsets and a standard win64 prolog assumes positive offsets, we can't
+ // emit sp alloc unwind metadata since the native OS unwinder will incorrectly restore sp. Excluding the alloc
+ // metadata on the other hand won't give the OS the information so it can just restore the frame pointer to sp and
+ // that will retrieve the expected results.
+ if (cfg->arch.omit_fp)
+ mono_emit_unwind_op_sp_alloc (cfg, code, alloc_size);
+ }
+
+ *cfa_offset_input = cfa_offset;
+ return code;
+}
+#endif /* TARGET_WIN32 */
+
guint8 *
mono_arch_emit_prolog (MonoCompile *cfg)
{
/*
* The prolog consists of the following parts:
* FP present:
- * - push rbp, mov rbp, rsp
- * - save callee saved regs using pushes
+ * - push rbp
+ * - mov rbp, rsp
+ * - save callee saved regs using moves
* - allocate frame
* - save rgctx if needed
* - save lmf if needed
mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
mono_emit_unwind_op_offset (cfg, code, AMD64_RBP, - cfa_offset);
async_exc_point (code);
-#ifdef TARGET_WIN32
- mono_arch_unwindinfo_add_push_nonvol (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
-#endif
/* These are handled automatically by the stack marking code */
mini_gc_set_slot_type_from_cfa (cfg, -cfa_offset, SLOT_NOREF);
-
+
amd64_mov_reg_reg (code, AMD64_RBP, AMD64_RSP, sizeof(mgreg_t));
mono_emit_unwind_op_def_cfa_reg (cfg, code, AMD64_RBP);
+ mono_emit_unwind_op_fp_alloc (cfg, code, AMD64_RBP, 0);
async_exc_point (code);
-#ifdef TARGET_WIN32
- mono_arch_unwindinfo_add_set_fpreg (&cfg->arch.unwindinfo, cfg->native_code, code, AMD64_RBP);
-#endif
}
/* The param area is always at offset 0 from sp */
cfg->arch.stack_alloc_size = alloc_size;
/* Allocate stack frame */
+#ifdef TARGET_WIN32
+ code = emit_prolog_setup_sp_win64 (cfg, code, alloc_size, &cfa_offset);
+#else
if (alloc_size) {
/* See mono_emit_stack_alloc */
-#if defined(TARGET_WIN32) || defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
+#if defined(MONO_ARCH_SIGSEGV_ON_ALTSTACK)
guint32 remaining_size = alloc_size;
/*FIXME handle unbounded code expansion, we should use a loop in case of more than X interactions*/
guint32 required_code_size = ((remaining_size / 0x1000) + 1) * 11; /*11 is the max size of amd64_alu_reg_imm + amd64_test_membase_reg*/
mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
}
async_exc_point (code);
-#ifdef TARGET_WIN32
- if (cfg->arch.omit_fp)
- mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, 0x1000);
-#endif
amd64_test_membase_reg (code, AMD64_RSP, 0, AMD64_RSP);
remaining_size -= 0x1000;
mono_emit_unwind_op_def_cfa_offset (cfg, code, cfa_offset);
async_exc_point (code);
}
-#ifdef TARGET_WIN32
- if (cfg->arch.omit_fp)
- mono_arch_unwindinfo_add_alloc_stack (&cfg->arch.unwindinfo, cfg->native_code, code, remaining_size);
-#endif
}
#else
amd64_alu_reg_imm (code, X86_SUB, AMD64_RSP, alloc_size);
}
#endif
}
+#endif
/* Stack alignment check */
#if 0
amd64_alu_reg_imm (code, X86_ADD, AMD64_RSP, cfg->arch.stack_alloc_size);
}
} else {
+#ifdef TARGET_WIN32
+ amd64_lea_membase (code, AMD64_RSP, AMD64_RBP, 0);
+ amd64_pop_reg (code, AMD64_RBP);
+ mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
+#else
amd64_leave (code);
mono_emit_unwind_op_same_value (cfg, code, AMD64_RBP);
+#endif
}
mono_emit_unwind_op_def_cfa (cfg, code, AMD64_RSP, 8);
async_exc_point (code);
}
/**
- * mono_breakpoint_clean_code:
+ * \return TRUE if no sw breakpoint was present.
*
- * Copy @size bytes from @code - @offset to the buffer @buf. If the debugger inserted software
+ * Copy \p size bytes from \p code - \p offset to the buffer \p buf. If the debugger inserted software
* breakpoints in the original code, they are removed in the copy.
- *
- * Returns TRUE if no sw breakpoint was present.
*/
gboolean
mono_breakpoint_clean_code (guint8 *method_start, guint8 *code, int offset, guint8 *buf, int size)
unwind_ops = mono_arch_get_cie_program ();
if (has_target) {
- start = code = (guint8 *)mono_global_codeman_reserve (64);
+ start = code = (guint8 *)mono_global_codeman_reserve (64 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
/* Replace the this argument with the target */
amd64_mov_reg_reg (code, AMD64_RAX, AMD64_ARG_REG1, 8);
amd64_jump_membase (code, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
g_assert ((code - start) < 64);
+ g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
} else {
- start = code = (guint8 *)mono_global_codeman_reserve (64);
+ start = code = (guint8 *)mono_global_codeman_reserve (64 + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
if (param_count == 0) {
amd64_jump_membase (code, AMD64_ARG_REG1, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
amd64_jump_membase (code, AMD64_RAX, MONO_STRUCT_OFFSET (MonoDelegate, method_ptr));
}
g_assert ((code - start) < 64);
+ g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
}
mono_arch_flush_icache (start, code - start);
if (offset / (int)sizeof (gpointer) > MAX_VIRTUAL_DELEGATE_OFFSET)
return NULL;
- start = code = (guint8 *)mono_global_codeman_reserve (size);
+ start = code = (guint8 *)mono_global_codeman_reserve (size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
unwind_ops = mono_arch_get_cie_program ();
size += item->chunk_size;
}
if (fail_tramp)
- code = (guint8 *)mono_method_alloc_generic_virtual_trampoline (domain, size);
+ code = (guint8 *)mono_method_alloc_generic_virtual_trampoline (domain, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
else
- code = (guint8 *)mono_domain_code_reserve (domain, size);
+ code = (guint8 *)mono_domain_code_reserve (domain, size + MONO_TRAMPOLINE_UNWINDINFO_SIZE(0));
start = code;
unwind_ops = mono_arch_get_cie_program ();
if (!fail_tramp)
mono_stats.imt_trampolines_size += code - start;
g_assert (code - start <= size);
+ g_assert_checked (mono_arch_unwindinfo_validate_size (unwind_ops, MONO_TRAMPOLINE_UNWINDINFO_SIZE(0)));
mono_profiler_code_buffer_new (start, code - start, MONO_PROFILER_CODE_BUFFER_IMT_TRAMPOLINE, NULL);