ef89a84f3e2f70ca18afebb4d151a6d2b6f8c8bb
[libfirm] / ir / stat / firmstat.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/ir/firmstat.c
4  * Purpose:     Statistics for Firm.
5  * Author:      Michael Beck
6  * Created:
7  * CVS-ID:      $Id$
8  * Copyright:   (c) 2004 Universität Karlsruhe
9  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #ifdef FIRM_STATISTICS
17
18 #include <stdio.h>
19
20 #ifdef HAVE_STDLIB_H
21 # include <stdlib.h>
22 #endif
23 #ifdef HAVE_STRING_H
24 # include <string.h>
25 #endif
26
27 #include "irouts.h"
28 #include "irdump.h"
29 #include "hashptr.h"
30 #include "firmstat_t.h"
31 #include "pattern.h"
32 #include "dags.h"
33 #include "stat_dmp.h"
34 #include "xmalloc.h"
35 #include "irhooks.h"
36
37 /*
38  * need this to be static:
39  * Special pseudo Opcodes that we need to count some interesting cases
40  */
41
42 /**
43  * The Phi0, a node that is created during SSA construction
44  */
45 static ir_op _op_Phi0;
46
47 /** The PhiM, just to count memory Phi's. */
48 static ir_op _op_PhiM;
49
50 /** The Mul by Const node. */
51 static ir_op _op_MulC;
52
53 /** The Div by Const node. */
54 static ir_op _op_DivC;
55
56 /** The Div by Const node. */
57 static ir_op _op_ModC;
58
59 /** The Div by Const node. */
60 static ir_op _op_DivModC;
61
62 /** The memory Proj node. */
63 static ir_op _op_ProjM;
64
65 /** A Sel of a Sel */
66 static ir_op _op_SelSel;
67
68 /** A Sel of a Sel of a Sel */
69 static ir_op _op_SelSelSel;
70
71 /* ---------------------------------------------------------------------------------- */
72
73 /** Marks the begin of a statistic (hook) function. */
74 #define STAT_ENTER              ++status->recursive
75
76 /** Marks the end of a statistic (hook) functions. */
77 #define STAT_LEAVE              --status->recursive
78
79 /** Allows to enter a statistic function only when we are not already in a hook. */
80 #define STAT_ENTER_SINGLE       do { if (status->recursive > 0) return; ++status->recursive; } while (0)
81
82 /**
83  * global status
84  */
85 static const int status_disable = 0;
86 static stat_info_t *status = (stat_info_t *)&status_disable;
87
88 /**
89  * compare two elements of the opcode hash
90  */
91 static int opcode_cmp(const void *elt, const void *key)
92 {
93   const node_entry_t *e1 = elt;
94   const node_entry_t *e2 = key;
95
96   return e1->op->code - e2->op->code;
97 }
98
99 /**
100  * compare two elements of the graph hash
101  */
102 static int graph_cmp(const void *elt, const void *key)
103 {
104   const graph_entry_t *e1 = elt;
105   const graph_entry_t *e2 = key;
106
107   return e1->irg != e2->irg;
108 }
109
110 /**
111  * compare two elements of the optimization hash
112  */
113 static int opt_cmp(const void *elt, const void *key)
114 {
115   const opt_entry_t *e1 = elt;
116   const opt_entry_t *e2 = key;
117
118   return e1->op->code != e2->op->code;
119 }
120
121 /**
122  * compare two elements of the block/extbb hash
123  */
124 static int block_cmp(const void *elt, const void *key)
125 {
126   const block_entry_t *e1 = elt;
127   const block_entry_t *e2 = key;
128
129   return e1->block_nr != e2->block_nr;
130 }
131
132 /**
133  * compare two elements of the ir_op hash
134  */
135 static int opcode_cmp_2(const void *elt, const void *key)
136 {
137   const ir_op *e1 = elt;
138   const ir_op *e2 = key;
139
140   return e1->code != e2->code;
141 }
142
143 /**
144  * compare two elements of the address_mark set
145  */
146 static int address_mark_cmp(const void *elt, const void *key, size_t size)
147 {
148   const address_mark_entry_t *e1 = elt;
149   const address_mark_entry_t *e2 = key;
150
151   /* compare only the nodes, the rest is used as data container */
152   return e1->node != e2->node;
153 }
154
155 /**
156  * clears all counter in a node_entry_t
157  */
158 static void opcode_clear_entry(node_entry_t *elem)
159 {
160   cnt_clr(&elem->cnt_alive);
161   cnt_clr(&elem->new_node);
162   cnt_clr(&elem->into_Id);
163 }
164
165 /**
166  * Returns the associates node_entry_t for an ir_op
167  *
168  * @param op    the IR operation
169  * @param hmap  a hash map containing ir_op* -> node_entry_t*
170  */
171 static node_entry_t *opcode_get_entry(const ir_op *op, hmap_node_entry_t *hmap)
172 {
173   node_entry_t key;
174   node_entry_t *elem;
175
176   key.op = op;
177
178   elem = pset_find(hmap, &key, op->code);
179   if (elem)
180     return elem;
181
182   elem = obstack_alloc(&status->cnts, sizeof(*elem));
183   memset(elem, 0, sizeof(*elem));
184
185   /* clear counter */
186   opcode_clear_entry(elem);
187
188   elem->op = op;
189
190   return pset_insert(hmap, elem, op->code);
191 }
192
193 /**
194  * Returns the associates ir_op for an opcode
195  *
196  * @param code  the IR opcode
197  * @param hmap  the hash map containing opcode -> ir_op*
198  */
199 static ir_op *opcode_find_entry(opcode code, hmap_ir_op *hmap)
200 {
201   ir_op key;
202
203   key.code = code;
204   return pset_find(hmap, &key, code);
205 }
206
207 /**
208  * clears all counter in a graph_entry_t
209  */
210 static void graph_clear_entry(graph_entry_t *elem, int all)
211 {
212   if (all) {
213     cnt_clr(&elem->cnt_walked);
214     cnt_clr(&elem->cnt_walked_blocks);
215     cnt_clr(&elem->cnt_was_inlined);
216     cnt_clr(&elem->cnt_got_inlined);
217     cnt_clr(&elem->cnt_strength_red);
218     cnt_clr(&elem->cnt_real_func_call);
219   }
220   cnt_clr(&elem->cnt_edges);
221   cnt_clr(&elem->cnt_all_calls);
222   cnt_clr(&elem->cnt_call_with_cnst_arg);
223   cnt_clr(&elem->cnt_indirect_calls);
224
225   if (elem->block_hash) {
226     del_pset(elem->block_hash);
227     elem->block_hash = NULL;
228   }
229
230   if (elem->extbb_hash) {
231     del_pset(elem->extbb_hash);
232     elem->extbb_hash = NULL;
233   }
234
235   obstack_free(&status->recalc_cnts, NULL);
236   obstack_init(&status->recalc_cnts);
237 }
238
239 /**
240  * Returns the associated graph_entry_t for an IR graph.
241  *
242  * @param irg   the IR graph
243  * @param hmap  the hash map containing ir_graph* -> graph_entry_t*
244  */
245 static graph_entry_t *graph_get_entry(ir_graph *irg, hmap_graph_entry_t *hmap)
246 {
247   graph_entry_t key;
248   graph_entry_t *elem;
249   int i;
250
251   key.irg = irg;
252
253   elem = pset_find(hmap, &key, HASH_PTR(irg));
254   if (elem)
255     return elem;
256
257   /* allocate a new one */
258   elem = obstack_alloc(&status->cnts, sizeof(*elem));
259   memset(elem, 0, sizeof(*elem));
260
261   /* clear counter */
262   graph_clear_entry(elem, 1);
263
264   /* new hash table for opcodes here  */
265   elem->opcode_hash  = new_pset(opcode_cmp, 5);
266   elem->address_mark = new_set(address_mark_cmp, 5);
267   elem->irg          = irg;
268
269   /* these hash tables are created on demand */
270   elem->block_hash = NULL;
271   elem->extbb_hash = NULL;
272
273   for (i = 0; i < sizeof(elem->opt_hash)/sizeof(elem->opt_hash[0]); ++i)
274     elem->opt_hash[i] = new_pset(opt_cmp, 4);
275
276   return pset_insert(hmap, elem, HASH_PTR(irg));
277 }
278
279 /**
280  * clears all counter in an opt_entry_t
281  */
282 static void opt_clear_entry(opt_entry_t *elem)
283 {
284   cnt_clr(&elem->count);
285 }
286
287 /**
288  * Returns the associated opt_entry_t for an IR operation.
289  *
290  * @param op    the IR operation
291  * @param hmap  the hash map containing ir_op* -> opt_entry_t*
292  */
293 static opt_entry_t *opt_get_entry(const ir_op *op, hmap_opt_entry_t *hmap)
294 {
295   opt_entry_t key;
296   opt_entry_t *elem;
297
298   key.op = op;
299
300   elem = pset_find(hmap, &key, op->code);
301   if (elem)
302     return elem;
303
304   elem = obstack_alloc(&status->cnts, sizeof(*elem));
305   memset(elem, 0, sizeof(*elem));
306
307   /* clear new counter */
308   opt_clear_entry(elem);
309
310   elem->op = op;
311
312   return pset_insert(hmap, elem, op->code);
313 }
314
315 /**
316  * clears all counter in a block_entry_t
317  */
318 static void block_clear_entry(block_entry_t *elem)
319 {
320   cnt_clr(&elem->cnt_nodes);
321   cnt_clr(&elem->cnt_edges);
322   cnt_clr(&elem->cnt_in_edges);
323   cnt_clr(&elem->cnt_out_edges);
324   cnt_clr(&elem->cnt_phi_data);
325 }
326
327 /**
328  * Returns the associated block_entry_t for an block.
329  *
330  * @param block_nr  an IR  block number
331  * @param hmap      a hash map containing long -> block_entry_t
332  */
333 static block_entry_t *block_get_entry(long block_nr, hmap_block_entry_t *hmap)
334 {
335   block_entry_t key;
336   block_entry_t *elem;
337
338   key.block_nr = block_nr;
339
340   elem = pset_find(hmap, &key, block_nr);
341   if (elem)
342     return elem;
343
344   elem = obstack_alloc(&status->recalc_cnts, sizeof(*elem));
345   memset(elem, 0, sizeof(*elem));
346
347   /* clear new counter */
348   block_clear_entry(elem);
349
350   elem->block_nr = block_nr;
351
352   return pset_insert(hmap, elem, block_nr);
353 }
354
355
356 /**
357  * Returns the ir_op for an IR-node,
358  * handles special cases and return pseudo op codes.
359  *
360  * @param none  an IR node
361  */
362 static ir_op *stat_get_irn_op(ir_node *node)
363 {
364   ir_op *op = get_irn_op(node);
365
366   if (op == op_Phi && get_irn_arity(node) == 0) {
367     /* special case, a Phi0 node, count on extra counter */
368     op = status->op_Phi0 ? status->op_Phi0 : op;
369   }
370   else if (op == op_Phi && get_irn_mode(node) == mode_M) {
371     /* special case, a Memory Phi node, count on extra counter */
372     op = status->op_PhiM ? status->op_PhiM : op;
373   }
374   else if (op == op_Proj && get_irn_mode(node) == mode_M) {
375     /* special case, a Memory Proj node, count on extra counter */
376     op = status->op_ProjM ? status->op_ProjM : op;
377   }
378   else if (op == op_Mul &&
379            (get_irn_op(get_Mul_left(node)) == op_Const || get_irn_op(get_Mul_right(node)) == op_Const)) {
380     /* special case, a Multiply by a const, count on extra counter */
381     op = status->op_MulC ? status->op_MulC : op;
382   }
383   else if (op == op_Div && get_irn_op(get_Div_right(node)) == op_Const) {
384     /* special case, a division by a const, count on extra counter */
385     op = status->op_DivC ? status->op_DivC : op;
386   }
387   else if (op == op_Mod && get_irn_op(get_Mod_right(node)) == op_Const) {
388     /* special case, a module by a const, count on extra counter */
389     op = status->op_ModC ? status->op_ModC : op;
390   }
391   else if (op == op_DivMod && get_irn_op(get_DivMod_right(node)) == op_Const) {
392     /* special case, a division/modulo by a const, count on extra counter */
393     op = status->op_DivModC ? status->op_DivModC : op;
394   }
395   else if (op == op_Sel && get_irn_op(get_Sel_ptr(node)) == op_Sel) {
396     /* special case, a Sel of a Sel, count on extra counter */
397     op = status->op_SelSel ? status->op_SelSel : op;
398
399     if (get_irn_op(get_Sel_ptr(get_Sel_ptr(node))) == op_Sel) {
400       /* special case, a Sel of a Sel of a Sel, count on extra counter */
401       op = status->op_SelSelSel ? status->op_SelSelSel : op;
402     }
403   }
404
405   return op;
406 }
407
408 /**
409  * update the block counter
410  */
411 static void undate_block_info(ir_node *node, graph_entry_t *graph)
412 {
413   ir_op *op = get_irn_op(node);
414   ir_node *block;
415   block_entry_t *b_entry;
416   int i, arity;
417
418   /* check for block */
419   if (op == op_Block) {
420     arity = get_irn_arity(node);
421     b_entry = block_get_entry(get_irn_node_nr(node), graph->block_hash);
422
423     /* count all incoming edges */
424     for (i = 0; i < arity; ++i) {
425       ir_node *pred = get_irn_n(node, i);
426       ir_node *other_block = get_nodes_block(pred);
427       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
428
429       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
430       cnt_inc(&b_entry_other->cnt_out_edges);
431     }
432     return;
433   }
434
435   block   = get_nodes_block(node);
436   b_entry = block_get_entry(get_irn_node_nr(block), graph->block_hash);
437
438   if (op == op_Phi && mode_is_datab(get_irn_mode(node))) {
439     /* count data Phi per block */
440     cnt_inc(&b_entry->cnt_phi_data);
441   }
442
443   /* we have a new node in our block */
444   cnt_inc(&b_entry->cnt_nodes);
445
446   /* don't count keep-alive edges */
447   if (get_irn_op(node) == op_End)
448     return;
449
450   arity = get_irn_arity(node);
451
452   for (i = 0; i < arity; ++i) {
453     ir_node *pred = get_irn_n(node, i);
454     ir_node *other_block;
455
456     other_block = get_nodes_block(pred);
457
458     if (other_block == block)
459       cnt_inc(&b_entry->cnt_edges);     /* a in block edge */
460     else {
461       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
462
463       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
464       cnt_inc(&b_entry_other->cnt_out_edges);
465     }
466   }
467 }
468
469 /**
470  * update the extended block counter
471  */
472 static void undate_extbb_info(ir_node *node, graph_entry_t *graph)
473 {
474   ir_op *op = get_irn_op(node);
475   ir_extblk *extbb;
476   extbb_entry_t *eb_entry;
477   int i, arity;
478
479   /* check for block */
480   if (op == op_Block) {
481     extbb = get_nodes_extbb(node);
482     arity = get_irn_arity(node);
483     eb_entry = block_get_entry(get_extbb_node_nr(extbb), graph->extbb_hash);
484
485     /* count all incoming edges */
486     for (i = 0; i < arity; ++i) {
487       ir_node *pred = get_irn_n(node, i);
488       ir_extblk *other_extbb = get_nodes_extbb(pred);
489
490       if (extbb != other_extbb) {
491         extbb_entry_t *eb_entry_other = block_get_entry(get_extbb_node_nr(other_extbb), graph->extbb_hash);
492
493         cnt_inc(&eb_entry->cnt_in_edges);       /* an edge coming from another extbb */
494         cnt_inc(&eb_entry_other->cnt_out_edges);
495       }
496     }
497     return;
498   }
499
500   extbb    = get_nodes_extbb(node);
501   eb_entry = block_get_entry(get_extbb_node_nr(extbb), graph->extbb_hash);
502
503   if (op == op_Phi && mode_is_datab(get_irn_mode(node))) {
504     /* count data Phi per extbb */
505     cnt_inc(&eb_entry->cnt_phi_data);
506   }
507
508   /* we have a new node in our block */
509   cnt_inc(&eb_entry->cnt_nodes);
510
511   /* don't count keep-alive edges */
512   if (get_irn_op(node) == op_End)
513     return;
514
515   arity = get_irn_arity(node);
516
517   for (i = 0; i < arity; ++i) {
518     ir_node *pred = get_irn_n(node, i);
519     ir_extblk *other_extbb = get_nodes_extbb(pred);
520
521     if (other_extbb == extbb)
522       cnt_inc(&eb_entry->cnt_edges);    /* a in extbb edge */
523     else {
524       extbb_entry_t *eb_entry_other = block_get_entry(get_extbb_node_nr(other_extbb), graph->extbb_hash);
525
526       cnt_inc(&eb_entry->cnt_in_edges); /* an edge coming from another extbb */
527       cnt_inc(&eb_entry_other->cnt_out_edges);
528     }
529   }
530 }
531
532 /** calculates how many arguments of the call are const */
533 static int cnt_const_args(ir_node *call)
534 {
535   int  i, res = 0;
536   int  n = get_Call_n_params(call);
537
538   for (i = 0; i < n; ++i) {
539     ir_node *param = get_Call_param(call, i);
540     ir_op   *op = get_irn_op(param);
541
542     if (op == op_Const || op == op_SymConst)
543       ++res;
544   }
545   return res;
546 }
547
548 /**
549  * update info on calls
550  *
551  * @param call   The call
552  * @param graph  The graph entry containing the call
553  */
554 static void stat_update_call(ir_node *call, graph_entry_t *graph)
555 {
556   ir_node  *block = get_nodes_block(call);
557   ir_node  *ptr = get_Call_ptr(call);
558   entity   *ent = NULL;
559   ir_graph *callee = NULL;
560   int      num_const_args;
561
562   /*
563    * If the block is bad, the whole subgraph will collapse later
564    * so do not count this call.
565    * This happens in dead code.
566    */
567   if (is_Bad(block))
568     return;
569
570   cnt_inc(&graph->cnt_all_calls);
571
572   /* found a call, this function is not a leaf */
573   graph->is_leaf = 0;
574
575   if (get_irn_op(ptr) == op_SymConst) {
576     if (get_SymConst_kind(ptr) == symconst_addr_ent) {
577       /* ok, we seems to know the entity */
578       ent = get_SymConst_entity(ptr);
579       callee = get_entity_irg(ent);
580
581       /* it is recursive, if it calls at least once */
582       if (callee == graph->irg)
583         graph->is_recursive = 1;
584     }
585   }
586   else {
587     /* indirect call, be could not predict */
588     cnt_inc(&graph->cnt_indirect_calls);
589
590     /* NOT a leaf call */
591     graph->is_leaf_call = LCS_NON_LEAF_CALL;
592   }
593
594   /* check, if it's a chain-call: Then, the call-block
595    * must dominate the end block. */
596   {
597     ir_node *curr = get_irg_end_block(graph->irg);
598     int depth = get_Block_dom_depth(block);
599
600     for (; curr != block && get_Block_dom_depth(curr) > depth;) {
601       curr = get_Block_idom(curr);
602
603       if (! curr || is_no_Block(curr))
604         break;
605     }
606
607     if (curr != block)
608       graph->is_chain_call = 0;
609   }
610
611   /* check, if the callee is a leaf */
612   if (callee) {
613     graph_entry_t *called = graph_get_entry(callee, status->irg_hash);
614
615     if (called->is_analyzed) {
616       if (! called->is_leaf)
617         graph->is_leaf_call = LCS_NON_LEAF_CALL;
618     }
619   }
620
621   /* check, if arguments of the call are const */
622   num_const_args = cnt_const_args(call);
623
624   if (num_const_args > 0)
625     cnt_inc(&graph->cnt_call_with_cnst_arg);
626 }
627
628 /**
629  * update info on calls for graphs on the wait queue
630  */
631 static void stat_update_call_2(ir_node *call, graph_entry_t *graph)
632 {
633   ir_node  *block = get_nodes_block(call);
634   ir_node  *ptr = get_Call_ptr(call);
635   entity   *ent = NULL;
636   ir_graph *callee = NULL;
637
638   /*
639    * If the block is bad, the whole subgraph will collapse later
640    * so do not count this call.
641    * This happens in dead code.
642    */
643   if (is_Bad(block))
644     return;
645
646   if (get_irn_op(ptr) == op_SymConst) {
647     if (get_SymConst_kind(ptr) == symconst_addr_ent) {
648       /* ok, we seems to know the entity */
649       ent = get_SymConst_entity(ptr);
650       callee = get_entity_irg(ent);
651     }
652   }
653
654   /* check, if the callee is a leaf */
655   if (callee) {
656     graph_entry_t *called = graph_get_entry(callee, status->irg_hash);
657
658     assert(called->is_analyzed);
659
660     if (! called->is_leaf)
661       graph->is_leaf_call = LCS_NON_LEAF_CALL;
662   }
663   else
664     graph->is_leaf_call = LCS_NON_LEAF_CALL;
665 }
666
667 /**
668  * walker for reachable nodes count
669  */
670 static void update_node_stat(ir_node *node, void *env)
671 {
672   graph_entry_t *graph = env;
673   node_entry_t *entry;
674
675   ir_op *op = stat_get_irn_op(node);
676   int arity = get_irn_arity(node);
677
678   entry = opcode_get_entry(op, graph->opcode_hash);
679
680   cnt_inc(&entry->cnt_alive);
681   cnt_add_i(&graph->cnt_edges, arity);
682
683   /* count block edges */
684   undate_block_info(node, graph);
685
686   /* count extended block edges */
687   if (status->stat_options & FIRMSTAT_COUNT_EXTBB) {
688     undate_extbb_info(node, graph);
689   }
690
691   /* handle statistics for special node types */
692
693   if (op == op_Const) {
694     if (status->stat_options & FIRMSTAT_COUNT_CONSTS) {
695       /* check properties of constants */
696       stat_update_const(status, node, graph);
697     }
698   }
699   else if (op == op_Call) {
700     /* check for properties that depends on calls like recursion/leaf/indirect call */
701     stat_update_call(node, graph);
702   }
703 }
704
705 /**
706  * walker for reachable nodes count for graphs on the wait_q
707  */
708 static void update_node_stat_2(ir_node *node, void *env)
709 {
710   graph_entry_t *graph = env;
711
712   /* check for properties that depends on calls like recursion/leaf/indirect call */
713   if (get_irn_op(node) == op_Call)
714     stat_update_call_2(node, graph);
715 }
716
717 /**
718  * get the current address mark
719  */
720 static unsigned get_adr_mark(graph_entry_t *graph, ir_node *node)
721 {
722   address_mark_entry_t *value = set_find(graph->address_mark, &node, sizeof(*value), HASH_PTR(node));
723
724   return value ? value->mark : 0;
725 }
726
727 /**
728  * set the current address mark
729  */
730 static void set_adr_mark(graph_entry_t *graph, ir_node *node, unsigned val)
731 {
732   address_mark_entry_t *value = set_insert(graph->address_mark, &node, sizeof(*value), HASH_PTR(node));
733
734   value->mark = val;
735 }
736
737 /**
738  * a vcg attribute hook: Color a node with a different color if
739  * it's identified as a part of an address expression or at least referenced
740  * by an address expression.
741  */
742 static int stat_adr_mark_hook(FILE *F, ir_node *node, ir_node *local)
743 {
744   ir_node *n           = local ? local : node;
745   ir_graph *irg        = get_irn_irg(n);
746   graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
747   unsigned mark        = get_adr_mark(graph, n);
748
749   if (mark & MARK_ADDRESS_CALC)
750     fprintf(F, "color: purple");
751   else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR)
752     fprintf(F, "color: pink");
753   else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == (MARK_REF_ADR|MARK_REF_NON_ADR))
754     fprintf(F, "color: lightblue");
755   else
756     return 0;
757
758   /* I know the color! */
759   return 1;
760 }
761
762 /**
763  * walker that marks every node that is an address calculation
764  *
765  * predecessor nodes must be visited first. We ensure this by
766  * calling in in the post of an outs walk. This should work even in cycles,
767  * while the pre in a normal walk will not.
768  */
769 static void mark_address_calc(ir_node *node, void *env)
770 {
771   graph_entry_t *graph = env;
772   ir_mode *mode = get_irn_mode(node);
773   int i, n;
774   unsigned mark_preds = MARK_REF_NON_ADR;
775
776   if (! mode_is_numP(mode))
777     return;
778
779   if (mode_is_reference(mode)) {
780     /* a reference is calculated here, we are sure */
781     set_adr_mark(graph, node, MARK_ADDRESS_CALC);
782
783     mark_preds = MARK_REF_ADR;
784   }
785   else {
786     unsigned mark = get_adr_mark(graph, node);
787
788     if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR) {
789       /*
790        * this node has not an reference mode, but is only
791        * referenced by address calculations
792        */
793       mark_preds = MARK_REF_ADR;
794     }
795   }
796
797   /* mark all predecessors */
798   for (i = 0, n = get_irn_arity(node); i < n; ++i) {
799     ir_node *pred = get_irn_n(node, i);
800
801     set_adr_mark(graph, pred, get_adr_mark(graph, pred) | mark_preds);
802   }
803 }
804
805 /**
806  * Called for every graph when the graph is either deleted or stat_dump_snapshot()
807  * is called, must recalculate all statistic info.
808  *
809  * @param global    The global entry
810  * @param graph     The current entry
811  */
812 static void update_graph_stat(graph_entry_t *global, graph_entry_t *graph)
813 {
814   node_entry_t *entry;
815
816   /* clear first the alive counter in the graph */
817   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
818     cnt_clr(&entry->cnt_alive);
819   }
820
821   /* set pessimistic values */
822   graph->is_leaf       = 1;
823   graph->is_leaf_call  = LCS_UNKNOWN;
824   graph->is_recursive  = 0;
825   graph->is_chain_call = 1;
826
827   /* create new block counter */
828   graph->block_hash = new_pset(block_cmp, 5);
829
830   /* we need dominator info */
831   if (graph->irg != get_const_code_irg())
832     if (get_irg_dom_state(graph->irg) != dom_consistent)
833       compute_doms(graph->irg);
834
835   if (status->stat_options & FIRMSTAT_COUNT_EXTBB) {
836     /* we need extended basic blocks */
837     compute_extbb(graph->irg);
838
839     /* create new extbb counter */
840     graph->extbb_hash = new_pset(block_cmp, 5);
841   }
842
843   /* count the nodes in the graph */
844   irg_walk_graph(graph->irg, update_node_stat, NULL, graph);
845
846 #if 0
847   /* Uncomment this code if chain-call means call exact one */
848   entry = opcode_get_entry(op_Call, graph->opcode_hash);
849
850   /* check if we have more than 1 call */
851   if (cnt_gt(entry->cnt_alive, 1))
852     graph->is_chain_call = 0;
853 #endif
854
855   /* recursive functions are never chain calls, leafs don't have calls */
856   if (graph->is_recursive || graph->is_leaf)
857     graph->is_chain_call = 0;
858
859   /* assume we walk every graph only ONCE, we could sum here the global count */
860   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
861     node_entry_t *g_entry = opcode_get_entry(entry->op, global->opcode_hash);
862
863     /* update the node counter */
864     cnt_add(&g_entry->cnt_alive, &entry->cnt_alive);
865   }
866
867   /* update the edge counter */
868   cnt_add(&global->cnt_edges, &graph->cnt_edges);
869
870   /* count the number of address calculation */
871   if (graph->irg != get_const_code_irg()) {
872     ir_graph *rem = current_ir_graph;
873
874     if (get_irg_outs_state(graph->irg) != outs_consistent)
875       compute_irg_outs(graph->irg);
876
877     /* Must be done an the outs graph */
878     current_ir_graph = graph->irg;
879     irg_out_walk(get_irg_start(graph->irg), NULL, mark_address_calc, graph);
880     current_ir_graph = rem;
881
882 #if 0
883     set_dump_node_vcgattr_hook(stat_adr_mark_hook);
884     dump_ir_block_graph(graph->irg, "-adr");
885     set_dump_node_vcgattr_hook(NULL);
886 #endif
887   }
888
889   /* count the DAG's */
890   if (status->stat_options & FIRMSTAT_COUNT_DAG)
891     count_dags_in_graph(global, graph);
892
893   /* calculate the patterns of this graph */
894   stat_calc_pattern_history(graph->irg);
895
896   /* leaf function did not call others */
897   if (graph->is_leaf)
898     graph->is_leaf_call = LCS_NON_LEAF_CALL;
899   else if (graph->is_leaf_call == LCS_UNKNOWN) {
900     /* we still don't know if this graph calls leaf-functions, so enqueue */
901     pdeq_putl(status->wait_q, graph);
902   }
903
904   /* we have analyzed this graph */
905   graph->is_analyzed = 1;
906 }
907
908 /**
909  * Called for every graph that was on the wait_q in stat_dump_snapshot()
910  *  must finish all statistic info calculations.
911  *
912  * @param global    The global entry
913  * @param graph     The current entry
914  */
915 static void update_graph_stat_2(graph_entry_t *global, graph_entry_t *graph)
916 {
917   if (graph->is_deleted) {
918     /* deleted, ignore */
919     return;
920   }
921
922   if (graph->irg) {
923     /* count the nodes in the graph */
924     irg_walk_graph(graph->irg, update_node_stat_2, NULL, graph);
925
926     if (graph->is_leaf_call == LCS_UNKNOWN)
927       graph->is_leaf_call = LCS_LEAF_CALL;
928   }
929 }
930
931 /**
932  * register a dumper
933  */
934 static void stat_register_dumper(const dumper_t *dumper)
935 {
936   dumper_t *p = xmalloc(sizeof(*p));
937
938   if (p) {
939     *p = *dumper;
940
941     p->next        = status->dumper;
942     p->status      = status;
943     status->dumper = p;
944   }
945
946   /* FIXME: memory leak */
947 }
948
949 /**
950  * dumps an IR graph.
951  */
952 static void stat_dump_graph(graph_entry_t *entry)
953 {
954   dumper_t *dumper;
955
956   for (dumper = status->dumper; dumper; dumper = dumper->next) {
957     if (dumper->dump_graph)
958       dumper->dump_graph(dumper, entry);
959   }
960 }
961
962 /**
963  * dumps a constant table
964  */
965 static void stat_dump_consts(const constant_info_t *tbl)
966 {
967   dumper_t *dumper;
968
969   for (dumper = status->dumper; dumper; dumper = dumper->next) {
970     if (dumper->dump_const_tbl)
971       dumper->dump_const_tbl(dumper, tbl);
972   }
973 }
974
975 /**
976  * initialize the dumper
977  */
978 static void stat_dump_init(const char *name)
979 {
980   dumper_t *dumper;
981
982   for (dumper = status->dumper; dumper; dumper = dumper->next) {
983     if (dumper->init)
984       dumper->init(dumper, name);
985   }
986 }
987
988 /**
989  * finish the dumper
990  */
991 static void stat_dump_finish(void)
992 {
993   dumper_t *dumper;
994
995   for (dumper = status->dumper; dumper; dumper = dumper->next) {
996     if (dumper->finish)
997       dumper->finish(dumper);
998   }
999 }
1000
1001 /* ---------------------------------------------------------------------- */
1002
1003 /*
1004  * helper: get an ir_op from an opcode
1005  */
1006 ir_op *stat_get_op_from_opcode(opcode code)
1007 {
1008   return opcode_find_entry(code, status->ir_op_hash);
1009 }
1010
1011 /**
1012  * A new IR op is registered.
1013  *
1014  * @param ctx  the hook context
1015  * @param op   the new IR opcode that was created.
1016  */
1017 static void stat_new_ir_op(void *ctx, ir_op *op)
1018 {
1019   if (! status->stat_options)
1020     return;
1021
1022   STAT_ENTER;
1023   {
1024     graph_entry_t *graph = graph_get_entry(NULL, status->irg_hash);
1025
1026     /* execute for side effect :-) */
1027     opcode_get_entry(op, graph->opcode_hash);
1028
1029     pset_insert(status->ir_op_hash, op, op->code);
1030   }
1031   STAT_LEAVE;
1032 }
1033
1034 /**
1035  * An IR op is freed.
1036  *
1037  * @param ctx  the hook context
1038  * @param op   the IR opcode that is freed
1039  */
1040 static void stat_free_ir_op(void *ctx, ir_op *op)
1041 {
1042   if (! status->stat_options)
1043     return;
1044
1045   STAT_ENTER;
1046   {
1047   }
1048   STAT_LEAVE;
1049 }
1050
1051 /**
1052  * A new node is created.
1053  *
1054  * @param ctx   the hook context
1055  * @param irg   the IR graph on which the node is created
1056  * @param node  the new IR node that was created
1057  */
1058 static void stat_new_node(void *ctx, ir_graph *irg, ir_node *node)
1059 {
1060   if (! status->stat_options)
1061     return;
1062
1063   /* do NOT count during dead node elimination */
1064   if (status->in_dead_node_elim > 0)
1065     return;
1066
1067   STAT_ENTER;
1068   {
1069     node_entry_t *entry;
1070     graph_entry_t *graph;
1071     ir_op *op = stat_get_irn_op(node);
1072
1073     /* increase global value */
1074     graph = graph_get_entry(NULL, status->irg_hash);
1075     entry = opcode_get_entry(op, graph->opcode_hash);
1076     cnt_inc(&entry->new_node);
1077
1078     /* increase local value */
1079     graph = graph_get_entry(current_ir_graph, status->irg_hash);
1080     entry = opcode_get_entry(op, graph->opcode_hash);
1081     cnt_inc(&entry->new_node);
1082   }
1083   STAT_LEAVE;
1084 }
1085
1086 /**
1087  * A node is changed into a Id node
1088  *
1089  * @param ctx   the hook context
1090  * @param node  the IR node that will be turned into an ID
1091  */
1092 static void stat_turn_into_id(void *ctx, ir_node *node)
1093 {
1094   if (! status->stat_options)
1095     return;
1096
1097   STAT_ENTER;
1098   {
1099     node_entry_t *entry;
1100     graph_entry_t *graph;
1101     ir_op *op = stat_get_irn_op(node);
1102
1103     /* increase global value */
1104     graph = graph_get_entry(NULL, status->irg_hash);
1105     entry = opcode_get_entry(op, graph->opcode_hash);
1106     cnt_inc(&entry->into_Id);
1107
1108     /* increase local value */
1109     graph = graph_get_entry(current_ir_graph, status->irg_hash);
1110     entry = opcode_get_entry(op, graph->opcode_hash);
1111     cnt_inc(&entry->into_Id);
1112   }
1113   STAT_LEAVE;
1114 }
1115
1116 /**
1117  * A new graph was created
1118  *
1119  * @param ctx  the hook context
1120  * @param irg  the new IR graph that was created
1121  * @param ent  the entity of this graph
1122  */
1123 static void stat_new_graph(void *ctx, ir_graph *irg, entity *ent)
1124 {
1125   if (! status->stat_options)
1126     return;
1127
1128   STAT_ENTER;
1129   {
1130     /* execute for side effect :-) */
1131     graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
1132
1133     graph->ent           = ent;
1134     graph->is_deleted    = 0;
1135     graph->is_leaf       = 0;
1136     graph->is_leaf_call  = 0;
1137     graph->is_recursive  = 0;
1138     graph->is_chain_call = 0;
1139     graph->is_analyzed   = 0;
1140   }
1141   STAT_LEAVE;
1142 }
1143
1144 /**
1145  * A graph will be deleted
1146  *
1147  * @param ctx  the hook context
1148  * @param irg  the IR graph that will be deleted
1149  *
1150  * Note that we still hold the information for this graph
1151  * in our hash maps, only a flag is set which prevents this
1152  * information from being changed, it's "frozen" from now.
1153  */
1154 static void stat_free_graph(void *ctx, ir_graph *irg)
1155 {
1156   if (! status->stat_options)
1157     return;
1158
1159   STAT_ENTER;
1160   {
1161     graph_entry_t *graph  = graph_get_entry(irg, status->irg_hash);
1162     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1163
1164     graph->is_deleted = 1;
1165
1166     if (status->stat_options & FIRMSTAT_COUNT_DELETED) {
1167       /* count the nodes of the graph yet, it will be destroyed later */
1168       update_graph_stat(global, graph);
1169     }
1170   }
1171   STAT_LEAVE;
1172 }
1173
1174 /**
1175  * A walk over a graph is initiated. Do not count walks from statistic code.
1176  *
1177  * @param ctx  the hook context
1178  * @param irg  the IR graph that will be walked
1179  * @param pre  the pre walker
1180  * @param post the post walker
1181  */
1182 static void stat_irg_walk(void *ctx, ir_graph *irg, generic_func *pre, generic_func *post)
1183 {
1184   if (! status->stat_options)
1185     return;
1186
1187   STAT_ENTER_SINGLE;
1188   {
1189     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1190
1191     cnt_inc(&graph->cnt_walked);
1192   }
1193   STAT_LEAVE;
1194 }
1195
1196 /**
1197  * A walk over a graph in block-wise order is initiated. Do not count walks from statistic code.
1198  *
1199  * @param ctx  the hook context
1200  * @param irg  the IR graph that will be walked
1201  * @param pre  the pre walker
1202  * @param post the post walker
1203  */
1204 static void stat_irg_walk_blkwise(void *ctx, ir_graph *irg, generic_func *pre, generic_func *post)
1205 {
1206   /* for now, do NOT differentiate between blockwise and normal */
1207   stat_irg_walk(ctx, irg, pre, post);
1208 }
1209
1210 /**
1211  * A walk over the graph's blocks is initiated. Do not count walks from statistic code.
1212  *
1213  * @param ctx  the hook context
1214  * @param irg  the IR graph that will be walked
1215  * @param node the IR node
1216  * @param pre  the pre walker
1217  * @param post the post walker
1218  */
1219 static void stat_irg_block_walk(void *ctx, ir_graph *irg, ir_node *node, generic_func *pre, generic_func *post)
1220 {
1221   if (! status->stat_options)
1222     return;
1223
1224   STAT_ENTER_SINGLE;
1225   {
1226     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1227
1228     cnt_inc(&graph->cnt_walked_blocks);
1229   }
1230   STAT_LEAVE;
1231 }
1232
1233 /**
1234  * called for every node that is removed due to an optimization.
1235  *
1236  * @param n     the IR node that will be removed
1237  * @param hmap  the hash map containing ir_op* -> opt_entry_t*
1238  */
1239 static void removed_due_opt(ir_node *n, hmap_opt_entry_t *hmap)
1240 {
1241   ir_op *op          = stat_get_irn_op(n);
1242   opt_entry_t *entry = opt_get_entry(op, hmap);
1243
1244   /* increase global value */
1245   cnt_inc(&entry->count);
1246 }
1247
1248 /**
1249  * Some nodes were optimized into some others due to an optimization.
1250  *
1251  * @param ctx  the hook context
1252  */
1253 static void stat_merge_nodes(
1254     void *ctx,
1255     ir_node **new_node_array, int new_num_entries,
1256     ir_node **old_node_array, int old_num_entries,
1257     hook_opt_kind opt)
1258 {
1259   if (! status->stat_options)
1260     return;
1261
1262   STAT_ENTER;
1263   {
1264     int i, j;
1265     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1266
1267     if (status->reassoc_run)
1268       opt = HOOK_OPT_REASSOC;
1269
1270     for (i = 0; i < old_num_entries; ++i) {
1271       for (j = 0; j < new_num_entries; ++j)
1272         if (old_node_array[i] == new_node_array[j])
1273           break;
1274
1275       /* nodes might be in new and old, these are NOT removed */
1276       if (j >= new_num_entries) {
1277         int xopt = opt;
1278
1279         /* sometimes we did not detect, that it is replaced by a Const */
1280         if (opt == HOOK_OPT_CONFIRM && new_num_entries == 1) {
1281           ir_op *op = get_irn_op(new_node_array[0]);
1282
1283           if (op == op_Const || op == op_SymConst)
1284             xopt = HOOK_OPT_CONFIRM_C;
1285         }
1286
1287         removed_due_opt(old_node_array[i], graph->opt_hash[xopt]);
1288       }
1289     }
1290   }
1291   STAT_LEAVE;
1292 }
1293
1294 /**
1295  * Reassociation is started/stopped.
1296  *
1297  * @param ctx   the hook context
1298  * @param flag  if non-zero, reassociation is started else stopped
1299  */
1300 static void stat_reassociate(void *ctx, int flag)
1301 {
1302   if (! status->stat_options)
1303     return;
1304
1305   STAT_ENTER;
1306   {
1307     status->reassoc_run = flag;
1308   }
1309   STAT_LEAVE;
1310 }
1311
1312 /**
1313  * A node was lowered into other nodes
1314  *
1315  * @param ctx  the hook context
1316  * @param node the IR node that will be lowered
1317  */
1318 static void stat_lower(void *ctx, ir_node *node)
1319 {
1320   if (! status->stat_options)
1321     return;
1322
1323   STAT_ENTER;
1324   {
1325     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1326
1327     removed_due_opt(node, graph->opt_hash[HOOK_LOWERED]);
1328   }
1329   STAT_LEAVE;
1330 }
1331
1332 /**
1333  * A graph was inlined.
1334  *
1335  * @param ctx  the hook context
1336  * @param call the IR call that will re changed into the body of
1337  *             the called IR graph
1338  * @param called_irg  the IR graph representing the called routine
1339  */
1340 static void stat_inline(void *ctx, ir_node *call, ir_graph *called_irg)
1341 {
1342   if (! status->stat_options)
1343     return;
1344
1345   STAT_ENTER;
1346   {
1347     ir_graph *irg = get_irn_irg(call);
1348     graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
1349     graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
1350
1351     cnt_inc(&graph->cnt_got_inlined);
1352     cnt_inc(&i_graph->cnt_was_inlined);
1353   }
1354   STAT_LEAVE;
1355 }
1356
1357 /**
1358  * A graph with tail-recursions was optimized.
1359  *
1360  * @param ctx  the hook context
1361  */
1362 static void stat_tail_rec(void *ctx, ir_graph *irg, int n_calls)
1363 {
1364   if (! status->stat_options)
1365     return;
1366
1367   STAT_ENTER;
1368   {
1369     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1370
1371     graph->num_tail_recursion += n_calls;
1372   }
1373   STAT_LEAVE;
1374 }
1375
1376 /**
1377  * Strength reduction was performed on an iteration variable.
1378  *
1379  * @param ctx  the hook context
1380  */
1381 static void stat_strength_red(void *ctx, ir_graph *irg, ir_node *strong, ir_node *cmp)
1382 {
1383   if (! status->stat_options)
1384     return;
1385
1386   STAT_ENTER;
1387   {
1388     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1389     cnt_inc(&graph->cnt_strength_red);
1390
1391     removed_due_opt(strong, graph->opt_hash[HOOK_OPT_STRENGTH_RED]);
1392   }
1393   STAT_LEAVE;
1394 }
1395
1396 /**
1397  * Start the dead node elimination.
1398  *
1399  * @param ctx  the hook context
1400  */
1401 static void stat_dead_node_elim_start(void *ctx, ir_graph *irg)
1402 {
1403   if (! status->stat_options)
1404     return;
1405
1406   ++status->in_dead_node_elim;
1407 }
1408
1409 /**
1410  * Stops the dead node elimination.
1411  *
1412  * @param ctx  the hook context
1413  */
1414 static void stat_dead_node_elim_stop(void *ctx, ir_graph *irg)
1415 {
1416   if (! status->stat_options)
1417     return;
1418
1419   --status->in_dead_node_elim;
1420 }
1421
1422 /**
1423  * if-conversion was tried
1424  */
1425 static void stat_if_conversion(void *context, ir_graph *irg, ir_node *phi,
1426                                int pos, ir_node *mux, if_result_t reason)
1427 {
1428   if (! status->stat_options)
1429     return;
1430
1431   STAT_ENTER;
1432   {
1433     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1434
1435     cnt_inc(&graph->cnt_if_conv[reason]);
1436   }
1437   STAT_LEAVE;
1438 }
1439
1440 /**
1441  * real function call was optimized
1442  */
1443 static void stat_func_call(void *context, ir_graph *irg, ir_node *call)
1444 {
1445   if (! status->stat_options)
1446     return;
1447
1448   STAT_ENTER;
1449   {
1450     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1451
1452     cnt_inc(&graph->cnt_real_func_call);
1453   }
1454   STAT_LEAVE;
1455 }
1456
1457 /**
1458  * A multiply was replaced by a series of Shifts/Adds/Subs
1459  *
1460  * @param ctx  the hook context
1461  */
1462 static void stat_arch_dep_replace_mul_with_shifts(void *ctx, ir_node *mul)
1463 {
1464   if (! status->stat_options)
1465     return;
1466
1467   STAT_ENTER;
1468   {
1469     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1470     removed_due_opt(mul, graph->opt_hash[HOOK_OPT_ARCH_DEP]);
1471   }
1472   STAT_LEAVE;
1473 }
1474
1475 /**
1476  * A division was replaced by a series of Shifts/Muls
1477  *
1478  * @param ctx  the hook context
1479  * @param div  the div node that will be optimized
1480  */
1481 static void stat_arch_dep_replace_div_by_const(void *ctx, ir_node *div)
1482 {
1483   if (! status->stat_options)
1484     return;
1485
1486   STAT_ENTER;
1487   {
1488     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1489     removed_due_opt(div, graph->opt_hash[HOOK_OPT_ARCH_DEP]);
1490   }
1491   STAT_LEAVE;
1492 }
1493
1494 /**
1495  * A modulo was replaced by a series of Shifts/Muls
1496  *
1497  * @param ctx  the hook context
1498  * @param mod  the mod node that will be optimized
1499  */
1500 static void stat_arch_dep_replace_mod_by_const(void *ctx, ir_node *mod)
1501 {
1502   if (! status->stat_options)
1503     return;
1504
1505   STAT_ENTER;
1506   {
1507     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1508     removed_due_opt(mod, graph->opt_hash[HOOK_OPT_ARCH_DEP]);
1509   }
1510   STAT_LEAVE;
1511 }
1512
1513 /**
1514  * A DivMod was replaced by a series of Shifts/Muls
1515  *
1516  * @param ctx     the hook context
1517  * @param divmod  the divmod node that will be optimized
1518  */
1519 static void stat_arch_dep_replace_DivMod_by_const(void *ctx, ir_node *divmod)
1520 {
1521   if (! status->stat_options)
1522     return;
1523
1524   STAT_ENTER;
1525   {
1526     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1527     removed_due_opt(divmod, graph->opt_hash[HOOK_OPT_ARCH_DEP]);
1528   }
1529   STAT_LEAVE;
1530 }
1531
1532 /* Dumps a statistics snapshot */
1533 void stat_dump_snapshot(const char *name, const char *phase)
1534 {
1535   char fname[2048];
1536   const char *p;
1537   int l;
1538
1539   if (! status->stat_options)
1540     return;
1541
1542   STAT_ENTER;
1543   {
1544     graph_entry_t *entry;
1545     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1546
1547     /*
1548      * The constant counter is only global, so we clear it here.
1549      * Note that it does NOT contain the constants in DELETED
1550      * graphs due to this.
1551      */
1552     if (status->stat_options & FIRMSTAT_COUNT_CONSTS)
1553       stat_const_clear(status);
1554
1555     /* build the name */
1556     p = strrchr(name, '/');
1557 #ifdef _WIN32
1558     {
1559       const char *q;
1560
1561       q = strrchr(name, '\\');
1562
1563       /* NULL might be not the smallest pointer */
1564       if (q && (!p || q > p))
1565         p = q;
1566     }
1567 #endif
1568     if (p) {
1569       ++p;
1570       l = p - name;
1571
1572       if (l > sizeof(fname) - 1)
1573         l = sizeof(fname) - 1;
1574
1575       memcpy(fname, name, l);
1576       fname[l] = '\0';
1577     }
1578     else {
1579       fname[0] = '\0';
1580       p = name;
1581     }
1582     strncat(fname, "firmstat-", sizeof(fname));
1583     strncat(fname, phase, sizeof(fname));
1584     strncat(fname, "-", sizeof(fname));
1585     strncat(fname, p, sizeof(fname));
1586
1587     stat_dump_init(fname);
1588
1589     /* calculate the graph statistics */
1590     for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1591
1592       if (entry->irg == NULL) {
1593         /* special entry for the global count */
1594         continue;
1595       }
1596
1597       if (! entry->is_deleted) {
1598         /* the graph is still alive, count the nodes on it */
1599         update_graph_stat(global, entry);
1600       }
1601     }
1602
1603     /* some calculations are dependent, we pushed them on the wait_q */
1604     while (! pdeq_empty(status->wait_q)) {
1605       entry = pdeq_getr(status->wait_q);
1606
1607       update_graph_stat_2(global, entry);
1608     }
1609
1610
1611     /* dump per graph */
1612     for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1613
1614       if (entry->irg == NULL) {
1615         /* special entry for the global count */
1616         continue;
1617       }
1618
1619       if (! entry->is_deleted || status->stat_options & FIRMSTAT_COUNT_DELETED)
1620         stat_dump_graph(entry);
1621
1622       if (! entry->is_deleted) {
1623         /* clear the counter that are not accumulated */
1624         graph_clear_entry(entry, 0);
1625       }
1626     }
1627
1628     /* dump global */
1629     stat_dump_graph(global);
1630
1631     /* dump the const info */
1632     if (status->stat_options & FIRMSTAT_COUNT_CONSTS)
1633       stat_dump_consts(&status->const_info);
1634
1635     stat_dump_finish();
1636
1637     stat_finish_pattern_history();
1638
1639     /* clear the global counter here */
1640     {
1641       node_entry_t *entry;
1642
1643       for (entry = pset_first(global->opcode_hash); entry; entry = pset_next(global->opcode_hash)) {
1644         opcode_clear_entry(entry);
1645       }
1646       /* clear all global counter */
1647       graph_clear_entry(global, 1);
1648     }
1649   }
1650   STAT_LEAVE;
1651 }
1652
1653 /** the hook entries for the Firm statistics module */
1654 static hook_entry_t stat_hooks[hook_last];
1655
1656 /* initialize the statistics module. */
1657 void init_stat(unsigned enable_options)
1658 {
1659 #define X(a)  a, sizeof(a)-1
1660 #define HOOK(h, fkt) \
1661   stat_hooks[h].hook._##h = fkt; register_hook(h, &stat_hooks[h])
1662
1663   if (! (enable_options & FIRMSTAT_ENABLED))
1664     return;
1665
1666   status = xmalloc(sizeof(*status));
1667   memset(status, 0, sizeof(*status));
1668
1669   /* enable statistics */
1670   status->stat_options = enable_options & FIRMSTAT_ENABLED ? enable_options : 0;
1671
1672   /* register all hooks */
1673   HOOK(hook_new_ir_op,                        stat_new_ir_op);
1674   HOOK(hook_free_ir_op,                       stat_free_ir_op);
1675   HOOK(hook_new_node,                         stat_new_node);
1676   HOOK(hook_turn_into_id,                     stat_turn_into_id);
1677   HOOK(hook_new_graph,                        stat_new_graph);
1678   HOOK(hook_free_graph,                       stat_free_graph);
1679   HOOK(hook_irg_walk,                         stat_irg_walk);
1680   HOOK(hook_irg_walk_blkwise,                 stat_irg_walk_blkwise);
1681   HOOK(hook_irg_block_walk,                   stat_irg_block_walk);
1682   HOOK(hook_merge_nodes,                      stat_merge_nodes);
1683   HOOK(hook_reassociate,                      stat_reassociate);
1684   HOOK(hook_lower,                            stat_lower);
1685   HOOK(hook_inline,                           stat_inline);
1686   HOOK(hook_tail_rec,                         stat_tail_rec);
1687   HOOK(hook_strength_red,                     stat_strength_red);
1688   HOOK(hook_dead_node_elim_start,             stat_dead_node_elim_start);
1689   HOOK(hook_dead_node_elim_stop,              stat_dead_node_elim_stop);
1690   HOOK(hook_if_conversion,                    stat_if_conversion);
1691   HOOK(hook_func_call,                        stat_func_call);
1692   HOOK(hook_arch_dep_replace_mul_with_shifts, stat_arch_dep_replace_mul_with_shifts);
1693   HOOK(hook_arch_dep_replace_div_by_const,    stat_arch_dep_replace_div_by_const);
1694   HOOK(hook_arch_dep_replace_mod_by_const,    stat_arch_dep_replace_mod_by_const);
1695   HOOK(hook_arch_dep_replace_DivMod_by_const, stat_arch_dep_replace_DivMod_by_const);
1696
1697   obstack_init(&status->cnts);
1698   obstack_init(&status->recalc_cnts);
1699
1700   /* create the hash-tables */
1701   status->irg_hash   = new_pset(graph_cmp, 8);
1702   status->ir_op_hash = new_pset(opcode_cmp_2, 1);
1703
1704   /* create the wait queue */
1705   status->wait_q     = new_pdeq();
1706
1707   if (enable_options & FIRMSTAT_COUNT_STRONG_OP) {
1708     /* build the pseudo-ops */
1709     _op_Phi0.code    = get_next_ir_opcode();
1710     _op_Phi0.name    = new_id_from_chars(X("Phi0"));
1711
1712     _op_PhiM.code    = get_next_ir_opcode();
1713     _op_PhiM.name    = new_id_from_chars(X("PhiM"));
1714
1715     _op_ProjM.code   = get_next_ir_opcode();
1716     _op_ProjM.name   = new_id_from_chars(X("ProjM"));
1717
1718     _op_MulC.code    = get_next_ir_opcode();
1719     _op_MulC.name    = new_id_from_chars(X("MulC"));
1720
1721     _op_DivC.code    = get_next_ir_opcode();
1722     _op_DivC.name    = new_id_from_chars(X("DivC"));
1723
1724     _op_ModC.code    = get_next_ir_opcode();
1725     _op_ModC.name    = new_id_from_chars(X("ModC"));
1726
1727     _op_DivModC.code = get_next_ir_opcode();
1728     _op_DivModC.name = new_id_from_chars(X("DivModC"));
1729
1730     status->op_Phi0    = &_op_Phi0;
1731     status->op_PhiM    = &_op_PhiM;
1732     status->op_ProjM   = &_op_ProjM;
1733     status->op_MulC    = &_op_MulC;
1734     status->op_DivC    = &_op_DivC;
1735     status->op_ModC    = &_op_ModC;
1736     status->op_DivModC = &_op_DivModC;
1737   }
1738   else {
1739     status->op_Phi0    = NULL;
1740     status->op_PhiM    = NULL;
1741     status->op_ProjM   = NULL;
1742     status->op_MulC    = NULL;
1743     status->op_DivC    = NULL;
1744     status->op_ModC    = NULL;
1745     status->op_DivModC = NULL;
1746   }
1747
1748   if (enable_options & FIRMSTAT_COUNT_SELS) {
1749     _op_SelSel.code    = get_next_ir_opcode();
1750     _op_SelSel.name    = new_id_from_chars(X("Sel(Sel)"));
1751
1752     _op_SelSelSel.code = get_next_ir_opcode();
1753     _op_SelSelSel.name = new_id_from_chars(X("Sel(Sel(Sel))"));
1754
1755     status->op_SelSel    = &_op_SelSel;
1756     status->op_SelSelSel = &_op_SelSelSel;
1757   }
1758   else {
1759     status->op_SelSel    = NULL;
1760     status->op_SelSelSel = NULL;
1761   }
1762
1763   /* register the dumper */
1764   stat_register_dumper(&simple_dumper);
1765
1766   if (enable_options & FIRMSTAT_CSV_OUTPUT)
1767     stat_register_dumper(&csv_dumper);
1768
1769   /* initialize the pattern hash */
1770   stat_init_pattern_history(enable_options & FIRMSTAT_PATTERN_ENABLED);
1771
1772   /* initialize the Const options */
1773   if (enable_options & FIRMSTAT_COUNT_CONSTS)
1774     stat_init_const_cnt(status);
1775
1776 #undef HOOK
1777 #undef X
1778 }
1779
1780 /* terminates the statistics module, frees all memory */
1781 void stat_term(void) {
1782   if (status != (stat_info_t *)&status_disable) {
1783     xfree(status);
1784     status = (stat_info_t *)&status_disable;
1785   }
1786 }
1787
1788 #else
1789
1790 /* initialize the statistics module. */
1791 void init_stat(unsigned enable_options) {}
1792
1793 /* Dumps a statistics snapshot */
1794 void stat_dump_snapshot(const char *name, const char *phase) {}
1795
1796 /* terminates the statistics module, frees all memory */
1797 void stat_term(void);
1798
1799 #endif /* FIRM_STATISTICS */