4a0103d34328a257e21b61da98076ed391883cad
[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
12 int
13 mono_is_power_of_two (guint32 val)
14 {
15         int i, j, k;
16
17         for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
18                 if (val & j)
19                         break;
20         }
21         if (i == 32 || val & k)
22                 return -1;
23         return i;
24 }
25
26 #define FOLD_BINOP(name,op)     \
27         case name:      \
28                 if (inst->inst_i0->opcode != OP_ICONST) \
29                         return; \
30                 if (inst->inst_i1->opcode == OP_ICONST) {       \
31                         inst->opcode = OP_ICONST;       \
32                         inst->inst_c0 = (gint32)(inst->inst_i0->inst_c0 op inst->inst_i1->inst_c0);     \
33                 } \
34                 return;
35
36 /*
37  * We try to put constants on the left side of a commutative operation
38  * because it reduces register pressure and it matches the usual cpu 
39  * instructions with immediates.
40  */
41 #define FOLD_BINOPCOMM(name,op) \
42         case name:      \
43                 if (inst->inst_i0->opcode == OP_ICONST) {\
44                         if (inst->inst_i1->opcode == OP_ICONST) {       \
45                                 inst->opcode = OP_ICONST;       \
46                                 inst->inst_c0 = (gint32)(inst->inst_i0->inst_c0 op inst->inst_i1->inst_c0);     \
47                                 return; \
48                         } else { \
49                                 MonoInst *tmp = inst->inst_i0;  \
50                                 inst->inst_i0 = inst->inst_i1;  \
51                                 inst->inst_i1 = tmp;    \
52                        } \
53                 } \
54                 if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_ADD) {    \
55                         if (inst->inst_i1->inst_c0 == 0) { \
56                                 *inst = *(inst->inst_i0); \
57                                 return; \
58                         } \
59                 } \
60                 if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_MUL) {    \
61                         int power2;     \
62                         if (inst->inst_i1->inst_c0 == 1) {      \
63                                 *inst = *(inst->inst_i0);       \
64                                 return; \
65                         } else if (inst->inst_i1->inst_c0 == -1) {      \
66                                 inst->opcode = CEE_NEG; \
67                                 return; \
68                         }       \
69                         power2 = mono_is_power_of_two (inst->inst_i1->inst_c0); \
70                         if (power2 < 0) return; \
71                         inst->opcode = CEE_SHL; \
72                         inst->inst_i1->inst_c0 = power2;        \
73                 } \
74                 return;
75
76 /* 
77  * We can't let this cause a division by zero exception since the division 
78  * might not be executed during runtime.
79  */
80 #define FOLD_BINOPZ(name,op,cast)       \
81         case name:      \
82                 if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_REM_UN && inst->inst_i1->inst_c0 == 2) {  \
83                         inst->opcode = CEE_AND; \
84                         inst->inst_i1->inst_c0 = 1;     \
85                         return; \
86                 }       \
87                 if (inst->inst_i1->opcode == OP_ICONST) {       \
88                         if (!inst->inst_i1->inst_c0) return;    \
89                         if (inst->inst_i0->opcode == OP_ICONST) {       \
90                 if ((inst->inst_i0->inst_c0 == G_MININT32) && (inst->inst_i1->inst_c0 == -1)) \
91                     return; \
92                                 inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
93                                 inst->opcode = OP_ICONST;       \
94                         } else {        \
95                                 int power2 = mono_is_power_of_two (inst->inst_i1->inst_c0);     \
96                                 if (power2 < 0) return; \
97                                 if (inst->opcode == CEE_REM_UN) {       \
98                                         inst->opcode = CEE_AND; \
99                                         inst->inst_i1->inst_c0 = (1 << power2) - 1;     \
100                                 } else if (inst->opcode == CEE_DIV_UN) {        \
101                                         inst->opcode = CEE_SHR_UN;      \
102                                         inst->inst_i1->inst_c0 = power2;        \
103                                 }       \
104                         }       \
105                 } \
106                 return;
107         
108 #define FOLD_CXX(name,op,cast)  \
109         case name:      \
110                 if (inst->inst_i0->opcode != OP_COMPARE)        \
111                         return; \
112                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
113                         return; \
114                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
115                         inst->opcode = OP_ICONST;       \
116                         inst->inst_c0 = (cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0; \
117                 } \
118                 return;
119
120 #define FOLD_UNOP(name,op)      \
121         case name:      \
122                 if (inst->inst_i0->opcode == OP_ICONST) {       \
123                         inst->opcode = OP_ICONST;       \
124                         inst->inst_c0 = op inst->inst_i0->inst_c0; \
125                 } else if (inst->inst_i0->opcode == OP_I8CONST) { \
126                         inst->opcode = OP_I8CONST;      \
127                         inst->inst_l = op inst->inst_i0->inst_l; \
128                 } return;
129
130 #define FOLD_BRBINOP(name,op,cast)      \
131         case name:      \
132                 if (inst->inst_i0->opcode != OP_COMPARE)        \
133                         return; \
134                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
135                         return; \
136                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
137                         if ((cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0)     \
138                                 inst->opcode = OP_BR;   \
139                         else    \
140                                 inst->opcode = OP_NOP;  \
141                 } \
142                 return;
143
144 /*
145  * Helper function to do constant expression evaluation.
146  * We do constant folding of integers only, FP stuff is much more tricky,
147  * int64 probably not worth it.
148  */
149 void
150 mono_constant_fold_inst (MonoInst *inst, gpointer data)
151 {
152         switch (inst->opcode) {
153
154         /* FIXME: the CEE_B* don't contain operands, need to use the OP_COMPARE instruction */
155         /*FOLD_BRBINOP (CEE_BEQ,==,gint32)
156         FOLD_BRBINOP (CEE_BGE,>=,gint32)
157         FOLD_BRBINOP (CEE_BGT,>,gint32)
158         FOLD_BRBINOP (CEE_BLE,<=,gint32)
159         FOLD_BRBINOP (CEE_BLT,<,gint32)
160         FOLD_BRBINOP (CEE_BNE_UN,!=,guint32)
161         FOLD_BRBINOP (CEE_BGE_UN,>=,guint32)
162         FOLD_BRBINOP (CEE_BGT_UN,>,guint32)
163         FOLD_BRBINOP (CEE_BLE_UN,<=,guint32)
164         FOLD_BRBINOP (CEE_BLT_UN,<,guint32)*/
165
166         FOLD_BINOPCOMM (CEE_MUL,*)
167
168         FOLD_BINOPCOMM (CEE_ADD,+)
169         FOLD_BINOP (CEE_SUB,-)
170         FOLD_BINOPZ (CEE_DIV,/,gint32)
171         FOLD_BINOPZ (CEE_DIV_UN,/,guint32)
172         FOLD_BINOPZ (CEE_REM,%,gint32)
173         FOLD_BINOPZ (CEE_REM_UN,%,guint32)
174         FOLD_BINOPCOMM (CEE_AND,&)
175         FOLD_BINOPCOMM (CEE_OR,|)
176         FOLD_BINOPCOMM (CEE_XOR,^)
177         FOLD_BINOP (CEE_SHL,<<)
178         FOLD_BINOP (CEE_SHR,>>)
179         case CEE_SHR_UN:
180                 if (inst->inst_i0->opcode != OP_ICONST)
181                         return;
182                 if (inst->inst_i1->opcode == OP_ICONST) {
183                         inst->opcode = OP_ICONST;
184                         inst->inst_c0 = (guint32)inst->inst_i0->inst_c0 >> (guint32)inst->inst_i1->inst_c0;
185                 }
186                 return;
187         FOLD_UNOP (CEE_NEG,-)
188         FOLD_UNOP (CEE_NOT,~)
189         FOLD_CXX (OP_CEQ,==,gint32)
190         FOLD_CXX (OP_CGT,>,gint32)
191         FOLD_CXX (OP_CGT_UN,>,guint32)
192         FOLD_CXX (OP_CLT,<,gint32)
193         FOLD_CXX (OP_CLT_UN,<,guint32)
194         case CEE_CONV_I8:
195                 if (inst->inst_i0->opcode == OP_ICONST) {
196                         inst->opcode = OP_I8CONST;
197                         inst->inst_l = inst->inst_i0->inst_c0;
198                 }
199                 return;
200         case CEE_CONV_I:
201         case CEE_CONV_U:
202                 if (inst->inst_i0->opcode == OP_ICONST) {
203                         inst->opcode = OP_ICONST;
204                         inst->inst_c0 = inst->inst_i0->inst_c0;
205                 } else if (inst->inst_i0->opcode == CEE_LDIND_I) {
206                         *inst = *inst->inst_i0;
207                 }
208                 return;
209         /* we should be able to handle isinst and castclass as well */
210         case CEE_ISINST:
211         case CEE_CASTCLASS:
212         /*
213          * TODO: 
214          *      conv.* opcodes.
215          *      *ovf* opcodes? I'ts slow and hard to do in C.
216          *      switch can be replaced by a simple jump 
217          */
218 #if SIZEOF_VOID_P == 4
219         case CEE_CONV_I4:
220                 if ((inst->inst_left->type == STACK_I4) || (inst->inst_left->type == STACK_PTR)) {
221                         *inst = *inst->inst_left;
222                 }
223                 break;  
224 #endif
225         default:
226                 return;
227         }
228 }
229
230 void
231 mono_constant_fold (MonoCompile *cfg)
232 {
233         MonoBasicBlock *bb;
234         
235         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
236                 MonoInst *ins;
237                 MONO_BB_FOR_EACH_INS (bb, ins)
238                         mono_inst_foreach (ins, mono_constant_fold_inst, NULL);
239         }
240 }
241
242 /*
243  * If the arguments to the cond branch are constants, eval and
244  * return BRANCH_NOT_TAKEN for not taken, BRANCH_TAKEN for taken,
245  * BRANCH_UNDEF otherwise.
246  * If this code is changed to handle also non-const values, make sure
247  * side effects are handled in optimize_branches() in mini.c, by
248  * inserting pop instructions.
249  */
250 int
251 mono_eval_cond_branch (MonoInst *ins)
252 {
253         MonoInst *left, *right;
254         /* FIXME: handle also 64 bit ints */
255         left = ins->inst_left->inst_left;
256         if (left->opcode != OP_ICONST && left->opcode != OP_PCONST)
257                 return BRANCH_UNDEF;
258         right = ins->inst_left->inst_right;
259         if (right->opcode != OP_ICONST && right->opcode != OP_PCONST)
260                 return BRANCH_UNDEF;
261         switch (ins->opcode) {
262         case CEE_BEQ:
263                 if (left->inst_c0 == right->inst_c0)
264                         return BRANCH_TAKEN;
265                 return BRANCH_NOT_TAKEN;
266         case CEE_BGE:
267                 if (left->inst_c0 >= right->inst_c0)
268                         return BRANCH_TAKEN;
269                 return BRANCH_NOT_TAKEN;
270         case CEE_BGT:
271                 if (left->inst_c0 > right->inst_c0)
272                         return BRANCH_TAKEN;
273                 return BRANCH_NOT_TAKEN;
274         case CEE_BLE:
275                 if (left->inst_c0 <= right->inst_c0)
276                         return BRANCH_TAKEN;
277                 return BRANCH_NOT_TAKEN;
278         case CEE_BLT:
279                 if (left->inst_c0 < right->inst_c0)
280                         return BRANCH_TAKEN;
281                 return BRANCH_NOT_TAKEN;
282         case CEE_BNE_UN:
283                 if ((gsize)left->inst_c0 != (gsize)right->inst_c0)
284                         return BRANCH_TAKEN;
285                 return BRANCH_NOT_TAKEN;
286         case CEE_BGE_UN:
287                 if ((gsize)left->inst_c0 >= (gsize)right->inst_c0)
288                         return BRANCH_TAKEN;
289                 return BRANCH_NOT_TAKEN;
290         case CEE_BGT_UN:
291                 if ((gsize)left->inst_c0 > (gsize)right->inst_c0)
292                         return BRANCH_TAKEN;
293                 return BRANCH_NOT_TAKEN;
294         case CEE_BLE_UN:
295                 if ((gsize)left->inst_c0 <= (gsize)right->inst_c0)
296                         return BRANCH_TAKEN;
297                 return BRANCH_NOT_TAKEN;
298         case CEE_BLT_UN:
299                 if ((gsize)left->inst_c0 < (gsize)right->inst_c0)
300                         return BRANCH_TAKEN;
301                 return BRANCH_NOT_TAKEN;
302         }
303         return BRANCH_UNDEF;
304 }
305
306 #ifndef G_MININT32
307 #define MYGINT32_MAX 2147483647
308 #define G_MININT32 (-MYGINT32_MAX -1)
309 #endif
310
311 #define FOLD_UNOP2(name,op)     \
312         case name:      \
313             dest->inst_c0 = op arg1->inst_c0; \
314         break;
315
316 #define FOLD_BINOP2(name, op) \
317         case name:      \
318             dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0;     \
319         break;
320
321 #define FOLD_BINOPC2(name,op,cast)      \
322         case name:      \
323             dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
324         break;
325
326 #define FOLD_BINOP2_IMM(name, op) \
327         case name:      \
328             dest->inst_c0 = arg1->inst_c0 op ins->inst_imm;     \
329         break;
330
331 #define FOLD_BINOPC2_IMM(name, op, cast) \
332         case name:      \
333             dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
334         break;
335
336 #define FOLD_BINOPCXX2(name,op,cast)    \
337         case name:      \
338             res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0;   \
339         break; \
340
341 #undef MONO_INST_NEW
342 #define MONO_INST_NEW(cfg,dest,op) do { \
343                 (dest) = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoInst));        \
344         (dest)->inst_p0 = (dest)->inst_p1 = (dest)->next = NULL; \
345                 (dest)->opcode = (op);  \
346         (dest)->flags = 0; \
347         (dest)->dreg = (dest)->sreg1 = (dest)->sreg2 = -1;  \
348         } while (0)
349
350 #define ALLOC_DEST(cfg, dest, ins) do { \
351     if (!(dest)) { \
352         MONO_INST_NEW ((cfg), (dest), -1); \
353         (dest)->dreg = (ins)->dreg; \
354     } \
355 } while (0)
356
357 /**
358  * mono_constant_fold_ins2:
359  *
360  * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is
361  * true, then store the result back into INS and return INS. Otherwise allocate a new ins,
362  * store the result into it and return it. If constant folding cannot be performed, return
363  * NULL.
364  */
365 MonoInst*
366 mono_constant_fold_ins2 (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite)
367 {
368         MonoInst *dest = NULL;
369
370         if (overwrite)
371                 dest = ins;
372
373         switch (ins->opcode) {
374         case OP_IMUL:
375         case OP_IADD:
376         case OP_IAND:
377         case OP_IOR:
378         case OP_IXOR:
379                 if (arg2->opcode == OP_ICONST) {
380                         if (arg1->opcode == OP_ICONST) {
381                                 ALLOC_DEST (cfg, dest, ins);
382                                 switch (ins->opcode) {
383                                         FOLD_BINOP2 (OP_IMUL, *);
384                                         FOLD_BINOP2 (OP_IADD, +);
385                                         FOLD_BINOP2 (OP_IAND, &);
386                                         FOLD_BINOP2 (OP_IOR, |);
387                                         FOLD_BINOP2 (OP_IXOR, ^);
388                                 }
389                                 dest->opcode = OP_ICONST;
390                                 dest->sreg1 = dest->sreg2 = -1;
391                         }
392                 } else if (arg1->opcode == OP_ICONST) {
393                         /* 
394                          * This is commutative so swap the arguments, allowing the _imm variant
395                          * to be used later.
396                          */
397                         if (mono_op_to_op_imm (ins->opcode) != -1) {
398                                 ALLOC_DEST (cfg, dest, ins);
399                                 dest->opcode = mono_op_to_op_imm (ins->opcode);
400                                 dest->sreg1 = ins->sreg2;
401                                 dest->sreg2 = -1;
402                                 dest->inst_imm = arg1->inst_c0;
403                         }
404                 }
405                 break;
406         case OP_IMUL_IMM:
407         case OP_IADD_IMM:
408         case OP_IAND_IMM:
409         case OP_IOR_IMM:
410         case OP_IXOR_IMM:
411         case OP_ISUB_IMM:
412         case OP_ISHL_IMM:
413         case OP_ISHR_IMM:
414         case OP_ISHR_UN_IMM:
415         case OP_SHL_IMM:
416                 if (arg1->opcode == OP_ICONST) {
417                         ALLOC_DEST (cfg, dest, ins);
418                         switch (ins->opcode) {
419                                 FOLD_BINOP2_IMM (OP_IMUL_IMM, *);
420                                 FOLD_BINOP2_IMM (OP_IADD_IMM, +);
421                                 FOLD_BINOP2_IMM (OP_IAND_IMM, &);
422                                 FOLD_BINOP2_IMM (OP_IOR_IMM, |);
423                                 FOLD_BINOP2_IMM (OP_IXOR_IMM, ^);
424                                 FOLD_BINOP2_IMM (OP_ISUB_IMM, -);
425                                 FOLD_BINOP2_IMM (OP_ISHL_IMM, <<);
426                                 FOLD_BINOP2_IMM (OP_ISHR_IMM, >>);
427                                 FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM, >>, guint32);
428                                 FOLD_BINOP2_IMM (OP_SHL_IMM, <<);
429                         }
430                         dest->opcode = OP_ICONST;
431                         dest->sreg1 = dest->sreg2 = -1;
432                 }
433                 break;
434         case OP_ISUB:
435         case OP_ISHL:
436         case OP_ISHR:
437         case OP_ISHR_UN:
438                 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
439                         ALLOC_DEST (cfg, dest, ins);
440                         switch (ins->opcode) {
441                                 FOLD_BINOP2 (OP_ISUB, -);
442                                 FOLD_BINOP2 (OP_ISHL, <<);
443                                 FOLD_BINOP2 (OP_ISHR, >>);
444                                 FOLD_BINOPC2 (OP_ISHR_UN, >>, guint32);
445                         }
446                         dest->opcode = OP_ICONST;
447                         dest->sreg1 = dest->sreg2 = -1;
448                 }
449                 break;
450         case OP_IDIV:
451         case OP_IDIV_UN:
452         case OP_IREM:
453         case OP_IREM_UN:
454                 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
455                         if ((arg2->inst_c0 == 0) || ((arg1->inst_c0 == G_MININT32) && (arg2->inst_c0 == -1)))
456                                 return NULL;
457                         ALLOC_DEST (cfg, dest, ins);
458                         switch (ins->opcode) {
459                                 FOLD_BINOPC2 (OP_IDIV, /, gint32);
460                                 FOLD_BINOPC2 (OP_IDIV_UN, /, guint32);
461                                 FOLD_BINOPC2 (OP_IREM, %, gint32);
462                                 FOLD_BINOPC2 (OP_IREM_UN, %, guint32);
463                         }
464                         dest->opcode = OP_ICONST;
465                         dest->sreg1 = dest->sreg2 = -1;
466                 }
467                 break;
468         case OP_IDIV_IMM:
469         case OP_IDIV_UN_IMM:
470         case OP_IREM_IMM:
471         case OP_IREM_UN_IMM:
472                 if (arg1->opcode == OP_ICONST) {
473                         if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1)))
474                                 return NULL;
475                         ALLOC_DEST (cfg, dest, ins);
476                         switch (ins->opcode) {
477                                 FOLD_BINOPC2_IMM (OP_IDIV_IMM, /, gint32);
478                                 FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM, /, guint32);
479                                 FOLD_BINOPC2_IMM (OP_IREM_IMM, %, gint32);
480                                 FOLD_BINOPC2_IMM (OP_IREM_UN_IMM, %, guint32);
481                         default:
482                                 g_assert_not_reached ();
483                         }
484                         dest->opcode = OP_ICONST;
485                         dest->sreg1 = dest->sreg2 = -1;
486                 }
487                 break;
488                 /* case OP_INEG: */
489         case OP_INOT:
490         case OP_INEG:
491                 if (arg1->opcode == OP_ICONST) {
492                         /* INEG sets cflags on x86, and the LNEG decomposition depends on that */
493                         if ((ins->opcode == OP_INEG) && ins->next && (ins->next->opcode == OP_ADC_IMM))
494                                 return NULL;
495                         ALLOC_DEST (cfg, dest, ins);
496                         switch (ins->opcode) {
497                                 FOLD_UNOP2 (OP_INEG,-);
498                                 FOLD_UNOP2 (OP_INOT,~);
499                         }
500                         dest->opcode = OP_ICONST;
501                         dest->sreg1 = dest->sreg2 = -1;
502                 }
503                 break;
504         case OP_MOVE:
505 #if SIZEOF_VOID_P == 8
506                 if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) {
507 #else
508                 if (arg1->opcode == OP_ICONST) {
509 #endif
510                         ALLOC_DEST (cfg, dest, ins);
511                         dest->opcode = arg1->opcode;
512                         dest->sreg1 = dest->sreg2 = -1;
513                         dest->inst_c0 = arg1->inst_c0;
514                 }
515                 break;
516         case OP_VMOVE:
517                 if (arg1->opcode == OP_VZERO) {
518                         ALLOC_DEST (cfg, dest, ins);
519                         dest->opcode = OP_VZERO;
520                         dest->sreg1 = -1;
521                 }
522                 break;
523         case OP_COMPARE:
524         case OP_ICOMPARE:
525         case OP_COMPARE_IMM:
526         case OP_ICOMPARE_IMM: {
527                 MonoInst dummy_arg2;
528                 if (ins->sreg2 == -1) {
529                         arg2 = &dummy_arg2;
530                         arg2->opcode = OP_ICONST;
531                         arg2->inst_c0 = ins->inst_imm;
532                 }
533
534                 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST) && ins->next) {
535                         MonoInst *next = ins->next;
536                         gboolean res = FALSE;
537
538                         switch (next->opcode) {
539                         case OP_CEQ:
540                         case OP_ICEQ:
541                         case OP_CGT:
542                         case OP_ICGT:
543                         case OP_CGT_UN:
544                         case OP_ICGT_UN:
545                         case OP_CLT:
546                         case OP_ICLT:
547                         case OP_CLT_UN:
548                         case OP_ICLT_UN:
549                                 switch (next->opcode) {
550                                         FOLD_BINOPCXX2 (OP_CEQ,==,gint32);
551                                         FOLD_BINOPCXX2 (OP_ICEQ,==,gint32);
552                                         FOLD_BINOPCXX2 (OP_CGT,>,gint32);
553                                         FOLD_BINOPCXX2 (OP_ICGT,>,gint32);
554                                         FOLD_BINOPCXX2 (OP_CGT_UN,>,guint32);
555                                         FOLD_BINOPCXX2 (OP_ICGT_UN,>,guint32);
556                                         FOLD_BINOPCXX2 (OP_CLT,<,gint32);
557                                         FOLD_BINOPCXX2 (OP_ICLT,<,gint32);
558                                         FOLD_BINOPCXX2 (OP_CLT_UN,<,guint32);
559                                         FOLD_BINOPCXX2 (OP_ICLT_UN,<,guint32);
560                                 }
561
562                                 if (overwrite) {
563                                         NULLIFY_INS (ins);
564                                         next->opcode = OP_ICONST;
565                                         next->inst_c0 = res;
566                                         next->sreg1 = next->sreg2 = -1;
567                                 } else {
568                                         ALLOC_DEST (cfg, dest, ins);
569                                         dest->opcode = OP_ICONST;
570                                         dest->inst_c0 = res;
571                                 }
572                                 break;
573                         case OP_IBEQ:
574                         case OP_IBNE_UN:
575                         case OP_IBGT:
576                         case OP_IBGT_UN:
577                         case OP_IBGE:
578                         case OP_IBGE_UN:
579                         case OP_IBLT:
580                         case OP_IBLT_UN:
581                         case OP_IBLE:
582                         case OP_IBLE_UN:
583                                 switch (next->opcode) {
584                                         FOLD_BINOPCXX2 (OP_IBEQ,==,gint32);
585                                         FOLD_BINOPCXX2 (OP_IBNE_UN,!=,guint32);
586                                         FOLD_BINOPCXX2 (OP_IBGT,>,gint32);
587                                         FOLD_BINOPCXX2 (OP_IBGT_UN,>,guint32);
588                                         FOLD_BINOPCXX2 (OP_IBGE,>=,gint32);
589                                         FOLD_BINOPCXX2 (OP_IBGE_UN,>=,guint32);
590                                         FOLD_BINOPCXX2 (OP_IBLT,<,gint32);
591                                         FOLD_BINOPCXX2 (OP_IBLT_UN,<,guint32);
592                                         FOLD_BINOPCXX2 (OP_IBLE,<=,gint32);
593                                         FOLD_BINOPCXX2 (OP_IBLE_UN,<=,guint32);
594                                 }
595
596                                 if (overwrite) {
597                                         /* 
598                                          * Can't nullify OP_COMPARE here since the decompose long branch 
599                                          * opcodes depend on it being executed. Also, the branch might not
600                                          * be eliminated after all if loop opts is disabled, for example.
601                                          */
602                                         if (res)
603                                                 next->flags |= MONO_INST_CFOLD_TAKEN;
604                                         else
605                                                 next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
606                                 } else {
607                                         ALLOC_DEST (cfg, dest, ins);
608                                         dest->opcode = OP_ICONST;
609                                         dest->inst_c0 = res;
610                                 }
611                                 break;
612                         case OP_NOP:
613                         case OP_BR:
614                                 /* This happens when a conditional branch is eliminated */
615                                 if (next->next == NULL) {
616                                         /* Last ins */
617                                         if (overwrite)
618                                                 NULLIFY_INS (ins);
619                                 }
620                                 break;
621                         default:
622                                 return NULL;
623                         }
624                 }
625                 break;
626         }
627         case OP_FMOVE:
628                 if (arg1->opcode == OP_R8CONST) {
629                         ALLOC_DEST (cfg, dest, ins);
630                         dest->opcode = OP_R8CONST;
631                         dest->sreg1 = -1;
632                         dest->inst_p0 = arg1->inst_p0;
633                 }
634                 break;
635
636                 /*
637                  * TODO: 
638                  *      conv.* opcodes.
639                  *      *ovf* opcodes? It's slow and hard to do in C.
640                  *      switch can be replaced by a simple jump 
641                  */
642         default:
643                 return NULL;
644         }
645                 
646     return dest;
647 }