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
typedef struct {
MonoType *type;
+ int mt;
int flags;
int offset;
} InterpLocal;
}
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);
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) {
--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); \
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++;
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);
}
}
+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)
{
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;
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
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
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;
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)
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) {
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));
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
* 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;
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;
}
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;
}
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;
}
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;
}
{
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;
}
}
{
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;
}
}
{
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;
}
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);
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;
}
} 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
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)
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
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];