[interp] Add constant propagation of integers (mono/mono#17326)
authorVlad Brezae <brezaevlad@gmail.com>
Wed, 16 Oct 2019 10:56:14 +0000 (13:56 +0300)
committerGitHub <noreply@github.com>
Wed, 16 Oct 2019 10:56:14 +0000 (13:56 +0300)
* [interp] Make StackValue more intuitive

Replace the opcode field with an enum which will represent what type of value is contained (STACK_VALUE_LOCAL, STACK_VALUE_I4 etc)

* [interp] Add the mint type to the local data

Makes the emitting code easier and we no longer compute the mint type for each local access, which is fairly expensive.

* [interp] Handle indirect locals saner during cprop

Instead of checking for the indirect flag when loading from a local, simply never store a StackValue into such a local since we can't use it. We use the same logic when not pushing on the stack values for such locals.

* [interp] Add constant folding for integers

We decode the values from LDC_I4 and LDC_I8 opcodes and track/propagate the values on the stack and in the locals using the existing infrastructure. When loading a local, we try to load using the known constant value instead, so we might get to kill that local.

* [interp] Similar to stloc.np we can optimize pop/push pairs

If the pushing instruction pushes the same value that was popped by the previous instruction, we can kill them both

* [tests] Remove issue that seems to have been fixed for a while

Commit migrated from https://github.com/mono/mono/commit/0aabccef9bf6f17313fa384349a8089e8d8b0642

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

index 238acba..3eee7d5 100644 (file)
@@ -61,6 +61,7 @@ typedef enum {
 #define MINT_IS_CONDITIONAL_BRANCH(op) ((op) >= MINT_BRFALSE_I4 && (op) <= MINT_BLT_UN_R8_S)
 #define MINT_IS_CALL(op) ((op) >= MINT_CALL && (op) <= MINT_JIT_CALL)
 #define MINT_IS_NEWOBJ(op) ((op) >= MINT_NEWOBJ && (op) <= MINT_NEWOBJ_MAGIC)
+#define MINT_IS_LDC_I4(op) ((op) >= MINT_LDC_I4_M1 && (op) <= MINT_LDC_I4)
 
 #define MINT_POP_ALL   -2
 #define MINT_VAR_PUSH  -1
index 36c199d..2dc8ac7 100644 (file)
@@ -52,13 +52,22 @@ typedef struct
        unsigned char flags;
 } StackInfo;
 
+#define STACK_VALUE_NONE 0
+#define STACK_VALUE_LOCAL 1
+#define STACK_VALUE_I4 2
+#define STACK_VALUE_I8 3
+
 // StackValue contains data to construct an InterpInst that is equivalent with the contents
 // of the stack slot / local.
 typedef struct {
-       // Indicates the type of the stored information. It could be a LDLOC or a LDC opcode
-       int opcode;
-       // If opcode is a LDLOC, this is the local_index
-       int data;
+       // Indicates the type of the stored information. It can be a local or a constant
+       int type;
+       // Holds the local index or the actual constant value
+       union {
+               int local;
+               gint32 i;
+               gint64 l;
+       };
 } StackValue;
 
 typedef struct
@@ -108,6 +117,7 @@ typedef struct {
 
 typedef struct {
        MonoType *type;
+       int mt;
        int flags;
        int offset;
 } InterpLocal;
@@ -761,9 +771,10 @@ store_arg(TransformData *td, int n)
 }
 
 static void
-load_local_general (TransformData *td, int local, MonoType *type)
+load_local (TransformData *td, int local)
 {
-       int mt = mint_type (type);
+       MonoType *type = td->locals [local].type;
+       int mt = td->locals [local].mt;
        MonoClass *klass = NULL;
        if (mt == MINT_TYPE_VT) {
                klass = mono_class_from_mono_type_internal (type);
@@ -782,17 +793,11 @@ load_local_general (TransformData *td, int local, MonoType *type)
        PUSH_TYPE(td, stack_type[mt], klass);
 }
 
-static void
-load_local (TransformData *td, int n)
-{
-       MonoType *type = td->header->locals [n];
-       load_local_general (td, n, type);
-}
-
 static void 
-store_local_general (TransformData *td, int local, MonoType *type)
+store_local (TransformData *td, int local)
 {
-       int mt = mint_type (type);
+       MonoType *type = td->locals [local].type;
+       int mt = td->locals [local].mt;
        CHECK_STACK (td, 1);
 #if SIZEOF_VOID_P == 8
        if (td->sp [-1].type == STACK_TYPE_I4 && stack_type [mt] == STACK_TYPE_I8) {
@@ -821,13 +826,6 @@ store_local_general (TransformData *td, int local, MonoType *type)
        --td->sp;
 }
 
-static void
-store_local (TransformData *td, int n)
-{
-       MonoType *type = td->header->locals [n];
-       store_local_general (td, n, type);
-}
-
 #define SIMPLE_OP(td, op) \
        do { \
                interp_add_ins (td, op); \
@@ -943,6 +941,7 @@ create_interp_local (TransformData *td, MonoType *type)
                td->locals = (InterpLocal*) g_realloc (td->locals, td->locals_capacity * sizeof (InterpLocal));
        }
        td->locals [td->locals_size].type = type;
+       td->locals [td->locals_size].mt = mint_type (type);
        td->locals [td->locals_size].flags = 0;
        td->locals [td->locals_size].offset = -1;
        td->locals_size++;
@@ -1020,7 +1019,7 @@ emit_store_value_as_local (TransformData *td, MonoType *src)
        int size = mini_magic_type_size (NULL, src);
        int local = create_interp_local (td, mini_native_type_replace_type (src));
 
-       store_local_general (td, local, src);
+       store_local (td, local);
 
        size = ALIGN_TO (size, MINT_VT_ALIGNMENT);
        interp_add_ins (td, MINT_LDLOC_VT);
@@ -1078,6 +1077,62 @@ interp_ldc_i4_get_const (InterpInst *ins)
        }
 }
 
+static InterpInst*
+interp_inst_replace_with_i4_const (TransformData *td, InterpInst *ins, gint32 ct)
+{
+       switch (ct) {
+               case -1: ins->opcode = MINT_LDC_I4_M1; break;
+               case 0: ins->opcode = MINT_LDC_I4_0; break;
+               case 1: ins->opcode = MINT_LDC_I4_1; break;
+               case 2: ins->opcode = MINT_LDC_I4_2; break;
+               case 3: ins->opcode = MINT_LDC_I4_3; break;
+               case 4: ins->opcode = MINT_LDC_I4_4; break;
+               case 5: ins->opcode = MINT_LDC_I4_5; break;
+               case 6: ins->opcode = MINT_LDC_I4_6; break;
+               case 7: ins->opcode = MINT_LDC_I4_7; break;
+               case 8: ins->opcode = MINT_LDC_I4_8; break;
+               default: {
+                       int size = mono_interp_oplen [ins->opcode];
+                       if (ct >= -128 && ct <= 127 ) {
+                               if (size < 2) {
+                                       ins = interp_insert_ins (td, ins, MINT_LDC_I4_S);
+                                       interp_clear_ins (td, ins->prev);
+                               } else {
+                                       ins->opcode = MINT_LDC_I4_S;
+                               }
+                               ins->data [0] = (gint8)ct;
+                       } else {
+                               if (size < 3) {
+                                       ins = interp_insert_ins (td, ins, MINT_LDC_I4);
+                                       interp_clear_ins (td, ins->prev);
+                               } else {
+                                       ins->opcode = MINT_LDC_I4;
+                               }
+                               WRITE32_INS (ins, 0, &ct);
+                       }
+                       break;
+               }
+       }
+
+       return ins;
+}
+
+static InterpInst*
+interp_inst_replace_with_i8_const (TransformData *td, InterpInst *ins, gint64 ct)
+{
+       int size = mono_interp_oplen [ins->opcode];
+
+       if (size < 5) {
+               ins = interp_insert_ins (td, ins, MINT_LDC_I8);
+               interp_clear_ins (td, ins->prev);
+       } else {
+               ins->opcode = MINT_LDC_I8;
+       }
+       WRITE64_INS (ins, 0, &ct);
+
+       return ins;
+}
+
 static int
 interp_get_ldind_for_mt (int mt)
 {
@@ -1433,9 +1488,9 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
 
                        int local = create_interp_local (td, local_type);
 
-                       store_local_general (td, local, local_type);
+                       store_local (td, local);
                        interp_emit_ldelema (td, target_method->klass, value_class);
-                       load_local_general (td, local, local_type);
+                       load_local (td, local);
                        interp_emit_stobj (td, element_class);
                        td->ip += 5;
                        return TRUE;
@@ -1685,11 +1740,11 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
                        localb = create_interp_local (td, t);
 
                        // Save arguments
-                       store_local_general (td, localb, t);
-                       store_local_general (td, locala, t);
+                       store_local (td, localb);
+                       store_local (td, locala);
                        // (a > b)
-                       load_local_general (td, locala, t);
-                       load_local_general (td, localb, t);
+                       load_local (td, locala);
+                       load_local (td, localb);
                        if (is_unsigned)
                                interp_add_ins (td, is_i8 ? MINT_CGT_UN_I8 : MINT_CGT_UN_I4);
                        else
@@ -1697,8 +1752,8 @@ interp_handle_intrinsics (TransformData *td, MonoMethod *target_method, MonoClas
                        td->sp --;
                        SET_SIMPLE_TYPE (td->sp - 1, STACK_TYPE_I4);
                        // (a < b)
-                       load_local_general (td, locala, t);
-                       load_local_general (td, localb, t);
+                       load_local (td, locala);
+                       load_local (td, localb);
                        if (is_unsigned)
                                interp_add_ins (td, is_i8 ? MINT_CLT_UN_I8 : MINT_CLT_UN_I4);
                        else
@@ -2765,6 +2820,8 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet
                imethod->local_offsets [i] = offset;
                td->locals [i].offset = offset;
                td->locals [i].flags = 0;
+               td->locals [i].type = header->locals [i];
+               td->locals [i].mt = mint_type (header->locals [i]);
                offset += size;
        }
        offset = (offset + 7) & ~7;
@@ -2781,14 +2838,6 @@ interp_method_compute_offsets (TransformData *td, InterpMethod *imethod, MonoMet
        td->total_locals_size = offset;
 }
 
-static MonoType*
-get_arg_type (MonoMethodSignature *signature, int arg_n)
-{
-       if (signature->hasthis && arg_n == 0)
-               return mono_get_object_type ();
-       return signature->params [arg_n - !!signature->hasthis];
-}
-
 /* Return false is failure to init basic blocks due to being in inline method */
 static gboolean
 init_bb_start (TransformData *td, MonoMethodHeader *header, gboolean inlining)
@@ -3160,7 +3209,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                for (i = signature->param_count - 1; i >= 0; i--) {
                        local = create_interp_local (td, signature->params [i]);
                        arg_locals [i + !!signature->hasthis] = local;
-                       store_local_general (td, local, signature->params [i]);
+                       store_local (td, local);
                }
 
                if (signature->hasthis) {
@@ -3171,7 +3220,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        MonoType *type = mono_get_object_type ();
                        local = create_interp_local (td, type);
                        arg_locals [0] = local;
-                       store_local_general (td, local, type);
+                       store_local (td, local);
                }
 
                local_locals = (guint32*) g_malloc (header->num_locals * sizeof (guint32));
@@ -3266,7 +3315,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                load_arg (td, arg_n);
                        else
-                               load_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
+                               load_local (td, arg_locals [arg_n]);
                        ++td->ip;
                        break;
                }
@@ -3278,7 +3327,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                load_local (td, loc_n);
                        else
-                               load_local_general (td, local_locals [loc_n], header->locals [loc_n]);
+                               load_local (td, local_locals [loc_n]);
                        ++td->ip;
                        break;
                }
