- get_Block_cfgpred_arr() IS supported, but should not be in the official
[libfirm] / ir / stat / dags.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  * @file
22  * @brief   Statistics for Firm. DAG's in graphs.
23  * @author  Michael Beck
24  * @version $Id$
25  */
26 #include "config.h"
27
28 #include <assert.h>
29
30 #include "irprintf.h"
31 #include "irdump.h"
32 #include "dags.h"
33 #include "irtools.h"
34
35 enum dag_counting_options_t {
36         FIRMSTAT_COPY_CONSTANTS = 0x00000001,  /**< if set, constants will be treated as they are in
37                                                     the same block as its successors */
38         FIRMSTAT_LOAD_IS_LEAVE  = 0x00000002,  /**< Load nodes are always leaves */
39         FIRMSTAT_CALL_IS_LEAVE  = 0x00000004,  /**< Call nodes are always leaves */
40         FIRMSTAT_ARGS_ARE_ROOTS = 0x00000008,  /**< arguments (Proj(Proj(Start)) are roots */
41 };
42
43 typedef struct _dag_entry_t dag_entry_t;
44
45 /**
46  * Environment for connecting DAG's
47  */
48 typedef struct _dag_env_t {
49         struct obstack obst;
50         unsigned       num_of_dags;   /**< Number of found DAGs so far. */
51         dag_entry_t    *list_of_dags; /**< List of found DAGs. */
52         unsigned       options;       /**< DAG counting options. */
53 } dag_env_t;
54
55 /**
56  * a DAG Entry
57  */
58 struct _dag_entry_t {
59         unsigned    id;               /**< assigned ID for this DAG */
60         ir_node     *root;            /**< one root of the DAG */
61         unsigned    num_roots;        /**< number of root nodes in the DAG */
62         unsigned    num_nodes;        /**< overall number of nodes in the DAG */
63         unsigned    num_inner_nodes;  /**< number of inner nodes in the DAG */
64         unsigned    is_dead:1;        /**< marks a dead entry */
65         unsigned    is_tree:1;        /**< True if this DAG is a tree. */
66         unsigned    is_ext_ref:1;     /**< True if this DAG is external referenced, so it cannot be combined. */
67         dag_entry_t *next;            /**< link all entries of a DAG */
68         dag_entry_t *link;            /**< if set, this entry is an ID */
69 };
70
71 /**
72  * return an DAG entry for the node n
73  */
74 static dag_entry_t *get_irn_dag_entry(ir_node *n)
75 {
76         dag_entry_t *p = get_irn_link(n);
77
78         if (p) {
79                 /* skip any dead links */
80                 if (p->link) {
81                         do {
82                                 p = p->link;
83                         } while (p->link != NULL);
84                         set_irn_link(n, p);
85                 }
86         }  /* if */
87         return p;
88 }  /* get_irn_dag_entry */
89
90 #define set_irn_dag_entry(n, e) set_irn_link(n, e)
91
92 /**
93  * checks whether a node is an arg
94  */
95 static int is_arg(ir_node *node)
96 {
97         if (! is_Proj(node))
98                 return 0;
99
100         node = get_Proj_pred(node);
101         if (! is_Proj(node))
102                 return 0;
103
104         node = get_Proj_pred(node);
105         return is_Start(node);
106 }  /* is_arg */
107
108 /**
109  * Allocate a new DAG entry.
110  */
111 static dag_entry_t *new_dag_entry(dag_env_t *dag_env, ir_node *node) {
112         dag_entry_t *entry = obstack_alloc(&dag_env->obst, sizeof(*entry));
113
114         entry->num_nodes       = 1;
115         entry->num_roots       = 1;
116         entry->num_inner_nodes = 0;
117         entry->root            = node;
118         entry->is_dead         = 0;
119         entry->is_tree         = 1;
120         entry->is_ext_ref      = 0;
121         entry->next            = dag_env->list_of_dags;
122         entry->link            = NULL;
123
124         ++dag_env->num_of_dags;
125         dag_env->list_of_dags = entry;
126
127         set_irn_dag_entry(node, entry);
128         return entry;
129 }  /* new_dag_entry */
130
131 /**
132  * Post-walker to detect DAG roots that are referenced form other blocks
133  */
134 static void find_dag_roots(ir_node *node, void *env)
135 {
136         dag_env_t   *dag_env = env;
137         int         i, arity;
138         dag_entry_t *entry;
139         ir_node     *block;
140
141         if (is_Block(node))
142                 return;
143
144         block = get_nodes_block(node);
145
146         /* ignore start end end blocks */
147         if (block == get_irg_start_block(current_ir_graph) ||
148                 block == get_irg_end_block(current_ir_graph)) {
149                 return;
150         }  /* if */
151
152         /* Phi nodes always references nodes from "other" block */
153         if (is_Phi(node)) {
154                 if (get_irn_mode(node) != mode_M) {
155                         for (i = 0, arity = get_irn_arity(node); i < arity; ++i) {
156                                 ir_node *prev = get_irn_n(node, i);
157
158                                 if (is_Phi(prev))
159                                         continue;
160
161                                 if (dag_env->options & FIRMSTAT_COPY_CONSTANTS) {
162                                         if (is_irn_constlike(prev))
163                                                 continue;
164                                 }  /* if */
165
166                                 entry = get_irn_dag_entry(prev);
167
168                                 if (! entry) {
169                                         /* found an unassigned node, a new root */
170                                         entry = new_dag_entry(dag_env, node);
171                                         entry->is_ext_ref = 1;
172                                 }  /* if */
173                         }  /* for */
174                 }  /* if */
175         } else {
176
177                 for (i = 0, arity = get_irn_arity(node); i < arity; ++i) {
178                                 ir_node *prev = get_irn_n(node, i);
179                                 ir_mode *mode = get_irn_mode(prev);
180
181                                 if (mode == mode_X || mode == mode_M)
182                                         continue;
183
184                                 if (is_Phi(prev))
185                                         continue;
186
187                                 if (dag_env->options & FIRMSTAT_COPY_CONSTANTS) {
188                                         if (is_irn_constlike(prev))
189                                                 continue;
190                                 }  /* if */
191
192                                 if (get_nodes_block(prev) != block) {
193                                         /* The predecessor is from another block. It forms
194                                            a root. */
195                                         entry = get_irn_dag_entry(prev);
196                                         if (! entry) {
197                                                 /* found an unassigned node, a new root */
198                                                 entry = new_dag_entry(dag_env, node);
199                                                 entry->is_ext_ref = 1;
200                                         }  /* if */
201                                 }  /* if */
202                         }  /* for */
203         }  /* if */
204 }  /* find_dag_roots */
205
206 /**
207  * Pre-walker for connecting DAGs and counting.
208  */
209 static void connect_dags(ir_node *node, void *env)
210 {
211         dag_env_t   *dag_env = env;
212         int         i, arity;
213         ir_node     *block;
214         dag_entry_t *entry;
215         ir_mode     *mode;
216
217         if (is_Block(node))
218                 return;
219
220         block = get_nodes_block(node);
221
222         /* ignore start end end blocks */
223         if (block == get_irg_start_block(current_ir_graph) ||
224                 block == get_irg_end_block(current_ir_graph)) {
225                 return;
226         }  /* if */
227
228         /* ignore Phi nodes */
229         if (is_Phi(node))
230                 return;
231
232         if (dag_env->options & FIRMSTAT_ARGS_ARE_ROOTS && is_arg(node))
233                 return;
234
235         mode = get_irn_mode(node);
236         if (mode == mode_X || mode == mode_M) {
237                 /* do NOT count mode_X and mode_M nodes */
238                 return;
239         }  /* if */
240
241         /* if this option is set, Loads are always leaves */
242         if (dag_env->options & FIRMSTAT_LOAD_IS_LEAVE && is_Load(node))
243                 return;
244
245         if (dag_env->options & FIRMSTAT_CALL_IS_LEAVE && is_Call(node))
246                 return;
247
248         entry = get_irn_dag_entry(node);
249
250         if (! entry) {
251                 /* found an unassigned node, maybe a new root */
252                 entry = new_dag_entry(dag_env, node);
253         }  /* if */
254
255         /* put the predecessors into the same DAG as the current */
256         for (i = 0, arity = get_irn_arity(node); i < arity; ++i) {
257                 ir_node *prev = get_irn_n(node, i);
258                 ir_mode *mode = get_irn_mode(prev);
259
260                 if (is_Phi(prev))
261                         continue;
262
263                 if (mode == mode_X || mode == mode_M)
264                         continue;
265
266                 /*
267                  * copy constants if requested into the DAG's
268                  * beware, do NOT add a link, as this will result in
269                  * wrong intersections
270                  */
271                 if (dag_env->options & FIRMSTAT_COPY_CONSTANTS) {
272                         if (is_irn_constlike(prev)) {
273                                 ++entry->num_nodes;
274                                 ++entry->num_inner_nodes;
275                         }  /* if */
276                 }  /* if */
277
278                 /* only nodes from the same block goes into the DAG */
279                 if (get_nodes_block(prev) == block) {
280                         dag_entry_t *prev_entry = get_irn_dag_entry(prev);
281
282                         if (! prev_entry) {
283                                 /* not assigned node, put it into the same DAG */
284                                 set_irn_dag_entry(prev, entry);
285                                 ++entry->num_nodes;
286                                 ++entry->num_inner_nodes;
287                         } else {
288                                 if (prev_entry == entry) {
289                                         /* We found a node that is already assigned to this DAG.
290                                            This DAG is not a tree. */
291                                         entry->is_tree = 0;
292                                 } else {
293                                         /* two DAGs intersect: copy the data to one of them
294                                            and kill the other */
295                                         entry->num_roots       += prev_entry->num_roots;
296                                         entry->num_nodes       += prev_entry->num_nodes;
297                                         entry->num_inner_nodes += prev_entry->num_inner_nodes;
298                                         entry->is_tree         &= prev_entry->is_tree;
299
300                                         --dag_env->num_of_dags;
301
302                                         prev_entry->is_dead = 1;
303                                         prev_entry->link    = entry;
304                                 }  /* if */
305                         }  /* if */
306                 }  /* if */
307         }  /* for */
308 }  /* connect_dags */
309
310 #define DEFAULT_RET     1
311 #define COLOR_RET       1
312
313 static unsigned mark_options;
314
315 /**
316  * a vcg attribute hook
317  */
318 static int stat_dag_mark_hook(FILE *F, ir_node *n, ir_node *l)
319 {
320         static const char *colors[] = { "purple", "pink", "lightblue", "orange", "khaki", "orchid", "lilac", "turquoise" };
321         dag_entry_t *entry;
322
323         /* do not count Bad / NoMem */
324         if (l) {
325                 ir_op *op = get_irn_op(l);
326
327                 if (op == op_NoMem || op == op_Bad)
328                         return DEFAULT_RET;
329
330                 /* check for additional options */
331                 op = get_irn_op(n);
332
333                 if (mark_options & FIRMSTAT_LOAD_IS_LEAVE && op == op_Load)
334                         return DEFAULT_RET;
335
336                 if (mark_options & FIRMSTAT_CALL_IS_LEAVE && op == op_Call)
337                         return DEFAULT_RET;
338         }  /* if */
339
340         entry = get_irn_dag_entry(n);
341         if (! entry)
342                 return DEFAULT_RET;
343
344         fprintf(F, "color: %s info3: \"DAG id: %u\"", colors[entry->id & 7], entry->id);
345
346         /* I know the color! */
347         return COLOR_RET;
348 }  /* stat_dag_mark_hook */
349
350 /**
351  * count the DAG's size of a graph
352  *
353  * @param global  the global entry
354  * @param graph   the current graph entry
355  */
356 void count_dags_in_graph(graph_entry_t *global, graph_entry_t *graph)
357 {
358         dag_env_t   root_env;
359         dag_entry_t *entry;
360         unsigned id;
361         (void) global;
362
363         /* do NOT check the const code irg */
364         if (graph->irg == get_const_code_irg())
365                 return;
366
367         /* first step, clear the links */
368         irg_walk_graph(graph->irg, firm_clear_link, NULL, NULL);
369
370         obstack_init(&root_env.obst);
371         root_env.num_of_dags  = 0;
372         root_env.list_of_dags = NULL;
373         root_env.options      = FIRMSTAT_COPY_CONSTANTS | FIRMSTAT_LOAD_IS_LEAVE | FIRMSTAT_CALL_IS_LEAVE;
374
375         /* find the DAG roots that are referenced from other block */
376         irg_walk_graph(graph->irg, NULL, find_dag_roots, &root_env);
377
378         /* connect and count them */
379         irg_walk_graph(graph->irg, connect_dags, NULL, &root_env);
380
381         printf("Graph %p %s --- %d\n", (void *)graph->irg, get_entity_name(get_irg_entity(graph->irg)),
382                 root_env.num_of_dags);
383
384         for (id = 0, entry = root_env.list_of_dags; entry; entry = entry->next) {
385                 if (entry->is_dead)
386                         continue;
387                 entry->id = id++;
388
389                 printf("number of roots %d number of nodes %d inner %d tree %u %ld\n",
390                         entry->num_roots,
391                         entry->num_nodes,
392                         entry->num_inner_nodes,
393                         entry->is_tree,
394                         get_irn_node_nr(entry->root));
395         }  /* for */
396
397 #if 1
398         /* dump for test */
399         mark_options = root_env.options;
400         set_dump_node_vcgattr_hook(stat_dag_mark_hook);
401         dump_ir_block_graph(graph->irg, "-dag");
402         set_dump_node_vcgattr_hook(NULL);
403 #endif
404
405         assert(id == root_env.num_of_dags);
406
407         obstack_free(&root_env.obst, NULL);
408 }  /* count_dags_in_graph */