New compilation engine for Mono
[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 static int
13 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 = 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 = 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                         return; \
123                 inst->opcode = OP_ICONST;       \
124                 inst->inst_c0 = op inst->inst_i0->inst_c0; \
125                 return;
126
127 #define FOLD_BRBINOP(name,op,cast)      \
128         case name:      \
129                 if (inst->inst_i0->opcode != OP_COMPARE)        \
130                         return; \
131                 if (inst->inst_i0->inst_i0->opcode != OP_ICONST)        \
132                         return; \
133                 if (inst->inst_i0->inst_i1->opcode == OP_ICONST) {      \
134                         if ((cast)inst->inst_i0->inst_i0->inst_c0 op (cast)inst->inst_i0->inst_i1->inst_c0)     \
135                                 inst->opcode = CEE_BR;  \
136                         else    \
137                                 inst->opcode = CEE_NOP; \
138                 } \
139                 return;
140
141 /*
142  * Helper function to do constant expression evaluation.
143  * We do constant folding of integers only, FP stuff is much more tricky,
144  * int64 probably not worth it.
145  */
146 void
147 mono_constant_fold_inst (MonoInst *inst, gpointer data)
148 {
149         switch (inst->opcode) {
150
151         /* FIXME: the CEE_B* don't contain operands, need to use the OP_COMPARE instruction */
152         /*FOLD_BRBINOP (CEE_BEQ,==,gint32)
153         FOLD_BRBINOP (CEE_BGE,>=,gint32)
154         FOLD_BRBINOP (CEE_BGT,>,gint32)
155         FOLD_BRBINOP (CEE_BLE,<=,gint32)
156         FOLD_BRBINOP (CEE_BLT,<,gint32)
157         FOLD_BRBINOP (CEE_BNE_UN,!=,guint32)
158         FOLD_BRBINOP (CEE_BGE_UN,>=,guint32)
159         FOLD_BRBINOP (CEE_BGT_UN,>,guint32)
160         FOLD_BRBINOP (CEE_BLE_UN,<=,guint32)
161         FOLD_BRBINOP (CEE_BLT_UN,<,guint32)*/
162
163         FOLD_BINOPCOMM (CEE_MUL,*)
164
165         FOLD_BINOPCOMM (CEE_ADD,+)
166         FOLD_BINOP (CEE_SUB,-)
167         FOLD_BINOPZ (CEE_DIV,/,gint32)
168         FOLD_BINOPZ (CEE_DIV_UN,/,guint32)
169         FOLD_BINOPZ (CEE_REM,%,gint32)
170         FOLD_BINOPZ (CEE_REM_UN,%,guint32)
171         FOLD_BINOPCOMM (CEE_AND,&)
172         FOLD_BINOPCOMM (CEE_OR,|)
173         FOLD_BINOPCOMM (CEE_XOR,^)
174         FOLD_BINOP (CEE_SHL,<<)
175         FOLD_BINOP (CEE_SHR,>>)
176         case CEE_SHR_UN:
177                 if (inst->inst_i0->opcode != OP_ICONST)
178                         return;
179                 if (inst->inst_i1->opcode == OP_ICONST) {
180                         inst->opcode = OP_ICONST;
181                         inst->inst_c0 = (guint32)inst->inst_i0->inst_c0 >> (guint32)inst->inst_i1->inst_c0;
182                 }
183                 return;
184         FOLD_UNOP (CEE_NEG,-)
185         FOLD_UNOP (CEE_NOT,~)
186         FOLD_CXX (OP_CEQ,==,gint32)
187         FOLD_CXX (OP_CGT,>,gint32)
188         FOLD_CXX (OP_CGT_UN,>,guint32)
189         FOLD_CXX (OP_CLT,<,gint32)
190         FOLD_CXX (OP_CLT_UN,<,guint32)
191         /* we should be able to handle isinst and castclass as well */
192         case CEE_ISINST:
193         case CEE_CASTCLASS:
194         /*
195          * TODO: 
196          *      conv.* opcodes.
197          *      *ovf* opcodes? I'ts slow and hard to do in C.
198          *      switch can be replaced by a simple jump 
199          */
200         default:
201                 return;
202         }
203 }
204
205 void
206 mono_constant_fold (MonoCompile *cfg)
207 {
208         MonoBasicBlock *bb;
209         
210         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
211                 MonoInst *ins;
212                 for (ins = bb->code; ins; ins = ins->next)      
213                         mono_inst_foreach (ins, mono_constant_fold_inst, NULL);
214         }
215 }
216