#include "mini.h"
-extern guint8 mono_burg_arity [];
-
#define USE_ORIGINAL_VARS
#define CREATE_PRUNED_SSA
} while (0)
+static GList*
+g_list_prepend_mempool (GList* l, MonoMemPool* mp, gpointer datum)
+{
+ GList* n = mono_mempool_alloc (mp, sizeof (GList));
+ n->next = l;
+ n->prev = NULL;
+ n->data = datum;
+ return n;
+}
+
static void
unlink_target (MonoBasicBlock *bb, MonoBasicBlock *target)
{
arity = mono_burg_arity [inst->opcode];
- if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) &&
+ if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_ADDRESS_TAKEN) &&
(inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
MonoInst *new_var;
int idx = inst->inst_i0->inst_c0;
} else {
new_var = cfg->varinfo [idx];
- if (new_var->opcode != OP_ARG) {
+ if ((new_var->opcode != OP_ARG) && (new_var->opcode != OP_LOCAL)) {
/* uninitialized variable ? */
g_warning ("using uninitialized variables %d in BB%d (%s)", idx, bb->block_num,
mono_method_full_name (cfg->method, TRUE));
}
}
+static int
+extends_live (MonoInst *inst)
+{
+ int arity;
+
+ if (!inst)
+ return 0;
+
+ arity = mono_burg_arity [inst->opcode];
+
+ if (inst->ssa_op == MONO_SSA_LOAD &&
+ (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+ return 1;
+ } else {
+ if (arity) {
+ if (inst->ssa_op != MONO_SSA_STORE)
+ if (extends_live (inst->inst_left))
+ return 1;
+ if (arity > 1)
+ if (extends_live (inst->inst_right))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int
+replace_usage_new (MonoCompile *cfg, MonoInst *inst, int varnum, MonoInst *rep)
+{
+ int arity;
+
+ if (!inst)
+ return 0;
+
+ arity = mono_burg_arity [inst->opcode];
+
+ if ((inst->ssa_op == MONO_SSA_LOAD) &&
+ (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
+ inst->inst_i0->inst_c0 == varnum && rep->type == inst->type) {
+ *inst = *rep;
+ return 1;
+ } else {
+ if (arity) {
+ if (inst->ssa_op != MONO_SSA_STORE)
+ if (replace_usage_new (cfg, inst->inst_left, varnum, rep))
+ return 1;
+ if (arity > 1)
+ if (replace_usage_new (cfg, inst->inst_right, varnum, rep))
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static void
mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, MonoInst **stack)
{
MonoInst *inst, *new_var;
int i, j, idx;
- GList *tmp;
- MonoInst *new_stack [max_vars];
+ GSList *tmp;
+ MonoInst **new_stack;
#ifdef DEBUG_SSA
printf ("RENAME VARS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
idx = inst->inst_i0->inst_c0;
g_assert (idx < max_vars);
- if (!stack [idx] && bb == cfg->bb_init) {
+ if ((!stack [idx]) && (bb == cfg->bb_init) && (inst->inst_i0->opcode != OP_ARG)) {
new_var = cfg->varinfo [idx];
} else {
new_var = mono_compile_create_var (cfg, inst->inst_i0->inst_vtype, inst->inst_i0->opcode);
}
}
- for (tmp = bb->dominated; tmp; tmp = tmp->next) {
- memcpy (new_stack, stack, sizeof (MonoInst *) * max_vars);
- mono_ssa_rename_vars (cfg, max_vars, (MonoBasicBlock *)tmp->data, new_stack);
+ if (bb->dominated) {
+ new_stack = g_new (MonoInst*, max_vars);
+ for (tmp = bb->dominated; tmp; tmp = tmp->next) {
+ memcpy (new_stack, stack, sizeof (MonoInst *) * max_vars);
+ mono_ssa_rename_vars (cfg, max_vars, (MonoBasicBlock *)tmp->data, new_stack);
+ }
+ g_free (new_stack);
}
}
g_assert (!(cfg->comp_done & MONO_COMP_SSA));
/* we dont support methods containing exception clauses */
- g_assert (((MonoMethodNormal *)cfg->method)->header->num_clauses == 0);
+ g_assert (mono_method_get_header (cfg->method)->num_clauses == 0);
g_assert (!cfg->disable_ssa);
//printf ("COMPUTS SSA %s %d\n", mono_method_full_name (cfg->method, TRUE), cfg->num_varinfo);
store->next = bb->code;
bb->code = store;
+ if (!bb->last_ins)
+ bb->last_ins = bb->code;
#ifdef DEBUG_SSA
printf ("ADD PHI BB%d %s\n", cfg->bblocks [idx]->block_num, mono_method_full_name (cfg->method, TRUE));
arity = mono_burg_arity [inst->opcode];
- if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD || inst->ssa_op == MONO_SSA_STORE) &&
+ if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_ADDRESS_TAKEN || inst->ssa_op == MONO_SSA_STORE) &&
(inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
MonoInst *new_var;
int idx = inst->inst_i0->inst_c0;
if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->ssa_op == MONO_SSA_LOAD &&
inst->inst_i0->inst_c0 == inst->inst_i1->inst_i0->inst_c0) {
inst->ssa_op = MONO_SSA_NOP;
- inst->opcode = CEE_NOP;
+ inst->opcode = OP_NOP;
}
}
}
/* remove the phi functions */
- inst->opcode = CEE_NOP;
+ inst->opcode = OP_NOP;
inst->ssa_op = MONO_SSA_NOP;
}
}
varlist_array = mono_ssa_get_allocatable_vars (cfg);
for (i = 0; i < varlist_array->len; i++) {
- GList *l, *t, *regs, *vars = g_ptr_array_index (varlist_array, i);
+ GList *l, *regs, *vars = g_ptr_array_index (varlist_array, i);
MonoMethodVar *vmv, *amv;
if (g_list_length (vars) <= 1) {
if (amv->range.last_use.abs_pos >= vmv->range.first_use.abs_pos)
break;
- active = g_list_remove_link (active, active);
+ active = g_list_delete_link (active, active);
regs = g_list_prepend (regs, (gpointer)amv->reg);
}
if (!regs)
- regs = g_list_prepend (regs, vmv->idx);
+ regs = g_list_prepend (regs, (gpointer)vmv->idx);
vmv->reg = (int)regs->data;
- regs = g_list_remove_link (regs, regs);
+ regs = g_list_delete_link (regs, regs);
active = mono_varlist_insert_sorted (cfg, active, vmv, TRUE);
}
MonoInst *inst;
} MonoVarUsageInfo;
-static void
+
+
+
+/*
+ * Returns TRUE if the tree can have side effects.
+ */
+static gboolean
analyze_dev_use (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *root, MonoInst *inst)
{
MonoMethodVar *info;
int i, idx, arity;
+ gboolean has_side_effects;
if (!inst)
- return;
+ return FALSE;
arity = mono_burg_arity [inst->opcode];
+ switch (inst->opcode) {
+#define ANALYZE_DEV_USE_SPECIFIC_OPS 1
+#define OPDEF(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) case a1:
+#include "simple-cee-ops.h"
+#undef OPDEF
+#define MINI_OP(a1,a2) case a1:
+#include "simple-mini-ops.h"
+#undef MINI_OP
+#undef ANALYZE_DEV_USE_SPECIFIC_OPS
+ has_side_effects = FALSE;
+ break;
+ default:
+ has_side_effects = TRUE;
+ }
if ((inst->ssa_op == MONO_SSA_STORE) &&
(inst->inst_i0->opcode == OP_LOCAL /*|| inst->inst_i0->opcode == OP_ARG */)) {
//printf ("FOUND %d\n", idx);
ui->bb = bb;
ui->inst = root;
- info->uses = g_list_prepend (info->uses, ui);
+ info->uses = g_list_prepend_mempool (info->uses, cfg->mempool, ui);
}
}
}
- if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) &&
+ if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_ADDRESS_TAKEN) &&
(inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo));
idx = inst->inst_i0->inst_c0;
//printf ("FOUND %d\n", idx);
ui->bb = bb;
ui->inst = root;
- info->uses = g_list_prepend (info->uses, ui);
+ info->uses = g_list_prepend_mempool (info->uses, cfg->mempool, ui);
} else {
if (arity) {
//if (inst->ssa_op != MONO_SSA_STORE)
- analyze_dev_use (cfg, bb, root, inst->inst_left);
+ if (analyze_dev_use (cfg, bb, root, inst->inst_left))
+ has_side_effects = TRUE;
if (arity > 1)
- analyze_dev_use (cfg, bb, root, inst->inst_right);
+ if (analyze_dev_use (cfg, bb, root, inst->inst_right))
+ has_side_effects = TRUE;
}
}
+
+ return has_side_effects;
}
+
/* avoid unnecessary copies of variables:
* Y <= X; Z = Y; is translated to Z = X;
*/
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
for (inst = bb->code; inst; inst = inst->next) {
- if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i0->opcode == OP_LOCAL) {
+ if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i0->opcode == OP_LOCAL &&
+ !IS_CALL (inst->inst_i1->opcode) && inst->inst_i1->opcode != OP_PHI && !inst->flags) {
i1 = cfg->vars [inst->inst_i0->inst_c0];
+/* fixme: compiling mcs does not work when I enable this */
+#if 0
+ if (g_list_length (i1->uses) == 1 && !extends_live (inst->inst_i1)) {
+ MonoVarUsageInfo *vi = (MonoVarUsageInfo *)i1->uses->data;
+ u = vi->inst;
+
+ //printf ("VAR %d %s\n", i1->idx, mono_method_full_name (cfg->method, TRUE));
+ //mono_print_tree (inst); printf ("\n");
+ //mono_print_tree (u); printf ("\n");
+
+ if (replace_usage_new (cfg, u, inst->inst_i0->inst_c0, inst->inst_i1)) {
+
+ //mono_print_tree (u); printf ("\n");
+
+ inst->opcode = OP_NOP;
+ inst->ssa_op = MONO_SSA_NOP;
+ }
+ }
+#endif
if ((next = inst->next) && next->ssa_op == MONO_SSA_STORE && next->inst_i0->opcode == OP_LOCAL &&
next->inst_i1->ssa_op == MONO_SSA_LOAD && next->inst_i1->inst_i0->opcode == OP_LOCAL &&
next->inst_i1->inst_i0->inst_c0 == inst->inst_i0->inst_c0 && g_list_length (i1->uses) == 1 &&
inst->inst_i0 = next->inst_i0;
i2->def = inst;
i1->def = NULL;
- g_list_free (i1->uses);
i1->uses = NULL;
- next->opcode = CEE_NOP;
+ next->opcode = OP_NOP;
next->ssa_op = MONO_SSA_NOP;
}
}
for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
MonoInst *inst;
for (inst = bb->code; inst; inst = inst->next) {
- analyze_dev_use (cfg, bb, inst, inst);
+ gboolean has_side_effects = analyze_dev_use (cfg, bb, inst, inst);
+ if (has_side_effects && (inst->ssa_op == MONO_SSA_STORE) &&
+ (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
+ inst->inst_i0->flags |= MONO_INST_DEFINITION_HAS_SIDE_EFFECTS;
+ }
}
}
return 0;
}
+static int
+simulate_long_compare (int opcode, gint64 a, gint64 b)
+{
+ switch (opcode) {
+ case CEE_BEQ:
+ return a == b;
+ case CEE_BGE:
+ return a >= b;
+ case CEE_BGT:
+ return a > b;
+ case CEE_BLE:
+ return a <= b;
+ case CEE_BLT:
+ return a < b;
+ case CEE_BNE_UN:
+ return a != b;
+ case CEE_BGE_UN:
+ return (guint64)a >= (guint64)b;
+ case CEE_BGT_UN:
+ return (guint64)a > (guint64)b;
+ case CEE_BLE_UN:
+ return (guint64)a <= (guint64)b;
+ case CEE_BLT_UN:
+ return (guint64)a < (guint64)b;
+ default:
+ g_assert_not_reached ();
+ }
+
+ return 0;
+}
+
#define EVAL_CXX(name,op,cast) \
case name: \
- if (inst->inst_i0->opcode == OP_COMPARE) { \
+ if ((inst->inst_i0->opcode == OP_COMPARE) || (inst->inst_i0->opcode == OP_LCOMPARE)) { \
r1 = evaluate_const_tree (cfg, inst->inst_i0->inst_i0, &a, carray); \
r2 = evaluate_const_tree (cfg, inst->inst_i0->inst_i1, &b, carray); \
if (r1 == 1 && r2 == 1) { \
break;
+/* fixme: this only works for interger constants, but not for other types (long, float) */
static int
evaluate_const_tree (MonoCompile *cfg, MonoInst *inst, int *res, MonoInst **carray)
{
}
if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) &&
- inst->inst_i0->opcode == OP_COMPARE) {
+ ((inst->inst_i0->opcode == OP_COMPARE) || (inst->inst_i0->opcode == OP_LCOMPARE))) {
MonoInst *v0 = inst->inst_i0->inst_i0;
MonoInst *v1 = inst->inst_i0->inst_i1;
+ MonoBasicBlock *target = NULL;
- if (evaluate_const_tree (cfg, v0, &a, carray) == 1 &&
- evaluate_const_tree (cfg, v1, &b, carray) == 1) {
- MonoBasicBlock *target;
-
+ /* hack for longs to optimize the simply cases */
+ if (v0->opcode == OP_I8CONST && v1->opcode == OP_I8CONST) {
+ if (simulate_long_compare (inst->opcode, v0->inst_l, v1->inst_l)) {
+ //unlink_target (bb, inst->inst_false_bb);
+ target = inst->inst_true_bb;
+ } else {
+ //unlink_target (bb, inst->inst_true_bb);
+ target = inst->inst_false_bb;
+ }
+ } else if (evaluate_const_tree (cfg, v0, &a, carray) == 1 &&
+ evaluate_const_tree (cfg, v1, &b, carray) == 1) {
if (simulate_compare (inst->opcode, a, b)) {
//unlink_target (bb, inst->inst_false_bb);
target = inst->inst_true_bb;
//unlink_target (bb, inst->inst_true_bb);
target = inst->inst_false_bb;
}
-
+ }
+
+ if (target) {
bb->out_bb [0] = target;
bb->out_count = 1;
- inst->opcode = CEE_BR;
+ inst->opcode = OP_BR;
inst->inst_target_bb = target;
}
- } else if (inst->opcode == CEE_SWITCH && evaluate_const_tree (cfg, inst->inst_left, &a, carray) == 1) {
+ } else if (inst->opcode == CEE_SWITCH && (evaluate_const_tree (cfg, inst->inst_left, &a, carray) == 1) && (a >= 0) && (a < GPOINTER_TO_INT (inst->klass))) {
bb->out_bb [0] = inst->inst_many_bb [a];
bb->out_count = 1;
inst->inst_target_bb = bb->out_bb [0];
- inst->opcode = CEE_BR;
+ inst->opcode = OP_BR;
}
}
if (inst->opcode == CEE_SWITCH) {
int r1, i, a;
+ int cases = GPOINTER_TO_INT (inst->klass);
r1 = evaluate_const_tree (cfg, inst->inst_left, &a, carray);
+ if ((r1 == 1) && ((a < 0) || (a >= cases)))
+ r1 = 2;
if (r1 == 1) {
MonoBasicBlock *tb = inst->inst_many_bb [a];
if (!(tb->flags & BB_REACHABLE)) {
*bblist = g_list_prepend (*bblist, tb);
}
} else if (r1 == 2) {
- for (i = (int)inst->klass; i >= 0; i--) {
+ for (i = GPOINTER_TO_INT (inst->klass); i >= 0; i--) {
MonoBasicBlock *tb = inst->inst_many_bb [i];
if (!(tb->flags & BB_REACHABLE)) {
tb->flags |= BB_REACHABLE;
}
}
}
+ } else if (inst->opcode == OP_BR) {
+ MonoBasicBlock *target = inst->inst_target_bb;
+
+ if (!(target->flags & BB_REACHABLE)) {
+ target->flags |= BB_REACHABLE;
+ *bblist = g_list_prepend (*bblist, target);
+ }
} else if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) &&
- inst->inst_i0->opcode == OP_COMPARE) {
+ ((inst->inst_i0->opcode == OP_COMPARE) || (inst->inst_i0->opcode == OP_LCOMPARE))) {
int a, b, r1, r2;
MonoInst *v0 = inst->inst_i0->inst_i0;
MonoInst *v1 = inst->inst_i0->inst_i1;
void
mono_ssa_cprop (MonoCompile *cfg)
{
- MonoInst *carray [cfg->num_varinfo];
+ MonoInst **carray;
MonoBasicBlock *bb;
GList *bblock_list, *cvars;
GList *tmp;
int i;
//printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
+ carray = g_new0 (MonoInst*, cfg->num_varinfo);
+
if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
mono_ssa_create_def_use (cfg);
bb = (MonoBasicBlock *)bblock_list->data;
- bblock_list = g_list_remove_link (bblock_list, bblock_list);
+ bblock_list = g_list_delete_link (bblock_list, bblock_list);
g_assert (bb->flags & BB_REACHABLE);
while (cvars) {
MonoMethodVar *info = (MonoMethodVar *)cvars->data;
- cvars = g_list_remove_link (cvars, cvars);
+ cvars = g_list_delete_link (cvars, cvars);
for (tmp = info->uses; tmp; tmp = tmp->next) {
MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
}
}
+ g_free (carray);
+
cfg->comp_done |= MONO_COMP_REACHABILITY;
}
for (tmp = use->uses; tmp; tmp = tmp->next) {
MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
if (ui->inst == var->def) {
+ /* from the mempool */
use->uses = g_list_remove_link (use->uses, tmp);
break;
}
for (i = 0; i < cfg->num_varinfo; i++) {
MonoMethodVar *info = cfg->vars [i];
work_list = g_list_prepend (work_list, info);
+
+ //if ((info->def != NULL) && (info->def->inst_i1->opcode != OP_PHI)) printf ("SSA DEADCE TOTAL LOCAL\n");
}
while (work_list) {
MonoMethodVar *info = (MonoMethodVar *)work_list->data;
- work_list = g_list_remove_link (work_list, work_list);
+ work_list = g_list_delete_link (work_list, work_list);
- if (!info->uses && info->def) {
+ if (!info->uses && info->def && (!(cfg->varinfo [info->idx]->flags & (MONO_INST_DEFINITION_HAS_SIDE_EFFECTS|MONO_INST_VOLATILE|MONO_INST_INDIRECT)))) {
MonoInst *i1;
//printf ("ELIMINATE %s: ", mono_method_full_name (cfg->method, TRUE)); mono_print_tree (info->def); printf ("\n");
MonoMethodVar *u = cfg->vars [i1->inst_i0->inst_c0];
add_to_dce_worklist (cfg, info, u, &work_list);
}
+ //if (i1->opcode != OP_PHI) printf ("SSA DEADCE DEAD LOCAL\n");
- info->def->opcode = CEE_NOP;
+ info->def->opcode = OP_NOP;
info->def->ssa_op = MONO_SSA_NOP;
}