4463fe74891092d3cfa13c269a3b608b6c3034c0
[libfirm] / ir / be / ia32 / ia32_intrinsics.c
1 /**
2  * This file implements the mapping of 64Bit intrinsic functions to
3  * code or library calls.
4  * @author Michael Beck
5  * $Id$
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include "config.h"
10 #endif
11
12 #include "irgmod.h"
13 #include "irop.h"
14 #include "irnode_t.h"
15 #include "ircons.h"
16 #include "irprog_t.h"
17 #include "lower_intrinsics.h"
18 #include "lower_dw.h"
19 #include "mangle.h"
20 #include "array.h"
21
22 #include "ia32_new_nodes.h"
23
24 /** The array of all intrinsics that must be mapped. */
25 static i_record *intrinsics;
26
27 /** An array to cache all entities */
28 static entity *i_ents[iro_MaxOpcode];
29
30 /*
31  * Maps all intrinsic calls that the backend support
32  * and map all instructions the backend did not support
33  * to runtime calls.
34  */
35 void ia32_handle_intrinsics(void) {
36         if (intrinsics && ARR_LEN(intrinsics) > 0)
37                 lower_intrinsics(intrinsics, ARR_LEN(intrinsics));
38 }
39
40 #define BINOP_Left_Low   0
41 #define BINOP_Left_High  1
42 #define BINOP_Right_Low  2
43 #define BINOP_Right_High 3
44
45 static void resolve_call(ir_node *call, ir_node *l_res, ir_node *h_res, ir_graph *irg, ir_node *block) {
46         ir_node *res, *in[2];
47
48         in[0] = l_res;
49         in[1] = h_res;
50         res = new_r_Tuple(irg, block, 2, in);
51
52         turn_into_tuple(call, pn_Call_max);
53         set_Tuple_pred(call, pn_Call_M_regular,        get_irg_no_mem(irg));
54         set_Tuple_pred(call, pn_Call_X_except,         get_irg_bad(irg));
55         set_Tuple_pred(call, pn_Call_T_result,         res);
56         set_Tuple_pred(call, pn_Call_M_except,         get_irg_bad(irg));
57         set_Tuple_pred(call, pn_Call_P_value_res_base, get_irg_bad(irg));
58 }
59
60 /**
61  * Map an Add (a_l, a_h, b_l, b_h)
62  */
63 static int map_Add(ir_node *call, void *ctx) {
64         ir_graph *irg        = current_ir_graph;
65         dbg_info *dbg        = get_irn_dbg_info(call);
66         ir_node  *block      = get_nodes_block(call);
67         ir_node  **params    = get_Call_param_arr(call);
68         ir_type  *method     = get_Call_type(call);
69         ir_node  *a_l        = params[BINOP_Left_Low];
70         ir_node  *a_h        = params[BINOP_Left_High];
71         ir_node  *b_l        = params[BINOP_Right_Low];
72         ir_node  *b_h        = params[BINOP_Right_High];
73         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
74         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
75         ir_node  *l_res, *h_res;
76
77         /* l_res = a_l + b_l */
78         l_res = new_rd_ia32_l_Add(dbg, irg, block, a_l, b_l, l_res_mode);
79
80         /* h_res = a_h + b_h + carry */
81         h_res = new_rd_ia32_l_AddC(dbg, irg, block, a_h, b_h, h_res_mode);
82
83         resolve_call(call, l_res, h_res, irg, block);
84         return 1;
85 }
86
87 /**
88  * Map a Sub (a_l, a_h, b_l, b_h)
89  */
90 static int map_Sub(ir_node *call, void *ctx) {
91         ir_graph *irg        = current_ir_graph;
92         dbg_info *dbg        = get_irn_dbg_info(call);
93         ir_node  *block      = get_nodes_block(call);
94         ir_node  **params    = get_Call_param_arr(call);
95         ir_type  *method     = get_Call_type(call);
96         ir_node  *a_l        = params[BINOP_Left_Low];
97         ir_node  *a_h        = params[BINOP_Left_High];
98         ir_node  *b_l        = params[BINOP_Right_Low];
99         ir_node  *b_h        = params[BINOP_Right_High];
100         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
101         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
102         ir_node  *l_res, *h_res;
103
104         /* l_res = a_l - b_l */
105         l_res = new_rd_ia32_l_Sub(dbg, irg, block, a_l, b_l, l_res_mode);
106
107         /* h_res = a_h - b_h - carry */
108         h_res = new_rd_ia32_l_SubC(dbg, irg, block, a_h, b_h, h_res_mode);
109
110         resolve_call(call, l_res, h_res, irg, block);
111
112         return 1;
113 }
114
115 /**
116  * Map a Shl (a_l, a_h, count)
117  */
118 static int map_Shl(ir_node *call, void *ctx) {
119         ir_graph *irg        = current_ir_graph;
120         dbg_info *dbg        = get_irn_dbg_info(call);
121         ir_node  *block      = get_nodes_block(call);
122         ir_node  **params    = get_Call_param_arr(call);
123         ir_type  *method     = get_Call_type(call);
124         ir_node  *a_l        = params[BINOP_Left_Low];
125         ir_node  *a_h        = params[BINOP_Left_High];
126         ir_node  *cnt        = params[BINOP_Right_Low];
127         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
128         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
129         ir_node  *l_res, *h_res;
130
131         /* h_res = SHLD a_h, a_l, cnt */
132         l_res = new_rd_ia32_l_ShlD(dbg, irg, block, a_h, a_l, cnt, l_res_mode);
133
134         /* l_res = SHL a_l, cnt */
135         h_res = new_rd_ia32_l_Shl(dbg, irg, block, a_l, cnt, h_res_mode);
136
137         resolve_call(call, l_res, h_res, irg, block);
138
139         return 1;
140 }
141
142 /**
143  * Map a Shr (a_l, a_h, count)
144  */
145 static int map_Shr(ir_node *call, void *ctx) {
146         ir_graph *irg        = current_ir_graph;
147         dbg_info *dbg        = get_irn_dbg_info(call);
148         ir_node  *block      = get_nodes_block(call);
149         ir_node  **params    = get_Call_param_arr(call);
150         ir_type  *method     = get_Call_type(call);
151         ir_node  *a_l        = params[BINOP_Left_Low];
152         ir_node  *a_h        = params[BINOP_Left_High];
153         ir_node  *cnt        = params[BINOP_Right_Low];
154         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
155         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
156         ir_node  *l_res, *h_res;
157
158         /* l_res = SHRD a_l, a_h, cnt */
159         l_res = new_rd_ia32_l_ShrD(dbg, irg, block, a_l, a_h, cnt, l_res_mode);
160
161         /* h_res = SHR a_h, cnt */
162         h_res = new_rd_ia32_l_Shr(dbg, irg, block, a_h, cnt, h_res_mode);
163
164         resolve_call(call, l_res, h_res, irg, block);
165
166         return 1;
167 }
168
169 /**
170  * Map a Shrs (a_l, a_h, count)
171  */
172 static int map_Shrs(ir_node *call, void *ctx) {
173         ir_graph *irg        = current_ir_graph;
174         dbg_info *dbg        = get_irn_dbg_info(call);
175         ir_node  *block      = get_nodes_block(call);
176         ir_node  **params    = get_Call_param_arr(call);
177         ir_type  *method     = get_Call_type(call);
178         ir_node  *a_l        = params[BINOP_Left_Low];
179         ir_node  *a_h        = params[BINOP_Left_High];
180         ir_node  *cnt        = params[BINOP_Right_Low];
181         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
182         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
183         ir_node  *l_res, *h_res;
184
185         /* l_res = SHRD a_l, a_h, cnt */
186         l_res = new_rd_ia32_l_ShrD(dbg, irg, block, a_l, a_h, cnt, l_res_mode);
187
188         /* h_res = SAR a_h, cnt */
189         h_res = new_rd_ia32_l_Shrs(dbg, irg, block, a_h, cnt, h_res_mode);
190
191         resolve_call(call, l_res, h_res, irg, block);
192
193         return 1;
194 }
195
196 /**
197  * Map a Mul (a_l, a_h, b_l, b_h)
198  */
199 static int map_Mul(ir_node *call, void *ctx) {
200         ir_graph *irg        = current_ir_graph;
201         dbg_info *dbg        = get_irn_dbg_info(call);
202         ir_node  *block      = get_nodes_block(call);
203         ir_node  **params    = get_Call_param_arr(call);
204         ir_type  *method     = get_Call_type(call);
205         ir_node  *a_l        = params[BINOP_Left_Low];
206         ir_node  *a_h        = params[BINOP_Left_High];
207         ir_node  *b_l        = params[BINOP_Right_Low];
208         ir_node  *b_h        = params[BINOP_Right_High];
209         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
210         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
211         ir_node  *l_res, *h_res, *mul, *pEDX, *add;
212
213         /*
214                 EDX:EAX = a_l * b_l
215                 l_res   = EAX
216
217                 t1 = b_l * a_h
218                 t2 = t1 + EDX
219                 t3 = a_l * b_h
220                 h_res = t2 + t3
221         */
222         mul   = new_rd_ia32_l_MulS(dbg, irg, block, a_l, b_l);
223         pEDX  = new_rd_Proj(dbg, irg, block, mul, l_res_mode, pn_ia32_l_MulS_EDX);
224         l_res = new_rd_Proj(dbg, irg, block, mul, l_res_mode, pn_ia32_l_MulS_EAX);
225
226         mul   = new_rd_ia32_l_Mul(dbg, irg, block, a_h, b_l, h_res_mode);
227         add   = new_rd_ia32_l_Add(dbg, irg, block, mul, pEDX, h_res_mode);
228         mul   = new_rd_ia32_l_Mul(dbg, irg, block, a_l, b_h, h_res_mode);
229         h_res = new_rd_ia32_l_Add(dbg, irg, block, add, mul, h_res_mode);
230
231         resolve_call(call, l_res, h_res, irg, block);
232
233         return 1;
234 }
235
236 /**
237  * Map a Minus (a_l, a_h)
238  */
239 static int map_Minus(ir_node *call, void *ctx) {
240         ir_graph *irg        = current_ir_graph;
241         dbg_info *dbg        = get_irn_dbg_info(call);
242         ir_node  *block      = get_nodes_block(call);
243         ir_node  **params    = get_Call_param_arr(call);
244         ir_type  *method     = get_Call_type(call);
245         ir_node  *a_l        = params[BINOP_Left_Low];
246         ir_node  *a_h        = params[BINOP_Left_High];
247         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
248         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
249         ir_node  *l_res, *h_res, *cnst;
250
251         /* l_res = 0 - a_l */
252         l_res = new_rd_ia32_l_Minus(dbg, irg, block, a_l, l_res_mode);
253
254         /* h_res = 0 - a_h - carry */
255
256         /* too bad: we need 0 in a register here */
257         cnst  = new_Const_long(h_res_mode, 0);
258         h_res = new_rd_ia32_l_SubC(dbg, irg, block, cnst, a_h, h_res_mode);
259
260         resolve_call(call, l_res, h_res, irg, block);
261
262         return 1;
263 }
264
265 /**
266  * Map a Abs (a_l, a_h)
267  */
268 static int map_Abs(ir_node *call, void *ctx) {
269         ir_graph *irg        = current_ir_graph;
270         dbg_info *dbg        = get_irn_dbg_info(call);
271         ir_node  *block      = get_nodes_block(call);
272         ir_node  **params    = get_Call_param_arr(call);
273         ir_type  *method     = get_Call_type(call);
274         ir_node  *a_l        = params[BINOP_Left_Low];
275         ir_node  *a_h        = params[BINOP_Left_High];
276         ir_mode  *l_res_mode = get_type_mode(get_method_res_type(method, 0));
277         ir_mode  *h_res_mode = get_type_mode(get_method_res_type(method, 1));
278         ir_node  *l_res, *h_res, *sign, *sub_l, *sub_h;
279
280         /*
281                 Code inspired by gcc output :) (although gcc doubles the
282                 operation for t1 as t2 and uses t1 for operations with low part
283                 and t2 for operations with high part which is actually unnecessary
284                 because t1 and t2 represent the same value)
285
286                 t1    = SHRS a_h, 31
287                 t2    = a_l ^ t1
288                 t3    = a_h ^ t1
289                 l_res = t2 - t1
290                 h_res = t3 - t1 - carry
291
292         */
293
294         sign  = new_rd_ia32_l_Shrs(dbg, irg, block, a_h, new_Const_long(h_res_mode, 31), h_res_mode);
295         sub_l = new_rd_ia32_l_Eor(dbg, irg, block, a_l, sign, l_res_mode);
296         sub_h = new_rd_ia32_l_Eor(dbg, irg, block, a_h, sign, h_res_mode);
297         l_res = new_rd_ia32_l_Sub(dbg, irg, block, sub_l, sign, l_res_mode);
298         h_res = new_rd_ia32_l_SubC(dbg, irg, block, sub_h, sign, l_res_mode);
299
300         resolve_call(call, l_res, h_res, irg, block);
301
302         return 1;
303 }
304
305 /* Ia32 implementation of intrinsic mapping. */
306 entity *ia32_create_intrinsic_fkt(ir_type *method, const ir_op *op,
307                                   const ir_mode *imode, const ir_mode *omode,
308                                   void *context)
309 {
310         i_record      elt;
311         entity        **ent = NULL;
312         i_mapper_func mapper;
313
314         if (! intrinsics)
315                 intrinsics = NEW_ARR_F(i_record, 0);
316
317         switch (get_op_code(op)) {
318         case iro_Add:
319                 ent    = &i_ents[iro_Add];
320                 mapper = map_Add;
321                 break;
322         case iro_Sub:
323                 ent    = &i_ents[iro_Sub];
324                 mapper = map_Sub;
325                 break;
326         case iro_Shl:
327                 ent    = &i_ents[iro_Shl];
328                 mapper = map_Shl;
329                 break;
330         case iro_Shr:
331                 ent    = &i_ents[iro_Shr];
332                 mapper = map_Shr;
333                 break;
334         case iro_Shrs:
335                 ent    = &i_ents[iro_Shrs];
336                 mapper = map_Shrs;
337                 break;
338         case iro_Mul:
339                 ent    = &i_ents[iro_Mul];
340                 mapper = map_Mul;
341                 break;
342         case iro_Minus:
343                 ent    = &i_ents[iro_Minus];
344                 mapper = map_Minus;
345                 break;
346         case iro_Abs:
347                 ent    = &i_ents[iro_Abs];
348                 mapper = map_Abs;
349                 break;
350         default:
351                 fprintf(stderr, "FIXME: unhandled op for ia32 intrinsic function %s\n", get_id_str(op->name));
352                 return def_create_intrinsic_fkt(method, op, imode, omode, context);
353         }
354
355         if (ent && ! *ent) {
356 #define IDENT(s)  new_id_from_chars(s, sizeof(s)-1)
357
358                 ident *id = mangle(IDENT("L"), get_op_ident(op));
359                 *ent = new_entity(get_glob_type(), id, method);
360         }
361
362         elt.i_call.kind     = INTRINSIC_CALL;
363         elt.i_call.i_ent    = *ent;
364         elt.i_call.i_mapper = mapper;
365         elt.i_call.ctx      = context;
366         elt.i_call.link     = NULL;
367
368         ARR_APP1(i_record, intrinsics, elt);
369         return *ent;
370 }