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