don't crash with missing argument
[libfirm] / ir / be / beprofile.c
1 /** vim: set sw=4 ts=4:
2  * @file   beprofile.c
3  * @date   2006-04-06
4  * @author Adam M. Szalkowski
5  * @cvs-id $Id$
6  *
7  * Code instrumentation and execution count profiling
8  *
9  * Copyright (C) 2006 Universitaet Karlsruhe
10  * Released under the GPL
11  */
12 #ifdef HAVE_CONFIG_H
13 #include "config.h"
14 #endif
15
16 #include <math.h>
17
18 #include "hashptr.h"
19 #include "debug.h"
20 #include "obst.h"
21 #include "set.h"
22 #include "list.h"
23 #include "pmap.h"
24
25 #include "entity.h"
26 #include "irprintf.h"
27 #include "irgwalk.h"
28 #include "irdump_t.h"
29 #include "irnode_t.h"
30 #include "ircons_t.h"
31 #include "irloop_t.h"
32 #include "iredges.h"
33 #include "execfreq.h"
34 #include "irvrfy.h"
35 #include "type.h"
36 #include "entity.h"
37
38 #include "be_t.h"
39 #include "belive_t.h"
40 #include "besched_t.h"
41 #include "beirgmod.h"
42 #include "bearch.h"
43 #include "beabi.h"
44 #include "benode_t.h"
45 #include "beutil.h"
46 #include "ircons.h"
47 #include "irhooks.h"
48
49 #include "bechordal_t.h"
50
51 #ifdef WITH_LIBCORE
52 #include <libcore/lc_opts.h>
53 #include <libcore/lc_opts_enum.h>
54 #endif /* WITH_LIBCORE */
55
56 #include "beprofile.h"
57
58 typedef struct _block_id_walker_data_t {
59         tarval        **array;
60         unsigned int    id;
61         ir_node *symconst;
62 } block_id_walker_data_t;
63
64 typedef struct _execcount_t {
65         unsigned int block;
66         unsigned int count;
67 } execcount_t;
68
69 static int
70 cmp_execcount(const void * a, const void * b, size_t size)
71 {
72         return ((execcount_t*)a)->block != ((execcount_t*)b)->block;
73 }
74
75 static void
76 block_counter(ir_node * bb, void * data)
77 {
78         unsigned int  *count = data;
79         *count = *count + 1;
80 }
81
82 static unsigned int
83 count_blocks(ir_graph * irg)
84 {
85         unsigned int count = 0;
86
87         irg_block_walk_graph(irg, block_counter, NULL, &count);
88         return count;
89 }
90
91 /* keep the execcounts here because they are only read once per compiler run */
92 static set * profile = NULL;
93 static hook_entry_t hook;
94
95 /**
96  * Instrument a block with code needed for profiling
97  */
98 static void
99 instrument_block(ir_node * bb, ir_node * address, unsigned int id)
100 {
101         ir_graph *irg = get_irn_irg(bb);
102         ir_node  *start_block = get_irg_start_block(irg);
103         ir_node  *load, *store, *offset, *add, *projm, *proji, *unknown;
104         ir_node  *cnst;
105
106         if(bb == start_block || bb == get_irg_end_block(irg))
107                 return;
108
109         unknown = new_r_Unknown(irg, mode_M);
110         cnst    = new_r_Const_long(irg, start_block, mode_Iu, get_mode_size_bytes(mode_Iu) * id);
111         offset  = new_r_Add(irg, bb, address, cnst, mode_P);
112         load    = new_r_Load(irg, bb, unknown, offset, mode_Iu);
113         projm   = new_r_Proj(irg, bb, load, mode_M, pn_Load_M);
114         proji   = new_r_Proj(irg, bb, load, mode_Iu, pn_Load_res);
115         cnst    = new_r_Const_long(irg, start_block, mode_Iu, 1);
116         add     = new_r_Add(irg, bb, proji, cnst, mode_Iu);
117         store   = new_r_Store(irg, bb, projm, offset, add);
118         projm   = new_r_Proj(irg, bb, store, mode_M, pn_Store_M);
119         set_irn_link(bb, projm);
120         set_irn_link(projm, load);
121 }
122
123 typedef struct fix_env {
124         ir_node *start_block;
125         ir_node *end_block;
126 } fix_env;
127
128 /**
129  * SSA Construction for instrumentation code memory
130  */
131 static void
132 fix_ssa(ir_node * bb, void * data)
133 {
134         fix_env *env = data;
135         ir_node *mem;
136         int     arity = get_Block_n_cfgpreds(bb);
137
138         /* start and end block are not instrumented, skip! */
139         if (bb == env->start_block || bb == env->end_block)
140                 return;
141
142         if (arity == 1) {
143                 mem = get_irn_link(get_Block_cfgpred_block(bb, 0));
144         } else {
145                 int n;
146                 ir_node **ins;
147                 ir_graph *irg = current_ir_graph;
148
149                 NEW_ARR_A(ir_node*, ins, arity);
150                 for (n = arity - 1; n >= 0; --n) {
151                         ins[n] = get_irn_link(get_Block_cfgpred_block(bb, n));
152                 }
153                 mem = new_r_Phi(irg, bb, arity, ins, mode_M);
154         }
155         set_Load_mem(get_irn_link(get_irn_link(bb)), mem);
156 }
157
158
159 /**
160  * Generates a new irg which calls the initializer
161  *
162  * Pseudocode:
163  *       void __firmprof_initializer(void) { __init_firmprof(ent_filename, bblock_id, bblock_counts, n_blocks); }
164  */
165 static ir_graph *
166 gen_initializer_irg(entity * ent_filename, entity * bblock_id, entity * bblock_counts, int n_blocks)
167 {
168         ir_node *start_block;
169
170         ir_node   *ins[4];
171         ident     *name = new_id_from_str("__firmprof_initializer");
172         entity    *ent  = new_entity(get_glob_type(), name, new_type_method(name, 0, 0));
173         ir_node   *ret, *call, *symconst;
174         symconst_symbol sym;
175
176         ident     *init_name = new_id_from_str("__init_firmprof");
177         ir_type   *init_type = new_type_method(init_name, 4, 0);
178         ir_type   *uint, *uintptr, *string;
179         entity    *init_ent;
180         ir_graph  *irg;
181         ir_node   *bb;
182         ir_type   *empty_frame_type;
183
184         set_entity_ld_ident(ent, name);
185
186         uint    = new_type_primitive(new_id_from_str("__uint"), mode_Iu);
187         uintptr = new_type_pointer(new_id_from_str("__uintptr"), uint, mode_P);
188         string  = new_type_pointer(new_id_from_str("__charptr"), new_type_primitive(new_id_from_str("__char"), mode_Bs), mode_P);
189
190         set_method_param_type(init_type, 0, string);
191         set_method_param_type(init_type, 1, uintptr);
192         set_method_param_type(init_type, 2, uintptr);
193         set_method_param_type(init_type, 3, uint);
194         init_ent = new_entity(get_glob_type(), init_name, init_type);
195         set_entity_ld_ident(init_ent, init_name);
196
197         irg = new_ir_graph(ent, 0);
198         empty_frame_type = get_irg_frame_type(irg);
199         set_type_size_bytes(empty_frame_type, 0);
200
201         bb = get_cur_block();
202
203         start_block = get_irg_start_block(irg);
204
205         sym.entity_p = init_ent;
206         symconst     = new_r_SymConst(irg, start_block, sym, symconst_addr_ent);
207
208         sym.entity_p = ent_filename;
209         ins[0] = new_r_SymConst(irg, start_block, sym, symconst_addr_ent);
210         sym.entity_p = bblock_id;
211         ins[1] = new_r_SymConst(irg, start_block, sym, symconst_addr_ent);
212         sym.entity_p = bblock_counts;
213         ins[2] = new_r_SymConst(irg, start_block, sym, symconst_addr_ent);
214         ins[3] = new_r_Const_long(irg, start_block, mode_Iu, n_blocks);
215
216         call = new_r_Call(irg, bb, get_irg_initial_mem(irg), symconst, 4, ins, init_type);
217         ret = new_r_Return(irg, bb, new_r_Proj(irg, bb, call, mode_M, pn_Call_M_regular), 0, NULL);
218         mature_immBlock(bb);
219
220         add_immBlock_pred(get_irg_end_block(irg), ret);
221         mature_immBlock(get_irg_end_block(irg));
222
223         irg_finalize_cons(irg);
224
225         return irg;
226 }
227
228 static void
229 block_id_walker(ir_node * bb, void * data)
230 {
231         block_id_walker_data_t *wd = data;
232
233         wd->array[wd->id] = new_tarval_from_long(get_irn_node_nr(bb), mode_Iu);
234         instrument_block(bb, wd->symconst, wd->id);
235         ++wd->id;
236 }
237
238 ir_graph *
239 be_profile_instrument(char * filename)
240 {
241         int            n, i;
242         unsigned int   n_blocks = 0;
243         entity        *bblock_id, *bblock_counts, *ent_filename;
244         ir_type       *array_type, *integer_type, *string_type, *character_type;
245         tarval       **tarval_array, **tarval_string, *tv;
246         int            filename_len = strlen(filename)+1;
247         ident         *cur_ident;
248
249         block_id_walker_data_t  wd;
250         symconst_symbol sym;
251
252         integer_type   = new_type_primitive(new_id_from_str("__uint"), mode_Iu);
253         array_type     = new_type_array(new_id_from_str("__block_info_array"), 1, integer_type);
254         set_array_bounds_int(array_type, 0, 0, n_blocks);
255
256         character_type = new_type_primitive(new_id_from_str("__char"), mode_Bs);
257         string_type    = new_type_array(new_id_from_str("__filename"), 1, character_type);
258         set_array_bounds_int(string_type, 0, 0, filename_len);
259
260         cur_ident      = new_id_from_str("__FIRMPROF__BLOCK_IDS");
261         bblock_id      = new_entity(get_glob_type(), cur_ident, array_type);
262         set_entity_ld_ident(bblock_id, cur_ident);
263         set_entity_variability(bblock_id, variability_initialized);
264
265         cur_ident      = new_id_from_str("__FIRMPROF__BLOCK_COUNTS");
266         bblock_counts  = new_entity(get_glob_type(), cur_ident, array_type);
267         set_entity_ld_ident(bblock_counts, cur_ident);
268         set_entity_variability(bblock_counts, variability_initialized);
269
270         cur_ident      = new_id_from_str("__FIRMPROF__FILE_NAME");
271         ent_filename   = new_entity(get_glob_type(), cur_ident, string_type);
272         set_entity_ld_ident(ent_filename, cur_ident);
273
274         for (n = get_irp_n_irgs() - 1; n >= 0; --n) {
275                 ir_graph *irg = get_irp_irg(n);
276
277                 n_blocks += count_blocks(irg);
278         }
279
280         /* initialize count array */
281         tarval_array = alloca(sizeof(*tarval_array) * n_blocks);
282         tv = get_tarval_null(mode_Iu);
283         for (i = 0; i < n_blocks; ++i) {
284                 tarval_array[i] = tv;
285         }
286         set_array_entity_values(bblock_counts, tarval_array, n_blocks);
287
288         /* initialize function name string constant */
289         tarval_string = alloca(sizeof(*tarval_string) * (filename_len));
290         for (i = 0; i < filename_len; ++i) {
291                 tarval_string[i] = new_tarval_from_long(filename[i], mode_Bs);
292         }
293         set_entity_variability(ent_filename, variability_constant);
294         set_array_entity_values(ent_filename, tarval_string, filename_len);
295
296         /* initialize block id array and instrument blocks */
297         wd.array = tarval_array;
298         wd.id    = 0;
299         for (n = get_irp_n_irgs() - 1; n >= 0; --n) {
300                 ir_graph      *irg = get_irp_irg(n);
301                 int            i;
302                 ir_node       *endbb = get_irg_end_block(irg);
303                 fix_env       env;
304
305                 set_current_ir_graph(irg);
306
307                 /* generate a symbolic constant pointing to the count array */
308                 sym.entity_p = bblock_counts;
309                 wd.symconst  = new_r_SymConst(irg, get_irg_start_block(irg), sym, symconst_addr_ent);
310
311                 irg_block_walk_graph(irg, block_id_walker, NULL, &wd);
312                 env.start_block = get_irg_start_block(irg);
313                 env.end_block   = get_irg_end_block(irg);
314                 set_irn_link(env.start_block, get_irg_no_mem(irg));
315                 irg_block_walk_graph(irg, fix_ssa, NULL, &env);
316                 for (i = get_Block_n_cfgpreds(endbb) - 1; i >= 0; --i) {
317                         ir_node *node = skip_Proj(get_Block_cfgpred(endbb, i));
318                         ir_node *bb   = get_Block_cfgpred_block(endbb, i);
319                         ir_node *sync;
320                         ir_node *ins[2];
321
322                         switch (get_irn_opcode(node)) {
323                         case iro_Return:
324                                 ins[0] = get_irn_link(bb);
325                                 ins[1] = get_Return_mem(node);
326                                 sync   = new_r_Sync(irg, bb, 2, ins);
327                                 set_Return_mem(node, sync);
328                                 break;
329                         case iro_Raise:
330                                 ins[0] = get_irn_link(bb);
331                                 ins[1] = get_Raise_mem(node);
332                                 sync   = new_r_Sync(irg, bb, 2, ins);
333                                 set_Raise_mem(node, sync);
334                                 break;
335                         default:
336                                 /* a fragile's op exception. There should be another path to End,
337                                    so ignore it */
338                                 assert(is_fragile_op(node) && "unexpected End control flow predecessor");
339                         }
340                 }
341         }
342         set_array_entity_values(bblock_id, tarval_array, n_blocks);
343
344         return gen_initializer_irg(ent_filename, bblock_id, bblock_counts, n_blocks);
345 }
346
347 static void
348 profile_node_info(void *ctx, FILE *f, const ir_node *irn)
349 {
350         if(is_Block(irn)) {
351                 fprintf(f, "profiled execution count: %u\n", be_profile_get_block_execcount(irn));
352         }
353 }
354
355 static void
356 register_vcg_hook(void)
357 {
358         memset(&hook, 0, sizeof(hook));
359         hook.hook._hook_node_info = profile_node_info;
360         register_hook(hook_node_info, &hook);
361 }
362
363 static void
364 unregister_vcg_hook(void)
365 {
366         unregister_hook(hook_node_info, &hook);
367 }
368
369 /**
370  * Reads the corresponding profile info file if it exists and returns a
371  * profile info struct
372  */
373 void
374 be_profile_read(char * filename)
375 {
376         FILE   *f;
377         char    buf[8];
378         size_t  ret;
379
380         f = fopen(filename, "r");
381         if(f == NULL) {
382                 return;
383         }
384         printf("found profile data.\n");
385
386         /* check magic */
387         ret = fread(buf, 8, 1, f);
388         if(ret == 0 || strncmp(buf, "firmprof", 8) != 0) {
389                 return;
390         }
391
392         if(profile) be_profile_free();
393         profile = new_set(cmp_execcount, 16);
394
395         do {
396                 execcount_t  query;
397                 ret = fread(&query, sizeof(unsigned int), 2, f);
398
399                 if(ret != 2) break;
400
401                 set_insert(profile, &query, sizeof(query), query.block);
402         } while(1);
403
404         fclose(f);
405         register_vcg_hook();
406 }
407
408 /**
409  * Frees the profile info
410  */
411 void
412 be_profile_free(void)
413 {
414         if(profile) {
415                 unregister_vcg_hook();
416                 del_set(profile);
417         }
418 }
419
420 /**
421  * Tells whether profile module has acquired data
422  */
423 int
424 be_profile_has_data(void)
425 {
426         return (profile != NULL);
427 }
428
429 /**
430  * Get block execution count as determined be profiling
431  */
432 unsigned int
433 be_profile_get_block_execcount(const ir_node * block)
434 {
435         execcount_t *ec, query;
436
437         if(!profile)
438                 return 1;
439
440         query.block = get_irn_node_nr(block);
441         ec = set_find(profile, &query, sizeof(query), get_irn_node_nr(block));
442
443         if(ec) {
444                 return ec->count;
445         } else {
446                 return 1;
447         }
448 }