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