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