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