Merge pull request #554 from deplinenoise/ppc_fixes
[mono.git] / mono / mini / cfold.c
1 /*
2  * cfold.c: Constant folding support
3  *
4  * Author:
5  *   Paolo Molaro (lupus@ximian.com)
6  *   Dietmar Maurer (dietmar@ximian.com)
7  *
8  * (C) 2003 Ximian, Inc.  http://www.ximian.com
9  */
10 #include <config.h>
11
12 #include "mini.h"
13 #include "ir-emit.h"
14
15 /* WTF is this doing here?!?!? */
16 int
17 mono_is_power_of_two (guint32 val)
18 {
19         int i, j, k;
20
21         for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
22                 if (val & j)
23                         break;
24         }
25         if (i == 32 || val & k)
26                 return -1;
27         return i;
28 }
29
30 #ifndef G_MININT32
31 #define MYGINT32_MAX 2147483647
32 #define G_MININT32 (-MYGINT32_MAX -1)
33 #endif
34
35 #define FOLD_UNOP(name,op)      \
36         case name:      \
37             dest->inst_c0 = op arg1->inst_c0; \
38         break;
39
40 #define FOLD_BINOP(name, op) \
41         case name:      \
42             dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0;     \
43         break;
44
45 #define FOLD_BINOPC(name,op,cast)       \
46         case name:      \
47             dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
48         break;
49
50 #define FOLD_BINOP2_IMM(name, op) \
51         case name:      \
52             dest->inst_c0 = arg1->inst_c0 op ins->inst_imm;     \
53         break;
54
55 #define FOLD_BINOPC2_IMM(name, op, cast) \
56         case name:      \
57             dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
58         break;
59
60 #define FOLD_BINOPCXX(name,op,cast)     \
61         case name:      \
62             res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0;   \
63         break; \
64
65 #define ALLOC_DEST(cfg, dest, ins) do { \
66     if (!(dest)) { \
67         MONO_INST_NEW ((cfg), (dest), -1); \
68         (dest)->dreg = (ins)->dreg; \
69     } \
70 } while (0)
71
72 #ifndef DISABLE_JIT
73
74 /**
75  * mono_constant_fold_ins:
76  *
77  * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is
78  * true, then store the result back into INS and return INS. Otherwise allocate a new ins,
79  * store the result into it and return it. If constant folding cannot be performed, return
80  * NULL.
81  */
82 MonoInst*
83 mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite)
84 {
85         MonoInst *dest = NULL;
86
87         if (overwrite)
88                 dest = ins;
89
90         switch (ins->opcode) {
91         case OP_IMUL:
92         case OP_IADD:
93         case OP_IAND:
94         case OP_IOR:
95         case OP_IXOR:
96                 if (arg2->opcode == OP_ICONST) {
97                         if (arg1->opcode == OP_ICONST) {
98                                 ALLOC_DEST (cfg, dest, ins);
99                                 switch (ins->opcode) {
100                                         FOLD_BINOP (OP_IMUL, *);
101                                         FOLD_BINOP (OP_IADD, +);
102                                         FOLD_BINOP (OP_IAND, &);
103                                         FOLD_BINOP (OP_IOR, |);
104                                         FOLD_BINOP (OP_IXOR, ^);
105                                 }
106                                 dest->opcode = OP_ICONST;
107                                 MONO_INST_NULLIFY_SREGS (dest);
108                         }
109                 } else if (arg1->opcode == OP_ICONST) {
110                         /* 
111                          * This is commutative so swap the arguments, allowing the _imm variant
112                          * to be used later.
113                          */
114                         if (mono_op_to_op_imm (ins->opcode) != -1) {
115                                 ALLOC_DEST (cfg, dest, ins);
116                                 dest->opcode = mono_op_to_op_imm (ins->opcode);
117                                 dest->sreg1 = ins->sreg2;
118                                 dest->sreg2 = -1;
119                                 dest->inst_imm = arg1->inst_c0;
120                         }
121                 }
122                 break;
123         case OP_IMUL_IMM:
124         case OP_IADD_IMM:
125         case OP_IAND_IMM:
126         case OP_IOR_IMM:
127         case OP_IXOR_IMM:
128         case OP_ISUB_IMM:
129         case OP_ISHL_IMM:
130         case OP_ISHR_IMM:
131         case OP_ISHR_UN_IMM:
132         case OP_SHL_IMM:
133                 if (arg1->opcode == OP_ICONST) {
134                         ALLOC_DEST (cfg, dest, ins);
135                         switch (ins->opcode) {
136                                 FOLD_BINOP2_IMM (OP_IMUL_IMM, *);
137                                 FOLD_BINOP2_IMM (OP_IADD_IMM, +);
138                                 FOLD_BINOP2_IMM (OP_IAND_IMM, &);
139                                 FOLD_BINOP2_IMM (OP_IOR_IMM, |);
140                                 FOLD_BINOP2_IMM (OP_IXOR_IMM, ^);
141                                 FOLD_BINOP2_IMM (OP_ISUB_IMM, -);
142                                 FOLD_BINOPC2_IMM (OP_ISHL_IMM, <<, gint32);
143                                 FOLD_BINOPC2_IMM (OP_ISHR_IMM, >>, gint32);
144                                 FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM, >>, guint32);
145                                 FOLD_BINOP2_IMM (OP_SHL_IMM, <<);
146                         }
147                         dest->opcode = OP_ICONST;
148                         MONO_INST_NULLIFY_SREGS (dest);
149                 }
150                 break;
151         case OP_ISUB:
152         case OP_ISHL:
153         case OP_ISHR:
154         case OP_ISHR_UN:
155                 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
156                         ALLOC_DEST (cfg, dest, ins);
157                         switch (ins->opcode) {
158                                 FOLD_BINOP (OP_ISUB, -);
159                                 FOLD_BINOP (OP_ISHL, <<);
160                                 FOLD_BINOP (OP_ISHR, >>);
161                                 FOLD_BINOPC (OP_ISHR_UN, >>, guint32);
162                         }
163                         dest->opcode = OP_ICONST;
164                         MONO_INST_NULLIFY_SREGS (dest);
165                 }
166                 break;
167         case OP_IDIV:
168         case OP_IDIV_UN:
169         case OP_IREM:
170         case OP_IREM_UN:
171                 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
172                         if ((arg2->inst_c0 == 0) || ((arg1->inst_c0 == G_MININT32) && (arg2->inst_c0 == -1)))
173                                 return NULL;
174                         ALLOC_DEST (cfg, dest, ins);
175                         switch (ins->opcode) {
176                                 FOLD_BINOPC (OP_IDIV, /, gint32);
177                                 FOLD_BINOPC (OP_IDIV_UN, /, guint32);
178                                 FOLD_BINOPC (OP_IREM, %, gint32);
179                                 FOLD_BINOPC (OP_IREM_UN, %, guint32);
180                         }
181                         dest->opcode = OP_ICONST;
182                         MONO_INST_NULLIFY_SREGS (dest);
183                 }
184                 break;
185         case OP_IDIV_IMM:
186         case OP_IDIV_UN_IMM:
187         case OP_IREM_IMM:
188         case OP_IREM_UN_IMM:
189                 if (arg1->opcode == OP_ICONST) {
190                         if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1)))
191                                 return NULL;
192                         ALLOC_DEST (cfg, dest, ins);
193                         switch (ins->opcode) {
194                                 FOLD_BINOPC2_IMM (OP_IDIV_IMM, /, gint32);
195                                 FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM, /, guint32);
196                                 FOLD_BINOPC2_IMM (OP_IREM_IMM, %, gint32);
197                                 FOLD_BINOPC2_IMM (OP_IREM_UN_IMM, %, guint32);
198                         default:
199                                 g_assert_not_reached ();
200                         }
201                         dest->opcode = OP_ICONST;
202                         MONO_INST_NULLIFY_SREGS (dest);
203                 }
204                 break;
205                 /* case OP_INEG: */
206         case OP_INOT:
207         case OP_INEG:
208                 if (arg1->opcode == OP_ICONST) {
209                         /* INEG sets cflags on x86, and the LNEG decomposition depends on that */
210 #if SIZEOF_REGISTER == 4
211                         if (ins->opcode == OP_INEG)
212                                 return NULL;
213 #endif
214                         ALLOC_DEST (cfg, dest, ins);
215                         switch (ins->opcode) {
216                                 FOLD_UNOP (OP_INEG,-);
217                                 FOLD_UNOP (OP_INOT,~);
218                         }
219                         dest->opcode = OP_ICONST;
220                         MONO_INST_NULLIFY_SREGS (dest);
221                 }
222                 break;
223         case OP_MOVE:
224 #if SIZEOF_REGISTER == 8
225                 if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) {
226 #else
227                 if (arg1->opcode == OP_ICONST) {
228 #endif
229                         ALLOC_DEST (cfg, dest, ins);
230                         dest->opcode = arg1->opcode;
231                         MONO_INST_NULLIFY_SREGS (dest);
232                         dest->inst_c0 = arg1->inst_c0;
233                 }
234                 break;
235         case OP_VMOVE:
236                 if (arg1->opcode == OP_VZERO) {
237                         ALLOC_DEST (cfg, dest, ins);
238                         dest->opcode = OP_VZERO;
239                         dest->sreg1 = -1;
240                 }
241                 break;
242         case OP_XMOVE:
243                 if (arg1->opcode == OP_XZERO) {
244                         ALLOC_DEST (cfg, dest, ins);
245                         dest->opcode = OP_XZERO;
246                         dest->sreg1 = -1;
247                 }
248                 break;
249         case OP_COMPARE:
250         case OP_ICOMPARE:
251         case OP_COMPARE_IMM:
252         case OP_ICOMPARE_IMM: {
253                 MonoInst dummy_arg2;
254                 if (ins->sreg2 == -1) {
255                         arg2 = &dummy_arg2;
256                         arg2->opcode = OP_ICONST;
257                         arg2->inst_c0 = ins->inst_imm;
258                 }
259
260                 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST) && ins->next) {
261                         MonoInst *next = ins->next;
262                         gboolean res = FALSE;
263
264                         switch (next->opcode) {
265                         case OP_CEQ:
266                         case OP_ICEQ:
267                         case OP_CGT:
268                         case OP_ICGT:
269                         case OP_CGT_UN:
270                         case OP_ICGT_UN:
271                         case OP_CLT:
272                         case OP_ICLT:
273                         case OP_CLT_UN:
274                         case OP_ICLT_UN:
275                                 switch (next->opcode) {
276                                         FOLD_BINOPCXX (OP_CEQ,==,gint32);
277                                         FOLD_BINOPCXX (OP_ICEQ,==,gint32);
278                                         FOLD_BINOPCXX (OP_CGT,>,gint32);
279                                         FOLD_BINOPCXX (OP_ICGT,>,gint32);
280                                         FOLD_BINOPCXX (OP_CGT_UN,>,guint32);
281                                         FOLD_BINOPCXX (OP_ICGT_UN,>,guint32);
282                                         FOLD_BINOPCXX (OP_CLT,<,gint32);
283                                         FOLD_BINOPCXX (OP_ICLT,<,gint32);
284                                         FOLD_BINOPCXX (OP_CLT_UN,<,guint32);
285                                         FOLD_BINOPCXX (OP_ICLT_UN,<,guint32);
286                                 }
287
288                                 if (overwrite) {
289                                         NULLIFY_INS (ins);
290                                         next->opcode = OP_ICONST;
291                                         next->inst_c0 = res;
292                                         MONO_INST_NULLIFY_SREGS (next);
293                                 } else {
294                                         ALLOC_DEST (cfg, dest, ins);
295                                         dest->opcode = OP_ICONST;
296                                         dest->inst_c0 = res;
297                                 }
298                                 break;
299                         case OP_IBEQ:
300                         case OP_IBNE_UN:
301                         case OP_IBGT:
302                         case OP_IBGT_UN:
303                         case OP_IBGE:
304                         case OP_IBGE_UN:
305                         case OP_IBLT:
306                         case OP_IBLT_UN:
307                         case OP_IBLE:
308                         case OP_IBLE_UN:
309                                 switch (next->opcode) {
310                                         FOLD_BINOPCXX (OP_IBEQ,==,gint32);
311                                         FOLD_BINOPCXX (OP_IBNE_UN,!=,guint32);
312                                         FOLD_BINOPCXX (OP_IBGT,>,gint32);
313                                         FOLD_BINOPCXX (OP_IBGT_UN,>,guint32);
314                                         FOLD_BINOPCXX (OP_IBGE,>=,gint32);
315                                         FOLD_BINOPCXX (OP_IBGE_UN,>=,guint32);
316                                         FOLD_BINOPCXX (OP_IBLT,<,gint32);
317                                         FOLD_BINOPCXX (OP_IBLT_UN,<,guint32);
318                                         FOLD_BINOPCXX (OP_IBLE,<=,gint32);
319                                         FOLD_BINOPCXX (OP_IBLE_UN,<=,guint32);
320                                 }
321
322                                 if (overwrite) {
323                                         /* 
324                                          * Can't nullify OP_COMPARE here since the decompose long branch 
325                                          * opcodes depend on it being executed. Also, the branch might not
326                                          * be eliminated after all if loop opts is disabled, for example.
327                                          */
328                                         if (res)
329                                                 next->flags |= MONO_INST_CFOLD_TAKEN;
330                                         else
331                                                 next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
332                                 } else {
333                                         ALLOC_DEST (cfg, dest, ins);
334                                         dest->opcode = OP_ICONST;
335                                         dest->inst_c0 = res;
336                                 }
337                                 break;
338                         case OP_NOP:
339                         case OP_BR:
340                                 /* This happens when a conditional branch is eliminated */
341                                 if (next->next == NULL) {
342                                         /* Last ins */
343                                         if (overwrite)
344                                                 NULLIFY_INS (ins);
345                                 }
346                                 break;
347                         default:
348                                 return NULL;
349                         }
350                 }
351                 break;
352         }
353         case OP_FMOVE:
354                 if (arg1->opcode == OP_R8CONST) {
355                         ALLOC_DEST (cfg, dest, ins);
356                         dest->opcode = OP_R8CONST;
357                         dest->sreg1 = -1;
358                         dest->inst_p0 = arg1->inst_p0;
359                 }
360                 break;
361
362                 /*
363                  * TODO: 
364                  *      conv.* opcodes.
365                  *      *ovf* opcodes? It's slow and hard to do in C.
366                  *      switch can be replaced by a simple jump 
367                  */
368         default:
369                 return NULL;
370         }
371                 
372     return dest;
373 }       
374
375
376 #endif /* DISABLE_JIT */