Fixed a bug concerning sizes of edge
[libfirm] / ir / ir / iredges.c
1 /**
2  * Always available outs.
3  * @author Sebastian Hack
4  * @date 14.1.2005
5  */
6
7 #include <alloca.h>
8
9 #ifdef HAVE_CONFIG_H
10 #include "config.h"
11 #endif
12
13 #include "irnode_t.h"
14 #include "iredges_t.h"
15 #include "irdump_t.h"
16 #include "irprintf.h"
17 #include "irhooks.h"
18 #include "debug.h"
19 #include "set.h"
20
21 static firm_dbg_module_t *dbg;
22
23 #if FIRM_EDGES_INPLACE
24
25 /**
26  * This flag is set to 1, if the edges get initialized for an irg.
27  * Then register additional data is forbidden.
28  */
29 static int edges_used = 0;
30
31 static int edges_private_size = 0;
32
33 int edges_register_private_data(size_t n)
34 {
35         int res = edges_private_size;
36
37         assert(!edges_used && "you cannot register private edge data, if edges have been initialized");
38
39         edges_private_size += n;
40         return res;
41 }
42
43 #define TIMES37(x) (((x) << 5) + ((x) << 2) + (x))
44
45 #define get_irn_out_list_head(irn) (&get_irn_out_info(irn)->outs)
46
47 static int edge_cmp(const void *p1, const void *p2, size_t len)
48 {
49         const ir_edge_t *e1 = p1;
50         const ir_edge_t *e2 = p2;
51         int res = e1->src == e2->src && e1->pos == e2->pos;
52
53         return !res;
54 }
55
56 #define edge_hash(edge) (TIMES37((edge)->pos) + HASH_PTR((edge)->src))
57
58 /**
59  * Initialize the out information for a graph.
60  * @note Dead node elim can call this on an already initialized graph.
61  */
62 void edges_init_graph(ir_graph *irg)
63 {
64         if(edges_activated(irg)) {
65                 irg_edge_info_t *info = _get_irg_edge_info(irg);
66                 int amount = 2048;
67
68                 edges_used = 1;
69
70                 if(info->edges) {
71                         amount = set_count(info->edges);
72                         del_set(info->edges);
73                 }
74
75                 info->edges = new_set(edge_cmp, amount);
76         }
77 }
78
79 #define EDGE_SIZE(src) \
80     (edges_private_size + (is_Block(src) ? sizeof(ir_block_edge_t) : sizeof(ir_edge_t)))
81
82
83 /**
84  * Get the edge object of an outgoing edge at a node.
85  * @param   irg The graph, the node is in.
86  * @param   src The node at which the edge originates.
87  * @param   pos The position of the edge.
88  * @return      The corresponding edge object or NULL,
89  *              if no such edge exists.
90  */
91 const ir_edge_t *get_irn_edge(ir_graph *irg, const ir_node *src, int pos)
92 {
93         if(edges_activated(irg)) {
94                 irg_edge_info_t *info = _get_irg_edge_info(irg);
95                 size_t size           = EDGE_SIZE(src);
96                 ir_edge_t *templ      = alloca(size);
97
98                 memset(templ, 0, size);
99                 templ->src = (ir_node *) src;
100                 templ->pos = pos;
101                 return set_find(info->edges, templ, size, edge_hash(templ));
102         }
103
104         return NULL;
105 }
106
107 void edges_notify_edge(ir_node *src, int pos, ir_node *tgt, ir_node *old_tgt, ir_graph *irg)
108 {
109         const char *msg = "";
110
111         if(!edges_activated(irg))
112                 return;
113
114 #if 0
115         assert(node_is_in_irgs_storage(irg, src) && "source not in irg");
116 #endif
117
118         /*
119          * Only do something, if the old and new target differ.
120          */
121         if(tgt != old_tgt) {
122                 int is_block_edge = is_Block(src);
123                 set *edges = _get_irg_edge_info(irg)->edges;
124                 ir_edge_t *edge;
125
126                 /*
127                  * This is scray, but:
128                  * If two entries in a set do not have the same size, they are
129                  * treated as unequal, ignoring the comparison function.
130                  * So, edges from blocks have extra storage (they are
131                  * ir_block_edge_t's).
132                  *
133                  * Also add the amount of registered private data to the
134                  * size of the edge.
135                  */
136                 size_t size      = EDGE_SIZE(src);
137                 ir_edge_t *templ = alloca(size);
138
139                 /* Initialize the edge template to search in the set. */
140                 memset(templ, 0, size);
141 #ifdef DEBUG_libfirm
142                 templ->src_nr = get_irn_node_nr(src);
143 #endif
144                 templ->src = src;
145                 templ->pos = pos;
146                 templ->invalid = 0;
147                 templ->present = 0;
148
149                 /*
150                  * If the target is NULL, the edge shall be deleted.
151                  */
152                 if(tgt == NULL) {
153                         /* search the edge in the set. */
154                         edge = set_find(edges, templ, size, edge_hash(templ));
155
156                         /* mark the edge invalid if it was found */
157                         if(edge) {
158                                 ir_block_edge_t *block_edge = (ir_block_edge_t *) edge;
159
160                                 msg = "deleting";
161                                 list_del(&edge->list);
162                                 edge->invalid = 1;
163                                 edge->pos = -2;
164                                 edge->src = NULL;
165
166                                 /*
167                                  * If the edge is a cf edge, we delete it also
168                                  * from the list of all block successor edges.
169                                  */
170                                 if(is_block_edge)
171                                         list_del(&block_edge->succ_list);
172                         }
173
174                         /* If the edge was not found issue a warning on the debug stream */
175                         else {
176                                 msg = "edge to delete not found!\n";
177                         }
178                 } /* if */
179
180                 /*
181                  * The target is not NULL and the old target differs
182                  * from the new target, the edge shall be moved (if the
183                  * old target was != NULL) or added (if the old target was
184                  * NULL).
185                  */
186                 else {
187                         struct list_head *head = _get_irn_outs_head(tgt);
188
189                         /*
190                          * The list head in the block of the edges target.
191                          * Therein all control flow edges directed at that block
192                          * are recorded.
193                          */
194                         struct list_head *succ_head =
195                                 is_block_edge ? _get_block_succ_head(get_nodes_block(tgt)) : NULL;
196
197                         ir_block_edge_t *block_edge;
198
199 #if 0
200                         if(!node_is_in_irgs_storage(irg, tgt))
201                                 return;
202 #endif
203                         assert(head->next && head->prev &&
204                                         "target list head must have been initialized");
205
206                         /*
207                          * insert the edge, if it is not yet in the set or return
208                          * the instance in the set.
209                          */
210                         edge = set_insert(edges, templ, size, edge_hash(templ));
211                         block_edge = (ir_block_edge_t *) edge;
212
213 #ifdef DEBUG_libfirm
214                         assert(!edge->invalid && "Invalid edge encountered");
215 #endif
216
217                         /* If the old target is not null, the edge is moved. */
218                         if(old_tgt) {
219                                 msg = "redirecting";
220                                 list_move(&edge->list, head);
221
222                                 /* If the edge is a cf edge, move it from the successor list. */
223                                 if(is_block_edge)
224                                         list_move(&block_edge->succ_list, succ_head);
225
226                                 _get_irn_edge_info(old_tgt)->out_count -= 1;
227                         }
228
229                         /* The old target was null, thus, the edge is newly created. */
230                         else {
231                                 msg = "adding";
232                                 list_add(&edge->list, head);
233
234                                 /*
235                                  * If the edge is cf edge, enter it into the successor list
236                                  * of the target node's block.
237                                  */
238                                 if(is_block_edge)
239                                         list_add(&block_edge->succ_list, succ_head);
240                         }
241
242                         _get_irn_edge_info(tgt)->out_count += 1;
243                 } /* else */
244         }
245
246         /* If the target and the old target are equal, nothing is done. */
247         DBG((dbg, LEVEL_5, "announce out edge: %n[%p] %d-> %n[%p](%n[%p]): %s\n",
248                                 src, src, pos, tgt, tgt, old_tgt, old_tgt, msg));
249 }
250
251 void edges_node_deleted(ir_node *old, ir_graph *irg)
252 {
253         if(edges_activated(irg)) {
254                 int not_a_block = !is_Block(old);
255                 ir_edge_t templ;
256                 int i, n;
257
258                 templ.src = old;
259                 DBG((dbg, LEVEL_5, "node deleted: %n\n", old));
260
261                 /* Change to get_irn_n */
262                 for(i = -not_a_block, n = get_irn_arity(old); i < n; ++i) {
263                         ir_node *old_tgt = get_irn_n(old, i);
264                         DBG((dbg, LEVEL_5, "\tdelete to old target %n\n", old_tgt));
265                         edges_notify_edge(old, i, NULL, old_tgt, irg);
266                 }
267
268         }
269 }
270
271 void edges_invalidate(ir_node *irn, ir_graph *irg)
272 {
273         edges_node_deleted(irn, irg);
274 }
275
276 static void build_edges_walker(ir_node *irn, void *data)
277 {
278         ir_graph *irg = data;
279         int not_a_block = !is_Block(irn);
280         int i, n;
281
282         for(i = -not_a_block, n = get_irn_arity(irn); i < n; ++i)
283                 edges_notify_edge(irn, i, get_irn_n(irn, i), NULL, irg);
284 }
285
286 static void init_lh_walker(ir_node *irn, void *data)
287 {
288         INIT_LIST_HEAD(_get_irn_outs_head(irn));
289         if(is_Block(irn))
290                 INIT_LIST_HEAD(_get_block_succ_head(irn));
291 }
292
293 void edges_activate(ir_graph *irg)
294 {
295         irg_edge_info_t *info = _get_irg_edge_info(irg);
296
297         info->activated = 1;
298         edges_init_graph(irg);
299         irg_walk_graph(irg, init_lh_walker, build_edges_walker, irg);
300 }
301
302 void edges_deactivate(ir_graph *irg)
303 {
304         irg_edge_info_t *info = _get_irg_edge_info(irg);
305
306         info->activated = 0;
307         if(info->edges) {
308                 del_set(info->edges);
309     info->edges = NULL;
310   }
311 }
312
313 int (edges_activated)(const ir_graph *irg)
314 {
315         return _edges_activated(irg);
316 }
317
318
319 /**
320  * Reroute all use-edges from a node to another.
321  * @param from The node whose use-edges shall be withdrawn.
322  * @param to The node to which all the use-edges of @p from shall be
323  * sent to.
324  */
325 void edges_reroute(ir_node *from, ir_node *to, ir_graph *irg)
326 {
327         if(edges_activated(irg)) {
328                 struct list_head *head = _get_irn_outs_head(from);
329
330                 DBG((firm_dbg_register(DBG_EDGES), LEVEL_5,
331                                         "reroute from %n to %n\n", from, to));
332
333                 while(head != head->next) {
334                         ir_edge_t *edge = list_entry(head->next, ir_edge_t, list);
335                         // DBG((dbg, LEVEL_5, "\t%n %d\n", edge->src, edge->pos));
336                         assert(edge->pos >= -1);
337                         set_irn_n(edge->src, edge->pos, to);
338                 }
339         }
340 }
341
342 static void verify_set_presence(ir_node *irn, void *data)
343 {
344         ir_graph *irg = data;
345         set *edges = _get_irg_edge_info(irg)->edges;
346         int not_a_block = !is_Block(irn);
347         int i, n;
348
349         for(i = 0, n = get_irn_arity(irn) + not_a_block; i < n; ++i) {
350     ir_block_edge_t space;
351                 ir_edge_t *templ = (ir_edge_t *) &space;
352                 ir_edge_t *e;
353     size_t size = not_a_block ? sizeof(ir_edge_t) : sizeof(ir_block_edge_t);
354
355                 templ->src = irn;
356                 templ->pos = i - not_a_block;
357
358                 e = set_find(edges, templ, size, edge_hash(templ));
359                 if(e != NULL)
360                         e->present = 1;
361                 else
362                         DBG((dbg, LEVEL_DEFAULT, "edge %n,%d is missing\n", irn, templ->pos));
363         }
364 }
365
366 static void verify_list_presence(ir_node *irn, void *data)
367 {
368         const ir_edge_t *e;
369
370         foreach_out_edge(irn, e) {
371                 ir_node *tgt = get_irn_n(e->src, e->pos);
372                 if(irn != tgt)
373                         DBG((dbg, LEVEL_DEFAULT, "edge %n,%d is no out edge of %n but of %n\n",
374                                         e->src, e->pos, irn, tgt));
375         }
376
377 }
378
379 void edges_verify(ir_graph *irg)
380 {
381         set *edges = _get_irg_edge_info(irg)->edges;
382         ir_edge_t *e;
383
384         /* Clear the present bit in all edges available. */
385         for(e = set_first(edges); e; e = set_next(edges))
386                 e->present = 0;
387
388         irg_walk_graph(irg, verify_set_presence, verify_list_presence, irg);
389
390         /*
391          * Dump all edges which are not invalid and not present.
392          * These edges are superfluous and their presence in the
393          * edge set is wrong.
394          */
395         for(e = set_first(edges); e; e = set_next(edges)) {
396                 if(!e->invalid && !e->present)
397                         DBG((dbg, LEVEL_DEFAULT, "edge %n,%d is superfluous\n", e->src, e->pos));
398         }
399 }
400
401 void init_edges(void)
402 {
403         dbg = firm_dbg_register(DBG_EDGES);
404         /* firm_dbg_set_mask(dbg, -1); */
405 }
406
407
408 const ir_edge_t *(get_irn_out_edge_first)(const ir_node *irn)
409 {
410         return _get_irn_out_edge_first(irn);
411 }
412
413 const ir_edge_t *(get_irn_out_edge_next)(const ir_node *irn, const ir_edge_t *last)
414 {
415         return _get_irn_out_edge_next(irn, last);
416 }
417
418 ir_node *(get_edge_src_irn)(const ir_edge_t *edge)
419 {
420         return _get_edge_src_irn(edge);
421 }
422
423 int (get_edge_src_pos)(const ir_edge_t *edge)
424 {
425         return _get_edge_src_pos(edge);
426 }
427
428 int (get_irn_n_edges)(const ir_node *irn)
429 {
430   return _get_irn_n_edges(irn);
431 }
432
433
434 #endif /* FIRM_EDGES_INPLACE */