}
/*
- * alloc_frame:
+ * reinit_frame:
*
- * Allocate a new frame from the frame stack.
+ * Reinitialize a frame.
*/
-static InterpFrame*
-alloc_frame (ThreadContext *ctx, gpointer native_stack_addr, InterpFrame *parent, InterpMethod *imethod, stackval *stack_args, stackval *retval)
+static void
+reinit_frame (InterpFrame *frame, InterpFrame *parent, InterpMethod *imethod, stackval *stack_args, stackval *retval)
{
- StackFragment *frag;
- InterpFrame *frame;
-
- // FIXME: Add stack overflow checks
- frame = (InterpFrame*)frame_stack_alloc (&ctx->iframe_stack, sizeof (InterpFrame), &frag);
-
- frame->iframe_frag = frag;
frame->parent = parent;
- frame->native_stack_addr = native_stack_addr;
frame->imethod = imethod;
frame->stack_args = stack_args;
frame->retval = retval;
frame->stack = NULL;
frame->ip = NULL;
frame->state.ip = NULL;
-
- return frame;
}
/*
alloc_stack_data (ThreadContext *ctx, InterpFrame *frame, int size)
{
StackFragment *frag;
- gpointer res;
-
- res = frame_stack_alloc (&ctx->data_stack, size, &frag);
+ gpointer res = frame_stack_alloc (&ctx->data_stack, size, &frag);
frame->stack = (stackval*)res;
frame->data_frag = frag;
{
if (frame->stack)
frame_stack_pop (&context->data_stack, frame->data_frag, frame->stack);
- frame_stack_pop (&context->iframe_stack, frame->iframe_frag, frame);
}
-#define interp_exec_method(frame, context, error) interp_exec_method_full ((frame), (context), NULL, error)
-
/*
* List of classes whose methods will be executed by transitioning to JITted code.
* Used for testing.
static gboolean interp_init_done = FALSE;
-static void interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClauseArgs *clause_args, MonoError *error);
+static void
+interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs *clause_args, MonoError *error);
static InterpMethod* lookup_method_pointer (gpointer addr);
if (context == NULL) {
context = g_new0 (ThreadContext, 1);
/*
- * Use two stacks, one for InterpFrame structures, one for data.
- * This is useful because InterpFrame structures don't need to be GC tracked.
+ * Initialize data stack.
+ * There are multiple advantages to this being separate from the frame stack.
+ * Frame stack can be alloca.
+ * Frame stack can be perfectly fit (if heap).
+ * Frame stack can skip GC tracking.
*/
- frame_stack_init (&context->iframe_stack, 8192);
frame_stack_init (&context->data_stack, 8192);
set_context (context);
}
{
ThreadContext *context = (ThreadContext*)ctx;
- frame_stack_free (&context->iframe_stack);
frame_stack_free (&context->data_stack);
g_free (context);
}
MonoContext ctx;
memset (&ctx, 0, sizeof (MonoContext));
- MONO_CONTEXT_SET_SP (&ctx, frame->native_stack_addr);
+ MONO_CONTEXT_SET_SP (&ctx, frame);
/*
* Call the JIT EH code. The EH code will call back to us using:
get_interp_to_native_trampoline () (addr, ccontext);
}
-/* MONO_NO_OPTIMIATION is needed due to usage of INTERP_PUSH_LMF_WITH_CTX. */
+/* MONO_NO_OPTIMIZATION is needed due to usage of INTERP_PUSH_LMF_WITH_CTX. */
#ifdef _MSC_VER
#pragma optimize ("", off)
#endif
static MONO_NO_OPTIMIZATION MONO_NEVER_INLINE void
-ves_pinvoke_method (InterpFrame *frame, MonoMethodSignature *sig, MonoFuncV addr, ThreadContext *context, gboolean save_last_error)
+ves_pinvoke_method (
+ MonoMethodSignature *sig,
+ MonoFuncV addr,
+ ThreadContext *context,
+ InterpFrame *parent_frame,
+ stackval *retval,
+ gboolean save_last_error,
+ stackval *sp)
{
+ InterpFrame frame = {parent_frame, NULL, sp, retval};
+
MonoLMFExt ext;
gpointer args;
- g_assert (!frame->imethod);
+ g_assert (!frame.imethod);
static MonoPIFunc entry_func = NULL;
if (!entry_func) {
#ifdef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP
CallContext ccontext;
MONO_ENTER_GC_UNSAFE;
- mono_arch_set_native_call_context_args (&ccontext, frame, sig);
+ mono_arch_set_native_call_context_args (&ccontext, &frame, sig);
MONO_EXIT_GC_UNSAFE;
args = &ccontext;
#else
- InterpMethodArguments *margs = build_args_from_sig (sig, frame);
+ InterpMethodArguments *margs = build_args_from_sig (sig, &frame);
args = margs;
#endif
- INTERP_PUSH_LMF_WITH_CTX (frame, ext, exit_pinvoke);
+ INTERP_PUSH_LMF_WITH_CTX (&frame, ext, exit_pinvoke);
entry_func ((gpointer) addr, args);
if (save_last_error)
mono_marshal_set_last_error ();
#ifdef MONO_ARCH_HAVE_INTERP_PINVOKE_TRAMP
if (!context->has_resume_state) {
MONO_ENTER_GC_UNSAFE;
- mono_arch_get_native_call_context_ret (&ccontext, frame, sig);
+ mono_arch_get_native_call_context_ret (&ccontext, &frame, sig);
MONO_EXIT_GC_UNSAFE;
}
- if (ccontext.stack != NULL)
- g_free (ccontext.stack);
+ g_free (ccontext.stack);
#else
if (!context->has_resume_state && !MONO_TYPE_ISSTRUCT (sig->ret))
- stackval_from_data (sig->ret, frame->retval, (char*)&frame->retval->data.p, sig->pinvoke);
+ stackval_from_data (sig->ret, frame.retval, (char*)&frame.retval->data.p, sig->pinvoke);
g_free (margs->iargs);
g_free (margs->fargs);
#define CHECK_MUL_OVERFLOW_NAT_UN(a,b) CHECK_MUL_OVERFLOW64_UN(a,b)
#endif
-static MonoObject*
+// Do not inline in case order of frame addresses matters.
+static MONO_NEVER_INLINE MonoObject*
interp_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObject **exc, MonoError *error)
{
- InterpFrame *frame;
ThreadContext *context = get_context ();
MonoMethodSignature *sig = mono_method_signature_internal (method);
MonoClass *klass = mono_class_from_mono_type_internal (sig->ret);
InterpMethod *imethod = mono_interp_get_imethod (domain, invoke_wrapper, error);
mono_error_assert_ok (error);
- frame = alloc_frame (context, &result, NULL, imethod, args, &result);
- interp_exec_method (frame, context, error);
+ InterpFrame frame = {NULL, imethod, args, &result};
+
+ interp_exec_method (&frame, context, NULL, error);
if (context->has_resume_state) {
// This can happen on wasm !?
} InterpEntryData;
/* Main function for entering the interpreter from compiled code */
-static void
+// Do not inline in case order of frame addresses matters.
+static MONO_NEVER_INLINE void
interp_entry (InterpEntryData *data)
{
- InterpFrame *frame;
InterpMethod *rmethod;
ThreadContext *context;
stackval result;
}
memset (&result, 0, sizeof (result));
- frame = alloc_frame (context, &result, NULL, data->rmethod, args, &result);
+
+ InterpFrame frame = {NULL, data->rmethod, args, &result};
type = rmethod->rtype;
switch (type->type) {
}
ERROR_DECL (error);
- interp_exec_method (frame, context, error);
+ interp_exec_method (&frame, context, NULL, error);
g_assert (!context->has_resume_state);
return sp;
}
-/* MONO_NO_OPTIMIATION is needed due to usage of INTERP_PUSH_LMF_WITH_CTX. */
+/* MONO_NO_OPTIMIZATION is needed due to usage of INTERP_PUSH_LMF_WITH_CTX. */
#ifdef _MSC_VER
#pragma optimize ("", off)
#endif
+// Do not inline in case order of frame addresses matters, and maybe other reasons.
static MONO_NO_OPTIMIZATION MONO_NEVER_INLINE stackval *
do_icall_wrapper (InterpFrame *frame, MonoMethodSignature *sig, int op, stackval *sp, gpointer ptr, gboolean save_last_error)
{
} \
} while (0)
-static void
+// Do not inline in case order of frame addresses matters.
+static MONO_NEVER_INLINE void
interp_entry_from_trampoline (gpointer ccontext_untyped, gpointer rmethod_untyped)
{
- InterpFrame *frame;
ThreadContext *context;
stackval result;
stackval *args;
args = (stackval*)alloca (sizeof (stackval) * (sig->param_count + (sig->hasthis ? 1 : 0)));
- frame = alloc_frame (context, &result, NULL, rmethod, args, &result);
-
/* Allocate storage for value types */
for (i = 0; i < sig->param_count; i++) {
MonoType *type = sig->params [i];
- alloc_storage_for_stackval (&frame->stack_args [i + sig->hasthis], type, sig->pinvoke);
+ alloc_storage_for_stackval (&args [i + sig->hasthis], type, sig->pinvoke);
}
if (sig->ret->type != MONO_TYPE_VOID)
- alloc_storage_for_stackval (frame->retval, sig->ret, sig->pinvoke);
+ alloc_storage_for_stackval (&result, sig->ret, sig->pinvoke);
+
+ InterpFrame frame = {NULL, rmethod, args, &result};
/* Copy the args saved in the trampoline to the frame stack */
- mono_arch_get_native_call_context_args (ccontext, frame, sig);
+ mono_arch_get_native_call_context_args (ccontext, &frame, sig);
ERROR_DECL (error);
- interp_exec_method (frame, context, error);
+ interp_exec_method (&frame, context, NULL, error);
g_assert (!context->has_resume_state);
/* Write back the return value */
/* 'frame' is still valid */
- mono_arch_set_native_call_context_ret (ccontext, frame, sig);
+ mono_arch_set_native_call_context_ret (ccontext, &frame, sig);
}
#else
}
// Do not inline use of alloca.
+// Do not inline in case order of frame addresses matters.
static MONO_NEVER_INLINE void
mono_interp_calli_nat_dynamic_pinvoke (
// Parameters are sorted by name.
- InterpFrame* child_frame,
guchar* code,
ThreadContext* context,
MonoMethodSignature* csignature,
- MonoError* error)
+ MonoError* error,
+ InterpFrame *parent_frame,
+ stackval *retval,
+ stackval *sp)
{
+ InterpFrame frame = {parent_frame, NULL, sp, retval};
+
// Recompute to limit parameters, which can also contribute to caller stack.
- InterpMethod* const imethod = child_frame->parent->imethod;
+ InterpMethod* const imethod = parent_frame->imethod;
g_assert (imethod->method->dynamic && csignature->pinvoke);
/* Pinvoke call is missing the wrapper. See mono_get_native_calli_wrapper */
- MonoMarshalSpec** mspecs = g_newa (MonoMarshalSpec*, csignature->param_count + 1);
- memset (mspecs, 0, sizeof (MonoMarshalSpec*) * (csignature->param_count + 1));
+ MonoMarshalSpec** mspecs = g_newa0 (MonoMarshalSpec*, csignature->param_count + 1);
MonoMethodPInvoke iinfo;
memset (&iinfo, 0, sizeof (iinfo));
{
ERROR_DECL (error);
- child_frame->imethod = mono_interp_get_imethod (imethod->domain, m, error);
+ frame.imethod = mono_interp_get_imethod (imethod->domain, m, error);
mono_error_cleanup (error); /* FIXME: don't swallow the error */
}
- interp_exec_method (child_frame, context, error);
+ interp_exec_method (&frame, context, NULL, error);
}
-static MonoException*
-mono_interp_leave (InterpFrame* child_frame)
+// Do not inline in case order of frame addresses matters.
+static MONO_NEVER_INLINE MonoException*
+mono_interp_leave (InterpFrame* parent_frame)
{
+ InterpFrame frame = {parent_frame};
+
stackval tmp_sp;
/*
* We need for mono_thread_get_undeniable_exception to be able to unwind
- * to check the abort threshold. For this to work we use child_frame as a
+ * to check the abort threshold. For this to work we use frame as a
* dummy frame that is stored in the lmf and serves as the transition frame
*/
- do_icall_wrapper (child_frame, NULL, MINT_ICALL_V_P, &tmp_sp, (gpointer)mono_thread_get_undeniable_exception, FALSE);
+ do_icall_wrapper (&frame, NULL, MINT_ICALL_V_P, &tmp_sp, (gpointer)mono_thread_get_undeniable_exception, FALSE);
return (MonoException*)tmp_sp.data.p;
}
* FRAME is only valid until the next call to alloc_frame ().
*/
static MONO_NEVER_INLINE void
-interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClauseArgs *clause_args, MonoError *error)
+interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs *clause_args, MonoError *error)
{
InterpMethod *cmethod;
MonoException *ex;
if (csignature->hasthis)
--sp;
- InterpFrame* const child_frame = alloc_frame (context, &retval, frame, NULL, sp, retval);
-
if (frame->imethod->method->dynamic && csignature->pinvoke) {
- mono_interp_calli_nat_dynamic_pinvoke (child_frame, code, context, csignature, error);
+ mono_interp_calli_nat_dynamic_pinvoke (code, context, csignature, error, frame, retval, sp);
} else {
const gboolean save_last_error = ip [-3 + 2];
- ves_pinvoke_method (child_frame, csignature, (MonoFuncV) code, context, save_last_error);
+ ves_pinvoke_method (csignature, (MonoFuncV)code, context, frame, retval, save_last_error, sp);
}
- pop_frame (context, child_frame);
-
CHECK_RESUME_STATE (context);
if (csignature->ret->type != MONO_TYPE_VOID) {
ip += 3;
#endif
call:;
- // FIXME This assumes a grow-down stack.
- gpointer native_stack_addr = frame->native_stack_addr ? (gpointer)((guint8*)frame->native_stack_addr - 1) : (gpointer)&retval;
-
/*
* Make a non-recursive call by loading the new interpreter state based on child frame,
* and going back to the main loop.
*/
SAVE_INTERP_STATE (frame);
- frame = alloc_frame (context, native_stack_addr, frame, cmethod, sp, retval);
-
+ // Allocate child frame.
+ // FIXME: Add stack overflow checks
+ {
+ InterpFrame *child_frame = frame->next_free;
+ if (!child_frame) {
+ child_frame = g_newa0 (InterpFrame, 1);
+ // Not free currently, but will be when allocation attempted.
+ frame->next_free = child_frame;
+ }
+ reinit_frame (child_frame, frame, cmethod, sp, retval);
+ frame = child_frame;
+ }
#if DEBUG_INTERP
int tracing;
#endif
MINT_IN_CASE(MINT_LEAVE_S)
MINT_IN_CASE(MINT_LEAVE_CHECK)
MINT_IN_CASE(MINT_LEAVE_S_CHECK) {
- int dummy;
- // Leave is split into pieces in order to consume less stack,
- // but not have to change how exception handling macros access labels and locals.
g_assert (sp >= frame->stack);
sp = frame->stack; /* spec says stack should be empty at endfinally so it should be at the start too */
gboolean const check = opcode == MINT_LEAVE_CHECK || opcode == MINT_LEAVE_S_CHECK;
if (check && frame->imethod->method->wrapper_type != MONO_WRAPPER_RUNTIME_INVOKE) {
- InterpFrame* const child_frame = alloc_frame (context, &dummy, frame, NULL, NULL, NULL);
- MonoException *abort_exc = mono_interp_leave (child_frame);
- pop_frame (context, child_frame);
+ MonoException *abort_exc = mono_interp_leave (frame);
if (abort_exc)
THROW_EX (abort_exc, frame->ip);
}
++ip;
MINT_IN_BREAK;
MINT_IN_CASE(MINT_MONO_GET_SP)
- sp->data.p = &frame;
+ sp->data.p = frame;
++sp;
++ip;
MINT_IN_BREAK;
ThreadContext *context = get_context ();
const unsigned short *old_ip = iframe->ip;
FrameClauseArgs clause_args;
- const guint16 *saved_ip;
+ const guint16 *state_ip;
memset (&clause_args, 0, sizeof (FrameClauseArgs));
clause_args.start_with_ip = (const guint16*)handler_ip;
clause_args.end_at_ip = (const guint16*)handler_ip_end;
clause_args.exit_clause = clause_index;
- saved_ip = iframe->state.ip;
+ state_ip = iframe->state.ip;
iframe->state.ip = NULL;
+ InterpFrame* const next_free = iframe->next_free;
+ iframe->next_free = NULL;
+
ERROR_DECL (error);
- interp_exec_method_full (iframe, context, &clause_args, error);
- iframe->state.ip = saved_ip;
+ interp_exec_method (iframe, context, &clause_args, error);
+
+ iframe->next_free = next_free;
+ iframe->state.ip = state_ip;
iframe->state.clause_args = NULL;
if (context->has_resume_state) {
return TRUE;
* Run the filter clause identified by CLAUSE_INDEX in the intepreter frame given by
* frame->interp_frame.
*/
-static gboolean
+// Do not inline in case order of frame addresses matters.
+static MONO_NEVER_INLINE gboolean
interp_run_filter (StackFrameInfo *frame, MonoException *ex, int clause_index, gpointer handler_ip, gpointer handler_ip_end)
{
InterpFrame *iframe = (InterpFrame*)frame->interp_frame;
ThreadContext *context = get_context ();
- InterpFrame *child_frame;
stackval retval;
FrameClauseArgs clause_args;
* Have to run the clause in a new frame which is a copy of IFRAME, since
* during debugging, there are two copies of the frame on the stack.
*/
- child_frame = alloc_frame (context, &retval, iframe, iframe->imethod, iframe->stack_args, &retval);
+ InterpFrame child_frame = {iframe, iframe->imethod, iframe->stack_args, &retval};
memset (&clause_args, 0, sizeof (FrameClauseArgs));
clause_args.start_with_ip = (const guint16*)handler_ip;
clause_args.base_frame = iframe;
ERROR_DECL (error);
- interp_exec_method_full (child_frame, context, &clause_args, error);
+ interp_exec_method (&child_frame, context, &clause_args, error);
+
/* ENDFILTER stores the result into child_frame->retval */
return retval.data.i ? TRUE : FALSE;
}
frame->managed = TRUE;
}
frame->ji = iframe->imethod->jinfo;
- frame->frame_addr = iframe->native_stack_addr;
+ frame->frame_addr = iframe;
stack_iter->current = iframe->parent;
return stackval_to_data_addr (sig->ret, iframe->retval);
}
-static gpointer
-interp_frame_get_native_stack_addr (MonoInterpFrameHandle frame)
-{
- InterpFrame *iframe = (InterpFrame*)frame;
-
- return iframe->native_stack_addr;
-}
-
static void
interp_start_single_stepping (void)
{