Don't run test-318 with gmcs.
[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 = 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 = 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_MUL) {    \
55                         int power2;     \
56                         if (inst->inst_i1->inst_c0 == 1) {      \
57                                 *inst = *(inst->inst_i0);       \
58                                 return; \
59                         } else if (inst->inst_i1->inst_c0 == -1) {      \
60                                 inst->opcode = CEE_NEG; \
61                                 return; \
62                         }       \
63                         power2 = mono_is_power_of_two (inst->inst_i1->inst_c0); \
64                         if (power2 < 0) return; \
65                         inst->opcode = CEE_SHL; \
66                         inst->inst_i1->inst_c0 = power2;        \
67                 } \
68                 return;
69
70 #ifndef G_MININT32
71 #define MYGINT32_MAX 2147483647
72 #define G_MININT32 (-MYGINT32_MAX -1)
73 #endif
74
75 /* 
76  * We can't let this cause a division by zero exception since the division 
77  * might not be executed during runtime.
78  */
79 #define FOLD_BINOPZ(name,op,cast)       \
80         case name:      \
81                 if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_REM_UN && inst->inst_i1->inst_c0 == 2) {  \
82                         inst->opcode = CEE_AND; \
83                         inst->inst_i1->inst_c0 = 1;     \
84                         return; \
85                 }       \
86                 if (inst->inst_i1->opcode == OP_ICONST) {       \
87                         if (!inst->inst_i1->inst_c0) return;    \
88                         if (inst->inst_i0->opcode == OP_ICONST) {       \
89                 if ((inst->inst_i0->inst_c0 == G_MININT32) && (inst->inst_i1->inst_c0 == -1)) \
90                     return; \
91                                 inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
92                                 inst->opcode = OP_ICONST;       \
93                         } else {        \
94                                 int power2 = mono_is_power_of_two (inst->inst_i1->inst_c0);     \
95                                 if (power2 < 0) return; \
96                                 if (inst->opcode == CEE_REM_UN) {       \
97                                         inst->opcode = CEE_AND; \
98                                         inst->inst_i1->inst_c0 = (1 << power2) - 1;     \
99                                 } else if (inst->opcode == CEE_DIV_UN) {        \
100                                         inst->opcode = CEE_SHR_UN;      \
101                                         inst->inst_i1->inst_c0 = power2;        \
102                                 }       \
103                         }       \
104                 } \
105                 return;
106         
107 #define FOLD_BINOPA(name,op,cast)       \
108         case name:      \
109                 if (inst->inst_i0->opcode != OP_ICONST) \
110                         return; \
111                 if (inst->inst_i1->opcode == OP_ICONST) {       \
112                         inst->opcode = OP_ICONST;       \
113                         inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0;   \
114                 } \
115                 return;
116         
117 #define FOLD_CXX(name,op,cast)  \
118         case name:      \
119                 if (inst->inst_i0->opcode != OP_COMPARE)        \
120                         return; \
121                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
122                         return; \
123                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
124                         inst->opcode = OP_ICONST;       \
125                         inst->inst_c0 = (cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0; \
126                 } \
127                 return;
128
129 #define FOLD_UNOP(name,op)      \
130         case name:      \
131                 if (inst->inst_i0->opcode == OP_ICONST) {       \
132                         inst->opcode = OP_ICONST;       \
133                         inst->inst_c0 = op inst->inst_i0->inst_c0; \
134                 } else if (inst->inst_i0->opcode == OP_I8CONST) { \
135                         inst->opcode = OP_I8CONST;      \
136                         inst->inst_l = op inst->inst_i0->inst_l; \
137                 } return;
138
139 #define FOLD_BRBINOP(name,op,cast)      \
140         case name:      \
141                 if (inst->inst_i0->opcode != OP_COMPARE)        \
142                         return; \
143                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
144                         return; \
145                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
146                         if ((cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0)     \
147                                 inst->opcode = CEE_BR;  \
148                         else    \
149                                 inst->opcode = CEE_NOP; \
150                 } \
151                 return;
152
153 /*
154  * Helper function to do constant expression evaluation.
155  * We do constant folding of integers only, FP stuff is much more tricky,
156  * int64 probably not worth it.
157  */
158 void
159 mono_constant_fold_inst (MonoInst *inst, gpointer data)
160 {
161         switch (inst->opcode) {
162
163         /* FIXME: the CEE_B* don't contain operands, need to use the OP_COMPARE instruction */
164         /*FOLD_BRBINOP (CEE_BEQ,==,gint32)
165         FOLD_BRBINOP (CEE_BGE,>=,gint32)
166         FOLD_BRBINOP (CEE_BGT,>,gint32)
167         FOLD_BRBINOP (CEE_BLE,<=,gint32)
168         FOLD_BRBINOP (CEE_BLT,<,gint32)
169         FOLD_BRBINOP (CEE_BNE_UN,!=,guint32)
170         FOLD_BRBINOP (CEE_BGE_UN,>=,guint32)
171         FOLD_BRBINOP (CEE_BGT_UN,>,guint32)
172         FOLD_BRBINOP (CEE_BLE_UN,<=,guint32)
173         FOLD_BRBINOP (CEE_BLT_UN,<,guint32)*/
174
175         FOLD_BINOPCOMM (CEE_MUL,*)
176
177         FOLD_BINOPCOMM (CEE_ADD,+)
178         FOLD_BINOP (CEE_SUB,-)
179         FOLD_BINOPZ (CEE_DIV,/,gint32)
180         FOLD_BINOPZ (CEE_DIV_UN,/,guint32)
181         FOLD_BINOPZ (CEE_REM,%,gint32)
182         FOLD_BINOPZ (CEE_REM_UN,%,guint32)
183         FOLD_BINOPCOMM (CEE_AND,&)
184         FOLD_BINOPCOMM (CEE_OR,|)
185         FOLD_BINOPCOMM (CEE_XOR,^)
186         FOLD_BINOP (CEE_SHL,<<)
187         FOLD_BINOP (CEE_SHR,>>)
188         case CEE_SHR_UN:
189                 if (inst->inst_i0->opcode != OP_ICONST)
190                         return;
191                 if (inst->inst_i1->opcode == OP_ICONST) {
192                         inst->opcode = OP_ICONST;
193                         inst->inst_c0 = (guint32)inst->inst_i0->inst_c0 >> (guint32)inst->inst_i1->inst_c0;
194                 }
195                 return;
196         FOLD_UNOP (CEE_NEG,-)
197         FOLD_UNOP (CEE_NOT,~)
198         FOLD_CXX (OP_CEQ,==,gint32)
199         FOLD_CXX (OP_CGT,>,gint32)
200         FOLD_CXX (OP_CGT_UN,>,guint32)
201         FOLD_CXX (OP_CLT,<,gint32)
202         FOLD_CXX (OP_CLT_UN,<,guint32)
203         case CEE_CONV_I8:
204                 if (inst->inst_i0->opcode == OP_ICONST) {
205                         inst->opcode = OP_I8CONST;
206                         inst->inst_l = inst->inst_i0->inst_c0;
207                 }
208                 return;
209         case CEE_CONV_I:
210         case CEE_CONV_U:
211                 if (inst->inst_i0->opcode == OP_ICONST) {
212                         inst->opcode = OP_ICONST;
213                         inst->inst_c0 = inst->inst_i0->inst_c0;
214                 } else if (inst->inst_i0->opcode == CEE_LDIND_I) {
215                         *inst = *inst->inst_i0;
216                 }
217                 return;
218         /* we should be able to handle isinst and castclass as well */
219         case CEE_ISINST:
220         case CEE_CASTCLASS:
221         /*
222          * TODO: 
223          *      conv.* opcodes.
224          *      *ovf* opcodes? I'ts slow and hard to do in C.
225          *      switch can be replaced by a simple jump 
226          */
227 #if SIZEOF_VOID_P == 4
228         case CEE_CONV_I4:
229                 if ((inst->inst_left->type == STACK_I4) || (inst->inst_left->type == STACK_PTR)) {
230                         *inst = *inst->inst_left;
231                 }
232                 break;  
233 #endif
234         default:
235                 return;
236         }
237 }
238
239 void
240 mono_constant_fold (MonoCompile *cfg)
241 {
242         MonoBasicBlock *bb;
243         
244         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
245                 MonoInst *ins;
246                 for (ins = bb->code; ins; ins = ins->next)      
247                         mono_inst_foreach (ins, mono_constant_fold_inst, NULL);
248         }
249 }
250