[netcore] Implement missing Bmi1/Bmi2 intrinsics (#16919)
[mono-project.git] / mono / mini / cfold.c
blob737f5ab5cfe338dca2147644edda717d0f8f45d3
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_FBINOP(name, op) \
47 case name: \
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)); \
50 break;
52 #define FOLD_RBINOP(name, op) \
53 case name: \
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)); \
56 break;
58 #define FOLD_BINOPC(name,op,cast) \
59 case name: \
60 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
61 break;
63 #define FOLD_BINOP2_IMM(name, op) \
64 case name: \
65 dest->inst_c0 = arg1->inst_c0 op ins->inst_imm; \
66 break;
68 #define FOLD_BINOPC2_IMM(name, op, cast) \
69 case name: \
70 dest->inst_c0 = (cast)arg1->inst_c0 op (cast)ins->inst_imm; \
71 break;
73 #define FOLD_BINOPCXX(name,op,cast) \
74 case name: \
75 res = (cast)arg1->inst_c0 op (cast)arg2->inst_c0; \
76 break; \
78 #define ALLOC_DEST(cfg, dest, ins) do { \
79 if (!(dest)) { \
80 MONO_INST_NEW ((cfg), (dest), -1); \
81 (dest)->dreg = (ins)->dreg; \
82 } \
83 } while (0)
85 #ifndef DISABLE_JIT
87 /**
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
93 * NULL.
95 MonoInst*
96 mono_constant_fold_ins (MonoCompile *cfg, MonoInst *ins, MonoInst *arg1, MonoInst *arg2, gboolean overwrite)
98 MonoInst *dest = NULL;
100 if (overwrite)
101 dest = ins;
103 switch (ins->opcode) {
104 case OP_FADD:
105 case OP_FMUL:
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);
115 break;
116 case OP_RADD:
117 case OP_RMUL:
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);
127 break;
128 case OP_IMUL:
129 case OP_IADD:
130 case OP_IAND:
131 case OP_IOR:
132 case OP_IXOR:
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
149 * to be used later.
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;
155 dest->sreg2 = -1;
156 dest->inst_imm = arg1->inst_c0;
159 break;
160 case OP_IMUL_IMM:
161 case OP_IADD_IMM:
162 case OP_IAND_IMM:
163 case OP_IOR_IMM:
164 case OP_IXOR_IMM:
165 case OP_ISUB_IMM:
166 case OP_ISHL_IMM:
167 case OP_ISHR_IMM:
168 case OP_ISHR_UN_IMM:
169 case OP_SHL_IMM:
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);
187 break;
188 case OP_ISUB:
189 case OP_ISHL:
190 case OP_ISHR:
191 case OP_ISHR_UN:
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);
203 break;
204 case OP_IDIV:
205 case OP_IDIV_UN:
206 case OP_IREM:
207 case OP_IREM_UN:
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)))
210 return NULL;
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);
221 break;
222 case OP_IDIV_IMM:
223 case OP_IDIV_UN_IMM:
224 case OP_IREM_IMM:
225 case OP_IREM_UN_IMM:
226 if (arg1->opcode == OP_ICONST) {
227 if ((ins->inst_imm == 0) || ((arg1->inst_c0 == G_MININT32) && (ins->inst_imm == -1)))
228 return NULL;
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);
235 default:
236 g_assert_not_reached ();
238 dest->opcode = OP_ICONST;
239 MONO_INST_NULLIFY_SREGS (dest);
241 break;
242 /* case OP_INEG: */
243 case OP_INOT:
244 case OP_INEG:
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)
249 return NULL;
250 #endif
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);
259 break;
260 case OP_SEXT_I4:
261 if (arg1->opcode == OP_ICONST && arg1->inst_c0 >= 0 && arg1->inst_c0 < (1 << 16) && overwrite) {
262 dest->opcode = OP_ICONST;
263 dest->sreg1 = -1;
264 dest->inst_c0 = arg1->inst_c0;
266 break;
267 case OP_MOVE:
268 #if SIZEOF_REGISTER == 8
269 if ((arg1->opcode == OP_ICONST) || (arg1->opcode == OP_I8CONST)) {
270 #else
271 if (arg1->opcode == OP_ICONST) {
272 #endif
273 ALLOC_DEST (cfg, dest, ins);
274 dest->opcode = arg1->opcode;
275 MONO_INST_NULLIFY_SREGS (dest);
276 dest->inst_c0 = arg1->inst_c0;
278 break;
279 case OP_FMOVE:
280 if (arg1->opcode == OP_R8CONST) {
281 ALLOC_DEST (cfg, dest, ins);
282 dest->opcode = arg1->opcode;
283 dest->sreg1 = -1;
284 dest->inst_p0 = arg1->inst_p0;
286 break;
287 case OP_RMOVE:
288 if (arg1->opcode == OP_R4CONST) {
289 ALLOC_DEST (cfg, dest, ins);
290 dest->opcode = arg1->opcode;
291 dest->sreg1 = -1;
292 dest->inst_p0 = arg1->inst_p0;
294 break;
295 case OP_VMOVE:
296 if (arg1->opcode == OP_VZERO) {
297 ALLOC_DEST (cfg, dest, ins);
298 dest->opcode = OP_VZERO;
299 dest->sreg1 = -1;
301 break;
302 case OP_XMOVE:
303 if (arg1->opcode == OP_XZERO) {
304 ALLOC_DEST (cfg, dest, ins);
305 dest->opcode = OP_XZERO;
306 dest->sreg1 = -1;
308 break;
309 case OP_COMPARE:
310 case OP_ICOMPARE:
311 case OP_COMPARE_IMM:
312 case OP_ICOMPARE_IMM: {
313 MonoInst dummy_arg2;
314 if (ins->sreg2 == -1) {
315 arg2 = &dummy_arg2;
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) {
325 case OP_CEQ:
326 case OP_ICEQ:
327 case OP_CGT:
328 case OP_ICGT:
329 case OP_CGT_UN:
330 case OP_ICGT_UN:
331 case OP_CLT:
332 case OP_ICLT:
333 case OP_CLT_UN:
334 case OP_ICLT_UN:
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);
348 if (overwrite) {
349 NULLIFY_INS (ins);
350 next->opcode = OP_ICONST;
351 next->inst_c0 = res;
352 MONO_INST_NULLIFY_SREGS (next);
353 } else {
354 ALLOC_DEST (cfg, dest, ins);
355 dest->opcode = OP_ICONST;
356 dest->inst_c0 = res;
358 break;
359 case OP_IBEQ:
360 case OP_IBNE_UN:
361 case OP_IBGT:
362 case OP_IBGT_UN:
363 case OP_IBGE:
364 case OP_IBGE_UN:
365 case OP_IBLT:
366 case OP_IBLT_UN:
367 case OP_IBLE:
368 case OP_IBLE_UN:
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);
382 if (overwrite) {
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.
388 if (res)
389 next->flags |= MONO_INST_CFOLD_TAKEN;
390 else
391 next->flags |= MONO_INST_CFOLD_NOT_TAKEN;
392 } else {
393 ALLOC_DEST (cfg, dest, ins);
394 dest->opcode = OP_ICONST;
395 dest->inst_c0 = res;
397 break;
398 case OP_COND_EXC_EQ:
399 res = arg1->inst_c0 == arg2->inst_c0;
400 if (!res) {
401 if (overwrite) {
402 NULLIFY_INS (ins);
403 NULLIFY_INS (next);
404 } else {
405 ALLOC_DEST (cfg, dest, ins);
406 dest->opcode = OP_ICONST;
407 dest->inst_c0 = res;
410 break;
411 case OP_NOP:
412 case OP_BR:
413 /* This happens when a conditional branch is eliminated */
414 if (next->next == NULL) {
415 /* Last ins */
416 if (overwrite)
417 NULLIFY_INS (ins);
419 break;
420 default:
421 return 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;
429 if (overwrite) {
430 NULLIFY_INS (ins);
431 next->opcode = OP_ICONST;
432 next->inst_c0 = res;
433 MONO_INST_NULLIFY_SREGS (next);
434 } else {
435 ALLOC_DEST (cfg, dest, ins);
436 dest->opcode = OP_ICONST;
437 dest->inst_c0 = res;
439 break;
442 break;
446 * TODO:
447 * conv.* opcodes.
448 * *ovf* opcodes? It's slow and hard to do in C.
449 * switch can be replaced by a simple jump
451 default:
452 return NULL;
455 return dest;
459 #endif /* DISABLE_JIT */