[jit] Don't allocate memory for unused vreg
[mono.git] / mono / mini / local-propagation.c
1 /*
2  * local-propagation.c: Local constant, copy and tree propagation.
3  *
4  * To make some sense of the tree mover, read mono/docs/tree-mover.txt
5  *
6  * Author:
7  *   Paolo Molaro (lupus@ximian.com)
8  *   Dietmar Maurer (dietmar@ximian.com)
9  *   Massimiliano Mantione (massi@ximian.com)
10  *
11  * (C) 2006 Novell, Inc.  http://www.novell.com
12  * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
13  */
14
15 #include <config.h>
16 #ifndef DISABLE_JIT
17
18 #include <string.h>
19 #include <stdio.h>
20 #ifdef HAVE_ALLOCA_H
21 #include <alloca.h>
22 #endif
23
24 #include <mono/metadata/debug-helpers.h>
25 #include <mono/metadata/mempool.h>
26 #include <mono/metadata/opcodes.h>
27 #include "mini.h"
28 #include "ir-emit.h"
29
30 #ifndef MONO_ARCH_IS_OP_MEMBASE
31 #define MONO_ARCH_IS_OP_MEMBASE(opcode) FALSE
32 #endif
33
34 static inline MonoBitSet* 
35 mono_bitset_mp_new_noinit (MonoMemPool *mp,  guint32 max_size)
36 {
37         int size = mono_bitset_alloc_size (max_size, 0);
38         gpointer mem;
39
40         mem = mono_mempool_alloc (mp, size);
41         return mono_bitset_mem_new (mem, max_size, MONO_BITSET_DONT_FREE);
42 }
43
44 /*
45  * Replaces ins with optimized opcodes.
46  * Returns TRUE if additional vregs were allocated.
47  */
48 static gboolean
49 mono_strength_reduction_ins (MonoCompile *cfg, MonoBasicBlock *bb, MonoInst *ins, const char **spec)
50 {
51         gboolean allocated_vregs = FALSE;
52
53         /* FIXME: Add long/float */
54         switch (ins->opcode) {
55         case OP_MOVE:
56         case OP_XMOVE:
57                 if (ins->dreg == ins->sreg1) {
58                         MONO_DELETE_INS (bb, ins);
59                 }
60                 break;
61         case OP_ADD_IMM:
62         case OP_IADD_IMM:
63         case OP_SUB_IMM:
64         case OP_ISUB_IMM:
65 #if SIZEOF_REGISTER == 8
66         case OP_LADD_IMM:
67         case OP_LSUB_IMM:
68 #endif
69                 if (ins->inst_imm == 0) {
70                         ins->opcode = OP_MOVE;
71                 }
72                 break;
73         case OP_MUL_IMM:
74         case OP_IMUL_IMM:
75 #if SIZEOF_REGISTER == 8
76         case OP_LMUL_IMM:
77 #endif
78                 if (ins->inst_imm == 0) {
79                         ins->opcode = (ins->opcode == OP_LMUL_IMM) ? OP_I8CONST : OP_ICONST;
80                         ins->inst_c0 = 0;
81                         ins->sreg1 = -1;
82                 } else if (ins->inst_imm == 1) {
83                         ins->opcode = OP_MOVE;
84                 } else if ((ins->opcode == OP_IMUL_IMM) && (ins->inst_imm == -1)) {
85                         ins->opcode = OP_INEG;
86                 } else if ((ins->opcode == OP_LMUL_IMM) && (ins->inst_imm == -1)) {
87                         ins->opcode = OP_LNEG;
88                 } else {
89                         int power2 = mono_is_power_of_two (ins->inst_imm);
90                         if (power2 >= 0) {
91                                 ins->opcode = (ins->opcode == OP_MUL_IMM) ? OP_SHL_IMM : ((ins->opcode == OP_LMUL_IMM) ? OP_LSHL_IMM : OP_ISHL_IMM);
92                                 ins->inst_imm = power2;
93                         }
94                 }
95                 break;
96         case OP_IREM_UN_IMM:
97         case OP_IDIV_UN_IMM: {
98                 int c = ins->inst_imm;
99                 int power2 = mono_is_power_of_two (c);
100
101                 if (power2 >= 0) {
102                         if (ins->opcode == OP_IREM_UN_IMM) {
103                                 ins->opcode = OP_IAND_IMM;
104                                 ins->sreg2 = -1;
105                                 ins->inst_imm = (1 << power2) - 1;
106                         } else if (ins->opcode == OP_IDIV_UN_IMM) {
107                                 ins->opcode = OP_ISHR_UN_IMM;
108                                 ins->sreg2 = -1;
109                                 ins->inst_imm = power2;
110                         }
111                 }
112                 break;
113         }
114         case OP_IDIV_IMM: {
115                 int c = ins->inst_imm;
116                 int power2 = mono_is_power_of_two (c);
117                 MonoInst *tmp1, *tmp2, *tmp3, *tmp4;
118
119                 /* FIXME: Move this elsewhere cause its hard to implement it here */
120                 if (power2 == 1) {
121                         int r1 = mono_alloc_ireg (cfg);
122
123                         NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_UN_IMM, r1, ins->sreg1, 31);
124                         mono_bblock_insert_after_ins (bb, ins, tmp1);
125                         NEW_BIALU (cfg, tmp2, OP_IADD, r1, r1, ins->sreg1);
126                         mono_bblock_insert_after_ins (bb, tmp1, tmp2);
127                         NEW_BIALU_IMM (cfg, tmp3, OP_ISHR_IMM, ins->dreg, r1, 1);
128                         mono_bblock_insert_after_ins (bb, tmp2, tmp3);
129
130                         NULLIFY_INS (ins);
131                         allocated_vregs = TRUE;
132                 } else if (power2 > 0 && power2 < 31) {
133                         int r1 = mono_alloc_ireg (cfg);
134
135                         NEW_BIALU_IMM (cfg, tmp1, OP_ISHR_IMM, r1, ins->sreg1, 31);
136                         mono_bblock_insert_after_ins (bb, ins, tmp1);
137                         NEW_BIALU_IMM (cfg, tmp2, OP_ISHR_UN_IMM, r1, r1, (32 - power2));
138                         mono_bblock_insert_after_ins (bb, tmp1, tmp2);
139                         NEW_BIALU (cfg, tmp3, OP_IADD, r1, r1, ins->sreg1);
140                         mono_bblock_insert_after_ins (bb, tmp2, tmp3);
141                         NEW_BIALU_IMM (cfg, tmp4, OP_ISHR_IMM, ins->dreg, r1, power2);
142                         mono_bblock_insert_after_ins (bb, tmp3, tmp4);
143
144                         NULLIFY_INS (ins);
145                         allocated_vregs = TRUE;
146                 }
147                 break;
148         }
149         default:
150                 break;
151         }
152
153         *spec = INS_INFO (ins->opcode);
154         return allocated_vregs;
155 }
156
157 /*
158  * mono_local_cprop:
159  *
160  *  A combined local copy and constant propagation pass.
161  */
162 void
163 mono_local_cprop (MonoCompile *cfg)
164 {
165         MonoBasicBlock *bb;
166         MonoInst **defs;
167         gint32 *def_index;
168         int max;
169         int filter = FILTER_IL_SEQ_POINT;
170
171 restart:
172
173         max = cfg->next_vreg;
174         defs = (MonoInst **)mono_mempool_alloc (cfg->mempool, sizeof (MonoInst*) * cfg->next_vreg);
175         def_index = (gint32 *)mono_mempool_alloc (cfg->mempool, sizeof (guint32) * cfg->next_vreg);
176
177         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
178                 MonoInst *ins;
179                 int ins_index;
180                 int last_call_index;
181
182                 /* Manually init the defs entries used by the bblock */
183                 MONO_BB_FOR_EACH_INS (bb, ins) {
184                         int sregs [MONO_MAX_SRC_REGS];
185                         int num_sregs, i;
186
187                         if ((ins->dreg != -1) && (ins->dreg < max)) {
188                                 defs [ins->dreg] = NULL;
189 #if SIZEOF_REGISTER == 4
190                                 defs [ins->dreg + 1] = NULL;
191 #endif
192                         }
193
194                         num_sregs = mono_inst_get_src_registers (ins, sregs);
195                         for (i = 0; i < num_sregs; ++i) {
196                                 int sreg = sregs [i];
197                                 if (sreg < max) {
198                                         defs [sreg] = NULL;
199 #if SIZEOF_REGISTER == 4
200                                         defs [sreg + 1] = NULL;
201 #endif
202                                 }
203                         }
204                 }
205
206                 ins_index = 0;
207                 last_call_index = -1;
208                 MONO_BB_FOR_EACH_INS (bb, ins) {
209                         const char *spec = INS_INFO (ins->opcode);
210                         int regtype, srcindex, sreg;
211                         int num_sregs;
212                         int sregs [MONO_MAX_SRC_REGS];
213
214                         if (ins->opcode == OP_NOP) {
215                                 MONO_DELETE_INS (bb, ins);
216                                 continue;
217                         }
218
219                         g_assert (ins->opcode > MONO_CEE_LAST);
220
221                         /* FIXME: Optimize this */
222                         if (ins->opcode == OP_LDADDR) {
223                                 MonoInst *var = (MonoInst *)ins->inst_p0;
224
225                                 defs [var->dreg] = NULL;
226                                 /*
227                                 if (!MONO_TYPE_ISSTRUCT (var->inst_vtype))
228                                         break;
229                                 */
230                         }
231
232                         if (MONO_IS_STORE_MEMBASE (ins)) {
233                                 sreg = ins->dreg;
234                                 regtype = 'i';
235
236                                 if ((regtype == 'i') && (sreg != -1) && defs [sreg]) {
237                                         MonoInst *def = defs [sreg];
238
239                                         if ((def->opcode == OP_MOVE) && (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg])) && !vreg_is_volatile (cfg, def->sreg1)) {
240                                                 int vreg = def->sreg1;
241                                                 if (cfg->verbose_level > 2) printf ("CCOPY: R%d -> R%d\n", sreg, vreg);
242                                                 ins->dreg = vreg;
243                                         }
244                                 }
245                         }
246
247                         num_sregs = mono_inst_get_src_registers (ins, sregs);
248                         for (srcindex = 0; srcindex < num_sregs; ++srcindex) {
249                                 MonoInst *def;
250
251                                 mono_inst_get_src_registers (ins, sregs);
252
253                                 regtype = spec [MONO_INST_SRC1 + srcindex];
254                                 sreg = sregs [srcindex];
255
256                                 if ((regtype == ' ') || (sreg == -1) || (!defs [sreg]))
257                                         continue;
258
259                                 def = defs [sreg];
260
261                                 /* Copy propagation */
262                                 /* 
263                                  * The first check makes sure the source of the copy did not change since 
264                                  * the copy was made.
265                                  * The second check avoids volatile variables.
266                                  * The third check avoids copy propagating local vregs through a call, 
267                                  * since the lvreg will be spilled 
268                                  * The fourth check avoids copy propagating a vreg in cases where
269                                  * it would be eliminated anyway by reverse copy propagation later,
270                                  * because propagating it would create another use for it, thus making 
271                                  * it impossible to use reverse copy propagation.
272                                  */
273                                 /* Enabling this for floats trips up the fp stack */
274                                 /* 
275                                  * Enabling this for floats on amd64 seems to cause a failure in 
276                                  * basic-math.cs, most likely because it gets rid of some r8->r4 
277                                  * conversions.
278                                  */
279                                 if (MONO_IS_MOVE (def) &&
280                                         (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg])) &&
281                                         !vreg_is_volatile (cfg, def->sreg1) &&
282                                         /* This avoids propagating local vregs across calls */
283                                         ((get_vreg_to_inst (cfg, def->sreg1) || !defs [def->sreg1] || (def_index [def->sreg1] >= last_call_index) || (def->opcode == OP_VMOVE))) &&
284                                         !(defs [def->sreg1] && mono_inst_next (defs [def->sreg1], filter) == def) &&
285                                         (!MONO_ARCH_USE_FPSTACK || (def->opcode != OP_FMOVE)) &&
286                                         (def->opcode != OP_FMOVE)) {
287                                         int vreg = def->sreg1;
288
289                                         if (cfg->verbose_level > 2) printf ("CCOPY/2: R%d -> R%d\n", sreg, vreg);
290                                         sregs [srcindex] = vreg;
291                                         mono_inst_set_src_registers (ins, sregs);
292
293                                         /* Allow further iterations */
294                                         srcindex = -1;
295                                         continue;
296                                 }
297
298                                 /* Constant propagation */
299                                 /* FIXME: Make is_inst_imm a macro */
300                                 /* FIXME: Make is_inst_imm take an opcode argument */
301                                 /* is_inst_imm is only needed for binops */
302                                 if ((((def->opcode == OP_ICONST) || ((sizeof (gpointer) == 8) && (def->opcode == OP_I8CONST))) &&
303                                          (((srcindex == 0) && (ins->sreg2 == -1)) || mono_arch_is_inst_imm (def->inst_c0))) || 
304                                         (!MONO_ARCH_USE_FPSTACK && (def->opcode == OP_R8CONST))) {
305                                         guint32 opcode2;
306
307                                         /* srcindex == 1 -> binop, ins->sreg2 == -1 -> unop */
308                                         if ((srcindex == 1) && (ins->sreg1 != -1) && defs [ins->sreg1] && (defs [ins->sreg1]->opcode == OP_ICONST) && defs [ins->sreg2]) {
309                                                 /* Both arguments are constants, perform cfold */
310                                                 mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], defs [ins->sreg2], TRUE);
311                                         } else if ((srcindex == 0) && (ins->sreg2 != -1) && defs [ins->sreg2]) {
312                                                 /* Arg 1 is constant, swap arguments if possible */
313                                                 int opcode = ins->opcode;
314                                                 mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], defs [ins->sreg2], TRUE);
315                                                 if (ins->opcode != opcode) {
316                                                         /* Allow further iterations */
317                                                         srcindex = -1;
318                                                         continue;
319                                                 }
320                                         } else if ((srcindex == 0) && (ins->sreg2 == -1)) {
321                                                 /* Constant unop, perform cfold */
322                                                 mono_constant_fold_ins (cfg, ins, defs [ins->sreg1], NULL, TRUE);
323                                         }
324
325                                         opcode2 = mono_op_to_op_imm (ins->opcode);
326                                         if ((opcode2 != -1) && mono_arch_is_inst_imm (def->inst_c0) && ((srcindex == 1) || (ins->sreg2 == -1))) {
327                                                 ins->opcode = opcode2;
328                                                 if ((def->opcode == OP_I8CONST) && (sizeof (gpointer) == 4)) {
329                                                         ins->inst_ls_word = def->inst_ls_word;
330                                                         ins->inst_ms_word = def->inst_ms_word;
331                                                 } else {
332                                                         ins->inst_imm = def->inst_c0;
333                                                 }
334                                                 sregs [srcindex] = -1;
335                                                 mono_inst_set_src_registers (ins, sregs);
336
337                                                 if ((opcode2 == OP_VOIDCALL) || (opcode2 == OP_CALL) || (opcode2 == OP_LCALL) || (opcode2 == OP_FCALL))
338                                                         ((MonoCallInst*)ins)->fptr = (gpointer)ins->inst_imm;
339
340                                                 /* Allow further iterations */
341                                                 srcindex = -1;
342                                                 continue;
343                                         }
344                                         else {
345                                                 /* Special cases */
346 #if defined(TARGET_X86) || defined(TARGET_AMD64)
347                                                 if ((ins->opcode == OP_X86_LEA) && (srcindex == 1)) {
348 #if SIZEOF_REGISTER == 8
349                                                         /* FIXME: Use OP_PADD_IMM when the new JIT is done */
350                                                         ins->opcode = OP_LADD_IMM;
351 #else
352                                                         ins->opcode = OP_ADD_IMM;
353 #endif
354                                                         ins->inst_imm += def->inst_c0 << ins->backend.shift_amount;
355                                                         ins->sreg2 = -1;
356                                                 }
357 #endif
358                                                 opcode2 = mono_load_membase_to_load_mem (ins->opcode);
359                                                 if ((srcindex == 0) && (opcode2 != -1) && mono_arch_is_inst_imm (def->inst_c0)) {
360                                                         ins->opcode = opcode2;
361                                                         ins->inst_imm = def->inst_c0 + ins->inst_offset;
362                                                         ins->sreg1 = -1;
363                                                 }
364                                         }
365                                 }
366                                 else if (((def->opcode == OP_ADD_IMM) || (def->opcode == OP_LADD_IMM)) && (MONO_IS_LOAD_MEMBASE (ins) || MONO_ARCH_IS_OP_MEMBASE (ins->opcode))) {
367                                         /* ADD_IMM is created by spill_global_vars */
368                                         /* 
369                                          * We have to guarantee that def->sreg1 haven't changed since def->dreg
370                                          * was defined. cfg->frame_reg is assumed to remain constant.
371                                          */
372                                         if ((def->sreg1 == cfg->frame_reg) || ((mono_inst_next (def, filter) == ins) && (def->dreg != def->sreg1))) {
373                                                 ins->inst_basereg = def->sreg1;
374                                                 ins->inst_offset += def->inst_imm;
375                                         }
376                                 } else if ((ins->opcode == OP_ISUB_IMM) && (def->opcode == OP_IADD_IMM) && (mono_inst_next (def, filter) == ins) && (def->dreg != def->sreg1)) {
377                                         ins->sreg1 = def->sreg1;
378                                         ins->inst_imm -= def->inst_imm;
379                                 } else if ((ins->opcode == OP_IADD_IMM) && (def->opcode == OP_ISUB_IMM) && (mono_inst_next (def, filter) == ins) && (def->dreg != def->sreg1)) {
380                                         ins->sreg1 = def->sreg1;
381                                         ins->inst_imm -= def->inst_imm;
382                                 } else if (ins->opcode == OP_STOREI1_MEMBASE_REG &&
383                                                    (def->opcode == OP_ICONV_TO_U1 || def->opcode == OP_ICONV_TO_I1 || def->opcode == OP_SEXT_I4 || (SIZEOF_REGISTER == 8 && def->opcode == OP_LCONV_TO_U1)) &&
384                                                    (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg]))) {
385                                         /* Avoid needless sign extension */
386                                         ins->sreg1 = def->sreg1;
387                                 } else if (ins->opcode == OP_STOREI2_MEMBASE_REG &&
388                                                    (def->opcode == OP_ICONV_TO_U2 || def->opcode == OP_ICONV_TO_I2 || def->opcode == OP_SEXT_I4 || (SIZEOF_REGISTER == 8 && def->opcode == OP_LCONV_TO_I2)) &&
389                                                    (!defs [def->sreg1] || (def_index [def->sreg1] < def_index [sreg]))) {
390                                         /* Avoid needless sign extension */
391                                         ins->sreg1 = def->sreg1;
392                                 }
393                         }
394
395                         /* Do strength reduction here */
396                         if (mono_strength_reduction_ins (cfg, bb, ins, &spec))
397                                 goto restart;
398
399                         if (spec [MONO_INST_DEST] != ' ') {
400                                 MonoInst *def = defs [ins->dreg];
401
402                                 if (def && (def->opcode == OP_ADD_IMM) && (def->sreg1 == cfg->frame_reg) && (MONO_IS_STORE_MEMBASE (ins))) {
403                                         /* ADD_IMM is created by spill_global_vars */
404                                         /* cfg->frame_reg is assumed to remain constant */
405                                         ins->inst_destbasereg = def->sreg1;
406                                         ins->inst_offset += def->inst_imm;
407                                 }
408                         }
409                         
410                         if ((spec [MONO_INST_DEST] != ' ') && !MONO_IS_STORE_MEMBASE (ins) && !vreg_is_volatile (cfg, ins->dreg)) {
411                                 defs [ins->dreg] = ins;
412                                 def_index [ins->dreg] = ins_index;
413                         }
414
415                         if (MONO_IS_CALL (ins))
416                                 last_call_index = ins_index;
417
418                         ins_index ++;
419                 }
420         }
421 }
422
423 static inline gboolean
424 reg_is_softreg_no_fpstack (int reg, const char spec)
425 {
426         return (spec == 'i' && reg >= MONO_MAX_IREGS)
427                 || ((spec == 'f' && reg >= MONO_MAX_FREGS) && !MONO_ARCH_USE_FPSTACK)
428 #ifdef MONO_ARCH_SIMD_INTRINSICS
429                 || (spec == 'x' && reg >= MONO_MAX_XREGS)
430 #endif
431                 || (spec == 'v');
432 }
433                 
434 static inline gboolean
435 reg_is_softreg (int reg, const char spec)
436 {
437         return (spec == 'i' && reg >= MONO_MAX_IREGS)
438                 || (spec == 'f' && reg >= MONO_MAX_FREGS)
439 #ifdef MONO_ARCH_SIMD_INTRINSICS
440                 || (spec == 'x' && reg >= MONO_MAX_XREGS)
441 #endif
442                 || (spec == 'v');
443 }
444
445 static inline gboolean
446 mono_is_simd_accessor (MonoInst *ins)
447 {
448         switch (ins->opcode) {
449 #ifdef MONO_ARCH_SIMD_INTRINSICS
450         case OP_INSERT_I1:
451         case OP_INSERT_I2:
452         case OP_INSERT_I4:
453         case OP_INSERT_I8:
454         case OP_INSERT_R4:
455         case OP_INSERT_R8:
456
457         case OP_INSERTX_U1_SLOW:
458         case OP_INSERTX_I4_SLOW:
459         case OP_INSERTX_R4_SLOW:
460         case OP_INSERTX_R8_SLOW:
461         case OP_INSERTX_I8_SLOW:
462                 return TRUE;
463 #endif
464         default:
465                 return FALSE;
466         }
467 }
468
469 /**
470  * mono_local_deadce:
471  *
472  *   Get rid of the dead assignments to local vregs like the ones created by the 
473  * copyprop pass.
474  */
475 void
476 mono_local_deadce (MonoCompile *cfg)
477 {
478         MonoBasicBlock *bb;
479         MonoInst *ins, *prev;
480         MonoBitSet *used, *defined;
481
482         //mono_print_code (cfg, "BEFORE LOCAL-DEADCE");
483
484         /*
485          * Assignments to global vregs can't be eliminated so this pass must come
486          * after the handle_global_vregs () pass.
487          */
488
489         used = mono_bitset_mp_new_noinit (cfg->mempool, cfg->next_vreg + 1);
490         defined = mono_bitset_mp_new_noinit (cfg->mempool, cfg->next_vreg + 1);
491
492         /* First pass: collect liveness info */
493         for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
494                 /* Manually init the defs entries used by the bblock */
495                 MONO_BB_FOR_EACH_INS (bb, ins) {
496                         const char *spec = INS_INFO (ins->opcode);
497                         int sregs [MONO_MAX_SRC_REGS];
498                         int num_sregs, i;
499
500                         if (spec [MONO_INST_DEST] != ' ') {
501                                 mono_bitset_clear_fast (used, ins->dreg);
502                                 mono_bitset_clear_fast (defined, ins->dreg);
503 #if SIZEOF_REGISTER == 4
504                                 /* Regpairs */
505                                 mono_bitset_clear_fast (used, ins->dreg + 1);
506                                 mono_bitset_clear_fast (defined, ins->dreg + 1);
507 #endif
508                         }
509                         num_sregs = mono_inst_get_src_registers (ins, sregs);
510                         for (i = 0; i < num_sregs; ++i) {
511                                 mono_bitset_clear_fast (used, sregs [i]);
512 #if SIZEOF_REGISTER == 4
513                                 mono_bitset_clear_fast (used, sregs [i] + 1);
514 #endif
515                         }
516                 }
517
518                 /*
519                  * Make a reverse pass over the instruction list
520                  */
521                 MONO_BB_FOR_EACH_INS_REVERSE_SAFE (bb, prev, ins) {
522                         const char *spec = INS_INFO (ins->opcode);
523                         int sregs [MONO_MAX_SRC_REGS];
524                         int num_sregs, i;
525                         MonoInst *prev_f = mono_inst_prev (ins, FILTER_NOP | FILTER_IL_SEQ_POINT);
526
527                         if (ins->opcode == OP_NOP) {
528                                 MONO_DELETE_INS (bb, ins);
529                                 continue;
530                         }
531
532                         g_assert (ins->opcode > MONO_CEE_LAST);
533
534                         if (MONO_IS_NON_FP_MOVE (ins) && prev_f) {
535                                 MonoInst *def;
536                                 const char *spec2;
537
538                                 def = prev_f;
539                                 spec2 = INS_INFO (def->opcode);
540
541                                 /* 
542                                  * Perform a limited kind of reverse copy propagation, i.e.
543                                  * transform B <- FOO; A <- B into A <- FOO
544                                  * This isn't copyprop, not deadce, but it can only be performed
545                                  * after handle_global_vregs () has run.
546                                  */
547                                 if (!get_vreg_to_inst (cfg, ins->sreg1) && (spec2 [MONO_INST_DEST] != ' ') && (def->dreg == ins->sreg1) && !mono_bitset_test_fast (used, ins->sreg1) && !MONO_IS_STORE_MEMBASE (def) && reg_is_softreg (ins->sreg1, spec [MONO_INST_DEST]) && !mono_is_simd_accessor (def)) {
548                                         if (cfg->verbose_level > 2) {
549                                                 printf ("\tReverse copyprop in BB%d on ", bb->block_num);
550                                                 mono_print_ins (ins);
551                                         }
552
553                                         def->dreg = ins->dreg;
554                                         MONO_DELETE_INS (bb, ins);
555                                         spec = INS_INFO (ins->opcode);
556                                 }
557                         }
558
559                         /* Enabling this on x86 could screw up the fp stack */
560                         if (reg_is_softreg_no_fpstack (ins->dreg, spec [MONO_INST_DEST])) {
561                                 /* 
562                                  * Assignments to global vregs can only be eliminated if there is another
563                                  * assignment to the same vreg later in the same bblock.
564                                  */
565                                 if (!mono_bitset_test_fast (used, ins->dreg) && 
566                                         (!get_vreg_to_inst (cfg, ins->dreg) || (!bb->extended && !vreg_is_volatile (cfg, ins->dreg) && mono_bitset_test_fast (defined, ins->dreg))) &&
567                                         MONO_INS_HAS_NO_SIDE_EFFECT (ins)) {
568                                         /* Happens with CMOV instructions */
569                                         if (prev_f && prev_f->opcode == OP_ICOMPARE_IMM) {
570                                                 MonoInst *prev = prev_f;
571                                                 /* 
572                                                  * Can't use DELETE_INS since that would interfere with the
573                                                  * FOR_EACH_INS loop.
574                                                  */
575                                                 NULLIFY_INS (prev);
576                                         }
577                                         //printf ("DEADCE: "); mono_print_ins (ins);
578                                         MONO_DELETE_INS (bb, ins);
579                                         spec = INS_INFO (ins->opcode);
580                                 }
581
582                                 if (spec [MONO_INST_DEST] != ' ')
583                                         mono_bitset_clear_fast (used, ins->dreg);
584                         }
585
586                         if (spec [MONO_INST_DEST] != ' ')
587                                 mono_bitset_set_fast (defined, ins->dreg);
588                         num_sregs = mono_inst_get_src_registers (ins, sregs);
589                         for (i = 0; i < num_sregs; ++i)
590                                 mono_bitset_set_fast (used, sregs [i]);
591                         if (MONO_IS_STORE_MEMBASE (ins))
592                                 mono_bitset_set_fast (used, ins->dreg);
593
594                         if (MONO_IS_CALL (ins)) {
595                                 MonoCallInst *call = (MonoCallInst*)ins;
596                                 GSList *l;
597
598                                 if (call->out_ireg_args) {
599                                         for (l = call->out_ireg_args; l; l = l->next) {
600                                                 guint32 regpair, reg;
601
602                                                 regpair = (guint32)(gssize)(l->data);
603                                                 reg = regpair & 0xffffff;
604                                         
605                                                 mono_bitset_set_fast (used, reg);
606                                         }
607                                 }
608
609                                 if (call->out_freg_args) {
610                                         for (l = call->out_freg_args; l; l = l->next) {
611                                                 guint32 regpair, reg;
612
613                                                 regpair = (guint32)(gssize)(l->data);
614                                                 reg = regpair & 0xffffff;
615                                         
616                                                 mono_bitset_set_fast (used, reg);
617                                         }
618                                 }
619                         }
620                 }
621         }
622
623         //mono_print_code (cfg, "AFTER LOCAL-DEADCE");
624 }
625
626 #endif /* DISABLE_JIT */