[interp] Optimize special static field access (mono/mono#14202)
authorVlad Brezae <brezaevlad@gmail.com>
Fri, 26 Apr 2019 21:28:12 +0000 (00:28 +0300)
committerGitHub <noreply@github.com>
Fri, 26 Apr 2019 21:28:12 +0000 (00:28 +0300)
* [interp] Optimize special static field access

3x faster. For value types 10x.

* [interp] Remove duplicated code

* [interp] Further optimize thread static field access

Provides an additional 50% perf gain.

Commit migrated from https://github.com/mono/mono/commit/a68022ae7e26952e7a5dfe6cbe99b3021b3c98cc

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

index 9b6b7cb..82b9142 100644 (file)
@@ -4842,23 +4842,46 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                        MINT_IN_BREAK;
                }
 
-               MINT_IN_CASE(MINT_LDSSFLD_SLOW)
-               MINT_IN_CASE(MINT_LDSSFLD_VT_SLOW) {
-                       gboolean is_vt = *ip == MINT_LDSSFLD_VT_SLOW;
+#define LDTSFLD(datamem, fieldtype) { \
+       guint32 offset = READ32(ip + 1); \
+       MonoInternalThread *thread = mono_thread_internal_current (); \
+       gpointer addr = ((char*)thread->static_data [offset & 0x3f]) + (offset >> 6); \
+       sp[0].data.datamem = *(fieldtype*)addr; \
+       ip += 3; \
+       ++sp; \
+       }
+               MINT_IN_CASE(MINT_LDTSFLD_I1) LDTSFLD(i, gint8); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_U1) LDTSFLD(i, guint8); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_I2) LDTSFLD(i, gint16); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_U2) LDTSFLD(i, guint16); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_I4) LDTSFLD(i, gint32); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_I8) LDTSFLD(l, gint64); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_R4) LDTSFLD(f_r4, float); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_R8) LDTSFLD(f, double); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_O) LDTSFLD(p, gpointer); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_LDTSFLD_P) LDTSFLD(p, gpointer); MINT_IN_BREAK;
+
+               MINT_IN_CASE(MINT_LDSSFLD) {
                        MonoClassField *field = (MonoClassField*)imethod->data_items [* (guint16 *)(ip + 1)];
-                       gpointer addr = mono_class_static_field_address (imethod->domain, field);
-                       EXCEPTION_CHECKPOINT;
-                       if (is_vt) {
-                               int size = READ32 (ip + 2);
-                               sp->data.p = vt_sp;
-                               vt_sp += ALIGN_TO (size, MINT_VT_ALIGNMENT);
-                       }
+                       guint32 offset = READ32(ip + 2);
+                       gpointer addr = mono_get_special_static_data (offset);
                        stackval_from_data (field->type, sp, addr, FALSE);
-                       ip += (is_vt ? 4 : 2);
+                       ip += 4;
                        ++sp;
                        MINT_IN_BREAK;
                }
+               MINT_IN_CASE(MINT_LDSSFLD_VT) {
+                       guint32 offset = READ32(ip + 1);
+                       gpointer addr = mono_get_special_static_data (offset);
 
+                       int size = READ32 (ip + 3);
+                       memcpy (vt_sp, addr, size);
+                       sp->data.p = vt_sp;
+                       vt_sp += ALIGN_TO (size, MINT_VT_ALIGNMENT);
+                       ip += 5;
+                       ++sp;
+                       MINT_IN_BREAK;
+               }
 #define STSFLD(datamem, fieldtype) { \
        MonoVTable *vtable = (MonoVTable*) imethod->data_items [*(guint16*)(ip + 1)]; \
        INIT_VTABLE (vtable); \
@@ -4891,23 +4914,46 @@ interp_exec_method_full (InterpFrame *frame, ThreadContext *context, FrameClause
                        MINT_IN_BREAK;
                }
 
