Added support for Mul/Div with constant detection
[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 #include <stdlib.h>
20 #include <string.h>
21
22 #include "firmstat_t.h"
23 #include "pattern.h"
24 #include "dags.h"
25
26 /**
27  * names of the optimizations
28  */
29 static const char *opt_names[] = {
30   "straightening optimization",
31   "if simplification",
32   "algebraic simplification",
33   "Phi optmization",
34   "Write-After-Write optimization",
35   "Write-After-Read optimization",
36   "Read-After-Write optimization",
37   "Read-After-Read optimization",
38   "Tuple optimization",
39   "ID optimization",
40   "Constant evaluation",
41   "Strength reduction",
42   "Lowered",
43 };
44
45 /*
46  * need this to be static:
47  * Special pseudo Opcodes that we need to count some interesting cases
48  */
49
50 /**
51  * The Phi0, a node that is created during SSA construction
52  */
53 static ir_op _op_Phi0;
54
55 /** The PhiM, just to count memorty Phi's. */
56 static ir_op _op_PhiM;
57
58 /** The Mul by Const node. */
59 static ir_op _op_MulC;
60
61 /** The Div by Const node. */
62 static ir_op _op_DivC;
63
64 /** The Div by Const node. */
65 static ir_op _op_ModC;
66
67 /** The Div by Const node. */
68 static ir_op _op_DivModC;
69
70 /* ---------------------------------------------------------------------------------- */
71
72 #define STAT_ENTER              ++status->recursive
73 #define STAT_LEAVE              --status->recursive
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  * clears all counter in a node_entry_t
138  */
139 static void opcode_clear_entry(node_entry_t *elem)
140 {
141   cnt_clr(&elem->cnt_alive);
142   cnt_clr(&elem->new_node);
143   cnt_clr(&elem->into_Id);
144 }
145
146 /**
147  * Returns the associates node_entry_t for an ir_op
148  */
149 static node_entry_t *opcode_get_entry(const ir_op *op, pset *set)
150 {
151   node_entry_t key;
152   node_entry_t *elem;
153
154   key.op = op;
155
156   elem = pset_find(set, &key, op->code);
157   if (elem)
158     return elem;
159
160   elem = obstack_alloc(&status->cnts, sizeof(*elem));
161
162   /* clear counter */
163   opcode_clear_entry(elem);
164
165   elem->op = op;
166
167   return pset_insert(set, elem, op->code);
168 }
169
170 /**
171  * Returns the associates ir_op for an opcode
172  */
173 static ir_op *opcode_find_entry(opcode code, pset *set)
174 {
175   ir_op key;
176
177   key.code = code;
178   return pset_find(set, &key, code);
179 }
180
181 /**
182  * calculates a hash value for an irg
183  * Addresses are typically aligned at 32bit, so we ignore the lowest bits
184  */
185 static INLINE unsigned irg_hash(const ir_graph *irg)
186 {
187   return (unsigned)irg >> 3;
188 }
189
190 /**
191  * clears all counter in a graph_entry_t
192  */
193 static void graph_clear_entry(graph_entry_t *elem)
194 {
195   cnt_clr(&elem->cnt_walked);
196   cnt_clr(&elem->cnt_walked_blocks);
197   cnt_clr(&elem->cnt_was_inlined);
198   cnt_clr(&elem->cnt_got_inlined);
199   cnt_clr(&elem->cnt_strength_red);
200   cnt_clr(&elem->cnt_edges);
201 }
202
203 /**
204  * Returns the acssociates graph_entry_t for an irg
205  */
206 static graph_entry_t *graph_get_entry(ir_graph *irg, pset *set)
207 {
208   graph_entry_t key;
209   graph_entry_t *elem;
210   int i;
211
212   key.irg = irg;
213
214   elem = pset_find(set, &key, irg_hash(irg));
215   if (elem)
216     return elem;
217
218   elem = obstack_alloc(&status->cnts, sizeof(*elem));
219
220   /* clear counter */
221   graph_clear_entry(elem);
222
223   /* new hash table for opcodes here  */
224   elem->opcode_hash  = new_pset(opcode_cmp, 5);
225   elem->block_hash   = new_pset(block_cmp, 5);
226   elem->irg          = irg;
227
228   for (i = 0; i < sizeof(elem->opt_hash)/sizeof(elem->opt_hash[0]); ++i)
229     elem->opt_hash[i] = new_pset(opt_cmp, 4);
230
231   return pset_insert(set, elem, irg_hash(irg));
232 }
233
234 /**
235  * clears all counter in an opt_entry_t
236  */
237 static void opt_clear_entry(opt_entry_t *elem)
238 {
239   cnt_clr(&elem->count);
240 }
241
242 /**
243  * Returns the associates opt_entry_t for an ir_op
244  */
245 static opt_entry_t *opt_get_entry(const ir_op *op, pset *set)
246 {
247   opt_entry_t key;
248   opt_entry_t *elem;
249
250   key.op = op;
251
252   elem = pset_find(set, &key, op->code);
253   if (elem)
254     return elem;
255
256   elem = obstack_alloc(&status->cnts, sizeof(*elem));
257
258   /* clear new counter */
259   opt_clear_entry(elem);
260
261   elem->op = op;
262
263   return pset_insert(set, elem, op->code);
264 }
265
266 /**
267  * clears all counter in a block_entry_t
268  */
269 static void block_clear_entry(block_entry_t *elem)
270 {
271   cnt_clr(&elem->cnt_nodes);
272   cnt_clr(&elem->cnt_edges);
273   cnt_clr(&elem->cnt_in_edges);
274   cnt_clr(&elem->cnt_out_edges);
275 }
276
277 /**
278  * Returns the associates block_entry_t for an block
279  */
280 static block_entry_t *block_get_entry(long block_nr, pset *set)
281 {
282   block_entry_t key;
283   block_entry_t *elem;
284
285   key.block_nr = block_nr;
286
287   elem = pset_find(set, &key, block_nr);
288   if (elem)
289     return elem;
290
291   elem = obstack_alloc(&status->cnts, sizeof(*elem));
292
293   /* clear new counter */
294   block_clear_entry(elem);
295
296   elem->block_nr = block_nr;
297
298   return pset_insert(set, elem, block_nr);
299 }
300
301
302 /**
303  * Returns the ir_op for an IR-node,
304  * handles special cases and return pseudo op codes
305  */
306 static ir_op *stat_get_irn_op(ir_node *node)
307 {
308   ir_op *op = get_irn_op(node);
309
310   if (op->code == iro_Phi && get_irn_arity(node) == 0) {
311     /* special case, a Phi0 node, count on extra counter */
312     op = status->op_Phi0;
313   }
314   else if (op->code == iro_Phi && get_irn_mode(node) == mode_M) {
315     /* special case, a Memory Phi node, count on extra counter */
316     op = status->op_PhiM;
317   }
318   else if (op->code == iro_Mul &&
319            (get_irn_op(get_Mul_left(node)) == op_Const || get_irn_op(get_Mul_right(node)) == op_Const)) {
320     /* special case, a Multiply by a const, count on extra counter */
321     op = status->op_MulC ? status->op_MulC : op_Mul;
322   }
323   else if (op->code == iro_Div && get_irn_op(get_Div_right(node)) == op_Const) {
324     /* special case, a division by a const, count on extra counter */
325     op = status->op_DivC ? status->op_DivC : op_Div;
326   }
327   else if (op->code == iro_Mod && get_irn_op(get_Mod_right(node)) == op_Const) {
328     /* special case, a module by a const, count on extra counter */
329     op = status->op_ModC ? status->op_ModC : op_Mod;
330   }
331   else if (op->code == iro_DivMod && get_irn_op(get_DivMod_right(node)) == op_Const) {
332     /* special case, a division/modulo by a const, count on extra counter */
333     op = status->op_DivModC ? status->op_DivModC : op_DivMod;
334   }
335
336   return op;
337 }
338
339 /**
340  * update the block counter
341  */
342 static void count_block_info(ir_node *node, graph_entry_t *graph)
343 {
344   ir_op *op = get_irn_op(node);
345   ir_node *block;
346   block_entry_t *b_entry;
347   int i, arity;
348
349   /* check for block */
350   if (op == op_Block) {
351     arity = get_irn_arity(node);
352     b_entry = block_get_entry(get_irn_node_nr(node), graph->block_hash);
353
354     /* count all incoming edges */
355     for (i = 0; i < arity; ++i) {
356       ir_node *pred = get_irn_n(node, i);
357       ir_node *other_block = get_nodes_block(pred);
358       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
359
360       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
361       cnt_inc(&b_entry_other->cnt_out_edges);
362     }
363     return;
364   }
365   else if (op == op_Call) {
366     // return;
367   }
368
369   block   = get_nodes_block(node);
370   b_entry = block_get_entry(get_irn_node_nr(block), graph->block_hash);
371
372   /* we have a new nodes */
373   cnt_inc(&b_entry->cnt_nodes);
374
375   arity = get_irn_arity(node);
376
377   for (i = 0; i < arity; ++i) {
378     ir_node *pred = get_irn_n(node, i);
379     ir_node *other_block;
380
381     if (get_irn_op(pred) == op_Block)
382       continue;
383
384     other_block = get_nodes_block(pred);
385
386     if (other_block == block)
387       cnt_inc(&b_entry->cnt_edges);     /* a in block edge */
388     else {
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   }
395 }
396
397 /**
398  * walker for reachable nodes count
399  */
400 static void count_nodes(ir_node *node, void *env)
401 {
402   graph_entry_t *graph = env;
403   node_entry_t *entry;
404
405   ir_op *op = stat_get_irn_op(node);
406   int arity = get_irn_arity(node);
407
408   entry = opcode_get_entry(op, graph->opcode_hash);
409
410   cnt_inc(&entry->cnt_alive);
411   cnt_add_i(&graph->cnt_edges, arity);
412
413   /* count block edges */
414   count_block_info(node, graph);
415 }
416
417 /**
418  * count all alive nodes and edges in a graph
419  */
420 static void count_nodes_in_graph(graph_entry_t *global, graph_entry_t *graph)
421 {
422   node_entry_t *entry;
423
424   /* clear first the alive counter in the graph */
425   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
426     cnt_clr(&entry->cnt_alive);
427   }
428
429   /* count the nodes in the graph */
430   irg_walk_graph(graph->irg, count_nodes, NULL, graph);
431
432   /* assume we walk every graph only ONCE, we could sum here the global count */
433   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
434     node_entry_t *g_entry = opcode_get_entry(entry->op, global->opcode_hash);
435
436     /* update the node counter */
437     cnt_add(&g_entry->cnt_alive, &entry->cnt_alive);
438   }
439
440   /* update the edge counter */
441   cnt_add(&global->cnt_edges, &graph->cnt_edges);
442 }
443
444 /**
445  * register a dumper
446  */
447 static void stat_register_dumper(dumper_t *dumper)
448 {
449   dumper->next   = status->dumper;
450   status->dumper = dumper;
451 }
452
453 /**
454  * dumps an irg
455  */
456 static void dump_graph(graph_entry_t *entry)
457 {
458   dumper_t *dumper;
459
460   for (dumper = status->dumper; dumper; dumper = dumper->next) {
461     if (dumper->dump_graph)
462       dumper->dump_graph(dumper, entry);
463   }
464 }
465
466 /**
467  * initialise the dumper
468  */
469 static void dump_init(const char *name)
470 {
471   dumper_t *dumper;
472
473   for (dumper = status->dumper; dumper; dumper = dumper->next) {
474     if (dumper->init)
475       dumper->init(dumper, name);
476   }
477 }
478
479 /**
480  * finish the dumper
481  */
482 static void dump_finish(void)
483 {
484   dumper_t *dumper;
485
486   for (dumper = status->dumper; dumper; dumper = dumper->next) {
487     if (dumper->finish)
488       dumper->finish(dumper);
489   }
490 }
491
492 /* ---------------------------------------------------------------------- */
493
494 /**
495  * dumps a opcode hash into human readable form
496  */
497 static void simple_dump_opcode_hash(dumper_t *dmp, pset *set)
498 {
499   node_entry_t *entry;
500   counter_t f_alive;
501   counter_t f_new_node;
502   counter_t f_Id;
503
504   cnt_clr(&f_alive);
505   cnt_clr(&f_new_node);
506   cnt_clr(&f_Id);
507
508   fprintf(dmp->f, "%-16s %-8s %-8s %-8s\n", "Opcode", "alive", "created", "->Id");
509   for (entry = pset_first(set); entry; entry = pset_next(set)) {
510     fprintf(dmp->f, "%-16s %8d %8d %8d\n",
511       get_id_str(entry->op->name), entry->cnt_alive.cnt[0], entry->new_node.cnt[0], entry->into_Id.cnt[0]);
512
513     cnt_add(&f_alive,    &entry->cnt_alive);
514     cnt_add(&f_new_node, &entry->new_node);
515     cnt_add(&f_Id,       &entry->into_Id);
516   }
517   fprintf(dmp->f, "-------------------------------------------\n");
518   fprintf(dmp->f, "%-16s %8d %8d %8d\n", "Sum",
519      f_alive.cnt[0],
520      f_new_node.cnt[0],
521      f_Id.cnt[0]);
522 }
523
524 /**
525  * dumps a optimization hash into human readable form
526  */
527 static void simple_dump_opt_hash(dumper_t *dmp, pset *set, int index)
528 {
529   opt_entry_t *entry = pset_first(set);
530
531   if (entry) {
532     fprintf(dmp->f, "\n%s:\n", opt_names[index]);
533     fprintf(dmp->f, "%-16s %-8s\n", "Opcode", "deref");
534
535     for (; entry; entry = pset_next(set)) {
536       fprintf(dmp->f, "%-16s %8d\n",
537         get_id_str(entry->op->name), entry->count.cnt[0]);
538     }
539   }
540 }
541
542 /**
543  * dumps the endges count
544  */
545 static void simple_dump_edges(dumper_t *dmp, counter_t *cnt)
546 {
547   fprintf(dmp->f, "%-16s %8d\n", "Edges", cnt->cnt[0]);
548 }
549
550 /**
551  * dumps the IRG
552  */
553 static void simple_dump_graph(dumper_t *dmp, graph_entry_t *entry)
554 {
555   int dump_opts = 1;
556   block_entry_t *b_entry;
557
558   if (entry->irg) {
559     ir_graph *const_irg = get_const_code_irg();
560
561     if (entry->irg == const_irg) {
562       fprintf(dmp->f, "\nConst code Irg %p", (void *)entry->irg);
563     }
564     else {
565       if (entry->ent)
566         fprintf(dmp->f, "\nEntity %s, Irg %p", get_entity_name(entry->ent), (void *)entry->irg);
567       else
568         fprintf(dmp->f, "\nIrg %p", (void *)entry->irg);
569     }
570
571     fprintf(dmp->f, " %swalked %d over blocks %d was inlined %d got inlined %d strength red %d:\n",
572         entry->deleted ? "DELETED " : "",
573         entry->cnt_walked.cnt[0], entry->cnt_walked_blocks.cnt[0],
574         entry->cnt_was_inlined.cnt[0],
575         entry->cnt_got_inlined.cnt[0],
576         entry->cnt_strength_red.cnt[0]
577     );
578   }
579   else {
580     fprintf(dmp->f, "\nGlobals counts:\n");
581     fprintf(dmp->f, "--------------\n");
582     dump_opts = 0;
583   }
584
585   simple_dump_opcode_hash(dmp, entry->opcode_hash);
586   simple_dump_edges(dmp, &entry->cnt_edges);
587
588   /* effects of optimizations */
589   if (dump_opts) {
590     int i;
591
592     for (i = 0; i < sizeof(entry->opt_hash)/sizeof(entry->opt_hash[0]); ++i) {
593       simple_dump_opt_hash(dmp, entry->opt_hash[i], i);
594     }
595
596     /* dump block info */
597     fprintf(dmp->f, "\n%12s %12s %12s %12s %12s %12s\n", "Block Nr", "Nodes", "intern E", "incoming E", "outgoing E", "quot");
598     for (b_entry = pset_first(entry->block_hash);
599          b_entry;
600          b_entry = pset_next(entry->block_hash)) {
601       fprintf(dmp->f, "BLK %12ld %12u %12u %12u %12u %4.8f\n",
602           b_entry->block_nr,
603           b_entry->cnt_nodes.cnt[0],
604           b_entry->cnt_edges.cnt[0],
605           b_entry->cnt_in_edges.cnt[0],
606           b_entry->cnt_out_edges.cnt[0],
607           (double)b_entry->cnt_edges.cnt[0] / (double)b_entry->cnt_nodes.cnt[0]
608       );
609     }
610   }
611 }
612
613 /**
614  * initialise the simple dumper
615  */
616 static void simple_init(dumper_t *dmp, const char *name)
617 {
618   char fname[2048];
619
620   snprintf(fname, sizeof(fname), "%s.txt", name);
621   dmp->f = fopen(fname, "w");
622 }
623
624 /**
625  * finishes the simple dumper
626  */
627 static void simple_finish(dumper_t *dmp)
628 {
629   fclose(dmp->f);
630   dmp->f = NULL;
631 }
632
633 /**
634  * the simple human readable dumper
635  */
636 static dumper_t simple_dumper = {
637   simple_dump_graph,
638   simple_init,
639   simple_finish,
640   NULL,
641   NULL,
642 };
643
644 /* ---------------------------------------------------------------------- */
645
646 /**
647  * count the nodes as needed:
648  *
649  * 1 normal (data) Phi's
650  * 2 memory Phi's
651  * 3 Proj
652  * 0 all other nodes
653  */
654 static void csv_count_nodes(graph_entry_t *graph, counter_t cnt[])
655 {
656   node_entry_t *entry;
657   int i;
658
659   for (i = 0; i < 4; ++i)
660     cnt_clr(&cnt[i]);
661
662   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
663     if (entry->op == op_Phi) {
664       /* normal Phi */
665       cnt_add(&cnt[1], &entry->cnt_alive);
666     }
667     else if (entry->op == status->op_PhiM) {
668       /* memory Phi */
669       cnt_add(&cnt[2], &entry->cnt_alive);
670     }
671     else if (entry->op == op_Proj) {
672       /* Proj */
673       cnt_add(&cnt[3], &entry->cnt_alive);
674     }
675     else {
676       /* all other nodes */
677       cnt_add(&cnt[0], &entry->cnt_alive);
678     }
679   }
680 }
681
682 /**
683  * dumps the IRG
684  */
685 static void csv_dump_graph(dumper_t *dmp, graph_entry_t *entry)
686 {
687   const char *name;
688
689   counter_t cnt[4];
690
691   if (entry->irg && !entry->deleted) {
692     ir_graph *const_irg = get_const_code_irg();
693
694     if (entry->irg == const_irg) {
695       name = "<Const code Irg>";
696       return;
697     }
698     else {
699       if (entry->ent)
700         name = get_entity_name(entry->ent);
701       else
702         name = "<UNKNOWN IRG>";
703     }
704
705     csv_count_nodes(entry, cnt);
706
707     fprintf(dmp->f, "%-40s, %p, %d, %d, %d, %d\n",
708         name,
709         (void *)entry->irg,
710         cnt[0].cnt[0],
711         cnt[1].cnt[0],
712         cnt[2].cnt[0],
713         cnt[3].cnt[0]
714     );
715   }
716 }
717
718 /**
719  * initialise the simple dumper
720  */
721 static void csv_init(dumper_t *dmp, const char *name)
722 {
723   char fname[2048];
724
725   snprintf(fname, sizeof(fname), "%s.csv", name);
726   dmp->f = fopen(fname, "a");
727 }
728
729 /**
730  * finishes the simple dumper
731  */
732 static void csv_finish(dumper_t *dmp)
733 {
734   fclose(dmp->f);
735   dmp->f = NULL;
736 }
737
738 /**
739  * the simple human readable dumper
740  */
741 static dumper_t csv_dumper = {
742   csv_dump_graph,
743   csv_init,
744   csv_finish,
745   NULL,
746   NULL,
747 };
748
749
750 /* ---------------------------------------------------------------------- */
751
752 /*
753  * helper: get an ir_op from an opcode
754  */
755 ir_op *stat_get_op_from_opcode(opcode code)
756 {
757   return opcode_find_entry(code, status->ir_op_hash);
758 }
759
760 /* initialize the statistics module. */
761 void init_stat(unsigned enable_options)
762 {
763 #define X(a)  a, sizeof(a)-1
764
765   int pseudo_id = 0;
766
767   /* enable statistics */
768   status->enable = enable_options & FIRMSTAT_ENABLED;
769
770   if (! status->enable)
771    return;
772
773   obstack_init(&status->cnts);
774
775   /* build the pseudo-ops */
776   _op_Phi0.code = --pseudo_id;
777   _op_Phi0.name = new_id_from_chars(X("Phi0"));
778
779   _op_PhiM.code = --pseudo_id;
780   _op_PhiM.name = new_id_from_chars(X("PhiM"));
781
782   _op_MulC.code = --pseudo_id;
783   _op_MulC.name = new_id_from_chars(X("MulC"));
784
785   _op_DivC.code = --pseudo_id;
786   _op_DivC.name = new_id_from_chars(X("DivC"));
787
788   _op_ModC.code = --pseudo_id;
789   _op_ModC.name = new_id_from_chars(X("ModC"));
790
791   _op_DivModC.code = --pseudo_id;
792   _op_DivModC.name = new_id_from_chars(X("DivModC"));
793
794   /* create the hash-tables */
795   status->irg_hash   = new_pset(graph_cmp, 8);
796   status->ir_op_hash = new_pset(opcode_cmp_2, 1);
797
798   status->op_Phi0    = &_op_Phi0;
799   status->op_PhiM    = &_op_PhiM;
800
801   if (enable_options & FIRMSTAT_COUNT_STRONG_OP) {
802     status->op_MulC    = &_op_MulC;
803     status->op_DivC    = &_op_DivC;
804     status->op_ModC    = &_op_ModC;
805     status->op_DivModC = &_op_DivModC;
806   }
807   else {
808     status->op_MulC    = NULL;
809     status->op_DivC    = NULL;
810     status->op_ModC    = NULL;
811     status->op_DivModC = NULL;
812   }
813
814   stat_register_dumper(&simple_dumper);
815   stat_register_dumper(&csv_dumper);
816
817   /* initialize the pattern hash */
818   stat_init_pattern_history(enable_options & FIRMSTAT_PATTERN_ENABLED);
819 #undef X
820 }
821
822 /* A new IR op is registered. */
823 void stat_new_ir_op(const ir_op *op)
824 {
825   if (! status->enable)
826     return;
827
828   STAT_ENTER;
829   {
830     graph_entry_t *graph = graph_get_entry(NULL, status->irg_hash);
831
832     /* execute for side effect :-) */
833     opcode_get_entry(op, graph->opcode_hash);
834
835     pset_insert(status->ir_op_hash, op, op->code);
836   }
837   STAT_LEAVE;
838 }
839
840 /* An IR op is freed. */
841 void stat_free_ir_op(const ir_op *op)
842 {
843   if (! status->enable)
844     return;
845
846   STAT_ENTER;
847   {
848   }
849   STAT_LEAVE;
850 }
851
852 /* A new node is created. */
853 void stat_new_node(ir_node *node)
854 {
855   if (! status->enable)
856     return;
857
858   /* do NOT count during dead node elimination */
859   if (status->in_dead_node_elim > 0)
860     return;
861
862   STAT_ENTER;
863   {
864     node_entry_t *entry;
865     graph_entry_t *graph;
866     ir_op *op = stat_get_irn_op(node);
867
868     /* increase global value */
869     graph = graph_get_entry(NULL, status->irg_hash);
870     entry = opcode_get_entry(op, graph->opcode_hash);
871     cnt_inc(&entry->new_node);
872
873     /* increase local value */
874     graph = graph_get_entry(current_ir_graph, status->irg_hash);
875     entry = opcode_get_entry(op, graph->opcode_hash);
876     cnt_inc(&entry->new_node);
877   }
878   STAT_LEAVE;
879 }
880
881 /* A node is changed into a Id node */
882 void stat_turn_into_id(ir_node *node)
883 {
884   if (! status->enable)
885     return;
886
887   STAT_ENTER;
888   {
889     node_entry_t *entry;
890     graph_entry_t *graph;
891     ir_op *op = stat_get_irn_op(node);
892
893     /* increase global value */
894     graph = graph_get_entry(NULL, status->irg_hash);
895     entry = opcode_get_entry(op, graph->opcode_hash);
896     cnt_inc(&entry->into_Id);
897
898     /* increase local value */
899     graph = graph_get_entry(current_ir_graph, status->irg_hash);
900     entry = opcode_get_entry(op, graph->opcode_hash);
901     cnt_inc(&entry->into_Id);
902   }
903   STAT_LEAVE;
904 }
905
906 /* A new graph was created */
907 void stat_new_graph(ir_graph *irg, entity *ent)
908 {
909   if (! status->enable)
910     return;
911
912   STAT_ENTER;
913   {
914     /* execute for side effect :-) */
915     graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
916
917     graph->ent     = ent;
918     graph->deleted = 0;
919   }
920   STAT_LEAVE;
921 }
922
923 /*
924  * A graph was deleted
925  */
926 void stat_free_graph(ir_graph *irg)
927 {
928   if (! status->enable)
929     return;
930
931   STAT_ENTER;
932   {
933     graph_entry_t *graph  = graph_get_entry(irg, status->irg_hash);
934     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
935
936     graph->deleted = 1;
937
938     /* count the nodes of the graph yet, it will be destroyed later */
939     count_nodes_in_graph(global, graph);
940
941     /* count the DAG's */
942 //    count_dags_in_graph(global, graph);
943
944     /* calculate the pattern */
945     stat_calc_pattern_history(irg);
946   }
947   STAT_LEAVE;
948 }
949
950 /*
951  * A walk over a graph is initiated. Do not count walks from statistic code.
952  */
953 void stat_irg_walk(ir_graph *irg, void *pre, void *post)
954 {
955   if (! status->enable)
956     return;
957
958   STAT_ENTER_SINGLE;
959   {
960     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
961
962     cnt_inc(&graph->cnt_walked);
963   }
964   STAT_LEAVE;
965 }
966
967 /*
968  * A walk over a graph in block-wise order is initiated. Do not count walks from statistic code.
969  */
970 void stat_irg_walk_blkwise(ir_graph *irg, void *pre, void *post)
971 {
972   /* for now, do NOT differentiate between blockwise and normal */
973   stat_irg_walk(irg, pre, post);
974 }
975
976 /*
977  * A walk over the graph's blocks is initiated. Do not count walks from statistic code.
978  */
979 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post)
980 {
981   if (! status->enable)
982     return;
983
984   STAT_ENTER_SINGLE;
985   {
986     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
987
988     cnt_inc(&graph->cnt_walked_blocks);
989   }
990   STAT_LEAVE;
991 }
992
993 /**
994  * called for every node that is removed due to an optimization
995  */
996 static void removed_due_opt(ir_node *n, pset *set)
997 {
998   ir_op *op          = stat_get_irn_op(n);
999   opt_entry_t *entry = opt_get_entry(op, set);
1000
1001   /* increase global value */
1002   cnt_inc(&entry->count);
1003 }
1004
1005 /*
1006  * Some nodes were optimized into some others due to an optimization
1007  */
1008 void stat_merge_nodes(
1009     ir_node **new_node_array, int new_num_entries,
1010     ir_node **old_node_array, int old_num_entries,
1011     stat_opt_kind opt)
1012 {
1013   if (! status->enable)
1014     return;
1015
1016   STAT_ENTER;
1017   {
1018     int i, j;
1019     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1020
1021     for (i = 0; i < old_num_entries; ++i) {
1022       for (j = 0; j < new_num_entries; ++j)
1023         if (old_node_array[i] == new_node_array[j])
1024           break;
1025
1026       /* nodes might be in new and old, these are NOT removed */
1027       if (j >= new_num_entries) {
1028         removed_due_opt(old_node_array[i], graph->opt_hash[opt]);
1029       }
1030     }
1031   }
1032   STAT_LEAVE;
1033 }
1034
1035 /*
1036  * A node was lowered into other nodes
1037  */
1038 void stat_lower(ir_node *node)
1039 {
1040   if (! status->enable)
1041     return;
1042
1043   STAT_ENTER;
1044   {
1045     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1046
1047     removed_due_opt(node, graph->opt_hash[STAT_LOWERED]);
1048   }
1049   STAT_LEAVE;
1050 }
1051
1052 /*
1053  * A graph was inlined
1054  */
1055 void stat_inline(ir_node *call, ir_graph *called_irg)
1056 {
1057   if (! status->enable)
1058     return;
1059
1060   STAT_ENTER;
1061   {
1062     ir_graph *irg = get_irn_irg(call);
1063     graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
1064     graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
1065
1066     cnt_inc(&graph->cnt_got_inlined);
1067     cnt_inc(&i_graph->cnt_was_inlined);
1068   }
1069   STAT_LEAVE;
1070 }
1071
1072 /*
1073  * A graph with tail-recursions was optimized.
1074  */
1075 void stat_tail_rec(ir_graph *irg)
1076 {
1077   if (! status->enable)
1078     return;
1079
1080   STAT_ENTER;
1081   {
1082   }
1083   STAT_LEAVE;
1084 }
1085
1086 /*
1087  * Strength reduction was performed on an iteration variable.
1088  */
1089 void stat_strength_red(ir_graph *irg, ir_node *strong, ir_node *cmp)
1090 {
1091   if (! status->enable)
1092     return;
1093
1094   STAT_ENTER;
1095   {
1096     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1097     cnt_inc(&graph->cnt_strength_red);
1098
1099     removed_due_opt(strong, graph->opt_hash[STAT_OPT_STRENGTH_RED]);
1100   }
1101   STAT_LEAVE;
1102 }
1103
1104 /*
1105  * Start the dead node elimination.
1106  */
1107 void stat_dead_node_elim_start(ir_graph *irg)
1108 {
1109   if (! status->enable)
1110     return;
1111
1112   ++status->in_dead_node_elim;
1113 }
1114
1115 /*
1116  * Stops the dead node elimination.
1117  */
1118 void stat_dead_node_elim_stop(ir_graph *irg)
1119 {
1120   if (! status->enable)
1121     return;
1122
1123   --status->in_dead_node_elim;
1124 }
1125
1126 /* Finish the statistics */
1127 void stat_finish(const char *name)
1128 {
1129   if (! status->enable)
1130     return;
1131
1132   STAT_ENTER;
1133   {
1134     graph_entry_t *entry;
1135     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1136
1137     dump_init(name);
1138
1139     /* dump per graph */
1140     for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1141
1142       if (entry->irg == NULL) {
1143         /* special entry for the global count */
1144         continue;
1145       }
1146
1147       if (! entry->deleted) {
1148         /* the graph is still alive, count the nodes on it */
1149         count_nodes_in_graph(global, entry);
1150
1151         /* count the DAG's */
1152 //        count_dags_in_graph(global, entry);
1153
1154         /* calculate the pattern */
1155         stat_calc_pattern_history(entry->irg);
1156       }
1157
1158       dump_graph(entry);
1159
1160       /* clear the counter here:
1161        * we need only the edge counter to be cleared, all others are cumulative
1162        */
1163       cnt_clr(&entry->cnt_edges);
1164     }
1165
1166     /* dump global */
1167 //    dump_graph(global);
1168     dump_finish();
1169
1170     stat_finish_pattern_history();
1171
1172     /* clear the global counter here */
1173     {
1174       node_entry_t *entry;
1175
1176       for (entry = pset_first(global->opcode_hash); entry; entry = pset_next(global->opcode_hash)) {
1177         opcode_clear_entry(entry);
1178       }
1179       graph_clear_entry(global);
1180     }
1181
1182     /* finished */
1183 //    status->enable = 0;
1184   }
1185   STAT_LEAVE;
1186 }
1187
1188 #else
1189
1190 /* need this for prototypes */
1191 #define FIRM_STATISTICS
1192 #include "firmstat.h"
1193
1194 void init_stat(unsigned enable_options) {}
1195
1196 void stat_finish(const char *name) {}
1197
1198 void stat_new_ir_op(const ir_op *op) {}
1199
1200 void stat_free_ir_op(const ir_op *op) {}
1201
1202 void stat_new_node(ir_node *node) {}
1203
1204 void stat_turn_into_id(ir_node *node) {}
1205
1206 void stat_new_graph(ir_graph *irg, entity *ent) {}
1207
1208 void stat_free_graph(ir_graph *irg) {}
1209
1210 void stat_irg_walk(ir_graph *irg, void *pre, void *post) {}
1211
1212 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post) {}
1213
1214 void stat_merge_nodes(
1215     ir_node **new_node_array, int new_num_entries,
1216     ir_node **old_node_array, int old_num_entries,
1217     stat_opt_kind opt) {}
1218
1219 void stat_lower(ir_node *node) {}
1220
1221 void stat_inline(ir_node *call, ir_graph *irg) {}
1222
1223 void stat_tail_rec(ir_graph *irg) {}
1224
1225 void stat_strength_red(ir_graph *irg, ir_node *strong, ir_node *cmp) {}
1226
1227 void stat_dead_node_elim_start(ir_graph *irg) {}
1228
1229 void stat_dead_node_elim_stop(ir_graph *irg) {}
1230
1231 #endif