@@ -3290,7 +3339,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                store_local (td, loc_n);
                        else
-                               store_local_general (td, local_locals [loc_n], header->locals [loc_n]);
+                               store_local (td, local_locals [loc_n]);
                        ++td->ip;
                        break;
                }
@@ -3299,7 +3348,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                load_arg (td, arg_n);
                        else
-                               load_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
+                               load_local (td, arg_locals [arg_n]);
                        td->ip += 2;
                        break;
                }
@@ -3326,7 +3375,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                store_arg (td, arg_n);
                        else
-                               store_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
+                               store_local (td, arg_locals [arg_n]);
                        td->ip += 2;
                        break;
                }
@@ -3335,7 +3384,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                load_local (td, loc_n);
                        else
-                               load_local_general (td, local_locals [loc_n], header->locals [loc_n]);
+                               load_local (td, local_locals [loc_n]);
                        td->ip += 2;
                        break;
                }
@@ -3355,7 +3404,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                        if (!inlining)
                                store_local (td, loc_n);
                        else
-                               store_local_general (td, local_locals [loc_n], header->locals [loc_n]);
+                               store_local (td, local_locals [loc_n]);
                        td->ip += 2;
                        break;
                }
@@ -4445,7 +4494,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                 * We create a local variable in the frame so that we can fetch its address.
                                 */
                                int local = create_interp_local (td, m_class_get_byval_arg (klass));
