[interp] Enable inlining of object constructors (mono/mono#15665)
authorVlad Brezae <brezaevlad@gmail.com>
Mon, 22 Jul 2019 14:03:26 +0000 (17:03 +0300)
committerGitHub <noreply@github.com>
Mon, 22 Jul 2019 14:03:26 +0000 (17:03 +0300)
* [interp] Split code for obj / vt newobj instruction

* [interp] Enable inlining of object constructors

If the ctor was inlined, the newobj instruction receives a max ushort as the data item for the index, so it doesn't execute the ctor in this case. Also, when inlining, we need to push on the stack the allocated object, once as argument for the ctor and the second needs to remain on the stack as the result of the IL newobj instruction.

Commit migrated from https://github.com/mono/mono/commit/009b0dd4c307f60e2b7d0b7e512fe8cf97ffb34f

src/mono/mono/mini/interp/interp.c
src/mono/mono/mini/interp/transform.c

index 4172221..12d37b8 100644 (file)
@@ -4383,11 +4383,51 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                        ip += 3;
                        MINT_IN_BREAK;
                }
-               MINT_IN_CASE(MINT_NEWOBJ_FAST)
+               MINT_IN_CASE(MINT_NEWOBJ_FAST) {
+                       guint16 param_count;
+                       guint16 imethod_index = *(guint16*) (ip + 1);
+
+                       const gboolean is_inlined = imethod_index == 0xffff;
+
+                       param_count = *(guint16*)(ip + 2);
+
+                       if (param_count) {
+                               sp -= param_count;
+                               memmove (sp + 1 + is_inlined, sp, param_count * sizeof (stackval));
+                       }
+
+                       MonoVTable *vtable = (MonoVTable*) imethod->data_items [*(guint16*)(ip + 3)];
+                       INIT_VTABLE (vtable);
+
+                       frame_objref (frame) = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
+                       if (G_UNLIKELY (!frame_objref (frame))) {
+                               mono_error_set_out_of_memory (error, "Could not allocate %i bytes", m_class_get_instance_size (vtable->klass));
+                               THROW_EX (mono_error_convert_to_exception (error), ip);
+                       }
+
+                       sp [0].data.o = frame_objref (frame);
+                       if (is_inlined) {
+                               sp [1].data.o = frame_objref (frame);
+                               sp += param_count + 2;
+                       } else {
+                               InterpMethod *ctor_method = (InterpMethod*) imethod->data_items [imethod_index];
+                               frame->ip = ip;
+
+                               child_frame.imethod = ctor_method;
+                               child_frame.stack_args = sp;
+
+                               interp_exec_method (&child_frame, context, error);
+                               CHECK_RESUME_STATE (context);
+                               sp [0].data.o = frame_objref (frame);
+                               sp++;
+                       }
+                       ip += 4;
+
+                       MINT_IN_BREAK;
+               }
                MINT_IN_CASE(MINT_NEWOBJ_VT_FAST)
                MINT_IN_CASE(MINT_NEWOBJ_VTST_FAST) {
                        guint16 param_count;
-                       gboolean vt = *ip != MINT_NEWOBJ_FAST;
                        stackval valuetype_this;
 
                        frame->ip = ip;
@@ -4401,42 +4441,23 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                        }
                        child_frame.stack_args = sp;
 
-                       if (vt) {
-                               gboolean vtst = *ip == MINT_NEWOBJ_VTST_FAST;
-                               if (vtst) {
-                                       memset (vt_sp, 0, *(guint16*)(ip + 3));
-                                       sp->data.p = vt_sp;
-                                       valuetype_this.data.p = vt_sp;
-                                       ip += 4;
-                               } else {
-                                       memset (&valuetype_this, 0, sizeof (stackval));
-                                       sp->data.p = &valuetype_this;
-                                       ip += 3;
-                               }
-                       } else {
-                               MonoVTable *vtable = (MonoVTable*) imethod->data_items [*(guint16*)(ip + 3)];
-                               if (G_UNLIKELY (!vtable->initialized)) {
-                                       mono_runtime_class_init_full (vtable, error);
-                                       if (!mono_error_ok (error))
-                                               THROW_EX (mono_error_convert_to_exception (error), ip);
-                               }
-                               frame_objref (frame) = mono_gc_alloc_obj (vtable, m_class_get_instance_size (vtable->klass));
-                               if (G_UNLIKELY (!frame_objref (frame))) {
-                                       mono_error_set_out_of_memory (error, "Could not allocate %i bytes", m_class_get_instance_size (vtable->klass));
-                                       THROW_EX (mono_error_convert_to_exception (error), ip);
-                               }
-                               sp->data.o = frame_objref (frame);
+                       gboolean vtst = *ip == MINT_NEWOBJ_VTST_FAST;
+                       if (vtst) {
+                               memset (vt_sp, 0, *(guint16*)(ip + 3));
+                               sp->data.p = vt_sp;
+                               valuetype_this.data.p = vt_sp;
                                ip += 4;
+                       } else {
+                               memset (&valuetype_this, 0, sizeof (stackval));
+                               sp->data.p = &valuetype_this;
+                               ip += 3;
                        }
 
                        interp_exec_method (&child_frame, context, error);
 
                        CHECK_RESUME_STATE (context);
 
-                       if (vt)
-                               *sp = valuetype_this;
-                       else
-                               sp->data.p = frame_objref (frame);
+                       *sp = valuetype_this;
                        ++sp;
                        MINT_IN_BREAK;
                }
