This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mono / mini / ssa.c
1 /*
2  * ssa.c: Static single assign form support for the JIT compiler.
3  *
4  * Author:
5  *    Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9 #include <string.h>
10 #include <mono/metadata/debug-helpers.h>
11
12 #include "mini.h"
13
14 extern guint8 mono_burg_arity [];
15
16 #define USE_ORIGINAL_VARS
17 #define CREATE_PRUNED_SSA
18
19 //#define DEBUG_SSA 1
20
21 #define NEW_PHI(cfg,dest,val) do {      \
22                 (dest) = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));       \
23                 (dest)->opcode = OP_PHI;        \
24                 (dest)->inst_c0 = (val);        \
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         } while (0)
33
34
35 static void 
36 unlink_target (MonoBasicBlock *bb, MonoBasicBlock *target)
37 {
38         int i;
39
40         for (i = 0; i < bb->out_count; i++) {
41                 if (bb->out_bb [i] == target) {
42                         bb->out_bb [i] = bb->out_bb [--bb->out_count];
43                         break;
44                 }
45         }
46         for (i = 0; i < target->in_count; i++) {
47                 if (target->in_bb [i] == bb) {
48                         target->in_bb [i] = target->in_bb [--target->in_count];
49                         break;
50                         
51                 }
52         }
53 }
54
55 static void
56 unlink_unused_bblocks (MonoCompile *cfg) 
57 {
58         int i, j;
59         MonoBasicBlock *bb;
60
61         g_assert (cfg->comp_done & MONO_COMP_REACHABILITY);
62
63         for (bb = cfg->bb_entry; bb && bb->next_bb;) {
64                 if (!(bb->next_bb->flags & BB_REACHABLE)) {
65                         bb->next_bb = bb->next_bb->next_bb;
66                 } else 
67                         bb = bb->next_bb;
68         }
69
70         for (i = 1; i < cfg->num_bblocks; i++) {
71                 bb = cfg->bblocks [i];
72                
73                 if (!(bb->flags & BB_REACHABLE)) {
74                         for (j = 0; j < bb->in_count; j++) {
75                                 unlink_target (bb->in_bb [j], bb);      
76                         }
77                         for (j = 0; j < bb->out_count; j++) {
78                                 unlink_target (bb, bb->out_bb [j]);     
79                         }
80                 }
81  
82         }
83 }
84
85
86
87 static void
88 replace_usage (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, MonoInst **stack)
89 {
90         int arity;
91
92         if (!inst)
93                 return;
94
95         arity = mono_burg_arity [inst->opcode];
96
97         if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) && 
98             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
99                 MonoInst *new_var;
100                 int idx = inst->inst_i0->inst_c0;
101                         
102                 if (stack [idx]) {
103                         new_var = stack [idx];
104                 } else {
105                         new_var = cfg->varinfo [idx];
106
107                         if ((new_var->opcode != OP_ARG) && (new_var->opcode != OP_LOCAL)) {
108                                 /* uninitialized variable ? */
109                                 g_warning ("using uninitialized variables %d in BB%d (%s)", idx, bb->block_num,
110                                            mono_method_full_name (cfg->method, TRUE));
111                                 //g_assert_not_reached ();
112                         }
113                 }
114 #ifdef DEBUG_SSA
115                 printf ("REPLACE BB%d %d %d\n", bb->block_num, idx, new_var->inst_c0);
116 #endif
117                 inst->inst_i0 = new_var;
118         } else {
119
120                 if (arity) {
121                         if (inst->ssa_op != MONO_SSA_STORE)
122                                 replace_usage (cfg, bb, inst->inst_left, stack);
123                         if (arity > 1)
124                                 replace_usage (cfg, bb, inst->inst_right, stack);
125                 }
126         }
127 }
128
129 static int
130 extends_live (MonoInst *inst)
131 {
132         int arity;
133
134         if (!inst)
135                 return 0;
136
137         arity = mono_burg_arity [inst->opcode];
138
139         if (inst->ssa_op == MONO_SSA_LOAD && 
140             (inst->inst_i0->opcode == OP_LOCAL /*|| inst->inst_i0->opcode == OP_ARG*/)) {
141                 return 1;
142         } else {
143                 if (arity) {
144                         if (inst->ssa_op != MONO_SSA_STORE)
145                                 if (extends_live (inst->inst_left))
146                                         return 1;
147                         if (arity > 1)
148                                 if (extends_live (inst->inst_right))
149                                         return 1;
150                 }
151         }
152
153         return 0;
154 }
155
156 static int
157 replace_usage_new (MonoCompile *cfg, MonoInst *inst, int varnum, MonoInst *rep)
158 {
159         int arity;
160
161         if (!inst)
162                 return 0;
163
164         arity = mono_burg_arity [inst->opcode];
165
166         if ((inst->ssa_op == MONO_SSA_LOAD) && 
167             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
168             inst->inst_i0->inst_c0 == varnum && rep->type == inst->type) {
169                 *inst = *rep;
170                 return 1;
171         } else {
172                 if (arity) {
173                         if (inst->ssa_op != MONO_SSA_STORE)
174                                 if (replace_usage_new (cfg, inst->inst_left, varnum, rep))
175                                         return 1;
176                         if (arity > 1)
177                                 if (replace_usage_new (cfg, inst->inst_right, varnum, rep))
178                                         return 1;
179                 }
180         }
181
182         return 0;
183 }
184
185 static void
186 mono_ssa_rename_vars (MonoCompile *cfg, int max_vars, MonoBasicBlock *bb, MonoInst **stack) 
187 {
188         MonoInst *inst, *new_var;
189         int i, j, idx;
190         GList *tmp;
191         MonoInst **new_stack;
192
193 #ifdef DEBUG_SSA
194         printf ("RENAME VARS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
195 #endif
196
197         for (inst = bb->code; inst; inst = inst->next) {
198                 if (inst->opcode != OP_PHI)
199                         replace_usage (cfg, bb, inst, stack);
200
201                 if (inst->ssa_op == MONO_SSA_STORE && 
202                     (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
203                         idx = inst->inst_i0->inst_c0;
204                         g_assert (idx < max_vars);
205
206                         if (!stack [idx] && bb == cfg->bb_init) {
207                                 new_var = cfg->varinfo [idx];
208                         } else {
209                                 new_var = mono_compile_create_var (cfg, inst->inst_i0->inst_vtype,  inst->inst_i0->opcode);
210                                 new_var->flags = inst->inst_i0->flags;
211                         }
212 #ifdef DEBUG_SSA
213                         printf ("DEF %d %d\n", idx, new_var->inst_c0);
214 #endif
215                         inst->inst_i0 = new_var;
216
217 #ifdef USE_ORIGINAL_VARS
218                         cfg->vars [new_var->inst_c0]->reg = idx;
219 #endif
220
221                         stack [idx] = new_var;
222                 }
223         }
224
225         for (i = 0; i < bb->out_count; i++) {
226                 MonoBasicBlock *n = bb->out_bb [i];
227
228                 for (j = 0; j < n->in_count; j++)
229                         if (n->in_bb [j] == bb)
230                                 break;
231                 
232                 for (inst = n->code; inst; inst = inst->next) {
233                         if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->opcode == OP_PHI) {
234                                 idx = inst->inst_i1->inst_c0;
235                                 if (stack [idx])
236                                         new_var = stack [idx];
237                                 else
238                                         new_var = cfg->varinfo [idx];
239 #ifdef DEBUG_SSA
240                                 printf ("FOUND PHI %d (%d, %d)\n", idx, j, new_var->inst_c0);
241 #endif
242                                 inst->inst_i1->inst_phi_args [j + 1] = new_var->inst_c0;
243                                 
244                         }
245                 }
246         }
247
248         if (bb->dominated) {
249                 new_stack = g_new (MonoInst*, max_vars);
250                 for (tmp = bb->dominated; tmp; tmp = tmp->next) {
251                         memcpy (new_stack, stack, sizeof (MonoInst *) * max_vars); 
252                         mono_ssa_rename_vars (cfg, max_vars, (MonoBasicBlock *)tmp->data, new_stack);
253                 }
254                 g_free (new_stack);
255         }
256 }
257
258 void
259 mono_ssa_compute (MonoCompile *cfg)
260 {
261         int i, idx;
262         MonoBitSet *set;
263         MonoMethodVar *vinfo = g_new0 (MonoMethodVar, cfg->num_varinfo);
264         MonoInst *inst, *store, **stack;
265
266         g_assert (!(cfg->comp_done & MONO_COMP_SSA));
267
268         /* we dont support methods containing exception clauses */
269         g_assert (((MonoMethodNormal *)cfg->method)->header->num_clauses == 0);
270         g_assert (!cfg->disable_ssa);
271
272         //printf ("COMPUTS SSA %s %d\n", mono_method_full_name (cfg->method, TRUE), cfg->num_varinfo);
273
274 #ifdef CREATE_PRUNED_SSA
275         /* we need liveness for pruned SSA */
276         if (!(cfg->comp_done & MONO_COMP_LIVENESS))
277                 mono_analyze_liveness (cfg);
278 #endif
279
280         mono_compile_dominator_info (cfg, MONO_COMP_DOM | MONO_COMP_IDOM | MONO_COMP_DFRONTIER);
281
282         for (i = 0; i < cfg->num_varinfo; ++i) {
283                 vinfo [i].def_in = mono_bitset_new (cfg->num_bblocks, 0);
284                 vinfo [i].idx = i;
285                 /* implizit reference at start */
286                 mono_bitset_set (vinfo [i].def_in, 0);
287         }
288         for (i = 0; i < cfg->num_bblocks; ++i) {
289                 for (inst = cfg->bblocks [i]->code; inst; inst = inst->next) {
290                         if (inst->ssa_op == MONO_SSA_STORE) {
291                                 idx = inst->inst_i0->inst_c0;
292                                 g_assert (idx < cfg->num_varinfo);
293                                 mono_bitset_set (vinfo [idx].def_in, i);
294                         } 
295                 }
296         }
297
298         /* insert phi functions */
299         for (i = 0; i < cfg->num_varinfo; ++i) {
300                 set = mono_compile_iterated_dfrontier (cfg, vinfo [i].def_in);
301                 vinfo [i].dfrontier = set;
302                 mono_bitset_foreach_bit (set, idx, cfg->num_bblocks) {
303                         MonoBasicBlock *bb = cfg->bblocks [idx];
304
305                         /* fixme: create pruned SSA? we would need liveness information for that */
306
307                         if (bb == cfg->bb_exit)
308                                 continue;
309
310                         if ((cfg->comp_done & MONO_COMP_LIVENESS) && !mono_bitset_test_fast (bb->live_in_set, i)) {
311                                 //printf ("%d is not live in BB%d %s\n", i, bb->block_num, mono_method_full_name (cfg->method, TRUE));
312                                 continue;
313                         }
314
315                         NEW_PHI (cfg, inst, i);
316
317                         inst->inst_phi_args =  mono_mempool_alloc0 (cfg->mempool, sizeof (int) * (cfg->bblocks [idx]->in_count + 1));
318                         inst->inst_phi_args [0] = cfg->bblocks [idx]->in_count;
319
320                         store = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoInst));
321                         if (!cfg->varinfo [i]->inst_vtype->type)
322                                 g_assert_not_reached ();
323                         store->opcode = mono_type_to_stind (cfg->varinfo [i]->inst_vtype);
324                         store->ssa_op = MONO_SSA_STORE;
325                         store->inst_i0 = cfg->varinfo [i];
326                         store->inst_i1 = inst;
327                         store->klass = store->inst_i0->klass;
328              
329                         store->next = bb->code;
330                         bb->code = store;
331
332 #ifdef DEBUG_SSA
333                         printf ("ADD PHI BB%d %s\n", cfg->bblocks [idx]->block_num, mono_method_full_name (cfg->method, TRUE));
334 #endif
335                 }
336         }
337
338         /* free the stuff */
339         for (i = 0; i < cfg->num_varinfo; ++i)
340                 mono_bitset_free (vinfo [i].def_in);
341         g_free (vinfo);
342
343
344         stack = alloca (sizeof (MonoInst *) * cfg->num_varinfo);
345                 
346         for (i = 0; i < cfg->num_varinfo; i++)
347                 stack [i] = NULL;
348
349         mono_ssa_rename_vars (cfg, cfg->num_varinfo, cfg->bb_entry, stack);
350
351         cfg->comp_done |= MONO_COMP_SSA;
352 }
353
354 #ifndef USE_ORIGINAL_VARS
355 static GPtrArray *
356 mono_ssa_get_allocatable_vars (MonoCompile *cfg)
357 {
358         GHashTable *type_hash;
359         GPtrArray *varlist_array = g_ptr_array_new ();
360         int tidx, i;
361
362         g_assert (cfg->comp_done & MONO_COMP_LIVENESS);
363
364         type_hash = g_hash_table_new (NULL, NULL);
365
366         for (i = 0; i < cfg->num_varinfo; i++) {
367                 MonoInst *ins = cfg->varinfo [i];
368                 MonoMethodVar *vmv = MONO_VARINFO (cfg, i);
369
370                 /* unused vars */
371                 if (vmv->range.first_use.abs_pos > vmv->range.last_use.abs_pos)
372                         continue;
373
374                 if (ins->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT) || 
375                     (ins->opcode != OP_LOCAL && ins->opcode != OP_ARG) || vmv->reg != -1)
376                         continue;
377
378                 g_assert (ins->inst_vtype);
379                 g_assert (vmv->reg == -1);
380                 g_assert (i == vmv->idx);
381
382                 if (!(tidx = (int)g_hash_table_lookup (type_hash, ins->inst_vtype))) {
383                         GList *vars = g_list_append (NULL, vmv);
384                         g_ptr_array_add (varlist_array, vars);
385                         g_hash_table_insert (type_hash, ins->inst_vtype, (gpointer)varlist_array->len);
386                 } else {
387                         tidx--;
388                         g_ptr_array_index (varlist_array, tidx) =
389                                 mono_varlist_insert_sorted (cfg, g_ptr_array_index (varlist_array, tidx), vmv, FALSE);
390                 }
391         }
392
393         g_hash_table_destroy (type_hash);
394
395         return varlist_array;
396 }
397 #endif
398
399 static void
400 mono_ssa_replace_copies (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, char *is_live)
401 {
402         int arity;
403
404         if (!inst)
405                 return;
406
407         arity = mono_burg_arity [inst->opcode];
408
409         if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD || inst->ssa_op == MONO_SSA_STORE) && 
410             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
411                 MonoInst *new_var;
412                 int idx = inst->inst_i0->inst_c0;
413                 MonoMethodVar *mv = cfg->vars [idx];
414
415                 if (mv->reg != -1 && mv->reg != mv->idx) {
416                        
417                         is_live [mv->reg] = 1;
418
419                         new_var = cfg->varinfo [mv->reg];
420
421 #if 0
422                         printf ("REPLACE COPY BB%d %d %d\n", bb->block_num, idx, new_var->inst_c0);
423                         g_assert (cfg->varinfo [mv->reg]->inst_vtype == cfg->varinfo [idx]->inst_vtype);
424 #endif
425                         inst->inst_i0 = new_var;
426                 } else {
427                         is_live [mv->idx] = 1;
428                 }
429         }
430
431
432         if (arity) {
433                 mono_ssa_replace_copies (cfg, bb, inst->inst_left, is_live);
434                 if (arity > 1)
435                         mono_ssa_replace_copies (cfg, bb, inst->inst_right, is_live);
436         }
437
438         if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->ssa_op == MONO_SSA_LOAD &&
439             inst->inst_i0->inst_c0 == inst->inst_i1->inst_i0->inst_c0) {
440                 inst->ssa_op = MONO_SSA_NOP;
441                 inst->opcode = CEE_NOP;
442         }
443
444 }
445
446 void
447 mono_ssa_remove (MonoCompile *cfg)
448 {
449         MonoInst *inst, *phi;
450         char *is_live;
451         int i, j;
452 #ifndef USE_ORIGINAL_VARS
453         GPtrArray *varlist_array;
454         GList *active;
455 #endif
456         g_assert (cfg->comp_done & MONO_COMP_SSA);
457
458         for (i = 0; i < cfg->num_bblocks; ++i) {
459                 MonoBasicBlock *bb = cfg->bblocks [i];
460                 for (inst = bb->code; inst; inst = inst->next) {
461                         if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i1->opcode == OP_PHI) {
462                                 
463                                 phi = inst->inst_i1;
464                                 g_assert (phi->inst_phi_args [0] == bb->in_count);
465
466                                 for (j = 0; j < bb->in_count; j++) {
467                                         MonoBasicBlock *pred = bb->in_bb [j];
468                                         int idx = phi->inst_phi_args [j + 1];
469                                         MonoMethodVar *mv = cfg->vars [idx];
470
471                                         if (mv->reg != -1 && mv->reg != mv->idx) {
472                                                 //printf ("PHICOPY %d %d -> %d\n", idx, mv->reg, inst->inst_i0->inst_c0);
473                                                 idx = mv->reg;
474                                         }
475
476                                         
477                                         if (idx != inst->inst_i0->inst_c0) {
478 #ifdef DEBUG_SSA
479                                                 printf ("MOVE %d to %d in BB%d\n", idx, inst->inst_i0->inst_c0, pred->block_num);
480 #endif
481                                                 mono_add_varcopy_to_end (cfg, pred, idx, inst->inst_i0->inst_c0);
482                                         }
483                                 }
484
485                                 /* remove the phi functions */
486                                 inst->opcode = CEE_NOP;
487                                 inst->ssa_op = MONO_SSA_NOP;
488                         } 
489                 }
490         }
491         
492 #ifndef USE_ORIGINAL_VARS
493         /* we compute liveness again */
494         cfg->comp_done &= ~MONO_COMP_LIVENESS;
495         mono_analyze_liveness (cfg);
496
497         varlist_array = mono_ssa_get_allocatable_vars (cfg);
498
499         for (i = 0; i < varlist_array->len; i++) {
500                 GList *l, *regs, *vars = g_ptr_array_index (varlist_array, i);
501                 MonoMethodVar *vmv, *amv;
502                 
503                 if (g_list_length (vars) <= 1) {
504                         continue;
505                 }
506
507                 active = NULL;
508                 regs = NULL;
509
510                 for (l = vars; l; l = l->next) {
511                         vmv = l->data;
512
513                         /* expire old intervals in active */
514                         while (active) {
515                                 amv = (MonoMethodVar *)active->data;
516
517                                 if (amv->range.last_use.abs_pos >= vmv->range.first_use.abs_pos)
518                                         break;
519
520                                 active = g_list_remove_link (active, active);
521                                 regs = g_list_prepend (regs, (gpointer)amv->reg);
522                         }
523
524                         if (!regs)
525                                 regs = g_list_prepend (regs, (gpointer)vmv->idx);
526
527                         vmv->reg = (int)regs->data;
528                         regs = g_list_remove_link (regs, regs);
529                         active = mono_varlist_insert_sorted (cfg, active, vmv, TRUE);           
530                 }
531
532                 g_list_free (active);
533                 g_list_free (regs);
534                 g_list_free (vars);
535         }
536
537         g_ptr_array_free (varlist_array, TRUE);
538
539 #endif
540
541         is_live = alloca (cfg->num_varinfo);
542         memset (is_live, 0, cfg->num_varinfo);
543
544         for (i = 0; i < cfg->num_bblocks; ++i) {
545                 MonoBasicBlock *bb = cfg->bblocks [i];
546
547                 for (inst = bb->code; inst; inst = inst->next)
548                         mono_ssa_replace_copies (cfg, bb, inst, is_live);
549         }
550
551         for (i = 0; i < cfg->num_varinfo; ++i) {
552                 cfg->vars [i]->reg = -1;
553                 if (!is_live [i]) {
554                         cfg->varinfo [i]->flags |= MONO_INST_IS_DEAD;
555                 }
556         }
557
558         if (cfg->comp_done & MONO_COMP_REACHABILITY)
559                 unlink_unused_bblocks (cfg);
560
561         cfg->comp_done &= ~MONO_COMP_SSA;
562 }
563
564
565 #define IS_CALL(op) (op == CEE_CALLI || op == CEE_CALL || op == CEE_CALLVIRT || (op >= OP_VOIDCALL && op <= OP_CALL_MEMBASE))
566
567 typedef struct {
568         MonoBasicBlock *bb;
569         MonoInst *inst;
570 } MonoVarUsageInfo;
571
572 static void
573 analyze_dev_use (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *root, MonoInst *inst)
574 {
575         MonoMethodVar *info;
576         int i, idx, arity;
577
578         if (!inst)
579                 return;
580
581         arity = mono_burg_arity [inst->opcode];
582
583         if ((inst->ssa_op == MONO_SSA_STORE) && 
584             (inst->inst_i0->opcode == OP_LOCAL /*|| inst->inst_i0->opcode == OP_ARG */)) {
585                 idx = inst->inst_i0->inst_c0;
586                 info = cfg->vars [idx];
587                 //printf ("%d defined in BB%d %p\n", idx, bb->block_num, root);
588                 if (info->def) {
589                         g_warning ("more than one definition of variable %d in %s", idx,
590                                    mono_method_full_name (cfg->method, TRUE));
591                         g_assert_not_reached ();
592                 }
593                 if (!IS_CALL (inst->inst_i1->opcode) /* && inst->inst_i1->opcode == OP_ICONST */) {
594                         g_assert (inst == root);
595                         info->def = root;
596                         info->def_bb = bb;
597                 }
598
599                 if (inst->inst_i1->opcode == OP_PHI) {
600                         for (i = inst->inst_i1->inst_phi_args [0]; i > 0; i--) {
601                                 MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo));
602                                 idx = inst->inst_i1->inst_phi_args [i]; 
603                                 info = cfg->vars [idx];
604                                 //printf ("FOUND %d\n", idx);
605                                 ui->bb = bb;
606                                 ui->inst = root;
607                                 info->uses = g_list_prepend (info->uses, ui);
608                         }
609                 }
610         }
611
612         if ((inst->ssa_op == MONO_SSA_LOAD || inst->ssa_op == MONO_SSA_MAYBE_LOAD) && 
613             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
614                 MonoVarUsageInfo *ui = mono_mempool_alloc (cfg->mempool, sizeof (MonoVarUsageInfo));
615                 idx = inst->inst_i0->inst_c0;   
616                 info = cfg->vars [idx];
617                 //printf ("FOUND %d\n", idx);
618                 ui->bb = bb;
619                 ui->inst = root;
620                 info->uses = g_list_prepend (info->uses, ui);
621         } else {
622                 if (arity) {
623                         //if (inst->ssa_op != MONO_SSA_STORE)
624                         analyze_dev_use (cfg, bb, root, inst->inst_left);
625                         if (arity > 1)
626                                 analyze_dev_use (cfg, bb, root, inst->inst_right);
627                 }
628         }
629 }
630
631
632 /* avoid unnecessary copies of variables:
633  * Y <= X; Z = Y; is translated to Z = X;
634  */
635 static void
636 mono_ssa_avoid_copies (MonoCompile *cfg)
637 {
638         MonoInst *inst, *next;
639         MonoBasicBlock *bb;
640         MonoMethodVar *i1, *i2;
641
642         g_assert ((cfg->comp_done & MONO_COMP_SSA_DEF_USE));
643
644         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
645                 for (inst = bb->code; inst; inst = inst->next) {
646                         if (inst->ssa_op == MONO_SSA_STORE && inst->inst_i0->opcode == OP_LOCAL &&
647                             !IS_CALL (inst->inst_i1->opcode) && inst->inst_i1->opcode != OP_PHI && !inst->flags) {
648                                 i1 = cfg->vars [inst->inst_i0->inst_c0];
649
650 /* fixme: compiling mcs does not work when I enable this */
651 #if 0
652                                 if (g_list_length (i1->uses) == 1 && !extends_live (inst->inst_i1)) {
653                                         MonoVarUsageInfo *vi = (MonoVarUsageInfo *)i1->uses->data;
654                                         u = vi->inst;
655
656                                         //printf ("VAR %d %s\n", i1->idx, mono_method_full_name (cfg->method, TRUE));
657                                         //mono_print_tree (inst); printf ("\n");
658                                         //mono_print_tree (u); printf ("\n");
659
660                                         if (replace_usage_new (cfg, u, inst->inst_i0->inst_c0,  inst->inst_i1)) {
661                                                                                                                 
662                                                 //mono_print_tree (u); printf ("\n");
663                                                         
664                                                 inst->opcode = CEE_NOP;
665                                                 inst->ssa_op = MONO_SSA_NOP;
666                                         }
667                                 }
668 #endif                  
669                                 if ((next = inst->next) && next->ssa_op == MONO_SSA_STORE && next->inst_i0->opcode == OP_LOCAL &&
670                                     next->inst_i1->ssa_op == MONO_SSA_LOAD &&  next->inst_i1->inst_i0->opcode == OP_LOCAL &&
671                                     next->inst_i1->inst_i0->inst_c0 == inst->inst_i0->inst_c0 && g_list_length (i1->uses) == 1 &&
672                                     inst->opcode == next->opcode && inst->inst_i0->type == next->inst_i0->type) {
673                                         i2 = cfg->vars [next->inst_i0->inst_c0];
674                                         //printf ("ELIM. COPY in BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
675                                         inst->inst_i0 = next->inst_i0;
676                                         i2->def = inst;
677                                         i1->def = NULL;
678                                         g_list_free (i1->uses);
679                                         i1->uses = NULL;
680                                         next->opcode = CEE_NOP;
681                                         next->ssa_op = MONO_SSA_NOP;
682                                 }
683                         }
684                 }
685         }
686 }
687
688 static void
689 mono_ssa_create_def_use (MonoCompile *cfg) 
690 {
691         MonoBasicBlock *bb;
692
693         g_assert (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE));
694
695         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
696                 MonoInst *inst;
697                 for (inst = bb->code; inst; inst = inst->next) {
698                         analyze_dev_use (cfg, bb, inst, inst);
699                 }
700         }
701
702         cfg->comp_done |= MONO_COMP_SSA_DEF_USE;
703 }
704
705 static int
706 simulate_compare (int opcode, int a, int b)
707 {
708         switch (opcode) {
709         case CEE_BEQ:
710                 return a == b;
711         case CEE_BGE:
712                 return a >= b;
713         case CEE_BGT:
714                 return a > b;
715         case CEE_BLE:
716                 return a <= b;
717         case CEE_BLT:
718                 return a < b;
719         case CEE_BNE_UN:
720                 return a != b;
721         case CEE_BGE_UN:
722                 return (unsigned)a >= (unsigned)b;
723         case CEE_BGT_UN:
724                 return (unsigned)a > (unsigned)b;
725         case CEE_BLE_UN:
726                 return (unsigned)a <= (unsigned)b;
727         case CEE_BLT_UN:
728                 return (unsigned)a < (unsigned)b;
729         default:
730                 g_assert_not_reached ();
731         }
732
733         return 0;
734 }
735
736 static int
737 simulate_long_compare (int opcode, gint64 a, gint64 b)
738 {
739         switch (opcode) {
740         case CEE_BEQ:
741                 return a == b;
742         case CEE_BGE:
743                 return a >= b;
744         case CEE_BGT:
745                 return a > b;
746         case CEE_BLE:
747                 return a <= b;
748         case CEE_BLT:
749                 return a < b;
750         case CEE_BNE_UN:
751                 return a != b;
752         case CEE_BGE_UN:
753                 return (unsigned)a >= (unsigned)b;
754         case CEE_BGT_UN:
755                 return (unsigned)a > (unsigned)b;
756         case CEE_BLE_UN:
757                 return (unsigned)a <= (unsigned)b;
758         case CEE_BLT_UN:
759                 return (unsigned)a < (unsigned)b;
760         default:
761                 g_assert_not_reached ();
762         }
763
764         return 0;
765 }
766
767 #define EVAL_CXX(name,op,cast)  \
768         case name:      \
769                 if (inst->inst_i0->opcode == OP_COMPARE) { \
770                         r1 = evaluate_const_tree (cfg, inst->inst_i0->inst_i0, &a, carray); \
771                         r2 = evaluate_const_tree (cfg, inst->inst_i0->inst_i1, &b, carray); \
772                         if (r1 == 1 && r2 == 1) { \
773                                 *res = ((cast)a op (cast)b); \
774                                 return 1; \
775                         } else { \
776                                 return MAX (r1, r2); \
777                         } \
778                 } \
779                 break;
780
781 #define EVAL_BINOP(name,op)     \
782         case name:      \
783                 r1 = evaluate_const_tree (cfg, inst->inst_i0, &a, carray); \
784                 r2 = evaluate_const_tree (cfg, inst->inst_i1, &b, carray); \
785                 if (r1 == 1 && r2 == 1) { \
786                         *res = (a op b); \
787                         return 1; \
788                 } else { \
789                         return MAX (r1, r2); \
790                 } \
791                 break;
792
793
794 /* fixme: this only works for interger constants, but not for other types (long, float) */
795 static int
796 evaluate_const_tree (MonoCompile *cfg, MonoInst *inst, int *res, MonoInst **carray)
797 {
798         MonoInst *c0;
799         int a, b, r1, r2;
800
801         if (!inst)
802                 return 0;
803
804         if (inst->ssa_op == MONO_SSA_LOAD && 
805             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
806             (c0 = carray [inst->inst_i0->inst_c0])) {
807                 *res = c0->inst_c0;
808                 return 1;
809         }
810
811         switch (inst->opcode) {
812         case OP_ICONST:
813                 *res = inst->inst_c0;
814                 return 1;
815
816         EVAL_CXX (OP_CEQ,==,gint32)
817         EVAL_CXX (OP_CGT,>,gint32)
818         EVAL_CXX (OP_CGT_UN,>,guint32)
819         EVAL_CXX (OP_CLT,<,gint32)
820         EVAL_CXX (OP_CLT_UN,<,guint32)
821
822         EVAL_BINOP (CEE_ADD,+)
823         EVAL_BINOP (CEE_SUB,-)
824         EVAL_BINOP (CEE_MUL,*)
825         EVAL_BINOP (CEE_AND,&)
826         EVAL_BINOP (CEE_OR,|)
827         EVAL_BINOP (CEE_XOR,^)
828         EVAL_BINOP (CEE_SHL,<<)
829         EVAL_BINOP (CEE_SHR,>>)
830
831         default:
832                 return 2;
833         }
834
835         return 2;
836 }
837
838 static void
839 fold_tree (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, MonoInst **carray)
840 {
841         MonoInst *c0;
842         int arity, a, b;
843
844         if (!inst)
845                 return;
846
847         arity = mono_burg_arity [inst->opcode];
848
849         if (inst->ssa_op == MONO_SSA_STORE && 
850             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
851             inst->inst_i1->opcode == OP_PHI && (c0 = carray [inst->inst_i0->inst_c0])) {
852                 //{static int cn = 0; printf ("PHICONST %d %d %s\n", cn++, c0->inst_c0, mono_method_full_name (cfg->method, TRUE));}
853                 *inst->inst_i1 = *c0;           
854         } else if (inst->ssa_op == MONO_SSA_LOAD && 
855             (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG) &&
856             (c0 = carray [inst->inst_i0->inst_c0])) {
857                 //{static int cn = 0; printf ("YCCOPY %d %d %s\n", cn++, c0->inst_c0, mono_method_full_name (cfg->method, TRUE));}
858                 *inst = *c0;
859         } else {
860
861                 if (arity) {
862                         fold_tree (cfg, bb, inst->inst_left, carray);
863                         if (arity > 1)
864                                 fold_tree (cfg, bb, inst->inst_right, carray);
865                         mono_constant_fold_inst (inst, NULL); 
866                 }
867         }
868
869         if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) &&
870             inst->inst_i0->opcode == OP_COMPARE) {
871                 MonoInst *v0 = inst->inst_i0->inst_i0;
872                 MonoInst *v1 = inst->inst_i0->inst_i1;
873                 MonoBasicBlock *target = NULL;
874
875                 /* hack for longs to optimize the simply cases */
876                 if (v0->opcode == OP_I8CONST && v1->opcode == OP_I8CONST) {
877                         if (simulate_long_compare (inst->opcode, v0->inst_l, v1->inst_l)) {
878                                 //unlink_target (bb, inst->inst_false_bb);
879                                 target = inst->inst_true_bb;
880                         } else {
881                                 //unlink_target (bb, inst->inst_true_bb);
882                                 target = inst->inst_false_bb;
883                         }                       
884                 } else if (evaluate_const_tree (cfg, v0, &a, carray) == 1 &&
885                            evaluate_const_tree (cfg, v1, &b, carray) == 1) {                            
886                         if (simulate_compare (inst->opcode, a, b)) {
887                                 //unlink_target (bb, inst->inst_false_bb);
888                                 target = inst->inst_true_bb;
889                         } else {
890                                 //unlink_target (bb, inst->inst_true_bb);
891                                 target = inst->inst_false_bb;
892                         }
893                 }
894
895                 if (target) {
896                         bb->out_bb [0] = target;
897                         bb->out_count = 1;
898                         inst->opcode = CEE_BR;
899                         inst->inst_target_bb = target;
900                 }
901         } else if (inst->opcode == CEE_SWITCH && evaluate_const_tree (cfg, inst->inst_left, &a, carray) == 1) {
902                 bb->out_bb [0] = inst->inst_many_bb [a];
903                 bb->out_count = 1;
904                 inst->inst_target_bb = bb->out_bb [0];
905                 inst->opcode = CEE_BR;
906         }
907
908 }
909
910 static void
911 change_varstate (MonoCompile *cfg, GList **cvars, MonoMethodVar *info, int state, MonoInst *c0, MonoInst **carray)
912 {
913         if (info->cpstate >= state)
914                 return;
915
916         info->cpstate = state;
917
918         //printf ("SETSTATE %d to %d\n", info->idx, info->cpstate);
919
920         if (state == 1)
921                 carray [info->idx] = c0;
922         else
923                 carray [info->idx] = NULL;
924
925         if (!g_list_find (*cvars, info)) {
926                 *cvars = g_list_prepend (*cvars, info);
927         }
928 }
929
930 static void
931 visit_inst (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, GList **cvars, GList **bblist, MonoInst **carray)
932 {
933         g_assert (inst);
934
935         if (inst->opcode == CEE_SWITCH) {
936                 int r1, i, a;
937
938                 r1 = evaluate_const_tree (cfg, inst->inst_left, &a, carray);
939                 if (r1 == 1) {
940                         MonoBasicBlock *tb = inst->inst_many_bb [a];
941                         if (!(tb->flags &  BB_REACHABLE)) {
942                                 tb->flags |= BB_REACHABLE;
943                                 *bblist = g_list_prepend (*bblist, tb);
944                         }
945                 } else if (r1 == 2) {
946                         for (i = (int)inst->klass; i >= 0; i--) {
947                                 MonoBasicBlock *tb = inst->inst_many_bb [i];
948                                 if (!(tb->flags &  BB_REACHABLE)) {
949                                         tb->flags |= BB_REACHABLE;
950                                         *bblist = g_list_prepend (*bblist, tb);
951                                 }
952                         }
953                 }
954         } else if ((inst->opcode >= CEE_BEQ && inst->opcode <= CEE_BLT_UN) &&
955             inst->inst_i0->opcode == OP_COMPARE) {
956                 int a, b, r1, r2;
957                 MonoInst *v0 = inst->inst_i0->inst_i0;
958                 MonoInst *v1 = inst->inst_i0->inst_i1;
959
960                 r1 = evaluate_const_tree (cfg, v0, &a, carray);
961                 r2 = evaluate_const_tree (cfg, v1, &b, carray);
962
963                 if (r1 == 1 && r2 == 1) {
964                         MonoBasicBlock *target;
965                                 
966                         if (simulate_compare (inst->opcode, a, b)) {
967                                 target = inst->inst_true_bb;
968                         } else {
969                                 target = inst->inst_false_bb;
970                         }
971                         if (!(target->flags &  BB_REACHABLE)) {
972                                 target->flags |= BB_REACHABLE;
973                                 *bblist = g_list_prepend (*bblist, target);
974                         }
975                 } else if (r1 == 2 || r2 == 2) {
976                         if (!(inst->inst_true_bb->flags &  BB_REACHABLE)) {
977                                 inst->inst_true_bb->flags |= BB_REACHABLE;
978                                 *bblist = g_list_prepend (*bblist, inst->inst_true_bb);
979                         }
980                         if (!(inst->inst_false_bb->flags &  BB_REACHABLE)) {
981                                 inst->inst_false_bb->flags |= BB_REACHABLE;
982                                 *bblist = g_list_prepend (*bblist, inst->inst_false_bb);
983                         }
984                 }       
985         } else if (inst->ssa_op == MONO_SSA_STORE && 
986                    (inst->inst_i0->opcode == OP_LOCAL || inst->inst_i0->opcode == OP_ARG)) {
987                 MonoMethodVar *info = cfg->vars [inst->inst_i0->inst_c0];
988                 MonoInst *i1 = inst->inst_i1;
989                 int res;
990                 
991                 if (info->cpstate < 2) {
992                         if (i1->opcode == OP_ICONST) { 
993                                 change_varstate (cfg, cvars, info, 1, i1, carray);
994                         } else if (i1->opcode == OP_PHI) {
995                                 MonoInst *c0 = NULL;
996                                 int j;
997
998                                 for (j = 1; j <= i1->inst_phi_args [0]; j++) {
999                                         MonoMethodVar *mv = cfg->vars [i1->inst_phi_args [j]];
1000                                         MonoInst *src = mv->def;
1001
1002                                         if (mv->def_bb && !(mv->def_bb->flags & BB_REACHABLE)) {
1003                                                 continue;
1004                                         }
1005
1006                                         if (!mv->def || !src || src->ssa_op != MONO_SSA_STORE ||
1007                                             !(src->inst_i0->opcode == OP_LOCAL || src->inst_i0->opcode == OP_ARG) ||
1008                                             mv->cpstate == 2) {
1009                                                 change_varstate (cfg, cvars, info, 2, NULL, carray);
1010                                                 break;
1011                                         }
1012                                         
1013                                         if (mv->cpstate == 0)
1014                                                 continue;
1015
1016                                         //g_assert (src->inst_i1->opcode == OP_ICONST);
1017                                         g_assert (carray [mv->idx]);
1018
1019                                         if (!c0) {
1020                                                 c0 = carray [mv->idx];
1021                                         }
1022                                         
1023                                         if (carray [mv->idx]->inst_c0 != c0->inst_c0) {
1024                                                 change_varstate (cfg, cvars, info, 2, NULL, carray);
1025                                                 break;
1026                                         }
1027                                 }
1028                                 
1029                                 if (c0 && info->cpstate < 1) {
1030                                         change_varstate (cfg, cvars, info, 1, c0, carray);
1031                                 }
1032                         } else {
1033                                 int state = evaluate_const_tree (cfg, i1, &res, carray);
1034                                 if (state == 1) {
1035                                         NEW_ICONST (cfg, i1, res);
1036                                         change_varstate (cfg, cvars, info, 1, i1, carray);
1037                                 } else {
1038                                         change_varstate (cfg, cvars, info, 2, NULL, carray);
1039                                 }
1040                         }
1041                 }
1042         }
1043 }
1044
1045 void
1046 mono_ssa_cprop (MonoCompile *cfg) 
1047 {
1048         MonoInst **carray;
1049         MonoBasicBlock *bb;
1050         GList *bblock_list, *cvars;
1051         GList *tmp;
1052         int i;
1053         //printf ("SIMPLE OPTS BB%d %s\n", bb->block_num, mono_method_full_name (cfg->method, TRUE));
1054
1055         carray = g_new0 (MonoInst*, cfg->num_varinfo);
1056
1057         if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
1058                 mono_ssa_create_def_use (cfg);
1059
1060         bblock_list = g_list_prepend (NULL, cfg->bb_entry);
1061         cfg->bb_entry->flags |= BB_REACHABLE;
1062
1063         memset (carray, 0, sizeof (MonoInst *) * cfg->num_varinfo);
1064
1065         for (i = 0; i < cfg->num_varinfo; i++) {
1066                 MonoMethodVar *info = cfg->vars [i];
1067                 if (!info->def)
1068                         info->cpstate = 2;
1069         }
1070
1071         cvars = NULL;
1072
1073         while (bblock_list) {
1074                 MonoInst *inst;
1075
1076                 bb = (MonoBasicBlock *)bblock_list->data;
1077
1078                 bblock_list = g_list_remove_link (bblock_list, bblock_list);
1079
1080                 g_assert (bb->flags &  BB_REACHABLE);
1081
1082                 if (bb->out_count == 1) {
1083                         if (!(bb->out_bb [0]->flags &  BB_REACHABLE)) {
1084                                 bb->out_bb [0]->flags |= BB_REACHABLE;
1085                                 bblock_list = g_list_prepend (bblock_list, bb->out_bb [0]);
1086                         }
1087                 }
1088
1089                 for (inst = bb->code; inst; inst = inst->next) {
1090                         visit_inst (cfg, bb, inst, &cvars, &bblock_list, carray);
1091                 }
1092
1093                 while (cvars) {
1094                         MonoMethodVar *info = (MonoMethodVar *)cvars->data;                     
1095                         cvars = g_list_remove_link (cvars, cvars);
1096
1097                         for (tmp = info->uses; tmp; tmp = tmp->next) {
1098                                 MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
1099                                 if (!(ui->bb->flags & BB_REACHABLE))
1100                                         continue;
1101                                 visit_inst (cfg, ui->bb, ui->inst, &cvars, &bblock_list, carray);
1102                         }
1103                 }
1104         }
1105
1106         for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
1107                 MonoInst *inst;
1108                 for (inst = bb->code; inst; inst = inst->next) {
1109                         fold_tree (cfg, bb, inst, carray);
1110                 }
1111         }
1112
1113         g_free (carray);
1114
1115         cfg->comp_done |= MONO_COMP_REACHABILITY;
1116 }
1117
1118 static void
1119 add_to_dce_worklist (MonoCompile *cfg, MonoMethodVar *var, MonoMethodVar *use, GList **wl)
1120 {
1121         GList *tmp;
1122
1123         *wl = g_list_prepend (*wl, use);
1124
1125         for (tmp = use->uses; tmp; tmp = tmp->next) {
1126                 MonoVarUsageInfo *ui = (MonoVarUsageInfo *)tmp->data;
1127                 if (ui->inst == var->def) {
1128                         use->uses = g_list_remove_link (use->uses, tmp);
1129                         break;
1130                 }
1131         }       
1132 }
1133
1134 void
1135 mono_ssa_deadce (MonoCompile *cfg) 
1136 {
1137         int i;
1138         GList *work_list;
1139
1140         g_assert (cfg->comp_done & MONO_COMP_SSA);
1141
1142         //printf ("DEADCE %s\n", mono_method_full_name (cfg->method, TRUE));
1143
1144         /* fixme: we should update usage infos during cprop, instead of computing it again */
1145         cfg->comp_done &=  ~MONO_COMP_SSA_DEF_USE;
1146         for (i = 0; i < cfg->num_varinfo; i++) {
1147                 MonoMethodVar *info = cfg->vars [i];
1148                 info->def = NULL;
1149                 info->uses = NULL;
1150         }
1151
1152         if (!(cfg->comp_done & MONO_COMP_SSA_DEF_USE))
1153                 mono_ssa_create_def_use (cfg);
1154
1155         mono_ssa_avoid_copies (cfg);
1156
1157         work_list = NULL;
1158         for (i = 0; i < cfg->num_varinfo; i++) {
1159                 MonoMethodVar *info = cfg->vars [i];
1160                 work_list = g_list_prepend (work_list, info);
1161         }
1162
1163         while (work_list) {
1164                 MonoMethodVar *info = (MonoMethodVar *)work_list->data;
1165                 work_list = g_list_remove_link (work_list, work_list);
1166
1167                 if (!info->uses && info->def) {
1168                         MonoInst *i1;
1169                         //printf ("ELIMINATE %s: ", mono_method_full_name (cfg->method, TRUE)); mono_print_tree (info->def); printf ("\n");
1170
1171                         i1 = info->def->inst_i1;
1172                         if (i1->opcode == OP_PHI) {
1173                                 int j;
1174                                 for (j = i1->inst_phi_args [0]; j > 0; j--) {
1175                                         MonoMethodVar *u = cfg->vars [i1->inst_phi_args [j]];
1176                                         add_to_dce_worklist (cfg, info, u, &work_list);
1177                                 }
1178                         } else if (i1->ssa_op == MONO_SSA_LOAD &&
1179                                    (i1->inst_i0->opcode == OP_LOCAL || i1->inst_i0->opcode == OP_ARG)) {
1180                                         MonoMethodVar *u = cfg->vars [i1->inst_i0->inst_c0];
1181                                         add_to_dce_worklist (cfg, info, u, &work_list);
1182                         }
1183
1184                         info->def->opcode = CEE_NOP;
1185                         info->def->ssa_op = MONO_SSA_NOP;
1186                 }
1187
1188         }
1189 }
1190
1191 #if 0
1192 void
1193 mono_ssa_strength_reduction (MonoCompile *cfg)
1194 {
1195         MonoBasicBlock *bb;
1196         int i;
1197
1198         g_assert (cfg->comp_done & MONO_COMP_SSA);
1199         g_assert (cfg->comp_done & MONO_COMP_LOOPS);
1200         g_assert (cfg->comp_done & MONO_COMP_SSA_DEF_USE);
1201
1202         for (bb = cfg->bb_entry->next_bb; bb; bb = bb->next_bb) {
1203                 GList *lp = bb->loop_blocks;
1204
1205                 if (lp) {
1206                         MonoBasicBlock *h = (MonoBasicBlock *)lp->data;
1207
1208                         /* we only consider loops with 2 in bblocks */
1209                         if (!h->in_count == 2)
1210                                 continue;
1211
1212                         for (i = 0; i < cfg->num_varinfo; i++) {
1213                                 MonoMethodVar *info = cfg->vars [i];
1214                         
1215                                 if (info->def && info->def->ssa_op == MONO_SSA_STORE &&
1216                                     info->def->inst_i0->opcode == OP_LOCAL && g_list_find (lp, info->def_bb)) {
1217                                         MonoInst *v = info->def->inst_i1;
1218
1219
1220                                         printf ("FOUND %d in %s\n", info->idx, mono_method_full_name (cfg->method, TRUE));
1221                                 }
1222                         }
1223                 }
1224         }
1225 }
1226 #endif