-                               store_local_general (td, local, m_class_get_byval_arg (klass));
+                               store_local (td, local);
                                interp_add_ins (td, MINT_LDLOCA_S);
                                td->last_ins->data [0] = local;
                                td->locals [local].flags |= INTERP_LOCAL_FLAG_INDIRECT;
@@ -5786,7 +5835,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                if (!inlining)
                                        load_arg (td, arg_n);
                                else
-                                       load_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
+                                       load_local (td, arg_locals [arg_n]);
                                td->ip += 3;
                                break;
                        }
@@ -5812,7 +5861,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                if (!inlining)
                                        store_arg (td, arg_n);
                                else
-                                       store_local_general (td, arg_locals [arg_n], get_arg_type (signature, arg_n));
+                                       store_local (td, arg_locals [arg_n]);
                                td->ip += 3;
                                break;
                        }
@@ -5821,7 +5870,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                if (!inlining)
                                        load_local (td, loc_n);
                                else
-                                       load_local_general (td, local_locals [loc_n], header->locals [loc_n]);
+                                       load_local (td, local_locals [loc_n]);
                                td->ip += 3;
                                break;
                        }
@@ -5841,7 +5890,7 @@ generate_code (TransformData *td, MonoMethod *method, MonoMethodHeader *header,
                                if (!inlining)
                                        store_local (td, loc_n);
                                else
-                                       store_local_general (td, local_locals [loc_n], header->locals [loc_n]);
+                                       store_local (td, local_locals [loc_n]);
                                td->ip += 3;
                                break;
                        }
