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