Replace if/else if cascade with switch(opcode)
[libfirm] / ir / ana / analyze_irg_args.c
1 /*
2  * Copyright (C) 1995-2008 University of Karlsruhe.  All right reserved.
3  *
4  * This file is part of libFirm.
5  *
6  * This file may be distributed and/or modified under the terms of the
7  * GNU General Public License version 2 as published by the Free Software
8  * Foundation and appearing in the file LICENSE.GPL included in the
9  * packaging of this file.
10  *
11  * Licensees holding valid libFirm Professional Edition licenses may use
12  * this file in accordance with the libFirm Commercial License.
13  * Agreement provided with the Software.
14  *
15  * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
16  * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE.
18  */
19
20 /**
21  * @file
22  * @brief      read/write analyze of graph argument, which have mode reference.
23  * @author     Beyhan Veliev
24  * @version    $Id$
25  */
26 #include "config.h"
27
28 #include <stdlib.h>
29
30 #include "irouts.h"
31 #include "irnode_t.h"
32 #include "irmode_t.h"
33 #include "array_t.h"
34 #include "irprog.h"
35 #include "entity_t.h"
36
37 #include "analyze_irg_args.h"
38
39 static char v;
40 static void *VISITED = &v;
41
42 /**
43  * Walk recursive the successors of a graph argument
44  * with mode reference and mark if it will be read,
45  * written or stored.
46  *
47  * @param arg   The graph argument with mode reference,
48  *             that must be checked.
49  */
50 static unsigned analyze_arg(ir_node *arg, unsigned bits)
51 {
52         int i, p;
53         ir_node *succ;
54
55         /* We must visit a node once to avoid endless recursion.*/
56         set_irn_link(arg, VISITED);
57
58         for (i = get_irn_n_outs(arg) - 1; i >= 0; --i) {
59                 succ = get_irn_out(arg, i);
60
61                 /* We was here.*/
62                 if (get_irn_link(succ) == VISITED)
63                         continue;
64
65                 /* We should not walk over the memory edge.*/
66                 if (get_irn_mode(succ) == mode_M)
67                         continue;
68
69                 /* If we reach with the recursion a Call node and our reference
70                    isn't the address of this Call we accept that the reference will
71                    be read and written if the graph of the method represented by
72                    "Call" isn't computed else we analyze that graph. If our
73                    reference is the address of this
74                    Call node that mean the reference will be read.*/
75                 switch (get_irn_opcode(succ)) {
76
77                 case iro_Call: {
78                         ir_node *ptr  = get_Call_ptr(succ);
79
80                         if (ptr == arg) {
81                                 /* Hmm: not sure what this is, most likely a read */
82                                 bits |= ptr_access_read;
83                         } else {
84                                 ir_entity *meth_ent;
85
86                                 if (is_Global(ptr)) {
87                                         meth_ent = get_Global_entity(ptr);
88
89                                         for (p = get_Call_n_params(succ) - 1; p >= 0; --p) {
90                                                 if (get_Call_param(succ, p) == arg) {
91                                                         /* an arg can be used more than once ! */
92                                                         bits |= get_method_param_access(meth_ent, p);
93                                                 }
94                                         }
95                                 } else if (is_Sel(ptr) && get_irp_callee_info_state() == irg_callee_info_consistent) {
96                                         /* is be a polymorphic call but callee information is available */
97                                         int i, n_params = get_Call_n_params(succ);
98
99                                         /* simply look into ALL possible callees */
100                                         for (i = get_Call_n_callees(succ) - 1; i >= 0; --i) {
101                                                 meth_ent = get_Call_callee(succ, i);
102
103                                                 /* unknown_entity is used to signal that we don't know what is called */
104                                                 if (meth_ent == unknown_entity) {
105                                                         bits |= ptr_access_all;
106                                                         break;
107                                                 }
108
109                                                 for (p = n_params - 1; p >= 0; --p) {
110                                                         if (get_Call_param(succ, p) == arg) {
111                                                                 /* an arg can be used more than once ! */
112                                                                 bits |= get_method_param_access(meth_ent, p);
113                                                         }
114                                                 }
115                                         }
116                                 } else /* can do anything */
117                                         bits |= ptr_access_all;
118                         }
119
120                         /* search stops here anyway */
121                         continue;
122                 }
123                 case iro_Store:
124                         /* We have reached a Store node => the reference is written or stored. */
125                         if (get_Store_ptr(succ) == arg) {
126                                 /* written to */
127                                 bits |= ptr_access_write;
128                         } else {
129                                 /* stored itself */
130                                 bits |= ptr_access_store;
131                         }
132
133                         /* search stops here anyway */
134                         continue;
135
136                 case iro_Load:
137                         /* We have reached a Load node => the reference is read. */
138                         bits |= ptr_access_read;
139
140                         /* search stops here anyway */
141                         continue;
142
143                 case iro_Conv:
144                         /* our address is casted into something unknown. Break our search. */
145                         bits = ptr_access_all;
146                         break;
147
148                 default:
149                         break;
150                 }
151
152                 /* If we know that, the argument will be read, write and stored, we
153                    can break the recursion.*/
154                 if (bits == ptr_access_all) {
155                         bits = ptr_access_all;
156                         break;
157                 }
158
159                 /*
160                  * A calculation that do not lead to a reference mode ends our search.
161                  * This is dangerous: It would allow to cast into integer and that cast back ...
162                  * so, when we detect a Conv we go mad, see the Conv case above.
163                  */
164                 if (!mode_is_reference(get_irn_mode(succ)))
165                         continue;
166
167                 /* follow further the address calculation */
168                 bits = analyze_arg(succ, bits);
169         }
170         set_irn_link(arg, NULL);
171         return bits;
172 }
173
174 /**
175  * Check if a argument of the ir graph with mode
176  * reference is read, write or both.
177  *
178  * @param irg   The ir graph to analyze.
179  */
180 static void analyze_ent_args(ir_entity *ent)
181 {
182         ir_graph *irg;
183         ir_node *irg_args, *arg;
184         ir_mode *arg_mode;
185         int nparams, i;
186         long proj_nr;
187         ir_type *mtp;
188         ptr_access_kind *rw_info;
189
190         mtp     = get_entity_type(ent);
191         nparams = get_method_n_params(mtp);
192
193         ent->attr.mtd_attr.param_access = NEW_ARR_F(ptr_access_kind, nparams);
194
195         /* If the method haven't parameters we have
196          * nothing to do.
197          */
198         if (nparams <= 0)
199                 return;
200
201         irg = get_entity_irg(ent);
202
203   /* we have not yet analyzed the graph, set ALL access for pointer args */
204         for (i = nparams - 1; i >= 0; --i) {
205                 ir_type *type = get_method_param_type(mtp, i);
206                 ent->attr.mtd_attr.param_access[i] = is_Pointer_type(type) ? ptr_access_all : ptr_access_none;
207         }
208
209         if (! irg) {
210                 /* no graph, no better info */
211                 return;
212         }
213
214         assure_irg_outs(irg);
215
216         irg_args = get_irg_args(irg);
217
218         /* A array to save the information for each argument with
219            mode reference.*/
220         NEW_ARR_A(ptr_access_kind, rw_info, nparams);
221
222         /* We initialize the element with none state. */
223         for (i = nparams - 1; i >= 0; --i)
224                 rw_info[i] = ptr_access_none;
225
226         /* search for arguments with mode reference
227            to analyze them.*/
228         for (i = get_irn_n_outs(irg_args) - 1; i >= 0; --i) {
229                 arg      = get_irn_out(irg_args, i);
230                 arg_mode = get_irn_mode(arg);
231                 proj_nr  = get_Proj_proj(arg);
232
233                 if (mode_is_reference(arg_mode))
234                         rw_info[proj_nr] |= analyze_arg(arg, rw_info[proj_nr]);
235         }
236
237         /* copy the temporary info */
238         memcpy(ent->attr.mtd_attr.param_access, rw_info,
239                 nparams * sizeof(ent->attr.mtd_attr.param_access[0]));
240
241 #if 0
242         printf("\n%s:\n", get_entity_name(ent));
243         for (i = 0; i < nparams; ++i) {
244                 if (is_Pointer_type(get_method_param_type(mtp, i)))
245                         if (ent->attr.mtd_attr.param_access[i] != ptr_access_none) {
246                                 printf("  Pointer Arg %d access: ", i);
247                                 if (ent->attr.mtd_attr.param_access[i] & ptr_access_read)
248                                         printf("READ ");
249                                 if (ent->attr.mtd_attr.param_access[i] & ptr_access_write)
250                                         printf("WRITE ");
251                                 if (ent->attr.mtd_attr.param_access[i] & ptr_access_store)
252                                         printf("STORE ");
253                                 printf("\n");
254                         }
255         }
256 #endif
257 }
258
259 /**
260  * Analyze how pointer arguments of a given
261  * ir graph are accessed.
262  *
263  * @param irg   The ir graph to analyze.
264  */
265 void analyze_irg_args(ir_graph *irg)
266 {
267         ir_entity *ent;
268
269         if (irg == get_const_code_irg())
270                 return;
271
272         ent = get_irg_entity(irg);
273         if (! ent)
274                 return;
275
276         if (! ent->attr.mtd_attr.param_access)
277                 analyze_ent_args(ent);
278 }
279
280 /*
281  * Compute for a method with pointer parameter(s)
282  * if they will be read or written.
283  */
284 ptr_access_kind get_method_param_access(ir_entity *ent, int pos)
285 {
286 #ifndef NDEBUG
287         ir_type *mtp = get_entity_type(ent);
288         int is_variadic = get_method_variadicity(mtp) == variadicity_variadic;
289
290         assert(0 <= pos && (is_variadic || pos < get_method_n_params(mtp)));
291 #endif
292
293         if (ent->attr.mtd_attr.param_access) {
294                 if (pos < ARR_LEN(ent->attr.mtd_attr.param_access))
295                         return ent->attr.mtd_attr.param_access[pos];
296                 else
297                         return ptr_access_all;
298         }
299
300         analyze_ent_args(ent);
301
302         if (pos < ARR_LEN(ent->attr.mtd_attr.param_access))
303                 return ent->attr.mtd_attr.param_access[pos];
304         else
305                 return ptr_access_all;
306 }
307
308 /* Weights for parameters */
309 enum args_weight {
310         null_weight          = 0,   /**< If can't be anything optimized. */
311         binop_weight         = 1,   /**< If the argument have mode_weight and take part in binop. */
312         const_binop_weight   = 1,   /**< If the argument have mode_weight and take part in binop with a constant.*/
313         cmp_weight           = 4,   /**< If the argument take part in cmp. */
314         const_cmp_weight     = 10,  /**< If the argument take part in cmp with a constant. */
315         indirect_call_weight = 125, /**< If the argument is the address of an indirect Call. */
316 };
317
318 /**
319  * Compute the weight of a method parameter
320  *
321  * @param arg  The parameter them weight muss be computed.
322  */
323 static unsigned calc_method_param_weight(ir_node *arg)
324 {
325         int      i, j, k;
326         ir_node  *succ, *op;
327         unsigned weight = null_weight;
328
329         /* We mark the nodes to avoid endless recursion */
330         set_irn_link(arg, VISITED);
331
332         for (i = get_irn_n_outs(arg) - 1; i >= 0; i--) {
333                 succ = get_irn_out(arg, i);
334
335                 /* We was here.*/
336                 if (get_irn_link(succ) == VISITED)
337                         continue;
338
339                 /* We should not walk over the memory edge.*/
340                 if (get_irn_mode(succ) == mode_M)
341                         continue;
342
343                 switch (get_irn_opcode(succ)) {
344                 case iro_Call:
345                         if (get_Call_ptr(succ) == arg) {
346                                 /* the arguments is used as an pointer input for a call,
347                                    we can probably change an indirect Call into a direct one. */
348                                 weight += indirect_call_weight;
349                         }
350                         break;
351                 case iro_Cmp:
352                         /* We have reached a cmp and we must increase the
353                            weight with the cmp_weight. */
354                         if (get_Cmp_left(succ) == arg)
355                                 op = get_Cmp_right(succ);
356                         else
357                                 op = get_Cmp_left(succ);
358
359                         if (is_irn_constlike(op)) {
360                                 weight += const_cmp_weight;
361                         } else
362                                 weight += cmp_weight;
363                         break;
364                 case iro_Cond:
365                         /* the argument is used for a SwitchCond, a big win */
366                         weight += const_cmp_weight * get_irn_n_outs(succ);
367                         break;
368                 case iro_Id:
369                         /* when looking backward we might find Id nodes */
370                         weight += calc_method_param_weight(succ);
371                         break;
372                 case iro_Tuple:
373                         /* unoptimized tuple */
374                         for (j = get_Tuple_n_preds(succ) - 1; j >= 0; --j) {
375                                 ir_node *pred = get_Tuple_pred(succ, j);
376                                 if (pred == arg) {
377                                         /* look for Proj(j) */
378                                         for (k = get_irn_n_outs(succ) - 1; k >= 0; --k) {
379                                                 ir_node *succ_succ = get_irn_out(succ, k);
380                                                 if (is_Proj(succ_succ)) {
381                                                         if (get_Proj_proj(succ_succ) == j) {
382                                                                 /* found */
383                                                                 weight += calc_method_param_weight(succ_succ);
384                                                         }
385                                                 } else {
386                                                         /* this should NOT happen */
387                                                 }
388                                         }
389                                 }
390                         }
391                         break;
392                 default:
393                         if (is_binop(succ)) {
394                                 /* We have reached a BinOp and we must increase the
395                                    weight with the binop_weight. If the other operand of the
396                                    BinOp is a constant we increase the weight with const_binop_weight
397                                    and call the function recursive.
398                                  */
399                                 if (get_binop_left(succ) == arg)
400                                         op = get_binop_right(succ);
401                                 else
402                                         op = get_binop_left(succ);
403
404                                 if (is_irn_constlike(op)) {
405                                         weight += const_binop_weight;
406                                         weight += calc_method_param_weight(succ);
407                                 } else
408                                         weight += binop_weight;
409                         } else if (is_unop(succ)) {
410                                 /* We have reached a binop and we must increase the
411                                    weight with the const_binop_weight and call the function recursive.*/
412                                 weight += const_binop_weight;
413                                 weight += calc_method_param_weight(succ);
414                         }
415                         break;
416                 }
417         }
418         set_irn_link(arg, NULL);
419         return weight;
420 }
421
422 /**
423  * Calculate a weight for each argument of an entity.
424  *
425  * @param ent  The entity of the ir_graph.
426  */
427 static void analyze_method_params_weight(ir_entity *ent)
428 {
429         ir_type  *mtp;
430         ir_graph *irg;
431         int      nparams, i, proj_nr;
432         ir_node  *irg_args, *arg;
433
434         mtp      = get_entity_type(ent);
435         nparams  = get_method_n_params(mtp);
436
437         /* allocate a new array. currently used as 'analysed' flag */
438         ent->attr.mtd_attr.param_weight = NEW_ARR_F(unsigned, nparams);
439
440         /* If the method haven't parameters we have nothing to do. */
441         if (nparams <= 0)
442           return;
443
444         /* First we initialize the parameter weights with 0. */
445         for (i = nparams - 1; i >= 0; i--)
446                 ent->attr.mtd_attr.param_weight[i] = null_weight;
447
448         irg = get_entity_irg(ent);
449         if (irg == NULL) {
450                 /* no graph, no better info */
451                 return;
452         }
453
454         /* Call algorithm that computes the out edges */
455         assure_irg_outs(irg);
456
457         irg_args = get_irg_args(irg);
458         for (i = get_irn_n_outs(irg_args) - 1; i >= 0; --i) {
459                 arg     = get_irn_out(irg_args, i);
460                 proj_nr = get_Proj_proj(arg);
461                 ent->attr.mtd_attr.param_weight[proj_nr] += calc_method_param_weight(arg);
462         }
463
464 #if 0
465         printf("\n%s:\n", get_entity_name(ent));
466         for (i = nparams - 1; i >= 0; --i)
467                 printf("The weight of argument %i is %f \n", i, ent->param_weight[i]);
468 #endif
469 }
470
471 /*
472  * Returns for a method the 'weight' that every parameter
473  * has on optimization possibility. Higher values allows
474  * higher optimization with procedure cloning.
475  *
476  * The values are calculation on demand only.
477  *
478  * @param ent  the entity to analyze
479  * @param pos  the argument number
480  *
481  * @return the parameter weight or null_weight if pos is greater
482  * than the number of arguments.
483  */
484 unsigned get_method_param_weight(ir_entity *ent, int pos)
485 {
486         if (ent->attr.mtd_attr.param_weight) {
487                 if (pos < ARR_LEN(ent->attr.mtd_attr.param_weight))
488                         return ent->attr.mtd_attr.param_weight[pos];
489                 else
490                         return null_weight;
491         }
492
493         analyze_method_params_weight(ent);
494
495         if (pos < ARR_LEN(ent->attr.mtd_attr.param_weight))
496                 return ent->attr.mtd_attr.param_weight[pos];
497         else
498                 return null_weight;
499 }
500
501 /**
502  * Analyze argument's weight of a given
503  * ir graph.
504  *
505  * @param irg The ir graph to analyze.
506  */
507 void analyze_irg_args_weight(ir_graph *irg)
508 {
509         ir_entity *ent;
510
511         ent = get_irg_entity(irg);
512         if (ent == NULL)
513                 return;
514
515         if (! ent->attr.mtd_attr.param_weight)
516                 analyze_method_params_weight(ent);
517 }