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