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