57e791b86fcac04772a52eaa4028e49b7d1e2a7b
[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 *n)
495 {
496   ir_graph *irg        = get_irn_irg(n);
497   graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
498   unsigned mark        = get_adr_mark(graph, n);
499
500   if (mark & MARK_ADDRESS_CALC)
501     fprintf(F, "color: purple");
502   else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR)
503     fprintf(F, "color: lightpurple");
504   else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == (MARK_REF_ADR|MARK_REF_NON_ADR))
505     fprintf(F, "color: lightblue");
506   else
507     return 0;
508
509   /* I know the color! */
510   return 1;
511 }
512
513 /**
514  * walker that marks every node that is an address calculation
515  *
516  * predecessor nodes must be visited first. We ensure this by
517  * calling in in the post of an outs walk. This should work even in cycles,
518  * while the pre in a normal walk will not.
519  */
520 static void mark_address_calc(ir_node *node, void *env)
521 {
522   graph_entry_t *graph = env;
523   ir_mode *mode = get_irn_mode(node);
524   int i, n;
525   unsigned mark_preds = MARK_REF_NON_ADR;
526
527   if (! mode_is_numP(mode))
528     return;
529
530   if (mode_is_reference(mode)) {
531     /* a reference is calculated here, we are sure */
532     set_adr_mark(graph, node, MARK_ADDRESS_CALC);
533
534     mark_preds = MARK_REF_ADR;
535   }
536   else {
537     unsigned mark = get_adr_mark(graph, node);
538
539     if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR) {
540       /*
541        * this node has not an reference mode, but is only
542        * referenced by address calculations
543        */
544       mark_preds = MARK_REF_ADR;
545     }
546   }
547
548   /* makr all predecessors */
549   for (i = 0, n = get_irn_arity(node); i < n; ++i) {
550     ir_node *pred = get_irn_n(node, i);
551
552     set_adr_mark(graph, pred, get_adr_mark(graph, pred) | mark_preds);
553   }
554 }
555
556 /**
557  * called for every graph when the graph is either deleted or stat_finish()
558  * is called, must recalculate all statistic info
559  */
560 static void update_graph_stat(graph_entry_t *global, graph_entry_t *graph)
561 {
562   node_entry_t *entry;
563
564   /* clear first the alive counter in the graph */
565   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
566     cnt_clr(&entry->cnt_alive);
567   }
568
569   /* set pessimistic values */
570   graph->is_leaf       = 1;
571   graph->is_recursive  = 0;
572   graph->is_chain_call = 1;
573
574   /* we need dominator info */
575   if (graph->irg != get_const_code_irg())
576     if (get_irg_dom_state(graph->irg) != dom_consistent)
577       compute_doms(graph->irg);
578
579   /* count the nodes in the graph */
580   irg_walk_graph(graph->irg, update_node_stat, NULL, graph);
581
582 #if 0
583   entry = opcode_get_entry(op_Call, graph->opcode_hash);
584
585   /* check if we have more than 1 call */
586   if (cnt_gt(entry->cnt_alive, 1))
587     graph->is_chain_call = 0;
588 #endif
589
590   /* recursive functions are never chain calls, leafs don't have calls */
591   if (graph->is_recursive || graph->is_leaf)
592     graph->is_chain_call = 0;
593
594   /* assume we walk every graph only ONCE, we could sum here the global count */
595   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
596     node_entry_t *g_entry = opcode_get_entry(entry->op, global->opcode_hash);
597
598     /* update the node counter */
599     cnt_add(&g_entry->cnt_alive, &entry->cnt_alive);
600   }
601
602   /* update the edge counter */
603   cnt_add(&global->cnt_edges, &graph->cnt_edges);
604
605   /* count the number of address calculation */
606   if (graph->irg != get_const_code_irg()) {
607     ir_graph *rem = current_ir_graph;
608
609     if (get_irg_outs_state(graph->irg) != outs_consistent)
610       compute_outs(graph->irg);
611
612     /* Must be done an the outs graph */
613     current_ir_graph = graph->irg;
614     irg_out_walk(get_irg_start(graph->irg), NULL, mark_address_calc, graph);
615     current_ir_graph = rem;
616
617 #if 0
618     set_dump_node_vcgattr_hook(stat_adr_mark_hook);
619     dump_ir_block_graph(graph->irg, "-adr");
620     set_dump_node_vcgattr_hook(NULL);
621 #endif
622   }
623 }
624
625 /**
626  * register a dumper
627  */
628 static void stat_register_dumper(const dumper_t *dumper)
629 {
630   dumper_t *p = malloc(sizeof(*p));
631
632   if (p) {
633     *p = *dumper;
634
635     p->next        = status->dumper;
636     p->status      = status;
637     status->dumper = p;
638   }
639
640   /* FIXME: memory leak */
641 }
642
643 /**
644  * dumps an irg
645  */
646 static void stat_dump_graph(graph_entry_t *entry)
647 {
648   dumper_t *dumper;
649
650   for (dumper = status->dumper; dumper; dumper = dumper->next) {
651     if (dumper->dump_graph)
652       dumper->dump_graph(dumper, entry);
653   }
654 }
655
656 /**
657  * initialise the dumper
658  */
659 static void stat_dump_init(const char *name)
660 {
661   dumper_t *dumper;
662
663   for (dumper = status->dumper; dumper; dumper = dumper->next) {
664     if (dumper->init)
665       dumper->init(dumper, name);
666   }
667 }
668
669 /**
670  * finish the dumper
671  */
672 static void stat_dump_finish(void)
673 {
674   dumper_t *dumper;
675
676   for (dumper = status->dumper; dumper; dumper = dumper->next) {
677     if (dumper->finish)
678       dumper->finish(dumper);
679   }
680 }
681
682 /* ---------------------------------------------------------------------- */
683
684 /*
685  * helper: get an ir_op from an opcode
686  */
687 ir_op *stat_get_op_from_opcode(opcode code)
688 {
689   return opcode_find_entry(code, status->ir_op_hash);
690 }
691
692 /* initialize the statistics module. */
693 void init_stat(unsigned enable_options)
694 {
695 #define X(a)  a, sizeof(a)-1
696
697   /* enable statistics */
698   status->enable = enable_options & FIRMSTAT_ENABLED;
699
700   if (! status->enable)
701    return;
702
703   obstack_init(&status->cnts);
704
705   /* create the hash-tables */
706   status->irg_hash   = new_pset(graph_cmp, 8);
707   status->ir_op_hash = new_pset(opcode_cmp_2, 1);
708
709   status->op_Phi0    = &_op_Phi0;
710   status->op_PhiM    = &_op_PhiM;
711
712   if (enable_options & FIRMSTAT_COUNT_STRONG_OP) {
713     /* build the pseudo-ops */
714     _op_Phi0.code    = get_next_ir_opcode();
715     _op_Phi0.name    = new_id_from_chars(X("Phi0"));
716
717     _op_PhiM.code    = get_next_ir_opcode();
718     _op_PhiM.name    = new_id_from_chars(X("PhiM"));
719
720     _op_MulC.code    = get_next_ir_opcode();
721     _op_MulC.name    = new_id_from_chars(X("MulC"));
722
723     _op_DivC.code    = get_next_ir_opcode();
724     _op_DivC.name    = new_id_from_chars(X("DivC"));
725
726     _op_ModC.code    = get_next_ir_opcode();
727     _op_ModC.name    = new_id_from_chars(X("ModC"));
728
729     _op_DivModC.code = get_next_ir_opcode();
730     _op_DivModC.name = new_id_from_chars(X("DivModC"));
731
732     status->op_MulC    = &_op_MulC;
733     status->op_DivC    = &_op_DivC;
734     status->op_ModC    = &_op_ModC;
735     status->op_DivModC = &_op_DivModC;
736   }
737   else {
738     status->op_MulC    = NULL;
739     status->op_DivC    = NULL;
740     status->op_ModC    = NULL;
741     status->op_DivModC = NULL;
742   }
743
744   /* register the dumper */
745   stat_register_dumper(&simple_dumper);
746
747   if (enable_options & FIRMSTAT_CSV_OUTPUT)
748     stat_register_dumper(&csv_dumper);
749
750   /* initialize the pattern hash */
751   stat_init_pattern_history(enable_options & FIRMSTAT_PATTERN_ENABLED);
752 #undef X
753 }
754
755 /* A new IR op is registered. */
756 void stat_new_ir_op(const ir_op *op)
757 {
758   if (! status->enable)
759     return;
760
761   STAT_ENTER;
762   {
763     graph_entry_t *graph = graph_get_entry(NULL, status->irg_hash);
764
765     /* execute for side effect :-) */
766     opcode_get_entry(op, graph->opcode_hash);
767
768     pset_insert(status->ir_op_hash, op, op->code);
769   }
770   STAT_LEAVE;
771 }
772
773 /* An IR op is freed. */
774 void stat_free_ir_op(const ir_op *op)
775 {
776   if (! status->enable)
777     return;
778
779   STAT_ENTER;
780   {
781   }
782   STAT_LEAVE;
783 }
784
785 /* A new node is created. */
786 void stat_new_node(ir_node *node)
787 {
788   if (! status->enable)
789     return;
790
791   /* do NOT count during dead node elimination */
792   if (status->in_dead_node_elim > 0)
793     return;
794
795   STAT_ENTER;
796   {
797     node_entry_t *entry;
798     graph_entry_t *graph;
799     ir_op *op = stat_get_irn_op(node);
800
801     /* increase global value */
802     graph = graph_get_entry(NULL, status->irg_hash);
803     entry = opcode_get_entry(op, graph->opcode_hash);
804     cnt_inc(&entry->new_node);
805
806     /* increase local value */
807     graph = graph_get_entry(current_ir_graph, status->irg_hash);
808     entry = opcode_get_entry(op, graph->opcode_hash);
809     cnt_inc(&entry->new_node);
810   }
811   STAT_LEAVE;
812 }
813
814 /* A node is changed into a Id node */
815 void stat_turn_into_id(ir_node *node)
816 {
817   if (! status->enable)
818     return;
819
820   STAT_ENTER;
821   {
822     node_entry_t *entry;
823     graph_entry_t *graph;
824     ir_op *op = stat_get_irn_op(node);
825
826     /* increase global value */
827     graph = graph_get_entry(NULL, status->irg_hash);
828     entry = opcode_get_entry(op, graph->opcode_hash);
829     cnt_inc(&entry->into_Id);
830
831     /* increase local value */
832     graph = graph_get_entry(current_ir_graph, status->irg_hash);
833     entry = opcode_get_entry(op, graph->opcode_hash);
834     cnt_inc(&entry->into_Id);
835   }
836   STAT_LEAVE;
837 }
838
839 /* A new graph was created */
840 void stat_new_graph(ir_graph *irg, entity *ent)
841 {
842   if (! status->enable)
843     return;
844
845   STAT_ENTER;
846   {
847     /* execute for side effect :-) */
848     graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
849
850     graph->ent           = ent;
851     graph->is_deleted    = 0;
852     graph->is_leaf       = 0;
853     graph->is_recursive  = 0;
854     graph->is_chain_call = 0;
855   }
856   STAT_LEAVE;
857 }
858
859 /*
860  * A graph will be deleted
861  */
862 void stat_free_graph(ir_graph *irg)
863 {
864   if (! status->enable)
865     return;
866
867   STAT_ENTER;
868   {
869     graph_entry_t *graph  = graph_get_entry(irg, status->irg_hash);
870     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
871
872     graph->is_deleted = 1;
873
874     /* count the nodes of the graph yet, it will be destroyed later */
875     update_graph_stat(global, graph);
876
877     /* count the DAG's */
878     //count_dags_in_graph(global, graph);
879
880     /* calculate the pattern */
881     stat_calc_pattern_history(irg);
882   }
883   STAT_LEAVE;
884 }
885
886 /*
887  * A walk over a graph is initiated. Do not count walks from statistic code.
888  */
889 void stat_irg_walk(ir_graph *irg, void *pre, void *post)
890 {
891   if (! status->enable)
892     return;
893
894   STAT_ENTER_SINGLE;
895   {
896     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
897
898     cnt_inc(&graph->cnt_walked);
899   }
900   STAT_LEAVE;
901 }
902
903 /*
904  * A walk over a graph in block-wise order is initiated. Do not count walks from statistic code.
905  */
906 void stat_irg_walk_blkwise(ir_graph *irg, void *pre, void *post)
907 {
908   /* for now, do NOT differentiate between blockwise and normal */
909   stat_irg_walk(irg, pre, post);
910 }
911
912 /*
913  * A walk over the graph's blocks is initiated. Do not count walks from statistic code.
914  */
915 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post)
916 {
917   if (! status->enable)
918     return;
919
920   STAT_ENTER_SINGLE;
921   {
922     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
923
924     cnt_inc(&graph->cnt_walked_blocks);
925   }
926   STAT_LEAVE;
927 }
928
929 /**
930  * called for every node that is removed due to an optimization
931  */
932 static void removed_due_opt(ir_node *n, pset *set)
933 {
934   ir_op *op          = stat_get_irn_op(n);
935   opt_entry_t *entry = opt_get_entry(op, set);
936
937   /* increase global value */
938   cnt_inc(&entry->count);
939 }
940
941 /*
942  * Some nodes were optimized into some others due to an optimization
943  */
944 void stat_merge_nodes(
945     ir_node **new_node_array, int new_num_entries,
946     ir_node **old_node_array, int old_num_entries,
947     stat_opt_kind opt)
948 {
949   if (! status->enable)
950     return;
951
952   STAT_ENTER;
953   {
954     int i, j;
955     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
956
957     if (status->reassoc_run)
958       opt = STAT_OPT_REASSOC;
959
960     for (i = 0; i < old_num_entries; ++i) {
961       for (j = 0; j < new_num_entries; ++j)
962         if (old_node_array[i] == new_node_array[j])
963           break;
964
965       /* nodes might be in new and old, these are NOT removed */
966       if (j >= new_num_entries) {
967         removed_due_opt(old_node_array[i], graph->opt_hash[opt]);
968       }
969     }
970   }
971   STAT_LEAVE;
972 }
973
974 /*
975  * reassociation started/stopped.
976  */
977 void stat_reassociate(int flag)
978 {
979   if (! status->enable)
980     return;
981
982   STAT_ENTER;
983   {
984     status->reassoc_run = flag;
985   }
986   STAT_LEAVE;
987 }
988
989 /*
990  * A node was lowered into other nodes
991  */
992 void stat_lower(ir_node *node)
993 {
994   if (! status->enable)
995     return;
996
997   STAT_ENTER;
998   {
999     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1000
1001     removed_due_opt(node, graph->opt_hash[STAT_LOWERED]);
1002   }
1003   STAT_LEAVE;
1004 }
1005
1006 /*
1007  * A graph was inlined
1008  */
1009 void stat_inline(ir_node *call, ir_graph *called_irg)
1010 {
1011   if (! status->enable)
1012     return;
1013
1014   STAT_ENTER;
1015   {
1016     ir_graph *irg = get_irn_irg(call);
1017     graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
1018     graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
1019
1020     cnt_inc(&graph->cnt_got_inlined);
1021     cnt_inc(&i_graph->cnt_was_inlined);
1022   }
1023   STAT_LEAVE;
1024 }
1025
1026 /*
1027  * A graph with tail-recursions was optimized.
1028  */
1029 void stat_tail_rec(ir_graph *irg)
1030 {
1031   if (! status->enable)
1032     return;
1033
1034   STAT_ENTER;
1035   {
1036   }
1037   STAT_LEAVE;
1038 }
1039
1040 /*
1041  * Strength reduction was performed on an iteration variable.
1042  */
1043 void stat_strength_red(ir_graph *irg, ir_node *strong, ir_node *cmp)
1044 {
1045   if (! status->enable)
1046     return;
1047
1048   STAT_ENTER;
1049   {
1050     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1051     cnt_inc(&graph->cnt_strength_red);
1052
1053     removed_due_opt(strong, graph->opt_hash[STAT_OPT_STRENGTH_RED]);
1054   }
1055   STAT_LEAVE;
1056 }
1057
1058 /*
1059  * Start the dead node elimination.
1060  */
1061 void stat_dead_node_elim_start(ir_graph *irg)
1062 {
1063   if (! status->enable)
1064     return;
1065
1066   ++status->in_dead_node_elim;
1067 }
1068
1069 /*
1070  * Stops the dead node elimination.
1071  */
1072 void stat_dead_node_elim_stop(ir_graph *irg)
1073 {
1074   if (! status->enable)
1075     return;
1076
1077   --status->in_dead_node_elim;
1078 }
1079
1080 /*
1081  * A multiply was replaced by a series of Shifts/Adds/Subs
1082  */
1083 void stat_arch_dep_replace_mul_with_shifts(ir_node *mul)
1084 {
1085   if (! status->enable)
1086     return;
1087
1088   STAT_ENTER;
1089   {
1090     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1091     removed_due_opt(mul, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1092   }
1093   STAT_LEAVE;
1094 }
1095
1096 /**
1097  * A division was replaced by a series of Shifts/Muls
1098  */
1099 void stat_arch_dep_replace_div_by_const(ir_node *div)
1100 {
1101   if (! status->enable)
1102     return;
1103
1104   STAT_ENTER;
1105   {
1106     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1107     removed_due_opt(div, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1108   }
1109   STAT_LEAVE;
1110 }
1111
1112 /**
1113  * A modulo was replaced by a series of Shifts/Muls
1114  */
1115 void stat_arch_dep_replace_mod_by_const(ir_node *mod)
1116 {
1117   if (! status->enable)
1118     return;
1119
1120   STAT_ENTER;
1121   {
1122     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1123     removed_due_opt(mod, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1124   }
1125   STAT_LEAVE;
1126 }
1127
1128 /**
1129  * A DivMod was replaced by a series of Shifts/Muls
1130  */
1131 void stat_arch_dep_replace_DivMod_by_const(ir_node *divmod)
1132 {
1133   if (! status->enable)
1134     return;
1135
1136   STAT_ENTER;
1137   {
1138     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1139     removed_due_opt(divmod, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1140   }
1141   STAT_LEAVE;
1142 }
1143
1144 /* Finish the statistics */
1145 void stat_finish(const char *name)
1146 {
1147   if (! status->enable)
1148     return;
1149
1150   STAT_ENTER;
1151   {
1152     graph_entry_t *entry;
1153     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1154
1155     stat_dump_init(name);
1156
1157     /* dump per graph */
1158     for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1159
1160       if (entry->irg == NULL) {
1161         /* special entry for the global count */
1162         continue;
1163       }
1164
1165       if (! entry->is_deleted) {
1166         /* the graph is still alive, count the nodes on it */
1167         update_graph_stat(global, entry);
1168
1169         /* count the DAG's */
1170         //count_dags_in_graph(global, entry);
1171
1172         /* calculate the pattern */
1173         stat_calc_pattern_history(entry->irg);
1174       }
1175
1176       stat_dump_graph(entry);
1177
1178       /* clear the counter that are not accumulated */
1179       graph_clear_entry(entry, 0);
1180     }
1181
1182     /* dump global */
1183     stat_dump_graph(global);
1184     stat_dump_finish();
1185
1186     stat_finish_pattern_history();
1187
1188     /* clear the global counter here */
1189     {
1190       node_entry_t *entry;
1191
1192       for (entry = pset_first(global->opcode_hash); entry; entry = pset_next(global->opcode_hash)) {
1193         opcode_clear_entry(entry);
1194       }
1195       /* clear all global counter */
1196       graph_clear_entry(global, 1);
1197     }
1198
1199     /* finished */
1200 //    status->enable = 0;
1201   }
1202   STAT_LEAVE;
1203 }
1204
1205 #else
1206
1207 /* need this for prototypes */
1208 #define FIRM_STATISTICS
1209 #include "firmstat.h"
1210
1211 void init_stat(unsigned enable_options) {}
1212
1213 void stat_finish(const char *name) {}
1214
1215 void stat_new_ir_op(const ir_op *op) {}
1216
1217 void stat_free_ir_op(const ir_op *op) {}
1218
1219 void stat_new_node(ir_node *node) {}
1220
1221 void stat_turn_into_id(ir_node *node) {}
1222
1223 void stat_new_graph(ir_graph *irg, entity *ent) {}
1224
1225 void stat_free_graph(ir_graph *irg) {}
1226
1227 void stat_irg_walk(ir_graph *irg, void *pre, void *post) {}
1228
1229 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post) {}
1230
1231 void stat_merge_nodes(
1232     ir_node **new_node_array, int new_num_entries,
1233     ir_node **old_node_array, int old_num_entries,
1234     stat_opt_kind opt) {}
1235
1236 void stat_reassociate(int start) {}
1237
1238 void stat_lower(ir_node *node) {}
1239
1240 void stat_inline(ir_node *call, ir_graph *irg) {}
1241
1242 void stat_tail_rec(ir_graph *irg) {}
1243
1244 void stat_strength_red(ir_graph *irg, ir_node *strong, ir_node *cmp) {}
1245
1246 void stat_dead_node_elim_start(ir_graph *irg) {}
1247
1248 void stat_dead_node_elim_stop(ir_graph *irg) {}
1249
1250 void stat_arch_dep_replace_mul_with_shifts(ir_node *mul) {}
1251
1252 void stat_arch_dep_replace_div_by_const(ir_node *div) {}
1253
1254 void stat_arch_dep_replace_mod_by_const(ir_node *mod) {}
1255
1256 void stat_arch_dep_replace_DivMod_by_const(ir_node *divmod) {}
1257
1258 #endif