-               MINT_IN_CASE(MINT_STSSFLD_SLOW)
-               MINT_IN_CASE(MINT_STSSFLD_VT_SLOW) {
-                       gboolean is_vt = *ip == MINT_STSSFLD_VT_SLOW;
+#define STTSFLD(datamem, fieldtype) { \
+       guint32 offset = READ32(ip + 1); \
+       MonoInternalThread *thread = mono_thread_internal_current (); \
+       gpointer addr = ((char*)thread->static_data [offset & 0x3f]) + (offset >> 6); \
+       sp--; \
+       *(fieldtype*)addr = sp[0].data.datamem; \
+       ip += 3; \
+       }
+
+               MINT_IN_CASE(MINT_STTSFLD_I1) STTSFLD(i, gint8); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_U1) STTSFLD(i, guint8); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_I2) STTSFLD(i, gint16); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_U2) STTSFLD(i, guint16); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_I4) STTSFLD(i, gint32); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_I8) STTSFLD(l, gint64); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_R4) STTSFLD(f_r4, float); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_R8) STTSFLD(f, double); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_P) STTSFLD(p, gpointer); MINT_IN_BREAK;
+               MINT_IN_CASE(MINT_STTSFLD_O) STTSFLD(p, gpointer); MINT_IN_BREAK;
+
+               MINT_IN_CASE(MINT_STSSFLD) {
                        MonoClassField *field = (MonoClassField*)imethod->data_items [* (guint16 *)(ip + 1)];
-                       gpointer addr = mono_class_static_field_address (imethod->domain, field);
-                       EXCEPTION_CHECKPOINT;
+                       guint32 offset = READ32(ip + 2);
+                       gpointer addr = mono_get_special_static_data (offset);
                        --sp;
                        stackval_to_data (field->type, sp, addr, FALSE);
-                       if (is_vt) {
-                               int size = READ32 (ip + 2);
-                               vt_sp -= ALIGN_TO (size, MINT_VT_ALIGNMENT);
-                               ip += 4;
-                       } else {
-                               ip += 2;
-                       }
+                       ip += 4;
                        MINT_IN_BREAK;
                }