index fbb6bd9..ad5695c 100644 (file)
@@ -582,7 +582,7 @@ can_store (int st_value, int vt_value)
        do { \
                (td)->stack_capacity *= 2; \
                (td)->stack = (StackInfo*)realloc ((td)->stack, (td)->stack_capacity * sizeof (td->stack [0])); \
-               (td)->sp = (td)->stack + sppos; \
+               (td)->sp = (td)->stack + (sppos); \
        } while (0);
 
 #define PUSH_SIMPLE_TYPE(td, ty) \
@@ -609,6 +609,27 @@ can_store (int st_value, int vt_value)
                SET_TYPE((td)->sp - 1, ty, k); \
        } while (0)
 
+static void
+move_stack (TransformData *td, int start, int amount)
+{
+       int sp_height = td->sp - td->stack;
+       int to_move = sp_height - start;
+
+       td->sp += amount;
+       sp_height += amount;
+       if (amount > 0) {
+               if (sp_height > td->max_stack_height)
+                       td->max_stack_height = sp_height;
+               if (sp_height > td->stack_capacity)
+                       REALLOC_STACK (td, sp_height);
+       } else {
+               g_assert (td->sp >= td->stack);
+       }
+
+       if (to_move > 0)
+               memmove (td->stack + start + amount, td->stack + start, to_move * sizeof (StackInfo));
+}
+
 #define PUSH_VT(td, size) \
        do { \
                (td)->vt_sp += ALIGN_TO ((size), MINT_VT_ALIGNMENT); \
@@ -4143,8 +4164,8 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                goto_if_nok (error, exit);
                        }
 
-                       td->sp -= csignature->param_count;
                        if (mono_class_is_magic_int (klass) || mono_class_is_magic_float (klass)) {
+                               td->sp -= csignature->param_count;
 #if SIZEOF_VOID_P == 8
                                if (mono_class_is_magic_int (klass) && td->sp [0].type == STACK_TYPE_I4)
                                        interp_add_ins (td, MINT_CONV_I8_I4);
@@ -4172,22 +4193,46 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                                !mono_class_is_marshalbyref (klass) &&
                                                !mono_class_has_finalizer (klass) &&
                                                !m_class_has_weak_fields (klass)) {
-                                       if (!m_class_is_valuetype (klass))
-                                               interp_add_ins (td, MINT_NEWOBJ_FAST);
-                                       else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
-                                               interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
-                                       else
-                                               interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
+                                       if (!m_class_is_valuetype (klass)) {
+                                               InterpInst *newobj_fast = interp_add_ins (td, MINT_NEWOBJ_FAST);
 
-                                       td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
-                                       td->last_ins->data [1] = csignature->param_count;
+                                               newobj_fast->data [1] = csignature->param_count;
 
-                                       if (!m_class_is_valuetype (klass)) {
                                                MonoVTable *vtable = mono_class_vtable_checked (domain, klass, error);
                                                goto_if_nok (error, exit);
-                                               td->last_ins->data [2] = get_data_item_index (td, vtable);
-                                       } else if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
-                                               td->last_ins->data [2] = mono_class_value_size (klass, NULL);
+                                               newobj_fast->data [2] = get_data_item_index (td, vtable);
+
+                                               move_stack (td, (td->sp - td->stack) - csignature->param_count, 2);
+
+                                               StackInfo *tmp_sp = td->sp - csignature->param_count - 2;
+                                               SET_TYPE (tmp_sp, STACK_TYPE_O, klass);
+                                               SET_TYPE (tmp_sp + 1, STACK_TYPE_O, klass);
+
+                                               if ((mono_interp_opt & INTERP_OPT_INLINE) && interp_method_check_inlining (td, m)) {
+                                                       MonoMethodHeader *mheader = interp_method_get_header (m, error);
+                                                       goto_if_nok (error, exit);
+
+                                                       if (interp_inline_method (td, m, mheader, error)) {
+                                                               newobj_fast->data [0] = 0xffff;
+                                                               break;
+                                                       }
+                                               }
+                                               // If inlining failed we need to restore the stack
+                                               move_stack (td, (td->sp - td->stack) - csignature->param_count, -2);
+                                               // Set the method to be executed as part of newobj instruction
+                                               newobj_fast->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
+                                       } else {
+                                               if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT)
+                                                       interp_add_ins (td, MINT_NEWOBJ_VTST_FAST);
+                                               else
+                                                       interp_add_ins (td, MINT_NEWOBJ_VT_FAST);
+
+                                               td->last_ins->data [0] = get_data_item_index (td, mono_interp_get_imethod (domain, m, error));
+                                               td->last_ins->data [1] = csignature->param_count;
+
+                                               if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
+                                                       td->last_ins->data [2] = mono_class_value_size (klass, NULL);
+                                               }
                                        }
                                } else {
                                        interp_add_ins (td, MINT_NEWOBJ);
@@ -4195,6 +4240,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                }
                                goto_if_nok (error, exit);
 
+                               td->sp -= csignature->param_count;
                                if (mint_type (m_class_get_byval_arg (klass)) == MINT_TYPE_VT) {
                                        vt_res_size = mono_class_value_size (klass, NULL);
                                        PUSH_VT (td, vt_res_size);