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