update rx (mobile builds).
[mono-project.git] / mono / mini / cfold.c
blobd9e35d4702ed2ccd72b686482737bd05be03f6d3
1 /*
2 * cfold.c: Constant folding support
4 * Author:
5 * Paolo Molaro (lupus@ximian.com)
6 * Dietmar Maurer (dietmar@ximian.com)
8 * (C) 2003 Ximian, Inc. http://www.ximian.com
9 */
10 #include "mini.h"
11 #include "ir-emit.h"
13 int
14 mono_is_power_of_two (guint32 val)
16 int i, j, k;
18 for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
19 if (val & j)
20 break;
22 if (i == 32 || val & k)
23 return -1;
24 return i;
27 #ifndef G_MININT32
28 #define MYGINT32_MAX 2147483647
29 #define G_MININT32 (-MYGINT32_MAX -1)
30 #endif
32 #define FOLD_UNOP(name,op) \
33 case name: \
34 dest->inst_c0 = op arg1->inst_c0; \
35 break;
37 #define FOLD_BINOP(name, op) \
38 case name: \
39 dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0; \
40 break;
42 #define FOLD_BINOPC(name,op,cast) \
43 case name: \
44 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
45 break;
47 #define FOLD_BINOP2_IMM(name, op) \
48 case name: \
49 dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \
50 break;
52 #define FOLD_BINOPC2_IMM(name, op, cast) \
53 case name: \
54 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
55 break;
57 #define FOLD_BINOPCXX(name,op,cast) \
58 case name: \
59 res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
60 break; \
62 #define ALLOC_DEST(cfg, dest, ins) do { \
63 if (!(dest)) { \
64 MONO_INST_NEW ((cfg), (dest), -1); \
65 (dest)->dreg = (ins)->dreg; \
66 } \
67 } while (0)
69 /**
70 * mono_constant_fold_ins:
72 * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is
73 * true, then store the result back into INS and return INS. Otherwise allocate a new ins,
74 * store the result into it and return it. If constant folding cannot be performed, return
75 * NULL.
77 MonoInst*
78 mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite)
80 MonoInst *dest = NULL;
82 if (overwrite)
83 dest = ins;
85 switch (ins->opcode) {
86 case OP_IMUL:
87 case OP_IADD:
88 case OP_IAND:
89 case OP_IOR:
90 case OP_IXOR:
91 if (arg2->opcode == OP_ICONST) {
92 if (arg1->opcode == OP_ICONST) {
93 ALLOC_DEST (cfg, dest, ins);
94 switch (ins->opcode) {
95 FOLD_BINOP (OP_IMUL, *);
96 FOLD_BINOP (OP_IADD, +);
97 FOLD_BINOP (OP_IAND, &);
98 FOLD_BINOP (OP_IOR, |);
99 FOLD_BINOP (OP_IXOR, ^);
101 dest->opcode = OP_ICONST;
102 MONO_INST_NULLIFY_SREGS (dest);
104 } else if (arg1->opcode == OP_ICONST) {
106 * This is commutative so swap the arguments, allowing the _imm variant
107 * to be used later.
109 if (mono_op_to_op_imm (ins->opcode) != -1) {
110 ALLOC_DEST (cfg, dest, ins);
111 dest->opcode = mono_op_to_op_imm (ins->opcode);
112 dest->sreg1 = ins->sreg2;
113 dest->sreg2 = -1;
114 dest->inst_imm = arg1->inst_c0;
117 break;
118 case OP_IMUL_IMM:
119 case OP_IADD_IMM:
120 case OP_IAND_IMM:
121 case OP_IOR_IMM:
122 case OP_IXOR_IMM:
123 case OP_ISUB_IMM:
124 case OP_ISHL_IMM:
125 case OP_ISHR_IMM:
126 case OP_ISHR_UN_IMM:
127 case OP_SHL_IMM:
128 if (arg1->opcode == OP_ICONST) {
129 ALLOC_DEST (cfg, dest, ins);
130 switch (ins->opcode) {
131 FOLD_BINOP2_IMM (OP_IMUL_IMM, *);
132 FOLD_BINOP2_IMM (OP_IADD_IMM, +);
133 FOLD_BINOP2_IMM (OP_IAND_IMM, &);
134 FOLD_BINOP2_IMM (OP_IOR_IMM, |);
135 FOLD_BINOP2_IMM (OP_IXOR_IMM, ^);
136 FOLD_BINOP2_IMM (OP_ISUB_IMM, -);
137 FOLD_BINOPC2_IMM (OP_ISHL_IMM, <<, gint32);
138 FOLD_BINOPC2_IMM (OP_ISHR_IMM, >>, gint32);
139 FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM, >>, guint32);
140 FOLD_BINOP2_IMM (OP_SHL_IMM, <<);
142 dest->opcode = OP_ICONST;
143 MONO_INST_NULLIFY_SREGS (dest);
145 break;
146 case OP_ISUB:
147 case OP_ISHL:
148 case OP_ISHR:
149 case OP_ISHR_UN:
150 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
151 ALLOC_DEST (cfg, dest, ins);
152 switch (ins->opcode) {
153 FOLD_BINOP (OP_ISUB, -);
154 FOLD_BINOP (OP_ISHL, <<);
155 FOLD_BINOP (OP_ISHR, >>);
156 FOLD_BINOPC (OP_ISHR_UN, >>, guint32);
158 dest->opcode = OP_ICONST;
159 MONO_INST_NULLIFY_SREGS (dest);
161 break;
162 case OP_IDIV:
163 case OP_IDIV_UN:
164 case OP_IREM:
165 case OP_IREM_UN:
166 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
167 if ((arg2->inst_c0 == 0) || ((arg1->inst_c0 == G_MININT32) && (arg2->inst_c0 == -1)))
168 return NULL;
169 ALLOC_DEST (cfg, dest, ins);
170 switch (ins->opcode) {
171 FOLD_BINOPC (OP_IDIV, /, gint32);
172 FOLD_BINOPC (OP_IDIV_UN, /, guint32);
173 FOLD_BINOPC (OP_IREM, %, gint32);
174 FOLD_BINOPC (OP_IREM_UN, %, guint32);
176 dest->opcode = OP_ICONST;
177 MONO_INST_NULLIFY_SREGS (dest);
179 break;
180 case OP_IDIV_IMM:
181 case OP_IDIV_UN_IMM:
182 case OP_IREM_IMM:
183 case OP_IREM_UN_IMM:
184 if (arg1->opcode == OP_ICONST) {
185 if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1)))
186 return NULL;
187 ALLOC_DEST (cfg, dest, ins);
188 switch (ins->opcode) {
189 FOLD_BINOPC2_IMM (OP_IDIV_IMM, /, gint32);
190 FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM, /, guint32);
191 FOLD_BINOPC2_IMM (OP_IREM_IMM, %, gint32);
192 FOLD_BINOPC2_IMM (OP_IREM_UN_IMM, %, guint32);
193 default:
194 g_assert_not_reached ();
196 dest->opcode = OP_ICONST;
197 MONO_INST_NULLIFY_SREGS (dest);
199 break;
200 /* case OP_INEG: */
201 case OP_INOT:
202 case OP_INEG:
203 if (arg1->opcode == OP_ICONST) {
204 /* INEG sets cflags on x86, and the LNEG decomposition depends on that */
205 #if SIZEOF_REGISTER == 4
206 if (ins->opcode == OP_INEG)
207 return NULL;
208 #endif
209 ALLOC_DEST (cfg, dest, ins);
210 switch (ins->opcode) {
211 FOLD_UNOP (OP_INEG,-);
212 FOLD_UNOP (OP_INOT,~);
214 dest->opcode = OP_ICONST;
215 MONO_INST_NULLIFY_SREGS (dest);
217 break;
218 case OP_MOVE:
219 #if SIZEOF_REGISTER == 8
220 if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) {
221 #else
222 if (arg1->opcode == OP_ICONST) {
223 #endif
224 ALLOC_DEST (cfg, dest, ins);
225 dest->opcode = arg1->opcode;
226 MONO_INST_NULLIFY_SREGS (dest);
227 dest->inst_c0 = arg1->inst_c0;
229 break;
230 case OP_VMOVE:
231 if (arg1->opcode == OP_VZERO) {
232 ALLOC_DEST (cfg, dest, ins);
233 dest->opcode = OP_VZERO;
234 dest->sreg1 = -1;
236 break;
237 case OP_XMOVE:
238 if (arg1->opcode == OP_XZERO) {
239 ALLOC_DEST (cfg, dest, ins);
240 dest->opcode = OP_XZERO;
241 dest->sreg1 = -1;
243 break;
244 case OP_COMPARE:
245 case OP_ICOMPARE:
246 case OP_COMPARE_IMM:
247 case OP_ICOMPARE_IMM: {
248 MonoInst dummy_arg2;
249 if (ins->sreg2 == -1) {
250 arg2 = &dummy_arg2;
251 arg2->opcode = OP_ICONST;
252 arg2->inst_c0 = ins->inst_imm;
255 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST) && ins->next) {
256 MonoInst *next = ins->next;
257 gboolean res = FALSE;
259 switch (next->opcode) {
260 case OP_CEQ:
261 case OP_ICEQ:
262 case OP_CGT:
263 case OP_ICGT:
264 case OP_CGT_UN:
265 case OP_ICGT_UN:
266 case OP_CLT:
267 case OP_ICLT:
268 case OP_CLT_UN:
269 case OP_ICLT_UN:
270 switch (next->opcode) {
271 FOLD_BINOPCXX (OP_CEQ,==,gint32);
272 FOLD_BINOPCXX (OP_ICEQ,==,gint32);
273 FOLD_BINOPCXX (OP_CGT,>,gint32);
274 FOLD_BINOPCXX (OP_ICGT,>,gint32);
275 FOLD_BINOPCXX (OP_CGT_UN,>,guint32);
276 FOLD_BINOPCXX (OP_ICGT_UN,>,guint32);
277 FOLD_BINOPCXX (OP_CLT,<,gint32);
278 FOLD_BINOPCXX (OP_ICLT,<,gint32);
279 FOLD_BINOPCXX (OP_CLT_UN,<,guint32);
280 FOLD_BINOPCXX (OP_ICLT_UN,<,guint32);
283 if (overwrite) {
284 NULLIFY_INS (ins);
285 next->opcode = OP_ICONST;
286 next->inst_c0 = res;
287 MONO_INST_NULLIFY_SREGS (next);
288 } else {
289 ALLOC_DEST (cfg, dest, ins);
290 dest->opcode = OP_ICONST;
291 dest->inst_c0 = res;
293 break;
294 case OP_IBEQ:
295 case OP_IBNE_UN:
296 case OP_IBGT:
297 case OP_IBGT_UN:
298 case OP_IBGE:
299 case OP_IBGE_UN:
300 case OP_IBLT:
301 case OP_IBLT_UN:
302 case OP_IBLE:
303 case OP_IBLE_UN:
304 switch (next->opcode) {
305 FOLD_BINOPCXX (OP_IBEQ,==,gint32);
306 FOLD_BINOPCXX (OP_IBNE_UN,!=,guint32);
307 FOLD_BINOPCXX (OP_IBGT,>,gint32);
308 FOLD_BINOPCXX (OP_IBGT_UN,>,guint32);
309 FOLD_BINOPCXX (OP_IBGE,>=,gint32);
310 FOLD_BINOPCXX (OP_IBGE_UN,>=,guint32);
311 FOLD_BINOPCXX (OP_IBLT,<,gint32);
312 FOLD_BINOPCXX (OP_IBLT_UN,<,guint32);
313 FOLD_BINOPCXX (OP_IBLE,<=,gint32);
314 FOLD_BINOPCXX (OP_IBLE_UN,<=,guint32);
317 if (overwrite) {
319 * Can't nullify OP_COMPARE here since the decompose long branch
320 * opcodes depend on it being executed. Also, the branch might not
321 * be eliminated after all if loop opts is disabled, for example.
323 if (res)
324 next->flags |= MONO_INST_CFOLD_TAKEN;
325 else
326 next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
327 } else {
328 ALLOC_DEST (cfg, dest, ins);
329 dest->opcode = OP_ICONST;
330 dest->inst_c0 = res;
332 break;
333 case OP_NOP:
334 case OP_BR:
335 /* This happens when a conditional branch is eliminated */
336 if (next->next == NULL) {
337 /* Last ins */
338 if (overwrite)
339 NULLIFY_INS (ins);
341 break;
342 default:
343 return NULL;
346 break;
348 case OP_FMOVE:
349 if (arg1->opcode == OP_R8CONST) {
350 ALLOC_DEST (cfg, dest, ins);
351 dest->opcode = OP_R8CONST;
352 dest->sreg1 = -1;
353 dest->inst_p0 = arg1->inst_p0;
355 break;
358 * TODO:
359 * conv.* opcodes.
360 * *ovf* opcodes? It's slow and hard to do in C.
361 * switch can be replaced by a simple jump
363 default:
364 return NULL;
367 return dest;