+               MINT_IN_CASE(MINT_STSSFLD_VT) {
+                       guint32 offset = READ32(ip + 1);
+                       gpointer addr = mono_get_special_static_data (offset);
+                       --sp;
+                       int size = READ32 (ip + 3);
+                       memcpy (addr, sp->data.vt, size);
+                       vt_sp -= ALIGN_TO (size, MINT_VT_ALIGNMENT);
+                       ip += 5;
+                       MINT_IN_BREAK;
+               }
+
                MINT_IN_CASE(MINT_STOBJ_VT) {
                        int size;
                        c = (MonoClass*)imethod->data_items[* (guint16 *)(ip + 1)];
index 1dc503e..5342285 100644 (file)
@@ -107,8 +107,19 @@ OPDEF(MINT_STFLD_R8_UNALIGNED, "stfld.r8.unaligned", 2, MintOpUShortInt)
 OPDEF(MINT_STRMFLD, "strmfld", 2, MintOpFieldToken)
 OPDEF(MINT_STRMFLD_VT, "strmfld.vt", 2, MintOpUShortInt)
 
-OPDEF(MINT_LDSSFLD_SLOW, "ldssfld.slow", 2, MintOpFieldToken)
-OPDEF(MINT_LDSSFLD_VT_SLOW, "ldssfld.vt.slow", 4, MintOpFieldToken)
+OPDEF(MINT_LDTSFLD_I1, "ldtsfld.i1", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_U1, "ldtsfld.u1", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_I2, "ldtsfld.i2", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_U2, "ldtsfld.u2", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_I4, "ldtsfld.i4", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_I8, "ldtsfld.i8", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_R4, "ldtsfld.r4", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_R8, "ldtsfld.r8", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_O, "ldtsfld.o", 3, MintOpInt)
+OPDEF(MINT_LDTSFLD_P, "ldtsfld.p", 3, MintOpInt)
+OPDEF(MINT_LDSSFLD, "ldssfld", 4, MintOpFieldToken)
+OPDEF(MINT_LDSSFLD_VT, "ldssfld.vt", 5, MintOpInt)
+
 OPDEF(MINT_LDSFLD_I1, "ldsfld.i1", 3, MintOpUShortInt)
 OPDEF(MINT_LDSFLD_U1, "ldsfld.u1", 3, MintOpUShortInt)
 OPDEF(MINT_LDSFLD_I2, "ldsfld.i2", 3, MintOpUShortInt)
@@ -121,8 +132,18 @@ OPDEF(MINT_LDSFLD_O, "ldsfld.o", 3, MintOpUShortInt)
 OPDEF(MINT_LDSFLD_P, "ldsfld.p", 3, MintOpUShortInt)
 OPDEF(MINT_LDSFLD_VT, "ldsfld.vt", 5, MintOpTwoShorts)
 
-OPDEF(MINT_STSSFLD_SLOW, "stssfld.slow", 2, MintOpFieldToken)
-OPDEF(MINT_STSSFLD_VT_SLOW, "stssfld.vt.slow", 4, MintOpFieldToken)
+OPDEF(MINT_STTSFLD_I1, "sttsfld.i1", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_U1, "sttsfld.u1", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_I2, "sttsfld.i2", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_U2, "sttsfld.u2", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_I4, "sttsfld.i4", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_I8, "sttsfld.i8", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_R4, "sttsfld.r4", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_R8, "sttsfld.r8", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_O, "sttsfld.o", 3, MintOpInt)
+OPDEF(MINT_STTSFLD_P, "sttsfld.p", 3, MintOpInt)
+OPDEF(MINT_STSSFLD, "stssfld", 4, MintOpFieldToken)
+OPDEF(MINT_STSSFLD_VT, "stssfld.vt", 5, MintOpInt)
 OPDEF(MINT_STSFLD_I1, "stsfld.i1", 3, MintOpUShortInt)
 OPDEF(MINT_STSFLD_U1, "stsfld.u1", 3, MintOpUShortInt)
 OPDEF(MINT_STSFLD_I2, "stsfld.i2", 3, MintOpUShortInt)
index 2390941..ba49c9e 100644 (file)
@@ -2560,45 +2560,46 @@ interp_emit_stobj (TransformData *td, MonoClass *klass)
 }
 
 static void
-interp_emit_ldsfld (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, MonoError *error)
+interp_emit_sfld_access (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, gboolean is_load, MonoError *error)
 {
-       if (mono_class_field_is_special_static (field)) {
-               interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSSFLD_VT_SLOW : MINT_LDSSFLD_SLOW);
-               td->last_ins->data [0] = get_data_item_index (td, field);
-               if (mt == MINT_TYPE_VT) {
-                       int size = mono_class_value_size (field_class, NULL);
-                       WRITE32_INS(td->last_ins, 1, &size);
-               }
-       } else {
-               MonoVTable *vtable = mono_class_vtable_checked (td->rtm->domain, field->parent, error);
-               return_if_nok (error);
-
-               interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
-               td->last_ins->data [0] = get_data_item_index (td, vtable);
-               td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
-
-               if (mt == MINT_TYPE_VT) {
-                       int size = mono_class_value_size (field_class, NULL);
-                       WRITE32_INS(td->last_ins, 2, &size);
-               }
-       }
-}
+       MonoDomain *domain = td->rtm->domain;
+       // Initialize the offset for the field
+       MonoVTable *vtable = mono_class_vtable_checked (domain, field->parent, error);
+       return_if_nok (error);
 
