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