- fixed (useless) assertion
[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 #ifdef HAVE_STDLIB_H
29 # include <stdlib.h>
30 #endif
31
32 #include "irouts.h"
33 #include "irnode_t.h"
34 #include "irmode_t.h"
35 #include "array_t.h"
36 #include "irprog.h"
37 #include "entity_t.h"
38
39 #include "analyze_irg_args.h"
40
41 static char v;
42 static void *VISITED = &v;
43
44 /**
45  * Walk recursive the successors of a graph argument
46  * with mode reference and mark if it will be read,
47  * written or stored.
48  *
49  * @param arg   The graph argument with mode reference,
50  *             that must be checked.
51  */
52 static unsigned analyze_arg(ir_node *arg, unsigned bits) {
53         int i, p;
54         ir_node *succ;
55
56         /* We must visit a node once to avoid endless recursion.*/
57         set_irn_link(arg, VISITED);
58
59         for (i = get_irn_n_outs(arg) - 1; i >= 0; --i) {
60                 succ = get_irn_out(arg, i);
61
62                 /* We was here.*/
63                 if (get_irn_link(succ) == VISITED)
64                         continue;
65
66                 /* We should not walk over the memory edge.*/
67                 if (get_irn_mode(succ) == mode_M)
68                         continue;
69
70                 /* If we reach with the recursion a Call node and our reference
71                    isn't the address of this Call we accept that the reference will
72                    be read and written if the graph of the method represented by
73                    "Call" isn't computed else we analyze that graph. If our
74                    reference is the address of this
75                    Call node that mean the reference will be read.*/
76                 switch (get_irn_opcode(succ)) {
77
78                 case iro_Call: {
79                         ir_node *ptr  = get_Call_ptr(succ);
80
81                         if (ptr == arg) {
82                                 /* Hmm: not sure what this is, most likely a read */
83                                 bits |= ptr_access_read;
84                         } else {
85                                 ir_entity *meth_ent;
86
87                                 if (is_Global(ptr)) {
88                                         meth_ent = get_Global_entity(ptr);
89
90                                         for (p = get_Call_n_params(succ) - 1; p >= 0; --p) {
91                                                 if (get_Call_param(succ, p) == arg) {
92                                                         /* an arg can be used more than once ! */
93                                                         bits |= get_method_param_access(meth_ent, p);
94                                                 }
95                                         }
96                                 } else if (is_Sel(ptr) && get_irp_callee_info_state() == irg_callee_info_consistent) {
97                                         /* is be a polymorphic call but callee information is available */
98                                         int i, n_params = get_Call_n_params(succ);
99
100                                         /* simply look into ALL possible callees */
101                                         for (i = get_Call_n_callees(succ) - 1; i >= 0; --i) {
102                                                 meth_ent = get_Call_callee(succ, i);
103
104                                                 /* unknown_entity is used to signal that we don't know what is called */
105                                                 if (meth_ent == unknown_entity) {
106                                                         bits |= ptr_access_all;
107                                                         break;
108                                                 }
109
110                                                 for (p = n_params - 1; p >= 0; --p) {
111                                                         if (get_Call_param(succ, p) == arg) {
112                                                                 /* an arg can be used more than once ! */
113                                                                 bits |= get_method_param_access(meth_ent, p);
114                                                         }
115                                                 }
116                                         }
117                                 } else /* can do anything */
118                                         bits |= ptr_access_all;
119                         }
120
121                         /* search stops here anyway */
122                         continue;
123                 }
124                 case iro_Store:
125                         /* We have reached a Store node => the reference is written or stored. */
126                         if (get_Store_ptr(succ) == arg) {
127                                 /* written to */
128                                 bits |= ptr_access_write;
129                         } else {
130                                 /* stored itself */
131                                 bits |= ptr_access_store;
132                         }
133
134                         /* search stops here anyway */
135                         continue;
136
137                 case iro_Load:
138                         /* We have reached a Load node => the reference is read. */
139                         bits |= ptr_access_read;
140
141                         /* search stops here anyway */
142                         continue;
143
144                 case iro_Conv:
145                         /* our address is casted into something unknown. Break our search. */
146                         bits = ptr_access_all;
147                         break;
148
149                 default:
150                         break;
151                 }
152
153                 /* If we know that, the argument will be read, write and stored, we
154                    can break the recursion.*/
155                 if (bits == ptr_access_all) {
156                         bits = ptr_access_all;
157                         break;
158                 }
159
160                 /*
161                  * A calculation that do not lead to a reference mode ends our search.
162                  * This is dangerous: It would allow to cast into integer and that cast back ...
163                  * so, when we detect a Conv we go mad, see the Conv case above.
164                  */
165                 if (!mode_is_reference(get_irn_mode(succ)))
166                         continue;
167
168                 /* follow further the address calculation */
169                 bits = analyze_arg(succ, bits);
170         }
171         set_irn_link(arg, NULL);
172         return bits;
173 }
174
175 /**
176  * Check if a argument of the ir graph with mode
177  * reference is read, write or both.
178  *
179  * @param irg   The ir graph to analyze.
180  */
181 static void analyze_ent_args(ir_entity *ent) {
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         ir_entity *ent;
267
268         if (irg == get_const_code_irg())
269                 return;
270
271         ent = get_irg_entity(irg);
272         if (! ent)
273                 return;
274
275         if (! ent->attr.mtd_attr.param_access)
276                 analyze_ent_args(ent);
277 }
278
279 /*
280  * Compute for a method with pointer parameter(s)
281  * if they will be read or written.
282  */
283 ptr_access_kind get_method_param_access(ir_entity *ent, int pos)
284 {
285 #ifndef NDEBUG
286         ir_type *mtp = get_entity_type(ent);
287         int is_variadic = get_method_variadicity(mtp) == variadicity_variadic;
288
289         assert(0 <= pos && (is_variadic || pos < get_method_n_params(mtp)));
290 #endif
291
292         if (ent->attr.mtd_attr.param_access) {
293                 if (pos < ARR_LEN(ent->attr.mtd_attr.param_access))
294                         return ent->attr.mtd_attr.param_access[pos];
295                 else
296                         return ptr_access_all;
297         }
298
299         analyze_ent_args(ent);
300
301         if (pos < ARR_LEN(ent->attr.mtd_attr.param_access))
302                 return ent->attr.mtd_attr.param_access[pos];
303         else
304                 return ptr_access_all;
305 }
306
307 /* Weights for parameters */
308 enum args_weight {
309         null_weight          = 0,   /**< If can't be anything optimized. */
310         binop_weight         = 1,   /**< If the argument have mode_weight and take part in binop. */
311         const_binop_weight   = 1,   /**< If the argument have mode_weight and take part in binop with a constant.*/
312         cmp_weight           = 4,   /**< If the argument take part in cmp. */
313         const_cmp_weight     = 10,  /**< If the argument take part in cmp with a constant. */
314         indirect_call_weight = 125, /**< If the argument is the address of an indirect Call. */
315 };
316
317 /**
318  * Compute the weight of a method parameter
319  *
320  * @param arg  The parameter them weight muss be computed.
321  */
322 static unsigned calc_method_param_weight(ir_node *arg) {
323         int      i, j, k;
324         ir_node  *succ, *op;
325         unsigned weight = null_weight;
326
327         /* We mark the nodes to avoid endless recursion */
328         set_irn_link(arg, VISITED);
329
330         for (i = get_irn_n_outs(arg) - 1; i >= 0; i--) {
331                 succ = get_irn_out(arg, i);
332
333                 /* We was here.*/
334                 if (get_irn_link(succ) == VISITED)
335                         continue;
336
337                 /* We should not walk over the memory edge.*/
338                 if (get_irn_mode(succ) == mode_M)
339                         continue;
340
341                 switch (get_irn_opcode(succ)) {
342                 case iro_Call:
343                         if (get_Call_ptr(succ) == arg) {
344                                 /* the arguments is used as an pointer input for a call,
345                                    we can probably change an indirect Call into a direct one. */
346                                 weight += indirect_call_weight;
347                         }
348                         break;
349                 case iro_Cmp:
350                         /* We have reached a cmp and we must increase the
351                            weight with the cmp_weight. */
352                         if (get_Cmp_left(succ) == arg)
353                                 op = get_Cmp_right(succ);
354                         else
355                                 op = get_Cmp_left(succ);
356
357                         if (is_irn_constlike(op)) {
358                                 weight += const_cmp_weight;
359                         } else
360                                 weight += cmp_weight;
361                         break;
362                 case iro_Cond:
363                         /* the argument is used for a SwitchCond, a big win */
364                         weight += const_cmp_weight * get_irn_n_outs(succ);
365                         break;
366                 case iro_Id:
367                         /* when looking backward we might find Id nodes */
368                         weight += calc_method_param_weight(succ);
369                         break;
370                 case iro_Tuple:
371                         /* unoptimized tuple */
372                         for (j = get_Tuple_n_preds(succ) - 1; j >= 0; --j) {
373                                 ir_node *pred = get_Tuple_pred(succ, j);
374                                 if (pred == arg) {
375                                         /* look for Proj(j) */
376                                         for (k = get_irn_n_outs(succ) - 1; k >= 0; --k) {
377                                                 ir_node *succ_succ = get_irn_out(succ, k);
378                                                 if (is_Proj(succ_succ)) {
379                                                         if (get_Proj_proj(succ_succ) == j) {
380                                                                 /* found */
381                                                                 weight += calc_method_param_weight(succ_succ);
382                                                         }
383                                                 } else {
384                                                         /* this should NOT happen */
385                                                 }
386                                         }
387                                 }
388                         }
389                         break;
390                 default:
391                         if (is_binop(succ)) {
392                                 /* We have reached a BinOp and we must increase the
393                                    weight with the binop_weight. If the other operand of the
394                                    BinOp is a constant we increase the weight with const_binop_weight
395                                    and call the function recursive.
396                                  */
397                                 if (get_binop_left(succ) == arg)
398                                         op = get_binop_right(succ);
399                                 else
400                                         op = get_binop_left(succ);
401
402                                 if (is_irn_constlike(op)) {
403                                         weight += const_binop_weight;
404                                         weight += calc_method_param_weight(succ);
405                                 } else
406                                         weight += binop_weight;
407                         } else if (is_unop(succ)) {
408                                 /* We have reached a binop and we must increase the
409                                    weight with the const_binop_weight and call the function recursive.*/
410                                 weight += const_binop_weight;
411                                 weight += calc_method_param_weight(succ);
412                         }
413                         break;
414                 }
415         }
416         set_irn_link(arg, NULL);
417         return weight;
418 }
419
420 /**
421  * Calculate a weight for each argument of an entity.
422  *
423  * @param ent  The entity of the ir_graph.
424  */
425 static void analyze_method_params_weight(ir_entity *ent) {
426         ir_type  *mtp;
427         ir_graph *irg;
428         int      nparams, i, proj_nr;
429         ir_node  *irg_args, *arg;
430
431         mtp      = get_entity_type(ent);
432         nparams  = get_method_n_params(mtp);
433
434         /* allocate a new array. currently used as 'analysed' flag */
435         ent->attr.mtd_attr.param_weight = NEW_ARR_F(unsigned, nparams);
436
437         /* If the method haven't parameters we have nothing to do. */
438         if (nparams <= 0)
439           return;
440
441         /* First we initialize the parameter weights with 0. */
442         for (i = nparams - 1; i >= 0; i--)
443                 ent->attr.mtd_attr.param_weight[i] = null_weight;
444
445         irg = get_entity_irg(ent);
446         if (irg == NULL) {
447                 /* no graph, no better info */
448                 return;
449         }
450
451         /* Call algorithm that computes the out edges */
452         assure_irg_outs(irg);
453
454         irg_args = get_irg_args(irg);
455         for (i = get_irn_n_outs(irg_args) - 1; i >= 0; --i) {
456                 arg     = get_irn_out(irg_args, i);
457                 proj_nr = get_Proj_proj(arg);
458                 ent->attr.mtd_attr.param_weight[proj_nr] += calc_method_param_weight(arg);
459         }
460
461 #if 0
462         printf("\n%s:\n", get_entity_name(ent));
463         for (i = nparams - 1; i >= 0; --i)
464                 printf("The weight of argument %i is %f \n", i, ent->param_weight[i]);
465 #endif
466 }
467
468 /*
469  * Returns for a method the 'weight' that every parameter
470  * has on optimization possibility. Higher values allows
471  * higher optimization with procedure cloning.
472  *
473  * The values are calculation on demand only.
474  *
475  * @param ent  the entity to analyze
476  * @param pos  the argument number
477  *
478  * @return the parameter weight or null_weight if pos is greater
479  * than the number of arguments.
480  */
481 unsigned get_method_param_weight(ir_entity *ent, int pos)
482 {
483         if (ent->attr.mtd_attr.param_weight) {
484                 if (pos < ARR_LEN(ent->attr.mtd_attr.param_weight))
485                         return ent->attr.mtd_attr.param_weight[pos];
486                 else
487                         return null_weight;
488         }
489
490         analyze_method_params_weight(ent);
491
492         if (pos < ARR_LEN(ent->attr.mtd_attr.param_weight))
493                 return ent->attr.mtd_attr.param_weight[pos];
494         else
495                 return null_weight;
496 }
497
498 /**
499  * Analyze argument's weight of a given
500  * ir graph.
501  *
502  * @param irg The ir graph to analyze.
503  */
504 void analyze_irg_args_weight(ir_graph *irg) {
505         ir_entity *ent;
506
507         ent = get_irg_entity(irg);
508         if (ent == NULL)
509                 return;
510
511         if (! ent->attr.mtd_attr.param_weight)
512                 analyze_method_params_weight(ent);
513 }