importing messaging-2008 branch to trunk, going on.
[mono.git] / mono / mini / ssa.c
1 /*
2  * ssa.c: Static single assign form support for the JIT compiler.
3  *
4  * Author:
5  *    Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9 #include <string.h>
10 #include <mono/metadata/debug-helpers.h>
11 #include <mono/metadata/mempool.h>
12 #include <mono/metadata/mempool-internals.h>
13
14 #ifndef DISABLE_JIT
15
16 #include "mini.h"
17
18 #define USE_ORIGINAL_VARS
19 #define CREATE_PRUNED_SSA
20
21 //#define DEBUG_SSA 1
22
23 #define NEW_PHI(cfg,dest,val) do {      \
24                 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
25                 (dest)->opcode = OP_PHI;        \
26                 (dest)->inst_c0 = (val);        \
27         (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1; \
28         } while (0)
29
30 typedef struct {
31         MonoBasicBlock *bb;
32         MonoInst *inst;
33 } MonoVarUsageInfo;
34
35 static void 
36 unlink_target (MonoBasicBlock *bb, MonoBasicBlock *target)
37 {
38         int i;
39
40         for (i = 0; i < bb->out_count; i++) {
41                 if (bb->out_bb [i] == target) {
42                         bb->out_bb [i] = bb->out_bb [--bb->out_count];
43                         break;
44                 }
45         }
46         for (i = 0; i < target->in_count; i++) {
47                 if (target->in_bb [i] == bb) {
48                         target->in_bb [i] = target->in_bb [--target->in_count];
49                         break;
50                         
51                 }
52         }
53 }
54
55 static void
56 unlink_unused_bblocks (MonoCompile *cfg) 
57 {
58         int i, j;
59         MonoBasicBlock *bb;
60
61         g_assert (cfg->comp_done & MONO_COMP_REACHABILITY);
62
63         if (G_UNLIKELY (cfg->verbose_level > 1))
64                 printf ("\nUNLINK UNUSED BBLOCKS:\n");
65
66         for (bb = cfg->bb_entry; bb && bb->next_bb;) {
67                 if (!(bb->next_bb->flags & BB_REACHABLE)) {
68                         bb->next_bb = bb->next_bb->next_bb;
69                 } else 
70                         bb = bb->next_bb;
71         }
72
73         for (i = 1; i < cfg->num_bblocks; i++) {
74                 bb = cfg->bblocks [i];
75                
76                 if (!(bb->flags & BB_REACHABLE)) {
77                         for (j = 0; j < bb->in_count; j++) {
78                                 unlink_target (bb->in_bb [j], bb);      
79                         }
80                         for (j = 0; j < bb->out_count; j++) {
81                                 unlink_target (bb, bb->out_bb [j]);     
82                         }
83                         if (G_UNLIKELY (cfg->verbose_level > 1))
84                                 printf ("\tUnlinked BB%d\n", bb->block_num);
85                 }
86  
87         }
88 }
89
90 /**
91  * remove_bb_from_phis:
92  *
93  *   Remove BB from the PHI statements in TARGET.
94  */
95 static void
96 remove_bb_from_phis (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *target)
97 {
98         MonoInst *ins;
99         int i, j;
100
101         for (i = 0; i < target->in_count; i++) {
102                 if (target->in_bb [i] == bb) {
103                         break;
104                 }
105         }
106         g_assert (i < target->in_count);
107
108         for (ins = target->code; ins; ins = ins->next) {
109                 if (MONO_IS_PHI (ins)) {
110                         for (j = i; j < ins->inst_phi_args [0] - 1; ++j)
111                                 ins->inst_phi_args [j + 1] = ins->inst_phi_args [j + 2];
112                         ins->inst_phi_args [0] --;
113                 }
114                 else
115                         break;
116         }
117 }
118
119 static inline int
120 op_phi_to_move (int opcode)
121 {
122         switch (opcode) {
123         case OP_PHI:
124                 return OP_MOVE;
125         case OP_FPHI:
126                 return OP_FMOVE;
127         case OP_VPHI:
128                 return OP_VMOVE;
129         default:
130                 g_assert_not_reached ();
131         }
132
133         return -1;
134 }
135
136 static inline void
137 record_use (MonoCompile *cfg, MonoInst *var, MonoBasicBlock *bb, MonoInst *ins)
138 {
139         MonoMethodVar *info;
140         MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo));
141
142         info = MONO_VARINFO (cfg, var->inst_c0);
143         
144         ui->bb = bb;
145         ui->inst = ins;
146         info->uses = g_list_prepend_mempool (cfg->mempool, info->uses, ui);
147 }       
148
149 typedef struct {
150         MonoInst *var;
151         int idx;
152 } RenameInfo;
153
154 /**
155  * mono_ssa_rename_vars:
156  *
157  *  Implement renaming of SSA variables. Also compute def-use information in parallel.
158  * @stack_history points to an area of memory which can be used for storing changes 
159  * made to the stack, so they can be reverted later.
160  */
161 static void
162 mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, gboolean *originals_used, MonoInst **stack,   guint32 *lvreg_stack, gboolean *lvreg_defined, RenameInfo *stack_history, int stack_history_size) 
163 {
164         MonoInst *ins, *new_var;
165         int i, j, idx;
166         GSList *tmp;
167         int stack_history_len = 0;
168
169         if (cfg->verbose_level >= 4)
170                 printf ("\nRENAME VARS BLOCK %d:\n", bb->block_num);
171
172         /* First pass: Create new vars */
173         for (ins = bb->code; ins; ins = ins->next) {
174                 const char *spec = INS_INFO (ins->opcode);
175
176 #ifdef DEBUG_SSA
177                 printf ("\tProcessing "); mono_print_ins (ins);
178 #endif
179                 if (ins->opcode == OP_NOP)
180                         continue;
181
182                 /* SREG1 */
183                 if (spec [MONO_INST_SRC1] != ' ') {
184                         MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
185                         if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
186                                 int idx = var->inst_c0;
187                                 if (stack [idx]) {
188                                         if (var->opcode != OP_ARG)
189                                                 g_assert (stack [idx]);
190                                         ins->sreg1 = stack [idx]->dreg;
191                                         record_use (cfg, stack [idx], bb, ins);
192                                 }
193                                 else
194                                         record_use (cfg, var, bb, ins);
195                         }
196                         else if (G_UNLIKELY (!var && lvreg_stack [ins->sreg1]))
197                                 ins->sreg1 = lvreg_stack [ins->sreg1];
198                 }                                       
199
200                 /* SREG2 */
201                 if (spec [MONO_INST_SRC2] != ' ') {
202                         MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
203                         if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
204                                 int idx = var->inst_c0;
205                                 if (stack [idx]) {
206                                         if (var->opcode != OP_ARG)
207                                                 g_assert (stack [idx]);
208
209                                         ins->sreg2 = stack [idx]->dreg;
210                                         record_use (cfg, stack [idx], bb, ins);
211                                 }
212                                 else
213                                         record_use (cfg, var, bb, ins);
214                         }
215                         else if (G_UNLIKELY (!var && lvreg_stack [ins->sreg2]))
216                                 ins->sreg2 = lvreg_stack [ins->sreg2];
217                 }
218
219                 if (MONO_IS_STORE_MEMBASE (ins)) {
220                         MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
221                         if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
222                                 int idx = var->inst_c0;
223                                 if (stack [idx]) {
224                                         if (var->opcode != OP_ARG)
225                                                 g_assert (stack [idx]);
226                                         ins->dreg = stack [idx]->dreg;
227                                         record_use (cfg, stack [idx], bb, ins);
228                                 }
229                                 else
230                                         record_use (cfg, var, bb, ins);
231                         }
232                         else if (G_UNLIKELY (!var && lvreg_stack [ins->dreg]))
233                                 ins->dreg = lvreg_stack [ins->dreg];
234                 }
235
236                 /* DREG */
237                 if ((spec [MONO_INST_DEST] != ' ') && !MONO_IS_STORE_MEMBASE (ins)) {
238                         MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
239                         MonoMethodVar *info;
240
241                         if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
242                                 idx = var->inst_c0;
243                                 g_assert (idx < max_vars);
244
245                                 if (var->opcode == OP_ARG)
246                                         originals_used [idx] = TRUE;
247
248                                 /* FIXME: */
249                                 g_assert (stack_history_len < stack_history_size);
250                                 stack_history [stack_history_len].var = stack [idx];
251                                 stack_history [stack_history_len].idx = idx;
252                                 stack_history_len ++;
253
254                                 if (originals_used [idx]) {
255                                         new_var = mono_compile_create_var (cfg, var->inst_vtype, OP_LOCAL);
256                                         new_var->flags = var->flags;
257                                         MONO_VARINFO (cfg, new_var->inst_c0)->reg = idx;
258
259                                         if (cfg->verbose_level >= 4)
260                                                 printf ("  R%d -> R%d\n", var->dreg, new_var->dreg);
261
262                                         stack [idx] = new_var;
263
264                                         ins->dreg = new_var->dreg;
265                                         var = new_var;
266                                 }
267                                 else {
268                                         stack [idx] = var;
269                                         originals_used [idx] = TRUE;
270                                 }
271
272                                 info = MONO_VARINFO (cfg, var->inst_c0);
273                                 info->def = ins;
274                                 info->def_bb = bb;
275                         }
276                         else if (G_UNLIKELY (!var && lvreg_defined [ins->dreg] && (ins->dreg >= MONO_MAX_IREGS))) {
277                                 /* Perform renaming for local vregs */
278                                 lvreg_stack [ins->dreg] = mono_alloc_preg (cfg);
279                                 ins->dreg = lvreg_stack [ins->dreg];
280                         }
281                         else
282                                 lvreg_defined [ins->dreg] = TRUE;
283                 }
284
285 #ifdef DEBUG_SSA
286                 printf ("\tAfter processing "); mono_print_ins (ins);
287 #endif
288
289         }
290
291         /* Rename PHI arguments in succeeding bblocks */
292         for (i = 0; i < bb->out_count; i++) {
293                 MonoBasicBlock *n = bb->out_bb [i];
294
295                 for (j = 0; j < n->in_count; j++)
296                         if (n->in_bb [j] == bb)
297                                 break;
298                 
299                 for (ins = n->code; ins; ins = ins->next) {
300                         if (MONO_IS_PHI (ins)) {
301                                 idx = ins->inst_c0;
302                                 if (stack [idx])
303                                         new_var = stack [idx];
304                                 else
305                                         new_var = cfg->varinfo [idx];
306 #ifdef DEBUG_SSA
307                                 printf ("FOUND PHI %d (%d, %d)\n", idx, j, new_var->inst_c0);
308 #endif
309                                 ins->inst_phi_args [j + 1] = new_var->dreg;
310                                 record_use (cfg,  new_var, n, ins);                             
311                                 if (G_UNLIKELY (cfg->verbose_level >= 4))
312                                         printf ("\tAdd PHI R%d <- R%d to BB%d\n", ins->dreg, new_var->dreg, n->block_num);
313                         }
314                         else
315                                 /* The phi nodes are at the beginning of the bblock */
316                                 break;
317                 }
318         }
319
320         if (bb->dominated) {
321                 for (tmp = bb->dominated; tmp; tmp = tmp->next) {
322                         mono_ssa_rename_vars (cfg, max_vars, (MonoBasicBlock *)tmp->data, originals_used, stack, lvreg_stack, lvreg_defined, stack_history + stack_history_len, stack_history_size - stack_history_len);
323                 }
324         }
325
326         /* Restore stack */
327         for (i = stack_history_len - 1; i >= 0; i--) {
328                 stack [stack_history [i].idx] = stack_history [i].var;
329         }
330
331         cfg->comp_done |= MONO_COMP_SSA_DEF_USE;
332 }
333
334 void
335 mono_ssa_compute (MonoCompile *cfg)
336 {
337         int i, j, idx, bitsize;
338         MonoBitSet *set;
339         MonoMethodVar *vinfo = g_new0 (MonoMethodVar, cfg->num_varinfo);
340         MonoInst *ins, **stack;
341         guint8 *buf, *buf_start;
342         RenameInfo *stack_history;
343         int stack_history_size;
344         gboolean *originals;
345         guint32 *lvreg_stack;
346         gboolean *lvreg_defined;
347
348         g_assert (!(cfg->comp_done & MONO_COMP_SSA));
349
350         /* we dont support methods containing exception clauses */
351         g_assert (mono_method_get_header (cfg->method)->num_clauses == 0);
352         g_assert (!cfg->disable_ssa);
353
354         if (cfg->verbose_level >= 4)
355                 printf ("\nCOMPUTE SSA %d (R%d-)\n\n", cfg->num_varinfo, cfg->next_vreg);
356
357 #ifdef CREATE_PRUNED_SSA
358         /* we need liveness for pruned SSA */
359         if (!(cfg->comp_done & MONO_COMP_LIVENESS))
360                 mono_analyze_liveness (cfg);
361 #endif
362
363         mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM | MONO_COMP_DFRONTIER);
364
365         bitsize = mono_bitset_alloc_size (cfg->num_bblocks, 0);
366         buf = buf_start = g_malloc0 (mono_bitset_alloc_size (cfg->num_bblocks, 0) * cfg->num_varinfo);
367
368         for (i = 0; i < cfg->num_varinfo; ++i) {
369                 vinfo [i].def_in = mono_bitset_mem_new (buf, cfg->num_bblocks, 0);
370                 buf += bitsize;
371                 vinfo [i].idx = i;
372                 /* implicit reference at start */
373                 if (cfg->varinfo [i]->opcode == OP_ARG)
374                         mono_bitset_set_fast (vinfo [i].def_in, 0);
375         }
376
377         for (i = 0; i < cfg->num_bblocks; ++i) {
378                 MONO_BB_FOR_EACH_INS (cfg->bblocks [i], ins) {
379                         if (ins->opcode == OP_NOP)
380                                 continue;
381
382                         if (!MONO_IS_STORE_MEMBASE (ins) && get_vreg_to_inst (cfg, ins->dreg)) {
383                                 mono_bitset_set_fast (vinfo [get_vreg_to_inst (cfg, ins->dreg)->inst_c0].def_in, i);
384                         }
385                 }
386         }
387
388         /* insert phi functions */
389         for (i = 0; i < cfg->num_varinfo; ++i) {
390                 MonoInst *var = cfg->varinfo [i];
391
392 #if SIZEOF_VOID_P == 4
393                 if (var->type == STACK_I8)
394                         continue;
395 #endif
396                 if (var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))
397                         continue;
398
399                 /* Most variables have only one definition */
400                 if (mono_bitset_count (vinfo [i].def_in) <= 1)
401                         continue;
402
403                 set = mono_compile_iterated_dfrontier (cfg, vinfo [i].def_in);
404
405                 if (cfg->verbose_level >= 4) {
406                         if (mono_bitset_count (set) > 0) {
407                                 printf ("\tR%d needs PHI functions in ", var->dreg);
408                                 mono_blockset_print (cfg, set, "", -1);
409                         }
410                 }
411                         
412                 mono_bitset_foreach_bit (set, idx, cfg->num_bblocks) {
413                         MonoBasicBlock *bb = cfg->bblocks [idx];
414
415                         /* fixme: create pruned SSA? we would need liveness information for that */
416
417                         if (bb == cfg->bb_exit)
418                                 continue;
419
420                         if ((cfg->comp_done & MONO_COMP_LIVENESS) && !mono_bitset_test_fast (bb->live_in_set, i)) {
421                                 //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE));
422                                 continue;
423                         }
424
425                         NEW_PHI (cfg, ins, i);
426
427                         switch (var->type) {
428                         case STACK_I4:
429                         case STACK_I8:
430                         case STACK_PTR:
431                         case STACK_MP:
432                         case STACK_OBJ:
433                                 ins->opcode = OP_PHI;
434                                 break;
435                         case STACK_R8:
436                                 ins->opcode = OP_FPHI;
437                                 break;
438                         case STACK_VTYPE:
439                                 ins->opcode = OP_VPHI;
440                                 ins->klass = var->klass;
441                                 break;
442                         }
443
444                         ins->inst_phi_args =  mono_mempool_alloc0 (cfg->mempool, sizeof (int) * (cfg->bblocks [idx]->in_count + 1));
445                         ins->inst_phi_args [0] = cfg->bblocks [idx]->in_count;
446
447                         /* For debugging */
448                         for (j = 0; j < cfg->bblocks [idx]->in_count; ++j)
449                                 ins->inst_phi_args [j + 1] = -1;
450
451                         ins->dreg = cfg->varinfo [i]->dreg;
452
453                         mono_bblock_insert_before_ins (bb, bb->code, ins);
454
455 #ifdef DEBUG_SSA
456                         printf ("ADD PHI BB%d %s\n", cfg->bblocks [idx]->block_num, mono_method_full_name (cfg->method, TRUE));
457 #endif
458                 }
459         }
460
461         /* free the stuff */
462         g_free (vinfo);
463         g_free (buf_start);
464
465         /* Renaming phase */
466
467         stack = alloca (sizeof (MonoInst *) * cfg->num_varinfo);
468         memset (stack, 0, sizeof (MonoInst *) * cfg->num_varinfo);
469
470         lvreg_stack = g_new0 (guint32, cfg->next_vreg);
471         lvreg_defined = g_new0 (gboolean, cfg->next_vreg);
472         stack_history_size = 10240;
473         stack_history = g_new (RenameInfo, stack_history_size);
474         originals = g_new0 (gboolean, cfg->num_varinfo);
475         mono_ssa_rename_vars (cfg, cfg->num_varinfo, cfg->bb_entry, originals, stack, lvreg_stack, lvreg_defined, stack_history, stack_history_size);
476         g_free (stack_history);
477         g_free (originals);
478
479         if (cfg->verbose_level >= 4)
480                 printf ("\nEND COMPUTE SSA.\n\n");
481
482         cfg->comp_done |= MONO_COMP_SSA;
483 }
484
485 void
486 mono_ssa_remove (MonoCompile *cfg)
487 {
488         MonoInst *ins, *var, *move;
489         int i, j, first;
490
491         g_assert (cfg->comp_done & MONO_COMP_SSA);
492
493         for (i = 0; i < cfg->num_bblocks; ++i) {
494                 MonoBasicBlock *bb = cfg->bblocks [i];
495
496                 if (cfg->verbose_level >= 4)
497                         printf ("\nREMOVE SSA %d:\n", bb->block_num);
498
499                 for (ins = bb->code; ins; ins = ins->next) {
500                         if (MONO_IS_PHI (ins)) {
501                                 g_assert (ins->inst_phi_args [0] == bb->in_count);
502                                 var = get_vreg_to_inst (cfg, ins->dreg);
503
504                                 /* Check for PHI nodes where all the inputs are the same */
505                                 first = ins->inst_phi_args [1];
506
507                                 for (j = 1; j < bb->in_count; ++j)
508                                         if (first != ins->inst_phi_args [j + 1])
509                                                 break;
510
511                                 if ((bb->in_count > 1) && (j == bb->in_count)) {
512                                         ins->opcode = op_phi_to_move (ins->opcode);
513                                         if (ins->opcode == OP_VMOVE)
514                                                 g_assert (ins->klass);
515                                         ins->sreg1 = first;
516                                 } else {
517                                         for (j = 0; j < bb->in_count; j++) {
518                                                 MonoBasicBlock *pred = bb->in_bb [j];
519                                                 int sreg = ins->inst_phi_args [j + 1];
520
521                                                 if (cfg->verbose_level >= 4)
522                                                         printf ("\tADD R%d <- R%d in BB%d\n", var->dreg, sreg, pred->block_num);
523                                                 if (var->dreg != sreg) {
524                                                         MONO_INST_NEW (cfg, move, op_phi_to_move (ins->opcode));
525                                                         if (move->opcode == OP_VMOVE) {
526                                                                 g_assert (ins->klass);
527                                                                 move->klass = ins->klass;
528                                                         }
529                                                         move->dreg = var->dreg;
530                                                         move->sreg1 = sreg;
531                                                         mono_add_ins_to_end (pred, move);
532                                                 }
533                                         }
534
535                                         ins->opcode = OP_NOP;
536                                         ins->dreg = -1;
537                                 }
538                         }
539                 }
540         }
541
542         if (cfg->verbose_level >= 4) {
543                 for (i = 0; i < cfg->num_bblocks; ++i) {
544                         MonoBasicBlock *bb = cfg->bblocks [i];
545
546                         mono_print_bb (bb, "AFTER REMOVE SSA:");
547                 }
548         }
549
550         /*
551          * Removal of SSA form introduces many copies. To avoid this, we tyry to coalesce 
552          * the variables if possible. Since the newly introduced SSA variables don't
553          * have overlapping live ranges (because we don't do agressive optimization), we
554          * can coalesce them into the original variable.
555          */
556
557         for (i = 0; i < cfg->num_bblocks; ++i) {
558                 MonoBasicBlock *bb = cfg->bblocks [i];
559
560                 for (ins = bb->code; ins; ins = ins->next) {
561                         const char *spec = INS_INFO (ins->opcode);
562
563                         if (ins->opcode == OP_NOP)
564                                 continue;
565
566                         if (spec [MONO_INST_DEST] != ' ') {
567                                 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
568
569                                 if (var) {
570                                         MonoMethodVar *vmv = MONO_VARINFO (cfg, var->inst_c0);
571                                         
572                                         /* 
573                                          * The third condition avoids coalescing with variables eliminated
574                                          * during deadce.
575                                          */
576                                         if ((vmv->reg != -1) && (vmv->idx != vmv->reg) && (MONO_VARINFO (cfg, vmv->reg)->reg != -1)) {
577                                                 printf ("COALESCE: R%d -> R%d\n", ins->dreg, cfg->varinfo [vmv->reg]->dreg);
578                                                 ins->dreg = cfg->varinfo [vmv->reg]->dreg; 
579                                         }
580                                 }
581                         }
582
583                         if (spec [MONO_INST_SRC1] != ' ') {
584                                 MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
585
586                                 if (var) {
587                                         MonoMethodVar *vmv = MONO_VARINFO (cfg, var->inst_c0);
588
589                                         if ((vmv->reg != -1) && (vmv->idx != vmv->reg) && (MONO_VARINFO (cfg, vmv->reg)->reg != -1)) {
590                                                 printf ("COALESCE: R%d -> R%d\n", ins->sreg1, cfg->varinfo [vmv->reg]->dreg);
591                                                 ins->sreg1 = cfg->varinfo [vmv->reg]->dreg; 
592                                         }
593                                 }
594                         }
595
596                         if (spec [MONO_INST_SRC2] != ' ') {
597                                 MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
598
599                                 if (var) {
600                                         MonoMethodVar *vmv = MONO_VARINFO (cfg, var->inst_c0);
601
602                                         if ((vmv->reg != -1) && (vmv->idx != vmv->reg) && (MONO_VARINFO (cfg, vmv->reg)->reg != -1)) {
603                                                 printf ("COALESCE: R%d -> R%d\n", ins->sreg2, cfg->varinfo [vmv->reg]->dreg);
604                                                 ins->sreg2 = cfg->varinfo [vmv->reg]->dreg; 
605                                         }
606                                 }
607                         }
608
609                 }
610         }
611
612         for (i = 0; i < cfg->num_varinfo; ++i) {
613                 MONO_VARINFO (cfg, i)->reg = -1;
614         }
615
616         if (cfg->comp_done & MONO_COMP_REACHABILITY)
617                 unlink_unused_bblocks (cfg);
618
619         cfg->comp_done &= ~MONO_COMP_LIVENESS;
620
621         cfg->comp_done &= ~MONO_COMP_SSA;
622 }
623
624 static void
625 mono_ssa_create_def_use (MonoCompile *cfg) 
626 {
627         MonoBasicBlock *bb;
628         MonoInst *ins;
629         int i;
630
631         g_assert (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE));
632
633         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
634                 for (ins = bb->code; ins; ins = ins->next) {
635                         const char *spec = INS_INFO (ins->opcode);
636                         MonoMethodVar *info;
637
638                         if (ins->opcode == OP_NOP)
639                                 continue;
640
641                         /* SREG1 */
642                         if (spec [MONO_INST_SRC1] != ' ') {
643                                 MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
644                                 if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
645                                         record_use (cfg, var, bb, ins);
646                         }
647
648                         /* SREG2 */
649                         if (spec [MONO_INST_SRC2] != ' ') {
650                                 MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
651                                 if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
652                                         record_use (cfg, var, bb, ins);
653                         }
654                                 
655                         if (MONO_IS_STORE_MEMBASE (ins)) {
656                                 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
657                                 if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
658                                         record_use (cfg, var, bb, ins);
659                         }
660
661                         if (MONO_IS_PHI (ins)) {
662                                 for (i = ins->inst_phi_args [0]; i > 0; i--) {
663                                         g_assert (ins->inst_phi_args [i] != -1);
664                                         record_use (cfg,  get_vreg_to_inst (cfg, ins->inst_phi_args [i]), bb, ins);
665                                 }
666                         }
667
668                         /* DREG */
669                         if ((spec [MONO_INST_DEST] != ' ') && !MONO_IS_STORE_MEMBASE (ins)) {
670                                 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
671
672                                 if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
673                                         info = MONO_VARINFO (cfg, var->inst_c0);
674                                         info->def = ins;
675                                         info->def_bb = bb;
676                                 }
677                         }
678                 }
679         }
680
681         cfg->comp_done |= MONO_COMP_SSA_DEF_USE;
682 }
683
684 static void
685 mono_ssa_copyprop (MonoCompile *cfg)
686 {
687         int i, index;
688         GList *l;
689
690         g_assert ((cfg->comp_done & MONO_COMP_SSA_DEF_USE));
691
692         for (index = 0; index < cfg->num_varinfo; ++index) {
693                 MonoInst *var = cfg->varinfo [index];
694                 MonoMethodVar *info = MONO_VARINFO (cfg, index);
695
696                 if (info->def && (MONO_IS_MOVE (info->def))) {
697                         MonoInst *var2 = get_vreg_to_inst (cfg, info->def->sreg1);
698
699                         if (var2 && !(var2->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && MONO_VARINFO (cfg, var2->inst_c0)->def && (!MONO_IS_PHI (MONO_VARINFO (cfg, var2->inst_c0)->def))) {
700                                 /* Rewrite all uses of var to be uses of var2 */
701                                 int dreg = var->dreg;
702                                 int sreg1 = var2->dreg;
703                                 const char *spec;
704
705                                 l = info->uses;
706                                 while (l) {
707                                         MonoVarUsageInfo *u = (MonoVarUsageInfo*)l->data;
708                                         MonoInst *ins = u->inst;
709                                         GList *next = l->next;
710
711                                         spec = INS_INFO (ins->opcode);
712
713                                         if (spec [MONO_INST_SRC1] != ' ' && ins->sreg1 == dreg) {
714                                                 ins->sreg1 = sreg1;
715                                         } else if (spec [MONO_INST_SRC2] != ' ' && ins->sreg2 == dreg) {
716                                                 ins->sreg2 = sreg1;
717                                         } else if (MONO_IS_STORE_MEMBASE (ins) && ins->dreg == dreg) {
718                                                 ins->dreg = sreg1;
719                                         } else if (MONO_IS_PHI (ins)) {
720                                                 for (i = ins->inst_phi_args [0]; i > 0; i--) {
721                                                         int sreg = ins->inst_phi_args [i];
722                                                         if (sreg == var->dreg)
723                                                                 break;
724                                                 }
725                                                 g_assert (i > 0);
726                                                 ins->inst_phi_args [i] = sreg1;
727                                         }
728                                         else
729                                                 g_assert_not_reached ();
730
731                                         record_use (cfg, var2, u->bb, ins);
732
733                                         l = next;
734                                 }
735
736                                 info->uses = NULL;
737                         }
738                 }
739         }
740
741         if (cfg->verbose_level >= 4) {
742                 MonoBasicBlock *bb;
743
744                 for (bb = cfg->bb_entry; bb; bb = bb->next_bb)
745                         mono_print_bb (bb, "AFTER SSA COPYPROP");
746         }
747 }
748
749 static int
750 evaluate_ins (MonoCompile *cfg, MonoInst *ins, MonoInst **res, MonoInst **carray)
751 {
752         MonoInst *arg0, *arg1, *c0;
753         int r1, r2;
754         gboolean const_args = FALSE;
755         const char *spec = INS_INFO (ins->opcode);
756
757         /* Short-circuit this */
758         if (ins->opcode == OP_ICONST) {
759                 *res = ins;
760                 return 1;
761         }
762
763         if (ins->opcode == OP_NOP)
764                 return 2;
765
766         arg0 = NULL;
767         if (spec [MONO_INST_SRC1] != ' ') {
768                 MonoInst *var = get_vreg_to_inst (cfg, ins->sreg1);
769
770                 r1 = 2;
771                 arg0 = carray [ins->sreg1];
772                 if (arg0)
773                         r1 = 1;
774                 else if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
775                         r1 = MONO_VARINFO (cfg, var->inst_c0)->cpstate;
776         } else {
777                 r1 = 2;
778         }
779
780         arg1 = NULL;
781         if (spec [MONO_INST_SRC2] != ' ') {
782                 MonoInst *var = get_vreg_to_inst (cfg, ins->sreg2);
783
784                 r2 = 2;
785                 arg1 = carray [ins->sreg2];
786                 if (arg1)
787                         r2 = 1;
788                 else if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
789                         r2 = MONO_VARINFO (cfg, var->inst_c0)->cpstate;
790         }
791         else {
792                 r2 = 0;
793         }
794
795         c0 = NULL;
796         if ((spec [MONO_INST_SRC1] != ' ') && (spec [MONO_INST_SRC2] != ' ')) {
797                 /* Binop */
798                 const_args = (r1 == 1) && (r2 == 1);
799         }
800         else if (spec [MONO_INST_SRC1] != ' ') {
801                 /* Unop */
802                 const_args = (r1 == 1);
803         }
804
805         if (const_args) {
806                 if ((spec [MONO_INST_DEST] != ' ') && carray [ins->dreg]) {
807                         // Cached value
808                         *res = carray [ins->dreg];
809                         return 1;
810                 }
811                 c0 = mono_constant_fold_ins (cfg, ins, arg0, arg1, FALSE);
812                 if (c0) {
813                         if (G_UNLIKELY (cfg->verbose_level > 1)) {
814                                 printf ("\t cfold -> ");
815                                 mono_print_ins (c0);
816                         }
817                         *res = c0;
818                         return 1;
819                 }
820                 else
821                         /* Can't cfold this ins */
822                         return 2;
823         }
824
825         if ((spec [MONO_INST_SRC1] != ' ') && (spec [MONO_INST_SRC2] != ' ')) {
826                 /* Binop */
827                 if ((r1 == 2) || (r2 == 2))
828                         return 2;
829                 else
830                         return 0;
831         }
832         else if (spec [MONO_INST_SRC1] != ' ') {
833                 /* Unop */
834                 if (r1 == 2)
835                         return 2;
836                 else
837                         return 0;
838         }
839         else
840                 return 2;
841 }
842
843 static inline void
844 change_varstate (MonoCompile *cfg, GList **cvars, MonoMethodVar *info, int state, MonoInst *c0, MonoInst **carray)
845 {
846         if (info->cpstate >= state)
847                 return;
848
849         info->cpstate = state;
850
851         if (G_UNLIKELY (cfg->verbose_level > 1))
852                 printf ("\tState of R%d set to %d\n", cfg->varinfo [info->idx]->dreg, info->cpstate);
853
854         if (state == 1)
855                 g_assert (c0);
856
857         carray [cfg->varinfo [info->idx]->dreg] = c0;
858
859         if (!g_list_find (*cvars, info)) {
860                 *cvars = g_list_prepend (*cvars, info);
861         }
862 }
863
864 static inline void
865 add_cprop_bb (MonoCompile *cfg, MonoBasicBlock *bb, GList **bblist)
866 {
867         if (G_UNLIKELY (cfg->verbose_level > 1))
868                 printf ("\tAdd BB%d to worklist\n", bb->block_num);
869
870     if (!(bb->flags &  BB_REACHABLE)) {
871             bb->flags |= BB_REACHABLE;
872                 *bblist = g_list_prepend (*bblist, bb);
873         }
874 }
875
876 static void
877 visit_inst (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, GList **cvars, GList **bblist, MonoInst **carray)
878 {
879         const char *spec = INS_INFO (ins->opcode);
880
881         if (ins->opcode == OP_NOP)
882                 return;
883
884         if (cfg->verbose_level > 1)
885                 mono_print_ins (ins);
886
887         /* FIXME: Support longs/floats */
888         /* FIXME: Work on vregs as well */
889
890         if (MONO_IS_PHI (ins)) {
891                 MonoMethodVar *info = MONO_VARINFO (cfg, get_vreg_to_inst (cfg, ins->dreg)->inst_c0);
892                 MonoInst *c0 = NULL;
893                 int j;
894
895                 for (j = 1; j <= ins->inst_phi_args [0]; j++) {
896                         MonoInst *var = get_vreg_to_inst (cfg, ins->inst_phi_args [j]);
897                         MonoMethodVar *mv = MONO_VARINFO (cfg, var->inst_c0);
898                         MonoInst *src = mv->def;
899
900                         if (mv->def_bb && !(mv->def_bb->flags & BB_REACHABLE))
901                                 continue;
902
903                         if (!mv->def || !src || mv->cpstate == 2) {
904                                 change_varstate (cfg, cvars, info, 2, NULL, carray);
905                                 break;
906                         }
907                                         
908                         if (mv->cpstate == 0)
909                                 continue;
910
911                         g_assert (carray [var->dreg]);
912
913                         if (!c0)
914                                 c0 = carray [var->dreg];
915                 
916                         /* FIXME: */
917                         if (c0->opcode != OP_ICONST) {
918                                 change_varstate (cfg, cvars, info, 2, NULL, carray);
919                                 break;
920                         }
921
922                         if (carray [var->dreg]->inst_c0 != c0->inst_c0) {
923                                 change_varstate (cfg, cvars, info, 2, NULL, carray);
924                                 break;
925                         }
926                 }
927                                 
928                 if (c0 && info->cpstate < 1) {
929                         change_varstate (cfg, cvars, info, 1, c0, carray);
930
931                         g_assert (c0->opcode == OP_ICONST);
932                 }
933         }
934         else if (!MONO_IS_STORE_MEMBASE (ins) && ((spec [MONO_INST_SRC1] != ' ') || (spec [MONO_INST_SRC2] != ' ') || (spec [MONO_INST_DEST] != ' '))) {
935                 MonoInst *var, *c0;
936                 int state;
937
938                 if (spec [MONO_INST_DEST] !=  ' ')
939                         var = get_vreg_to_inst (cfg, ins->dreg);
940                 else
941                         var = NULL;
942
943                 c0 = NULL;
944                 state = evaluate_ins (cfg, ins, &c0, carray);
945
946                 if (var && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
947                         MonoMethodVar *info = MONO_VARINFO (cfg, var->inst_c0);
948
949                         if (info->cpstate < 2) {
950                                 if (state == 1)
951                                         change_varstate (cfg, cvars, info, 1, c0, carray);
952                                 else if (state == 2)
953                                         change_varstate (cfg, cvars, info, 2, NULL, carray);
954                         }
955                 }
956                 else if (!var && (ins->dreg != -1)) {
957                         /*
958                          * We don't record def-use information for local vregs since it would be 
959                          * expensive. Instead, we depend on the fact that all uses of the vreg are in
960                          * the same bblock, so they will be examined after the definition.
961                          * FIXME: This isn't true if the ins is visited through an SSA edge.
962                          */
963                         if (c0) {
964                                 carray [ins->dreg] = c0;
965                         } else {
966                                 if (carray [ins->dreg]) {
967                                         /* 
968                                          * The state of the vreg changed from constant to non-constant
969                                          * -> need to rescan the whole bblock.
970                                          */
971                                         carray [ins->dreg] = NULL;
972                                         /* FIXME: Speed this up */
973
974                                         if (!g_list_find (*bblist, bb))
975                                                 *bblist = g_list_prepend (*bblist, bb);
976                                 }
977                         }
978                 }
979
980                 if (ins->opcode == OP_JUMP_TABLE) {
981                         int i;
982                         MonoJumpInfoBBTable *table = ins->inst_p0;
983
984                         if (ins->next->opcode != OP_PADD) {
985                                 /* The PADD was optimized away */
986                                 /* FIXME: handle this as well */
987                                 for (i = 0; i < table->table_size; i++)
988                                         if (table->table [i])
989                                                 add_cprop_bb (cfg, table->table [i], bblist);
990                                 return;
991                         }
992
993                         g_assert (ins->next->opcode == OP_PADD);
994                         g_assert (ins->next->sreg1 == ins->dreg);
995
996                         if (carray [ins->next->sreg2]) {
997 #if SIZEOF_VOID_P == 8
998                                 int idx = carray [ins->next->sreg2]->inst_c0 >> 3;
999 #else
1000                                 int idx = carray [ins->next->sreg2]->inst_c0 >> 2;
1001 #endif
1002                                 if ((idx < 0) || (idx >= table->table_size))
1003                                         /* Out-of-range, no branch is executed */
1004                                         return;
1005                                 else
1006                                         if (table->table [idx])
1007                                                 add_cprop_bb (cfg, table->table [idx], bblist);
1008                         }
1009                         else {
1010                                 for (i = 0; i < table->table_size; i++)
1011                                         if (table->table [i])
1012                                                 add_cprop_bb (cfg, table->table [i], bblist);
1013                         }
1014                 }
1015
1016                 if (ins->opcode == OP_SWITCH) {
1017                         int i;
1018                         MonoJumpInfoBBTable *table = ins->inst_p0;
1019
1020                         for (i = 0; i < table->table_size; i++)
1021                                 if (table->table [i])
1022                                         add_cprop_bb (cfg, table->table [i], bblist);
1023                 }
1024
1025                 /* Handle COMPARE+BRCOND pairs */
1026                 if (ins->next && MONO_IS_COND_BRANCH_OP (ins->next)) {
1027                         if (c0) {
1028                                 g_assert (c0->opcode == OP_ICONST);
1029
1030                                 if (c0->inst_c0)
1031                                         ins->next->flags |= MONO_INST_CFOLD_TAKEN;
1032                                 else
1033                                         ins->next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
1034                         }
1035                         else {
1036                                 ins->next->flags &= ~(MONO_INST_CFOLD_TAKEN | MONO_INST_CFOLD_NOT_TAKEN);
1037                         }
1038
1039                         visit_inst (cfg, bb, ins->next, cvars, bblist, carray);
1040                 }
1041         } else if (ins->opcode == OP_BR) {
1042                 add_cprop_bb (cfg, ins->inst_target_bb, bblist);
1043         } else if (MONO_IS_COND_BRANCH_OP (ins)) {
1044                 if (ins->flags & MONO_INST_CFOLD_TAKEN) {
1045                         add_cprop_bb (cfg, ins->inst_true_bb, bblist);
1046                 } else if (ins->flags & MONO_INST_CFOLD_NOT_TAKEN) {
1047                         if (ins->inst_false_bb)
1048                                 add_cprop_bb (cfg, ins->inst_false_bb, bblist);
1049         } else {
1050                         add_cprop_bb (cfg, ins->inst_true_bb, bblist);
1051                         if (ins->inst_false_bb)
1052                                 add_cprop_bb (cfg, ins->inst_false_bb, bblist);
1053                 }
1054         }
1055 }
1056
1057 /**
1058  * fold_ins:
1059  *
1060  *   Replace INS with its constant value, if it exists
1061  */
1062 static void
1063 fold_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, MonoInst **carray)
1064 {
1065         const char *spec = INS_INFO (ins->opcode);
1066         int opcode2;
1067
1068         if ((ins->opcode != OP_NOP) && (ins->dreg != -1) && !MONO_IS_STORE_MEMBASE (ins)) {
1069                 if (carray [ins->dreg] && (spec [MONO_INST_DEST] == 'i') && (ins->dreg >= MONO_MAX_IREGS)) {
1070                         /* Perform constant folding */
1071                         /* FIXME: */
1072                         g_assert (carray [ins->dreg]->opcode == OP_ICONST);
1073                         ins->opcode = OP_ICONST;
1074                         ins->inst_c0 = carray [ins->dreg]->inst_c0;
1075                         ins->sreg1 = ins->sreg2 = -1;
1076                 }
1077                 else if ((spec [MONO_INST_SRC2] != ' ') && carray [ins->sreg2]) {
1078                         /* Perform op->op_imm conversion */
1079                         opcode2 = mono_op_to_op_imm (ins->opcode);
1080                         if (opcode2 != -1) {
1081                                 ins->opcode = opcode2;
1082                                 ins->inst_imm = carray [ins->sreg2]->inst_c0;
1083                                 ins->sreg2 = -1;
1084
1085                                 if ((opcode2 == OP_VOIDCALL) || (opcode2 == OP_CALL) || (opcode2 == OP_LCALL) || (opcode2 == OP_FCALL))
1086                                         ((MonoCallInst*)ins)->fptr = (gpointer)ins->inst_imm;
1087                         }
1088                 }
1089
1090                 if (ins->opcode == OP_JUMP_TABLE) {
1091                         int i;
1092                         MonoJumpInfoBBTable *table = ins->inst_p0;
1093
1094                         if (ins->next->opcode != OP_PADD) {
1095                                 /* The PADD was optimized away */
1096                                 /* FIXME: handle this as well */
1097                                 return;
1098                         }
1099
1100                         g_assert (ins->next->opcode == OP_PADD);
1101                         g_assert (ins->next->sreg1 == ins->dreg);
1102                         g_assert (ins->next->next->opcode == OP_LOAD_MEMBASE);
1103
1104                         if (carray [ins->next->sreg2]) {
1105                                 /* Convert to a simple branch */
1106 #if SIZEOF_VOID_P == 8
1107                                 int idx = carray [ins->next->sreg2]->inst_c0 >> 3;
1108 #else
1109                                 int idx = carray [ins->next->sreg2]->inst_c0 >> 2;
1110 #endif
1111
1112                                 if (!((idx >= 0) && (idx < table->table_size))) {
1113                                         /* Out of range, eliminate the whole switch */
1114                                         for (i = 0; i < table->table_size; ++i) {
1115                                                 remove_bb_from_phis (cfg, bb, table->table [i]);
1116                                                 mono_unlink_bblock (cfg, bb, table->table [i]);
1117                                         }
1118
1119                                         NULLIFY_INS (ins);
1120                                         NULLIFY_INS (ins->next);
1121                                         NULLIFY_INS (ins->next->next);
1122                                         NULLIFY_INS (ins->next->next->next);
1123
1124                                         return;
1125                                 }
1126
1127                                 if (ins->next->next->next->opcode != OP_BR_REG) {
1128                                         /* A one-way switch which got optimized away */
1129                                         if (G_UNLIKELY (cfg->verbose_level > 1)) {
1130                                                 printf ("\tNo cfold on ");
1131                                                 mono_print_ins (ins);
1132                                         }
1133                                         return;
1134                                 }
1135
1136                                 if (G_UNLIKELY (cfg->verbose_level > 1)) {
1137                                         printf ("\tcfold on ");
1138                                         mono_print_ins (ins);
1139                                 }
1140
1141                                 /* Unlink target bblocks */
1142                                 for (i = 0; i < table->table_size; ++i) {
1143                                         if (i != idx) {
1144                                                 remove_bb_from_phis (cfg, bb, table->table [i]);
1145                                                 mono_unlink_bblock (cfg, bb, table->table [i]);
1146                                         }
1147                                 }
1148
1149                                 /* Change the OP_BR_REG to a simple branch */
1150                                 ins->next->next->next->opcode = OP_BR;
1151                                 ins->next->next->next->inst_target_bb = table->table [idx];
1152                                 ins->next->next->next->sreg1 = -1;
1153
1154                                 /* Nullify the other instructions */
1155                                 NULLIFY_INS (ins);
1156                                 NULLIFY_INS (ins->next);
1157                                 NULLIFY_INS (ins->next->next);
1158                         }
1159                 }
1160         } 
1161         else if (MONO_IS_COND_BRANCH_OP (ins)) {
1162                 if (ins->flags & MONO_INST_CFOLD_TAKEN) {
1163                         remove_bb_from_phis (cfg, bb, ins->inst_false_bb);
1164                         mono_unlink_bblock (cfg, bb, ins->inst_false_bb);
1165                         ins->opcode = OP_BR;
1166                         ins->inst_target_bb = ins->inst_true_bb;
1167                 } else if (ins->flags & MONO_INST_CFOLD_NOT_TAKEN) {
1168                         remove_bb_from_phis (cfg, bb, ins->inst_true_bb);
1169                         mono_unlink_bblock (cfg, bb, ins->inst_true_bb);
1170                         ins->opcode = OP_BR;
1171                         ins->inst_target_bb = ins->inst_false_bb;
1172                 }
1173         }
1174 }
1175
1176 void
1177 mono_ssa_cprop (MonoCompile *cfg) 
1178 {
1179         MonoInst **carray;
1180         MonoBasicBlock *bb;
1181         GList *bblock_list, *cvars;
1182         GList *tmp;
1183         int i;
1184         //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
1185
1186         carray = g_new0 (MonoInst*, cfg->next_vreg);
1187
1188         if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
1189                 mono_ssa_create_def_use (cfg);
1190
1191         bblock_list = g_list_prepend (NULL, cfg->bb_entry);
1192         cfg->bb_entry->flags |= BB_REACHABLE;
1193
1194         memset (carray, 0, sizeof (MonoInst *) * cfg->num_varinfo);
1195
1196         for (i = 0; i < cfg->num_varinfo; i++) {
1197                 MonoMethodVar *info = MONO_VARINFO (cfg, i);
1198                 if (!info->def)
1199                         info->cpstate = 2;
1200         }
1201
1202         cvars = NULL;
1203
1204         while (bblock_list) {
1205                 MonoInst *inst;
1206
1207                 bb = (MonoBasicBlock *)bblock_list->data;
1208
1209                 bblock_list = g_list_delete_link (bblock_list, bblock_list);
1210
1211                 g_assert (bb->flags &  BB_REACHABLE);
1212
1213                 if (bb->out_count == 1) {
1214                         if (!(bb->out_bb [0]->flags &  BB_REACHABLE)) {
1215                                 bb->out_bb [0]->flags |= BB_REACHABLE;
1216                                 bblock_list = g_list_prepend (bblock_list, bb->out_bb [0]);
1217                         }
1218                 }
1219
1220                 if (cfg->verbose_level > 1)
1221                         printf ("\nSSA CONSPROP BB%d:\n", bb->block_num);
1222
1223                 for (inst = bb->code; inst; inst = inst->next) {
1224                         visit_inst (cfg, bb, inst, &cvars, &bblock_list, carray);
1225                 }
1226
1227                 while (cvars) {
1228                         MonoMethodVar *info = (MonoMethodVar *)cvars->data;                     
1229                         cvars = g_list_delete_link (cvars, cvars);
1230
1231                         for (tmp = info->uses; tmp; tmp = tmp->next) {
1232                                 MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
1233                                 if (!(ui->bb->flags & BB_REACHABLE))
1234                                         continue;
1235                                 visit_inst (cfg, ui->bb, ui->inst, &cvars, &bblock_list, carray);
1236                         }
1237                 }
1238         }
1239
1240         for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
1241                 MonoInst *inst;
1242                 for (inst = bb->code; inst; inst = inst->next) {
1243                         fold_ins (cfg, bb, inst, carray);
1244                 }
1245         }
1246
1247         g_free (carray);
1248
1249         cfg->comp_done |= MONO_COMP_REACHABILITY;
1250
1251         /* fixme: we should update usage infos during cprop, instead of computing it again */
1252         cfg->comp_done &=  ~MONO_COMP_SSA_DEF_USE;
1253         for (i = 0; i < cfg->num_varinfo; i++) {
1254                 MonoMethodVar *info = MONO_VARINFO (cfg, i);
1255                 info->def = NULL;
1256                 info->uses = NULL;
1257         }
1258 }
1259
1260 static inline void
1261 add_to_dce_worklist (MonoCompile *cfg, MonoMethodVar *var, MonoMethodVar *use, GList **wl)
1262 {
1263         GList *tmp;
1264
1265         *wl = g_list_prepend_mempool (cfg->mempool, *wl, use);
1266
1267         for (tmp = use->uses; tmp; tmp = tmp->next) {
1268                 MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
1269                 if (ui->inst == var->def) {
1270                         /* from the mempool */
1271                         use->uses = g_list_remove_link (use->uses, tmp);
1272                         break;
1273                 }
1274         }       
1275 }
1276
1277 void
1278 mono_ssa_deadce (MonoCompile *cfg) 
1279 {
1280         int i;
1281         GList *work_list;
1282
1283         g_assert (cfg->comp_done & MONO_COMP_SSA);
1284
1285         //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE));
1286
1287         if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
1288                 mono_ssa_create_def_use (cfg);
1289
1290         mono_ssa_copyprop (cfg);
1291
1292         work_list = NULL;
1293         for (i = 0; i < cfg->num_varinfo; i++) {
1294                 MonoMethodVar *info = MONO_VARINFO (cfg, i);
1295                 work_list = g_list_prepend_mempool (cfg->mempool, work_list, info);
1296         }
1297
1298         while (work_list) {
1299                 MonoMethodVar *info = (MonoMethodVar *)work_list->data;
1300                 work_list = g_list_remove_link (work_list, work_list);
1301
1302                 /* 
1303                  * The second part of the condition happens often when PHI nodes have their dreg
1304                  * as one of their arguments due to the fact that we use the original vars.
1305                  */
1306                 if (info->def && (!info->uses || ((info->uses->next == NULL) && (((MonoVarUsageInfo*)info->uses->data)->inst == info->def)))) {
1307                         MonoInst *def = info->def;
1308
1309                         /* Eliminating FMOVE could screw up the fp stack */
1310                         if (MONO_IS_MOVE (def) && (!MONO_ARCH_USE_FPSTACK || (def->opcode != OP_FMOVE))) {
1311                                 MonoInst *src_var = get_vreg_to_inst (cfg, def->sreg1);
1312                                 if (src_var && !(src_var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
1313                                         add_to_dce_worklist (cfg, info, MONO_VARINFO (cfg, src_var->inst_c0), &work_list);
1314                                 def->opcode = OP_NOP;
1315                                 def->dreg = def->sreg1 = def->sreg2 = -1;
1316                                 info->reg = -1;
1317                         } else if ((def->opcode == OP_ICONST) || (def->opcode == OP_I8CONST) || MONO_IS_ZERO (def)) {
1318                                 def->opcode = OP_NOP;
1319                                 def->dreg = def->sreg1 = def->sreg2 = -1;
1320                                 info->reg = -1;
1321                         } else if (MONO_IS_PHI (def)) {
1322                                 int j;
1323                                 for (j = def->inst_phi_args [0]; j > 0; j--) {
1324                                         MonoMethodVar *u = MONO_VARINFO (cfg, get_vreg_to_inst (cfg, def->inst_phi_args [j])->inst_c0);
1325                                         add_to_dce_worklist (cfg, info, u, &work_list);
1326                                 }
1327                                 def->opcode = OP_NOP;
1328                                 def->dreg = def->sreg1 = def->sreg2 = -1;
1329                                 info->reg = -1;
1330                         }
1331                         else if (def->opcode == OP_NOP) {
1332                         }
1333                         //else
1334                         //mono_print_ins (def);
1335                 }
1336
1337         }
1338 }
1339
1340 #if 0
1341 void
1342 mono_ssa_strength_reduction (MonoCompile *cfg)
1343 {
1344         MonoBasicBlock *bb;
1345         int i;
1346
1347         g_assert (cfg->comp_done & MONO_COMP_SSA);
1348         g_assert (cfg->comp_done & MONO_COMP_LOOPS);
1349         g_assert (cfg->comp_done & MONO_COMP_SSA_DEF_USE);
1350
1351         for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
1352                 GList *lp = bb->loop_blocks;
1353
1354                 if (lp) {
1355                         MonoBasicBlock *h = (MonoBasicBlock *)lp->data;
1356
1357                         /* we only consider loops with 2 in bblocks */
1358                         if (!h->in_count == 2)
1359                                 continue;
1360
1361                         for (i = 0; i < cfg->num_varinfo; i++) {
1362                                 MonoMethodVar *info = MONO_VARINFO (cfg, i);
1363                         
1364                                 if (info->def && info->def->ssa_op == MONO_SSA_STORE &&
1365                                     info->def->inst_i0->opcode == OP_LOCAL && g_list_find (lp, info->def_bb)) {
1366                                         MonoInst *v = info->def->inst_i1;
1367
1368
1369                                         printf ("FOUND %d in %s\n", info->idx, mono_method_full_name (cfg->method, TRUE));
1370                                 }
1371                         }
1372                 }
1373         }
1374 }
1375 #endif
1376
1377 #endif /* DISABLE_JIT */