fix a few errors and warnings in the new pass code; improve some comments
[libfirm] / ir / lower / lower_intrinsics.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   lowering of Calls of intrinsic functions
23  * @author  Michael Beck
24  * @version $Id$
25  */
26
27 #include "config.h"
28
29 #include "lowering.h"
30 #include "irop_t.h"
31 #include "irprog_t.h"
32 #include "irnode_t.h"
33 #include "irprog_t.h"
34 #include "irgwalk.h"
35 #include "ircons.h"
36 #include "irgmod.h"
37 #include "irgopt.h"
38 #include "trouts.h"
39 #include "irvrfy.h"
40 #include "pmap.h"
41 #include "array_t.h"
42 #include "irpass_t.h"
43 #include "iropt_dbg.h"
44 #include "error.h"
45
46 /** Walker environment. */
47 typedef struct _walker_env {
48         pmap     *c_map;              /**< The intrinsic call map. */
49         unsigned nr_of_intrinsics;    /**< statistics */
50         i_instr_record **i_map;       /**< The intrinsic instruction map. */
51 } walker_env_t;
52
53 /**
54  * walker: call all mapper functions
55  */
56 static void call_mapper(ir_node *node, void *env) {
57         walker_env_t *wenv = env;
58         ir_op *op = get_irn_op(node);
59
60         if (op == op_Call) {
61                 ir_node *symconst;
62                 pmap_entry *p;
63                 const i_call_record *r;
64                 ir_entity *ent;
65
66                 symconst = get_Call_ptr(node);
67                 if (! is_Global(symconst))
68                         return;
69
70                 ent = get_Global_entity(symconst);
71                 p   = pmap_find(wenv->c_map, ent);
72
73                 if (p) {
74                         r = p->value;
75                         wenv->nr_of_intrinsics += r->i_mapper(node, r->ctx) ? 1 : 0;
76                 }
77         } else {
78                 if (op->code < (unsigned) 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 }  /* call_mapper */
91
92 /* Go through all graphs and map calls to intrinsic functions. */
93 unsigned lower_intrinsics(i_record *list, int length, int part_block_used) {
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                 } else {
110                         ir_op *op = list[i].i_instr.op;
111                         assert(op->code < (unsigned) ARR_LEN(i_map));
112
113                         list[i].i_instr.link = i_map[op->code];
114                         i_map[op->code] = &list[i].i_instr;
115                 }
116         }
117
118         wenv.c_map = c_map;
119         wenv.i_map = i_map;
120
121         for (i = get_irp_n_irgs() - 1; i >= 0; --i) {
122                 irg = get_irp_irg(i);
123
124                 if (part_block_used) {
125                         ir_reserve_resources(irg, IR_RESOURCE_IRN_LINK | IR_RESOURCE_PHI_LIST);
126                         collect_phiprojs(irg);
127                 }
128
129                 wenv.nr_of_intrinsics = 0;
130                 irg_walk_graph(irg, NULL, call_mapper, &wenv);
131
132                 if (part_block_used)
133                         ir_free_resources(irg, IR_RESOURCE_IRN_LINK | IR_RESOURCE_PHI_LIST);
134
135                 if (wenv.nr_of_intrinsics > 0) {
136                         /* Changes detected: we might have added/removed nodes. */
137                         set_irg_outs_inconsistent(irg);
138                         set_irg_callee_info_state(irg, irg_callee_info_inconsistent);
139
140                         /* Exception control flow might have changed / new block might have added. */
141                         set_irg_doms_inconsistent(irg);
142                         set_irg_extblk_inconsistent(irg);
143                         set_irg_loopinfo_inconsistent(irg);
144
145                         /* Calls might be removed/added. */
146                         set_trouts_inconsistent();
147
148                         /* verify here */
149                         irg_verify(irg, VRFY_NORMAL);
150
151                         /* Optimize it, tuple might be created. */
152                         optimize_graph_df(irg);
153
154                         nr_of_intrinsics += wenv.nr_of_intrinsics;
155                 }
156         }
157         pmap_destroy(c_map);
158
159         return nr_of_intrinsics;
160 }  /* lower_intrinsics */
161
162 struct pass_t {
163         ir_prog_pass_t pass;
164
165         int part_block_used;
166         int      length;
167         i_record list[1];
168 };
169
170 /**
171  * Wrapper for running lower_intrinsics() as an ir_prog pass.
172  */
173 static int pass_wrapper(ir_prog *irp, void *context)
174 {
175         struct pass_t *pass = context;
176         (void) irp; /* TODO: set current irp, or remove parameter */
177         lower_intrinsics(pass->list, pass->length, pass->part_block_used);
178         /* probably this pass should not run again */
179         return 0;
180 }  /* pass_wrapper */
181
182 /**
183  * Creates an ir_prog pass for lower_intrinsics.
184  *
185  * @param name             the name of this pass or NULL
186  * @param list             an array of intrinsic map records
187  * @param length           the length of the array
188  * @param part_block_used  set to true if part_block() must be using during lowering
189  */
190 ir_prog_pass_t *lower_intrinsics_pass(
191         const char *name,
192         i_record *list, int length, int part_block_used)
193 {
194         struct pass_t *pass = xmalloc(sizeof(*pass) + (length-1) * sizeof(pass->list[0]));
195
196         memset(&pass->pass, 0, sizeof(pass->pass));
197         pass->pass.kind          = k_ir_prog_pass;
198         pass->pass.run_on_irprog = pass_wrapper;
199         pass->pass.context       = pass;
200         pass->pass.name          = name ? name : "lower_intrinsics";
201
202         INIT_LIST_HEAD(&pass->pass.list);
203
204         memcpy(pass->list, list, sizeof(list[0]) * length);
205         pass->length          = length;
206         pass->part_block_used = part_block_used;
207
208         return &pass->pass;
209 }  /* lower_intrinsics_pass*/
210
211 /**
212  * Helper function, replace the call by the given node.
213  *
214  * @param irn      the result node
215  * @param call     the call to replace
216  * @param mem      the new mem result
217  * @param reg_jmp  new regular control flow, if NULL, a Jmp will be used
218  * @param exc_jmp  new exception control flow, if reg_jmp == NULL, a Bad will be used
219  */
220 static void replace_call(ir_node *irn, ir_node *call, ir_node *mem, ir_node *reg_jmp, ir_node *exc_jmp) {
221         ir_node *block = get_nodes_block(call);
222
223         if (reg_jmp == NULL) {
224
225                 /* Beware: do we need here a protection against CSE? Better we do it. */
226                 int old_cse = get_opt_cse();
227                 set_opt_cse(0);
228                 reg_jmp = new_r_Jmp(block);
229                 set_opt_cse(old_cse);
230                 exc_jmp = new_Bad();
231         }
232         irn = new_r_Tuple(block, 1, &irn);
233
234         turn_into_tuple(call, pn_Call_max);
235         set_Tuple_pred(call, pn_Call_M_regular, mem);
236         set_Tuple_pred(call, pn_Call_X_regular, reg_jmp);
237         set_Tuple_pred(call, pn_Call_X_except, exc_jmp);
238         set_Tuple_pred(call, pn_Call_T_result, irn);
239         set_Tuple_pred(call, pn_Call_M_except, mem);
240         set_Tuple_pred(call, pn_Call_P_value_res_base, new_Bad());
241 }  /* replace_call */
242
243 /* A mapper for the integer abs. */
244 int i_mapper_abs(ir_node *call, void *ctx) {
245         ir_node *mem   = get_Call_mem(call);
246         ir_node *block = get_nodes_block(call);
247         ir_node *op    = get_Call_param(call, 0);
248         ir_node *irn;
249         dbg_info *dbg  = get_irn_dbg_info(call);
250         (void) ctx;
251
252         irn = new_rd_Abs(dbg, block, op, get_irn_mode(op));
253         DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_ABS);
254         replace_call(irn, call, mem, NULL, NULL);
255         return 1;
256 }  /* i_mapper_abs */
257
258 /* A mapper for the integer bswap. */
259 int i_mapper_bswap(ir_node *call, void *ctx) {
260         ir_node *mem   = get_Call_mem(call);
261         ir_node *block = get_nodes_block(call);
262         ir_node *op    = get_Call_param(call, 0);
263         ir_type *tp    = get_Call_type(call);
264         dbg_info *dbg  = get_irn_dbg_info(call);
265         ir_node *irn;
266         (void) ctx;
267
268         irn = new_rd_Builtin(dbg, block, get_irg_no_mem(current_ir_graph), 1, &op, ir_bk_bswap, tp);
269         set_irn_pinned(irn, op_pin_state_floats);
270         DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_ABS);
271         irn = new_r_Proj(block, irn, get_irn_mode(op), pn_Builtin_1_result);
272         replace_call(irn, call, mem, NULL, NULL);
273         return 1;
274 }  /* i_mapper_bswap */
275
276 /* A mapper for the alloca() function. */
277 int i_mapper_alloca(ir_node *call, void *ctx) {
278         ir_node *mem   = get_Call_mem(call);
279         ir_node *block = get_nodes_block(call);
280         ir_node *op    = get_Call_param(call, 0);
281         ir_node *irn, *exc, *no_exc;
282         dbg_info *dbg  = get_irn_dbg_info(call);
283         (void) ctx;
284
285         if (mode_is_signed(get_irn_mode(op))) {
286                 ir_mode *mode = get_irn_mode(op);
287                 mode = find_unsigned_mode(mode);
288                 if (mode == NULL) {
289                         panic("Cannot find unsigned mode for %M", mode);
290                 }
291                 op = new_rd_Conv(dbg, block, op, mode);
292         }
293
294         irn    = new_rd_Alloc(dbg, block, mem, op, firm_unknown_type, stack_alloc);
295         mem    = new_rd_Proj(dbg, block, irn, mode_M, pn_Alloc_M);
296         no_exc = new_rd_Proj(dbg, block, irn, mode_X, pn_Alloc_X_regular);
297         exc    = new_rd_Proj(dbg, block, irn, mode_X, pn_Alloc_X_except);
298         irn    = new_rd_Proj(dbg, block, irn, get_modeP_data(), pn_Alloc_res);
299
300         DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_ALLOCA);
301         replace_call(irn, call, mem, no_exc, exc);
302         return 1;
303 }  /* i_mapper_alloca */
304
305 /* A mapper for the floating point sqrt. */
306 int i_mapper_sqrt(ir_node *call, void *ctx) {
307         ir_node *mem;
308         tarval *tv;
309         ir_node *op = get_Call_param(call, 0);
310         (void) ctx;
311
312         if (!is_Const(op))
313                 return 0;
314
315         tv = get_Const_tarval(op);
316         if (! tarval_is_null(tv) && !tarval_is_one(tv))
317                 return 0;
318
319         mem = get_Call_mem(call);
320
321         /* sqrt(0) = 0, sqrt(1) = 1 */
322         DBG_OPT_ALGSIM0(call, op, FS_OPT_RTS_SQRT);
323         replace_call(op, call, mem, NULL, NULL);
324         return 1;
325 }  /* i_mapper_sqrt */
326
327 /* A mapper for the floating point cbrt. */
328 int i_mapper_cbrt(ir_node *call, void *ctx) {
329         ir_node *mem;
330         tarval *tv;
331         ir_node *op = get_Call_param(call, 0);
332         (void) ctx;
333
334         if (!is_Const(op))
335                 return 0;
336
337         tv = get_Const_tarval(op);
338         if (! tarval_is_null(tv) && !tarval_is_one(tv) && !tarval_is_minus_one(tv))
339                 return 0;
340
341         mem = get_Call_mem(call);
342
343         /* cbrt(0) = 0, cbrt(1) = 1, cbrt(-1) = -1 */
344         DBG_OPT_ALGSIM0(call, op, FS_OPT_RTS_CBRT);
345         replace_call(op, call, mem, NULL, NULL);
346         return 1;
347 }  /* i_mapper_cbrt */
348
349 /* A mapper for the floating point pow. */
350 int i_mapper_pow(ir_node *call, void *ctx) {
351         dbg_info *dbg;
352         ir_node *mem;
353         ir_node *left  = get_Call_param(call, 0);
354         ir_node *right = get_Call_param(call, 1);
355         ir_node *block = get_nodes_block(call);
356         ir_node *irn, *reg_jmp = NULL, *exc_jmp = NULL;
357         (void) ctx;
358
359         if (is_Const(left) && is_Const_one(left)) {
360                 /* pow (1.0, x) = 1.0 */
361                 irn = left;
362         } else if (is_Const(right)) {
363                 tarval *tv = get_Const_tarval(right);
364                 if (tarval_is_null(tv)) {
365                         /* pow(x, 0.0) = 1.0 */
366                         ir_mode *mode = get_tarval_mode(tv);
367                         irn = new_Const(get_mode_one(mode));
368                 } else if (tarval_is_one(tv)) {
369                         /* pow(x, 1.0) = x */
370                         irn = left;
371                 } else if (tarval_is_minus_one(tv)) {
372                         /* pow(x, -1.0) = 1/x */
373                         irn = NULL;
374                 } else
375                         return 0;
376         } else {
377                 return 0;
378         }
379
380         mem = get_Call_mem(call);
381         dbg = get_irn_dbg_info(call);
382
383         if (irn == NULL) {
384                 ir_mode *mode = get_irn_mode(left);
385                 ir_node *quot;
386
387                 irn  = new_Const(get_mode_one(mode));
388                 quot = new_rd_Quot(dbg, block, mem, irn, left, mode, op_pin_state_pinned);
389                 mem  = new_r_Proj(block, quot, mode_M, pn_Quot_M);
390                 irn  = new_r_Proj(block, quot, mode, pn_Quot_res);
391                 reg_jmp = new_r_Proj(block, quot, mode_X, pn_Quot_X_regular);
392                 exc_jmp = new_r_Proj(block, quot, mode_X, pn_Quot_X_except);
393         }
394         DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_POW);
395         replace_call(irn, call, mem, reg_jmp, exc_jmp);
396         return 1;
397 }  /* i_mapper_pow */
398
399 /* A mapper for the floating point exp. */
400 int i_mapper_exp(ir_node *call, void *ctx) {
401         ir_node *val  = get_Call_param(call, 0);
402         (void) ctx;
403
404         if (is_Const(val) && is_Const_null(val)) {
405                 /* exp(0.0) = 1.0 */
406                 ir_mode *mode  = get_irn_mode(val);
407                 ir_node *irn   = new_Const(get_mode_one(mode));
408                 ir_node *mem   = get_Call_mem(call);
409                 DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_EXP);
410                 replace_call(irn, call, mem, NULL, NULL);
411                 return 1;
412         }
413         return 0;
414 }  /* i_mapper_exp */
415
416 /**
417  * A mapper for mapping f(0.0) to 0.0.
418  */
419 static int i_mapper_zero_to_zero(ir_node *call, void *ctx, int reason) {
420         ir_node *val  = get_Call_param(call, 0);
421         (void) ctx;
422
423         if (is_Const(val) && is_Const_null(val)) {
424                 /* f(0.0) = 0.0 */
425                 ir_node *mem = get_Call_mem(call);
426                 DBG_OPT_ALGSIM0(call, val, reason);
427                 replace_call(val, call, mem, NULL, NULL);
428                 return 1;
429         }
430         return 0;
431 }  /* i_mapper_zero_to_zero */
432
433 /**
434  * A mapper for mapping f(1.0) to 0.0.
435  */
436 static int i_mapper_one_to_zero(ir_node *call, void *ctx, int reason) {
437         ir_node *val  = get_Call_param(call, 0);
438         (void) ctx;
439
440         if (is_Const(val) && is_Const_one(val)) {
441                 /* acos(1.0) = 0.0 */
442                 ir_mode *mode  = get_irn_mode(val);
443                 ir_node *irn   = new_Const(get_mode_null(mode));
444                 ir_node *mem   = get_Call_mem(call);
445                 DBG_OPT_ALGSIM0(call, irn, reason);
446                 replace_call(irn, call, mem, NULL, NULL);
447                 return 1;
448         }
449         return 0;
450 }  /* i_mapper_one_to_zero */
451
452 /**
453  * A mapper for mapping a functions with the following characteristics:
454  * f(-x)  = f(x).
455  * f(0.0) = 1.0
456  */
457 static int i_mapper_symmetric_zero_to_one(ir_node *call, void *ctx, int reason) {
458         ir_node *val  = get_Call_param(call, 0);
459         (void) ctx;
460
461         if (is_strictConv(val)) {
462                 ir_node *op = get_Conv_op(val);
463                 if (is_Minus(op)) {
464                         /* f(-x) = f(x) with strictConv */
465                         ir_node *block = get_nodes_block(call);
466                         ir_mode *mode  = get_irn_mode(val);
467                         dbg_info *dbg  = get_irn_dbg_info(val);
468
469                         op = get_Minus_op(op);
470                         val = new_rd_Conv(dbg, block, op, mode);
471                         if (is_Conv(val)) {
472                                 /* still a Conv ? */
473                                 set_Conv_strict(val, 1);
474                         }
475                         DBG_OPT_ALGSIM2(call, op, call, FS_OPT_RTS_SYMMETRIC);
476                         set_Call_param(call, 0, val);
477                 }
478         } else if (is_Minus(val)) {
479                 /* f(-x) = f(x) */
480                 val = get_Minus_op(val);
481                 DBG_OPT_ALGSIM2(call, val, call, FS_OPT_RTS_SYMMETRIC);
482                 set_Call_param(call, 0, val);
483         }
484
485         if (is_Const(val) && is_Const_null(val)) {
486                 /* f(0.0) = 1.0 */
487                 ir_mode *mode  = get_irn_mode(val);
488                 ir_node *irn   = new_Const(get_mode_one(mode));
489                 ir_node *mem   = get_Call_mem(call);
490                 DBG_OPT_ALGSIM0(call, irn, reason);
491                 replace_call(irn, call, mem, NULL, NULL);
492                 return 1;
493         }
494         return 0;
495 }  /* i_mapper_symmetric_zero_to_one */
496
497 /* A mapper for the floating point log. */
498 int i_mapper_log(ir_node *call, void *ctx) {
499         /* log(1.0) = 0.0 */
500         return i_mapper_one_to_zero(call, ctx, FS_OPT_RTS_LOG);
501 }  /* i_mapper_log */
502
503 /* A mapper for the floating point sin. */
504 int i_mapper_sin(ir_node *call, void *ctx) {
505         /* sin(0.0) = 0.0 */
506         return i_mapper_zero_to_zero(call, ctx, FS_OPT_RTS_SIN);
507 }  /* i_mapper_sin */
508
509 /* A mapper for the floating point cos. */
510 int i_mapper_cos(ir_node *call, void *ctx) {
511         /* cos(0.0) = 1.0, cos(-x) = x */
512         return i_mapper_symmetric_zero_to_one(call, ctx, FS_OPT_RTS_COS);
513 }  /* i_mapper_cos */
514
515 /* A mapper for the floating point tan. */
516 int i_mapper_tan(ir_node *call, void *ctx) {
517         /* tan(0.0) = 0.0 */
518         return i_mapper_zero_to_zero(call, ctx, FS_OPT_RTS_TAN);
519 }  /* i_mapper_tan */
520
521 /* A mapper for the floating point asin. */
522 int i_mapper_asin(ir_node *call, void *ctx) {
523         /* asin(0.0) = 0.0 */
524         return i_mapper_zero_to_zero(call, ctx, FS_OPT_RTS_ASIN);
525 }  /* i_mapper_asin */
526
527 /* A mapper for the floating point acos. */
528 int i_mapper_acos(ir_node *call, void *ctx) {
529         /* acos(1.0) = 0.0 */
530         return i_mapper_one_to_zero(call, ctx, FS_OPT_RTS_ACOS);
531 }  /* i_mapper_acos */
532
533 /* A mapper for the floating point atan. */
534 int i_mapper_atan(ir_node *call, void *ctx) {
535         /* atan(0.0) = 0.0 */
536         return i_mapper_zero_to_zero(call, ctx, FS_OPT_RTS_ATAN);
537 }  /* i_mapper_atan */
538
539 /* A mapper for the floating point sinh. */
540 int i_mapper_sinh(ir_node *call, void *ctx) {
541         /* sinh(0.0) = 0.0 */
542         return i_mapper_zero_to_zero(call, ctx, FS_OPT_RTS_SINH);
543 }  /* i_mapper_sinh */
544
545 /* A mapper for the floating point cosh. */
546 int i_mapper_cosh(ir_node *call, void *ctx) {
547         /* cosh(0.0) = 1.0, cosh(-x) = x */
548         return i_mapper_symmetric_zero_to_one(call, ctx, FS_OPT_RTS_COSH);
549 }  /* i_mapper_cosh */
550
551 /* A mapper for the floating point tanh. */
552 int i_mapper_tanh(ir_node *call, void *ctx) {
553         /* tanh(0.0) = 0.0 */
554         return i_mapper_zero_to_zero(call, ctx, FS_OPT_RTS_TANH);
555 }  /* i_mapper_tanh */
556
557 /**
558  * Return the const entity that is accessed through the pointer ptr or
559  * NULL if there is no entity (or the entity is not constant).
560  *
561  * @param ptr  the pointer
562  */
563 static ir_entity *get_const_entity(ir_node *ptr) {
564         /* FIXME: this cannot handle constant strings inside struct initializers yet */
565         if (is_Global(ptr)) {
566                 ir_entity *ent = get_Global_entity(ptr);
567
568                 if (get_entity_variability(ent) == variability_constant) {
569                         /* a constant entity */
570                         return ent;
571                 }
572         }
573         return NULL;
574 }  /* get_const_entity */
575
576 /**
577  * Calculate the value of strlen if possible.
578  *
579  * @param ent     the entity
580  * @param res_tp  the result type
581  *
582  * @return a Const node containing the strlen() result or NULL
583  *         if the evaluation fails
584  */
585 static ir_node *eval_strlen(ir_entity *ent, ir_type *res_tp) {
586         ir_type *tp = get_entity_type(ent);
587         ir_mode *mode;
588         int     i, n, len = -1;
589
590         if (! is_Array_type(tp))
591                 return NULL;
592         tp = get_array_element_type(tp);
593         if (! is_Primitive_type(tp))
594                 return NULL;
595         mode = get_type_mode(tp);
596
597         /* FIXME: This is too restrict, as the type char might be more the 8bits */
598         if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
599                 return NULL;
600
601         n = get_compound_ent_n_values(ent);
602         for (i = 0; i < n; ++i) {
603                 ir_node *irn = get_compound_ent_value(ent, i);
604
605                 if (! is_Const(irn))
606                         return NULL;
607
608                 if (is_Const_null(irn)) {
609                         /* found the length */
610                         len = i;
611                         break;
612                 }
613         }
614         if (len >= 0) {
615                 tarval *tv = new_tarval_from_long(len, get_type_mode(res_tp));
616                 return new_Const_type(tv, res_tp);
617         }
618         return NULL;
619 }  /* eval_strlen */
620
621 /* A mapper for strlen */
622 int i_mapper_strlen(ir_node *call, void *ctx) {
623         ir_node *s     = get_Call_param(call, 0);
624         ir_entity *ent = get_const_entity(s);
625
626         (void) ctx;
627
628         /* FIXME: this cannot handle constant strings inside struct initializers yet */
629         if (ent != NULL) {
630                 /* a constant entity */
631                 ir_type *tp = get_Call_type(call);
632                 ir_node *irn;
633
634                 tp  = get_method_res_type(tp, 0);
635                 irn = eval_strlen(ent, tp);
636
637                 if (irn) {
638                         ir_node *mem = get_Call_mem(call);
639                         DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_STRLEN);
640                         replace_call(irn, call, mem, NULL, NULL);
641                         return 1;
642                 }
643         }
644         return 0;
645 }  /* i_mapper_strlen */
646
647 /**
648  * Calculate the value of strlen if possible.
649  *
650  * @param left    the left entity
651  * @param right   the right entity
652  * @param res_tp  the result type
653  *
654  * @return a Const node containing the strcmp() result or NULL
655  *         if the evaluation fails
656  */
657 static ir_node *eval_strcmp(ir_entity *left, ir_entity *right, ir_type *res_tp) {
658         ir_type *tp;
659         ir_mode *mode;
660         int     i, n, n_r, res;
661
662         tp = get_entity_type(left);
663         if (! is_Array_type(tp))
664                 return NULL;
665         tp = get_array_element_type(tp);
666         if (! is_Primitive_type(tp))
667                 return NULL;
668         mode = get_type_mode(tp);
669
670         /* FIXME: This is too restrict, as the type char might be more the 8bits */
671         if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
672                 return NULL;
673
674         tp = get_entity_type(right);
675         if (! is_Array_type(tp))
676                 return NULL;
677         tp = get_array_element_type(tp);
678         if (! is_Primitive_type(tp))
679                 return NULL;
680         mode = get_type_mode(tp);
681
682         /* FIXME: This is too restrict, as the type char might be more the 8bits */
683         if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
684                 return NULL;
685
686         n   = get_compound_ent_n_values(left);
687         n_r = get_compound_ent_n_values(right);
688         if (n_r < n)
689                 n = n_r;
690         for (i = 0; i < n; ++i) {
691                 ir_node *irn;
692                 long v_l, v_r;
693                 tarval *tv;
694
695                 irn = get_compound_ent_value(left, i);
696                 if (! is_Const(irn))
697                         return NULL;
698                 tv = get_Const_tarval(irn);
699                 v_l = get_tarval_long(tv);
700
701                 irn = get_compound_ent_value(right, i);
702                 if (! is_Const(irn))
703                         return NULL;
704                 tv = get_Const_tarval(irn);
705                 v_r = get_tarval_long(tv);
706
707                 if (v_l < v_r) {
708                         res = -1;
709                         break;
710                 }
711                 if (v_l > v_r) {
712                         res = +1;
713                         break;
714                 }
715
716                 if (v_l == 0) {
717                         res = 0;
718                         break;
719                 }
720         }
721         if (i < n) {
722                 /* we found an end */
723                 tarval *tv = new_tarval_from_long(res, get_type_mode(res_tp));
724                 return new_Const_type(tv, res_tp);
725         }
726         return NULL;
727 }  /* eval_strcmp */
728
729 /**
730  * Checks if an entity represents the empty string.
731  *
732  * @param ent     the entity
733  *
734  * @return non-zero if ent represents the empty string
735  */
736 static int is_empty_string(ir_entity *ent) {
737         ir_type *tp = get_entity_type(ent);
738         ir_mode *mode;
739         int     n;
740         ir_node *irn;
741
742         if (! is_Array_type(tp))
743                 return 0;
744         tp = get_array_element_type(tp);
745         if (! is_Primitive_type(tp))
746                 return 0;
747         mode = get_type_mode(tp);
748
749         /* FIXME: This is too restrict, as the type char might be more the 8bits */
750         if (!mode_is_int(mode) || get_mode_size_bits(mode) != 8)
751                 return 0;
752
753         n = get_compound_ent_n_values(ent);
754         if (n < 1)
755                 return 0;
756         irn = get_compound_ent_value(ent, 0);
757
758         return is_Const(irn) && is_Const_null(irn);
759 }  /* is_empty_string */
760
761 /* A mapper for strcmp */
762 int i_mapper_strcmp(ir_node *call, void *ctx) {
763         ir_node   *left    = get_Call_param(call, 0);
764         ir_node   *right   = get_Call_param(call, 1);
765         ir_node   *irn     = NULL;
766         ir_node   *exc     = NULL;
767         ir_node   *reg     = NULL;
768         ir_type   *call_tp = get_Call_type(call);
769         ir_type   *res_tp  = get_method_res_type(call_tp, 0);
770         ir_entity *ent_l, *ent_r;
771         ir_type   *char_tp;
772         ir_node   *v;
773
774         (void) ctx;
775
776         /* do some type checks first */
777         if (! is_Primitive_type(res_tp))
778                 return 0;
779         char_tp = get_method_param_type(call_tp, 0);
780         if (char_tp != get_method_param_type(call_tp, 1))
781                 return 0;
782         if (! is_Pointer_type(char_tp))
783                 return 0;
784         char_tp = get_pointer_points_to_type(char_tp);
785
786         if (left == right) {
787                 /* a strcmp(s, s) ==> 0 */
788                 ir_node *mem   = get_Call_mem(call);
789                 ir_mode *mode  = get_type_mode(res_tp);
790
791                 irn = new_Const(get_mode_null(mode));
792                 DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_STRCMP);
793                 replace_call(irn, call, mem, NULL, NULL);
794                 return 1;
795         }
796         ent_l = get_const_entity(left);
797         ent_r = get_const_entity(right);
798
799         if (ent_l != NULL && ent_r != NULL) {
800                 /* both entities are const, try to evaluate */
801                 irn = eval_strcmp(ent_l, ent_r, res_tp);
802         } else if (ent_l != NULL) {
803                 if (is_empty_string(ent_l)) {
804                         /* s strcmp("", s) ==> -(*s)*/
805                         v = right;
806                         goto replace_by_call;
807                 }
808         } else if (ent_r != NULL) {
809                 if (is_empty_string(ent_r)) {
810                         /* s strcmp(s, "") ==> (*s) */
811                         ir_node  *mem, *block;
812                         dbg_info *dbg;
813                         ir_mode  *mode;
814
815                         v = left;
816 replace_by_call:
817                         mem   = get_Call_mem(call);
818                         block = get_nodes_block(call);
819                         dbg   = get_irn_dbg_info(call);
820                         mode  = get_type_mode(char_tp);
821
822                         /* replace the strcmp by (*x) */
823                         irn = new_rd_Load(dbg, block, mem, v, mode, 0);
824                         mem = new_r_Proj(block, irn, mode_M, pn_Load_M);
825                         exc = new_r_Proj(block, irn, mode_X, pn_Load_X_except);
826                         reg = new_r_Proj(block, irn, mode_X, pn_Load_X_regular);
827                         irn = new_r_Proj(block, irn, mode, pn_Load_res);
828
829                         /* conv to the result mode */
830                         mode = get_type_mode(res_tp);
831                         irn  = new_rd_Conv(dbg, block, irn, mode);
832
833                         if (v == right) {
834                                 /* negate in the ("", s) case */
835                                 irn = new_rd_Minus(dbg, block, irn, mode);
836                         }
837                 }
838         }
839
840         if (irn != NULL) {
841                 ir_node *mem = get_Call_mem(call);
842                 DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_STRCMP);
843                 replace_call(irn, call, mem, reg, exc);
844                 return 1;
845         }
846
847         return 0;
848 }  /* i_mapper_strcmp */
849
850 /* A mapper for strncmp */
851 int i_mapper_strncmp(ir_node *call, void *ctx) {
852         ir_node *left  = get_Call_param(call, 0);
853         ir_node *right = get_Call_param(call, 1);
854         ir_node *len   = get_Call_param(call, 2);
855         ir_node *irn;
856         (void) ctx;
857
858         if (left == right || (is_Const(len) && is_Const_null(len))) {
859                 /* a strncmp(s, s, len) ==> 0 OR
860                    a strncmp(a, b, 0) ==> 0 */
861                 ir_node   *mem     = get_Call_mem(call);
862                 ir_node   *adr     = get_Call_ptr(call);
863                 ir_entity *ent     = get_SymConst_entity(adr);
864                 ir_type   *call_tp = get_entity_type(ent);
865                 ir_type   *res_tp  = get_method_res_type(call_tp, 0);
866                 ir_mode   *mode    = get_type_mode(res_tp);
867
868                 irn = new_Const(get_mode_null(mode));
869                 DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_STRNCMP);
870                 replace_call(irn, call, mem, NULL, NULL);
871                 return 1;
872         }
873         return 0;
874 }  /* i_mapper_strncmp */
875
876 /* A mapper for strcpy */
877 int i_mapper_strcpy(ir_node *call, void *ctx) {
878         ir_node *dst = get_Call_param(call, 0);
879         ir_node *src = get_Call_param(call, 1);
880         (void) ctx;
881
882         if (dst == src) {
883                 /* a strcpy(d, s) ==> d */
884                 ir_node *mem = get_Call_mem(call);
885                 ir_node *dst = get_Call_param(call, 0);
886
887                 DBG_OPT_ALGSIM0(call, dst, FS_OPT_RTS_STRCPY);
888                 replace_call(dst, call, mem, NULL, NULL);
889                 return 1;
890         }
891         return 0;
892 }  /* i_mapper_strcpy */
893
894 /* A mapper for memcpy */
895 int i_mapper_memcpy(ir_node *call, void *ctx) {
896         ir_node *dst = get_Call_param(call, 0);
897         ir_node *src = get_Call_param(call, 1);
898         ir_node *len = get_Call_param(call, 2);
899         (void) ctx;
900
901         if (dst == src || (is_Const(len) && is_Const_null(len))) {
902                 /* a memcpy(d, d, len) ==> d OR
903                    a memcpy(d, s, 0) ==> d */
904                 ir_node *mem = get_Call_mem(call);
905
906                 DBG_OPT_ALGSIM0(call, dst, FS_OPT_RTS_MEMCPY);
907                 replace_call(dst, call, mem, NULL, NULL);
908                 return 1;
909         }
910         return 0;
911 }  /* i_mapper_memcpy */
912
913 /* A mapper for mempcpy */
914 int i_mapper_mempcpy(ir_node *call, void *ctx) {
915         ir_node *dst = get_Call_param(call, 0);
916         ir_node *src = get_Call_param(call, 1);
917         ir_node *len = get_Call_param(call, 2);
918         (void) ctx;
919
920         if (dst == src || (is_Const(len) && is_Const_null(len))) {
921                 /* a memcpy(d, d, len) ==> d + len OR
922                    a memcpy(d, s, 0) ==> d + 0 */
923                 dbg_info *dbg = get_irn_dbg_info(call);
924                 ir_node *mem  = get_Call_mem(call);
925                 ir_node *blk  = get_nodes_block(call);
926                 ir_mode *mode = get_irn_mode(dst);
927                 ir_node *res  = new_rd_Add(dbg, blk, dst, len, mode);
928
929                 DBG_OPT_ALGSIM0(call, res, FS_OPT_RTS_MEMPCPY);
930                 replace_call(res, call, mem, NULL, NULL);
931                 return 1;
932         }
933         return 0;
934 }  /* i_mapper_mempcpy */
935
936 /* A mapper for memmove */
937 int i_mapper_memmove(ir_node *call, void *ctx) {
938         ir_node *dst = get_Call_param(call, 0);
939         ir_node *src = get_Call_param(call, 1);
940         ir_node *len = get_Call_param(call, 2);
941         (void) ctx;
942
943         if (dst == src || (is_Const(len) && is_Const_null(len))) {
944                 /* a memmove(d, d, len) ==> d OR
945                    a memmove(d, s, 0) ==> d */
946                 ir_node *mem = get_Call_mem(call);
947
948                 DBG_OPT_ALGSIM0(call, dst, FS_OPT_RTS_MEMMOVE);
949                 replace_call(dst, call, mem, NULL, NULL);
950                 return 1;
951         }
952         return 0;
953 }  /* i_mapper_memmove */
954
955 /* A mapper for memset */
956 int i_mapper_memset(ir_node *call, void *ctx) {
957         ir_node *len = get_Call_param(call, 2);
958         (void) ctx;
959
960         if (is_Const(len) && is_Const_null(len)) {
961                 /* a memset(d, C, 0) ==> d */
962                 ir_node *mem = get_Call_mem(call);
963                 ir_node *dst = get_Call_param(call, 0);
964
965                 DBG_OPT_ALGSIM0(call, dst, FS_OPT_RTS_MEMSET);
966                 replace_call(dst, call, mem, NULL, NULL);
967                 return 1;
968         }
969         return 0;
970 }  /* i_mapper_memset */
971
972 /* A mapper for memcmp */
973 int i_mapper_memcmp(ir_node *call, void *ctx) {
974         ir_node *left  = get_Call_param(call, 0);
975         ir_node *right = get_Call_param(call, 1);
976         ir_node *len   = get_Call_param(call, 2);
977         ir_node *irn;
978         (void) ctx;
979
980         if (left == right || (is_Const(len) && is_Const_null(len))) {
981                 /* a memcmp(s, s, len) ==> 0 OR
982                    a memcmp(a, b, 0) ==> 0 */
983                 ir_node   *mem     = get_Call_mem(call);
984                 ir_node   *adr     = get_Call_ptr(call);
985                 ir_entity *ent     = get_SymConst_entity(adr);
986                 ir_type   *call_tp = get_entity_type(ent);
987                 ir_type   *res_tp  = get_method_res_type(call_tp, 0);
988                 ir_mode   *mode    = get_type_mode(res_tp);
989
990                 irn = new_Const(get_mode_null(mode));
991                 DBG_OPT_ALGSIM0(call, irn, FS_OPT_RTS_STRNCMP);
992                 replace_call(irn, call, mem, NULL, NULL);
993                 return 1;
994         }
995         return 0;
996 }  /* i_mapper_memcmp */
997
998 /**
999  * Returns the result mode of a node.
1000  */
1001 static ir_mode *get_irn_res_mode(ir_node *node) {
1002         switch (get_irn_opcode(node)) {
1003         case iro_Load:   return get_Load_mode(node);
1004         case iro_Quot:   return get_Quot_resmode(node);
1005         case iro_Div:    return get_Div_resmode(node);
1006         case iro_Mod:    return get_Mod_resmode(node);
1007         case iro_DivMod: return get_DivMod_resmode(node);
1008         default: return NULL;
1009         }
1010 }  /* get_irn_res_mode */
1011
1012 #define LMAX(a, b) ((a) > (b) ? (a) : (b))
1013
1014 /* A mapper for mapping unsupported instructions to runtime calls. */
1015 int i_mapper_RuntimeCall(ir_node *node, runtime_rt *rt) {
1016         int i, j, arity, first, n_param, n_res;
1017         long n_proj;
1018         ir_type *mtp;
1019         ir_node *mem, *bl, *call, *addr, *res_proj;
1020         ir_node **in;
1021         ir_graph *irg;
1022         symconst_symbol sym;
1023         ir_mode *mode = get_irn_mode(node);
1024
1025         if (mode != rt->mode)
1026                 return 0;
1027         /* check if the result modes match */
1028         if (mode == mode_T && rt->res_mode != NULL) {
1029                 mode = get_irn_res_mode(node);
1030                 if (mode != rt->res_mode)
1031                         return 0;
1032         }
1033
1034         arity = get_irn_arity(node);
1035         if (arity <= 0)
1036                 return 0;
1037
1038         mtp     = get_entity_type(rt->ent);
1039         n_param = get_method_n_params(mtp);
1040         irg     = current_ir_graph;
1041
1042         mem = get_irn_n(node, 0);
1043         if (get_irn_mode(mem) != mode_M) {
1044                 mem = new_r_NoMem(irg);
1045                 first = 0;
1046         } else
1047                 first = 1;
1048
1049         /* check if the modes of the predecessors match the parameter modes */
1050         if (arity - first != n_param)
1051                 return 0;
1052
1053         for (i = first, j = 0; i < arity; ++i, ++j) {
1054                 ir_type *param_tp = get_method_param_type(mtp, j);
1055                 ir_node *pred = get_irn_n(node, i);
1056
1057                 if (get_type_mode(param_tp) != get_irn_mode(pred))
1058                         return 0;
1059         }
1060
1061         n_res = get_method_n_ress(mtp);
1062
1063         /* step 0: calculate the number of needed Proj's */
1064         n_proj = 0;
1065         n_proj = LMAX(n_proj, rt->mem_proj_nr + 1);
1066         n_proj = LMAX(n_proj, rt->regular_proj_nr + 1);
1067         n_proj = LMAX(n_proj, rt->exc_proj_nr + 1);
1068         n_proj = LMAX(n_proj, rt->exc_mem_proj_nr + 1);
1069         n_proj = LMAX(n_proj, rt->res_proj_nr + 1);
1070
1071         if (n_proj > 0) {
1072                 if (rt->mode != mode_T) /* must be mode_T */
1073                         return 0;
1074         } else {
1075                 if (n_res > 0)
1076                         /* must match */
1077                         if (get_type_mode(get_method_res_type(mtp, 0)) != rt->mode)
1078                                 return 0;
1079         }
1080
1081         /* ok, when we are here, the number of predecessors match as well as the parameter modes */
1082         bl = get_nodes_block(node);
1083
1084         in = NULL;
1085         if (n_param > 0) {
1086                 NEW_ARR_A(ir_node *, in, n_param);
1087                 for (i = 0; i < n_param; ++i)
1088                         in[i] = get_irn_n(node, first + i);
1089         }
1090
1091         /* step 1: create the call */
1092         sym.entity_p = rt->ent;
1093         addr = new_r_SymConst(irg, mode_P_code, sym, symconst_addr_ent);
1094         call = new_rd_Call(get_irn_dbg_info(node), bl, mem, addr, n_param, in, mtp);
1095         set_irn_pinned(call, get_irn_pinned(node));
1096
1097         if (n_res > 0)
1098                 res_proj = new_r_Proj(bl, call, mode_T, pn_Call_T_result);
1099         else
1100                 res_proj = NULL;
1101
1102         if (n_proj > 0) {
1103                 n_proj += n_res - 1;
1104
1105                 /* we are ready */
1106                 turn_into_tuple(node, n_proj);
1107
1108                 for (i = 0; i < n_proj; ++i)
1109                         set_Tuple_pred(node, i, new_r_Bad(irg));
1110                 if (rt->mem_proj_nr >= 0)
1111                         set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(bl, call, mode_M, pn_Call_M_regular));
1112                 if (!is_NoMem(mem)) {
1113                         /* Exceptions can only be handled with real memory */
1114                         if (rt->regular_proj_nr >= 0)
1115                                 set_Tuple_pred(node, rt->regular_proj_nr, new_r_Proj(bl, call, mode_X, pn_Call_X_regular));
1116                         if (rt->exc_proj_nr >= 0)
1117                                 set_Tuple_pred(node, rt->exc_proj_nr, new_r_Proj(bl, call, mode_X, pn_Call_X_except));
1118                         if (rt->exc_mem_proj_nr >= 0)
1119                                 set_Tuple_pred(node, rt->mem_proj_nr, new_r_Proj(bl, call, mode_M, pn_Call_M_except));
1120                 }
1121
1122                 if (rt->res_proj_nr >= 0)
1123                         for (i = 0; i < n_res; ++i)
1124                                 set_Tuple_pred(node, rt->res_proj_nr + i,
1125                                 new_r_Proj(bl, res_proj, get_type_mode(get_method_res_type(mtp, i)), i));
1126                         return 1;
1127         } else {
1128                 /* only one return value supported */
1129                 if (n_res > 0) {
1130                         ir_mode *mode = get_type_mode(get_method_res_type(mtp, 0));
1131
1132                         res_proj = new_r_Proj(bl, call, mode_T, pn_Call_T_result);
1133                         res_proj = new_r_Proj(bl, res_proj, mode, 0);
1134
1135                         exchange(node, res_proj);
1136                         return 1;
1137                 }
1138         }
1139         /* should not happen */
1140         return 0;
1141 }  /* i_mapper_RuntimeCall */