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;
340 /* This happens when a conditional branch is eliminated */
341 if (next->next == NULL) {
354 if (arg1->opcode == OP_R8CONST) {
355 ALLOC_DEST (cfg, dest, ins);
356 dest->opcode = OP_R8CONST;
358 dest->inst_p0 = arg1->inst_p0;
365 * *ovf* opcodes? It's slow and hard to do in C.
366 * switch can be replaced by a simple jump
376 #endif /* DISABLE_JIT */