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