a81af00b8d05806e46f0de8bdb9124efaabadfe5
[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
20 #ifdef HAVE_STDLIB_H
21 # include <stdlib.h>
22 #endif
23 #ifdef HAVE_STRING_H
24 # include <string.h>
25 #endif
26
27 #include "irouts.h"
28 #include "irdump.h"
29 #include "hashptr.h"
30 #include "firmstat_t.h"
31 #include "pattern.h"
32 #include "dags.h"
33 #include "stat_dmp.h"
34 #include "xmalloc.h"
35 #include "irhooks.h"
36
37 /*
38  * need this to be static:
39  * Special pseudo Opcodes that we need to count some interesting cases
40  */
41
42 /**
43  * The Phi0, a node that is created during SSA construction
44  */
45 static ir_op _op_Phi0;
46
47 /** The PhiM, just to count memory Phi's. */
48 static ir_op _op_PhiM;
49
50 /** The Mul by Const node. */
51 static ir_op _op_MulC;
52
53 /** The Div by Const node. */
54 static ir_op _op_DivC;
55
56 /** The Div by Const node. */
57 static ir_op _op_ModC;
58
59 /** The Div by Const node. */
60 static ir_op _op_DivModC;
61
62 /** The memory Proj node. */
63 static ir_op _op_ProjM;
64
65 /** A Sel of a Sel */
66 static ir_op _op_SelSel;
67
68 /** A Sel of a Sel of a Sel */
69 static ir_op _op_SelSelSel;
70
71 /* ---------------------------------------------------------------------------------- */
72
73 /** Marks the begin of a statistic (hook) function. */
74 #define STAT_ENTER              ++status->recursive
75
76 /** Marks the end of a statistic (hook) functions. */
77 #define STAT_LEAVE              --status->recursive
78
79 /** Allows to enter a statistic function only when we are not already in a hook. */
80 #define STAT_ENTER_SINGLE       do { if (status->recursive > 0) return; ++status->recursive; } while (0)
81
82 /**
83  * global status
84  */
85 static const int status_disable = 0;
86 static stat_info_t *status = (stat_info_t *)&status_disable;
87
88 /**
89  * Compare two elements of the opcode hash.
90  */
91 static int opcode_cmp(const void *elt, const void *key) {
92         const node_entry_t *e1 = elt;
93         const node_entry_t *e2 = key;
94
95         return e1->op->code - e2->op->code;
96 }  /* opcode_cmp */
97
98 /**
99  * Compare two elements of the graph hash.
100  */
101 static int graph_cmp(const void *elt, const void *key) {
102         const graph_entry_t *e1 = elt;
103         const graph_entry_t *e2 = key;
104
105         return e1->irg != e2->irg;
106 }  /* graph_cmp */
107
108 /**
109  * Compare two elements of the optimization hash.
110  */
111 static int opt_cmp(const void *elt, const void *key) {
112         const opt_entry_t *e1 = elt;
113         const opt_entry_t *e2 = key;
114
115         return e1->op->code != e2->op->code;
116 }  /* opt_cmp */
117
118 /**
119  * Compare two elements of the block/extbb hash.
120  */
121 static int block_cmp(const void *elt, const void *key) {
122         const block_entry_t *e1 = elt;
123         const block_entry_t *e2 = key;
124
125         return e1->block_nr != e2->block_nr;
126 }  /* block_cmp */
127
128 /**
129  * Compare two elements of the be_block hash.
130  */
131 static int be_block_cmp(const void *elt, const void *key) {
132         const be_block_entry_t *e1 = elt;
133         const be_block_entry_t *e2 = key;
134
135         return e1->block_nr != e2->block_nr;
136 }  /* be_block_cmp */
137
138 /**
139  * Compare two elements of reg pressure hash.
140  */
141 static int reg_pressure_cmp(const void *elt, const void *key) {
142         const reg_pressure_entry_t *e1 = elt;
143         const reg_pressure_entry_t *e2 = key;
144
145         return e1->class_name != e2->class_name;
146 }  /* reg_pressure_cmp */
147
148 /**
149  * Compare two elements of the perm_stat hash.
150  */
151 static int perm_stat_cmp(const void *elt, const void *key) {
152         const perm_stat_entry_t *e1 = elt;
153         const perm_stat_entry_t *e2 = key;
154
155         return e1->perm != e2->perm;
156 }  /* perm_stat_cmp */
157
158 /**
159  * Compare two elements of the perm_class hash.
160  */
161 static int perm_class_cmp(const void *elt, const void *key) {
162         const perm_class_entry_t *e1 = elt;
163         const perm_class_entry_t *e2 = key;
164
165         return e1->class_name != e2->class_name;
166 }  /* perm_class_cmp */
167
168 /**
169  * Compare two elements of the ir_op hash.
170  */
171 static int opcode_cmp_2(const void *elt, const void *key) {
172         const ir_op *e1 = elt;
173         const ir_op *e2 = key;
174
175         return e1->code != e2->code;
176 }  /* opcode_cmp_2 */
177
178 /**
179  * Compare two elements of the address_mark set.
180  */
181 static int address_mark_cmp(const void *elt, const void *key, size_t size) {
182         const address_mark_entry_t *e1 = elt;
183         const address_mark_entry_t *e2 = key;
184
185         /* compare only the nodes, the rest is used as data container */
186         return e1->node != e2->node;
187 }  /* address_mark_cmp */
188
189 /**
190  * Clear all counter in a node_entry_t.
191  */
192 static void opcode_clear_entry(node_entry_t *elem) {
193         cnt_clr(&elem->cnt_alive);
194         cnt_clr(&elem->new_node);
195         cnt_clr(&elem->into_Id);
196 }  /* opcode_clear_entry */
197
198 /**
199  * Returns the associates node_entry_t for an ir_op
200  *
201  * @param op    the IR operation
202  * @param hmap  a hash map containing ir_op* -> node_entry_t*
203  */
204 static node_entry_t *opcode_get_entry(const ir_op *op, hmap_node_entry_t *hmap) {
205         node_entry_t key;
206         node_entry_t *elem;
207
208         key.op = op;
209
210         elem = pset_find(hmap, &key, op->code);
211         if (elem)
212                 return elem;
213
214         elem = obstack_alloc(&status->cnts, sizeof(*elem));
215         memset(elem, 0, sizeof(*elem));
216
217         /* clear counter */
218         opcode_clear_entry(elem);
219
220         elem->op = op;
221
222         return pset_insert(hmap, elem, op->code);
223 }  /* opcode_get_entry */
224
225 /**
226  * Returns the associates ir_op for an opcode
227  *
228  * @param code  the IR opcode
229  * @param hmap  the hash map containing opcode -> ir_op*
230  */
231 static ir_op *opcode_find_entry(opcode code, hmap_ir_op *hmap) {
232         ir_op key;
233
234         key.code = code;
235         return pset_find(hmap, &key, code);
236 }  /* opcode_find_entry */
237
238 /**
239  * Clears all counter in a graph_entry_t.
240  *
241  * @param elem  the graph entry
242  * @param all   if non-zero, clears all counters, else leave accumulated ones
243  */
244 static void graph_clear_entry(graph_entry_t *elem, int all) {
245         if (all) {
246                 cnt_clr(&elem->cnt_walked);
247                 cnt_clr(&elem->cnt_walked_blocks);
248                 cnt_clr(&elem->cnt_was_inlined);
249                 cnt_clr(&elem->cnt_got_inlined);
250                 cnt_clr(&elem->cnt_strength_red);
251                 cnt_clr(&elem->cnt_real_func_call);
252         }  /* if */
253         cnt_clr(&elem->cnt_edges);
254         cnt_clr(&elem->cnt_all_calls);
255         cnt_clr(&elem->cnt_call_with_cnst_arg);
256         cnt_clr(&elem->cnt_indirect_calls);
257         cnt_clr(&elem->cnt_pure_adr_ops);
258         cnt_clr(&elem->cnt_all_adr_ops);
259
260         if (elem->block_hash) {
261                 del_pset(elem->block_hash);
262                 elem->block_hash = NULL;
263         }  /* if */
264
265         if (elem->extbb_hash) {
266                 del_pset(elem->extbb_hash);
267                 elem->extbb_hash = NULL;
268         }  /* if */
269
270         obstack_free(&elem->recalc_cnts, NULL);
271         obstack_init(&elem->recalc_cnts);
272 }  /* graph_clear_entry */
273
274 /**
275  * Returns the associated graph_entry_t for an IR graph.
276  *
277  * @param irg   the IR graph
278  * @param hmap  the hash map containing ir_graph* -> graph_entry_t*
279  */
280 static graph_entry_t *graph_get_entry(ir_graph *irg, hmap_graph_entry_t *hmap)
281 {
282         graph_entry_t key;
283         graph_entry_t *elem;
284         int i;
285
286         key.irg = irg;
287
288         elem = pset_find(hmap, &key, HASH_PTR(irg));
289
290         if (elem) {
291                 /* create hash map backend block information */
292                 if (! elem->be_block_hash)
293                         elem->be_block_hash = new_pset(be_block_cmp, 5);
294
295                 return elem;
296         }  /* if */
297
298         /* allocate a new one */
299         elem = obstack_alloc(&status->cnts, sizeof(*elem));
300         memset(elem, 0, sizeof(*elem));
301         obstack_init(&elem->recalc_cnts);
302
303         /* clear counter */
304         graph_clear_entry(elem, 1);
305
306         /* new hash table for opcodes here  */
307         elem->opcode_hash   = new_pset(opcode_cmp, 5);
308         elem->address_mark  = new_set(address_mark_cmp, 5);
309         elem->irg           = irg;
310
311         /* these hash tables are created on demand */
312         elem->block_hash = NULL;
313         elem->extbb_hash = NULL;
314
315         for (i = 0; i < sizeof(elem->opt_hash)/sizeof(elem->opt_hash[0]); ++i)
316                 elem->opt_hash[i] = new_pset(opt_cmp, 4);
317
318         return pset_insert(hmap, elem, HASH_PTR(irg));
319 }  /* graph_get_entry */
320
321 /**
322  * Clear all counter in an opt_entry_t.
323  */
324 static void opt_clear_entry(opt_entry_t *elem) {
325         cnt_clr(&elem->count);
326 }  /* opt_clear_entry */
327
328 /**
329  * Returns the associated opt_entry_t for an IR operation.
330  *
331  * @param op    the IR operation
332  * @param hmap  the hash map containing ir_op* -> opt_entry_t*
333  */
334 static opt_entry_t *opt_get_entry(const ir_op *op, hmap_opt_entry_t *hmap)
335 {
336         opt_entry_t key;
337         opt_entry_t *elem;
338
339         key.op = op;
340
341         elem = pset_find(hmap, &key, op->code);
342         if (elem)
343                 return elem;
344
345         elem = obstack_alloc(&status->cnts, sizeof(*elem));
346         memset(elem, 0, sizeof(*elem));
347
348         /* clear new counter */
349         opt_clear_entry(elem);
350
351         elem->op = op;
352
353         return pset_insert(hmap, elem, op->code);
354 }  /* opt_get_entry */
355
356 /**
357  * clears all counter in a block_entry_t
358  */
359 static void block_clear_entry(block_entry_t *elem) {
360         cnt_clr(&elem->cnt_nodes);
361         cnt_clr(&elem->cnt_edges);
362         cnt_clr(&elem->cnt_in_edges);
363         cnt_clr(&elem->cnt_out_edges);
364         cnt_clr(&elem->cnt_phi_data);
365 }  /* block_clear_entry */
366
367 /**
368  * Returns the associated block_entry_t for an block.
369  *
370  * @param block_nr  an IR  block number
371  * @param hmap      a hash map containing long -> block_entry_t
372  */
373 static block_entry_t *block_get_entry(struct obstack *obst, long block_nr, hmap_block_entry_t *hmap)
374 {
375         block_entry_t key;
376         block_entry_t *elem;
377
378         key.block_nr = block_nr;
379
380         elem = pset_find(hmap, &key, block_nr);
381         if (elem)
382                 return elem;
383
384         elem = obstack_alloc(obst, sizeof(*elem));
385         memset(elem, 0, sizeof(*elem));
386
387         /* clear new counter */
388         block_clear_entry(elem);
389
390         elem->block_nr = block_nr;
391
392         return pset_insert(hmap, elem, block_nr);
393 }  /* block_get_entry */
394
395 /**
396  * Clear all sets in be_block_entry_t.
397  */
398 static void be_block_clear_entry(be_block_entry_t *elem)
399 {
400         if (elem->reg_pressure)
401                 del_pset(elem->reg_pressure);
402
403         if (elem->sched_ready)
404                 stat_delete_distrib_tbl(elem->sched_ready);
405
406         if (elem->perm_class_stat)
407                 del_pset(elem->perm_class_stat);
408
409         elem->reg_pressure    = new_pset(reg_pressure_cmp, 5);
410         elem->sched_ready     = stat_new_int_distrib_tbl();
411         elem->perm_class_stat = new_pset(perm_class_cmp, 5);
412 }  /* be_block_clear_entry */
413
414 /**
415  * Returns the associated be_block_entry_t for an block.
416  *
417  * @param block_nr  an IR  block number
418  * @param hmap      a hash map containing long -> be_block_entry_t
419  */
420 static be_block_entry_t *be_block_get_entry(struct obstack *obst, long block_nr, hmap_be_block_entry_t *hmap)
421 {
422         be_block_entry_t key;
423         be_block_entry_t *elem;
424
425         key.block_nr = block_nr;
426
427         elem = pset_find(hmap, &key, block_nr);
428         if (elem)
429                 return elem;
430
431         elem = obstack_alloc(obst, sizeof(*elem));
432         memset(elem, 0, sizeof(*elem));
433
434         /* clear new counter */
435         be_block_clear_entry(elem);
436
437         elem->block_nr = block_nr;
438
439         return pset_insert(hmap, elem, block_nr);
440 }  /* be_block_get_entry */
441
442 /**
443  * clears all sets in perm_class_entry_t
444  */
445 static void perm_class_clear_entry(perm_class_entry_t *elem) {
446         if (elem->perm_stat)
447                 del_pset(elem->perm_stat);
448
449         elem->perm_stat = new_pset(perm_stat_cmp, 5);
450 }  /* perm_class_clear_entry */
451
452 /**
453  * Returns the associated perm_class entry for a register class.
454  *
455  * @param class_name  the register class name
456  * @param hmap        a hash map containing class_name -> perm_class_entry_t
457  */
458 static perm_class_entry_t *perm_class_get_entry(struct obstack *obst, const char *class_name,
459                                                 hmap_perm_class_entry_t *hmap)
460 {
461         perm_class_entry_t key;
462         perm_class_entry_t *elem;
463
464         key.class_name = class_name;
465
466         elem = pset_find(hmap, &key, HASH_PTR(class_name));
467         if (elem)
468                 return elem;
469
470         elem = obstack_alloc(obst, sizeof(*elem));
471         memset(elem, 0, sizeof(*elem));
472
473         /* clear new counter */
474         perm_class_clear_entry(elem);
475
476         elem->class_name = class_name;
477
478         return pset_insert(hmap, elem, HASH_PTR(class_name));
479 }  /* perm_class_get_entry */
480
481 /**
482  * clears all sets in perm_stat_entry_t
483  */
484 static void perm_stat_clear_entry(perm_stat_entry_t *elem) {
485         if (elem->chains)
486                 stat_delete_distrib_tbl(elem->chains);
487
488         if (elem->cycles)
489                 stat_delete_distrib_tbl(elem->cycles);
490
491         elem->chains = stat_new_int_distrib_tbl();
492         elem->cycles = stat_new_int_distrib_tbl();
493 }  /* perm_stat_clear_entry */
494
495 /**
496  * Returns the associated perm_stat entry for a perm.
497  *
498  * @param perm      the perm node
499  * @param hmap      a hash map containing perm -> perm_stat_entry_t
500  */
501 static perm_stat_entry_t *perm_stat_get_entry(struct obstack *obst, ir_node *perm, hmap_perm_stat_entry_t *hmap)
502 {
503         perm_stat_entry_t key;
504         perm_stat_entry_t *elem;
505
506         key.perm = perm;
507
508         elem = pset_find(hmap, &key, HASH_PTR(perm));
509         if (elem)
510                 return elem;
511
512         elem = obstack_alloc(obst, sizeof(*elem));
513         memset(elem, 0, sizeof(*elem));
514
515         /* clear new counter */
516         perm_stat_clear_entry(elem);
517
518         elem->perm = perm;
519
520         return pset_insert(hmap, elem, HASH_PTR(perm));
521 }  /* perm_stat_get_entry */
522
523 /**
524  * Returns the ir_op for an IR-node,
525  * handles special cases and return pseudo op codes.
526  *
527  * @param none  an IR node
528  */
529 static ir_op *stat_get_irn_op(ir_node *node)
530 {
531         ir_op *op = get_irn_op(node);
532
533         if (op == op_Phi && get_irn_arity(node) == 0) {
534                 /* special case, a Phi0 node, count on extra counter */
535                 op = status->op_Phi0 ? status->op_Phi0 : op;
536         } else if (op == op_Phi && get_irn_mode(node) == mode_M) {
537                 /* special case, a Memory Phi node, count on extra counter */
538                 op = status->op_PhiM ? status->op_PhiM : op;
539         } else if (op == op_Proj && get_irn_mode(node) == mode_M) {
540                 /* special case, a Memory Proj node, count on extra counter */
541                 op = status->op_ProjM ? status->op_ProjM : op;
542         } else if (op == op_Mul &&
543                 (get_irn_op(get_Mul_left(node)) == op_Const || get_irn_op(get_Mul_right(node)) == op_Const)) {
544                 /* special case, a Multiply by a const, count on extra counter */
545                 op = status->op_MulC ? status->op_MulC : op;
546         } else if (op == op_Div && get_irn_op(get_Div_right(node)) == op_Const) {
547                 /* special case, a division by a const, count on extra counter */
548                 op = status->op_DivC ? status->op_DivC : op;
549         } else if (op == op_Mod && get_irn_op(get_Mod_right(node)) == op_Const) {
550                 /* special case, a module by a const, count on extra counter */
551                 op = status->op_ModC ? status->op_ModC : op;
552         } else if (op == op_DivMod && get_irn_op(get_DivMod_right(node)) == op_Const) {
553                 /* special case, a division/modulo by a const, count on extra counter */
554                 op = status->op_DivModC ? status->op_DivModC : op;
555         } else if (op == op_Sel && get_irn_op(get_Sel_ptr(node)) == op_Sel) {
556                 /* special case, a Sel of a Sel, count on extra counter */
557                 op = status->op_SelSel ? status->op_SelSel : op;
558
559                 if (get_irn_op(get_Sel_ptr(get_Sel_ptr(node))) == op_Sel) {
560                         /* special case, a Sel of a Sel of a Sel, count on extra counter */
561                         op = status->op_SelSelSel ? status->op_SelSelSel : op;
562                 }  /* if */
563         }  /* if */
564
565         return op;
566 }  /* stat_get_irn_op */
567
568 /**
569  * update the block counter
570  */
571 static void undate_block_info(ir_node *node, graph_entry_t *graph)
572 {
573         ir_op *op = get_irn_op(node);
574         ir_node *block;
575         block_entry_t *b_entry;
576         int i, arity;
577
578         /* check for block */
579         if (op == op_Block) {
580                 arity = get_irn_arity(node);
581                 b_entry = block_get_entry(&graph->recalc_cnts, get_irn_node_nr(node), graph->block_hash);
582
583                 /* count all incoming edges */
584                 for (i = 0; i < arity; ++i) {
585                         ir_node *pred = get_irn_n(node, i);
586                         ir_node *other_block = get_nodes_block(pred);
587                         block_entry_t *b_entry_other = block_get_entry(&graph->recalc_cnts, get_irn_node_nr(other_block), graph->block_hash);
588
589                         cnt_inc(&b_entry->cnt_in_edges);        /* an edge coming from another block */
590                         cnt_inc(&b_entry_other->cnt_out_edges);
591                 }  /* for */
592                 return;
593         }  /* if */
594
595         block   = get_nodes_block(node);
596         b_entry = block_get_entry(&graph->recalc_cnts, get_irn_node_nr(block), graph->block_hash);
597
598         if (op == op_Phi && mode_is_datab(get_irn_mode(node))) {
599                 /* count data Phi per block */
600                 cnt_inc(&b_entry->cnt_phi_data);
601         }  /* if */
602
603         /* we have a new node in our block */
604         cnt_inc(&b_entry->cnt_nodes);
605
606         /* don't count keep-alive edges */
607         if (get_irn_op(node) == op_End)
608                 return;
609
610         arity = get_irn_arity(node);
611
612         for (i = 0; i < arity; ++i) {
613                 ir_node *pred = get_irn_n(node, i);
614                 ir_node *other_block;
615
616                 other_block = get_nodes_block(pred);
617
618                 if (other_block == block)
619                         cnt_inc(&b_entry->cnt_edges);   /* a in block edge */
620                 else {
621                         block_entry_t *b_entry_other = block_get_entry(&graph->recalc_cnts, get_irn_node_nr(other_block), graph->block_hash);
622
623                         cnt_inc(&b_entry->cnt_in_edges);        /* an edge coming from another block */
624                         cnt_inc(&b_entry_other->cnt_out_edges);
625                 }  /* if */
626         }  /* for */
627 }  /* undate_block_info */
628
629 /**
630  * Update the extended block counter.
631  */
632 static void update_extbb_info(ir_node *node, graph_entry_t *graph)
633 {
634         ir_op *op = get_irn_op(node);
635         ir_extblk *extbb;
636         extbb_entry_t *eb_entry;
637         int i, arity;
638
639         /* check for block */
640         if (op == op_Block) {
641                 extbb = get_nodes_extbb(node);
642                 arity = get_irn_arity(node);
643                 eb_entry = block_get_entry(&graph->recalc_cnts, get_extbb_node_nr(extbb), graph->extbb_hash);
644
645                 /* count all incoming edges */
646                 for (i = 0; i < arity; ++i) {
647                         ir_node *pred = get_irn_n(node, i);
648                         ir_extblk *other_extbb = get_nodes_extbb(pred);
649
650                         if (extbb != other_extbb) {
651                                 extbb_entry_t *eb_entry_other = block_get_entry(&graph->recalc_cnts, get_extbb_node_nr(other_extbb), graph->extbb_hash);
652
653                                 cnt_inc(&eb_entry->cnt_in_edges);       /* an edge coming from another extbb */
654                                 cnt_inc(&eb_entry_other->cnt_out_edges);
655                         }  /* if */
656                 }  /* for */
657                 return;
658         }  /* if */
659
660         extbb    = get_nodes_extbb(node);
661         eb_entry = block_get_entry(&graph->recalc_cnts, get_extbb_node_nr(extbb), graph->extbb_hash);
662
663         if (op == op_Phi && mode_is_datab(get_irn_mode(node))) {
664                 /* count data Phi per extbb */
665                 cnt_inc(&eb_entry->cnt_phi_data);
666         }  /* if */
667
668         /* we have a new node in our block */
669         cnt_inc(&eb_entry->cnt_nodes);
670
671         /* don't count keep-alive edges */
672         if (get_irn_op(node) == op_End)
673                 return;
674
675         arity = get_irn_arity(node);
676
677         for (i = 0; i < arity; ++i) {
678                 ir_node *pred = get_irn_n(node, i);
679                 ir_extblk *other_extbb = get_nodes_extbb(pred);
680
681                 if (other_extbb == extbb)
682                         cnt_inc(&eb_entry->cnt_edges);  /* a in extbb edge */
683                 else {
684                         extbb_entry_t *eb_entry_other = block_get_entry(&graph->recalc_cnts, get_extbb_node_nr(other_extbb), graph->extbb_hash);
685
686                         cnt_inc(&eb_entry->cnt_in_edges);       /* an edge coming from another extbb */
687                         cnt_inc(&eb_entry_other->cnt_out_edges);
688                 }  /* if */
689         }  /* for */
690 }  /* update_extbb_info */
691
692 /** Calculates how many arguments of the call are const. */
693 static int cnt_const_args(ir_node *call) {
694         int i, res = 0;
695         int n = get_Call_n_params(call);
696
697         for (i = 0; i < n; ++i) {
698                 ir_node *param = get_Call_param(call, i);
699                 ir_op   *op = get_irn_op(param);
700
701                 if (op == op_Const || op == op_SymConst)
702                         ++res;
703         }  /* for */
704         return res;
705 }  /* cnt_const_args */
706
707 /**
708  * Update info on calls.
709  *
710  * @param call   The call
711  * @param graph  The graph entry containing the call
712  */
713 static void stat_update_call(ir_node *call, graph_entry_t *graph)
714 {
715         ir_node  *block = get_nodes_block(call);
716         ir_node  *ptr = get_Call_ptr(call);
717         entity   *ent = NULL;
718         ir_graph *callee = NULL;
719         int      num_const_args;
720
721         /*
722          * If the block is bad, the whole subgraph will collapse later
723          * so do not count this call.
724          * This happens in dead code.
725          */
726         if (is_Bad(block))
727                 return;
728
729         cnt_inc(&graph->cnt_all_calls);
730
731         /* found a call, this function is not a leaf */
732         graph->is_leaf = 0;
733
734         if (get_irn_op(ptr) == op_SymConst) {
735                 if (get_SymConst_kind(ptr) == symconst_addr_ent) {
736                         /* ok, we seems to know the entity */
737                         ent = get_SymConst_entity(ptr);
738                         callee = get_entity_irg(ent);
739
740                         /* it is recursive, if it calls at least once */
741                         if (callee == graph->irg)
742                                 graph->is_recursive = 1;
743                 }  /* if */
744         } else {
745                 /* indirect call, be could not predict */
746                 cnt_inc(&graph->cnt_indirect_calls);
747
748                 /* NOT a leaf call */
749                 graph->is_leaf_call = LCS_NON_LEAF_CALL;
750         }  /* if */
751
752         /* check, if it's a chain-call: Then, the call-block
753         * must dominate the end block. */
754         {
755                 ir_node *curr = get_irg_end_block(graph->irg);
756                 int depth = get_Block_dom_depth(block);
757
758                 for (; curr != block && get_Block_dom_depth(curr) > depth;) {
759                         curr = get_Block_idom(curr);
760
761                         if (! curr || is_no_Block(curr))
762                                 break;
763                 }  /* for */
764
765                 if (curr != block)
766                         graph->is_chain_call = 0;
767         }
768
769         /* check, if the callee is a leaf */
770         if (callee) {
771                 graph_entry_t *called = graph_get_entry(callee, status->irg_hash);
772
773                 if (called->is_analyzed) {
774                         if (! called->is_leaf)
775                                 graph->is_leaf_call = LCS_NON_LEAF_CALL;
776                 }  /* if */
777         }  /* if */
778
779         /* check, if arguments of the call are const */
780         num_const_args = cnt_const_args(call);
781
782         if (num_const_args > 0)
783                 cnt_inc(&graph->cnt_call_with_cnst_arg);
784 }  /* stat_update_call */
785
786 /**
787  * Update info on calls for graphs on the wait queue.
788  */
789 static void stat_update_call_2(ir_node *call, graph_entry_t *graph)
790 {
791         ir_node  *block = get_nodes_block(call);
792         ir_node  *ptr = get_Call_ptr(call);
793         entity   *ent = NULL;
794         ir_graph *callee = NULL;
795
796         /*
797          * If the block is bad, the whole subgraph will collapse later
798          * so do not count this call.
799          * This happens in dead code.
800          */
801         if (is_Bad(block))
802                 return;
803
804         if (get_irn_op(ptr) == op_SymConst) {
805                 if (get_SymConst_kind(ptr) == symconst_addr_ent) {
806                         /* ok, we seems to know the entity */
807                         ent = get_SymConst_entity(ptr);
808                         callee = get_entity_irg(ent);
809                 }  /* if */
810         }  /* if */
811
812         /* check, if the callee is a leaf */
813         if (callee) {
814                 graph_entry_t *called = graph_get_entry(callee, status->irg_hash);
815
816                 assert(called->is_analyzed);
817
818                 if (! called->is_leaf)
819                         graph->is_leaf_call = LCS_NON_LEAF_CALL;
820         } else
821                 graph->is_leaf_call = LCS_NON_LEAF_CALL;
822 }  /* stat_update_call_2 */
823
824 /**
825  * Walker for reachable nodes count.
826  */
827 static void update_node_stat(ir_node *node, void *env)
828 {
829         graph_entry_t *graph = env;
830         node_entry_t *entry;
831
832         ir_op *op = stat_get_irn_op(node);
833         int arity = get_irn_arity(node);
834
835         entry = opcode_get_entry(op, graph->opcode_hash);
836
837         cnt_inc(&entry->cnt_alive);
838         cnt_add_i(&graph->cnt_edges, arity);
839
840         /* count block edges */
841         undate_block_info(node, graph);
842
843         /* count extended block edges */
844         if (status->stat_options & FIRMSTAT_COUNT_EXTBB) {
845                 if (graph->irg != get_const_code_irg())
846                         update_extbb_info(node, graph);
847         }  /* if */
848
849         /* handle statistics for special node types */
850
851         if (op == op_Const) {
852                 if (status->stat_options & FIRMSTAT_COUNT_CONSTS) {
853                         /* check properties of constants */
854                         stat_update_const(status, node, graph);
855                 }  /* if */
856         } else if (op == op_Call) {
857                 /* check for properties that depends on calls like recursion/leaf/indirect call */
858                 stat_update_call(node, graph);
859         }  /* if */
860 }  /* update_node_stat */
861
862 /**
863  * Walker for reachable nodes count for graphs on the wait_q.
864  */
865 static void update_node_stat_2(ir_node *node, void *env) {
866         graph_entry_t *graph = env;
867
868         /* check for properties that depends on calls like recursion/leaf/indirect call */
869         if (is_Call(node))
870                 stat_update_call_2(node, graph);
871 }  /* update_node_stat_2 */
872
873 /**
874  * Get the current address mark.
875  */
876 static unsigned get_adr_mark(graph_entry_t *graph, ir_node *node) {
877         address_mark_entry_t *value = set_find(graph->address_mark, &node, sizeof(*value), HASH_PTR(node));
878
879         return value ? value->mark : 0;
880 }  /* get_adr_mark */
881
882 /**
883  * Set the current address mark.
884  */
885 static void set_adr_mark(graph_entry_t *graph, ir_node *node, unsigned val) {
886         address_mark_entry_t *value = set_insert(graph->address_mark, &node, sizeof(*value), HASH_PTR(node));
887
888         value->mark = val;
889 }  /* set_adr_mark */
890
891 #undef DUMP_ADR_MODE
892
893 #ifdef DUMP_ADR_MODE
894 /**
895  * a vcg attribute hook: Color a node with a different color if
896  * it's identified as a part of an address expression or at least referenced
897  * by an address expression.
898  */
899 static int stat_adr_mark_hook(FILE *F, ir_node *node, ir_node *local)
900 {
901         ir_node *n           = local ? local : node;
902         ir_graph *irg        = get_irn_irg(n);
903         graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
904         unsigned mark        = get_adr_mark(graph, n);
905
906         if (mark & MARK_ADDRESS_CALC)
907                 fprintf(F, "color: purple");
908         else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR)
909                 fprintf(F, "color: pink");
910         else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == (MARK_REF_ADR|MARK_REF_NON_ADR))
911                 fprintf(F, "color: lightblue");
912         else
913                 return 0;
914
915         /* I know the color! */
916         return 1;
917 }  /* stat_adr_mark_hook */
918 #endif /* DUMP_ADR_MODE */
919
920 /**
921  * Return the "operational" mode of a Firm node.
922  */
923 static ir_mode *get_irn_op_mode(ir_node *node) {
924         switch (get_irn_opcode(node)) {
925         case iro_Load:
926                 return get_Load_mode(node);
927         case iro_Store:
928                 return get_irn_mode(get_Store_value(node));
929         case iro_DivMod:
930                 return get_irn_mode(get_DivMod_left(node));
931         case iro_Div:
932                 return get_irn_mode(get_Div_left(node));
933         case iro_Mod:
934                 return get_irn_mode(get_Mod_left(node));
935         case iro_Cmp:
936                 /* Cmp is no address calculation, or is it? */
937         default:
938                 return get_irn_mode(node);
939         }  /* switch */
940 }  /* get_irn_op_mode */
941
942 /**
943  * Post-walker that marks every node that is an address calculation.
944  *
945  * Users of a node must be visited first. We ensure this by
946  * calling it in the post of an outs walk. This should work even in cycles,
947  * while the normal pre-walk will not.
948  */
949 static void mark_address_calc(ir_node *node, void *env) {
950         graph_entry_t *graph = env;
951         ir_mode *mode = get_irn_op_mode(node);
952         int i, n;
953         unsigned mark_preds = MARK_REF_NON_ADR;
954
955         if (! mode_is_numP(mode))
956                 return;
957
958         if (mode_is_reference(mode)) {
959                 /* a reference is calculated here, we are sure */
960                 set_adr_mark(graph, node, MARK_ADDRESS_CALC);
961
962                 mark_preds = MARK_REF_ADR;
963         } else {
964                 unsigned mark = get_adr_mark(graph, node);
965
966                 if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR) {
967                         /*
968                          * this node has no reference mode, but is only
969                          * referenced by address calculations
970                          */
971                         mark_preds = MARK_REF_ADR;
972                 }  /* if */
973         }  /* if */
974
975         /* mark all predecessors */
976         for (i = 0, n = get_irn_arity(node); i < n; ++i) {
977                 ir_node *pred = get_irn_n(node, i);
978
979                 mode = get_irn_op_mode(pred);
980                 if (! mode_is_numP(mode))
981                         continue;
982
983                 set_adr_mark(graph, pred, get_adr_mark(graph, pred) | mark_preds);
984         }  /* for */
985 }  /* mark_address_calc */
986
987 /**
988  * Post-walker that marks every node that is an address calculation.
989  *
990  * Users of a node must be visited first. We ensure this by
991  * calling it in the post of an outs walk. This should work even in cycles,
992  * while the normal pre-walk will not.
993  */
994 static void count_adr_ops(ir_node *node, void *env) {
995         graph_entry_t *graph = env;
996         unsigned mark        = get_adr_mark(graph, node);
997
998         if (mark & MARK_ADDRESS_CALC)
999                 cnt_inc(&graph->cnt_pure_adr_ops);
1000         else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR)
1001                 cnt_inc(&graph->cnt_pure_adr_ops);
1002         else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == (MARK_REF_ADR|MARK_REF_NON_ADR))
1003                 cnt_inc(&graph->cnt_all_adr_ops);
1004 }  /* count_adr_ops */
1005
1006 /**
1007  * Called for every graph when the graph is either deleted or stat_dump_snapshot()
1008  * is called, must recalculate all statistic info.
1009  *
1010  * @param global    The global entry
1011  * @param graph     The current entry
1012  */
1013 static void update_graph_stat(graph_entry_t *global, graph_entry_t *graph)
1014 {
1015         node_entry_t *entry;
1016
1017         /* clear first the alive counter in the graph */
1018         for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
1019                 cnt_clr(&entry->cnt_alive);
1020         }  /* for */
1021
1022         /* set pessimistic values */
1023         graph->is_leaf       = 1;
1024         graph->is_leaf_call  = LCS_UNKNOWN;
1025         graph->is_recursive  = 0;
1026         graph->is_chain_call = 1;
1027
1028         /* create new block counter */
1029         graph->block_hash = new_pset(block_cmp, 5);
1030
1031         /* we need dominator info */
1032         if (graph->irg != get_const_code_irg()) {
1033                 assure_doms(graph->irg);
1034
1035                 if (status->stat_options & FIRMSTAT_COUNT_EXTBB) {
1036                         /* we need extended basic blocks */
1037                         compute_extbb(graph->irg);
1038
1039                         /* create new extbb counter */
1040                         graph->extbb_hash = new_pset(block_cmp, 5);
1041                 }  /* if */
1042         }  /* if */
1043
1044         /* count the nodes in the graph */
1045         irg_walk_graph(graph->irg, update_node_stat, NULL, graph);
1046
1047 #if 0
1048         /* Uncomment this code if chain-call means call exact one. */
1049         entry = opcode_get_entry(op_Call, graph->opcode_hash);
1050
1051         /* check if we have more than 1 call */
1052         if (cnt_gt(entry->cnt_alive, 1))
1053                 graph->is_chain_call = 0;
1054 #endif
1055
1056         /* recursive functions are never chain calls, leafs don't have calls */
1057         if (graph->is_recursive || graph->is_leaf)
1058                 graph->is_chain_call = 0;
1059
1060         /* assume we walk every graph only ONCE, we could sum here the global count */
1061         for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
1062                 node_entry_t *g_entry = opcode_get_entry(entry->op, global->opcode_hash);
1063
1064                 /* update the node counter */
1065                 cnt_add(&g_entry->cnt_alive, &entry->cnt_alive);
1066         }  /* for */
1067
1068         /* update the edge counter */
1069         cnt_add(&global->cnt_edges, &graph->cnt_edges);
1070
1071         /* count the number of address calculation */
1072         if (graph->irg != get_const_code_irg()) {
1073                 ir_graph *rem = current_ir_graph;
1074
1075                 assure_irg_outs(graph->irg);
1076
1077                 /* Must be done an the outs graph */
1078                 current_ir_graph = graph->irg;
1079                 irg_out_walk(get_irg_start(graph->irg), NULL, mark_address_calc, graph);
1080                 current_ir_graph = rem;
1081
1082 #ifdef DUMP_ADR_MODE
1083                 /* register the vcg hook and dump the graph for test */
1084                 set_dump_node_vcgattr_hook(stat_adr_mark_hook);
1085                 dump_ir_block_graph(graph->irg, "-adr");
1086                 set_dump_node_vcgattr_hook(NULL);
1087 #endif /* DUMP_ADR_MODE */
1088
1089                 irg_walk_graph(graph->irg, NULL, count_adr_ops, graph);
1090         }  /* if */
1091
1092         /* count the DAG's */
1093         if (status->stat_options & FIRMSTAT_COUNT_DAG)
1094                 count_dags_in_graph(global, graph);
1095
1096         /* calculate the patterns of this graph */
1097         stat_calc_pattern_history(graph->irg);
1098
1099         /* leaf function did not call others */
1100         if (graph->is_leaf)
1101                 graph->is_leaf_call = LCS_NON_LEAF_CALL;
1102         else if (graph->is_leaf_call == LCS_UNKNOWN) {
1103                 /* we still don't know if this graph calls leaf-functions, so enqueue */
1104                 pdeq_putl(status->wait_q, graph);
1105         }  /* if */
1106
1107         /* we have analyzed this graph */
1108         graph->is_analyzed = 1;
1109 }  /* update_graph_stat */
1110
1111 /**
1112  * Called for every graph that was on the wait_q in stat_dump_snapshot()
1113  * must finish all statistic info calculations.
1114  *
1115  * @param global    The global entry
1116  * @param graph     The current entry
1117  */
1118 static void update_graph_stat_2(graph_entry_t *global, graph_entry_t *graph)
1119 {
1120         if (graph->is_deleted) {
1121                 /* deleted, ignore */
1122                 return;
1123         }
1124
1125         if (graph->irg) {
1126                 /* count the nodes in the graph */
1127                 irg_walk_graph(graph->irg, update_node_stat_2, NULL, graph);
1128
1129                 if (graph->is_leaf_call == LCS_UNKNOWN)
1130                         graph->is_leaf_call = LCS_LEAF_CALL;
1131         }  /* if */
1132 }  /* update_graph_stat_2 */
1133
1134 /**
1135  * Register a dumper.
1136  */
1137 static void stat_register_dumper(const dumper_t *dumper) {
1138         dumper_t *p = xmalloc(sizeof(*p));
1139
1140         if (p) {
1141                 memcpy(p, dumper, sizeof(*p));
1142
1143                 p->next        = status->dumper;
1144                 p->status      = status;
1145                 status->dumper = p;
1146         }
1147
1148         /* FIXME: memory leak */
1149 }  /* stat_register_dumper */
1150
1151 /**
1152  * Dumps the statistics of an IR graph.
1153  */
1154 static void stat_dump_graph(graph_entry_t *entry) {
1155         dumper_t *dumper;
1156
1157         for (dumper = status->dumper; dumper; dumper = dumper->next) {
1158                 if (dumper->dump_graph)
1159                         dumper->dump_graph(dumper, entry);
1160         }  /* for */
1161 }  /* stat_dump_graph */
1162
1163 /**
1164  * Calls all registered dumper functions.
1165  */
1166 static void stat_dump_registered(graph_entry_t *entry) {
1167         dumper_t *dumper;
1168
1169         for (dumper = status->dumper; dumper; dumper = dumper->next) {
1170                 if (dumper->func_map) {
1171                         dump_graph_FUNC func;
1172
1173                         foreach_pset(dumper->func_map, func)
1174                                 func(dumper, entry);
1175                 }  /* if */
1176         }  /* for */
1177 }  /* stat_dump_registered */
1178
1179 /**
1180  * Dumps a constant table.
1181  */
1182 static void stat_dump_consts(const constant_info_t *tbl) {
1183         dumper_t *dumper;
1184
1185         for (dumper = status->dumper; dumper; dumper = dumper->next) {
1186                 if (dumper->dump_const_tbl)
1187                         dumper->dump_const_tbl(dumper, tbl);
1188         }  /* for */
1189 }  /* stat_dump_consts */
1190
1191 /**
1192  * Initialize the dumper.
1193  */
1194 static void stat_dump_init(const char *name) {
1195         dumper_t *dumper;
1196
1197         for (dumper = status->dumper; dumper; dumper = dumper->next) {
1198                 if (dumper->init)
1199                         dumper->init(dumper, name);
1200         }  /* for */
1201 }  /* stat_dump_init */
1202
1203 /**
1204  * Finish the dumper.
1205  */
1206 static void stat_dump_finish(void) {
1207         dumper_t *dumper;
1208
1209         for (dumper = status->dumper; dumper; dumper = dumper->next) {
1210                 if (dumper->finish)
1211                         dumper->finish(dumper);
1212         }  /* for */
1213 }  /* stat_dump_finish */
1214
1215 /**
1216  * Register an additional function for all dumper.
1217  */
1218 void stat_register_dumper_func(dump_graph_FUNC func) {
1219         dumper_t *dumper;
1220
1221         for (dumper = status->dumper; dumper; dumper = dumper->next) {
1222                 if (! dumper->func_map)
1223                         dumper->func_map = pset_new_ptr(3);
1224                 pset_insert_ptr(dumper->func_map, func);
1225         }  /* for */
1226 }  /* stat_register_dumper_func */
1227
1228 /* ---------------------------------------------------------------------- */
1229
1230 /*
1231  * Helper: get an ir_op from an opcode.
1232  */
1233 ir_op *stat_get_op_from_opcode(opcode code) {
1234         return opcode_find_entry(code, status->ir_op_hash);
1235 }  /* stat_get_op_from_opcode */
1236
1237 /**
1238  * Hook: A new IR op is registered.
1239  *
1240  * @param ctx  the hook context
1241  * @param op   the new IR opcode that was created.
1242  */
1243 static void stat_new_ir_op(void *ctx, ir_op *op) {
1244         if (! status->stat_options)
1245                 return;
1246
1247         STAT_ENTER;
1248         {
1249                 graph_entry_t *graph = graph_get_entry(NULL, status->irg_hash);
1250
1251                 /* execute for side effect :-) */
1252                 (void)opcode_get_entry(op, graph->opcode_hash);
1253
1254                 pset_insert(status->ir_op_hash, op, op->code);
1255         }
1256         STAT_LEAVE;
1257 }  /* stat_new_ir_op */
1258
1259 /**
1260  * Hook: An IR op is freed.
1261  *
1262  * @param ctx  the hook context
1263  * @param op   the IR opcode that is freed
1264  */
1265 static void stat_free_ir_op(void *ctx, ir_op *op) {
1266         if (! status->stat_options)
1267                 return;
1268
1269         STAT_ENTER;
1270         {
1271         }
1272         STAT_LEAVE;
1273 }  /* stat_free_ir_op */
1274
1275 /**
1276  * Hook: A new node is created.
1277  *
1278  * @param ctx   the hook context
1279  * @param irg   the IR graph on which the node is created
1280  * @param node  the new IR node that was created
1281  */
1282 static void stat_new_node(void *ctx, ir_graph *irg, ir_node *node) {
1283         if (! status->stat_options)
1284                 return;
1285
1286         /* do NOT count during dead node elimination */
1287         if (status->in_dead_node_elim > 0)
1288                 return;
1289
1290         STAT_ENTER;
1291         {
1292                 node_entry_t *entry;
1293                 graph_entry_t *graph;
1294                 ir_op *op = stat_get_irn_op(node);
1295
1296                 /* increase global value */
1297                 graph = graph_get_entry(NULL, status->irg_hash);
1298                 entry = opcode_get_entry(op, graph->opcode_hash);
1299                 cnt_inc(&entry->new_node);
1300
1301                 /* increase local value */
1302                 graph = graph_get_entry(current_ir_graph, status->irg_hash);
1303                 entry = opcode_get_entry(op, graph->opcode_hash);
1304                 cnt_inc(&entry->new_node);
1305         }
1306         STAT_LEAVE;
1307 }  /* stat_new_node */
1308
1309 /**
1310  * Hook: A node is changed into a Id node
1311  *
1312  * @param ctx   the hook context
1313  * @param node  the IR node that will be turned into an ID
1314  */
1315 static void stat_turn_into_id(void *ctx, ir_node *node) {
1316         if (! status->stat_options)
1317                 return;
1318
1319         STAT_ENTER;
1320         {
1321                 node_entry_t *entry;
1322                 graph_entry_t *graph;
1323                 ir_op *op = stat_get_irn_op(node);
1324
1325                 /* increase global value */
1326                 graph = graph_get_entry(NULL, status->irg_hash);
1327                 entry = opcode_get_entry(op, graph->opcode_hash);
1328                 cnt_inc(&entry->into_Id);
1329
1330                 /* increase local value */
1331                 graph = graph_get_entry(current_ir_graph, status->irg_hash);
1332                 entry = opcode_get_entry(op, graph->opcode_hash);
1333                 cnt_inc(&entry->into_Id);
1334         }
1335         STAT_LEAVE;
1336 }  /* stat_turn_into_id */
1337
1338 /**
1339  * Hook: A new graph was created
1340  *
1341  * @param ctx  the hook context
1342  * @param irg  the new IR graph that was created
1343  * @param ent  the entity of this graph
1344  */
1345 static void stat_new_graph(void *ctx, ir_graph *irg, entity *ent) {
1346         if (! status->stat_options)
1347                 return;
1348
1349         STAT_ENTER;
1350         {
1351                 /* execute for side effect :-) */
1352                 graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
1353
1354                 graph->ent           = ent;
1355                 graph->is_deleted    = 0;
1356                 graph->is_leaf       = 0;
1357                 graph->is_leaf_call  = 0;
1358                 graph->is_recursive  = 0;
1359                 graph->is_chain_call = 0;
1360                 graph->is_analyzed   = 0;
1361         }
1362         STAT_LEAVE;
1363 }  /* stat_new_graph */
1364
1365 /**
1366  * Hook: A graph will be deleted
1367  *
1368  * @param ctx  the hook context
1369  * @param irg  the IR graph that will be deleted
1370  *
1371  * Note that we still hold the information for this graph
1372  * in our hash maps, only a flag is set which prevents this
1373  * information from being changed, it's "frozen" from now.
1374  */
1375 static void stat_free_graph(void *ctx, ir_graph *irg) {
1376         if (! status->stat_options)
1377                 return;
1378
1379         STAT_ENTER;
1380         {
1381                 graph_entry_t *graph  = graph_get_entry(irg, status->irg_hash);
1382                 graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1383
1384                 graph->is_deleted = 1;
1385
1386                 if (status->stat_options & FIRMSTAT_COUNT_DELETED) {
1387                         /* count the nodes of the graph yet, it will be destroyed later */
1388                         update_graph_stat(global, graph);
1389                 }  /* if */
1390         }
1391         STAT_LEAVE;
1392 }  /* stat_free_graph */
1393
1394 /**
1395  * Hook: A walk over a graph is initiated. Do not count walks from statistic code.
1396  *
1397  * @param ctx  the hook context
1398  * @param irg  the IR graph that will be walked
1399  * @param pre  the pre walker
1400  * @param post the post walker
1401  */
1402 static void stat_irg_walk(void *ctx, ir_graph *irg, generic_func *pre, generic_func *post)
1403 {
1404         if (! status->stat_options)
1405                 return;
1406
1407         STAT_ENTER_SINGLE;
1408         {
1409                 graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1410
1411                 cnt_inc(&graph->cnt_walked);
1412         }
1413         STAT_LEAVE;
1414 }  /* stat_irg_walk */
1415
1416 /**
1417  * Hook: A walk over a graph in block-wise order is initiated. Do not count walks from statistic code.
1418  *
1419  * @param ctx  the hook context
1420  * @param irg  the IR graph that will be walked
1421  * @param pre  the pre walker
1422  * @param post the post walker
1423  */
1424 static void stat_irg_walk_blkwise(void *ctx, ir_graph *irg, generic_func *pre, generic_func *post)
1425 {
1426         /* for now, do NOT differentiate between blockwise and normal */
1427         stat_irg_walk(ctx, irg, pre, post);
1428 }  /* stat_irg_walk_blkwise */
1429
1430 /**
1431  * Hook: A walk over the graph's blocks is initiated. Do not count walks from statistic code.
1432  *
1433  * @param ctx  the hook context
1434  * @param irg  the IR graph that will be walked
1435  * @param node the IR node
1436  * @param pre  the pre walker
1437  * @param post the post walker
1438  */
1439 static void stat_irg_block_walk(void *ctx, ir_graph *irg, ir_node *node, generic_func *pre, generic_func *post)
1440 {
1441         if (! status->stat_options)
1442                 return;
1443
1444         STAT_ENTER_SINGLE;
1445         {
1446                 graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1447
1448                 cnt_inc(&graph->cnt_walked_blocks);
1449         }
1450         STAT_LEAVE;
1451 }  /* stat_irg_block_walk */
1452
1453 /**
1454  * Called for every node that is removed due to an optimization.
1455  *
1456  * @param n     the IR node that will be removed
1457  * @param hmap  the hash map containing ir_op* -> opt_entry_t*
1458  */
1459 static void removed_due_opt(ir_node *n, hmap_opt_entry_t *hmap) {
1460         ir_op *op          = stat_get_irn_op(n);
1461         opt_entry_t *entry = opt_get_entry(op, hmap);
1462
1463         /* increase global value */
1464         cnt_inc(&entry->count);
1465 }  /* removed_due_opt */
1466
1467 /**
1468  * Hook: Some nodes were optimized into some others due to an optimization.
1469  *
1470  * @param ctx  the hook context
1471  */
1472 static void stat_merge_nodes(
1473     void *ctx,
1474     ir_node **new_node_array, int new_num_entries,
1475     ir_node **old_node_array, int old_num_entries,
1476     hook_opt_kind opt)
1477 {
1478         if (! status->stat_options)
1479                 return;
1480
1481         STAT_ENTER;
1482         {
1483                 int i, j;
1484                 graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1485
1486                 if (status->reassoc_run)
1487                         opt = HOOK_OPT_REASSOC;
1488
1489                 for (i = 0; i < old_num_entries; ++i) {
1490                         for (j = 0; j < new_num_entries; ++j) {
1491                                 if (old_node_array[i] == new_node_array[j])
1492                                         break;
1493
1494                                 /* nodes might be in new and old, these are NOT removed */
1495                                 if (j >= new_num_entries) {
1496                                         int xopt = opt;
1497
1498                                         /* sometimes we did not detect, that it is replaced by a Const */
1499                                         if (opt == HOOK_OPT_CONFIRM && new_num_entries == 1) {
1500                                                 ir_op *op = get_irn_op(new_node_array[0]);
1501
1502                                                 if (op == op_Const || op == op_SymConst)
1503                                                         xopt = HOOK_OPT_CONFIRM_C;
1504                                         }  /* if */
1505
1506                                         removed_due_opt(old_node_array[i], graph->opt_hash[xopt]);
1507                                 }  /* if */
1508                         }  /* for */
1509                 }  /* for */
1510         }
1511         STAT_LEAVE;
1512 }  /* stat_merge_nodes */
1513
1514 /**
1515  * Hook: Reassociation is started/stopped.
1516  *
1517  * @param ctx   the hook context
1518  * @param flag  if non-zero, reassociation is started else stopped
1519  */
1520 static void stat_reassociate(void *ctx, int flag) {
1521         if (! status->stat_options)
1522                 return;
1523
1524         STAT_ENTER;
1525         {
1526                 status->reassoc_run = flag;
1527         }
1528         STAT_LEAVE;
1529 }  /* stat_reassociate */
1530
1531 /**
1532  * Hook: A node was lowered into other nodes
1533  *
1534  * @param ctx  the hook context
1535  * @param node the IR node that will be lowered
1536  */
1537 static void stat_lower(void *ctx, ir_node *node) {
1538         if (! status->stat_options)
1539                 return;
1540
1541         STAT_ENTER;
1542         {
1543                 graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1544
1545                 removed_due_opt(node, graph->opt_hash[HOOK_LOWERED]);
1546         }
1547         STAT_LEAVE;
1548 }  /* stat_lower */
1549
1550 /**
1551  * Hook: A graph was inlined.
1552  *
1553  * @param ctx  the hook context
1554  * @param call the IR call that will re changed into the body of
1555  *             the called IR graph
1556  * @param called_irg  the IR graph representing the called routine
1557  */
1558 static void stat_inline(void *ctx, ir_node *call, ir_graph *called_irg)
1559 {
1560         if (! status->stat_options)
1561                 return;
1562
1563         STAT_ENTER;
1564         {
1565                 ir_graph *irg = get_irn_irg(call);
1566                 graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
1567                 graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
1568
1569                 cnt_inc(&graph->cnt_got_inlined);
1570                 cnt_inc(&i_graph->cnt_was_inlined);
1571         }
1572         STAT_LEAVE;
1573 }  /* stat_inline */
1574
1575 /**
1576  * Hook: A graph with tail-recursions was optimized.
1577  *
1578  * @param ctx  the hook context
1579  */
1580 static void stat_tail_rec(void *ctx, ir_graph *irg, int n_calls) {
1581         if (! status->stat_options)
1582                 return;
1583
1584         STAT_ENTER;
1585         {
1586                 graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1587
1588                 graph->num_tail_recursion += n_calls;
1589         }
1590         STAT_LEAVE;
1591 }  /* stat_tail_rec */
1592
1593 /**
1594  * Strength reduction was performed on an iteration variable.
1595  *
1596  * @param ctx  the hook context
1597  */
1598 static void stat_strength_red(void *ctx, ir_graph *irg, ir_node *strong) {
1599         if (! status->stat_options)
1600                 return;
1601
1602         STAT_ENTER;
1603         {
1604                 graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1605                 cnt_inc(&graph->cnt_strength_red);
1606
1607                 removed_due_opt(strong, graph->opt_hash[HOOK_OPT_STRENGTH_RED]);
1608         }
1609         STAT_LEAVE;
1610 }  /* stat_strength_red */
1611
1612 /**
1613  * Hook: Start/Stop the dead node elimination.
1614  *
1615  * @param ctx  the hook context
1616  */
1617 static void stat_dead_node_elim(void *ctx, ir_graph *irg, int start) {
1618         if (! status->stat_options)
1619                 return;
1620
1621         if (start)
1622                 ++status->in_dead_node_elim;
1623         else
1624                 --status->in_dead_node_elim;
1625 }  /* stat_dead_node_elim */
1626
1627 /**
1628  * Hook: if-conversion was tried.
1629  */
1630 static void stat_if_conversion(void *context, ir_graph *irg, ir_node *phi,
1631                                int pos, ir_node *mux, if_result_t reason)
1632 {
1633         if (! status->stat_options)
1634                 return;
1635
1636         STAT_ENTER;
1637         {
1638                 graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1639
1640                 cnt_inc(&graph->cnt_if_conv[reason]);
1641         }
1642         STAT_LEAVE;
1643 }  /* stat_if_conversion */
1644
1645 /**
1646  * Hook: real function call was optimized.
1647  */
1648 static void stat_func_call(void *context, ir_graph *irg, ir_node *call)
1649 {
1650         if (! status->stat_options)
1651                 return;
1652
1653         STAT_ENTER;
1654         {
1655                 graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1656
1657                 cnt_inc(&graph->cnt_real_func_call);
1658         }
1659         STAT_LEAVE;
1660 }  /* stat_func_call */
1661
1662 /**
1663  * Hook: A multiply was replaced by a series of Shifts/Adds/Subs.
1664  *
1665  * @param ctx  the hook context
1666  */
1667 static void stat_arch_dep_replace_mul_with_shifts(void *ctx, ir_node *mul) {
1668         if (! status->stat_options)
1669                 return;
1670
1671         STAT_ENTER;
1672         {
1673                 graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1674                 removed_due_opt(mul, graph->opt_hash[HOOK_OPT_ARCH_DEP]);
1675         }
1676         STAT_LEAVE;
1677 }  /* stat_arch_dep_replace_mul_with_shifts */
1678
1679 /**
1680  * Hook: A division by const was replaced.
1681  *
1682  * @param ctx   the hook context
1683  * @param node  the division node that will be optimized
1684  */
1685 static void stat_arch_dep_replace_division_by_const(void *ctx, ir_node *node) {
1686         if (! status->stat_options)
1687                 return;
1688
1689         STAT_ENTER;
1690         {
1691                 graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1692                 removed_due_opt(node, graph->opt_hash[HOOK_OPT_ARCH_DEP]);
1693         }
1694         STAT_LEAVE;
1695 }  /* stat_arch_dep_replace_division_by_const */
1696
1697 /*
1698  * Update the register pressure of a block.
1699  *
1700  * @param irg        the irg containing the block
1701  * @param block      the block for which the reg pressure should be set
1702  * @param pressure   the pressure
1703  * @param class_name the name of the register class
1704  */
1705 void stat_be_block_regpressure(ir_graph *irg, ir_node *block, int pressure, const char *class_name)
1706 {
1707         if (! status->stat_options)
1708                 return;
1709
1710         STAT_ENTER;
1711         {
1712                 graph_entry_t        *graph = graph_get_entry(irg, status->irg_hash);
1713                 be_block_entry_t     *block_ent;
1714                 reg_pressure_entry_t *rp_ent;
1715
1716                 block_ent = be_block_get_entry(&status->be_data, get_irn_node_nr(block), graph->be_block_hash);
1717                 rp_ent    = obstack_alloc(&status->be_data, sizeof(*rp_ent));
1718                 memset(rp_ent, 0, sizeof(*rp_ent));
1719
1720                 rp_ent->class_name = class_name;
1721                 rp_ent->pressure   = pressure;
1722
1723                 pset_insert(block_ent->reg_pressure, rp_ent, HASH_PTR(class_name));
1724         }
1725         STAT_LEAVE;
1726 }  /* stat_be_block_regpressure */
1727
1728 /**
1729  * Update the distribution of ready nodes of a block
1730  *
1731  * @param irg        the irg containing the block
1732  * @param block      the block for which the reg pressure should be set
1733  * @param num_ready  the number of ready nodes
1734  */
1735 void stat_be_block_sched_ready(ir_graph *irg, ir_node *block, int num_ready)
1736 {
1737         if (! status->stat_options)
1738                 return;
1739
1740         STAT_ENTER;
1741         {
1742                 graph_entry_t    *graph = graph_get_entry(irg, status->irg_hash);
1743                 be_block_entry_t *block_ent;
1744
1745                 block_ent = be_block_get_entry(&status->be_data, get_irn_node_nr(block), graph->be_block_hash);
1746
1747                 /* increase the counter of corresponding number of ready nodes */
1748                 stat_inc_int_distrib_tbl(block_ent->sched_ready, num_ready);
1749         }
1750         STAT_LEAVE;
1751 }  /* stat_be_block_sched_ready */
1752
1753 /**
1754  * Update the permutation statistic of a block.
1755  *
1756  * @param class_name the name of the register class
1757  * @param n_regs     number of registers in the register class
1758  * @param perm       the perm node
1759  * @param block      the block containing the perm
1760  * @param size       the size of the perm
1761  * @param real_size  number of pairs with different registers
1762  */
1763 void stat_be_block_stat_perm(const char *class_name, int n_regs, ir_node *perm, ir_node *block,
1764                              int size, int real_size)
1765 {
1766         if (! status->stat_options)
1767                 return;
1768
1769         STAT_ENTER;
1770         {
1771                 graph_entry_t      *graph = graph_get_entry(get_irn_irg(block), status->irg_hash);
1772                 be_block_entry_t   *block_ent;
1773                 perm_class_entry_t *pc_ent;
1774                 perm_stat_entry_t  *ps_ent;
1775
1776                 block_ent = be_block_get_entry(&status->be_data, get_irn_node_nr(block), graph->be_block_hash);
1777                 pc_ent    = perm_class_get_entry(&status->be_data, class_name, block_ent->perm_class_stat);
1778                 ps_ent    = perm_stat_get_entry(&status->be_data, perm, pc_ent->perm_stat);
1779
1780                 pc_ent->n_regs = n_regs;
1781
1782                 /* update information */
1783                 ps_ent->size      = size;
1784                 ps_ent->real_size = real_size;
1785         }
1786         STAT_LEAVE;
1787 }  /* stat_be_block_stat_perm */
1788
1789 /**
1790  * Update the permutation statistic of a single perm.
1791  *
1792  * @param class_name the name of the register class
1793  * @param perm       the perm node
1794  * @param block      the block containing the perm
1795  * @param is_chain   1 if chain, 0 if cycle
1796  * @param size       length of the cycle/chain
1797  * @param n_ops      the number of ops representing this cycle/chain after lowering
1798  */
1799 void stat_be_block_stat_permcycle(const char *class_name, ir_node *perm, ir_node *block,
1800                                   int is_chain, int size, int n_ops)
1801 {
1802         if (! status->stat_options)
1803                 return;
1804
1805         STAT_ENTER;
1806         {
1807                 graph_entry_t      *graph = graph_get_entry(get_irn_irg(block), status->irg_hash);
1808                 be_block_entry_t   *block_ent;
1809                 perm_class_entry_t *pc_ent;
1810                 perm_stat_entry_t  *ps_ent;
1811
1812                 block_ent = be_block_get_entry(&status->be_data, get_irn_node_nr(block), graph->be_block_hash);
1813                 pc_ent    = perm_class_get_entry(&status->be_data, class_name, block_ent->perm_class_stat);
1814                 ps_ent    = perm_stat_get_entry(&status->be_data, perm, pc_ent->perm_stat);
1815
1816                 if (is_chain) {
1817                         ps_ent->n_copies += n_ops;
1818                         stat_inc_int_distrib_tbl(ps_ent->chains, size);
1819                 } else {
1820                         ps_ent->n_exchg += n_ops;
1821                         stat_inc_int_distrib_tbl(ps_ent->cycles, size);
1822                 }  /* if */
1823         }
1824         STAT_LEAVE;
1825 }  /* stat_be_block_stat_permcycle */
1826
1827 /* Dumps a statistics snapshot. */
1828 void stat_dump_snapshot(const char *name, const char *phase)
1829 {
1830         char fname[2048];
1831         const char *p;
1832         int l;
1833
1834         if (! status->stat_options)
1835                 return;
1836
1837         STAT_ENTER;
1838         {
1839                 graph_entry_t *entry;
1840                 graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1841
1842                 /*
1843                  * The constant counter is only global, so we clear it here.
1844                  * Note that it does NOT contain the constants in DELETED
1845                  * graphs due to this.
1846                  */
1847                 if (status->stat_options & FIRMSTAT_COUNT_CONSTS)
1848                         stat_const_clear(status);
1849
1850                 /* build the name */
1851                 p = strrchr(name, '/');
1852 #ifdef _WIN32
1853                 {
1854                         const char *q;
1855
1856                         q = strrchr(name, '\\');
1857
1858                         /* NULL might be not the smallest pointer */
1859                         if (q && (!p || q > p))
1860                                 p = q;
1861                 }
1862 #endif /* _WIN32 */
1863                 if (p) {
1864                         ++p;
1865                         l = p - name;
1866
1867                         if (l > sizeof(fname) - 1)
1868                                 l = sizeof(fname) - 1;
1869
1870                         memcpy(fname, name, l);
1871                         fname[l] = '\0';
1872                 } else {
1873                         fname[0] = '\0';
1874                         p = name;
1875                 }  /* if */
1876                 strncat(fname, "firmstat-", sizeof(fname));
1877                 strncat(fname, phase, sizeof(fname));
1878                 strncat(fname, "-", sizeof(fname));
1879                 strncat(fname, p, sizeof(fname));
1880
1881                 stat_dump_init(fname);
1882
1883                 /* calculate the graph statistics */
1884                 for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1885                         if (entry->irg == NULL) {
1886                                 /* special entry for the global count */
1887                                 continue;
1888                         }  /* if */
1889                         if (! entry->is_deleted) {
1890                                 /* the graph is still alive, count the nodes on it */
1891                                 update_graph_stat(global, entry);
1892                         }  /* if */
1893                 }  /* for */
1894
1895                 /* some calculations are dependent, we pushed them on the wait_q */
1896                 while (! pdeq_empty(status->wait_q)) {
1897                         entry = pdeq_getr(status->wait_q);
1898
1899                         update_graph_stat_2(global, entry);
1900                 }  /* while */
1901
1902                 /* dump per graph */
1903                 for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1904                         if (entry->irg == NULL) {
1905                                 /* special entry for the global count */
1906                                 continue;
1907                         }  /* if */
1908
1909                         if (! entry->is_deleted || status->stat_options & FIRMSTAT_COUNT_DELETED) {
1910                                 stat_dump_graph(entry);
1911                                 stat_dump_registered(entry);
1912                         }  /* if */
1913
1914                         if (! entry->is_deleted) {
1915                                 /* clear the counter that are not accumulated */
1916                                 graph_clear_entry(entry, 0);
1917                         }  /* if */
1918                 }  /* for */
1919
1920                 /* dump global */
1921                 stat_dump_graph(global);
1922
1923                 /* dump the const info */
1924                 if (status->stat_options & FIRMSTAT_COUNT_CONSTS)
1925                         stat_dump_consts(&status->const_info);
1926
1927                 stat_dump_finish();
1928
1929                 stat_finish_pattern_history(fname);
1930
1931                 /* clear the global counter here */
1932                 {
1933                         node_entry_t *entry;
1934
1935                         for (entry = pset_first(global->opcode_hash); entry; entry = pset_next(global->opcode_hash)) {
1936                                 opcode_clear_entry(entry);
1937                         }  /* for */
1938                         /* clear all global counter */
1939                         graph_clear_entry(global, 1);
1940                 }
1941         }
1942         STAT_LEAVE;
1943 }  /* stat_dump_snapshot */
1944
1945 /** the hook entries for the Firm statistics module */
1946 static hook_entry_t stat_hooks[hook_last];
1947
1948 /* initialize the statistics module. */
1949 void firm_init_stat(unsigned enable_options)
1950 {
1951 #define X(a)  a, sizeof(a)-1
1952 #define HOOK(h, fkt) \
1953         stat_hooks[h].hook._##h = fkt; register_hook(h, &stat_hooks[h])
1954         unsigned num = 0;
1955
1956         if (! (enable_options & FIRMSTAT_ENABLED))
1957                 return;
1958
1959         status = xmalloc(sizeof(*status));
1960         memset(status, 0, sizeof(*status));
1961
1962         /* enable statistics */
1963         status->stat_options = enable_options & FIRMSTAT_ENABLED ? enable_options : 0;
1964
1965         /* register all hooks */
1966         HOOK(hook_new_ir_op,                          stat_new_ir_op);
1967         HOOK(hook_free_ir_op,                         stat_free_ir_op);
1968         HOOK(hook_new_node,                           stat_new_node);
1969         HOOK(hook_turn_into_id,                       stat_turn_into_id);
1970         HOOK(hook_new_graph,                          stat_new_graph);
1971         HOOK(hook_free_graph,                         stat_free_graph);
1972         HOOK(hook_irg_walk,                           stat_irg_walk);
1973         HOOK(hook_irg_walk_blkwise,                   stat_irg_walk_blkwise);
1974         HOOK(hook_irg_block_walk,                     stat_irg_block_walk);
1975         HOOK(hook_merge_nodes,                        stat_merge_nodes);
1976         HOOK(hook_reassociate,                        stat_reassociate);
1977         HOOK(hook_lower,                              stat_lower);
1978         HOOK(hook_inline,                             stat_inline);
1979         HOOK(hook_tail_rec,                           stat_tail_rec);
1980         HOOK(hook_strength_red,                       stat_strength_red);
1981         HOOK(hook_dead_node_elim,                     stat_dead_node_elim);
1982         HOOK(hook_if_conversion,                      stat_if_conversion);
1983         HOOK(hook_func_call,                          stat_func_call);
1984         HOOK(hook_arch_dep_replace_mul_with_shifts,   stat_arch_dep_replace_mul_with_shifts);
1985         HOOK(hook_arch_dep_replace_division_by_const, stat_arch_dep_replace_division_by_const);
1986
1987         obstack_init(&status->cnts);
1988         obstack_init(&status->be_data);
1989
1990         /* create the hash-tables */
1991         status->irg_hash   = new_pset(graph_cmp, 8);
1992         status->ir_op_hash = new_pset(opcode_cmp_2, 1);
1993
1994         /* create the wait queue */
1995         status->wait_q     = new_pdeq();
1996
1997         if (enable_options & FIRMSTAT_COUNT_STRONG_OP) {
1998                 /* build the pseudo-ops */
1999
2000                 _op_Phi0.code    = --num;
2001                 _op_Phi0.name    = new_id_from_chars(X("Phi0"));
2002
2003                 _op_PhiM.code    = --num;
2004                 _op_PhiM.name    = new_id_from_chars(X("PhiM"));
2005
2006                 _op_ProjM.code   = --num;
2007                 _op_ProjM.name   = new_id_from_chars(X("ProjM"));
2008
2009                 _op_MulC.code    = --num;
2010                 _op_MulC.name    = new_id_from_chars(X("MulC"));
2011
2012                 _op_DivC.code    = --num;
2013                 _op_DivC.name    = new_id_from_chars(X("DivC"));
2014
2015                 _op_ModC.code    = --num;
2016                 _op_ModC.name    = new_id_from_chars(X("ModC"));
2017
2018                 _op_DivModC.code = --num;
2019                 _op_DivModC.name = new_id_from_chars(X("DivModC"));
2020
2021                 status->op_Phi0    = &_op_Phi0;
2022                 status->op_PhiM    = &_op_PhiM;
2023                 status->op_ProjM   = &_op_ProjM;
2024                 status->op_MulC    = &_op_MulC;
2025                 status->op_DivC    = &_op_DivC;
2026                 status->op_ModC    = &_op_ModC;
2027                 status->op_DivModC = &_op_DivModC;
2028         } else {
2029                 status->op_Phi0    = NULL;
2030                 status->op_PhiM    = NULL;
2031                 status->op_ProjM   = NULL;
2032                 status->op_MulC    = NULL;
2033                 status->op_DivC    = NULL;
2034                 status->op_ModC    = NULL;
2035                 status->op_DivModC = NULL;
2036         }  /* if */
2037
2038         /* for Florian: count the Sel depth */
2039         if (enable_options & FIRMSTAT_COUNT_SELS) {
2040                 _op_SelSel.code    = --num;
2041                 _op_SelSel.name    = new_id_from_chars(X("Sel(Sel)"));
2042
2043                 _op_SelSelSel.code = --num;
2044                 _op_SelSelSel.name = new_id_from_chars(X("Sel(Sel(Sel))"));
2045
2046                 status->op_SelSel    = &_op_SelSel;
2047                 status->op_SelSelSel = &_op_SelSelSel;
2048         } else {
2049                 status->op_SelSel    = NULL;
2050                 status->op_SelSelSel = NULL;
2051         }  /* if */
2052
2053         /* register the dumper */
2054         stat_register_dumper(&simple_dumper);
2055
2056         if (enable_options & FIRMSTAT_CSV_OUTPUT)
2057                 stat_register_dumper(&csv_dumper);
2058
2059         /* initialize the pattern hash */
2060         stat_init_pattern_history(enable_options & FIRMSTAT_PATTERN_ENABLED);
2061
2062         /* initialize the Const options */
2063         if (enable_options & FIRMSTAT_COUNT_CONSTS)
2064                 stat_init_const_cnt(status);
2065
2066 #undef HOOK
2067 #undef X
2068 }  /* firm_init_stat */
2069
2070 /**
2071  * Frees all dumper structures.
2072  */
2073 static void stat_term_dumper(void) {
2074         dumper_t *dumper, *next_dumper;
2075
2076         for (dumper = status->dumper; dumper; /* iteration done in loop body */ ) {
2077                 if (dumper->func_map)
2078                         del_pset(dumper->func_map);
2079
2080                 next_dumper = dumper->next;
2081                 free(dumper);
2082                 dumper = next_dumper;
2083         }  /* for */
2084 }  /* stat_term_dumper */
2085
2086
2087 /* Terminates the statistics module, frees all memory. */
2088 void stat_term(void) {
2089         if (status != (stat_info_t *)&status_disable) {
2090                 obstack_free(&status->be_data, NULL);
2091                 obstack_free(&status->cnts, NULL);
2092
2093                 stat_term_dumper();
2094
2095                 xfree(status);
2096                 status = (stat_info_t *)&status_disable;
2097         }
2098 }  /* stat_term */
2099
2100 /* returns 1 if statistics were initialized, 0 otherwise */
2101 int stat_is_active(void) {
2102         return status != (stat_info_t *)&status_disable;
2103 }  /* stat_is_active */
2104
2105 #else
2106
2107 /* initialize the statistics module. */
2108 void firm_init_stat(unsigned enable_options) {}
2109
2110 /* Dumps a statistics snapshot */
2111 void stat_dump_snapshot(const char *name, const char *phase) {}
2112
2113 /* terminates the statistics module, frees all memory */
2114 void stat_term(void);
2115
2116 #endif /* FIRM_STATISTICS */