Add irn_visited_else_mark(), which combines irn_visited() and mark_irn_visited().
[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 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #ifdef HAVE_STDLIB_H
31 # include <stdlib.h>
32 #endif
33
34 #include "irouts.h"
35 #include "irnode_t.h"
36 #include "irmode_t.h"
37 #include "array_t.h"
38 #include "irprog.h"
39 #include "entity_t.h"
40
41 #include "analyze_irg_args.h"
42
43 static char v;
44 static void *VISITED = &v;
45
46 /**
47  * Walk recursive the successors of a graph argument
48  * with mode reference and mark if it will be read,
49  * written or stored.
50  *
51  * @param arg   The graph argument with mode reference,
52  *             that must be checked.
53  */
54 static unsigned analyze_arg(ir_node *arg, unsigned bits) {
55         int i, p;
56         ir_node *succ;
57
58         /* We must visit a node once to avoid endless recursion.*/
59         set_irn_link(arg, VISITED);
60
61         for (i = get_irn_n_outs(arg) - 1; i >= 0; --i) {
62                 succ = get_irn_out(arg, i);
63
64                 /* We was here.*/
65                 if (get_irn_link(succ) == VISITED)
66                         continue;
67
68                 /* We should not walk over the memory edge.*/
69                 if (get_irn_mode(succ) == mode_M)
70                         continue;
71
72                 /* If we reach with the recursion a Call node and our reference
73                    isn't the address of this Call we accept that the reference will
74                    be read and written if the graph of the method represented by
75                    "Call" isn't computed else we analyze that graph. If our
76                    reference is the address of this
77                    Call node that mean the reference will be read.*/
78                 switch (get_irn_opcode(succ)) {
79
80                 case iro_Call: {
81                         ir_node *ptr  = get_Call_ptr(succ);
82
83                         if (ptr == arg) {
84                                 /* Hmm: not sure what this is, most likely a read */
85                                 bits |= ptr_access_read;
86                         } else {
87                                 ir_entity *meth_ent;
88
89                                 if (is_Global(ptr)) {
90                                         meth_ent = get_Global_entity(ptr);
91
92                                         for (p = get_Call_n_params(succ) - 1; p >= 0; --p) {
93                                                 if (get_Call_param(succ, p) == arg) {
94                                                         /* an arg can be used more than once ! */
95                                                         bits |= get_method_param_access(meth_ent, p);
96                                                 }
97                                         }
98                                 } else if (is_Sel(ptr) && get_irp_callee_info_state() == irg_callee_info_consistent) {
99                                         /* is be a polymorphic call but callee information is available */
100                                         int i, n_params = get_Call_n_params(succ);
101
102                                         /* simply look into ALL possible callees */
103                                         for (i = get_Call_n_callees(succ) - 1; i >= 0; --i) {
104                                                 meth_ent = get_Call_callee(succ, i);
105
106                                                 /* unknown_entity is used to signal that we don't know what is called */
107                                                 if (meth_ent == unknown_entity) {
108                                                         bits |= ptr_access_all;
109                                                         break;
110                                                 }
111
112                                                 for (p = n_params - 1; p >= 0; --p) {
113                                                         if (get_Call_param(succ, p) == arg) {
114                                                                 /* an arg can be used more than once ! */
115                                                                 bits |= get_method_param_access(meth_ent, p);
116                                                         }
117                                                 }
118                                         }
119                                 } else /* can do anything */
120                                         bits |= ptr_access_all;
121                         }
122
123                         /* search stops here anyway */
124                         continue;
125                 }
126                 case iro_Store:
127                         /* We have reached a Store node => the reference is written or stored. */
128                         if (get_Store_ptr(succ) == arg) {
129                                 /* written to */
130                                 bits |= ptr_access_write;
131                         } else {
132                                 /* stored itself */
133                                 bits |= ptr_access_store;
134                         }
135
136                         /* search stops here anyway */
137                         continue;
138
139                 case iro_Load:
140                         /* We have reached a Load node => the reference is read. */
141                         bits |= ptr_access_read;
142
143                         /* search stops here anyway */
144                         continue;
145
146                 case iro_Conv:
147                         /* our address is casted into something unknown. Break our search. */
148                         bits = ptr_access_all;
149                         break;
150
151                 default:
152                         break;
153                 }
154
155                 /* If we know that, the argument will be read, write and stored, we
156                    can break the recursion.*/
157                 if (bits == ptr_access_all) {
158                         bits = ptr_access_all;
159                         break;
160                 }
161
162                 /*
163                  * A calculation that do not lead to a reference mode ends our search.
164                  * This is dangerous: It would allow to cast into integer and that cast back ...
165                  * so, when we detect a Conv we go mad, see the Conv case above.
166                  */
167                 if (!mode_is_reference(get_irn_mode(succ)))
168                         continue;
169
170                 /* follow further the address calculation */
171                 bits = analyze_arg(succ, bits);
172         }
173         set_irn_link(arg, NULL);
174         return bits;
175 }
176
177 /**
178  * Check if a argument of the ir graph with mode
179  * reference is read, write or both.
180  *
181  * @param irg   The ir graph to analyze.
182  */
183 static void analyze_ent_args(ir_entity *ent) {
184         ir_graph *irg;
185         ir_node *irg_args, *arg;
186         ir_mode *arg_mode;
187         int nparams, i;
188         long proj_nr;
189         ir_type *mtp;
190         ptr_access_kind *rw_info;
191
192         mtp     = get_entity_type(ent);
193         nparams = get_method_n_params(mtp);
194
195         ent->attr.mtd_attr.param_access = NEW_ARR_F(ptr_access_kind, nparams);
196
197         /* If the method haven't parameters we have
198          * nothing to do.
199          */
200         if (nparams <= 0)
201                 return;
202
203         irg = get_entity_irg(ent);
204
205   /* we have not yet analyzed the graph, set ALL access for pointer args */
206         for (i = nparams - 1; i >= 0; --i) {
207                 ir_type *type = get_method_param_type(mtp, i);
208                 ent->attr.mtd_attr.param_access[i] = is_Pointer_type(type) ? ptr_access_all : ptr_access_none;
209         }
210
211         if (! irg) {
212                 /* no graph, no better info */
213                 return;
214         }
215
216         assure_irg_outs(irg);
217
218         irg_args = get_irg_args(irg);
219
220         /* A array to save the information for each argument with
221            mode reference.*/
222         NEW_ARR_A(ptr_access_kind, rw_info, nparams);
223
224         /* We initialize the element with none state. */
225         for (i = nparams - 1; i >= 0; --i)
226                 rw_info[i] = ptr_access_none;
227
228         /* search for arguments with mode reference
229            to analyze them.*/
230         for (i = get_irn_n_outs(irg_args) - 1; i >= 0; --i) {
231                 arg      = get_irn_out(irg_args, i);
232                 arg_mode = get_irn_mode(arg);
233                 proj_nr  = get_Proj_proj(arg);
234
235                 if (mode_is_reference(arg_mode))
236                         rw_info[proj_nr] |= analyze_arg(arg, rw_info[proj_nr]);
237         }
238
239         /* copy the temporary info */
240         memcpy(ent->attr.mtd_attr.param_access, rw_info,
241                 nparams * sizeof(ent->attr.mtd_attr.param_access[0]));
242
243 #if 0
244         printf("\n%s:\n", get_entity_name(ent));
245         for (i = 0; i < nparams; ++i) {
246                 if (is_Pointer_type(get_method_param_type(mtp, i)))
247                         if (ent->attr.mtd_attr.param_access[i] != ptr_access_none) {
248                                 printf("  Pointer Arg %d access: ", i);
249                                 if (ent->attr.mtd_attr.param_access[i] & ptr_access_read)
250                                         printf("READ ");
251                                 if (ent->attr.mtd_attr.param_access[i] & ptr_access_write)
252                                         printf("WRITE ");
253                                 if (ent->attr.mtd_attr.param_access[i] & ptr_access_store)
254                                         printf("STORE ");
255                                 printf("\n");
256                         }
257         }
258 #endif
259 }
260
261 /**
262  * Analyze how pointer arguments of a given
263  * ir graph are accessed.
264  *
265  * @param irg   The ir graph to analyze.
266  */
267 void analyze_irg_args(ir_graph *irg) {
268         ir_entity *ent;
269
270         if (irg == get_const_code_irg())
271                 return;
272
273         ent = get_irg_entity(irg);
274         if (! ent)
275                 return;
276
277         if (! ent->attr.mtd_attr.param_access)
278                 analyze_ent_args(ent);
279 }
280
281 /*
282  * Compute for a method with pointer parameter(s)
283  * if they will be read or written.
284  */
285 ptr_access_kind get_method_param_access(ir_entity *ent, int pos)
286 {
287 #ifndef NDEBUG
288         ir_type *mtp = get_entity_type(ent);
289         int is_variadic = get_method_variadicity(mtp) == variadicity_variadic;
290
291         assert(0 <= pos && (is_variadic || pos < get_method_n_params(mtp)));
292 #endif
293
294         if (ent->attr.mtd_attr.param_access) {
295                 if (pos < ARR_LEN(ent->attr.mtd_attr.param_access))
296                         return ent->attr.mtd_attr.param_access[pos];
297                 else
298                         return ptr_access_all;
299         }
300
301         analyze_ent_args(ent);
302
303         if (pos < ARR_LEN(ent->attr.mtd_attr.param_access))
304                 return ent->attr.mtd_attr.param_access[pos];
305         else
306                 return ptr_access_all;
307 }
308
309 /* Weights for parameters */
310 enum args_weight {
311         null_weight          = 0,   /**< If can't be anything optimized. */
312         binop_weight         = 1,   /**< If the argument have mode_weight and take part in binop. */
313         const_binop_weight   = 1,   /**< If the argument have mode_weight and take part in binop with a constant.*/
314         cmp_weight           = 4,   /**< If the argument take part in cmp. */
315         const_cmp_weight     = 10,  /**< If the argument take part in cmp with a constant. */
316         indirect_call_weight = 125, /**< If the argument is the address of an indirect Call. */
317 };
318
319 /**
320  * Compute the weight of a method parameter
321  *
322  * @param arg  The parameter them weight muss be computed.
323  */
324 static unsigned calc_method_param_weight(ir_node *arg) {
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         ir_type  *mtp;
429         ir_graph *irg;
430         int      nparams, i, proj_nr;
431         ir_node  *irg_args, *arg;
432
433         mtp      = get_entity_type(ent);
434         nparams  = get_method_n_params(mtp);
435
436         /* allocate a new array. currently used as 'analysed' flag */
437         ent->attr.mtd_attr.param_weight = NEW_ARR_F(unsigned, nparams);
438
439         /* If the method haven't parameters we have nothing to do. */
440         if (nparams <= 0)
441           return;
442
443         /* First we initialize the parameter weights with 0. */
444         for (i = nparams - 1; i >= 0; i--)
445                 ent->attr.mtd_attr.param_weight[i] = null_weight;
446
447         irg = get_entity_irg(ent);
448         if (irg == NULL) {
449                 /* no graph, no better info */
450                 return;
451         }
452
453         /* Call algorithm that computes the out edges */
454         assure_irg_outs(irg);
455
456         irg_args = get_irg_args(irg);
457         for (i = get_irn_n_outs(irg_args) - 1; i >= 0; --i) {
458                 arg     = get_irn_out(irg_args, i);
459                 proj_nr = get_Proj_proj(arg);
460                 ent->attr.mtd_attr.param_weight[proj_nr] += calc_method_param_weight(arg);
461         }
462
463 #if 0
464         printf("\n%s:\n", get_entity_name(ent));
465         for (i = nparams - 1; i >= 0; --i)
466                 printf("The weight of argument %i is %f \n", i, ent->param_weight[i]);
467 #endif
468 }
469
470 /*
471  * Returns for a method the 'weight' that every parameter
472  * has on optimization possibility. Higher values allows
473  * higher optimization with procedure cloning.
474  *
475  * The values are calculation on demand only.
476  *
477  * @param ent  the entity to analyze
478  * @param pos  the argument number
479  *
480  * @return the parameter weight or null_weight if pos is greater
481  * than the number of arguments.
482  */
483 unsigned get_method_param_weight(ir_entity *ent, int pos)
484 {
485         if (ent->attr.mtd_attr.param_weight) {
486                 if (pos < ARR_LEN(ent->attr.mtd_attr.param_weight))
487                         return ent->attr.mtd_attr.param_weight[pos];
488                 else
489                         return null_weight;
490         }
491
492         analyze_method_params_weight(ent);
493
494         if (pos < ARR_LEN(ent->attr.mtd_attr.param_weight))
495                 return ent->attr.mtd_attr.param_weight[pos];
496         else
497                 return null_weight;
498 }
499
500 /**
501  * Analyze argument's weight of a given
502  * ir graph.
503  *
504  * @param irg The ir graph to analyze.
505  */
506 void analyze_irg_args_weight(ir_graph *irg) {
507         ir_entity *ent;
508
509         ent = get_irg_entity(irg);
510         if (ent == NULL)
511                 return;
512
513         if (! ent->attr.mtd_attr.param_weight)
514                 analyze_method_params_weight(ent);
515 }