[coop] Use unbalanced transition to GC Unsafe in mono_raise_exception
[mono-project.git] / mono / mini / cfold.c
blob66bb216e4755c76a03c3c90c2ed934edaa8e74f1
1 /**
2 * \file
3 * Constant folding support
5 * Author:
6 * Paolo Molaro (lupus@ximian.com)
7 * Dietmar Maurer (dietmar@ximian.com)
9 * (C) 2003 Ximian, Inc. http://www.ximian.com
11 #include <config.h>
13 #include "mini.h"
14 #include "ir-emit.h"
16 /* WTF is this doing here?!?!? */
17 int
18 mono_is_power_of_two (guint32 val)
20 int i, j, k;
22 for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
23 if (val & j)
24 break;
26 if (i == 32 || val & k)
27 return -1;
28 return i;
31 #ifndef G_MININT32
32 #define MYGINT32_MAX 2147483647
33 #define G_MININT32 (-MYGINT32_MAX -1)
34 #endif
36 #define FOLD_UNOP(name,op) \
37 case name: \
38 dest->inst_c0 = op arg1->inst_c0; \
39 break;
41 #define FOLD_BINOP(name, op) \
42 case name: \
43 dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0; \
44 break;
46 #define FOLD_BINOPC(name,op,cast) \
47 case name: \
48 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
49 break;
51 #define FOLD_BINOP2_IMM(name, op) \
52 case name: \
53 dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \
54 break;
56 #define FOLD_BINOPC2_IMM(name, op, cast) \
57 case name: \
58 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
59 break;
61 #define FOLD_BINOPCXX(name,op,cast) \
62 case name: \
63 res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
64 break; \
66 #define ALLOC_DEST(cfg, dest, ins) do { \
67 if (!(dest)) { \
68 MONO_INST_NEW ((cfg), (dest), -1); \
69 (dest)->dreg = (ins)->dreg; \
70 } \
71 } while (0)
73 #ifndef DISABLE_JIT
75 /**
76 * mono_constant_fold_ins:
78 * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is
79 * true, then store the result back into INS and return INS. Otherwise allocate a new ins,
80 * store the result into it and return it. If constant folding cannot be performed, return
81 * NULL.
83 MonoInst*
84 mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite)
86 MonoInst *dest = NULL;
88 if (overwrite)
89 dest = ins;
91 switch (ins->opcode) {
92 case OP_IMUL:
93 case OP_IADD:
94 case OP_IAND:
95 case OP_IOR:
96 case OP_IXOR:
97 if (arg2->opcode == OP_ICONST) {
98 if (arg1->opcode == OP_ICONST) {
99 ALLOC_DEST (cfg, dest, ins);
100 switch (ins->opcode) {
101 FOLD_BINOP (OP_IMUL, *);
102 FOLD_BINOP (OP_IADD, +);
103 FOLD_BINOP (OP_IAND, &);
104 FOLD_BINOP (OP_IOR, |);
105 FOLD_BINOP (OP_IXOR, ^);
107 dest->opcode = OP_ICONST;
108 MONO_INST_NULLIFY_SREGS (dest);
110 } else if (arg1->opcode == OP_ICONST) {
112 * This is commutative so swap the arguments, allowing the _imm variant
113 * to be used later.
115 if (mono_op_to_op_imm (ins->opcode) != -1) {
116 ALLOC_DEST (cfg, dest, ins);
117 dest->opcode = mono_op_to_op_imm (ins->opcode);
118 dest->sreg1 = ins->sreg2;
119 dest->sreg2 = -1;
120 dest->inst_imm = arg1->inst_c0;
123 break;
124 case OP_IMUL_IMM:
125 case OP_IADD_IMM:
126 case OP_IAND_IMM:
127 case OP_IOR_IMM:
128 case OP_IXOR_IMM:
129 case OP_ISUB_IMM:
130 case OP_ISHL_IMM:
131 case OP_ISHR_IMM:
132 case OP_ISHR_UN_IMM:
133 case OP_SHL_IMM:
134 if (arg1->opcode == OP_ICONST) {
135 ALLOC_DEST (cfg, dest, ins);
136 switch (ins->opcode) {
137 FOLD_BINOP2_IMM (OP_IMUL_IMM, *);
138 FOLD_BINOP2_IMM (OP_IADD_IMM, +);
139 FOLD_BINOP2_IMM (OP_IAND_IMM, &);
140 FOLD_BINOP2_IMM (OP_IOR_IMM, |);
141 FOLD_BINOP2_IMM (OP_IXOR_IMM, ^);
142 FOLD_BINOP2_IMM (OP_ISUB_IMM, -);
143 FOLD_BINOPC2_IMM (OP_ISHL_IMM, <<, gint32);
144 FOLD_BINOPC2_IMM (OP_ISHR_IMM, >>, gint32);
145 FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM, >>, guint32);
146 FOLD_BINOP2_IMM (OP_SHL_IMM, <<);
148 dest->opcode = OP_ICONST;
149 MONO_INST_NULLIFY_SREGS (dest);
151 break;
152 case OP_ISUB:
153 case OP_ISHL:
154 case OP_ISHR:
155 case OP_ISHR_UN:
156 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
157 ALLOC_DEST (cfg, dest, ins);
158 switch (ins->opcode) {
159 FOLD_BINOP (OP_ISUB, -);
160 FOLD_BINOP (OP_ISHL, <<);
161 FOLD_BINOP (OP_ISHR, >>);
162 FOLD_BINOPC (OP_ISHR_UN, >>, guint32);
164 dest->opcode = OP_ICONST;
165 MONO_INST_NULLIFY_SREGS (dest);
167 break;
168 case OP_IDIV:
169 case OP_IDIV_UN:
170 case OP_IREM:
171 case OP_IREM_UN:
172 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST)) {
173 if ((arg2->inst_c0 == 0) || ((arg1->inst_c0 == G_MININT32) && (arg2->inst_c0 == -1)))
174 return NULL;
175 ALLOC_DEST (cfg, dest, ins);
176 switch (ins->opcode) {
177 FOLD_BINOPC (OP_IDIV, /, gint32);
178 FOLD_BINOPC (OP_IDIV_UN, /, guint32);
179 FOLD_BINOPC (OP_IREM, %, gint32);
180 FOLD_BINOPC (OP_IREM_UN, %, guint32);
182 dest->opcode = OP_ICONST;
183 MONO_INST_NULLIFY_SREGS (dest);
185 break;
186 case OP_IDIV_IMM:
187 case OP_IDIV_UN_IMM:
188 case OP_IREM_IMM:
189 case OP_IREM_UN_IMM:
190 if (arg1->opcode == OP_ICONST) {
191 if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1)))
192 return NULL;
193 ALLOC_DEST (cfg, dest, ins);
194 switch (ins->opcode) {
195 FOLD_BINOPC2_IMM (OP_IDIV_IMM, /, gint32);
196 FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM, /, guint32);
197 FOLD_BINOPC2_IMM (OP_IREM_IMM, %, gint32);
198 FOLD_BINOPC2_IMM (OP_IREM_UN_IMM, %, guint32);
199 default:
200 g_assert_not_reached ();
202 dest->opcode = OP_ICONST;
203 MONO_INST_NULLIFY_SREGS (dest);
205 break;
206 /* case OP_INEG: */
207 case OP_INOT:
208 case OP_INEG:
209 if (arg1->opcode == OP_ICONST) {
210 /* INEG sets cflags on x86, and the LNEG decomposition depends on that */
211 #if SIZEOF_REGISTER == 4
212 if (ins->opcode == OP_INEG)
213 return NULL;
214 #endif
215 ALLOC_DEST (cfg, dest, ins);
216 switch (ins->opcode) {
217 FOLD_UNOP (OP_INEG,-);
218 FOLD_UNOP (OP_INOT,~);
220 dest->opcode = OP_ICONST;
221 MONO_INST_NULLIFY_SREGS (dest);
223 break;
224 case OP_SEXT_I4:
225 if (arg1->opcode == OP_ICONST && arg1->inst_c0 >= 0 && arg1->inst_c0 < (1 << 16) && overwrite) {
226 dest->opcode = OP_ICONST;
227 dest->sreg1 = -1;
228 dest->inst_c0 = arg1->inst_c0;
230 break;
231 case OP_MOVE:
232 #if SIZEOF_REGISTER == 8
233 if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) {
234 #else
235 if (arg1->opcode == OP_ICONST) {
236 #endif
237 ALLOC_DEST (cfg, dest, ins);
238 dest->opcode = arg1->opcode;
239 MONO_INST_NULLIFY_SREGS (dest);
240 dest->inst_c0 = arg1->inst_c0;
242 break;
243 case OP_VMOVE:
244 if (arg1->opcode == OP_VZERO) {
245 ALLOC_DEST (cfg, dest, ins);
246 dest->opcode = OP_VZERO;
247 dest->sreg1 = -1;
249 break;
250 case OP_XMOVE:
251 if (arg1->opcode == OP_XZERO) {
252 ALLOC_DEST (cfg, dest, ins);
253 dest->opcode = OP_XZERO;
254 dest->sreg1 = -1;
256 break;
257 case OP_COMPARE:
258 case OP_ICOMPARE:
259 case OP_COMPARE_IMM:
260 case OP_ICOMPARE_IMM: {
261 MonoInst dummy_arg2;
262 if (ins->sreg2 == -1) {
263 arg2 = &dummy_arg2;
264 arg2->opcode = OP_ICONST;
265 arg2->inst_c0 = ins->inst_imm;
268 if ((arg1->opcode == OP_ICONST) && (arg2->opcode == OP_ICONST) && ins->next) {
269 MonoInst *next = ins->next;
270 gboolean res = FALSE;
272 switch (next->opcode) {
273 case OP_CEQ:
274 case OP_ICEQ:
275 case OP_CGT:
276 case OP_ICGT:
277 case OP_CGT_UN:
278 case OP_ICGT_UN:
279 case OP_CLT:
280 case OP_ICLT:
281 case OP_CLT_UN:
282 case OP_ICLT_UN:
283 switch (next->opcode) {
284 FOLD_BINOPCXX (OP_CEQ,==,gint32);
285 FOLD_BINOPCXX (OP_ICEQ,==,gint32);
286 FOLD_BINOPCXX (OP_CGT,>,gint32);
287 FOLD_BINOPCXX (OP_ICGT,>,gint32);
288 FOLD_BINOPCXX (OP_CGT_UN,>,guint32);
289 FOLD_BINOPCXX (OP_ICGT_UN,>,guint32);
290 FOLD_BINOPCXX (OP_CLT,<,gint32);
291 FOLD_BINOPCXX (OP_ICLT,<,gint32);
292 FOLD_BINOPCXX (OP_CLT_UN,<,guint32);
293 FOLD_BINOPCXX (OP_ICLT_UN,<,guint32);
296 if (overwrite) {
297 NULLIFY_INS (ins);
298 next->opcode = OP_ICONST;
299 next->inst_c0 = res;
300 MONO_INST_NULLIFY_SREGS (next);
301 } else {
302 ALLOC_DEST (cfg, dest, ins);
303 dest->opcode = OP_ICONST;
304 dest->inst_c0 = res;
306 break;
307 case OP_IBEQ:
308 case OP_IBNE_UN:
309 case OP_IBGT:
310 case OP_IBGT_UN:
311 case OP_IBGE:
312 case OP_IBGE_UN:
313 case OP_IBLT:
314 case OP_IBLT_UN:
315 case OP_IBLE:
316 case OP_IBLE_UN:
317 switch (next->opcode) {
318 FOLD_BINOPCXX (OP_IBEQ,==,gint32);
319 FOLD_BINOPCXX (OP_IBNE_UN,!=,guint32);
320 FOLD_BINOPCXX (OP_IBGT,>,gint32);
321 FOLD_BINOPCXX (OP_IBGT_UN,>,guint32);
322 FOLD_BINOPCXX (OP_IBGE,>=,gint32);
323 FOLD_BINOPCXX (OP_IBGE_UN,>=,guint32);
324 FOLD_BINOPCXX (OP_IBLT,<,gint32);
325 FOLD_BINOPCXX (OP_IBLT_UN,<,guint32);
326 FOLD_BINOPCXX (OP_IBLE,<=,gint32);
327 FOLD_BINOPCXX (OP_IBLE_UN,<=,guint32);
330 if (overwrite) {
332 * Can't nullify OP_COMPARE here since the decompose long branch
333 * opcodes depend on it being executed. Also, the branch might not
334 * be eliminated after all if loop opts is disabled, for example.
336 if (res)
337 next->flags |= MONO_INST_CFOLD_TAKEN;
338 else
339 next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
340 } else {
341 ALLOC_DEST (cfg, dest, ins);
342 dest->opcode = OP_ICONST;
343 dest->inst_c0 = res;
345 break;
346 case OP_COND_EXC_EQ:
347 res = arg1->inst_c0 == arg2->inst_c0;
348 if (!res) {
349 if (overwrite) {
350 NULLIFY_INS (ins);
351 NULLIFY_INS (next);
352 } else {
353 ALLOC_DEST (cfg, dest, ins);
354 dest->opcode = OP_ICONST;
355 dest->inst_c0 = res;
358 break;
359 case OP_NOP:
360 case OP_BR:
361 /* This happens when a conditional branch is eliminated */
362 if (next->next == NULL) {
363 /* Last ins */
364 if (overwrite)
365 NULLIFY_INS (ins);
367 break;
368 default:
369 return NULL;
372 if ((arg1->opcode == OP_PCONST) && (arg2->opcode == OP_PCONST) && ins->next) {
373 MonoInst *next = ins->next;
375 if (next->opcode == OP_LCEQ) {
376 gboolean res = arg1->inst_p0 == arg2->inst_p0;
377 if (overwrite) {
378 NULLIFY_INS (ins);
379 next->opcode = OP_ICONST;
380 next->inst_c0 = res;
381 MONO_INST_NULLIFY_SREGS (next);
382 } else {
383 ALLOC_DEST (cfg, dest, ins);
384 dest->opcode = OP_ICONST;
385 dest->inst_c0 = res;
387 break;
390 break;
392 case OP_FMOVE:
393 if (arg1->opcode == OP_R8CONST) {
394 ALLOC_DEST (cfg, dest, ins);
395 dest->opcode = OP_R8CONST;
396 dest->sreg1 = -1;
397 dest->inst_p0 = arg1->inst_p0;
399 break;
402 * TODO:
403 * conv.* opcodes.
404 * *ovf* opcodes? It's slow and hard to do in C.
405 * switch can be replaced by a simple jump
407 default:
408 return NULL;
411 return dest;
415 #endif /* DISABLE_JIT */