DECL_OFFSET(MonoContext, llvm_exc_reg)
DECL_OFFSET(MonoLMF, lmf_addr)
+DECL_OFFSET(MonoLMF, method)
#elif defined(TARGET_X86)
DECL_OFFSET(MonoContext, eax)
+#include "mini-runtime.h"
#include "mini.h"
static void
gboolean
mono_arch_unwind_frame (MonoDomain *domain, MonoJitTlsData *jit_tls,
- MonoJitInfo *ji, MonoContext *ctx,
- MonoContext *new_ctx, MonoLMF **lmf,
- host_mgreg_t **save_locations,
- StackFrameInfo *frame)
+ MonoJitInfo *ji, MonoContext *ctx,
+ MonoContext *new_ctx, MonoLMF **lmf,
+ host_mgreg_t **save_locations,
+ StackFrameInfo *frame)
{
- if (ji)
- g_error ("Can't unwind compiled code");
+ memset (frame, 0, sizeof (StackFrameInfo));
+ frame->ji = ji;
+ *new_ctx = *ctx;
+
+ g_assert (!ji);
+
+ /*
+ * Can't unwind native frames on WASM, so we only process the ones
+ * which push an LMF frame. See the needs_stack_walk code in
+ * method-to-ir.c.
+ */
if (*lmf) {
- if ((*lmf)->top_entry)
+ ERROR_DECL (error);
+
+ if (*lmf == jit_tls->first_lmf)
return FALSE;
- g_error ("Can't handle non-top-entry LMFs\n");
+
+ /* This will compute the original method address */
+ g_assert ((*lmf)->method);
+ gpointer addr = mono_compile_method_checked ((*lmf)->method, error);
+ mono_error_assert_ok (error);
+
+ ji = mini_jit_info_table_find (domain, addr, NULL);
+ g_assert (ji);
+
+ frame->type = FRAME_TYPE_MANAGED;
+ frame->ji = ji;
+ frame->actual_method = (*lmf)->method;
+
+ *lmf = (MonoLMF *)(((guint64)(*lmf)->previous_lmf) & ~3);
+ return TRUE;
}
return FALSE;
if (!cfg->lmf_addr_var)
cfg->lmf_addr_var = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+ if (!cfg->lmf_var) {
+ MonoInst *lmf_var = mono_compile_create_var (cfg, mono_get_int_type (), OP_LOCAL);
+ lmf_var->flags |= MONO_INST_VOLATILE;
+ lmf_var->flags |= MONO_INST_LMF;
+ cfg->lmf_var = lmf_var;
+ }
+
lmf_ins = mono_create_tls_get (cfg, TLS_KEY_LMF_ADDR);
g_assert (lmf_ins);
gboolean direct_icall; direct_icall = FALSE;
gboolean tailcall_calli; tailcall_calli = FALSE;
gboolean noreturn; noreturn = FALSE;
+ gboolean needs_stack_walk; needs_stack_walk = FALSE;
// Variables shared by CEE_CALLI and CEE_CALL/CEE_CALLVIRT.
common_call = FALSE;
if (mono_security_core_clr_enabled ())
ensure_method_is_allowed_to_call_method (cfg, method, cil_method);
+ if (cfg->llvm_only && cmethod && method_needs_stack_walk (cfg, cmethod))
+ needs_stack_walk = TRUE;
+
if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
if (!mono_class_is_interface (method->klass))
emit_bad_image_failure (cfg, method, cil_method);
INLINE_FAILURE ("call");
common_call = TRUE;
+#ifdef TARGET_WASM
+ /* Push an LMF so these frames can be enumerated during stack walks by mono_arch_unwind_frame () */
+ if (needs_stack_walk) {
+ MonoInst *method_ins;
+ int lmf_reg;
+
+ emit_push_lmf (cfg);
+
+ EMIT_NEW_VARLOADA (cfg, ins, cfg->lmf_var, NULL);
+ lmf_reg = ins->dreg;
+
+ /* The lmf->method field will be used to look up the MonoJitInfo for this method */
+ method_ins = emit_get_rgctx_method (cfg, mono_method_check_context_used (cfg->method), cfg->method, MONO_RGCTX_INFO_METHOD);
+ EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, lmf_reg, MONO_STRUCT_OFFSET (MonoLMF, method), method_ins->dreg);
+ }
+#endif
+
call_end:
// Check that the decision to tailcall would not have changed.
g_assert (!called_is_supported_tailcall || tailcall_method == method);
if (cmethod)
ins = handle_call_res_devirt (cfg, cmethod, ins);
+#ifdef TARGET_WASM
+ if (common_call && needs_stack_walk)
+ emit_pop_lmf (cfg);
+#endif
+
if (noreturn) {
MONO_INST_NEW (cfg, ins, OP_NOT_REACHED);
MONO_ADD_INS (cfg->cbb, ins);
frame.il_offset = il_offset;
- if ((unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD) && frame.ji) {
- frame.actual_method = get_method_from_stack_frame (frame.ji, get_generic_info_from_stack_frame (frame.ji, &ctx));
- } else {
- frame.actual_method = frame.method;
+ /* actual_method might already be set by mono_arch_unwind_frame () */
+ if (!frame.actual_method) {
+ if ((unwind_options & MONO_UNWIND_LOOKUP_ACTUAL_METHOD) && frame.ji)
+ frame.actual_method = get_method_from_stack_frame (frame.ji, get_generic_info_from_stack_frame (frame.ji, &ctx));
+ else
+ frame.actual_method = frame.method;
}
if (get_reg_locations)
return;
/* Could be already created by an OP_VPHI */
if (!ctx->addresses [var->dreg]) {
- ctx->addresses [var->dreg] = build_alloca (ctx, var->inst_vtype);
+ if (var->flags & MONO_INST_LMF)
+ ctx->addresses [var->dreg] = build_alloca_llvm_type (ctx, LLVMArrayType (LLVMInt8Type (), MONO_ABI_SIZEOF (MonoLMF)), sizeof (target_mgreg_t));
+ else
+ ctx->addresses [var->dreg] = build_alloca (ctx, var->inst_vtype);
//LLVMSetValueName (ctx->addresses [var->dreg], g_strdup_printf ("vreg_loc_%d", var->dreg));
}
ctx->vreg_cli_types [var->dreg] = var->inst_vtype;
} else {
mono_arch_create_vars (cfg);
}
+
+ cfg->lmf_ir = TRUE;
}
/*
#define MONO_LMFEXT_INTERP_EXIT_WITH_CTX 3
/*
+ * The MonoLMF structure is arch specific, it includes at least these fields.
+ * LMF means 'last-managed-frame'. Originally, these were allocated
+ * on the stack to mark the last frame before transitioning to
+ * native code, but currently, they are used to mark all kinds of
+ * other transitions as well, see MonoLMFExt.
+ */
+#if 0
+typedef struct {
+ /*
+ * If the second lowest bit is set to 1, then this is a MonoLMFExt structure, and
+ * the other fields are not valid.
+ */
+ gpointer previous_lmf;
+ gpointer lmf_addr;
+} MonoLMF;
+#endif
+
+/*
* This structure is an extension of MonoLMF and contains extra information.
*/
typedef struct {
if (cfg->gen_sdb_seq_points)
g_error ("gen_sdb_seq_points not supported");
- if (cfg->method->save_lmf)
+ if (cfg->method->save_lmf) {
cfg->create_lmf_var = TRUE;
-
- if (cfg->method->save_lmf)
cfg->lmf_ir = TRUE;
+ }
}
void
gpointer previous_lmf;
gpointer lmf_addr;
- /* This is set to signal this is the top lmf entry */
- gboolean top_entry;
+ MonoMethod *method;
};
typedef struct {
gpointer cinfo;
} MonoCompileArch;
-#define MONO_ARCH_INIT_TOP_LMF_ENTRY(lmf) do { (lmf)->top_entry = TRUE; } while (0)
+#define MONO_ARCH_INIT_TOP_LMF_ENTRY(lmf) do { } while (0)
#define MONO_CONTEXT_SET_LLVM_EXC_REG(ctx, exc) do { (ctx)->llvm_exc_reg = (gsize)exc; } while (0)