3 * Constant folding support
6 * Paolo Molaro (lupus@ximian.com)
7 * Dietmar Maurer (dietmar@ximian.com)
9 * (C) 2003 Ximian, Inc. http://www.ximian.com
16 /* WTF is this doing here?!?!? */
18 mono_is_power_of_two (guint32 val
)
22 for (i
= 0, j
= 1, k
= 0xfffffffe; i
< 32; ++i
, j
= j
<< 1, k
= k
<< 1) {
26 if (i
== 32 || val
& k
)
32 #define MYGINT32_MAX 2147483647
33 #define G_MININT32 (-MYGINT32_MAX -1)
36 #define FOLD_UNOP(name,op) \
38 dest->inst_c0 = op arg1->inst_c0; \
41 #define FOLD_BINOP(name, op) \
43 dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0; \
46 #define FOLD_FBINOP(name, op) \
48 dest->inst_p0 = (double *)mono_domain_alloc (cfg->domain, sizeof (double)); \
49 *(double *)dest->inst_p0 = (*((double *) arg1->inst_p0)) op (*((double *) arg2->inst_p0)); \
52 #define FOLD_RBINOP(name, op) \
54 dest->inst_p0 = (float *)mono_domain_alloc (cfg->domain, sizeof (float)); \
55 *(float *)dest->inst_p0 = (*((float *) arg1->inst_p0)) op (*((float *) arg2->inst_p0)); \
58 #define FOLD_BINOPC(name,op,cast) \
60 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
63 #define FOLD_BINOP2_IMM(name, op) \
65 dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \
68 #define FOLD_BINOPC2_IMM(name, op, cast) \
70 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
73 #define FOLD_BINOPCXX(name,op,cast) \
75 res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
78 #define ALLOC_DEST(cfg, dest, ins) do { \
80 MONO_INST_NEW ((cfg), (dest), -1); \
81 (dest)->dreg = (ins)->dreg; \
88 * mono_constant_fold_ins:
90 * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is
91 * true, then store the result back into INS and return INS. Otherwise allocate a new ins,
92 * store the result into it and return it. If constant folding cannot be performed, return
96 mono_constant_fold_ins (MonoCompile
*cfg
, MonoInst
*ins
, MonoInst
*arg1
, MonoInst
*arg2
, gboolean overwrite
)
98 MonoInst
*dest
= NULL
;
103 switch (ins
->opcode
) {
106 if (arg1
->opcode
== OP_R8CONST
&& arg2
->opcode
== OP_R8CONST
) {
107 ALLOC_DEST (cfg
, dest
, ins
);
108 switch (ins
->opcode
) {
109 FOLD_FBINOP (OP_FADD
, +);
110 FOLD_FBINOP (OP_FMUL
, *);
112 dest
->opcode
= OP_R8CONST
;
113 MONO_INST_NULLIFY_SREGS (dest
);
118 if (arg1
->opcode
== OP_R4CONST
&& arg2
->opcode
== OP_R4CONST
) {
119 ALLOC_DEST (cfg
, dest
, ins
);
120 switch (ins
->opcode
) {
121 FOLD_RBINOP (OP_RADD
, +);
122 FOLD_RBINOP (OP_RMUL
, *);
124 dest
->opcode
= OP_R4CONST
;
125 MONO_INST_NULLIFY_SREGS (dest
);
133 if (arg2
->opcode
== OP_ICONST
) {
134 if (arg1
->opcode
== OP_ICONST
) {
135 ALLOC_DEST (cfg
, dest
, ins
);
136 switch (ins
->opcode
) {
137 FOLD_BINOP (OP_IMUL
, *);
138 FOLD_BINOP (OP_IADD
, +);
139 FOLD_BINOP (OP_IAND
, &);
140 FOLD_BINOP (OP_IOR
, |);
141 FOLD_BINOP (OP_IXOR
, ^);
143 dest
->opcode
= OP_ICONST
;
144 MONO_INST_NULLIFY_SREGS (dest
);
146 } else if (arg1
->opcode
== OP_ICONST
) {
148 * This is commutative so swap the arguments, allowing the _imm variant
151 if (mono_op_to_op_imm (ins
->opcode
) != -1) {
152 ALLOC_DEST (cfg
, dest
, ins
);
153 dest
->opcode
= mono_op_to_op_imm (ins
->opcode
);
154 dest
->sreg1
= ins
->sreg2
;
156 dest
->inst_imm
= arg1
->inst_c0
;
170 if (arg1
->opcode
== OP_ICONST
) {
171 ALLOC_DEST (cfg
, dest
, ins
);
172 switch (ins
->opcode
) {
173 FOLD_BINOP2_IMM (OP_IMUL_IMM
, *);
174 FOLD_BINOP2_IMM (OP_IADD_IMM
, +);
175 FOLD_BINOP2_IMM (OP_IAND_IMM
, &);
176 FOLD_BINOP2_IMM (OP_IOR_IMM
, |);
177 FOLD_BINOP2_IMM (OP_IXOR_IMM
, ^);
178 FOLD_BINOP2_IMM (OP_ISUB_IMM
, -);
179 FOLD_BINOPC2_IMM (OP_ISHL_IMM
, <<, gint32
);
180 FOLD_BINOPC2_IMM (OP_ISHR_IMM
, >>, gint32
);
181 FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM
, >>, guint32
);
182 FOLD_BINOP2_IMM (OP_SHL_IMM
, <<);
184 dest
->opcode
= OP_ICONST
;
185 MONO_INST_NULLIFY_SREGS (dest
);
192 if ((arg1
->opcode
== OP_ICONST
) && (arg2
->opcode
== OP_ICONST
)) {
193 ALLOC_DEST (cfg
, dest
, ins
);
194 switch (ins
->opcode
) {
195 FOLD_BINOP (OP_ISUB
, -);
196 FOLD_BINOP (OP_ISHL
, <<);
197 FOLD_BINOP (OP_ISHR
, >>);
198 FOLD_BINOPC (OP_ISHR_UN
, >>, guint32
);
200 dest
->opcode
= OP_ICONST
;
201 MONO_INST_NULLIFY_SREGS (dest
);
208 if ((arg1
->opcode
== OP_ICONST
) && (arg2
->opcode
== OP_ICONST
)) {
209 if ((arg2
->inst_c0
== 0) || ((arg1
->inst_c0
== G_MININT32
) && (arg2
->inst_c0
== -1)))
211 ALLOC_DEST (cfg
, dest
, ins
);
212 switch (ins
->opcode
) {
213 FOLD_BINOPC (OP_IDIV
, /, gint32
);
214 FOLD_BINOPC (OP_IDIV_UN
, /, guint32
);
215 FOLD_BINOPC (OP_IREM
, %, gint32
);
216 FOLD_BINOPC (OP_IREM_UN
, %, guint32
);
218 dest
->opcode
= OP_ICONST
;
219 MONO_INST_NULLIFY_SREGS (dest
);
226 if (arg1
->opcode
== OP_ICONST
) {
227 if ((ins
->inst_imm
== 0) || ((arg1
->inst_c0
== G_MININT32
) && (ins
->inst_imm
== -1)))
229 ALLOC_DEST (cfg
, dest
, ins
);
230 switch (ins
->opcode
) {
231 FOLD_BINOPC2_IMM (OP_IDIV_IMM
, /, gint32
);
232 FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM
, /, guint32
);
233 FOLD_BINOPC2_IMM (OP_IREM_IMM
, %, gint32
);
234 FOLD_BINOPC2_IMM (OP_IREM_UN_IMM
, %, guint32
);
236 g_assert_not_reached ();
238 dest
->opcode
= OP_ICONST
;
239 MONO_INST_NULLIFY_SREGS (dest
);
245 if (arg1
->opcode
== OP_ICONST
) {
246 /* INEG sets cflags on x86, and the LNEG decomposition depends on that */
247 #if SIZEOF_REGISTER == 4
248 if (ins
->opcode
== OP_INEG
)
251 ALLOC_DEST (cfg
, dest
, ins
);
252 switch (ins
->opcode
) {
253 FOLD_UNOP (OP_INEG
,-);
254 FOLD_UNOP (OP_INOT
,~);
256 dest
->opcode
= OP_ICONST
;
257 MONO_INST_NULLIFY_SREGS (dest
);
261 if (arg1
->opcode
== OP_ICONST
&& arg1
->inst_c0
>= 0 && arg1
->inst_c0
< (1 << 16) && overwrite
) {
262 dest
->opcode
= OP_ICONST
;
264 dest
->inst_c0
= arg1
->inst_c0
;
268 #if SIZEOF_REGISTER == 8
269 if ((arg1
->opcode
== OP_ICONST
) || (arg1
->opcode
== OP_I8CONST
)) {
271 if (arg1
->opcode
== OP_ICONST
) {
273 ALLOC_DEST (cfg
, dest
, ins
);
274 dest
->opcode
= arg1
->opcode
;
275 MONO_INST_NULLIFY_SREGS (dest
);
276 dest
->inst_c0
= arg1
->inst_c0
;
280 if (arg1
->opcode
== OP_R8CONST
) {
281 ALLOC_DEST (cfg
, dest
, ins
);
282 dest
->opcode
= arg1
->opcode
;
284 dest
->inst_p0
= arg1
->inst_p0
;
288 if (arg1
->opcode
== OP_R4CONST
) {
289 ALLOC_DEST (cfg
, dest
, ins
);
290 dest
->opcode
= arg1
->opcode
;
292 dest
->inst_p0
= arg1
->inst_p0
;
296 if (arg1
->opcode
== OP_VZERO
) {
297 ALLOC_DEST (cfg
, dest
, ins
);
298 dest
->opcode
= OP_VZERO
;
303 if (arg1
->opcode
== OP_XZERO
) {
304 ALLOC_DEST (cfg
, dest
, ins
);
305 dest
->opcode
= OP_XZERO
;
312 case OP_ICOMPARE_IMM
: {
314 if (ins
->sreg2
== -1) {
316 arg2
->opcode
= OP_ICONST
;
317 arg2
->inst_c0
= ins
->inst_imm
;
320 if ((arg1
->opcode
== OP_ICONST
) && (arg2
->opcode
== OP_ICONST
) && ins
->next
) {
321 MonoInst
*next
= ins
->next
;
322 gboolean res
= FALSE
;
324 switch (next
->opcode
) {
335 switch (next
->opcode
) {
336 FOLD_BINOPCXX (OP_CEQ
,==,gint32
);
337 FOLD_BINOPCXX (OP_ICEQ
,==,gint32
);
338 FOLD_BINOPCXX (OP_CGT
,>,gint32
);
339 FOLD_BINOPCXX (OP_ICGT
,>,gint32
);
340 FOLD_BINOPCXX (OP_CGT_UN
,>,guint32
);
341 FOLD_BINOPCXX (OP_ICGT_UN
,>,guint32
);
342 FOLD_BINOPCXX (OP_CLT
,<,gint32
);
343 FOLD_BINOPCXX (OP_ICLT
,<,gint32
);
344 FOLD_BINOPCXX (OP_CLT_UN
,<,guint32
);
345 FOLD_BINOPCXX (OP_ICLT_UN
,<,guint32
);
350 next
->opcode
= OP_ICONST
;
352 MONO_INST_NULLIFY_SREGS (next
);
354 ALLOC_DEST (cfg
, dest
, ins
);
355 dest
->opcode
= OP_ICONST
;
369 switch (next
->opcode
) {
370 FOLD_BINOPCXX (OP_IBEQ
,==,gint32
);
371 FOLD_BINOPCXX (OP_IBNE_UN
,!=,guint32
);
372 FOLD_BINOPCXX (OP_IBGT
,>,gint32
);
373 FOLD_BINOPCXX (OP_IBGT_UN
,>,guint32
);
374 FOLD_BINOPCXX (OP_IBGE
,>=,gint32
);
375 FOLD_BINOPCXX (OP_IBGE_UN
,>=,guint32
);
376 FOLD_BINOPCXX (OP_IBLT
,<,gint32
);
377 FOLD_BINOPCXX (OP_IBLT_UN
,<,guint32
);
378 FOLD_BINOPCXX (OP_IBLE
,<=,gint32
);
379 FOLD_BINOPCXX (OP_IBLE_UN
,<=,guint32
);
384 * Can't nullify OP_COMPARE here since the decompose long branch
385 * opcodes depend on it being executed. Also, the branch might not
386 * be eliminated after all if loop opts is disabled, for example.
389 next
->flags
|= MONO_INST_CFOLD_TAKEN
;
391 next
->flags
|= MONO_INST_CFOLD_NOT_TAKEN
;
393 ALLOC_DEST (cfg
, dest
, ins
);
394 dest
->opcode
= OP_ICONST
;
399 res
= arg1
->inst_c0
== arg2
->inst_c0
;
405 ALLOC_DEST (cfg
, dest
, ins
);
406 dest
->opcode
= OP_ICONST
;
413 /* This happens when a conditional branch is eliminated */
414 if (next
->next
== NULL
) {
424 if ((arg1
->opcode
== OP_PCONST
) && (arg2
->opcode
== OP_PCONST
) && ins
->next
) {
425 MonoInst
*next
= ins
->next
;
427 if (next
->opcode
== OP_LCEQ
) {
428 gboolean res
= arg1
->inst_p0
== arg2
->inst_p0
;
431 next
->opcode
= OP_ICONST
;
433 MONO_INST_NULLIFY_SREGS (next
);
435 ALLOC_DEST (cfg
, dest
, ins
);
436 dest
->opcode
= OP_ICONST
;
448 * *ovf* opcodes? It's slow and hard to do in C.
449 * switch can be replaced by a simple jump
459 #endif /* DISABLE_JIT */