50449fce219949ef188762ed1a6d5b0225976de2
[libfirm] / ir / ana / irouts.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/ana/irouts.c
4  * Purpose:     Compute and access out edges.
5  * Author:      Goetz Lindenmaier
6  * Modified by:
7  * Created:     1.2002
8  * CVS-ID:      $Id$
9  * Copyright:   (c) 2002-2003 Universität Karlsruhe
10  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
11  */
12
13
14
15  /* Copyright (C) 2002 by Universitaet Karlsruhe
16 * All rights reserved.
17 *
18 * Authors:  Goetz Lindenmaier
19 *
20 * irouts.c --- Compute out edges for ir nodes (also called def-use
21 * edges).
22 *
23 */
24
25 /* $Id$ */
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include "irouts.h"
31 #include "irnode_t.h"
32 #include "irgraph_t.h"     /* To access irg->outs field (which is private to this module)
33                               without public access routine */
34 #include "irprog.h"
35
36 /**********************************************************************/
37 /** Accessing the out datastructures                                 **/
38 /**********************************************************************/
39
40 /* returns the number of successors of the node: */
41 INLINE int get_irn_n_outs    (ir_node *node) {
42   return (int)(node->out[0]);
43 }
44
45 /* Access successor n */
46 INLINE ir_node *get_irn_out      (ir_node *node, int pos) {
47   assert(node);
48   assert(pos >= 0 && pos < get_irn_n_outs(node));
49   return node->out[pos+1];
50 }
51
52 INLINE void set_irn_out      (ir_node *node, int pos, ir_node *out) {
53   assert(node && out);
54   assert(pos >= 0 && pos < get_irn_n_outs(node));
55   node->out[pos+1] = out;
56 }
57
58
59 INLINE int get_Block_n_cfg_outs (ir_node *bl) {
60   int i, n_cfg_outs = 0;
61   assert(bl && (get_irn_op(bl) == op_Block));
62   for (i = 0; i < (int)bl->out[0]; i++)
63     if ((get_irn_mode(bl->out[i+1]) == mode_X) &&
64         (get_irn_op(bl->out[i+1]) != op_End)) n_cfg_outs++;
65   return n_cfg_outs;
66 }
67
68
69 INLINE ir_node *get_Block_cfg_out  (ir_node *bl, int pos) {
70   int i, out_pos = 0;
71   assert(bl && (get_irn_op(bl) == op_Block));
72   for (i = 0; i < (int)bl->out[0]; i++)
73     if ((get_irn_mode(bl->out[i+1]) == mode_X)  &&
74         (get_irn_op(bl->out[i+1]) != op_End)) {
75       if (out_pos == pos) {
76         ir_node *cfop = bl->out[i+1];
77         return cfop->out[0+1];
78       } else {
79         out_pos++;
80       }
81     }
82   return NULL;
83 }
84
85 void irg_out_walk_2(ir_node *node,  irg_walk_func *pre,
86                     irg_walk_func *post, void *env) {
87   int i;
88   ir_node *succ;
89
90   assert(node);
91   assert(get_irn_visited(node) < get_irg_visited(current_ir_graph));
92
93   set_irn_visited(node, get_irg_visited(current_ir_graph));
94
95   if (pre) pre(node, env);
96
97   for (i = 0; i < get_irn_n_outs(node); i++) {
98     succ = get_irn_out(node, i);
99     if (get_irn_visited(succ) < get_irg_visited(current_ir_graph))
100       irg_out_walk_2(succ, pre, post, env);
101   }
102
103   if (post) post(node, env);
104
105   return;
106 }
107
108 void irg_out_walk(ir_node *node,
109                         irg_walk_func *pre, irg_walk_func *post,
110                         void *env) {
111   assert(node);
112   if (get_irg_outs_state(current_ir_graph) != no_outs) {
113     inc_irg_visited (current_ir_graph);
114     irg_out_walk_2(node, pre, post, env);
115   }
116   return;
117 }
118
119 void irg_out_block_walk2(ir_node *bl,
120                         irg_walk_func *pre, irg_walk_func *post,
121                         void *env) {
122   int i;
123
124   assert(get_irn_opcode(bl) == iro_Block);
125
126   if(get_Block_block_visited(bl) < get_irg_block_visited(current_ir_graph)) {
127     set_Block_block_visited(bl, get_irg_block_visited(current_ir_graph));
128
129     if(pre)
130       pre(bl, env);
131
132     for(i = 0; i < get_Block_n_cfg_outs(bl); i++) {
133       /* find the corresponding predecessor block. */
134       ir_node *pred = get_Block_cfg_out(bl, i);
135       assert(get_irn_opcode(pred) == iro_Block);
136       /* recursion */
137       irg_out_block_walk2(pred, pre, post, env);
138     }
139
140     if(post)
141       post(bl, env);
142   }
143   return;
144 }
145
146 /* Walks only over Block nodes in the graph.  Has it's own visited
147    flag, so that it can be interleaved with the other walker.         */
148 void irg_out_block_walk(ir_node *node,
149                         irg_walk_func *pre, irg_walk_func *post,
150                         void *env) {
151
152   assert((get_irn_op(node) == op_Block) || (get_irn_mode(node) == mode_X));
153
154   inc_irg_block_visited(current_ir_graph);
155
156   if (get_irn_mode(node) == mode_X) node = node->out[1];
157   assert(get_irn_opcode(node)  == iro_Block);
158
159   irg_out_block_walk2(node, pre, post, env);
160
161   return;
162
163 }
164
165 /**********************************************************************/
166 /** Building and Removing the out datasturcture                      **/
167 /**                                                                  **/
168 /** The outs of a graph are allocated in a single, large array.      **/
169 /** This allows to allocate and deallocate the memory for the outs   **/
170 /** on demand.  The large array is separated into many small ones    **/
171 /** for each node.  Only a single field to reference the out array   **/
172 /** is stored in each node and a field referencing the large out     **/
173 /** array in irgraph.  The 0 field of each out array contains the    **/
174 /** size of this array.  This saves memory in the irnodes themselves.**/
175 /** The construction does two passes over the graph.  The first pass **/
176 /** counts the overall number of outs and the outs of each node.  It **/
177 /** stores the outs of each node in the out reference of the node.   **/
178 /** Then the large array is allocated.  The second iteration chops   **/
179 /** the large array into smaller parts, sets the out edges and       **/
180 /** recounts the out edges.                                          **/
181 /**********************************************************************/
182
183
184 /* Returns the amount of out edges for not yet visited successors. */
185 static int count_outs(ir_node *n) {
186   int start, i, res;
187   ir_node *succ;
188
189   set_irn_visited(n, get_irg_visited(current_ir_graph));
190   n->out = (ir_node **) 1;     /* Space for array size. */
191
192   if ((get_irn_op(n) == op_Block)) start = 0; else start = -1;
193   res = get_irn_arity(n) - start +1;  /* --1 or --0; 1 for array size. */
194   for (i = start; i < get_irn_arity(n); i++) {
195     /* Optimize Tuples.  They annoy if walking the cfg. */
196     succ = skip_Tuple(get_irn_n(n, i));
197     set_irn_n(n, i, succ);
198     /* count outs for successors */
199     if (get_irn_visited(succ) < get_irg_visited(current_ir_graph))
200       res += count_outs(succ);
201     /* Count my outs */
202     succ->out = (ir_node **)( (int)succ->out +1);
203   }
204   return res;
205 }
206
207 static ir_node **set_out_edges(ir_node *n, ir_node **free) {
208   int n_outs, start, i;
209   ir_node *succ;
210
211   set_irn_visited(n, get_irg_visited(current_ir_graph));
212
213   /* Allocate my array */
214   n_outs = (int) n->out;
215   n->out = free;
216   free = &free[n_outs];
217   /* We count the successors again, the space will be sufficient.
218      We use this counter to remember the position for the next back
219      edge. */
220   n->out[0] = (ir_node *)0;
221
222   if (get_irn_op(n) == op_Block) start = 0; else start = -1;
223   for (i = start; i < get_irn_arity(n); i++) {
224     succ = get_irn_n(n, i);
225     /* Recursion */
226     if (get_irn_visited(succ) < get_irg_visited(current_ir_graph))
227       free = set_out_edges(succ, free);
228     /* Remember our back edge */
229     succ->out[get_irn_n_outs(succ)+1] = n;
230     succ->out[0] = (ir_node *) (get_irn_n_outs(succ) + 1);
231   }
232   return free;
233 }
234
235 static INLINE void fix_start_proj(ir_graph *irg) {
236   ir_node *proj = NULL, *startbl;
237   int i;
238   if (get_Block_n_cfg_outs(get_irg_start_block(irg))) {
239     startbl = get_irg_start_block(irg);
240     for (i = 0; i < get_irn_n_outs(startbl); i++)
241       if (get_irn_mode(get_irn_out(startbl, i)) == mode_X)
242         proj = get_irn_out(startbl, i);
243     if (get_irn_out(proj, 0) == startbl) {
244       assert(get_irn_n_outs(proj) == 2);
245       set_irn_out(proj, 0, get_irn_out(proj, 1));
246       set_irn_out(proj, 1, startbl);
247     }
248   }
249 }
250
251 void compute_outs(ir_graph *irg) {
252   ir_graph *rem = current_ir_graph;
253   int n_out_edges = 0;
254
255   current_ir_graph = irg;
256
257   /* Update graph state */
258   assert(get_irg_phase_state(current_ir_graph) != phase_building);
259   current_ir_graph->outs_state = outs_consistent;
260
261   /* This first iteration counts the overall number of out edges and the
262      number of out edges for each node. */
263   inc_irg_visited(irg);
264   n_out_edges = count_outs(get_irg_end(irg));
265
266   /* allocate memory for all out edges. */
267   irg->outs = (ir_node **) malloc (n_out_edges * sizeof(ir_node *));
268
269   /* The second iteration splits the irg->outs array into smaller arrays
270      for each node and writes the back edges into this array. */
271   inc_irg_visited(irg);
272   set_out_edges(get_irg_end(irg), irg->outs);
273
274   /* We want that the out of ProjX from Start contains the next block at
275      position 1, the Start block at position 2.  This is necessary for
276      the out block walker. */
277   fix_start_proj(irg);
278
279   current_ir_graph = rem;
280 }
281
282
283 void compute_ip_outs(ir_graph *irg) { /*irg_walk_func *pre, irg_walk_func *post, void *env) { */
284   int i;
285   ir_graph *rem = current_ir_graph;
286   int rem_view = interprocedural_view;
287
288   interprocedural_view = true;
289
290   inc_max_irg_visited();
291   /* Fix all irg_visited flags */
292   for (i = 0; i < get_irp_n_irgs(); i++)
293     set_irg_visited(get_irp_irg(i), get_max_irg_visited());
294
295   /* Walk starting at unreachable procedures. Only these
296    * have End blocks visible in interprocedural view. */
297   for (i = 0; i < get_irp_n_irgs(); i++) {
298     ir_node *sb;
299     current_ir_graph = get_irp_irg(i);
300
301     sb = get_irg_start_block(current_ir_graph);
302
303     if ((get_Block_n_cfgpreds(sb) > 1) ||
304         (get_nodes_block(get_Block_cfgpred(sb, 0)) != sb)) continue;
305
306     compute_outs(current_ir_graph); /*cg_walk_2(get_irg_end(current_ir_graph), pre, post, env);*/
307   }
308
309   /* Check whether we walked all procedures: there could be procedures
310      with cyclic calls but no call from the outside. */
311   for (i = 0; i < get_irp_n_irgs(); i++) {
312     ir_node *sb;
313     current_ir_graph = get_irp_irg(i);
314
315     /* Test start block: if inner procedure end and end block are not
316      * visible and therefore not marked. */
317     sb = get_irg_start_block(current_ir_graph);
318     if (get_irn_visited(sb) < get_irg_visited(current_ir_graph)) {
319       compute_outs(current_ir_graph); /*cg_walk_2(sb, pre, post, env);    */
320     }
321   }
322
323   /* Walk all endless loops in inner procedures.
324    * We recognize an inner procedure if the End node is not visited. */
325   for (i = 0; i < get_irp_n_irgs(); i++) {
326     ir_node *e;
327     current_ir_graph = get_irp_irg(i);
328     e = get_irg_end(current_ir_graph);
329     if (get_irn_visited(e) < get_irg_visited(current_ir_graph)) {
330       int j;
331       /* Don't visit the End node. */
332       /*   for (j = 0; j < get_End_n_keepalives(e); j++)
333            cg_walk_2(get_End_keepalive(e, j), pre, post, env);*/
334       compute_outs(current_ir_graph);
335     }
336   }
337
338   interprocedural_view = rem_view;
339   current_ir_graph = rem;
340 }
341
342
343
344
345 void free_outs(ir_graph *irg) {
346
347   /* Update graph state */
348   assert(get_irg_phase_state(current_ir_graph) != phase_building);
349   current_ir_graph->outs_state = no_outs;
350
351   if (irg->outs) free(irg->outs);
352   irg->outs = NULL;
353 }