[interp] Rework frame data storage (#38085)
authormonojenkins <jo.shields+jenkins@xamarin.com>
Sun, 21 Jun 2020 17:00:00 +0000 (13:00 -0400)
committerGitHub <noreply@github.com>
Sun, 21 Jun 2020 17:00:00 +0000 (20:00 +0300)
This PR has 2 objectives. First it makes the interp stack allocated in a single contiguous area of memory. This will enable us to do fast pointer bumping when switching between frames and not have to worry about scattered memory fragments. Second, it makes the stack space used by a frame (locals + execution stack) a compile time constant. This could prove useful for future optimizations, because we can tell at compile time at which stack offset every instruction outputs its result. In order to achieve this, localloc memory has been kicked out of the normal stack space into its own allocator, which is almost identical to the old stack fragment allocator.

Co-authored-by: BrzVlad <BrzVlad@users.noreply.github.com>
src/mono/mono/mini/interp/interp-internals.h
src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/interp/mintops.def
src/mono/mono/mini/interp/transform.c
src/mono/mono/mini/interp/transform.h
src/mono/mono/utils/mono-mmap.c
src/mono/mono/utils/mono-mmap.h

index 2c4fb40..3ddd2b9 100644 (file)
@@ -27,6 +27,8 @@
 
 #define MINT_VT_ALIGNMENT 8
 
+#define INTERP_STACK_SIZE (1024*1024)
+
 enum {
        VAL_I32     = 0,
        VAL_DOUBLE  = 1,
@@ -175,10 +177,11 @@ struct InterpMethod {
        unsigned int needs_thread_attach : 1;
 };
 
-typedef struct _StackFragment StackFragment;
-struct _StackFragment {
+/* Used for localloc memory allocation */
+typedef struct _FrameDataFragment FrameDataFragment;
+struct _FrameDataFragment {
        guint8 *pos, *end;
-       struct _StackFragment *next;
+       struct _FrameDataFragment *next;
 #if SIZEOF_VOID_P == 4
        /* Align data field to MINT_VT_ALIGNMENT */
        gint32 pad;
@@ -187,10 +190,23 @@ struct _StackFragment {
 };
 
 typedef struct {
-       StackFragment *first, *current;
+       InterpFrame *frame;
+       /*
+        * frag and pos hold the current allocation position when the stored frame
+        * starts allocating memory. This is used for restoring the localloc stack
+        * when frame returns.
+        */
+       FrameDataFragment *frag;
+       guint8 *pos;
+} FrameDataInfo;
+
+typedef struct {
+       FrameDataFragment *first, *current;
+       FrameDataInfo *infos;
+       int infos_len, infos_capacity;
        /* For GC sync */
        int inited;
-} FrameStack;
+} FrameDataAllocator;
 
 
 /* Arguments that are passed when invoking only a finally/filter clause from the frame */
@@ -211,8 +227,6 @@ struct InterpFrame {
        stackval       *retval; /* parent */
        stackval       *stack;
        InterpFrame    *next_free;
-       /* Stack fragments this frame was allocated from */
-       StackFragment *data_frag;
        /* State saved before calls */
        /* This is valid if state.ip != NULL */
        InterpState state;
@@ -231,8 +245,17 @@ typedef struct {
        MonoJitExceptionInfo *handler_ei;
        /* Exception that is being thrown. Set with rest of resume state */
        MonoGCHandle exc_gchandle;
-       /* Stack of frame data */
-       FrameStack data_stack;
+       /* This is a contiguous space allocated for interp execution stack */
+       guchar *stack_start;
+       /*
+        * This stack pointer is the highest stack memory that can be used by the current frame. This does not
+        * change throughout the execution of a frame and it is essentially the upper limit of the execution
+        * stack pointer. It is needed when re-entering interp, to know from which address we can start using
+        * stack, and also needed for the GC to be able to scan the stack.
+        */
+       guchar *stack_pointer;
+       /* Used for allocation of localloc regions */
+       FrameDataAllocator data_stack;
 } ThreadContext;
 
 typedef struct {
index acb185e..cd833ec 100644 (file)
@@ -103,10 +103,10 @@ struct FrameClauseArgs {
  * This code synchronizes with interp_mark_stack () using compiler memory barriers.
  */
 
-static StackFragment*
-stack_frag_new (int size)
+static FrameDataFragment*
+frame_data_frag_new (int size)
 {
-       StackFragment *frag = (StackFragment*)g_malloc (size);
+       FrameDataFragment *frag = (FrameDataFragment*)g_malloc (size);
 
        frag->pos = (guint8*)&frag->data;
        frag->end = (guint8*)frag + size;
@@ -115,107 +115,105 @@ stack_frag_new (int size)
 }
 
 static void
-frame_stack_init (FrameStack *stack, int size)
+frame_data_frag_free (FrameDataFragment *frag)
 {
-       StackFragment *frag;
+       while (frag) {
+               FrameDataFragment *next = frag->next;
+               g_free (frag);
+               frag = next;
+       }
+}
+
+static void
+frame_data_allocator_init (FrameDataAllocator *stack, int size)
+{
+       FrameDataFragment *frag;
 
-       frag = stack_frag_new (size);
+       frag = frame_data_frag_new (size);
        stack->first = stack->current = frag;
-       mono_compiler_barrier ();
-       stack->inited = 1;
+       stack->infos_capacity = 4;
+       stack->infos = g_malloc (stack->infos_capacity * sizeof (FrameDataInfo));
 }
 
-static StackFragment*
-add_frag (FrameStack *stack, int size)
+static void
+frame_data_allocator_free (FrameDataAllocator *stack)
 {
-       StackFragment *new_frag;
+       /* Assert to catch leaks */
+       g_assert_checked (stack->current == stack->first && stack->current->pos == (guint8*)&stack->current->data);
+       frame_data_frag_free (stack->first);
+}
+
+static FrameDataFragment*
+frame_data_allocator_add_frag (FrameDataAllocator *stack, int size)
+{
+       FrameDataFragment *new_frag;
 
        // FIXME:
        int frag_size = 4096;
-       if (size + sizeof (StackFragment) > frag_size)
-               frag_size = size + sizeof (StackFragment);
-       new_frag = stack_frag_new (frag_size);
+       if (size + sizeof (FrameDataFragment) > frag_size)
+               frag_size = size + sizeof (FrameDataFragment);
+       new_frag = frame_data_frag_new (frag_size);
        mono_compiler_barrier ();
        stack->current->next = new_frag;
        stack->current = new_frag;
        return new_frag;
 }
 
-static void
-free_frag (StackFragment *frag)
-{
-       while (frag) {
-               StackFragment *next = frag->next;
-               g_free (frag);
-               frag = next;
-       }
-}
-
-static MONO_ALWAYS_INLINE gpointer
-frame_stack_alloc_ovf (FrameStack *stack, int size, StackFragment **out_frag)
+static gpointer
+frame_data_allocator_alloc (FrameDataAllocator *stack, InterpFrame *frame, int size)
 {
-       StackFragment *current = stack->current;
+       FrameDataFragment *current = stack->current;
        gpointer res;
 
-       if (current->next && current->next->pos + size <= current->next->end) {
-               current = stack->current = current->next;
-               current->pos = (guint8*)&current->data;
-       } else {
-               StackFragment *tmp = current->next;
-               /* avoid linking to be freed fragments, so the GC can't trip over it */
-               current->next = NULL;
-               mono_compiler_barrier ();
-               free_frag (tmp);
+       int infos_len = stack->infos_len;
 
-               current = add_frag (stack, size);
+       if (!infos_len || (infos_len > 0 && stack->infos [infos_len - 1].frame != frame)) {
+               /* First allocation by this frame. Save the markers for restore */
+               if (infos_len == stack->infos_capacity) {
+                       stack->infos_capacity = infos_len * 2;
+                       stack->infos = g_realloc (stack->infos, stack->infos_capacity * sizeof (FrameDataInfo));
+               }
+               stack->infos [infos_len].frame = frame;
+               stack->infos [infos_len].frag = current;
+               stack->infos [infos_len].pos = current->pos;
+               stack->infos_len++;
        }
-       g_assert (current->pos + size <= current->end);
-       res = (gpointer)current->pos;
-       current->pos += size;
-
-       mono_compiler_barrier ();
-
-       if (out_frag)
-               *out_frag = current;
-       return res;
-}
-
-static MONO_ALWAYS_INLINE gpointer
-frame_stack_alloc (FrameStack *stack, int size, StackFragment **out_frag)
-{
-       StackFragment *current = stack->current;
-       gpointer res;
 
        if (G_LIKELY (current->pos + size <= current->end)) {
                res = current->pos;
                current->pos += size;
-               mono_compiler_barrier ();
-
-               if (out_frag)
-                       *out_frag = current;
-               return res;
        } else {
-               return frame_stack_alloc_ovf (stack, size, out_frag);
-       }
-}
+               if (current->next && current->next->pos + size <= current->next->end) {
+                       current = stack->current = current->next;
+                       current->pos = (guint8*)&current->data;
+               } else {
+                       FrameDataFragment *tmp = current->next;
+                       /* avoid linking to be freed fragments, so the GC can't trip over it */
+                       current->next = NULL;
+                       mono_compiler_barrier ();
+                       frame_data_frag_free (tmp);
 
-static MONO_ALWAYS_INLINE void
-frame_stack_pop (FrameStack *stack, StackFragment *frag, gpointer pos)
-{
-       g_assert_checked ((guint8*)pos >= (guint8*)&frag->data && (guint8*)pos <= (guint8*)frag->end);
-       stack->current = frag;
-       mono_compiler_barrier ();
-       stack->current->pos = (guint8*)pos;
+                       current = frame_data_allocator_add_frag (stack, size);
+               }
+               g_assert (current->pos + size <= current->end);
+               res = (gpointer)current->pos;
+               current->pos += size;
+       }
        mono_compiler_barrier ();
-       //memset (stack->current->pos, 0, stack->current->end - stack->current->pos);
+       return res;
 }
 
 static void
-frame_stack_free (FrameStack *stack)
+frame_data_allocator_pop (FrameDataAllocator *stack, InterpFrame *frame)
 {
-       stack->inited = 0;
-       mono_compiler_barrier ();
-       free_frag (stack->first);
+       int infos_len = stack->infos_len;
+
+       if (infos_len > 0 && stack->infos [infos_len - 1].frame == frame) {
+               infos_len--;
+               stack->current = stack->infos [infos_len].frag;
+               stack->current->pos = stack->infos [infos_len].pos;
+               stack->infos_len = infos_len;
+       }
 }
 
 /*
@@ -234,42 +232,6 @@ reinit_frame (InterpFrame *frame, InterpFrame *parent, InterpMethod *imethod, st
 }
 
 /*
- * alloc_data_stack:
- *
- *   Allocate stack space for a frame.
- */
-static MONO_ALWAYS_INLINE void
-alloc_stack_data (ThreadContext *ctx, InterpFrame *frame, int size)
-{
-       StackFragment *frag;
-       gpointer res = frame_stack_alloc (&ctx->data_stack, size, &frag);
-
-       frame->stack = (stackval*)res;
-       frame->data_frag = frag;
-}
-
-static gpointer
-alloc_extra_stack_data (ThreadContext *ctx, int size)
-{
-       StackFragment *frag;
-
-       return frame_stack_alloc (&ctx->data_stack, size, &frag);
-}
-
-/*
- * pop_frame:
- *
- *   Pop FRAME and its child frames from the frame stack.
- * FRAME stays valid until the next alloc_frame () call.
- */
-static void
-pop_frame (ThreadContext *context, InterpFrame *frame)
-{
-       if (frame->stack)
-               frame_stack_pop (&context->data_stack, frame->data_frag, frame->stack);
-}
-
-/*
  * List of classes whose methods will be executed by transitioning to JITted code.
  * Used for testing.
  */
@@ -435,14 +397,12 @@ get_context (void)
        ThreadContext *context = (ThreadContext *) mono_native_tls_get_value (thread_context_id);
        if (context == NULL) {
                context = g_new0 (ThreadContext, 1);
-               /*
-                * 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->data_stack, 8192);
+               context->stack_start = (guchar*)mono_valloc (0, INTERP_STACK_SIZE, MONO_MMAP_READ | MONO_MMAP_WRITE, MONO_MEM_ACCOUNT_INTERP_STACK);
+               context->stack_pointer = context->stack_start;
+
+               frame_data_allocator_init (&context->data_stack, 8192);
+               /* Make sure all data is initialized before publishing the context */
+               mono_compiler_barrier ();
                set_context (context);
        }
        return context;
@@ -453,7 +413,11 @@ interp_free_context (gpointer ctx)
 {
        ThreadContext *context = (ThreadContext*)ctx;
 
-       frame_stack_free (&context->data_stack);
+       mono_vfree (context->stack_start, INTERP_STACK_SIZE, MONO_MEM_ACCOUNT_INTERP_STACK);
+       /* Prevent interp_mark_stack from trying to scan the data_stack, before freeing it */
+       context->stack_start = NULL;
+       mono_compiler_barrier ();
+       frame_data_allocator_free (&context->data_stack);
        g_free (context);
 }
 
@@ -3329,12 +3293,21 @@ method_entry (ThreadContext *context, InterpFrame *frame,
                MonoException *ex = do_transform_method (frame, context);
                if (ex) {
                        *out_ex = ex;
+                       /*
+                        * Initialize the stack base pointer here, in the uncommon branch, so we don't
+                        * need to check for it everytime when exitting a frame.
+                        */
+                       frame->stack = (stackval*)context->stack_pointer;
                        return slow;
                }
        }
 
-       if (!clause_args || clause_args->base_frame)
-               alloc_stack_data (context, frame, frame->imethod->alloca_size);
+       if (!clause_args || clause_args->base_frame) {
+               frame->stack = (stackval*)context->stack_pointer;
+               context->stack_pointer += frame->imethod->alloca_size;
+               /* Make sure the stack pointer is bumped before we store any references on the stack */
+               mono_compiler_barrier ();
+       }
 
        return slow;
 }
@@ -3608,18 +3581,12 @@ main_loop:
                                        THROW_EX (ex, ip);
                                EXCEPTION_CHECKPOINT;
                        }
-                       const gboolean realloc_frame = new_method->alloca_size > frame->imethod->alloca_size;
-                       frame->imethod = new_method;
                        /*
-                        * We allocate the stack frame from scratch and store the arguments in the
-                        * locals again since it's possible for the caller stack frame to be smaller
+                        * It's possible for the caller stack frame to be smaller
                         * than the callee stack frame (at the interp level)
                         */
-                       if (realloc_frame) {
-                               alloc_stack_data (context, frame, frame->imethod->alloca_size);
-                               memset (frame->stack, 0, frame->imethod->alloca_size);
-                               locals = (guchar*)frame->stack;
-                       }
+                       context->stack_pointer = (guchar*)frame->stack + new_method->alloca_size;
+                       frame->imethod = new_method;
                        vt_sp = locals + frame->imethod->total_locals_size;
 #if DEBUG_INTERP
                        vtalloc = vt_sp;
@@ -4042,6 +4009,40 @@ call:
                        g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
                        goto exit_frame;
                }
+               MINT_IN_CASE(MINT_RET_LOCALLOC)
+                       --sp;
+                       if (frame->parent) {
+                               frame->parent->state.sp [0] = *sp;
+                               frame->parent->state.sp++;
+                       } else {
+                               // FIXME This can only happen in a few wrappers. Add separate opcode for it
+                               *frame->retval = *sp;
+                       }
+                       frame_data_allocator_pop (&context->data_stack, frame);
+                       g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+                       goto exit_frame;
+               MINT_IN_CASE(MINT_RET_VOID_LOCALLOC)
+                       frame_data_allocator_pop (&context->data_stack, frame);
+                       g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+                       goto exit_frame;
+               MINT_IN_CASE(MINT_RET_VT_LOCALLOC) {
+                       gpointer dest_vt;
+                       int const i32 = READ32 (ip + 1);
+                       --sp;
+                       if (frame->parent) {
+                               dest_vt = frame->parent->state.vt_sp;
+                               /* Push the valuetype in the parent frame */
+                               frame->parent->state.sp [0].data.p = dest_vt;
+                               frame->parent->state.sp++;
+                               frame->parent->state.vt_sp += ALIGN_TO (i32, MINT_VT_ALIGNMENT);
+                       } else {
+                               dest_vt = frame->retval->data.p;
+                       }
+                       memcpy (dest_vt, sp->data.p, i32);
+                       frame_data_allocator_pop (&context->data_stack, frame);
+                       g_assert_checked (sp == (stackval*)(locals + frame->imethod->total_locals_size + frame->imethod->vt_stack_size));
+                       goto exit_frame;
+               }
 
 #ifdef ENABLE_EXPERIMENT_TIERED
 #define BACK_BRANCH_PROFILE(offset) do { \
@@ -6437,16 +6438,10 @@ call_newobj:
                        ip += short_offset ? (short)*(ip + 1) : (gint32)READ32 (ip + 1);
                        const guint16 *endfinally_ip = ip;
                        GSList *old_list = finally_ips;
-                       MonoMethod *method = frame->imethod->method;
 #if DEBUG_INTERP
                        if (tracing)
                                g_print ("* Handle finally IL_%04x\n", endfinally_ip - frame->imethod->code);
 #endif
-                       // FIXME Null check for frame->imethod follows deref.
-                       if (frame->imethod == NULL || (method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)
-                                       || (method->iflags & (METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL | METHOD_IMPL_ATTRIBUTE_RUNTIME)))
-                               goto exit_frame;
-
                        finally_ips = g_slist_prepend (finally_ips, (void *)endfinally_ip);
 
                        for (int i = frame->imethod->num_clauses - 1; i >= 0; i--) {
@@ -6511,6 +6506,7 @@ call_newobj:
                             mono_method_signature_internal (frame->imethod->method)->pinvoke);
                        if (sp > frame->stack)
                                g_warning_d ("retobj: more values on stack: %d", sp - frame->stack);
+                       frame_data_allocator_pop (&context->data_stack, frame);
                        goto exit_frame;
                MINT_IN_CASE(MINT_MONO_SGEN_THREAD_INFO)
                        sp->data.p = mono_tls_get_sgen_thread_info ();
@@ -6881,6 +6877,7 @@ call_newobj:
                        }
 
                        ip += 4;
+                       frame_data_allocator_pop (&context->data_stack, frame);
                        goto exit_frame;
                }
                MINT_IN_CASE(MINT_PROF_COVERAGE_STORE) {
@@ -6989,7 +6986,8 @@ call_newobj:
                                goto abort_label;
 
                        int len = sp [-1].data.i;
-                       sp [-1].data.p = alloc_extra_stack_data (context, ALIGN_TO (len, 8));
+                       // FIXME we need a separate allocator for localloc sections
+                       sp [-1].data.p = frame_data_allocator_alloc (&context->data_stack, frame, ALIGN_TO (len, MINT_VT_ALIGNMENT));
 
                        if (frame->imethod->init_locals)
                                memset (sp [-1].data.p, 0, len);
@@ -7284,6 +7282,9 @@ resume:
                 */
                goto exit_clause;
        }
+       // Because we are resuming in another frame, bypassing a normal ret opcode,
+       // we need to make sure to reset the localloc stack
+       frame_data_allocator_pop (&context->data_stack, frame);
        // fall through
 exit_frame:
        g_assert_checked (frame->imethod);
@@ -7291,13 +7292,11 @@ exit_frame:
        if (frame->parent && frame->parent->state.ip) {
                /* Return to the main loop after a non-recursive interpreter call */
                //printf ("R: %s -> %s %p\n", mono_method_get_full_name (frame->imethod->method), mono_method_get_full_name (frame->parent->imethod->method), frame->parent->state.ip);
-               InterpFrame* const child_frame = frame;
-
+               g_assert_checked (frame->stack);
+               context->stack_pointer = (guchar*)frame->stack;
                frame = frame->parent;
                LOAD_INTERP_STATE (frame);
 
-               pop_frame (context, child_frame);
-
                CHECK_RESUME_STATE (context);
 
                goto main_loop;
@@ -7310,10 +7309,10 @@ exit_clause:
                        char *locals_base = (char*)clause_args->base_frame->stack;
 
                        memcpy (locals_base, locals, frame->imethod->locals_size);
-                       pop_frame (context, frame);
+                       context->stack_pointer = (guchar*)frame->stack;
                }
        } else {
-               pop_frame (context, frame);
+               context->stack_pointer = (guchar*)frame->stack;
        }
 
        DEBUG_LEAVE ();
