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