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