2004-12-03 Massimiliano Mantione <massi@ximian.com>
authorMassimiliano Mantione <massi@mono-cvs.ximian.com>
Fri, 3 Dec 2004 11:42:12 +0000 (11:42 -0000)
committerMassimiliano Mantione <massi@mono-cvs.ximian.com>
Fri, 3 Dec 2004 11:42:12 +0000 (11:42 -0000)
* mini.c: Added removal of critical edges (prerequisite for SSAPRE),
and some utility functions (always for SSAPRE), integrated SSAPRE.
* mini.h: Likewise.
* driver.c: Added ssapre option.
* ssa.c: Small fix on OP_ARG handling.
* ssapre.c, ssapre.h: Added files containing SSAPRE implementation.
* Makefile.am: Likewise.
* ../docs/ssapre.txt: Added SSAPRE documentation.

svn path=/trunk/mono/; revision=37005

docs/ssapre.txt [new file with mode: 0644]
mono/mini/ChangeLog
mono/mini/Makefile.am
mono/mini/driver.c
mono/mini/mini.c
mono/mini/mini.h
mono/mini/ssa.c
mono/mini/ssapre.c [new file with mode: 0644]
mono/mini/ssapre.h [new file with mode: 0644]

diff --git a/docs/ssapre.txt b/docs/ssapre.txt
new file mode 100644 (file)
index 0000000..49b9a9a
--- /dev/null
@@ -0,0 +1,248 @@
+
+SSAPRE stands for "SSA based Partial Redundancy Elimination".
+
+The algorithm is explained in this paper:
+
+Partial Redundancy Elimination in SSA Form (1999)
+Robert Kennedy, Sun Chan, SHIN-MING LIU, RAYMOND LO, PENG TU, FRED CHOW
+ACM Transactions on Programming Languages and Systems
+
+http://citeseer.ist.psu.edu/kennedy99partial.html
+
+In this document I give a gentle introduction to the concept of "partial"
+redundancies, and I explain the basic design decisions I took in implementing
+SSAPRE, but the paper is essential to understand the code.
+
+Partial Redundancy Elimination (or PRE) is an optimization that (guess what?)
+tries to remove redundant computations.
+It achieves this by saving the result of "not redundant" evaluations of
+expressions into appositely created temporary variables, so that "redundant"
+evaluations can be replaced by a load from the appropriate variable.
+
+Of course, on register starved architectures (x86) a temporary could cost more
+than the evaluation itself... PRE guarantees that the live range of the
+introduced variables is the minimal possible, but the added pressure on the
+register allocator can be an issue.
+
+The nice thing about PRE is that it not only removes "full" redundancies, but
+also "partial" ones.
+A full redundancy is easy to spot, and straightforward to handle, like in the
+following example (in every example here, the "expression" is "a + b"):
+
+int FullRedundancy1 (int a, int b) {
+     int v1 = a + b;
+     int v2 = a + b;
+     return v1 + v2;
+}
+
+PRE would transform it like this:
+
+int FullRedundancy1 (int a, int b) {
+     int t = a + b;
+     int v1 = t;
+     int v2 = t;
+     return v1 + v2;
+}
+
+Of course, either a copy propagation pass or a register allocator smart enough
+to remove unneeded variables would be necessary afterwords.
+
+Another example of full redundancy is the following:
+
+int FullRedundancy2 (int a, int b) {
+     int v1;
+     
+     if (a >= 0) {
+          v1 = a + b; // BB1
+     } else {
+          a = -a; // BB2
+          v1 = a + b;
+     }
+     
+     int v2 = a + b; // BB3
+     return v1 + v2;
+}
+
+Here the two expressions in BB1 and BB2 are *not* the same thing (a is
+modified in BB2), but both are redundant with the expression in BB3, so the
+code can be transformed like this:
+
+int FullRedundancy2 (int a, int b) {
+     int v1;
+     int t;
+     
+     if (a >= 0) {
+          t = a + b; // BB1
+          v1 = t;
+     } else {
+          a = -a; // BB2
+          t = a + b;
+          v1 = t;
+     }
+     
+     int v2 = t; // BB3
+     return v1 + v2;
+}
+
+Note that there are still two occurrences of the expression, while it can be
+easily seen that one (at the beginning of BB3) would suffice.
+This, however, is not a redundancy for PRE, because there is no path in the
+CFG where the expression is evaluated twice.
+Maybe this other kind of redundancy (which affects code size, and not the
+computations that are actually performed) would be eliminated by code hoisting,
+but I should check it; anyway, it is not a PRE related thing.
+
+An example of partial redundancy, on the other hand, is the following:
+
+int PartialRedundancy (int a, int b) {
+     int v1;
+     
+     if (a >= 0) {
+          v1 = a + b; // BB1
+     } else {
+          v1 = 0; // BB2
+     }
+     
+     int v2 = a + b; // BB3
+     return v1 + v2;
+}
+
+The redundancy is partial because the expression is computed more than once
+along some path in the CFG, not all paths.
+In fact, on the path BB1 - BB3 the expression is computed twice, but on the
+path BB2 - BB3 it is computed only once.
+In this case, PRE must insert new occurrences of the expression in order to
+obtain a full redundancy, and then use temporary variables as before.
+Adding a computation in BB2 would do the job.
+
+One nice thing about PRE is that loop invariants can be seen as partial
+redundancies.
+The idea is that you can get into the loop from two locations: from before the
+loop (at the 1st iteration), and from inside the loop itself (at any other
+iteration). If there is a computation inside the loop that is in fact a loop
+invariant, PRE will spot this, and will handle the BB before the loop as a
+place where to insert a new computation to get a full redundancy.
+At this point, the computation inside the loop would be replaced by an use of
+the temporary stored before the loop, effectively performing "loop invariant
+code motion".
+
+Now, this is what PRE does to the code.
+
+But how does it work?
+
+In "classic" solutions, PRE is formulated as a data flow analysis problem.
+The Muchnick provides a detailed description of the algorithm in this way (it
+is by far the most complex problem of this kind in the whole book).
+The point is that this algorithm does not exploit the SSA form.
+In fact, it has to perform all that amount of data flow analysis exactly
+because it does not take advantage of the work already done if you have put
+the program into SSA form.
+
+The SSAPRE algorithm, on the other hand, is designed with SSA in mind.
+It fully exploits the properties of SSA variables, it also explicitly reuses
+some data structures that must have been computed when building the SSA form,
+and takes great care to output its code in that form already (which means that
+the temporaries it introduces are already "versioned", with all the phi
+variables correctly placed).
+
+The main concept used in this algorithm is the "Factored Redundancy Graph" (or
+FRG in short). Basically, for each given expression, this graph shows all its
+occurrences in the program, and redundant ones are linked to their
+"representative occurrence" (the 1st occurrence met in a CFG traversal).
+The central observation is that the FRG is "factored" because each expression
+occurrence has exactly one representative occurrence, in the same way as the
+SSA form is a "factored" use-definition graph (each use is related to exactly
+one definition).
+And in fact building the FRG is much like building an SSA representation, with
+PHI nodes and all.
+By the way, I use "PHI" for "PHI expression occurrences", and "phi" for the
+usual phi definitions in SSA, because the paper uses an uppercase phi greek
+letter for PHI occurrences (while the lowercase one is standard in SSA).
+
+The really interesting point is that the FRG for a given expression has exactly
+the same "shape" of the use-definition graph for the temporary var that must
+be introduced to remove the redundancy, and this is why SSAPRE can easily emit
+its output code in correct SSA form.
+
+One particular characteristic of the SSAPRE algorithm is that it is "sparse",
+in the sense that it operates on expressions individually, looking only at the
+specific nodes it needs. This is in contrast to the classical way of solving
+data flow analysis problems, which is to operate globally, using data
+structures that work "in parallel" on all the entities they operate on (in
+practice bit sets, with for instance one bit per variable, or one bit per
+expression occurrence, you get the idea). This is handy (it exploits the
+parallelism of bit operations), but in general setting up those data
+structures is time consuming, and if the number of the things represented by
+the bits is not fixed in advance the code can get quite messy (bit sets must
+become growable).
+Moreover, applying special handling to individual expressions becomes a very
+tricky thing.
+
+SSAPRE, on the other hand, looks at the whole program (method in our case) only
+once, when it scans the code to collect (and classify) all expression
+occurrences.
+From here on, it operates on one expression at a time, looking only at its
+specific data structures (which, for each expression, are much smaller than
+the whole program, so the operations are fast).
+
+This approach has another advantage: the data structures used to operate on
+one expressions can be recycled when operating on other expressions, making
+the memory usage of the compiler lower, and (also important) avoiding losing
+time with memory allocations at all.
+This reflects directly on the design of those data structures.
+We can better see these advantages following which data structures are used
+during the application of SSAPRE to a method.
+
+The steps that are performed are the following:
+
+    * Initialization: scan the whole program, collect all the occurrences, and
+      build a worklist of expressions to be processed (each worklist entry
+      describes all the occurrences of the given expression).
+Here the data structures are the following:
+- One struct (the working area), containing the worklist and other
+ "global" data. The worklist itself contains an entry for each expression
+  which in turn has an entry for each occurrence.
+- One "info" struct for each BB, containing interesting dominance and CFG
+  related properties of the BB.
+
+Then, for each entry in the worklist, these operations are performed:
+
+    * PHI placement: find where the PHI nodes of the FRG must be placed.
+    * Renaming: assign appropriate "redundancy classes" to all occurrences (it
+      is like assigning variable versions when building an SSA form).
+    * Analyze: compute various flags in PHI nodes (which are the only places
+      that define where additional computations may be added).
+      This conceptually is composed of two data flow analysis passes, which in
+      practice only scan the PHI nodes in the FRG, not the whole code, so they
+      are not that heavy.
+    * Finalize: make so that the FRG is exactly like the use-def graph for the
+      temporary that will be introduced (it generally must be reshaped a bit
+      according to the flags computed in the previous step).
+      This is also made of two steps, but more for implementation reasons than
+      for conceptual ones.
+    * Code motion: actually update the code using the FRG.
+Here, what's needed is the following:
+- PHI occurrences (and some flags sbout them)
+- PHI argument occurrences (and some flags sbout them)
+- The renaming stack
+In practice, one can observe that each BB can have at most one PHI (we work
+on one expression at a time), and also one PHI argument (which we consider
+occurring at the end of the BB). Therefore, we do not have separate structures
+for these, but store them directly in the BB infos (which are kept for the
+whole SSAPRE invocation).
+The renaming stack is managed directly as a list of occurrences, with
+special handling for PHI nodes (which, being represented directly by their
+BB, are not "occurrences").
+
+
+So far, the only two missing things (with respect to SSAPRE in the paper) are
+unneeded PHIs eliminantion and the handling of "composite" expressions.
+Otherwise, the implementation is complete.
+
+Other interesting issues are:
+- SSAPRE has the assumption that:
+  - each SSA variable is related to one "original" (not SSA) variable, and
+  - no more than one version of each originsl variable is live at the same time
+    in the CFG.
+  It would be better to relax these assumptions.
+- SSAPRE operates on "syntactic"
index 7ef51c8066405c294cdb4aa07e99c880e2547bec..e044eb700aa32c8403cb2f49c0608c78bf9d2bc5 100644 (file)
@@ -1,3 +1,13 @@
+2004-12-03  Massimiliano Mantione  <massi@ximian.com>
+
+       * mini.c: Added removal of critical edges (prerequisite for SSAPRE),
+       and some utility functions (always for SSAPRE), integrated SSAPRE.
+       * mini.h: Likewise.
+       * driver.c: Added ssapre option.
+       * ssa.c: Small fix on OP_ARG handling.
+       * ssapre.c, ssapre.h: Added files containing SSAPRE implementation.
+       * Makefile.am: Likewise.
+
 2004-12-02  Zoltan Varga  <vargaz@freemail.hu>
 
        * tramp-x86.c (mono_arch_create_jit_trampoline): Remove code which is
index 2c13777629344b705e09980ab07eb3dd886410df..779753d1eb553e6e81ff3379e4cc928923c384d5 100644 (file)
@@ -159,6 +159,8 @@ common_sources = \
        ssa.c           \
        abcremoval.c    \
        abcremoval.h    \
+       ssapre.c        \
+       ssapre.h        \
        driver.c        \
        debug-mini.c    \
        linear-scan.c   \
@@ -341,4 +343,3 @@ EXTRA_DIST = $(common_BURGSRC) jit-icalls.c cprop.c TestDriver.cs ldscript $(tes
        $(sparc_sources) inssel-sparc.brg cpu-sparc.md \
        $(s390_sources) inssel-s390.brg cpu-s390.md \
        $(s390x_sources) inssel-s390x.brg cpu-s390x.md
-
index e07095aac46627805d94ee5ba3bba85c2a555833..d6094be730f92f679eb6e1836a9663d817208c62 100644 (file)
@@ -79,7 +79,8 @@ opt_names [] = {
        {"leaf",     "Leaf procedures optimizations"},
        {"aot",      "Usage of Ahead Of Time compiled code"},
        {"precomp",  "Precompile all methods before executing Main"},
-       {"abcrem",   "Array bound checks removal"}
+       {"abcrem",   "Array bound checks removal"},     
+       {"ssapre",   "SSA based Partial Redundancy Elimination"}
 };
 
 #define DEFAULT_OPTIMIZATIONS (        \
@@ -89,7 +90,7 @@ opt_names [] = {
        MONO_OPT_LINEARS |      \
        MONO_OPT_INTRINS |  \
        MONO_OPT_LOOP |  \
-    MONO_OPT_AOT)
+       MONO_OPT_AOT)
 
 #define EXCLUDED_FROM_ALL (MONO_OPT_SHARED | MONO_OPT_PRECOMP)
 
@@ -228,6 +229,7 @@ opt_sets [] = {
        MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE,
        MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE | MONO_OPT_LOOP | MONO_OPT_INLINE | MONO_OPT_INTRINS,
        MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE | MONO_OPT_LOOP | MONO_OPT_INLINE | MONO_OPT_INTRINS | MONO_OPT_ABCREM,
+       MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE | MONO_OPT_LOOP | MONO_OPT_INLINE | MONO_OPT_INTRINS | MONO_OPT_SSAPRE,
        MONO_OPT_BRANCH | MONO_OPT_PEEPHOLE | MONO_OPT_LINEARS | MONO_OPT_COPYPROP | MONO_OPT_CONSPROP | MONO_OPT_DEADCE | MONO_OPT_LOOP | MONO_OPT_INLINE | MONO_OPT_INTRINS | MONO_OPT_ABCREM | MONO_OPT_SHARED
 };
 
@@ -958,5 +960,3 @@ mono_jit_cleanup (MonoDomain *domain)
 {
        mini_cleanup (domain);
 }
-
-
index 0c1a9138d35852004eb5db46779e5559c5868cb6..2318142ecf61b00fd1a6a3f2580d79dfd3f00eef 100644 (file)
@@ -1436,6 +1436,39 @@ mono_compile_create_var (MonoCompile *cfg, MonoType *type, int opcode)
        return inst;
 }
 
