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