2008-03-05 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
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_BINOPA(name,op,cast)       \
109         case name:      \
110                 if (inst->inst_i0->opcode != OP_ICONST) \
111                         return; \
112                 if (inst->inst_i1->opcode == OP_ICONST) {       \
113                         inst->opcode = OP_ICONST;       \
114                         inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
115                 } \
116                 return;
117         
118 #define FOLD_CXX(name,op,cast)  \
119         case name:      \
120                 if (inst->inst_i0->opcode != OP_COMPARE)        \
121                         return; \
122                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
123                         return; \
124                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
125                         inst->opcode = OP_ICONST;       \
126                         inst->inst_c0 = (cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0; \
127                 } \
128                 return;
129
130 #define FOLD_UNOP(name,op)      \
131         case name:      \
132                 if (inst->inst_i0->opcode == OP_ICONST) {       \
133                         inst->opcode = OP_ICONST;       \
134                         inst->inst_c0 = op inst->inst_i0->inst_c0; \
135                 } else if (inst->inst_i0->opcode == OP_I8CONST) { \
136                         inst->opcode = OP_I8CONST;      \
137                         inst->inst_l = op inst->inst_i0->inst_l; \
138                 } return;
139
140 #define FOLD_BRBINOP(name,op,cast)      \
141         case name:      \
142                 if (inst->inst_i0->opcode != OP_COMPARE)        \
143                         return; \
144                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
145                         return; \
146                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
147                         if ((cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0)     \
148                                 inst->opcode = OP_BR;   \
149                         else    \
150                                 inst->opcode = OP_NOP;  \
151                 } \
152                 return;
153
154 /*
155  * Helper function to do constant expression evaluation.
156  * We do constant folding of integers only, FP stuff is much more tricky,
157  * int64 probably not worth it.
158  */
159 void
160 mono_constant_fold_inst (MonoInst *inst, gpointer data)
161 {
162         switch (inst->opcode) {
163
164         /* FIXME: the CEE_B* don't contain operands, need to use the OP_COMPARE instruction */
165         /*FOLD_BRBINOP (CEE_BEQ,==,gint32)
166         FOLD_BRBINOP (CEE_BGE,>=,gint32)
167         FOLD_BRBINOP (CEE_BGT,>,gint32)
168         FOLD_BRBINOP (CEE_BLE,<=,gint32)
169         FOLD_BRBINOP (CEE_BLT,<,gint32)
170         FOLD_BRBINOP (CEE_BNE_UN,!=,guint32)
171         FOLD_BRBINOP (CEE_BGE_UN,>=,guint32)
172         FOLD_BRBINOP (CEE_BGT_UN,>,guint32)
173         FOLD_BRBINOP (CEE_BLE_UN,<=,guint32)
174         FOLD_BRBINOP (CEE_BLT_UN,<,guint32)*/
175
176         FOLD_BINOPCOMM (CEE_MUL,*)
177
178         FOLD_BINOPCOMM (CEE_ADD,+)
179         FOLD_BINOP (CEE_SUB,-)
180         FOLD_BINOPZ (CEE_DIV,/,gint32)
181         FOLD_BINOPZ (CEE_DIV_UN,/,guint32)
182         FOLD_BINOPZ (CEE_REM,%,gint32)
183         FOLD_BINOPZ (CEE_REM_UN,%,guint32)
184         FOLD_BINOPCOMM (CEE_AND,&)
185         FOLD_BINOPCOMM (CEE_OR,|)
186         FOLD_BINOPCOMM (CEE_XOR,^)
187         FOLD_BINOP (CEE_SHL,<<)
188         FOLD_BINOP (CEE_SHR,>>)
189         case CEE_SHR_UN:
190                 if (inst->inst_i0->opcode != OP_ICONST)
191                         return;
192                 if (inst->inst_i1->opcode == OP_ICONST) {
193                         inst->opcode = OP_ICONST;
194                         inst->inst_c0 = (guint32)inst->inst_i0->inst_c0 >> (guint32)inst->inst_i1->inst_c0;
195                 }
196                 return;
197         FOLD_UNOP (CEE_NEG,-)
198         FOLD_UNOP (CEE_NOT,~)
199         FOLD_CXX (OP_CEQ,==,gint32)
200         FOLD_CXX (OP_CGT,>,gint32)
201         FOLD_CXX (OP_CGT_UN,>,guint32)
202         FOLD_CXX (OP_CLT,<,gint32)
203         FOLD_CXX (OP_CLT_UN,<,guint32)
204         case CEE_CONV_I8:
205                 if (inst->inst_i0->opcode == OP_ICONST) {
206                         inst->opcode = OP_I8CONST;
207                         inst->inst_l = inst->inst_i0->inst_c0;
208                 }
209                 return;
210         case CEE_CONV_I:
211         case CEE_CONV_U:
212                 if (inst->inst_i0->opcode == OP_ICONST) {
213                         inst->opcode = OP_ICONST;
214                         inst->inst_c0 = inst->inst_i0->inst_c0;
215                 } else if (inst->inst_i0->opcode == CEE_LDIND_I) {
216                         *inst = *inst->inst_i0;
217                 }
218                 return;
219         /* we should be able to handle isinst and castclass as well */
220         case CEE_ISINST:
221         case CEE_CASTCLASS:
222         /*
223          * TODO: 
224          *      conv.* opcodes.
225          *      *ovf* opcodes? I'ts slow and hard to do in C.
226          *      switch can be replaced by a simple jump 
227          */
228 #if SIZEOF_VOID_P == 4
229         case CEE_CONV_I4:
230                 if ((inst->inst_left->type == STACK_I4) || (inst->inst_left->type == STACK_PTR)) {
231                         *inst = *inst->inst_left;
232                 }
233                 break;  
234 #endif
235         default:
236                 return;
237         }
238 }
239
240 void
241 mono_constant_fold (MonoCompile *cfg)
242 {
243         MonoBasicBlock *bb;
244         
245         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
246                 MonoInst *ins;
247                 MONO_BB_FOR_EACH_INS (bb, ins)
248                         mono_inst_foreach (ins, mono_constant_fold_inst, NULL);
249         }
250 }
251
252 /*
253  * If the arguments to the cond branch are constants, eval and
254  * return BRANCH_NOT_TAKEN for not taken, BRANCH_TAKEN for taken,
255  * BRANCH_UNDEF otherwise.
256  * If this code is changed to handle also non-const values, make sure
257  * side effects are handled in optimize_branches() in mini.c, by
258  * inserting pop instructions.
259  */
260 int
261 mono_eval_cond_branch (MonoInst *ins)
262 {
263         MonoInst *left, *right;
264         /* FIXME: handle also 64 bit ints */
265         left = ins->inst_left->inst_left;
266         if (left->opcode != OP_ICONST && left->opcode != OP_PCONST)
267                 return BRANCH_UNDEF;
268         right = ins->inst_left->inst_right;
269         if (right->opcode != OP_ICONST && right->opcode != OP_PCONST)
270                 return BRANCH_UNDEF;
271         switch (ins->opcode) {
272         case CEE_BEQ:
273                 if (left->inst_c0 == right->inst_c0)
274                         return BRANCH_TAKEN;
275                 return BRANCH_NOT_TAKEN;
276         case CEE_BGE:
277                 if (left->inst_c0 >= right->inst_c0)
278                         return BRANCH_TAKEN;
279                 return BRANCH_NOT_TAKEN;
280         case CEE_BGT:
281                 if (left->inst_c0 > right->inst_c0)
282                         return BRANCH_TAKEN;
283                 return BRANCH_NOT_TAKEN;
284         case CEE_BLE:
285                 if (left->inst_c0 <= right->inst_c0)
286                         return BRANCH_TAKEN;
287                 return BRANCH_NOT_TAKEN;
288         case CEE_BLT:
289                 if (left->inst_c0 < right->inst_c0)
290                         return BRANCH_TAKEN;
291                 return BRANCH_NOT_TAKEN;
292         case CEE_BNE_UN:
293                 if ((gsize)left->inst_c0 != (gsize)right->inst_c0)
294                         return BRANCH_TAKEN;
295                 return BRANCH_NOT_TAKEN;
296         case CEE_BGE_UN:
297                 if ((gsize)left->inst_c0 >= (gsize)right->inst_c0)
298                         return BRANCH_TAKEN;
299                 return BRANCH_NOT_TAKEN;
300         case CEE_BGT_UN:
301                 if ((gsize)left->inst_c0 > (gsize)right->inst_c0)
302                         return BRANCH_TAKEN;
303                 return BRANCH_NOT_TAKEN;
304         case CEE_BLE_UN:
305                 if ((gsize)left->inst_c0 <= (gsize)right->inst_c0)
306                         return BRANCH_TAKEN;
307                 return BRANCH_NOT_TAKEN;
308         case CEE_BLT_UN:
309                 if ((gsize)left->inst_c0 < (gsize)right->inst_c0)
310                         return BRANCH_TAKEN;
311                 return BRANCH_NOT_TAKEN;
312         }
313         return BRANCH_UNDEF;
314 }
315