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