Added long version of get_exec_freq()
[libfirm] / ir / ana / execfreq.c
1 /*
2  * Project:     libFIRM
3  * File name:   ir/ana/execfreq.c
4  * Purpose:     Compute an estimate of basic block executions.
5  * Author:      Adam M. Szalkowski
6  * Modified by:
7  * Created:     28.05.2006
8  * CVS-ID:      $Id$
9  * Copyright:   (c) 2006 Universität Karlsruhe
10  * Licence:     This file protected by GPL -  GNU GENERAL PUBLIC LICENSE.
11  */
12
13 #ifdef HAVE_CONFIG_H
14 #include "config.h"
15 #endif
16
17 //#define USE_GSL
18
19 #include <stdio.h>
20 #include <string.h>
21 #include <math.h>
22
23 #ifdef USE_GSL
24 #include <gsl/gsl_linalg.h>
25 #include <gsl/gsl_vector.h>
26 #else
27 #include "gaussjordan.h"
28 #endif
29
30 #include "execfreq.h"
31
32 #include "firm_common_t.h"
33 #include "set.h"
34 #include "hashptr.h"
35
36 #include "irprog_t.h"
37 #include "irgraph_t.h"
38 #include "irnode_t.h"
39 #include "irloop.h"
40 #include "irgwalk.h"
41 #include "irouts.h"
42 #include "irprintf.h"
43 #include "irhooks.h"
44
45 #include "execfreq.h"
46
47 #define set_foreach(s,i) for((i)=set_first((s)); (i); (i)=set_next((s)))
48
49 typedef struct _freq_t {
50         const ir_node    *irn;
51         double            freq;
52 } freq_t;
53
54
55 typedef struct _walkerdata_t {
56   set    *set;
57   size_t  idx;
58 } walkerdata_t;
59
60 struct _exec_freq_t {
61         set *set;
62         hook_entry_t hook;
63         double min_non_zero;
64         unsigned infeasible : 1;
65 };
66
67 static int
68 cmp_freq(const void *a, const void *b, size_t size)
69 {
70   const freq_t *p = a;
71   const freq_t *q = b;
72
73   return !(p->irn == q->irn);
74 }
75
76 static freq_t *
77 set_find_freq(set * set, const ir_node * irn)
78 {
79   freq_t     query;
80
81   query.irn = irn;
82   return set_find(set, &query, sizeof(query), HASH_PTR(irn));
83 }
84
85 static freq_t *
86 set_insert_freq(set * set, const ir_node * irn)
87 {
88   freq_t     query;
89
90   query.irn = irn;
91   query.freq = 0.0;
92   return set_insert(set, &query, sizeof(query), HASH_PTR(irn));
93 }
94
95 double
96 get_block_execfreq(const exec_freq_t *ef, const ir_node * irn)
97 {
98         if(!ef->infeasible) {
99                 set *freqs = ef->set;
100                 freq_t *freq;
101                 assert(is_Block(irn));
102                 freq = set_find_freq(freqs, irn);
103                 assert(freq);
104                 return freq->freq;
105         }
106
107         return 1.0;
108 }
109
110 unsigned long
111 get_block_execfreq_ulong(const exec_freq_t *ef, const ir_node *bb)
112 {
113         double f = get_block_execfreq(ef, bb);
114         return (unsigned long) (f / ef->min_non_zero);
115 }
116
117 #define ZERO(x)   (fabs(x) < 0.0001)
118
119 static void
120 block_walker(ir_node * bb, void * data)
121 {
122   walkerdata_t  *wd = data;
123
124   set_insert_freq(wd->set, bb);
125   set_irn_link(bb, (void*)wd->idx++);
126 }
127
128 #ifdef USE_GSL
129 static gsl_vector *
130 solve_lgs(double * a_data, double * b_data, size_t size)
131 {
132   gsl_matrix_view m
133     = gsl_matrix_view_array (a_data, size, size);
134
135   gsl_vector_view b
136     = gsl_vector_view_array (b_data, size);
137
138   gsl_vector *x = gsl_vector_alloc (size);
139
140   int s;
141
142   gsl_permutation * p = gsl_permutation_alloc (size);
143
144   gsl_linalg_LU_decomp (&m.matrix, p, &s);
145
146   gsl_linalg_LU_solve (&m.matrix, p, &b.vector, x);
147
148   gsl_permutation_free (p);
149
150   return x;
151 }
152 #else
153 static double *
154 solve_lgs(double * A, double * b, size_t size)
155 {
156   if(firm_gaussjordansolve(A,b,size) == 0) {
157     return b;
158   } else {
159     return NULL;
160   }
161 }
162 #endif
163
164 static double
165 get_cf_probability(ir_node *bb, int pos, double loop_weight)
166 {
167   double  sum = 0.0;
168   double  cur = 0.0;
169   int     i;
170   ir_node *pred = get_Block_cfgpred_block(bb, pos);
171
172   cur = get_loop_depth(get_irn_loop(bb)) < get_loop_depth(get_irn_loop(pred)) ? 1.0 : loop_weight;
173
174   for(i = get_Block_n_cfg_outs(pred) - 1; i >= 0; --i) {
175     ir_node *succ = get_Block_cfg_out(pred, i);
176
177     sum += get_loop_depth(get_irn_loop(succ)) < get_loop_depth(get_irn_loop(pred)) ? 1.0 : loop_weight;
178   }
179
180   return cur/sum;
181 }
182
183 static void exec_freq_node_info(void *ctx, FILE *f, const ir_node *irn)
184 {
185         if(is_Block(irn)) {
186                 exec_freq_t *ef = ctx;
187                 fprintf(f, "execution frequency: %g/%lu\n", get_block_execfreq(ef, irn), get_block_execfreq_ulong(ef, irn));
188         }
189 }
190
191 exec_freq_t *
192 compute_execfreq(ir_graph * irg, double loop_weight)
193 {
194   size_t        size;
195   double       *matrix;
196   double       *rhs;
197   int           i;
198   freq_t       *freq;
199   walkerdata_t  wd;
200         exec_freq_t  *ef;
201         set          *freqs;
202 #ifdef USE_GSL
203   gsl_vector   *x;
204 #else
205   double       *x;
206 #endif
207
208         ef = xmalloc(sizeof(ef[0]));
209         memset(ef, 0, sizeof(ef[0]));
210         ef->min_non_zero = 1e50; /* initialize with a reasonable large number. */
211   freqs = ef->set = new_set(cmp_freq, 32);
212
213   construct_cf_backedges(irg);
214
215   wd.idx = 0;
216   wd.set = freqs;
217
218   irg_block_walk_graph(irg, block_walker, NULL, &wd);
219
220   size = set_count(freqs);
221   matrix = xmalloc(size*size*sizeof(*matrix));
222   memset(matrix, 0, size*size*sizeof(*matrix));
223   rhs = xmalloc(size*sizeof(*rhs));
224   memset(rhs, 0, size*sizeof(*rhs));
225
226   set_foreach(freqs, freq) {
227     ir_node *bb = (ir_node *)freq->irn;
228     size_t  idx = (int)get_irn_link(bb);
229
230     matrix[idx * (size + 1)] = -1.0;
231
232     if (bb == get_irg_start_block(irg)) {
233       rhs[(int)get_irn_link(bb)] = -1.0;
234       continue;
235     }
236
237     for(i = get_Block_n_cfgpreds(bb) - 1; i >= 0; --i) {
238       ir_node *pred    = get_Block_cfgpred_block(bb, i);
239       size_t  pred_idx = (int)get_irn_link(pred);
240
241 //      matrix[pred_idx + idx*size] += 1.0/(double)get_Block_n_cfg_outs(pred);
242       matrix[pred_idx + idx * size] += get_cf_probability(bb, i, loop_weight);
243     }
244   }
245
246   x = solve_lgs(matrix, rhs, size);
247   if(x == NULL) {
248                 ef->infeasible = 1;
249                 return ef;
250   }
251
252   set_foreach(freqs, freq) {
253     const ir_node *bb = freq->irn;
254     size_t        idx = PTR_TO_INT(get_irn_link(bb));
255
256 #ifdef USE_GSL
257     freq->freq = ZERO(gsl_vector_get(x, idx)) ? 0.0 : gsl_vector_get(x, idx);
258 #else
259     freq->freq = ZERO(x[idx]) ? 0.0 : x[idx];
260 #endif
261         /* Get the minimum non-zero execution frequency. */
262         if(freq->freq != 0.0)
263                 ef->min_non_zero = MIN(ef->min_non_zero, freq->freq);
264   }
265
266 #ifdef USE_GSL
267   gsl_vector_free(x);
268 #endif
269   free(matrix);
270
271         memset(&ef->hook, 0, sizeof(ef->hook));
272         ef->hook.context = ef;
273         ef->hook.hook._hook_node_info = exec_freq_node_info;
274         register_hook(hook_node_info, &ef->hook);
275
276   return ef;
277 }
278
279 void
280 free_execfreq(exec_freq_t *ef)
281 {
282         del_set(ef->set);
283         unregister_hook(hook_node_info, &ef->hook);
284         free(ef);
285 }
286
287 #undef ELEM