+/*
+ * Transform a MonoInst into a load from the variable of index var_index.
+ */
+void
+mono_compile_make_var_load (MonoCompile *cfg, MonoInst *dest, gssize var_index) {
+       memset (dest, 0, sizeof (MonoInst));
+       dest->ssa_op = MONO_SSA_LOAD;
+       dest->inst_i0 = cfg->varinfo [var_index];
+       dest->opcode = mono_type_to_ldind (dest->inst_i0->inst_vtype);
+       type_to_eval_stack_type (dest->inst_i0->inst_vtype, dest);
+       dest->klass = dest->inst_i0->klass;
+}
+
+/*
+ * Create a MonoInst that is a load from the variable of index var_index.
+ */
+MonoInst*
+mono_compile_create_var_load (MonoCompile *cfg, gssize var_index) {
+       MonoInst *dest;
+       NEW_TEMPLOAD (cfg,dest,var_index);
+       return dest;
+}
+
+/*
+ * Create a MonoInst that is a store of the given value into the variable of index var_index.
+ */
+MonoInst*
+mono_compile_create_var_store (MonoCompile *cfg, gssize var_index, MonoInst *value) {
+       MonoInst *dest;
+       NEW_TEMPSTORE (cfg, dest, var_index, value);
+       return dest;
+}
+
 static MonoType*
 type_from_stack_type (MonoInst *ins) {
        switch (ins->type) {
@@ -1452,6 +1485,11 @@ type_from_stack_type (MonoInst *ins) {
        return NULL;
 }
 
+MonoType*
+mono_type_from_stack_type (MonoInst *ins) {
+       return type_from_stack_type (ins);
+}
+
 static MonoClass*
 array_access_to_klass (int opcode)
 {
@@ -1492,7 +1530,7 @@ array_access_to_klass (int opcode)
        return NULL;
 }
 
-static void
+void
 mono_add_ins_to_end (MonoBasicBlock *bb, MonoInst *inst)
 {
        MonoInst *prev;
@@ -6002,6 +6040,7 @@ mono_print_tree (MonoInst *tree) {
                printf ("[%s] <- [%s + 0x%x]", mono_arch_regname (tree->dreg), mono_arch_regname (tree->inst_basereg), tree->inst_offset);
                break;
        case CEE_BR:
+       case OP_CALL_HANDLER:
                printf ("[B%d]", tree->inst_target_bb->block_num);
                break;
        case CEE_SWITCH:
@@ -7152,8 +7191,12 @@ mono_compile_create_vars (MonoCompile *cfg)
        if (sig->hasthis)
                mono_compile_create_var (cfg, &cfg->method->klass->this_arg, OP_ARG);
 
-       for (i = 0; i < sig->param_count; ++i)
+       for (i = 0; i < sig->param_count; ++i) {
                mono_compile_create_var (cfg, sig->params [i], OP_ARG);
+               if (sig->params [i]->byref) {
+                       cfg->disable_ssa = TRUE;
+               }
+       }
 
        cfg->locals_start = cfg->num_varinfo;
 
@@ -7697,6 +7740,136 @@ mono_local_cprop (MonoCompile *cfg)
        }
 }
 
+static void
+remove_critical_edges (MonoCompile *cfg) {
+       MonoBasicBlock *bb;
+       
+       if (cfg->verbose_level > 3) {
+               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+                       int i;
+                       printf ("remove_critical_edges %s, BEFORE BB%d (in:", mono_method_full_name (cfg->method, TRUE), bb->block_num);
+                       for (i = 0; i < bb->in_count; i++) {
+                               printf (" %d", bb->in_bb [i]->block_num);
+                       }
+                       printf (") (out:");
+                       for (i = 0; i < bb->out_count; i++) {
+                               printf (" %d", bb->out_bb [i]->block_num);
+                       }
+                       printf (")");
+                       if (bb->last_ins != NULL) {
+                               printf (" ");
+                               mono_print_tree (bb->last_ins);
+                       }
+                       printf ("\n");
+               }
+       }
+       
+       for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+               if (bb->out_count > 1) {
+                       int out_bb_index;
+                       for (out_bb_index = 0; out_bb_index < bb->out_count; out_bb_index++) {
+                               MonoBasicBlock *out_bb = bb->out_bb [out_bb_index];
+                               if (out_bb->in_count > 1) {
+                                       MonoInst *inst;
+                                       MonoBasicBlock *new_bb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
+                                       new_bb->block_num = cfg->num_bblocks++;
+                                       new_bb->next_bb = bb->next_bb;
+                                       bb->next_bb = new_bb;
+                                       new_bb->in_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
+                                       new_bb->in_bb [0] = bb;
+                                       new_bb->in_count = 1;
+                                       new_bb->out_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
+                                       new_bb->out_bb [0] = out_bb;
+                                       new_bb->out_count = 1;
+                                       replace_out_block (bb, out_bb, new_bb);
+                                       replace_in_block (out_bb, bb, new_bb);
+                                       for (inst = bb->code; inst != NULL; inst = inst->next) {
+                                               if (inst->opcode == OP_CALL_HANDLER) {
+                                                       if (inst->inst_target_bb == out_bb) {
+                                                               inst->inst_target_bb = new_bb;
+                                                       }
+                                               }
+                                       }
+                                       if (bb->last_ins != NULL) {
+                                               switch (bb->last_ins->opcode) {
+                                               case CEE_BR:
+                                                       if (bb->last_ins->inst_target_bb == out_bb) {
+                                                               bb->last_ins->inst_target_bb = new_bb;
+                                                       }
+                                                       break;
+                                               case CEE_SWITCH: {
+                                                       int i;
+                                                       int n = GPOINTER_TO_INT (bb->last_ins->klass);
+                                                       for (i = 0; i < n; i++ ) {
+                                                               if (bb->last_ins->inst_many_bb [i] == out_bb) {
+                                                                       bb->last_ins->inst_many_bb [i] = new_bb;
+                                                                       break;
+                                                               }
+                                                       }
+                                                       break;
+                                               }
+                                               case CEE_BNE_UN:
+                                               case CEE_BEQ:
+                                               case CEE_BLT:
+                                               case CEE_BLT_UN:
+                                               case CEE_BGT:
+                                               case CEE_BGT_UN:
+                                               case CEE_BGE:
+                                               case CEE_BGE_UN:
+                                               case CEE_BLE:
+                                               case CEE_BLE_UN:
+                                                       if (bb->last_ins->inst_true_bb == out_bb) {
+                                                               bb->last_ins->inst_true_bb = new_bb;
+                                                       }
+                                                       if (bb->last_ins->inst_false_bb == out_bb) {
+                                                               bb->last_ins->inst_false_bb = new_bb;
+                                                       }
+                                                       break;
+                                               default:
+                                                       break;
+                                               }
+                                       }
+//                                     new_bb->real_offset = bb->real_offset;
+                                       new_bb->region = bb->region;
+                                       
+                                       if (new_bb->next_bb != out_bb) {
+                                               MonoInst *jump;
+                                               MONO_INST_NEW (cfg, jump, CEE_BR);
+                                               MONO_ADD_INS (new_bb, jump);
+                                               jump->cil_code = bb->cil_code;
+                                               jump->inst_target_bb = out_bb;
+                                       }
+                                       
+                                       if (cfg->verbose_level > 2) {
+                                               printf ("remove_critical_edges %s, removed critical edge from BB%d to BB%d (added BB%d)\n", mono_method_full_name (cfg->method, TRUE), bb->block_num, out_bb->block_num, new_bb->block_num);
+                                       }
+                               }
+                       }
+               }
+       }
+       
+       if (cfg->verbose_level > 3) {
+               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
+                       int i;
+                       printf ("remove_critical_edges %s, AFTER BB%d (in:", mono_method_full_name (cfg->method, TRUE), bb->block_num);
+                       for (i = 0; i < bb->in_count; i++) {
+                               printf (" %d", bb->in_bb [i]->block_num);
+                       }
+                       printf (") (out:");
+                       for (i = 0; i < bb->out_count; i++) {
+                               printf (" %d", bb->out_bb [i]->block_num);
+                       }
+                       printf (")");
+                       if (bb->last_ins != NULL) {
+                               printf (" ");
+                               mono_print_tree (bb->last_ins);
+                       }
+                       printf ("\n");
+               }
+       }
+}
+
+
 MonoCompile*
 mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gboolean run_cctors, int parts)
 {
@@ -7751,12 +7924,16 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        }
        /*g_print ("numblocks = %d\n", cfg->num_bblocks);*/
 
-       /* Depth-first ordering on basic blocks */
-       cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1));
-
        if (cfg->opt & MONO_OPT_BRANCH)
                optimize_branches (cfg);
 
