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