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