+       if ((cfg->opt & MONO_OPT_SSAPRE) && ! (cfg->opt & (MONO_OPT_CONSPROP|MONO_OPT_COPYPROP))) {
+               remove_critical_edges (cfg);
+       }
+
+       /* Depth-first ordering on basic blocks */
+       cfg->bblocks = mono_mempool_alloc (cfg->mempool, sizeof (MonoBasicBlock*) * (cfg->num_bblocks + 1));
+
        df_visit (cfg->bb_entry, &dfn, cfg->bblocks);
        if (cfg->num_bblocks != dfn + 1) {
                MonoBasicBlock *bb;
@@ -7801,7 +7978,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
 #else 
 
        /* fixme: add all optimizations which requires SSA */
-       if (cfg->opt & (MONO_OPT_DEADCE | MONO_OPT_ABCREM)) {
+       if (cfg->opt & (MONO_OPT_DEADCE | MONO_OPT_ABCREM | MONO_OPT_SSAPRE)) {
                if (!(cfg->comp_done & MONO_COMP_SSA) && !header->num_clauses && !cfg->disable_ssa) {
                        mono_local_cprop (cfg);
                        mono_ssa_compute (cfg);
@@ -7817,7 +7994,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        if (parts == 2)
                return cfg;
 
-       if ((cfg->opt & MONO_OPT_CONSPROP) ||  (cfg->opt & MONO_OPT_COPYPROP)) {
+       if ((cfg->opt & MONO_OPT_CONSPROP) || (cfg->opt & MONO_OPT_COPYPROP)) {
                if (cfg->comp_done & MONO_COMP_SSA) {
                        mono_ssa_cprop (cfg);
                } else {
@@ -7832,7 +8009,10 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
 
                if ((cfg->flags & MONO_CFG_HAS_LDELEMA) && (cfg->opt & MONO_OPT_ABCREM))
                        mono_perform_abc_removal (cfg);
-
+               
+               if ((cfg->opt & MONO_OPT_SSAPRE) && ! (cfg->opt & (MONO_OPT_CONSPROP|MONO_OPT_COPYPROP)))
+                       mono_perform_ssapre (cfg);
+               
                mono_ssa_remove (cfg);
 
                if (cfg->opt & MONO_OPT_BRANCH)
index 5828657143f8a9ed87bceb2de34712f9693aefef..60278a72de18ad254b25b702ae640d1187238373 100644 (file)
@@ -464,7 +464,8 @@ enum {
        MONO_OPT_LEAF     = 1 << 15,
        MONO_OPT_AOT      = 1 << 16,
        MONO_OPT_PRECOMP  = 1 << 17,
-       MONO_OPT_ABCREM   = 1 << 18
+       MONO_OPT_ABCREM   = 1 << 18,
+       MONO_OPT_SSAPRE   = 1 << 19
 };
 
 /* Bit-fields in the MonoBasicBlock.region */
@@ -696,6 +697,10 @@ void      mono_constant_fold_inst           (MonoInst *inst, gpointer data);
 int       mono_is_power_of_two              (guint32 val);
 void      mono_cprop_local                  (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst **acp, int acp_size);
 MonoInst* mono_compile_create_var           (MonoCompile *cfg, MonoType *type, int opcode);
+void      mono_compile_make_var_load        (MonoCompile *cfg, MonoInst *dest, gssize var_index);
+MonoInst* mono_compile_create_var_load      (MonoCompile *cfg, gssize var_index);
+MonoInst* mono_compile_create_var_store     (MonoCompile *cfg, gssize var_index, MonoInst *value);
+MonoType* mono_type_from_stack_type         (MonoInst *ins);
 void      mono_blockset_print               (MonoCompile *cfg, MonoBitSet *set, const char *name, guint idom);
 void      mono_print_tree                   (MonoInst *tree);
 void      mono_print_tree_nl                (MonoInst *tree);
@@ -728,6 +733,7 @@ void      mono_register_opcode_emulation    (int opcode, const char* name, MonoM
 void      mono_arch_register_lowlevel_calls (void);
 void      mono_draw_graph                   (MonoCompile *cfg, MonoGraphOptions draw_options);
 void      mono_add_varcopy_to_end           (MonoCompile *cfg, MonoBasicBlock *bb, int src, int dest);
+void      mono_add_ins_to_end               (MonoBasicBlock *bb, MonoInst *inst);
 
 int               mono_find_method_opcode      (MonoMethod *method);
 MonoJitICallInfo *mono_find_jit_icall_by_name  (const char *name);
@@ -840,5 +846,7 @@ gboolean       mono_trace_eval                  (MonoMethod *method);
 
 extern void
 mono_perform_abc_removal (MonoCompile *cfg);
+extern void
+mono_perform_ssapre (MonoCompile *cfg);
 
-#endif /* __MONO_MINI_H__ */  
+#endif /* __MONO_MINI_H__ */
index dfb09e65abf9a830031fae5a62dddbee0d26e497..f346975b07cd156471f7560f378b5ce3c28cb668 100644 (file)
@@ -147,7 +147,7 @@ extends_live (MonoInst *inst)
        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->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
                return 1;
        } else {
                if (arity) {
diff --git a/mono/mini/ssapre.c b/mono/mini/ssapre.c
new file mode 100644 (file)
index 0000000..1b226c9
--- /dev/null
@@ -0,0 +1,1817 @@
+/*
+ * ssapre.h: SSA Partial Redundancy Elimination
+ *
+ * Author:
+ *   Massimiliano Mantione (massi@ximian.com)
+ *
+ * (C) 2004 Novell, Inc.  http://www.novell.com
+ */
+
+#include <string.h>
+#include <stdio.h>
+
+#include <mono/metadata/debug-helpers.h>
+#include <mono/metadata/opcodes.h>
+
+#include "inssel.h"
+
+#include "ssapre.h"
+
+extern guint8 mono_burg_arity [];
+
+/* Logging conditions */
+#define DUMP_LEVEL (4)
+#define TRACE_LEVEL (3)
+#define STATISTICS_LEVEL (2)
+#define LOG_LEVEL (1)
+#define DUMP_SSAPRE (area->cfg->verbose_level >= DUMP_LEVEL)
+#define TRACE_SSAPRE (area->cfg->verbose_level >= TRACE_LEVEL)
+#define STATISTICS_SSAPRE (area->cfg->verbose_level >= STATISTICS_LEVEL)
+#define LOG_SSAPRE (area->cfg->verbose_level >= LOG_LEVEL)
+
+/* "Bottom" symbol definition (see paper) */
+#define BOTTOM_REDUNDANCY_CLASS (-1)
+
+/* Various printing functions... */
+static void
+print_argument (MonoSsapreExpressionArgument *argument) {
+       switch (argument->type) {
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY:
+                       printf ("ANY");
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_NOT_PRESENT:
+                       printf ("NONE");
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE:
+                       printf ("ORIGINAL_VARIABLE %d", argument->argument.original_variable);
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE:
+                       printf ("SSA_VARIABLE %d", argument->argument.ssa_variable);
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_INTEGER_CONSTANT:
+                       printf ("INTEGER_CONSTANT %d", argument->argument.integer_constant);
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_LONG_COSTANT:
+                       printf ("LONG_COSTANT %lld", *(argument->argument.long_constant));
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_FLOAT_COSTANT:
+                       printf ("FLOAT_COSTANT %f", *(argument->argument.float_constant));
+                       break;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_DOUBLE_COSTANT:
+                       printf ("DOUBLE_COSTANT %f", *(argument->argument.double_constant));
+                       break;
+               default:
+                       printf ("UNKNOWN: %d", argument->type);
+       }
+}
+
+
+static void
+print_expression_description (MonoSsapreExpressionDescription *expression_description) {
+       if (expression_description->opcode != 0) {
+               printf ("%s ([", mono_inst_name (expression_description->opcode) );
+               print_argument (&(expression_description->left_argument));
+               printf ("],[");
+               print_argument (&(expression_description->right_argument));
+               printf ("])");
+       } else {
+               printf ("ANY");
+       }
+}
+
+#define GBOOLEAN_TO_STRING(b) ((b)?"TRUE":"FALSE")
+
+static void
+print_expression_occurrence (MonoSsapreExpressionOccurrence *occurrence, int number) {
+       printf (" ([%d][bb %d [ID %d]][class %d]: ", number, occurrence->bb_info->cfg_dfn, occurrence->bb_info->bb->block_num, occurrence->redundancy_class);
+       print_expression_description (&(occurrence->description));
+       if (occurrence->is_first_in_bb) {
+               printf (" [FIRST in BB]");
+       }
+       if (occurrence->is_last_in_bb) {
+               printf (" [LAST in BB]");
+       }
+       printf (" (save = %s)", GBOOLEAN_TO_STRING (occurrence->save));
+       printf (" (reload = %s)", GBOOLEAN_TO_STRING (occurrence->reload));
+       printf ("\n");
+       occurrence = occurrence->next;
+}
+
+static void
+print_expression_occurrences (MonoSsapreExpressionOccurrence *occurrences) {
+       int i = 0;
+       while (occurrences != NULL) {
+               i++;
+               print_expression_occurrence (occurrences, i);
+               occurrences = occurrences->next;
+       }
+}
+
+static void
+print_worklist (MonoSsapreExpression *expression) {
+       if (expression != NULL) {
+               print_worklist (expression->previous);
+               
+               printf ("{%d}: ", expression->tree_size);
+               print_expression_description (&(expression->description));
+               printf ("\n");
+               print_expression_occurrences (expression->occurrences);
+               
+               print_worklist (expression->next);
+       }
+}
+
+static void
+print_bb_info (MonoSsapreBBInfo *bb_info, gboolean print_occurrences) {
+       int i;
+       MonoSsapreExpressionOccurrence *current_occurrence = bb_info->first_expression_in_bb;
+       
+       printf ("bb %d [ID %d]: IN { ", bb_info->cfg_dfn, bb_info->bb->block_num);
+       for (i = 0; i < bb_info->in_count; i++) {
+               printf ("%d [ID %d] ", bb_info->in_bb [i]->cfg_dfn, bb_info->in_bb [i]->bb->block_num);
+       }
+       printf ("}, OUT {");
+       for (i = 0; i < bb_info->out_count; i++) {
+               printf ("%d [ID %d] ", bb_info->out_bb [i]->cfg_dfn, bb_info->out_bb [i]->bb->block_num);
+       }
+       printf ("}");
+       if (bb_info->next_interesting_bb != NULL) {
+               printf (", NEXT %d [ID %d]", bb_info->next_interesting_bb->cfg_dfn, bb_info->next_interesting_bb->bb->block_num);
+       }
+       printf ("\n");
+       if (bb_info->has_phi) {
+               printf (" PHI, class %d [ ", bb_info->phi_redundancy_class);
+               for (i = 0; i < bb_info->in_count; i++) {
+                       int argument_class = bb_info->phi_arguments_classes [i];
+                       if (argument_class != BOTTOM_REDUNDANCY_CLASS) {
+                               printf ("%d ", argument_class);
+                       } else {
+                               printf ("BOTTOM ");
+                       }
+               }
+               printf ("]\n(phi_defines_a_real_occurrence:%s) (phi_is_down_safe:%s) (phi_can_be_available:%s) (phi_is_later:%s)\n",
+                               GBOOLEAN_TO_STRING (bb_info->phi_defines_a_real_occurrence), GBOOLEAN_TO_STRING (bb_info->phi_is_down_safe),
+                               GBOOLEAN_TO_STRING (bb_info->phi_can_be_available), GBOOLEAN_TO_STRING (bb_info->phi_is_later));
+       }
+       if (print_occurrences) {
+               i = 0;
+               while ((current_occurrence != NULL) && (current_occurrence->bb_info == bb_info)) {
+                       print_expression_occurrence (current_occurrence, i);
+                       current_occurrence = current_occurrence->next;
+                       i++;
+               }
+       }
+       if (bb_info->has_phi_argument) {
+               printf (" PHI ARGUMENT, class ");
+               if (bb_info->phi_argument_class != BOTTOM_REDUNDANCY_CLASS) {
+                       printf ("%d ", bb_info->phi_argument_class);
+               } else {
+                       printf ("BOTTOM ");
+               }
+               if (bb_info->phi_argument_defined_by_real_occurrence != NULL) {
+                       printf ("(Defined by real occurrence %d)", bb_info->phi_argument_defined_by_real_occurrence->redundancy_class);
+               } else if (bb_info->phi_argument_defined_by_phi != NULL) {
+                       printf ("(Defined by phi %d)", bb_info->phi_argument_defined_by_phi->phi_redundancy_class);
+               } else {
+                       printf ("(Undefined)");
+               }
+               printf (" (real occurrence arguments: left ");
+               if (bb_info->phi_argument_left_argument_version != BOTTOM_REDUNDANCY_CLASS) {
+                       printf ("%d ", bb_info->phi_argument_left_argument_version);
+               } else {
+                       printf ("NONE ");
+               }
+               printf (", right ");
+               if (bb_info->phi_argument_right_argument_version != BOTTOM_REDUNDANCY_CLASS) {
+                       printf ("%d ", bb_info->phi_argument_right_argument_version);
+               } else {
+                       printf ("NONE ");
+               }
+               printf (")\n(phi_argument_has_real_use:%s) (phi_argument_needs_insert:%s) (phi_argument_has_been_processed:%s)\n",
+                               GBOOLEAN_TO_STRING (bb_info->phi_argument_has_real_use), GBOOLEAN_TO_STRING (bb_info->phi_argument_needs_insert),
+                               GBOOLEAN_TO_STRING (bb_info->phi_argument_has_been_processed));
+       }
+}
+
+static void
+print_bb_infos (MonoSsapreWorkArea *area) {
+       int i;
+       for (i = 0; i < area->num_bblocks; i++) {
+               MonoSsapreBBInfo *bb_info = &(area->bb_infos [i]);
+               print_bb_info (bb_info, TRUE);
+       }
+}
+
+static void
+print_interesting_bb_infos (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               print_bb_info (current_bb, FALSE);
+       }
+}
+
+static void dump_code (MonoSsapreWorkArea *area) {
+       int i;
+       
+       for (i = 0; i < area->num_bblocks; i++) {
+               MonoSsapreBBInfo *current_bb = &(area->bb_infos [i]);
+               MonoInst *current_inst;
+               
+               print_bb_info (current_bb, TRUE);
+               for (current_inst = current_bb->bb->code; current_inst != NULL; current_inst = current_inst->next) {
+                       mono_print_tree_nl (current_inst);
+               }
+       }
+}
+
+/*
+ * Given a MonoInst, it tries to describe it as an expression argument.
+ * If this is not possible, the result will be of type
+ * MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY.
+ */
+static void
+analyze_argument (MonoInst *argument, MonoSsapreExpressionArgument *result) {
+       switch (argument->opcode) {
+       case CEE_LDIND_I1:
+       case CEE_LDIND_U1:
+       case CEE_LDIND_I2:
+       case CEE_LDIND_U2:
+       case CEE_LDIND_I4:
+       case CEE_LDIND_U4:
+       case CEE_LDIND_I8:
+       case CEE_LDIND_I:
+       case CEE_LDIND_R4:
+       case CEE_LDIND_R8:
+       case CEE_LDIND_REF:
+               analyze_argument (argument->inst_left, result);
+               break;
+       case OP_ICONST:
+               result->type = MONO_SSAPRE_EXPRESSION_ARGUMENT_INTEGER_CONSTANT;
+               result->argument.integer_constant = argument->inst_c0;
+               break;
+       case OP_I8CONST:
+               result->type = MONO_SSAPRE_EXPRESSION_ARGUMENT_LONG_COSTANT;
+               result->argument.long_constant = &(argument->inst_l);
+               break;
+       case OP_R4CONST:
+               result->type = MONO_SSAPRE_EXPRESSION_ARGUMENT_FLOAT_COSTANT;
+               result->argument.float_constant = (float*)argument->inst_p0;
+               break;
+       case OP_R8CONST:
+               result->type = MONO_SSAPRE_EXPRESSION_ARGUMENT_DOUBLE_COSTANT;
+               result->argument.double_constant = (double*)argument->inst_p0;
+               break;
+       case OP_ARG:
+       case OP_LOCAL:
+               result->type = MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE;
+               result->argument.ssa_variable = argument->inst_c0;
+               break;
+       default:
+               result->type = MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY;
+       }
+}
+
+/*
+ * Given a MonoInst, it tries to describe it as an expression.
+ * If this is not possible, the result will have opcode 0.
+ */
+static void
+analyze_expression (MonoInst *expression, MonoSsapreExpressionDescription *result) {
+       switch (expression->opcode) {
+       case CEE_LDIND_I1:
+       case CEE_LDIND_U1:
+       case CEE_LDIND_I2:
+       case CEE_LDIND_U2:
+       case CEE_LDIND_I4:
+       case CEE_LDIND_U4:
+       case CEE_LDIND_I8:
+       case CEE_LDIND_I:
+       case CEE_LDIND_R4:
+       case CEE_LDIND_R8:
+       case CEE_LDIND_REF:
+               analyze_expression (expression->inst_left, result);
+               break;
+       case CEE_SWITCH:
+       case CEE_ISINST:
+       case CEE_CASTCLASS:
+       case OP_OUTARG:
+       case OP_CALL_REG:
+       case OP_FCALL_REG:
+       case OP_LCALL_REG:
+       case OP_VCALL_REG:
+       case OP_VOIDCALL_REG:
+       case CEE_CALL:
+       case CEE_CALLVIRT:
+       case OP_FCALL:
+       case OP_FCALLVIRT:
+       case OP_LCALL:
+       case OP_LCALLVIRT:
+       case OP_VCALL:
+       case OP_VCALLVIRT:
+       case OP_VOIDCALL:
+       case OP_VOIDCALLVIRT:
+       case OP_RENAME:
+       case OP_RETARG:
+//     case OP_OUTARG:
+       case OP_OUTARG_REG:
+       case OP_OUTARG_IMM:
+       case OP_OUTARG_R4:
+       case OP_OUTARG_R8:
+       case OP_OUTARG_VT:
+       case CEE_NOP:
+       case CEE_JMP:
+       case CEE_BREAK:
+       case OP_COMPARE:
+       case OP_COMPARE_IMM:
+       case OP_FCOMPARE:
+       case OP_LCOMPARE:
+       case OP_ICOMPARE:
+       case OP_ICOMPARE_IMM:
+               result->opcode = 0;
+               break;
+       default:
+               if ( (expression->type == STACK_I4) ||
+                               (expression->type == STACK_I8) ||
+                               (expression->type == STACK_R8) ) {
+                       if (mono_burg_arity [expression->opcode] > 0) {
+                               analyze_argument (expression->inst_left, &(result->left_argument));
+                               if (result->left_argument.type != MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY) {
+                                       if (mono_burg_arity [expression->opcode] > 1) {
+                                               analyze_argument (expression->inst_right, &(result->right_argument));
+                                               if (result->right_argument.type != MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY) {
+                                                       result->opcode = expression->opcode;
+                                               } else {
+                                                       result->opcode = 0;
+                                               }
+                                       } else {
+                                               result->right_argument.type = MONO_SSAPRE_EXPRESSION_ARGUMENT_NOT_PRESENT;
+                                               result->opcode = expression->opcode;
+                                       }
+                               } else {
+                                       result->opcode = 0;
+                               }
+                       } else {
+                               result->opcode = 0;
+                       }
+               } else {
+                       result->opcode = 0;
+               }
+       }
+}
+
+/*
+ * Given an expression description, if any of its arguments has type
+ * MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE it transforms it to a
+ * MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE, converting the
+ * variable index to its "original" one.
+ * The resulting description is OK for a "MonoSsapreExpression" (the
+ * initial description came from a "MonoSsapreExpressionOccurrence").
+ */
+static void
+convert_ssa_variables_to_original_names (MonoSsapreExpressionDescription *result, MonoSsapreExpressionDescription *expression, MonoCompile *cfg) {
+       gssize original_variable;
+       
+       result->opcode = expression->opcode;
+       if (expression->left_argument.type != MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE) {
+               result->left_argument = expression->left_argument;
+       } else {
+               result->left_argument.type = MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE;
+               original_variable = cfg->vars [expression->left_argument.argument.ssa_variable]->reg;
+               if (original_variable >= 0) {
+                       result->left_argument.argument.original_variable = original_variable;
+               } else {
+                       result->left_argument.argument.original_variable = expression->left_argument.argument.ssa_variable;
+               }
+       }
+       if (expression->right_argument.type != MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE) {
+               result->right_argument = expression->right_argument;
+       } else {
+               result->right_argument.type = MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE;
+               original_variable = cfg->vars [expression->right_argument.argument.ssa_variable]->reg;
+               if (original_variable >= 0) {
+                       result->right_argument.argument.original_variable = original_variable;
+               } else {
+                       result->right_argument.argument.original_variable = expression->right_argument.argument.ssa_variable;
+               }
+       }
+}
+
+/*
+ * Given a MonoInst, checks if it is a phi definition.
+ */
+static gboolean
+is_phi_definition (MonoInst *definition) {
+       if (definition != NULL) {
+               switch (definition->opcode) {
+               case CEE_STIND_REF:
+               case CEE_STIND_I:
+               case CEE_STIND_I4:
+               case CEE_STIND_I1:
+               case CEE_STIND_I2:
+               case CEE_STIND_I8:
+               case CEE_STIND_R4:
+               case CEE_STIND_R8:
+                       if ((definition->inst_left->opcode == OP_LOCAL) &&
+                                       (definition->inst_right->opcode == OP_PHI)) {
+                               return TRUE;
+                       } else {
+                               return FALSE;
+                       }
+               default:
+                       return FALSE;
+               }
+       } else {
+               return FALSE;
+       }
+}
+
+/*
+ * Given a variable index, checks if it is a phi definition
+ * (it assumes the "area" is visible).
+ */
+#define IS_PHI_DEFINITION(v) is_phi_definition (area->cfg->vars [v]->def)
+
+/*
+ * Given a variable index, checks if it is a phi definition.
+ * If it is so, it returns the array of integers pointed to by the phi MonoInst
+ * (the one containing the length and the phi arguments), otherwise returns NULL.
+ */
+static int*
+get_phi_definition (MonoCompile *cfg, gssize variable) {
+       MonoInst *definition = cfg->vars [variable]->def;
+       if (is_phi_definition (definition) && (definition->inst_left->inst_c0 == variable)) {
+               return definition->inst_right->inst_phi_args;
+       } else {
+               return NULL;
+       }
+}
+
+/*
+ * Given a variable index, returns the BB where the variable is defined.
+ * If the variable appears to have no definition, it returns the entry BB
+ * (the logic is that if it has no definition it is an argument, or a sort
+ * of constant, and in both cases it is defined in the entry BB).
+ */
+static MonoSsapreBBInfo*
+get_bb_info_of_definition (MonoSsapreWorkArea *area, gssize variable) {
+       MonoBasicBlock *def_bb = area->cfg->vars [variable]->def_bb;
+       if (def_bb != NULL) {
+               return area->bb_infos_in_cfg_dfn_order [def_bb->dfn];
+       } else {
+               return area->bb_infos;
+       }
+}
+
+/*
+ * Given:
+ * - a variable index,
+ * - a BB (the "current" BB), and
+ * - a "phi argument index" (or index in the "in_bb" of the current BB),
+ * it checks if the variable is a phi.
+ * If it is so, it returns the variable index at the in_bb corresponding to
+ * the given index, otherwise it return the variable index unchanged.
+ * The logic is to return the variable version at the given predecessor BB.
+ */
+static gssize
+get_variable_version_at_predecessor_bb (MonoSsapreWorkArea *area, gssize variable, MonoSsapreBBInfo *current_bb, int phi_operand) {
+       MonoSsapreBBInfo *definition_bb = get_bb_info_of_definition (area, variable);
+       int *phi_definition = get_phi_definition (area->cfg, variable);
+       if ((definition_bb == current_bb) && (phi_definition != NULL)) {
+               return phi_definition [phi_operand + 1];
+       } else {
+               return variable;
+       }
+}
+
+/*
+ * Adds an expression to an autobalancing binary tree of expressions.
+ * Returns the new tree root (it can be different from "tree" because of the
+ * autobalancing).
+ */
+static MonoSsapreExpression*
+add_expression_to_tree (MonoSsapreExpression *tree, MonoSsapreExpression *expression) {
+       MonoSsapreExpression *subtree;
+       MonoSsapreExpression **subtree_reference;
+       int comparison;
+       gssize max_tree_depth, max_subtree_size;
+       
+       if (tree == NULL) {
+               expression->previous = NULL;
+               expression->next = NULL;
+               expression->tree_size = 1;
+               return expression;
+       }
+       
+       tree->tree_size++;
+       
+       comparison = MONO_COMPARE_SSAPRE_EXPRESSION_DESCRIPTIONS (expression->description, tree->description);
+       if (comparison > 0) {
+               if (tree->next != NULL) {
+                       subtree = tree->next;
+                       subtree_reference = &(tree->next);
+               } else {
+                       tree->next = expression;
+                       expression->father = tree;
+                       expression->previous = NULL;
+                       expression->next = NULL;
+                       expression->tree_size = 1;
+                       return tree;
+               }
+       } else if (comparison < 0) {
+               if (tree->previous != NULL) {
+                       subtree = tree->previous;
+                       subtree_reference = &(tree->previous);
+               } else {
+                       tree->previous = expression;
+                       expression->father = tree;
+                       expression->previous = NULL;
+                       expression->next = NULL;
+                       expression->tree_size = 1;
+                       return tree;
+               }
+       } else {
+               g_assert_not_reached ();
+               return NULL;
+       }
+       
+       MONO_SSAPRE_MAX_TREE_DEPTH (tree->tree_size, max_tree_depth);
+       max_subtree_size = (1 << max_tree_depth) -1;
+       
+       if (subtree->tree_size < max_subtree_size) {
+               *subtree_reference = add_expression_to_tree (subtree, expression);
+               (*subtree_reference)->father = tree;
+               return tree;
+       } else {
+               MonoSsapreExpression *other_subtree;
+               MonoSsapreExpression *border_expression;
+               MonoSsapreExpression *border_expression_subtree;
+               MonoSsapreExpression **border_expression_reference_from_father;
+               int comparison_with_border;
+               
+               border_expression = subtree;
+               if (comparison > 0) {
+                       other_subtree = tree->previous;
+                       MONO_SSAPRE_GOTO_FIRST_EXPRESSION (border_expression);
+                       border_expression_subtree = border_expression->next;
+                       border_expression_reference_from_father = &(border_expression->father->previous);
+               } else if (comparison < 0) {
+                       other_subtree = tree->next;
+                       MONO_SSAPRE_GOTO_LAST_EXPRESSION (border_expression);
+                       border_expression_subtree = border_expression->previous;
+                       border_expression_reference_from_father = &(border_expression->father->next);
+               } else {
+                       g_assert_not_reached ();
+                       return NULL;
+               }
+               comparison_with_border = MONO_COMPARE_SSAPRE_EXPRESSION_DESCRIPTIONS (expression->description, border_expression->description);
+               
+               if ((comparison + comparison_with_border) != 0) {
+                       MonoSsapreExpression *border_expression_iterator = border_expression->father;
+                       while (border_expression_iterator != tree) {
+                               border_expression_iterator->tree_size--;
+                               border_expression_iterator = border_expression_iterator->father;
+                       }
+                       
+                       if (border_expression != subtree) {
+                               *border_expression_reference_from_father = border_expression_subtree;
+                       } else {
+                               subtree = border_expression_subtree;
+                       }
+                       if (border_expression_subtree != NULL) {
+                               border_expression_subtree->father = border_expression->father;
+                       }
+                       
+                       if (comparison > 0) {
+                               border_expression->previous = add_expression_to_tree (other_subtree, tree);
+                               border_expression->next = add_expression_to_tree (subtree, expression);
+                       } else if (comparison < 0) {
+                               border_expression->previous = add_expression_to_tree (subtree, expression);
+                               border_expression->next = add_expression_to_tree (other_subtree, tree);
+                       } else {
+                               g_assert_not_reached ();
+                               return NULL;
+                       }
+                       border_expression->tree_size = 1 + border_expression->previous->tree_size + border_expression->next->tree_size;
+                       return border_expression;
+               } else {
+                       if (comparison > 0) {
+                               expression->previous = add_expression_to_tree (other_subtree, tree);
+                               expression->next = subtree;
+                       } else if (comparison < 0) {
+                               expression->previous = subtree;
+                               expression->next = add_expression_to_tree (other_subtree, tree);
+                       } else {
+                               g_assert_not_reached ();
+                               return NULL;
+                       }
+                       expression->tree_size = 1 + expression->previous->tree_size + expression->next->tree_size;
+                       return expression;
+               }
+       }
+}
+
+/*
+ * Adds an expression to the worklist (putting the given occurrence as first
+ * occurrence of this expression).
+ */
+static void
+add_expression_to_worklist (MonoSsapreWorkArea *area, MonoSsapreExpressionOccurrence *occurrence) {
+       MonoSsapreExpression *expression;
+       
+       expression = (MonoSsapreExpression*) mono_mempool_alloc (area->mempool, sizeof (MonoSsapreExpression));
+       
+       convert_ssa_variables_to_original_names (&(expression->description), &(occurrence->description), area->cfg);
+       expression->type = mono_type_from_stack_type (occurrence->occurrence);
+       expression->occurrences = NULL;
+       expression->last_occurrence = NULL;
+       MONO_SSAPRE_ADD_EXPRESSION_OCCURRANCE (expression, occurrence);
+       
+       area->worklist = add_expression_to_tree (area->worklist, expression);
+       area->worklist->father = NULL;
+}
+
+/*
+ * Adds an expression occurrence to the worklist.
+ * If its expression is not yet in the worklist, it is created.
+ */
+static void
+add_occurrence_to_worklist (MonoSsapreWorkArea *area, MonoSsapreExpressionOccurrence *occurrence) {
+       MonoSsapreExpressionDescription description;
+       MonoSsapreExpression *current_expression;
+       int comparison;
+       
+       convert_ssa_variables_to_original_names (&description, &(occurrence->description), area->cfg);
+       current_expression = area->worklist;
+       
+       do {
+               if (current_expression != NULL) {
+                       comparison = MONO_COMPARE_SSAPRE_EXPRESSION_DESCRIPTIONS (description, current_expression->description);
+
+                       if (comparison > 0) {
+                               current_expression = current_expression->next;
+                       } else if (comparison < 0) {
+                               current_expression = current_expression->previous;
+                       } else {
+                               MONO_SSAPRE_ADD_EXPRESSION_OCCURRANCE (current_expression, occurrence);
+                       }
+               } else {
+                       add_expression_to_worklist (area, occurrence);
+                       comparison = 0;
+               }
+       } while (comparison != 0);
+}
+
+/*
+ * Process a MonoInst, and of it is a valid expression add it to the worklist.
+ */
+static MonoSsapreExpressionOccurrence*
+process_inst (MonoSsapreWorkArea *area, MonoInst* inst, MonoInst* previous_inst, MonoSsapreBBInfo *bb_info, MonoSsapreExpressionOccurrence *current_occurrence) {
+       
+       /* Ugly hack to fix missing variable definitions */
+       /* (the SSA construction code should have done it already!) */
+       switch (inst->opcode) {
+       case CEE_STIND_REF:
+       case CEE_STIND_I:
+       case CEE_STIND_I4:
+       case CEE_STIND_I1:
+       case CEE_STIND_I2:
+       case CEE_STIND_I8:
+       case CEE_STIND_R4:
+       case CEE_STIND_R8:
+               if ((inst->inst_left->opcode == OP_LOCAL) || (inst->inst_left->opcode == OP_ARG)) {
+                       int variable_index = inst->inst_left->inst_c0;
+                       
+                       if (area->cfg->vars [variable_index]->def_bb == NULL) {
+                               if (area->cfg->verbose_level >= 4) {
+                                       printf ("SSAPRE WARNING: variable %d has no definition, fixing.\n", variable_index);
+                               }
+                               area->cfg->vars [variable_index]->def_bb = bb_info->bb;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+       
+       if (mono_burg_arity [inst->opcode] > 0) {
+               current_occurrence = process_inst (area, inst->inst_left, previous_inst, bb_info, current_occurrence);
+               if (mono_burg_arity [inst->opcode] > 1) {
+                       current_occurrence = process_inst (area, inst->inst_right, previous_inst, bb_info, current_occurrence);
+               }
+       }
+       
+       analyze_expression (inst, &(current_occurrence->description));
+       if (current_occurrence->description.opcode != 0) {
+               current_occurrence->occurrence = inst;
+               current_occurrence->previous_tree = previous_inst;
+               current_occurrence->bb_info = bb_info;
+               current_occurrence->is_first_in_bb = FALSE;
+               current_occurrence->is_last_in_bb = FALSE;
+               add_occurrence_to_worklist (area, current_occurrence);
+               current_occurrence = (MonoSsapreExpressionOccurrence*) mono_mempool_alloc (area->mempool, sizeof (MonoSsapreExpressionOccurrence));
+       }
+       
+       return current_occurrence;
+}
+
+/*
+ * Process a BB, and (recursively) all its children in the dominator tree.
+ * The result is that all the expressions end up in the worklist, and the
+ * auxiliary MonoSsapreBBInfo fields (dt_dfn, dt_descendants) are initialized
+ * (with all the info that comes from the MonoBasicBlock).
+ */
+static MonoSsapreExpressionOccurrence*
+process_bb (MonoSsapreWorkArea *area, MonoBasicBlock *bb, int *dt_dfn, int *upper_descendants, MonoSsapreExpressionOccurrence *current_occurrence) {
+       MonoSsapreBBInfo *bb_info;
+       int descendants;
+       GList *dominated_bb;
+       MonoInst* current_inst;
+       MonoInst* previous_inst;
+       
+       bb_info = &(area->bb_infos [*dt_dfn]);
+       bb_info->dt_dfn = *dt_dfn;
+       (*dt_dfn)++;
+       bb_info->cfg_dfn = bb->dfn;
+       area->bb_infos_in_cfg_dfn_order [bb->dfn] = bb_info;
+       bb_info->in_count = bb->in_count;
+       bb_info->out_count = bb->out_count;
+       bb_info->dfrontier = bb->dfrontier;
+       bb_info->bb = bb;
+       bb_info->in_bb = (MonoSsapreBBInfo**) mono_mempool_alloc (area->mempool, sizeof (MonoSsapreBBInfo*) * (bb->in_count));
+       bb_info->out_bb = (MonoSsapreBBInfo**) mono_mempool_alloc (area->mempool, sizeof (MonoSsapreBBInfo*) * (bb->out_count));
+       bb_info->phi_arguments_classes = (gssize*) mono_mempool_alloc (area->mempool, sizeof (gssize) * (bb->in_count));
+       
+       bb_info->phi_insertion_point = NULL;
+       
+       current_inst = bb->code;
+       previous_inst = NULL;
+       while (current_inst != NULL) {
+               if (is_phi_definition (current_inst)) {
+                       bb_info->phi_insertion_point = current_inst;
+               }
+               current_occurrence = process_inst (area, current_inst, previous_inst, bb_info, current_occurrence);
+               previous_inst = current_inst;
+               current_inst = current_inst->next;
+       }
+       
+       descendants = 0;
+       for (dominated_bb = g_list_first (bb->dominated); dominated_bb != NULL; dominated_bb = g_list_next (dominated_bb)) {
+               current_occurrence = process_bb (area, (MonoBasicBlock*) (dominated_bb->data), dt_dfn, &descendants, current_occurrence);
+       }
+       bb_info->dt_descendants = descendants;
+       *upper_descendants += (descendants + 1);
+       
+       return current_occurrence;
+}
+
+/*
+ * Reset the MonoSsapreBBInfo fields that must be recomputed for each expression.
+ */
+static void
+clean_bb_infos (MonoSsapreWorkArea *area) {
+       int i;
+       for (i = 0; i < area->num_bblocks; i++) {
+               MonoSsapreBBInfo *bb_info = &(area->bb_infos [i]);
+               bb_info->has_phi = FALSE;
+               bb_info->phi_defines_a_real_occurrence = FALSE;
+               bb_info->phi_is_down_safe = FALSE;
+               bb_info->phi_can_be_available = FALSE;
+               bb_info->phi_is_later = FALSE;
+               bb_info->phi_redundancy_class = BOTTOM_REDUNDANCY_CLASS;
+               bb_info->phi_variable_index = BOTTOM_REDUNDANCY_CLASS;
+               bb_info->has_phi_argument = FALSE;
+               bb_info->phi_argument_has_real_use = FALSE;
+               bb_info->phi_argument_needs_insert = FALSE;
+               bb_info->phi_argument_has_been_processed = FALSE;
+               bb_info->phi_argument_class = BOTTOM_REDUNDANCY_CLASS;
+               bb_info->phi_argument_variable_index = BOTTOM_REDUNDANCY_CLASS;
+               bb_info->phi_argument_defined_by_real_occurrence = NULL;
+               bb_info->phi_argument_defined_by_phi = NULL;
+               bb_info->phi_argument_left_argument_version = BOTTOM_REDUNDANCY_CLASS;
+               bb_info->phi_argument_right_argument_version = BOTTOM_REDUNDANCY_CLASS;
+               bb_info->first_expression_in_bb = NULL;
+               bb_info->next_interesting_bb = NULL;
+               bb_info->next_in_renaming_stack = NULL;
+               bb_info->top_of_local_renaming_stack = NULL;
+       }
+}
+
+/*
+ * Reset the MonoSsapreWorkArea fields that must be recomputed for each expression.
+ */
+static void
+clean_area_infos (MonoSsapreWorkArea *area) {
+       mono_bitset_clear_all (area->left_argument_bb_bitset);
+       mono_bitset_clear_all (area->right_argument_bb_bitset);
+       area->num_vars = area->cfg->num_varinfo;
+       area->top_of_renaming_stack = NULL;
+       area->bb_on_top_of_renaming_stack = NULL;
+       area->first_interesting_bb = NULL;
+       area->saved_occurrences = 0;
+       area->reloaded_occurrences = 0;
+       area->inserted_occurrences = 0;
+       area->unaltered_occurrences = 0;
+       area->added_phis = 0;
+       clean_bb_infos (area);
+}
+
+/*
+ * Utility function to combine the dominance frontiers of a set of BBs.
+ */
+static void
+compute_combined_dfrontier (MonoSsapreWorkArea *area, MonoBitSet* result, MonoBitSet *bblocks) 
+{
+       int i;
+       mono_bitset_clear_all (result);
+       mono_bitset_foreach_bit (bblocks, i, area->num_bblocks) {
+               mono_bitset_union (result, area->bb_infos_in_cfg_dfn_order [i]->dfrontier);
+       }
+}
+
+/*
+ * Utility function to compute the combined dominance frontier of a set of BBs.
+ */
+static void
+compute_combined_iterated_dfrontier (MonoSsapreWorkArea *area, MonoBitSet *result, MonoBitSet *bblocks, MonoBitSet *buffer) 
+{
+       gboolean change = TRUE;
+
+       compute_combined_dfrontier (area, result, bblocks);
+       do {
+               change = FALSE;
+               compute_combined_dfrontier (area, buffer, result);
+               mono_bitset_union (buffer, result);
+
+               if (!mono_bitset_equal (buffer, result)) {
+                       mono_bitset_copyto (buffer, result);
+                       change = TRUE;
+               }
+       } while (change);
+}
+
+/*
+ * See paper, figure 18, function Set_var_phis
+ */
+static void process_phi_variable_in_phi_insertion (MonoSsapreWorkArea *area, gssize variable, MonoBitSet *phi_bbs) {
+       int* phi_definition = get_phi_definition (area->cfg, variable);
+       
+       if (phi_definition != NULL) {
+               int definition_bb = area->cfg->vars [variable]->def_bb->dfn;
+               if (! mono_bitset_test (phi_bbs, definition_bb)) {
+                       int i;
+                       mono_bitset_set (phi_bbs, definition_bb);
+                       for (i = 0; i < *phi_definition; i++) {
+                               process_phi_variable_in_phi_insertion (area, phi_definition [i+1], phi_bbs);
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 18, function phi_insertion
+ */
+static void
+phi_insertion (MonoSsapreWorkArea *area, MonoSsapreExpression *expression) {
+       MonoSsapreExpressionOccurrence *current_expression = NULL;
+       MonoSsapreExpressionOccurrence *previous_expression = NULL;
+       MonoSsapreBBInfo *current_bb = NULL;
+       MonoSsapreBBInfo *previous_interesting_bb = NULL;
+       int i, j, current_bb_dt_dfn;
+       
+       mono_bitset_clear_all (area->expression_occurrences_buffer);
+       for (current_expression = expression->occurrences; current_expression != NULL; current_expression = current_expression->next) {
+               mono_bitset_set (area->expression_occurrences_buffer, current_expression->bb_info->cfg_dfn);
+               if (current_expression->description.left_argument.type == MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE) {
+                       process_phi_variable_in_phi_insertion (area, current_expression->description.left_argument.argument.ssa_variable, area->left_argument_bb_bitset);
+               }
+               if (current_expression->description.right_argument.type == MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE) {
+                       process_phi_variable_in_phi_insertion (area, current_expression->description.right_argument.argument.ssa_variable, area->right_argument_bb_bitset);
+               }
+               
+               if (previous_expression != NULL) {
+                       if (previous_expression->bb_info != current_expression->bb_info) {
+                               previous_expression->is_last_in_bb = TRUE;
+                               current_expression->is_first_in_bb = TRUE;
+                               current_expression->bb_info->first_expression_in_bb = current_expression;
+                       }
+               } else {
+                       current_expression->is_first_in_bb = TRUE;
+                       current_expression->bb_info->first_expression_in_bb = current_expression;
+               }
+               
+               previous_expression = current_expression;
+       }
+       previous_expression->is_last_in_bb = TRUE;
+       
+       compute_combined_iterated_dfrontier (area, area->iterated_dfrontier_buffer, area->expression_occurrences_buffer, area->bb_iteration_buffer);
+       
+       mono_bitset_union (area->iterated_dfrontier_buffer, area->left_argument_bb_bitset);
+       mono_bitset_union (area->iterated_dfrontier_buffer, area->right_argument_bb_bitset);
+       
+       mono_bitset_foreach_bit (area->iterated_dfrontier_buffer, i, area->num_bblocks) {
+               area->bb_infos_in_cfg_dfn_order [i]->has_phi = TRUE;
+               area->bb_infos_in_cfg_dfn_order [i]->phi_can_be_available = TRUE;
+               for (j = 0; j < area->bb_infos_in_cfg_dfn_order [i]->in_count; j++) {
+                       area->bb_infos_in_cfg_dfn_order [i]->in_bb [j]->has_phi_argument = TRUE;
+               }
+       }
+       
+       for (current_bb = area->bb_infos, current_bb_dt_dfn = 0; current_bb_dt_dfn < area->num_bblocks; current_bb++, current_bb_dt_dfn++) {
+               if ((current_bb->first_expression_in_bb != NULL) || (current_bb->has_phi) || (current_bb->has_phi_argument)) {
+                       if (previous_interesting_bb != NULL) {
+                               previous_interesting_bb->next_interesting_bb = current_bb;
+                       } else {
+                               area->first_interesting_bb = current_bb;
+                       }
+                       previous_interesting_bb = current_bb;
+               }
+       }
+}
+
+/*
+ * Macro that pushes a real occurrence on the stack
+ */
+#define PUSH_REAL_OCCURRENCE(o) do{\
+                       if (area->bb_on_top_of_renaming_stack != (o)->bb_info) { \
+                               (o)->bb_info->next_in_renaming_stack = area->bb_on_top_of_renaming_stack; \
+                               area->bb_on_top_of_renaming_stack =(o)->bb_info; \
+                               (o)->next_in_renaming_stack = NULL; \
+                       } else { \
+                               (o)->next_in_renaming_stack = area->top_of_renaming_stack; \
+                       } \
+                       (o)->bb_info->top_of_local_renaming_stack = (o); \
+                       area->top_of_renaming_stack = (o); \
+                       area->bb_on_top_of_renaming_stack->phi_argument_has_real_use = FALSE; \
+               } while(0)
+
+/*
+ * Macro that pushes a PHI occurrence on the stack
+ */
+#define PUSH_PHI_OCCURRENCE(b) do{\
+                       if (area->bb_on_top_of_renaming_stack != (b)) { \
+                               (b)->next_in_renaming_stack = area->bb_on_top_of_renaming_stack; \
+                               area->bb_on_top_of_renaming_stack = (b); \
+                       } \
+                       (b)->top_of_local_renaming_stack = NULL; \
+                       area->top_of_renaming_stack = NULL; \
+                       area->bb_on_top_of_renaming_stack->phi_argument_has_real_use = FALSE; \
+               } while(0)
+
+/*
+ * See paper, section 5.1, definition of "Dominate"
+ */
+static gboolean
+dominates (MonoSsapreBBInfo *dominator, MonoSsapreBBInfo *dominated) {
+       if ((dominator->dt_dfn <= dominated->dt_dfn) && (dominated->dt_dfn <= (dominator->dt_dfn + dominator->dt_descendants))) {
+               return TRUE;
+       } else {
+               return FALSE;
+       }
+}
+
+/*
+ * See paper, section 5.4.
+ * The two passes are coded sequentially (no separate "rename1" and "rename2" functions).
+ */
+static void
+renaming_pass (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       MonoSsapreBBInfo *previous_bb = NULL;
+       MonoSsapreExpressionOccurrence *current_expression = NULL;
+       gssize current_class = 0;
+       
+       /* This loop is "rename1" */
+       for (current_bb = area->first_interesting_bb, previous_bb = NULL; current_bb != NULL; previous_bb = current_bb, current_bb = current_bb->next_interesting_bb) {
+               if ((previous_bb != NULL) && ! dominates (previous_bb, current_bb)) {
+                       if ((area->bb_on_top_of_renaming_stack != NULL) && (area->top_of_renaming_stack == NULL) && (previous_bb->phi_argument_has_real_use == FALSE)) {
+                               if (TRACE_SSAPRE) {
+                                       printf ("Clearing down safe in PHI %d because of backtracking (previous block is [bb %d [ID %d]])\n",
+                                                       area->bb_on_top_of_renaming_stack->phi_redundancy_class, previous_bb->cfg_dfn, previous_bb->bb->block_num);
+                               }
+                               area->bb_on_top_of_renaming_stack->phi_is_down_safe = FALSE;
+                       }
+                       while ((area->bb_on_top_of_renaming_stack != NULL) && ! dominates (area->bb_on_top_of_renaming_stack, current_bb)) {
+                               MonoSsapreBBInfo *top_bb = area->bb_on_top_of_renaming_stack;
+                               if (top_bb->next_in_renaming_stack != NULL) {
+                                       area->top_of_renaming_stack = top_bb->next_in_renaming_stack->top_of_local_renaming_stack;
+                               } else {
+                                       area->top_of_renaming_stack = NULL;
+                               }
+                               area->bb_on_top_of_renaming_stack = top_bb->next_in_renaming_stack;
+                       }
+               }
+               if (current_bb->idominator != NULL) {
+                       current_bb->phi_argument_has_real_use = current_bb->idominator->phi_argument_has_real_use;
+               } else {
+                       current_bb->phi_argument_has_real_use = FALSE;
+               }
+               
+               if (current_bb->has_phi) {
+                       current_bb->phi_is_down_safe = TRUE;
+                       current_bb->phi_redundancy_class = current_class;
+                       current_class++;
+                       PUSH_PHI_OCCURRENCE (current_bb);
+               }
+               
+               for (current_expression = current_bb->first_expression_in_bb; (current_expression != NULL) && (current_expression->bb_info == current_bb); current_expression = current_expression->next) {
+                       if (area->top_of_renaming_stack != NULL) {
+                               MonoSsapreExpressionOccurrence *top = area->top_of_renaming_stack;
+                               
+                               if (((current_expression->description.left_argument.type != MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE) ||
+                                               (current_expression->description.left_argument.argument.ssa_variable == top->description.left_argument.argument.ssa_variable)) &&
+                                               ((current_expression->description.right_argument.type != MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE) ||
+                                               (current_expression->description.right_argument.argument.ssa_variable == top->description.right_argument.argument.ssa_variable))) {
+                                       current_expression->redundancy_class = top->redundancy_class;
+                                       current_expression->defined_by_real_occurrence = top;
+                               } else {
+                                       current_expression->redundancy_class = current_class;
+                                       current_class++;
+                                       current_expression->defined_by_real_occurrence = NULL;
+                                       PUSH_REAL_OCCURRENCE (current_expression);
+                               }
+                               current_expression->defined_by_phi = NULL;
+                       } else if (area->bb_on_top_of_renaming_stack != NULL) {
+                               MonoSsapreBBInfo *phi_bb = area->bb_on_top_of_renaming_stack;
+                               gssize left_argument_version = ((current_expression->description.left_argument.type == MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE)?
+                                               (current_expression->description.left_argument.argument.ssa_variable):BOTTOM_REDUNDANCY_CLASS);
+                               gssize right_argument_version = ((current_expression->description.right_argument.type == MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE)?
+                                               (current_expression->description.right_argument.argument.ssa_variable):BOTTOM_REDUNDANCY_CLASS);
+                               
+                               /* See remark in section 5.4 about the dominance relation between PHI */
+                               /* occurrences and phi definitions */
+                               MonoSsapreBBInfo *left_argument_definition_bb = ((left_argument_version != BOTTOM_REDUNDANCY_CLASS)?
+                                               (get_bb_info_of_definition (area, left_argument_version)):NULL);
+                               MonoSsapreBBInfo *right_argument_definition_bb = ((right_argument_version != BOTTOM_REDUNDANCY_CLASS)?
+                                               (get_bb_info_of_definition (area, right_argument_version)):NULL);
+                               
+                               gboolean left_same_class_condition = ((left_argument_definition_bb == NULL) || ((left_argument_definition_bb != phi_bb) ? dominates (left_argument_definition_bb, phi_bb) :
+                                               (IS_PHI_DEFINITION (left_argument_version) ? TRUE : FALSE)));
+                               gboolean right_same_class_condition = ((right_argument_definition_bb == NULL) || ((right_argument_definition_bb != phi_bb) ? dominates (right_argument_definition_bb, phi_bb) :
+                                               (IS_PHI_DEFINITION (right_argument_version) ? TRUE : FALSE)));
+                                       
+                               if (left_same_class_condition && right_same_class_condition) {
+                                       int phi_argument;
+                                       
+                                       phi_bb->phi_defines_a_real_occurrence = TRUE;
+                                       current_bb->phi_argument_has_real_use = TRUE;
+                                       current_expression->redundancy_class = phi_bb->phi_redundancy_class;
+                                       current_expression->defined_by_phi = phi_bb;
+                                       
+                                       /*
+                                        * Major difference from the paper here...
+                                        * Instead of maintaining "set_for_rename2" (see figure 20), we just
+                                        * compute "phi_argument_left_argument_version" (and right) here, and
+                                        * use that in rename2, saving us the hassle of maintaining a set
+                                        * data structure (and the related allocations).
+                                        */
+                                       for (phi_argument = 0; phi_argument < phi_bb->in_count; phi_argument++) {
+                                               MonoSsapreBBInfo *previous_bb = phi_bb->in_bb [phi_argument];
+                                               if (left_argument_version != BOTTOM_REDUNDANCY_CLASS) {
+                                                       previous_bb->phi_argument_left_argument_version = get_variable_version_at_predecessor_bb (area,
+                                                                       left_argument_version, phi_bb, phi_argument);
+                                               }
+                                               if (right_argument_version != BOTTOM_REDUNDANCY_CLASS) {
+                                                       previous_bb->phi_argument_right_argument_version = get_variable_version_at_predecessor_bb (area,
+                                                                       right_argument_version, phi_bb, phi_argument);
+                                               }
+                                       }
+                               } else {
+                                       current_expression->redundancy_class = current_class;
+                                       current_class++;
+                                       current_expression->defined_by_phi = NULL;
+                                       PUSH_REAL_OCCURRENCE (current_expression);
+                                       phi_bb->phi_is_down_safe = FALSE;
+                                       if (TRACE_SSAPRE) {
+                                               printf ("Clearing down safe in PHI %d because of real occurrence %d\n",
+                                                               phi_bb->phi_redundancy_class, current_expression->redundancy_class);
+                                       }
+                               }
+                               current_expression->defined_by_real_occurrence = NULL;
+                       } else {
+                               current_expression->redundancy_class = current_class;
+                               current_class++;
+                               current_expression->defined_by_real_occurrence = NULL;
+                               current_expression->defined_by_phi = NULL;
+                               PUSH_REAL_OCCURRENCE (current_expression);
+                       }
+               }
+               
+               if (current_bb->has_phi_argument) {
+                       if (area->top_of_renaming_stack != NULL) {
+                               current_bb->phi_argument_defined_by_real_occurrence = area->top_of_renaming_stack;
+                               current_bb->phi_argument_class = area->top_of_renaming_stack->redundancy_class;
+                       } else if (area->bb_on_top_of_renaming_stack != NULL) {
+                               current_bb->phi_argument_defined_by_phi = area->bb_on_top_of_renaming_stack;
+                               current_bb->phi_argument_class = area->bb_on_top_of_renaming_stack->phi_redundancy_class;
+                       } else {
+                               current_bb->phi_argument_class = BOTTOM_REDUNDANCY_CLASS;
+                       }
+               }
+       }
+       if ((area->bb_on_top_of_renaming_stack != NULL) && (area->top_of_renaming_stack == NULL) && (previous_bb->phi_argument_has_real_use == FALSE)) {
+               if (TRACE_SSAPRE) {
+                       printf ("Clearing down safe in PHI %d because of backtracking (previous block is [bb %d [ID %d]])\n",
+                                       area->bb_on_top_of_renaming_stack->phi_redundancy_class, previous_bb->cfg_dfn, previous_bb->bb->block_num);
+               }
+               area->bb_on_top_of_renaming_stack->phi_is_down_safe = FALSE;
+       }
+       area->number_of_classes = current_class;
+       
+       /* This loop is "rename2" */
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi_argument) {
+                       if ((current_bb->phi_argument_left_argument_version != BOTTOM_REDUNDANCY_CLASS) || (current_bb->phi_argument_right_argument_version != BOTTOM_REDUNDANCY_CLASS)) {
+                               if (current_bb->phi_argument_defined_by_real_occurrence != NULL) {
+                                       if (((current_bb->phi_argument_left_argument_version != BOTTOM_REDUNDANCY_CLASS) &&
+                                                       (current_bb->phi_argument_left_argument_version != current_bb->phi_argument_defined_by_real_occurrence->description.left_argument.argument.ssa_variable)) ||
+                                                       ((current_bb->phi_argument_right_argument_version != BOTTOM_REDUNDANCY_CLASS) &&
+                                                       (current_bb->phi_argument_right_argument_version != current_bb->phi_argument_defined_by_real_occurrence->description.right_argument.argument.ssa_variable))) {
+                                               current_bb->phi_argument_class = BOTTOM_REDUNDANCY_CLASS;
+                                       }
+                               } else if (current_bb->phi_argument_defined_by_phi != NULL) {
+                                       MonoSsapreBBInfo *phi_bb = current_bb->phi_argument_defined_by_phi;
+                                       MonoSsapreBBInfo *left_argument_definition_bb = (current_bb->phi_argument_left_argument_version != BOTTOM_REDUNDANCY_CLASS) ?
+                                                       get_bb_info_of_definition (area, current_bb->phi_argument_left_argument_version) : NULL;
+                                       MonoSsapreBBInfo *right_argument_definition_bb = (current_bb->phi_argument_right_argument_version != BOTTOM_REDUNDANCY_CLASS) ?
+                                                       get_bb_info_of_definition (area, current_bb->phi_argument_right_argument_version) : NULL;
+                                       /* See remark in section 5.4 about the dominance relation between PHI */
+                                       /* occurrences and phi definitions */
+                                       gboolean left_bottom_condition = ((left_argument_definition_bb != NULL) && ! ((left_argument_definition_bb != phi_bb) ? dominates (left_argument_definition_bb, phi_bb) :
+                                                       (IS_PHI_DEFINITION (current_bb->phi_argument_left_argument_version) ? TRUE : FALSE)));
+                                       gboolean right_bottom_condition = ((right_argument_definition_bb != NULL) && ! ((right_argument_definition_bb != phi_bb) ? dominates (right_argument_definition_bb, phi_bb) :
+                                                       (IS_PHI_DEFINITION (current_bb->phi_argument_right_argument_version) ? TRUE : FALSE)));
+                                       
+                                       if (left_bottom_condition || right_bottom_condition) {
+                                               current_bb->phi_argument_class = BOTTOM_REDUNDANCY_CLASS;
+                                       }
+                               } else {
+                                       current_bb->phi_argument_class = BOTTOM_REDUNDANCY_CLASS;
+                               }
+                       } else {
+                               current_bb->phi_argument_class = BOTTOM_REDUNDANCY_CLASS;
+                       }
+                       
+                       if (current_bb->phi_argument_class == BOTTOM_REDUNDANCY_CLASS) {
+                               if ((current_bb->phi_argument_defined_by_phi != NULL) && (! current_bb->phi_argument_has_real_use)) {
+                                       if (TRACE_SSAPRE) {
+                                               printf ("Clearing down safe in PHI %d because PHI argument in block [bb %d [ID %d]] is BOTTOM\n",
+                                                               current_bb->phi_argument_defined_by_phi->phi_redundancy_class, current_bb->cfg_dfn, current_bb->bb->block_num);
+                                       }
+                                       current_bb->phi_argument_defined_by_phi->phi_is_down_safe = FALSE;
+                               }
+                               current_bb->phi_argument_has_real_use = FALSE;
+                       }
+               }
+       }
+       
+       /* Final quick pass to copy the result of "rename2" into PHI nodes */
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       int i;
+                       for (i = 0; i < current_bb->in_count; i++) {
+                               current_bb->phi_arguments_classes [i] = current_bb->in_bb [i]->phi_argument_class;
+                       }
+               }
+       }
+}
+
+#undef PUSH_REAL_OCCURRENCE
+#undef PUSH_PHI_OCCURRENCE
+
+/*
+ * See paper, figure 8
+ */
+static void
+reset_down_safe (MonoSsapreBBInfo *phi_argument) {
+       if ((phi_argument->phi_argument_class != BOTTOM_REDUNDANCY_CLASS) && (! phi_argument->phi_argument_has_real_use) && (phi_argument->phi_argument_defined_by_phi != NULL) && (phi_argument->phi_argument_defined_by_phi->phi_is_down_safe)) {
+               int i;
+               MonoSsapreBBInfo *phi = phi_argument->phi_argument_defined_by_phi;
+//             if (TRACE_SSAPRE) {
+//                     printf ("Clearing down safe in PHI %d inside reset_down_safe\n", phi->phi_redundancy_class);
+//             }
+               phi->phi_is_down_safe = FALSE;
+               for (i = 0; i < phi->in_count; i++) {
+                       reset_down_safe (phi->in_bb [i]);
+               }
+       }
+}
+
+/*
+ * See paper, figure 8
+ */
+static void
+down_safety (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       if (! current_bb->phi_is_down_safe) {
+                               int i;
+                               for (i = 0; i < current_bb->in_count; i++) {
+                                       reset_down_safe (current_bb->in_bb [i]);
+                               }
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 10
+ */
+static void
+reset_can_be_available (MonoSsapreWorkArea *area, MonoSsapreBBInfo *phi) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       
+       phi->phi_can_be_available = FALSE;
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       gboolean phi_is_interesting = FALSE;
+                       int i;
+                       
+                       for (i = 0; i < current_bb->in_count; i++) {
+                               MonoSsapreBBInfo *phi_argument = current_bb->in_bb [i];
+                               if ((phi_argument->phi_argument_class == current_bb->phi_redundancy_class) && (! phi_argument->phi_argument_has_real_use)) {
+                                       phi_is_interesting = TRUE;
+                                       break;
+                               }
+                       }
+                       
+                       if (phi_is_interesting && (! current_bb->phi_is_down_safe) && (current_bb->phi_can_be_available)) {
+                               reset_can_be_available (area, current_bb);
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 10
+ */
+static void
+compute_can_be_available (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       if ((! current_bb->phi_is_down_safe) && (current_bb->phi_can_be_available)) {
+                               gboolean phi_is_interesting = FALSE;
+                               int i;
+                               
+                               for (i = 0; i < current_bb->in_count; i++) {
+                                       if (current_bb->phi_arguments_classes [i] == BOTTOM_REDUNDANCY_CLASS) {
+                                               phi_is_interesting = TRUE;
+                                               break;
+                                       }
+                               }
+                               
+                               if (phi_is_interesting) {
+                                       reset_can_be_available (area, current_bb);
+                               }
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 10
+ */
+static void
+reset_later (MonoSsapreWorkArea *area, MonoSsapreBBInfo *phi) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       
+       if (phi->phi_is_later) {
+               phi->phi_is_later = FALSE;
+               for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+                       if (current_bb->has_phi) {
+                               gboolean phi_is_interesting = FALSE;
+                               int i;
+                               
+                               for (i = 0; i < current_bb->in_count; i++) {
+                                       MonoSsapreBBInfo *phi_argument = current_bb->in_bb [i];
+                                       if (phi_argument->phi_argument_class == current_bb->phi_redundancy_class) {
+                                               phi_is_interesting = TRUE;
+                                               break;
+                                       }
+                               }
+                               
+                               if (phi_is_interesting) {
+                                       reset_later (area, current_bb);
+                               }
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 10
+ */
+static void
+compute_later (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       current_bb->phi_is_later = current_bb->phi_can_be_available;
+               }
+       }
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       if (current_bb->phi_is_later) {
+                               gboolean phi_is_interesting = FALSE;
+                               int i;
+                               
+                               for (i = 0; i < current_bb->in_count; i++) {
+                                       MonoSsapreBBInfo *phi_argument = current_bb->in_bb [i];
+                                       if ((phi_argument->phi_argument_class != BOTTOM_REDUNDANCY_CLASS) && (phi_argument->phi_argument_has_real_use)) {
+                                               phi_is_interesting = TRUE;
+                                               break;
+                                       }
+                               }
+                               
+                               if (phi_is_interesting) {
+                                       reset_later (area, current_bb);
+                               }
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 12, function Finalize_1
+ */
+static void finalize_availability_and_reload (MonoSsapreWorkArea *area, MonoSsapreAvailabilityTableElement *availability_table) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       MonoSsapreExpressionOccurrence *current_expression = NULL;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->has_phi) {
+                       if (current_bb->phi_can_be_available && ! current_bb->phi_is_later) {
+                               availability_table [current_bb->phi_redundancy_class].class_defined_by_phi = current_bb;
+                       }
+               }
+               
+               for (current_expression = current_bb->first_expression_in_bb; (current_expression != NULL) && (current_expression->bb_info == current_bb); current_expression = current_expression->next) {
+                       MonoSsapreBBInfo *defining_bb = availability_table [current_expression->redundancy_class].class_defined_by_phi;
+                       if (defining_bb == NULL && availability_table [current_expression->redundancy_class].class_defined_by_real_occurrence != NULL) {
+                               defining_bb = availability_table [current_expression->redundancy_class].class_defined_by_real_occurrence->bb_info;
+                       }
+                       if (defining_bb != NULL) {
+                               if (! dominates (defining_bb, current_bb)) {
+                                       defining_bb = NULL;
+                               }
+                       }
+                       
+                       if (defining_bb == NULL) {
+                               current_expression->reload = FALSE;
+                               availability_table [current_expression->redundancy_class].class_defined_by_real_occurrence = current_expression;
+                       } else {
+                               current_expression->reload = TRUE;
+                               current_expression->defined_by_phi = availability_table [current_expression->redundancy_class].class_defined_by_phi;
+                               current_expression->defined_by_real_occurrence = availability_table [current_expression->redundancy_class].class_defined_by_real_occurrence;
+                       }
+                       
+                       current_expression->save = FALSE;
+               }
+               
+               if (current_bb->has_phi_argument) {
+                       MonoSsapreBBInfo *phi_bb = current_bb->out_bb [0];
+                       if (((phi_bb->phi_can_be_available) && (! phi_bb->phi_is_later)) &&
+                                       ((current_bb->phi_argument_class == BOTTOM_REDUNDANCY_CLASS) ||
+                                       ((current_bb->phi_argument_has_real_use == FALSE) && (current_bb->phi_argument_defined_by_phi != NULL) &&
+                                               (! ((current_bb->phi_argument_defined_by_phi->phi_can_be_available) && (! current_bb->phi_argument_defined_by_phi->phi_is_later)))
+                                       ))) {
+                               current_bb->phi_argument_needs_insert = TRUE;
+                               current_bb->phi_argument_defined_by_real_occurrence = NULL;
+                               current_bb->phi_argument_defined_by_phi = NULL;
+                       } else {
+                               current_bb->phi_argument_needs_insert = FALSE;
+                               if (current_bb->phi_argument_class != BOTTOM_REDUNDANCY_CLASS) {
+                                       current_bb->phi_argument_defined_by_real_occurrence = availability_table [current_bb->phi_argument_class].class_defined_by_real_occurrence;
+                                       current_bb->phi_argument_defined_by_phi = availability_table [current_bb->phi_argument_class].class_defined_by_phi;
+                               }
+                       }
+                       
+                       current_bb->phi_argument_has_been_processed = FALSE;
+               }
+       }
+}
+
+/*
+ * See paper, figure 13, function Set_save
+ */
+static void set_save (MonoSsapreBBInfo *phi_occurrance, MonoSsapreExpressionOccurrence *real_occurrance) {
+       if (real_occurrance != NULL) {
+               real_occurrance->save = TRUE;
+       } else if (phi_occurrance != NULL) {
+               int i;
+               for (i = 0; i < phi_occurrance->in_count; i++) {
+                       MonoSsapreBBInfo *phi_argument = phi_occurrance->in_bb [i];
+                       if (! phi_argument->phi_argument_has_been_processed) {
+                               phi_argument->phi_argument_has_been_processed = TRUE;
+                               set_save (phi_argument->phi_argument_defined_by_phi, phi_argument->phi_argument_defined_by_real_occurrence);
+                       }
+               }
+       }
+}
+
+/*
+ * See paper, figure 13, function Finalize_2 (note that we still don't do
+ * extraneous PHI elimination, see function Set_replacement)
+ */
+static void finalize_save (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       MonoSsapreExpressionOccurrence *current_expression = NULL;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               for (current_expression = current_bb->first_expression_in_bb; (current_expression != NULL) && (current_expression->bb_info == current_bb); current_expression = current_expression->next) {
+                       if (current_expression->reload) {
+                               set_save (current_expression->defined_by_phi, current_expression->defined_by_real_occurrence);
+                       }
+               }
+               
+               if ((current_bb->has_phi_argument) &&
+                               (! current_bb->phi_argument_needs_insert) &&
+                               (current_bb->phi_argument_class != BOTTOM_REDUNDANCY_CLASS) &&
+                               (current_bb->phi_argument_defined_by_real_occurrence != NULL)) {
+                       current_bb->phi_argument_defined_by_real_occurrence->save = TRUE;
+               }
+       }
+}
+
+/*
+ * Perform all "finalize" steps
+ */
+static void finalize (MonoSsapreWorkArea *area) {
+       MonoSsapreAvailabilityTableElement *availability_table = alloca (sizeof (MonoSsapreAvailabilityTableElement) * (area->number_of_classes));
+       int i;
+       
+       for (i = 0; i < area->number_of_classes; i++) {
+               availability_table[i].class_defined_by_phi = NULL;
+               availability_table[i].class_defined_by_real_occurrence = NULL;
+       }
+       
+       finalize_availability_and_reload (area, availability_table);
+       finalize_save (area);
+}
+
+/*
+ * Macros that help in creating constants
+ */
+#define NEW_INST(dest,op) do { \
+               (dest) = mono_mempool_alloc0 (area->cfg->mempool, sizeof (MonoInst));   \
+               (dest)->opcode = (op);  \
+       } while (0)
+
+#define NEW_I4(dest,val) do {  \
+               NEW_INST ((dest), OP_ICONST); \
+               (dest)->inst_c0 = (val);        \
+               (dest)->type = STACK_I4;        \
+       } while (0)
+
+#define NEW_I8(dest,val) do {  \
+               NEW_INST ((dest), OP_I8CONST); \
+               (dest)->inst_l = (val); \
+               (dest)->type = STACK_I8;        \
+       } while (0)
+
+#define NEW_R4(dest,f) do {    \
+               NEW_INST ((dest), OP_R4CONST); \
+               (dest)->inst_p0 = f;    \
+               (dest)->type = STACK_R8;        \
+       } while (0)
+
+#define NEW_R8(dest,d) do {    \
+               NEW_INST ((dest), OP_R8CONST); \
+               (dest)->inst_p0 = d;    \
+               (dest)->type = STACK_R8;        \
+       } while (0)
+
+/*
+ * Create a MonoInst that represents an expression argument
+ */
+static MonoInst*
+create_expression_argument (MonoSsapreWorkArea *area, MonoSsapreExpressionArgument *argument) {
+       MonoInst *result;
+       
+       switch (argument->type) {
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_NOT_PRESENT:
+                       return NULL;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE:
+                       return mono_compile_create_var_load (area->cfg, argument->argument.ssa_variable);
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_INTEGER_CONSTANT:
+                       NEW_I4 (result, argument->argument.integer_constant);
+                       return result;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_LONG_COSTANT:
+                       NEW_I8 (result, *(argument->argument.long_constant));
+                       return result;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_FLOAT_COSTANT:
+                       NEW_R4 (result, argument->argument.float_constant);
+                       return result;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_DOUBLE_COSTANT:
+                       NEW_R8 (result, argument->argument.double_constant);
+                       return result;
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE:
+               case MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY:
+               default:
+                       g_assert_not_reached ();
+                       return NULL;
+       }
+}
+
+/*
+ * Create a MonoInst that represents an expression
+ */
+static MonoInst*
+create_expression (MonoSsapreWorkArea *area, MonoSsapreExpressionDescription *expression) {
+       MonoInst *result;
+       NEW_INST (result, expression->opcode);
+       result->inst_left = create_expression_argument (area, &(expression->left_argument));
+       result->inst_right = create_expression_argument (area, &(expression->right_argument));
+       return result;
+}
+
+/*
+ * See paper, section 3.6
+ */
+static void code_motion (MonoSsapreWorkArea *area) {
+       MonoSsapreBBInfo *current_bb = NULL;
+       MonoSsapreExpressionOccurrence *current_expression = NULL;
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {       
+               if ((current_bb->has_phi) && (current_bb->phi_can_be_available && ! current_bb->phi_is_later)) {
+                       MonoInst *new_var = mono_compile_create_var (area->cfg, area->current_expression->type, OP_LOCAL);
+                       current_bb->phi_variable_index = new_var->inst_c0;
+               } else {
+                       current_bb->phi_variable_index = BOTTOM_REDUNDANCY_CLASS;
+               }
+               
+               for (current_expression = current_bb->first_expression_in_bb; (current_expression != NULL) && (current_expression->bb_info == current_bb); current_expression = current_expression->next) {
+                       if (current_expression->save) {
+                               MonoInst *new_var = mono_compile_create_var (area->cfg, area->current_expression->type, OP_LOCAL);
+                               current_expression->variable_index = new_var->inst_c0;
+                       } else {
+                               current_expression->variable_index = BOTTOM_REDUNDANCY_CLASS;
+                       }
+               }
+               
+               if ((current_bb->has_phi_argument) && (current_bb->phi_argument_needs_insert)) {
+                       MonoInst *new_var = mono_compile_create_var (area->cfg, area->current_expression->type, OP_LOCAL);
+                       current_bb->phi_argument_variable_index = new_var->inst_c0;
+               } else {
+                       current_bb->phi_argument_variable_index = BOTTOM_REDUNDANCY_CLASS;
+               }
+       }
+       
+       for (current_bb = area->first_interesting_bb; current_bb != NULL; current_bb = current_bb->next_interesting_bb) {
+               if (current_bb->phi_variable_index != BOTTOM_REDUNDANCY_CLASS) {
+                       MonoInst *phi;
+                       MonoInst *store;
+                       int in_bb;
+                       
+                       NEW_INST (phi, OP_PHI);
+                       phi->inst_phi_args = mono_mempool_alloc (area->cfg->mempool, (sizeof (int) * ((current_bb->in_count) + 1)));
+                       phi->inst_phi_args [0] = current_bb->in_count;
+                       for (in_bb = 0; in_bb < current_bb->in_count; in_bb++) {
+                               gssize phi_argument_variable_index = current_bb->in_bb [in_bb]->phi_argument_variable_index;
+                               if (phi_argument_variable_index == BOTTOM_REDUNDANCY_CLASS) {
+                                       MonoSsapreBBInfo *phi_argument_bb = current_bb->in_bb [in_bb];
+                                       if (phi_argument_bb->phi_argument_defined_by_phi !=NULL) {
+                                               phi_argument_variable_index = phi_argument_bb->phi_argument_defined_by_phi->phi_variable_index;
+                                       } else if (phi_argument_bb->phi_argument_defined_by_real_occurrence !=NULL) {
+                                               phi_argument_variable_index = phi_argument_bb->phi_argument_defined_by_real_occurrence->variable_index;
+                                       } else {
+                                               g_assert_not_reached ();
+                                       }
+                               }
+                               phi->inst_phi_args [in_bb + 1] = phi_argument_variable_index;
+                       }
+                       store = mono_compile_create_var_store (area->cfg, current_bb->phi_variable_index, phi);
+                       if (current_bb->phi_insertion_point != NULL) {
+                               store->next = current_bb->phi_insertion_point->next;
+                               current_bb->phi_insertion_point->next = store;
+                       } else {
+                               store->next = current_bb->bb->code;
+                               current_bb->bb->code = store;
+                       }
+                       current_bb->phi_insertion_point = store;
+                       
+                       area->added_phis ++;
+               }
+               
+               for (current_expression = current_bb->first_expression_in_bb; (current_expression != NULL) && (current_expression->bb_info == current_bb); current_expression = current_expression->next) {
+                       gboolean altered = FALSE;
+                       if (current_expression->reload) {
+                               gssize variable_index;
+                               if (current_expression->defined_by_phi != NULL) {
+                                       variable_index = current_expression->defined_by_phi->phi_variable_index;
+                               } else if (current_expression->defined_by_real_occurrence != NULL) {
+                                       variable_index = current_expression->defined_by_real_occurrence->variable_index;
+                               } else {
+                                       variable_index = BOTTOM_REDUNDANCY_CLASS;
+                                       g_assert_not_reached ();
+                               }
+                               mono_compile_make_var_load (area->cfg, current_expression->occurrence, variable_index);
+                               
+                               area->reloaded_occurrences ++;
+                               altered = TRUE;
+                       }
+                       if (current_expression->save) {
+                               MonoInst *store;
+                               MonoInst *moved_expression = mono_mempool_alloc (area->cfg->mempool, sizeof (MonoInst));
+                               *moved_expression = *(current_expression->occurrence);
+                               store = mono_compile_create_var_store (area->cfg, current_expression->variable_index, moved_expression);
+                               if (current_expression->previous_tree != NULL) {
+                                       store->next = current_expression->previous_tree->next;
+                                       current_expression->previous_tree->next = store;
+                               } else {
+                                       store->next = current_bb->bb->code;
+                                       current_bb->bb->code = store;
+                               }
+                               mono_compile_make_var_load (area->cfg, current_expression->occurrence, current_expression->variable_index);
+                               
+                               area->saved_occurrences ++;
+                               altered = TRUE;
+                       }
+                       if (! altered) {
+                               area->unaltered_occurrences ++;
+                       }
+               }
+               
+               if (current_bb->phi_argument_variable_index != BOTTOM_REDUNDANCY_CLASS) {
+                       MonoSsapreExpressionDescription expression_description;
+                       MonoInst *inserted_expression;
+                       MonoInst *store;
+                       
+                       expression_description = area->current_expression->description;
+                       if (expression_description.left_argument.type == MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE) {
+                               expression_description.left_argument.argument.ssa_variable = current_bb->phi_argument_left_argument_version;
+                               expression_description.left_argument.type = MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE;
+                       }
+                       if (expression_description.right_argument.type == MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE) {
+                               expression_description.right_argument.argument.ssa_variable = current_bb->phi_argument_right_argument_version;
+                               expression_description.right_argument.type = MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE;
+                       }
+                       
+                       inserted_expression = create_expression (area, &expression_description);
+                       store = mono_compile_create_var_store (area->cfg, current_bb->phi_argument_variable_index, inserted_expression);
+                       store->next = NULL;
+                       mono_add_ins_to_end (current_bb->bb, store);
+                       
+                       area->inserted_occurrences ++;
+               }
+       }
+}
+
+/*
+ * Perform all SSAPRE steps for an expression
+ */
+static void
+process_expression (MonoSsapreWorkArea *area, MonoSsapreExpression *expression) {
+       if (area->cfg->verbose_level >= TRACE_LEVEL) {
+               printf ("SSAPRE STARTS PROCESSING EXPRESSION ");
+               print_expression_description (&(expression->description));
+               printf ("\n");
+       }
+       
+       clean_area_infos (area);
+       
+       area->current_expression = expression;
+       phi_insertion (area, expression);
+       renaming_pass (area);
+       
+       if (area->cfg->verbose_level >= TRACE_LEVEL) {
+               printf ("START SUMMARY OF BB INFOS\n");
+               print_bb_infos (area);
+               printf ("END SUMMARY OF BB INFOS\n");
+       }
+       
+       down_safety (area);
+       compute_can_be_available (area);
+       compute_later (area);
+       finalize (area);
+       code_motion (area);
+       
+       if (area->cfg->verbose_level >= DUMP_LEVEL) {
+               printf ("START DUMP OF BB INFOS\n");
+               dump_code (area);
+               printf ("END DUMP OF BB INFOS\n");
+       } else if (area->cfg->verbose_level >= TRACE_LEVEL) {
+               printf ("START SUMMARY OF BB INFOS\n");
+               print_interesting_bb_infos (area);
+               printf ("END SUMMARY OF BB INFOS\n");
+       }
+       
+       if (area->cfg->verbose_level >= STATISTICS_LEVEL) {
+               printf ("STATISTICS: saved_occurrences = %d, reloaded_occurrences = %d, inserted_occurrences = %d, unaltered_occurrences = %d, added_phis = %d\n",
+                       area->saved_occurrences, area->reloaded_occurrences, area->inserted_occurrences, area->unaltered_occurrences, area->added_phis);
+       }
+       
+       if (area->cfg->verbose_level >= TRACE_LEVEL) {
+               printf ("SSAPRE ENDS PROCESSING EXPRESSION ");
+               print_expression_description (&(expression->description));
+               printf ("\n");
+       }
+}
+
+/*
+ * Perform all SSAPRE steps for all the expressions in the worklist
+ */
+static void
+process_worklist (MonoSsapreWorkArea *area, MonoSsapreExpression *expression) {
+       if (expression != NULL) {
+               process_worklist (area, expression->previous);
+               process_expression (area, expression);
+               process_worklist (area, expression->next);
+       }
+}
+
+/*
+ * Hack to apply SSAPRE only to a given method (invaluable in debugging)
+ */
+#define APPLY_SSAPRE_TO_SINGLE_METHOD 0
+#if (APPLY_SSAPRE_TO_SINGLE_METHOD)
+static char *mono_ssapre_method_name = NULL;
+static gboolean check_ssapre_method_name (MonoCompile *cfg) {
+       if (mono_ssapre_method_name == NULL) {
+               mono_ssapre_method_name = getenv ("MONO_SSAPRE_METHOD_NAME");
+       }
+       if (mono_ssapre_method_name != NULL) {
+               char *method_name = mono_method_full_name (cfg->method, TRUE);
+               if (strstr (mono_ssapre_method_name, method_name) != NULL) {
+                       return TRUE;
+               } else {
+                       return FALSE;
+               }
+       } else {
+               return TRUE;
+       }
+}
+#endif
+
+/*
+ * Apply SSAPRE to a MonoCompile
+ */
+void
+mono_perform_ssapre (MonoCompile *cfg) {
+       MonoSsapreWorkArea area;
+       MonoSsapreExpressionOccurrence *current_occurrence;
+       int dt_dfn, descendants, block, i;
+       
+#if (APPLY_SSAPRE_TO_SINGLE_METHOD)
+       if (! check_ssapre_method_name (cfg)) return;
+#endif
+       
+       area.cfg = cfg;
+       area.mempool = mono_mempool_new ();
+       
+       area.num_bblocks = cfg->num_bblocks;
+       area.bb_infos = (MonoSsapreBBInfo*) mono_mempool_alloc (area.mempool, sizeof (MonoSsapreBBInfo) * (cfg->num_bblocks));
+       area.bb_infos_in_cfg_dfn_order = (MonoSsapreBBInfo**) mono_mempool_alloc (area.mempool, sizeof (MonoSsapreBBInfo*) * (cfg->num_bblocks));
+       
+       area.sizeof_bb_bitset = mono_bitset_alloc_size (cfg->num_bblocks, 0);
+       area.expression_occurrences_buffer = mono_bitset_mem_new (mono_mempool_alloc (area.mempool, area.sizeof_bb_bitset), area.num_bblocks, 0);
+       area.bb_iteration_buffer = mono_bitset_mem_new (mono_mempool_alloc (area.mempool, area.sizeof_bb_bitset), area.num_bblocks, 0);
+       area.iterated_dfrontier_buffer = mono_bitset_mem_new (mono_mempool_alloc (area.mempool, area.sizeof_bb_bitset), area.num_bblocks, 0);
+       area.left_argument_bb_bitset = mono_bitset_mem_new (mono_mempool_alloc (area.mempool, area.sizeof_bb_bitset), area.num_bblocks, 0);
+       area.right_argument_bb_bitset = mono_bitset_mem_new (mono_mempool_alloc (area.mempool, area.sizeof_bb_bitset), area.num_bblocks, 0);
+       
+       area.worklist = NULL;
+       
+       if (area.cfg->verbose_level >= LOG_LEVEL) {
+               printf ("SSAPRE STARTS PROCESSING METHOD %s\n", mono_method_full_name (cfg->method, TRUE));
+       }
+       
+       current_occurrence = (MonoSsapreExpressionOccurrence*) mono_mempool_alloc (area.mempool, sizeof (MonoSsapreExpressionOccurrence));
+       dt_dfn = 0;
+       descendants = 0;
+       process_bb (&area, cfg->bblocks [0], &dt_dfn, &descendants, current_occurrence);
+       for (block = 0; block < area.num_bblocks; block++) {
+               MonoSsapreBBInfo *bb_info = &(area.bb_infos [block]);
+               MonoBasicBlock *bb = bb_info->bb;
+               if (bb->idom != NULL) {
+                       bb_info->idominator = area.bb_infos_in_cfg_dfn_order [bb->idom->dfn];
+               } else {
+                       bb_info->idominator = NULL;
+               }
+               for (i = 0; i < bb->in_count; i++) {
+                       bb_info->in_bb [i] = area.bb_infos_in_cfg_dfn_order [bb->in_bb [i]->dfn];
+               }
+               for (i = 0; i < bb->out_count; i++) {
+                       bb_info->out_bb [i] = area.bb_infos_in_cfg_dfn_order [bb->out_bb [i]->dfn];
+               }
+       }
+       
+       if (area.cfg->verbose_level >= TRACE_LEVEL) {
+               printf ("SSAPRE START WORKLIST\n");
+               print_worklist (area.worklist);
+               printf ("SSAPRE END WORKLIST\n");
+       }
+       
+       process_worklist (&area, area.worklist);
+       
+       if (area.cfg->verbose_level >= TRACE_LEVEL) {
+               printf ("SSAPRE ENDS PROCESSING METHOD %s\n", mono_method_full_name (cfg->method, TRUE));
+       }
+       
+       mono_mempool_destroy (area.mempool);
+}
diff --git a/mono/mini/ssapre.h b/mono/mini/ssapre.h
new file mode 100644 (file)
index 0000000..ca00c4f
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * ssapre.h: SSA Partial Redundancy Elimination
+ *
+ * Author:
+ *   Massimiliano Mantione (massi@ximian.com)
+ *
+ * (C) 2004 Novell, Inc.  http://www.novell.com
+ */
+
+#ifndef __MONO_SSAPRE_H__
+#define __MONO_SSAPRE_H__
+
+
+#include "mini.h"
+#include <mono/metadata/mempool.h>
+
+/*
+ * All the different kind of arguments we can handle.
+ * "ANY" means the argument is unknown or cannot be handled, and "NOT_PRESENT"
+ * that the expression does not have this argument (has not "enough" arity).
+ */
+typedef enum {
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_ANY,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_NOT_PRESENT,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_INTEGER_CONSTANT,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_LONG_COSTANT,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_FLOAT_COSTANT,
+       MONO_SSAPRE_EXPRESSION_ARGUMENT_DOUBLE_COSTANT
+} MonoSsapreExpressionArgumentType;
+
+/*
+ * A struct representing an expression argument (the used branch in the
+ * union depends on the value of the type field).
+ */
+typedef struct MonoSsapreExpressionArgument {
+       MonoSsapreExpressionArgumentType type;
+       union {
+               gssize original_variable;
+               gssize ssa_variable;
+               gssize integer_constant;
+               gint64* long_constant;
+               float* float_constant;
+               double* double_constant;
+       } argument;
+} MonoSsapreExpressionArgument;
+
+/*
+ * Macros used when comparing expression arguments, which return -1,0 or 1.
+ */
+#define MONO_COMPARE_SSAPRE_DIRECT_VALUES(v1,v2) (((v2)>(v1)?(1):((v2)<(v1)?(-1):(0))))
+#define MONO_COMPARE_SSAPRE_POINTER_VALUES(p1,p2) (((*p2)>(*p1)?(1):((*p2)<(*p1)?(-1):(0))))
+
+#define MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENT_VALUES(t,v1,v2) (\
+               (t)==MONO_SSAPRE_EXPRESSION_ARGUMENT_ORIGINAL_VARIABLE?\
+                       MONO_COMPARE_SSAPRE_DIRECT_VALUES ((v1).original_variable,(v2).original_variable):(\
+               (t)==MONO_SSAPRE_EXPRESSION_ARGUMENT_SSA_VARIABLE?\
+                       MONO_COMPARE_SSAPRE_DIRECT_VALUES ((v1).ssa_variable,(v2).ssa_variable):(\
+               (t)==MONO_SSAPRE_EXPRESSION_ARGUMENT_INTEGER_CONSTANT?\
+                       MONO_COMPARE_SSAPRE_DIRECT_VALUES ((v1).integer_constant,(v2).integer_constant):(\
+               (t)==MONO_SSAPRE_EXPRESSION_ARGUMENT_LONG_COSTANT?\
+                       MONO_COMPARE_SSAPRE_POINTER_VALUES ((v1).long_constant,(v2).long_constant):(\
+               (t)==MONO_SSAPRE_EXPRESSION_ARGUMENT_FLOAT_COSTANT?\
+                       MONO_COMPARE_SSAPRE_POINTER_VALUES ((v1).float_constant,(v2).float_constant):(\
+               (t)==MONO_SSAPRE_EXPRESSION_ARGUMENT_DOUBLE_COSTANT?\
+                       MONO_COMPARE_SSAPRE_POINTER_VALUES ((v1).double_constant,(v2).double_constant):(\
+               0)))))))
+
+#define MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENTS(a1,a2) (\
+               MONO_COMPARE_SSAPRE_DIRECT_VALUES ((a1).type,(a2).type)!=0?\
+                       MONO_COMPARE_SSAPRE_DIRECT_VALUES ((a1).type,(a2).type):\
+               MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENT_VALUES ((a1).type,(a1).argument,(a2).argument) )
+
+
+/*
+ * A struct representing an expression, with its opcode and two arguments
+ * (if the opcode has arity 1 right_argument is MONO_SSAPRE_EXPRESSION_ARGUMENT_NOT_PRESENT).
+ */
+typedef struct MonoSsapreExpressionDescription {
+       guint16 opcode;
+       MonoSsapreExpressionArgument left_argument;
+       MonoSsapreExpressionArgument right_argument;
+} MonoSsapreExpressionDescription;
+
+/*
+ * Macro that compares two expression descriptions (returns -1, 0 or 1).
+ */
+#define MONO_COMPARE_SSAPRE_EXPRESSION_DESCRIPTIONS(d1,d2) (\
+               MONO_COMPARE_SSAPRE_DIRECT_VALUES ((d1).opcode,(d2).opcode)!=0?\
+                       MONO_COMPARE_SSAPRE_DIRECT_VALUES ((d1).opcode,(d2).opcode):(\
+               MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENTS ((d1).left_argument,(d2).left_argument)!=0?\
+                       MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENTS ((d1).left_argument,(d2).left_argument):(\
+               MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENTS ((d1).right_argument,(d2).right_argument)!=0?\
+                       MONO_COMPARE_SSAPRE_EXPRESSION_ARGUMENTS ((d1).right_argument,(d2).right_argument):(\
+               0))))
+
+/*
+ * Struct that contains all the information related to a BB.
+ * Some of them are taken from the corresponding MonoBasicBlock, some are
+ * constant during the compilation of the whole method, others must be
+ * recomputed for each expression.
+ */
+typedef struct MonoSsapreBBInfo {
+       /* Information constant during the compilation of the whole method: */
+       
+       /* Depth First Number relative to a traversal of the dominator tree */
+       gint32 dt_dfn;
+       /* Depth First Number relative to a traversal of the CFG */
+       gint32 cfg_dfn;
+       /* Number of descendants in the dominator tree (is equal to the number of strictly dominated BBs) */
+       int dt_descendants;
+       /* In and out count (taken from the corresponding MonoBasicBlock) */
+       gint16 in_count, out_count;
+       /* Idominator (taken from the corresponding MonoBasicBlock, but pointing */
+       /* to the MonoSsapreBBInfo for convenience) */
+       struct MonoSsapreBBInfo *idominator;
+       /* In and out BBs (taken from the corresponding MonoBasicBlock, but pointing */
+       /* to the MonoSsapreBBInfo for convenience) */
+       struct MonoSsapreBBInfo **in_bb;
+       struct MonoSsapreBBInfo **out_bb;
+       /* Dominance frontier (taken from the corresponding MonoBasicBlock) */
+       MonoBitSet *dfrontier;
+       
+       /* MonoInst where new phi definitions must be added in the BB */
+       /* (the last existing phi definition, or NULL if there is none) */
+       MonoInst *phi_insertion_point;
+       
+       /* Information reused during the analysis of each expression: */
+       
+       /* True if this BB has a PHI occurrence */
+       gboolean has_phi;
+       /* True if this PHI defines a real occurrence */
+       gboolean phi_defines_a_real_occurrence;
+       /* True if this PHI is down safe */
+       gboolean phi_is_down_safe;
+       /* True if this PHI can be available */
+       gboolean phi_can_be_available;
+       /* True if this PHI is "later" */
+       gboolean phi_is_later;
+       /* The PHI class number */
+       gssize phi_redundancy_class;
+       /* The index of this PHI in the cfg->vars array */
+       gssize phi_variable_index;
+       /* Array of the class numbers of the PHI arguments (has "in_count" elements) */
+       gssize *phi_arguments_classes;
+       
+       
+       /* True if this BB has a PHI argument */
+       gboolean has_phi_argument;
+       /* True if this PHI argument "has real use" */
+       gboolean phi_argument_has_real_use;
+       /* True if this PHI argument needs the insertion of a new occurrence */
+       gboolean phi_argument_needs_insert;
+       /* True if this PHI argument has been processed (see "set_save") */
+       gboolean phi_argument_has_been_processed;
+       /* The PHI argument class number */
+       gssize phi_argument_class;
+       /* The index of this PHI argument in the cfg->vars array */
+       gssize phi_argument_variable_index;
+       /* Points to the real occurrence defining this PHI argument (NULL otherwise) */
+       struct MonoSsapreExpressionOccurrence *phi_argument_defined_by_real_occurrence;
+       /* Points to the BB containing the PHI defining this PHI argument (NULL otherwise) */
+       struct MonoSsapreBBInfo *phi_argument_defined_by_phi;
+       /* Variable version of the left argument og the PHI argument "expected" at */
+       /* the PHI (or BOTTOM_REDUNDANCY_CLASS otherwise), see "renaming_pass" */
+       gssize phi_argument_left_argument_version;
+       /* As above, but for the right argument */
+       gssize phi_argument_right_argument_version;
+       
+       /* The first real occurrence in this BB (NULL if there is none) */
+       struct MonoSsapreExpressionOccurrence *first_expression_in_bb;
+       /* Next BB which has either a real occurrence, a PHI or a PHI argument */
+       /* (NULL if there is none, BBs are in dominator tree depth first preorder) */
+       struct MonoSsapreBBInfo *next_interesting_bb;
+       
+       /* Used in maintaining the renaming stack */
+       struct MonoSsapreBBInfo *next_in_renaming_stack;
+       struct MonoSsapreExpressionOccurrence *top_of_local_renaming_stack;
+       
+       /* MonoBasicBlock representing this BB in the CFG (this is obviously constant) */
+       MonoBasicBlock *bb;
+} MonoSsapreBBInfo;
+
+
+/*
+ * A "real" occurrence.
+ */
+typedef struct MonoSsapreExpressionOccurrence {
+       /* The occurrence in the CFG */
+       MonoInst *occurrence;
+       /* The tree just before the occurrence in the CFG (if the occurrence must */
+       /* saved into a temporary, the definition will be placed just after that tree) */
+       MonoInst *previous_tree;
+       /* The BB where this occurrence is found */
+       MonoSsapreBBInfo *bb_info;
+       /* The description of the occurrence */
+       MonoSsapreExpressionDescription description;
+       /* Next occurrence of this expression */
+       struct MonoSsapreExpressionOccurrence *next;
+       /* Previois occurrence of this expression */
+       struct MonoSsapreExpressionOccurrence *previous;
+       /* True if this occurrence is the first in its BB */
+       gboolean is_first_in_bb;
+       /* True if this occurrence is the last in its BB */
+       gboolean is_last_in_bb;
+       /* "reload" flag (see "finalize") */
+       gboolean reload;
+       /* "save" flag (see "finalize") */
+       gboolean save;
+       
+       /* Used in maintaining the renaming stack */
+       struct MonoSsapreExpressionOccurrence *next_in_renaming_stack;
+       
+       /* Class number of this occurrence */
+       gssize redundancy_class;
+       /* The index of the temporary of this occurrence in the cfg->vars array */
+       gssize variable_index;
+       /* Points to the real occurrence defining this occurrence (NULL otherwise) */
+       struct MonoSsapreExpressionOccurrence *defined_by_real_occurrence;
+       /* Points to the BB containing the PHI defining this occurrence (NULL otherwise) */
+       struct MonoSsapreBBInfo *defined_by_phi;
+} MonoSsapreExpressionOccurrence;
+
+
+/*
+ * An expression to be processed (in the worklist).
+ */
+typedef struct MonoSsapreExpression {
+       /* The description of the expression */
+       MonoSsapreExpressionDescription description;
+       /* The type to use when creating values of this expression */
+       MonoType *type;
+       /* The list of expression occurrences */
+       MonoSsapreExpressionOccurrence *occurrences;
+       /* The last expression occurrence in the list */
+       MonoSsapreExpressionOccurrence *last_occurrence;
+       
+       /* Used in maintaining the worklist (an autobalancing binary tree) */
+       struct MonoSsapreExpression *father;
+       struct MonoSsapreExpression *previous;
+       struct MonoSsapreExpression *next;
+       gssize tree_size;
+} MonoSsapreExpression;
+
+/*
+ * Macros used to maintain the worklist
+ */
+#define MONO_SSAPRE_GOTO_FIRST_EXPRESSION(e) do{\
+               while ((e)->previous != NULL) (e) = (e)->previous;\
+       } while (0)
+#define MONO_SSAPRE_REMOVE_FIRST_EXPRESSION(e) do{\
+               if ((e)->father != NULL) {\
+                       (e)->father->previous = (e)->next;\
+               }\
+       } while (0)
+#define MONO_SSAPRE_GOTO_LAST_EXPRESSION(e) do{\
+               while ((e)->next != NULL) (e) = (e)->next;\
+       } while (0)
+#define MONO_SSAPRE_REMOVE_LAST_EXPRESSION(e) do{\
+               if ((e)->father != NULL) {\
+                       (e)->father->next = (e)->previous;\
+               }\
+       } while (0)
+
+#define MONO_SSAPRE_MAX_TREE_DEPTH(size,depth) do{\
+               unsigned __mask__ = ~1;\
+               (depth) = 1;\
+               while (((size)&__mask__)!=0) {\
+                       __mask__ <<= 1;\
+                       (depth)++;\
+               }\
+       } while (0)
+
+#define MONO_SSAPRE_ADD_EXPRESSION_OCCURRANCE(e,o) do{\
+               if ((e)->occurrences == NULL) {\
+                       (e)->occurrences = (o);\
+               } else {\
+                       (e)->last_occurrence->next = (o);\
+               }\
+               (o)->next = NULL;\
+               (o)->previous = (e)->last_occurrence;\
+               (e)->last_occurrence = (o);\
+       } while (0)
+#define MONO_SSAPRE_REMOVE_EXPRESSION_OCCURRANCE(e,o) do{\
+               if ((e)->occurrences == (o)) {\
+                       (e)->occurrences = (o)->next;\
+               }\
+               if ((e)->last_occurrence == (o)) {\
+                       (e)->last_occurrence = (o)->previous;\
+               }\
+               if ((o)->previous != NULL) {\
+                       (o)->previous->next = (o)->next;\
+               }\
+               if ((o)->next != NULL) {\
+                       (o)->next->previous = (o)->previous;\
+               }\
+               (o)->next = NULL;\
+               (o)->previous = NULL;\
+       } while (0)
+
+
+/*
+ * Availability table element (see "finalize"), one for each redundancy class
+ */
+typedef struct MonoSsapreAvailabilityTableElement {
+       /* Points to the real occurrence defining this redundancy class (NULL otherwise) */
+       struct MonoSsapreExpressionOccurrence *class_defined_by_real_occurrence;
+       /* Points to the BB containing the PHI defining this redundancy class (NULL otherwise) */
+       struct MonoSsapreBBInfo *class_defined_by_phi;
+} MonoSsapreAvailabilityTableElement;
+
+/*
+ * The "main" work area for the algorithm.
+ */
+typedef struct MonoSsapreWorkArea {
+       /* The CFG */
+       MonoCompile *cfg;
+       /* The SSAPRE specific mempool */
+       MonoMemPool *mempool;
+       
+       /* Number of BBs in the CFG (from cfg) */
+       int num_bblocks;
+       /* BB information, in dominator tree depth first preorder */
+       MonoSsapreBBInfo *bb_infos;
+       /* Pointers to BB information, in CFG depth first preorder */
+       MonoSsapreBBInfo **bb_infos_in_cfg_dfn_order;
+       
+       /* Number of variables in the CFG */
+       int num_vars;
+       /* Size of bitset for BBs */
+       int sizeof_bb_bitset;
+       /* Various bitsets used when working with iterated dfrontiers */
+       MonoBitSet *expression_occurrences_buffer;
+       MonoBitSet *bb_iteration_buffer;
+       MonoBitSet *iterated_dfrontier_buffer;
+       MonoBitSet *left_argument_bb_bitset;
+       MonoBitSet *right_argument_bb_bitset;
+       
+       /* The expression worklist */
+       MonoSsapreExpression *worklist;
+       
+       /* The expression being processed */
+       MonoSsapreExpression *current_expression;
+       
+       /* The BB on top of the renaming stack (if "top_of_renaming_stack" is NULL */
+       /* but this is not, then the top of the stack is the PHI in this BB) */
+       struct MonoSsapreBBInfo *bb_on_top_of_renaming_stack;
+       /* The top of the renaming stack */
+       struct MonoSsapreExpressionOccurrence *top_of_renaming_stack;
+       
+       /* The head of the list of "interesting" BBs */
+       struct MonoSsapreBBInfo *first_interesting_bb;
+       
+       /* The number of generated class numbers */
+       int number_of_classes;
+       
+       /* Statistics fields (per expression)  */
+       int saved_occurrences;
+       int reloaded_occurrences;
+       int inserted_occurrences;
+       int unaltered_occurrences;
+       int added_phis;
+} MonoSsapreWorkArea;
+
+
+#endif /* __MONO_SSAPRE_H__ */