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
15 /* WTF is this doing here?!?!? */
17 mono_is_power_of_two (guint32 val
)
21 for (i
= 0, j
= 1, k
= 0xfffffffe; i
< 32; ++i
, j
= j
<< 1, k
= k
<< 1) {
25 if (i
== 32 || val
& k
)
31 #define MYGINT32_MAX 2147483647
32 #define G_MININT32 (-MYGINT32_MAX -1)
35 #define FOLD_UNOP(name,op) \
37 dest->inst_c0 = op arg1->inst_c0; \
40 #define FOLD_BINOP(name, op) \
42 dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0; \
45 #define FOLD_BINOPC(name,op,cast) \
47 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
50 #define FOLD_BINOP2_IMM(name, op) \
52 dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \
55 #define FOLD_BINOPC2_IMM(name, op, cast) \
57 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
60 #define FOLD_BINOPCXX(name,op,cast) \
62 res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
65 #define ALLOC_DEST(cfg, dest, ins) do { \
67 MONO_INST_NEW ((cfg), (dest), -1); \
68 (dest)->dreg = (ins)->dreg; \
75 * mono_constant_fold_ins:
77 * Perform constant folding on INS, using ARG1 and ARG2 as the arguments. If OVERWRITE is
78 * true, then store the result back into INS and return INS. Otherwise allocate a new ins,
79 * store the result into it and return it. If constant folding cannot be performed, return
83 mono_constant_fold_ins (MonoCompile
*cfg
, MonoInst
*ins
, MonoInst
*arg1
, MonoInst
*arg2
, gboolean overwrite
)
85 MonoInst
*dest
= NULL
;
90 switch (ins
->opcode
) {
96 if (arg2
->opcode
== OP_ICONST
) {
97 if (arg1
->opcode
== OP_ICONST
) {
98 ALLOC_DEST (cfg
, dest
, ins
);
99 switch (ins
->opcode
) {
100 FOLD_BINOP (OP_IMUL
, *);
101 FOLD_BINOP (OP_IADD
, +);
102 FOLD_BINOP (OP_IAND
, &);
103 FOLD_BINOP (OP_IOR
, |);
104 FOLD_BINOP (OP_IXOR
, ^);
106 dest
->opcode
= OP_ICONST
;
107 MONO_INST_NULLIFY_SREGS (dest
);
109 } else if (arg1
->opcode
== OP_ICONST
) {
111 * This is commutative so swap the arguments, allowing the _imm variant
114 if (mono_op_to_op_imm (ins
->opcode
) != -1) {
115 ALLOC_DEST (cfg
, dest
, ins
);
116 dest
->opcode
= mono_op_to_op_imm (ins
->opcode
);
117 dest
->sreg1
= ins
->sreg2
;
119 dest
->inst_imm
= arg1
->inst_c0
;
133 if (arg1
->opcode
== OP_ICONST
) {
134 ALLOC_DEST (cfg
, dest
, ins
);
135 switch (ins
->opcode
) {
136 FOLD_BINOP2_IMM (OP_IMUL_IMM
, *);
137 FOLD_BINOP2_IMM (OP_IADD_IMM
, +);
138 FOLD_BINOP2_IMM (OP_IAND_IMM
, &);
139 FOLD_BINOP2_IMM (OP_IOR_IMM
, |);
140 FOLD_BINOP2_IMM (OP_IXOR_IMM
, ^);
141 FOLD_BINOP2_IMM (OP_ISUB_IMM
, -);
142 FOLD_BINOPC2_IMM (OP_ISHL_IMM
, <<, gint32
);
143 FOLD_BINOPC2_IMM (OP_ISHR_IMM
, >>, gint32
);
144 FOLD_BINOPC2_IMM (OP_ISHR_UN_IMM
, >>, guint32
);
145 FOLD_BINOP2_IMM (OP_SHL_IMM
, <<);
147 dest
->opcode
= OP_ICONST
;
148 MONO_INST_NULLIFY_SREGS (dest
);
155 if ((arg1
->opcode
== OP_ICONST
) && (arg2
->opcode
== OP_ICONST
)) {
156 ALLOC_DEST (cfg
, dest
, ins
);
157 switch (ins
->opcode
) {
158 FOLD_BINOP (OP_ISUB
, -);
159 FOLD_BINOP (OP_ISHL
, <<);
160 FOLD_BINOP (OP_ISHR
, >>);
161 FOLD_BINOPC (OP_ISHR_UN
, >>, guint32
);
163 dest
->opcode
= OP_ICONST
;
164 MONO_INST_NULLIFY_SREGS (dest
);
171 if ((arg1
->opcode
== OP_ICONST
) && (arg2
->opcode
== OP_ICONST
)) {
172 if ((arg2
->inst_c0
== 0) || ((arg1
->inst_c0
== G_MININT32
) && (arg2
->inst_c0
== -1)))
174 ALLOC_DEST (cfg
, dest
, ins
);
175 switch (ins
->opcode
) {
176 FOLD_BINOPC (OP_IDIV
, /, gint32
);
177 FOLD_BINOPC (OP_IDIV_UN
, /, guint32
);
178 FOLD_BINOPC (OP_IREM
, %, gint32
);
179 FOLD_BINOPC (OP_IREM_UN
, %, guint32
);
181 dest
->opcode
= OP_ICONST
;
182 MONO_INST_NULLIFY_SREGS (dest
);
189 if (arg1
->opcode
== OP_ICONST
) {
190 if ((ins
->inst_imm
== 0) || ((arg1
->inst_c0
== G_MININT32
) && (ins
->inst_imm
== -1)))
192 ALLOC_DEST (cfg
, dest
, ins
);
193 switch (ins
->opcode
) {
194 FOLD_BINOPC2_IMM (OP_IDIV_IMM
, /, gint32
);
195 FOLD_BINOPC2_IMM (OP_IDIV_UN_IMM
, /, guint32
);
196 FOLD_BINOPC2_IMM (OP_IREM_IMM
, %, gint32
);
197 FOLD_BINOPC2_IMM (OP_IREM_UN_IMM
, %, guint32
);
199 g_assert_not_reached ();
201 dest
->opcode
= OP_ICONST
;
202 MONO_INST_NULLIFY_SREGS (dest
);
208 if (arg1
->opcode
== OP_ICONST
) {
209 /* INEG sets cflags on x86, and the LNEG decomposition depends on that */
210 #if SIZEOF_REGISTER == 4
211 if (ins
->opcode
== OP_INEG
)
214 ALLOC_DEST (cfg
, dest
, ins
);
215 switch (ins
->opcode
) {
216 FOLD_UNOP (OP_INEG
,-);
217 FOLD_UNOP (OP_INOT
,~);
219 dest
->opcode
= OP_ICONST
;
220 MONO_INST_NULLIFY_SREGS (dest
);
224 #if SIZEOF_REGISTER == 8
225 if ((arg1
->opcode
== OP_ICONST
) || (arg1
->opcode
== OP_I8CONST
)) {
227 if (arg1
->opcode
== OP_ICONST
) {
229 ALLOC_DEST (cfg
, dest
, ins
);
230 dest
->opcode
= arg1
->opcode
;
231 MONO_INST_NULLIFY_SREGS (dest
);
232 dest
->inst_c0
= arg1
->inst_c0
;
236 if (arg1
->opcode
== OP_VZERO
) {
237 ALLOC_DEST (cfg
, dest
, ins
);
238 dest
->opcode
= OP_VZERO
;
243 if (arg1
->opcode
== OP_XZERO
) {
244 ALLOC_DEST (cfg
, dest
, ins
);
245 dest
->opcode
= OP_XZERO
;
252 case OP_ICOMPARE_IMM
: {
254 if (ins
->sreg2
== -1) {
256 arg2
->opcode
= OP_ICONST
;
257 arg2
->inst_c0
= ins
->inst_imm
;
260 if ((arg1
->opcode
== OP_ICONST
) && (arg2
->opcode
== OP_ICONST
) && ins
->next
) {
261 MonoInst
*next
= ins
->next
;
262 gboolean res
= FALSE
;
264 switch (next
->opcode
) {
275 switch (next
->opcode
) {
276 FOLD_BINOPCXX (OP_CEQ
,==,gint32
);
277 FOLD_BINOPCXX (OP_ICEQ
,==,gint32
);
278 FOLD_BINOPCXX (OP_CGT
,>,gint32
);
279 FOLD_BINOPCXX (OP_ICGT
,>,gint32
);
280 FOLD_BINOPCXX (OP_CGT_UN
,>,guint32
);
281 FOLD_BINOPCXX (OP_ICGT_UN
,>,guint32
);
282 FOLD_BINOPCXX (OP_CLT
,<,gint32
);
283 FOLD_BINOPCXX (OP_ICLT
,<,gint32
);
284 FOLD_BINOPCXX (OP_CLT_UN
,<,guint32
);
285 FOLD_BINOPCXX (OP_ICLT_UN
,<,guint32
);
290 next
->opcode
= OP_ICONST
;
292 MONO_INST_NULLIFY_SREGS (next
);
294 ALLOC_DEST (cfg
, dest
, ins
);
295 dest
->opcode
= OP_ICONST
;
309 switch (next
->opcode
) {
310 FOLD_BINOPCXX (OP_IBEQ
,==,gint32
);
311 FOLD_BINOPCXX (OP_IBNE_UN
,!=,guint32
);
312 FOLD_BINOPCXX (OP_IBGT
,>,gint32
);
313 FOLD_BINOPCXX (OP_IBGT_UN
,>,guint32
);
314 FOLD_BINOPCXX (OP_IBGE
,>=,gint32
);
315 FOLD_BINOPCXX (OP_IBGE_UN
,>=,guint32
);
316 FOLD_BINOPCXX (OP_IBLT
,<,gint32
);
317 FOLD_BINOPCXX (OP_IBLT_UN
,<,guint32
);
318 FOLD_BINOPCXX (OP_IBLE
,<=,gint32
);
319 FOLD_BINOPCXX (OP_IBLE_UN
,<=,guint32
);
324 * Can't nullify OP_COMPARE here since the decompose long branch
325 * opcodes depend on it being executed. Also, the branch might not
326 * be eliminated after all if loop opts is disabled, for example.
329 next
->flags
|= MONO_INST_CFOLD_TAKEN
;
331 next
->flags
|= MONO_INST_CFOLD_NOT_TAKEN
;
333 ALLOC_DEST (cfg
, dest
, ins
);
334 dest
->opcode
= OP_ICONST
;
339 res
= arg1
->inst_c0
== arg2
->inst_c0
;
345 ALLOC_DEST (cfg
, dest
, ins
);
346 dest
->opcode
= OP_ICONST
;
353 /* This happens when a conditional branch is eliminated */
354 if (next
->next
== NULL
) {
364 if ((arg1
->opcode
== OP_PCONST
) && (arg2
->opcode
== OP_PCONST
) && ins
->next
) {
365 MonoInst
*next
= ins
->next
;
367 if (next
->opcode
== OP_LCEQ
) {
368 gboolean res
= arg1
->inst_p0
== arg2
->inst_p0
;
371 next
->opcode
= OP_ICONST
;
373 MONO_INST_NULLIFY_SREGS (next
);
375 ALLOC_DEST (cfg
, dest
, ins
);
376 dest
->opcode
= OP_ICONST
;
385 if (arg1
->opcode
== OP_R8CONST
) {
386 ALLOC_DEST (cfg
, dest
, ins
);
387 dest
->opcode
= OP_R8CONST
;
389 dest
->inst_p0
= arg1
->inst_p0
;
396 * *ovf* opcodes? It's slow and hard to do in C.
397 * switch can be replaced by a simple jump
407 #endif /* DISABLE_JIT */