Renamaed host.h to align.h, removed unneeded things
[libfirm] / ir / stat / firmstat.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/ir/firmstat.c
4  * Purpose:     Statistics for Firm.
5  * Author:      Michael Beck
6  * Created:
7  * CVS-ID:      $Id$
8  * Copyright:   (c) 2004 Universität Karlsruhe
9  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
10  */
11
12 #ifdef HAVE_CONFIG_H
13 # include "config.h"
14 #endif
15
16 #ifdef FIRM_STATISTICS
17
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
21
22 #include "irouts.h"
23 #include "irdump.h"
24 #include "hashptr.h"
25 #include "firmstat_t.h"
26 #include "pattern.h"
27 #include "dags.h"
28 #include "stat_dmp.h"
29
30 /*
31  * need this to be static:
32  * Special pseudo Opcodes that we need to count some interesting cases
33  */
34
35 /**
36  * The Phi0, a node that is created during SSA construction
37  */
38 static ir_op _op_Phi0;
39
40 /** The PhiM, just to count memorty Phi's. */
41 static ir_op _op_PhiM;
42
43 /** The Mul by Const node. */
44 static ir_op _op_MulC;
45
46 /** The Div by Const node. */
47 static ir_op _op_DivC;
48
49 /** The Div by Const node. */
50 static ir_op _op_ModC;
51
52 /** The Div by Const node. */
53 static ir_op _op_DivModC;
54
55 /* ---------------------------------------------------------------------------------- */
56
57 #define STAT_ENTER              ++status->recursive
58 #define STAT_LEAVE              --status->recursive
59 #define STAT_ENTER_SINGLE       do { if (status->recursive > 0) return; ++status->recursive; } while (0)
60
61 /**
62  * global status
63  */
64 static stat_info_t _status, *status = &_status;
65
66 /**
67  * compare two elements of the opcode hash
68  */
69 static int opcode_cmp(const void *elt, const void *key)
70 {
71   const node_entry_t *e1 = elt;
72   const node_entry_t *e2 = key;
73
74   return e1->op->code - e2->op->code;
75 }
76
77 /**
78  * compare two elements of the graph hash
79  */
80 static int graph_cmp(const void *elt, const void *key)
81 {
82   const graph_entry_t *e1 = elt;
83   const graph_entry_t *e2 = key;
84
85   return e1->irg != e2->irg;
86 }
87
88 /**
89  * compare two elements of the optimization hash
90  */
91 static int opt_cmp(const void *elt, const void *key)
92 {
93   const opt_entry_t *e1 = elt;
94   const opt_entry_t *e2 = key;
95
96   return e1->op->code != e2->op->code;
97 }
98
99 /**
100  * compare two elements of the block hash
101  */
102 static int block_cmp(const void *elt, const void *key)
103 {
104   const block_entry_t *e1 = elt;
105   const block_entry_t *e2 = key;
106
107   return e1->block_nr != e2->block_nr;
108 }
109
110 /**
111  * compare two elements of the ir_op hash
112  */
113 static int opcode_cmp_2(const void *elt, const void *key)
114 {
115   const ir_op *e1 = elt;
116   const ir_op *e2 = key;
117
118   return e1->code != e2->code;
119 }
120
121 /**
122  * compare two elements of the address_mark set
123  */
124 static int address_mark_cmp(const void *elt, const void *key, size_t size)
125 {
126   const address_mark_entry_t *e1 = elt;
127   const address_mark_entry_t *e2 = key;
128
129   /* compare only the nodes, the rest is used as data container */
130   return e1->node != e2->node;
131 }
132
133 /**
134  * clears all counter in a node_entry_t
135  */
136 static void opcode_clear_entry(node_entry_t *elem)
137 {
138   cnt_clr(&elem->cnt_alive);
139   cnt_clr(&elem->new_node);
140   cnt_clr(&elem->into_Id);
141 }
142
143 /**
144  * Returns the associates node_entry_t for an ir_op
145  */
146 static node_entry_t *opcode_get_entry(const ir_op *op, pset *set)
147 {
148   node_entry_t key;
149   node_entry_t *elem;
150
151   key.op = op;
152
153   elem = pset_find(set, &key, op->code);
154   if (elem)
155     return elem;
156
157   elem = obstack_alloc(&status->cnts, sizeof(*elem));
158
159   /* clear counter */
160   opcode_clear_entry(elem);
161
162   elem->op = op;
163
164   return pset_insert(set, elem, op->code);
165 }
166
167 /**
168  * Returns the associates ir_op for an opcode
169  */
170 static ir_op *opcode_find_entry(opcode code, pset *set)
171 {
172   ir_op key;
173
174   key.code = code;
175   return pset_find(set, &key, code);
176 }
177
178 /**
179  * clears all counter in a graph_entry_t
180  */
181 static void graph_clear_entry(graph_entry_t *elem, int all)
182 {
183   if (all) {
184     cnt_clr(&elem->cnt_walked);
185     cnt_clr(&elem->cnt_walked_blocks);
186     cnt_clr(&elem->cnt_was_inlined);
187     cnt_clr(&elem->cnt_got_inlined);
188     cnt_clr(&elem->cnt_strength_red);
189   }
190   cnt_clr(&elem->cnt_edges);
191   cnt_clr(&elem->cnt_all_calls);
192   cnt_clr(&elem->cnt_indirect_calls);
193 }
194
195 /**
196  * Returns the acssociates graph_entry_t for an irg
197  */
198 static graph_entry_t *graph_get_entry(ir_graph *irg, pset *set)
199 {
200   graph_entry_t key;
201   graph_entry_t *elem;
202   int i;
203
204   key.irg = irg;
205
206   elem = pset_find(set, &key, HASH_PTR(irg));
207   if (elem)
208     return elem;
209
210   /* allocate a new one */
211   elem = obstack_alloc(&status->cnts, sizeof(*elem));
212
213   /* clear counter */
214   graph_clear_entry(elem, 1);
215
216   /* new hash table for opcodes here  */
217   elem->opcode_hash  = new_pset(opcode_cmp, 5);
218   elem->block_hash   = new_pset(block_cmp, 5);
219   elem->address_mark = new_set(address_mark_cmp, 5);
220   elem->irg          = irg;
221
222   for (i = 0; i < sizeof(elem->opt_hash)/sizeof(elem->opt_hash[0]); ++i)
223     elem->opt_hash[i] = new_pset(opt_cmp, 4);
224
225   return pset_insert(set, elem, HASH_PTR(irg));
226 }
227
228 /**
229  * clears all counter in an opt_entry_t
230  */
231 static void opt_clear_entry(opt_entry_t *elem)
232 {
233   cnt_clr(&elem->count);
234 }
235
236 /**
237  * Returns the associates opt_entry_t for an ir_op
238  */
239 static opt_entry_t *opt_get_entry(const ir_op *op, pset *set)
240 {
241   opt_entry_t key;
242   opt_entry_t *elem;
243
244   key.op = op;
245
246   elem = pset_find(set, &key, op->code);
247   if (elem)
248     return elem;
249
250   elem = obstack_alloc(&status->cnts, sizeof(*elem));
251
252   /* clear new counter */
253   opt_clear_entry(elem);
254
255   elem->op = op;
256
257   return pset_insert(set, elem, op->code);
258 }
259
260 /**
261  * clears all counter in a block_entry_t
262  */
263 static void block_clear_entry(block_entry_t *elem)
264 {
265   cnt_clr(&elem->cnt_nodes);
266   cnt_clr(&elem->cnt_edges);
267   cnt_clr(&elem->cnt_in_edges);
268   cnt_clr(&elem->cnt_out_edges);
269 }
270
271 /**
272  * Returns the associates block_entry_t for an block
273  */
274 static block_entry_t *block_get_entry(long block_nr, pset *set)
275 {
276   block_entry_t key;
277   block_entry_t *elem;
278
279   key.block_nr = block_nr;
280
281   elem = pset_find(set, &key, block_nr);
282   if (elem)
283     return elem;
284
285   elem = obstack_alloc(&status->cnts, sizeof(*elem));
286
287   /* clear new counter */
288   block_clear_entry(elem);
289
290   elem->block_nr = block_nr;
291
292   return pset_insert(set, elem, block_nr);
293 }
294
295
296 /**
297  * Returns the ir_op for an IR-node,
298  * handles special cases and return pseudo op codes
299  */
300 static ir_op *stat_get_irn_op(ir_node *node)
301 {
302   ir_op *op = get_irn_op(node);
303
304   if (op->code == iro_Phi && get_irn_arity(node) == 0) {
305     /* special case, a Phi0 node, count on extra counter */
306     op = status->op_Phi0;
307   }
308   else if (op->code == iro_Phi && get_irn_mode(node) == mode_M) {
309     /* special case, a Memory Phi node, count on extra counter */
310     op = status->op_PhiM;
311   }
312   else if (op->code == iro_Mul &&
313            (get_irn_op(get_Mul_left(node)) == op_Const || get_irn_op(get_Mul_right(node)) == op_Const)) {
314     /* special case, a Multiply by a const, count on extra counter */
315     op = status->op_MulC ? status->op_MulC : op_Mul;
316   }
317   else if (op->code == iro_Div && get_irn_op(get_Div_right(node)) == op_Const) {
318     /* special case, a division by a const, count on extra counter */
319     op = status->op_DivC ? status->op_DivC : op_Div;
320   }
321   else if (op->code == iro_Mod && get_irn_op(get_Mod_right(node)) == op_Const) {
322     /* special case, a module by a const, count on extra counter */
323     op = status->op_ModC ? status->op_ModC : op_Mod;
324   }
325   else if (op->code == iro_DivMod && get_irn_op(get_DivMod_right(node)) == op_Const) {
326     /* special case, a division/modulo by a const, count on extra counter */
327     op = status->op_DivModC ? status->op_DivModC : op_DivMod;
328   }
329
330   return op;
331 }
332
333 /**
334  * update the block counter
335  */
336 static void count_block_info(ir_node *node, graph_entry_t *graph)
337 {
338   ir_op *op = get_irn_op(node);
339   ir_node *block;
340   block_entry_t *b_entry;
341   int i, arity;
342
343   /* check for block */
344   if (op == op_Block) {
345     arity = get_irn_arity(node);
346     b_entry = block_get_entry(get_irn_node_nr(node), graph->block_hash);
347
348     /* count all incoming edges */
349     for (i = 0; i < arity; ++i) {
350       ir_node *pred = get_irn_n(node, i);
351       ir_node *other_block = get_nodes_block(pred);
352       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
353
354       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
355       cnt_inc(&b_entry_other->cnt_out_edges);
356     }
357     return;
358   }
359
360   block   = get_nodes_block(node);
361   b_entry = block_get_entry(get_irn_node_nr(block), graph->block_hash);
362
363   /* we have a new nodes */
364   cnt_inc(&b_entry->cnt_nodes);
365
366   arity = get_irn_arity(node);
367
368   for (i = 0; i < arity; ++i) {
369     ir_node *pred = get_irn_n(node, i);
370     ir_node *other_block;
371
372     if (get_irn_op(pred) == op_Block)
373       continue;
374
375     other_block = get_nodes_block(pred);
376
377     if (other_block == block)
378       cnt_inc(&b_entry->cnt_edges);     /* a in block edge */
379     else {
380       block_entry_t *b_entry_other = block_get_entry(get_irn_node_nr(other_block), graph->block_hash);
381
382       cnt_inc(&b_entry->cnt_in_edges);  /* an edge coming from another block */
383       cnt_inc(&b_entry_other->cnt_out_edges);
384     }
385   }
386 }
387
388 /**
389  * update info on calls
390  */
391 static void update_call_stat(ir_node *call, graph_entry_t *graph)
392 {
393   ir_node *block = get_nodes_block(call);
394   ir_node *ptr = get_Call_ptr(call);
395   entity *ent = NULL;
396
397   /*
398    * If the block is bad, the whole subgraph will colabse later
399    * so do not count this call.
400    * This happens in dead code
401    */
402   if (is_Bad(block))
403     return;
404
405   cnt_inc(&graph->cnt_all_calls);
406
407   /* found a call, is not a leaf function */
408   graph->is_leaf = 0;
409
410   if (get_irn_op(ptr) == op_SymConst) {
411     if (get_SymConst_kind(ptr) == symconst_addr_ent) {
412       /* ok, we seems to know the entity */
413       ent = get_SymConst_entity(ptr);
414
415       if (get_entity_irg(ent) == graph->irg)
416         graph->is_recursive = 1;
417     }
418   }
419   else {
420     /* indirect call */
421     cnt_inc(&graph->cnt_indirect_calls);
422   }
423
424   /* check, if it's a chain-call: Then, the call-block
425    * must dominate the end block. */
426   {
427     ir_node *curr = get_irg_end_block(graph->irg);
428     int depth = get_Block_dom_depth(block);
429
430     for (; curr != block && get_Block_dom_depth(curr) > depth;) {
431       curr = get_Block_idom(curr);
432
433       if (! curr || is_no_Block(curr))
434         break;
435     }
436
437     if (curr != block)
438       graph->is_chain_call = 0;
439   }
440 }
441
442 /**
443  * walker for reachable nodes count
444  */
445 static void update_node_stat(ir_node *node, void *env)
446 {
447   graph_entry_t *graph = env;
448   node_entry_t *entry;
449
450   ir_op *op = stat_get_irn_op(node);
451   int arity = get_irn_arity(node);
452
453   entry = opcode_get_entry(op, graph->opcode_hash);
454
455   cnt_inc(&entry->cnt_alive);
456   cnt_add_i(&graph->cnt_edges, arity);
457
458   /* count block edges */
459   count_block_info(node, graph);
460
461   /* check for properties that depends on calls like recursion/leaf/indirect call */
462   if (get_irn_op(node) == op_Call)
463     update_call_stat(node, graph);
464 }
465
466 /**
467  * get the current address mark
468  */
469 static unsigned get_adr_mark(graph_entry_t *graph, ir_node *node)
470 {
471   address_mark_entry_t *value = set_find(graph->address_mark, &node, sizeof(*value), HASH_PTR(node));
472
473   return value ? value->mark : 0;
474 }
475
476 /**
477  * set the current address mark
478  */
479 static void set_adr_mark(graph_entry_t *graph, ir_node *node, unsigned val)
480 {
481   address_mark_entry_t *value = set_insert(graph->address_mark, &node, sizeof(*value), HASH_PTR(node));
482
483   value->mark = val;
484 }
485
486 /**
487  * a vcg attribute hook
488  */
489 static int stat_adr_mark_hook(FILE *F, ir_node *n)
490 {
491   ir_graph *irg        = get_irn_irg(n);
492   graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
493   unsigned mark        = get_adr_mark(graph, n);
494
495   if (mark & MARK_ADDRESS_CALC)
496     fprintf(F, "color: purple");
497   else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR)
498     fprintf(F, "color: lightpurple");
499   else if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == (MARK_REF_ADR|MARK_REF_NON_ADR))
500     fprintf(F, "color: lightblue");
501   else
502     return 0;
503
504   /* I know the color! */
505   return 1;
506 }
507
508 /**
509  * walker that marks every node that is an address calculation
510  *
511  * predecessor nodes must be visited first. We ensure this by
512  * calling in in the post of an outs walk. This should work even in cycles,
513  * while the pre in a normal walk will not.
514  */
515 static void mark_address_calc(ir_node *node, void *env)
516 {
517   graph_entry_t *graph = env;
518   ir_mode *mode = get_irn_mode(node);
519   int i, n;
520   unsigned mark_preds = MARK_REF_NON_ADR;
521
522   if (! mode_is_numP(mode))
523     return;
524
525   if (mode_is_reference(mode)) {
526     /* a reference is calculated here, we are sure */
527     set_adr_mark(graph, node, MARK_ADDRESS_CALC);
528
529     mark_preds = MARK_REF_ADR;
530   }
531   else {
532     unsigned mark = get_adr_mark(graph, node);
533
534     if ((mark & (MARK_REF_ADR | MARK_REF_NON_ADR)) == MARK_REF_ADR) {
535       /*
536        * this node has not an reference mode, but is only
537        * referenced by address calculations
538        */
539       mark_preds = MARK_REF_ADR;
540     }
541   }
542
543   /* makr all predecessors */
544   for (i = 0, n = get_irn_arity(node); i < n; ++i) {
545     ir_node *pred = get_irn_n(node, i);
546
547     set_adr_mark(graph, pred, get_adr_mark(graph, pred) | mark_preds);
548   }
549 }
550
551 /**
552  * called for every graph when the graph is either deleted or stat_finish()
553  * is called, must recalculate all statistic info
554  */
555 static void update_graph_stat(graph_entry_t *global, graph_entry_t *graph)
556 {
557   node_entry_t *entry;
558
559   /* clear first the alive counter in the graph */
560   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
561     cnt_clr(&entry->cnt_alive);
562   }
563
564   /* set pessimistic values */
565   graph->is_leaf       = 1;
566   graph->is_recursive  = 0;
567   graph->is_chain_call = 1;
568
569   /* we need dominator info */
570   if (graph->irg != get_const_code_irg())
571     if (get_irg_dom_state(graph->irg) != dom_consistent)
572       compute_doms(graph->irg);
573
574   /* count the nodes in the graph */
575   irg_walk_graph(graph->irg, update_node_stat, NULL, graph);
576
577 #if 0
578   entry = opcode_get_entry(op_Call, graph->opcode_hash);
579
580   /* check if we have more than 1 call */
581   if (cnt_gt(entry->cnt_alive, 1))
582     graph->is_chain_call = 0;
583 #endif
584
585   /* recursive functions are never chain calls, leafs don't have calls */
586   if (graph->is_recursive || graph->is_leaf)
587     graph->is_chain_call = 0;
588
589   /* assume we walk every graph only ONCE, we could sum here the global count */
590   for (entry = pset_first(graph->opcode_hash); entry; entry = pset_next(graph->opcode_hash)) {
591     node_entry_t *g_entry = opcode_get_entry(entry->op, global->opcode_hash);
592
593     /* update the node counter */
594     cnt_add(&g_entry->cnt_alive, &entry->cnt_alive);
595   }
596
597   /* update the edge counter */
598   cnt_add(&global->cnt_edges, &graph->cnt_edges);
599
600   /* count the number of address calculation */
601   if (graph->irg != get_const_code_irg()) {
602     ir_graph *rem = current_ir_graph;
603
604     if (get_irg_outs_state(graph->irg) != outs_consistent)
605       compute_outs(graph->irg);
606
607     /* Must be done an the outs graph */
608     current_ir_graph = graph->irg;
609     irg_out_walk(get_irg_start(graph->irg), NULL, mark_address_calc, graph);
610     current_ir_graph = rem;
611
612 #if 0
613     set_dump_node_vcgattr_hook(stat_adr_mark_hook);
614     dump_ir_block_graph(graph->irg, "-adr");
615     set_dump_node_vcgattr_hook(NULL);
616 #endif
617   }
618 }
619
620 /**
621  * register a dumper
622  */
623 static void stat_register_dumper(const dumper_t *dumper)
624 {
625   dumper_t *p = malloc(sizeof(*p));
626
627   if (p) {
628     *p = *dumper;
629
630     p->next        = status->dumper;
631     p->status      = status;
632     status->dumper = p;
633   }
634
635   /* FIXME: memory leak */
636 }
637
638 /**
639  * dumps an irg
640  */
641 static void stat_dump_graph(graph_entry_t *entry)
642 {
643   dumper_t *dumper;
644
645   for (dumper = status->dumper; dumper; dumper = dumper->next) {
646     if (dumper->dump_graph)
647       dumper->dump_graph(dumper, entry);
648   }
649 }
650
651 /**
652  * initialise the dumper
653  */
654 static void stat_dump_init(const char *name)
655 {
656   dumper_t *dumper;
657
658   for (dumper = status->dumper; dumper; dumper = dumper->next) {
659     if (dumper->init)
660       dumper->init(dumper, name);
661   }
662 }
663
664 /**
665  * finish the dumper
666  */
667 static void stat_dump_finish(void)
668 {
669   dumper_t *dumper;
670
671   for (dumper = status->dumper; dumper; dumper = dumper->next) {
672     if (dumper->finish)
673       dumper->finish(dumper);
674   }
675 }
676
677 /* ---------------------------------------------------------------------- */
678
679 /*
680  * helper: get an ir_op from an opcode
681  */
682 ir_op *stat_get_op_from_opcode(opcode code)
683 {
684   return opcode_find_entry(code, status->ir_op_hash);
685 }
686
687 /* initialize the statistics module. */
688 void init_stat(unsigned enable_options)
689 {
690 #define X(a)  a, sizeof(a)-1
691
692   /* enable statistics */
693   status->enable = enable_options & FIRMSTAT_ENABLED;
694
695   if (! status->enable)
696    return;
697
698   obstack_init(&status->cnts);
699
700   /* create the hash-tables */
701   status->irg_hash   = new_pset(graph_cmp, 8);
702   status->ir_op_hash = new_pset(opcode_cmp_2, 1);
703
704   status->op_Phi0    = &_op_Phi0;
705   status->op_PhiM    = &_op_PhiM;
706
707   if (enable_options & FIRMSTAT_COUNT_STRONG_OP) {
708     /* build the pseudo-ops */
709     _op_Phi0.code    = get_next_ir_opcode();
710     _op_Phi0.name    = new_id_from_chars(X("Phi0"));
711
712     _op_PhiM.code    = get_next_ir_opcode();
713     _op_PhiM.name    = new_id_from_chars(X("PhiM"));
714
715     _op_MulC.code    = get_next_ir_opcode();
716     _op_MulC.name    = new_id_from_chars(X("MulC"));
717
718     _op_DivC.code    = get_next_ir_opcode();
719     _op_DivC.name    = new_id_from_chars(X("DivC"));
720
721     _op_ModC.code    = get_next_ir_opcode();
722     _op_ModC.name    = new_id_from_chars(X("ModC"));
723
724     _op_DivModC.code = get_next_ir_opcode();
725     _op_DivModC.name = new_id_from_chars(X("DivModC"));
726
727     status->op_MulC    = &_op_MulC;
728     status->op_DivC    = &_op_DivC;
729     status->op_ModC    = &_op_ModC;
730     status->op_DivModC = &_op_DivModC;
731   }
732   else {
733     status->op_MulC    = NULL;
734     status->op_DivC    = NULL;
735     status->op_ModC    = NULL;
736     status->op_DivModC = NULL;
737   }
738
739   /* register the dumper */
740   stat_register_dumper(&simple_dumper);
741
742   if (enable_options & FIRMSTAT_CSV_OUTPUT)
743     stat_register_dumper(&csv_dumper);
744
745   /* initialize the pattern hash */
746   stat_init_pattern_history(enable_options & FIRMSTAT_PATTERN_ENABLED);
747 #undef X
748 }
749
750 /* A new IR op is registered. */
751 void stat_new_ir_op(const ir_op *op)
752 {
753   if (! status->enable)
754     return;
755
756   STAT_ENTER;
757   {
758     graph_entry_t *graph = graph_get_entry(NULL, status->irg_hash);
759
760     /* execute for side effect :-) */
761     opcode_get_entry(op, graph->opcode_hash);
762
763     pset_insert(status->ir_op_hash, op, op->code);
764   }
765   STAT_LEAVE;
766 }
767
768 /* An IR op is freed. */
769 void stat_free_ir_op(const ir_op *op)
770 {
771   if (! status->enable)
772     return;
773
774   STAT_ENTER;
775   {
776   }
777   STAT_LEAVE;
778 }
779
780 /* A new node is created. */
781 void stat_new_node(ir_node *node)
782 {
783   if (! status->enable)
784     return;
785
786   /* do NOT count during dead node elimination */
787   if (status->in_dead_node_elim > 0)
788     return;
789
790   STAT_ENTER;
791   {
792     node_entry_t *entry;
793     graph_entry_t *graph;
794     ir_op *op = stat_get_irn_op(node);
795
796     /* increase global value */
797     graph = graph_get_entry(NULL, status->irg_hash);
798     entry = opcode_get_entry(op, graph->opcode_hash);
799     cnt_inc(&entry->new_node);
800
801     /* increase local value */
802     graph = graph_get_entry(current_ir_graph, status->irg_hash);
803     entry = opcode_get_entry(op, graph->opcode_hash);
804     cnt_inc(&entry->new_node);
805   }
806   STAT_LEAVE;
807 }
808
809 /* A node is changed into a Id node */
810 void stat_turn_into_id(ir_node *node)
811 {
812   if (! status->enable)
813     return;
814
815   STAT_ENTER;
816   {
817     node_entry_t *entry;
818     graph_entry_t *graph;
819     ir_op *op = stat_get_irn_op(node);
820
821     /* increase global value */
822     graph = graph_get_entry(NULL, status->irg_hash);
823     entry = opcode_get_entry(op, graph->opcode_hash);
824     cnt_inc(&entry->into_Id);
825
826     /* increase local value */
827     graph = graph_get_entry(current_ir_graph, status->irg_hash);
828     entry = opcode_get_entry(op, graph->opcode_hash);
829     cnt_inc(&entry->into_Id);
830   }
831   STAT_LEAVE;
832 }
833
834 /* A new graph was created */
835 void stat_new_graph(ir_graph *irg, entity *ent)
836 {
837   if (! status->enable)
838     return;
839
840   STAT_ENTER;
841   {
842     /* execute for side effect :-) */
843     graph_entry_t * graph = graph_get_entry(irg, status->irg_hash);
844
845     graph->ent           = ent;
846     graph->is_deleted    = 0;
847     graph->is_leaf       = 0;
848     graph->is_recursive  = 0;
849     graph->is_chain_call = 0;
850   }
851   STAT_LEAVE;
852 }
853
854 /*
855  * A graph will be deleted
856  */
857 void stat_free_graph(ir_graph *irg)
858 {
859   if (! status->enable)
860     return;
861
862   STAT_ENTER;
863   {
864     graph_entry_t *graph  = graph_get_entry(irg, status->irg_hash);
865     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
866
867     graph->is_deleted = 1;
868
869     /* count the nodes of the graph yet, it will be destroyed later */
870     update_graph_stat(global, graph);
871
872     /* count the DAG's */
873 //    count_dags_in_graph(global, graph);
874
875     /* calculate the pattern */
876     stat_calc_pattern_history(irg);
877   }
878   STAT_LEAVE;
879 }
880
881 /*
882  * A walk over a graph is initiated. Do not count walks from statistic code.
883  */
884 void stat_irg_walk(ir_graph *irg, void *pre, void *post)
885 {
886   if (! status->enable)
887     return;
888
889   STAT_ENTER_SINGLE;
890   {
891     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
892
893     cnt_inc(&graph->cnt_walked);
894   }
895   STAT_LEAVE;
896 }
897
898 /*
899  * A walk over a graph in block-wise order is initiated. Do not count walks from statistic code.
900  */
901 void stat_irg_walk_blkwise(ir_graph *irg, void *pre, void *post)
902 {
903   /* for now, do NOT differentiate between blockwise and normal */
904   stat_irg_walk(irg, pre, post);
905 }
906
907 /*
908  * A walk over the graph's blocks is initiated. Do not count walks from statistic code.
909  */
910 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post)
911 {
912   if (! status->enable)
913     return;
914
915   STAT_ENTER_SINGLE;
916   {
917     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
918
919     cnt_inc(&graph->cnt_walked_blocks);
920   }
921   STAT_LEAVE;
922 }
923
924 /**
925  * called for every node that is removed due to an optimization
926  */
927 static void removed_due_opt(ir_node *n, pset *set)
928 {
929   ir_op *op          = stat_get_irn_op(n);
930   opt_entry_t *entry = opt_get_entry(op, set);
931
932   /* increase global value */
933   cnt_inc(&entry->count);
934 }
935
936 /*
937  * Some nodes were optimized into some others due to an optimization
938  */
939 void stat_merge_nodes(
940     ir_node **new_node_array, int new_num_entries,
941     ir_node **old_node_array, int old_num_entries,
942     stat_opt_kind opt)
943 {
944   if (! status->enable)
945     return;
946
947   STAT_ENTER;
948   {
949     int i, j;
950     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
951
952     if (status->reassoc_run)
953       opt = STAT_OPT_REASSOC;
954
955     for (i = 0; i < old_num_entries; ++i) {
956       for (j = 0; j < new_num_entries; ++j)
957         if (old_node_array[i] == new_node_array[j])
958           break;
959
960       /* nodes might be in new and old, these are NOT removed */
961       if (j >= new_num_entries) {
962         removed_due_opt(old_node_array[i], graph->opt_hash[opt]);
963       }
964     }
965   }
966   STAT_LEAVE;
967 }
968
969 /*
970  * reassociation started/stopped.
971  */
972 void stat_reassociate(int flag)
973 {
974   if (! status->enable)
975     return;
976
977   STAT_ENTER;
978   {
979     status->reassoc_run = flag;
980   }
981   STAT_LEAVE;
982 }
983
984 /*
985  * A node was lowered into other nodes
986  */
987 void stat_lower(ir_node *node)
988 {
989   if (! status->enable)
990     return;
991
992   STAT_ENTER;
993   {
994     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
995
996     removed_due_opt(node, graph->opt_hash[STAT_LOWERED]);
997   }
998   STAT_LEAVE;
999 }
1000
1001 /*
1002  * A graph was inlined
1003  */
1004 void stat_inline(ir_node *call, ir_graph *called_irg)
1005 {
1006   if (! status->enable)
1007     return;
1008
1009   STAT_ENTER;
1010   {
1011     ir_graph *irg = get_irn_irg(call);
1012     graph_entry_t *i_graph = graph_get_entry(called_irg, status->irg_hash);
1013     graph_entry_t *graph   = graph_get_entry(irg, status->irg_hash);
1014
1015     cnt_inc(&graph->cnt_got_inlined);
1016     cnt_inc(&i_graph->cnt_was_inlined);
1017   }
1018   STAT_LEAVE;
1019 }
1020
1021 /*
1022  * A graph with tail-recursions was optimized.
1023  */
1024 void stat_tail_rec(ir_graph *irg)
1025 {
1026   if (! status->enable)
1027     return;
1028
1029   STAT_ENTER;
1030   {
1031   }
1032   STAT_LEAVE;
1033 }
1034
1035 /*
1036  * Strength reduction was performed on an iteration variable.
1037  */
1038 void stat_strength_red(ir_graph *irg, ir_node *strong, ir_node *cmp)
1039 {
1040   if (! status->enable)
1041     return;
1042
1043   STAT_ENTER;
1044   {
1045     graph_entry_t *graph = graph_get_entry(irg, status->irg_hash);
1046     cnt_inc(&graph->cnt_strength_red);
1047
1048     removed_due_opt(strong, graph->opt_hash[STAT_OPT_STRENGTH_RED]);
1049   }
1050   STAT_LEAVE;
1051 }
1052
1053 /*
1054  * Start the dead node elimination.
1055  */
1056 void stat_dead_node_elim_start(ir_graph *irg)
1057 {
1058   if (! status->enable)
1059     return;
1060
1061   ++status->in_dead_node_elim;
1062 }
1063
1064 /*
1065  * Stops the dead node elimination.
1066  */
1067 void stat_dead_node_elim_stop(ir_graph *irg)
1068 {
1069   if (! status->enable)
1070     return;
1071
1072   --status->in_dead_node_elim;
1073 }
1074
1075 /*
1076  * A multiply was replaced by a series of Shifts/Adds/Subs
1077  */
1078 void stat_arch_dep_replace_mul_with_shifts(ir_node *mul)
1079 {
1080   if (! status->enable)
1081     return;
1082
1083   STAT_ENTER;
1084   {
1085     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1086     removed_due_opt(mul, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1087   }
1088   STAT_LEAVE;
1089 }
1090
1091 /**
1092  * A division was replaced by a series of Shifts/Muls
1093  */
1094 void stat_arch_dep_replace_div_by_const(ir_node *div)
1095 {
1096   if (! status->enable)
1097     return;
1098
1099   STAT_ENTER;
1100   {
1101     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1102     removed_due_opt(div, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1103   }
1104   STAT_LEAVE;
1105 }
1106
1107 /**
1108  * A modulo was replaced by a series of Shifts/Muls
1109  */
1110 void stat_arch_dep_replace_mod_by_const(ir_node *mod)
1111 {
1112   if (! status->enable)
1113     return;
1114
1115   STAT_ENTER;
1116   {
1117     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1118     removed_due_opt(mod, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1119   }
1120   STAT_LEAVE;
1121 }
1122
1123 /**
1124  * A DivMod was replaced by a series of Shifts/Muls
1125  */
1126 void stat_arch_dep_replace_DivMod_by_const(ir_node *divmod)
1127 {
1128   if (! status->enable)
1129     return;
1130
1131   STAT_ENTER;
1132   {
1133     graph_entry_t *graph = graph_get_entry(current_ir_graph, status->irg_hash);
1134     removed_due_opt(divmod, graph->opt_hash[STAT_OPT_ARCH_DEP]);
1135   }
1136   STAT_LEAVE;
1137 }
1138
1139 /* Finish the statistics */
1140 void stat_finish(const char *name)
1141 {
1142   if (! status->enable)
1143     return;
1144
1145   STAT_ENTER;
1146   {
1147     graph_entry_t *entry;
1148     graph_entry_t *global = graph_get_entry(NULL, status->irg_hash);
1149
1150     stat_dump_init(name);
1151
1152     /* dump per graph */
1153     for (entry = pset_first(status->irg_hash); entry; entry = pset_next(status->irg_hash)) {
1154
1155       if (entry->irg == NULL) {
1156         /* special entry for the global count */
1157         continue;
1158       }
1159
1160       if (! entry->is_deleted) {
1161         /* the graph is still alive, count the nodes on it */
1162         update_graph_stat(global, entry);
1163
1164         /* count the DAG's */
1165 //        count_dags_in_graph(global, entry);
1166
1167         /* calculate the pattern */
1168         stat_calc_pattern_history(entry->irg);
1169       }
1170
1171       stat_dump_graph(entry);
1172
1173       /* clear the counter that are not accumulated */
1174       graph_clear_entry(entry, 0);
1175     }
1176
1177     /* dump global */
1178     stat_dump_graph(global);
1179     stat_dump_finish();
1180
1181     stat_finish_pattern_history();
1182
1183     /* clear the global counter here */
1184     {
1185       node_entry_t *entry;
1186
1187       for (entry = pset_first(global->opcode_hash); entry; entry = pset_next(global->opcode_hash)) {
1188         opcode_clear_entry(entry);
1189       }
1190       /* clear all global counter */
1191       graph_clear_entry(global, 1);
1192     }
1193
1194     /* finished */
1195 //    status->enable = 0;
1196   }
1197   STAT_LEAVE;
1198 }
1199
1200 #else
1201
1202 /* need this for prototypes */
1203 #define FIRM_STATISTICS
1204 #include "firmstat.h"
1205
1206 void init_stat(unsigned enable_options) {}
1207
1208 void stat_finish(const char *name) {}
1209
1210 void stat_new_ir_op(const ir_op *op) {}
1211
1212 void stat_free_ir_op(const ir_op *op) {}
1213
1214 void stat_new_node(ir_node *node) {}
1215
1216 void stat_turn_into_id(ir_node *node) {}
1217
1218 void stat_new_graph(ir_graph *irg, entity *ent) {}
1219
1220 void stat_free_graph(ir_graph *irg) {}
1221
1222 void stat_irg_walk(ir_graph *irg, void *pre, void *post) {}
1223
1224 void stat_irg_block_walk(ir_graph *irg, const ir_node *node, void *pre, void *post) {}
1225
1226 void stat_merge_nodes(
1227     ir_node **new_node_array, int new_num_entries,
1228     ir_node **old_node_array, int old_num_entries,
1229     stat_opt_kind opt) {}
1230
1231 void stat_reassociate(int start) {}
1232
1233 void stat_lower(ir_node *node) {}
1234
1235 void stat_inline(ir_node *call, ir_graph *irg) {}
1236
1237 void stat_tail_rec(ir_graph *irg) {}
1238
1239 void stat_strength_red(ir_graph *irg, ir_node *strong, ir_node *cmp) {}
1240
1241 void stat_dead_node_elim_start(ir_graph *irg) {}
1242
1243 void stat_dead_node_elim_stop(ir_graph *irg) {}
1244
1245 void stat_arch_dep_replace_mul_with_shifts(ir_node *mul) {}
1246
1247 void stat_arch_dep_replace_div_by_const(ir_node *div) {}
1248
1249 void stat_arch_dep_replace_mod_by_const(ir_node *mod) {}
1250
1251 void stat_arch_dep_replace_DivMod_by_const(ir_node *divmod) {}
1252
1253 #endif