2 * cfold.c: Constant folding support
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2003 Ximian, Inc. http://www.ximian.com
13 mono_is_power_of_two (guint32 val)
17 for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
21 if (i == 32 || val & k)
26 #define FOLD_BINOP(name,op) \
28 if (inst->inst_i0->opcode != OP_ICONST) \
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); \
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.
41 #define FOLD_BINOPCOMM(name,op) \
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); \
49 MonoInst *tmp = inst->inst_i0; \
50 inst->inst_i0 = inst->inst_i1; \
51 inst->inst_i1 = tmp; \
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); \
60 if (inst->inst_i1->opcode == OP_ICONST && inst->opcode == CEE_MUL) { \
62 if (inst->inst_i1->inst_c0 == 1) { \
63 *inst = *(inst->inst_i0); \
65 } else if (inst->inst_i1->inst_c0 == -1) { \
66 inst->opcode = CEE_NEG; \
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; \
77 * We can't let this cause a division by zero exception since the division
78 * might not be executed during runtime.
80 #define FOLD_BINOPZ(name,op,cast) \
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; \
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)) \
92 inst->inst_c0 = (cast)inst->inst_i0->inst_c0 op (cast)inst->inst_i1->inst_c0; \
93 inst->opcode = OP_ICONST; \
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; \
108 #define FOLD_BINOPA(name,op,cast) \
110 if (inst->inst_i0->opcode != OP_ICONST) \
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; \
118 #define FOLD_CXX(name,op,cast) \
120 if (inst->inst_i0->opcode != OP_COMPARE) \
122 if (inst->inst_i0->inst_i0->opcode != OP_ICONST) \
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; \
130 #define FOLD_UNOP(name,op) \
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; \
140 #define FOLD_BRBINOP(name,op,cast) \
142 if (inst->inst_i0->opcode != OP_COMPARE) \
144 if (inst->inst_i0->inst_i0->opcode != OP_ICONST) \
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; \
150 inst->opcode = OP_NOP; \
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.
160 mono_constant_fold_inst (MonoInst *inst, gpointer data)
162 switch (inst->opcode) {
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)*/
176 FOLD_BINOPCOMM (CEE_MUL,*)
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,>>)
190 if (inst->inst_i0->opcode != OP_ICONST)
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;
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)
205 if (inst->inst_i0->opcode == OP_ICONST) {
206 inst->opcode = OP_I8CONST;
207 inst->inst_l = inst->inst_i0->inst_c0;
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;
219 /* we should be able to handle isinst and castclass as well */
225 * *ovf* opcodes? I'ts slow and hard to do in C.
226 * switch can be replaced by a simple jump
228 #if SIZEOF_VOID_P == 4
230 if ((inst->inst_left->type == STACK_I4) || (inst->inst_left->type == STACK_PTR)) {
231 *inst = *inst->inst_left;
241 mono_constant_fold (MonoCompile *cfg)
245 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
247 MONO_BB_FOR_EACH_INS (bb, ins)
248 mono_inst_foreach (ins, mono_constant_fold_inst, NULL);
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.
261 mono_eval_cond_branch (MonoInst *ins)
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)
268 right = ins->inst_left->inst_right;
269 if (right->opcode != OP_ICONST && right->opcode != OP_PCONST)
271 switch (ins->opcode) {
273 if (left->inst_c0 == right->inst_c0)
275 return BRANCH_NOT_TAKEN;
277 if (left->inst_c0 >= right->inst_c0)
279 return BRANCH_NOT_TAKEN;
281 if (left->inst_c0 > right->inst_c0)
283 return BRANCH_NOT_TAKEN;
285 if (left->inst_c0 <= right->inst_c0)
287 return BRANCH_NOT_TAKEN;
289 if (left->inst_c0 < right->inst_c0)
291 return BRANCH_NOT_TAKEN;
293 if ((gsize)left->inst_c0 != (gsize)right->inst_c0)
295 return BRANCH_NOT_TAKEN;
297 if ((gsize)left->inst_c0 >= (gsize)right->inst_c0)
299 return BRANCH_NOT_TAKEN;
301 if ((gsize)left->inst_c0 > (gsize)right->inst_c0)
303 return BRANCH_NOT_TAKEN;
305 if ((gsize)left->inst_c0 <= (gsize)right->inst_c0)
307 return BRANCH_NOT_TAKEN;
309 if ((gsize)left->inst_c0 < (gsize)right->inst_c0)
311 return BRANCH_NOT_TAKEN;