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