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