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