@@ -6327,11 +6376,8 @@ clear_stack_content_info_for_local (StackContentInfo *start, StackContentInfo *e
 {
        StackContentInfo *si;
        for (si = start; si < end; si++) {
-               if (si->val.opcode != MINT_NOP) {
-                       g_assert (MINT_IS_LDLOC (si->val.opcode));
-                       if (si->val.data == local)
-                               si->val.opcode = MINT_NOP;
-               }
+               if (si->val.type == STACK_VALUE_LOCAL && si->val.local == local)
+                       si->val.type = STACK_VALUE_NONE;
        }
 }
 
@@ -6342,11 +6388,8 @@ clear_local_content_info_for_local (StackValue *start, StackValue *end, int loca
 {
        StackValue *sval;
        for (sval = start; sval < end; sval++) {
-               if (sval->opcode != MINT_NOP) {
-                       g_assert (MINT_IS_LDLOC (sval->opcode));
-                       if (sval->data == local)
-                               sval->opcode = MINT_NOP;
-               }
+               if (sval->type == STACK_VALUE_LOCAL && sval->local == local)
+                       sval->type = STACK_VALUE_NONE;
        }
 }
 
@@ -6405,18 +6448,18 @@ interp_local_equal (StackValue *locals, int local1, int local2)
 {
        if (local1 == local2)
                return TRUE;
-       if (locals [local1].opcode != MINT_NOP) {
-               g_assert (MINT_IS_LDLOC (locals [local1].opcode));
+       if (locals [local1].type == STACK_VALUE_LOCAL && locals [local1].local == local2) {
                // local1 is a copy of local2
-               if (locals [local1].data == local2)
-                       return TRUE;
+               return TRUE;
        }
-       if (locals [local2].opcode != MINT_NOP) {
-               g_assert (MINT_IS_LDLOC (locals [local2].opcode));
+       if (locals [local2].type == STACK_VALUE_LOCAL && locals [local2].local == local1) {
                // local2 is a copy of local1
-               if (locals [local2].data == local1)
-                       return TRUE;
+               return TRUE;
        }
+       if (locals [local1].type == STACK_VALUE_I4 && locals [local2].type == STACK_VALUE_I4)
+               return locals [local1].i == locals [local2].i;
+       if (locals [local1].type == STACK_VALUE_I8 && locals [local2].type == STACK_VALUE_I8)
+               return locals [local1].l == locals [local2].l;
        return FALSE;
 }
 
@@ -6473,10 +6516,12 @@ retry:
                                                int stored_local = prev_ins->data [0];
                                                if (td->verbose_level)
                                                        g_print ("Add stloc.np : ldloc (off %p), stloc (off %p)\n", ins->il_offset, prev_ins->il_offset);
-                                               // We know what local is on the stack now. Track it
                                                sp->ins = NULL;
-                                               sp->val.opcode = ins->opcode;
-                                               sp->val.data = stored_local;
+                                               if (sp->val.type == STACK_VALUE_NONE) {
+                                                       // We know what local is on the stack now. Track it
+                                                       sp->val.type = STACK_VALUE_LOCAL;
+                                                       sp->val.local = stored_local;
+                                               }
 
                                                // Clear the previous stloc instruction
                                                interp_clear_ins (td, prev_ins);
@@ -6486,25 +6531,41 @@ retry:
                                                local_ref_count [loaded_local]--;
                                        }
                                }
-                       } else if (locals [loaded_local].opcode != MINT_NOP && !(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
-                               g_assert (MINT_IS_LDLOC (locals [loaded_local].opcode));
+                       } else if (locals [loaded_local].type == STACK_VALUE_LOCAL) {
+                               g_assert (locals [loaded_local].type == STACK_VALUE_LOCAL);
+                               g_assert (!(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT));
                                // do copy propagation of the original source
                                if (td->verbose_level)
-                                       g_print ("cprop %d -> %d\n", loaded_local, locals [loaded_local].data);
+                                       g_print ("cprop %d -> %d\n", loaded_local, locals [loaded_local].local);
                                mono_interp_stats.copy_propagations++;
                                local_ref_count [loaded_local]--;
-                               ins->data [0] = locals [loaded_local].data;
+                               ins->data [0] = locals [loaded_local].local;
                                local_ref_count [ins->data [0]]++;
+                       } else if (locals [loaded_local].type == STACK_VALUE_I4 || locals [loaded_local].type == STACK_VALUE_I8) {
+                               gboolean is_i4 = locals [loaded_local].type == STACK_VALUE_I4;
+                               g_assert (!(td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT));
+                               if (is_i4)
+                                       ins = interp_inst_replace_with_i4_const (td, ins, locals [loaded_local].i);
+                               else
+                                       ins = interp_inst_replace_with_i8_const (td, ins, locals [loaded_local].l);
+                               sp->ins = ins;
+                               sp->val = locals [loaded_local];
+                               local_ref_count [loaded_local]--;
+                               mono_interp_stats.copy_propagations++;
+                               if (td->verbose_level)
+                                       g_print ("cprop %d -> ct %d\n", loaded_local, is_i4 ? locals [loaded_local].i : locals [loaded_local].l);
+                               // FIXME this replace_op got ugly
+                               replace_op = ins->opcode;
                        }
                        if (!replace_op) {
                                // Save the ldloc on the stack if it wasn't optimized away
                                // For simplicity we don't track locals that have their address taken
                                // since it is hard to detect instructions that change the local value.
                                if (td->locals [loaded_local].flags & INTERP_LOCAL_FLAG_INDIRECT) {
-                                       sp->val.opcode = MINT_NOP;
+                                       sp->val.type = STACK_VALUE_NONE;
                                } else {
-                                       sp->val.opcode = ins->opcode;
-                                       sp->val.data = ins->data [0];
+                                       sp->val.type = STACK_VALUE_LOCAL;
+                                       sp->val.local = ins->data [0];
                                }
                                sp->ins = ins;
                        }
@@ -6512,17 +6573,17 @@ retry:
                } else if (MINT_IS_STLOC (ins->opcode)) {
                        int dest_local = ins->data [0];
                        sp--;
-                       if (sp->val.opcode != MINT_NOP) {
-                               g_assert (MINT_IS_LDLOC (sp->val.opcode));
-                               int mt = sp->val.opcode - MINT_LDLOC_I1;
-                               if (ins->opcode - MINT_STLOC_I1 == mt) {
-                                       // Same local, same type of load and store. Propagate value
-                                       int src_local = sp->val.data;
+                       if (sp->val.type == STACK_VALUE_LOCAL) {
+                               int src_local = sp->val.local;
+                               if (td->locals [src_local].mt == td->locals [dest_local].mt) {
+                                       // The locals have the same type. We can propagate the value
                                        int vtsize = (ins->opcode == MINT_STLOC_VT) ? ins->data [1] : 0;
 
-                                       // Track what exactly is stored into local
-                                       locals [dest_local].opcode = sp->val.opcode;
-                                       locals [dest_local].data = src_local;
+                                       if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
+                                               // Track what exactly is stored into local
+                                               locals [dest_local].type = STACK_VALUE_LOCAL;
+                                               locals [dest_local].local = src_local;
+                                       }
 
                                        if (sp->ins) {
                                                // Clear ldloc / stloc pair and replace it with movloc superinstruction
@@ -6531,7 +6592,7 @@ retry:
                                                interp_clear_ins (td, sp->ins);
                                                interp_clear_ins (td, ins);
 
-                                               ins = interp_insert_ins (td, ins, get_movloc_for_type (mt));
+                                               ins = interp_insert_ins (td, ins, get_movloc_for_type (td->locals [src_local].mt));
                                                ins->data [0] = src_local;
                                                ins->data [1] = dest_local;
                                                if (vtsize)
@@ -6539,41 +6600,72 @@ retry:
                                                mono_interp_stats.movlocs++;
                                        }
                                } else {
-                                       locals [dest_local].opcode = MINT_NOP;
+                                       locals [dest_local].type = STACK_VALUE_NONE;
                                }
+                       } else if (sp->val.type == STACK_VALUE_NONE) {
+                               locals [dest_local].type = STACK_VALUE_NONE;
                        } else {
-                               locals [dest_local].opcode = MINT_NOP;
+                               g_assert (sp->val.type == STACK_VALUE_I4 || sp->val.type == STACK_VALUE_I8);
+                               if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT))
+                                       locals [dest_local] = sp->val;
                        }
                        clear_stack_content_info_for_local (stack, sp, dest_local);
                        clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
+               } else if (MINT_IS_LDC_I4 (ins->opcode) || ins->opcode == MINT_LDC_I8) {
+                       StackValue val;
+                       gboolean is_i8 = ins->opcode == MINT_LDC_I8;
+                       InterpInst *prev_ins = interp_prev_ins (ins);
+
+                       if (is_i8) {
+                               val.type = STACK_VALUE_I8;
+                               val.l = READ64 (&ins->data [0]);
+                       } else {
+                               val.type = STACK_VALUE_I4;
+                               val.i = interp_ldc_i4_get_const (ins);
+                       }
+
+                       if (prev_ins && prev_ins->opcode == MINT_POP &&
+                                       ((is_i8 && sp->val.type == STACK_VALUE_I8 && sp->val.l == val.l) ||
+                                       (!is_i8 && sp->val.type == STACK_VALUE_I4 && sp->val.i == val.i)) &&
+                                       !interp_is_bb_start (td, prev_ins, ins)) {
+                               // The previous instruction pops the stack of the value we are pushing
+                               // right now. We can kill both instructions
+                               if (td->verbose_level)
+                                       g_print ("Kill redundant pop/ldc pair: pop (off %p), ldc (off %p)\n", prev_ins->il_offset, ins->il_offset);
+                               interp_clear_ins (td, prev_ins);
+                               interp_clear_ins (td, ins);
+                               mono_interp_stats.killed_instructions += 2;
+                       } else {
+                               sp->ins = ins;
+                               sp->val = val;
+                       }
+                       sp++;
                } else if (MINT_IS_MOVLOC (ins->opcode)) {
                        int src_local = ins->data [0];
                        int dest_local = ins->data [1];
                        local_ref_count [src_local]++;
-                       if (locals [src_local].opcode != MINT_NOP) {
-                               locals [dest_local].opcode = locals [src_local].opcode;
-                               locals [dest_local].data = locals [src_local].data;
-                       } else {
-                               // FIXME We lost the type information. We don't really seem to use it.
-                               // It might make sense to switch opcode field to an enum instead, when
-                               // we will also support constant propagation.
-                               locals [dest_local].opcode = MINT_LDLOC_I1;
-                               locals [dest_local].data = src_local;
+                       if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
+                               if (locals [src_local].type != STACK_VALUE_NONE) {
+                                       locals [dest_local] = locals [src_local];
+                               } else {
+                                       locals [dest_local].type = STACK_VALUE_LOCAL;
+                                       locals [dest_local].local = src_local;
+                               }
+                               clear_stack_content_info_for_local (stack, sp, dest_local);
+                               clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
                        }
-                       clear_stack_content_info_for_local (stack, sp, dest_local);
-                       clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
                } else if (MINT_IS_STLOC_NP (ins->opcode)) {
                        int dest_local = ins->data [0];
                        // Prevent optimizing away the instruction that pushed the value on the stack
                        sp [-1].ins = NULL;
                        // The local contains the value of the top of stack
-                       locals [dest_local].opcode = sp [-1].val.opcode;
-                       locals [dest_local].data = sp [-1].val.data;
-                       clear_stack_content_info_for_local (stack, sp, dest_local);
-                       clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
+                       if (!(td->locals [dest_local].flags & INTERP_LOCAL_FLAG_INDIRECT)) {
+                               locals [dest_local] = sp [-1].val;
+                               clear_stack_content_info_for_local (stack, sp, dest_local);
+                               clear_local_content_info_for_local (locals, locals + td->locals_size, dest_local);
+                       }
                } else if (ins->opcode == MINT_DUP || ins->opcode == MINT_DUP_VT) {
-                       sp [0].val.opcode = sp [-1].val.opcode;
-                       sp [0].val.data = sp [-1].val.data;
+                       sp [0].val = sp [-1].val;
                        sp [0].ins = ins;
                        // If top of stack is known, we could also replace dup with an explicit
                        // propagated instruction, so we remove the top of stack dependency
@@ -6606,6 +6698,10 @@ retry:
                                interp_clear_ins (td, sp->ins);
                                interp_clear_ins (td, ins);
                                mono_interp_stats.killed_instructions += 2;
+                               // The value pop-ed by this instruction can still be accessed. If we also
+                               // kill the instruction pushing the value, then we need to empty the
+                               // value of the stack, so it is not considered for further optimizations.
+                               sp->val.type = STACK_VALUE_NONE;
                        }
                } else if (ins->opcode == MINT_NEWOBJ_FAST && ins->data [0] == INLINED_METHOD_FLAG) {
                        int param_count = ins->data [1];