@@ -7665,10 +7664,14 @@ interp_mark_stack (gpointer thread_data, GcScanFunc func, gpointer gc_data, gboo
                return;
 
        ThreadContext *context = (ThreadContext*)jit_tls->interp_context;
-       if (!context || !context->data_stack.inited)
+       if (!context || !context->stack_start)
                return;
 
-       StackFragment *frag;
+       // FIXME: Scan the whole area with 1 call
+       for (gpointer *p = (gpointer*)context->stack_start; p < (gpointer*)context->stack_pointer; p++)
+               func (p, gc_data);
+
+       FrameDataFragment *frag;
        for (frag = context->data_stack.first; frag; frag = frag->next) {
                // FIXME: Scan the whole area with 1 call
                for (gpointer *p = (gpointer*)&frag->data; p < (gpointer*)frag->pos; ++p)
index f342835..c580f33 100644 (file)
@@ -22,6 +22,9 @@ OPDEF(MINT_POP1, "pop1", 1, Pop2, Push1, MintOpNoArgs)
 OPDEF(MINT_RET, "ret", 1, Pop1, Push0, MintOpNoArgs)
 OPDEF(MINT_RET_VOID, "ret.void", 1, Pop0, Push0, MintOpNoArgs)
 OPDEF(MINT_RET_VT, "ret.vt", 3, Pop1, Push0, MintOpInt)
+OPDEF(MINT_RET_LOCALLOC, "ret.localloc", 1, Pop1, Push0, MintOpNoArgs)
+OPDEF(MINT_RET_VOID_LOCALLOC, "ret.void.localloc", 1, Pop0, Push0, MintOpNoArgs)
+OPDEF(MINT_RET_VT_LOCALLOC, "ret.vt.localloc", 3, Pop1, Push0, MintOpInt)
 
 /* FIXME if ret_size != null it clobbers sp [-1] */
 OPDEF(MINT_VTRESULT, "vtresult", 4, Pop0, Push0, MintOpShortAndInt) /*FIX should be unsigned*/
index 2a01c1c..c193e14 100644 (file)
@@ -6224,6 +6224,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                        g_warning("CEE_LOCALLOC: stack not empty");
                                ++td->ip;
                                SET_SIMPLE_TYPE(td->sp - 1, STACK_TYPE_MP);
+                               td->has_localloc = TRUE;
                                break;
 #if 0
                        case CEE_UNUSED57: ves_abort(); break;
@@ -7567,6 +7568,22 @@ interp_optimize_code (TransformData *td)
                MONO_TIME_TRACK (mono_interp_stats.super_instructions_time, interp_super_instructions (td));
 }
 
+/*
+ * Very few methods have localloc. Handle it separately to not impact performance
+ * of other methods. We replace the normal return opcodes with opcodes that also
+ * reset the localloc stack.
+ */
+static void
+interp_fix_localloc_ret (TransformData *td)
+{
+       g_assert (td->has_localloc);
+       InterpInst *ins;
+       for (ins = td->first_ins; ins != NULL; ins = ins->next) {
+               if (ins->opcode >= MINT_RET && ins->opcode <= MINT_RET_VT)
+                       ins->opcode += MINT_RET_LOCALLOC - MINT_RET;
+       }
+}
+
 static void
 generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoGenericContext *generic_context, MonoError *error)
 {
@@ -7643,6 +7660,9 @@ generate (MonoMethod *method, MonoMethodHeader *header, InterpMethod *rtm, MonoG
        generate_code (td, method, header, generic_context, error);
        goto_if_nok (error, exit);
 
+       if (td->has_localloc)
+               interp_fix_localloc_ret (td);
+
        interp_optimize_code (td);
 
        generate_compacted_code (td);
index 46a6508..a814859 100644 (file)
@@ -144,6 +144,7 @@ typedef struct
        gboolean prof_coverage;
        MonoProfilerCoverageInfo *coverage_info;
        GList *dont_inline;
+       int has_localloc : 1;
 } TransformData;
 
 #define STACK_TYPE_I4 0
index 251e637..2dff889 100644 (file)
@@ -126,6 +126,7 @@ mono_mem_account_type_name (MonoMemAccountType type)
                "SGen binary protocol",
                "exceptions",
                "profiler",
+               "interp stack",
                "other"
        };
 
index 25ce4ec..14e6a09 100644 (file)
@@ -39,6 +39,7 @@ typedef enum {
        MONO_MEM_ACCOUNT_SGEN_BINARY_PROTOCOL,
        MONO_MEM_ACCOUNT_EXCEPTIONS,
        MONO_MEM_ACCOUNT_PROFILER,
+       MONO_MEM_ACCOUNT_INTERP_STACK,
        MONO_MEM_ACCOUNT_OTHER,
        MONO_MEM_ACCOUNT_MAX
 } MonoMemAccountType;