[mono-api-info] Use XmlWriter instead of XmlDocument to make this faster.
[mono-project.git] / mono / mini / cfold.c
blobbdbab4af138913aa18dc4413697e4f7883644ab5
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 <config.h>
12 #include "mini.h"
13 #include "ir-emit.h"
15 /* WTF is this doing here?!?!? */
16 int
17 mono_is_power_of_two (guint32 val)
19 int i, j, k;
21 for (i = 0, j = 1, k = 0xfffffffe; i < 32; ++i, j = j << 1, k = k << 1) {
22 if (val & j)
23 break;
25 if (i == 32 || val & k)
26 return -1;
27 return i;
30 #ifndef G_MININT32
31 #define MYGINT32_MAX 2147483647
32 #define G_MININT32 (-MYGINT32_MAX -1)
33 #endif
35 #define FOLD_UNOP(name,op) \
36 case name: \
37 dest->inst_c0 = op arg1->inst_c0; \
38 break;
40 #define FOLD_BINOP(name, op) \
41 case name: \
42 dest->inst_c0 = arg1->inst_c0 op arg2->inst_c0; \
43 break;
45 #define FOLD_BINOPC(name,op,cast) \
46 case name: \
47 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
48 break;
50 #define FOLD_BINOP2_IMM(name, op) \
51 case name: \
52 dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \
53 break;
55 #define FOLD_BINOPC2_IMM(name, op, cast) \
56 case name: \
57 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
58 break;
60 #define FOLD_BINOPCXX(name,op,cast) \
61 case name: \
62 res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
63 break; \
65 #define ALLOC_DEST(cfg, dest, ins) do { \
66 if (!(dest)) { \
67 MONO_INST_NEW ((cfg), (dest), -1); \
68 (dest)->dreg = (ins)->dreg; \
69 } \
70 } while (0)
72 #ifndef DISABLE_JIT
74 /**
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
80 * NULL.
82 MonoInst*
83 mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite)
85 MonoInst *dest = NULL;
87 if (overwrite)
88 dest = ins;
90 switch (ins->opcode) {
91 case OP_IMUL:
92 case OP_IADD:
93 case OP_IAND:
94 case OP_IOR:
95 case OP_IXOR:
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
112 * to be used later.
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;
118 dest->sreg2 = -1;
119 dest->inst_imm = arg1->inst_c0;
122 break;
123 case OP_IMUL_IMM:
124 case OP_IADD_IMM:
125 case OP_IAND_IMM:
126 case OP_IOR_IMM:
127 case OP_IXOR_IMM:
128 case OP_ISUB_IMM:
129 case OP_ISHL_IMM:
130 case OP_ISHR_IMM:
131 case OP_ISHR_UN_IMM:
132 case OP_SHL_IMM:
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);
150 break;
151 case OP_ISUB:
152 case OP_ISHL:
153 case OP_ISHR:
154 case OP_ISHR_UN:
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);
166 break;
167 case OP_IDIV:
168 case OP_IDIV_UN:
169 case OP_IREM:
170 case OP_IREM_UN:
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)))
173 return NULL;
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);
184 break;
185 case OP_IDIV_IMM:
186 case OP_IDIV_UN_IMM:
187 case OP_IREM_IMM:
188 case OP_IREM_UN_IMM:
189 if (arg1->opcode == OP_ICONST) {
190 if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1)))
191 return NULL;
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);
198 default:
199 g_assert_not_reached ();
201 dest->opcode = OP_ICONST;
202 MONO_INST_NULLIFY_SREGS (dest);
204 break;
205 /* case OP_INEG: */
206 case OP_INOT:
207 case OP_INEG:
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)
212 return NULL;
213 #endif
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);
222 break;
223 case OP_MOVE:
224 #if SIZEOF_REGISTER == 8
225 if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) {
226 #else
227 if (arg1->opcode == OP_ICONST) {
228 #endif
229 ALLOC_DEST (cfg, dest, ins);
230 dest->opcode = arg1->opcode;
231 MONO_INST_NULLIFY_SREGS (dest);
232 dest->inst_c0 = arg1->inst_c0;
234 break;
235 case OP_VMOVE:
236 if (arg1->opcode == OP_VZERO) {
237 ALLOC_DEST (cfg, dest, ins);
238 dest->opcode = OP_VZERO;
239 dest->sreg1 = -1;
241 break;
242 case OP_XMOVE:
243 if (arg1->opcode == OP_XZERO) {
244 ALLOC_DEST (cfg, dest, ins);
245 dest->opcode = OP_XZERO;
246 dest->sreg1 = -1;
248 break;
249 case OP_COMPARE:
250 case OP_ICOMPARE:
251 case OP_COMPARE_IMM:
252 case OP_ICOMPARE_IMM: {
253 MonoInst dummy_arg2;
254 if (ins->sreg2 == -1) {
255 arg2 = &dummy_arg2;
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) {
265 case OP_CEQ:
266 case OP_ICEQ:
267 case OP_CGT:
268 case OP_ICGT:
269 case OP_CGT_UN:
270 case OP_ICGT_UN:
271 case OP_CLT:
272 case OP_ICLT:
273 case OP_CLT_UN:
274 case OP_ICLT_UN:
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);
288 if (overwrite) {
289 NULLIFY_INS (ins);
290 next->opcode = OP_ICONST;
291 next->inst_c0 = res;
292 MONO_INST_NULLIFY_SREGS (next);
293 } else {
294 ALLOC_DEST (cfg, dest, ins);
295 dest->opcode = OP_ICONST;
296 dest->inst_c0 = res;
298 break;
299 case OP_IBEQ:
300 case OP_IBNE_UN:
301 case OP_IBGT:
302 case OP_IBGT_UN:
303 case OP_IBGE:
304 case OP_IBGE_UN:
305 case OP_IBLT:
306 case OP_IBLT_UN:
307 case OP_IBLE:
308 case OP_IBLE_UN:
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);
322 if (overwrite) {
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.
328 if (res)
329 next->flags |= MONO_INST_CFOLD_TAKEN;
330 else
331 next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
332 } else {
333 ALLOC_DEST (cfg, dest, ins);
334 dest->opcode = OP_ICONST;
335 dest->inst_c0 = res;
337 break;
338 case OP_NOP:
339 case OP_BR:
340 /* This happens when a conditional branch is eliminated */
341 if (next->next == NULL) {
342 /* Last ins */
343 if (overwrite)
344 NULLIFY_INS (ins);
346 break;
347 default:
348 return NULL;
351 break;
353 case OP_FMOVE:
354 if (arg1->opcode == OP_R8CONST) {
355 ALLOC_DEST (cfg, dest, ins);
356 dest->opcode = OP_R8CONST;
357 dest->sreg1 = -1;
358 dest->inst_p0 = arg1->inst_p0;
360 break;
363 * TODO:
364 * conv.* opcodes.
365 * *ovf* opcodes? It's slow and hard to do in C.
366 * switch can be replaced by a simple jump
368 default:
369 return NULL;
372 return dest;
376 #endif /* DISABLE_JIT */