Fixed the last fix again:
[libfirm] / ir / lower / lower_intrinsics.c
1 /*
2  * Copyright (C) 1995-2007 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   lowering of Calls of intrinsic functions
23  * @author  Michael Beck
24  * @version $Id$
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include "irop_t.h"
32 #include "irprog_t.h"
33 #include "irnode_t.h"
34 #include "irprog_t.h"
35 #include "irgwalk.h"
36 #include "ircons.h"
37 #include "irgmod.h"
38 #include "irgopt.h"
39 #include "trouts.h"
40 #include "lower_intrinsics.h"
41 #include "pmap.h"
42 #include "xmalloc.h"
43
44 /** Walker environment */
45 typedef struct _walker_env {
46   pmap     *c_map;              /**< The intrinsic call map. */
47   unsigned nr_of_intrinsics;    /**< statistics */
48   i_instr_record **i_map;       /**< The intrinsic instruction map. */
49 } walker_env_t;
50
51 /**
52  * walker: call all mapper functions
53  */
54 static void call_mapper(ir_node *node, void *env) {
55   walker_env_t *wenv = env;
56   ir_op *op = get_irn_op(node);
57
58   if (op == op_Call) {
59     ir_node *symconst;
60     pmap_entry *p;
61     const i_call_record *r;
62     ir_entity *ent;
63
64     symconst = get_Call_ptr(node);
65     if (get_irn_op(symconst) != op_SymConst ||
66         get_SymConst_kind(symconst) != symconst_addr_ent)
67       return;
68
69     ent = get_SymConst_entity(symconst);
70     p   = pmap_find(wenv->c_map, ent);
71
72     if (p) {
73       r = p->value;
74       wenv->nr_of_intrinsics += r->i_mapper(node, r->ctx) ? 1 : 0;
75     }
76   }
77   else {
78     if (0 <= op->code && op->code < ARR_LEN(wenv->i_map)) {
79       const i_instr_record *r = wenv->i_map[op->code];
80       /* run all possible mapper */
81       while (r) {
82         if (r->i_mapper(node, r->ctx)) {
83           ++wenv->nr_of_intrinsics;
84           break;
85         }
86         r = r->link;
87       }
88     }
89   }
90 }
91
92 /* Go through all graphs and map calls to intrinsic functions. */
93 unsigned lower_intrinsics(i_record *list, int length) {
94   int            i, n_ops = get_irp_n_opcodes();
95   ir_graph       *irg;
96   pmap           *c_map = pmap_create_ex(length);
97   i_instr_record **i_map;
98   unsigned       nr_of_intrinsics = 0;
99   walker_env_t   wenv;
100
101   /* we use the ir_op generic pointers here */
102   NEW_ARR_A(const i_instr_record *, i_map, n_ops);
103   memset((void *)i_map, 0, sizeof(*i_map) * n_ops);
104
105   /* fill a map for faster search */
106   for (i = length - 1; i >= 0; --i) {
107     if (list[i].i_call.kind == INTRINSIC_CALL) {
108       pmap_insert(c_map, list[i].i_call.i_ent, (void *)&list[i].i_call);
109     }
110     else {
111       ir_op *op = list[i].i_instr.op;
112       assert(0 <= op->code && op->code < ARR_LEN(i_map));
113
114       list[i].i_instr.link = i_map[op->code];
115       i_map[op->code] = &list[i].i_instr;
116     }
117   }
118
119   wenv.c_map = c_map;
120   wenv.i_map = i_map;
121
122   for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
123     irg = get_irp_irg(i);
124
125     wenv.nr_of_intrinsics = 0;
126     irg_walk_graph(irg, NULL, call_mapper, &wenv);
127
128     if (wenv.nr_of_intrinsics) {
129       /* changes detected */
130       set_irg_outs_inconsistent(irg);
131       set_irg_callee_info_state(irg, irg_callee_info_inconsistent);
132
133       /* exception control flow might have changed */
134       set_irg_doms_inconsistent(irg);
135       set_irg_extblk_inconsistent(irg);
136       set_irg_loopinfo_inconsistent(irg);
137
138       /* calls might be removed/added */
139       set_trouts_inconsistent();
140
141       /* optimize it, tuple might be created */
142       local_optimize_graph(irg);
143
144       nr_of_intrinsics += wenv.nr_of_intrinsics;
145     }
146   }
147   pmap_destroy(c_map);
148
149   return nr_of_intrinsics;
150 }
151
152 /* A mapper for the integer abs. */
153 int i_mapper_Abs(ir_node *call, void *ctx) {
154   ir_node *mem   = get_Call_mem(call);
155   ir_node *block = get_nodes_block(call);
156   ir_node *op    = get_Call_param(call, 0);
157   ir_node *irn;
158   dbg_info *dbg  = get_irn_dbg_info(call);
159
160   irn = new_rd_Abs(dbg, current_ir_graph, block, op, get_irn_mode(op));
161   irn = new_Tuple(1, &irn);
162
163   turn_into_tuple(call, pn_Call_max);
164   set_Tuple_pred(call, pn_Call_M_regular, mem);
165   set_Tuple_pred(call, pn_Call_X_except, new_Bad());
166   set_Tuple_pred(call, pn_Call_T_result, irn);
167   set_Tuple_pred(call, pn_Call_M_except, mem);
168   set_Tuple_pred(call, pn_Call_P_value_res_base, new_Bad());
169
170   return 1;
171 }
172
173 /* A mapper for the alloca() function. */
174 int i_mapper_Alloca(ir_node *call, void *ctx) {
175   ir_node *mem   = get_Call_mem(call);
176   ir_node *block = get_nodes_block(call);
177   ir_node *op    = get_Call_param(call, 0);
178   ir_node *irn, *exc;
179   dbg_info *dbg  = get_irn_dbg_info(call);
180
181   irn = new_rd_Alloc(dbg, current_ir_graph, block, mem, op, firm_unknown_type, stack_alloc);
182   mem = new_Proj(irn, mode_M, pn_Alloc_M);
183   exc = new_Proj(irn, mode_X, pn_Alloc_X_except);
184   irn = new_Proj(irn, get_modeP_data(), pn_Alloc_res);
185   irn = new_Tuple(1, &irn);
186
187   turn_into_tuple(call, pn_Call_max);
188   set_Tuple_pred(call, pn_Call_M_regular, mem);
189   set_Tuple_pred(call, pn_Call_X_except, exc);
190   set_Tuple_pred(call, pn_Call_T_result, irn);
191   set_Tuple_pred(call, pn_Call_M_except, mem);
192   set_Tuple_pred(call, pn_Call_P_value_res_base, new_Bad());
193
194   return 1;
195 }
196
197 #define LMAX(a, b) ((a) > (b) ? (a) : (b))
198
199 /* A mapper for mapping unsupported instructions to runtime calls. */
200 int i_mapper_RuntimeCall(ir_node *node, runtime_rt *rt) {
201   int i, j, arity, first, n_param, n_res;
202   long n_proj;
203   ir_type *mtp;
204   ir_node *mem, *bl, *call, *addr, *res_proj;
205   ir_node **in;
206   ir_graph *irg;
207   symconst_symbol sym;
208
209   /* check if the result modes match */
210   if (get_irn_mode(node) != rt->mode)
211     return 0;
212
213   arity = get_irn_arity(node);
214   if (arity <= 0)
215     return 0;
216
217   mtp     = get_entity_type(rt->ent);
218   n_param = get_method_n_params(mtp);
219   irg     = current_ir_graph;
220
221   mem = get_irn_n(node, 0);
222   if (get_irn_mode(mem) != mode_M) {
223     mem = new_r_NoMem(irg);
224     first = 0;
225   }
226   else
227     first = 1;
228
229   /* check if the modes of the predecessors match the parameter modes */
230   if (arity - first != n_param)
231     return 0;
232
233   for (i = first, j = 0; i < arity; ++i, ++j) {
234     ir_type *param_tp = get_method_param_type(mtp, j);
235     ir_node *pred = get_irn_n(node, i);
236
237     if (get_type_mode(param_tp) != get_irn_mode(pred))
238       return 0;
239   }
240
241   n_res = get_method_n_ress(mtp);
242
243   /* step 0: calculate the number of needed Proj's */
244   n_proj = 0;
245   n_proj = LMAX(n_proj, rt->mem_proj_nr + 1);
246   n_proj = LMAX(n_proj, rt->exc_proj_nr + 1);
247   n_proj = LMAX(n_proj, rt->exc_mem_proj_nr + 1);
248   n_proj = LMAX(n_proj, rt->res_proj_nr + 1);
249
250   if (n_proj > 0) {
251     if (rt->mode != mode_T) /* must be mode_T */
252       return 0;
253   }
254   else {
255     if (n_res > 0)
256       /* must match */
257       if (get_type_mode(get_method_res_type(mtp, 0)) != rt->mode)
258         return 0;
259   }
260
261   /* ok, when we are here, the number of predecessors match as well as the parameter modes */
262   bl = get_nodes_block(node);
263
264   in = NULL;
265   if (n_param > 0) {
266     NEW_ARR_A(ir_node *, in, n_param);
267     for (i = 0; i < n_param; ++i)
268       in[i] = get_irn_n(node, first + i);
269   }
270
271   /* step 1: create the call */
272   sym.entity_p = rt->ent;
273   addr = new_r_SymConst(irg, bl, sym, symconst_addr_ent);
274   call = new_rd_Call(get_irn_dbg_info(node), irg, bl, mem, addr, n_param, in, mtp);
275   set_irn_pinned(call, get_irn_pinned(node));
276
277   if (n_res > 0)
278     res_proj = new_r_Proj(irg, bl, call, mode_T, pn_Call_T_result);
279   else
280     res_proj = NULL;
281
282   if (n_proj > 0) {
283     n_proj += n_res - 1;
284
285     /* we are ready */
286     turn_into_tuple(node, n_proj);
287
288     for (i = 0; i < n_proj; ++i)
289       set_Tuple_pred(node, i, new_r_Bad(irg));
290     if (rt->mem_proj_nr >= 0)
291       set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(irg, bl, call, mode_M, pn_Call_M_regular));
292     if (get_irn_op(mem) != op_NoMem) {
293       /* Exceptions can only be handled with real memory */
294       if (rt->exc_proj_nr >= 0)
295         set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(irg, bl, call, mode_X, pn_Call_X_except));
296       if (rt->exc_mem_proj_nr >= 0)
297         set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(irg, bl, call, mode_M, pn_Call_M_except));
298     }
299
300     if (rt->res_proj_nr >= 0)
301       for (i = 0; i < n_res; ++i)
302         set_Tuple_pred(node, rt->res_proj_nr + i,
303           new_r_Proj(irg, bl, res_proj, get_type_mode(get_method_res_type(mtp, i)), i));
304     return 1;
305   }
306   else {
307     /* only one return value supported */
308     if (n_res > 0) {
309       ir_mode *mode = get_type_mode(get_method_res_type(mtp, 0));
310
311       res_proj = new_r_Proj(irg, bl, call, mode_T, pn_Call_T_result);
312       res_proj = new_r_Proj(irg, bl, res_proj, mode, 0);
313
314       exchange(node, res_proj);
315       return 1;
316     }
317   }
318   /* should not happen */
319   return 0;
320 }