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