- more work on the Unknown problems: the only
[libfirm] / ir / opt / escape_ana.c
1 /*
2  * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19
20 /*
21  * Project:     libFIRM
22  * File name:   ir/opt/escape_ana.c
23  * Purpose:     escape analysis and optimization
24  * Author:      Michael Beck
25  * Modified by:
26  * Created:     03.11.2005
27  * CVS-ID:      $Id$
28  * Copyright:   (c) 1999-2005 Universität Karlsruhe
29  */
30
31 /**
32  * @file escape_ana.c
33  *
34  * A fast and simple Escape analysis.
35  */
36 #include "config.h"
37
38 #include "iroptimize.h"
39
40 #include "irgraph_t.h"
41 #include "irnode_t.h"
42 #include "type_t.h"
43 #include "irgwalk.h"
44 #include "irouts.h"
45 #include "analyze_irg_args.h"
46 #include "irgmod.h"
47 #include "ircons.h"
48 #include "irprintf.h"
49 #include "debug.h"
50
51 /**
52  * walker environment
53  */
54 typedef struct _walk_env {
55   ir_node *found_allocs;            /**< list of all found non-escaped allocs */
56   ir_node *dead_allocs;             /**< list of all found dead alloc */
57   check_alloc_entity_func callback; /**< callback that checks a given entity for allocation */
58   unsigned nr_removed;              /**< number of removed allocs (placed of frame) */
59   unsigned nr_changed;              /**< number of changed allocs (allocated on stack now) */
60   unsigned nr_deads;                /**< number of dead allocs */
61
62   /* these fields are only used in the global escape analysis */
63   ir_graph *irg;                    /**< the irg for this environment */
64   struct _walk_env *next;           /**< for linking environments */
65
66 } walk_env_t;
67
68 /** debug handle */
69 DEBUG_ONLY(firm_dbg_module_t *dbgHandle;)
70
71 /**
72  * checks whether a Raise leaves a method
73  */
74 static int is_method_leaving_raise(ir_node *raise)
75 {
76   int i;
77   ir_node *proj = NULL;
78   ir_node *n;
79
80   for (i = get_irn_n_outs(raise) - 1; i >= 0; --i) {
81     ir_node *succ = get_irn_out(raise, i);
82
83     /* there should be only one ProjX node */
84     if (get_Proj_proj(succ) == pn_Raise_X) {
85       proj = succ;
86       break;
87     }
88   }
89
90   if (! proj) {
91     /* Hmm: no ProjX from a Raise? This should be a verification
92      * error. For now we just assert and return.
93      */
94     assert(! "No ProjX after Raise found");
95     return 1;
96   }
97
98   if (get_irn_n_outs(proj) != 1) {
99     /* Hmm: more than one user of ProjX: This is a verification
100      * error.
101      */
102     assert(! "More than one user of ProjX");
103     return 1;
104   }
105
106   n = get_irn_out(proj, 0);
107   assert(is_Block(n) && "Argh: user of ProjX is no block");
108
109   if (n == get_irg_end_block(get_irn_irg(n)))
110     return 1;
111
112   /* ok, we get here so the raise will not leave the function */
113   return 0;
114 }
115
116 /**
117  * returns an Alloc node if the node adr Select
118  * from one
119  */
120 static ir_node *is_depend_alloc(ir_node *adr) {
121   ir_node *alloc;
122
123   if (!is_Sel(adr))
124     return NULL;
125
126   /* should be a simple Sel */
127   if (get_Sel_n_indexs(adr) != 0)
128     return NULL;
129
130   alloc = skip_Proj(get_Sel_ptr(adr));
131   if (!is_Alloc(alloc))
132     return NULL;
133
134   /* hmm, we depend on this Alloc */
135   ir_printf("depend alloc %+F\n", alloc);
136
137   return NULL;
138 }
139
140 /**
141  * determine if a value calculated by n "escape", ie
142  * is stored somewhere we could not track
143  */
144 static int can_escape(ir_node *n) {
145   int i, j, k;
146
147   /* should always be pointer mode or we made some mistake */
148   assert(mode_is_reference(get_irn_mode(n)));
149
150   for (i = get_irn_n_outs(n) - 1; i >= 0; --i) {
151     ir_node *succ = get_irn_out(n, i);
152
153     switch (get_irn_opcode(succ)) {
154     case iro_Store:
155       if (get_Store_value(succ) == n) {
156         ir_node *adr = get_Store_ptr(succ);
157
158         /*
159          * if this Alloc depends on another one,
160          * we can enqueue it
161          */
162         if (is_depend_alloc(adr))
163           break;
164
165         /*
166          * We are storing n. As long as we do not further
167          * evaluate things, the pointer 'escape' here
168          */
169         return 1;
170       }
171       break;
172
173     case iro_Conv:
174       /*
175        * Should not happen, but if it does we leave the pointer
176        * path and do not track further
177        */
178       return 1;
179
180     case iro_Call: { /* most complicated case */
181       ir_node *ptr = get_Call_ptr(succ);
182       ir_entity *ent;
183
184       if (is_SymConst_addr_ent(ptr)) {
185         ent = get_SymConst_entity(ptr);
186
187         /* we know the called entity */
188         for (j = get_Call_n_params(succ) - 1; j >= 0; --j) {
189           if (get_Call_param(succ, j) == n) {
190             /* n is the j'th param of the call */
191             if (get_method_param_access(ent, j) & ptr_access_store)
192               /* n is store in ent */
193               return 1;
194           }
195         }
196       }
197       else if (is_Sel(ptr)) {
198         /* go through all possible callees */
199         for (k = get_Call_n_callees(succ) - 1; k >= 0; --k) {
200           ent = get_Call_callee(succ, k);
201
202           if (ent == unknown_entity) {
203             /* we don't know what will be called, a possible escape */
204             return 1;
205           }
206
207           for (j = get_Call_n_params(succ) - 1; j >= 0; --j) {
208             if (get_Call_param(succ, j) == n) {
209               /* n is the j'th param of the call */
210               if (get_method_param_access(ent, j) & ptr_access_store)
211                 /* n is store in ent */
212                 return 1;
213             }
214           }
215         }
216       }
217       else /* we don't know want will called */
218         return 1;
219
220       break;
221     }
222
223     case iro_Return:
224       /* Bad: the allocate object is returned */
225       return 1;
226
227     case iro_Raise:
228       /* Hmm: if we do NOT leave the method, it's local */
229       if (is_method_leaving_raise(succ))
230         return 1;
231       break;
232
233     case iro_Tuple: {
234       ir_node *proj;
235
236       /* Bad: trace the tuple backwards */
237       for (j = get_irn_arity(succ) - 1; j >= 0; --j)
238         if (get_irn_n(succ, j) == n)
239           break;
240
241       assert(j >= 0);
242
243
244       for (k = get_irn_n_outs(succ); k >= 0; --k) {
245         proj = get_irn_out(succ, k);
246
247         if (get_Proj_proj(proj) == j) {
248           /* we found the right Proj */
249           succ = proj;
250           break;
251         }
252       }
253
254       /*
255        * If we haven't found the right Proj, succ is still
256        * the Tuple and the search will end here.
257        */
258       break;
259     }
260
261     default:
262       break;
263
264     }
265
266     if (! mode_is_reference(get_irn_mode(succ)))
267       continue;
268
269     if (can_escape(succ))
270       return 1;
271   }
272   return 0;
273 }
274
275 /**
276  * walker: search for Alloc nodes and follow the usages
277  */
278 static void find_allocations(ir_node *alloc, void *ctx)
279 {
280   int i;
281   ir_node *adr;
282   walk_env_t *env = ctx;
283
284   if (! is_Alloc(alloc))
285     return;
286
287   /* we searching only for heap allocations */
288   if (get_Alloc_where(alloc) != heap_alloc)
289     return;
290
291   adr = NULL;
292   for (i = get_irn_n_outs(alloc) - 1; i >= 0; --i) {
293     ir_node *proj = get_irn_out(alloc, i);
294
295     if (get_Proj_proj(proj) == pn_Alloc_res) {
296       adr = proj;
297       break;
298     }
299   }
300
301   if (! adr) {
302     /*
303      * bad: no-one wants the result, should NOT happen but
304      * if it does we could delete it.
305      */
306     set_irn_link(alloc, env->dead_allocs);
307     env->dead_allocs = alloc;
308
309     return;
310   }
311
312   if (! can_escape(adr)) {
313     set_irn_link(alloc, env->found_allocs);
314     env->found_allocs = alloc;
315   }
316 }
317
318 /**
319  * walker: search for allocation Call nodes and follow the usages
320  */
321 static void find_allocation_calls(ir_node *call, void *ctx)
322 {
323   int        i;
324   ir_node    *adr;
325   ir_entity  *ent;
326   walk_env_t *env = ctx;
327
328   if (! is_Call(call))
329     return;
330   adr = get_Call_ptr(call);
331   if (! is_SymConst_addr_ent(adr))
332     return;
333   ent = get_SymConst_entity(adr);
334   if (! env->callback(ent))
335     return;
336
337   adr = NULL;
338   for (i = get_irn_n_outs(call) - 1; i >= 0; --i) {
339     ir_node *res_proj = get_irn_out(call, i);
340
341     if (get_Proj_proj(res_proj) == pn_Call_T_result) {
342       for (i = get_irn_n_outs(res_proj) - 1; i >= 0; --i) {
343         ir_node *proj = get_irn_out(res_proj, i);
344
345         if (get_Proj_proj(proj) == 0) {
346           /* found first result */
347           adr = proj;
348           break;
349         }
350       }
351       break;
352     }
353   }
354
355   if (! adr) {
356     /*
357      * bad: no-one wants the result, should NOT happen but
358      * if it does we could delete it.
359      */
360     set_irn_link(call, env->dead_allocs);
361     env->dead_allocs = call;
362
363     return;
364   }
365
366   if (! can_escape(adr)) {
367     set_irn_link(call, env->found_allocs);
368     env->found_allocs = call;
369   }
370 }
371
372 /**
373  * Do the necessary graph transformations to transform
374  * Alloc nodes.
375  */
376 static void transform_allocs(ir_graph *irg, walk_env_t *env)
377 {
378   ir_node *alloc, *next, *mem, *sel, *size, *blk;
379   ir_type *ftp, *atp, *tp;
380   ir_entity *ent;
381   char name[128];
382   unsigned nr = 0;
383   dbg_info *dbg;
384
385   /* kill all dead allocs */
386   for (alloc = env->dead_allocs; alloc; alloc = next) {
387     next = get_irn_link(alloc);
388
389     DBG((dbgHandle, LEVEL_1, "%+F allocation of %+F unused, deleted.\n", irg, alloc));
390
391     mem = get_Alloc_mem(alloc);
392         blk = get_nodes_block(alloc);
393     turn_into_tuple(alloc, pn_Alloc_max);
394     set_Tuple_pred(alloc, pn_Alloc_M, mem);
395     set_Tuple_pred(alloc, pn_Alloc_X_regular, new_r_Jmp(irg, blk));
396     set_Tuple_pred(alloc, pn_Alloc_X_except, new_r_Bad(irg));
397
398     ++env->nr_deads;
399   }
400
401   /* convert all non-escaped heap allocs into frame variables */
402   ftp = get_irg_frame_type(irg);
403   for (alloc = env->found_allocs; alloc; alloc = next) {
404     next = get_irn_link(alloc);
405     size = get_Alloc_size(alloc);
406     atp  = get_Alloc_type(alloc);
407
408     tp = NULL;
409     if (is_SymConst(size) && get_SymConst_kind(size) == symconst_type_size)  {
410       /* if the size is a type size and the types matched */
411       assert(atp == get_SymConst_type(size));
412       tp = atp;
413     }
414     else if (is_Const(size)) {
415       tarval *tv = get_Const_tarval(size);
416
417       if (tv != tarval_bad && tarval_is_long(tv) &&
418           get_type_state(atp) == layout_fixed &&
419           (unsigned)get_tarval_long(tv) == get_type_size_bytes(atp)) {
420         /* a already lowered type size */
421         tp = atp;
422       }
423     }
424
425     if (tp && tp != firm_unknown_type) {
426       /* we could determine the type, so we could place it on the frame */
427       dbg  = get_irn_dbg_info(alloc);
428       blk  = get_nodes_block(alloc);
429
430       DBG((dbgHandle, LEVEL_DEFAULT, "%+F allocation of %+F type %+F placed on frame\n", irg, alloc, tp));
431
432       snprintf(name, sizeof(name), "%s_NE_%u", get_entity_name(get_irg_entity(irg)), nr++);
433       name[sizeof(name) - 1] = '\0';
434       ent = new_d_entity(ftp, new_id_from_str(name), get_Alloc_type(alloc), dbg);
435
436       sel = new_rd_simpleSel(dbg, irg, get_nodes_block(alloc),
437         get_irg_no_mem(irg), get_irg_frame(irg), ent);
438       mem = get_Alloc_mem(alloc);
439
440       turn_into_tuple(alloc, pn_Alloc_max);
441       set_Tuple_pred(alloc, pn_Alloc_M, mem);
442           set_Tuple_pred(alloc, pn_Alloc_X_regular, new_r_Jmp(irg, blk));
443       set_Tuple_pred(alloc, pn_Alloc_X_except, new_r_Bad(irg));
444       set_Tuple_pred(alloc, pn_Alloc_res, sel);
445
446       ++env->nr_removed;
447     }
448     else {
449       /*
450        * We could not determine the type or it is variable size.
451        * At least, we could place it on the stack
452        */
453       DBG((dbgHandle, LEVEL_DEFAULT, "%+F allocation of %+F type %+F placed on stack\n", irg, alloc));
454       set_Alloc_where(alloc, stack_alloc);
455
456       ++env->nr_changed;
457     }
458   }
459
460   /* if allocs were removed somehow */
461   if (env->nr_removed | env->nr_deads) {
462     set_irg_outs_inconsistent(irg);
463
464     if (env->nr_deads) {
465       /* exception control flow might have been changed */
466       set_irg_doms_inconsistent(irg);
467     }
468   }
469 }
470
471 /**
472  * Do the necessary graph transformations to transform
473  * Call nodes.
474  */
475 static void transform_alloc_calls(ir_graph *irg, walk_env_t *env)
476 {
477   ir_node *call, *next, *mem, *blk;
478   ir_type *ftp;
479
480   /* kill all dead allocs */
481   for (call = env->dead_allocs; call; call = next) {
482     next = get_irn_link(call);
483
484     DBG((dbgHandle, LEVEL_1, "%+F allocation of %+F unused, deleted.\n", irg, call));
485
486     mem = get_Call_mem(call);
487         blk = get_nodes_block(call);
488     turn_into_tuple(call, pn_Call_max);
489     set_Tuple_pred(call, pn_Call_M_regular, mem);
490         set_Tuple_pred(call, pn_Call_X_regular, new_r_Jmp(irg, blk));
491     set_Tuple_pred(call, pn_Call_X_except, new_r_Bad(irg));
492     set_Tuple_pred(call, pn_Call_T_result, new_r_Bad(irg));
493     set_Tuple_pred(call, pn_Call_M_except, mem);
494     set_Tuple_pred(call, pn_Call_P_value_res_base, new_r_Bad(irg));
495
496     ++env->nr_deads;
497   }
498
499   /* convert all non-escaped heap allocs into frame variables */
500   ftp = get_irg_frame_type(irg);
501   for (call = env->found_allocs; call; call = next) {
502     next = get_irn_link(call);
503   }
504 }
505
506
507 /* Do simple and fast escape analysis for one graph. */
508 void escape_enalysis_irg(ir_graph *irg, check_alloc_entity_func callback)
509 {
510   walk_env_t env;
511
512   if (get_irg_callee_info_state(irg) != irg_callee_info_consistent) {
513     /* no way yet to calculate this for one irg */
514     assert(! "need callee info");
515     return;
516   }
517
518   if (get_irg_outs_state(irg) != outs_consistent)
519     compute_irg_outs(irg);
520
521   env.found_allocs = NULL;
522   env.dead_allocs  = NULL;
523   env.callback     = callback;
524   env.nr_removed   = 0;
525   env.nr_changed   = 0;
526   env.nr_deads     = 0;
527
528   if (callback) {
529     /* search for Calls */
530     irg_walk_graph(irg, NULL, find_allocation_calls, &env);
531     transform_alloc_calls(irg, &env);
532   } else {
533     /* search for Alloc nodes */
534     irg_walk_graph(irg, NULL, find_allocations, &env);
535     transform_allocs(irg, &env);
536   }
537 }
538
539 /* Do simple and fast escape analysis for all graphs. */
540 void escape_analysis(int run_scalar_replace, check_alloc_entity_func callback)
541 {
542   ir_graph *irg;
543   int i;
544   struct obstack obst;
545   walk_env_t *env, *elist;
546   (void) run_scalar_replace;
547
548   if (get_irp_callee_info_state() != irg_callee_info_consistent) {
549     assert(! "need callee info");
550     return;
551   }
552
553   FIRM_DBG_REGISTER(dbgHandle, "firm.opt.escape_ana");
554
555   /*
556    * We treat memory for speed: we first collect all info in a
557    * list of environments, than do the transformation.
558    * Doing it this way, no analysis info gets invalid while we run
559    * over graphs
560    */
561   obstack_init(&obst);
562   elist = NULL;
563
564   env = obstack_alloc(&obst, sizeof(*env));
565   env->found_allocs = NULL;
566   env->dead_allocs  = NULL;
567   env->callback     = callback;
568
569   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
570     irg = get_irp_irg(i);
571
572     assure_irg_outs(irg);
573
574     if (callback) {
575       /* search for Calls */
576       irg_walk_graph(irg, NULL, find_allocation_calls, env);
577     } else {
578       /* search for Alloc nodes */
579       irg_walk_graph(irg, NULL, find_allocations, env);
580     }
581
582     if (env->found_allocs || env->dead_allocs) {
583       env->nr_removed   = 0;
584       env->nr_deads     = 0;
585       env->irg          = irg;
586       env->next         = elist;
587
588       elist = env;
589
590       env = obstack_alloc(&obst, sizeof(*env));
591       env->found_allocs = NULL;
592       env->dead_allocs  = NULL;
593       env->callback     = callback;
594     }
595   }
596
597   if (callback) {
598     for (env = elist; env; env = env->next) {
599       transform_alloc_calls(env->irg, env);
600     }
601   } else {
602     for (env = elist; env; env = env->next) {
603       transform_allocs(env->irg, env);
604     }
605   }
606
607   obstack_free(&obst, NULL);
608 }