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