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