New tests.
[mono.git] / mono / mini / liveness.c
1 /*
2  * liveness.c: liveness analysis
3  *
4  * Author:
5  *   Dietmar Maurer (dietmar@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include "mini.h"
11 #include "inssel.h"
12 #include "aliasing.h"
13
14 #define SPILL_COST_INCREMENT (1 << (bb->nesting << 1))
15
16 //#define DEBUG_LIVENESS
17
18 #if SIZEOF_VOID_P == 8
19 #define BITS_PER_CHUNK 64
20 #else
21 #define BITS_PER_CHUNK 32
22 #endif
23
24 static void mono_analyze_liveness2 (MonoCompile *cfg);
25
26 static void
27 optimize_initlocals (MonoCompile *cfg);
28
29 static void
30 optimize_initlocals2 (MonoCompile *cfg);
31
32 /* mono_bitset_mp_new:
33  * 
34  * allocates a MonoBitSet inside a memory pool
35  */
36 static inline MonoBitSet* 
37 mono_bitset_mp_new (MonoMemPool *mp, guint32 size, guint32 max_size)
38 {
39         guint8 *mem = mono_mempool_alloc0 (mp, size);
40         return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
41 }
42
43 static inline MonoBitSet* 
44 mono_bitset_mp_new_noinit (MonoMemPool *mp, guint32 size, guint32 max_size)
45 {
46         guint8 *mem = mono_mempool_alloc (mp, size);
47         return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
48 }
49
50 G_GNUC_UNUSED static void
51 mono_bitset_print (MonoBitSet *set)
52 {
53         int i;
54
55         printf ("{");
56         for (i = 0; i < mono_bitset_size (set); i++) {
57
58                 if (mono_bitset_test (set, i))
59                         printf ("%d, ", i);
60
61         }
62         printf ("}\n");
63 }
64
65 static inline void
66 update_live_range (MonoCompile *cfg, int idx, int block_dfn, int tree_pos)
67 {
68         MonoLiveRange *range = &MONO_VARINFO (cfg, idx)->range;
69         guint32 abs_pos = (block_dfn << 16) | tree_pos;
70
71         if (range->first_use.abs_pos > abs_pos)
72                 range->first_use.abs_pos = abs_pos;
73
74         if (range->last_use.abs_pos < abs_pos)
75                 range->last_use.abs_pos = abs_pos;
76 }
77
78 static void
79 update_gen_kill_set (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst, int inst_num)
80 {
81         int arity;
82         int max_vars = cfg->num_varinfo;
83
84         arity = mono_burg_arity [inst->opcode];
85         if (arity)
86                 update_gen_kill_set (cfg, bb, inst->inst_i0, inst_num);
87
88         if (arity > 1)
89                 update_gen_kill_set (cfg, bb, inst->inst_i1, inst_num);
90
91         if ((inst->ssa_op & MONO_SSA_LOAD_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
92                 MonoLocalVariableList* affected_variables;
93                 MonoLocalVariableList local_affected_variable;
94                 
95                 if (cfg->aliasing_info == NULL) {
96                         if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
97                                 local_affected_variable.variable_index = inst->inst_i0->inst_c0;
98                                 local_affected_variable.next = NULL;
99                                 affected_variables = &local_affected_variable;
100                         } else {
101                                 affected_variables = NULL;
102                         }
103                 } else {
104                         affected_variables = mono_aliasing_get_affected_variables_for_inst_traversing_code (cfg->aliasing_info, inst);
105                 }
106                 
107                 if (inst->ssa_op & MONO_SSA_LOAD) {
108                         MonoLocalVariableList* affected_variable = affected_variables;
109                         while (affected_variable != NULL) {
110                                 int idx = affected_variable->variable_index;
111                                 MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
112                                 g_assert (idx < max_vars);
113                                 update_live_range (cfg, idx, bb->dfn, inst_num); 
114                                 if (!mono_bitset_test_fast (bb->kill_set, idx))
115                                         mono_bitset_set_fast (bb->gen_set, idx);
116                                 if (inst->ssa_op == MONO_SSA_LOAD)
117                                         vi->spill_costs += SPILL_COST_INCREMENT;
118                                 
119                                 affected_variable = affected_variable->next;
120                         }
121                 } else if ((inst->ssa_op == MONO_SSA_STORE) || (inst->opcode == OP_DUMMY_STORE)) {
122                         MonoLocalVariableList* affected_variable = affected_variables;
123                         while (affected_variable != NULL) {
124                                 int idx = affected_variable->variable_index;
125                                 MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
126                                 g_assert (idx < max_vars);
127                                 //if (arity > 0)
128                                         //g_assert (inst->inst_i1->opcode != OP_PHI);
129                                 update_live_range (cfg, idx, bb->dfn, inst_num); 
130                                 mono_bitset_set_fast (bb->kill_set, idx);
131                                 if (inst->ssa_op == MONO_SSA_STORE)
132                                         vi->spill_costs += SPILL_COST_INCREMENT;
133                                 
134                                 affected_variable = affected_variable->next;
135                         }
136                 }
137         } else if (inst->opcode == OP_JMP) {
138                 /* Keep arguments live! */
139                 int i;
140                 for (i = 0; i < cfg->num_varinfo; i++) {
141                         if (cfg->varinfo [i]->opcode == OP_ARG) {
142                                 if (!mono_bitset_test_fast (bb->kill_set, i))
143                                         mono_bitset_set_fast (bb->gen_set, i);
144                         }
145                 }
146         }
147
148
149 static void
150 update_volatile (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *inst)
151 {
152         int arity = mono_burg_arity [inst->opcode];
153         int max_vars = cfg->num_varinfo;
154
155         if (arity)
156                 update_volatile (cfg, bb, inst->inst_i0);
157
158         if (arity > 1)
159                 update_volatile (cfg, bb, inst->inst_i1);
160
161         if (inst->ssa_op & MONO_SSA_LOAD_STORE) {
162                 MonoLocalVariableList* affected_variables;
163                 MonoLocalVariableList local_affected_variable;
164                 
165                 if (cfg->aliasing_info == NULL) {
166                         if ((inst->ssa_op == MONO_SSA_LOAD) || (inst->ssa_op == MONO_SSA_STORE)) {
167                                 local_affected_variable.variable_index = inst->inst_i0->inst_c0;
168                                 local_affected_variable.next = NULL;
169                                 affected_variables = &local_affected_variable;
170                         } else {
171                                 affected_variables = NULL;
172                         }
173                 } else {
174                         affected_variables = mono_aliasing_get_affected_variables_for_inst_traversing_code (cfg->aliasing_info, inst);
175                 }
176                 
177                 while (affected_variables != NULL) {
178                         int idx = affected_variables->variable_index;
179                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
180                         g_assert (idx < max_vars);
181                         cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
182                         
183                         affected_variables = affected_variables->next;
184                 }
185         }
186
187
188 static void
189 visit_bb (MonoCompile *cfg, MonoBasicBlock *bb, GSList **visited)
190 {
191         int i;
192         MonoInst *ins;
193
194         if (g_slist_find (*visited, bb))
195                 return;
196
197         if (cfg->new_ir) {
198                 for (ins = bb->code; ins; ins = ins->next) {
199                         const char *spec = INS_INFO (ins->opcode);
200                         int regtype, srcindex, sreg;
201
202                         if (ins->opcode == OP_NOP)
203                                 continue;
204
205                         /* DREG */
206                         regtype = spec [MONO_INST_DEST];
207                         g_assert (((ins->dreg == -1) && (regtype == ' ')) || ((ins->dreg != -1) && (regtype != ' ')));
208                                 
209                         if ((ins->dreg != -1) && get_vreg_to_inst (cfg, ins->dreg)) {
210                                 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
211                                 int idx = var->inst_c0;
212                                 MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
213
214                                 cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
215                         }
216                         
217                         /* SREGS */
218                         for (srcindex = 0; srcindex < 2; ++srcindex) {
219                                 regtype = spec [(srcindex == 0) ? MONO_INST_SRC1 : MONO_INST_SRC2];
220                                 sreg = srcindex == 0 ? ins->sreg1 : ins->sreg2;
221
222                                 g_assert (((sreg == -1) && (regtype == ' ')) || ((sreg != -1) && (regtype != ' ')));
223                                 if ((sreg != -1) && get_vreg_to_inst (cfg, sreg)) {
224                                         MonoInst *var = get_vreg_to_inst (cfg, sreg);
225                                         int idx = var->inst_c0;
226                                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
227
228                                         cfg->varinfo [vi->idx]->flags |= MONO_INST_VOLATILE;
229                                 }
230                         }
231                 }
232         } else {
233                 if (cfg->aliasing_info != NULL)
234                         mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb);
235
236                 for (ins = bb->code; ins; ins = ins->next) {
237                         update_volatile (cfg, bb, ins);
238                 }
239         }
240
241         *visited = g_slist_append (*visited, bb);
242
243         /* 
244          * Need to visit all bblocks reachable from this one since they can be
245          * reached during exception handling.
246          */
247         for (i = 0; i < bb->out_count; ++i) {
248                 visit_bb (cfg, bb->out_bb [i], visited);
249         }
250 }
251
252 void
253 mono_liveness_handle_exception_clauses (MonoCompile *cfg)
254 {
255         MonoBasicBlock *bb;
256         GSList *visited = NULL;
257
258         /*
259          * Variables in exception handler register cannot be allocated to registers
260          * so make them volatile. See bug #42136. This will not be neccessary when
261          * the back ends could guarantee that the variables will be in the
262          * correct registers when a handler is called.
263          * This includes try blocks too, since a variable in a try block might be
264          * accessed after an exception handler has been run.
265          */
266         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
267
268                 if (bb->region == -1 || MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))
269                         continue;
270
271                 visit_bb (cfg, bb, &visited);
272         }
273         g_slist_free (visited);
274 }
275
276 static inline void
277 update_live_range2 (MonoMethodVar *var, int abs_pos)
278 {
279         if (var->range.first_use.abs_pos > abs_pos)
280                 var->range.first_use.abs_pos = abs_pos;
281
282         if (var->range.last_use.abs_pos < abs_pos)
283                 var->range.last_use.abs_pos = abs_pos;
284 }
285
286 static void
287 analyze_liveness_bb (MonoCompile *cfg, MonoBasicBlock *bb)
288 {
289         MonoInst *ins;
290         int sreg, inst_num;
291         MonoMethodVar *vars = cfg->vars;
292         guint32 abs_pos = (bb->dfn << 16);
293         
294         for (inst_num = 0, ins = bb->code; ins; ins = ins->next, inst_num += 2) {
295                 const char *spec = INS_INFO (ins->opcode);
296
297 #ifdef DEBUG_LIVENESS
298                         printf ("\t"); mono_print_ins (ins);
299 #endif
300
301                 if (ins->opcode == OP_NOP)
302                         continue;
303
304                 if (ins->opcode == OP_LDADDR) {
305                         MonoInst *var = ins->inst_p0;
306                         int idx = var->inst_c0;
307                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
308
309 #ifdef DEBUG_LIVENESS
310                         printf ("\tGEN: R%d(%d)\n", var->dreg, idx);
311 #endif
312                         update_live_range2 (&vars [idx], abs_pos + inst_num); 
313                         if (!mono_bitset_test_fast (bb->kill_set, idx))
314                                 mono_bitset_set_fast (bb->gen_set, idx);
315                         vi->spill_costs += SPILL_COST_INCREMENT;
316                 }                               
317
318                 /* SREGs must come first, so MOVE r <- r is handled correctly */
319
320                 /* SREG1 */
321                 sreg = ins->sreg1;
322                 if ((spec [MONO_INST_SRC1] != ' ') && get_vreg_to_inst (cfg, sreg)) {
323                         MonoInst *var = get_vreg_to_inst (cfg, sreg);
324                         int idx = var->inst_c0;
325                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
326
327 #ifdef DEBUG_LIVENESS
328                         printf ("\tGEN: R%d(%d)\n", sreg, idx);
329 #endif
330                         update_live_range2 (&vars [idx], abs_pos + inst_num); 
331                         if (!mono_bitset_test_fast (bb->kill_set, idx))
332                                 mono_bitset_set_fast (bb->gen_set, idx);
333                         vi->spill_costs += SPILL_COST_INCREMENT;
334                 }
335
336                 /* SREG2 */
337                 sreg = ins->sreg2;
338                 if ((spec [MONO_INST_SRC2] != ' ') && get_vreg_to_inst (cfg, sreg)) {
339                         MonoInst *var = get_vreg_to_inst (cfg, sreg);
340                         int idx = var->inst_c0;
341                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
342
343 #ifdef DEBUG_LIVENESS
344                         printf ("\tGEN: R%d(%d)\n", sreg, idx);
345 #endif
346                         update_live_range2 (&vars [idx], abs_pos + inst_num); 
347                         if (!mono_bitset_test_fast (bb->kill_set, idx))
348                                 mono_bitset_set_fast (bb->gen_set, idx);
349                         vi->spill_costs += SPILL_COST_INCREMENT;
350                 }
351
352                 /* DREG */
353                 if ((spec [MONO_INST_DEST] != ' ') && get_vreg_to_inst (cfg, ins->dreg)) {
354                         MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
355                         int idx = var->inst_c0;
356                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
357
358                         if (MONO_IS_STORE_MEMBASE (ins)) {
359                                 update_live_range2 (&vars [idx], abs_pos + inst_num); 
360                                 if (!mono_bitset_test_fast (bb->kill_set, idx))
361                                         mono_bitset_set_fast (bb->gen_set, idx);
362                                 vi->spill_costs += SPILL_COST_INCREMENT;
363                         } else {
364 #ifdef DEBUG_LIVENESS
365                                 printf ("\tKILL: R%d(%d)\n", ins->dreg, idx);
366 #endif
367                                 update_live_range2 (&vars [idx], abs_pos + inst_num + 1); 
368                                 mono_bitset_set_fast (bb->kill_set, idx);
369                                 vi->spill_costs += SPILL_COST_INCREMENT;
370                         }
371                 }
372         }
373 }
374
375 /* generic liveness analysis code. CFG specific parts are 
376  * in update_gen_kill_set()
377  */
378 void
379 mono_analyze_liveness (MonoCompile *cfg)
380 {
381         MonoBitSet *old_live_out_set;
382         int i, j, max_vars = cfg->num_varinfo;
383         int out_iter;
384         gboolean *in_worklist;
385         MonoBasicBlock **worklist;
386         guint32 l_end;
387         int bitsize;
388
389 #ifdef DEBUG_LIVENESS
390         printf ("LIVENESS %s\n", mono_method_full_name (cfg->method, TRUE));
391 #endif
392
393         g_assert (!(cfg->comp_done & MONO_COMP_LIVENESS));
394
395         cfg->comp_done |= MONO_COMP_LIVENESS;
396         
397         if (max_vars == 0)
398                 return;
399
400         bitsize = mono_bitset_alloc_size (max_vars, 0);
401
402         for (i = 0; i < max_vars; i ++) {
403                 MONO_VARINFO (cfg, i)->range.first_use.abs_pos = ~ 0;
404                 MONO_VARINFO (cfg, i)->range.last_use .abs_pos =   0;
405                 MONO_VARINFO (cfg, i)->spill_costs = 0;
406         }
407
408         for (i = 0; i < cfg->num_bblocks; ++i) {
409                 MonoBasicBlock *bb = cfg->bblocks [i];
410                 MonoInst *inst;
411                 int tree_num;
412
413                 bb->gen_set = mono_bitset_mp_new (cfg->mempool, bitsize, max_vars);
414                 bb->kill_set = mono_bitset_mp_new (cfg->mempool, bitsize, max_vars);
415
416                 if (cfg->new_ir) {
417                         analyze_liveness_bb (cfg, bb);
418                 } else {
419                         if (cfg->aliasing_info != NULL)
420                                 mono_aliasing_initialize_code_traversal (cfg->aliasing_info, bb);
421
422                         tree_num = 0;
423                         MONO_BB_FOR_EACH_INS (bb, inst) {
424 #ifdef DEBUG_LIVENESS
425                                 mono_print_tree (inst); printf ("\n");
426 #endif
427                                 update_gen_kill_set (cfg, bb, inst, tree_num);
428                                 tree_num ++;
429                         }
430                 }
431
432 #ifdef DEBUG_LIVENESS
433                 printf ("BLOCK BB%d (", bb->block_num);
434                 for (j = 0; j < bb->out_count; j++) 
435                         printf ("BB%d, ", bb->out_bb [j]->block_num);
436                 
437                 printf (")\n");
438                 printf ("GEN  BB%d: ", bb->block_num); mono_bitset_print (bb->gen_set);
439                 printf ("KILL BB%d: ", bb->block_num); mono_bitset_print (bb->kill_set);
440 #endif
441         }
442
443         old_live_out_set = mono_bitset_new (max_vars, 0);
444         in_worklist = g_new0 (gboolean, cfg->num_bblocks + 1);
445
446         worklist = g_new (MonoBasicBlock *, cfg->num_bblocks + 1);
447         l_end = 0;
448
449         /*
450          * This is a backward dataflow analysis problem, so we process blocks in
451          * decreasing dfn order, this speeds up the iteration.
452          */
453         for (i = 0; i < cfg->num_bblocks; i ++) {
454                 MonoBasicBlock *bb = cfg->bblocks [i];
455
456                 worklist [l_end ++] = bb;
457                 in_worklist [bb->dfn] = TRUE;
458
459                 /* Initialized later */
460                 bb->live_in_set = NULL;
461                 bb->live_out_set = mono_bitset_mp_new (cfg->mempool, bitsize, max_vars);
462         }
463
464         out_iter = 0;
465
466         while (l_end != 0) {
467                 MonoBasicBlock *bb = worklist [--l_end];
468                 MonoBasicBlock *out_bb;
469                 gboolean changed;
470
471                 in_worklist [bb->dfn] = FALSE;
472
473 #ifdef DEBUG_LIVENESS
474                 printf ("P: %d(%d): IN: ", bb->block_num, bb->dfn);
475                 for (j = 0; j < bb->in_count; ++j) 
476                         printf ("BB%d ", bb->in_bb [j]->block_num);
477                 printf ("OUT:");
478                 for (j = 0; j < bb->out_count; ++j) 
479                         printf ("BB%d ", bb->out_bb [j]->block_num);
480                 printf ("\n");
481 #endif
482
483
484                 if (bb->out_count == 0)
485                         continue;
486
487                 out_iter ++;
488
489                 if (!bb->live_in_set) {
490                         /* First pass over this bblock */
491                         changed = TRUE;
492                 }
493                 else {
494                         changed = FALSE;
495                         mono_bitset_copyto_fast (bb->live_out_set, old_live_out_set);
496                 }
497  
498                 for (j = 0; j < bb->out_count; j++) {
499                         out_bb = bb->out_bb [j];
500
501                         if (!out_bb->live_in_set) {
502                                 out_bb->live_in_set = mono_bitset_mp_new_noinit (cfg->mempool, bitsize, max_vars);
503
504                                 mono_bitset_copyto_fast (out_bb->live_out_set, out_bb->live_in_set);
505                                 mono_bitset_sub_fast (out_bb->live_in_set, out_bb->kill_set);
506                                 mono_bitset_union_fast (out_bb->live_in_set, out_bb->gen_set);
507                         }
508
509                         mono_bitset_union_fast (bb->live_out_set, out_bb->live_in_set);
510                 }
511                                 
512                 if (changed || !mono_bitset_equal (old_live_out_set, bb->live_out_set)) {
513                         if (!bb->live_in_set)
514                                 bb->live_in_set = mono_bitset_mp_new_noinit (cfg->mempool, bitsize, max_vars);
515                         mono_bitset_copyto_fast (bb->live_out_set, bb->live_in_set);
516                         mono_bitset_sub_fast (bb->live_in_set, bb->kill_set);
517                         mono_bitset_union_fast (bb->live_in_set, bb->gen_set);
518
519                         for (j = 0; j < bb->in_count; j++) {
520                                 MonoBasicBlock *in_bb = bb->in_bb [j];
521                                 /* 
522                                  * Some basic blocks do not seem to be in the 
523                                  * cfg->bblocks array...
524                                  */
525                                 if (in_bb->gen_set && !in_worklist [in_bb->dfn]) {
526 #ifdef DEBUG_LIVENESS
527                                         printf ("\tADD: %d\n", in_bb->block_num);
528 #endif
529                                         /*
530                                          * Put the block at the top of the stack, so it
531                                          * will be processed right away.
532                                          */
533                                         worklist [l_end ++] = in_bb;
534                                         in_worklist [in_bb->dfn] = TRUE;
535                                 }
536                         }
537                 }
538         }
539
540 #ifdef DEBUG_LIVENESS
541                 printf ("IT: %d %d.\n", cfg->num_bblocks, out_iter);
542 #endif
543
544         mono_bitset_free (old_live_out_set);
545
546         g_free (worklist);
547         g_free (in_worklist);
548
549         /* Compute live_in_set for bblocks skipped earlier */
550         for (i = 0; i < cfg->num_bblocks; ++i) {
551                 MonoBasicBlock *bb = cfg->bblocks [i];
552
553                 if (!bb->live_in_set) {
554                         bb->live_in_set = mono_bitset_mp_new (cfg->mempool, bitsize, max_vars);
555
556                         mono_bitset_copyto_fast (bb->live_out_set, bb->live_in_set);
557                         mono_bitset_sub_fast (bb->live_in_set, bb->kill_set);
558                         mono_bitset_union_fast (bb->live_in_set, bb->gen_set);
559                 }
560         }
561
562         for (i = 0; i < cfg->num_bblocks; ++i) {
563                 MonoBasicBlock *bb = cfg->bblocks [i];
564                 guint32 rem, max;
565                 guint32 abs_pos = (bb->dfn << 16);
566                 MonoMethodVar *vars = cfg->vars;
567
568                 if (!bb->live_out_set)
569                         continue;
570
571                 rem = max_vars % BITS_PER_CHUNK;
572                 max = ((max_vars + (BITS_PER_CHUNK -1)) / BITS_PER_CHUNK);
573                 for (j = 0; j < max; ++j) {
574                         gsize bits_in;
575                         gsize bits_out;
576                         int k;
577
578                         bits_in = mono_bitset_get_fast (bb->live_in_set, j);
579                         bits_out = mono_bitset_get_fast (bb->live_out_set, j);
580
581                         k = (j * BITS_PER_CHUNK);
582                         while ((bits_in || bits_out)) {
583                                 if (bits_in & 1)
584                                         update_live_range2 (&vars [k], abs_pos + 0);
585                                 if (bits_out & 1)
586                                         update_live_range2 (&vars [k], abs_pos + 0xffff);
587                                 bits_in >>= 1;
588                                 bits_out >>= 1;
589                                 k ++;
590                         }
591                 }
592         }
593
594         /*
595          * Arguments need to have their live ranges extended to the beginning of
596          * the method to account for the arg reg/memory -> global register copies
597          * in the prolog (bug #74992).
598          */
599
600         for (i = 0; i < max_vars; i ++) {
601                 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
602                 if (cfg->varinfo [vi->idx]->opcode == OP_ARG) {
603                         if (vi->range.last_use.abs_pos == 0 && !(cfg->varinfo [vi->idx]->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)))
604                                 cfg->varinfo [vi->idx]->flags |= MONO_INST_IS_DEAD;
605                         vi->range.first_use.abs_pos = 0;
606                 }
607         }
608
609 #ifdef DEBUG_LIVENESS
610         for (i = cfg->num_bblocks - 1; i >= 0; i--) {
611                 MonoBasicBlock *bb = cfg->bblocks [i];
612                 
613                 printf ("LIVE IN  BB%d: ", bb->block_num); 
614                 mono_bitset_print (bb->live_in_set); 
615                 printf ("LIVE OUT BB%d: ", bb->block_num); 
616                 mono_bitset_print (bb->live_out_set); 
617         }
618 #endif
619
620         if (cfg->new_ir) {
621                 if (!cfg->disable_initlocals_opt)
622                         optimize_initlocals2 (cfg);
623
624                 /* This improves code size by about 5% but slows down compilation too much */
625                 if (cfg->compile_aot)
626                         mono_analyze_liveness2 (cfg);
627         }
628         else {
629                 if (!cfg->disable_initlocals_opt)
630                         optimize_initlocals (cfg);
631         }
632 }
633
634 /**
635  * optimize_initlocals:
636  *
637  * Try to optimize away some of the redundant initialization code inserted because of
638  * 'locals init' using the liveness information.
639  */
640 static void
641 optimize_initlocals2 (MonoCompile *cfg)
642 {
643         MonoBitSet *used;
644         MonoInst *ins;
645         MonoBasicBlock *initlocals_bb;
646
647         used = mono_bitset_new (cfg->next_vreg + 1, 0);
648
649         mono_bitset_clear_all (used);
650         initlocals_bb = cfg->bb_entry->next_bb;
651         for (ins = initlocals_bb->code; ins; ins = ins->next) {
652                 const char *spec = INS_INFO (ins->opcode);
653
654                 if (spec [MONO_INST_SRC1] != ' ')
655                         mono_bitset_set_fast (used, ins->sreg1);
656                 if (spec [MONO_INST_SRC2] != ' ')
657                         mono_bitset_set_fast (used, ins->sreg2);
658                 if (MONO_IS_STORE_MEMBASE (ins))
659                         mono_bitset_set_fast (used, ins->dreg);
660         }
661
662         for (ins = initlocals_bb->code; ins; ins = ins->next) {
663                 const char *spec = INS_INFO (ins->opcode);
664
665                 /* Look for statements whose dest is not used in this bblock and not live on exit. */
666                 if ((spec [MONO_INST_DEST] != ' ') && !MONO_IS_STORE_MEMBASE (ins)) {
667                         MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
668
669                         if (var && !mono_bitset_test_fast (used, ins->dreg) && !mono_bitset_test_fast (initlocals_bb->live_out_set, var->inst_c0) && (var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
670                                 //printf ("DEAD: "); mono_print_ins (ins);
671                                 if ((ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || (ins->opcode == OP_R8CONST)) {
672                                         NULLIFY_INS (ins);
673                                         MONO_VARINFO (cfg, var->inst_c0)->spill_costs -= 1;
674                                         /* 
675                                          * We should shorten the liveness interval of these vars as well, but
676                                          * don't have enough info to do that.
677                                          */
678                                 }
679                         }
680                 }
681         }
682
683         g_free (used);
684 }
685
686 void
687 mono_linterval_add_range (MonoCompile *cfg, MonoLiveInterval *interval, int from, int to)
688 {
689         MonoLiveRange2 *prev_range, *next_range, *new_range;
690
691         g_assert (to >= from);
692
693         /* Optimize for extending the first interval backwards */
694         if (G_LIKELY (interval->range && (interval->range->from > from) && (interval->range->from == to))) {
695                 interval->range->from = from;
696                 return;
697         }
698
699         /* Find a place in the list for the new range */
700         prev_range = NULL;
701         next_range = interval->range;
702         while ((next_range != NULL) && (next_range->from <= from)) {
703                 prev_range = next_range;
704                 next_range = next_range->next;
705         }
706
707         if (prev_range && prev_range->to == from) {
708                 /* Merge with previous */
709                 prev_range->to = to;
710         } else if (next_range && next_range->from == to) {
711                 /* Merge with previous */
712                 next_range->from = from;
713         } else {
714                 /* Insert it */
715                 new_range = mono_mempool_alloc (cfg->mempool, sizeof (MonoLiveRange2));
716                 new_range->from = from;
717                 new_range->to = to;
718                 new_range->next = NULL;
719
720                 if (prev_range)
721                         prev_range->next = new_range;
722                 else
723                         interval->range = new_range;
724                 if (next_range)
725                         new_range->next = next_range;
726                 else
727                         interval->last_range = new_range;
728         }
729
730         /* FIXME: Merge intersecting ranges */
731 }
732
733 void
734 mono_linterval_print (MonoLiveInterval *interval)
735 {
736         MonoLiveRange2 *range;
737
738         for (range = interval->range; range != NULL; range = range->next)
739                 printf ("[%x-%x] ", range->from, range->to);
740 }
741
742 void
743 mono_linterval_print_nl (MonoLiveInterval *interval)
744 {
745         mono_linterval_print (interval);
746         printf ("\n");
747 }
748
749 /**
750  * mono_linterval_convers:
751  *
752  *   Return whenever INTERVAL covers the position POS.
753  */
754 gboolean
755 mono_linterval_covers (MonoLiveInterval *interval, int pos)
756 {
757         MonoLiveRange2 *range;
758
759         for (range = interval->range; range != NULL; range = range->next) {
760                 if (pos >= range->from && pos <= range->to)
761                         return TRUE;
762                 if (range->from > pos)
763                         return FALSE;
764         }
765
766         return FALSE;
767 }
768
769 /**
770  * mono_linterval_get_intersect_pos:
771  *
772  *   Determine whenever I1 and I2 intersect, and if they do, return the first
773  * point of intersection. Otherwise, return -1.
774  */
775 gint32
776 mono_linterval_get_intersect_pos (MonoLiveInterval *i1, MonoLiveInterval *i2)
777 {
778         MonoLiveRange2 *r1, *r2;
779
780         /* FIXME: Optimize this */
781         for (r1 = i1->range; r1 != NULL; r1 = r1->next) {
782                 for (r2 = i2->range; r2 != NULL; r2 = r2->next) {
783                         if (r2->to > r1->from && r2->from < r1->to) {
784                                 if (r2->from <= r1->from)
785                                         return r1->from;
786                                 else
787                                         return r2->from;
788                         }
789                 }
790         }
791
792         return -1;
793 }
794  
795 /**
796  * mono_linterval_split
797  *
798  *   Split L at POS and store the newly created intervals into L1 and L2. POS becomes
799  * part of L2.
800  */
801 void
802 mono_linterval_split (MonoCompile *cfg, MonoLiveInterval *interval, MonoLiveInterval **i1, MonoLiveInterval **i2, int pos)
803 {
804         MonoLiveRange2 *r;
805
806         g_assert (pos > interval->range->from && pos <= interval->last_range->to);
807
808         *i1 = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
809         *i2 = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
810
811         for (r = interval->range; r; r = r->next) {
812                 if (pos > r->to) {
813                         /* Add it to the first child */
814                         mono_linterval_add_range (cfg, *i1, r->from, r->to);
815                 } else if (pos > r->from && pos <= r->to) {
816                         /* Split at pos */
817                         mono_linterval_add_range (cfg, *i1, r->from, pos - 1);
818                         mono_linterval_add_range (cfg, *i2, pos, r->to);
819                 } else {
820                         /* Add it to the second child */
821                         mono_linterval_add_range (cfg, *i2, r->from, r->to);
822                 }
823         }
824 }
825
826 #if 0
827 #define LIVENESS_DEBUG(a) do { a; } while (0)
828 #else
829 #define LIVENESS_DEBUG(a)
830 #endif
831
832 static inline void
833 update_liveness2 (MonoCompile *cfg, MonoInst *ins, gboolean set_volatile, int inst_num, gint32 *last_use)
834 {
835         const char *spec = INS_INFO (ins->opcode);
836         int sreg;
837
838         LIVENESS_DEBUG (printf ("\t%x: ", inst_num); mono_print_ins (ins));
839
840         if (ins->opcode == OP_NOP)
841                 return;
842
843         /* DREG */
844         if ((spec [MONO_INST_DEST] != ' ') && get_vreg_to_inst (cfg, ins->dreg)) {
845                 MonoInst *var = get_vreg_to_inst (cfg, ins->dreg);
846                 int idx = var->inst_c0;
847                 MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
848
849                 if (MONO_IS_STORE_MEMBASE (ins)) {
850                         if (last_use [idx] == 0) {
851                                 LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", ins->dreg, inst_num));
852                                 last_use [idx] = inst_num;
853                         }
854                 } else {
855                         if (last_use [idx] > 0) {
856                                 LIVENESS_DEBUG (printf ("\tadd range to R%d: [%x, %x)\n", ins->dreg, inst_num, last_use [idx]));
857                                 mono_linterval_add_range (cfg, vi->interval, inst_num, last_use [idx]);
858                                 last_use [idx] = 0;
859                         }
860                         else {
861                                 /* Try dead code elimination */
862                                 if ((var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT)) && ((ins->opcode == OP_ICONST) || (ins->opcode == OP_I8CONST) || (ins->opcode == OP_R8CONST))) {
863                                         LIVENESS_DEBUG (printf ("\tdead def of R%d, eliminated\n", ins->dreg));
864                                         ins->opcode = OP_NOP;
865                                         ins->dreg = ins->sreg1 = ins->sreg2 = -1;
866                                         return;
867                                 }
868
869                                 LIVENESS_DEBUG (printf ("\tdead def of R%d, add range to R%d: [%x, %x]\n", ins->dreg, ins->dreg, inst_num, inst_num + 1));
870                                 mono_linterval_add_range (cfg, vi->interval, inst_num, inst_num + 1);
871                         }
872                 }
873         }
874
875         /* SREG1 */
876         sreg = ins->sreg1;
877         if ((spec [MONO_INST_SRC1] != ' ') && get_vreg_to_inst (cfg, sreg)) {
878                 MonoInst *var = get_vreg_to_inst (cfg, sreg);
879                 int idx = var->inst_c0;
880
881                 if (last_use [idx] == 0) {
882                         LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num));
883                         last_use [idx] = inst_num;
884                 }
885         }
886
887         /* SREG2 */
888         sreg = ins->sreg2;
889         if ((spec [MONO_INST_SRC2] != ' ') && get_vreg_to_inst (cfg, sreg)) {
890                 MonoInst *var = get_vreg_to_inst (cfg, sreg);
891                 int idx = var->inst_c0;
892
893                 if (last_use [idx] == 0) {
894                         LIVENESS_DEBUG (printf ("\tlast use of R%d set to %x\n", sreg, inst_num));
895                         last_use [idx] = inst_num;
896                 }
897         }
898 }
899
900 static void
901 mono_analyze_liveness2 (MonoCompile *cfg)
902 {
903         int bnum, idx, i, j, nins, rem, max, max_vars, block_from, block_to, pos, reverse_len;
904         gint32 *last_use;
905         static guint32 disabled = -1;
906         MonoInst **reverse;
907
908         if (disabled == -1)
909                 disabled = getenv ("DISABLED") != NULL;
910
911         if (disabled)
912                 return;
913
914         LIVENESS_DEBUG (printf ("LIVENESS 2 %s\n", mono_method_full_name (cfg->method, TRUE)));
915
916         /*
917         if (strstr (cfg->method->name, "test_") != cfg->method->name)
918                 return;
919         */
920
921         max_vars = cfg->num_varinfo;
922         last_use = g_new0 (gint32, max_vars);
923
924         reverse_len = 1024;
925         reverse = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * reverse_len);
926
927         for (idx = 0; idx < max_vars; ++idx) {
928                 MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
929
930                 vi->interval = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
931         }
932
933         /*
934          * Process bblocks in reverse order, so the addition of new live ranges
935          * to the intervals is faster.
936          */
937         for (bnum = cfg->num_bblocks - 1; bnum >= 0; --bnum) {
938                 MonoBasicBlock *bb = cfg->bblocks [bnum];
939                 MonoInst *ins;
940
941                 block_from = (bb->dfn << 16) + 1; /* so pos > 0 */
942                 if (bnum < cfg->num_bblocks - 1)
943                         /* Beginning of the next bblock */
944                         block_to = (cfg->bblocks [bnum + 1]->dfn << 16) + 1;
945                 else
946                         block_to = (bb->dfn << 16) + 0xffff;
947
948                 LIVENESS_DEBUG (printf ("LIVENESS BLOCK BB%d:\n", bb->block_num));
949
950                 memset (last_use, 0, max_vars * sizeof (gint32));
951                 
952                 /* For variables in bb->live_out, set last_use to block_to */
953
954                 rem = max_vars % BITS_PER_CHUNK;
955                 max = ((max_vars + (BITS_PER_CHUNK -1)) / BITS_PER_CHUNK);
956                 for (j = 0; j < max; ++j) {
957                         gsize bits_out;
958                         int k;
959
960                         bits_out = mono_bitset_get_fast (bb->live_out_set, j);
961                         k = (j * BITS_PER_CHUNK);       
962                         while (bits_out) {
963                                 if (bits_out & 1) {
964                                         LIVENESS_DEBUG (printf ("Var R%d live at exit, set last_use to %x\n", cfg->varinfo [k]->dreg, block_to));
965                                         last_use [k] = block_to;
966                                 }
967                                 bits_out >>= 1;
968                                 k ++;
969                         }
970                 }
971
972                 if (cfg->ret)
973                         last_use [cfg->ret->inst_c0] = block_to;
974
975                 for (nins = 0, pos = block_from, ins = bb->code; ins; ins = ins->next, ++nins, ++pos) {
976                         if (nins >= reverse_len) {
977                                 int new_reverse_len = reverse_len * 2;
978                                 MonoInst **new_reverse = mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * new_reverse_len);
979                                 memcpy (new_reverse, reverse, sizeof (MonoInst*) * reverse_len);
980                                 reverse = new_reverse;
981                                 reverse_len = new_reverse_len;
982                         }
983
984                         reverse [nins] = ins;
985                 }
986
987                 /* Process instructions backwards */
988                 for (i = nins - 1; i >= 0; --i) {
989                         MonoInst *ins = (MonoInst*)reverse [i];
990
991                         update_liveness2 (cfg, ins, FALSE, pos, last_use);
992
993                         pos --;
994                 }
995
996                 for (idx = 0; idx < max_vars; ++idx) {
997                         MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
998
999                         if (last_use [idx] != 0) {
1000                                 /* Live at exit, not written -> live on enter */
1001                                 LIVENESS_DEBUG (printf ("Var R%d live at enter, add range to R%d: [%x, %x)\n", cfg->varinfo [idx]->dreg, cfg->varinfo [idx]->dreg, block_from, last_use [idx]));
1002                                 mono_linterval_add_range (cfg, vi->interval, block_from, last_use [idx]);
1003                         }
1004                 }
1005         }
1006
1007         /*
1008          * Arguments need to have their live ranges extended to the beginning of
1009          * the method to account for the arg reg/memory -> global register copies
1010          * in the prolog (bug #74992).
1011          */
1012         for (i = 0; i < max_vars; i ++) {
1013                 MonoMethodVar *vi = MONO_VARINFO (cfg, i);
1014                 if (cfg->varinfo [vi->idx]->opcode == OP_ARG)
1015                         mono_linterval_add_range (cfg, vi->interval, 0, 1);
1016         }
1017
1018 #if 0
1019         for (idx = 0; idx < max_vars; ++idx) {
1020                 MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
1021                 
1022                 LIVENESS_DEBUG (printf ("LIVENESS R%d: ", cfg->varinfo [idx]->dreg));
1023                 LIVENESS_DEBUG (mono_linterval_print (vi->interval));
1024                 LIVENESS_DEBUG (printf ("\n"));
1025         }
1026 #endif
1027
1028         g_free (last_use);
1029 }
1030
1031 static void
1032 update_used (MonoCompile *cfg, MonoInst *inst, MonoBitSet *used)
1033 {
1034         int arity = mono_burg_arity [inst->opcode];
1035
1036         if (arity)
1037                 update_used (cfg, inst->inst_i0, used);
1038
1039         if (arity > 1)
1040                 update_used (cfg, inst->inst_i1, used);
1041
1042         if (inst->ssa_op & MONO_SSA_LOAD_STORE) {
1043                 if (inst->ssa_op == MONO_SSA_LOAD) {
1044                         int idx = inst->inst_i0->inst_c0;
1045
1046                         mono_bitset_set_fast (used, idx);
1047                 }
1048         }
1049
1050
1051 /**
1052  * optimize_initlocals:
1053  *
1054  * Try to optimize away some of the redundant initialization code inserted because of
1055  * 'locals init' using the liveness information.
1056  */
1057 static void
1058 optimize_initlocals (MonoCompile *cfg)
1059 {
1060         MonoBitSet *used;
1061         MonoInst *ins;
1062         MonoBasicBlock *initlocals_bb;
1063
1064         used = mono_bitset_new (cfg->num_varinfo, 0);
1065
1066         mono_bitset_clear_all (used);
1067         initlocals_bb = cfg->bb_entry->next_bb;
1068         MONO_BB_FOR_EACH_INS (initlocals_bb, ins)
1069                 update_used (cfg, ins, used);
1070
1071         MONO_BB_FOR_EACH_INS (initlocals_bb, ins) {
1072                 if (ins->ssa_op == MONO_SSA_STORE) {
1073                         int idx = ins->inst_i0->inst_c0;
1074                         MonoInst *var = cfg->varinfo [idx];
1075
1076                         if (var && !mono_bitset_test_fast (used, idx) && !mono_bitset_test_fast (initlocals_bb->live_out_set, var->inst_c0) && (var != cfg->ret) && !(var->flags & (MONO_INST_VOLATILE|MONO_INST_INDIRECT))) {
1077                                 if (ins->inst_i1 && ((ins->inst_i1->opcode == OP_ICONST) || (ins->inst_i1->opcode == OP_I8CONST))) {
1078                                         NULLIFY_INS (ins);
1079                                         ins->ssa_op = MONO_SSA_NOP;
1080                                         MONO_VARINFO (cfg, var->inst_c0)->spill_costs -= 1;                                     
1081                                 }
1082                         }
1083                 }
1084         }
1085
1086         g_free (used);
1087 }
1088