-static void
-interp_emit_stsfld (TransformData *td, MonoClassField *field, MonoClass *field_class, int mt, MonoError *error)
-{
        if (mono_class_field_is_special_static (field)) {
-               interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSSFLD_VT_SLOW : MINT_STSSFLD_SLOW);
-               td->last_ins->data [0] = get_data_item_index (td, field);
-               if (mt == MINT_TYPE_VT) {
-                       int size = mono_class_value_size (field_class, NULL);
-                       WRITE32_INS(td->last_ins, 1, &size);
+               guint32 offset;
+
+               mono_domain_lock (domain);
+               g_assert (domain->special_static_fields);
+               offset = GPOINTER_TO_UINT (g_hash_table_lookup (domain->special_static_fields, field));
+               mono_domain_unlock (domain);
+               g_assert (offset);
+
+               // Offset is SpecialStaticOffset
+               if ((offset & 0x80000000) == 0 && mt != MINT_TYPE_VT) {
+                       // This field is thread static
+                       interp_add_ins (td, (is_load ? MINT_LDTSFLD_I1 : MINT_STTSFLD_I1) + mt);
+                       WRITE32_INS(td->last_ins, 0, &offset);
+               } else {
+                       if (mt == MINT_TYPE_VT) {
+                               interp_add_ins (td, is_load ? MINT_LDSSFLD_VT : MINT_STSSFLD_VT);
+                               WRITE32_INS(td->last_ins, 0, &offset);
+
+                               int size = mono_class_value_size (field_class, NULL);
+                               WRITE32_INS(td->last_ins, 2, &size);
+                       } else {
+                               interp_add_ins (td, is_load ? MINT_LDSSFLD : MINT_STSSFLD);
+                               td->last_ins->data [0] = get_data_item_index (td, field);
+                               WRITE32_INS(td->last_ins, 1, &offset);
+                       }
                }
        } else {
-               MonoVTable *vtable = mono_class_vtable_checked (td->rtm->domain, field->parent, error);
-               return_if_nok (error);
+               if (is_load)
+                       interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_LDSFLD_VT : (MINT_LDSFLD_I1 + mt - MINT_TYPE_I1));
+               else
+                       interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
 
-               interp_add_ins (td, (mt == MINT_TYPE_VT) ? MINT_STSFLD_VT : (MINT_STSFLD_I1 + mt - MINT_TYPE_I1));
                td->last_ins->data [0] = get_data_item_index (td, vtable);
                td->last_ins->data [1] = get_data_item_index (td, (char*)mono_vtable_get_static_field_data (vtable) + field->offset);
 
@@ -4057,7 +4058,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                if (is_static) {
                                        interp_add_ins (td, MINT_POP);
                                        td->last_ins->data [0] = 0;
-                                       interp_emit_ldsfld (td, field, field_klass, mt, error);
+                                       interp_emit_sfld_access (td, field, field_klass, mt, TRUE, error);
                                        goto_if_nok (error, exit);
                                } else {
                                        int opcode = MINT_LDFLD_I1 + mt - MINT_TYPE_I1;
@@ -4127,7 +4128,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                if (is_static) {
                                        interp_add_ins (td, MINT_POP);
                                        td->last_ins->data [0] = 1;
-                                       interp_emit_stsfld (td, field, field_klass, mt, error);
+                                       interp_emit_sfld_access (td, field, field_klass, mt, FALSE, error);
                                        goto_if_nok (error, exit);
 
                                        /* the vtable of the field might not be initialized at this point */
@@ -4177,7 +4178,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        mt = mint_type (ftype);
                        klass = mono_class_from_mono_type_internal (ftype);
 
-                       interp_emit_ldsfld (td, field, klass, mt, error);
+                       interp_emit_sfld_access (td, field, klass, mt, TRUE, error);
                        goto_if_nok (error, exit);
 
                        if (mt == MINT_TYPE_VT) {
@@ -4201,7 +4202,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        mono_class_vtable_checked (domain, fld_klass, error);
                        goto_if_nok (error, exit);
 
-                       interp_emit_stsfld (td, field, fld_klass, mt, error);
+                       interp_emit_sfld_access (td, field, fld_klass, mt, FALSE, error);
                        goto_if_nok (error, exit);
 
                        if (mt == MINT_TYPE_VT) {